@prisma-idb/client-idb 0.0.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.
package/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # `@prisma-idb/client-idb`
2
+
3
+ > Part of the [`@prisma-idb`](https://prisma-idb.dev/) driver stack for Prisma 8.
4
+
5
+ **client-idb** is the ORM layer — the `idbOrm` typed query builder that end users and framework integrations call directly.
6
+
7
+ ## Stack position
8
+
9
+ ```
10
+ family-idb (CLI / config)
11
+ client-idb ← you are here (ORM query builder)
12
+ runtime-idb (RuntimeCore)
13
+ adapter-idb (query AST → IDB plan)
14
+ driver-idb (window.indexedDB wrapper)
15
+ target-idb (identity + migrations)
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ Consumed internally by [`@prisma-idb/family-idb`](https://www.npmjs.com/package/@prisma-idb/family-idb). You generally do not install this package directly — use `family-idb` as your entry point.
21
+
22
+ ## License
23
+
24
+ MIT
@@ -0,0 +1,132 @@
1
+ import { b as IdbContract } from "./idb-orm-zCRkkD_D.mjs";
2
+ import { a as IdbClientOptions, i as IdbClient, n as ManagedIdbClientOptions, o as createIdbClient, r as createManagedIdbClient, t as ManagedIdbClient } from "./managed-client-kdyHm72t.mjs";
3
+ import { Contract } from "@prisma/orm-framework/contract/types";
4
+ import { ContractSpace, MigrationOperationClass } from "@prisma/orm-framework/components/control";
5
+ import { IdbExtensionSpace } from "@prisma-idb/family-idb/control";
6
+ //#region src/core/auto-migrate.d.ts
7
+ /**
8
+ * Migration policy for the browser-side apply path.
9
+ *
10
+ * Two knobs:
11
+ *
12
+ * - `allowedOperationClasses`: filter applied to each op's `operationClass`.
13
+ * Defaults to `['additive', 'widening']`. Anything outside this set is
14
+ * dropped before the upgrade transaction opens.
15
+ * - `onDestructive`: what to do if the planner emitted a destructive op
16
+ * that the filter just dropped. `'refuse'` (default) throws so the user
17
+ * sees the situation; `'allow'` re-includes destructive ops.
18
+ *
19
+ * Default is **safe**: a contract change that drops a store will refuse to
20
+ * apply unless the developer opts in. A user's local IDB can hold months
21
+ * of accumulated state (drafts, offline queue, cached content) and the
22
+ * spec explicitly calls out the silent-data-loss risk if destructive ops
23
+ * apply on every page load. See `FEEDBACKS.md` §4.
24
+ */
25
+ interface MigrationPolicy {
26
+ readonly allowedOperationClasses?: readonly MigrationOperationClass[];
27
+ readonly onDestructive?: "refuse" | "allow";
28
+ }
29
+ /**
30
+ * Options for {@link createAutoMigratingIdbClient}.
31
+ *
32
+ * `contractSpace` is the bundled artefact produced at design time by
33
+ * `prisma-idb migration contract-space`. It carries the canonical
34
+ * contract JSON, the ordered list of migration packages, and the head
35
+ * ref the runtime walks toward.
36
+ *
37
+ * `extensions` is an optional list of additional contract spaces contributed
38
+ * by IDB extensions (e.g. the sync extension). Each extension space migrates
39
+ * independently of the application space and writes its own marker row keyed
40
+ * by the extension's `spaceId`.
41
+ */
42
+ interface AutoMigrateClientOptions<TContract extends IdbContract> {
43
+ readonly contractSpace: ContractSpace<TContract>;
44
+ readonly dbName: string;
45
+ /** Migration policy. Defaults to safe (additive + widening only, refuse destructive). */
46
+ readonly policy?: MigrationPolicy;
47
+ /** IDB factory override — primarily for tests. Defaults to `indexedDB`. */
48
+ readonly factory?: IDBFactory;
49
+ /**
50
+ * Additional contract spaces from IDB extensions.
51
+ *
52
+ * Every space with pending work — the application space plus each
53
+ * extension — is applied together in one combined `upgradeneeded`
54
+ * transaction and one batched marker-write transaction (ADR 010). Each
55
+ * space still gets its own marker row in `_prisma_next_marker`.
56
+ */
57
+ readonly extensions?: ReadonlyArray<IdbExtensionSpace>;
58
+ }
59
+ /**
60
+ * Create a typed IDB client, applying any pending migrations from the bundled
61
+ * `contractSpace` first.
62
+ *
63
+ * **What runs**:
64
+ *
65
+ * 1. Open the database at the current local version, read the marker from
66
+ * `_prisma_next_marker`. (Null for a fresh database)
67
+ * 2. If the marker hash equals `contractSpace.headRef.hash`, the database
68
+ * is already at the target — return the client immediately.
69
+ * 3. Otherwise, walk `contractSpace.migrations` from the marker hash (or
70
+ * `null` for fresh) to `headRef.hash`, collecting each pending
71
+ * package's `ops` in chain order.
72
+ * 4. Apply the policy filter. Refuse if any destructive op was filtered
73
+ * out and `onDestructive === 'refuse'`.
74
+ * 5. If `extensions` are configured, repeat steps 1–4 for each extension
75
+ * space, then combine every space's pending ops into ONE reopen at
76
+ * `db.version + 1` so `upgradeneeded` fires once; apply every collected
77
+ * op (all spaces) inside that single version-change transaction.
78
+ * 6. Write every migrated space's marker in one batched readwrite tx.
79
+ * 7. Hand back the typed `IdbClient`.
80
+ *
81
+ * **What does NOT run in the browser**:
82
+ *
83
+ * The planner does not ship to the browser. The differ does not run. Live-DB
84
+ * schema introspection does not happen. All the planning was done once at
85
+ * design time and is encoded in the bundled `ops.json` blobs inside
86
+ * `contractSpace.migrations`.
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * import { createAutoMigratingIdbClient } from '@prisma-idb/client-idb/client-auto';
91
+ * import { contractSpace } from './prisma/contract-space.generated';
92
+ *
93
+ * const db = await createAutoMigratingIdbClient({ contractSpace, dbName: 'my-app' });
94
+ * const users = await db.orm.users.all().toArray();
95
+ * ```
96
+ */
97
+ declare function createAutoMigratingIdbClient<TContract extends IdbContract>(options: AutoMigrateClientOptions<TContract>): Promise<IdbClient<TContract>>;
98
+ /**
99
+ * The migration loop. Exported for tests.
100
+ *
101
+ * Collects pending ops from the app space and every extension space, then
102
+ * applies all of them in a single combined `upgradeneeded` transaction
103
+ * (one IDB version bump total) followed by one batched marker-write
104
+ * transaction covering every space that migrated. See ADR 010 for details.
105
+ *
106
+ * @internal Prefer {@link createAutoMigratingIdbClient}.
107
+ */
108
+ declare function autoMigrate(input: {
109
+ readonly contractSpace: ContractSpace<Contract>;
110
+ readonly dbName: string;
111
+ readonly policy: Required<MigrationPolicy>;
112
+ readonly factory: IDBFactory;
113
+ readonly extensions?: ReadonlyArray<IdbExtensionSpace>;
114
+ }): Promise<void>;
115
+ //#endregion
116
+ //#region src/core/managed-auto-migrate.d.ts
117
+ /**
118
+ * @example
119
+ * ```ts
120
+ * import { createManagedAutoIdbClient } from '@prisma-idb/client-idb/client-auto';
121
+ *
122
+ * const db = createManagedAutoIdbClient({ contractSpace, dbName: 'my-app' });
123
+ *
124
+ * const client = await db.get();
125
+ * // ... on logout:
126
+ * await db.reset();
127
+ * ```
128
+ */
129
+ declare function createManagedAutoIdbClient<TContract extends IdbContract>(options: AutoMigrateClientOptions<TContract>): ManagedIdbClient<IdbClient<TContract>>;
130
+ //#endregion
131
+ export { type AutoMigrateClientOptions, type IdbClient, type IdbClientOptions, type ManagedIdbClient, type ManagedIdbClientOptions, type MigrationPolicy, autoMigrate, createAutoMigratingIdbClient, createIdbClient, createManagedAutoIdbClient, createManagedIdbClient };
132
+ //# sourceMappingURL=client-auto.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-auto.d.mts","names":[],"sources":["../src/core/auto-migrate.ts","../src/core/managed-auto-migrate.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;UAqCiB;WACN,mCAAmC;WACnC;;;;;;;;;;;;;;;UAuBM,yBAAyB,kBAAkB;WACjD,eAAe,cAAc;WAC7B;;WAEA,SAAS;;WAET,UAAU;;;;;;;;;WASV,aAAa,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyChB,6BAA6B,kBAAkB,aACnE,SAAS,yBAAyB,aACjC,QAAQ,UAAU;;;;;;;;;;;iBAyCC,YAAY;WAKvB,eAAe,cAAc;WAC7B;WACA,QAAQ,SAAS;WACjB,SAAS;WACT,aAAa,cAAc;IAClC;;;;;;;;;;;;;;;iBC5IY,2BAA2B,kBAAkB,aAC3D,SAAS,yBAAyB,aACjC,iBAAiB,UAAU"}
@@ -0,0 +1,277 @@
1
+ import { n as createIdbClient, t as createManagedIdbClient } from "./managed-client-B_8JZM3h.mjs";
2
+ import { isIdbDdlOp, openAndUpgrade, readMarker } from "@prisma-idb/target-idb/runtime";
3
+ import { APP_SPACE_ID } from "@prisma/orm-framework/components/control";
4
+ import { canonicalizeJson } from "@prisma/orm-framework/components/utils";
5
+ //#region src/core/migration-hash.ts
6
+ /** Hex-encode the bytes of a digest buffer. */
7
+ function toHex(buffer) {
8
+ return Array.from(new Uint8Array(buffer), (byte) => byte.toString(16).padStart(2, "0")).join("");
9
+ }
10
+ /** SHA-256 → lowercase hex, via WebCrypto (`crypto.subtle`) — works in the browser. */
11
+ async function sha256Hex(input) {
12
+ return toHex(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(input)));
13
+ }
14
+ /**
15
+ * Browser-safe re-implementation of `computeMigrationHash` from
16
+ * `@prisma/orm-toolchain/migration-tools/hash`.
17
+ *
18
+ * The framework version hashes with Node's `node:crypto` `createHash`, which
19
+ * does not exist in the browser — so importing it into the runtime auto-migrate
20
+ * integrity check (PLAN Issue #23 / ADR 199) throws `createHash is not a
21
+ * function` on every client init and breaks the whole app. This version is
22
+ * **byte-identical** to the framework's: it reuses the same `canonicalizeJson`
23
+ * and the same nested SHA-256/hex scheme — strip `migrationHash` from the
24
+ * metadata, hash the canonicalized metadata and ops separately, then hash
25
+ * the canonicalized pair of those two hashes, as bare lowercase hex (no
26
+ * `sha256:` prefix — dropped repo-wide at rc.4). Reusing the
27
+ * identical canonicalization + algorithm guarantees the result matches the
28
+ * `migrationHash` the CLI recorded, so the integrity check stays meaningful.
29
+ *
30
+ * `crypto.subtle.digest` is async, hence the `Promise` return (the framework's
31
+ * Node version is synchronous). Callers in the async migration path await it.
32
+ */
33
+ async function computeMigrationHash(metadata, ops) {
34
+ const stripped = { ...metadata };
35
+ delete stripped["migrationHash"];
36
+ const inner = await Promise.all([sha256Hex(canonicalizeJson(stripped)), sha256Hex(canonicalizeJson(ops))]);
37
+ return sha256Hex(canonicalizeJson(inner));
38
+ }
39
+ //#endregion
40
+ //#region src/core/auto-migrate.ts
41
+ const SAFE_POLICY = {
42
+ allowedOperationClasses: ["additive", "widening"],
43
+ onDestructive: "refuse"
44
+ };
45
+ /**
46
+ * Create a typed IDB client, applying any pending migrations from the bundled
47
+ * `contractSpace` first.
48
+ *
49
+ * **What runs**:
50
+ *
51
+ * 1. Open the database at the current local version, read the marker from
52
+ * `_prisma_next_marker`. (Null for a fresh database)
53
+ * 2. If the marker hash equals `contractSpace.headRef.hash`, the database
54
+ * is already at the target — return the client immediately.
55
+ * 3. Otherwise, walk `contractSpace.migrations` from the marker hash (or
56
+ * `null` for fresh) to `headRef.hash`, collecting each pending
57
+ * package's `ops` in chain order.
58
+ * 4. Apply the policy filter. Refuse if any destructive op was filtered
59
+ * out and `onDestructive === 'refuse'`.
60
+ * 5. If `extensions` are configured, repeat steps 1–4 for each extension
61
+ * space, then combine every space's pending ops into ONE reopen at
62
+ * `db.version + 1` so `upgradeneeded` fires once; apply every collected
63
+ * op (all spaces) inside that single version-change transaction.
64
+ * 6. Write every migrated space's marker in one batched readwrite tx.
65
+ * 7. Hand back the typed `IdbClient`.
66
+ *
67
+ * **What does NOT run in the browser**:
68
+ *
69
+ * The planner does not ship to the browser. The differ does not run. Live-DB
70
+ * schema introspection does not happen. All the planning was done once at
71
+ * design time and is encoded in the bundled `ops.json` blobs inside
72
+ * `contractSpace.migrations`.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * import { createAutoMigratingIdbClient } from '@prisma-idb/client-idb/client-auto';
77
+ * import { contractSpace } from './prisma/contract-space.generated';
78
+ *
79
+ * const db = await createAutoMigratingIdbClient({ contractSpace, dbName: 'my-app' });
80
+ * const users = await db.orm.users.all().toArray();
81
+ * ```
82
+ */
83
+ async function createAutoMigratingIdbClient(options) {
84
+ const factory = options.factory ?? indexedDB;
85
+ const policy = mergePolicy(options.policy);
86
+ await autoMigrate({
87
+ contractSpace: options.contractSpace,
88
+ dbName: options.dbName,
89
+ policy,
90
+ factory,
91
+ ...options.extensions !== void 0 ? { extensions: options.extensions } : {}
92
+ });
93
+ return createIdbClient({
94
+ contract: options.contractSpace.contractJson,
95
+ dbName: options.dbName,
96
+ factory
97
+ });
98
+ }
99
+ function mergePolicy(p) {
100
+ return {
101
+ allowedOperationClasses: p?.allowedOperationClasses ?? SAFE_POLICY.allowedOperationClasses,
102
+ onDestructive: p?.onDestructive ?? SAFE_POLICY.onDestructive
103
+ };
104
+ }
105
+ /**
106
+ * The migration loop. Exported for tests.
107
+ *
108
+ * Collects pending ops from the app space and every extension space, then
109
+ * applies all of them in a single combined `upgradeneeded` transaction
110
+ * (one IDB version bump total) followed by one batched marker-write
111
+ * transaction covering every space that migrated. See ADR 010 for details.
112
+ *
113
+ * @internal Prefer {@link createAutoMigratingIdbClient}.
114
+ */
115
+ async function autoMigrate(input) {
116
+ const { dbName, policy, factory } = input;
117
+ const spaces = [{
118
+ spaceId: APP_SPACE_ID,
119
+ contractSpace: input.contractSpace
120
+ }, ...input.extensions ?? []];
121
+ const seenSpaceIds = /* @__PURE__ */ new Set();
122
+ for (const extension of input.extensions ?? []) {
123
+ if (extension.spaceId === APP_SPACE_ID || seenSpaceIds.has(extension.spaceId)) throw new Error(`Invalid duplicate or reserved extension space ID: "${extension.spaceId}"`);
124
+ seenSpaceIds.add(extension.spaceId);
125
+ }
126
+ for (const space of spaces) {
127
+ const declaredHash = space.contractSpace.contractJson.storage.storageHash;
128
+ const headHash = space.contractSpace.headRef.hash;
129
+ if (declaredHash !== headHash) throw new Error(`Contract space "${space.spaceId}" is internally inconsistent: contractJson.storage.storageHash (${declaredHash}) does not match headRef.hash (${headHash}). The contract source likely changed without regenerating migrations/refs/head.json for this space — rebuild its migration chain.`);
130
+ }
131
+ const { currentVersion: initialVersion } = await openAndReadMarker(dbName, factory, APP_SPACE_ID);
132
+ const pendingPerSpace = [];
133
+ let totalDestructiveDropped = 0;
134
+ for (const space of spaces) {
135
+ const targetHash = space.contractSpace.headRef.hash;
136
+ const { markerHash } = await openAndReadMarker(dbName, factory, space.spaceId);
137
+ if (markerHash === targetHash) continue;
138
+ const { pendingOps, destructiveDropped } = await walkChain({
139
+ markerHash,
140
+ headHash: targetHash,
141
+ migrations: space.contractSpace.migrations,
142
+ policy
143
+ });
144
+ totalDestructiveDropped += destructiveDropped;
145
+ pendingPerSpace.push({
146
+ spaceId: space.spaceId,
147
+ ops: pendingOps,
148
+ storageHash: targetHash
149
+ });
150
+ }
151
+ if (totalDestructiveDropped > 0 && policy.onDestructive === "refuse") throw new Error(`Auto-migration refused: ${totalDestructiveDropped} destructive operation(s) in the pending chain would drop user data. To allow them, pass \`policy: { onDestructive: 'allow' }\` to createAutoMigratingIdbClient. Per-tab persistent state (drafts, offline queue, cached content) will be lost when destructive ops apply silently — review the change before opting in.`);
152
+ if (pendingPerSpace.length === 0) return;
153
+ const orderedPending = [...pendingPerSpace].sort((a, b) => {
154
+ if (a.spaceId === APP_SPACE_ID) return 1;
155
+ if (b.spaceId === APP_SPACE_ID) return -1;
156
+ return a.spaceId.localeCompare(b.spaceId);
157
+ });
158
+ await openAndUpgrade({
159
+ factory,
160
+ dbName,
161
+ targetVersion: initialVersion + 1,
162
+ ops: orderedPending.flatMap((space) => space.ops),
163
+ markers: orderedPending.map((space) => ({
164
+ space: space.spaceId,
165
+ storageHash: space.storageHash
166
+ }))
167
+ });
168
+ }
169
+ /**
170
+ * Walk the migration chain from `markerHash` (or `null` for a fresh DB) to
171
+ * `headHash`, collecting each pending package's ops in order. Applies the
172
+ * policy filter on each op as it's added; returns the count of destructive
173
+ * ops that were dropped so the caller can refuse if the policy demands.
174
+ *
175
+ * Throws on chain discontinuity (no package whose `from === cursor`) so
176
+ * misconfigured `contractSpace` inputs fail loudly rather than silently
177
+ * leaving the DB at an intermediate state.
178
+ */
179
+ async function walkChain(input) {
180
+ const byFrom = /* @__PURE__ */ new Map();
181
+ for (const pkg of input.migrations) byFrom.set(pkg.metadata.from, pkg);
182
+ const allowed = new Set(input.policy.allowedOperationClasses);
183
+ const pendingOps = [];
184
+ let destructiveDropped = 0;
185
+ let cursor = input.markerHash;
186
+ const visited = /* @__PURE__ */ new Set();
187
+ while (cursor !== input.headHash) {
188
+ if (visited.has(cursor)) throw new Error(`Auto-migration chain contains a cycle at hash ${JSON.stringify(cursor)}. Re-run \`prisma-idb migration contract-space\` to rebuild a valid chain.`);
189
+ visited.add(cursor);
190
+ const next = byFrom.get(cursor);
191
+ if (!next) throw new Error(`Auto-migration chain broken: no migration package with from === ${JSON.stringify(cursor)}. Verify that contract-space.generated.ts is up to date by re-running \`prisma-idb migration contract-space\`.`);
192
+ const computedHash = await computeMigrationHash(next.metadata, next.ops);
193
+ if (computedHash !== next.metadata.migrationHash) throw new Error(`Migration package "${next.dirName}" failed integrity check: stored migrationHash ${next.metadata.migrationHash} does not match computed hash ${computedHash}. The ops may have been edited after the package was generated. Re-run \`prisma-idb migration plan\` to regenerate the package.`);
194
+ for (const op of next.ops) {
195
+ if (!isIdbDdlOp(op)) throw new Error(`Non-IDB operation found in migration package ${next.dirName}: ${JSON.stringify(op)}`);
196
+ if (allowed.has(op.operationClass)) pendingOps.push(op);
197
+ else if (op.operationClass === "destructive") if (input.policy.onDestructive === "allow") pendingOps.push(op);
198
+ else destructiveDropped += 1;
199
+ }
200
+ cursor = next.metadata.to;
201
+ }
202
+ return {
203
+ pendingOps,
204
+ destructiveDropped
205
+ };
206
+ }
207
+ /**
208
+ * Open the database at its current local version (no version arg), read the
209
+ * marker for `spaceId`, then close the connection. Returns the current integer
210
+ * version so the caller can compute `currentVersion + 1` for the upgrade re-open.
211
+ */
212
+ function openAndReadMarker(dbName, factory, spaceId) {
213
+ return new Promise((resolve, reject) => {
214
+ let req;
215
+ try {
216
+ req = factory.open(dbName);
217
+ } catch (err) {
218
+ reject(err);
219
+ return;
220
+ }
221
+ req.onsuccess = () => {
222
+ const db = req.result;
223
+ const currentVersion = db.version;
224
+ (async () => {
225
+ try {
226
+ const record = await readMarker(db, spaceId);
227
+ resolve({
228
+ currentVersion,
229
+ markerHash: record?.storageHash ?? null
230
+ });
231
+ } catch (err) {
232
+ reject(err);
233
+ } finally {
234
+ db.close();
235
+ }
236
+ })();
237
+ };
238
+ req.onerror = () => {
239
+ reject(req.error ?? /* @__PURE__ */ new Error(`IDB open failed while reading marker for "${dbName}"`));
240
+ };
241
+ });
242
+ }
243
+ //#endregion
244
+ //#region src/core/managed-auto-migrate.ts
245
+ /**
246
+ * `createManagedAutoIdbClient` — `createManagedIdbClient` pre-wired to
247
+ * `createAutoMigratingIdbClient`. Composing those two by hand means writing
248
+ * `dbName` twice, into two independent option bags with nothing tying them
249
+ * together — `createManagedIdbClient`'s `dbName` is used only by `reset()`'s
250
+ * `deleteDatabase` call, so if it ever drifts from the factory's own
251
+ * `dbName` (typo, copy-paste into a second app), `reset()` silently deletes
252
+ * the wrong database (or nothing) while the real one keeps its data. This
253
+ * reads `dbName` (and a `factory` override, if set) once and threads it to
254
+ * both layers, so they can't diverge.
255
+ */
256
+ /**
257
+ * @example
258
+ * ```ts
259
+ * import { createManagedAutoIdbClient } from '@prisma-idb/client-idb/client-auto';
260
+ *
261
+ * const db = createManagedAutoIdbClient({ contractSpace, dbName: 'my-app' });
262
+ *
263
+ * const client = await db.get();
264
+ * // ... on logout:
265
+ * await db.reset();
266
+ * ```
267
+ */
268
+ function createManagedAutoIdbClient(options) {
269
+ return createManagedIdbClient(() => createAutoMigratingIdbClient(options), {
270
+ dbName: options.dbName,
271
+ ...options.factory !== void 0 ? { idbFactory: options.factory } : {}
272
+ });
273
+ }
274
+ //#endregion
275
+ export { autoMigrate, createAutoMigratingIdbClient, createIdbClient, createManagedAutoIdbClient, createManagedIdbClient };
276
+
277
+ //# sourceMappingURL=client-auto.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-auto.mjs","names":[],"sources":["../src/core/migration-hash.ts","../src/core/auto-migrate.ts","../src/core/managed-auto-migrate.ts"],"sourcesContent":["import type { MigrationPackage } from \"@prisma/orm-framework/components/control\";\nimport { canonicalizeJson } from \"@prisma/orm-framework/components/utils\";\n\n/** Hex-encode the bytes of a digest buffer. */\nfunction toHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer), (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/** SHA-256 → lowercase hex, via WebCrypto (`crypto.subtle`) — works in the browser. */\nasync function sha256Hex(input: string): Promise<string> {\n const digest = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(input));\n return toHex(digest);\n}\n\n/**\n * Browser-safe re-implementation of `computeMigrationHash` from\n * `@prisma/orm-toolchain/migration-tools/hash`.\n *\n * The framework version hashes with Node's `node:crypto` `createHash`, which\n * does not exist in the browser — so importing it into the runtime auto-migrate\n * integrity check (PLAN Issue #23 / ADR 199) throws `createHash is not a\n * function` on every client init and breaks the whole app. This version is\n * **byte-identical** to the framework's: it reuses the same `canonicalizeJson`\n * and the same nested SHA-256/hex scheme — strip `migrationHash` from the\n * metadata, hash the canonicalized metadata and ops separately, then hash\n * the canonicalized pair of those two hashes, as bare lowercase hex (no\n * `sha256:` prefix — dropped repo-wide at rc.4). Reusing the\n * identical canonicalization + algorithm guarantees the result matches the\n * `migrationHash` the CLI recorded, so the integrity check stays meaningful.\n *\n * `crypto.subtle.digest` is async, hence the `Promise` return (the framework's\n * Node version is synchronous). Callers in the async migration path await it.\n */\nexport async function computeMigrationHash(\n metadata: MigrationPackage[\"metadata\"],\n ops: MigrationPackage[\"ops\"]\n): Promise<string> {\n // v0.12.0 strips only `migrationHash` before hashing (`hints`/`labels` were\n // removed from the on-disk manifest schema entirely).\n const stripped: Record<string, unknown> = { ...metadata };\n delete stripped[\"migrationHash\"];\n const inner = await Promise.all([sha256Hex(canonicalizeJson(stripped)), sha256Hex(canonicalizeJson(ops))]);\n return sha256Hex(canonicalizeJson(inner));\n}\n","import type { Contract } from \"@prisma/orm-framework/contract/types\";\nimport type {\n ContractSpace,\n MigrationOperationClass,\n MigrationPackage,\n} from \"@prisma/orm-framework/components/control\";\nimport { APP_SPACE_ID } from \"@prisma/orm-framework/components/control\";\nimport type { IdbExtensionSpace } from \"@prisma-idb/family-idb/control\";\n// Browser-safe (WebCrypto) hash — the framework's `@prisma/orm-toolchain/migration-tools/hash`\n// uses `node:crypto` and throws in the browser (PLAN Issue #23 regression).\nimport { computeMigrationHash } from \"./migration-hash\";\n// Import from `./runtime` (not `./migration`) so `MigrationCLI` → `node:fs`\n// is not bundled into the browser client.\nimport { isIdbDdlOp, openAndUpgrade, readMarker, type IdbDdlOp } from \"@prisma-idb/target-idb/runtime\";\nimport { createIdbClient, type IdbClient } from \"./idb-client\";\nimport type { IdbContract } from \"./types\";\n\n// ── Public policy types ──────────────────────────────────────────────────────\n\n/**\n * Migration policy for the browser-side apply path.\n *\n * Two knobs:\n *\n * - `allowedOperationClasses`: filter applied to each op's `operationClass`.\n * Defaults to `['additive', 'widening']`. Anything outside this set is\n * dropped before the upgrade transaction opens.\n * - `onDestructive`: what to do if the planner emitted a destructive op\n * that the filter just dropped. `'refuse'` (default) throws so the user\n * sees the situation; `'allow'` re-includes destructive ops.\n *\n * Default is **safe**: a contract change that drops a store will refuse to\n * apply unless the developer opts in. A user's local IDB can hold months\n * of accumulated state (drafts, offline queue, cached content) and the\n * spec explicitly calls out the silent-data-loss risk if destructive ops\n * apply on every page load. See `FEEDBACKS.md` §4.\n */\nexport interface MigrationPolicy {\n readonly allowedOperationClasses?: readonly MigrationOperationClass[];\n readonly onDestructive?: \"refuse\" | \"allow\";\n}\n\nconst SAFE_POLICY: Required<MigrationPolicy> = {\n allowedOperationClasses: [\"additive\", \"widening\"],\n onDestructive: \"refuse\",\n};\n\n// ── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Options for {@link createAutoMigratingIdbClient}.\n *\n * `contractSpace` is the bundled artefact produced at design time by\n * `prisma-idb migration contract-space`. It carries the canonical\n * contract JSON, the ordered list of migration packages, and the head\n * ref the runtime walks toward.\n *\n * `extensions` is an optional list of additional contract spaces contributed\n * by IDB extensions (e.g. the sync extension). Each extension space migrates\n * independently of the application space and writes its own marker row keyed\n * by the extension's `spaceId`.\n */\nexport interface AutoMigrateClientOptions<TContract extends IdbContract> {\n readonly contractSpace: ContractSpace<TContract>;\n readonly dbName: string;\n /** Migration policy. Defaults to safe (additive + widening only, refuse destructive). */\n readonly policy?: MigrationPolicy;\n /** IDB factory override — primarily for tests. Defaults to `indexedDB`. */\n readonly factory?: IDBFactory;\n /**\n * Additional contract spaces from IDB extensions.\n *\n * Every space with pending work — the application space plus each\n * extension — is applied together in one combined `upgradeneeded`\n * transaction and one batched marker-write transaction (ADR 010). Each\n * space still gets its own marker row in `_prisma_next_marker`.\n */\n readonly extensions?: ReadonlyArray<IdbExtensionSpace>;\n}\n\n/**\n * Create a typed IDB client, applying any pending migrations from the bundled\n * `contractSpace` first.\n *\n * **What runs**:\n *\n * 1. Open the database at the current local version, read the marker from\n * `_prisma_next_marker`. (Null for a fresh database)\n * 2. If the marker hash equals `contractSpace.headRef.hash`, the database\n * is already at the target — return the client immediately.\n * 3. Otherwise, walk `contractSpace.migrations` from the marker hash (or\n * `null` for fresh) to `headRef.hash`, collecting each pending\n * package's `ops` in chain order.\n * 4. Apply the policy filter. Refuse if any destructive op was filtered\n * out and `onDestructive === 'refuse'`.\n * 5. If `extensions` are configured, repeat steps 1–4 for each extension\n * space, then combine every space's pending ops into ONE reopen at\n * `db.version + 1` so `upgradeneeded` fires once; apply every collected\n * op (all spaces) inside that single version-change transaction.\n * 6. Write every migrated space's marker in one batched readwrite tx.\n * 7. Hand back the typed `IdbClient`.\n *\n * **What does NOT run in the browser**:\n *\n * The planner does not ship to the browser. The differ does not run. Live-DB\n * schema introspection does not happen. All the planning was done once at\n * design time and is encoded in the bundled `ops.json` blobs inside\n * `contractSpace.migrations`.\n *\n * @example\n * ```ts\n * import { createAutoMigratingIdbClient } from '@prisma-idb/client-idb/client-auto';\n * import { contractSpace } from './prisma/contract-space.generated';\n *\n * const db = await createAutoMigratingIdbClient({ contractSpace, dbName: 'my-app' });\n * const users = await db.orm.users.all().toArray();\n * ```\n */\nexport async function createAutoMigratingIdbClient<TContract extends IdbContract>(\n options: AutoMigrateClientOptions<TContract>\n): Promise<IdbClient<TContract>> {\n const factory = options.factory ?? indexedDB;\n const policy = mergePolicy(options.policy);\n\n await autoMigrate({\n // The public `AutoMigrateClientOptions<TContract>` is generic over the\n // user's narrow IDB contract; the internal `autoMigrate` only consumes\n // chain-walking fields, so widen to `ContractSpace<Contract>` here.\n contractSpace: options.contractSpace as unknown as ContractSpace<Contract>,\n dbName: options.dbName,\n policy,\n factory,\n ...(options.extensions !== undefined ? { extensions: options.extensions } : {}),\n });\n\n return createIdbClient({\n contract: options.contractSpace.contractJson,\n dbName: options.dbName,\n factory,\n });\n}\n\nfunction mergePolicy(p?: MigrationPolicy): Required<MigrationPolicy> {\n return {\n allowedOperationClasses: p?.allowedOperationClasses ?? SAFE_POLICY.allowedOperationClasses,\n onDestructive: p?.onDestructive ?? SAFE_POLICY.onDestructive,\n };\n}\n\n// ── Core migration loop ──────────────────────────────────────────────────────\n\n/**\n * The migration loop. Exported for tests.\n *\n * Collects pending ops from the app space and every extension space, then\n * applies all of them in a single combined `upgradeneeded` transaction\n * (one IDB version bump total) followed by one batched marker-write\n * transaction covering every space that migrated. See ADR 010 for details.\n *\n * @internal Prefer {@link createAutoMigratingIdbClient}.\n */\nexport async function autoMigrate(input: {\n // `ContractSpace<Contract>` instead of `<unknown>` so the generic\n // constraint `TContract extends Contract` from the framework is satisfied.\n // The internal apply path only reads `headRef.hash` and `migrations`, so\n // the precise contract shape inside `contractJson` doesn't matter here.\n readonly contractSpace: ContractSpace<Contract>;\n readonly dbName: string;\n readonly policy: Required<MigrationPolicy>;\n readonly factory: IDBFactory;\n readonly extensions?: ReadonlyArray<IdbExtensionSpace>;\n}): Promise<void> {\n const { dbName, policy, factory } = input;\n\n // Order here only affects the destructive-op collection loop below, not the\n // final apply order (see the combined-apply step, which re-sorts to match\n // upstream ADR 212's extension-first convention).\n const spaces: Array<{ spaceId: string; contractSpace: ContractSpace<Contract> }> = [\n { spaceId: APP_SPACE_ID, contractSpace: input.contractSpace },\n ...(input.extensions ?? []),\n ];\n\n // Reject reserved/duplicate extension space IDs before touching the\n // database. Without this, two extensions sharing a spaceId would combine\n // their pending DDL and the upgrade would try to create the same stores\n // twice; an extension using APP_SPACE_ID would have its marker write\n // silently overwrite (or be overwritten by) the app space's marker.\n const seenSpaceIds = new Set<string>();\n for (const extension of input.extensions ?? []) {\n if (extension.spaceId === APP_SPACE_ID || seenSpaceIds.has(extension.spaceId)) {\n throw new Error(`Invalid duplicate or reserved extension space ID: \"${extension.spaceId}\"`);\n }\n seenSpaceIds.add(extension.spaceId);\n }\n\n // Descriptor self-consistency (upstream ADR 212): a space's pinned headRef\n // must match the hash actually embedded in its contractJson. Catches an\n // extension author who edited the contract source without regenerating\n // migrations/refs/head.json — fails loudly here instead of surfacing as a\n // confusing chain-walk error deep inside walkChain.\n for (const space of spaces) {\n const declaredHash = space.contractSpace.contractJson.storage.storageHash;\n const headHash = space.contractSpace.headRef.hash;\n if (declaredHash !== headHash) {\n throw new Error(\n `Contract space \"${space.spaceId}\" is internally inconsistent: ` +\n `contractJson.storage.storageHash (${declaredHash}) does not match ` +\n `headRef.hash (${headHash}). The contract source likely changed without ` +\n \"regenerating migrations/refs/head.json for this space — rebuild its migration chain.\"\n );\n }\n }\n\n // Read current DB version once (all spaces share the same IDB database).\n const { currentVersion: initialVersion } = await openAndReadMarker(dbName, factory, APP_SPACE_ID);\n\n // Collect pending work per space without applying yet, so we can surface\n // all destructive violations before touching the database.\n const pendingPerSpace: Array<{ spaceId: string; ops: IdbDdlOp[]; storageHash: string }> = [];\n let totalDestructiveDropped = 0;\n\n for (const space of spaces) {\n const targetHash = space.contractSpace.headRef.hash;\n const { markerHash } = await openAndReadMarker(dbName, factory, space.spaceId);\n if (markerHash === targetHash) continue;\n\n const { pendingOps, destructiveDropped } = await walkChain({\n markerHash,\n headHash: targetHash,\n migrations: space.contractSpace.migrations,\n policy,\n });\n totalDestructiveDropped += destructiveDropped;\n // Push whenever the marker is behind `targetHash` — even if every package\n // walked had zero ops (a hash-only \"bridge\" migration, e.g. re-emitting\n // the contract under a new hashing algorithm with no structural change).\n // Gating on `pendingOps.length > 0` here would skip the marker write\n // below for that space, and since nothing ever changes on a later\n // retry either, the marker gets stuck at the old hash forever — the\n // space never converges to `targetHash`, even though there was never\n // any actual work to do.\n pendingPerSpace.push({ spaceId: space.spaceId, ops: pendingOps, storageHash: targetHash });\n }\n\n // Refuse if any space had destructive ops dropped under refuse policy.\n if (totalDestructiveDropped > 0 && policy.onDestructive === \"refuse\") {\n throw new Error(\n `Auto-migration refused: ${totalDestructiveDropped} destructive operation(s) ` +\n \"in the pending chain would drop user data. To allow them, pass \" +\n \"`policy: { onDestructive: 'allow' }` to createAutoMigratingIdbClient. \" +\n \"Per-tab persistent state (drafts, offline queue, cached content) will \" +\n \"be lost when destructive ops apply silently — review the change before opting in.\"\n );\n }\n\n if (pendingPerSpace.length === 0) return;\n\n // Combine every pending space into ONE upgradeneeded transaction (one IDB\n // version bump total) and ONE batched marker-write transaction (ADR 010).\n // This gives true cross-space atomicity — a failure partway through no\n // longer leaves some spaces migrated and others not — and cuts the number\n // of versionchange/blocked cycles a multi-tab user hits on cold start from\n // N (one per space) to 1.\n //\n // DDL op order within the combined transaction is extensions\n // (alphabetical-by-spaceId) first, app-space last, matching upstream\n // ADR 212's convention. That ordering is safe here specifically because\n // marker writes happen in the separate phase-2 transaction below, which\n // only runs after every space's DDL — including the app space's\n // `_prisma_next_marker` creation — has already committed in phase 1.\n const orderedPending = [...pendingPerSpace].sort((a, b) => {\n if (a.spaceId === APP_SPACE_ID) return 1;\n if (b.spaceId === APP_SPACE_ID) return -1;\n return a.spaceId.localeCompare(b.spaceId);\n });\n\n await openAndUpgrade({\n factory,\n dbName,\n targetVersion: initialVersion + 1,\n ops: orderedPending.flatMap((space) => space.ops),\n markers: orderedPending.map((space) => ({ space: space.spaceId, storageHash: space.storageHash })),\n });\n}\n\ninterface WalkResult {\n readonly pendingOps: IdbDdlOp[];\n readonly destructiveDropped: number;\n}\n\n/**\n * Walk the migration chain from `markerHash` (or `null` for a fresh DB) to\n * `headHash`, collecting each pending package's ops in order. Applies the\n * policy filter on each op as it's added; returns the count of destructive\n * ops that were dropped so the caller can refuse if the policy demands.\n *\n * Throws on chain discontinuity (no package whose `from === cursor`) so\n * misconfigured `contractSpace` inputs fail loudly rather than silently\n * leaving the DB at an intermediate state.\n */\nasync function walkChain(input: {\n readonly markerHash: string | null;\n readonly headHash: string;\n readonly migrations: readonly MigrationPackage[];\n readonly policy: Required<MigrationPolicy>;\n}): Promise<WalkResult> {\n const byFrom = new Map<string | null, MigrationPackage>();\n for (const pkg of input.migrations) {\n byFrom.set(pkg.metadata.from, pkg);\n }\n\n const allowed = new Set(input.policy.allowedOperationClasses);\n const pendingOps: IdbDdlOp[] = [];\n let destructiveDropped = 0;\n let cursor: string | null = input.markerHash;\n const visited = new Set<string | null>();\n\n while (cursor !== input.headHash) {\n if (visited.has(cursor)) {\n throw new Error(\n `Auto-migration chain contains a cycle at hash ${JSON.stringify(cursor)}. ` +\n \"Re-run `prisma-idb migration contract-space` to rebuild a valid chain.\"\n );\n }\n visited.add(cursor);\n const next = byFrom.get(cursor);\n if (!next) {\n throw new Error(\n `Auto-migration chain broken: no migration package with from === ${JSON.stringify(cursor)}. ` +\n \"Verify that contract-space.generated.ts is up to date by re-running \" +\n \"`prisma-idb migration contract-space`.\"\n );\n }\n const computedHash = await computeMigrationHash(next.metadata, next.ops);\n if (computedHash !== next.metadata.migrationHash) {\n throw new Error(\n `Migration package \"${next.dirName}\" failed integrity check: ` +\n `stored migrationHash ${next.metadata.migrationHash} does not match ` +\n `computed hash ${computedHash}. ` +\n \"The ops may have been edited after the package was generated. \" +\n \"Re-run `prisma-idb migration plan` to regenerate the package.\"\n );\n }\n for (const op of next.ops) {\n if (!isIdbDdlOp(op)) {\n throw new Error(`Non-IDB operation found in migration package ${next.dirName}: ${JSON.stringify(op)}`);\n }\n if (allowed.has(op.operationClass)) {\n pendingOps.push(op);\n } else if (op.operationClass === \"destructive\") {\n if (input.policy.onDestructive === \"allow\") {\n pendingOps.push(op);\n } else {\n destructiveDropped += 1;\n }\n }\n // Other classes filtered silently.\n }\n cursor = next.metadata.to;\n }\n\n return { pendingOps, destructiveDropped };\n}\n\n/**\n * Open the database at its current local version (no version arg), read the\n * marker for `spaceId`, then close the connection. Returns the current integer\n * version so the caller can compute `currentVersion + 1` for the upgrade re-open.\n */\nfunction openAndReadMarker(\n dbName: string,\n factory: IDBFactory,\n spaceId: string\n): Promise<{ currentVersion: number; markerHash: string | null }> {\n return new Promise((resolve, reject) => {\n let req: IDBOpenDBRequest;\n try {\n req = factory.open(dbName);\n } catch (err) {\n reject(err);\n return;\n }\n\n req.onsuccess = () => {\n const db = req.result;\n const currentVersion = db.version;\n void (async () => {\n try {\n const record = await readMarker(db, spaceId);\n resolve({ currentVersion, markerHash: record?.storageHash ?? null });\n } catch (err) {\n reject(err);\n } finally {\n db.close();\n }\n })();\n };\n req.onerror = () => {\n reject(req.error ?? new Error(`IDB open failed while reading marker for \"${dbName}\"`));\n };\n });\n}\n","/**\n * `createManagedAutoIdbClient` — `createManagedIdbClient` pre-wired to\n * `createAutoMigratingIdbClient`. Composing those two by hand means writing\n * `dbName` twice, into two independent option bags with nothing tying them\n * together — `createManagedIdbClient`'s `dbName` is used only by `reset()`'s\n * `deleteDatabase` call, so if it ever drifts from the factory's own\n * `dbName` (typo, copy-paste into a second app), `reset()` silently deletes\n * the wrong database (or nothing) while the real one keeps its data. This\n * reads `dbName` (and a `factory` override, if set) once and threads it to\n * both layers, so they can't diverge.\n */\n\nimport { createAutoMigratingIdbClient } from \"./auto-migrate\";\nimport type { AutoMigrateClientOptions } from \"./auto-migrate\";\nimport type { IdbClient } from \"./idb-client\";\nimport { createManagedIdbClient } from \"./managed-client\";\nimport type { ManagedIdbClient } from \"./managed-client\";\nimport type { IdbContract } from \"./types\";\n\n/**\n * @example\n * ```ts\n * import { createManagedAutoIdbClient } from '@prisma-idb/client-idb/client-auto';\n *\n * const db = createManagedAutoIdbClient({ contractSpace, dbName: 'my-app' });\n *\n * const client = await db.get();\n * // ... on logout:\n * await db.reset();\n * ```\n */\nexport function createManagedAutoIdbClient<TContract extends IdbContract>(\n options: AutoMigrateClientOptions<TContract>\n): ManagedIdbClient<IdbClient<TContract>> {\n return createManagedIdbClient(() => createAutoMigratingIdbClient(options), {\n dbName: options.dbName,\n ...(options.factory !== undefined ? { idbFactory: options.factory } : {}),\n });\n}\n"],"mappings":";;;;;;AAIA,SAAS,MAAM,QAA6B;CAC1C,OAAO,MAAM,KAAK,IAAI,WAAW,MAAM,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE;AACjG;;AAGA,eAAe,UAAU,OAAgC;CAEvD,OAAO,MAAM,MADQ,OAAO,OAAO,OAAO,WAAW,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,CAAC,CACjE;AACrB;;;;;;;;;;;;;;;;;;;;AAqBA,eAAsB,qBACpB,UACA,KACiB;CAGjB,MAAM,WAAoC,EAAE,GAAG,SAAS;CACxD,OAAO,SAAS;CAChB,MAAM,QAAQ,MAAM,QAAQ,IAAI,CAAC,UAAU,iBAAiB,QAAQ,CAAC,GAAG,UAAU,iBAAiB,GAAG,CAAC,CAAC,CAAC;CACzG,OAAO,UAAU,iBAAiB,KAAK,CAAC;AAC1C;;;ACDA,MAAM,cAAyC;CAC7C,yBAAyB,CAAC,YAAY,UAAU;CAChD,eAAe;AACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyEA,eAAsB,6BACpB,SAC+B;CAC/B,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,SAAS,YAAY,QAAQ,MAAM;CAEzC,MAAM,YAAY;EAIhB,eAAe,QAAQ;EACvB,QAAQ,QAAQ;EAChB;EACA;EACA,GAAI,QAAQ,eAAe,KAAA,IAAY,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;CAC/E,CAAC;CAED,OAAO,gBAAgB;EACrB,UAAU,QAAQ,cAAc;EAChC,QAAQ,QAAQ;EAChB;CACF,CAAC;AACH;AAEA,SAAS,YAAY,GAAgD;CACnE,OAAO;EACL,yBAAyB,GAAG,2BAA2B,YAAY;EACnE,eAAe,GAAG,iBAAiB,YAAY;CACjD;AACF;;;;;;;;;;;AAcA,eAAsB,YAAY,OAUhB;CAChB,MAAM,EAAE,QAAQ,QAAQ,YAAY;CAKpC,MAAM,SAA6E,CACjF;EAAE,SAAS;EAAc,eAAe,MAAM;CAAc,GAC5D,GAAI,MAAM,cAAc,CAAC,CAC3B;CAOA,MAAM,+BAAe,IAAI,IAAY;CACrC,KAAK,MAAM,aAAa,MAAM,cAAc,CAAC,GAAG;EAC9C,IAAI,UAAU,YAAY,gBAAgB,aAAa,IAAI,UAAU,OAAO,GAC1E,MAAM,IAAI,MAAM,sDAAsD,UAAU,QAAQ,EAAE;EAE5F,aAAa,IAAI,UAAU,OAAO;CACpC;CAOA,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,eAAe,MAAM,cAAc,aAAa,QAAQ;EAC9D,MAAM,WAAW,MAAM,cAAc,QAAQ;EAC7C,IAAI,iBAAiB,UACnB,MAAM,IAAI,MACR,mBAAmB,MAAM,QAAQ,kEACM,aAAa,iCACjC,SAAS,mIAE9B;CAEJ;CAGA,MAAM,EAAE,gBAAgB,mBAAmB,MAAM,kBAAkB,QAAQ,SAAS,YAAY;CAIhG,MAAM,kBAAoF,CAAC;CAC3F,IAAI,0BAA0B;CAE9B,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,aAAa,MAAM,cAAc,QAAQ;EAC/C,MAAM,EAAE,eAAe,MAAM,kBAAkB,QAAQ,SAAS,MAAM,OAAO;EAC7E,IAAI,eAAe,YAAY;EAE/B,MAAM,EAAE,YAAY,uBAAuB,MAAM,UAAU;GACzD;GACA,UAAU;GACV,YAAY,MAAM,cAAc;GAChC;EACF,CAAC;EACD,2BAA2B;EAS3B,gBAAgB,KAAK;GAAE,SAAS,MAAM;GAAS,KAAK;GAAY,aAAa;EAAW,CAAC;CAC3F;CAGA,IAAI,0BAA0B,KAAK,OAAO,kBAAkB,UAC1D,MAAM,IAAI,MACR,2BAA2B,wBAAwB,yTAKrD;CAGF,IAAI,gBAAgB,WAAW,GAAG;CAelC,MAAM,iBAAiB,CAAC,GAAG,eAAe,CAAC,CAAC,MAAM,GAAG,MAAM;EACzD,IAAI,EAAE,YAAY,cAAc,OAAO;EACvC,IAAI,EAAE,YAAY,cAAc,OAAO;EACvC,OAAO,EAAE,QAAQ,cAAc,EAAE,OAAO;CAC1C,CAAC;CAED,MAAM,eAAe;EACnB;EACA;EACA,eAAe,iBAAiB;EAChC,KAAK,eAAe,SAAS,UAAU,MAAM,GAAG;EAChD,SAAS,eAAe,KAAK,WAAW;GAAE,OAAO,MAAM;GAAS,aAAa,MAAM;EAAY,EAAE;CACnG,CAAC;AACH;;;;;;;;;;;AAiBA,eAAe,UAAU,OAKD;CACtB,MAAM,yBAAS,IAAI,IAAqC;CACxD,KAAK,MAAM,OAAO,MAAM,YACtB,OAAO,IAAI,IAAI,SAAS,MAAM,GAAG;CAGnC,MAAM,UAAU,IAAI,IAAI,MAAM,OAAO,uBAAuB;CAC5D,MAAM,aAAyB,CAAC;CAChC,IAAI,qBAAqB;CACzB,IAAI,SAAwB,MAAM;CAClC,MAAM,0BAAU,IAAI,IAAmB;CAEvC,OAAO,WAAW,MAAM,UAAU;EAChC,IAAI,QAAQ,IAAI,MAAM,GACpB,MAAM,IAAI,MACR,iDAAiD,KAAK,UAAU,MAAM,EAAE,2EAE1E;EAEF,QAAQ,IAAI,MAAM;EAClB,MAAM,OAAO,OAAO,IAAI,MAAM;EAC9B,IAAI,CAAC,MACH,MAAM,IAAI,MACR,mEAAmE,KAAK,UAAU,MAAM,EAAE,+GAG5F;EAEF,MAAM,eAAe,MAAM,qBAAqB,KAAK,UAAU,KAAK,GAAG;EACvE,IAAI,iBAAiB,KAAK,SAAS,eACjC,MAAM,IAAI,MACR,sBAAsB,KAAK,QAAQ,iDACT,KAAK,SAAS,cAAc,gCACnC,aAAa,gIAGlC;EAEF,KAAK,MAAM,MAAM,KAAK,KAAK;GACzB,IAAI,CAAC,WAAW,EAAE,GAChB,MAAM,IAAI,MAAM,gDAAgD,KAAK,QAAQ,IAAI,KAAK,UAAU,EAAE,GAAG;GAEvG,IAAI,QAAQ,IAAI,GAAG,cAAc,GAC/B,WAAW,KAAK,EAAE;QACb,IAAI,GAAG,mBAAmB,eAC/B,IAAI,MAAM,OAAO,kBAAkB,SACjC,WAAW,KAAK,EAAE;QAElB,sBAAsB;EAI5B;EACA,SAAS,KAAK,SAAS;CACzB;CAEA,OAAO;EAAE;EAAY;CAAmB;AAC1C;;;;;;AAOA,SAAS,kBACP,QACA,SACA,SACgE;CAChE,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,IAAI;EACJ,IAAI;GACF,MAAM,QAAQ,KAAK,MAAM;EAC3B,SAAS,KAAK;GACZ,OAAO,GAAG;GACV;EACF;EAEA,IAAI,kBAAkB;GACpB,MAAM,KAAK,IAAI;GACf,MAAM,iBAAiB,GAAG;GAC1B,CAAM,YAAY;IAChB,IAAI;KACF,MAAM,SAAS,MAAM,WAAW,IAAI,OAAO;KAC3C,QAAQ;MAAE;MAAgB,YAAY,QAAQ,eAAe;KAAK,CAAC;IACrE,SAAS,KAAK;KACZ,OAAO,GAAG;IACZ,UAAU;KACR,GAAG,MAAM;IACX;GACF,EAAA,CAAG;EACL;EACA,IAAI,gBAAgB;GAClB,OAAO,IAAI,yBAAS,IAAI,MAAM,6CAA6C,OAAO,EAAE,CAAC;EACvF;CACF,CAAC;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;AClXA,SAAgB,2BACd,SACwC;CACxC,OAAO,6BAA6B,6BAA6B,OAAO,GAAG;EACzE,QAAQ,QAAQ;EAChB,GAAI,QAAQ,YAAY,KAAA,IAAY,EAAE,YAAY,QAAQ,QAAQ,IAAI,CAAC;CACzE,CAAC;AACH"}
@@ -0,0 +1,2 @@
1
+ import { a as IdbClientOptions, i as IdbClient, n as ManagedIdbClientOptions, o as createIdbClient, r as createManagedIdbClient, t as ManagedIdbClient } from "./managed-client-kdyHm72t.mjs";
2
+ export { type IdbClient, type IdbClientOptions, type ManagedIdbClient, type ManagedIdbClientOptions, createIdbClient, createManagedIdbClient };
@@ -0,0 +1,2 @@
1
+ import { n as createIdbClient, t as createManagedIdbClient } from "./managed-client-B_8JZM3h.mjs";
2
+ export { createIdbClient, createManagedIdbClient };