@prisma-next/family-sql 0.3.0-pr.99.6 → 0.4.0-dev.1
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/LICENSE +201 -0
- package/README.md +58 -22
- package/dist/authoring-type-constructors-DgU-RFaP.mjs +203 -0
- package/dist/authoring-type-constructors-DgU-RFaP.mjs.map +1 -0
- package/dist/control-adapter.d.mts +54 -0
- package/dist/control-adapter.d.mts.map +1 -0
- package/dist/control-adapter.mjs +1 -0
- package/dist/control.d.mts +386 -0
- package/dist/control.d.mts.map +1 -0
- package/dist/control.mjs +643 -0
- package/dist/control.mjs.map +1 -0
- package/dist/operation-descriptors.d.mts +380 -0
- package/dist/operation-descriptors.d.mts.map +1 -0
- package/dist/operation-descriptors.mjs +294 -0
- package/dist/operation-descriptors.mjs.map +1 -0
- package/dist/pack.d.mts +248 -0
- package/dist/pack.d.mts.map +1 -0
- package/dist/pack.mjs +18 -0
- package/dist/pack.mjs.map +1 -0
- package/dist/runtime.d.mts +27 -0
- package/dist/runtime.d.mts.map +1 -0
- package/dist/runtime.mjs +38 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/schema-verify.d.mts +48 -0
- package/dist/schema-verify.d.mts.map +1 -0
- package/dist/schema-verify.mjs +3 -0
- package/dist/test-utils.d.mts +2 -0
- package/dist/test-utils.mjs +3 -0
- package/dist/types-BaUzKt6Q.d.mts +353 -0
- package/dist/types-BaUzKt6Q.d.mts.map +1 -0
- package/dist/verify-DZHtfcmj.mjs +108 -0
- package/dist/verify-DZHtfcmj.mjs.map +1 -0
- package/dist/verify-sql-schema-BBhkqEDo.d.mts +67 -0
- package/dist/verify-sql-schema-BBhkqEDo.d.mts.map +1 -0
- package/dist/verify-sql-schema-lR-tlboL.mjs +1174 -0
- package/dist/verify-sql-schema-lR-tlboL.mjs.map +1 -0
- package/dist/verify.d.mts +31 -0
- package/dist/verify.d.mts.map +1 -0
- package/dist/verify.mjs +3 -0
- package/package.json +35 -43
- package/src/core/assembly.ts +123 -155
- package/src/core/authoring-field-presets.ts +207 -0
- package/src/core/authoring-type-constructors.ts +17 -0
- package/src/core/control-adapter.ts +18 -10
- package/src/core/control-descriptor.ts +28 -0
- package/src/core/control-instance.ts +700 -0
- package/src/core/migrations/contract-to-schema-ir.ts +269 -0
- package/src/core/migrations/descriptor-schemas.ts +172 -0
- package/src/core/migrations/operation-descriptors.ts +213 -0
- package/src/core/migrations/policies.ts +1 -1
- package/src/core/migrations/types.ts +199 -175
- package/src/core/runtime-descriptor.ts +19 -41
- package/src/core/runtime-instance.ts +11 -133
- package/src/core/schema-verify/verify-helpers.ts +104 -22
- package/src/core/schema-verify/verify-sql-schema.ts +964 -418
- package/src/core/verify.ts +4 -13
- package/src/exports/control.ts +31 -8
- package/src/exports/operation-descriptors.ts +52 -0
- package/src/exports/pack.ts +16 -0
- package/src/exports/runtime.ts +2 -6
- package/src/exports/schema-verify.ts +4 -1
- package/src/exports/test-utils.ts +3 -4
- package/dist/chunk-GYEG3I7U.js +0 -624
- package/dist/chunk-GYEG3I7U.js.map +0 -1
- package/dist/chunk-SU7LN2UH.js +0 -96
- package/dist/chunk-SU7LN2UH.js.map +0 -1
- package/dist/chunk-XH2Y5NTD.js +0 -715
- package/dist/chunk-XH2Y5NTD.js.map +0 -1
- package/dist/core/assembly.d.ts +0 -43
- package/dist/core/assembly.d.ts.map +0 -1
- package/dist/core/control-adapter.d.ts +0 -42
- package/dist/core/control-adapter.d.ts.map +0 -1
- package/dist/core/descriptor.d.ts +0 -28
- package/dist/core/descriptor.d.ts.map +0 -1
- package/dist/core/instance.d.ts +0 -140
- package/dist/core/instance.d.ts.map +0 -1
- package/dist/core/migrations/plan-helpers.d.ts +0 -20
- package/dist/core/migrations/plan-helpers.d.ts.map +0 -1
- package/dist/core/migrations/policies.d.ts +0 -6
- package/dist/core/migrations/policies.d.ts.map +0 -1
- package/dist/core/migrations/types.d.ts +0 -280
- package/dist/core/migrations/types.d.ts.map +0 -1
- package/dist/core/runtime-descriptor.d.ts +0 -19
- package/dist/core/runtime-descriptor.d.ts.map +0 -1
- package/dist/core/runtime-instance.d.ts +0 -54
- package/dist/core/runtime-instance.d.ts.map +0 -1
- package/dist/core/schema-verify/verify-helpers.d.ts +0 -96
- package/dist/core/schema-verify/verify-helpers.d.ts.map +0 -1
- package/dist/core/schema-verify/verify-sql-schema.d.ts +0 -45
- package/dist/core/schema-verify/verify-sql-schema.d.ts.map +0 -1
- package/dist/core/verify.d.ts +0 -39
- package/dist/core/verify.d.ts.map +0 -1
- package/dist/exports/control-adapter.d.ts +0 -2
- package/dist/exports/control-adapter.d.ts.map +0 -1
- package/dist/exports/control-adapter.js +0 -1
- package/dist/exports/control-adapter.js.map +0 -1
- package/dist/exports/control.d.ts +0 -13
- package/dist/exports/control.d.ts.map +0 -1
- package/dist/exports/control.js +0 -149
- package/dist/exports/control.js.map +0 -1
- package/dist/exports/runtime.d.ts +0 -8
- package/dist/exports/runtime.d.ts.map +0 -1
- package/dist/exports/runtime.js +0 -64
- package/dist/exports/runtime.js.map +0 -1
- package/dist/exports/schema-verify.d.ts +0 -11
- package/dist/exports/schema-verify.d.ts.map +0 -1
- package/dist/exports/schema-verify.js +0 -15
- package/dist/exports/schema-verify.js.map +0 -1
- package/dist/exports/test-utils.d.ts +0 -7
- package/dist/exports/test-utils.d.ts.map +0 -1
- package/dist/exports/test-utils.js +0 -17
- package/dist/exports/test-utils.js.map +0 -1
- package/dist/exports/verify.d.ts +0 -2
- package/dist/exports/verify.d.ts.map +0 -1
- package/dist/exports/verify.js +0 -11
- package/dist/exports/verify.js.map +0 -1
- package/src/core/descriptor.ts +0 -33
- package/src/core/instance.ts +0 -909
package/dist/control.mjs
ADDED
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
import { n as sqlFamilyAuthoringFieldPresets, t as sqlFamilyAuthoringTypes } from "./authoring-type-constructors-DgU-RFaP.mjs";
|
|
2
|
+
import { c as assembleControlMutationDefaultContributions, l as assemblePslInterpretationContributions, o as collectInitDependencies, s as isDatabaseDependencyProvider, t as verifySqlSchema, u as extractCodecControlHooks } from "./verify-sql-schema-lR-tlboL.mjs";
|
|
3
|
+
import { r as readMarker, t as collectSupportedCodecTypeIds } from "./verify-DZHtfcmj.mjs";
|
|
4
|
+
import { sqlEmission } from "@prisma-next/sql-contract-emitter";
|
|
5
|
+
import { emptyCodecLookup } from "@prisma-next/framework-components/codec";
|
|
6
|
+
import { SchemaTreeNode, VERIFY_CODE_HASH_MISMATCH, VERIFY_CODE_MARKER_MISSING, VERIFY_CODE_TARGET_MISMATCH, assembleAuthoringContributions } from "@prisma-next/framework-components/control";
|
|
7
|
+
import { validateContract } from "@prisma-next/sql-contract/validate";
|
|
8
|
+
import { ensureSchemaStatement, ensureTableStatement, writeContractMarker } from "@prisma-next/sql-runtime";
|
|
9
|
+
import { defaultIndexName } from "@prisma-next/sql-schema-ir/naming";
|
|
10
|
+
import { ifDefined } from "@prisma-next/utils/defined";
|
|
11
|
+
import { notOk, ok } from "@prisma-next/utils/result";
|
|
12
|
+
|
|
13
|
+
//#region src/core/control-instance.ts
|
|
14
|
+
function extractCodecTypeIdsFromContract(contract) {
|
|
15
|
+
const typeIds = /* @__PURE__ */ new Set();
|
|
16
|
+
if (typeof contract === "object" && contract !== null && "storage" in contract && typeof contract.storage === "object" && contract.storage !== null && "tables" in contract.storage) {
|
|
17
|
+
const storage = contract.storage;
|
|
18
|
+
if (storage.tables && typeof storage.tables === "object") {
|
|
19
|
+
for (const table of Object.values(storage.tables)) if (typeof table === "object" && table !== null && "columns" in table && typeof table.columns === "object" && table.columns !== null) {
|
|
20
|
+
const columns = table.columns;
|
|
21
|
+
for (const column of Object.values(columns)) if (column && typeof column === "object" && "codecId" in column && typeof column.codecId === "string") typeIds.add(column.codecId);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return Array.from(typeIds).sort();
|
|
26
|
+
}
|
|
27
|
+
function createVerifyResult(options) {
|
|
28
|
+
const contract = { storageHash: options.contractStorageHash };
|
|
29
|
+
if (options.contractProfileHash) contract.profileHash = options.contractProfileHash;
|
|
30
|
+
const target = { expected: options.expectedTargetId };
|
|
31
|
+
if (options.actualTargetId) target.actual = options.actualTargetId;
|
|
32
|
+
const meta = { contractPath: options.contractPath };
|
|
33
|
+
if (options.configPath) meta.configPath = options.configPath;
|
|
34
|
+
const result = {
|
|
35
|
+
ok: options.ok,
|
|
36
|
+
summary: options.summary,
|
|
37
|
+
contract,
|
|
38
|
+
target,
|
|
39
|
+
meta,
|
|
40
|
+
timings: { total: options.totalTime }
|
|
41
|
+
};
|
|
42
|
+
if (options.code) result.code = options.code;
|
|
43
|
+
if (options.marker) result.marker = {
|
|
44
|
+
storageHash: options.marker.storageHash,
|
|
45
|
+
profileHash: options.marker.profileHash
|
|
46
|
+
};
|
|
47
|
+
if (options.missingCodecs) result.missingCodecs = options.missingCodecs;
|
|
48
|
+
if (options.codecCoverageSkipped) result.codecCoverageSkipped = options.codecCoverageSkipped;
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
function isSqlControlAdapter(value) {
|
|
52
|
+
return typeof value === "object" && value !== null && "introspect" in value && typeof value.introspect === "function";
|
|
53
|
+
}
|
|
54
|
+
function buildSqlTypeMetadataRegistry(options) {
|
|
55
|
+
const { target, adapter, extensionPacks: extensions } = options;
|
|
56
|
+
const registry = /* @__PURE__ */ new Map();
|
|
57
|
+
const targetId = adapter.targetId;
|
|
58
|
+
const descriptors = [
|
|
59
|
+
target,
|
|
60
|
+
adapter,
|
|
61
|
+
...extensions
|
|
62
|
+
];
|
|
63
|
+
for (const descriptor of descriptors) {
|
|
64
|
+
const storageTypes = descriptor.types?.storage;
|
|
65
|
+
if (!storageTypes) continue;
|
|
66
|
+
for (const storageType of storageTypes) if (storageType.familyId === "sql" && storageType.targetId === targetId) registry.set(storageType.typeId, {
|
|
67
|
+
typeId: storageType.typeId,
|
|
68
|
+
familyId: "sql",
|
|
69
|
+
targetId: storageType.targetId,
|
|
70
|
+
...storageType.nativeType !== void 0 ? { nativeType: storageType.nativeType } : {}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return registry;
|
|
74
|
+
}
|
|
75
|
+
function createSqlFamilyInstance(stack) {
|
|
76
|
+
if (!stack.adapter) throw new Error("SQL family requires an adapter descriptor in ControlStack");
|
|
77
|
+
const target = stack.target;
|
|
78
|
+
const adapter = stack.adapter;
|
|
79
|
+
const extensions = stack.extensionPacks;
|
|
80
|
+
const { codecTypeImports, operationTypeImports, extensionIds } = stack;
|
|
81
|
+
const typeMetadataRegistry = buildSqlTypeMetadataRegistry({
|
|
82
|
+
target,
|
|
83
|
+
adapter,
|
|
84
|
+
extensionPacks: extensions
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
familyId: "sql",
|
|
88
|
+
codecTypeImports,
|
|
89
|
+
operationTypeImports,
|
|
90
|
+
extensionIds,
|
|
91
|
+
typeMetadataRegistry,
|
|
92
|
+
validateContract(contractJson) {
|
|
93
|
+
return validateContract(contractJson, emptyCodecLookup);
|
|
94
|
+
},
|
|
95
|
+
async verify(verifyOptions) {
|
|
96
|
+
const { driver, contract: rawContract, expectedTargetId, contractPath, configPath } = verifyOptions;
|
|
97
|
+
const startTime = Date.now();
|
|
98
|
+
const contract = validateContract(rawContract, emptyCodecLookup);
|
|
99
|
+
const contractStorageHash = contract.storage.storageHash;
|
|
100
|
+
const contractProfileHash = contract.profileHash;
|
|
101
|
+
const contractTarget = contract.target;
|
|
102
|
+
const marker = await readMarker(driver);
|
|
103
|
+
let missingCodecs;
|
|
104
|
+
let codecCoverageSkipped = false;
|
|
105
|
+
const supportedTypeIds = collectSupportedCodecTypeIds([
|
|
106
|
+
adapter,
|
|
107
|
+
target,
|
|
108
|
+
...extensions
|
|
109
|
+
]);
|
|
110
|
+
if (supportedTypeIds.length === 0) codecCoverageSkipped = true;
|
|
111
|
+
else {
|
|
112
|
+
const supportedSet = new Set(supportedTypeIds);
|
|
113
|
+
const missing = extractCodecTypeIdsFromContract(contract).filter((id) => !supportedSet.has(id));
|
|
114
|
+
if (missing.length > 0) missingCodecs = missing;
|
|
115
|
+
}
|
|
116
|
+
if (!marker) return createVerifyResult({
|
|
117
|
+
ok: false,
|
|
118
|
+
code: VERIFY_CODE_MARKER_MISSING,
|
|
119
|
+
summary: "Marker missing",
|
|
120
|
+
contractStorageHash,
|
|
121
|
+
expectedTargetId,
|
|
122
|
+
contractPath,
|
|
123
|
+
totalTime: Date.now() - startTime,
|
|
124
|
+
...contractProfileHash ? { contractProfileHash } : {},
|
|
125
|
+
...missingCodecs ? { missingCodecs } : {},
|
|
126
|
+
...codecCoverageSkipped ? { codecCoverageSkipped } : {},
|
|
127
|
+
...configPath ? { configPath } : {}
|
|
128
|
+
});
|
|
129
|
+
if (contractTarget !== expectedTargetId) return createVerifyResult({
|
|
130
|
+
ok: false,
|
|
131
|
+
code: VERIFY_CODE_TARGET_MISMATCH,
|
|
132
|
+
summary: "Target mismatch",
|
|
133
|
+
contractStorageHash,
|
|
134
|
+
marker,
|
|
135
|
+
expectedTargetId,
|
|
136
|
+
actualTargetId: contractTarget,
|
|
137
|
+
contractPath,
|
|
138
|
+
totalTime: Date.now() - startTime,
|
|
139
|
+
...contractProfileHash ? { contractProfileHash } : {},
|
|
140
|
+
...missingCodecs ? { missingCodecs } : {},
|
|
141
|
+
...codecCoverageSkipped ? { codecCoverageSkipped } : {},
|
|
142
|
+
...configPath ? { configPath } : {}
|
|
143
|
+
});
|
|
144
|
+
if (marker.storageHash !== contractStorageHash) return createVerifyResult({
|
|
145
|
+
ok: false,
|
|
146
|
+
code: VERIFY_CODE_HASH_MISMATCH,
|
|
147
|
+
summary: "Hash mismatch",
|
|
148
|
+
contractStorageHash,
|
|
149
|
+
marker,
|
|
150
|
+
expectedTargetId,
|
|
151
|
+
contractPath,
|
|
152
|
+
totalTime: Date.now() - startTime,
|
|
153
|
+
...contractProfileHash ? { contractProfileHash } : {},
|
|
154
|
+
...missingCodecs ? { missingCodecs } : {},
|
|
155
|
+
...codecCoverageSkipped ? { codecCoverageSkipped } : {},
|
|
156
|
+
...configPath ? { configPath } : {}
|
|
157
|
+
});
|
|
158
|
+
if (contractProfileHash && marker.profileHash !== contractProfileHash) return createVerifyResult({
|
|
159
|
+
ok: false,
|
|
160
|
+
code: VERIFY_CODE_HASH_MISMATCH,
|
|
161
|
+
summary: "Hash mismatch",
|
|
162
|
+
contractStorageHash,
|
|
163
|
+
contractProfileHash,
|
|
164
|
+
marker,
|
|
165
|
+
expectedTargetId,
|
|
166
|
+
contractPath,
|
|
167
|
+
totalTime: Date.now() - startTime,
|
|
168
|
+
...missingCodecs ? { missingCodecs } : {},
|
|
169
|
+
...codecCoverageSkipped ? { codecCoverageSkipped } : {},
|
|
170
|
+
...configPath ? { configPath } : {}
|
|
171
|
+
});
|
|
172
|
+
return createVerifyResult({
|
|
173
|
+
ok: true,
|
|
174
|
+
summary: "Database matches contract",
|
|
175
|
+
contractStorageHash,
|
|
176
|
+
marker,
|
|
177
|
+
expectedTargetId,
|
|
178
|
+
contractPath,
|
|
179
|
+
totalTime: Date.now() - startTime,
|
|
180
|
+
...contractProfileHash ? { contractProfileHash } : {},
|
|
181
|
+
...missingCodecs ? { missingCodecs } : {},
|
|
182
|
+
...codecCoverageSkipped ? { codecCoverageSkipped } : {},
|
|
183
|
+
...configPath ? { configPath } : {}
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
async schemaVerify(options) {
|
|
187
|
+
const { driver, contract: contractInput, strict, context, frameworkComponents } = options;
|
|
188
|
+
const contract = validateContract(contractInput, emptyCodecLookup);
|
|
189
|
+
const controlAdapter = adapter.create();
|
|
190
|
+
if (!isSqlControlAdapter(controlAdapter)) throw new Error("Adapter does not implement SqlControlAdapter.introspect()");
|
|
191
|
+
return verifySqlSchema({
|
|
192
|
+
contract,
|
|
193
|
+
schema: await controlAdapter.introspect(driver, contractInput),
|
|
194
|
+
strict,
|
|
195
|
+
...ifDefined("context", context),
|
|
196
|
+
typeMetadataRegistry,
|
|
197
|
+
frameworkComponents,
|
|
198
|
+
...ifDefined("normalizeDefault", controlAdapter.normalizeDefault),
|
|
199
|
+
...ifDefined("normalizeNativeType", controlAdapter.normalizeNativeType)
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
async sign(options) {
|
|
203
|
+
const { driver, contract: contractInput, contractPath, configPath } = options;
|
|
204
|
+
const startTime = Date.now();
|
|
205
|
+
const contract = validateContract(contractInput, emptyCodecLookup);
|
|
206
|
+
const contractStorageHash = contract.storage.storageHash;
|
|
207
|
+
const contractProfileHash = "profileHash" in contract && typeof contract.profileHash === "string" ? contract.profileHash : contractStorageHash;
|
|
208
|
+
const contractTarget = contract.target;
|
|
209
|
+
await driver.query(ensureSchemaStatement.sql, ensureSchemaStatement.params);
|
|
210
|
+
await driver.query(ensureTableStatement.sql, ensureTableStatement.params);
|
|
211
|
+
const existingMarker = await readMarker(driver);
|
|
212
|
+
let markerCreated = false;
|
|
213
|
+
let markerUpdated = false;
|
|
214
|
+
let previousHashes;
|
|
215
|
+
if (!existingMarker) {
|
|
216
|
+
const write = writeContractMarker({
|
|
217
|
+
storageHash: contractStorageHash,
|
|
218
|
+
profileHash: contractProfileHash,
|
|
219
|
+
contractJson: contractInput,
|
|
220
|
+
canonicalVersion: 1
|
|
221
|
+
});
|
|
222
|
+
await driver.query(write.insert.sql, write.insert.params);
|
|
223
|
+
markerCreated = true;
|
|
224
|
+
} else {
|
|
225
|
+
const existingStorageHash = existingMarker.storageHash;
|
|
226
|
+
const existingProfileHash = existingMarker.profileHash;
|
|
227
|
+
if (!(existingStorageHash === contractStorageHash) || !(existingProfileHash === contractProfileHash)) {
|
|
228
|
+
previousHashes = {
|
|
229
|
+
storageHash: existingStorageHash,
|
|
230
|
+
profileHash: existingProfileHash
|
|
231
|
+
};
|
|
232
|
+
const write = writeContractMarker({
|
|
233
|
+
storageHash: contractStorageHash,
|
|
234
|
+
profileHash: contractProfileHash,
|
|
235
|
+
contractJson: contractInput,
|
|
236
|
+
canonicalVersion: existingMarker.canonicalVersion ?? 1
|
|
237
|
+
});
|
|
238
|
+
await driver.query(write.update.sql, write.update.params);
|
|
239
|
+
markerUpdated = true;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
let summary;
|
|
243
|
+
if (markerCreated) summary = "Database signed (marker created)";
|
|
244
|
+
else if (markerUpdated) summary = `Database signed (marker updated from ${previousHashes?.storageHash ?? "unknown"})`;
|
|
245
|
+
else summary = "Database already signed with this contract";
|
|
246
|
+
const totalTime = Date.now() - startTime;
|
|
247
|
+
return {
|
|
248
|
+
ok: true,
|
|
249
|
+
summary,
|
|
250
|
+
contract: {
|
|
251
|
+
storageHash: contractStorageHash,
|
|
252
|
+
profileHash: contractProfileHash
|
|
253
|
+
},
|
|
254
|
+
target: {
|
|
255
|
+
expected: contractTarget,
|
|
256
|
+
actual: contractTarget
|
|
257
|
+
},
|
|
258
|
+
marker: {
|
|
259
|
+
created: markerCreated,
|
|
260
|
+
updated: markerUpdated,
|
|
261
|
+
...previousHashes ? { previous: previousHashes } : {}
|
|
262
|
+
},
|
|
263
|
+
meta: {
|
|
264
|
+
contractPath,
|
|
265
|
+
...configPath ? { configPath } : {}
|
|
266
|
+
},
|
|
267
|
+
timings: { total: totalTime }
|
|
268
|
+
};
|
|
269
|
+
},
|
|
270
|
+
async readMarker(options) {
|
|
271
|
+
return readMarker(options.driver);
|
|
272
|
+
},
|
|
273
|
+
async introspect(options) {
|
|
274
|
+
const { driver, contract } = options;
|
|
275
|
+
const controlAdapter = adapter.create();
|
|
276
|
+
if (!isSqlControlAdapter(controlAdapter)) throw new Error("Adapter does not implement SqlControlAdapter.introspect()");
|
|
277
|
+
return controlAdapter.introspect(driver, contract);
|
|
278
|
+
},
|
|
279
|
+
toSchemaView(schema) {
|
|
280
|
+
const tableNodes = Object.entries(schema.tables).map(([tableName, table]) => {
|
|
281
|
+
const children = [];
|
|
282
|
+
const columnNodes = [];
|
|
283
|
+
for (const [columnName, column] of Object.entries(table.columns)) {
|
|
284
|
+
const label = `${columnName}: ${column.nativeType} (${column.nullable ? "nullable" : "not nullable"})`;
|
|
285
|
+
columnNodes.push(new SchemaTreeNode({
|
|
286
|
+
kind: "field",
|
|
287
|
+
id: `column-${tableName}-${columnName}`,
|
|
288
|
+
label,
|
|
289
|
+
meta: {
|
|
290
|
+
nativeType: column.nativeType,
|
|
291
|
+
nullable: column.nullable,
|
|
292
|
+
...ifDefined("default", column.default)
|
|
293
|
+
}
|
|
294
|
+
}));
|
|
295
|
+
}
|
|
296
|
+
if (columnNodes.length > 0) children.push(new SchemaTreeNode({
|
|
297
|
+
kind: "collection",
|
|
298
|
+
id: `columns-${tableName}`,
|
|
299
|
+
label: "columns",
|
|
300
|
+
children: columnNodes
|
|
301
|
+
}));
|
|
302
|
+
if (table.primaryKey) {
|
|
303
|
+
const pkColumns = table.primaryKey.columns.join(", ");
|
|
304
|
+
children.push(new SchemaTreeNode({
|
|
305
|
+
kind: "index",
|
|
306
|
+
id: `primary-key-${tableName}`,
|
|
307
|
+
label: `primary key: ${pkColumns}`,
|
|
308
|
+
meta: {
|
|
309
|
+
columns: table.primaryKey.columns,
|
|
310
|
+
...table.primaryKey.name ? { name: table.primaryKey.name } : {}
|
|
311
|
+
}
|
|
312
|
+
}));
|
|
313
|
+
}
|
|
314
|
+
for (const unique of table.uniques) {
|
|
315
|
+
const name = unique.name ?? `${tableName}_${unique.columns.join("_")}_unique`;
|
|
316
|
+
const label = `unique ${name}`;
|
|
317
|
+
children.push(new SchemaTreeNode({
|
|
318
|
+
kind: "index",
|
|
319
|
+
id: `unique-${tableName}-${name}`,
|
|
320
|
+
label,
|
|
321
|
+
meta: {
|
|
322
|
+
columns: unique.columns,
|
|
323
|
+
unique: true
|
|
324
|
+
}
|
|
325
|
+
}));
|
|
326
|
+
}
|
|
327
|
+
for (const index of table.indexes) {
|
|
328
|
+
const name = index.name ?? defaultIndexName(tableName, index.columns);
|
|
329
|
+
const label = index.unique ? `unique index ${name}` : `index ${name}`;
|
|
330
|
+
children.push(new SchemaTreeNode({
|
|
331
|
+
kind: "index",
|
|
332
|
+
id: `index-${tableName}-${name}`,
|
|
333
|
+
label,
|
|
334
|
+
meta: {
|
|
335
|
+
columns: index.columns,
|
|
336
|
+
unique: index.unique
|
|
337
|
+
}
|
|
338
|
+
}));
|
|
339
|
+
}
|
|
340
|
+
const tableMeta = {};
|
|
341
|
+
if (table.primaryKey) {
|
|
342
|
+
tableMeta["primaryKey"] = table.primaryKey.columns;
|
|
343
|
+
if (table.primaryKey.name) tableMeta["primaryKeyName"] = table.primaryKey.name;
|
|
344
|
+
}
|
|
345
|
+
if (table.foreignKeys.length > 0) tableMeta["foreignKeys"] = table.foreignKeys.map((fk) => ({
|
|
346
|
+
columns: fk.columns,
|
|
347
|
+
referencedTable: fk.referencedTable,
|
|
348
|
+
referencedColumns: fk.referencedColumns,
|
|
349
|
+
...fk.name ? { name: fk.name } : {}
|
|
350
|
+
}));
|
|
351
|
+
return new SchemaTreeNode({
|
|
352
|
+
kind: "entity",
|
|
353
|
+
id: `table-${tableName}`,
|
|
354
|
+
label: `table ${tableName}`,
|
|
355
|
+
...Object.keys(tableMeta).length > 0 ? { meta: tableMeta } : {},
|
|
356
|
+
...children.length > 0 ? { children } : {}
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
const dependencyNodes = schema.dependencies.map((dep) => {
|
|
360
|
+
const shortName = dep.id.split(".").pop() ?? dep.id;
|
|
361
|
+
return new SchemaTreeNode({
|
|
362
|
+
kind: "dependency",
|
|
363
|
+
id: `dependency-${dep.id}`,
|
|
364
|
+
label: `${shortName} dependency is installed`
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
const rootChildren = [...tableNodes, ...dependencyNodes];
|
|
368
|
+
return { root: new SchemaTreeNode({
|
|
369
|
+
kind: "root",
|
|
370
|
+
id: "sql-schema",
|
|
371
|
+
label: "database",
|
|
372
|
+
...rootChildren.length > 0 ? { children: rootChildren } : {}
|
|
373
|
+
}) };
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
//#endregion
|
|
379
|
+
//#region src/core/control-descriptor.ts
|
|
380
|
+
var SqlFamilyDescriptor = class {
|
|
381
|
+
kind = "family";
|
|
382
|
+
id = "sql";
|
|
383
|
+
familyId = "sql";
|
|
384
|
+
version = "0.0.1";
|
|
385
|
+
emission = sqlEmission;
|
|
386
|
+
authoring = {
|
|
387
|
+
field: sqlFamilyAuthoringFieldPresets,
|
|
388
|
+
type: sqlFamilyAuthoringTypes
|
|
389
|
+
};
|
|
390
|
+
create(stack) {
|
|
391
|
+
return createSqlFamilyInstance(stack);
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
//#endregion
|
|
396
|
+
//#region src/core/migrations/contract-to-schema-ir.ts
|
|
397
|
+
function convertColumn(name, column, expandNativeType, renderDefault) {
|
|
398
|
+
return {
|
|
399
|
+
name,
|
|
400
|
+
nativeType: expandNativeType ? expandNativeType({
|
|
401
|
+
nativeType: column.nativeType,
|
|
402
|
+
codecId: column.codecId,
|
|
403
|
+
...ifDefined("typeParams", column.typeParams)
|
|
404
|
+
}) : column.nativeType,
|
|
405
|
+
nullable: column.nullable,
|
|
406
|
+
...ifDefined("default", column.default != null && renderDefault ? renderDefault(column.default, column) : void 0)
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
function convertUnique(unique) {
|
|
410
|
+
return {
|
|
411
|
+
columns: unique.columns,
|
|
412
|
+
...ifDefined("name", unique.name)
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
function convertIndex(index) {
|
|
416
|
+
return {
|
|
417
|
+
columns: index.columns,
|
|
418
|
+
unique: false,
|
|
419
|
+
...ifDefined("name", index.name)
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function convertForeignKey(fk) {
|
|
423
|
+
return {
|
|
424
|
+
columns: fk.columns,
|
|
425
|
+
referencedTable: fk.references.table,
|
|
426
|
+
referencedColumns: fk.references.columns,
|
|
427
|
+
...ifDefined("name", fk.name)
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function convertTable(name, table, expandNativeType, renderDefault) {
|
|
431
|
+
const columns = {};
|
|
432
|
+
for (const [colName, colDef] of Object.entries(table.columns)) columns[colName] = convertColumn(colName, colDef, expandNativeType, renderDefault);
|
|
433
|
+
const satisfiedIndexColumns = new Set([
|
|
434
|
+
...table.indexes.map((idx) => idx.columns.join(",")),
|
|
435
|
+
...table.uniques.map((unique) => unique.columns.join(",")),
|
|
436
|
+
...table.primaryKey ? [table.primaryKey.columns.join(",")] : []
|
|
437
|
+
]);
|
|
438
|
+
const fkBackingIndexes = [];
|
|
439
|
+
for (const fk of table.foreignKeys) {
|
|
440
|
+
if (fk.index === false) continue;
|
|
441
|
+
const key = fk.columns.join(",");
|
|
442
|
+
if (satisfiedIndexColumns.has(key)) continue;
|
|
443
|
+
fkBackingIndexes.push({
|
|
444
|
+
columns: fk.columns,
|
|
445
|
+
unique: false,
|
|
446
|
+
name: defaultIndexName(name, fk.columns)
|
|
447
|
+
});
|
|
448
|
+
satisfiedIndexColumns.add(key);
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
name,
|
|
452
|
+
columns,
|
|
453
|
+
...ifDefined("primaryKey", table.primaryKey),
|
|
454
|
+
foreignKeys: table.foreignKeys.map(convertForeignKey),
|
|
455
|
+
uniques: table.uniques.map(convertUnique),
|
|
456
|
+
indexes: [...table.indexes.map(convertIndex), ...fkBackingIndexes]
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Detects destructive changes between two contract storages.
|
|
461
|
+
*
|
|
462
|
+
* The additive-only planner silently ignores removals (tables, columns).
|
|
463
|
+
* This function detects those removals so callers can report them as conflicts
|
|
464
|
+
* rather than silently producing an empty plan.
|
|
465
|
+
*
|
|
466
|
+
* Returns an empty array if no destructive changes are found.
|
|
467
|
+
*/
|
|
468
|
+
function detectDestructiveChanges(from, to) {
|
|
469
|
+
if (!from) return [];
|
|
470
|
+
const hasOwn = (value, key) => Object.hasOwn(value, key);
|
|
471
|
+
const conflicts = [];
|
|
472
|
+
for (const tableName of Object.keys(from.tables)) {
|
|
473
|
+
if (!hasOwn(to.tables, tableName)) {
|
|
474
|
+
conflicts.push({
|
|
475
|
+
kind: "tableRemoved",
|
|
476
|
+
summary: `Table "${tableName}" was removed`
|
|
477
|
+
});
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
const toTable = to.tables[tableName];
|
|
481
|
+
const fromTable = from.tables[tableName];
|
|
482
|
+
if (!fromTable) continue;
|
|
483
|
+
for (const columnName of Object.keys(fromTable.columns)) if (!hasOwn(toTable.columns, columnName)) conflicts.push({
|
|
484
|
+
kind: "columnRemoved",
|
|
485
|
+
summary: `Column "${tableName}"."${columnName}" was removed`
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
return conflicts;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Converts a `Contract` to `SqlSchemaIR`.
|
|
492
|
+
*
|
|
493
|
+
* Reads `contract.storage` for tables, `contract.storage.types` for type
|
|
494
|
+
* annotations, and derives database dependencies from `frameworkComponents`
|
|
495
|
+
* (each component's `databaseDependencies.init[].id`).
|
|
496
|
+
* Storage-type annotations are written under `options.annotationNamespace`.
|
|
497
|
+
*
|
|
498
|
+
* Drops codec metadata (`codecId`, `typeRef`) since the schema IR only represents
|
|
499
|
+
* structural information. When `expandNativeType` is provided, parameterized types
|
|
500
|
+
* are expanded (e.g. `character` + `{ length: 36 }` → `character(36)`) so the
|
|
501
|
+
* resulting IR compares correctly against the "to" contract during planning.
|
|
502
|
+
*
|
|
503
|
+
* Returns an empty schema IR when `contract` is `null` (new project).
|
|
504
|
+
*/
|
|
505
|
+
function contractToSchemaIR(contract, options) {
|
|
506
|
+
if (options.annotationNamespace.length === 0) throw new Error("annotationNamespace must be a non-empty string");
|
|
507
|
+
if (!contract) return {
|
|
508
|
+
tables: {},
|
|
509
|
+
dependencies: []
|
|
510
|
+
};
|
|
511
|
+
const storage = contract.storage;
|
|
512
|
+
const tables = {};
|
|
513
|
+
for (const [tableName, tableDef] of Object.entries(storage.tables)) tables[tableName] = convertTable(tableName, tableDef, options.expandNativeType, options.renderDefault);
|
|
514
|
+
return {
|
|
515
|
+
tables,
|
|
516
|
+
dependencies: deduplicateDependencyIRs(collectInitDependencies(options.frameworkComponents ?? [])),
|
|
517
|
+
...ifDefined("annotations", deriveAnnotations(storage, options.annotationNamespace))
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
function deduplicateDependencyIRs(deps) {
|
|
521
|
+
const seen = /* @__PURE__ */ new Set();
|
|
522
|
+
const result = [];
|
|
523
|
+
for (const dep of deps) {
|
|
524
|
+
if (dep.id.trim().length === 0) throw new Error("Dependency id must be a non-empty string");
|
|
525
|
+
if (seen.has(dep.id)) continue;
|
|
526
|
+
seen.add(dep.id);
|
|
527
|
+
result.push({ id: dep.id });
|
|
528
|
+
}
|
|
529
|
+
return result;
|
|
530
|
+
}
|
|
531
|
+
function deriveAnnotations(storage, annotationNamespace) {
|
|
532
|
+
if (!storage.types || Object.keys(storage.types).length === 0) return void 0;
|
|
533
|
+
const byNativeType = {};
|
|
534
|
+
for (const typeInstance of Object.values(storage.types)) byNativeType[typeInstance.nativeType] = typeInstance;
|
|
535
|
+
return { [annotationNamespace]: { storageTypes: byNativeType } };
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
//#endregion
|
|
539
|
+
//#region src/core/migrations/plan-helpers.ts
|
|
540
|
+
const readOnlyEmptyObject = Object.freeze({});
|
|
541
|
+
function cloneRecord(value) {
|
|
542
|
+
if (value === readOnlyEmptyObject) return value;
|
|
543
|
+
return Object.freeze({ ...value });
|
|
544
|
+
}
|
|
545
|
+
function freezeSteps(steps) {
|
|
546
|
+
if (steps.length === 0) return Object.freeze([]);
|
|
547
|
+
return Object.freeze(steps.map((step) => Object.freeze({
|
|
548
|
+
description: step.description,
|
|
549
|
+
sql: step.sql,
|
|
550
|
+
...step.meta ? { meta: cloneRecord(step.meta) } : {}
|
|
551
|
+
})));
|
|
552
|
+
}
|
|
553
|
+
function freezeDetailsValue(value) {
|
|
554
|
+
if (value === null || value === void 0) return value;
|
|
555
|
+
if (typeof value !== "object") return value;
|
|
556
|
+
if (Array.isArray(value)) return Object.freeze([...value]);
|
|
557
|
+
return Object.freeze({ ...value });
|
|
558
|
+
}
|
|
559
|
+
function freezeTargetDetails(target) {
|
|
560
|
+
return Object.freeze({
|
|
561
|
+
id: target.id,
|
|
562
|
+
...target.details !== void 0 ? { details: freezeDetailsValue(target.details) } : {}
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
function freezeOperation(operation) {
|
|
566
|
+
return Object.freeze({
|
|
567
|
+
id: operation.id,
|
|
568
|
+
label: operation.label,
|
|
569
|
+
...operation.summary ? { summary: operation.summary } : {},
|
|
570
|
+
operationClass: operation.operationClass,
|
|
571
|
+
target: freezeTargetDetails(operation.target),
|
|
572
|
+
precheck: freezeSteps(operation.precheck),
|
|
573
|
+
execute: freezeSteps(operation.execute),
|
|
574
|
+
postcheck: freezeSteps(operation.postcheck),
|
|
575
|
+
...operation.meta ? { meta: cloneRecord(operation.meta) } : {}
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
function freezeOperations(operations) {
|
|
579
|
+
if (operations.length === 0) return Object.freeze([]);
|
|
580
|
+
return Object.freeze(operations.map((operation) => freezeOperation(operation)));
|
|
581
|
+
}
|
|
582
|
+
function createMigrationPlan(options) {
|
|
583
|
+
return Object.freeze({
|
|
584
|
+
targetId: options.targetId,
|
|
585
|
+
...options.origin !== void 0 ? { origin: options.origin ? Object.freeze({ ...options.origin }) : null } : {},
|
|
586
|
+
destination: Object.freeze({ ...options.destination }),
|
|
587
|
+
operations: freezeOperations(options.operations),
|
|
588
|
+
...options.meta ? { meta: cloneRecord(options.meta) } : {}
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
function plannerSuccess(plan) {
|
|
592
|
+
return Object.freeze({
|
|
593
|
+
kind: "success",
|
|
594
|
+
plan
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
function plannerFailure(conflicts) {
|
|
598
|
+
return Object.freeze({
|
|
599
|
+
kind: "failure",
|
|
600
|
+
conflicts: Object.freeze(conflicts.map((conflict) => Object.freeze({
|
|
601
|
+
kind: conflict.kind,
|
|
602
|
+
summary: conflict.summary,
|
|
603
|
+
...conflict.why ? { why: conflict.why } : {},
|
|
604
|
+
...conflict.location ? { location: Object.freeze({ ...conflict.location }) } : {},
|
|
605
|
+
...conflict.meta ? { meta: cloneRecord(conflict.meta) } : {}
|
|
606
|
+
})))
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Creates a successful migration runner result.
|
|
611
|
+
*/
|
|
612
|
+
function runnerSuccess(value) {
|
|
613
|
+
return ok(Object.freeze({
|
|
614
|
+
operationsPlanned: value.operationsPlanned,
|
|
615
|
+
operationsExecuted: value.operationsExecuted
|
|
616
|
+
}));
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Creates a failed migration runner result.
|
|
620
|
+
*/
|
|
621
|
+
function runnerFailure(code, summary, options) {
|
|
622
|
+
return notOk(Object.freeze({
|
|
623
|
+
code,
|
|
624
|
+
summary,
|
|
625
|
+
...options?.why ? { why: options.why } : {},
|
|
626
|
+
...options?.meta ? { meta: cloneRecord(options.meta) } : {}
|
|
627
|
+
}));
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
//#endregion
|
|
631
|
+
//#region src/core/migrations/policies.ts
|
|
632
|
+
/**
|
|
633
|
+
* Policy used by `db init`: additive-only operations, no widening/destructive steps.
|
|
634
|
+
*/
|
|
635
|
+
const INIT_ADDITIVE_POLICY = Object.freeze({ allowedOperationClasses: Object.freeze(["additive"]) });
|
|
636
|
+
|
|
637
|
+
//#endregion
|
|
638
|
+
//#region src/exports/control.ts
|
|
639
|
+
var control_default = new SqlFamilyDescriptor();
|
|
640
|
+
|
|
641
|
+
//#endregion
|
|
642
|
+
export { INIT_ADDITIVE_POLICY, assembleAuthoringContributions, assembleControlMutationDefaultContributions, assemblePslInterpretationContributions, collectInitDependencies, contractToSchemaIR, createMigrationPlan, control_default as default, detectDestructiveChanges, extractCodecControlHooks, isDatabaseDependencyProvider, plannerFailure, plannerSuccess, runnerFailure, runnerSuccess };
|
|
643
|
+
//# sourceMappingURL=control.mjs.map
|