@prisma-next/contract 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.
@@ -35,9 +35,10 @@ function omitDefaults(obj, path) {
35
35
  if ((key === "onDelete" || key === "onUpdate") && value === "noAction") continue;
36
36
  if (isDefaultValue(value)) {
37
37
  const isRequiredModels = isArrayEqual(currentPath, ["models"]);
38
- const isRequiredTables = isArrayEqual(currentPath, ["storage", "tables"]);
39
- const isRequiredCollections = isArrayEqual(currentPath, ["storage", "collections"]);
40
- const isCollectionEntry = currentPath.length === 3 && isArrayEqual([currentPath[0], currentPath[1]], ["storage", "collections"]);
38
+ const isRequiredNamespaces = isArrayEqual(currentPath, ["storage", "namespaces"]);
39
+ const isNamespaceSlot = currentPath.length === 3 && isArrayEqual([currentPath[0], currentPath[1]], ["storage", "namespaces"]);
40
+ const isRequiredNamespaceTables = currentPath.length === 4 && currentPath[0] === "storage" && currentPath[1] === "namespaces" && currentPath[3] === "tables";
41
+ const isNamespaceTableEntry = currentPath.length === 5 && currentPath[0] === "storage" && currentPath[1] === "namespaces" && currentPath[3] === "tables";
41
42
  const isRequiredRoots = isArrayEqual(currentPath, ["roots"]);
42
43
  const isRequiredExtensionPacks = isArrayEqual(currentPath, ["extensionPacks"]);
43
44
  const isRequiredCapabilities = isArrayEqual(currentPath, ["capabilities"]);
@@ -50,36 +51,12 @@ function omitDefaults(obj, path) {
50
51
  const isExtensionNamespace = currentPath.length === 2 && currentPath[0] === "extensionPacks";
51
52
  const isModelRelations = currentPath.length === 3 && isArrayEqual([currentPath[0], currentPath[2]], ["models", "relations"]);
52
53
  const isModelStorage = currentPath.length === 3 && isArrayEqual([currentPath[0], currentPath[2]], ["models", "storage"]);
53
- const isTableUniques = currentPath.length === 4 && isArrayEqual([
54
- currentPath[0],
55
- currentPath[1],
56
- currentPath[3]
57
- ], [
58
- "storage",
59
- "tables",
60
- "uniques"
61
- ]);
62
- const isTableIndexes = currentPath.length === 4 && isArrayEqual([
63
- currentPath[0],
64
- currentPath[1],
65
- currentPath[3]
66
- ], [
67
- "storage",
68
- "tables",
69
- "indexes"
70
- ]);
71
- const isTableForeignKeys = currentPath.length === 4 && isArrayEqual([
72
- currentPath[0],
73
- currentPath[1],
74
- currentPath[3]
75
- ], [
76
- "storage",
77
- "tables",
78
- "foreignKeys"
79
- ]);
54
+ const isNamespaceTableUniques = currentPath.length === 6 && currentPath[0] === "storage" && currentPath[1] === "namespaces" && currentPath[3] === "tables" && currentPath[5] === "uniques";
55
+ const isNamespaceTableIndexes = currentPath.length === 6 && currentPath[0] === "storage" && currentPath[1] === "namespaces" && currentPath[3] === "tables" && currentPath[5] === "indexes";
56
+ const isNamespaceTableForeignKeys = currentPath.length === 6 && currentPath[0] === "storage" && currentPath[1] === "namespaces" && currentPath[3] === "tables" && currentPath[5] === "foreignKeys";
80
57
  const isStorageTypeTypeParams = currentPath.length === 4 && currentPath[0] === "storage" && currentPath[1] === "types" && key === "typeParams";
81
- const isFkBooleanField = currentPath.length === 5 && currentPath[0] === "storage" && currentPath[1] === "tables" && currentPath[3] === "foreignKeys" && (key === "constraint" || key === "index");
82
- if (!isRequiredModels && !isRequiredTables && !isRequiredCollections && !isCollectionEntry && !isRequiredRoots && !isRequiredExtensionPacks && !isRequiredCapabilities && !isRequiredMeta && !isRequiredExecutionDefaults && !isExtensionNamespace && !isModelRelations && !isModelStorage && !isTableUniques && !isTableIndexes && !isTableForeignKeys && !isFkBooleanField && !(key === "nullable") && !isStorageTypeTypeParams) continue;
58
+ const isFkBooleanField = currentPath.length === 7 && currentPath[0] === "storage" && currentPath[1] === "namespaces" && currentPath[3] === "tables" && currentPath[5] === "foreignKeys" && (key === "constraint" || key === "index");
59
+ if (!isRequiredModels && !isRequiredNamespaces && !isNamespaceSlot && !isRequiredNamespaceTables && !isNamespaceTableEntry && !isRequiredRoots && !isRequiredExtensionPacks && !isRequiredCapabilities && !isRequiredMeta && !isRequiredExecutionDefaults && !isExtensionNamespace && !isModelRelations && !isModelStorage && !isNamespaceTableUniques && !isNamespaceTableIndexes && !isNamespaceTableForeignKeys && !isFkBooleanField && !(key === "nullable") && !isStorageTypeTypeParams) continue;
83
60
  }
84
61
  result[key] = omitDefaults(value, currentPath);
85
62
  }
@@ -93,33 +70,55 @@ function sortObjectKeys(obj) {
93
70
  for (const key of keys) sorted[key] = sortObjectKeys(obj[key]);
94
71
  return sorted;
95
72
  }
73
+ function sortTableArrays(tableObj) {
74
+ const sortedTable = { ...tableObj };
75
+ if (Array.isArray(tableObj.indexes)) sortedTable.indexes = [...tableObj.indexes].sort((a, b) => {
76
+ const nameA = a?.name || "";
77
+ const nameB = b?.name || "";
78
+ return nameA.localeCompare(nameB);
79
+ });
80
+ if (Array.isArray(tableObj.uniques)) sortedTable.uniques = [...tableObj.uniques].sort((a, b) => {
81
+ const nameA = a?.name || "";
82
+ const nameB = b?.name || "";
83
+ return nameA.localeCompare(nameB);
84
+ });
85
+ return sortedTable;
86
+ }
96
87
  function sortIndexesAndUniques(storage) {
97
88
  if (!storage || typeof storage !== "object") return storage;
98
89
  const storageObj = storage;
99
- if (!storageObj.tables || typeof storageObj.tables !== "object") return storage;
100
- const tables = storageObj.tables;
101
- const result = { ...storageObj };
102
- result.tables = {};
103
- const sortedTableNames = Object.keys(tables).sort();
104
- for (const tableName of sortedTableNames) {
105
- const table = tables[tableName];
106
- if (!table || typeof table !== "object") {
107
- result.tables[tableName] = table;
90
+ if (!storageObj.namespaces || typeof storageObj.namespaces !== "object") return storage;
91
+ const namespaces = storageObj.namespaces;
92
+ const result = {
93
+ ...storageObj,
94
+ namespaces: {}
95
+ };
96
+ const resultNamespaces = result.namespaces;
97
+ for (const nsId of Object.keys(namespaces)) {
98
+ const ns = namespaces[nsId];
99
+ if (!ns || typeof ns !== "object") {
100
+ resultNamespaces[nsId] = ns;
101
+ continue;
102
+ }
103
+ const nsObj = ns;
104
+ if (!nsObj.tables || typeof nsObj.tables !== "object") {
105
+ resultNamespaces[nsId] = ns;
108
106
  continue;
109
107
  }
110
- const tableObj = table;
111
- const sortedTable = { ...tableObj };
112
- if (Array.isArray(tableObj.indexes)) sortedTable.indexes = [...tableObj.indexes].sort((a, b) => {
113
- const nameA = a?.name || "";
114
- const nameB = b?.name || "";
115
- return nameA.localeCompare(nameB);
116
- });
117
- if (Array.isArray(tableObj.uniques)) sortedTable.uniques = [...tableObj.uniques].sort((a, b) => {
118
- const nameA = a?.name || "";
119
- const nameB = b?.name || "";
120
- return nameA.localeCompare(nameB);
121
- });
122
- result.tables[tableName] = sortedTable;
108
+ const sortedTables = {};
109
+ const sortedTableNames = Object.keys(nsObj.tables).sort();
110
+ for (const tableName of sortedTableNames) {
111
+ const table = nsObj.tables[tableName];
112
+ if (!table || typeof table !== "object") {
113
+ sortedTables[tableName] = table;
114
+ continue;
115
+ }
116
+ sortedTables[tableName] = sortTableArrays(table);
117
+ }
118
+ resultNamespaces[nsId] = {
119
+ ...nsObj,
120
+ tables: sortedTables
121
+ };
123
122
  }
124
123
  return result;
125
124
  }
@@ -201,4 +200,4 @@ function computeProfileHash(args) {
201
200
  //#endregion
202
201
  export { canonicalizeContractToObject as a, canonicalizeContract as i, computeProfileHash as n, computeStorageHash as r, computeExecutionHash as t };
203
202
 
204
- //# sourceMappingURL=hashing-JryrY8ZF.mjs.map
203
+ //# sourceMappingURL=hashing-BizZH8d0.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hashing-BizZH8d0.mjs","names":[],"sources":["../src/canonicalization.ts","../src/hashing.ts"],"sourcesContent":["import { isArrayEqual } from '@prisma-next/utils/array-equal';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { JsonObject } from '@prisma-next/utils/json';\n\nimport type { Contract } from './contract-types';\n\n/**\n * Per-target contract serializer hook. The framework canonicalizer uses\n * this to convert an in-memory contract (which may carry class-instance\n * IR nodes whose runtime-only fields must not appear in the on-disk\n * envelope) into a plain JsonObject before applying the family-agnostic\n * canonical-key ordering / default-omission / sort steps. Targets whose\n * contract is JSON-clean by construction return the contract unchanged.\n */\nexport type SerializeContract = (contract: Contract) => JsonObject;\n\nconst TOP_LEVEL_ORDER = [\n 'schemaVersion',\n 'canonicalVersion',\n 'targetFamily',\n 'target',\n 'profileHash',\n 'roots',\n 'models',\n 'valueObjects',\n 'storage',\n 'execution',\n 'capabilities',\n 'extensionPacks',\n 'meta',\n] as const;\n\nfunction isDefaultValue(value: unknown): boolean {\n if (value === false) return true;\n if (value === null) return false;\n if (Array.isArray(value) && value.length === 0) return true;\n if (typeof value === 'object' && value !== null) {\n const keys = Object.keys(value);\n return keys.length === 0;\n }\n return false;\n}\n\nfunction omitDefaults(obj: unknown, path: readonly string[]): unknown {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => omitDefaults(item, path));\n }\n\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(obj)) {\n const currentPath = [...path, key];\n\n if (key === '_generated') {\n continue;\n }\n\n if (key === 'generated' && value === false) {\n continue;\n }\n\n if ((key === 'onDelete' || key === 'onUpdate') && value === 'noAction') {\n continue;\n }\n\n if (isDefaultValue(value)) {\n const isRequiredModels = isArrayEqual(currentPath, ['models']);\n const isRequiredNamespaces = isArrayEqual(currentPath, ['storage', 'namespaces']);\n const isNamespaceSlot =\n currentPath.length === 3 &&\n isArrayEqual([currentPath[0], currentPath[1]], ['storage', 'namespaces']);\n const isRequiredNamespaceTables =\n currentPath.length === 4 &&\n currentPath[0] === 'storage' &&\n currentPath[1] === 'namespaces' &&\n currentPath[3] === 'tables';\n // Preserve per-table payloads even when empty. SQL tables are never\n // emitted empty; Mongo collections legitimately are (a declared\n // collection with no schema is a valid representation), and the\n // family-agnostic canonicalizer must not strip them.\n const isNamespaceTableEntry =\n currentPath.length === 5 &&\n currentPath[0] === 'storage' &&\n currentPath[1] === 'namespaces' &&\n currentPath[3] === 'tables';\n const isRequiredRoots = isArrayEqual(currentPath, ['roots']);\n const isRequiredExtensionPacks = isArrayEqual(currentPath, ['extensionPacks']);\n const isRequiredCapabilities = isArrayEqual(currentPath, ['capabilities']);\n const isRequiredMeta = isArrayEqual(currentPath, ['meta']);\n const isRequiredExecutionDefaults = isArrayEqual(currentPath, [\n 'execution',\n 'mutations',\n 'defaults',\n ]);\n const isExtensionNamespace = currentPath.length === 2 && currentPath[0] === 'extensionPacks';\n const isModelRelations =\n currentPath.length === 3 &&\n isArrayEqual([currentPath[0], currentPath[2]], ['models', 'relations']);\n const isModelStorage =\n currentPath.length === 3 &&\n isArrayEqual([currentPath[0], currentPath[2]], ['models', 'storage']);\n const isNamespaceTableUniques =\n currentPath.length === 6 &&\n currentPath[0] === 'storage' &&\n currentPath[1] === 'namespaces' &&\n currentPath[3] === 'tables' &&\n currentPath[5] === 'uniques';\n const isNamespaceTableIndexes =\n currentPath.length === 6 &&\n currentPath[0] === 'storage' &&\n currentPath[1] === 'namespaces' &&\n currentPath[3] === 'tables' &&\n currentPath[5] === 'indexes';\n const isNamespaceTableForeignKeys =\n currentPath.length === 6 &&\n currentPath[0] === 'storage' &&\n currentPath[1] === 'namespaces' &&\n currentPath[3] === 'tables' &&\n currentPath[5] === 'foreignKeys';\n\n // `storage.types.<name>.typeParams` is part of the StorageTypeInstance\n // shape (validators require it). Preserve it even when empty so the\n // emitted contract.json remains structurally valid after a round-trip.\n const isStorageTypeTypeParams =\n currentPath.length === 4 &&\n currentPath[0] === 'storage' &&\n currentPath[1] === 'types' &&\n key === 'typeParams';\n\n const isFkBooleanField =\n currentPath.length === 7 &&\n currentPath[0] === 'storage' &&\n currentPath[1] === 'namespaces' &&\n currentPath[3] === 'tables' &&\n currentPath[5] === 'foreignKeys' &&\n (key === 'constraint' || key === 'index');\n\n const isNullableField = key === 'nullable';\n\n if (\n !isRequiredModels &&\n !isRequiredNamespaces &&\n !isNamespaceSlot &&\n !isRequiredNamespaceTables &&\n !isNamespaceTableEntry &&\n !isRequiredRoots &&\n !isRequiredExtensionPacks &&\n !isRequiredCapabilities &&\n !isRequiredMeta &&\n !isRequiredExecutionDefaults &&\n !isExtensionNamespace &&\n !isModelRelations &&\n !isModelStorage &&\n !isNamespaceTableUniques &&\n !isNamespaceTableIndexes &&\n !isNamespaceTableForeignKeys &&\n !isFkBooleanField &&\n !isNullableField &&\n !isStorageTypeTypeParams\n ) {\n continue;\n }\n }\n\n result[key] = omitDefaults(value, currentPath);\n }\n\n return result;\n}\n\nfunction sortObjectKeys(obj: unknown): unknown {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => sortObjectKeys(item));\n }\n\n const sorted: Record<string, unknown> = {};\n const keys = Object.keys(obj).sort();\n for (const key of keys) {\n sorted[key] = sortObjectKeys((obj as Record<string, unknown>)[key]);\n }\n\n return sorted;\n}\n\ntype NamespaceObject = {\n tables?: Record<string, unknown>;\n [key: string]: unknown;\n};\n\ntype StorageObject = {\n namespaces?: Record<string, unknown>;\n [key: string]: unknown;\n};\n\ntype TableObject = {\n indexes?: unknown[];\n uniques?: unknown[];\n [key: string]: unknown;\n};\n\nfunction sortTableArrays(tableObj: TableObject): TableObject {\n const sortedTable: TableObject = { ...tableObj };\n\n if (Array.isArray(tableObj.indexes)) {\n sortedTable.indexes = [...tableObj.indexes].sort((a, b) => {\n const nameA = (a as { name?: string })?.name || '';\n const nameB = (b as { name?: string })?.name || '';\n return nameA.localeCompare(nameB);\n });\n }\n\n if (Array.isArray(tableObj.uniques)) {\n sortedTable.uniques = [...tableObj.uniques].sort((a, b) => {\n const nameA = (a as { name?: string })?.name || '';\n const nameB = (b as { name?: string })?.name || '';\n return nameA.localeCompare(nameB);\n });\n }\n\n return sortedTable;\n}\n\nfunction sortIndexesAndUniques(storage: unknown): unknown {\n if (!storage || typeof storage !== 'object') {\n return storage;\n }\n\n const storageObj = storage as StorageObject;\n if (!storageObj.namespaces || typeof storageObj.namespaces !== 'object') {\n return storage;\n }\n\n const namespaces = storageObj.namespaces;\n const result: StorageObject = { ...storageObj, namespaces: {} };\n const resultNamespaces = result.namespaces as Record<string, unknown>;\n\n for (const nsId of Object.keys(namespaces)) {\n const ns = namespaces[nsId];\n if (!ns || typeof ns !== 'object') {\n resultNamespaces[nsId] = ns;\n continue;\n }\n\n const nsObj = ns as NamespaceObject;\n if (!nsObj.tables || typeof nsObj.tables !== 'object') {\n resultNamespaces[nsId] = ns;\n continue;\n }\n\n const sortedTables: Record<string, unknown> = {};\n const sortedTableNames = Object.keys(nsObj.tables).sort();\n for (const tableName of sortedTableNames) {\n const table = nsObj.tables[tableName];\n if (!table || typeof table !== 'object') {\n sortedTables[tableName] = table;\n continue;\n }\n sortedTables[tableName] = sortTableArrays(table as TableObject);\n }\n\n resultNamespaces[nsId] = { ...nsObj, tables: sortedTables };\n }\n\n return result;\n}\n\nexport function orderTopLevel(obj: Record<string, unknown>): Record<string, unknown> {\n const ordered: Record<string, unknown> = {};\n const remaining = new Set(Object.keys(obj));\n\n for (const key of TOP_LEVEL_ORDER) {\n if (remaining.has(key)) {\n ordered[key] = obj[key];\n remaining.delete(key);\n }\n }\n\n for (const key of Array.from(remaining).sort()) {\n ordered[key] = obj[key];\n }\n\n return ordered;\n}\n\nexport interface CanonicalizeContractOptions {\n readonly schemaVersion?: string;\n /**\n * Per-target hook that converts the in-memory contract (which may\n * carry class-instance IR nodes) into a plain JsonObject before the\n * family-agnostic canonicalization steps run.\n *\n * Routing through the hook is what lets each target decide which\n * fields appear in the on-disk envelope; runtime-only class API\n * fields stay invisible to the canonicalization walk by virtue of\n * the per-target serializer not putting them in the JSON shape.\n */\n readonly serializeContract: SerializeContract;\n}\n\n/**\n * Object-form variant of {@link canonicalizeContract}. Exported because the\n * emitter writes the canonical contract through a separate JSON-stringify\n * pass and consumes the structured object directly.\n */\nexport function canonicalizeContractToObject(\n contract: Contract,\n options: CanonicalizeContractOptions,\n): Record<string, unknown> {\n const serialized = options.serializeContract(contract);\n const normalized: Record<string, unknown> = {\n ...ifDefined('schemaVersion', options.schemaVersion),\n targetFamily: serialized['targetFamily'],\n target: serialized['target'],\n profileHash: serialized['profileHash'],\n roots: serialized['roots'],\n models: serialized['models'],\n ...ifDefined('valueObjects', serialized['valueObjects']),\n storage: serialized['storage'],\n ...ifDefined('execution', serialized['execution']),\n extensionPacks: serialized['extensionPacks'],\n capabilities: serialized['capabilities'],\n meta: serialized['meta'],\n };\n const withDefaultsOmitted = omitDefaults(normalized, []) as Record<string, unknown>;\n const withSortedIndexes = sortIndexesAndUniques(withDefaultsOmitted['storage']);\n const withSortedStorage = { ...withDefaultsOmitted, storage: withSortedIndexes };\n const withSortedKeys = sortObjectKeys(withSortedStorage) as Record<string, unknown>;\n return orderTopLevel(withSortedKeys);\n}\n\nexport function canonicalizeContract(\n contract: Contract,\n options: CanonicalizeContractOptions,\n): string {\n return JSON.stringify(canonicalizeContractToObject(contract, options), null, 2);\n}\n","import { createHash } from 'node:crypto';\nimport type { JsonObject } from '@prisma-next/utils/json';\nimport { canonicalizeContract } from './canonicalization';\nimport type { Contract } from './contract-types';\nimport type { ExecutionHashBase, ProfileHashBase, StorageHashBase } from './types';\n\nconst SCHEMA_VERSION = '1';\n\nfunction sha256(content: string): string {\n const hash = createHash('sha256');\n hash.update(content);\n return `sha256:${hash.digest('hex')}`;\n}\n\nfunction hashContract(section: Record<string, unknown>): string {\n // Blind cast: the synthesised object is a hash-only stand-in\n // — never returned to callers, never executed as a Contract.\n // `canonicalizeContract` only walks the storage / execution /\n // capabilities slices, all of which are populated above, so the\n // missing precise Contract typing on the other slots is\n // immaterial for the hash result.\n const contract = {\n targetFamily: section['targetFamily'],\n target: section['target'],\n roots: {},\n models: {},\n storage: section['storage'] ?? {},\n execution: section['execution'],\n extensionPacks: {},\n capabilities: section['capabilities'] ?? {},\n meta: {},\n profileHash: '',\n ...section,\n } as unknown as Contract;\n return canonicalizeContract(contract, {\n schemaVersion: SCHEMA_VERSION,\n serializeContract: (c) => JSON.parse(JSON.stringify(c)) as JsonObject,\n });\n}\n\nexport function computeStorageHash(args: {\n target: string;\n targetFamily: string;\n storage: Record<string, unknown>;\n}): StorageHashBase<string> {\n return sha256(hashContract(args)) as StorageHashBase<string>;\n}\n\nexport function computeExecutionHash(args: {\n target: string;\n targetFamily: string;\n execution: Record<string, unknown>;\n}): ExecutionHashBase<string> {\n return sha256(hashContract(args)) as ExecutionHashBase<string>;\n}\n\nexport function computeProfileHash(args: {\n target: string;\n targetFamily: string;\n capabilities: Record<string, Record<string, boolean>>;\n}): ProfileHashBase<string> {\n return sha256(hashContract(args)) as ProfileHashBase<string>;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"hashing.d.mts","names":[],"sources":["../src/canonicalization.ts","../src/hashing.ts"],"mappings":";;;;;;AAcA;;;;;;KAAY,iBAAA,IAAqB,QAAA,EAAU,QAAA,KAAa,UAAA;AAAA,UA+OvC,2BAAA;EAAA,SACN,aAAA;EADiC;;;;;;;;AAoB5C;;EApB4C,SAYjC,iBAAA,EAAmB,iBAAA;AAAA;;;;;;iBAQd,4BAAA,CACd,QAAA,EAAU,QAAA,EACV,OAAA,EAAS,2BAAA,GACR,MAAA;AAAA,iBAuBa,oBAAA,CACd,QAAA,EAAU,QAAA,EACV,OAAA,EAAS,2BAAA;;;iBCrQK,kBAAA,CAAmB,IAAA;EACjC,MAAA;EACA,YAAA;EACA,OAAA,EAAS,MAAA;AAAA,IACP,eAAA;AAAA,iBAIY,oBAAA,CAAqB,IAAA;EACnC,MAAA;EACA,YAAA;EACA,SAAA,EAAW,MAAA;AAAA,IACT,iBAAA;AAAA,iBAIY,kBAAA,CAAmB,IAAA;EACjC,MAAA;EACA,YAAA;EACA,YAAA,EAAc,MAAA,SAAe,MAAA;AAAA,IAC3B,eAAA"}
1
+ {"version":3,"file":"hashing.d.mts","names":[],"sources":["../src/canonicalization.ts","../src/hashing.ts"],"mappings":";;;;;;AAcA;;;;;;KAAY,iBAAA,IAAqB,QAAA,EAAU,QAAA,KAAa,UAAA;AAAA,UAsRvC,2BAAA;EAAA,SACN,aAAA;EADiC;;;;;;;;AAoB5C;;EApB4C,SAYjC,iBAAA,EAAmB,iBAAA;AAAA;;;;;;iBAQd,4BAAA,CACd,QAAA,EAAU,QAAA,EACV,OAAA,EAAS,2BAAA,GACR,MAAA;AAAA,iBAuBa,oBAAA,CACd,QAAA,EAAU,QAAA,EACV,OAAA,EAAS,2BAAA;;;iBC5SK,kBAAA,CAAmB,IAAA;EACjC,MAAA;EACA,YAAA;EACA,OAAA,EAAS,MAAA;AAAA,IACP,eAAA;AAAA,iBAIY,oBAAA,CAAqB,IAAA;EACnC,MAAA;EACA,YAAA;EACA,SAAA,EAAW,MAAA;AAAA,IACT,iBAAA;AAAA,iBAIY,kBAAA,CAAmB,IAAA;EACjC,MAAA;EACA,YAAA;EACA,YAAA,EAAc,MAAA,SAAe,MAAA;AAAA,IAC3B,eAAA"}
package/dist/hashing.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as canonicalizeContractToObject, i as canonicalizeContract, n as computeProfileHash, r as computeStorageHash, t as computeExecutionHash } from "./hashing-JryrY8ZF.mjs";
1
+ import { a as canonicalizeContractToObject, i as canonicalizeContract, n as computeProfileHash, r as computeStorageHash, t as computeExecutionHash } from "./hashing-BizZH8d0.mjs";
2
2
  export { canonicalizeContract, canonicalizeContractToObject, computeExecutionHash, computeProfileHash, computeStorageHash };
@@ -17,7 +17,10 @@ type ContractOverrides<TStorage extends StorageBase = StorageBase, TModels exten
17
17
  declare const DUMMY_HASH: StorageHashBase<"sha256:test">;
18
18
  declare function createContract<TStorage extends StorageBase = StorageBase, TModels extends Record<string, ContractModelBase> = Record<string, ContractModel>>(overrides?: ContractOverrides<TStorage, TModels>): Contract<TStorage, TModels>;
19
19
  type SqlStorageLike = StorageBase & {
20
- readonly tables: Record<string, unknown>;
20
+ readonly namespaces: Readonly<Record<string, {
21
+ readonly id: string;
22
+ readonly tables: Readonly<Record<string, unknown>>;
23
+ }>>;
21
24
  readonly types?: Record<string, unknown>;
22
25
  };
23
26
  type SqlModelLike = ContractModel<ModelStorageBase & {
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.mts","names":[],"sources":["../src/testing-factories.ts"],"mappings":";;;KAYK,iBAAA,kBACc,WAAA,GAAc,WAAA,kBACf,MAAA,SAAe,iBAAA,IAAqB,MAAA,SAAe,aAAA;EAEnE,MAAA;EACA,YAAA;EACA,KAAA,GAAQ,MAAA;EACR,MAAA,GAAS,OAAA;EACT,OAAA,GAAU,IAAA,CAAK,QAAA;EACf,YAAA,GAAe,MAAA,SAAe,mBAAA;EAC9B,YAAA,GAAe,MAAA,SAAe,MAAA;EAC9B,cAAA,GAAiB,MAAA;EACjB,SAAA,GAAY,IAAA,CAAK,gBAAA;EACjB,WAAA,GAAc,eAAA;EACd,IAAA,GAAO,MAAA;AAAA;AAAA,cAGH,UAAA,EAAoC,eAAA;AAAA,iBAE1B,cAAA,kBACG,WAAA,GAAc,WAAA,kBACf,MAAA,SAAe,iBAAA,IAAqB,MAAA,SAAe,aAAA,EAAA,CACnE,SAAA,GAAW,iBAAA,CAAkB,QAAA,EAAU,OAAA,IAAgB,QAAA,CAAS,QAAA,EAAU,OAAA;AAAA,KAgDvE,cAAA,GAAiB,WAAA;EAAA,SACX,MAAA,EAAQ,MAAA;EAAA,SACR,KAAA,GAAQ,MAAA;AAAA;AAAA,KAGd,YAAA,GAAe,aAAA,CAAc,gBAAA;EAAqB,KAAA;AAAA;AAAA,iBAEvC,iBAAA,CACd,SAAA,GAAW,iBAAA,CAAkB,cAAA,EAAgB,MAAA,SAAe,YAAA,KAC3D,QAAA,CAAS,cAAA,EAAgB,MAAA,SAAe,YAAA"}
1
+ {"version":3,"file":"testing.d.mts","names":[],"sources":["../src/testing-factories.ts"],"mappings":";;;KAYK,iBAAA,kBACc,WAAA,GAAc,WAAA,kBACf,MAAA,SAAe,iBAAA,IAAqB,MAAA,SAAe,aAAA;EAEnE,MAAA;EACA,YAAA;EACA,KAAA,GAAQ,MAAA;EACR,MAAA,GAAS,OAAA;EACT,OAAA,GAAU,IAAA,CAAK,QAAA;EACf,YAAA,GAAe,MAAA,SAAe,mBAAA;EAC9B,YAAA,GAAe,MAAA,SAAe,MAAA;EAC9B,cAAA,GAAiB,MAAA;EACjB,SAAA,GAAY,IAAA,CAAK,gBAAA;EACjB,WAAA,GAAc,eAAA;EACd,IAAA,GAAO,MAAA;AAAA;AAAA,cAGH,UAAA,EAAoC,eAAA;AAAA,iBAe1B,cAAA,kBACG,WAAA,GAAc,WAAA,kBACf,MAAA,SAAe,iBAAA,IAAqB,MAAA,SAAe,aAAA,EAAA,CACnE,SAAA,GAAW,iBAAA,CAAkB,QAAA,EAAU,OAAA,IAAgB,QAAA,CAAS,QAAA,EAAU,OAAA;AAAA,KA+CvE,cAAA,GAAiB,WAAA;EAAA,SACX,UAAA,EAAY,QAAA,CACnB,MAAA;IAAA,SAA0B,EAAA;IAAA,SAAqB,MAAA,EAAQ,QAAA,CAAS,MAAA;EAAA;EAAA,SAEzD,KAAA,GAAQ,MAAA;AAAA;AAAA,KAGd,YAAA,GAAe,aAAA,CAAc,gBAAA;EAAqB,KAAA;AAAA;AAAA,iBAEvC,iBAAA,CACd,SAAA,GAAW,iBAAA,CAAkB,cAAA,EAAgB,MAAA,SAAe,YAAA,KAC3D,QAAA,CAAS,cAAA,EAAgB,MAAA,SAAe,YAAA"}
package/dist/testing.mjs CHANGED
@@ -1,13 +1,19 @@
1
1
  import { t as coreHash } from "./types-CVGwkRLa.mjs";
2
- import { n as computeProfileHash, r as computeStorageHash, t as computeExecutionHash } from "./hashing-JryrY8ZF.mjs";
2
+ import { n as computeProfileHash, r as computeStorageHash, t as computeExecutionHash } from "./hashing-BizZH8d0.mjs";
3
3
  import { ifDefined } from "@prisma-next/utils/defined";
4
4
  //#region src/testing-factories.ts
5
5
  const DUMMY_HASH = coreHash("sha256:test");
6
+ const DEFAULT_FRAMEWORK_STORAGE = { namespaces: {} };
7
+ const UNBOUND_NAMESPACE_ID = "__unbound__";
8
+ const DEFAULT_SQL_STORAGE = { namespaces: { [UNBOUND_NAMESPACE_ID]: {
9
+ id: UNBOUND_NAMESPACE_ID,
10
+ tables: {}
11
+ } } };
6
12
  function createContract(overrides = {}) {
7
13
  const target = overrides.target ?? "postgres";
8
14
  const targetFamily = overrides.targetFamily ?? "sql";
9
15
  const capabilities = overrides.capabilities ?? {};
10
- const rawStorage = overrides.storage ?? { tables: {} };
16
+ const rawStorage = overrides.storage ?? DEFAULT_FRAMEWORK_STORAGE;
11
17
  const storageHash = computeStorageHash({
12
18
  target,
13
19
  targetFamily,
@@ -45,10 +51,10 @@ function createContract(overrides = {}) {
45
51
  }
46
52
  function createSqlContract(overrides = {}) {
47
53
  return createContract({
48
- target: "postgres",
49
- targetFamily: "sql",
50
- storage: overrides.storage ?? { tables: {} },
51
- ...overrides
54
+ ...overrides,
55
+ target: overrides.target ?? "postgres",
56
+ targetFamily: overrides.targetFamily ?? "sql",
57
+ storage: overrides.storage ?? DEFAULT_SQL_STORAGE
52
58
  });
53
59
  }
54
60
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"testing.mjs","names":[],"sources":["../src/testing-factories.ts"],"sourcesContent":["import { ifDefined } from '@prisma-next/utils/defined';\nimport type { Contract } from './contract-types';\nimport type {\n ContractModel,\n ContractModelBase,\n ContractValueObject,\n ModelStorageBase,\n} from './domain-types';\nimport { computeExecutionHash, computeProfileHash, computeStorageHash } from './hashing';\nimport type { ExecutionSection, ProfileHashBase, StorageBase } from './types';\nimport { coreHash } from './types';\n\ntype ContractOverrides<\n TStorage extends StorageBase = StorageBase,\n TModels extends Record<string, ContractModelBase> = Record<string, ContractModel>,\n> = {\n target?: string;\n targetFamily?: string;\n roots?: Record<string, string>;\n models?: TModels;\n storage?: Omit<TStorage, 'storageHash'>;\n valueObjects?: Record<string, ContractValueObject>;\n capabilities?: Record<string, Record<string, boolean>>;\n extensionPacks?: Record<string, unknown>;\n execution?: Omit<ExecutionSection, 'executionHash'>;\n profileHash?: ProfileHashBase<string>;\n meta?: Record<string, unknown>;\n};\n\nconst DUMMY_HASH = coreHash('sha256:test');\n\nexport function createContract<\n TStorage extends StorageBase = StorageBase,\n TModels extends Record<string, ContractModelBase> = Record<string, ContractModel>,\n>(overrides: ContractOverrides<TStorage, TModels> = {}): Contract<TStorage, TModels> {\n const target = overrides.target ?? 'postgres';\n const targetFamily = overrides.targetFamily ?? 'sql';\n const capabilities = overrides.capabilities ?? {};\n\n const rawStorage =\n overrides.storage ?? ({ tables: {} } as unknown as Omit<TStorage, 'storageHash'>);\n\n const storageHash = computeStorageHash({\n target,\n targetFamily,\n storage: rawStorage as Record<string, unknown>,\n });\n\n const storage = {\n ...rawStorage,\n storageHash,\n } as TStorage;\n\n const computedProfileHash =\n overrides.profileHash ?? computeProfileHash({ target, targetFamily, capabilities });\n\n return {\n target,\n targetFamily,\n roots: overrides.roots ?? {},\n models: (overrides.models ?? {}) as TModels,\n ...ifDefined('valueObjects', overrides.valueObjects),\n storage,\n capabilities,\n extensionPacks: overrides.extensionPacks ?? {},\n ...(overrides.execution !== undefined\n ? {\n execution: {\n ...overrides.execution,\n executionHash: computeExecutionHash({\n target,\n targetFamily,\n execution: overrides.execution,\n }),\n },\n }\n : {}),\n profileHash: computedProfileHash,\n meta: overrides.meta ?? {},\n };\n}\n\ntype SqlStorageLike = StorageBase & {\n readonly tables: Record<string, unknown>;\n readonly types?: Record<string, unknown>;\n};\n\ntype SqlModelLike = ContractModel<ModelStorageBase & { table: string }>;\n\nexport function createSqlContract(\n overrides: ContractOverrides<SqlStorageLike, Record<string, SqlModelLike>> = {},\n): Contract<SqlStorageLike, Record<string, SqlModelLike>> {\n return createContract<SqlStorageLike, Record<string, SqlModelLike>>({\n target: 'postgres',\n targetFamily: 'sql',\n storage: overrides.storage ?? { tables: {} },\n ...overrides,\n });\n}\n\nexport { DUMMY_HASH };\n"],"mappings":";;;;AA6BA,MAAM,aAAa,SAAS,cAAc;AAE1C,SAAgB,eAGd,YAAkD,EAAE,EAA+B;CACnF,MAAM,SAAS,UAAU,UAAU;CACnC,MAAM,eAAe,UAAU,gBAAgB;CAC/C,MAAM,eAAe,UAAU,gBAAgB,EAAE;CAEjD,MAAM,aACJ,UAAU,WAAY,EAAE,QAAQ,EAAE,EAAE;CAEtC,MAAM,cAAc,mBAAmB;EACrC;EACA;EACA,SAAS;EACV,CAAC;CAEF,MAAM,UAAU;EACd,GAAG;EACH;EACD;CAED,MAAM,sBACJ,UAAU,eAAe,mBAAmB;EAAE;EAAQ;EAAc;EAAc,CAAC;CAErF,OAAO;EACL;EACA;EACA,OAAO,UAAU,SAAS,EAAE;EAC5B,QAAS,UAAU,UAAU,EAAE;EAC/B,GAAG,UAAU,gBAAgB,UAAU,aAAa;EACpD;EACA;EACA,gBAAgB,UAAU,kBAAkB,EAAE;EAC9C,GAAI,UAAU,cAAc,KAAA,IACxB,EACE,WAAW;GACT,GAAG,UAAU;GACb,eAAe,qBAAqB;IAClC;IACA;IACA,WAAW,UAAU;IACtB,CAAC;GACH,EACF,GACD,EAAE;EACN,aAAa;EACb,MAAM,UAAU,QAAQ,EAAE;EAC3B;;AAUH,SAAgB,kBACd,YAA6E,EAAE,EACvB;CACxD,OAAO,eAA6D;EAClE,QAAQ;EACR,cAAc;EACd,SAAS,UAAU,WAAW,EAAE,QAAQ,EAAE,EAAE;EAC5C,GAAG;EACJ,CAAC"}
1
+ {"version":3,"file":"testing.mjs","names":[],"sources":["../src/testing-factories.ts"],"sourcesContent":["import { ifDefined } from '@prisma-next/utils/defined';\nimport type { Contract } from './contract-types';\nimport type {\n ContractModel,\n ContractModelBase,\n ContractValueObject,\n ModelStorageBase,\n} from './domain-types';\nimport { computeExecutionHash, computeProfileHash, computeStorageHash } from './hashing';\nimport type { ExecutionSection, ProfileHashBase, StorageBase } from './types';\nimport { coreHash } from './types';\n\ntype ContractOverrides<\n TStorage extends StorageBase = StorageBase,\n TModels extends Record<string, ContractModelBase> = Record<string, ContractModel>,\n> = {\n target?: string;\n targetFamily?: string;\n roots?: Record<string, string>;\n models?: TModels;\n storage?: Omit<TStorage, 'storageHash'>;\n valueObjects?: Record<string, ContractValueObject>;\n capabilities?: Record<string, Record<string, boolean>>;\n extensionPacks?: Record<string, unknown>;\n execution?: Omit<ExecutionSection, 'executionHash'>;\n profileHash?: ProfileHashBase<string>;\n meta?: Record<string, unknown>;\n};\n\nconst DUMMY_HASH = coreHash('sha256:test');\n\nconst DEFAULT_FRAMEWORK_STORAGE = { namespaces: {} } as const;\n\nconst UNBOUND_NAMESPACE_ID = '__unbound__' as const;\n\nconst DEFAULT_SQL_STORAGE = {\n namespaces: {\n [UNBOUND_NAMESPACE_ID]: {\n id: UNBOUND_NAMESPACE_ID,\n tables: {},\n },\n },\n} as const;\n\nexport function createContract<\n TStorage extends StorageBase = StorageBase,\n TModels extends Record<string, ContractModelBase> = Record<string, ContractModel>,\n>(overrides: ContractOverrides<TStorage, TModels> = {}): Contract<TStorage, TModels> {\n const target = overrides.target ?? 'postgres';\n const targetFamily = overrides.targetFamily ?? 'sql';\n const capabilities = overrides.capabilities ?? {};\n\n const rawStorage = overrides.storage ?? DEFAULT_FRAMEWORK_STORAGE;\n\n const storageHash = computeStorageHash({\n target,\n targetFamily,\n storage: rawStorage as Record<string, unknown>,\n });\n\n const storage = {\n ...rawStorage,\n storageHash,\n } as TStorage;\n\n const computedProfileHash =\n overrides.profileHash ?? computeProfileHash({ target, targetFamily, capabilities });\n\n return {\n target,\n targetFamily,\n roots: overrides.roots ?? {},\n models: (overrides.models ?? {}) as TModels,\n ...ifDefined('valueObjects', overrides.valueObjects),\n storage,\n capabilities,\n extensionPacks: overrides.extensionPacks ?? {},\n ...(overrides.execution !== undefined\n ? {\n execution: {\n ...overrides.execution,\n executionHash: computeExecutionHash({\n target,\n targetFamily,\n execution: overrides.execution,\n }),\n },\n }\n : {}),\n profileHash: computedProfileHash,\n meta: overrides.meta ?? {},\n };\n}\n\ntype SqlStorageLike = StorageBase & {\n readonly namespaces: Readonly<\n Record<string, { readonly id: string; readonly tables: Readonly<Record<string, unknown>> }>\n >;\n readonly types?: Record<string, unknown>;\n};\n\ntype SqlModelLike = ContractModel<ModelStorageBase & { table: string }>;\n\nexport function createSqlContract(\n overrides: ContractOverrides<SqlStorageLike, Record<string, SqlModelLike>> = {},\n): Contract<SqlStorageLike, Record<string, SqlModelLike>> {\n return createContract<SqlStorageLike, Record<string, SqlModelLike>>({\n ...overrides,\n target: overrides.target ?? 'postgres',\n targetFamily: overrides.targetFamily ?? 'sql',\n storage: overrides.storage ?? DEFAULT_SQL_STORAGE,\n });\n}\n\nexport { DUMMY_HASH };\n"],"mappings":";;;;AA6BA,MAAM,aAAa,SAAS,cAAc;AAE1C,MAAM,4BAA4B,EAAE,YAAY,EAAE,EAAE;AAEpD,MAAM,uBAAuB;AAE7B,MAAM,sBAAsB,EAC1B,YAAY,GACT,uBAAuB;CACtB,IAAI;CACJ,QAAQ,EAAE;CACX,EACF,EACF;AAED,SAAgB,eAGd,YAAkD,EAAE,EAA+B;CACnF,MAAM,SAAS,UAAU,UAAU;CACnC,MAAM,eAAe,UAAU,gBAAgB;CAC/C,MAAM,eAAe,UAAU,gBAAgB,EAAE;CAEjD,MAAM,aAAa,UAAU,WAAW;CAExC,MAAM,cAAc,mBAAmB;EACrC;EACA;EACA,SAAS;EACV,CAAC;CAEF,MAAM,UAAU;EACd,GAAG;EACH;EACD;CAED,MAAM,sBACJ,UAAU,eAAe,mBAAmB;EAAE;EAAQ;EAAc;EAAc,CAAC;CAErF,OAAO;EACL;EACA;EACA,OAAO,UAAU,SAAS,EAAE;EAC5B,QAAS,UAAU,UAAU,EAAE;EAC/B,GAAG,UAAU,gBAAgB,UAAU,aAAa;EACpD;EACA;EACA,gBAAgB,UAAU,kBAAkB,EAAE;EAC9C,GAAI,UAAU,cAAc,KAAA,IACxB,EACE,WAAW;GACT,GAAG,UAAU;GACb,eAAe,qBAAqB;IAClC;IACA;IACA,WAAW,UAAU;IACtB,CAAC;GACH,EACF,GACD,EAAE;EACN,aAAa;EACb,MAAM,UAAU,QAAQ,EAAE;EAC3B;;AAYH,SAAgB,kBACd,YAA6E,EAAE,EACvB;CACxD,OAAO,eAA6D;EAClE,GAAG;EACH,QAAQ,UAAU,UAAU;EAC5B,cAAc,UAAU,gBAAgB;EACxC,SAAS,UAAU,WAAW;EAC/B,CAAC"}
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@prisma-next/contract",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "Data contract type definitions and JSON schema for Prisma Next",
8
8
  "dependencies": {
9
- "@prisma-next/utils": "0.9.0",
9
+ "@prisma-next/utils": "0.10.0",
10
10
  "@standard-schema/spec": "^1.1.0",
11
11
  "arktype": "^2.2.0"
12
12
  },
13
13
  "devDependencies": {
14
- "@prisma-next/test-utils": "0.9.0",
15
- "@prisma-next/tsconfig": "0.9.0",
16
- "@prisma-next/tsdown": "0.9.0",
14
+ "@prisma-next/test-utils": "0.10.0",
15
+ "@prisma-next/tsconfig": "0.10.0",
16
+ "@prisma-next/tsdown": "0.10.0",
17
17
  "tsdown": "0.22.0",
18
18
  "typescript": "5.9.3",
19
19
  "vitest": "4.1.6"
@@ -69,11 +69,24 @@ function omitDefaults(obj: unknown, path: readonly string[]): unknown {
69
69
 
70
70
  if (isDefaultValue(value)) {
71
71
  const isRequiredModels = isArrayEqual(currentPath, ['models']);
72
- const isRequiredTables = isArrayEqual(currentPath, ['storage', 'tables']);
73
- const isRequiredCollections = isArrayEqual(currentPath, ['storage', 'collections']);
74
- const isCollectionEntry =
72
+ const isRequiredNamespaces = isArrayEqual(currentPath, ['storage', 'namespaces']);
73
+ const isNamespaceSlot =
75
74
  currentPath.length === 3 &&
76
- isArrayEqual([currentPath[0], currentPath[1]], ['storage', 'collections']);
75
+ isArrayEqual([currentPath[0], currentPath[1]], ['storage', 'namespaces']);
76
+ const isRequiredNamespaceTables =
77
+ currentPath.length === 4 &&
78
+ currentPath[0] === 'storage' &&
79
+ currentPath[1] === 'namespaces' &&
80
+ currentPath[3] === 'tables';
81
+ // Preserve per-table payloads even when empty. SQL tables are never
82
+ // emitted empty; Mongo collections legitimately are (a declared
83
+ // collection with no schema is a valid representation), and the
84
+ // family-agnostic canonicalizer must not strip them.
85
+ const isNamespaceTableEntry =
86
+ currentPath.length === 5 &&
87
+ currentPath[0] === 'storage' &&
88
+ currentPath[1] === 'namespaces' &&
89
+ currentPath[3] === 'tables';
77
90
  const isRequiredRoots = isArrayEqual(currentPath, ['roots']);
78
91
  const isRequiredExtensionPacks = isArrayEqual(currentPath, ['extensionPacks']);
79
92
  const isRequiredCapabilities = isArrayEqual(currentPath, ['capabilities']);
@@ -90,24 +103,24 @@ function omitDefaults(obj: unknown, path: readonly string[]): unknown {
90
103
  const isModelStorage =
91
104
  currentPath.length === 3 &&
92
105
  isArrayEqual([currentPath[0], currentPath[2]], ['models', 'storage']);
93
- const isTableUniques =
94
- currentPath.length === 4 &&
95
- isArrayEqual(
96
- [currentPath[0], currentPath[1], currentPath[3]],
97
- ['storage', 'tables', 'uniques'],
98
- );
99
- const isTableIndexes =
100
- currentPath.length === 4 &&
101
- isArrayEqual(
102
- [currentPath[0], currentPath[1], currentPath[3]],
103
- ['storage', 'tables', 'indexes'],
104
- );
105
- const isTableForeignKeys =
106
- currentPath.length === 4 &&
107
- isArrayEqual(
108
- [currentPath[0], currentPath[1], currentPath[3]],
109
- ['storage', 'tables', 'foreignKeys'],
110
- );
106
+ const isNamespaceTableUniques =
107
+ currentPath.length === 6 &&
108
+ currentPath[0] === 'storage' &&
109
+ currentPath[1] === 'namespaces' &&
110
+ currentPath[3] === 'tables' &&
111
+ currentPath[5] === 'uniques';
112
+ const isNamespaceTableIndexes =
113
+ currentPath.length === 6 &&
114
+ currentPath[0] === 'storage' &&
115
+ currentPath[1] === 'namespaces' &&
116
+ currentPath[3] === 'tables' &&
117
+ currentPath[5] === 'indexes';
118
+ const isNamespaceTableForeignKeys =
119
+ currentPath.length === 6 &&
120
+ currentPath[0] === 'storage' &&
121
+ currentPath[1] === 'namespaces' &&
122
+ currentPath[3] === 'tables' &&
123
+ currentPath[5] === 'foreignKeys';
111
124
 
112
125
  // `storage.types.<name>.typeParams` is part of the StorageTypeInstance
113
126
  // shape (validators require it). Preserve it even when empty so the
@@ -119,19 +132,21 @@ function omitDefaults(obj: unknown, path: readonly string[]): unknown {
119
132
  key === 'typeParams';
120
133
 
121
134
  const isFkBooleanField =
122
- currentPath.length === 5 &&
135
+ currentPath.length === 7 &&
123
136
  currentPath[0] === 'storage' &&
124
- currentPath[1] === 'tables' &&
125
- currentPath[3] === 'foreignKeys' &&
137
+ currentPath[1] === 'namespaces' &&
138
+ currentPath[3] === 'tables' &&
139
+ currentPath[5] === 'foreignKeys' &&
126
140
  (key === 'constraint' || key === 'index');
127
141
 
128
142
  const isNullableField = key === 'nullable';
129
143
 
130
144
  if (
131
145
  !isRequiredModels &&
132
- !isRequiredTables &&
133
- !isRequiredCollections &&
134
- !isCollectionEntry &&
146
+ !isRequiredNamespaces &&
147
+ !isNamespaceSlot &&
148
+ !isRequiredNamespaceTables &&
149
+ !isNamespaceTableEntry &&
135
150
  !isRequiredRoots &&
136
151
  !isRequiredExtensionPacks &&
137
152
  !isRequiredCapabilities &&
@@ -140,9 +155,9 @@ function omitDefaults(obj: unknown, path: readonly string[]): unknown {
140
155
  !isExtensionNamespace &&
141
156
  !isModelRelations &&
142
157
  !isModelStorage &&
143
- !isTableUniques &&
144
- !isTableIndexes &&
145
- !isTableForeignKeys &&
158
+ !isNamespaceTableUniques &&
159
+ !isNamespaceTableIndexes &&
160
+ !isNamespaceTableForeignKeys &&
146
161
  !isFkBooleanField &&
147
162
  !isNullableField &&
148
163
  !isStorageTypeTypeParams
@@ -175,59 +190,83 @@ function sortObjectKeys(obj: unknown): unknown {
175
190
  return sorted;
176
191
  }
177
192
 
178
- type StorageObject = {
193
+ type NamespaceObject = {
179
194
  tables?: Record<string, unknown>;
180
195
  [key: string]: unknown;
181
196
  };
182
197
 
198
+ type StorageObject = {
199
+ namespaces?: Record<string, unknown>;
200
+ [key: string]: unknown;
201
+ };
202
+
183
203
  type TableObject = {
184
204
  indexes?: unknown[];
185
205
  uniques?: unknown[];
186
206
  [key: string]: unknown;
187
207
  };
188
208
 
209
+ function sortTableArrays(tableObj: TableObject): TableObject {
210
+ const sortedTable: TableObject = { ...tableObj };
211
+
212
+ if (Array.isArray(tableObj.indexes)) {
213
+ sortedTable.indexes = [...tableObj.indexes].sort((a, b) => {
214
+ const nameA = (a as { name?: string })?.name || '';
215
+ const nameB = (b as { name?: string })?.name || '';
216
+ return nameA.localeCompare(nameB);
217
+ });
218
+ }
219
+
220
+ if (Array.isArray(tableObj.uniques)) {
221
+ sortedTable.uniques = [...tableObj.uniques].sort((a, b) => {
222
+ const nameA = (a as { name?: string })?.name || '';
223
+ const nameB = (b as { name?: string })?.name || '';
224
+ return nameA.localeCompare(nameB);
225
+ });
226
+ }
227
+
228
+ return sortedTable;
229
+ }
230
+
189
231
  function sortIndexesAndUniques(storage: unknown): unknown {
190
232
  if (!storage || typeof storage !== 'object') {
191
233
  return storage;
192
234
  }
193
235
 
194
236
  const storageObj = storage as StorageObject;
195
- if (!storageObj.tables || typeof storageObj.tables !== 'object') {
237
+ if (!storageObj.namespaces || typeof storageObj.namespaces !== 'object') {
196
238
  return storage;
197
239
  }
198
240
 
199
- const tables = storageObj.tables;
200
- const result: StorageObject = { ...storageObj };
241
+ const namespaces = storageObj.namespaces;
242
+ const result: StorageObject = { ...storageObj, namespaces: {} };
243
+ const resultNamespaces = result.namespaces as Record<string, unknown>;
201
244
 
202
- result.tables = {};
203
- const sortedTableNames = Object.keys(tables).sort();
204
- for (const tableName of sortedTableNames) {
205
- const table = tables[tableName];
206
- if (!table || typeof table !== 'object') {
207
- result.tables[tableName] = table;
245
+ for (const nsId of Object.keys(namespaces)) {
246
+ const ns = namespaces[nsId];
247
+ if (!ns || typeof ns !== 'object') {
248
+ resultNamespaces[nsId] = ns;
208
249
  continue;
209
250
  }
210
251
 
211
- const tableObj = table as TableObject;
212
- const sortedTable: TableObject = { ...tableObj };
213
-
214
- if (Array.isArray(tableObj.indexes)) {
215
- sortedTable.indexes = [...tableObj.indexes].sort((a, b) => {
216
- const nameA = (a as { name?: string })?.name || '';
217
- const nameB = (b as { name?: string })?.name || '';
218
- return nameA.localeCompare(nameB);
219
- });
252
+ const nsObj = ns as NamespaceObject;
253
+ if (!nsObj.tables || typeof nsObj.tables !== 'object') {
254
+ resultNamespaces[nsId] = ns;
255
+ continue;
220
256
  }
221
257
 
222
- if (Array.isArray(tableObj.uniques)) {
223
- sortedTable.uniques = [...tableObj.uniques].sort((a, b) => {
224
- const nameA = (a as { name?: string })?.name || '';
225
- const nameB = (b as { name?: string })?.name || '';
226
- return nameA.localeCompare(nameB);
227
- });
258
+ const sortedTables: Record<string, unknown> = {};
259
+ const sortedTableNames = Object.keys(nsObj.tables).sort();
260
+ for (const tableName of sortedTableNames) {
261
+ const table = nsObj.tables[tableName];
262
+ if (!table || typeof table !== 'object') {
263
+ sortedTables[tableName] = table;
264
+ continue;
265
+ }
266
+ sortedTables[tableName] = sortTableArrays(table as TableObject);
228
267
  }
229
268
 
230
- result.tables[tableName] = sortedTable;
269
+ resultNamespaces[nsId] = { ...nsObj, tables: sortedTables };
231
270
  }
232
271
 
233
272
  return result;
@@ -29,6 +29,19 @@ type ContractOverrides<
29
29
 
30
30
  const DUMMY_HASH = coreHash('sha256:test');
31
31
 
32
+ const DEFAULT_FRAMEWORK_STORAGE = { namespaces: {} } as const;
33
+
34
+ const UNBOUND_NAMESPACE_ID = '__unbound__' as const;
35
+
36
+ const DEFAULT_SQL_STORAGE = {
37
+ namespaces: {
38
+ [UNBOUND_NAMESPACE_ID]: {
39
+ id: UNBOUND_NAMESPACE_ID,
40
+ tables: {},
41
+ },
42
+ },
43
+ } as const;
44
+
32
45
  export function createContract<
33
46
  TStorage extends StorageBase = StorageBase,
34
47
  TModels extends Record<string, ContractModelBase> = Record<string, ContractModel>,
@@ -37,8 +50,7 @@ export function createContract<
37
50
  const targetFamily = overrides.targetFamily ?? 'sql';
38
51
  const capabilities = overrides.capabilities ?? {};
39
52
 
40
- const rawStorage =
41
- overrides.storage ?? ({ tables: {} } as unknown as Omit<TStorage, 'storageHash'>);
53
+ const rawStorage = overrides.storage ?? DEFAULT_FRAMEWORK_STORAGE;
42
54
 
43
55
  const storageHash = computeStorageHash({
44
56
  target,
@@ -81,7 +93,9 @@ export function createContract<
81
93
  }
82
94
 
83
95
  type SqlStorageLike = StorageBase & {
84
- readonly tables: Record<string, unknown>;
96
+ readonly namespaces: Readonly<
97
+ Record<string, { readonly id: string; readonly tables: Readonly<Record<string, unknown>> }>
98
+ >;
85
99
  readonly types?: Record<string, unknown>;
86
100
  };
87
101
 
@@ -91,10 +105,10 @@ export function createSqlContract(
91
105
  overrides: ContractOverrides<SqlStorageLike, Record<string, SqlModelLike>> = {},
92
106
  ): Contract<SqlStorageLike, Record<string, SqlModelLike>> {
93
107
  return createContract<SqlStorageLike, Record<string, SqlModelLike>>({
94
- target: 'postgres',
95
- targetFamily: 'sql',
96
- storage: overrides.storage ?? { tables: {} },
97
108
  ...overrides,
109
+ target: overrides.target ?? 'postgres',
110
+ targetFamily: overrides.targetFamily ?? 'sql',
111
+ storage: overrides.storage ?? DEFAULT_SQL_STORAGE,
98
112
  });
99
113
  }
100
114
 
@@ -1 +0,0 @@
1
- {"version":3,"file":"hashing-JryrY8ZF.mjs","names":[],"sources":["../src/canonicalization.ts","../src/hashing.ts"],"sourcesContent":["import { isArrayEqual } from '@prisma-next/utils/array-equal';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { JsonObject } from '@prisma-next/utils/json';\n\nimport type { Contract } from './contract-types';\n\n/**\n * Per-target contract serializer hook. The framework canonicalizer uses\n * this to convert an in-memory contract (which may carry class-instance\n * IR nodes whose runtime-only fields must not appear in the on-disk\n * envelope) into a plain JsonObject before applying the family-agnostic\n * canonical-key ordering / default-omission / sort steps. Targets whose\n * contract is JSON-clean by construction return the contract unchanged.\n */\nexport type SerializeContract = (contract: Contract) => JsonObject;\n\nconst TOP_LEVEL_ORDER = [\n 'schemaVersion',\n 'canonicalVersion',\n 'targetFamily',\n 'target',\n 'profileHash',\n 'roots',\n 'models',\n 'valueObjects',\n 'storage',\n 'execution',\n 'capabilities',\n 'extensionPacks',\n 'meta',\n] as const;\n\nfunction isDefaultValue(value: unknown): boolean {\n if (value === false) return true;\n if (value === null) return false;\n if (Array.isArray(value) && value.length === 0) return true;\n if (typeof value === 'object' && value !== null) {\n const keys = Object.keys(value);\n return keys.length === 0;\n }\n return false;\n}\n\nfunction omitDefaults(obj: unknown, path: readonly string[]): unknown {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => omitDefaults(item, path));\n }\n\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(obj)) {\n const currentPath = [...path, key];\n\n if (key === '_generated') {\n continue;\n }\n\n if (key === 'generated' && value === false) {\n continue;\n }\n\n if ((key === 'onDelete' || key === 'onUpdate') && value === 'noAction') {\n continue;\n }\n\n if (isDefaultValue(value)) {\n const isRequiredModels = isArrayEqual(currentPath, ['models']);\n const isRequiredTables = isArrayEqual(currentPath, ['storage', 'tables']);\n const isRequiredCollections = isArrayEqual(currentPath, ['storage', 'collections']);\n const isCollectionEntry =\n currentPath.length === 3 &&\n isArrayEqual([currentPath[0], currentPath[1]], ['storage', 'collections']);\n const isRequiredRoots = isArrayEqual(currentPath, ['roots']);\n const isRequiredExtensionPacks = isArrayEqual(currentPath, ['extensionPacks']);\n const isRequiredCapabilities = isArrayEqual(currentPath, ['capabilities']);\n const isRequiredMeta = isArrayEqual(currentPath, ['meta']);\n const isRequiredExecutionDefaults = isArrayEqual(currentPath, [\n 'execution',\n 'mutations',\n 'defaults',\n ]);\n const isExtensionNamespace = currentPath.length === 2 && currentPath[0] === 'extensionPacks';\n const isModelRelations =\n currentPath.length === 3 &&\n isArrayEqual([currentPath[0], currentPath[2]], ['models', 'relations']);\n const isModelStorage =\n currentPath.length === 3 &&\n isArrayEqual([currentPath[0], currentPath[2]], ['models', 'storage']);\n const isTableUniques =\n currentPath.length === 4 &&\n isArrayEqual(\n [currentPath[0], currentPath[1], currentPath[3]],\n ['storage', 'tables', 'uniques'],\n );\n const isTableIndexes =\n currentPath.length === 4 &&\n isArrayEqual(\n [currentPath[0], currentPath[1], currentPath[3]],\n ['storage', 'tables', 'indexes'],\n );\n const isTableForeignKeys =\n currentPath.length === 4 &&\n isArrayEqual(\n [currentPath[0], currentPath[1], currentPath[3]],\n ['storage', 'tables', 'foreignKeys'],\n );\n\n // `storage.types.<name>.typeParams` is part of the StorageTypeInstance\n // shape (validators require it). Preserve it even when empty so the\n // emitted contract.json remains structurally valid after a round-trip.\n const isStorageTypeTypeParams =\n currentPath.length === 4 &&\n currentPath[0] === 'storage' &&\n currentPath[1] === 'types' &&\n key === 'typeParams';\n\n const isFkBooleanField =\n currentPath.length === 5 &&\n currentPath[0] === 'storage' &&\n currentPath[1] === 'tables' &&\n currentPath[3] === 'foreignKeys' &&\n (key === 'constraint' || key === 'index');\n\n const isNullableField = key === 'nullable';\n\n if (\n !isRequiredModels &&\n !isRequiredTables &&\n !isRequiredCollections &&\n !isCollectionEntry &&\n !isRequiredRoots &&\n !isRequiredExtensionPacks &&\n !isRequiredCapabilities &&\n !isRequiredMeta &&\n !isRequiredExecutionDefaults &&\n !isExtensionNamespace &&\n !isModelRelations &&\n !isModelStorage &&\n !isTableUniques &&\n !isTableIndexes &&\n !isTableForeignKeys &&\n !isFkBooleanField &&\n !isNullableField &&\n !isStorageTypeTypeParams\n ) {\n continue;\n }\n }\n\n result[key] = omitDefaults(value, currentPath);\n }\n\n return result;\n}\n\nfunction sortObjectKeys(obj: unknown): unknown {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => sortObjectKeys(item));\n }\n\n const sorted: Record<string, unknown> = {};\n const keys = Object.keys(obj).sort();\n for (const key of keys) {\n sorted[key] = sortObjectKeys((obj as Record<string, unknown>)[key]);\n }\n\n return sorted;\n}\n\ntype StorageObject = {\n tables?: Record<string, unknown>;\n [key: string]: unknown;\n};\n\ntype TableObject = {\n indexes?: unknown[];\n uniques?: unknown[];\n [key: string]: unknown;\n};\n\nfunction sortIndexesAndUniques(storage: unknown): unknown {\n if (!storage || typeof storage !== 'object') {\n return storage;\n }\n\n const storageObj = storage as StorageObject;\n if (!storageObj.tables || typeof storageObj.tables !== 'object') {\n return storage;\n }\n\n const tables = storageObj.tables;\n const result: StorageObject = { ...storageObj };\n\n result.tables = {};\n const sortedTableNames = Object.keys(tables).sort();\n for (const tableName of sortedTableNames) {\n const table = tables[tableName];\n if (!table || typeof table !== 'object') {\n result.tables[tableName] = table;\n continue;\n }\n\n const tableObj = table as TableObject;\n const sortedTable: TableObject = { ...tableObj };\n\n if (Array.isArray(tableObj.indexes)) {\n sortedTable.indexes = [...tableObj.indexes].sort((a, b) => {\n const nameA = (a as { name?: string })?.name || '';\n const nameB = (b as { name?: string })?.name || '';\n return nameA.localeCompare(nameB);\n });\n }\n\n if (Array.isArray(tableObj.uniques)) {\n sortedTable.uniques = [...tableObj.uniques].sort((a, b) => {\n const nameA = (a as { name?: string })?.name || '';\n const nameB = (b as { name?: string })?.name || '';\n return nameA.localeCompare(nameB);\n });\n }\n\n result.tables[tableName] = sortedTable;\n }\n\n return result;\n}\n\nexport function orderTopLevel(obj: Record<string, unknown>): Record<string, unknown> {\n const ordered: Record<string, unknown> = {};\n const remaining = new Set(Object.keys(obj));\n\n for (const key of TOP_LEVEL_ORDER) {\n if (remaining.has(key)) {\n ordered[key] = obj[key];\n remaining.delete(key);\n }\n }\n\n for (const key of Array.from(remaining).sort()) {\n ordered[key] = obj[key];\n }\n\n return ordered;\n}\n\nexport interface CanonicalizeContractOptions {\n readonly schemaVersion?: string;\n /**\n * Per-target hook that converts the in-memory contract (which may\n * carry class-instance IR nodes) into a plain JsonObject before the\n * family-agnostic canonicalization steps run.\n *\n * Routing through the hook is what lets each target decide which\n * fields appear in the on-disk envelope; runtime-only class API\n * fields stay invisible to the canonicalization walk by virtue of\n * the per-target serializer not putting them in the JSON shape.\n */\n readonly serializeContract: SerializeContract;\n}\n\n/**\n * Object-form variant of {@link canonicalizeContract}. Exported because the\n * emitter writes the canonical contract through a separate JSON-stringify\n * pass and consumes the structured object directly.\n */\nexport function canonicalizeContractToObject(\n contract: Contract,\n options: CanonicalizeContractOptions,\n): Record<string, unknown> {\n const serialized = options.serializeContract(contract);\n const normalized: Record<string, unknown> = {\n ...ifDefined('schemaVersion', options.schemaVersion),\n targetFamily: serialized['targetFamily'],\n target: serialized['target'],\n profileHash: serialized['profileHash'],\n roots: serialized['roots'],\n models: serialized['models'],\n ...ifDefined('valueObjects', serialized['valueObjects']),\n storage: serialized['storage'],\n ...ifDefined('execution', serialized['execution']),\n extensionPacks: serialized['extensionPacks'],\n capabilities: serialized['capabilities'],\n meta: serialized['meta'],\n };\n const withDefaultsOmitted = omitDefaults(normalized, []) as Record<string, unknown>;\n const withSortedIndexes = sortIndexesAndUniques(withDefaultsOmitted['storage']);\n const withSortedStorage = { ...withDefaultsOmitted, storage: withSortedIndexes };\n const withSortedKeys = sortObjectKeys(withSortedStorage) as Record<string, unknown>;\n return orderTopLevel(withSortedKeys);\n}\n\nexport function canonicalizeContract(\n contract: Contract,\n options: CanonicalizeContractOptions,\n): string {\n return JSON.stringify(canonicalizeContractToObject(contract, options), null, 2);\n}\n","import { createHash } from 'node:crypto';\nimport type { JsonObject } from '@prisma-next/utils/json';\nimport { canonicalizeContract } from './canonicalization';\nimport type { Contract } from './contract-types';\nimport type { ExecutionHashBase, ProfileHashBase, StorageHashBase } from './types';\n\nconst SCHEMA_VERSION = '1';\n\nfunction sha256(content: string): string {\n const hash = createHash('sha256');\n hash.update(content);\n return `sha256:${hash.digest('hex')}`;\n}\n\nfunction hashContract(section: Record<string, unknown>): string {\n // Blind cast: the synthesised object is a hash-only stand-in\n // — never returned to callers, never executed as a Contract.\n // `canonicalizeContract` only walks the storage / execution /\n // capabilities slices, all of which are populated above, so the\n // missing precise Contract typing on the other slots is\n // immaterial for the hash result.\n const contract = {\n targetFamily: section['targetFamily'],\n target: section['target'],\n roots: {},\n models: {},\n storage: section['storage'] ?? {},\n execution: section['execution'],\n extensionPacks: {},\n capabilities: section['capabilities'] ?? {},\n meta: {},\n profileHash: '',\n ...section,\n } as unknown as Contract;\n return canonicalizeContract(contract, {\n schemaVersion: SCHEMA_VERSION,\n serializeContract: (c) => JSON.parse(JSON.stringify(c)) as JsonObject,\n });\n}\n\nexport function computeStorageHash(args: {\n target: string;\n targetFamily: string;\n storage: Record<string, unknown>;\n}): StorageHashBase<string> {\n return sha256(hashContract(args)) as StorageHashBase<string>;\n}\n\nexport function computeExecutionHash(args: {\n target: string;\n targetFamily: string;\n execution: Record<string, unknown>;\n}): ExecutionHashBase<string> {\n return sha256(hashContract(args)) as ExecutionHashBase<string>;\n}\n\nexport function computeProfileHash(args: {\n target: string;\n targetFamily: string;\n capabilities: Record<string, Record<string, boolean>>;\n}): ProfileHashBase<string> {\n return sha256(hashContract(args)) as ProfileHashBase<string>;\n}\n"],"mappings":"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"}