@prisma-next/target-mongo 0.4.1 → 0.4.3

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.
Files changed (39) hide show
  1. package/README.md +4 -1
  2. package/dist/control.d.mts +43 -22
  3. package/dist/control.d.mts.map +1 -1
  4. package/dist/control.mjs +126 -110
  5. package/dist/control.mjs.map +1 -1
  6. package/dist/descriptor-meta-D9_5quQi.mjs +14 -0
  7. package/dist/descriptor-meta-D9_5quQi.mjs.map +1 -0
  8. package/dist/{migration-factories-gwi81C8u.mjs → migration-factories-CoNYWrd1.mjs} +3 -1
  9. package/dist/migration-factories-CoNYWrd1.mjs.map +1 -0
  10. package/dist/migration.d.mts +7 -1
  11. package/dist/migration.d.mts.map +1 -1
  12. package/dist/migration.mjs +1 -1
  13. package/dist/{op-factory-call-BjNAcPSF.d.mts → op-factory-call--nK5dk8n.d.mts} +1 -1
  14. package/dist/{op-factory-call-BjNAcPSF.d.mts.map → op-factory-call--nK5dk8n.d.mts.map} +1 -1
  15. package/dist/pack.mjs +1 -11
  16. package/dist/pack.mjs.map +1 -1
  17. package/dist/runtime.d.mts +20 -0
  18. package/dist/runtime.d.mts.map +1 -0
  19. package/dist/runtime.mjs +28 -0
  20. package/dist/runtime.mjs.map +1 -0
  21. package/dist/schema-verify.d.mts +22 -0
  22. package/dist/schema-verify.d.mts.map +1 -0
  23. package/dist/schema-verify.mjs +3 -0
  24. package/dist/verify-mongo-schema-Daa7BMJY.mjs +582 -0
  25. package/dist/verify-mongo-schema-Daa7BMJY.mjs.map +1 -0
  26. package/package.json +19 -13
  27. package/src/core/marker-ledger.ts +90 -20
  28. package/src/core/migration-factories.ts +8 -0
  29. package/src/core/mongo-ops-serializer.ts +0 -8
  30. package/src/core/mongo-planner.ts +8 -2
  31. package/src/core/mongo-runner.ts +105 -70
  32. package/src/core/planner-produced-migration.ts +0 -1
  33. package/src/core/render-typescript.ts +28 -16
  34. package/src/core/schema-diff.ts +402 -0
  35. package/src/core/schema-verify/canonicalize-introspection.ts +389 -0
  36. package/src/core/schema-verify/verify-mongo-schema.ts +60 -0
  37. package/src/exports/runtime.ts +38 -0
  38. package/src/exports/schema-verify.ts +2 -0
  39. package/dist/migration-factories-gwi81C8u.mjs.map +0 -1
package/README.md CHANGED
@@ -14,6 +14,8 @@ MongoDB target pack for Prisma Next.
14
14
  - `./pack`: pure target pack ref used by `@prisma-next/family-mongo` and `@prisma-next/mongo-contract-ts`
15
15
  - `./codec-types`: base Mongo codec type map
16
16
  - `./migration`: factory functions (the `Migration` base class is in `@prisma-next/family-mongo/migration`)
17
+ - `./control`: `MongoMigrationRunner` and `createMongoRunnerDeps` for runtime migration execution
18
+ - `./schema-verify`: pure `verifyMongoSchema(...)` (no DB I/O); composes `contractToMongoSchemaIR` and `diffMongoSchemas` so the runner's post-apply verify step and `MongoFamilyInstance.schemaVerify` agree on "matches the contract" by construction
17
19
 
18
20
  ## Usage
19
21
 
@@ -33,6 +35,7 @@ const contract = defineContract({
33
35
  ### Migration authoring
34
36
 
35
37
  ```typescript
38
+ import { MigrationCLI } from '@prisma-next/cli/migration-cli';
36
39
  import { Migration } from '@prisma-next/family-mongo/migration';
37
40
  import { createIndex, createCollection } from '@prisma-next/target-mongo/migration';
38
41
 
@@ -49,7 +52,7 @@ class UsersMigration extends Migration {
49
52
  }
50
53
 
51
54
  export default UsersMigration;
52
- Migration.run(import.meta.url, UsersMigration)
55
+ MigrationCLI.run(import.meta.url, UsersMigration);
53
56
  ```
54
57
 
55
58
  Run `tsx migration.ts` to produce `ops.json` and `migration.json` (when `describe()` is implemented). Use `--dry-run` to preview without writing.
@@ -1,11 +1,11 @@
1
- import { a as DropCollectionCall, c as schemaCollectionToCreateCollectionOptions, i as CreateIndexCall, l as schemaIndexToCreateIndexOptions, n as CollModMeta, o as DropIndexCall, r as CreateCollectionCall, s as OpFactoryCall, t as CollModCall } from "./op-factory-call-BjNAcPSF.mjs";
1
+ import { a as DropCollectionCall, c as schemaCollectionToCreateCollectionOptions, i as CreateIndexCall, l as schemaIndexToCreateIndexOptions, n as CollModMeta, o as DropIndexCall, r as CreateCollectionCall, s as OpFactoryCall, t as CollModCall } from "./op-factory-call--nK5dk8n.mjs";
2
2
  import { MongoSchemaIR } from "@prisma-next/mongo-schema-ir";
3
3
  import { MongoQueryPlan } from "@prisma-next/mongo-query-ast/execution";
4
4
  import { AnyMongoMigrationOperation, MongoAndExpr, MongoDdlCommandVisitor, MongoExistsExpr, MongoExprFilter, MongoFieldFilter, MongoFilterExpr, MongoFilterVisitor, MongoInspectionCommandVisitor, MongoMigrationPlanOperation, MongoNotExpr, MongoOrExpr } from "@prisma-next/mongo-query-ast/control";
5
5
  import { Migration, MigrationMeta } from "@prisma-next/migration-tools/migration";
6
+ import { MigrationOperationPolicy, MigrationPlan, MigrationPlanOperation, MigrationPlanWithAuthoringSurface, MigrationPlanner, MigrationPlannerConflict, MigrationPlannerResult, MigrationRunnerExecutionChecks, MigrationRunnerResult, MigrationScaffoldContext, OperationContext } from "@prisma-next/framework-components/control";
6
7
  import { MongoContract } from "@prisma-next/mongo-contract";
7
- import { MigrationOperationPolicy, MigrationPlan, MigrationPlanOperation, MigrationPlanWithAuthoringSurface, MigrationPlanner, MigrationPlannerConflict, MigrationPlannerResult, MigrationRunnerExecutionChecks, MigrationRunnerResult, MigrationScaffoldContext } from "@prisma-next/framework-components/control";
8
- import { ContractMarkerRecord } from "@prisma-next/contract/types";
8
+ import { Contract, ContractMarkerRecord } from "@prisma-next/contract/types";
9
9
  import { Db } from "mongodb";
10
10
  import { TargetBoundComponentDescriptor } from "@prisma-next/framework-components/components";
11
11
  import { MongoAdapter, MongoDriver } from "@prisma-next/mongo-lowering";
@@ -33,10 +33,21 @@ declare function readMarker(db: Db): Promise<ContractMarkerRecord | null>;
33
33
  declare function initMarker(db: Db, destination: {
34
34
  readonly storageHash: string;
35
35
  readonly profileHash: string;
36
+ readonly invariants?: readonly string[];
36
37
  }): Promise<void>;
38
+ /**
39
+ * Updates the marker doc atomically (CAS on `expectedFrom`).
40
+ *
41
+ * `destination.invariants`:
42
+ * - `undefined` → existing field left untouched.
43
+ * - explicit value → merged into the existing field server-side via an
44
+ * aggregation pipeline (`$setUnion + $sortArray`), atomic at the
45
+ * document level. `[]` is a no-op merge.
46
+ */
37
47
  declare function updateMarker(db: Db, expectedFrom: string, destination: {
38
48
  readonly storageHash: string;
39
49
  readonly profileHash: string;
50
+ readonly invariants?: readonly string[];
40
51
  }): Promise<boolean>;
41
52
  declare function writeLedgerEntry(db: Db, entry: {
42
53
  readonly edgeId: string;
@@ -68,7 +79,12 @@ declare class MongoMigrationPlanner implements MigrationPlanner<'mongo', 'mongo'
68
79
  readonly contract: unknown;
69
80
  readonly schema: unknown;
70
81
  readonly policy: MigrationOperationPolicy;
71
- readonly fromHash: string;
82
+ /**
83
+ * The "from" contract (state the planner assumes the database starts at),
84
+ * or `null` for reconciliation flows. Used to populate `describe().from`
85
+ * on the produced plan as `fromContract?.storage.storageHash ?? null`.
86
+ */
87
+ readonly fromContract: Contract | null;
72
88
  readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'mongo', 'mongo'>>;
73
89
  }): MigrationPlannerResult;
74
90
  /**
@@ -89,10 +105,12 @@ interface MarkerOperations {
89
105
  initMarker(destination: {
90
106
  readonly storageHash: string;
91
107
  readonly profileHash: string;
108
+ readonly invariants?: readonly string[];
92
109
  }): Promise<void>;
93
110
  updateMarker(expectedFrom: string, destination: {
94
111
  readonly storageHash: string;
95
112
  readonly profileHash: string;
113
+ readonly invariants?: readonly string[];
96
114
  }): Promise<boolean>;
97
115
  writeLedgerEntry(entry: {
98
116
  readonly edgeId: string;
@@ -106,21 +124,25 @@ interface MongoRunnerDependencies {
106
124
  readonly adapter: MongoAdapter;
107
125
  readonly driver: MongoDriver;
108
126
  readonly markerOps: MarkerOperations;
127
+ readonly introspectSchema: () => Promise<MongoSchemaIR>;
128
+ }
129
+ interface MongoMigrationRunnerExecuteOptions {
130
+ readonly plan: MigrationPlan;
131
+ readonly destinationContract: MongoContract;
132
+ readonly policy: MigrationOperationPolicy;
133
+ readonly callbacks?: {
134
+ onOperationStart?(op: MigrationPlanOperation): void;
135
+ onOperationComplete?(op: MigrationPlanOperation): void;
136
+ };
137
+ readonly executionChecks?: MigrationRunnerExecutionChecks;
138
+ readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'mongo', 'mongo'>>;
139
+ readonly strictVerification?: boolean;
140
+ readonly context?: OperationContext;
109
141
  }
110
142
  declare class MongoMigrationRunner {
111
143
  private readonly deps;
112
144
  constructor(deps: MongoRunnerDependencies);
113
- execute(options: {
114
- readonly plan: MigrationPlan;
115
- readonly destinationContract: unknown;
116
- readonly policy: MigrationOperationPolicy;
117
- readonly callbacks?: {
118
- onOperationStart?(op: MigrationPlanOperation): void;
119
- onOperationComplete?(op: MigrationPlanOperation): void;
120
- };
121
- readonly executionChecks?: MigrationRunnerExecutionChecks;
122
- readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'mongo', 'mongo'>>;
123
- }): Promise<MigrationRunnerResult>;
145
+ execute(options: MongoMigrationRunnerExecuteOptions): Promise<MigrationRunnerResult>;
124
146
  private executeDataTransform;
125
147
  private evaluateDataTransformChecks;
126
148
  private evaluateChecks;
@@ -161,9 +183,8 @@ declare function renderOps(calls: ReadonlyArray<OpFactoryCall>): MongoMigrationP
161
183
  //#endregion
162
184
  //#region src/core/render-typescript.d.ts
163
185
  interface RenderMigrationMeta {
164
- readonly from: string;
186
+ readonly from: string | null;
165
187
  readonly to: string;
166
- readonly kind?: string;
167
188
  readonly labels?: readonly string[];
168
189
  }
169
190
  /**
@@ -172,16 +193,16 @@ interface RenderMigrationMeta {
172
193
  * `Migration` (i.e. `MongoMigration`) from `@prisma-next/family-mongo`, and
173
194
  * implements the abstract `operations` and `describe` members. `meta` is
174
195
  * always rendered — `describe()` is part of the `Migration` contract, so
175
- * even an empty stub must satisfy it; callers pass empty strings for a
176
- * migration-new scaffold.
196
+ * even an empty stub must satisfy it; callers pass `from: null` for a
197
+ * baseline `migration-new` scaffold (and a real `to` hash either way).
177
198
  *
178
199
  * The walk is polymorphic: each call node contributes its own
179
200
  * `renderTypeScript()` expression and declares its own
180
201
  * `importRequirements()`. The top-level renderer aggregates imports
181
202
  * across all nodes and emits one `import { … } from "…"` line per module.
182
- * The `Migration` import from `@prisma-next/family-mongo/migration` is
183
- * always emitted it's driven by `meta` (the rendered scaffold always
184
- * extends `Migration`), not by any node.
203
+ * The `Migration` and `MigrationCLI` imports are always emitted — they're
204
+ * structural to the rendered scaffold (extends `Migration`, calls
205
+ * `MigrationCLI.run`), not driven by any node.
185
206
  */
186
207
  declare function renderCallsToTypeScript(calls: ReadonlyArray<OpFactoryCall>, meta: RenderMigrationMeta): string;
187
208
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/contract-to-schema.ts","../src/core/ddl-formatter.ts","../src/core/filter-evaluator.ts","../src/core/marker-ledger.ts","../src/core/mongo-ops-serializer.ts","../src/core/mongo-planner.ts","../src/core/mongo-runner.ts","../src/core/planner-produced-migration.ts","../src/core/render-ops.ts","../src/core/render-typescript.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;iBAoDgB,uBAAA,WAAkC,uBAAuB;;;iBC6CzD,qBAAA,sBAA2C;;;cC/C9C,eAAA,YAA2B;;mBAGrB,sBAAsB;cAK3B;YAKF;WAID;YAIC;eAIG;cAKD;;;;iBCjDQ,UAAA,KAAe,KAAK,QAAQ;iBAgB5B,UAAA,KAChB;;;IAEH;iBAcmB,YAAA,KAChB;;;IAGH;iBAiBmB,gBAAA,KAChB;;EHlCU,SAAA,IAAA,EAAA,MAAA;;IGoCb;;;iBC8ea,kBAAA,iBAAmC;iBAOnC,mBAAA,4BAA+C;iBAI/C,iBAAA,eAAgC;;;KC7epC,eAAA;;kBACoC;;;sBACI;;cAEvC,qBAAA,YAAiC;;;ILpD9B,SAAA,MAAA,EAAA,OAAuB;qBKwDlB;kCACa,cAAc;MAC1C;EJbU,IAAA,CAAA,OAAA,EAAA;;;qBIiIK;IHhLR,SAAA,QAAgB,EAAA,MAAA;IAGV,SAAA,mBAAA,EG+Ke,aH/Kf,CG+K6B,8BH/K7B,CAAA,OAAA,EAAA,OAAA,CAAA,CAAA;EAAsB,CAAA,CAAA,EGgLnC,sBHhLmC;EAK3B;;;;;;;;;0BGiMY,2BAA2B;;;;UCtNpC,gBAAA;gBACD,QAAQ;;;;MAIlB;;;INUU,SAAA,WAAA,EAAuB,MAAA;MMNlC;;;ILmDW,SAAA,IAAA,EAAA,MAAqB;;MK9C/B;;AJDO,UIII,uBAAA,CJJY;EAGV,SAAA,eAAA,EIES,sBJFT,CIEgC,OJFhC,CAAA,IAAA,CAAA,CAAA;EAAsB,SAAA,kBAAA,EIGV,6BJHU,CIGoB,OJHpB,CIG4B,MJH5B,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,CAAA,CAAA;EAK3B,SAAA,OAAA,EIDM,YJCN;EAKF,SAAA,MAAA,EILO,WJKP;EAID,SAAA,SAAA,EIRW,gBJQX;;AAQI,cIDF,oBAAA,CJCE;EAKD,iBAAA,IAAA;EA9B0B,WAAA,CAAA,IAAA,EIyBH,uBJzBG;EAAkB,OAAA,CAAA,OAAA,EAAA;mBI4BvC;;qBAEE;IHjDC,SAAU,SAAA,CAAA,EAAA;MAAK,gBAAA,EAAA,EAAA,EGmDT,sBHnDS,CAAA,EAAA,IAAA;MAAa,mBAAA,EAAA,EAAA,EGoDnB,sBHpDmB,CAAA,EAAA,IAAA;IAAR,CAAA;IAAO,SAAA,eAAA,CAAA,EGsDlB,8BHtDkB;IAgB3B,SAAU,mBAG7B,EGoC+B,aHpCxB,CGoCsC,8BHpCtC,CAAA,OAAA,EAAA,OAAA,CAAA,CAAA;EAcY,CAAA,CAAA,EGuBhB,OHvBgB,CGuBR,qBHnBX,CAAA;EAiBmB,QAAA,oBAAgB;;;;ECiftB,QAAA,0BAAmC;EAOnC,QAAA,yBAAmB;AAInC;;;;;;;;;;;;AJ7hBA;;;;AC6CA;;;cMzEa,6BAAA,SACH,UAAU,uCACP;ELwBA,iBAAA,KAAgB;EAGV,iBAAA,IAAA;EAAsB,SAAA,QAAA,EAAA,OAAA;EAK3B,WAAA,CAAA,KAAA,EAAA,SK3BuB,aL2BvB,EAAA,EAAA,IAAA,EK1Ba,aL0Bb;EAKF,IAAA,UAAA,CAAA,CAAA,EAAA,SK1B0B,0BL0B1B,EAAA;EAID,QAAA,CAAA,CAAA,EK1BY,aL0BZ;EAIC,gBAAA,CAAA,CAAA,EAAA,MAAA;;;;iBMpEI,SAAA,QAAiB,cAAc,iBAAiB;;;UCC/C,mBAAA;;;;;;;;;;;;ATgDjB;;;;AC6CA;;;;AC/CA;;;AAQc,iBOzBE,uBAAA,CPyBF,KAAA,EOxBL,aPwBK,COxBS,aPwBT,CAAA,EAAA,IAAA,EOvBN,mBPuBM,CAAA,EAAA,MAAA"}
1
+ {"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/contract-to-schema.ts","../src/core/ddl-formatter.ts","../src/core/filter-evaluator.ts","../src/core/marker-ledger.ts","../src/core/mongo-ops-serializer.ts","../src/core/mongo-planner.ts","../src/core/mongo-runner.ts","../src/core/planner-produced-migration.ts","../src/core/render-ops.ts","../src/core/render-typescript.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/control.mjs CHANGED
@@ -1,54 +1,16 @@
1
- import { a as dropCollection, n as createCollection, o as dropIndex, r as createIndex, t as collMod } from "./migration-factories-gwi81C8u.mjs";
2
- import { MongoSchemaCollection, MongoSchemaCollectionOptions, MongoSchemaIR, MongoSchemaIndex, MongoSchemaValidator, canonicalize, deepEqual } from "@prisma-next/mongo-schema-ir";
1
+ import { n as contractToMongoSchemaIR, t as verifyMongoSchema } from "./verify-mongo-schema-Daa7BMJY.mjs";
2
+ import { a as dropCollection, n as createCollection, o as dropIndex, r as createIndex, t as collMod } from "./migration-factories-CoNYWrd1.mjs";
3
+ import { canonicalize, deepEqual } from "@prisma-next/mongo-schema-ir";
3
4
  import { AggregateCommand, MongoAddFieldsStage, MongoLimitStage, MongoLookupStage, MongoMatchStage, MongoMergeStage, MongoProjectStage, MongoSortStage, RawAggregateCommand, RawDeleteManyCommand, RawDeleteOneCommand, RawFindOneAndDeleteCommand, RawFindOneAndUpdateCommand, RawInsertManyCommand, RawInsertOneCommand, RawUpdateManyCommand, RawUpdateOneCommand } from "@prisma-next/mongo-query-ast/execution";
5
+ import { type } from "arktype";
4
6
  import { CollModCommand, CreateCollectionCommand, CreateIndexCommand, DropCollectionCommand, DropIndexCommand, ListCollectionsCommand, ListIndexesCommand, MongoAndExpr, MongoExistsExpr, MongoFieldFilter, MongoNotExpr, MongoOrExpr } from "@prisma-next/mongo-query-ast/control";
5
7
  import { ifDefined } from "@prisma-next/utils/defined";
6
- import { type } from "arktype";
7
8
  import { TsExpression, jsonToTsSource, renderImports } from "@prisma-next/ts-render";
8
9
  import { Migration } from "@prisma-next/migration-tools/migration";
9
10
  import { detectScaffoldRuntime, shebangLineFor } from "@prisma-next/migration-tools/migration-ts";
10
11
  import { errorRunnerFailed } from "@prisma-next/errors/execution";
11
12
  import { notOk, ok } from "@prisma-next/utils/result";
12
13
 
13
- //#region src/core/contract-to-schema.ts
14
- function convertIndex(index) {
15
- return new MongoSchemaIndex({
16
- keys: index.keys,
17
- unique: index.unique,
18
- sparse: index.sparse,
19
- expireAfterSeconds: index.expireAfterSeconds,
20
- partialFilterExpression: index.partialFilterExpression,
21
- wildcardProjection: index.wildcardProjection,
22
- collation: index.collation,
23
- weights: index.weights,
24
- default_language: index.default_language,
25
- language_override: index.language_override
26
- });
27
- }
28
- function convertValidator(v) {
29
- return new MongoSchemaValidator({
30
- jsonSchema: v.jsonSchema,
31
- validationLevel: v.validationLevel,
32
- validationAction: v.validationAction
33
- });
34
- }
35
- function convertOptions(o) {
36
- return new MongoSchemaCollectionOptions(o);
37
- }
38
- function convertCollection(name, def) {
39
- return new MongoSchemaCollection({
40
- name,
41
- indexes: (def.indexes ?? []).map(convertIndex),
42
- ...def.validator != null && { validator: convertValidator(def.validator) },
43
- ...def.options != null && { options: convertOptions(def.options) }
44
- });
45
- }
46
- function contractToMongoSchemaIR(contract) {
47
- if (!contract) return new MongoSchemaIR([]);
48
- return new MongoSchemaIR(Object.entries(contract.storage.collections).map(([name, def]) => convertCollection(name, def)));
49
- }
50
-
51
- //#endregion
52
14
  //#region src/core/ddl-formatter.ts
53
15
  function formatKeySpec(keys) {
54
16
  return `{ ${keys.map((k) => `${JSON.stringify(k.field)}: ${JSON.stringify(k.direction)}`).join(", ")} }`;
@@ -176,6 +138,31 @@ var FilterEvaluator = class {
176
138
  //#region src/core/marker-ledger.ts
177
139
  const COLLECTION = "_prisma_migrations";
178
140
  const MARKER_ID = "marker";
141
+ const MongoMarkerDocSchema = type({
142
+ storageHash: "string",
143
+ profileHash: "string",
144
+ "contractJson?": "unknown | null",
145
+ "canonicalVersion?": "number | null",
146
+ "updatedAt?": "Date",
147
+ "appTag?": "string | null",
148
+ "meta?": type({ "[string]": "unknown" }).or("null"),
149
+ "invariants?": type("string").array(),
150
+ "+": "delete"
151
+ });
152
+ function parseMongoMarkerDoc(doc) {
153
+ const result = MongoMarkerDocSchema(doc);
154
+ if (result instanceof type.errors) throw new Error(`Invalid marker doc on ${COLLECTION}: ${result.summary}`);
155
+ return {
156
+ storageHash: result.storageHash,
157
+ profileHash: result.profileHash,
158
+ contractJson: result.contractJson ?? null,
159
+ canonicalVersion: result.canonicalVersion ?? null,
160
+ updatedAt: result.updatedAt ?? /* @__PURE__ */ new Date(),
161
+ appTag: result.appTag ?? null,
162
+ meta: result.meta ?? {},
163
+ invariants: result.invariants ?? []
164
+ };
165
+ }
179
166
  async function executeAggregate(db, cmd) {
180
167
  return db.collection(cmd.collection).aggregate(cmd.pipeline).toArray();
181
168
  }
@@ -188,15 +175,7 @@ async function executeFindOneAndUpdate(db, cmd) {
188
175
  async function readMarker(db) {
189
176
  const doc = (await executeAggregate(db, new RawAggregateCommand(COLLECTION, [{ $match: { _id: MARKER_ID } }, { $limit: 1 }])))[0];
190
177
  if (!doc) return null;
191
- return {
192
- storageHash: doc["storageHash"],
193
- profileHash: doc["profileHash"],
194
- contractJson: doc["contractJson"] ?? null,
195
- canonicalVersion: doc["canonicalVersion"] ?? null,
196
- updatedAt: doc["updatedAt"],
197
- appTag: doc["appTag"] ?? null,
198
- meta: doc["meta"] ?? {}
199
- };
178
+ return parseMongoMarkerDoc(doc);
200
179
  }
201
180
  async function initMarker(db, destination) {
202
181
  await executeInsertOne(db, new RawInsertOneCommand(COLLECTION, {
@@ -207,18 +186,36 @@ async function initMarker(db, destination) {
207
186
  canonicalVersion: null,
208
187
  updatedAt: /* @__PURE__ */ new Date(),
209
188
  appTag: null,
210
- meta: {}
189
+ meta: {},
190
+ invariants: destination.invariants ?? []
211
191
  }));
212
192
  }
193
+ /**
194
+ * Updates the marker doc atomically (CAS on `expectedFrom`).
195
+ *
196
+ * `destination.invariants`:
197
+ * - `undefined` → existing field left untouched.
198
+ * - explicit value → merged into the existing field server-side via an
199
+ * aggregation pipeline (`$setUnion + $sortArray`), atomic at the
200
+ * document level. `[]` is a no-op merge.
201
+ */
213
202
  async function updateMarker(db, expectedFrom, destination) {
214
- return await executeFindOneAndUpdate(db, new RawFindOneAndUpdateCommand(COLLECTION, {
215
- _id: MARKER_ID,
216
- storageHash: expectedFrom
217
- }, { $set: {
203
+ const setBase = {
218
204
  storageHash: destination.storageHash,
219
205
  profileHash: destination.profileHash,
220
206
  updatedAt: /* @__PURE__ */ new Date()
221
- } }, false)) !== null;
207
+ };
208
+ const update = destination.invariants === void 0 ? { $set: setBase } : [{ $set: {
209
+ ...setBase,
210
+ invariants: { $sortArray: {
211
+ input: { $setUnion: [{ $ifNull: ["$invariants", []] }, destination.invariants] },
212
+ sortBy: 1
213
+ } }
214
+ } }];
215
+ return await executeFindOneAndUpdate(db, new RawFindOneAndUpdateCommand(COLLECTION, {
216
+ _id: MARKER_ID,
217
+ storageHash: expectedFrom
218
+ }, update, false)) !== null;
222
219
  }
223
220
  async function writeLedgerEntry(db, entry) {
224
221
  await executeInsertOne(db, new RawInsertOneCommand(COLLECTION, {
@@ -358,13 +355,9 @@ const QueryPlanJson = type({
358
355
  target: "string",
359
356
  storageHash: "string",
360
357
  lane: "string",
361
- paramDescriptors: "unknown[]",
362
358
  "targetFamily?": "string",
363
359
  "profileHash?": "string",
364
- "annotations?": "Record<string, unknown>",
365
- "refs?": "Record<string, unknown>",
366
- "projection?": "Record<string, string> | string[]",
367
- "projectionTypes?": "Record<string, string>"
360
+ "annotations?": "Record<string, unknown>"
368
361
  })
369
362
  });
370
363
  const CheckJson = type({
@@ -528,13 +521,9 @@ function deserializeMongoQueryPlan(json) {
528
521
  target: m.target,
529
522
  storageHash: m.storageHash,
530
523
  lane: m.lane,
531
- paramDescriptors: m.paramDescriptors,
532
524
  ...ifDefined("targetFamily", m.targetFamily),
533
525
  ...ifDefined("profileHash", m.profileHash),
534
- ...ifDefined("annotations", m.annotations),
535
- ...ifDefined("refs", m.refs),
536
- ...ifDefined("projection", m.projection),
537
- ...ifDefined("projectionTypes", m.projectionTypes)
526
+ ...ifDefined("annotations", m.annotations)
538
527
  };
539
528
  return {
540
529
  collection: data.collection,
@@ -827,26 +816,45 @@ function renderOps(calls) {
827
816
 
828
817
  //#endregion
829
818
  //#region src/core/render-typescript.ts
830
- const BASE_IMPORT = {
819
+ /**
820
+ * Always-present base imports for the rendered scaffold:
821
+ *
822
+ * - `Migration` from `@prisma-next/family-mongo/migration` — the
823
+ * user-facing Mongo `Migration` base; subclasses don't need to
824
+ * redeclare `targetId` or thread family/target generics.
825
+ * - `MigrationCLI` from `@prisma-next/cli/migration-cli` — the
826
+ * migration-file CLI entrypoint that loads `prisma-next.config.ts`,
827
+ * assembles a `ControlStack`, and instantiates the migration class.
828
+ * The migration file owns this dependency directly: pulling CLI
829
+ * machinery in at script run time is acceptable because the script's
830
+ * whole purpose is to be invoked from the project that owns the
831
+ * config. (Mirrors the postgres facade pattern; pulling `MigrationCLI`
832
+ * into `@prisma-next/family-mongo/migration` so a Mongo migration only
833
+ * needs one import is tracked separately as a follow-up.)
834
+ */
835
+ const BASE_IMPORTS = [{
831
836
  moduleSpecifier: "@prisma-next/family-mongo/migration",
832
837
  symbol: "Migration"
833
- };
838
+ }, {
839
+ moduleSpecifier: "@prisma-next/cli/migration-cli",
840
+ symbol: "MigrationCLI"
841
+ }];
834
842
  /**
835
843
  * Render a list of Mongo `OpFactoryCall`s as a `migration.ts`
836
844
  * source string. The result is shebanged, extends the user-facing
837
845
  * `Migration` (i.e. `MongoMigration`) from `@prisma-next/family-mongo`, and
838
846
  * implements the abstract `operations` and `describe` members. `meta` is
839
847
  * always rendered — `describe()` is part of the `Migration` contract, so
840
- * even an empty stub must satisfy it; callers pass empty strings for a
841
- * migration-new scaffold.
848
+ * even an empty stub must satisfy it; callers pass `from: null` for a
849
+ * baseline `migration-new` scaffold (and a real `to` hash either way).
842
850
  *
843
851
  * The walk is polymorphic: each call node contributes its own
844
852
  * `renderTypeScript()` expression and declares its own
845
853
  * `importRequirements()`. The top-level renderer aggregates imports
846
854
  * across all nodes and emits one `import { … } from "…"` line per module.
847
- * The `Migration` import from `@prisma-next/family-mongo/migration` is
848
- * always emitted it's driven by `meta` (the rendered scaffold always
849
- * extends `Migration`), not by any node.
855
+ * The `Migration` and `MigrationCLI` imports are always emitted — they're
856
+ * structural to the rendered scaffold (extends `Migration`, calls
857
+ * `MigrationCLI.run`), not driven by any node.
850
858
  */
851
859
  function renderCallsToTypeScript(calls, meta) {
852
860
  const imports = buildImports(calls);
@@ -865,12 +873,12 @@ function renderCallsToTypeScript(calls, meta) {
865
873
  "}",
866
874
  "",
867
875
  "export default M;",
868
- "Migration.run(import.meta.url, M);",
876
+ "MigrationCLI.run(import.meta.url, M);",
869
877
  ""
870
878
  ].join("\n");
871
879
  }
872
880
  function buildImports(calls) {
873
- const requirements = [BASE_IMPORT];
881
+ const requirements = [...BASE_IMPORTS];
874
882
  for (const call of calls) for (const req of call.importRequirements()) requirements.push(req);
875
883
  return renderImports(requirements);
876
884
  }
@@ -880,7 +888,6 @@ function buildDescribeMethod(meta) {
880
888
  lines.push(" return {");
881
889
  lines.push(` from: ${JSON.stringify(meta.from)},`);
882
890
  lines.push(` to: ${JSON.stringify(meta.to)},`);
883
- if (meta.kind) lines.push(` kind: ${JSON.stringify(meta.kind)},`);
884
891
  if (meta.labels && meta.labels.length > 0) lines.push(` labels: ${jsonToTsSource(meta.labels)},`);
885
892
  lines.push(" };");
886
893
  lines.push(" }");
@@ -927,7 +934,6 @@ var PlannerProducedMongoMigration = class extends Migration {
927
934
  return renderCallsToTypeScript(this.calls, {
928
935
  from: this.meta.from,
929
936
  to: this.meta.to,
930
- ...ifDefined("kind", this.meta.kind),
931
937
  ...ifDefined("labels", this.meta.labels)
932
938
  });
933
939
  }
@@ -1049,7 +1055,7 @@ var MongoMigrationPlanner = class {
1049
1055
  return {
1050
1056
  kind: "success",
1051
1057
  plan: new PlannerProducedMongoMigration(result.calls, {
1052
- from: options.fromHash,
1058
+ from: options.fromContract?.storage.storageHash ?? null,
1053
1059
  to: contract.storage.storageHash
1054
1060
  })
1055
1061
  };
@@ -1109,9 +1115,6 @@ function planMutableOptionsDiffCall(collName, origin, dest) {
1109
1115
  //#endregion
1110
1116
  //#region src/core/mongo-runner.ts
1111
1117
  const READ_ONLY_CHECK_COMMAND_KINDS = new Set(["aggregate", "rawAggregate"]);
1112
- function hasProfileHash(value) {
1113
- return typeof value === "object" && value !== null && Object.hasOwn(value, "profileHash") && typeof value.profileHash === "string";
1114
- }
1115
1118
  function runnerFailure(code, summary, opts) {
1116
1119
  return notOk({
1117
1120
  code,
@@ -1163,29 +1166,45 @@ var MongoMigrationRunner = class {
1163
1166
  }
1164
1167
  }
1165
1168
  const destination = options.plan.destination;
1166
- const profileHash = hasProfileHash(options.destinationContract) ? options.destinationContract.profileHash : destination.storageHash;
1167
- if (operationsExecuted === 0 && existingMarker?.storageHash === destination.storageHash && existingMarker.profileHash === profileHash) return ok({
1168
- operationsPlanned: operations.length,
1169
- operationsExecuted
1170
- });
1171
- if (existingMarker) {
1172
- if (!await markerOps.updateMarker(existingMarker.storageHash, {
1169
+ const profileHash = options.destinationContract.profileHash ?? destination.storageHash;
1170
+ const incomingInvariants = options.plan.providedInvariants ?? [];
1171
+ const existingInvariantSet = new Set(existingMarker?.invariants ?? []);
1172
+ const incomingIsSubsetOfExisting = incomingInvariants.every((id) => existingInvariantSet.has(id));
1173
+ const markerAlreadyAtDestination = existingMarker !== null && existingMarker.storageHash === destination.storageHash && existingMarker.profileHash === profileHash;
1174
+ if (!(operationsExecuted === 0 && markerAlreadyAtDestination && incomingIsSubsetOfExisting)) {
1175
+ const liveSchema = await this.deps.introspectSchema();
1176
+ const verifyResult = verifyMongoSchema({
1177
+ contract: options.destinationContract,
1178
+ schema: liveSchema,
1179
+ strict: options.strictVerification ?? true,
1180
+ frameworkComponents: options.frameworkComponents,
1181
+ ...options.context ? { context: options.context } : {}
1182
+ });
1183
+ if (!verifyResult.ok) return runnerFailure("SCHEMA_VERIFY_FAILED", verifyResult.summary, {
1184
+ why: "The resulting database schema does not satisfy the destination contract.",
1185
+ meta: { issues: verifyResult.schema.issues }
1186
+ });
1187
+ if (existingMarker) {
1188
+ if (!await markerOps.updateMarker(existingMarker.storageHash, {
1189
+ storageHash: destination.storageHash,
1190
+ profileHash,
1191
+ invariants: incomingInvariants
1192
+ })) return runnerFailure("MARKER_CAS_FAILURE", "Marker was modified by another process during migration execution.", { meta: {
1193
+ expectedStorageHash: existingMarker.storageHash,
1194
+ destinationStorageHash: destination.storageHash
1195
+ } });
1196
+ } else await markerOps.initMarker({
1173
1197
  storageHash: destination.storageHash,
1174
- profileHash
1175
- })) return runnerFailure("MARKER_CAS_FAILURE", "Marker was modified by another process during migration execution.", { meta: {
1176
- expectedStorageHash: existingMarker.storageHash,
1177
- destinationStorageHash: destination.storageHash
1178
- } });
1179
- } else await markerOps.initMarker({
1180
- storageHash: destination.storageHash,
1181
- profileHash
1182
- });
1183
- const originHash = existingMarker?.storageHash ?? "";
1184
- await markerOps.writeLedgerEntry({
1185
- edgeId: `${originHash}->${destination.storageHash}`,
1186
- from: originHash,
1187
- to: destination.storageHash
1188
- });
1198
+ profileHash,
1199
+ invariants: incomingInvariants
1200
+ });
1201
+ const originHash = existingMarker?.storageHash ?? "";
1202
+ await markerOps.writeLedgerEntry({
1203
+ edgeId: `${originHash}->${destination.storageHash}`,
1204
+ from: originHash,
1205
+ to: destination.storageHash
1206
+ });
1207
+ }
1189
1208
  return ok({
1190
1209
  operationsPlanned: operations.length,
1191
1210
  operationsExecuted
@@ -1205,7 +1224,7 @@ var MongoMigrationRunner = class {
1205
1224
  };
1206
1225
  }
1207
1226
  for (const plan of op.run) {
1208
- const wireCommand = adapter.lower(plan);
1227
+ const wireCommand = await adapter.lower(plan, {});
1209
1228
  for await (const _ of driver.execute(wireCommand));
1210
1229
  }
1211
1230
  if (runPostchecks && op.postcheck.length > 0) {
@@ -1231,7 +1250,7 @@ var MongoMigrationRunner = class {
1231
1250
  collection: check.source.collection
1232
1251
  }
1233
1252
  });
1234
- const wireCommand = adapter.lower(check.source);
1253
+ const wireCommand = await adapter.lower(check.source, {});
1235
1254
  let matchFound = false;
1236
1255
  for await (const row of driver.execute(wireCommand)) if (filterEvaluator.evaluate(check.filter, row)) {
1237
1256
  matchFound = true;
@@ -1264,10 +1283,7 @@ var MongoMigrationRunner = class {
1264
1283
  }
1265
1284
  ensureMarkerCompatibility(marker, plan) {
1266
1285
  const origin = plan.origin ?? null;
1267
- if (!origin) {
1268
- if (marker) return runnerFailure("MARKER_ORIGIN_MISMATCH", "Database already has a contract marker but the plan has no origin. This would silently overwrite the existing marker.", { meta: { markerStorageHash: marker.storageHash } });
1269
- return;
1270
- }
1286
+ if (!origin) return;
1271
1287
  if (!marker) return runnerFailure("MARKER_ORIGIN_MISMATCH", `Missing contract marker: expected origin storage hash ${origin.storageHash}.`, { meta: { expectedOriginStorageHash: origin.storageHash } });
1272
1288
  if (marker.storageHash !== origin.storageHash) return runnerFailure("MARKER_ORIGIN_MISMATCH", `Existing contract marker (${marker.storageHash}) does not match plan origin (${origin.storageHash}).`, { meta: {
1273
1289
  markerStorageHash: marker.storageHash,