@prisma-idb/client-idb 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client-auto.mjs +20 -15
- package/dist/client-auto.mjs.map +1 -1
- package/dist/client.mjs +1 -1
- package/dist/idb-orm-MOH1S3ES.d.mts.map +1 -1
- package/dist/{idb-orm-C8yXJp6R.mjs → idb-orm-kDKFzR3S.mjs} +57 -134
- package/dist/idb-orm-kDKFzR3S.mjs.map +1 -0
- package/dist/{managed-client-DofL09P8.mjs → managed-client-CA1_fthM.mjs} +2 -2
- package/dist/{managed-client-DofL09P8.mjs.map → managed-client-CA1_fthM.mjs.map} +1 -1
- package/dist/orm.d.mts.map +1 -1
- package/dist/orm.mjs +1 -1
- package/package.json +6 -6
- package/dist/idb-orm-C8yXJp6R.mjs.map +0 -1
package/dist/client-auto.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as createIdbClient, t as createManagedIdbClient } from "./managed-client-
|
|
1
|
+
import { n as createIdbClient, t as createManagedIdbClient } from "./managed-client-CA1_fthM.mjs";
|
|
2
2
|
import { isIdbDdlOp, openAndUpgrade, readMarker } from "@prisma-idb/target-idb/runtime";
|
|
3
3
|
import { APP_SPACE_ID } from "@prisma/orm-framework/components/control";
|
|
4
4
|
import { canonicalizeJson } from "@prisma/orm-framework/components/utils";
|
|
@@ -106,20 +106,7 @@ async function createAutoMigratingIdbClient(options) {
|
|
|
106
106
|
*/
|
|
107
107
|
async function autoMigrate(input) {
|
|
108
108
|
const { dbName, factory } = input;
|
|
109
|
-
const spaces = [
|
|
110
|
-
spaceId: APP_SPACE_ID,
|
|
111
|
-
contractSpace: input.contractSpace
|
|
112
|
-
}, ...input.extensions ?? []];
|
|
113
|
-
const seenSpaceIds = /* @__PURE__ */ new Set();
|
|
114
|
-
for (const extension of input.extensions ?? []) {
|
|
115
|
-
if (extension.spaceId === APP_SPACE_ID || seenSpaceIds.has(extension.spaceId)) throw new Error(`Invalid duplicate or reserved extension space ID: "${extension.spaceId}"`);
|
|
116
|
-
seenSpaceIds.add(extension.spaceId);
|
|
117
|
-
}
|
|
118
|
-
for (const space of spaces) {
|
|
119
|
-
const declaredHash = space.contractSpace.contractJson.storage.storageHash;
|
|
120
|
-
const headHash = space.contractSpace.headRef.hash;
|
|
121
|
-
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.`);
|
|
122
|
-
}
|
|
109
|
+
const spaces = resolveContractSpaces(input.contractSpace, input.extensions ?? []);
|
|
123
110
|
const { currentVersion: initialVersion } = await openAndReadMarker(dbName, factory, APP_SPACE_ID);
|
|
124
111
|
const pendingPerSpace = [];
|
|
125
112
|
for (const space of spaces) {
|
|
@@ -154,6 +141,24 @@ async function autoMigrate(input) {
|
|
|
154
141
|
}))
|
|
155
142
|
});
|
|
156
143
|
}
|
|
144
|
+
/** Validates bundled descriptors before the migration loop opens the database. */
|
|
145
|
+
function resolveContractSpaces(contractSpace, extensions) {
|
|
146
|
+
const spaces = [{
|
|
147
|
+
spaceId: APP_SPACE_ID,
|
|
148
|
+
contractSpace
|
|
149
|
+
}, ...extensions];
|
|
150
|
+
const seenSpaceIds = /* @__PURE__ */ new Set();
|
|
151
|
+
for (const extension of extensions) {
|
|
152
|
+
if (extension.spaceId === APP_SPACE_ID || seenSpaceIds.has(extension.spaceId)) throw new Error(`Invalid duplicate or reserved extension space ID: "${extension.spaceId}"`);
|
|
153
|
+
seenSpaceIds.add(extension.spaceId);
|
|
154
|
+
}
|
|
155
|
+
for (const space of spaces) {
|
|
156
|
+
const declaredHash = space.contractSpace.contractJson.storage.storageHash;
|
|
157
|
+
const headHash = space.contractSpace.headRef.hash;
|
|
158
|
+
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.`);
|
|
159
|
+
}
|
|
160
|
+
return spaces;
|
|
161
|
+
}
|
|
157
162
|
/**
|
|
158
163
|
* Walk the migration chain from `markerHash` (or `null` for a fresh DB) to
|
|
159
164
|
* `headHash`, collecting every pending package's ops in order.
|
package/dist/client-auto.mjs.map
CHANGED
|
@@ -1 +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 { ContractSpace, MigrationPackage } 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 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 /** 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 `upgradeneeded` transaction,\n * which also writes every space's marker. Each space still gets its own\n * 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. If `extensions` are configured, repeat steps 1–3 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, which\n * also writes every migrated space's marker.\n * 5. Hand back the typed `IdbClient`.\n *\n * Every pending op is applied exactly as planned, destructive ones included.\n * The developer reviewed them when running `prisma-idb migration plan`,\n * which warns about any that delete data. Skipping some ops would leave the\n * database claiming a schema it doesn't have, and refusing them would stop\n * the app from opening for every user.\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\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 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\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 `upgradeneeded` transaction (one IDB\n * version bump total). The same transaction writes the marker of every\n * space that migrated, so schema changes and markers commit together.\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 factory: IDBFactory;\n readonly extensions?: ReadonlyArray<IdbExtensionSpace>;\n}): Promise<void> {\n const { dbName, factory } = input;\n\n // Order here only affects the collection loop below, not the final apply\n // order (see the combined-apply step, which re-sorts to match upstream\n // 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 a broken chain in\n // any space fails before the database is touched.\n const pendingPerSpace: Array<{ spaceId: string; ops: IdbDdlOp[]; storageHash: string }> = [];\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 = await walkChain({\n markerHash,\n headHash: targetHash,\n migrations: space.contractSpace.migrations,\n });\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 if (pendingPerSpace.length === 0) return;\n\n // Combine every pending space into ONE upgradeneeded transaction (one IDB\n // version bump total), which also writes every space's marker. A failure\n // partway through rolls back all spaces' schema changes and markers, and a\n // multi-tab user hits one versionchange/blocked cycle on cold start instead\n // of one per space.\n //\n // DDL op order is extensions (alphabetical by spaceId) first, app space\n // last, matching upstream ADR 212's convention. That's safe even though the\n // app space creates `_prisma_next_marker`: openAndUpgrade writes the\n // markers only after every op has run.\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\n/**\n * Walk the migration chain from `markerHash` (or `null` for a fresh DB) to\n * `headHash`, collecting every pending package's ops in order.\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}): Promise<IdbDdlOp[]> {\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 pendingOps: IdbDdlOp[] = [];\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 pendingOps.push(op);\n }\n cursor = next.metadata.to;\n }\n\n return pendingOps;\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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC2CA,eAAsB,6BACpB,SAC+B;CAC/B,MAAM,UAAU,QAAQ,WAAW;CAEnC,MAAM,YAAY;EAIhB,eAAe,QAAQ;EACvB,QAAQ,QAAQ;EAChB;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;;;;;;;;;;;AAcA,eAAsB,YAAY,OAShB;CAChB,MAAM,EAAE,QAAQ,YAAY;CAK5B,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;CAE3F,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,aAAa,MAAM,UAAU;GACjC;GACA,UAAU;GACV,YAAY,MAAM,cAAc;EAClC,CAAC;EASD,gBAAgB,KAAK;GAAE,SAAS,MAAM;GAAS,KAAK;GAAY,aAAa;EAAW,CAAC;CAC3F;CAEA,IAAI,gBAAgB,WAAW,GAAG;CAYlC,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;;;;;;;;;AAUA,eAAe,UAAU,OAID;CACtB,MAAM,yBAAS,IAAI,IAAqC;CACxD,KAAK,MAAM,OAAO,MAAM,YACtB,OAAO,IAAI,IAAI,SAAS,MAAM,GAAG;CAGnC,MAAM,aAAyB,CAAC;CAChC,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,WAAW,KAAK,EAAE;EACpB;EACA,SAAS,KAAK,SAAS;CACzB;CAEA,OAAO;AACT;;;;;;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;;;;;;;;;;;;;;;;;;;;;;;;;;ACpSA,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"}
|
|
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 { ContractSpace, MigrationPackage } 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 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 /** 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 `upgradeneeded` transaction,\n * which also writes every space's marker. Each space still gets its own\n * 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. If `extensions` are configured, repeat steps 1–3 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, which\n * also writes every migrated space's marker.\n * 5. Hand back the typed `IdbClient`.\n *\n * Every pending op is applied exactly as planned, destructive ones included.\n * The developer reviewed them when running `prisma-idb migration plan`,\n * which warns about any that delete data. Skipping some ops would leave the\n * database claiming a schema it doesn't have, and refusing them would stop\n * the app from opening for every user.\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\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 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\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 `upgradeneeded` transaction (one IDB\n * version bump total). The same transaction writes the marker of every\n * space that migrated, so schema changes and markers commit together.\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 factory: IDBFactory;\n readonly extensions?: ReadonlyArray<IdbExtensionSpace>;\n}): Promise<void> {\n const { dbName, factory } = input;\n\n const spaces = resolveContractSpaces(input.contractSpace, input.extensions ?? []);\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 a broken chain in\n // any space fails before the database is touched.\n const pendingPerSpace: Array<{ spaceId: string; ops: IdbDdlOp[]; storageHash: string }> = [];\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 = await walkChain({\n markerHash,\n headHash: targetHash,\n migrations: space.contractSpace.migrations,\n });\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 if (pendingPerSpace.length === 0) return;\n\n // Combine every pending space into ONE upgradeneeded transaction (one IDB\n // version bump total), which also writes every space's marker. A failure\n // partway through rolls back all spaces' schema changes and markers, and a\n // multi-tab user hits one versionchange/blocked cycle on cold start instead\n // of one per space.\n //\n // DDL op order is extensions (alphabetical by spaceId) first, app space\n // last, matching upstream ADR 212's convention. That's safe even though the\n // app space creates `_prisma_next_marker`: openAndUpgrade writes the\n // markers only after every op has run.\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\n/** Validates bundled descriptors before the migration loop opens the database. */\nfunction resolveContractSpaces(\n contractSpace: ContractSpace<Contract>,\n extensions: ReadonlyArray<IdbExtensionSpace>\n): Array<{ spaceId: string; contractSpace: ContractSpace<Contract> }> {\n // Validate and collect app-first. The combined upgrade applies extensions\n // first, matching upstream ADR 212.\n const spaces: Array<{ spaceId: string; contractSpace: ContractSpace<Contract> }> = [\n { spaceId: APP_SPACE_ID, contractSpace },\n ...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 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 return spaces;\n}\n\n/**\n * Walk the migration chain from `markerHash` (or `null` for a fresh DB) to\n * `headHash`, collecting every pending package's ops in order.\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}): Promise<IdbDdlOp[]> {\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 pendingOps: IdbDdlOp[] = [];\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 pendingOps.push(op);\n }\n cursor = next.metadata.to;\n }\n\n return pendingOps;\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":"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"}
|
package/dist/client.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as createIdbClient, t as createManagedIdbClient } from "./managed-client-
|
|
1
|
+
import { n as createIdbClient, t as createManagedIdbClient } from "./managed-client-CA1_fthM.mjs";
|
|
2
2
|
export { createIdbClient, createManagedIdbClient };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idb-orm-MOH1S3ES.d.mts","names":[],"sources":["../src/core/types.ts","../src/core/executor.ts","../src/core/model-accessor.ts","../src/core/store-state.ts","../src/core/grouped-accessor.ts","../src/core/store-accessor.ts","../src/core/idb-orm.ts"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"idb-orm-MOH1S3ES.d.mts","names":[],"sources":["../src/core/types.ts","../src/core/executor.ts","../src/core/model-accessor.ts","../src/core/store-state.ts","../src/core/grouped-accessor.ts","../src/core/store-accessor.ts","../src/core/idb-orm.ts"],"mappings":"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"}
|
|
@@ -41,9 +41,9 @@ function extractKeyFromRow(row, keyPath) {
|
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
43
|
* Build a field → indexName lookup for every single-field, non-multi-entry
|
|
44
|
-
* index on `storeName`.
|
|
45
|
-
*
|
|
46
|
-
*
|
|
44
|
+
* index on `storeName`. Top-level scans use this map to find indexed equality
|
|
45
|
+
* conditions in combined filter expressions. Compound and multi-entry indexes
|
|
46
|
+
* cannot serve these single-field point lookups.
|
|
47
47
|
*/
|
|
48
48
|
function buildFieldToIndexMap(contract, storeName) {
|
|
49
49
|
const storeDef = contract.storage.stores[storeName];
|
|
@@ -173,22 +173,6 @@ function flattenAnd(filter) {
|
|
|
173
173
|
if (filter.kind !== "and") return [filter];
|
|
174
174
|
return filter.exprs.flatMap(flattenAnd);
|
|
175
175
|
}
|
|
176
|
-
/** Fold the leftover AND children (after peeling an indexed condition) back into a single filter, or `undefined` if none remain. */
|
|
177
|
-
function foldRemainder(rest) {
|
|
178
|
-
if (rest.length === 0) return void 0;
|
|
179
|
-
if (rest.length === 1) return rest[0];
|
|
180
|
-
return andExpr(rest);
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* `IDBKeyRange.only()` throws `DataError` for values that aren't valid
|
|
184
|
-
* IndexedDB keys (`null`/`undefined`, booleans, `NaN`, `BigInt`, plain
|
|
185
|
-
* objects, or arrays containing any of those) — an `eq` condition on such a
|
|
186
|
-
* value can't be served by an index point-range scan and must fall back to
|
|
187
|
-
* a full scan. Delegates to the shared {@link isValidIdbKey} check.
|
|
188
|
-
*/
|
|
189
|
-
function isIndexableEqValue(value) {
|
|
190
|
-
return isValidIdbKey(value);
|
|
191
|
-
}
|
|
192
176
|
/**
|
|
193
177
|
* `true` when `field` is a *single-field* primary key that a lone `eq`
|
|
194
178
|
* condition can directly point-range-query (`store.get`/`openCursor(range)`
|
|
@@ -223,7 +207,7 @@ function extractIndexEqualityHint(filter, fieldToIndexName, keyPath) {
|
|
|
223
207
|
if (filter === void 0) return null;
|
|
224
208
|
if (filter.kind === "field" && filter.op === "eq") {
|
|
225
209
|
const indexName = fieldToIndexName[filter.field];
|
|
226
|
-
if ((indexName !== void 0 || isPrimaryKeyField(filter.field, keyPath)) &&
|
|
210
|
+
if ((indexName !== void 0 || isPrimaryKeyField(filter.field, keyPath)) && isValidIdbKey(filter.value)) return {
|
|
227
211
|
indexName,
|
|
228
212
|
value: filter.value,
|
|
229
213
|
remainingFilter: void 0
|
|
@@ -236,12 +220,12 @@ function extractIndexEqualityHint(filter, fieldToIndexName, keyPath) {
|
|
|
236
220
|
const expr = flat[i];
|
|
237
221
|
if (expr.kind === "field" && expr.op === "eq") {
|
|
238
222
|
const indexName = fieldToIndexName[expr.field];
|
|
239
|
-
if ((indexName !== void 0 || isPrimaryKeyField(expr.field, keyPath)) &&
|
|
223
|
+
if ((indexName !== void 0 || isPrimaryKeyField(expr.field, keyPath)) && isValidIdbKey(expr.value)) {
|
|
240
224
|
const rest = flat.filter((_, j) => j !== i);
|
|
241
225
|
return {
|
|
242
226
|
indexName,
|
|
243
227
|
value: expr.value,
|
|
244
|
-
remainingFilter:
|
|
228
|
+
remainingFilter: combineFilterExprs(rest)
|
|
245
229
|
};
|
|
246
230
|
}
|
|
247
231
|
}
|
|
@@ -261,7 +245,7 @@ function tryExtractOrBranches(orNode, fieldToIndexName, keyPath, remainingFilter
|
|
|
261
245
|
for (const expr of orNode.exprs) {
|
|
262
246
|
if (expr.kind !== "field" || expr.op !== "eq") return null;
|
|
263
247
|
const indexName = fieldToIndexName[expr.field];
|
|
264
|
-
if (indexName === void 0 && !isPrimaryKeyField(expr.field, keyPath) || !
|
|
248
|
+
if (indexName === void 0 && !isPrimaryKeyField(expr.field, keyPath) || !isValidIdbKey(expr.value)) return null;
|
|
265
249
|
branches.push({
|
|
266
250
|
indexName,
|
|
267
251
|
value: expr.value
|
|
@@ -293,7 +277,7 @@ function extractIndexOrHint(filter, fieldToIndexName, keyPath) {
|
|
|
293
277
|
if (orIndices.length !== 1) return null;
|
|
294
278
|
const orIdx = orIndices[0];
|
|
295
279
|
const orNode = flat[orIdx];
|
|
296
|
-
return tryExtractOrBranches(orNode, fieldToIndexName, keyPath,
|
|
280
|
+
return tryExtractOrBranches(orNode, fieldToIndexName, keyPath, combineFilterExprs(flat.filter((_, j) => j !== orIdx)));
|
|
297
281
|
}
|
|
298
282
|
return null;
|
|
299
283
|
}
|
|
@@ -360,7 +344,7 @@ function clampCount(total, skip, take) {
|
|
|
360
344
|
* Build the aggregate selector factory handed to `.aggregate(agg => …)` and
|
|
361
345
|
* `groupBy(...).aggregate(agg => …)`.
|
|
362
346
|
*
|
|
363
|
-
* Each method returns
|
|
347
|
+
* Each method returns an {@link IdbAggregateSelector} marker; the actual
|
|
364
348
|
* reduction happens in {@link reduceAggregate} once the matching rows are
|
|
365
349
|
* materialised in memory. Mirrors `createAggregateBuilder` from the vendor
|
|
366
350
|
* `sql-orm-client/aggregate-builder.ts`, minus the field→column mapping (IDB
|
|
@@ -470,6 +454,7 @@ function assertValidAggregateSpec(spec, context) {
|
|
|
470
454
|
function groupKeyOf(by, row) {
|
|
471
455
|
return JSON.stringify(by.map((field) => row[field] ?? null));
|
|
472
456
|
}
|
|
457
|
+
/** Build a grouped accessor that reduces materialized rows from the source accessor. */
|
|
473
458
|
function createGroupedAccessor(init) {
|
|
474
459
|
return { async aggregate(fn) {
|
|
475
460
|
const spec = fn(createAggregateBuilder());
|
|
@@ -735,6 +720,11 @@ async function loadRelation(relName, entry, rows, contract, modelName, executor,
|
|
|
735
720
|
};
|
|
736
721
|
for await (const row of executor.query(plan)) relatedRows.push(row);
|
|
737
722
|
}
|
|
723
|
+
return attachRelatedRows(relName, entry, rows, relatedRows, relation);
|
|
724
|
+
}
|
|
725
|
+
/** Attach related rows by their join tuple, shaping each parent's collection independently. */
|
|
726
|
+
function attachRelatedRows(relName, entry, rows, relatedRows, relation) {
|
|
727
|
+
const { cardinality, on: { localFields, targetFields } } = relation;
|
|
738
728
|
if (cardinality === "1:N") {
|
|
739
729
|
const grouped = /* @__PURE__ */ new Map();
|
|
740
730
|
for (const rrow of relatedRows) {
|
|
@@ -748,7 +738,7 @@ async function loadRelation(relName, entry, rows, contract, modelName, executor,
|
|
|
748
738
|
const token = tupleToken(row, localFields);
|
|
749
739
|
return token === null ? [] : grouped.get(token) ?? [];
|
|
750
740
|
};
|
|
751
|
-
if (
|
|
741
|
+
if (entry.kind === "scalar") return rows.map((row) => ({
|
|
752
742
|
...row,
|
|
753
743
|
[relName]: groupFor(row).length
|
|
754
744
|
}));
|
|
@@ -980,7 +970,7 @@ async function createGraph(scope, contract, modelName, input, defaultsCache) {
|
|
|
980
970
|
const scalarData = { ...parsed.scalarData };
|
|
981
971
|
for (const item of parentOwned) {
|
|
982
972
|
if (item.mutation.kind === "disconnect") throw new Error("disconnect() is only supported in update() nested mutations");
|
|
983
|
-
await applyParentOwnedMutation(scope, contract,
|
|
973
|
+
await applyParentOwnedMutation(scope, contract, scalarData, item.relation, item.mutation, defaultsCache);
|
|
984
974
|
}
|
|
985
975
|
const parentRow = await insertSingleRow(scope, contract, modelName, scalarData, defaultsCache);
|
|
986
976
|
for (const item of childOwned) {
|
|
@@ -995,7 +985,7 @@ async function updateFirstGraph(scope, contract, modelName, filters, input, defa
|
|
|
995
985
|
const parsed = parseMutationInput(contract, modelName, input);
|
|
996
986
|
const { parentOwned, childOwned } = partitionByOwnership(parsed.relationMutations);
|
|
997
987
|
const scalarData = { ...parsed.scalarData };
|
|
998
|
-
for (const item of parentOwned) await applyParentOwnedMutation(scope, contract,
|
|
988
|
+
for (const item of parentOwned) await applyParentOwnedMutation(scope, contract, scalarData, item.relation, item.mutation, defaultsCache);
|
|
999
989
|
let parentRow = existingRow;
|
|
1000
990
|
if (Object.keys(scalarData).length > 0) {
|
|
1001
991
|
const storeName = getStoreName(contract, modelName);
|
|
@@ -1056,7 +1046,7 @@ function partitionByOwnership(mutations) {
|
|
|
1056
1046
|
childOwned
|
|
1057
1047
|
};
|
|
1058
1048
|
}
|
|
1059
|
-
async function applyParentOwnedMutation(scope, contract,
|
|
1049
|
+
async function applyParentOwnedMutation(scope, contract, scalarData, relation, mutation, defaultsCache) {
|
|
1060
1050
|
if (mutation.kind === "disconnect") {
|
|
1061
1051
|
for (const localField of relation.localFields) scalarData[localField] = null;
|
|
1062
1052
|
return;
|
|
@@ -1064,16 +1054,16 @@ async function applyParentOwnedMutation(scope, contract, parentModelName, scalar
|
|
|
1064
1054
|
if (mutation.kind === "create") {
|
|
1065
1055
|
const row = mutation.data[0];
|
|
1066
1056
|
if (!row) throw new Error(`create() nested mutation for relation "${relation.relationName}" requires data`);
|
|
1067
|
-
copyRelatedValuesToParent(relation, scalarData, await insertSingleRow(scope, contract, relation.relatedModelName, row, defaultsCache)
|
|
1057
|
+
copyRelatedValuesToParent(relation, scalarData, await insertSingleRow(scope, contract, relation.relatedModelName, row, defaultsCache));
|
|
1068
1058
|
return;
|
|
1069
1059
|
}
|
|
1070
1060
|
const criterion = mutation.criteria[0];
|
|
1071
1061
|
if (!criterion) throw new Error(`connect() nested mutation for relation "${relation.relationName}" requires a criterion`);
|
|
1072
1062
|
const relatedRow = await findRowByCriterion(scope, contract, relation.relatedModelName, criterion);
|
|
1073
1063
|
if (!relatedRow) throw new Error(`connect() nested mutation for relation "${relation.relationName}" did not find a matching row`);
|
|
1074
|
-
copyRelatedValuesToParent(relation, scalarData, relatedRow
|
|
1064
|
+
copyRelatedValuesToParent(relation, scalarData, relatedRow);
|
|
1075
1065
|
}
|
|
1076
|
-
function copyRelatedValuesToParent(relation, scalarData, relatedRow
|
|
1066
|
+
function copyRelatedValuesToParent(relation, scalarData, relatedRow) {
|
|
1077
1067
|
for (let i = 0; i < relation.localFields.length; i++) {
|
|
1078
1068
|
const localField = relation.localFields[i];
|
|
1079
1069
|
const targetField = relation.targetFields[i];
|
|
@@ -1262,13 +1252,7 @@ function findDeclaredAction(contract, modelName, def, kind) {
|
|
|
1262
1252
|
function enforcedAction(declared) {
|
|
1263
1253
|
return declared === void 0 || declared === "noAction" ? "restrict" : declared;
|
|
1264
1254
|
}
|
|
1265
|
-
function
|
|
1266
|
-
return getReferentialActionForRelation(contract, modelName, def, "onDelete");
|
|
1267
|
-
}
|
|
1268
|
-
function getOnUpdateForRelation(contract, modelName, def) {
|
|
1269
|
-
return getReferentialActionForRelation(contract, modelName, def, "onUpdate");
|
|
1270
|
-
}
|
|
1271
|
-
function isDeleteEnforcementRelation(contract, modelName, def) {
|
|
1255
|
+
function isChildEnforcementRelation(contract, modelName, def) {
|
|
1272
1256
|
if (def.cardinality === "1:N") return true;
|
|
1273
1257
|
if (def.cardinality === "1:1") {
|
|
1274
1258
|
const keyPath = getKeyPath(contract, modelName);
|
|
@@ -1418,17 +1402,17 @@ function collectOnUpdateEnforcementStoreNames(contract, modelName, data) {
|
|
|
1418
1402
|
if (visitedModels.has(mName)) return;
|
|
1419
1403
|
visitedModels.add(mName);
|
|
1420
1404
|
for (const def of getRelationDefinitions(contract, mName)) {
|
|
1421
|
-
if (!
|
|
1422
|
-
const action =
|
|
1405
|
+
if (!isChildEnforcementRelation(contract, mName, def)) continue;
|
|
1406
|
+
const action = getReferentialActionForRelation(contract, mName, def, "onUpdate");
|
|
1423
1407
|
stores.add(def.relatedStoreName);
|
|
1424
1408
|
if (action === "cascade") walkCascadeChain(def.relatedModelName);
|
|
1425
1409
|
}
|
|
1426
1410
|
}
|
|
1427
1411
|
visitedModels.add(modelName);
|
|
1428
1412
|
for (const def of getRelationDefinitions(contract, modelName)) {
|
|
1429
|
-
if (!
|
|
1413
|
+
if (!isChildEnforcementRelation(contract, modelName, def)) continue;
|
|
1430
1414
|
if (!def.localFields.some((f) => f in data)) continue;
|
|
1431
|
-
const action =
|
|
1415
|
+
const action = getReferentialActionForRelation(contract, modelName, def, "onUpdate");
|
|
1432
1416
|
enforces = true;
|
|
1433
1417
|
stores.add(def.relatedStoreName);
|
|
1434
1418
|
if (action === "cascade") walkCascadeChain(def.relatedModelName);
|
|
@@ -1455,11 +1439,8 @@ function collectOnUpdateEnforcementStoreNames(contract, modelName, data) {
|
|
|
1455
1439
|
* `setNull`/`setDefault` are leaf actions: the child's FK field changes but
|
|
1456
1440
|
* its own key doesn't, so nothing below it needs re-enforcement.
|
|
1457
1441
|
*
|
|
1458
|
-
*
|
|
1459
|
-
*
|
|
1460
|
-
* from the same `oldRow`/`patch`, so there's no risk of assembling a value
|
|
1461
|
-
* from two unrelated rows the way independently-validated FK-existence
|
|
1462
|
-
* checks could.
|
|
1442
|
+
* Compound relations use values from the same `oldRow`/`patch`, so every
|
|
1443
|
+
* child is matched against the full pre-change tuple.
|
|
1463
1444
|
*/
|
|
1464
1445
|
async function applyReferentialActionsForRowOnUpdate(scope, contract, modelName, oldRow, patch, visited = /* @__PURE__ */ new Set()) {
|
|
1465
1446
|
const keyPath = getKeyPath(contract, modelName);
|
|
@@ -1468,10 +1449,10 @@ async function applyReferentialActionsForRowOnUpdate(scope, contract, modelName,
|
|
|
1468
1449
|
visited.add(rowKey);
|
|
1469
1450
|
const meta = makePlanMeta(contract);
|
|
1470
1451
|
for (const def of getRelationDefinitions(contract, modelName)) {
|
|
1471
|
-
if (!
|
|
1452
|
+
if (!isChildEnforcementRelation(contract, modelName, def)) continue;
|
|
1472
1453
|
const changedFields = def.localFields.filter((f) => f in patch && !fieldValuesEqual(patch[f], oldRow[f]));
|
|
1473
1454
|
if (changedFields.length === 0) continue;
|
|
1474
|
-
const action =
|
|
1455
|
+
const action = getReferentialActionForRelation(contract, modelName, def, "onUpdate");
|
|
1475
1456
|
const childFilter = buildChildFilterFromRow(def, oldRow);
|
|
1476
1457
|
if (action === "restrict") {
|
|
1477
1458
|
if (await childExists(scope, contract, meta, def, oldRow)) throw new Error(`Cannot update ${modelName} '${keyToken(extractKeyFromRow(oldRow, keyPath))}': changing field(s) ${changedFields.join(", ")} would orphan child records on relation '${def.relationName}'. Update or remove those children first, or declare onUpdate: Cascade, SetNull or SetDefault on the relation.`);
|
|
@@ -1650,8 +1631,8 @@ async function executeScalarCreateAllWithFkValidation(options) {
|
|
|
1650
1631
|
* Combines FK-existence store scope (this model's own N:1 relations) with
|
|
1651
1632
|
* `onUpdate` enforcement store scope (this model's child relations) for a
|
|
1652
1633
|
* single write. Returns the union alongside whether `onUpdate` enforcement
|
|
1653
|
-
* actually applies
|
|
1654
|
-
*
|
|
1634
|
+
* actually applies, including self-relations that touch only this model's
|
|
1635
|
+
* own store. Callers use that flag to choose between the existing
|
|
1655
1636
|
* fast blind-write path and the read-before-write enforcement path.
|
|
1656
1637
|
*/
|
|
1657
1638
|
function collectUpdateStoreNames(contract, modelName, data) {
|
|
@@ -1662,68 +1643,25 @@ function collectUpdateStoreNames(contract, modelName, data) {
|
|
|
1662
1643
|
enforcesOnUpdate: enforces
|
|
1663
1644
|
};
|
|
1664
1645
|
}
|
|
1646
|
+
/** Updates the first matching row with FK validation and referential actions. */
|
|
1665
1647
|
async function executeScalarUpdateWithFkValidation(options) {
|
|
1666
|
-
|
|
1667
|
-
const storeName = getStoreName(contract, modelName);
|
|
1668
|
-
const patch = applyUpdateDefaults(contract.execution?.mutations.defaults, storeName, data, createMutationDefaultsCache());
|
|
1669
|
-
assertEnumValues(contract, modelName, patch);
|
|
1670
|
-
const { storeNames, enforcesOnUpdate } = collectUpdateStoreNames(contract, modelName, patch);
|
|
1671
|
-
const needsRowForFks = fkCheckNeedsExistingRow(contract, modelName, patch);
|
|
1672
|
-
return withMutationScope(executor, storeNames, async (scope) => {
|
|
1673
|
-
if (!needsRowForFks) await validateScalarFks(scope, contract, modelName, patch);
|
|
1674
|
-
const meta = makePlanMeta(contract);
|
|
1675
|
-
const combined = filters.length === 0 ? void 0 : filters.length === 1 ? filters[0] : {
|
|
1676
|
-
kind: "and",
|
|
1677
|
-
exprs: filters
|
|
1678
|
-
};
|
|
1679
|
-
const filter = combined !== void 0 ? (row) => evaluateFilter(combined, row) : void 0;
|
|
1680
|
-
if (!enforcesOnUpdate && !needsRowForFks) return (await scope.execute({
|
|
1681
|
-
meta,
|
|
1682
|
-
kind: "scan-write",
|
|
1683
|
-
storeName,
|
|
1684
|
-
write: "put-merged",
|
|
1685
|
-
patch,
|
|
1686
|
-
take: 1,
|
|
1687
|
-
...filter !== void 0 ? { filter } : {}
|
|
1688
|
-
}))[0] ?? null;
|
|
1689
|
-
const oldRow = (await scope.execute({
|
|
1690
|
-
meta,
|
|
1691
|
-
kind: "cursor-scan",
|
|
1692
|
-
storeName,
|
|
1693
|
-
take: 1,
|
|
1694
|
-
...filter !== void 0 ? { filter } : {}
|
|
1695
|
-
}))[0];
|
|
1696
|
-
if (!oldRow) return null;
|
|
1697
|
-
if (needsRowForFks) await validateScalarFks(scope, contract, modelName, patch, oldRow);
|
|
1698
|
-
if (enforcesOnUpdate) await applyReferentialActionsForRowOnUpdate(scope, contract, modelName, oldRow, patch);
|
|
1699
|
-
const key = extractKeyFromRow(oldRow, getKeyPath(contract, modelName));
|
|
1700
|
-
return (await scope.execute({
|
|
1701
|
-
meta,
|
|
1702
|
-
kind: "update",
|
|
1703
|
-
storeName,
|
|
1704
|
-
key,
|
|
1705
|
-
patch
|
|
1706
|
-
}))[0] ?? null;
|
|
1707
|
-
});
|
|
1648
|
+
return (await executeScalarUpdates(options, 1))[0] ?? null;
|
|
1708
1649
|
}
|
|
1709
1650
|
/**
|
|
1710
1651
|
* Bulk counterpart to {@link executeScalarUpdateWithFkValidation}: same FK
|
|
1711
1652
|
* validation and `onUpdate` enforcement, but applies the patch to every row
|
|
1712
1653
|
* the filter matches (no `take: 1`) and returns all of them.
|
|
1713
1654
|
*
|
|
1714
|
-
*
|
|
1715
|
-
*
|
|
1716
|
-
*
|
|
1717
|
-
*
|
|
1718
|
-
* transaction is exactly what the sync interceptor needs to track the write
|
|
1719
|
-
* correctly: its transaction-scope hook (`SyncInterceptingTransactionScope#maybeTrack`'s
|
|
1720
|
-
* `scan-write`/`update` cases) writes one outbox event per row it's handed,
|
|
1721
|
-
* atomically with the write itself. The plan-level path `update()` falls
|
|
1722
|
-
* back to for a non-enforced single row has no equivalent hook — a plan is
|
|
1723
|
-
* extended with outbox ops BEFORE it runs, which only works when the
|
|
1724
|
-
* affected key is knowable up front.
|
|
1655
|
+
* The transaction scope lets the sync interceptor observe the affected rows
|
|
1656
|
+
* and write one outbox event per row in the same transaction. A bulk write's
|
|
1657
|
+
* affected keys are only known after it runs, so tracking cannot happen by
|
|
1658
|
+
* extending the plan before execution.
|
|
1725
1659
|
*/
|
|
1726
1660
|
async function executeBulkUpdateWithFkValidation(options) {
|
|
1661
|
+
return executeScalarUpdates(options);
|
|
1662
|
+
}
|
|
1663
|
+
/** Shares validation and atomic writes; only single-row updates limit the scan. */
|
|
1664
|
+
async function executeScalarUpdates(options, take) {
|
|
1727
1665
|
const { executor, contract, modelName, filters, data } = options;
|
|
1728
1666
|
const storeName = getStoreName(contract, modelName);
|
|
1729
1667
|
const patch = applyUpdateDefaults(contract.execution?.mutations.defaults, storeName, data, createMutationDefaultsCache());
|
|
@@ -1744,12 +1682,14 @@ async function executeBulkUpdateWithFkValidation(options) {
|
|
|
1744
1682
|
storeName,
|
|
1745
1683
|
write: "put-merged",
|
|
1746
1684
|
patch,
|
|
1685
|
+
...take !== void 0 ? { take } : {},
|
|
1747
1686
|
...filter !== void 0 ? { filter } : {}
|
|
1748
1687
|
});
|
|
1749
1688
|
const oldRows = await scope.execute({
|
|
1750
1689
|
meta,
|
|
1751
1690
|
kind: "cursor-scan",
|
|
1752
1691
|
storeName,
|
|
1692
|
+
...take !== void 0 ? { take } : {},
|
|
1753
1693
|
...filter !== void 0 ? { filter } : {}
|
|
1754
1694
|
});
|
|
1755
1695
|
const keyPath = getKeyPath(contract, modelName);
|
|
@@ -1776,7 +1716,7 @@ async function executeBulkUpdateWithFkValidation(options) {
|
|
|
1776
1716
|
* behaves like `restrict`, which is also the default.
|
|
1777
1717
|
*/
|
|
1778
1718
|
function hasEnforceableChildRelations(contract, modelName) {
|
|
1779
|
-
return getRelationDefinitions(contract, modelName).some((def) =>
|
|
1719
|
+
return getRelationDefinitions(contract, modelName).some((def) => isChildEnforcementRelation(contract, modelName, def));
|
|
1780
1720
|
}
|
|
1781
1721
|
/**
|
|
1782
1722
|
* Transitively walks the `onDelete` cascade graph from `modelName`, collecting
|
|
@@ -1797,8 +1737,8 @@ function collectDeleteStoreNames(contract, modelName) {
|
|
|
1797
1737
|
visitedModels.add(mName);
|
|
1798
1738
|
stores.add(getStoreName(contract, mName));
|
|
1799
1739
|
for (const def of getRelationDefinitions(contract, mName)) {
|
|
1800
|
-
if (!
|
|
1801
|
-
const action =
|
|
1740
|
+
if (!isChildEnforcementRelation(contract, mName, def)) continue;
|
|
1741
|
+
const action = getReferentialActionForRelation(contract, mName, def, "onDelete");
|
|
1802
1742
|
stores.add(def.relatedStoreName);
|
|
1803
1743
|
if (action === "cascade") walk(def.relatedModelName);
|
|
1804
1744
|
}
|
|
@@ -1831,8 +1771,8 @@ async function applyReferentialActionsForRow(scope, contract, modelName, row, vi
|
|
|
1831
1771
|
visited.add(rowKey);
|
|
1832
1772
|
const meta = makePlanMeta(contract);
|
|
1833
1773
|
for (const def of getRelationDefinitions(contract, modelName)) {
|
|
1834
|
-
if (!
|
|
1835
|
-
const action =
|
|
1774
|
+
if (!isChildEnforcementRelation(contract, modelName, def)) continue;
|
|
1775
|
+
const action = getReferentialActionForRelation(contract, modelName, def, "onDelete");
|
|
1836
1776
|
const childFilter = buildChildFilterFromRow(def, row);
|
|
1837
1777
|
if (action === "restrict") {
|
|
1838
1778
|
if (await childExists(scope, contract, meta, def, row)) throw new Error(`Cannot delete ${modelName} '${keyToken(extractKeyFromRow(row, keyPath))}': child records exist on relation '${def.relationName}'. Delete those children first, or declare onDelete: Cascade, SetNull or SetDefault on the relation.`);
|
|
@@ -2422,29 +2362,10 @@ var IdbStoreAccessorImpl = class IdbStoreAccessorImpl {
|
|
|
2422
2362
|
...this.#state.skip !== void 0 ? { skip: this.#state.skip } : {},
|
|
2423
2363
|
...this.#state.take !== void 0 ? { take: this.#state.take } : {}
|
|
2424
2364
|
};
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
const filter = remainingFilter !== void 0 ? (row) => evaluateFilter(remainingFilter, row) : void 0;
|
|
2430
|
-
return {
|
|
2431
|
-
meta,
|
|
2432
|
-
ast,
|
|
2433
|
-
idbPlan: {
|
|
2434
|
-
meta,
|
|
2435
|
-
kind: "cursor-scan",
|
|
2436
|
-
storeName: this.#storeName,
|
|
2437
|
-
...indexName !== void 0 ? { indexName } : {},
|
|
2438
|
-
range: IDBKeyRange.only(value),
|
|
2439
|
-
...filter !== void 0 ? { filter } : {},
|
|
2440
|
-
...comparator !== void 0 ? { comparator } : {},
|
|
2441
|
-
...this.#state.skip !== void 0 ? { skip: this.#state.skip } : {},
|
|
2442
|
-
...this.#state.take !== void 0 ? { take: this.#state.take } : {}
|
|
2443
|
-
}
|
|
2444
|
-
};
|
|
2445
|
-
}
|
|
2446
|
-
}
|
|
2447
|
-
const filter = combined !== void 0 ? (row) => evaluateFilter(combined, row) : void 0;
|
|
2365
|
+
let hint = null;
|
|
2366
|
+
if (typeof IDBKeyRange !== "undefined") hint = extractIndexEqualityHint(combined, fieldToIndexMap ?? buildFieldToIndexMap(this.#contract, this.#storeName), getKeyPath(this.#contract, this.#modelName));
|
|
2367
|
+
const remainingFilter = hint !== null ? hint.remainingFilter : combined;
|
|
2368
|
+
const filter = remainingFilter !== void 0 ? (row) => evaluateFilter(remainingFilter, row) : void 0;
|
|
2448
2369
|
return {
|
|
2449
2370
|
meta,
|
|
2450
2371
|
ast,
|
|
@@ -2452,6 +2373,8 @@ var IdbStoreAccessorImpl = class IdbStoreAccessorImpl {
|
|
|
2452
2373
|
meta,
|
|
2453
2374
|
kind: "cursor-scan",
|
|
2454
2375
|
storeName: this.#storeName,
|
|
2376
|
+
...hint?.indexName !== void 0 ? { indexName: hint.indexName } : {},
|
|
2377
|
+
...hint !== null ? { range: IDBKeyRange.only(hint.value) } : {},
|
|
2455
2378
|
...filter !== void 0 ? { filter } : {},
|
|
2456
2379
|
...comparator !== void 0 ? { comparator } : {},
|
|
2457
2380
|
...this.#state.skip !== void 0 ? { skip: this.#state.skip } : {},
|
|
@@ -2584,4 +2507,4 @@ function idbOrm(options) {
|
|
|
2584
2507
|
//#endregion
|
|
2585
2508
|
export { createRelationMutator as a, withMutationScope as c, getStoreName as d, keyEquals as f, hasNestedMutationCallbacks as i, extractKeyFromRow as l, applyReferentialActionsForRow as n, isRelationMutationCallback as o, keyToken as p, collectDeleteStoreNames as r, isRelationMutationDescriptor as s, idbOrm as t, getKeyPath as u };
|
|
2586
2509
|
|
|
2587
|
-
//# sourceMappingURL=idb-orm-
|
|
2510
|
+
//# sourceMappingURL=idb-orm-kDKFzR3S.mjs.map
|