@prisma-next/sql-contract-ts 0.11.0-dev.9 → 0.12.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.
package/package.json CHANGED
@@ -1,31 +1,39 @@
1
1
  {
2
2
  "name": "@prisma-next/sql-contract-ts",
3
- "version": "0.11.0-dev.9",
3
+ "version": "0.12.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "SQL-specific TypeScript contract authoring surface for Prisma Next",
8
8
  "dependencies": {
9
- "@prisma-next/config": "0.11.0-dev.9",
10
- "@prisma-next/contract": "0.11.0-dev.9",
11
- "@prisma-next/contract-authoring": "0.11.0-dev.9",
12
- "@prisma-next/framework-components": "0.11.0-dev.9",
13
- "@prisma-next/sql-contract": "0.11.0-dev.9",
14
- "@prisma-next/utils": "0.11.0-dev.9",
9
+ "@prisma-next/config": "0.12.0",
10
+ "@prisma-next/contract": "0.12.0",
11
+ "@prisma-next/contract-authoring": "0.12.0",
12
+ "@prisma-next/framework-components": "0.12.0",
13
+ "@prisma-next/sql-contract": "0.12.0",
14
+ "@prisma-next/utils": "0.12.0",
15
15
  "arktype": "^2.2.0",
16
16
  "pathe": "^2.0.3",
17
17
  "ts-toolbelt": "^9.6.0"
18
18
  },
19
19
  "devDependencies": {
20
- "@prisma-next/test-utils": "0.11.0-dev.9",
21
- "@prisma-next/tsconfig": "0.11.0-dev.9",
20
+ "@prisma-next/test-utils": "0.12.0",
21
+ "@prisma-next/tsconfig": "0.12.0",
22
22
  "@types/pg": "8.20.0",
23
23
  "pg": "8.20.0",
24
- "@prisma-next/tsdown": "0.11.0-dev.9",
24
+ "@prisma-next/tsdown": "0.12.0",
25
25
  "tsdown": "0.22.0",
26
26
  "typescript": "5.9.3",
27
27
  "vitest": "4.1.6"
28
28
  },
29
+ "peerDependencies": {
30
+ "typescript": ">=5.9"
31
+ },
32
+ "peerDependenciesMeta": {
33
+ "typescript": {
34
+ "optional": true
35
+ }
36
+ },
29
37
  "files": [
30
38
  "dist",
31
39
  "src",
@@ -36,6 +44,9 @@
36
44
  "./contract-builder": "./dist/contract-builder.mjs",
37
45
  "./package.json": "./package.json"
38
46
  },
47
+ "engines": {
48
+ "node": ">=24"
49
+ },
39
50
  "repository": {
40
51
  "type": "git",
41
52
  "url": "https://github.com/prisma/prisma-next.git",
@@ -4,6 +4,7 @@ import {
4
4
  computeStorageHash,
5
5
  } from '@prisma-next/contract/hashing';
6
6
  import {
7
+ asNamespaceId,
7
8
  type ColumnDefault,
8
9
  type ColumnDefaultLiteralInputValue,
9
10
  type Contract,
@@ -11,13 +12,17 @@ import {
11
12
  type ContractModel,
12
13
  type ContractRelation,
13
14
  type ContractValueObject,
15
+ type CrossReference,
14
16
  coreHash,
17
+ crossRef,
15
18
  type ExecutionMutationDefault,
16
19
  type JsonValue,
17
20
  type StorageHashBase,
18
21
  } from '@prisma-next/contract/types';
22
+ import { type CapabilityMatrix, mergeCapabilityMatrices } from '@prisma-next/contract-authoring';
19
23
  import type { CodecLookup } from '@prisma-next/framework-components/codec';
20
- import { type Namespace, UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
24
+ import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
25
+ import { sqlContractCanonicalizationHooks } from '@prisma-next/sql-contract/canonicalization-hooks';
21
26
  import { validateIndexTypes } from '@prisma-next/sql-contract/index-type-validation';
22
27
  import {
23
28
  createIndexTypeRegistry,
@@ -26,6 +31,7 @@ import {
26
31
  } from '@prisma-next/sql-contract/index-types';
27
32
  import {
28
33
  applyFkDefaults,
34
+ buildSqlNamespace,
29
35
  isPostgresEnumStorageEntry,
30
36
  type PostgresEnumStorageEntry,
31
37
  type SqlNamespaceTablesInput,
@@ -38,6 +44,7 @@ import {
38
44
  toStorageTypeInstance,
39
45
  } from '@prisma-next/sql-contract/types';
40
46
  import { validateStorageSemantics } from '@prisma-next/sql-contract/validators';
47
+ import { blindCast } from '@prisma-next/utils/casts';
41
48
  import { ifDefined } from '@prisma-next/utils/defined';
42
49
  import type {
43
50
  ContractDefinition,
@@ -146,6 +153,17 @@ function isValueObjectField(
146
153
  const JSONB_CODEC_ID = 'pg/jsonb@1';
147
154
  const JSONB_NATIVE_TYPE = 'jsonb';
148
155
 
156
+ function resolveModelNamespaceId(
157
+ model: ModelNode,
158
+ modelNameToNamespaceId: ReadonlyMap<string, string>,
159
+ targetId: string,
160
+ ): string {
161
+ if (model.namespaceId !== undefined && model.namespaceId.length > 0) {
162
+ return model.namespaceId;
163
+ }
164
+ return modelNameToNamespaceId.get(model.modelName) ?? defaultModelNamespaceId(targetId);
165
+ }
166
+
149
167
  function buildStorageColumn(
150
168
  field: FieldNode | ValueObjectFieldNode,
151
169
  codecLookup?: CodecLookup,
@@ -213,7 +231,7 @@ function buildDomainField(
213
231
 
214
232
  function collectStorageNamespaceCoordinateIds(definition: ContractDefinition): Set<string> {
215
233
  const ids = new Set<string>();
216
- ids.add(UNBOUND_NAMESPACE_ID);
234
+ ids.add(defaultModelNamespaceId(definition.target.targetId));
217
235
  for (const id of definition.namespaces ?? []) {
218
236
  if (id.length > 0) {
219
237
  ids.add(id);
@@ -228,6 +246,11 @@ function collectStorageNamespaceCoordinateIds(definition: ContractDefinition): S
228
246
  }
229
247
 
230
248
  const POSTGRES_ENUM_NAMESPACE_ID = 'public';
249
+ const POSTGRES_DEFAULT_NAMESPACE_ID = 'public';
250
+
251
+ function defaultModelNamespaceId(targetId: string): string {
252
+ return targetId === 'postgres' ? POSTGRES_DEFAULT_NAMESPACE_ID : UNBOUND_NAMESPACE_ID;
253
+ }
231
254
 
232
255
  function partitionStorageTypesForTarget(
233
256
  targetId: string,
@@ -286,13 +309,19 @@ export function buildSqlContractFromDefinition(
286
309
 
287
310
  const tablesByNamespace: Record<string, Record<string, StorageTable>> = {};
288
311
  const tableNameToNamespaceId = new Map<string, string>();
312
+ const modelNameToNamespaceId = new Map<string, string>();
289
313
  const executionDefaults: ExecutionMutationDefault[] = [];
290
- const models: Record<string, ContractModel> = {};
291
- const roots: Record<string, string> = {};
314
+ const modelsByNamespace: Record<string, Record<string, ContractModel>> = {};
315
+ const roots: Record<string, CrossReference> = {};
292
316
 
293
317
  for (const semanticModel of definition.models) {
294
318
  const tableName = semanticModel.tableName;
295
- roots[tableName] = semanticModel.modelName;
319
+ const namespaceId =
320
+ semanticModel.namespaceId !== undefined && semanticModel.namespaceId.length > 0
321
+ ? semanticModel.namespaceId
322
+ : defaultModelNamespaceId(target);
323
+ modelNameToNamespaceId.set(semanticModel.modelName, namespaceId);
324
+ roots[tableName] = crossRef(semanticModel.modelName, namespaceId);
296
325
 
297
326
  // --- Build storage table ---
298
327
 
@@ -344,11 +373,6 @@ export function buildSqlContractFromDefinition(
344
373
  }
345
374
  }
346
375
 
347
- const namespaceId =
348
- semanticModel.namespaceId !== undefined && semanticModel.namespaceId.length > 0
349
- ? semanticModel.namespaceId
350
- : UNBOUND_NAMESPACE_ID;
351
-
352
376
  const foreignKeys = (semanticModel.foreignKeys ?? []).map((fk) => {
353
377
  const targetModel = assertKnownTargetModel(
354
378
  modelsByName,
@@ -366,11 +390,11 @@ export function buildSqlContractFromDefinition(
366
390
  fk.references.namespaceId ??
367
391
  (targetModel.namespaceId !== undefined && targetModel.namespaceId.length > 0
368
392
  ? targetModel.namespaceId
369
- : UNBOUND_NAMESPACE_ID);
393
+ : defaultModelNamespaceId(target));
370
394
  return {
371
- source: { namespaceId, tableName, columns: fk.columns },
395
+ source: { namespaceId: asNamespaceId(namespaceId), tableName, columns: fk.columns },
372
396
  target: {
373
- namespaceId: targetNamespaceId,
397
+ namespaceId: asNamespaceId(targetNamespaceId),
374
398
  tableName: fk.references.table,
375
399
  columns: fk.references.columns,
376
400
  },
@@ -461,7 +485,10 @@ export function buildSqlContractFromDefinition(
461
485
  );
462
486
 
463
487
  modelRelations[relation.fieldName] = {
464
- to: relation.toModel,
488
+ to: crossRef(
489
+ relation.toModel,
490
+ resolveModelNamespaceId(targetModel, modelNameToNamespaceId, target),
491
+ ),
465
492
  // RelationDefinition.cardinality includes 'N:M' which isn't in
466
493
  // ContractReferenceRelation yet — cast is needed until the contract
467
494
  // type is extended to cover many-to-many.
@@ -482,7 +509,12 @@ export function buildSqlContractFromDefinition(
482
509
  };
483
510
  }
484
511
 
485
- models[semanticModel.modelName] = {
512
+ let namespaceModels = modelsByNamespace[namespaceId];
513
+ if (namespaceModels === undefined) {
514
+ namespaceModels = {};
515
+ modelsByNamespace[namespaceId] = namespaceModels;
516
+ }
517
+ namespaceModels[semanticModel.modelName] = {
486
518
  storage: {
487
519
  table: tableName,
488
520
  fields: storageFields,
@@ -526,19 +558,24 @@ export function buildSqlContractFromDefinition(
526
558
  namespaceCoordinateIds.add(id);
527
559
  }
528
560
  const { createNamespace } = definition;
529
- const namespaces: Record<string, SqlNamespaceTablesInput | Namespace> = Object.fromEntries(
530
- [...namespaceCoordinateIds].sort().map((id) => {
531
- const enumTypes = namespaceEnumTypesById[id];
532
- const nsInput: SqlNamespaceTablesInput = {
533
- id,
534
- tables: tablesByNamespace[id] ?? {},
535
- ...ifDefined('enum', enumTypes),
536
- };
537
- return [id, createNamespace ? createNamespace(nsInput) : nsInput];
538
- }),
561
+ const namespaces = blindCast<
562
+ SqlStorageInput['namespaces'],
563
+ 'contract authoring materialises each namespace coordinate from the model set and explicit namespace list'
564
+ >(
565
+ Object.fromEntries(
566
+ [...namespaceCoordinateIds].sort().map((id) => {
567
+ const enumTypes = namespaceEnumTypesById[id];
568
+ const nsInput: SqlNamespaceTablesInput = {
569
+ id,
570
+ tables: tablesByNamespace[id] ?? {},
571
+ ...ifDefined('enum', enumTypes),
572
+ };
573
+ return [id, createNamespace ? createNamespace(nsInput) : buildSqlNamespace(nsInput)];
574
+ }),
575
+ ),
539
576
  );
540
577
  const storageWithoutHash = {
541
- types: documentTypes,
578
+ ...(Object.keys(documentTypes).length > 0 ? { types: documentTypes } : {}),
542
579
  namespaces,
543
580
  };
544
581
  const storageHash: StorageHashBase<string> = definition.storageHash
@@ -547,8 +584,9 @@ export function buildSqlContractFromDefinition(
547
584
  target,
548
585
  targetFamily,
549
586
  storage: storageWithoutHash as Record<string, unknown>,
587
+ ...sqlContractCanonicalizationHooks,
550
588
  });
551
- const storage = new SqlStorage({ ...storageWithoutHash, storageHash } as SqlStorageInput); // Builder types are wider than SqlStorageInput until SqlStorage normalises document types.
589
+ const storage = new SqlStorage({ ...storageWithoutHash, storageHash });
552
590
 
553
591
  const executionSection =
554
592
  executionDefaults.length > 0
@@ -578,8 +616,24 @@ export function buildSqlContractFromDefinition(
578
616
  }
579
617
  }
580
618
 
581
- const capabilities: Record<string, Record<string, boolean>> = definition.capabilities || {};
582
- const profileHash = computeProfileHash({ target, targetFamily, capabilities });
619
+ const extensionPackCapabilitySources = definition.extensionPacks
620
+ ? Object.values(definition.extensionPacks).map(
621
+ (pack) => pack.capabilities as CapabilityMatrix | undefined,
622
+ )
623
+ : [];
624
+ const capabilities = mergeCapabilityMatrices(
625
+ definition.target.capabilities as CapabilityMatrix | undefined,
626
+ ...extensionPackCapabilitySources,
627
+ );
628
+ // Internal `profileHash` computation is unchanged from `origin/main`: it
629
+ // continues to fingerprint the author-declared capability subset. With
630
+ // `capabilities` removed from the `defineContract` input that subset is
631
+ // now always empty, so the hash naturally stabilises at `hash({})`.
632
+ const profileHash = computeProfileHash({
633
+ target,
634
+ targetFamily,
635
+ capabilities: {},
636
+ });
583
637
 
584
638
  const executionWithHash = executionSection
585
639
  ? {
@@ -618,14 +672,32 @@ export function buildSqlContractFromDefinition(
618
672
  )
619
673
  : undefined;
620
674
 
675
+ const defaultNamespaceId = defaultModelNamespaceId(target);
676
+ const domainNamespaceIds = new Set(Object.keys(modelsByNamespace));
677
+ if (domainNamespaceIds.size === 0) {
678
+ domainNamespaceIds.add(defaultNamespaceId);
679
+ }
680
+ if (valueObjects !== undefined) {
681
+ domainNamespaceIds.add(defaultNamespaceId);
682
+ }
683
+ const domainNamespaces = Object.fromEntries(
684
+ [...domainNamespaceIds].sort().map((namespaceId) => {
685
+ const modelsInNs = modelsByNamespace[namespaceId] ?? {};
686
+ const namespaceSlice =
687
+ namespaceId === defaultNamespaceId && valueObjects !== undefined
688
+ ? { models: modelsInNs, valueObjects }
689
+ : { models: modelsInNs };
690
+ return [namespaceId, namespaceSlice];
691
+ }),
692
+ );
693
+
621
694
  const contract: Contract<SqlStorage> = {
622
695
  target,
623
696
  targetFamily,
624
- models,
697
+ domain: { namespaces: domainNamespaces },
625
698
  roots,
626
699
  storage,
627
700
  ...(executionWithHash ? { execution: executionWithHash } : {}),
628
- ...ifDefined('valueObjects', valueObjects),
629
701
  extensionPacks,
630
702
  capabilities,
631
703
  profileHash,
@@ -1,9 +1,11 @@
1
1
  import { pathToFileURL } from 'node:url';
2
2
  import type { ContractConfig } from '@prisma-next/config/config-types';
3
3
  import type { Contract } from '@prisma-next/contract/types';
4
+ import type { TargetPackRef } from '@prisma-next/framework-components/components';
4
5
  import { ifDefined } from '@prisma-next/utils/defined';
5
6
  import { ok } from '@prisma-next/utils/result';
6
7
  import { extname } from 'pathe';
8
+ import { buildSqlContractFromDefinition } from './build-contract';
7
9
 
8
10
  /**
9
11
  * Derives the emit output path from the TS contract input so artefacts land
@@ -17,6 +19,18 @@ function defaultOutputFromContractPath(contractPath: string): string {
17
19
  return `${contractPath.slice(0, -ext.length)}.json`;
18
20
  }
19
21
 
22
+ export function emptyContract(options: {
23
+ readonly output?: string;
24
+ readonly target: TargetPackRef<'sql', string>;
25
+ }): ContractConfig {
26
+ return {
27
+ source: {
28
+ load: async () => ok(buildSqlContractFromDefinition({ target: options.target, models: [] })),
29
+ },
30
+ ...ifDefined('output', options.output),
31
+ };
32
+ }
33
+
20
34
  export function typescriptContract(contract: Contract, output?: string): ContractConfig {
21
35
  return {
22
36
  source: {
@@ -11,6 +11,7 @@ import type {
11
11
  SqlNamespaceTablesInput,
12
12
  StorageTypeInstance,
13
13
  } from '@prisma-next/sql-contract/types';
14
+ import { blindCast } from '@prisma-next/utils/casts';
14
15
  import { buildSqlContractFromDefinition } from './build-contract';
15
16
  import {
16
17
  type ComposedAuthoringHelpers,
@@ -53,7 +54,6 @@ type ContractDefinition<
53
54
  Types extends Record<string, StorageTypeInstance | PostgresEnumStorageEntry>,
54
55
  Models extends Record<string, ModelLike>,
55
56
  ExtensionPacks extends Record<string, ExtensionPackRef<'sql', string>> | undefined,
56
- Capabilities extends Record<string, Record<string, boolean>> | undefined,
57
57
  Naming extends ContractInput['naming'] | undefined,
58
58
  StorageHash extends string | undefined,
59
59
  ForeignKeyDefaults extends ForeignKeyDefaultsState | undefined,
@@ -65,7 +65,6 @@ type ContractDefinition<
65
65
  readonly naming?: Naming;
66
66
  readonly storageHash?: StorageHash;
67
67
  readonly foreignKeyDefaults?: ForeignKeyDefaults;
68
- readonly capabilities?: Capabilities;
69
68
  readonly namespaces?: Namespaces;
70
69
  readonly createNamespace?: (input: SqlNamespaceTablesInput) => Namespace;
71
70
  readonly types?: Types;
@@ -77,7 +76,6 @@ type ContractScaffold<
77
76
  Family extends FamilyPackRef<string>,
78
77
  Target extends TargetPackRef<'sql', string>,
79
78
  ExtensionPacks extends Record<string, ExtensionPackRef<'sql', string>> | undefined,
80
- Capabilities extends Record<string, Record<string, boolean>> | undefined,
81
79
  Naming extends ContractInput['naming'] | undefined,
82
80
  StorageHash extends string | undefined,
83
81
  ForeignKeyDefaults extends ForeignKeyDefaultsState | undefined,
@@ -89,9 +87,10 @@ type ContractScaffold<
89
87
  readonly naming?: Naming;
90
88
  readonly storageHash?: StorageHash;
91
89
  readonly foreignKeyDefaults?: ForeignKeyDefaults;
92
- readonly capabilities?: Capabilities;
93
90
  readonly namespaces?: Namespaces;
94
91
  readonly createNamespace?: (input: SqlNamespaceTablesInput) => Namespace;
92
+ readonly types?: never;
93
+ readonly models?: never;
95
94
  readonly codecLookup?: CodecLookup;
96
95
  };
97
96
 
@@ -274,11 +273,7 @@ function validateExtensionPackRefs(
274
273
 
275
274
  function buildContractFromDsl<Definition extends ContractInput>(
276
275
  definition: Definition,
277
- ): SqlContractResult<Definition>;
278
-
279
- function buildContractFromDsl(
280
- definition: ContractInput,
281
- ): ReturnType<typeof buildSqlContractFromDefinition> {
276
+ ): SqlContractResult<Definition> {
282
277
  validateTargetPackRef(definition.family, definition.target);
283
278
  validateExtensionPackRefs(definition.target, definition.extensionPacks);
284
279
  validateNamespaceDeclarations(definition.target, definition.namespaces);
@@ -288,10 +283,10 @@ function buildContractFromDsl(
288
283
  (definition.models ?? {}) as Record<string, ModelLike>,
289
284
  );
290
285
 
291
- return buildSqlContractFromDefinition(
292
- buildContractDefinition(definition),
293
- definition.codecLookup,
294
- );
286
+ return blindCast<
287
+ SqlContractResult<Definition>,
288
+ 'buildSqlContractFromDefinition return type is wide; SqlContractResult conditional resolves correctly at runtime for any concrete Definition'
289
+ >(buildSqlContractFromDefinition(buildContractDefinition(definition), definition.codecLookup));
295
290
  }
296
291
 
297
292
  export function defineContract<
@@ -305,7 +300,6 @@ export function defineContract<
305
300
  const ExtensionPacks extends
306
301
  | Record<string, ExtensionPackRef<'sql', string>>
307
302
  | undefined = undefined,
308
- const Capabilities extends Record<string, Record<string, boolean>> | undefined = undefined,
309
303
  const Naming extends ContractInput['naming'] | undefined = undefined,
310
304
  const StorageHash extends string | undefined = undefined,
311
305
  const ForeignKeyDefaults extends ForeignKeyDefaultsState | undefined = undefined,
@@ -317,7 +311,6 @@ export function defineContract<
317
311
  Types,
318
312
  Models,
319
313
  ExtensionPacks,
320
- Capabilities,
321
314
  Naming,
322
315
  StorageHash,
323
316
  ForeignKeyDefaults,
@@ -330,7 +323,6 @@ export function defineContract<
330
323
  Types,
331
324
  Models,
332
325
  ExtensionPacks,
333
- Capabilities,
334
326
  Naming,
335
327
  StorageHash,
336
328
  ForeignKeyDefaults,
@@ -348,7 +340,6 @@ export function defineContract<
348
340
  const ExtensionPacks extends
349
341
  | Record<string, ExtensionPackRef<'sql', string>>
350
342
  | undefined = undefined,
351
- const Capabilities extends Record<string, Record<string, boolean>> | undefined = undefined,
352
343
  const Naming extends ContractInput['naming'] | undefined = undefined,
353
344
  const StorageHash extends string | undefined = undefined,
354
345
  const ForeignKeyDefaults extends ForeignKeyDefaultsState | undefined = undefined,
@@ -358,7 +349,6 @@ export function defineContract<
358
349
  Family,
359
350
  Target,
360
351
  ExtensionPacks,
361
- Capabilities,
362
352
  Naming,
363
353
  StorageHash,
364
354
  ForeignKeyDefaults,
@@ -372,7 +362,6 @@ export function defineContract<
372
362
  Types,
373
363
  Models,
374
364
  ExtensionPacks,
375
- Capabilities,
376
365
  Naming,
377
366
  StorageHash,
378
367
  ForeignKeyDefaults,
@@ -118,7 +118,6 @@ export interface ModelNode {
118
118
  export interface ContractDefinition {
119
119
  readonly target: TargetPackRef<'sql', string>;
120
120
  readonly extensionPacks?: Record<string, ExtensionPackRef<'sql', string>>;
121
- readonly capabilities?: Record<string, Record<string, boolean>>;
122
121
  readonly storageHash?: string;
123
122
  readonly foreignKeyDefaults?: ForeignKeyDefaultsState;
124
123
  readonly storageTypes?: Record<string, StorageTypeInstance | PostgresEnumStorageEntry>;
@@ -1209,7 +1209,6 @@ export type ContractInput<
1209
1209
  >
1210
1210
  > = Record<never, never>,
1211
1211
  ExtensionPacks extends Record<string, ExtensionPackRef<'sql', string>> | undefined = undefined,
1212
- Capabilities extends Record<string, Record<string, boolean>> | undefined = undefined,
1213
1212
  > = {
1214
1213
  readonly family: Family;
1215
1214
  readonly target: Target;
@@ -1217,7 +1216,6 @@ export type ContractInput<
1217
1216
  readonly naming?: NamingConfig;
1218
1217
  readonly storageHash?: string;
1219
1218
  readonly foreignKeyDefaults?: ForeignKeyDefaultsState;
1220
- readonly capabilities?: Capabilities;
1221
1219
  /**
1222
1220
  * Declared namespace coordinates the contract recognises. Per-model
1223
1221
  * `namespace` references must reference an entry in this list (or the
@@ -708,7 +708,6 @@ export function buildContractDefinition(definition: ContractInput): ContractDefi
708
708
  return {
709
709
  target: definition.target,
710
710
  ...(definition.extensionPacks ? { extensionPacks: definition.extensionPacks } : {}),
711
- ...(definition.capabilities ? { capabilities: definition.capabilities } : {}),
712
711
  ...(definition.storageHash ? { storageHash: definition.storageHash } : {}),
713
712
  ...(definition.foreignKeyDefaults ? { foreignKeyDefaults: definition.foreignKeyDefaults } : {}),
714
713
  ...(Object.keys(collection.storageTypes).length > 0
@@ -2,6 +2,7 @@ import type {
2
2
  ColumnDefault,
3
3
  Contract,
4
4
  ContractRelation,
5
+ NamespaceId,
5
6
  StorageHashBase,
6
7
  } from '@prisma-next/contract/types';
7
8
  import type { ExtensionPackRef, TargetPackRef } from '@prisma-next/framework-components/components';
@@ -69,11 +70,35 @@ type DefinitionExtensionPacks<Definition> = Definition extends {
69
70
  ? Packs
70
71
  : Record<never, never>;
71
72
 
72
- type DefinitionCapabilities<Definition> = Definition extends {
73
- readonly capabilities?: infer Capabilities extends Record<string, Record<string, boolean>>;
73
+ type ExtractPackCapabilities<P> = P extends {
74
+ readonly capabilities?: infer Caps extends Record<string, Record<string, boolean>>;
74
75
  }
75
- ? Capabilities
76
- : undefined;
76
+ ? Caps
77
+ : never;
78
+
79
+ type MergeExtensionPackCapabilities<Packs> =
80
+ Packs extends Record<string, unknown>
81
+ ? keyof Packs extends never
82
+ ? Record<string, never>
83
+ : UnionToIntersection<
84
+ {
85
+ [K in keyof Packs]: ExtractPackCapabilities<Packs[K]>;
86
+ }[keyof Packs]
87
+ >
88
+ : Record<string, never>;
89
+
90
+ type Defaulted<T, Fallback> = [T] extends [never] ? Fallback : T;
91
+
92
+ // Build-time capability derivation no longer reads an author-declared
93
+ // `capabilities` block — that field was removed from the `defineContract`
94
+ // input. The build-time matrix is the merge of the target pack's caps and
95
+ // every extension pack's caps; adapter and driver caps are layered in by
96
+ // `enrichContract` at CLI emit time.
97
+ type DerivedCapabilities<Definition> = Defaulted<
98
+ ExtractPackCapabilities<DefinitionTarget<Definition>>,
99
+ Record<string, never>
100
+ > &
101
+ MergeExtensionPackCapabilities<DefinitionExtensionPacks<Definition>>;
77
102
 
78
103
  type DefinitionTargetId<Definition> = Definition extends {
79
104
  readonly target: TargetPackRef<'sql', infer Target>;
@@ -509,12 +534,12 @@ type BuiltStorageTables<Definition> = {
509
534
  readonly indexes: ReadonlyArray<Index>;
510
535
  readonly foreignKeys: ReadonlyArray<{
511
536
  readonly source: {
512
- readonly namespaceId: string;
537
+ readonly namespaceId: NamespaceId;
513
538
  readonly tableName: string;
514
539
  readonly columns: readonly string[];
515
540
  };
516
541
  readonly target: {
517
- readonly namespaceId: string;
542
+ readonly namespaceId: NamespaceId;
518
543
  readonly tableName: string;
519
544
  readonly columns: readonly string[];
520
545
  };
@@ -538,33 +563,48 @@ type BuiltStorageTables<Definition> = {
538
563
  : Record<string, never>);
539
564
  };
540
565
 
541
- type BuiltStorageTypes<Definition> = {
566
+ type BuiltDocumentScopedTypes<Definition> = {
542
567
  readonly [K in keyof DefinitionTypes<Definition> as DefinitionTypes<Definition>[K] extends PostgresEnumStorageEntry
543
568
  ? never
544
569
  : K]: DefinitionTypes<Definition>[K];
545
570
  };
546
571
 
572
+ type BuiltDomain<Definition> =
573
+ BuiltDocumentScopedTypes<Definition> extends Record<never, never>
574
+ ? Record<string, never>
575
+ : {
576
+ readonly __unbound__: {
577
+ readonly types: BuiltDocumentScopedTypes<Definition>;
578
+ };
579
+ };
580
+
581
+ type DefaultStorageNamespaceId<Definition> =
582
+ DefinitionTargetId<Definition> extends 'postgres' ? 'public' : '__unbound__';
583
+
547
584
  type BuiltStorage<Definition> = {
548
585
  readonly storageHash: StorageHashBase<string>;
549
- readonly types: BuiltStorageTypes<Definition>;
550
- // SQL contracts always carry a literal `__unbound__` namespace whose tables
551
- // slot is narrowed to the actual built table shape so downstream DSL
552
- // surfaces (TableProxyContract, Ref, SelectBuilder) keep literal-keyed
553
- // access without an optional-narrowing dance. The shape is described
554
- // inline (rather than intersecting with `SqlStorage['namespaces']`) so
555
- // its `Readonly<Record<string, Namespace>>` index signature doesn't
586
+ readonly types?: BuiltDocumentScopedTypes<Definition>;
587
+ // The primary namespace key is target-specific: Postgres uses `public` (the
588
+ // default schema), all other SQL targets use `__unbound__`. The namespace
589
+ // carries the narrowed tables shape so downstream DSL surfaces keep
590
+ // literal-keyed access without an optional-narrowing dance. The shape is
591
+ // described inline (rather than intersecting with `SqlStorage['namespaces']`)
592
+ // so its `Readonly<Record<string, Namespace>>` index signature doesn't
556
593
  // collapse `keyof tables` to `string`. The literal object is still
557
- // structurally assignable to `SqlStorage['namespaces']` because every
558
- // value satisfies the framework `Namespace` interface.
594
+ // structurally assignable to `SqlStorage['namespaces']` because every value
595
+ // satisfies the framework `Namespace` interface.
559
596
  readonly namespaces: {
560
- readonly __unbound__: {
561
- readonly id: '__unbound__';
597
+ readonly [K in DefaultStorageNamespaceId<Definition>]: {
598
+ readonly id: K;
562
599
  readonly kind: string;
563
600
  readonly tables: BuiltStorageTables<Definition>;
564
601
  readonly enum?: Readonly<Record<string, PostgresEnumStorageEntry>>;
565
602
  };
566
603
  } & {
567
- readonly [Ns in Exclude<DefinitionNamespaces<Definition>, '__unbound__'>]: {
604
+ readonly [Ns in Exclude<
605
+ DefinitionNamespaces<Definition>,
606
+ DefaultStorageNamespaceId<Definition>
607
+ >]: {
568
608
  readonly id: Ns;
569
609
  readonly kind: string;
570
610
  readonly tables: Record<never, never>;
@@ -604,16 +644,11 @@ export type SqlContractResult<Definition> = ContractWithTypeMaps<
604
644
  Contract<BuiltStorage<Definition>, BuiltModels<Definition>> & {
605
645
  readonly target: DefinitionTargetId<Definition>;
606
646
  readonly targetFamily: 'sql';
607
- } & {
647
+ } & { readonly domain: BuiltDomain<Definition> } & {
608
648
  readonly extensionPacks: keyof DefinitionExtensionPacks<Definition> extends never
609
649
  ? Record<string, never>
610
650
  : DefinitionExtensionPacks<Definition>;
611
- readonly capabilities: DefinitionCapabilities<Definition> extends Record<
612
- string,
613
- Record<string, boolean>
614
- >
615
- ? DefinitionCapabilities<Definition>
616
- : Record<string, Record<string, boolean>>;
651
+ readonly capabilities: DerivedCapabilities<Definition>;
617
652
  },
618
653
  TypeMaps<
619
654
  CodecTypesFromDefinition<Definition>,
@@ -1,2 +1,6 @@
1
1
  export type { ContractConfig } from '@prisma-next/config/config-types';
2
- export { typescriptContract, typescriptContractFromPath } from '../config-types';
2
+ export {
3
+ emptyContract,
4
+ typescriptContract,
5
+ typescriptContractFromPath,
6
+ } from '../config-types';