@prisma-next-idb/family-idb 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/bin/prisma-next-idb.d.mts +1 -0
- package/dist/bin/prisma-next-idb.mjs +281 -0
- package/dist/bin/prisma-next-idb.mjs.map +1 -0
- package/dist/exports/config-types.d.mts +39 -0
- package/dist/exports/config-types.d.mts.map +1 -0
- package/dist/exports/config-types.mjs +66 -0
- package/dist/exports/config-types.mjs.map +1 -0
- package/dist/exports/contract-psl.d.mts +48 -0
- package/dist/exports/contract-psl.d.mts.map +1 -0
- package/dist/exports/contract-psl.mjs +463 -0
- package/dist/exports/contract-psl.mjs.map +1 -0
- package/dist/exports/contract-ts.d.mts +73 -0
- package/dist/exports/contract-ts.d.mts.map +1 -0
- package/dist/exports/contract-ts.mjs +162 -0
- package/dist/exports/contract-ts.mjs.map +1 -0
- package/dist/exports/control.d.mts +79 -0
- package/dist/exports/control.d.mts.map +1 -0
- package/dist/exports/control.mjs +566 -0
- package/dist/exports/control.mjs.map +1 -0
- package/dist/exports/pack.d.mts +10 -0
- package/dist/exports/pack.d.mts.map +1 -0
- package/dist/exports/pack.mjs +11 -0
- package/dist/exports/pack.mjs.map +1 -0
- package/dist/generate-baseline-Dg3vfBpB.mjs +130 -0
- package/dist/generate-baseline-Dg3vfBpB.mjs.map +1 -0
- package/dist/generate-migration-D8bDx9jo.mjs +150 -0
- package/dist/generate-migration-D8bDx9jo.mjs.map +1 -0
- package/dist/preflight-D8GLQXy3.mjs +112 -0
- package/dist/preflight-D8GLQXy3.mjs.map +1 -0
- package/dist/validate-DL9NthnR.mjs +48 -0
- package/dist/validate-DL9NthnR.mjs.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
import { t as validateContract } from "../validate-DL9NthnR.mjs";
|
|
2
|
+
import { VERIFY_CODE_TARGET_MISMATCH } from "@prisma-next/framework-components/control";
|
|
3
|
+
//#region src/core/emission.ts
|
|
4
|
+
/**
|
|
5
|
+
* Converts a JavaScript primitive or plain object into a TypeScript type-literal
|
|
6
|
+
* string. Arrays become `readonly [...]` tuples; objects become `{ readonly k: v }`
|
|
7
|
+
* type literals; strings are single-quoted with escaping.
|
|
8
|
+
*/
|
|
9
|
+
function serializeValue(value) {
|
|
10
|
+
if (value === null) return "null";
|
|
11
|
+
if (value === void 0) return "undefined";
|
|
12
|
+
if (typeof value === "string") return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
13
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
14
|
+
if (typeof value === "bigint") return `${value}n`;
|
|
15
|
+
if (Array.isArray(value)) return `readonly [${value.map((v) => serializeValue(v)).join(", ")}]`;
|
|
16
|
+
if (typeof value === "object") {
|
|
17
|
+
const entries = [];
|
|
18
|
+
for (const [k, v] of Object.entries(value)) entries.push(`readonly ${serializeObjectKey(k)}: ${serializeValue(v)}`);
|
|
19
|
+
return `{ ${entries.join("; ")} }`;
|
|
20
|
+
}
|
|
21
|
+
return "unknown";
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Converts an object key to a safe TypeScript identifier. If the key is already a
|
|
25
|
+
* valid identifier it is returned as-is; otherwise it is wrapped in single-quoted
|
|
26
|
+
* string literal form.
|
|
27
|
+
*/
|
|
28
|
+
function serializeObjectKey(key) {
|
|
29
|
+
if (/^[$A-Z_a-z][$\w]*$/.test(key)) return key;
|
|
30
|
+
return serializeValue(key);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* IDB family emission plugin.
|
|
34
|
+
*
|
|
35
|
+
* Registered as `emission` on {@link IdbFamilyDescriptor}. The Prisma Next emitter
|
|
36
|
+
* calls these methods when generating `contract.d.ts` for any schema that targets the
|
|
37
|
+
* IDB family. Each method returns a raw TypeScript source string that the emitter
|
|
38
|
+
* splices into the generated file at the appropriate location.
|
|
39
|
+
*
|
|
40
|
+
* @see {@link https://github.com/prisma/prisma-next} for the emitter entry point
|
|
41
|
+
* (`generateContractDts` in `@prisma-next/emitter`).
|
|
42
|
+
*/
|
|
43
|
+
const idbEmission = {
|
|
44
|
+
id: "idb",
|
|
45
|
+
/**
|
|
46
|
+
* Serializes the full `storage` section of the contract into a TypeScript type
|
|
47
|
+
* literal. The result is used as the first generic argument of `ContractType<...>`
|
|
48
|
+
* in the generated `contract.d.ts`.
|
|
49
|
+
*
|
|
50
|
+
* For IDB the storage shape is:
|
|
51
|
+
* ```ts
|
|
52
|
+
* { readonly stores: { readonly <storeName>: { readonly keyPath: '...'; ... }; ... };
|
|
53
|
+
* readonly storageHash: <storageHashTypeName> }
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @param contract - The resolved Prisma contract.
|
|
57
|
+
* @param storageHashTypeName - Emitter-provided name of the `StorageHash` type alias
|
|
58
|
+
* to embed (e.g. `'StorageHash'`).
|
|
59
|
+
*/
|
|
60
|
+
generateStorageType(contract, storageHashTypeName) {
|
|
61
|
+
const storage = contract.storage;
|
|
62
|
+
const stores = [];
|
|
63
|
+
for (const [storeName, store] of Object.entries(storage.stores).sort(([a], [b]) => a.localeCompare(b))) {
|
|
64
|
+
const storeParts = [`readonly keyPath: ${serializeValue(store.keyPath)}`];
|
|
65
|
+
if (store.autoIncrement !== void 0) storeParts.push(`readonly autoIncrement: ${store.autoIncrement}`);
|
|
66
|
+
const indexes = store.indexes ?? {};
|
|
67
|
+
if (Object.keys(indexes).length > 0) {
|
|
68
|
+
const indexEntries = [];
|
|
69
|
+
for (const [indexName, index] of Object.entries(indexes).sort(([a], [b]) => a.localeCompare(b))) {
|
|
70
|
+
const indexParts = [`readonly keyPath: ${serializeValue(index.keyPath)}`, `readonly unique: ${index.unique}`];
|
|
71
|
+
if (index.multiEntry !== void 0) indexParts.push(`readonly multiEntry: ${index.multiEntry}`);
|
|
72
|
+
indexEntries.push(`readonly ${serializeObjectKey(indexName)}: { ${indexParts.join("; ")} }`);
|
|
73
|
+
}
|
|
74
|
+
storeParts.push(`readonly indexes: { ${indexEntries.join("; ")} }`);
|
|
75
|
+
} else storeParts.push(`readonly indexes: Record<string, never>`);
|
|
76
|
+
stores.push(`readonly ${serializeObjectKey(storeName)}: { ${storeParts.join("; ")} }`);
|
|
77
|
+
}
|
|
78
|
+
const storesType = stores.length > 0 ? `{ ${stores.join("; ")} }` : "Record<string, never>";
|
|
79
|
+
const nsEntries = [];
|
|
80
|
+
for (const [nsId, ns] of Object.entries(storage.namespaces ?? {}).sort(([a], [b]) => a.localeCompare(b))) {
|
|
81
|
+
const entriesObj = ns.entries ?? {};
|
|
82
|
+
const entriesType = Object.keys(entriesObj).length === 0 ? "Record<string, never>" : serializeValue(entriesObj);
|
|
83
|
+
nsEntries.push(`readonly ${serializeObjectKey(nsId)}: { readonly id: ${serializeValue(ns.id)}; readonly entries: ${entriesType} }`);
|
|
84
|
+
}
|
|
85
|
+
return `{ readonly stores: ${storesType}; readonly namespaces: ${nsEntries.length > 0 ? `{ ${nsEntries.join("; ")} }` : "Record<string, never>"}; readonly storageHash: ${storageHashTypeName} }`;
|
|
86
|
+
},
|
|
87
|
+
/**
|
|
88
|
+
* Serializes the per-model storage metadata into a TypeScript type literal.
|
|
89
|
+
* The result is used as the `storage` field of the model's entry in the
|
|
90
|
+
* generated `models` type.
|
|
91
|
+
*
|
|
92
|
+
* For IDB, each model maps to exactly one object store:
|
|
93
|
+
* ```ts
|
|
94
|
+
* { readonly storeName: '<name>'; readonly keyPath: '<field>' }
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* @param _modelName - The Prisma model name (unused; provided for API symmetry).
|
|
98
|
+
* @param model - The resolved `ContractModel` whose `storage` is cast to
|
|
99
|
+
* {@link IdbModelStorage}.
|
|
100
|
+
*/
|
|
101
|
+
generateModelStorageType(_modelName, model) {
|
|
102
|
+
const idbModel = model;
|
|
103
|
+
return [`{ readonly storeName: ${serializeValue(idbModel.storage.storeName)}`, `readonly keyPath: ${serializeValue(idbModel.storage.keyPath)} }`].join("; ");
|
|
104
|
+
},
|
|
105
|
+
/**
|
|
106
|
+
* Returns the `import type` lines prepended to `contract.d.ts` that bring
|
|
107
|
+
* IDB-family types into scope.
|
|
108
|
+
*
|
|
109
|
+
* Imports {@link IdbContractWithTypeMaps} (the phantom wrapper used by
|
|
110
|
+
* {@link getContractWrapper}) and {@link IdbTypeMaps} (used by
|
|
111
|
+
* {@link getTypeMapsExpression}).
|
|
112
|
+
*/
|
|
113
|
+
getFamilyImports() {
|
|
114
|
+
return ["import type { IdbContractWithTypeMaps, IdbTypeMaps } from '@prisma-next-idb/target-idb/pack';"];
|
|
115
|
+
},
|
|
116
|
+
/**
|
|
117
|
+
* Returns additional type alias declarations appended to `contract.d.ts`
|
|
118
|
+
* after the codec and operation type aliases.
|
|
119
|
+
*
|
|
120
|
+
* For IDB this exports `LaneCodecTypes` (used by the generated client to
|
|
121
|
+
* look up codec input/output pairs) as a simple alias of the assembled
|
|
122
|
+
* `CodecTypes`. IDB has no query-operation types, so no further aliases
|
|
123
|
+
* are needed.
|
|
124
|
+
*/
|
|
125
|
+
getFamilyTypeAliases() {
|
|
126
|
+
return "export type LaneCodecTypes = CodecTypes;";
|
|
127
|
+
},
|
|
128
|
+
/**
|
|
129
|
+
* Returns the TypeScript expression for the `TypeMaps<...>` generic
|
|
130
|
+
* instantiation written into `contract.d.ts`.
|
|
131
|
+
*
|
|
132
|
+
* IDB has no operation types or query-operation types, so the expression
|
|
133
|
+
* uses the three-argument form of {@link IdbTypeMaps}.
|
|
134
|
+
*/
|
|
135
|
+
getTypeMapsExpression() {
|
|
136
|
+
return "IdbTypeMaps<CodecTypes, FieldOutputTypes, FieldInputTypes>";
|
|
137
|
+
},
|
|
138
|
+
/**
|
|
139
|
+
* Returns the final `export type Contract = ...` declaration and convenience
|
|
140
|
+
* re-exports derived from it.
|
|
141
|
+
*
|
|
142
|
+
* @param contractBaseName - Emitter-provided name of the base contract type
|
|
143
|
+
* (always `'ContractBase'`).
|
|
144
|
+
* @param typeMapsName - Emitter-provided name of the assembled TypeMaps type
|
|
145
|
+
* (always `'TypeMaps'`).
|
|
146
|
+
*/
|
|
147
|
+
getContractWrapper(contractBaseName, typeMapsName) {
|
|
148
|
+
return [
|
|
149
|
+
`export type Contract = IdbContractWithTypeMaps<${contractBaseName}, ${typeMapsName}>;`,
|
|
150
|
+
"",
|
|
151
|
+
"export type Stores = Contract['storage']['stores'];"
|
|
152
|
+
].join("\n");
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/core/schema-verify.ts
|
|
157
|
+
function passNode(kind, name, contractPath, children = []) {
|
|
158
|
+
return {
|
|
159
|
+
status: "pass",
|
|
160
|
+
kind,
|
|
161
|
+
name,
|
|
162
|
+
contractPath,
|
|
163
|
+
code: "ok",
|
|
164
|
+
message: `${name} matches`,
|
|
165
|
+
expected: null,
|
|
166
|
+
actual: null,
|
|
167
|
+
children
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function failNode(kind, name, contractPath, code, message, expected, actual, children = []) {
|
|
171
|
+
return {
|
|
172
|
+
status: "fail",
|
|
173
|
+
kind,
|
|
174
|
+
name,
|
|
175
|
+
contractPath,
|
|
176
|
+
code,
|
|
177
|
+
message,
|
|
178
|
+
expected,
|
|
179
|
+
actual,
|
|
180
|
+
children
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function warnNode(kind, name, contractPath, code, message, expected, actual, children = []) {
|
|
184
|
+
return {
|
|
185
|
+
status: "warn",
|
|
186
|
+
kind,
|
|
187
|
+
name,
|
|
188
|
+
contractPath,
|
|
189
|
+
code,
|
|
190
|
+
message,
|
|
191
|
+
expected,
|
|
192
|
+
actual,
|
|
193
|
+
children
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function countStatuses(nodes) {
|
|
197
|
+
let pass = 0, warn = 0, fail = 0, totalNodes = 0;
|
|
198
|
+
const stack = [...nodes];
|
|
199
|
+
while (stack.length > 0) {
|
|
200
|
+
const node = stack.pop();
|
|
201
|
+
totalNodes++;
|
|
202
|
+
if (node.status === "pass") pass++;
|
|
203
|
+
else if (node.status === "warn") warn++;
|
|
204
|
+
else fail++;
|
|
205
|
+
stack.push(...node.children);
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
pass,
|
|
209
|
+
warn,
|
|
210
|
+
fail,
|
|
211
|
+
totalNodes
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function verifyIndex(indexName, contractIndex, actualIndex, storePath, issues) {
|
|
215
|
+
const indexPath = `${storePath}.indexes.${indexName}`;
|
|
216
|
+
if (actualIndex === void 0) {
|
|
217
|
+
const msg = `Index "${indexName}" defined in contract is missing from manifest schema`;
|
|
218
|
+
issues.push({
|
|
219
|
+
kind: "missing_column",
|
|
220
|
+
table: storePath,
|
|
221
|
+
column: indexName,
|
|
222
|
+
message: msg
|
|
223
|
+
});
|
|
224
|
+
return failNode("index", indexName, indexPath, "missing_column", msg, contractIndex, void 0);
|
|
225
|
+
}
|
|
226
|
+
const children = [];
|
|
227
|
+
if (contractIndex.keyPath !== actualIndex.keyPath) {
|
|
228
|
+
const msg = `Index "${indexName}" keyPath mismatch: expected "${contractIndex.keyPath}", got "${actualIndex.keyPath}"`;
|
|
229
|
+
issues.push({
|
|
230
|
+
kind: "index_mismatch",
|
|
231
|
+
table: storePath,
|
|
232
|
+
indexOrConstraint: indexName,
|
|
233
|
+
message: msg
|
|
234
|
+
});
|
|
235
|
+
children.push(failNode("field", "keyPath", `${indexPath}.keyPath`, "index_mismatch", msg, contractIndex.keyPath, actualIndex.keyPath));
|
|
236
|
+
} else children.push(passNode("field", "keyPath", `${indexPath}.keyPath`));
|
|
237
|
+
if ((contractIndex.unique ?? false) !== (actualIndex.unique ?? false)) {
|
|
238
|
+
const msg = `Index "${indexName}" unique mismatch: expected ${contractIndex.unique}, got ${actualIndex.unique}`;
|
|
239
|
+
issues.push({
|
|
240
|
+
kind: "index_mismatch",
|
|
241
|
+
table: storePath,
|
|
242
|
+
indexOrConstraint: indexName,
|
|
243
|
+
message: msg
|
|
244
|
+
});
|
|
245
|
+
children.push(failNode("field", "unique", `${indexPath}.unique`, "index_mismatch", msg, contractIndex.unique, actualIndex.unique));
|
|
246
|
+
} else children.push(passNode("field", "unique", `${indexPath}.unique`));
|
|
247
|
+
const contractME = contractIndex.multiEntry ?? false;
|
|
248
|
+
const actualME = actualIndex.multiEntry ?? false;
|
|
249
|
+
if (contractME !== actualME) {
|
|
250
|
+
const msg = `Index "${indexName}" multiEntry mismatch: expected ${contractME}, got ${actualME}`;
|
|
251
|
+
issues.push({
|
|
252
|
+
kind: "index_mismatch",
|
|
253
|
+
table: storePath,
|
|
254
|
+
indexOrConstraint: indexName,
|
|
255
|
+
message: msg
|
|
256
|
+
});
|
|
257
|
+
children.push(failNode("field", "multiEntry", `${indexPath}.multiEntry`, "index_mismatch", msg, contractME, actualME));
|
|
258
|
+
} else if (contractME) children.push(passNode("field", "multiEntry", `${indexPath}.multiEntry`));
|
|
259
|
+
if (children.some((c) => c.status === "fail")) return failNode("index", indexName, indexPath, "index_mismatch", `Index "${indexName}" has mismatches`, null, null, children);
|
|
260
|
+
return passNode("index", indexName, indexPath, children);
|
|
261
|
+
}
|
|
262
|
+
function verifyStore(storeName, contractStore, actualStore, strict, issues) {
|
|
263
|
+
const storePath = `storage.stores.${storeName}`;
|
|
264
|
+
if (actualStore === void 0) {
|
|
265
|
+
const msg = `Object store "${storeName}" defined in contract is missing from manifest schema`;
|
|
266
|
+
issues.push({
|
|
267
|
+
kind: "missing_table",
|
|
268
|
+
table: storeName,
|
|
269
|
+
message: msg
|
|
270
|
+
});
|
|
271
|
+
return failNode("collection", storeName, storePath, "missing_table", msg, contractStore, void 0);
|
|
272
|
+
}
|
|
273
|
+
const children = [];
|
|
274
|
+
if (contractStore.keyPath !== actualStore.keyPath) {
|
|
275
|
+
const msg = `Store "${storeName}" keyPath mismatch: expected "${contractStore.keyPath}", got "${actualStore.keyPath}"`;
|
|
276
|
+
issues.push({
|
|
277
|
+
kind: "primary_key_mismatch",
|
|
278
|
+
table: storeName,
|
|
279
|
+
message: msg
|
|
280
|
+
});
|
|
281
|
+
children.push(failNode("field", "keyPath", `${storePath}.keyPath`, "primary_key_mismatch", msg, contractStore.keyPath, actualStore.keyPath));
|
|
282
|
+
} else children.push(passNode("field", "keyPath", `${storePath}.keyPath`));
|
|
283
|
+
const contractAI = contractStore.autoIncrement ?? false;
|
|
284
|
+
const actualAI = actualStore.autoIncrement ?? false;
|
|
285
|
+
if (contractAI !== actualAI) {
|
|
286
|
+
const msg = `Store "${storeName}" autoIncrement mismatch: expected ${contractAI}, got ${actualAI}`;
|
|
287
|
+
issues.push({
|
|
288
|
+
kind: "type_mismatch",
|
|
289
|
+
table: storeName,
|
|
290
|
+
message: msg
|
|
291
|
+
});
|
|
292
|
+
children.push(failNode("field", "autoIncrement", `${storePath}.autoIncrement`, "type_mismatch", msg, contractAI, actualAI));
|
|
293
|
+
} else if (contractAI) children.push(passNode("field", "autoIncrement", `${storePath}.autoIncrement`));
|
|
294
|
+
const contractIndexes = contractStore.indexes ?? {};
|
|
295
|
+
const actualIndexes = actualStore.indexes ?? {};
|
|
296
|
+
for (const [indexName, contractIndex] of Object.entries(contractIndexes)) {
|
|
297
|
+
const actualIndex = actualIndexes[indexName];
|
|
298
|
+
children.push(verifyIndex(indexName, contractIndex, actualIndex, storePath, issues));
|
|
299
|
+
}
|
|
300
|
+
for (const indexName of Object.keys(actualIndexes)) if (!(indexName in contractIndexes)) {
|
|
301
|
+
const msg = `Index "${indexName}" exists in manifest but is not in contract`;
|
|
302
|
+
if (strict) {
|
|
303
|
+
issues.push({
|
|
304
|
+
kind: "extra_index",
|
|
305
|
+
table: storeName,
|
|
306
|
+
indexOrConstraint: indexName,
|
|
307
|
+
message: msg
|
|
308
|
+
});
|
|
309
|
+
children.push(failNode("index", indexName, `${storePath}.indexes.${indexName}`, "extra_index", msg, void 0, actualIndexes[indexName]));
|
|
310
|
+
} else children.push(warnNode("index", indexName, `${storePath}.indexes.${indexName}`, "extra_index", msg, void 0, actualIndexes[indexName]));
|
|
311
|
+
}
|
|
312
|
+
if (children.some((c) => c.status === "fail")) return failNode("collection", storeName, storePath, "schema_mismatch", `Store "${storeName}" has schema issues`, null, null, children);
|
|
313
|
+
if (children.some((c) => c.status === "warn")) return {
|
|
314
|
+
status: "warn",
|
|
315
|
+
kind: "collection",
|
|
316
|
+
name: storeName,
|
|
317
|
+
contractPath: storePath,
|
|
318
|
+
code: "ok",
|
|
319
|
+
message: `Store "${storeName}" has warnings`,
|
|
320
|
+
expected: null,
|
|
321
|
+
actual: null,
|
|
322
|
+
children
|
|
323
|
+
};
|
|
324
|
+
return passNode("collection", storeName, storePath, children);
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Pure, synchronous schema verification for IndexedDB.
|
|
328
|
+
*
|
|
329
|
+
* Compares the stores and indexes defined in the contract against the manifest
|
|
330
|
+
* schema (which represents the actual IndexedDB database). Returns a
|
|
331
|
+
* {@link VerifyDatabaseSchemaResult} that callers can inspect for issues.
|
|
332
|
+
*
|
|
333
|
+
* Does not perform any I/O — all inputs must already be available.
|
|
334
|
+
*
|
|
335
|
+
* @param contract - Validated IDB contract (what we expect the schema to be).
|
|
336
|
+
* @param schema - Schema IR from the manifest (what the schema actually is).
|
|
337
|
+
* @param strict - When `true`, extra stores/indexes in the manifest are failures.
|
|
338
|
+
* @param options - Optional context paths for metadata / error reporting.
|
|
339
|
+
*/
|
|
340
|
+
function verifyIdbSchema(contract, schema, strict) {
|
|
341
|
+
const start = Date.now();
|
|
342
|
+
const issues = [];
|
|
343
|
+
const storeNodes = [];
|
|
344
|
+
const contractStores = contract.storage.stores;
|
|
345
|
+
const actualStores = schema.stores;
|
|
346
|
+
for (const [storeName, contractStore] of Object.entries(contractStores)) {
|
|
347
|
+
const actualStore = actualStores[storeName];
|
|
348
|
+
storeNodes.push(verifyStore(storeName, contractStore, actualStore, strict, issues));
|
|
349
|
+
}
|
|
350
|
+
for (const [storeName, actualStore] of Object.entries(actualStores)) if (!(storeName in contractStores)) {
|
|
351
|
+
const msg = `Object store "${storeName}" exists in manifest but is not in contract`;
|
|
352
|
+
if (strict) {
|
|
353
|
+
issues.push({
|
|
354
|
+
kind: "extra_table",
|
|
355
|
+
table: storeName,
|
|
356
|
+
message: msg
|
|
357
|
+
});
|
|
358
|
+
storeNodes.push(failNode("collection", storeName, `storage.stores.${storeName}`, "extra_table", msg, void 0, actualStore));
|
|
359
|
+
} else storeNodes.push(warnNode("collection", storeName, `storage.stores.${storeName}`, "extra_table", msg, void 0, actualStore));
|
|
360
|
+
}
|
|
361
|
+
const root = {
|
|
362
|
+
status: storeNodes.some((n) => n.status === "fail") ? "fail" : storeNodes.some((n) => n.status === "warn") ? "warn" : "pass",
|
|
363
|
+
kind: "root",
|
|
364
|
+
name: "idb",
|
|
365
|
+
contractPath: "storage.stores",
|
|
366
|
+
code: issues.length === 0 ? "ok" : "schema_mismatch",
|
|
367
|
+
message: issues.length === 0 ? "All stores and indexes match" : `${issues.length} schema issue(s) found`,
|
|
368
|
+
expected: null,
|
|
369
|
+
actual: null,
|
|
370
|
+
children: storeNodes
|
|
371
|
+
};
|
|
372
|
+
const counts = countStatuses([root, ...storeNodes]);
|
|
373
|
+
const ok = root.status !== "fail";
|
|
374
|
+
const storageHash = contract.storage.storageHash;
|
|
375
|
+
const profileHash = contract.profileHash;
|
|
376
|
+
const contractField = profileHash !== void 0 ? {
|
|
377
|
+
storageHash,
|
|
378
|
+
profileHash
|
|
379
|
+
} : { storageHash };
|
|
380
|
+
const metaField = { strict };
|
|
381
|
+
const sharedResult = {
|
|
382
|
+
summary: ok ? "Schema verification passed" : `Schema verification failed: ${issues.length} issue(s)`,
|
|
383
|
+
contract: contractField,
|
|
384
|
+
target: { expected: "idb" },
|
|
385
|
+
schema: {
|
|
386
|
+
issues,
|
|
387
|
+
root,
|
|
388
|
+
counts
|
|
389
|
+
},
|
|
390
|
+
meta: metaField,
|
|
391
|
+
timings: { total: Date.now() - start }
|
|
392
|
+
};
|
|
393
|
+
if (ok) return {
|
|
394
|
+
ok: true,
|
|
395
|
+
...sharedResult
|
|
396
|
+
};
|
|
397
|
+
return {
|
|
398
|
+
ok: false,
|
|
399
|
+
code: VERIFY_CODE_SCHEMA_FAILURE,
|
|
400
|
+
...sharedResult
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
const VERIFY_CODE_SCHEMA_FAILURE = "PN-RUN-3010";
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region src/core/control-instance.ts
|
|
406
|
+
const REFUSAL_CODE = "IDB-CLI-UNSUPPORTED";
|
|
407
|
+
/**
|
|
408
|
+
* Single-string refusal message baked into `summary` because the framework
|
|
409
|
+
* `VerifyDatabaseResult` / `SignDatabaseResult` `meta` field is strict-shaped
|
|
410
|
+
* (`{ contractPath, configPath? }`) and doesn't accept extra explanation
|
|
411
|
+
* fields. The summary is the only free-form text field on every result.
|
|
412
|
+
*/
|
|
413
|
+
function refusalSummary(action) {
|
|
414
|
+
return `IndexedDB cannot be ${action} from the CLI. IndexedDB only exists in the browser; the CLI runs in Node.js, so there is no live database for it to inspect or update. Author migrations with \`prisma-next migration new\` / \`migration plan\`, validate the chain with \`prisma-next-idb preflight\`, and let \`createAutoMigratingIdbClient\` apply them the next time the app opens in a browser.`;
|
|
415
|
+
}
|
|
416
|
+
function contractInfo(storageHash, profileHash) {
|
|
417
|
+
return profileHash !== void 0 ? {
|
|
418
|
+
storageHash,
|
|
419
|
+
profileHash
|
|
420
|
+
} : { storageHash };
|
|
421
|
+
}
|
|
422
|
+
function verifyMeta(contractPath, configPath) {
|
|
423
|
+
return configPath !== void 0 ? {
|
|
424
|
+
contractPath,
|
|
425
|
+
configPath
|
|
426
|
+
} : { contractPath };
|
|
427
|
+
}
|
|
428
|
+
function signMeta(contractPath, configPath) {
|
|
429
|
+
return configPath !== void 0 ? {
|
|
430
|
+
contractPath,
|
|
431
|
+
configPath
|
|
432
|
+
} : { contractPath };
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Creates an IDB control family instance for the given control stack.
|
|
436
|
+
*
|
|
437
|
+
* **CLI surface — refusals**: IndexedDB is a browser API, so the CLI cannot
|
|
438
|
+
* read or write the live database. Every method that would normally talk to a
|
|
439
|
+
* database (`verify`, `sign`, `readMarker`, `readAllMarkers`, `introspect`)
|
|
440
|
+
* returns a structured refusal pointing the user at the contract-space
|
|
441
|
+
* authoring + preflight workflow.
|
|
442
|
+
*
|
|
443
|
+
* The CLI-side `db init`, `db update`, and `db verify` commands therefore
|
|
444
|
+
* surface a uniform `IDB-CLI-UNSUPPORTED` envelope rather than silently
|
|
445
|
+
* succeeding.
|
|
446
|
+
*
|
|
447
|
+
* **Active surface**: `deserializeContract` (pure) and `verifySchema` (pure
|
|
448
|
+
* function over an in-memory `IdbSchemaIR`) continue to work — neither
|
|
449
|
+
* needs a live database.
|
|
450
|
+
*
|
|
451
|
+
* @param _stack - The assembled control stack (unused; IDB has no adapter/extension layer).
|
|
452
|
+
*/
|
|
453
|
+
function createIdbFamilyInstance(_stack) {
|
|
454
|
+
return {
|
|
455
|
+
familyId: "idb",
|
|
456
|
+
deserializeContract(contractJson) {
|
|
457
|
+
return validateContract(contractJson);
|
|
458
|
+
},
|
|
459
|
+
async verify(options) {
|
|
460
|
+
const start = Date.now();
|
|
461
|
+
const contract = validateContract(options.contract);
|
|
462
|
+
const storageHash = contract.storage.storageHash;
|
|
463
|
+
const profileHash = contract.profileHash;
|
|
464
|
+
if (options.expectedTargetId !== "idb") return {
|
|
465
|
+
ok: false,
|
|
466
|
+
code: VERIFY_CODE_TARGET_MISMATCH,
|
|
467
|
+
summary: `Target mismatch: expected "idb", got "${options.expectedTargetId}"`,
|
|
468
|
+
contract: contractInfo(storageHash, profileHash),
|
|
469
|
+
target: {
|
|
470
|
+
expected: options.expectedTargetId,
|
|
471
|
+
actual: "idb"
|
|
472
|
+
},
|
|
473
|
+
meta: verifyMeta(options.contractPath, options.configPath),
|
|
474
|
+
timings: { total: Date.now() - start }
|
|
475
|
+
};
|
|
476
|
+
return {
|
|
477
|
+
ok: false,
|
|
478
|
+
code: REFUSAL_CODE,
|
|
479
|
+
summary: refusalSummary("verified"),
|
|
480
|
+
contract: contractInfo(storageHash, profileHash),
|
|
481
|
+
target: {
|
|
482
|
+
expected: "idb",
|
|
483
|
+
actual: "idb"
|
|
484
|
+
},
|
|
485
|
+
meta: verifyMeta(options.contractPath, options.configPath),
|
|
486
|
+
timings: { total: Date.now() - start }
|
|
487
|
+
};
|
|
488
|
+
},
|
|
489
|
+
verifySchema(options) {
|
|
490
|
+
return verifyIdbSchema(validateContract(options.contract), options.schema, options.strict);
|
|
491
|
+
},
|
|
492
|
+
async sign(options) {
|
|
493
|
+
const start = Date.now();
|
|
494
|
+
const contract = validateContract(options.contract);
|
|
495
|
+
const storageHash = contract.storage.storageHash;
|
|
496
|
+
const profileHash = contract.profileHash ?? "";
|
|
497
|
+
return {
|
|
498
|
+
ok: false,
|
|
499
|
+
summary: refusalSummary("signed"),
|
|
500
|
+
contract: contractInfo(storageHash, profileHash),
|
|
501
|
+
target: {
|
|
502
|
+
expected: "idb",
|
|
503
|
+
actual: "idb"
|
|
504
|
+
},
|
|
505
|
+
marker: {
|
|
506
|
+
created: false,
|
|
507
|
+
updated: false
|
|
508
|
+
},
|
|
509
|
+
meta: signMeta(options.contractPath, options.configPath),
|
|
510
|
+
timings: { total: Date.now() - start }
|
|
511
|
+
};
|
|
512
|
+
},
|
|
513
|
+
async readMarker(_options) {
|
|
514
|
+
return null;
|
|
515
|
+
},
|
|
516
|
+
async readAllMarkers(_options) {
|
|
517
|
+
return /* @__PURE__ */ new Map();
|
|
518
|
+
},
|
|
519
|
+
async readLedger(_options) {
|
|
520
|
+
return [];
|
|
521
|
+
},
|
|
522
|
+
async introspect(_options) {
|
|
523
|
+
return { stores: {} };
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
//#endregion
|
|
528
|
+
//#region src/core/control-descriptor.ts
|
|
529
|
+
/**
|
|
530
|
+
* IDB family descriptor — the control-plane entry point for the IDB family.
|
|
531
|
+
*
|
|
532
|
+
* Registered in a Prisma Next config file under the `family` key:
|
|
533
|
+
* ```ts
|
|
534
|
+
* import idb from '@prisma-next-idb/family-idb/control';
|
|
535
|
+
* export default defineConfig({ family: idb, ... });
|
|
536
|
+
* ```
|
|
537
|
+
*
|
|
538
|
+
* **Responsibilities:**
|
|
539
|
+
* - Carries the {@link idbEmission} plugin used by `prisma-next contract emit`
|
|
540
|
+
* to generate `contract.d.ts`.
|
|
541
|
+
* - Acts as a factory: `create(stack)` returns an {@link IdbControlFamilyInstance}
|
|
542
|
+
* with domain-action methods consumed by the CLI.
|
|
543
|
+
*/
|
|
544
|
+
var IdbFamilyDescriptor = class {
|
|
545
|
+
kind = "family";
|
|
546
|
+
id = "idb";
|
|
547
|
+
familyId = "idb";
|
|
548
|
+
version = "0.0.1";
|
|
549
|
+
emission = idbEmission;
|
|
550
|
+
/**
|
|
551
|
+
* Creates an {@link IdbControlFamilyInstance} for the given control stack.
|
|
552
|
+
*
|
|
553
|
+
* The instance is a plain object whose methods implement the CLI domain
|
|
554
|
+
* actions (validateContract, verify, sign, etc.).
|
|
555
|
+
*/
|
|
556
|
+
create(stack) {
|
|
557
|
+
return createIdbFamilyInstance(stack);
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
//#endregion
|
|
561
|
+
//#region src/exports/control.ts
|
|
562
|
+
var control_default = new IdbFamilyDescriptor();
|
|
563
|
+
//#endregion
|
|
564
|
+
export { control_default as default };
|
|
565
|
+
|
|
566
|
+
//# sourceMappingURL=control.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control.mjs","names":[],"sources":["../../src/core/emission.ts","../../src/core/schema-verify.ts","../../src/core/control-instance.ts","../../src/core/control-descriptor.ts","../../src/exports/control.ts"],"sourcesContent":["import type { Contract, ContractModel } from \"@prisma-next/contract/types\";\nimport type { EmissionSpi } from \"@prisma-next/framework-components/emission\";\nimport type { IdbModelStorage, IdbStorage } from \"@prisma-next-idb/target-idb/pack\";\n\n// ── Serialization utilities ──────────────────────────────────────────────────\n// Adapted from @prisma-next/emitter/domain-type-generation.\n// These utilities convert runtime JS values into TypeScript type-literal strings\n// that are spliced verbatim into the generated contract.d.ts file.\n\n/**\n * Converts a JavaScript primitive or plain object into a TypeScript type-literal\n * string. Arrays become `readonly [...]` tuples; objects become `{ readonly k: v }`\n * type literals; strings are single-quoted with escaping.\n */\nfunction serializeValue(value: unknown): string {\n if (value === null) return \"null\";\n if (value === undefined) return \"undefined\";\n if (typeof value === \"string\") {\n const escaped = value.replace(/\\\\/g, \"\\\\\\\\\").replace(/'/g, \"\\\\'\");\n return `'${escaped}'`;\n }\n if (typeof value === \"number\" || typeof value === \"boolean\") return String(value);\n if (typeof value === \"bigint\") return `${value}n`;\n if (Array.isArray(value)) {\n const items = value.map((v) => serializeValue(v)).join(\", \");\n return `readonly [${items}]`;\n }\n if (typeof value === \"object\") {\n const entries: string[] = [];\n for (const [k, v] of Object.entries(value)) {\n entries.push(`readonly ${serializeObjectKey(k)}: ${serializeValue(v)}`);\n }\n return `{ ${entries.join(\"; \")} }`;\n }\n return \"unknown\";\n}\n\n/**\n * Converts an object key to a safe TypeScript identifier. If the key is already a\n * valid identifier it is returned as-is; otherwise it is wrapped in single-quoted\n * string literal form.\n */\nfunction serializeObjectKey(key: string): string {\n if (/^[$A-Z_a-z][$\\w]*$/.test(key)) return key;\n return serializeValue(key);\n}\n\n// ── EmissionSpi ───────────────────────────────────────────────────────────────\n\n/**\n * IDB family emission plugin.\n *\n * Registered as `emission` on {@link IdbFamilyDescriptor}. The Prisma Next emitter\n * calls these methods when generating `contract.d.ts` for any schema that targets the\n * IDB family. Each method returns a raw TypeScript source string that the emitter\n * splices into the generated file at the appropriate location.\n *\n * @see {@link https://github.com/prisma/prisma-next} for the emitter entry point\n * (`generateContractDts` in `@prisma-next/emitter`).\n */\nexport const idbEmission = {\n id: \"idb\",\n\n /**\n * Serializes the full `storage` section of the contract into a TypeScript type\n * literal. The result is used as the first generic argument of `ContractType<...>`\n * in the generated `contract.d.ts`.\n *\n * For IDB the storage shape is:\n * ```ts\n * { readonly stores: { readonly <storeName>: { readonly keyPath: '...'; ... }; ... };\n * readonly storageHash: <storageHashTypeName> }\n * ```\n *\n * @param contract - The resolved Prisma contract.\n * @param storageHashTypeName - Emitter-provided name of the `StorageHash` type alias\n * to embed (e.g. `'StorageHash'`).\n */\n generateStorageType(contract: Contract, storageHashTypeName: string): string {\n const storage = contract.storage as IdbStorage;\n const stores: string[] = [];\n\n for (const [storeName, store] of Object.entries(storage.stores).sort(([a], [b]) => a.localeCompare(b))) {\n const storeParts: string[] = [`readonly keyPath: ${serializeValue(store.keyPath)}`];\n\n if (store.autoIncrement !== undefined) {\n storeParts.push(`readonly autoIncrement: ${store.autoIncrement}`);\n }\n\n const indexes = store.indexes ?? {};\n if (Object.keys(indexes).length > 0) {\n const indexEntries: string[] = [];\n for (const [indexName, index] of Object.entries(indexes).sort(([a], [b]) => a.localeCompare(b))) {\n const indexParts = [`readonly keyPath: ${serializeValue(index.keyPath)}`, `readonly unique: ${index.unique}`];\n if (index.multiEntry !== undefined) {\n indexParts.push(`readonly multiEntry: ${index.multiEntry}`);\n }\n indexEntries.push(`readonly ${serializeObjectKey(indexName)}: { ${indexParts.join(\"; \")} }`);\n }\n storeParts.push(`readonly indexes: { ${indexEntries.join(\"; \")} }`);\n } else {\n storeParts.push(`readonly indexes: Record<string, never>`);\n }\n\n stores.push(`readonly ${serializeObjectKey(storeName)}: { ${storeParts.join(\"; \")} }`);\n }\n\n const storesType = stores.length > 0 ? `{ ${stores.join(\"; \")} }` : \"Record<string, never>\";\n\n // v0.12.0: StorageBase now requires `namespaces`. Generate the literal type from\n // the contract's storage namespaces so the emitted storage type satisfies StorageBase.\n const nsEntries: string[] = [];\n for (const [nsId, ns] of Object.entries(\n (storage as unknown as { namespaces?: Record<string, { id: string; entries?: Record<string, unknown> }> })\n .namespaces ?? {}\n ).sort(([a], [b]) => a.localeCompare(b))) {\n const entriesObj = ns.entries ?? {};\n const entriesType = Object.keys(entriesObj).length === 0 ? \"Record<string, never>\" : serializeValue(entriesObj);\n nsEntries.push(\n `readonly ${serializeObjectKey(nsId)}: { readonly id: ${serializeValue(ns.id)}; readonly entries: ${entriesType} }`\n );\n }\n const namespacesType = nsEntries.length > 0 ? `{ ${nsEntries.join(\"; \")} }` : \"Record<string, never>\";\n\n return `{ readonly stores: ${storesType}; readonly namespaces: ${namespacesType}; readonly storageHash: ${storageHashTypeName} }`;\n },\n\n /**\n * Serializes the per-model storage metadata into a TypeScript type literal.\n * The result is used as the `storage` field of the model's entry in the\n * generated `models` type.\n *\n * For IDB, each model maps to exactly one object store:\n * ```ts\n * { readonly storeName: '<name>'; readonly keyPath: '<field>' }\n * ```\n *\n * @param _modelName - The Prisma model name (unused; provided for API symmetry).\n * @param model - The resolved `ContractModel` whose `storage` is cast to\n * {@link IdbModelStorage}.\n */\n generateModelStorageType(_modelName: string, model: ContractModel): string {\n const idbModel = model as ContractModel<IdbModelStorage>;\n return [\n `{ readonly storeName: ${serializeValue(idbModel.storage.storeName)}`,\n `readonly keyPath: ${serializeValue(idbModel.storage.keyPath)} }`,\n ].join(\"; \");\n },\n\n /**\n * Returns the `import type` lines prepended to `contract.d.ts` that bring\n * IDB-family types into scope.\n *\n * Imports {@link IdbContractWithTypeMaps} (the phantom wrapper used by\n * {@link getContractWrapper}) and {@link IdbTypeMaps} (used by\n * {@link getTypeMapsExpression}).\n */\n getFamilyImports(): string[] {\n return [\"import type { IdbContractWithTypeMaps, IdbTypeMaps } from '@prisma-next-idb/target-idb/pack';\"];\n },\n\n /**\n * Returns additional type alias declarations appended to `contract.d.ts`\n * after the codec and operation type aliases.\n *\n * For IDB this exports `LaneCodecTypes` (used by the generated client to\n * look up codec input/output pairs) as a simple alias of the assembled\n * `CodecTypes`. IDB has no query-operation types, so no further aliases\n * are needed.\n */\n getFamilyTypeAliases(): string {\n return \"export type LaneCodecTypes = CodecTypes;\";\n },\n\n /**\n * Returns the TypeScript expression for the `TypeMaps<...>` generic\n * instantiation written into `contract.d.ts`.\n *\n * IDB has no operation types or query-operation types, so the expression\n * uses the three-argument form of {@link IdbTypeMaps}.\n */\n getTypeMapsExpression(): string {\n return \"IdbTypeMaps<CodecTypes, FieldOutputTypes, FieldInputTypes>\";\n },\n\n /**\n * Returns the final `export type Contract = ...` declaration and convenience\n * re-exports derived from it.\n *\n * @param contractBaseName - Emitter-provided name of the base contract type\n * (always `'ContractBase'`).\n * @param typeMapsName - Emitter-provided name of the assembled TypeMaps type\n * (always `'TypeMaps'`).\n */\n getContractWrapper(contractBaseName: string, typeMapsName: string): string {\n return [\n `export type Contract = IdbContractWithTypeMaps<${contractBaseName}, ${typeMapsName}>;`,\n \"\",\n \"export type Stores = Contract['storage']['stores'];\",\n ].join(\"\\n\");\n },\n} as const satisfies EmissionSpi;\n","import type {\n SchemaIssue,\n SchemaVerificationNode,\n VerifyDatabaseSchemaResult,\n} from \"@prisma-next/framework-components/control\";\nimport type { IdbContract } from \"./validate\";\nimport type { IdbIndexIR, IdbSchemaIR, IdbStoreIR } from \"./schema-ir\";\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction passNode(\n kind: string,\n name: string,\n contractPath: string,\n children: readonly SchemaVerificationNode[] = []\n): SchemaVerificationNode {\n return {\n status: \"pass\",\n kind,\n name,\n contractPath,\n code: \"ok\",\n message: `${name} matches`,\n expected: null,\n actual: null,\n children,\n };\n}\n\nfunction failNode(\n kind: string,\n name: string,\n contractPath: string,\n code: string,\n message: string,\n expected: unknown,\n actual: unknown,\n children: readonly SchemaVerificationNode[] = []\n): SchemaVerificationNode {\n return {\n status: \"fail\",\n kind,\n name,\n contractPath,\n code,\n message,\n expected,\n actual,\n children,\n };\n}\n\nfunction warnNode(\n kind: string,\n name: string,\n contractPath: string,\n code: string,\n message: string,\n expected: unknown,\n actual: unknown,\n children: readonly SchemaVerificationNode[] = []\n): SchemaVerificationNode {\n return {\n status: \"warn\",\n kind,\n name,\n contractPath,\n code,\n message,\n expected,\n actual,\n children,\n };\n}\n\nfunction countStatuses(nodes: readonly SchemaVerificationNode[]): {\n pass: number;\n warn: number;\n fail: number;\n totalNodes: number;\n} {\n let pass = 0,\n warn = 0,\n fail = 0,\n totalNodes = 0;\n const stack = [...nodes];\n while (stack.length > 0) {\n const node = stack.pop()!;\n totalNodes++;\n if (node.status === \"pass\") pass++;\n else if (node.status === \"warn\") warn++;\n else fail++;\n stack.push(...node.children);\n }\n return { pass, warn, fail, totalNodes };\n}\n\n// ── Per-index verification ────────────────────────────────────────────────────\n\nfunction verifyIndex(\n indexName: string,\n contractIndex: { keyPath: string; unique: boolean; multiEntry?: boolean },\n actualIndex: IdbIndexIR | undefined,\n storePath: string,\n issues: SchemaIssue[]\n): SchemaVerificationNode {\n const indexPath = `${storePath}.indexes.${indexName}`;\n\n if (actualIndex === undefined) {\n const msg = `Index \"${indexName}\" defined in contract is missing from manifest schema`;\n issues.push({\n kind: \"missing_column\",\n table: storePath,\n column: indexName,\n message: msg,\n });\n return failNode(\"index\", indexName, indexPath, \"missing_column\", msg, contractIndex, undefined);\n }\n\n const children: SchemaVerificationNode[] = [];\n\n // keyPath\n if (contractIndex.keyPath !== actualIndex.keyPath) {\n const msg = `Index \"${indexName}\" keyPath mismatch: expected \"${contractIndex.keyPath}\", got \"${actualIndex.keyPath}\"`;\n issues.push({ kind: \"index_mismatch\", table: storePath, indexOrConstraint: indexName, message: msg });\n children.push(\n failNode(\n \"field\",\n \"keyPath\",\n `${indexPath}.keyPath`,\n \"index_mismatch\",\n msg,\n contractIndex.keyPath,\n actualIndex.keyPath\n )\n );\n } else {\n children.push(passNode(\"field\", \"keyPath\", `${indexPath}.keyPath`));\n }\n\n // unique — treat undefined as false to match how multiEntry is handled\n if ((contractIndex.unique ?? false) !== (actualIndex.unique ?? false)) {\n const msg = `Index \"${indexName}\" unique mismatch: expected ${contractIndex.unique}, got ${actualIndex.unique}`;\n issues.push({ kind: \"index_mismatch\", table: storePath, indexOrConstraint: indexName, message: msg });\n children.push(\n failNode(\n \"field\",\n \"unique\",\n `${indexPath}.unique`,\n \"index_mismatch\",\n msg,\n contractIndex.unique,\n actualIndex.unique\n )\n );\n } else {\n children.push(passNode(\"field\", \"unique\", `${indexPath}.unique`));\n }\n\n // multiEntry (only check if contract has it set to true)\n const contractME = contractIndex.multiEntry ?? false;\n const actualME = actualIndex.multiEntry ?? false;\n if (contractME !== actualME) {\n const msg = `Index \"${indexName}\" multiEntry mismatch: expected ${contractME}, got ${actualME}`;\n issues.push({ kind: \"index_mismatch\", table: storePath, indexOrConstraint: indexName, message: msg });\n children.push(\n failNode(\"field\", \"multiEntry\", `${indexPath}.multiEntry`, \"index_mismatch\", msg, contractME, actualME)\n );\n } else if (contractME) {\n children.push(passNode(\"field\", \"multiEntry\", `${indexPath}.multiEntry`));\n }\n\n // Extra indexes in manifest not in contract\n // (handled at store level — see verifyStore)\n\n const hasFail = children.some((c) => c.status === \"fail\");\n if (hasFail) {\n return failNode(\n \"index\",\n indexName,\n indexPath,\n \"index_mismatch\",\n `Index \"${indexName}\" has mismatches`,\n null,\n null,\n children\n );\n }\n return passNode(\"index\", indexName, indexPath, children);\n}\n\n// ── Per-store verification ────────────────────────────────────────────────────\n\nfunction verifyStore(\n storeName: string,\n contractStore: {\n keyPath: string;\n autoIncrement?: boolean;\n indexes?: Record<string, { keyPath: string; unique: boolean; multiEntry?: boolean }>;\n },\n actualStore: IdbStoreIR | undefined,\n strict: boolean,\n issues: SchemaIssue[]\n): SchemaVerificationNode {\n const storePath = `storage.stores.${storeName}`;\n\n if (actualStore === undefined) {\n const msg = `Object store \"${storeName}\" defined in contract is missing from manifest schema`;\n issues.push({ kind: \"missing_table\", table: storeName, message: msg });\n return failNode(\"collection\", storeName, storePath, \"missing_table\", msg, contractStore, undefined);\n }\n\n const children: SchemaVerificationNode[] = [];\n\n // keyPath\n if (contractStore.keyPath !== actualStore.keyPath) {\n const msg = `Store \"${storeName}\" keyPath mismatch: expected \"${contractStore.keyPath}\", got \"${actualStore.keyPath}\"`;\n issues.push({ kind: \"primary_key_mismatch\", table: storeName, message: msg });\n children.push(\n failNode(\n \"field\",\n \"keyPath\",\n `${storePath}.keyPath`,\n \"primary_key_mismatch\",\n msg,\n contractStore.keyPath,\n actualStore.keyPath\n )\n );\n } else {\n children.push(passNode(\"field\", \"keyPath\", `${storePath}.keyPath`));\n }\n\n // autoIncrement\n const contractAI = contractStore.autoIncrement ?? false;\n const actualAI = actualStore.autoIncrement ?? false;\n if (contractAI !== actualAI) {\n const msg = `Store \"${storeName}\" autoIncrement mismatch: expected ${contractAI}, got ${actualAI}`;\n issues.push({ kind: \"type_mismatch\", table: storeName, message: msg });\n children.push(\n failNode(\"field\", \"autoIncrement\", `${storePath}.autoIncrement`, \"type_mismatch\", msg, contractAI, actualAI)\n );\n } else if (contractAI) {\n children.push(passNode(\"field\", \"autoIncrement\", `${storePath}.autoIncrement`));\n }\n\n // indexes\n const contractIndexes = contractStore.indexes ?? {};\n const actualIndexes = actualStore.indexes ?? {};\n\n for (const [indexName, contractIndex] of Object.entries(contractIndexes)) {\n const actualIndex = actualIndexes[indexName];\n children.push(verifyIndex(indexName, contractIndex, actualIndex, storePath, issues));\n }\n\n // extra indexes in manifest (only relevant in strict mode)\n for (const indexName of Object.keys(actualIndexes)) {\n if (!(indexName in contractIndexes)) {\n const msg = `Index \"${indexName}\" exists in manifest but is not in contract`;\n if (strict) {\n issues.push({ kind: \"extra_index\", table: storeName, indexOrConstraint: indexName, message: msg });\n children.push(\n failNode(\n \"index\",\n indexName,\n `${storePath}.indexes.${indexName}`,\n \"extra_index\",\n msg,\n undefined,\n actualIndexes[indexName]\n )\n );\n } else {\n children.push(\n warnNode(\n \"index\",\n indexName,\n `${storePath}.indexes.${indexName}`,\n \"extra_index\",\n msg,\n undefined,\n actualIndexes[indexName]\n )\n );\n }\n }\n }\n\n const hasFail = children.some((c) => c.status === \"fail\");\n if (hasFail) {\n return failNode(\n \"collection\",\n storeName,\n storePath,\n \"schema_mismatch\",\n `Store \"${storeName}\" has schema issues`,\n null,\n null,\n children\n );\n }\n const hasWarn = children.some((c) => c.status === \"warn\");\n if (hasWarn) {\n return {\n status: \"warn\",\n kind: \"collection\",\n name: storeName,\n contractPath: storePath,\n code: \"ok\",\n message: `Store \"${storeName}\" has warnings`,\n expected: null,\n actual: null,\n children,\n };\n }\n return passNode(\"collection\", storeName, storePath, children);\n}\n\n// ── Main entry point ──────────────────────────────────────────────────────────\n\n/**\n * Pure, synchronous schema verification for IndexedDB.\n *\n * Compares the stores and indexes defined in the contract against the manifest\n * schema (which represents the actual IndexedDB database). Returns a\n * {@link VerifyDatabaseSchemaResult} that callers can inspect for issues.\n *\n * Does not perform any I/O — all inputs must already be available.\n *\n * @param contract - Validated IDB contract (what we expect the schema to be).\n * @param schema - Schema IR from the manifest (what the schema actually is).\n * @param strict - When `true`, extra stores/indexes in the manifest are failures.\n * @param options - Optional context paths for metadata / error reporting.\n */\nexport function verifyIdbSchema(\n contract: IdbContract,\n schema: IdbSchemaIR,\n strict: boolean\n): VerifyDatabaseSchemaResult {\n const start = Date.now();\n const issues: SchemaIssue[] = [];\n const storeNodes: SchemaVerificationNode[] = [];\n\n const contractStores = (\n contract.storage as {\n stores: Record<\n string,\n {\n keyPath: string;\n autoIncrement?: boolean;\n indexes?: Record<string, { keyPath: string; unique: boolean; multiEntry?: boolean }>;\n }\n >;\n }\n ).stores;\n const actualStores = schema.stores;\n\n // Verify each store defined in the contract.\n for (const [storeName, contractStore] of Object.entries(contractStores)) {\n const actualStore: IdbStoreIR | undefined = actualStores[storeName];\n storeNodes.push(verifyStore(storeName, contractStore, actualStore, strict, issues));\n }\n\n // Extra stores in manifest (only in strict mode as failures, otherwise warnings).\n for (const [storeName, actualStore] of Object.entries(actualStores)) {\n if (!(storeName in contractStores)) {\n const msg = `Object store \"${storeName}\" exists in manifest but is not in contract`;\n if (strict) {\n issues.push({ kind: \"extra_table\", table: storeName, message: msg });\n storeNodes.push(\n failNode(\"collection\", storeName, `storage.stores.${storeName}`, \"extra_table\", msg, undefined, actualStore)\n );\n } else {\n storeNodes.push(\n warnNode(\"collection\", storeName, `storage.stores.${storeName}`, \"extra_table\", msg, undefined, actualStore)\n );\n }\n }\n }\n\n const root: SchemaVerificationNode = {\n status: storeNodes.some((n) => n.status === \"fail\")\n ? \"fail\"\n : storeNodes.some((n) => n.status === \"warn\")\n ? \"warn\"\n : \"pass\",\n kind: \"root\",\n name: \"idb\",\n contractPath: \"storage.stores\",\n code: issues.length === 0 ? \"ok\" : \"schema_mismatch\",\n message: issues.length === 0 ? \"All stores and indexes match\" : `${issues.length} schema issue(s) found`,\n expected: null,\n actual: null,\n children: storeNodes,\n };\n\n // Count statuses including root.\n const allNodes = [root, ...storeNodes];\n const counts = countStatuses(allNodes);\n const ok = root.status !== \"fail\";\n\n const storageHash = (contract.storage as { storageHash: string }).storageHash;\n const profileHash = (contract as { profileHash?: string }).profileHash;\n\n // exactOptionalPropertyTypes: build optional sub-objects conditionally.\n const contractField =\n profileHash !== undefined ? ({ storageHash, profileHash } as const) : ({ storageHash } as const);\n\n const metaField = {\n strict,\n };\n\n const sharedResult = {\n summary: ok ? \"Schema verification passed\" : `Schema verification failed: ${issues.length} issue(s)`,\n contract: contractField,\n target: {\n expected: \"idb\",\n },\n schema: {\n issues,\n root,\n counts,\n },\n meta: metaField,\n timings: { total: Date.now() - start },\n };\n\n if (ok) {\n return { ok: true, ...sharedResult };\n }\n return { ok: false, code: VERIFY_CODE_SCHEMA_FAILURE, ...sharedResult };\n}\n\nconst VERIFY_CODE_SCHEMA_FAILURE = \"PN-RUN-3010\";\n","import type { Contract, ContractMarkerRecord, LedgerEntryRecord } from \"@prisma-next/contract/types\";\nimport type { TargetBoundComponentDescriptor } from \"@prisma-next/framework-components/components\";\nimport type {\n ControlDriverInstance,\n ControlFamilyInstance,\n ControlStack,\n SignDatabaseResult,\n VerifyDatabaseResult,\n VerifyDatabaseSchemaResult,\n} from \"@prisma-next/framework-components/control\";\nimport { VERIFY_CODE_TARGET_MISMATCH } from \"@prisma-next/framework-components/control\";\nimport type { IdbSchemaIR } from \"./schema-ir\";\nimport { verifyIdbSchema } from \"./schema-verify\";\nimport { validateContract } from \"./validate\";\n\n// ── Structured refusal helpers ────────────────────────────────────────────────\n// IndexedDB only exists in the browser; the CLI runs in Node.js. The control\n// plane's read/write methods therefore return structured failures rather\n// than reading from any kind of file-backed shadow.\n//\n// The refusal code shares a prefix (`IDB-CLI-`) so callers can branch on the\n// family without parsing the summary text.\n\nconst REFUSAL_CODE = \"IDB-CLI-UNSUPPORTED\" as const;\n\n/**\n * Single-string refusal message baked into `summary` because the framework\n * `VerifyDatabaseResult` / `SignDatabaseResult` `meta` field is strict-shaped\n * (`{ contractPath, configPath? }`) and doesn't accept extra explanation\n * fields. The summary is the only free-form text field on every result.\n */\nfunction refusalSummary(action: \"verified\" | \"signed\"): string {\n return (\n `IndexedDB cannot be ${action} from the CLI. ` +\n \"IndexedDB only exists in the browser; the CLI runs in Node.js, so there \" +\n \"is no live database for it to inspect or update. Author migrations with \" +\n \"`prisma-next migration new` / `migration plan`, validate the chain with \" +\n \"`prisma-next-idb preflight`, and let `createAutoMigratingIdbClient` apply \" +\n \"them the next time the app opens in a browser.\"\n );\n}\n\n// ── exactOptionalPropertyTypes helpers ───────────────────────────────────────\n\nfunction contractInfo(storageHash: string, profileHash: string | undefined) {\n return profileHash !== undefined ? ({ storageHash, profileHash } as const) : ({ storageHash } as const);\n}\n\nfunction verifyMeta(contractPath: string, configPath: string | undefined) {\n return configPath !== undefined ? ({ contractPath, configPath } as const) : ({ contractPath } as const);\n}\n\nfunction signMeta(contractPath: string, configPath: string | undefined) {\n return configPath !== undefined ? ({ contractPath, configPath } as const) : ({ contractPath } as const);\n}\n\n/** Fully-typed IDB control family instance returned by {@link createIdbFamilyInstance}. */\nexport type IdbControlFamilyInstance = ControlFamilyInstance<\"idb\", IdbSchemaIR>;\n\n/**\n * Creates an IDB control family instance for the given control stack.\n *\n * **CLI surface — refusals**: IndexedDB is a browser API, so the CLI cannot\n * read or write the live database. Every method that would normally talk to a\n * database (`verify`, `sign`, `readMarker`, `readAllMarkers`, `introspect`)\n * returns a structured refusal pointing the user at the contract-space\n * authoring + preflight workflow.\n *\n * The CLI-side `db init`, `db update`, and `db verify` commands therefore\n * surface a uniform `IDB-CLI-UNSUPPORTED` envelope rather than silently\n * succeeding.\n *\n * **Active surface**: `deserializeContract` (pure) and `verifySchema` (pure\n * function over an in-memory `IdbSchemaIR`) continue to work — neither\n * needs a live database.\n *\n * @param _stack - The assembled control stack (unused; IDB has no adapter/extension layer).\n */\nexport function createIdbFamilyInstance(_stack: ControlStack<\"idb\", string>): IdbControlFamilyInstance {\n return {\n familyId: \"idb\",\n\n // ── deserializeContract (active, pure) ─────────────────────────────────\n\n deserializeContract(contractJson: unknown): Contract {\n return validateContract(contractJson) as Contract;\n },\n\n // ── verify (CLI refusal) ───────────────────────────────────────────────\n\n async verify(options: {\n readonly driver: ControlDriverInstance<\"idb\", string>;\n readonly contract: unknown;\n readonly expectedTargetId: string;\n readonly contractPath: string;\n readonly configPath?: string;\n }): Promise<VerifyDatabaseResult> {\n const start = Date.now();\n const contract = validateContract(options.contract);\n const storageHash = (contract.storage as { storageHash: string }).storageHash;\n const profileHash = (contract as { profileHash?: string }).profileHash;\n\n if (options.expectedTargetId !== \"idb\") {\n return {\n ok: false,\n code: VERIFY_CODE_TARGET_MISMATCH,\n summary: `Target mismatch: expected \"idb\", got \"${options.expectedTargetId}\"`,\n contract: contractInfo(storageHash, profileHash),\n target: { expected: options.expectedTargetId, actual: \"idb\" },\n meta: verifyMeta(options.contractPath, options.configPath),\n timings: { total: Date.now() - start },\n };\n }\n\n return {\n ok: false,\n code: REFUSAL_CODE,\n summary: refusalSummary(\"verified\"),\n contract: contractInfo(storageHash, profileHash),\n target: { expected: \"idb\", actual: \"idb\" },\n meta: verifyMeta(options.contractPath, options.configPath),\n timings: { total: Date.now() - start },\n };\n },\n\n // ── verifySchema (active, pure) ────────────────────────────────────────\n\n verifySchema(options: {\n readonly contract: unknown;\n readonly schema: IdbSchemaIR;\n readonly strict: boolean;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<\"idb\", string>>;\n }): VerifyDatabaseSchemaResult {\n const contract = validateContract(options.contract);\n return verifyIdbSchema(contract, options.schema, options.strict);\n },\n\n // ── sign (CLI refusal) ──────────────────────────────────────────────────\n\n async sign(options: {\n readonly driver: ControlDriverInstance<\"idb\", string>;\n readonly contract: unknown;\n readonly contractPath: string;\n readonly configPath?: string;\n }): Promise<SignDatabaseResult> {\n const start = Date.now();\n const contract = validateContract(options.contract);\n const storageHash = (contract.storage as { storageHash: string }).storageHash;\n const profileHash = (contract as { profileHash?: string }).profileHash ?? \"\";\n\n return {\n ok: false,\n summary: refusalSummary(\"signed\"),\n contract: contractInfo(storageHash, profileHash),\n target: { expected: \"idb\", actual: \"idb\" },\n // `SignDatabaseResult.marker` is required; carry an \"untouched\"\n // record so callers don't crash on null-deref.\n marker: { created: false, updated: false },\n meta: signMeta(options.contractPath, options.configPath),\n timings: { total: Date.now() - start },\n };\n },\n\n // ── readMarker / readAllMarkers (CLI refusal: return null/empty) ───────\n // The framework typing requires `ContractMarkerRecord | null` here, not a\n // structured envelope. Returning `null` is the existing semantics for\n // \"no marker on file\"; we keep that contract so the CLI's downstream\n // logic (e.g. db init's \"create new marker\" path) treats IDB as\n // perpetually empty rather than erroring out of band.\n\n async readMarker(_options: {\n readonly driver: ControlDriverInstance<\"idb\", string>;\n readonly space: string;\n }): Promise<ContractMarkerRecord | null> {\n return null;\n },\n\n async readAllMarkers(_options: {\n readonly driver: ControlDriverInstance<\"idb\", string>;\n }): Promise<ReadonlyMap<string, ContractMarkerRecord>> {\n return new Map<string, ContractMarkerRecord>();\n },\n\n async readLedger(_options: {\n readonly driver: ControlDriverInstance<\"idb\", string>;\n readonly space?: string;\n }): Promise<readonly LedgerEntryRecord[]> {\n return [];\n },\n\n // ── introspect (CLI refusal: return empty schema) ──────────────────────\n // The framework typing requires `IdbSchemaIR` (not a result envelope), so\n // we return an empty schema. The structural refusal is communicated via\n // the sibling `verify`/`sign` methods that the CLI actually surfaces to\n // the user when running `db init` / `db update` / `db verify` against\n // an IDB project.\n\n async introspect(_options: {\n readonly driver: ControlDriverInstance<\"idb\", string>;\n readonly contract?: unknown;\n }): Promise<IdbSchemaIR> {\n return { stores: {} };\n },\n };\n}\n\nexport type { ControlDriverInstance, SignDatabaseResult, VerifyDatabaseResult, VerifyDatabaseSchemaResult };\nexport type { IdbSchemaIR } from \"./schema-ir\";\n","import type { ControlFamilyDescriptor, ControlStack } from \"@prisma-next/framework-components/control\";\nimport { idbEmission } from \"./emission\";\nimport { type IdbControlFamilyInstance, createIdbFamilyInstance } from \"./control-instance\";\n\n/**\n * IDB family descriptor — the control-plane entry point for the IDB family.\n *\n * Registered in a Prisma Next config file under the `family` key:\n * ```ts\n * import idb from '@prisma-next-idb/family-idb/control';\n * export default defineConfig({ family: idb, ... });\n * ```\n *\n * **Responsibilities:**\n * - Carries the {@link idbEmission} plugin used by `prisma-next contract emit`\n * to generate `contract.d.ts`.\n * - Acts as a factory: `create(stack)` returns an {@link IdbControlFamilyInstance}\n * with domain-action methods consumed by the CLI.\n */\nexport class IdbFamilyDescriptor implements ControlFamilyDescriptor<\"idb\", IdbControlFamilyInstance> {\n readonly kind = \"family\" as const;\n readonly id = \"idb\";\n readonly familyId = \"idb\" as const;\n readonly version = \"0.0.1\";\n readonly emission = idbEmission;\n\n /**\n * Creates an {@link IdbControlFamilyInstance} for the given control stack.\n *\n * The instance is a plain object whose methods implement the CLI domain\n * actions (validateContract, verify, sign, etc.).\n */\n create(stack: ControlStack<\"idb\", string>): IdbControlFamilyInstance {\n return createIdbFamilyInstance(stack);\n }\n}\n","import { IdbFamilyDescriptor } from \"../core/control-descriptor\";\n\nexport type { IdbControlFamilyInstance } from \"../core/control-instance\";\nexport type { IdbSchemaIR, IdbStoreIR, IdbIndexIR } from \"../core/schema-ir\";\nexport type { IdbContract } from \"../core/validate\";\n\nexport default new IdbFamilyDescriptor();\n"],"mappings":"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"}
|