@prisma-next/mongo-query-builder 0.12.0-dev.50 → 0.12.0-dev.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,78 @@
1
+ import { b as DocShape, c as UpdaterResult, n as FieldAccessor } from "../field-accessor-DvsRehA9.mjs";
2
+ import { AggregateCommand, FindOneAndUpdateCommand, InsertOneCommand, MongoFilterExpr, MongoPipelineStage } from "@prisma-next/mongo-query-ast/execution";
3
+ import { MongoValue } from "@prisma-next/mongo-value";
4
+
5
+ //#region src/contract-free/collection.d.ts
6
+ /**
7
+ * Fluent aggregate chain. Accumulates `$match` / `$sort` / `$limit` stages and
8
+ * produces an `AggregateCommand` via `.build()`.
9
+ *
10
+ * Instances are immutable — each stage method returns a new chain.
11
+ */
12
+ declare class AggregateChain<Shape extends DocShape> {
13
+ #private;
14
+ constructor(collection: string, stages: ReadonlyArray<MongoPipelineStage>);
15
+ match(filterFn: (fields: FieldAccessor<Shape>) => MongoFilterExpr): AggregateChain<Shape>;
16
+ sort(spec: Record<string, 1 | -1>): AggregateChain<Shape>;
17
+ limit(n: number): AggregateChain<Shape>;
18
+ build(): AggregateCommand;
19
+ }
20
+ /**
21
+ * Fluent find-and-modify chain. Holds an accumulated list of filter expressions
22
+ * (AND-folded into the wire command's `filter` slot) and exposes
23
+ * `.findOneAndUpdate(...)` as the only terminal.
24
+ *
25
+ * Multiple `.match()` calls AND-fold internally — `MongoAndExpr.of` never
26
+ * appears at the call site.
27
+ *
28
+ * Instances are immutable — `.match()` returns a new `FilteredBuilder`.
29
+ */
30
+ declare class FilteredBuilder<Shape extends DocShape> {
31
+ #private;
32
+ constructor(collection: string, filters: ReadonlyArray<MongoFilterExpr>);
33
+ match(filterFn: (fields: FieldAccessor<Shape>) => MongoFilterExpr): FilteredBuilder<Shape>;
34
+ findOneAndUpdate(updaterFn: (fields: FieldAccessor<Shape>) => UpdaterResult, opts?: {
35
+ readonly upsert?: boolean;
36
+ readonly returnDocument?: 'before' | 'after';
37
+ }): FindOneAndUpdateCommand;
38
+ }
39
+ /**
40
+ * Contract-free fluent Mongo collection builder. Produces the canonical
41
+ * `AggregateCommand` / `InsertOneCommand` / `FindOneAndUpdateCommand` command
42
+ * nodes without any contract coupling — parameterised by an explicit `DocShape`
43
+ * instead of a `MongoContract`.
44
+ *
45
+ * Mirrors SQL's `table(source, schema)` → `TableHandle` in spirit: a top-level
46
+ * entry point that exposes fluent query chains from which call sites never write
47
+ * `new MongoMatchStage(...)`, `MongoAndExpr.of([...])`, or `new AggregateCommand(...)`.
48
+ *
49
+ * ```ts
50
+ * const markerLedger = collection<MarkerLedgerDocShape>('_prisma_migrations');
51
+ *
52
+ * // aggregate
53
+ * markerLedger.aggregate().match(f => f._id.eq(space)).limit(1).build();
54
+ *
55
+ * // insertOne
56
+ * markerLedger.insertOne({ _id: space, space, storageHash });
57
+ *
58
+ * // findOneAndUpdate (CAS)
59
+ * markerLedger.match(f => f._id.eq(space)).match(f => f.storageHash.eq(expectedFrom))
60
+ * .findOneAndUpdate(f => [f.stage.set({ storageHash: newHash })], { upsert: false });
61
+ * ```
62
+ */
63
+ interface CollectionBuilder<Shape extends DocShape> {
64
+ aggregate(): AggregateChain<Shape>;
65
+ insertOne(document: Record<string, MongoValue>): InsertOneCommand;
66
+ match(filterFn: (fields: FieldAccessor<Shape>) => MongoFilterExpr): FilteredBuilder<Shape>;
67
+ }
68
+ /**
69
+ * Declare a contract-free collection builder parameterised by a `DocShape`.
70
+ * The collection name is bound once; field access reuses `createFieldAccessor`
71
+ * so the shape is typed at every call site without a contract.
72
+ *
73
+ * @param name The MongoDB collection name (e.g. `'_prisma_migrations'`)
74
+ */
75
+ declare function collection<Shape extends DocShape>(name: string): CollectionBuilder<Shape>;
76
+ //#endregion
77
+ export { AggregateChain, type CollectionBuilder, FilteredBuilder, collection };
78
+ //# sourceMappingURL=contract-free.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract-free.d.mts","names":[],"sources":["../../src/contract-free/collection.ts"],"mappings":";;;;;AAoCA;;;;;;AAAA,cAAa,cAAA,eAA6B,QAAA;EAAA;cAI5B,UAAA,UAAoB,MAAA,EAAQ,aAAA,CAAc,kBAAA;EAKtD,KAAA,CAAM,QAAA,GAAW,MAAA,EAAQ,aAAA,CAAc,KAAA,MAAW,eAAA,GAAkB,cAAA,CAAe,KAAA;EASnF,IAAA,CAAK,IAAA,EAAM,MAAA,mBAAyB,cAAA,CAAe,KAAA;EAInD,KAAA,CAAM,CAAA,WAAY,cAAA,CAAe,KAAA;EAIjC,KAAA,CAAA,GAAS,gBAAA;AAAA;;;;;;;;;;;cAeE,eAAA,eAA8B,QAAA;EAAA;cAI7B,UAAA,UAAoB,OAAA,EAAS,aAAA,CAAc,eAAA;EAQvD,KAAA,CAAM,QAAA,GAAW,MAAA,EAAQ,aAAA,CAAc,KAAA,MAAW,eAAA,GAAkB,eAAA,CAAgB,KAAA;EAMpF,gBAAA,CACE,SAAA,GAAY,MAAA,EAAQ,aAAA,CAAc,KAAA,MAAW,aAAA,EAC7C,IAAA;IAAA,SAAiB,MAAA;IAAA,SAA2B,cAAA;EAAA,IAC3C,uBAAA;AAAA;;;;;;;;;;;;;;;AApCsB;AAe3B;;;;;;;;;UA6DiB,iBAAA,eAAgC,QAAA;EAC/C,SAAA,IAAa,cAAA,CAAe,KAAA;EAC5B,SAAA,CAAU,QAAA,EAAU,MAAA,SAAe,UAAA,IAAc,gBAAA;EACjD,KAAA,CAAM,QAAA,GAAW,MAAA,EAAQ,aAAA,CAAc,KAAA,MAAW,eAAA,GAAkB,eAAA,CAAgB,KAAA;AAAA;;;;;;;;iBAgCtE,UAAA,eAAyB,QAAA,CAAA,CAAU,IAAA,WAAe,iBAAA,CAAkB,KAAA"}
@@ -0,0 +1,98 @@
1
+ import { r as resolveUpdaterResult, t as createFieldAccessor } from "../field-accessor-D9Oy4PUD.mjs";
2
+ import { AggregateCommand, FindOneAndUpdateCommand, InsertOneCommand, MongoAndExpr, MongoLimitStage, MongoMatchStage, MongoSortStage } from "@prisma-next/mongo-query-ast/execution";
3
+ //#region src/contract-free/collection.ts
4
+ /**
5
+ * Fold an array of filter expressions into a single `MongoFilterExpr`. Length-1
6
+ * short-circuits to avoid a redundant `$and` wrapper; the call site never writes
7
+ * `MongoAndExpr.of([...])` directly.
8
+ */
9
+ function foldFilters(filters) {
10
+ const first = filters[0];
11
+ if (first === void 0) throw new Error("foldFilters: invariant violated — empty filter list");
12
+ return filters.length === 1 ? first : MongoAndExpr.of(filters);
13
+ }
14
+ /**
15
+ * Fluent aggregate chain. Accumulates `$match` / `$sort` / `$limit` stages and
16
+ * produces an `AggregateCommand` via `.build()`.
17
+ *
18
+ * Instances are immutable — each stage method returns a new chain.
19
+ */
20
+ var AggregateChain = class AggregateChain {
21
+ #collection;
22
+ #stages;
23
+ constructor(collection, stages) {
24
+ this.#collection = collection;
25
+ this.#stages = stages;
26
+ }
27
+ match(filterFn) {
28
+ const filter = filterFn(createFieldAccessor());
29
+ return new AggregateChain(this.#collection, [...this.#stages, new MongoMatchStage(filter)]);
30
+ }
31
+ sort(spec) {
32
+ return new AggregateChain(this.#collection, [...this.#stages, new MongoSortStage(spec)]);
33
+ }
34
+ limit(n) {
35
+ return new AggregateChain(this.#collection, [...this.#stages, new MongoLimitStage(n)]);
36
+ }
37
+ build() {
38
+ return new AggregateCommand(this.#collection, this.#stages);
39
+ }
40
+ };
41
+ /**
42
+ * Fluent find-and-modify chain. Holds an accumulated list of filter expressions
43
+ * (AND-folded into the wire command's `filter` slot) and exposes
44
+ * `.findOneAndUpdate(...)` as the only terminal.
45
+ *
46
+ * Multiple `.match()` calls AND-fold internally — `MongoAndExpr.of` never
47
+ * appears at the call site.
48
+ *
49
+ * Instances are immutable — `.match()` returns a new `FilteredBuilder`.
50
+ */
51
+ var FilteredBuilder = class FilteredBuilder {
52
+ #collection;
53
+ #filters;
54
+ constructor(collection, filters) {
55
+ if (filters.length === 0) throw new Error("FilteredBuilder requires at least one filter");
56
+ this.#collection = collection;
57
+ this.#filters = filters;
58
+ }
59
+ match(filterFn) {
60
+ const filter = filterFn(createFieldAccessor());
61
+ return new FilteredBuilder(this.#collection, [...this.#filters, filter]);
62
+ }
63
+ findOneAndUpdate(updaterFn, opts = {}) {
64
+ const filter = foldFilters(this.#filters);
65
+ const update = resolveUpdaterResult(updaterFn(createFieldAccessor()));
66
+ return new FindOneAndUpdateCommand(this.#collection, filter, update, opts.upsert ?? false, void 0, opts.returnDocument ?? "after");
67
+ }
68
+ };
69
+ var CollectionBuilderImpl = class {
70
+ #name;
71
+ constructor(name) {
72
+ this.#name = name;
73
+ }
74
+ aggregate() {
75
+ return new AggregateChain(this.#name, []);
76
+ }
77
+ insertOne(document) {
78
+ return new InsertOneCommand(this.#name, document);
79
+ }
80
+ match(filterFn) {
81
+ const filter = filterFn(createFieldAccessor());
82
+ return new FilteredBuilder(this.#name, [filter]);
83
+ }
84
+ };
85
+ /**
86
+ * Declare a contract-free collection builder parameterised by a `DocShape`.
87
+ * The collection name is bound once; field access reuses `createFieldAccessor`
88
+ * so the shape is typed at every call site without a contract.
89
+ *
90
+ * @param name The MongoDB collection name (e.g. `'_prisma_migrations'`)
91
+ */
92
+ function collection(name) {
93
+ return new CollectionBuilderImpl(name);
94
+ }
95
+ //#endregion
96
+ export { AggregateChain, FilteredBuilder, collection };
97
+
98
+ //# sourceMappingURL=contract-free.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract-free.mjs","names":["#collection","#stages","#filters","#name"],"sources":["../../src/contract-free/collection.ts"],"sourcesContent":["import {\n AggregateCommand,\n FindOneAndUpdateCommand,\n InsertOneCommand,\n MongoAndExpr,\n type MongoFilterExpr,\n MongoLimitStage,\n MongoMatchStage,\n type MongoPipelineStage,\n MongoSortStage,\n type MongoUpdatePipelineStage,\n} from '@prisma-next/mongo-query-ast/execution';\nimport type { MongoValue } from '@prisma-next/mongo-value';\nimport { createFieldAccessor, type FieldAccessor } from '../field-accessor';\nimport type { DocShape } from '../types';\nimport { resolveUpdaterResult, type UpdaterResult } from '../update-ops';\n\n/**\n * Fold an array of filter expressions into a single `MongoFilterExpr`. Length-1\n * short-circuits to avoid a redundant `$and` wrapper; the call site never writes\n * `MongoAndExpr.of([...])` directly.\n */\nfunction foldFilters(filters: ReadonlyArray<MongoFilterExpr>): MongoFilterExpr {\n const first = filters[0];\n if (first === undefined) {\n throw new Error('foldFilters: invariant violated — empty filter list');\n }\n return filters.length === 1 ? first : MongoAndExpr.of(filters);\n}\n\n/**\n * Fluent aggregate chain. Accumulates `$match` / `$sort` / `$limit` stages and\n * produces an `AggregateCommand` via `.build()`.\n *\n * Instances are immutable — each stage method returns a new chain.\n */\nexport class AggregateChain<Shape extends DocShape> {\n readonly #collection: string;\n readonly #stages: ReadonlyArray<MongoPipelineStage>;\n\n constructor(collection: string, stages: ReadonlyArray<MongoPipelineStage>) {\n this.#collection = collection;\n this.#stages = stages;\n }\n\n match(filterFn: (fields: FieldAccessor<Shape>) => MongoFilterExpr): AggregateChain<Shape> {\n const f = createFieldAccessor<Shape>();\n const filter = filterFn(f);\n return new AggregateChain<Shape>(this.#collection, [\n ...this.#stages,\n new MongoMatchStage(filter),\n ]);\n }\n\n sort(spec: Record<string, 1 | -1>): AggregateChain<Shape> {\n return new AggregateChain<Shape>(this.#collection, [...this.#stages, new MongoSortStage(spec)]);\n }\n\n limit(n: number): AggregateChain<Shape> {\n return new AggregateChain<Shape>(this.#collection, [...this.#stages, new MongoLimitStage(n)]);\n }\n\n build(): AggregateCommand {\n return new AggregateCommand(this.#collection, this.#stages);\n }\n}\n\n/**\n * Fluent find-and-modify chain. Holds an accumulated list of filter expressions\n * (AND-folded into the wire command's `filter` slot) and exposes\n * `.findOneAndUpdate(...)` as the only terminal.\n *\n * Multiple `.match()` calls AND-fold internally — `MongoAndExpr.of` never\n * appears at the call site.\n *\n * Instances are immutable — `.match()` returns a new `FilteredBuilder`.\n */\nexport class FilteredBuilder<Shape extends DocShape> {\n readonly #collection: string;\n readonly #filters: ReadonlyArray<MongoFilterExpr>;\n\n constructor(collection: string, filters: ReadonlyArray<MongoFilterExpr>) {\n if (filters.length === 0) {\n throw new Error('FilteredBuilder requires at least one filter');\n }\n this.#collection = collection;\n this.#filters = filters;\n }\n\n match(filterFn: (fields: FieldAccessor<Shape>) => MongoFilterExpr): FilteredBuilder<Shape> {\n const f = createFieldAccessor<Shape>();\n const filter = filterFn(f);\n return new FilteredBuilder<Shape>(this.#collection, [...this.#filters, filter]);\n }\n\n findOneAndUpdate(\n updaterFn: (fields: FieldAccessor<Shape>) => UpdaterResult,\n opts: { readonly upsert?: boolean; readonly returnDocument?: 'before' | 'after' } = {},\n ): FindOneAndUpdateCommand {\n const filter = foldFilters(this.#filters);\n const f = createFieldAccessor<Shape>();\n const items = updaterFn(f);\n const update = resolveUpdaterResult(items);\n return new FindOneAndUpdateCommand(\n this.#collection,\n filter,\n update,\n opts.upsert ?? false,\n undefined,\n opts.returnDocument ?? 'after',\n );\n }\n}\n\n/**\n * Contract-free fluent Mongo collection builder. Produces the canonical\n * `AggregateCommand` / `InsertOneCommand` / `FindOneAndUpdateCommand` command\n * nodes without any contract coupling — parameterised by an explicit `DocShape`\n * instead of a `MongoContract`.\n *\n * Mirrors SQL's `table(source, schema)` → `TableHandle` in spirit: a top-level\n * entry point that exposes fluent query chains from which call sites never write\n * `new MongoMatchStage(...)`, `MongoAndExpr.of([...])`, or `new AggregateCommand(...)`.\n *\n * ```ts\n * const markerLedger = collection<MarkerLedgerDocShape>('_prisma_migrations');\n *\n * // aggregate\n * markerLedger.aggregate().match(f => f._id.eq(space)).limit(1).build();\n *\n * // insertOne\n * markerLedger.insertOne({ _id: space, space, storageHash });\n *\n * // findOneAndUpdate (CAS)\n * markerLedger.match(f => f._id.eq(space)).match(f => f.storageHash.eq(expectedFrom))\n * .findOneAndUpdate(f => [f.stage.set({ storageHash: newHash })], { upsert: false });\n * ```\n */\nexport interface CollectionBuilder<Shape extends DocShape> {\n aggregate(): AggregateChain<Shape>;\n insertOne(document: Record<string, MongoValue>): InsertOneCommand;\n match(filterFn: (fields: FieldAccessor<Shape>) => MongoFilterExpr): FilteredBuilder<Shape>;\n}\n\nclass CollectionBuilderImpl<Shape extends DocShape> implements CollectionBuilder<Shape> {\n readonly #name: string;\n\n constructor(name: string) {\n this.#name = name;\n }\n\n aggregate(): AggregateChain<Shape> {\n return new AggregateChain<Shape>(this.#name, []);\n }\n\n insertOne(document: Record<string, MongoValue>): InsertOneCommand {\n return new InsertOneCommand(this.#name, document);\n }\n\n match(filterFn: (fields: FieldAccessor<Shape>) => MongoFilterExpr): FilteredBuilder<Shape> {\n const f = createFieldAccessor<Shape>();\n const filter = filterFn(f);\n return new FilteredBuilder<Shape>(this.#name, [filter]);\n }\n}\n\n/**\n * Declare a contract-free collection builder parameterised by a `DocShape`.\n * The collection name is bound once; field access reuses `createFieldAccessor`\n * so the shape is typed at every call site without a contract.\n *\n * @param name The MongoDB collection name (e.g. `'_prisma_migrations'`)\n */\nexport function collection<Shape extends DocShape>(name: string): CollectionBuilder<Shape> {\n return new CollectionBuilderImpl<Shape>(name);\n}\n\nexport type { MongoUpdatePipelineStage };\n"],"mappings":";;;;;;;;AAsBA,SAAS,YAAY,SAA0D;CAC7E,MAAM,QAAQ,QAAQ;CACtB,IAAI,UAAU,KAAA,GACZ,MAAM,IAAI,MAAM,qDAAqD;CAEvE,OAAO,QAAQ,WAAW,IAAI,QAAQ,aAAa,GAAG,OAAO;AAC/D;;;;;;;AAQA,IAAa,iBAAb,MAAa,eAAuC;CAClD;CACA;CAEA,YAAY,YAAoB,QAA2C;EACzE,KAAKA,cAAc;EACnB,KAAKC,UAAU;CACjB;CAEA,MAAM,UAAoF;EAExF,MAAM,SAAS,SADL,oBACc,CAAC;EACzB,OAAO,IAAI,eAAsB,KAAKD,aAAa,CACjD,GAAG,KAAKC,SACR,IAAI,gBAAgB,MAAM,CAC5B,CAAC;CACH;CAEA,KAAK,MAAqD;EACxD,OAAO,IAAI,eAAsB,KAAKD,aAAa,CAAC,GAAG,KAAKC,SAAS,IAAI,eAAe,IAAI,CAAC,CAAC;CAChG;CAEA,MAAM,GAAkC;EACtC,OAAO,IAAI,eAAsB,KAAKD,aAAa,CAAC,GAAG,KAAKC,SAAS,IAAI,gBAAgB,CAAC,CAAC,CAAC;CAC9F;CAEA,QAA0B;EACxB,OAAO,IAAI,iBAAiB,KAAKD,aAAa,KAAKC,OAAO;CAC5D;AACF;;;;;;;;;;;AAYA,IAAa,kBAAb,MAAa,gBAAwC;CACnD;CACA;CAEA,YAAY,YAAoB,SAAyC;EACvE,IAAI,QAAQ,WAAW,GACrB,MAAM,IAAI,MAAM,8CAA8C;EAEhE,KAAKD,cAAc;EACnB,KAAKE,WAAW;CAClB;CAEA,MAAM,UAAqF;EAEzF,MAAM,SAAS,SADL,oBACc,CAAC;EACzB,OAAO,IAAI,gBAAuB,KAAKF,aAAa,CAAC,GAAG,KAAKE,UAAU,MAAM,CAAC;CAChF;CAEA,iBACE,WACA,OAAoF,CAAC,GAC5D;EACzB,MAAM,SAAS,YAAY,KAAKA,QAAQ;EAGxC,MAAM,SAAS,qBADD,UADJ,oBACc,CACgB,CAAC;EACzC,OAAO,IAAI,wBACT,KAAKF,aACL,QACA,QACA,KAAK,UAAU,OACf,KAAA,GACA,KAAK,kBAAkB,OACzB;CACF;AACF;AAgCA,IAAM,wBAAN,MAAwF;CACtF;CAEA,YAAY,MAAc;EACxB,KAAKG,QAAQ;CACf;CAEA,YAAmC;EACjC,OAAO,IAAI,eAAsB,KAAKA,OAAO,CAAC,CAAC;CACjD;CAEA,UAAU,UAAwD;EAChE,OAAO,IAAI,iBAAiB,KAAKA,OAAO,QAAQ;CAClD;CAEA,MAAM,UAAqF;EAEzF,MAAM,SAAS,SADL,oBACc,CAAC;EACzB,OAAO,IAAI,gBAAuB,KAAKA,OAAO,CAAC,MAAM,CAAC;CACxD;AACF;;;;;;;;AASA,SAAgB,WAAmC,MAAwC;CACzF,OAAO,IAAI,sBAA6B,IAAI;AAC9C"}
@@ -0,0 +1,230 @@
1
+ import { MongoAddFieldsStage, MongoAggFieldRef, MongoExistsExpr, MongoExprFilter, MongoFieldFilter, MongoProjectStage, MongoReplaceRootStage } from "@prisma-next/mongo-query-ast/execution";
2
+ //#region src/update-ops.ts
3
+ const setOp = (path, value) => ({
4
+ op: "$set",
5
+ path,
6
+ value
7
+ });
8
+ const unsetOp = (path) => ({
9
+ op: "$unset",
10
+ path
11
+ });
12
+ const renameOp = (path, newName) => ({
13
+ op: "$rename",
14
+ path,
15
+ newName
16
+ });
17
+ const incOp = (path, amount) => ({
18
+ op: "$inc",
19
+ path,
20
+ amount
21
+ });
22
+ const mulOp = (path, factor) => ({
23
+ op: "$mul",
24
+ path,
25
+ factor
26
+ });
27
+ const minOp = (path, value) => ({
28
+ op: "$min",
29
+ path,
30
+ value
31
+ });
32
+ const maxOp = (path, value) => ({
33
+ op: "$max",
34
+ path,
35
+ value
36
+ });
37
+ const pushOp = (path, value) => ({
38
+ op: "$push",
39
+ path,
40
+ value
41
+ });
42
+ const addToSetOp = (path, value) => ({
43
+ op: "$addToSet",
44
+ path,
45
+ value
46
+ });
47
+ const popOp = (path, direction) => ({
48
+ op: "$pop",
49
+ path,
50
+ direction
51
+ });
52
+ const pullOp = (path, value) => ({
53
+ op: "$pull",
54
+ path,
55
+ value
56
+ });
57
+ const pullAllOp = (path, values) => ({
58
+ op: "$pullAll",
59
+ path,
60
+ values
61
+ });
62
+ const currentDateOp = (path) => ({
63
+ op: "$currentDate",
64
+ path
65
+ });
66
+ const setOnInsertOp = (path, value) => ({
67
+ op: "$setOnInsert",
68
+ path,
69
+ value
70
+ });
71
+ /**
72
+ * Fold an array of `TypedUpdateOp` into the non-pipeline variant of
73
+ * `MongoUpdateSpec` (`{ $set: { … }, $inc: { … }, … }`).
74
+ *
75
+ * Throws if the same operator targets the same path twice — a clear authoring
76
+ * error that Mongo would otherwise silently coalesce.
77
+ */
78
+ function foldUpdateOps(ops) {
79
+ const buckets = {};
80
+ const seen = /* @__PURE__ */ new Set();
81
+ const ensure = (key) => {
82
+ let bucket = buckets[key];
83
+ if (!bucket) {
84
+ bucket = {};
85
+ buckets[key] = bucket;
86
+ }
87
+ return bucket;
88
+ };
89
+ const claim = (op, path) => {
90
+ const k = `${op}::${path}`;
91
+ if (seen.has(k)) throw new Error(`Update spec collision: ${op} on '${path}' was specified more than once. Combine the operations into a single call site.`);
92
+ seen.add(k);
93
+ };
94
+ for (const entry of ops) {
95
+ claim(entry.op, entry.path);
96
+ switch (entry.op) {
97
+ case "$set":
98
+ case "$min":
99
+ case "$max":
100
+ case "$push":
101
+ case "$addToSet":
102
+ case "$pull":
103
+ case "$setOnInsert":
104
+ ensure(entry.op)[entry.path] = entry.value;
105
+ break;
106
+ case "$unset":
107
+ ensure("$unset")[entry.path] = "";
108
+ break;
109
+ case "$rename":
110
+ ensure("$rename")[entry.path] = entry.newName;
111
+ break;
112
+ case "$inc":
113
+ ensure("$inc")[entry.path] = entry.amount;
114
+ break;
115
+ case "$mul":
116
+ ensure("$mul")[entry.path] = entry.factor;
117
+ break;
118
+ case "$pop":
119
+ ensure("$pop")[entry.path] = entry.direction;
120
+ break;
121
+ case "$pullAll":
122
+ ensure("$pullAll")[entry.path] = entry.values;
123
+ break;
124
+ case "$currentDate":
125
+ ensure("$currentDate")[entry.path] = true;
126
+ break;
127
+ }
128
+ }
129
+ return buckets;
130
+ }
131
+ /**
132
+ * Classify an array of updater items and produce a `MongoUpdateSpec`.
133
+ *
134
+ * - All `TypedUpdateOp` → fold via `foldUpdateOps` (classic `{ $set, $inc, … }`)
135
+ * - All `MongoUpdatePipelineStage` → return as-is (pipeline-style update)
136
+ * - Mixed → throw (also a type error at the call site via the union shape)
137
+ */
138
+ function resolveUpdaterResult(items) {
139
+ if (items.length === 0) throw new Error("Updater returned no operations. Return at least one update from the callback (e.g. `[f.amount.set(0)]`).");
140
+ const isOp = (item) => "op" in item && typeof item.op === "string";
141
+ const first = items[0];
142
+ if (first === void 0) throw new Error("Unreachable: items.length > 0 but first is undefined");
143
+ const firstIsOp = isOp(first);
144
+ for (let i = 1; i < items.length; i++) {
145
+ const item = items[i];
146
+ if (item === void 0) continue;
147
+ if (isOp(item) !== firstIsOp) throw new Error("Cannot mix TypedUpdateOp values and pipeline stages in a single updater. Use either `[f.amount.set(0)]` (operator form) or `[f.stage.set({...})]` (pipeline form), not both.");
148
+ }
149
+ if (firstIsOp) return foldUpdateOps(items);
150
+ return items;
151
+ }
152
+ //#endregion
153
+ //#region src/field-accessor.ts
154
+ function buildStageEmitters() {
155
+ return {
156
+ set: (fields) => new MongoAddFieldsStage(fields),
157
+ unset: (...paths) => {
158
+ const spec = {};
159
+ for (const p of paths) spec[p] = 0;
160
+ return new MongoProjectStage(spec);
161
+ },
162
+ replaceRoot: (newRoot) => new MongoReplaceRootStage(newRoot),
163
+ replaceWith: (newRoot) => new MongoReplaceRootStage(newRoot)
164
+ };
165
+ }
166
+ /**
167
+ * Wrap a boolean aggregation expression as an `$expr` filter
168
+ * (`MongoExprFilter`). Lets a `$match` express an aggregation-expression
169
+ * predicate — e.g. comparing two field references via `fn.eq(a, b)` — in
170
+ * the typed AST rather than via a raw escape hatch. Pairs with `.type()`
171
+ * to express filters like `{ _id: { $type: 'string' }, $expr: { $eq: ['$_id', '$space'] } }`.
172
+ */
173
+ function expr(predicate) {
174
+ return MongoExprFilter.of(predicate.node);
175
+ }
176
+ function buildExpression(path) {
177
+ return {
178
+ _field: void 0,
179
+ _path: path,
180
+ node: MongoAggFieldRef.of(path),
181
+ eq: (value) => MongoFieldFilter.eq(path, value),
182
+ ne: (value) => MongoFieldFilter.neq(path, value),
183
+ gt: (value) => MongoFieldFilter.gt(path, value),
184
+ gte: (value) => MongoFieldFilter.gte(path, value),
185
+ lt: (value) => MongoFieldFilter.lt(path, value),
186
+ lte: (value) => MongoFieldFilter.lte(path, value),
187
+ in: (values) => MongoFieldFilter.in(path, values),
188
+ nin: (values) => MongoFieldFilter.nin(path, values),
189
+ exists: (flag) => flag === false ? MongoExistsExpr.notExists(path) : MongoExistsExpr.exists(path),
190
+ type: (bsonType) => MongoFieldFilter.of(path, "$type", bsonType),
191
+ set: (value) => setOp(path, value),
192
+ unset: () => unsetOp(path),
193
+ rename: (newName) => renameOp(path, newName),
194
+ inc: (amount) => incOp(path, amount),
195
+ mul: (factor) => mulOp(path, factor),
196
+ min: (value) => minOp(path, value),
197
+ max: (value) => maxOp(path, value),
198
+ push: (value) => pushOp(path, value),
199
+ addToSet: (value) => addToSetOp(path, value),
200
+ pop: (direction = 1) => popOp(path, direction),
201
+ pull: (value) => pullOp(path, value),
202
+ pullAll: (values) => pullAllOp(path, values),
203
+ currentDate: () => currentDateOp(path),
204
+ setOnInsert: (value) => setOnInsertOp(path, value)
205
+ };
206
+ }
207
+ /**
208
+ * Construct a unified `FieldAccessor<S, N>` proxy. Property access creates
209
+ * an `Expression` using the property name as the field path; callable
210
+ * form accepts a dot-path string validated against `N` at compile time.
211
+ *
212
+ * The proxy target is a function so the resulting object is both callable
213
+ * and indexable. Symbol-keyed accesses (e.g. `Symbol.toPrimitive`) return
214
+ * `undefined` to keep accidental coercion behaviour unsurprising —
215
+ * matching the previous `FieldProxy` / `FilterProxy` semantics.
216
+ */
217
+ function createFieldAccessor() {
218
+ const stageInstance = buildStageEmitters();
219
+ const callable = ((path) => buildExpression(path));
220
+ return new Proxy(callable, { get(target, prop, receiver) {
221
+ if (typeof prop === "symbol") return Reflect.get(target, prop, receiver);
222
+ if (prop === "stage") return stageInstance;
223
+ if (prop === "rawPath") return (path) => buildExpression(path);
224
+ return buildExpression(prop);
225
+ } });
226
+ }
227
+ //#endregion
228
+ export { expr as n, resolveUpdaterResult as r, createFieldAccessor as t };
229
+
230
+ //# sourceMappingURL=field-accessor-D9Oy4PUD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-accessor-D9Oy4PUD.mjs","names":[],"sources":["../src/update-ops.ts","../src/field-accessor.ts"],"sourcesContent":["import type {\n MongoUpdatePipelineStage,\n MongoUpdateSpec,\n} from '@prisma-next/mongo-query-ast/execution';\nimport type { MongoValue } from '@prisma-next/mongo-value';\n\n/**\n * Per-field update operations produced by `Expression`'s update methods\n * (`set`, `inc`, `push`, …). A write terminal folds an array of these into a\n * `MongoUpdateSpec` record (`{ $set: { … }, $inc: { … }, … }`) before\n * constructing the underlying `UpdateManyCommand` / `UpdateOneCommand` AST node.\n *\n * One `TypedUpdateOp` value corresponds to one Mongo update operator applied\n * to one field path. The `op` string is the wire-level operator name (`$set`,\n * `$inc`, …); the `path` is the dot-path to the field (or its top-level name).\n */\nexport type TypedUpdateOp =\n | { readonly op: '$set'; readonly path: string; readonly value: MongoValue }\n | { readonly op: '$unset'; readonly path: string }\n | { readonly op: '$rename'; readonly path: string; readonly newName: string }\n | { readonly op: '$inc'; readonly path: string; readonly amount: number }\n | { readonly op: '$mul'; readonly path: string; readonly factor: number }\n | { readonly op: '$min'; readonly path: string; readonly value: MongoValue }\n | { readonly op: '$max'; readonly path: string; readonly value: MongoValue }\n | { readonly op: '$push'; readonly path: string; readonly value: MongoValue }\n | { readonly op: '$addToSet'; readonly path: string; readonly value: MongoValue }\n | { readonly op: '$pop'; readonly path: string; readonly direction: 1 | -1 }\n | { readonly op: '$pull'; readonly path: string; readonly value: MongoValue }\n | { readonly op: '$pullAll'; readonly path: string; readonly values: ReadonlyArray<MongoValue> }\n | { readonly op: '$currentDate'; readonly path: string }\n | { readonly op: '$setOnInsert'; readonly path: string; readonly value: MongoValue };\n\nexport const setOp = (path: string, value: MongoValue): TypedUpdateOp => ({\n op: '$set',\n path,\n value,\n});\nexport const unsetOp = (path: string): TypedUpdateOp => ({ op: '$unset', path });\nexport const renameOp = (path: string, newName: string): TypedUpdateOp => ({\n op: '$rename',\n path,\n newName,\n});\nexport const incOp = (path: string, amount: number): TypedUpdateOp => ({\n op: '$inc',\n path,\n amount,\n});\nexport const mulOp = (path: string, factor: number): TypedUpdateOp => ({\n op: '$mul',\n path,\n factor,\n});\nexport const minOp = (path: string, value: MongoValue): TypedUpdateOp => ({\n op: '$min',\n path,\n value,\n});\nexport const maxOp = (path: string, value: MongoValue): TypedUpdateOp => ({\n op: '$max',\n path,\n value,\n});\nexport const pushOp = (path: string, value: MongoValue): TypedUpdateOp => ({\n op: '$push',\n path,\n value,\n});\nexport const addToSetOp = (path: string, value: MongoValue): TypedUpdateOp => ({\n op: '$addToSet',\n path,\n value,\n});\nexport const popOp = (path: string, direction: 1 | -1): TypedUpdateOp => ({\n op: '$pop',\n path,\n direction,\n});\nexport const pullOp = (path: string, value: MongoValue): TypedUpdateOp => ({\n op: '$pull',\n path,\n value,\n});\nexport const pullAllOp = (path: string, values: ReadonlyArray<MongoValue>): TypedUpdateOp => ({\n op: '$pullAll',\n path,\n values,\n});\nexport const currentDateOp = (path: string): TypedUpdateOp => ({ op: '$currentDate', path });\nexport const setOnInsertOp = (path: string, value: MongoValue): TypedUpdateOp => ({\n op: '$setOnInsert',\n path,\n value,\n});\n\n/**\n * Per-operator bucket: `{ '<fieldPath>': <operatorValue> }`. Every value is\n * already a `MongoValue` (operators store numbers/strings/booleans/arrays\n * directly), so no blind casts are needed at assignment sites.\n */\ntype UpdateOpBucket = Record<string, MongoValue>;\n\n/**\n * The full nested shape that `foldUpdateOps` accumulates before returning a\n * `MongoUpdateSpec`: `operator → fieldPath → value`. Each inner bucket is\n * itself a `MongoDocument`-compatible record, which is why the outer map is\n * structurally a `MongoUpdateSpec` (`Record<string, MongoValue>` where every\n * value is a document).\n */\ntype UpdateOpBuckets = Record<string, UpdateOpBucket>;\n\n/**\n * Fold an array of `TypedUpdateOp` into the non-pipeline variant of\n * `MongoUpdateSpec` (`{ $set: { … }, $inc: { … }, … }`).\n *\n * Throws if the same operator targets the same path twice — a clear authoring\n * error that Mongo would otherwise silently coalesce.\n */\nexport function foldUpdateOps(ops: ReadonlyArray<TypedUpdateOp>): MongoUpdateSpec {\n const buckets: UpdateOpBuckets = {};\n const seen = new Set<string>();\n\n const ensure = (key: string): UpdateOpBucket => {\n let bucket = buckets[key];\n if (!bucket) {\n bucket = {};\n buckets[key] = bucket;\n }\n return bucket;\n };\n\n const claim = (op: string, path: string): void => {\n const k = `${op}::${path}`;\n if (seen.has(k)) {\n throw new Error(\n `Update spec collision: ${op} on '${path}' was specified more than once. Combine the operations into a single call site.`,\n );\n }\n seen.add(k);\n };\n\n for (const entry of ops) {\n claim(entry.op, entry.path);\n switch (entry.op) {\n case '$set':\n case '$min':\n case '$max':\n case '$push':\n case '$addToSet':\n case '$pull':\n case '$setOnInsert':\n ensure(entry.op)[entry.path] = entry.value;\n break;\n case '$unset':\n ensure('$unset')[entry.path] = '';\n break;\n case '$rename':\n ensure('$rename')[entry.path] = entry.newName;\n break;\n case '$inc':\n ensure('$inc')[entry.path] = entry.amount;\n break;\n case '$mul':\n ensure('$mul')[entry.path] = entry.factor;\n break;\n case '$pop':\n ensure('$pop')[entry.path] = entry.direction;\n break;\n case '$pullAll':\n ensure('$pullAll')[entry.path] = entry.values;\n break;\n case '$currentDate':\n ensure('$currentDate')[entry.path] = true;\n break;\n }\n }\n\n return buckets;\n}\n\nexport type UpdaterItem = TypedUpdateOp | MongoUpdatePipelineStage;\n\n/**\n * The return type for updater callbacks. Typed as a union of homogeneous\n * arrays so mixed-shape updaters (operator + pipeline stage in the same\n * array) are a compile error. The runtime guard in `resolveUpdaterResult`\n * remains as defence-in-depth.\n */\nexport type UpdaterResult = ReadonlyArray<TypedUpdateOp> | ReadonlyArray<MongoUpdatePipelineStage>;\n\n/**\n * Classify an array of updater items and produce a `MongoUpdateSpec`.\n *\n * - All `TypedUpdateOp` → fold via `foldUpdateOps` (classic `{ $set, $inc, … }`)\n * - All `MongoUpdatePipelineStage` → return as-is (pipeline-style update)\n * - Mixed → throw (also a type error at the call site via the union shape)\n */\nexport function resolveUpdaterResult(items: ReadonlyArray<UpdaterItem>): MongoUpdateSpec {\n if (items.length === 0) {\n throw new Error(\n 'Updater returned no operations. Return at least one update from the callback (e.g. `[f.amount.set(0)]`).',\n );\n }\n\n const isOp = (item: UpdaterItem): item is TypedUpdateOp =>\n 'op' in item && typeof (item as TypedUpdateOp).op === 'string';\n\n const first = items[0];\n if (first === undefined) {\n throw new Error('Unreachable: items.length > 0 but first is undefined');\n }\n const firstIsOp = isOp(first);\n\n for (let i = 1; i < items.length; i++) {\n const item = items[i];\n if (item === undefined) continue;\n if (isOp(item) !== firstIsOp) {\n throw new Error(\n 'Cannot mix TypedUpdateOp values and pipeline stages in a single updater. ' +\n 'Use either `[f.amount.set(0)]` (operator form) or `[f.stage.set({...})]` (pipeline form), not both.',\n );\n }\n }\n\n if (firstIsOp) {\n return foldUpdateOps(items as ReadonlyArray<TypedUpdateOp>);\n }\n return items as ReadonlyArray<MongoUpdatePipelineStage>;\n}\n","import type {\n MongoAggExpr,\n MongoFilterExpr,\n MongoUpdatePipelineStage,\n} from '@prisma-next/mongo-query-ast/execution';\nimport {\n MongoAddFieldsStage,\n MongoAggFieldRef,\n MongoExistsExpr,\n MongoExprFilter,\n MongoFieldFilter,\n MongoProjectStage,\n MongoReplaceRootStage,\n} from '@prisma-next/mongo-query-ast/execution';\nimport type { MongoValue } from '@prisma-next/mongo-value';\nimport type { NestedDocShape, ObjectField, ResolvePath, ValidPaths } from './resolve-path';\nimport type { DocField, DocShape, TypedAggExpr } from './types';\nimport type { TypedUpdateOp } from './update-ops';\nimport {\n addToSetOp,\n currentDateOp,\n incOp,\n maxOp,\n minOp,\n mulOp,\n popOp,\n pullAllOp,\n pullOp,\n pushOp,\n renameOp,\n setOnInsertOp,\n setOp,\n unsetOp,\n} from './update-ops';\n\n/**\n * Operator surface for leaf (scalar) paths — today's full set: filter,\n * update, and aggregation operators. Returned by `Expression<F>` for any\n * `F extends DocField` that is not an `ObjectField<…>` sub-tree.\n *\n * Operator surfaces are intentionally not trait-gated by codec in this\n * revision — tracked on Linear as TML-2259 (scope extended to cover the\n * query-builder's `Expression<F>`). Calling, e.g. `.inc(1)` on a\n * string-typed expression compiles; the runtime relies on Mongo to\n * surface the error. Trait-gating can be tightened in a follow-up\n * without changing the accessor's public shape.\n */\nexport interface LeafExpression<F extends DocField> extends TypedAggExpr<F> {\n readonly _path: string;\n\n // Filter operators\n eq(value: MongoValue): MongoFilterExpr;\n ne(value: MongoValue): MongoFilterExpr;\n gt(value: MongoValue): MongoFilterExpr;\n gte(value: MongoValue): MongoFilterExpr;\n lt(value: MongoValue): MongoFilterExpr;\n lte(value: MongoValue): MongoFilterExpr;\n in(values: ReadonlyArray<MongoValue>): MongoFilterExpr;\n nin(values: ReadonlyArray<MongoValue>): MongoFilterExpr;\n exists(flag?: boolean): MongoFilterExpr;\n\n /**\n * `$type` filter: `{ field: { $type: bsonType } }`. Rides\n * `MongoFieldFilter`'s generic `op` string — no dedicated AST node. The\n * BSON type is expressed as Mongo's alias string (e.g. `'string'`) or\n * numeric type code; an array selects any of several types.\n */\n type(bsonType: MongoValue): MongoFilterExpr;\n\n // Update operators ($set family)\n set(value: MongoValue): TypedUpdateOp;\n unset(): TypedUpdateOp;\n rename(newName: string): TypedUpdateOp;\n\n // Numeric update operators\n inc(amount: number): TypedUpdateOp;\n mul(factor: number): TypedUpdateOp;\n min(value: MongoValue): TypedUpdateOp;\n max(value: MongoValue): TypedUpdateOp;\n\n // Array update operators\n push(value: MongoValue): TypedUpdateOp;\n addToSet(value: MongoValue): TypedUpdateOp;\n pop(direction?: 1 | -1): TypedUpdateOp;\n pull(value: MongoValue): TypedUpdateOp;\n pullAll(values: ReadonlyArray<MongoValue>): TypedUpdateOp;\n\n // Date / upsert helpers\n currentDate(): TypedUpdateOp;\n setOnInsert(value: MongoValue): TypedUpdateOp;\n}\n\n/**\n * Operator surface for non-leaf (value-object) paths — `f('address')`\n * when `address` is a `ContractValueObject`. Intentionally minimal: the\n * whole-value ops that make sense on a structured sub-document\n * (`set`/`unset`/`exists`, null presence via `eq(null)`/`ne(null)`). Field-\n * level ops belong on the constituent leaves (`f('address.city')`).\n *\n * The aggregation `node` is still present (`TypedAggExpr<ObjectField<N>>`)\n * so the value object can be piped through `$addFields` /\n * `$replaceRoot` / etc. as-is.\n */\nexport interface ObjectExpression<N extends NestedDocShape> extends TypedAggExpr<ObjectField<N>> {\n readonly _path: string;\n\n exists(flag?: boolean): MongoFilterExpr;\n eq(value: null): MongoFilterExpr;\n ne(value: null): MongoFilterExpr;\n\n set(value: MongoValue): TypedUpdateOp;\n unset(): TypedUpdateOp;\n}\n\n/**\n * The unified field accessor expression returned by `FieldAccessor` (per\n * [ADR 180](../../../../docs/architecture%20docs/adrs/ADR%20180%20-%20Dot-path%20field%20accessor.md)).\n *\n * Resolves to `ObjectExpression<Sub>` when `F` is an `ObjectField<Sub>`\n * (non-leaf path), otherwise to `LeafExpression<F>` (the full operator\n * surface). The conditional is driven off the `fields` marker that\n * `ObjectField` adds to `DocField`, so existing code that uses plain\n * `DocField` shapes continues to resolve to `LeafExpression`.\n */\nexport type Expression<F extends DocField> =\n F extends ObjectField<infer N> ? ObjectExpression<N> : LeafExpression<F>;\n\n/**\n * Emitters for MongoDB update-pipeline stages (`$addFields`/`$set`,\n * `$project`/`$unset`, `$replaceRoot`/`$replaceWith`). These return\n * `MongoUpdatePipelineStage` nodes and let an updater callback express\n * the pipeline-form update as an alternative to the typed-operator form.\n *\n * The two forms are mutually exclusive per updater call: `resolveUpdaterResult`\n * rejects arrays that mix `TypedUpdateOp` and `MongoUpdatePipelineStage`\n * entries with a clear error — an updater callback must return either all\n * typed ops or all pipeline stages. Pick the form that matches the update\n * you want and commit to it for that call site.\n *\n * Accessible via `f.stage` on the `FieldAccessor`.\n */\nexport interface StageEmitters {\n set(fields: Record<string, MongoAggExpr>): MongoUpdatePipelineStage;\n unset(...paths: ReadonlyArray<string>): MongoUpdatePipelineStage;\n replaceRoot(newRoot: MongoAggExpr): MongoUpdatePipelineStage;\n replaceWith(newRoot: MongoAggExpr): MongoUpdatePipelineStage;\n}\n\nfunction buildStageEmitters(): StageEmitters {\n return {\n set: (fields) => new MongoAddFieldsStage(fields),\n unset: (...paths) => {\n const spec: Record<string, 0> = {};\n for (const p of paths) {\n spec[p] = 0;\n }\n return new MongoProjectStage(spec);\n },\n replaceRoot: (newRoot) => new MongoReplaceRootStage(newRoot),\n replaceWith: (newRoot) => new MongoReplaceRootStage(newRoot),\n };\n}\n\n/**\n * The unified `FieldAccessor` per ADR 180.\n *\n * - Property access (`f.status`) returns an `Expression<F>` whose codec\n * comes from the current pipeline shape `S`.\n * - Callable form (`f('address.city')`) returns an `Expression<ResolvePath<N, P>>`\n * where `N` is the nested shape carrying value-object sub-shapes.\n * Paths that don't exist in `N` are rejected with a compile-time error\n * (via `P extends ValidPaths<N>`). Non-leaf paths like `f('address')`\n * resolve to an `ObjectExpression` whose reduced surface covers the\n * whole-value operations (`set`, `unset`, `exists`, `eq(null)`,\n * `ne(null)`).\n * - `f.rawPath('path')` is a deliberate escape hatch that skips path\n * validation and returns a `LeafExpression<F>` for the given string.\n * Intended for migration authoring where the target field is not yet\n * part of the typed contract (e.g. a backfill writing a newly-added\n * column before the contract hash rolls forward). The method name is\n * deliberately `rawPath` rather than `raw` so it does not shadow a\n * legitimate top-level `raw` field on a user model.\n * - `f.stage` exposes pipeline-style update emitters (`$set`, `$unset`,\n * `$replaceRoot`, `$replaceWith`).\n *\n * When `N` is `Record<string, never>` (the default — e.g. after a\n * replacement stage like `$group` / `$project` / `$replaceRoot`),\n * `ValidPaths<N>` is `never` and the callable form is effectively\n * disabled at the type level. This keeps the builder sound downstream of\n * stages that invalidate the original document's nested-path tree.\n * `f.rawPath(...)` remains available in that state for callers that need\n * an explicit unvalidated path.\n */\nexport type FieldAccessor<S extends DocShape, N extends NestedDocShape = Record<string, never>> = {\n readonly [K in keyof S & string]: Expression<S[K]>;\n} & (<P extends ValidPaths<N>>(path: P) => Expression<ResolvePath<N, P>>) & {\n readonly stage: StageEmitters;\n /**\n * Escape hatch: build a `LeafExpression<F>` for an unvalidated string\n * path. Use only when the path is intentionally outside the typed\n * model surface — data-migration authoring is the canonical case\n * (e.g. backfilling a field that is not yet in the contract). Default\n * `F` is the opaque `DocField`; callers can narrow via the explicit\n * generic: `f.rawPath<StringField>(\"status\").set(\"active\")`.\n *\n * The method is named `rawPath` (not `raw`) so a user model with a\n * top-level `raw` field still resolves `f.raw` to the field-expression\n * property, not to this escape hatch. Does not participate in\n * `ValidPaths<N>` / `ResolvePath<N, P>` — the path is passed through\n * verbatim and no IDE autocomplete is offered.\n */\n rawPath<F extends DocField = DocField>(path: string): LeafExpression<F>;\n };\n\n/**\n * Wrap a boolean aggregation expression as an `$expr` filter\n * (`MongoExprFilter`). Lets a `$match` express an aggregation-expression\n * predicate — e.g. comparing two field references via `fn.eq(a, b)` — in\n * the typed AST rather than via a raw escape hatch. Pairs with `.type()`\n * to express filters like `{ _id: { $type: 'string' }, $expr: { $eq: ['$_id', '$space'] } }`.\n */\nexport function expr(predicate: TypedAggExpr<DocField>): MongoFilterExpr {\n return MongoExprFilter.of(predicate.node);\n}\n\nfunction buildExpression<F extends DocField>(path: string): Expression<F> {\n // The runtime object carries the full operator surface unconditionally;\n // `ObjectExpression` is a strict subset of `LeafExpression`, so a single\n // implementation satisfies both type-level shapes. Compile-time gating\n // prevents misuse of leaf-only operators on object paths.\n return {\n _field: undefined as never,\n _path: path,\n node: MongoAggFieldRef.of(path),\n\n eq: (value: MongoValue) => MongoFieldFilter.eq(path, value),\n ne: (value: MongoValue) => MongoFieldFilter.neq(path, value),\n gt: (value: MongoValue) => MongoFieldFilter.gt(path, value),\n gte: (value: MongoValue) => MongoFieldFilter.gte(path, value),\n lt: (value: MongoValue) => MongoFieldFilter.lt(path, value),\n lte: (value: MongoValue) => MongoFieldFilter.lte(path, value),\n in: (values: ReadonlyArray<MongoValue>) => MongoFieldFilter.in(path, values),\n nin: (values: ReadonlyArray<MongoValue>) => MongoFieldFilter.nin(path, values),\n exists: (flag?: boolean) =>\n flag === false ? MongoExistsExpr.notExists(path) : MongoExistsExpr.exists(path),\n type: (bsonType: MongoValue) => MongoFieldFilter.of(path, '$type', bsonType),\n\n set: (value: MongoValue) => setOp(path, value),\n unset: () => unsetOp(path),\n rename: (newName: string) => renameOp(path, newName),\n\n inc: (amount: number) => incOp(path, amount),\n mul: (factor: number) => mulOp(path, factor),\n min: (value: MongoValue) => minOp(path, value),\n max: (value: MongoValue) => maxOp(path, value),\n\n push: (value: MongoValue) => pushOp(path, value),\n addToSet: (value: MongoValue) => addToSetOp(path, value),\n pop: (direction: 1 | -1 = 1) => popOp(path, direction),\n pull: (value: MongoValue) => pullOp(path, value),\n pullAll: (values: ReadonlyArray<MongoValue>) => pullAllOp(path, values),\n\n currentDate: () => currentDateOp(path),\n setOnInsert: (value: MongoValue) => setOnInsertOp(path, value),\n } as unknown as Expression<F>;\n}\n\n/**\n * Construct a unified `FieldAccessor<S, N>` proxy. Property access creates\n * an `Expression` using the property name as the field path; callable\n * form accepts a dot-path string validated against `N` at compile time.\n *\n * The proxy target is a function so the resulting object is both callable\n * and indexable. Symbol-keyed accesses (e.g. `Symbol.toPrimitive`) return\n * `undefined` to keep accidental coercion behaviour unsurprising —\n * matching the previous `FieldProxy` / `FilterProxy` semantics.\n */\nexport function createFieldAccessor<\n S extends DocShape,\n N extends NestedDocShape = Record<string, never>,\n>(): FieldAccessor<S, N> {\n const stageInstance = buildStageEmitters();\n const callable = ((path: string) => buildExpression<DocField>(path)) as unknown as FieldAccessor<\n S,\n N\n >;\n return new Proxy(callable, {\n get(target, prop, receiver) {\n if (typeof prop === 'symbol') {\n return Reflect.get(target, prop, receiver);\n }\n if (prop === 'stage') {\n return stageInstance;\n }\n if (prop === 'rawPath') {\n return (path: string) => buildExpression<DocField>(path);\n }\n return buildExpression(prop);\n },\n });\n}\n"],"mappings":"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"}