@prisma-next/family-mongo 0.12.0-dev.3 → 0.12.0-dev.31

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.
@@ -1,4 +1,8 @@
1
- import type { Contract, ContractMarkerRecord } from '@prisma-next/contract/types';
1
+ import type {
2
+ Contract,
3
+ ContractMarkerRecord,
4
+ LedgerEntryRecord,
5
+ } from '@prisma-next/contract/types';
2
6
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
3
7
  import type {
4
8
  ControlDriverInstance,
@@ -76,19 +80,6 @@ function asValidatedMongoContract(contract: unknown): MongoContract {
76
80
  return contract as MongoContract;
77
81
  }
78
82
 
79
- function isMongoControlAdapter(value: unknown): value is MongoControlAdapter<'mongo'> {
80
- return (
81
- typeof value === 'object' &&
82
- value !== null &&
83
- 'readMarker' in value &&
84
- typeof (value as { readMarker: unknown }).readMarker === 'function' &&
85
- 'readAllMarkers' in value &&
86
- typeof (value as { readAllMarkers: unknown }).readAllMarkers === 'function' &&
87
- 'introspectSchema' in value &&
88
- typeof (value as { introspectSchema: unknown }).introspectSchema === 'function'
89
- );
90
- }
91
-
92
83
  function buildVerifyResult(opts: {
93
84
  ok: boolean;
94
85
  code?: string;
@@ -164,13 +155,7 @@ export function createMongoFamilyInstance(controlStack: ControlStack): MongoCont
164
155
  if (!adapter) {
165
156
  throw new Error('Mongo family requires an adapter descriptor in ControlStack');
166
157
  }
167
- const controlAdapter = adapter.create(controlStack as ControlStack<'mongo', 'mongo'>);
168
- if (!isMongoControlAdapter(controlAdapter)) {
169
- throw new Error(
170
- 'Adapter does not implement MongoControlAdapter (missing readMarker, readAllMarkers, or introspectSchema)',
171
- );
172
- }
173
- return controlAdapter;
158
+ return adapter.create(controlStack as ControlStack<'mongo', 'mongo'>);
174
159
  };
175
160
 
176
161
  // The family-level driver type is `ControlDriverInstance<'mongo', string>`,
@@ -371,6 +356,10 @@ export function createMongoFamilyInstance(controlStack: ControlStack): MongoCont
371
356
  return getControlAdapter().readAllMarkers(asMongoDriver(options.driver));
372
357
  },
373
358
 
359
+ async readLedger(options): Promise<readonly LedgerEntryRecord[]> {
360
+ return getControlAdapter().readLedger(asMongoDriver(options.driver), options.space);
361
+ },
362
+
374
363
  async introspect(options): Promise<MongoSchemaIR> {
375
364
  return getControlAdapter().introspectSchema(asMongoDriver(options.driver));
376
365
  },
@@ -0,0 +1,4 @@
1
+ export {
2
+ defaultMongoDomainNamespaceId,
3
+ defaultMongoStorageNamespaceId,
4
+ } from '@prisma-next/mongo-contract';
@@ -1,3 +1,5 @@
1
+ import type { ControlPolicy } from '@prisma-next/contract/types';
2
+ import { effectiveControlPolicy } from '@prisma-next/contract/types';
1
3
  import type {
2
4
  SchemaIssue,
3
5
  SchemaVerifier,
@@ -5,7 +7,8 @@ import type {
5
7
  SchemaVerifyResult,
6
8
  } from '@prisma-next/framework-components/control';
7
9
  import type { Namespace } from '@prisma-next/framework-components/ir';
8
- import type { MongoStorage } from '@prisma-next/mongo-contract';
10
+ import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
11
+ import type { MongoCollection, MongoStorage } from '@prisma-next/mongo-contract';
9
12
 
10
13
  /**
11
14
  * Mongo family `SchemaVerifier` abstract base. Commits the Mongo family
@@ -29,7 +32,10 @@ import type { MongoStorage } from '@prisma-next/mongo-contract';
29
32
  * list when no extensions exist over the Mongo family alphabet.
30
33
  */
31
34
  export abstract class MongoSchemaVerifierBase<
32
- TContract extends { readonly storage: MongoStorage },
35
+ TContract extends {
36
+ readonly storage: MongoStorage;
37
+ readonly defaultControlPolicy?: ControlPolicy;
38
+ },
33
39
  TSchema,
34
40
  > implements SchemaVerifier<TContract, TSchema>
35
41
  {
@@ -40,6 +46,22 @@ export abstract class MongoSchemaVerifierBase<
40
46
  return { ok: issues.length === 0, issues };
41
47
  }
42
48
 
49
+ protected effectiveCollectionControlPolicy(
50
+ contract: TContract,
51
+ collection: MongoCollection | undefined,
52
+ ): ControlPolicy {
53
+ return effectiveControlPolicy(collection?.control, contract.defaultControlPolicy);
54
+ }
55
+
56
+ protected collectionControlPolicyForName(
57
+ contract: TContract,
58
+ collectionName: string,
59
+ ): ControlPolicy {
60
+ const namespace = contract.storage.namespaces[UNBOUND_NAMESPACE_ID];
61
+ const collection = namespace?.collections[collectionName];
62
+ return this.effectiveCollectionControlPolicy(contract, collection);
63
+ }
64
+
43
65
  protected verifyCommonMongoSchema(
44
66
  options: SchemaVerifyOptions<TContract, TSchema>,
45
67
  ): readonly SchemaIssue[] {
@@ -1,3 +1,4 @@
1
+ import type { ControlPolicy } from '@prisma-next/contract/types';
1
2
  import type {
2
3
  SchemaIssue,
3
4
  SchemaVerificationNode,
@@ -9,11 +10,13 @@ import type {
9
10
  MongoSchemaIR,
10
11
  } from '@prisma-next/mongo-schema-ir';
11
12
  import { canonicalize, deepEqual } from '@prisma-next/mongo-schema-ir';
13
+ import { emitMongoIssueAndNodeUnderControlPolicy } from './schema-verify/mongo-control-verify-emit';
12
14
 
13
15
  export function diffMongoSchemas(
14
16
  live: MongoSchemaIR,
15
17
  expected: MongoSchemaIR,
16
18
  strict: boolean,
19
+ collectionControlPolicy: (collectionName: string) => ControlPolicy,
17
20
  ): {
18
21
  root: SchemaVerificationNode;
19
22
  issues: SchemaIssue[];
@@ -32,54 +35,70 @@ export function diffMongoSchemas(
32
35
  const expectedColl = expected.collection(name);
33
36
 
34
37
  if (!liveColl && expectedColl) {
35
- issues.push({
38
+ const controlPolicy = collectionControlPolicy(name);
39
+ const issue: SchemaIssue = {
36
40
  kind: 'missing_table',
37
41
  table: name,
38
42
  message: `Collection "${name}" is missing from the database`,
39
- });
40
- collectionChildren.push({
41
- status: 'fail',
42
- kind: 'collection',
43
- name,
44
- contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${name}`,
45
- code: 'MISSING_COLLECTION',
46
- message: `Collection "${name}" is missing`,
47
- expected: name,
48
- actual: null,
49
- children: [],
50
- });
51
- fail++;
43
+ };
44
+ const disposition = emitMongoIssueAndNodeUnderControlPolicy(
45
+ controlPolicy,
46
+ issue,
47
+ {
48
+ status: 'fail',
49
+ kind: 'collection',
50
+ name,
51
+ contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${name}`,
52
+ code: 'MISSING_COLLECTION',
53
+ message: `Collection "${name}" is missing`,
54
+ expected: name,
55
+ actual: null,
56
+ children: [],
57
+ },
58
+ issues,
59
+ collectionChildren,
60
+ );
61
+ if (disposition === 'fail') fail++;
62
+ else if (disposition === 'warn') warn++;
52
63
  continue;
53
64
  }
54
65
 
55
66
  if (liveColl && !expectedColl) {
56
- const status = strict ? 'fail' : 'warn';
57
- issues.push({
67
+ const controlPolicy = collectionControlPolicy(name);
68
+ const issue: SchemaIssue = {
58
69
  kind: 'extra_table',
59
70
  table: name,
60
71
  message: `Extra collection "${name}" exists in the database but not in the contract`,
61
- });
62
- collectionChildren.push({
63
- status,
64
- kind: 'collection',
65
- name,
66
- contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${name}`,
67
- code: 'EXTRA_COLLECTION',
68
- message: `Extra collection "${name}" found`,
69
- expected: null,
70
- actual: name,
71
- children: [],
72
- });
73
- if (status === 'fail') fail++;
74
- else warn++;
72
+ };
73
+ const baseStatus = strict ? 'fail' : 'warn';
74
+ const disposition = emitMongoIssueAndNodeUnderControlPolicy(
75
+ controlPolicy,
76
+ issue,
77
+ {
78
+ status: baseStatus,
79
+ kind: 'collection',
80
+ name,
81
+ contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${name}`,
82
+ code: 'EXTRA_COLLECTION',
83
+ message: `Extra collection "${name}" found`,
84
+ expected: null,
85
+ actual: name,
86
+ children: [],
87
+ },
88
+ issues,
89
+ collectionChildren,
90
+ );
91
+ if (disposition === 'fail') fail++;
92
+ else if (disposition === 'warn') warn++;
75
93
  continue;
76
94
  }
77
95
 
78
96
  const lc = liveColl as MongoSchemaCollection;
79
97
  const ec = expectedColl as MongoSchemaCollection;
80
- const indexChildren = diffIndexes(name, lc, ec, strict, issues);
81
- const validatorChildren = diffValidator(name, lc, ec, strict, issues);
82
- const optionsChildren = diffOptions(name, lc, ec, strict, issues);
98
+ const controlPolicy = collectionControlPolicy(name);
99
+ const indexChildren = diffIndexes(name, lc, ec, strict, controlPolicy, issues);
100
+ const validatorChildren = diffValidator(name, lc, ec, strict, controlPolicy, issues);
101
+ const optionsChildren = diffOptions(name, lc, ec, strict, controlPolicy, issues);
83
102
  const children = [...indexChildren, ...validatorChildren, ...optionsChildren];
84
103
 
85
104
  const worstStatus = children.reduce<'pass' | 'warn' | 'fail'>(
@@ -156,6 +175,7 @@ function diffIndexes(
156
175
  live: MongoSchemaCollection,
157
176
  expected: MongoSchemaCollection,
158
177
  strict: boolean,
178
+ collectionControlPolicy: ControlPolicy,
159
179
  issues: SchemaIssue[],
160
180
  ): SchemaVerificationNode[] {
161
181
  const nodes: SchemaVerificationNode[] = [];
@@ -179,46 +199,58 @@ function diffIndexes(
179
199
  children: [],
180
200
  });
181
201
  } else {
182
- issues.push({
202
+ const issue: SchemaIssue = {
183
203
  kind: 'index_mismatch',
184
204
  table: collName,
185
205
  indexOrConstraint: formatIndexName(idx),
186
206
  message: `Index ${formatIndexName(idx)} missing on collection "${collName}"`,
187
- });
188
- nodes.push({
189
- status: 'fail',
190
- kind: 'index',
191
- name: formatIndexName(idx),
192
- contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.indexes`,
193
- code: 'MISSING_INDEX',
194
- message: `Index ${formatIndexName(idx)} missing`,
195
- expected: key,
196
- actual: null,
197
- children: [],
198
- });
207
+ };
208
+ emitMongoIssueAndNodeUnderControlPolicy(
209
+ collectionControlPolicy,
210
+ issue,
211
+ {
212
+ status: 'fail',
213
+ kind: 'index',
214
+ name: formatIndexName(idx),
215
+ contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.indexes`,
216
+ code: 'MISSING_INDEX',
217
+ message: `Index ${formatIndexName(idx)} missing`,
218
+ expected: key,
219
+ actual: null,
220
+ children: [],
221
+ },
222
+ issues,
223
+ nodes,
224
+ );
199
225
  }
200
226
  }
201
227
 
202
228
  for (const [key, idx] of liveLookup) {
203
229
  if (!expectedLookup.has(key)) {
204
- const status = strict ? 'fail' : 'warn';
205
- issues.push({
230
+ const issue: SchemaIssue = {
206
231
  kind: 'extra_index',
207
232
  table: collName,
208
233
  indexOrConstraint: formatIndexName(idx),
209
234
  message: `Extra index ${formatIndexName(idx)} on collection "${collName}"`,
210
- });
211
- nodes.push({
212
- status,
213
- kind: 'index',
214
- name: formatIndexName(idx),
215
- contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.indexes`,
216
- code: 'EXTRA_INDEX',
217
- message: `Extra index ${formatIndexName(idx)}`,
218
- expected: null,
219
- actual: key,
220
- children: [],
221
- });
235
+ };
236
+ const baseStatus = strict ? 'fail' : 'warn';
237
+ emitMongoIssueAndNodeUnderControlPolicy(
238
+ collectionControlPolicy,
239
+ issue,
240
+ {
241
+ status: baseStatus,
242
+ kind: 'index',
243
+ name: formatIndexName(idx),
244
+ contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.indexes`,
245
+ code: 'EXTRA_INDEX',
246
+ message: `Extra index ${formatIndexName(idx)}`,
247
+ expected: null,
248
+ actual: key,
249
+ children: [],
250
+ },
251
+ issues,
252
+ nodes,
253
+ );
222
254
  }
223
255
  }
224
256
 
@@ -230,17 +262,21 @@ function diffValidator(
230
262
  live: MongoSchemaCollection,
231
263
  expected: MongoSchemaCollection,
232
264
  strict: boolean,
265
+ collectionControlPolicy: ControlPolicy,
233
266
  issues: SchemaIssue[],
234
267
  ): SchemaVerificationNode[] {
235
268
  if (!live.validator && !expected.validator) return [];
236
269
 
237
270
  if (expected.validator && !live.validator) {
238
- issues.push({
271
+ const issue: SchemaIssue = {
239
272
  kind: 'type_missing',
240
273
  table: collName,
241
274
  message: `Validator missing on collection "${collName}"`,
242
- });
243
- return [
275
+ };
276
+ const nodes: SchemaVerificationNode[] = [];
277
+ emitMongoIssueAndNodeUnderControlPolicy(
278
+ collectionControlPolicy,
279
+ issue,
244
280
  {
245
281
  status: 'fail',
246
282
  kind: 'validator',
@@ -252,19 +288,25 @@ function diffValidator(
252
288
  actual: null,
253
289
  children: [],
254
290
  },
255
- ];
291
+ issues,
292
+ nodes,
293
+ );
294
+ return nodes;
256
295
  }
257
296
 
258
297
  if (!expected.validator && live.validator) {
259
- const status = strict ? 'fail' : 'warn';
260
- issues.push({
298
+ const issue: SchemaIssue = {
261
299
  kind: 'extra_validator',
262
300
  table: collName,
263
301
  message: `Extra validator on collection "${collName}"`,
264
- });
265
- return [
302
+ };
303
+ const nodes: SchemaVerificationNode[] = [];
304
+ const baseStatus = strict ? 'fail' : 'warn';
305
+ emitMongoIssueAndNodeUnderControlPolicy(
306
+ collectionControlPolicy,
307
+ issue,
266
308
  {
267
- status,
309
+ status: baseStatus,
268
310
  kind: 'validator',
269
311
  name: 'validator',
270
312
  contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.validator`,
@@ -274,7 +316,10 @@ function diffValidator(
274
316
  actual: canonicalize(live.validator.jsonSchema),
275
317
  children: [],
276
318
  },
277
- ];
319
+ issues,
320
+ nodes,
321
+ );
322
+ return nodes;
278
323
  }
279
324
 
280
325
  const liveVal = live.validator as NonNullable<typeof live.validator>;
@@ -287,14 +332,17 @@ function diffValidator(
287
332
  liveVal.validationLevel !== expectedVal.validationLevel ||
288
333
  liveVal.validationAction !== expectedVal.validationAction
289
334
  ) {
290
- issues.push({
335
+ const issue: SchemaIssue = {
291
336
  kind: 'type_mismatch',
292
337
  table: collName,
293
338
  expected: expectedSchema,
294
339
  actual: liveSchema,
295
340
  message: `Validator mismatch on collection "${collName}"`,
296
- });
297
- return [
341
+ };
342
+ const nodes: SchemaVerificationNode[] = [];
343
+ emitMongoIssueAndNodeUnderControlPolicy(
344
+ collectionControlPolicy,
345
+ issue,
298
346
  {
299
347
  status: 'fail',
300
348
  kind: 'validator',
@@ -314,7 +362,10 @@ function diffValidator(
314
362
  },
315
363
  children: [],
316
364
  },
317
- ];
365
+ issues,
366
+ nodes,
367
+ );
368
+ return nodes;
318
369
  }
319
370
 
320
371
  return [
@@ -337,21 +388,25 @@ function diffOptions(
337
388
  live: MongoSchemaCollection,
338
389
  expected: MongoSchemaCollection,
339
390
  strict: boolean,
391
+ collectionControlPolicy: ControlPolicy,
340
392
  issues: SchemaIssue[],
341
393
  ): SchemaVerificationNode[] {
342
394
  if (!live.options && !expected.options) return [];
343
395
 
344
396
  if (!expected.options && live.options) {
345
- const status = strict ? 'fail' : 'warn';
346
- issues.push({
397
+ const issue: SchemaIssue = {
347
398
  kind: 'type_mismatch',
348
399
  table: collName,
349
400
  actual: canonicalize(live.options),
350
401
  message: `Extra collection options on "${collName}"`,
351
- });
352
- return [
402
+ };
403
+ const nodes: SchemaVerificationNode[] = [];
404
+ const baseStatus = strict ? 'fail' : 'warn';
405
+ emitMongoIssueAndNodeUnderControlPolicy(
406
+ collectionControlPolicy,
407
+ issue,
353
408
  {
354
- status,
409
+ status: baseStatus,
355
410
  kind: 'options',
356
411
  name: 'options',
357
412
  contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.options`,
@@ -361,7 +416,10 @@ function diffOptions(
361
416
  actual: live.options,
362
417
  children: [],
363
418
  },
364
- ];
419
+ issues,
420
+ nodes,
421
+ );
422
+ return nodes;
365
423
  }
366
424
 
367
425
  if (deepEqual(live.options, expected.options)) {
@@ -380,14 +438,17 @@ function diffOptions(
380
438
  ];
381
439
  }
382
440
 
383
- issues.push({
441
+ const issue: SchemaIssue = {
384
442
  kind: 'type_mismatch',
385
443
  table: collName,
386
444
  expected: canonicalize(expected.options),
387
445
  actual: canonicalize(live.options),
388
446
  message: `Collection options mismatch on "${collName}"`,
389
- });
390
- return [
447
+ };
448
+ const nodes: SchemaVerificationNode[] = [];
449
+ emitMongoIssueAndNodeUnderControlPolicy(
450
+ collectionControlPolicy,
451
+ issue,
391
452
  {
392
453
  status: 'fail',
393
454
  kind: 'options',
@@ -399,5 +460,8 @@ function diffOptions(
399
460
  actual: live.options,
400
461
  children: [],
401
462
  },
402
- ];
463
+ issues,
464
+ nodes,
465
+ );
466
+ return nodes;
403
467
  }
@@ -0,0 +1,53 @@
1
+ import type { ControlPolicy } from '@prisma-next/contract/types';
2
+ import type {
3
+ SchemaIssue,
4
+ SchemaVerificationNode,
5
+ VerifierOutcome,
6
+ } from '@prisma-next/framework-components/control';
7
+ import { verifierDisposition } from './verifier-disposition';
8
+
9
+ /**
10
+ * Reconciles a control-policy disposition with the Mongo family's strict-mode
11
+ * contract for live-only extras — the single point where `strict` and the
12
+ * control policy meet.
13
+ *
14
+ * The control policy decides first; only a `fail` is reconciled against the
15
+ * caller's base node status. Call sites stamp a live-only extra with
16
+ * `strict ? 'fail' : 'warn'` and a declared missing/mismatch with `fail`, so
17
+ * this one step encodes the whole matrix:
18
+ *
19
+ * | live-vs-declared | strict | non-strict |
20
+ * |-------------------------------------|----------|------------|
21
+ * | declared missing / mismatch | fail | fail |
22
+ * | live-only extra (managed/tolerated) | fail | warn |
23
+ * | live-only extra (external) | suppress (extras ignored, both modes) |
24
+ * | anything (observed) | warn (both modes) |
25
+ *
26
+ * `tolerated` no longer diverges from `managed` on a non-strict extra index:
27
+ * both soften to `warn`, because the softening comes from the base status the
28
+ * caller already computed from `strict`, not from per-policy special-casing.
29
+ */
30
+ function reconcileMongoOutcome(
31
+ controlPolicy: ControlPolicy,
32
+ issueKind: SchemaIssue['kind'],
33
+ baseStatus: SchemaVerificationNode['status'],
34
+ ): VerifierOutcome {
35
+ const disposition = verifierDisposition(controlPolicy, issueKind);
36
+ return disposition === 'fail' ? baseStatus : disposition;
37
+ }
38
+
39
+ export function emitMongoIssueAndNodeUnderControlPolicy(
40
+ controlPolicy: ControlPolicy,
41
+ issue: SchemaIssue,
42
+ node: SchemaVerificationNode,
43
+ issues: SchemaIssue[],
44
+ nodes: SchemaVerificationNode[],
45
+ ): VerifierOutcome {
46
+ const outcome = reconcileMongoOutcome(controlPolicy, issue.kind, node.status);
47
+ if (outcome === 'suppress') {
48
+ return 'suppress';
49
+ }
50
+ issues.push(issue);
51
+ nodes.push({ ...node, status: outcome });
52
+ return outcome;
53
+ }
@@ -0,0 +1,42 @@
1
+ import type { ControlPolicy } from '@prisma-next/contract/types';
2
+ import type {
3
+ SchemaIssue,
4
+ VerifierIssueCategory,
5
+ VerifierOutcome,
6
+ } from '@prisma-next/framework-components/control';
7
+ import { dispositionForCategory } from '@prisma-next/framework-components/control';
8
+
9
+ /**
10
+ * Classifies the verifier issue kinds the Mongo schema differ emits into the
11
+ * target-neutral categories the framework grades. Mongo only emits the kinds
12
+ * listed below (missing/extra collections, missing/extra indexes, missing/extra
13
+ * validators, and validator/options mismatches coded as `type_mismatch`); any
14
+ * other kind never reaches this classifier and is graded conservatively as a
15
+ * declared-incompatible divergence. Mongo owns this mapping rather than
16
+ * importing the SQL classifier — the two families share only the framework's
17
+ * category grading.
18
+ */
19
+ export function classifyMongoVerifierIssueKind(kind: SchemaIssue['kind']): VerifierIssueCategory {
20
+ switch (kind) {
21
+ case 'extra_table':
22
+ return 'extraTopLevelObject';
23
+ case 'extra_index':
24
+ case 'extra_validator':
25
+ return 'extraAuxiliary';
26
+ case 'missing_table':
27
+ case 'type_missing':
28
+ return 'declaredMissing';
29
+ case 'index_mismatch':
30
+ case 'type_mismatch':
31
+ return 'declaredIncompatible';
32
+ default:
33
+ return 'declaredIncompatible';
34
+ }
35
+ }
36
+
37
+ export function verifierDisposition(
38
+ controlPolicy: ControlPolicy,
39
+ issueKind: SchemaIssue['kind'],
40
+ ): VerifierOutcome {
41
+ return dispositionForCategory(controlPolicy, classifyMongoVerifierIssueKind(issueKind));
42
+ }
@@ -1,10 +1,12 @@
1
+ import { type ControlPolicy, effectiveControlPolicy } from '@prisma-next/contract/types';
1
2
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
2
3
  import type {
3
4
  OperationContext,
4
5
  VerifyDatabaseSchemaResult,
5
6
  } from '@prisma-next/framework-components/control';
6
7
  import { VERIFY_CODE_SCHEMA_FAILURE } from '@prisma-next/framework-components/control';
7
- import type { MongoContract } from '@prisma-next/mongo-contract';
8
+ import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
9
+ import type { MongoCollection, MongoContract } from '@prisma-next/mongo-contract';
8
10
  import type { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';
9
11
  import { ifDefined } from '@prisma-next/utils/defined';
10
12
  import { contractToMongoSchemaIR } from '../contract-to-schema';
@@ -35,7 +37,13 @@ export function verifyMongoSchema(options: VerifyMongoSchemaOptions): VerifyData
35
37
  schema,
36
38
  expectedIR,
37
39
  );
38
- const { root, issues, counts } = diffMongoSchemas(canonicalLive, canonicalExpected, strict);
40
+ const collectionControlPolicy = resolveMongoCollectionControlPolicy(contract);
41
+ const { root, issues, counts } = diffMongoSchemas(
42
+ canonicalLive,
43
+ canonicalExpected,
44
+ strict,
45
+ collectionControlPolicy,
46
+ );
39
47
 
40
48
  const ok = counts.fail === 0;
41
49
  const profileHash = typeof contract.profileHash === 'string' ? contract.profileHash : '';
@@ -58,3 +66,13 @@ export function verifyMongoSchema(options: VerifyMongoSchemaOptions): VerifyData
58
66
  timings: { total: Date.now() - startTime },
59
67
  };
60
68
  }
69
+
70
+ function resolveMongoCollectionControlPolicy(
71
+ contract: MongoContract,
72
+ ): (collectionName: string) => ControlPolicy {
73
+ const namespace = contract.storage.namespaces[UNBOUND_NAMESPACE_ID];
74
+ const collections: Record<string, MongoCollection> = namespace?.collections ?? {};
75
+ const defaultControlPolicy = contract.defaultControlPolicy;
76
+ return (collectionName: string) =>
77
+ effectiveControlPolicy(collections[collectionName]?.control, defaultControlPolicy);
78
+ }
@@ -0,0 +1,4 @@
1
+ export {
2
+ defaultMongoDomainNamespaceId,
3
+ defaultMongoStorageNamespaceId,
4
+ } from '../core/default-namespace';