@prisma-next/family-sql 0.9.0 → 0.10.0

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.
@@ -7,6 +7,7 @@ import type {
7
7
  SchemaIssue,
8
8
  SchemaVerificationNode,
9
9
  } from '@prisma-next/framework-components/control';
10
+ import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
10
11
  import type {
11
12
  ForeignKey,
12
13
  Index,
@@ -133,12 +134,14 @@ export function verifyPrimaryKey(
133
134
  contractPK: PrimaryKey,
134
135
  schemaPK: PrimaryKey | undefined,
135
136
  tableName: string,
137
+ namespaceId: string,
136
138
  issues: SchemaIssue[],
137
139
  ): 'pass' | 'fail' {
138
140
  if (!schemaPK) {
139
141
  issues.push({
140
142
  kind: 'primary_key_mismatch',
141
143
  table: tableName,
144
+ namespaceId,
142
145
  expected: contractPK.columns.join(', '),
143
146
  message: `Table "${tableName}" is missing primary key`,
144
147
  });
@@ -149,6 +152,7 @@ export function verifyPrimaryKey(
149
152
  issues.push({
150
153
  kind: 'primary_key_mismatch',
151
154
  table: tableName,
155
+ namespaceId,
152
156
  expected: contractPK.columns.join(', '),
153
157
  actual: schemaPK.columns.join(', '),
154
158
  message: `Table "${tableName}" has primary key mismatch: expected columns [${contractPK.columns.join(', ')}], got [${schemaPK.columns.join(', ')}]`,
@@ -173,6 +177,7 @@ export function verifyForeignKeys(
173
177
  contractFKs: readonly ForeignKey[],
174
178
  schemaFKs: readonly SqlForeignKeyIR[],
175
179
  tableName: string,
180
+ namespaceId: string,
176
181
  tablePath: string,
177
182
  issues: SchemaIssue[],
178
183
  strict: boolean,
@@ -181,12 +186,23 @@ export function verifyForeignKeys(
181
186
 
182
187
  // Check each contract FK exists in schema
183
188
  for (const contractFK of contractFKs) {
184
- const fkPath = `${tablePath}.foreignKeys[${contractFK.columns.join(',')}]`;
189
+ const fkPath = `${tablePath}.foreignKeys[${contractFK.source.columns.join(',')}]`;
185
190
  const matchingFK = schemaFKs.find((fk) => {
191
+ // When the schema FK carries referencedSchema (populated by the Postgres
192
+ // adapter for cross-schema FKs), compare the full (namespace, table) pair
193
+ // against the contract FK's target coordinate. When referencedSchema is
194
+ // absent (same-schema FKs, older introspection, or hand-crafted test
195
+ // fixtures), fall back to table-name-only comparison so same-namespace
196
+ // contracts continue to verify correctly.
197
+ const tablesMatch =
198
+ fk.referencedSchema !== undefined && contractFK.target.namespaceId !== UNBOUND_NAMESPACE_ID
199
+ ? fk.referencedSchema === contractFK.target.namespaceId &&
200
+ fk.referencedTable === contractFK.target.tableName
201
+ : fk.referencedTable === contractFK.target.tableName;
186
202
  return (
187
- arraysEqual(fk.columns, contractFK.columns) &&
188
- fk.referencedTable === contractFK.references.table &&
189
- arraysEqual(fk.referencedColumns, contractFK.references.columns)
203
+ arraysEqual(fk.columns, contractFK.source.columns) &&
204
+ tablesMatch &&
205
+ arraysEqual(fk.referencedColumns, contractFK.target.columns)
190
206
  );
191
207
  });
192
208
 
@@ -194,13 +210,14 @@ export function verifyForeignKeys(
194
210
  issues.push({
195
211
  kind: 'foreign_key_mismatch',
196
212
  table: tableName,
197
- expected: `${contractFK.columns.join(', ')} -> ${contractFK.references.table}(${contractFK.references.columns.join(', ')})`,
198
- message: `Table "${tableName}" is missing foreign key: ${contractFK.columns.join(', ')} -> ${contractFK.references.table}(${contractFK.references.columns.join(', ')})`,
213
+ namespaceId,
214
+ expected: `${contractFK.source.columns.join(', ')} -> ${contractFK.target.tableName}(${contractFK.target.columns.join(', ')})`,
215
+ message: `Table "${tableName}" is missing foreign key: ${contractFK.source.columns.join(', ')} -> ${contractFK.target.tableName}(${contractFK.target.columns.join(', ')})`,
199
216
  });
200
217
  nodes.push({
201
218
  status: 'fail',
202
219
  kind: 'foreignKey',
203
- name: `foreignKey(${contractFK.columns.join(', ')})`,
220
+ name: `foreignKey(${contractFK.source.columns.join(', ')})`,
204
221
  contractPath: fkPath,
205
222
  code: 'foreign_key_mismatch',
206
223
  message: 'Foreign key missing',
@@ -217,17 +234,18 @@ export function verifyForeignKeys(
217
234
  issues.push({
218
235
  kind: 'foreign_key_mismatch',
219
236
  table: tableName,
237
+ namespaceId,
220
238
  // Set indexOrConstraint so the planner classifies this as a non-additive
221
239
  // conflict (existing FK with wrong actions cannot be fixed additively).
222
- indexOrConstraint: matchingFK.name ?? `fk(${contractFK.columns.join(',')})`,
240
+ indexOrConstraint: matchingFK.name ?? `fk(${contractFK.source.columns.join(',')})`,
223
241
  expected: combinedExpected,
224
242
  actual: combinedActual,
225
- message: `Table "${tableName}" foreign key ${contractFK.columns.join(', ')} -> ${contractFK.references.table}: ${combinedMessage}`,
243
+ message: `Table "${tableName}" foreign key ${contractFK.source.columns.join(', ')} -> ${contractFK.target.tableName}: ${combinedMessage}`,
226
244
  });
227
245
  nodes.push({
228
246
  status: 'fail',
229
247
  kind: 'foreignKey',
230
- name: `foreignKey(${contractFK.columns.join(', ')})`,
248
+ name: `foreignKey(${contractFK.source.columns.join(', ')})`,
231
249
  contractPath: fkPath,
232
250
  code: 'foreign_key_mismatch',
233
251
  message: combinedMessage,
@@ -239,7 +257,7 @@ export function verifyForeignKeys(
239
257
  nodes.push({
240
258
  status: 'pass',
241
259
  kind: 'foreignKey',
242
- name: `foreignKey(${contractFK.columns.join(', ')})`,
260
+ name: `foreignKey(${contractFK.source.columns.join(', ')})`,
243
261
  contractPath: fkPath,
244
262
  code: '',
245
263
  message: '',
@@ -255,10 +273,15 @@ export function verifyForeignKeys(
255
273
  if (strict) {
256
274
  for (const schemaFK of schemaFKs) {
257
275
  const matchingFK = contractFKs.find((fk) => {
276
+ const tablesMatch =
277
+ schemaFK.referencedSchema !== undefined && fk.target.namespaceId !== UNBOUND_NAMESPACE_ID
278
+ ? schemaFK.referencedSchema === fk.target.namespaceId &&
279
+ schemaFK.referencedTable === fk.target.tableName
280
+ : schemaFK.referencedTable === fk.target.tableName;
258
281
  return (
259
- arraysEqual(fk.columns, schemaFK.columns) &&
260
- fk.references.table === schemaFK.referencedTable &&
261
- arraysEqual(fk.references.columns, schemaFK.referencedColumns)
282
+ arraysEqual(fk.source.columns, schemaFK.columns) &&
283
+ tablesMatch &&
284
+ arraysEqual(fk.target.columns, schemaFK.referencedColumns)
262
285
  );
263
286
  });
264
287
 
@@ -266,6 +289,7 @@ export function verifyForeignKeys(
266
289
  issues.push({
267
290
  kind: 'extra_foreign_key',
268
291
  table: tableName,
292
+ namespaceId,
269
293
  indexOrConstraint: schemaFK.name ?? `fk(${schemaFK.columns.join(',')})`,
270
294
  message: `Extra foreign key found in database (not in contract): ${schemaFK.columns.join(', ')} -> ${schemaFK.referencedTable}(${schemaFK.referencedColumns.join(', ')})`,
271
295
  });
@@ -303,6 +327,7 @@ export function verifyUniqueConstraints(
303
327
  schemaUniques: readonly SqlUniqueIR[],
304
328
  schemaIndexes: readonly SqlIndexIR[],
305
329
  tableName: string,
330
+ namespaceId: string,
306
331
  tablePath: string,
307
332
  issues: SchemaIssue[],
308
333
  strict: boolean,
@@ -327,6 +352,7 @@ export function verifyUniqueConstraints(
327
352
  issues.push({
328
353
  kind: 'unique_constraint_mismatch',
329
354
  table: tableName,
355
+ namespaceId,
330
356
  expected: contractUnique.columns.join(', '),
331
357
  message: `Table "${tableName}" is missing unique constraint: ${contractUnique.columns.join(', ')}`,
332
358
  });
@@ -369,6 +395,7 @@ export function verifyUniqueConstraints(
369
395
  issues.push({
370
396
  kind: 'extra_unique_constraint',
371
397
  table: tableName,
398
+ namespaceId,
372
399
  indexOrConstraint: schemaUnique.name ?? `unique(${schemaUnique.columns.join(',')})`,
373
400
  message: `Extra unique constraint found in database (not in contract): ${schemaUnique.columns.join(', ')}`,
374
401
  });
@@ -406,6 +433,7 @@ export function verifyIndexes(
406
433
  schemaIndexes: readonly SqlIndexIR[],
407
434
  schemaUniques: readonly SqlUniqueIR[],
408
435
  tableName: string,
436
+ namespaceId: string,
409
437
  tablePath: string,
410
438
  issues: SchemaIssue[],
411
439
  strict: boolean,
@@ -436,6 +464,7 @@ export function verifyIndexes(
436
464
  issues.push({
437
465
  kind: 'index_mismatch',
438
466
  table: tableName,
467
+ namespaceId,
439
468
  expected: contractIndex.columns.join(', '),
440
469
  message: `Table "${tableName}" is missing index: ${contractIndex.columns.join(', ')}`,
441
470
  });
@@ -484,6 +513,7 @@ export function verifyIndexes(
484
513
  issues.push({
485
514
  kind: 'extra_index',
486
515
  table: tableName,
516
+ namespaceId,
487
517
  indexOrConstraint: schemaIndex.name ?? `idx(${schemaIndex.columns.join(',')})`,
488
518
  message: `Extra index found in database (not in contract): ${schemaIndex.columns.join(', ')}`,
489
519
  });
@@ -20,6 +20,7 @@ import {
20
20
  type PostgresEnumStorageEntry,
21
21
  type SqlStorage,
22
22
  type StorageColumn,
23
+ StorageTable,
23
24
  type StorageTypeInstance,
24
25
  } from '@prisma-next/sql-contract/types';
25
26
  import type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';
@@ -128,7 +129,21 @@ export function verifySqlSchema(options: VerifySqlSchemaOptions): VerifyDatabase
128
129
 
129
130
  const { contractStorageHash, contractProfileHash, contractTarget } =
130
131
  extractContractMetadata(contract);
131
- const storageTypes = (contract.storage.types ?? {}) as Readonly<
132
+ const allStorageTypesMap: Record<string, PostgresEnumStorageEntry | StorageTypeInstance> = {
133
+ ...((contract.storage.types ?? {}) as Record<
134
+ string,
135
+ PostgresEnumStorageEntry | StorageTypeInstance
136
+ >),
137
+ };
138
+ for (const ns of Object.values(contract.storage.namespaces)) {
139
+ const nsTypes = (ns as { types?: Record<string, PostgresEnumStorageEntry> }).types;
140
+ if (nsTypes) {
141
+ for (const [k, v] of Object.entries(nsTypes)) {
142
+ allStorageTypesMap[k] = v;
143
+ }
144
+ }
145
+ }
146
+ const storageTypes = allStorageTypesMap as Readonly<
132
147
  Record<string, PostgresEnumStorageEntry | StorageTypeInstance>
133
148
  >;
134
149
  const { issues, rootChildren } = verifySchemaTables({
@@ -335,52 +350,73 @@ function verifySchemaTables(options: {
335
350
  } = options;
336
351
  const issues: SchemaIssue[] = [];
337
352
  const rootChildren: SchemaVerificationNode[] = [];
338
- const contractTables = contract.storage.tables;
339
353
  const schemaTables = schema.tables;
354
+ const namespaceIds = Object.keys(contract.storage.namespaces).sort((a, b) =>
355
+ a < b ? -1 : a > b ? 1 : 0,
356
+ );
340
357
 
341
- for (const [tableName, contractTable] of Object.entries(contractTables)) {
342
- const schemaTable = schemaTables[tableName];
343
- const tablePath = `storage.tables.${tableName}`;
358
+ for (const namespaceId of namespaceIds) {
359
+ const ns = contract.storage.namespaces[namespaceId];
360
+ if (!ns) continue;
361
+ for (const [tableName, contractTableRaw] of Object.entries(ns.tables)) {
362
+ if (!(contractTableRaw instanceof StorageTable)) {
363
+ throw new Error(
364
+ `verifySqlSchema: expected StorageTable at storage.namespaces.${namespaceId}.tables.${tableName}`,
365
+ );
366
+ }
367
+ const contractTable = contractTableRaw;
368
+ const schemaTable = schemaTables[tableName];
369
+ const tablePath = `storage.namespaces.${namespaceId}.tables.${tableName}`;
344
370
 
345
- if (!schemaTable) {
346
- issues.push({
347
- kind: 'missing_table',
348
- table: tableName,
349
- message: `Table "${tableName}" is missing from database`,
350
- });
351
- rootChildren.push({
352
- status: 'fail',
353
- kind: 'table',
354
- name: `table ${tableName}`,
355
- contractPath: tablePath,
356
- code: 'missing_table',
357
- message: `Table "${tableName}" is missing`,
358
- expected: undefined,
359
- actual: undefined,
360
- children: [],
371
+ if (!schemaTable) {
372
+ issues.push({
373
+ kind: 'missing_table',
374
+ table: tableName,
375
+ namespaceId,
376
+ message: `Table "${tableName}" is missing from database`,
377
+ });
378
+ rootChildren.push({
379
+ status: 'fail',
380
+ kind: 'table',
381
+ name: `table ${tableName}`,
382
+ contractPath: tablePath,
383
+ code: 'missing_table',
384
+ message: `Table "${tableName}" is missing`,
385
+ expected: undefined,
386
+ actual: undefined,
387
+ children: [],
388
+ });
389
+ continue;
390
+ }
391
+
392
+ const tableChildren = verifyTableChildren({
393
+ contractTable,
394
+ schemaTable,
395
+ tableName,
396
+ namespaceId,
397
+ tablePath,
398
+ issues,
399
+ strict,
400
+ typeMetadataRegistry,
401
+ codecHooks,
402
+ storageTypes,
403
+ ...ifDefined('normalizeDefault', normalizeDefault),
404
+ ...ifDefined('normalizeNativeType', normalizeNativeType),
361
405
  });
362
- continue;
406
+ rootChildren.push(buildTableNode(tableName, tablePath, tableChildren));
363
407
  }
364
-
365
- const tableChildren = verifyTableChildren({
366
- contractTable,
367
- schemaTable,
368
- tableName,
369
- tablePath,
370
- issues,
371
- strict,
372
- typeMetadataRegistry,
373
- codecHooks,
374
- storageTypes,
375
- ...ifDefined('normalizeDefault', normalizeDefault),
376
- ...ifDefined('normalizeNativeType', normalizeNativeType),
377
- });
378
- rootChildren.push(buildTableNode(tableName, tablePath, tableChildren));
379
408
  }
380
409
 
381
410
  if (strict) {
382
411
  for (const tableName of Object.keys(schemaTables)) {
383
- if (!contractTables[tableName]) {
412
+ const claimed = namespaceIds.some(
413
+ (namespaceId) => contract.storage.namespaces[namespaceId]?.tables[tableName] !== undefined,
414
+ );
415
+ if (!claimed) {
416
+ // `namespaceId` is intentionally absent: an extra table exists in the
417
+ // live database but is not claimed by any contract namespace, so there
418
+ // is no contract coordinate to stamp here. Planners that consume this
419
+ // issue must handle the unstamped case (drop / quarantine by name).
384
420
  issues.push({
385
421
  kind: 'extra_table',
386
422
  table: tableName,
@@ -390,7 +426,7 @@ function verifySchemaTables(options: {
390
426
  status: 'fail',
391
427
  kind: 'table',
392
428
  name: `table ${tableName}`,
393
- contractPath: `storage.tables.${tableName}`,
429
+ contractPath: `storage.namespaces.*.tables.${tableName}`,
394
430
  code: 'extra_table',
395
431
  message: `Extra table "${tableName}" found`,
396
432
  expected: undefined,
@@ -405,9 +441,10 @@ function verifySchemaTables(options: {
405
441
  }
406
442
 
407
443
  function verifyTableChildren(options: {
408
- contractTable: Contract<SqlStorage>['storage']['tables'][string];
444
+ contractTable: StorageTable;
409
445
  schemaTable: SqlSchemaIR['tables'][string];
410
446
  tableName: string;
447
+ namespaceId: string;
411
448
  tablePath: string;
412
449
  issues: SchemaIssue[];
413
450
  strict: boolean;
@@ -421,6 +458,7 @@ function verifyTableChildren(options: {
421
458
  contractTable,
422
459
  schemaTable,
423
460
  tableName,
461
+ namespaceId,
424
462
  tablePath,
425
463
  issues,
426
464
  strict,
@@ -435,6 +473,7 @@ function verifyTableChildren(options: {
435
473
  contractTable,
436
474
  schemaTable,
437
475
  tableName,
476
+ namespaceId,
438
477
  tablePath,
439
478
  issues,
440
479
  strict,
@@ -452,6 +491,7 @@ function verifyTableChildren(options: {
452
491
  contractTable,
453
492
  schemaTable,
454
493
  tableName,
494
+ namespaceId,
455
495
  tablePath,
456
496
  issues,
457
497
  columnNodes,
@@ -463,6 +503,7 @@ function verifyTableChildren(options: {
463
503
  contractTable.primaryKey,
464
504
  schemaTable.primaryKey,
465
505
  tableName,
506
+ namespaceId,
466
507
  issues,
467
508
  );
468
509
  if (pkStatus === 'fail') {
@@ -494,6 +535,7 @@ function verifyTableChildren(options: {
494
535
  issues.push({
495
536
  kind: 'extra_primary_key',
496
537
  table: tableName,
538
+ namespaceId,
497
539
  message: 'Extra primary key found in database (not in contract)',
498
540
  });
499
541
  tableChildren.push({
@@ -518,6 +560,7 @@ function verifyTableChildren(options: {
518
560
  constraintFks,
519
561
  schemaTable.foreignKeys,
520
562
  tableName,
563
+ namespaceId,
521
564
  tablePath,
522
565
  issues,
523
566
  strict,
@@ -530,6 +573,7 @@ function verifyTableChildren(options: {
530
573
  schemaTable.uniques,
531
574
  schemaTable.indexes,
532
575
  tableName,
576
+ namespaceId,
533
577
  tablePath,
534
578
  issues,
535
579
  strict,
@@ -543,9 +587,9 @@ function verifyTableChildren(options: {
543
587
  .filter(
544
588
  (fk) =>
545
589
  fk.index === true &&
546
- !contractTable.indexes.some((idx) => arraysEqual(idx.columns, fk.columns)),
590
+ !contractTable.indexes.some((idx) => arraysEqual(idx.columns, fk.source.columns)),
547
591
  )
548
- .map((fk) => ({ columns: fk.columns }));
592
+ .map((fk) => ({ columns: fk.source.columns }));
549
593
  const allExpectedIndexes = [...contractTable.indexes, ...fkBackingIndexes];
550
594
 
551
595
  const indexStatuses = verifyIndexes(
@@ -553,6 +597,7 @@ function verifyTableChildren(options: {
553
597
  schemaTable.indexes,
554
598
  schemaTable.uniques,
555
599
  tableName,
600
+ namespaceId,
556
601
  tablePath,
557
602
  issues,
558
603
  strict,
@@ -563,9 +608,10 @@ function verifyTableChildren(options: {
563
608
  }
564
609
 
565
610
  function collectContractColumnNodes(options: {
566
- contractTable: Contract<SqlStorage>['storage']['tables'][string];
611
+ contractTable: StorageTable;
567
612
  schemaTable: SqlSchemaIR['tables'][string];
568
613
  tableName: string;
614
+ namespaceId: string;
569
615
  tablePath: string;
570
616
  issues: SchemaIssue[];
571
617
  strict: boolean;
@@ -579,6 +625,7 @@ function collectContractColumnNodes(options: {
579
625
  contractTable,
580
626
  schemaTable,
581
627
  tableName,
628
+ namespaceId,
582
629
  tablePath,
583
630
  issues,
584
631
  strict,
@@ -598,6 +645,7 @@ function collectContractColumnNodes(options: {
598
645
  issues.push({
599
646
  kind: 'missing_column',
600
647
  table: tableName,
648
+ namespaceId,
601
649
  column: columnName,
602
650
  message: `Column "${tableName}"."${columnName}" is missing from database`,
603
651
  });
@@ -618,6 +666,7 @@ function collectContractColumnNodes(options: {
618
666
  columnNodes.push(
619
667
  verifyColumn({
620
668
  tableName,
669
+ namespaceId,
621
670
  columnName,
622
671
  contractColumn,
623
672
  schemaColumn,
@@ -637,19 +686,22 @@ function collectContractColumnNodes(options: {
637
686
  }
638
687
 
639
688
  function appendExtraColumnNodes(options: {
640
- contractTable: Contract<SqlStorage>['storage']['tables'][string];
689
+ contractTable: StorageTable;
641
690
  schemaTable: SqlSchemaIR['tables'][string];
642
691
  tableName: string;
692
+ namespaceId: string;
643
693
  tablePath: string;
644
694
  issues: SchemaIssue[];
645
695
  columnNodes: SchemaVerificationNode[];
646
696
  }): void {
647
- const { contractTable, schemaTable, tableName, tablePath, issues, columnNodes } = options;
697
+ const { contractTable, schemaTable, tableName, namespaceId, tablePath, issues, columnNodes } =
698
+ options;
648
699
  for (const [columnName, { nativeType }] of Object.entries(schemaTable.columns)) {
649
700
  if (!contractTable.columns[columnName]) {
650
701
  issues.push({
651
702
  kind: 'extra_column',
652
703
  table: tableName,
704
+ namespaceId,
653
705
  column: columnName,
654
706
  message: `Extra column "${tableName}"."${columnName}" found in database (not in contract)`,
655
707
  });
@@ -670,8 +722,9 @@ function appendExtraColumnNodes(options: {
670
722
 
671
723
  function verifyColumn(options: {
672
724
  tableName: string;
725
+ namespaceId: string;
673
726
  columnName: string;
674
- contractColumn: Contract<SqlStorage>['storage']['tables'][string]['columns'][string];
727
+ contractColumn: StorageTable['columns'][string];
675
728
  schemaColumn: SqlSchemaIR['tables'][string]['columns'][string];
676
729
  columnPath: string;
677
730
  issues: SchemaIssue[];
@@ -684,6 +737,7 @@ function verifyColumn(options: {
684
737
  }): SchemaVerificationNode {
685
738
  const {
686
739
  tableName,
740
+ namespaceId,
687
741
  columnName,
688
742
  contractColumn,
689
743
  schemaColumn,
@@ -713,6 +767,7 @@ function verifyColumn(options: {
713
767
  issues.push({
714
768
  kind: 'type_mismatch',
715
769
  table: tableName,
770
+ namespaceId,
716
771
  column: columnName,
717
772
  expected: contractNativeType,
718
773
  actual: schemaNativeType,
@@ -768,6 +823,7 @@ function verifyColumn(options: {
768
823
  issues.push({
769
824
  kind: 'nullability_mismatch',
770
825
  table: tableName,
826
+ namespaceId,
771
827
  column: columnName,
772
828
  expected: String(contractColumn.nullable),
773
829
  actual: String(schemaColumn.nullable),
@@ -793,6 +849,7 @@ function verifyColumn(options: {
793
849
  issues.push({
794
850
  kind: 'default_missing',
795
851
  table: tableName,
852
+ namespaceId,
796
853
  column: columnName,
797
854
  expected: defaultDescription,
798
855
  message: `Column "${tableName}"."${columnName}" should have default ${defaultDescription} but database has no default`,
@@ -823,6 +880,7 @@ function verifyColumn(options: {
823
880
  issues.push({
824
881
  kind: 'default_mismatch',
825
882
  table: tableName,
883
+ namespaceId,
826
884
  column: columnName,
827
885
  expected: expectedDescription,
828
886
  actual: actualDescription,
@@ -845,6 +903,7 @@ function verifyColumn(options: {
845
903
  issues.push({
846
904
  kind: 'extra_default',
847
905
  table: tableName,
906
+ namespaceId,
848
907
  column: columnName,
849
908
  actual: schemaColumn.default,
850
909
  message: `Column "${tableName}"."${columnName}" has default ${schemaColumn.default} in database but contract specifies no default`,
@@ -1020,7 +1079,7 @@ function validateFrameworkComponentsForExtensions(
1020
1079
  * target-specific adapters (like Postgres) to provide their own expansion logic.
1021
1080
  */
1022
1081
  function renderExpectedNativeType(
1023
- contractColumn: Contract<SqlStorage>['storage']['tables'][string]['columns'][string],
1082
+ contractColumn: StorageColumn,
1024
1083
  storageTypes: Readonly<Record<string, StorageTypeInstance | PostgresEnumStorageEntry>>,
1025
1084
  codecHooks: Map<string, CodecControlHooks>,
1026
1085
  context?: {
@@ -1 +0,0 @@
1
- {"version":3,"file":"sql-contract-serializer-qUQCnP-k.mjs","names":[],"sources":["../src/core/ir/sql-contract-serializer-base.ts","../src/core/ir/sql-contract-serializer.ts"],"sourcesContent":["import type { Contract } from '@prisma-next/contract/types';\nimport type { ContractSerializer } from '@prisma-next/framework-components/control';\nimport { SqlStorage, type SqlStorageTypeEntry } from '@prisma-next/sql-contract/types';\nimport { validateSqlContractFully } from '@prisma-next/sql-contract/validators';\nimport type { JsonObject } from '@prisma-next/utils/json';\n\nexport type SqlEntityHydrationFactory = (entry: unknown) => SqlStorageTypeEntry;\n\n/**\n * SQL family `ContractSerializer` abstract base. Carries the SQL-shared\n * deserialization pipeline:\n *\n * 1. `parseSqlContractStructure` validates the on-disk JSON envelope\n * against the SQL contract arktype schema (`validateSqlContractFully`)\n * and returns the validated flat-data shape.\n * 2. `hydrateSqlStorage` walks the validated `storage` subtree and\n * constructs the family-shared SQL Contract IR class hierarchy\n * (`SqlStorage` -> `StorageTable` -> `StorageColumn` / `PrimaryKey`\n * / …). The rest of the contract envelope is JSON-clean primitive\n * data and passes through unchanged.\n * 3. `constructTargetContract` is the target-specific extension hook;\n * defaults to identity. Targets that need to attach target-only\n * fields (e.g. target-specific derived storage fields) override it.\n *\n * Default `serializeContract` is identity over the contract — concrete\n * SQL targets ship JSON-clean class instances, so the contract value\n * can be stringified directly. The non-enumerable family-level `kind`\n * discriminator on `SqlNode` instances stays out of the persisted\n * envelope automatically. Targets that need to canonicalize on the way\n * out (key ordering, dropping computed-only fields) override\n * `serializeContract` directly.\n */\nexport abstract class SqlContractSerializerBase<TContract extends Contract<SqlStorage>>\n implements ContractSerializer<TContract>\n{\n constructor(\n private readonly entityTypeRegistry: ReadonlyMap<string, SqlEntityHydrationFactory>,\n ) {}\n\n deserializeContract(json: unknown): TContract {\n const validated = this.parseSqlContractStructure(json);\n const hydrated = this.hydrateSqlStorage(validated);\n return this.constructTargetContract(hydrated);\n }\n\n serializeContract(contract: TContract): JsonObject {\n // Targets that ship enumerable runtime-only fields must override\n // this method (mirroring `MongoTargetContractSerializer.serializeContract`)\n // to construct the persisted envelope explicitly; the default identity\n // works only when every enumerable own property belongs in the JSON.\n return contract as unknown as JsonObject;\n }\n\n /**\n * Family-shared validation pipeline (delegates to\n * `validateSqlContractFully` in `@prisma-next/sql-contract/validators`):\n * structural arktype + framework-shared domain + SQL storage\n * logical-consistency + SQL storage semantic + model ↔ storage\n * reference checks. Subclasses override to add target-specific\n * structural checks before hydration; the family default suffices\n * for targets whose contract shape is the SQL-shared shape\n * (Postgres, SQLite today).\n */\n protected parseSqlContractStructure(json: unknown): Contract<SqlStorage> {\n return validateSqlContractFully<Contract<SqlStorage>>(json);\n }\n\n /**\n * Family-shared hydration walker. Lifts the validated flat-data\n * `storage` subtree into the SQL Contract IR class hierarchy by\n * constructing a single `SqlStorage` instance — its constructor\n * cascades nested instantiation of `StorageTable`, `StorageColumn`,\n * `PrimaryKey`, `UniqueConstraint`, `Index`, `ForeignKey`,\n * `ForeignKeyReferences`, and `StorageTypeInstance`. The rest of the\n * contract envelope (target identity, hashes, capabilities, models,\n * meta, …) is JSON-clean primitive data and passes through unchanged.\n *\n * Polymorphic `storage.types` entries are normalised before the\n * `SqlStorage` constructor runs: when an entry carries an enumerable\n * string `kind`, the serializer looks up a pack-registered hydration\n * factory for that discriminator and delegates reconstruction. Entries\n * with no registered factory pass through unchanged (codec-typed JSON\n * stays codec-typed until `SqlStorage` normalises it).\n */\n protected hydrateSqlStorage(validated: Contract<SqlStorage>): Contract<SqlStorage> {\n const types = validated.storage.types;\n const hydratedTypes =\n types !== undefined\n ? Object.fromEntries(\n Object.entries(types).map(([name, entry]) => [\n name,\n this.hydrateStorageTypeEntry(entry),\n ]),\n )\n : undefined;\n\n return {\n ...validated,\n storage: new SqlStorage({\n ...validated.storage,\n ...(hydratedTypes !== undefined ? { types: hydratedTypes } : {}),\n }),\n };\n }\n\n /**\n * Per-entry hydration dispatcher for `storage.types`. When `kind` is a\n * string and the constructor registry supplies a factory for that key,\n * the factory returns the hydrated `SqlStorageTypeEntry`. Otherwise the\n * entry passes through unchanged for `SqlStorage` to normalise.\n */\n protected hydrateStorageTypeEntry(entry: SqlStorageTypeEntry): SqlStorageTypeEntry {\n if (typeof entry !== 'object' || entry === null) {\n return entry;\n }\n const kind = (entry as { kind?: unknown }).kind;\n if (typeof kind !== 'string') {\n return entry;\n }\n const factory = this.entityTypeRegistry.get(kind);\n if (factory === undefined) {\n return entry;\n }\n return factory(entry);\n }\n\n /**\n * Target-specific construction hook. Defaults to identity; targets\n * that need to wrap the hydrated contract (e.g. attach target-only\n * derived fields, narrow the contract type to a target-specific\n * subtype) override.\n */\n protected constructTargetContract(hydrated: Contract<SqlStorage>): TContract {\n return hydrated as TContract;\n }\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport { SqlContractSerializerBase } from './sql-contract-serializer-base';\n\n/**\n * Default SQL family `ContractSerializer` concretion. Inherits the\n * full SQL-shared deserialization pipeline (structural validation +\n * IR-class hydration) without pack-registered `storage.types`\n * hydration factories — targets that emit polymorphic JSON outside the\n * codec-typed envelope wire a target-specific subclass with a populated\n * registry (see Postgres). Family-level call sites instantiate this\n * default directly when no target serializer is supplied.\n */\nexport class SqlContractSerializer extends SqlContractSerializerBase<Contract<SqlStorage>> {\n constructor() {\n super(new Map());\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAsB,4BAAtB,MAEA;CAEqB;CADnB,YACE,oBACA;EADiB,KAAA,qBAAA;;CAGnB,oBAAoB,MAA0B;EAC5C,MAAM,YAAY,KAAK,0BAA0B,KAAK;EACtD,MAAM,WAAW,KAAK,kBAAkB,UAAU;EAClD,OAAO,KAAK,wBAAwB,SAAS;;CAG/C,kBAAkB,UAAiC;EAKjD,OAAO;;;;;;;;;;;;CAaT,0BAAoC,MAAqC;EACvE,OAAO,yBAA+C,KAAK;;;;;;;;;;;;;;;;;;;CAoB7D,kBAA4B,WAAuD;EACjF,MAAM,QAAQ,UAAU,QAAQ;EAChC,MAAM,gBACJ,UAAU,KAAA,IACN,OAAO,YACL,OAAO,QAAQ,MAAM,CAAC,KAAK,CAAC,MAAM,WAAW,CAC3C,MACA,KAAK,wBAAwB,MAAM,CACpC,CAAC,CACH,GACD,KAAA;EAEN,OAAO;GACL,GAAG;GACH,SAAS,IAAI,WAAW;IACtB,GAAG,UAAU;IACb,GAAI,kBAAkB,KAAA,IAAY,EAAE,OAAO,eAAe,GAAG,EAAE;IAChE,CAAC;GACH;;;;;;;;CASH,wBAAkC,OAAiD;EACjF,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO;EAET,MAAM,OAAQ,MAA6B;EAC3C,IAAI,OAAO,SAAS,UAClB,OAAO;EAET,MAAM,UAAU,KAAK,mBAAmB,IAAI,KAAK;EACjD,IAAI,YAAY,KAAA,GACd,OAAO;EAET,OAAO,QAAQ,MAAM;;;;;;;;CASvB,wBAAkC,UAA2C;EAC3E,OAAO;;;;;;;;;;;;;;ACxHX,IAAa,wBAAb,cAA2C,0BAAgD;CACzF,cAAc;EACZ,sBAAM,IAAI,KAAK,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types-DMINfGUO.d.mts","names":[],"sources":["../src/core/control-instance.ts","../src/core/migrations/types.ts"],"mappings":"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"}