@prisma-next-idb/target-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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Prisma IDB
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,129 @@
1
+ import { a as IdbIndexDefinition, u as IdbStoreDefinition } from "./idb-contract-types-DndrdtW0.mjs";
2
+ import { MigrationPlanOperation } from "@prisma-next/framework-components/control";
3
+ import { ContractMarkerRecord } from "@prisma-next/contract/types";
4
+
5
+ //#region src/core/migration-factories.d.ts
6
+ /** DDL operation that creates a new object store. Always `additive`. */
7
+ type CreateObjectStoreOp = MigrationPlanOperation & {
8
+ readonly kind: "createObjectStore";
9
+ readonly storeName: string;
10
+ readonly def: IdbStoreDefinition;
11
+ };
12
+ /** DDL operation that drops an existing object store and all its indexes. Always `destructive`. */
13
+ type DropObjectStoreOp = MigrationPlanOperation & {
14
+ readonly kind: "dropObjectStore";
15
+ readonly storeName: string;
16
+ };
17
+ /** DDL operation that creates a secondary index on an object store. Always `additive`. */
18
+ type CreateIndexOp = MigrationPlanOperation & {
19
+ readonly kind: "createIndex";
20
+ readonly storeName: string;
21
+ readonly indexName: string;
22
+ readonly def: IdbIndexDefinition;
23
+ };
24
+ /** DDL operation that drops a secondary index from an object store. Always `destructive`. */
25
+ type DropIndexOp = MigrationPlanOperation & {
26
+ readonly kind: "dropIndex";
27
+ readonly storeName: string;
28
+ readonly indexName: string;
29
+ };
30
+ /** Union of all IDB DDL plan operations. */
31
+ type IdbDdlOp = CreateObjectStoreOp | DropObjectStoreOp | CreateIndexOp | DropIndexOp;
32
+ /** Returns `true` if `op` is one of the four IDB DDL op kinds. */
33
+ declare function isIdbDdlOp(op: MigrationPlanOperation): op is IdbDdlOp;
34
+ declare function createObjectStoreOp(storeName: string, def: IdbStoreDefinition): CreateObjectStoreOp;
35
+ declare function dropObjectStoreOp(storeName: string): DropObjectStoreOp;
36
+ declare function createIndexOp(storeName: string, indexName: string, def: IdbIndexDefinition): CreateIndexOp;
37
+ declare function dropIndexOp(storeName: string, indexName: string): DropIndexOp;
38
+ //#endregion
39
+ //#region src/core/apply-ddl-op.d.ts
40
+ /**
41
+ * Execute a single DDL operation against an open `upgradeneeded` transaction.
42
+ *
43
+ * All IndexedDB DDL (createObjectStore, deleteObjectStore, createIndex,
44
+ * deleteIndex) MUST happen inside the version-change transaction that fires
45
+ * in `upgradeneeded`. The `db` and `tx` references are valid only for the
46
+ * duration of that callback.
47
+ *
48
+ * Shared by the runner (target-idb), the browser auto-migrate path
49
+ * (client-idb), and the preflight CLI (family-idb) so all three apply paths
50
+ * use a single, byte-identical implementation.
51
+ *
52
+ * **Idempotency.** Each op is guarded by an existence check so re-applying an
53
+ * already-applied op is a no-op rather than a throw. This is the load-bearing
54
+ * guarantee behind the two-phase marker write (ADR 002): if a tab is killed in
55
+ * the window between the version-change transaction committing and the marker
56
+ * `put` landing, the schema is advanced but the marker still points at the old
57
+ * hash. On the next open, the chain walk re-collects the already-applied ops
58
+ * and replays them here. Without the guards, `createObjectStore` /
59
+ * `createIndex` throw `ConstraintError` on the existing store/index, the
60
+ * version-change transaction aborts, and the database is permanently wedged
61
+ * (every subsequent open repeats the failed upgrade). Contrary to a common
62
+ * assumption, IndexedDB itself offers **no** "already exists" tolerance — these
63
+ * guards are what make replay safe. (Was PLAN Issue #25.)
64
+ */
65
+ declare function applyOneDdlOp(db: IDBDatabase, tx: IDBTransaction, op: IdbDdlOp): void;
66
+ /**
67
+ * Marker write input. The `space` field is the contract-space identifier
68
+ * (`"app"` for the single app space; per-extension callers pass their own
69
+ * space id when extensions land on IDB). All other fields mirror
70
+ * {@link ContractMarkerRecord} exactly so the in-DB record has full parity
71
+ * with the framework's canonical marker shape.
72
+ */
73
+ interface MarkerWriteInput {
74
+ readonly space: string;
75
+ readonly storageHash: string;
76
+ readonly profileHash?: string;
77
+ readonly invariants?: readonly string[];
78
+ readonly contractJson?: unknown;
79
+ readonly canonicalVersion?: number | null;
80
+ readonly appTag?: string | null;
81
+ readonly meta?: Record<string, unknown>;
82
+ }
83
+ /**
84
+ * In-DB marker record shape stored in `_prisma_next_marker`.
85
+ *
86
+ * Identical to {@link ContractMarkerRecord} plus the keying `space` field;
87
+ * `updatedAt` is a `Date` (not an ISO string) because IndexedDB serialises
88
+ * Dates natively via structured-clone.
89
+ */
90
+ type IdbMarkerRecord = ContractMarkerRecord & {
91
+ readonly space: string;
92
+ };
93
+ /**
94
+ * Write the contract marker into the `_prisma_next_marker` store using a
95
+ * separate `readwrite` transaction. The marker store is created inside the
96
+ * version-change transaction during the migration's first run (see
97
+ * `createMarkerStoreOp`); subsequent runs reuse it.
98
+ *
99
+ * Keyed by `space` (defaulting to `"app"` at the caller layer) so the
100
+ * storage layout doesn't have to be migrated when IDB eventually grows
101
+ * extension support (see ADR 021 + feedback issue #5).
102
+ */
103
+ declare function writeMarker(db: IDBDatabase, input: MarkerWriteInput): Promise<void>;
104
+ /**
105
+ * Read the marker record for a given space from the `_prisma_next_marker`
106
+ * store. Returns `null` when the store doesn't exist (fresh DB) or the
107
+ * record is absent.
108
+ */
109
+ declare function readMarker(db: IDBDatabase, space: string): Promise<IdbMarkerRecord | null>;
110
+ /**
111
+ * Open `dbName` at `targetVersion`, apply `ops` inside the `upgradeneeded`
112
+ * callback, optionally write the contract marker (in a separate readwrite
113
+ * tx after `onsuccess`), then close the connection.
114
+ *
115
+ * Returns the number of ops applied. Throws on open-request error or DDL
116
+ * application error.
117
+ */
118
+ declare function openAndUpgrade(input: {
119
+ readonly factory: IDBFactory;
120
+ readonly dbName: string;
121
+ readonly targetVersion: number;
122
+ readonly ops: readonly IdbDdlOp[];
123
+ readonly marker?: MarkerWriteInput;
124
+ readonly onOperationStart?: (op: IdbDdlOp) => void;
125
+ readonly onOperationComplete?: (op: IdbDdlOp) => void;
126
+ }): Promise<number>;
127
+ //#endregion
128
+ export { readMarker as a, CreateObjectStoreOp as c, IdbDdlOp as d, createIndexOp as f, isIdbDdlOp as g, dropObjectStoreOp as h, openAndUpgrade as i, DropIndexOp as l, dropIndexOp as m, MarkerWriteInput as n, writeMarker as o, createObjectStoreOp as p, applyOneDdlOp as r, CreateIndexOp as s, IdbMarkerRecord as t, DropObjectStoreOp as u };
129
+ //# sourceMappingURL=apply-ddl-op-DZAoihY0.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-ddl-op-DZAoihY0.d.mts","names":[],"sources":["../src/core/migration-factories.ts","../src/core/apply-ddl-op.ts"],"mappings":";;;;;;KAqBY,mBAAA,GAAsB,sBAAA;EAAA,SACvB,IAAA;EAAA,SACA,SAAA;EAAA,SACA,GAAA,EAAK,kBAAkB;AAAA;;KAItB,iBAAA,GAAoB,sBAAsB;EAAA,SAC3C,IAAA;EAAA,SACA,SAAA;AAAA;AANuB;AAAA,KAUtB,aAAA,GAAgB,sBAAA;EAAA,SACjB,IAAA;EAAA,SACA,SAAA;EAAA,SACA,SAAA;EAAA,SACA,GAAA,EAAK,kBAAkB;AAAA;;KAItB,WAAA,GAAc,sBAAsB;EAAA,SACrC,IAAA;EAAA,SACA,SAAA;EAAA,SACA,SAAA;AAAA;;KAIC,QAAA,GAAW,mBAAA,GAAsB,iBAAA,GAAoB,aAAA,GAAgB,WAAA;;iBAKjE,UAAA,CAAW,EAAA,EAAI,sBAAA,GAAyB,EAAA,IAAM,QAAQ;AAAA,iBAYtD,mBAAA,CAAoB,SAAA,UAAmB,GAAA,EAAK,kBAAA,GAAqB,mBAAmB;AAAA,iBAWpF,iBAAA,CAAkB,SAAA,WAAoB,iBAAiB;AAAA,iBAUvD,aAAA,CAAc,SAAA,UAAmB,SAAA,UAAmB,GAAA,EAAK,kBAAA,GAAqB,aAAa;AAAA,iBAY3F,WAAA,CAAY,SAAA,UAAmB,SAAA,WAAoB,WAAW;;;;;;AA9E9E;;;;;;;;;;AAGkC;AAIlC;;;;;;;;AAEoB;AAIpB;;iBCNgB,aAAA,CAAc,EAAA,EAAI,WAAA,EAAa,EAAA,EAAI,cAAA,EAAgB,EAAA,EAAI,QAAA;;;;;;;;UAwCtD,gBAAA;EAAA,SACN,KAAA;EAAA,SACA,WAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA;EAAA,SACA,YAAA;EAAA,SACA,gBAAA;EAAA,SACA,MAAA;EAAA,SACA,IAAA,GAAO,MAAM;AAAA;;AD/BJ;AAIpB;;;;;KCqCY,eAAA,GAAkB,oBAAoB;EAAA,SAAc,KAAK;AAAA;;;;;;;ADrCuB;AAK5F;;;iBC4CgB,WAAA,CAAY,EAAA,EAAI,WAAA,EAAa,KAAA,EAAO,gBAAA,GAAmB,OAAA;;;;;;iBAqCvD,UAAA,CAAW,EAAA,EAAI,WAAA,EAAa,KAAA,WAAgB,OAAA,CAAQ,eAAA;ADrEpE;;;;;;;;AAAA,iBC8FgB,cAAA,CAAe,KAAA;EAAA,SACpB,OAAA,EAAS,UAAA;EAAA,SACT,MAAA;EAAA,SACA,aAAA;EAAA,SACA,GAAA,WAAc,QAAA;EAAA,SACd,MAAA,GAAS,gBAAA;EAAA,SACT,gBAAA,IAAoB,EAAA,EAAI,QAAA;EAAA,SACxB,mBAAA,IAAuB,EAAA,EAAI,QAAA;AAAA,IAClC,OAAA"}
@@ -0,0 +1,232 @@
1
+ //#region src/core/migration-factories.ts
2
+ /** Name of the internal marker store. Must match {@link MARKER_STORE_NAME} in driver-idb. */
3
+ const IDB_MARKER_STORE = "_prisma_next_marker";
4
+ /**
5
+ * Default keyPath for the marker store.
6
+ *
7
+ * Keyed by `space` (per-contract-space, defaulting to `"app"`) rather than
8
+ * the legacy `"id"`/`"default"` shape so the storage layout doesn't need to
9
+ * be migrated later if IDB grows extension support. See ADR 021 +
10
+ * feedback issue #5.
11
+ */
12
+ const MARKER_KEYPATH = "space";
13
+ /** Returns `true` if `op` is one of the four IDB DDL op kinds. */
14
+ function isIdbDdlOp(op) {
15
+ return "kind" in op && (op.kind === "createObjectStore" || op.kind === "dropObjectStore" || op.kind === "createIndex" || op.kind === "dropIndex");
16
+ }
17
+ function createObjectStoreOp(storeName, def) {
18
+ return {
19
+ kind: "createObjectStore",
20
+ id: `object-store.${storeName}.create`,
21
+ label: `Create object store "${storeName}"`,
22
+ operationClass: "additive",
23
+ storeName,
24
+ def
25
+ };
26
+ }
27
+ function dropObjectStoreOp(storeName) {
28
+ return {
29
+ kind: "dropObjectStore",
30
+ id: `object-store.${storeName}.drop`,
31
+ label: `Drop object store "${storeName}"`,
32
+ operationClass: "destructive",
33
+ storeName
34
+ };
35
+ }
36
+ function createIndexOp(storeName, indexName, def) {
37
+ return {
38
+ kind: "createIndex",
39
+ id: `index.${storeName}.${indexName}.create`,
40
+ label: `Create index "${indexName}" on "${storeName}"`,
41
+ operationClass: "additive",
42
+ storeName,
43
+ indexName,
44
+ def
45
+ };
46
+ }
47
+ function dropIndexOp(storeName, indexName) {
48
+ return {
49
+ kind: "dropIndex",
50
+ id: `index.${storeName}.${indexName}.drop`,
51
+ label: `Drop index "${indexName}" on "${storeName}"`,
52
+ operationClass: "destructive",
53
+ storeName,
54
+ indexName
55
+ };
56
+ }
57
+ /**
58
+ * Create the internal `_prisma_next_marker` object store.
59
+ *
60
+ * This store holds the contract marker (`storageHash` + `profileHash`) that
61
+ * the runtime verifies before executing queries. It is always additive and
62
+ * should be the first op in any migration plan.
63
+ */
64
+ function createMarkerStoreOp() {
65
+ return {
66
+ kind: "createObjectStore",
67
+ id: `object-store.${IDB_MARKER_STORE}.create`,
68
+ label: `Create internal marker store "${IDB_MARKER_STORE}"`,
69
+ operationClass: "additive",
70
+ storeName: IDB_MARKER_STORE,
71
+ def: { keyPath: MARKER_KEYPATH }
72
+ };
73
+ }
74
+ //#endregion
75
+ //#region src/core/apply-ddl-op.ts
76
+ /**
77
+ * Execute a single DDL operation against an open `upgradeneeded` transaction.
78
+ *
79
+ * All IndexedDB DDL (createObjectStore, deleteObjectStore, createIndex,
80
+ * deleteIndex) MUST happen inside the version-change transaction that fires
81
+ * in `upgradeneeded`. The `db` and `tx` references are valid only for the
82
+ * duration of that callback.
83
+ *
84
+ * Shared by the runner (target-idb), the browser auto-migrate path
85
+ * (client-idb), and the preflight CLI (family-idb) so all three apply paths
86
+ * use a single, byte-identical implementation.
87
+ *
88
+ * **Idempotency.** Each op is guarded by an existence check so re-applying an
89
+ * already-applied op is a no-op rather than a throw. This is the load-bearing
90
+ * guarantee behind the two-phase marker write (ADR 002): if a tab is killed in
91
+ * the window between the version-change transaction committing and the marker
92
+ * `put` landing, the schema is advanced but the marker still points at the old
93
+ * hash. On the next open, the chain walk re-collects the already-applied ops
94
+ * and replays them here. Without the guards, `createObjectStore` /
95
+ * `createIndex` throw `ConstraintError` on the existing store/index, the
96
+ * version-change transaction aborts, and the database is permanently wedged
97
+ * (every subsequent open repeats the failed upgrade). Contrary to a common
98
+ * assumption, IndexedDB itself offers **no** "already exists" tolerance — these
99
+ * guards are what make replay safe. (Was PLAN Issue #25.)
100
+ */
101
+ function applyOneDdlOp(db, tx, op) {
102
+ switch (op.kind) {
103
+ case "createObjectStore":
104
+ if (db.objectStoreNames.contains(op.storeName)) return;
105
+ db.createObjectStore(op.storeName, {
106
+ keyPath: op.def.keyPath,
107
+ ...op.def.autoIncrement !== void 0 && { autoIncrement: op.def.autoIncrement }
108
+ });
109
+ return;
110
+ case "dropObjectStore":
111
+ if (!db.objectStoreNames.contains(op.storeName)) return;
112
+ db.deleteObjectStore(op.storeName);
113
+ return;
114
+ case "createIndex": {
115
+ const store = tx.objectStore(op.storeName);
116
+ if (store.indexNames.contains(op.indexName)) return;
117
+ store.createIndex(op.indexName, op.def.keyPath, {
118
+ unique: op.def.unique,
119
+ ...op.def.multiEntry !== void 0 && { multiEntry: op.def.multiEntry }
120
+ });
121
+ return;
122
+ }
123
+ case "dropIndex": {
124
+ const store = tx.objectStore(op.storeName);
125
+ if (!store.indexNames.contains(op.indexName)) return;
126
+ store.deleteIndex(op.indexName);
127
+ return;
128
+ }
129
+ }
130
+ }
131
+ /**
132
+ * Write the contract marker into the `_prisma_next_marker` store using a
133
+ * separate `readwrite` transaction. The marker store is created inside the
134
+ * version-change transaction during the migration's first run (see
135
+ * `createMarkerStoreOp`); subsequent runs reuse it.
136
+ *
137
+ * Keyed by `space` (defaulting to `"app"` at the caller layer) so the
138
+ * storage layout doesn't have to be migrated when IDB eventually grows
139
+ * extension support (see ADR 021 + feedback issue #5).
140
+ */
141
+ function writeMarker(db, input) {
142
+ return new Promise((resolve, reject) => {
143
+ if (!db.objectStoreNames.contains("_prisma_next_marker")) {
144
+ console.warn("[prisma-next] _prisma_next_marker store not found after DDL — this indicates a bug in the migration planner.");
145
+ resolve();
146
+ return;
147
+ }
148
+ const tx = db.transaction(IDB_MARKER_STORE, "readwrite");
149
+ const store = tx.objectStore(IDB_MARKER_STORE);
150
+ const record = {
151
+ space: input.space,
152
+ storageHash: input.storageHash,
153
+ profileHash: input.profileHash ?? "",
154
+ updatedAt: /* @__PURE__ */ new Date(),
155
+ invariants: input.invariants ?? [],
156
+ contractJson: input.contractJson ?? null,
157
+ canonicalVersion: input.canonicalVersion ?? null,
158
+ appTag: input.appTag ?? null,
159
+ meta: input.meta ?? {}
160
+ };
161
+ const putReq = store.put(record);
162
+ putReq.onerror = () => reject(putReq.error);
163
+ tx.oncomplete = () => resolve();
164
+ tx.onerror = () => reject(tx.error);
165
+ });
166
+ }
167
+ /**
168
+ * Read the marker record for a given space from the `_prisma_next_marker`
169
+ * store. Returns `null` when the store doesn't exist (fresh DB) or the
170
+ * record is absent.
171
+ */
172
+ function readMarker(db, space) {
173
+ return new Promise((resolve, reject) => {
174
+ if (!db.objectStoreNames.contains("_prisma_next_marker")) {
175
+ resolve(null);
176
+ return;
177
+ }
178
+ const req = db.transaction(IDB_MARKER_STORE, "readonly").objectStore(IDB_MARKER_STORE).get(space);
179
+ req.onsuccess = () => {
180
+ const result = req.result;
181
+ resolve(result ?? null);
182
+ };
183
+ req.onerror = () => reject(req.error);
184
+ });
185
+ }
186
+ /**
187
+ * Open `dbName` at `targetVersion`, apply `ops` inside the `upgradeneeded`
188
+ * callback, optionally write the contract marker (in a separate readwrite
189
+ * tx after `onsuccess`), then close the connection.
190
+ *
191
+ * Returns the number of ops applied. Throws on open-request error or DDL
192
+ * application error.
193
+ */
194
+ function openAndUpgrade(input) {
195
+ return new Promise((resolve, reject) => {
196
+ const request = input.factory.open(input.dbName, input.targetVersion);
197
+ request.onupgradeneeded = (event) => {
198
+ const target = event.target;
199
+ const db = target.result;
200
+ const tx = target.transaction;
201
+ if (tx === null) {
202
+ reject(/* @__PURE__ */ new Error("IDB: upgradeneeded fired with null version-change transaction"));
203
+ return;
204
+ }
205
+ for (const op of input.ops) {
206
+ input.onOperationStart?.(op);
207
+ applyOneDdlOp(db, tx, op);
208
+ input.onOperationComplete?.(op);
209
+ }
210
+ };
211
+ request.onsuccess = async (event) => {
212
+ const db = event.target.result;
213
+ try {
214
+ if (input.marker !== void 0) await writeMarker(db, input.marker);
215
+ } finally {
216
+ db.close();
217
+ }
218
+ resolve(input.ops.length);
219
+ };
220
+ request.onerror = (event) => {
221
+ const err = event.target.error;
222
+ reject(err ?? /* @__PURE__ */ new Error("IDB: migration open request failed without an error object"));
223
+ };
224
+ request.onblocked = () => {
225
+ reject(/* @__PURE__ */ new Error(`IDB: migration of "${input.dbName}" is blocked — another connection is open at an older version and did not close. Close other tabs/connections to this database and retry.`));
226
+ };
227
+ });
228
+ }
229
+ //#endregion
230
+ export { createIndexOp as a, dropIndexOp as c, writeMarker as i, dropObjectStoreOp as l, openAndUpgrade as n, createMarkerStoreOp as o, readMarker as r, createObjectStoreOp as s, applyOneDdlOp as t, isIdbDdlOp as u };
231
+
232
+ //# sourceMappingURL=apply-ddl-op-LP-l0o4O.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-ddl-op-LP-l0o4O.mjs","names":[],"sources":["../src/core/migration-factories.ts","../src/core/apply-ddl-op.ts"],"sourcesContent":["import type { MigrationOperationClass, MigrationPlanOperation } from \"@prisma-next/framework-components/control\";\nimport type { IdbIndexDefinition, IdbStoreDefinition } from \"./idb-contract-types\";\n\n// ── Marker store ─────────────────────────────────────────────────────────────\n\n/** Name of the internal marker store. Must match {@link MARKER_STORE_NAME} in driver-idb. */\nexport const IDB_MARKER_STORE = \"_prisma_next_marker\";\n\n/**\n * Default keyPath for the marker store.\n *\n * Keyed by `space` (per-contract-space, defaulting to `\"app\"`) rather than\n * the legacy `\"id\"`/`\"default\"` shape so the storage layout doesn't need to\n * be migrated later if IDB grows extension support. See ADR 021 +\n * feedback issue #5.\n */\nconst MARKER_KEYPATH = \"space\";\n\n// ── Op kinds ──────────────────────────────────────────────────────────────────\n\n/** DDL operation that creates a new object store. Always `additive`. */\nexport type CreateObjectStoreOp = MigrationPlanOperation & {\n readonly kind: \"createObjectStore\";\n readonly storeName: string;\n readonly def: IdbStoreDefinition;\n};\n\n/** DDL operation that drops an existing object store and all its indexes. Always `destructive`. */\nexport type DropObjectStoreOp = MigrationPlanOperation & {\n readonly kind: \"dropObjectStore\";\n readonly storeName: string;\n};\n\n/** DDL operation that creates a secondary index on an object store. Always `additive`. */\nexport type CreateIndexOp = MigrationPlanOperation & {\n readonly kind: \"createIndex\";\n readonly storeName: string;\n readonly indexName: string;\n readonly def: IdbIndexDefinition;\n};\n\n/** DDL operation that drops a secondary index from an object store. Always `destructive`. */\nexport type DropIndexOp = MigrationPlanOperation & {\n readonly kind: \"dropIndex\";\n readonly storeName: string;\n readonly indexName: string;\n};\n\n/** Union of all IDB DDL plan operations. */\nexport type IdbDdlOp = CreateObjectStoreOp | DropObjectStoreOp | CreateIndexOp | DropIndexOp;\n\n// ── Type guard ────────────────────────────────────────────────────────────────\n\n/** Returns `true` if `op` is one of the four IDB DDL op kinds. */\nexport function isIdbDdlOp(op: MigrationPlanOperation): op is IdbDdlOp {\n return (\n \"kind\" in op &&\n (op.kind === \"createObjectStore\" ||\n op.kind === \"dropObjectStore\" ||\n op.kind === \"createIndex\" ||\n op.kind === \"dropIndex\")\n );\n}\n\n// ── Factories ─────────────────────────────────────────────────────────────────\n\nexport function createObjectStoreOp(storeName: string, def: IdbStoreDefinition): CreateObjectStoreOp {\n return {\n kind: \"createObjectStore\",\n id: `object-store.${storeName}.create`,\n label: `Create object store \"${storeName}\"`,\n operationClass: \"additive\" as MigrationOperationClass,\n storeName,\n def,\n };\n}\n\nexport function dropObjectStoreOp(storeName: string): DropObjectStoreOp {\n return {\n kind: \"dropObjectStore\",\n id: `object-store.${storeName}.drop`,\n label: `Drop object store \"${storeName}\"`,\n operationClass: \"destructive\" as MigrationOperationClass,\n storeName,\n };\n}\n\nexport function createIndexOp(storeName: string, indexName: string, def: IdbIndexDefinition): CreateIndexOp {\n return {\n kind: \"createIndex\",\n id: `index.${storeName}.${indexName}.create`,\n label: `Create index \"${indexName}\" on \"${storeName}\"`,\n operationClass: \"additive\" as MigrationOperationClass,\n storeName,\n indexName,\n def,\n };\n}\n\nexport function dropIndexOp(storeName: string, indexName: string): DropIndexOp {\n return {\n kind: \"dropIndex\",\n id: `index.${storeName}.${indexName}.drop`,\n label: `Drop index \"${indexName}\" on \"${storeName}\"`,\n operationClass: \"destructive\" as MigrationOperationClass,\n storeName,\n indexName,\n };\n}\n\n/**\n * Create the internal `_prisma_next_marker` object store.\n *\n * This store holds the contract marker (`storageHash` + `profileHash`) that\n * the runtime verifies before executing queries. It is always additive and\n * should be the first op in any migration plan.\n */\nexport function createMarkerStoreOp(): CreateObjectStoreOp {\n return {\n kind: \"createObjectStore\",\n id: `object-store.${IDB_MARKER_STORE}.create`,\n label: `Create internal marker store \"${IDB_MARKER_STORE}\"`,\n operationClass: \"additive\" as MigrationOperationClass,\n storeName: IDB_MARKER_STORE,\n def: { keyPath: MARKER_KEYPATH },\n };\n}\n","import type { ContractMarkerRecord } from \"@prisma-next/contract/types\";\nimport { IDB_MARKER_STORE, type IdbDdlOp } from \"./migration-factories\";\n\n/**\n * Execute a single DDL operation against an open `upgradeneeded` transaction.\n *\n * All IndexedDB DDL (createObjectStore, deleteObjectStore, createIndex,\n * deleteIndex) MUST happen inside the version-change transaction that fires\n * in `upgradeneeded`. The `db` and `tx` references are valid only for the\n * duration of that callback.\n *\n * Shared by the runner (target-idb), the browser auto-migrate path\n * (client-idb), and the preflight CLI (family-idb) so all three apply paths\n * use a single, byte-identical implementation.\n *\n * **Idempotency.** Each op is guarded by an existence check so re-applying an\n * already-applied op is a no-op rather than a throw. This is the load-bearing\n * guarantee behind the two-phase marker write (ADR 002): if a tab is killed in\n * the window between the version-change transaction committing and the marker\n * `put` landing, the schema is advanced but the marker still points at the old\n * hash. On the next open, the chain walk re-collects the already-applied ops\n * and replays them here. Without the guards, `createObjectStore` /\n * `createIndex` throw `ConstraintError` on the existing store/index, the\n * version-change transaction aborts, and the database is permanently wedged\n * (every subsequent open repeats the failed upgrade). Contrary to a common\n * assumption, IndexedDB itself offers **no** \"already exists\" tolerance — these\n * guards are what make replay safe. (Was PLAN Issue #25.)\n */\nexport function applyOneDdlOp(db: IDBDatabase, tx: IDBTransaction, op: IdbDdlOp): void {\n switch (op.kind) {\n case \"createObjectStore\": {\n if (db.objectStoreNames.contains(op.storeName)) return;\n db.createObjectStore(op.storeName, {\n keyPath: op.def.keyPath,\n ...(op.def.autoIncrement !== undefined && { autoIncrement: op.def.autoIncrement }),\n });\n return;\n }\n case \"dropObjectStore\": {\n if (!db.objectStoreNames.contains(op.storeName)) return;\n db.deleteObjectStore(op.storeName);\n return;\n }\n case \"createIndex\": {\n const store = tx.objectStore(op.storeName);\n if (store.indexNames.contains(op.indexName)) return;\n store.createIndex(op.indexName, op.def.keyPath, {\n unique: op.def.unique,\n ...(op.def.multiEntry !== undefined && { multiEntry: op.def.multiEntry }),\n });\n return;\n }\n case \"dropIndex\": {\n const store = tx.objectStore(op.storeName);\n if (!store.indexNames.contains(op.indexName)) return;\n store.deleteIndex(op.indexName);\n return;\n }\n }\n}\n\n/**\n * Marker write input. The `space` field is the contract-space identifier\n * (`\"app\"` for the single app space; per-extension callers pass their own\n * space id when extensions land on IDB). All other fields mirror\n * {@link ContractMarkerRecord} exactly so the in-DB record has full parity\n * with the framework's canonical marker shape.\n */\nexport interface MarkerWriteInput {\n readonly space: string;\n readonly storageHash: string;\n readonly profileHash?: string;\n readonly invariants?: readonly string[];\n readonly contractJson?: unknown;\n readonly canonicalVersion?: number | null;\n readonly appTag?: string | null;\n readonly meta?: Record<string, unknown>;\n}\n\n/**\n * In-DB marker record shape stored in `_prisma_next_marker`.\n *\n * Identical to {@link ContractMarkerRecord} plus the keying `space` field;\n * `updatedAt` is a `Date` (not an ISO string) because IndexedDB serialises\n * Dates natively via structured-clone.\n */\nexport type IdbMarkerRecord = ContractMarkerRecord & { readonly space: string };\n\n/**\n * Write the contract marker into the `_prisma_next_marker` store using a\n * separate `readwrite` transaction. The marker store is created inside the\n * version-change transaction during the migration's first run (see\n * `createMarkerStoreOp`); subsequent runs reuse it.\n *\n * Keyed by `space` (defaulting to `\"app\"` at the caller layer) so the\n * storage layout doesn't have to be migrated when IDB eventually grows\n * extension support (see ADR 021 + feedback issue #5).\n */\nexport function writeMarker(db: IDBDatabase, input: MarkerWriteInput): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!db.objectStoreNames.contains(IDB_MARKER_STORE)) {\n // Marker store missing — should never happen because the planner emits\n // its creation as the first op. Non-fatal so the runner can still\n // report DDL success, but worth surfacing as a planner invariant bug.\n console.warn(\n \"[prisma-next] _prisma_next_marker store not found after DDL — this indicates a bug in the migration planner.\"\n );\n resolve();\n return;\n }\n const tx = db.transaction(IDB_MARKER_STORE, \"readwrite\");\n const store = tx.objectStore(IDB_MARKER_STORE);\n const record: IdbMarkerRecord = {\n space: input.space,\n storageHash: input.storageHash,\n profileHash: input.profileHash ?? \"\",\n updatedAt: new Date(),\n invariants: input.invariants ?? [],\n contractJson: input.contractJson ?? null,\n canonicalVersion: input.canonicalVersion ?? null,\n appTag: input.appTag ?? null,\n meta: input.meta ?? {},\n };\n const putReq = store.put(record);\n putReq.onerror = () => reject(putReq.error);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n}\n\n/**\n * Read the marker record for a given space from the `_prisma_next_marker`\n * store. Returns `null` when the store doesn't exist (fresh DB) or the\n * record is absent.\n */\nexport function readMarker(db: IDBDatabase, space: string): Promise<IdbMarkerRecord | null> {\n return new Promise((resolve, reject) => {\n if (!db.objectStoreNames.contains(IDB_MARKER_STORE)) {\n resolve(null);\n return;\n }\n const tx = db.transaction(IDB_MARKER_STORE, \"readonly\");\n const store = tx.objectStore(IDB_MARKER_STORE);\n const req = store.get(space);\n req.onsuccess = () => {\n const result = req.result as IdbMarkerRecord | undefined;\n resolve(result ?? null);\n };\n req.onerror = () => reject(req.error);\n });\n}\n\n/**\n * Open `dbName` at `targetVersion`, apply `ops` inside the `upgradeneeded`\n * callback, optionally write the contract marker (in a separate readwrite\n * tx after `onsuccess`), then close the connection.\n *\n * Returns the number of ops applied. Throws on open-request error or DDL\n * application error.\n */\nexport function openAndUpgrade(input: {\n readonly factory: IDBFactory;\n readonly dbName: string;\n readonly targetVersion: number;\n readonly ops: readonly IdbDdlOp[];\n readonly marker?: MarkerWriteInput;\n readonly onOperationStart?: (op: IdbDdlOp) => void;\n readonly onOperationComplete?: (op: IdbDdlOp) => void;\n}): Promise<number> {\n return new Promise((resolve, reject) => {\n const request = input.factory.open(input.dbName, input.targetVersion);\n\n request.onupgradeneeded = (event) => {\n const target = event.target as IDBOpenDBRequest;\n const db = target.result;\n const tx = target.transaction;\n if (tx === null) {\n reject(new Error(\"IDB: upgradeneeded fired with null version-change transaction\"));\n return;\n }\n for (const op of input.ops) {\n input.onOperationStart?.(op);\n applyOneDdlOp(db, tx, op);\n input.onOperationComplete?.(op);\n }\n };\n\n request.onsuccess = async (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n try {\n if (input.marker !== undefined) {\n await writeMarker(db, input.marker);\n }\n } finally {\n db.close();\n }\n resolve(input.ops.length);\n };\n\n request.onerror = (event) => {\n const err = (event.target as IDBOpenDBRequest).error;\n reject(err ?? new Error(\"IDB: migration open request failed without an error object\"));\n };\n\n // Another open connection at an older version is holding the upgrade back.\n // Runtime connections self-close on `versionchange` (see driver-idb), so\n // this should be unreachable in normal operation — but a stray third-party\n // connection on the same database name would otherwise hang the upgrade\n // forever. Fail fast with an actionable message instead.\n request.onblocked = () => {\n reject(\n new Error(\n `IDB: migration of \"${input.dbName}\" is blocked — another connection is open at an older ` +\n \"version and did not close. Close other tabs/connections to this database and retry.\"\n )\n );\n };\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { c as IdbMigrationControlDriverDescriptor, i as IdbMigrationPlanner, l as extractMigrationDriver, o as IdbMigrationRunner, s as IdbMigrationControlDriver, u as IdbSchemaDiffInput } from "./migration-B43yOFXO.mjs";
2
+ import { ContractSerializer, ControlAdapterInstance, ControlFamilyInstance, ControlTargetInstance } from "@prisma-next/framework-components/control";
3
+ import { Contract } from "@prisma-next/contract/types";
4
+ import { TargetBoundComponentDescriptor } from "@prisma-next/framework-components/components";
5
+
6
+ //#region src/exports/control.d.ts
7
+ declare const idbControlTargetDescription: {
8
+ contractSerializer: ContractSerializer<Contract<import("@prisma-next/contract/types").StorageBase<string>>>;
9
+ migrations: {
10
+ createPlanner(_adapter: ControlAdapterInstance<"idb", "idb">): IdbMigrationPlanner;
11
+ createRunner(_family: ControlFamilyInstance<"idb", unknown>): IdbMigrationRunner;
12
+ contractToSchema(contract: Contract | null, _frameworkComponents?: ReadonlyArray<TargetBoundComponentDescriptor<"idb", "idb">>): IdbSchemaDiffInput | null;
13
+ };
14
+ create(): ControlTargetInstance<"idb", "idb">;
15
+ kind: "target";
16
+ familyId: "idb";
17
+ targetId: "idb";
18
+ id: "idb";
19
+ version: "0.0.1";
20
+ types: {
21
+ readonly codecTypes: {
22
+ readonly import: {
23
+ readonly package: "@prisma-next-idb/target-idb/pack";
24
+ readonly named: "CodecTypes";
25
+ readonly alias: "IdbCodecTypes";
26
+ };
27
+ readonly codecDescriptors: readonly import("@prisma-next/framework-components/codec").AnyCodecDescriptor[];
28
+ };
29
+ };
30
+ };
31
+ //#endregion
32
+ export { type IdbMigrationControlDriver, IdbMigrationControlDriverDescriptor, idbControlTargetDescription as default, extractMigrationDriver };
33
+ //# sourceMappingURL=control.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control.d.mts","names":[],"sources":["../src/exports/control.ts"],"mappings":";;;;;;cA+CM,2BAAA;;;4BAdoB,sBAAA,iBAAoC,mBAAA;0BAGtC,qBAAA,mBAAqC,kBAAA;+BAI/C,QAAA,SAAe,oBAAA,GACF,aAAA,CAAc,8BAAA,kBAAD,kBAAA;EAAA;YAU5B,qBAAA"}
@@ -0,0 +1,44 @@
1
+ import { t as idbTargetDescriptorMeta } from "./descriptor-meta-CKLekROR.mjs";
2
+ import { a as contractToIdbSchema, i as IdbMigrationPlanner, n as extractMigrationDriver, r as IdbMigrationRunner, t as IdbMigrationControlDriverDescriptor } from "./migration-driver-4T8Hk1vu.mjs";
3
+ //#region src/exports/control.ts
4
+ /**
5
+ * IDB contract serializer — validates on input, passes through on output.
6
+ *
7
+ * IDB contracts are TypeScript-first (`defineContract`), so they are
8
+ * already plain objects with no class instances. Serialization is identity;
9
+ * deserialization runs validation.
10
+ */
11
+ const idbContractSerializer = {
12
+ deserializeContract(json) {
13
+ return json;
14
+ },
15
+ serializeContract(_contract) {
16
+ return _contract;
17
+ }
18
+ };
19
+ const idbMigrationsCapability = {
20
+ createPlanner(_adapter) {
21
+ return new IdbMigrationPlanner();
22
+ },
23
+ createRunner(_family) {
24
+ return new IdbMigrationRunner();
25
+ },
26
+ contractToSchema(contract, _frameworkComponents) {
27
+ return contractToIdbSchema(contract);
28
+ }
29
+ };
30
+ const idbControlTargetDescription = {
31
+ ...idbTargetDescriptorMeta,
32
+ contractSerializer: idbContractSerializer,
33
+ migrations: idbMigrationsCapability,
34
+ create() {
35
+ return {
36
+ familyId: "idb",
37
+ targetId: "idb"
38
+ };
39
+ }
40
+ };
41
+ //#endregion
42
+ export { IdbMigrationControlDriverDescriptor, idbControlTargetDescription as default, extractMigrationDriver };
43
+
44
+ //# sourceMappingURL=control.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control.mjs","names":[],"sources":["../src/exports/control.ts"],"sourcesContent":["import type { Contract } from \"@prisma-next/contract/types\";\nimport type { TargetBoundComponentDescriptor } from \"@prisma-next/framework-components/components\";\nimport type {\n ContractSerializer,\n ControlAdapterInstance,\n ControlFamilyInstance,\n ControlTargetInstance,\n MigratableTargetDescriptor,\n TargetMigrationsCapability,\n} from \"@prisma-next/framework-components/control\";\nimport { idbTargetDescriptorMeta } from \"../core/descriptor-meta\";\nimport { IdbMigrationPlanner, contractToIdbSchema } from \"../core/migration-planner\";\nimport { IdbMigrationRunner } from \"../core/migration-runner\";\n\n/**\n * IDB contract serializer — validates on input, passes through on output.\n *\n * IDB contracts are TypeScript-first (`defineContract`), so they are\n * already plain objects with no class instances. Serialization is identity;\n * deserialization runs validation.\n */\nconst idbContractSerializer: ContractSerializer<Contract> = {\n deserializeContract<T extends Contract = Contract>(json: unknown): T {\n return json as T;\n },\n serializeContract(_contract: Contract) {\n // IDB contracts are plain JSON-safe objects. Serialization is identity.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return _contract as any;\n },\n};\n\nconst idbMigrationsCapability = {\n createPlanner(_adapter: ControlAdapterInstance<\"idb\", \"idb\">) {\n return new IdbMigrationPlanner();\n },\n createRunner(_family: ControlFamilyInstance<\"idb\", unknown>) {\n return new IdbMigrationRunner();\n },\n contractToSchema(\n contract: Contract | null,\n _frameworkComponents?: ReadonlyArray<TargetBoundComponentDescriptor<\"idb\", \"idb\">>\n ) {\n return contractToIdbSchema(contract);\n },\n} satisfies TargetMigrationsCapability<\"idb\", \"idb\">;\n\nconst idbControlTargetDescription = {\n ...idbTargetDescriptorMeta,\n contractSerializer: idbContractSerializer,\n migrations: idbMigrationsCapability,\n create(): ControlTargetInstance<\"idb\", \"idb\"> {\n return { familyId: \"idb\", targetId: \"idb\" };\n },\n} satisfies MigratableTargetDescriptor<\"idb\", \"idb\">;\n\nexport default idbControlTargetDescription;\nexport { IdbMigrationControlDriverDescriptor, extractMigrationDriver } from \"../core/migration-driver\";\nexport type { IdbMigrationControlDriver } from \"../core/migration-driver\";\n"],"mappings":";;;;;;;;;;AAqBA,MAAM,wBAAsD;CAC1D,oBAAmD,MAAkB;EACnE,OAAO;CACT;CACA,kBAAkB,WAAqB;EAGrC,OAAO;CACT;AACF;AAEA,MAAM,0BAA0B;CAC9B,cAAc,UAAgD;EAC5D,OAAO,IAAI,oBAAoB;CACjC;CACA,aAAa,SAAgD;EAC3D,OAAO,IAAI,mBAAmB;CAChC;CACA,iBACE,UACA,sBACA;EACA,OAAO,oBAAoB,QAAQ;CACrC;AACF;AAEA,MAAM,8BAA8B;CAClC,GAAG;CACH,oBAAoB;CACpB,YAAY;CACZ,SAA8C;EAC5C,OAAO;GAAE,UAAU;GAAO,UAAU;EAAM;CAC5C;AACF"}