import type { Expr, AssignExpr, UpdateOp, UpdateFilter } from "./ast.ts"; export declare class CodegenError extends Error { readonly pos: number; constructor(message: string, pos?: number); } /** * Throw a `CodegenError` flagged as a jsmql bug. Use for invariants the * parser is supposed to uphold — if a user ever sees one of these messages, * something has slipped past the parser's validation and we want them to * report it. Keeps the wording consistent across every internal-only throw * site so they're trivially greppable. */ export declare function internalError(detail: string, pos?: number): never; export declare class UnknownIdentifierError extends CodegenError { identifier: string; constructor(identifier: string, pos?: number); } export type GenerateCtx = { lambdaParams: ReadonlySet; reduceRemap?: ReadonlyMap; /** * Pipeline-scoped `let` bindings in scope. Key is the user-facing name; value * is the field-path string to read it back (e.g. `"__jsmql.subtotal"` — no * leading `$`, that gets prepended at lookup sites). Threaded through * pipeline lowering by `pipeline.ts`; ignored in expression-mode codegen. */ pipelineLets?: ReadonlyMap; /** * Names of lets that were dropped by an earlier scope-reshaping stage * (`$group`, `$replaceRoot`, …). Value is the stage that dropped them, used * to produce a precise "let X can't be read after $group" error rather than * the generic "unknown identifier" fallback. */ droppedLets?: ReadonlyMap; /** * Function-form parameter bindings in scope. Key is the destructured binding * name; value is the raw JS value supplied at call time (already validated * JSON-safe by `validateInterpolatable`). A `ParamRef` whose name lives here * emits the value as an inline literal in the MQL output — the same shape * the template-tag form produces from `${value}` interpolation. Bindings * are *compile-time constants*, not document state, so unlike `pipelineLets` * they cross sub-pipeline boundaries; `freshSubPipelineCtx` preserves them. */ bindings?: ReadonlyMap; /** * Static type of selected in-scope lambda bindings. Currently populated only * by the `.reduce()` codegen when both `initialValue` and the lambda body * are statically the same compound type — see the reduce case below. Read by * the `IndexAccess` codegen to skip the runtime `$cond` on `$isArray` when * the receiver is a `ParamRef` whose name is in this map. Keyed by the * user-facing param name (pre-`reduceRemap`), so the lookup happens on the * raw AST `ParamRef.name` before any MQL variable-name remap. */ bindingTypes?: ReadonlyMap; /** * When true, suppress the auto-`$literal` wrap on `"$..."`-shaped string * literals. Set by the `$literal(...)` operator codegen on the recursive * call for its argument — the whole subtree is already inside a `$literal` * envelope, so MongoDB will not interpret nested strings as field refs and * a second wrap would produce a literal of a literal. Propagated through * `extendCtx` so it survives lambda bodies and other ctx-modifying paths. */ insideLiteral?: boolean; /** * When true, suppress the auto-`$literal` wrap on `"$..."`-shaped string * literals because we're in a *field-path position*, not an expression. Set * by `pipeline.ts` when lowering a `$unwind` body: `$unwind`'s path is a * field path (`"$items"`), so the leading `$` is exactly what the user means * — wrapping it in `$literal` would break the stage. Distinct from * `insideLiteral` (which means "already inside a `$literal` envelope"); the * effect on `literalSafeString` is the same, but the reason differs. */ fieldPathString?: boolean; /** * Accumulator context — set by `pipeline.ts` when descending into a `$group` * field-value body (other than `_id`) or a `$setWindowFields.output[]` * slot. Used by the operator-call codegen to gate operators that only make * sense inside one of these contexts: * - `"group"`: accumulator-only operators ($addToSet, $push, $bottom*, etc.) * are allowed; window-only operators ($rank, $denseRank, etc.) are NOT. * - `"window-output"`: BOTH accumulator-only AND window-only operators are * allowed. * - unset / undefined: neither — outside any aggregation accumulator scope. */ accumulatorContext?: "group" | "window-output"; }; declare const EMPTY_CTX: GenerateCtx; /** Add a new pipeline let to the context. Returns a fresh ctx; never mutates. */ export declare function extendCtxLets(ctx: GenerateCtx, name: string, fieldPath: string): GenerateCtx; /** Drop all pipeline lets, moving them to `droppedLets` with the stage name. */ export declare function clearCtxLets(ctx: GenerateCtx, droppedByStage: string): GenerateCtx; /** Public access for pipeline.ts to read the let-bindings count. */ export declare function ctxHasLets(ctx: GenerateCtx): boolean; /** * Construct a fresh ctx for sub-pipeline lowering. Outer `let` bindings do NOT * cross — they're per-document state and the sub-pipeline starts against a * different document (e.g. `$lookup.pipeline` runs against the foreign * collection). Function-form `bindings` DO cross: they are compile-time * constants, not document state, and inlining them inside a sub-pipeline is * the same shape as the user writing the literal there directly. */ export declare function freshSubPipelineCtx(outer: GenerateCtx): GenerateCtx; /** * Construct a sub-pipeline ctx that PRESERVES outer pipeline lets — used by * `$facet` branches. Unlike `$lookup.pipeline` / `$unionWith.pipeline` * (which operate on a different document set), every facet branch operates * on the SAME input docs that arrived at the outer pipeline's $facet stage. * Those docs still carry the `__jsmql.` fields the outer lets * materialised into, so `$__jsmql.` references inside the branch * resolve correctly. */ export declare function freshFacetCtx(outer: GenerateCtx): GenerateCtx; /** Return a fresh ctx with the given function-form parameter bindings applied. */ export declare function withBindings(ctx: GenerateCtx, bindings: ReadonlyMap): GenerateCtx; /** Public re-export of EMPTY_CTX for pipeline.ts. */ export { EMPTY_CTX }; /** * BSON instance values that the MongoDB driver consumes in-situ (i.e. the * driver expects the actual JS object, not a JSON-shaped surrogate). They * have no fidelity-preserving JSON representation: `JSON.stringify` returns * `"{}"` for `RegExp` and `Uint8Array`, an ISO string for `Date` (which * compares as a string in BSON, not a date), and `{}` for ObjectId. * * ObjectId is detected by `_bsontype` because importing the MongoDB driver * would add a hard dependency the library deliberately avoids; the BSON * library tags instances with `"ObjectID"` (older versions) or `"ObjectId"` * (newer versions). Accept both. */ export declare function isOpaqueBsonValue(value: unknown): boolean; export declare function generate(expr: Expr): unknown; /** Generate an expression with an explicit context (e.g. pipeline-let bindings). */ export declare function generateWithCtx(expr: Expr, ctx: GenerateCtx): unknown; type DirectBinaryOp = "-" | "/" | "%" | "**" | "===" | "!==" | ">" | ">=" | "<" | "<="; type ChainBinaryOp = "*" | "??" | "&" | "|" | "^"; /** The MQL operator name for a direct/chain binary op — the one accessor other * modules use (match-translation's query-document path), so `BINARY_OP_TO_MQL` * stays the single source of truth. */ export declare function mqlForBinaryOp(op: DirectBinaryOp | ChainBinaryOp): string; /** * Predicate: can `target` appear on the LHS of an `AssignExpr`? Mirrors the * parser's constraint for `$. = ` — a `FieldRef` with a non-empty * path, or a `MemberAccess` chain rooted at one. Bare `$` (path `""`) is the * `$replaceWith` sugar, not an assignable field. */ export declare function isWritableFieldPath(expr: Expr): boolean; export type MutatorRewrite = { kind: "rewrite"; assign: AssignExpr; } | { kind: "passthrough"; }; /** * If `expr` is a mutating array method on a writable field-path receiver, * return the synthesized `$. = ` `AssignExpr`. The RHS * AST is built from existing Expr node types so it flows through normal * codegen — there is no per-mutator branch in the lowering path. */ export declare function tryRewriteMutatorCall(expr: Expr): MutatorRewrite; /** * Compile a top-level `UpdateFilter` to either a single stage object (if * everything coalesces into one $set/$unset) or an array of stage objects. * * The shape mirrors `jsmql()`'s existing top-level convention: one stage → * bare object, multiple stages → array. */ export declare function generateUpdateFilter(prog: UpdateFilter, ctx?: GenerateCtx): object | object[]; /** * Coalescer used by both jsmql() top-level update ops and by pipeline.ts when * update ops appear as pipeline elements. Returns one or more stage objects. * * Grouping rule (preserves JS sequential semantics): * - Consecutive same-kind (assign/delete) update ops join one group, UNLESS * - A new update op's write path collides (equals or is a parent/child) with * any prior write in the group, OR * - For assignments: the new RHS reads any path that was written earlier in * the group. (Delete has no reads.) */ export declare function generateUpdateOpGroups(ops: UpdateOp[], ctx?: GenerateCtx): object[]; /** Reconstruct the dotted write path from a update op target. */ export declare function updateOpWritePath(m: UpdateOp): string; //# sourceMappingURL=codegen.d.ts.map