@prisma-idb/client-idb 0.8.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.
@@ -1,4 +1,4 @@
1
- import { n as createIdbClient, t as createManagedIdbClient } from "./managed-client-DofL09P8.mjs";
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.
@@ -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-DofL09P8.mjs";
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":";;;;;;;;;;;;;;KAgCK,SAAS,aAAa;WAA6B;aAAmB,kBAAkB;;IACzF,SAAS;WAAuB,cAAc;IAC5C,UAAU,eAAe,qBACvB,IACA,eAAe,qBACjB,eAAe,qBACjB,eAAe;;;;;;KASP,cAAc,SAAS;;;;;;;;;;;;KAiB9B,mBAAmB,aAAa,2BAA2B,iBAAiB,2BAA2B;KAEvG,kBAAkB,WAAW,iDAAiD,2BAA2B,aAC1G,0BACA,mBAAmB,mBAAmB,0BACpC,wBAAwB,mBAAmB,0BAC5B,WAAW,mBAAmB,WAAW,aAAa,mBAAmB,WAAW,WAAW,QAC5G,0BACF;;;;;;;KAQD,kBAAkB,aAAa,0BAA0B,iBAAiB,0BAA0B;KAEpG,iBAAiB,WAAW,iDAAiD,0BAA0B,aACxG,0BACA,kBAAkB,mBAAmB,0BACnC,wBAAwB,kBAAkB,0BAC3B,WAAW,kBAAkB,WAAW,aAAa,kBAAkB,WAAW,WAAW,QAC1G,0BACF;;KAKM,gBAAgB,WAAW,4BAA4B,kBAAkB,WAAW;;;;;;;;KAWpF,YAAY,WAAW,wCACvB,WAAW,gBAAgB,WAAW,cAAc,gBAAgB,WAAW,WAAW;;;;;;;;;;KAc1F,aAAa,WAAW,4BAA4B,wBAAwB,SAAS,aAC7F,SAAS,WAAW;EAAqB;IAAW,eAAe;;IACjE,mBACE,IACA,8BACE;;;;;;;;;;KAcE,oBAAoB,WAAW,4BAA4B,wBAAwB,SAAS,aACpG,SAAS,WAAW;EAAqB;IAAW,eAAe;;IACjE,6BACY,KACV,8BACE;;KAML,aAAa,WAAW,0BAA0B,uCACpD,WAAW,SAAS,OAAO,iBAAiB,kBAAkB,WAAW,aACtE,kBAAkB,WAAW,WAAW,OAAO,WAAW,kBAAkB,WAAW,cACvF;;;;;;;;;;;;KAcD,QAAQ,MAAM;;;;;;;;;;;;;;KAeP,QAAQ,WAAW,4BAC7B,QAAQ,oBAAoB,WAAW,2BACnC,aAAa,WAAW,yBAAyB,kBAAkB,WAAW,aAC5E,kBAAkB,WAAW,WAAW,aAAa,WAAW,cAChE,cACF,oBAAoB,WAAW,mCAAmC,yBAChE,qBAAqB,kBAAkB,WAAW,aAChD,kBAAkB,WAAW,WAAW,UACxC,cACF,aAAa,WAAW,WAAW,oBAAoB,WAAW;;KAKrE,eAAe,WAAW,4BAA4B,wBAAwB,SAAS,aACxF,SAAS,WAAW;WAA8B;aAAoB,iBAAiB;;IACrF,mBACE;;KAMH,qBAAqB,WAAW,4BAA4B;WACtD;aAAoB,cAAc;;IAEzC,eAAe,WAAW,yBAAyB,SACjD,OAAO,eAAe,WAAW;WAA+B;;;;;;;;;;;KAejE,qBAAqB,WAAW,4BACnC,qBAAqB,WAAW,0BAC5B,oBAAoB,WAAW,mCAAmC,yBAChE;;KAKH,aAAa,WAAW,4BAA4B,qBAAqB,WAAW,mBACjF,iBAAiB,WAAW;;KAG/B,oBAAoB,aAAa;WAC3B;aAAsB;eAAsB,gBAAgB;;;IAEnE,+BACE;;;;;;;;KAWD,wBAAwB,WAAW,4BAA4B,QAClE,oBAAoB;WACT;aAAgB,OAAO,eAAe,WAAW;;WAAuB;;;KAIhF,oBAAoB,WAAW,4BAA4B,wBAAwB,WAAW,mBAC3F,iBAAiB,WAAW;;;;;;;KAQxB,YAAY,WAAW,4BAA4B,KAC7D,iBAAiB,WAAW,YAC5B,qBAAqB,WAAW,aAAa,wBAAwB,WAAW,cAEhF,QACE,KACE,iBAAiB,WAAW,YAC5B,aAAa,WAAW,aAAa,oBAAoB,WAAW;;KAOrE,eAAe,WAAW,4BAA4B,wBAAwB,SAAS,aACxF,SAAS,WAAW;EAAqB,iBAAiB;IACxD,UAAU,0BACR,IACA,wBACF,wBACF;;;;;;;;KASQ,iBAAiB,WAAW,+BACrC,WAAW,eAAe,WAAW,aAAa,eACjD,WACA,WACA,WAAW,4BACT,mBAEE,eAAe,WAAW;;;;;;;KAS7B,gBAAgB,WAAW,0BAA0B,yBAAyB,qBAAqB,eACtG,WACA,aAEE,eAAe,WAAW,WAAW,gBAAgB,4BACnD,eAAe,WAAW,WAAW;EACnC;IAAM,aAAa;;EACnB,mBAAmB;IAEnB,kBACE,gBAAgB,WAAW,QAC3B,gBAAgB,WAAW;;;;;;;;;KAazB;;KAGA,YAAY,WAAW,4BAA4B,QAC7D,OAAO,iBAAiB,WAAW,YAAY;;KAIrC,aAAa;;;;;;;;;KAUb,cAAc,WAAW,0BAA0B,kBAAkB,YAAY,WAAW,2BAEpG,WAAW,sBAAsB,UAAU,WAAW,gBAAgB,YACrE,UAAU,+BAA+B,gBAAgB,WAAW,WAAW;;;;;;;KASxE,YACV,WACA,0BACA,kBAAkB,YAAY,WAAW,cACvC,gBAAgB,WAAW,aAAa,cAAc,WAAW,WAAW;;;;;;;;;KAUpE,YACV,WACA,0BACA,kBAAkB,YAAY,WAAW,YACzC,6BACG,6BACD,YAAY,WAAW,WAAW,aAClC,KAAK,gBAAgB,WAAW,YAAY,kBAAkB,gBAAgB,WAAW,cACvF,cAAc,WAAW,WAAW;;;;;;KAS9B,WAAW,WAAW,4BAA4B,QAAQ,gBAAgB,WAAW;;KAKrF,eACV,WACA,0BACA,0BACE,wBAAwB,SAAS,aACjC,SAAS,WAAW;EAClB,WAAW,OAAO;IAAW;MAAM,aAAa;;;IAEhD;;UAKW,uBAAuB,WAAW;WACxC;WACA,eAAe,YAAY,WAAW;;;UAIhC;WACN;WACA,mBAAmB;;;;;;UAOb;WACN;WACA,oBAAoB;;;KAInB,oBAAoB,WAAW,4BACzC,uBAAuB,WAAW,aAAa,0BAA0B;;UAG1D,mBAAmB,WAAW;EAC7C,OACE,MAAM,YAAY,WAAW,sBAAsB,YAAY,WAAW,eACzE,uBAAuB,WAAW;EACrC,QAAQ,UAAU,mCAAmC,4BAA4B;EACjF,WAAW,oBAAoB,4BAA4B;;;;;;;;;;;;;;KAexD,uBAAuB,WAAW,2CACtB,iBAAiB,WAAW,uBAEvC,WACG,KAAK,iBAAiB,WAAW,cAChC,SAAS,mBAAmB,WAAW,eAAe,WAAW,WAAW,QACzE,oBAAoB,WAAW,eAAe,WAAW,WAAW;;;;;;;;;;;KAa5E,kBAAkB,WAAW,+BAC/B,WAAW,eAAe,WAAW,aAAa,eAAe,WAAW,WAAW;WAC7E;WACA;aAAe,mBAAmB;;IAEzC,gCAEE,eAAe,WAAW;;;;;;;;;;;KAa7B,kBAAkB,WAAW,4BAA4B,KAC5D,YAAY,WAAW,YACvB,kBAAkB,WAAW,cAE7B,QACE,KACE,YAAY,WAAW,YACvB,kBAAkB,WAAW,mBAAmB,YAAY,WAAW;;;;;;KASjE,oBAAoB,WAAW,4BAA4B,kBAAkB,WAAW,aAClG,uBAAuB,WAAW;;;;;;KAOxB,oBAAoB,WAAW,4BAA4B,WAAW,WAAW,aAC3F,uBAAuB,WAAW;;KAKxB;;KAGA,YAAY,WAAW,4BAA4B,QAC7D,sBAAsB,gBAAgB,WAAW,YAAY;;KAMnD;;;;;;;;;;KAWA,kBAAkB,WAAW,iDAAiD,gBACxF,WACA,yBAIK,WAAW,gBAAgB,WAAW,sBAAsB,YAC3D,gBAAgB,WAAW,WAAW,qBAEpC,mBAEE,gBAAgB,WAAW;cAEzB;;;;;;;;UASG,qBAAqB;WAC3B;WACA,IAAI;WACJ;YACC,2BAA2B;;;KAI3B,mBAAmB,eAAe;;KAGlC,mBAAmB,aAAa,uBACzC,WAAW,OAAO,KAAK,WAAW,2BAA2B,UAAU;;;;;;UAQzD,oBAAoB,WAAW;EAC9C,SAAS;EACT,IAAI,UAAU,kBAAkB,WAAW,YAAY,OAAO,IAAI;EAClE,IAAI,UAAU,kBAAkB,WAAW,YAAY,OAAO,IAAI;EAClE,IAAI,UAAU,kBAAkB,WAAW,YAAY,OAAO,IAAI;EAClE,IAAI,UAAU,kBAAkB,WAAW,YAAY,OAAO,IAAI;;;;;;iBASpD,aAAa,UAAU,aAAa;;;;;;;;;;;iBAepC,WAAW,UAAU,aAAa,oBAAoB;;;;;;;;;iBAiBtD,kBAAkB,KAAK,yBAAyB,SAAS,aAAa;;;;;;;;;;;;;;;;;;;;;;;UCrmBrE;EACf,MAAM,KAAK,MAAM,aAAa,OAAO,oBAAoB;;;;;;;;;UCI1C,iBAAiB;EAChC,GAAG,OAAO,IAAI;EACd,IAAI,OAAO,IAAI;EACf,GAAG,OAAO,IAAI;EACd,GAAG,OAAO,IAAI;EACd,IAAI,OAAO,IAAI;EACf,IAAI,OAAO,IAAI;EACf,GAAG,QAAQ,cAAc,KAAK;EAC9B,MAAM,QAAQ,cAAc,KAAK;EACjC,SAAS,cAAc;EACvB,WAAW,cAAc;EACzB,SAAS,cAAc;EACvB,UAAU;EACV,aAAa;;;;;;;;;;KAWH,iBAAiB,WAAW,wCAC5B,WAAW,gBAAgB,WAAW,eAAe,iBAC7D,gBAAgB,WAAW,WAAW;;;;;;;;;;;;;;;;;KCtC9B;WACG;WAA6B,OAAO;;WACpC;WAAyB;WAAsB,OAAO;;;;;;;;;;UAUpD;WACN;WACA;WACA,OAAO;;;;;;;;;;UAuBD;;;;;;;WAON,SAAS,cAAc;;WAEvB,UAAU;;WAEV;;WAEA;;;;;;WAMA,UAAU,eAAe;;;;;;WAMzB,iBAAiB;;;;;;;;KClEhB,iBACV,WACA,0BACA,kCACA,aAAa,oBACX,KAAK,gBAAgB,WAAW,YAAY,uBAAuB,gBAAgB,WAAW,cAChG,mBAAmB;;;;;;;;;UAUJ,mBAAmB,WAAW,0BAA0B;;;;;;;;;;;;;EAavE,UAAU,aAAa,kBACrB,KAAK,KAAK,oBAAoB,WAAW,eAAe,OACvD,QAAQ,MAAM,iBAAiB,WAAW,WAAW,QAAQ;;;;;KC+CtD,cAAc,WAAW,6BACnC,GAAG,iBAAiB,WAAW,eAC5B;;KAGA,WAAW,WAAW,4CACnB,gBAAgB,WAAW,+BACvB,gBAAgB,WAAW;;;;;;;;;;;UAatB,6BAA6B,WAAW;EACvD,MACE,QAAQ,YAAY,WAAW,aAAa,cAAc,WAAW,aACpE,6BAA6B,WAAW;EAC3C,QAAQ,MAAM,YAAY,WAAW,aAAa,6BAA6B,WAAW;EAC1F,KAAK,YAAY,6BAA6B,WAAW;EACzD,KAAK,YAAY,6BAA6B,WAAW;EACzD,SAAS;;;KAIN,gBAAgB,WAAW,0BAA0B,kBAAkB,MAC1E,YAAY,6BAA6B,WAAW,eAAe,WAAW,WAAW,QACtF;;;;;;;;;;;;;;;;UAmBY,iBACf,WACA,0BACA,kBAAkB,YAAY,WAAW,aAAa,YACtD;;;;;;;;;;;;;;EAeA,MACE,QAAQ,YAAY,WAAW,aAAa,cAAc,WAAW,aACpE,iBAAiB,WAAW,WAAW,WAAW;;EAGrD,QAAQ,MAAM,YAAY,WAAW,aAAa,iBAAiB,WAAW,WAAW,WAAW;;EAGpG,KAAK,YAAY,iBAAiB,WAAW,WAAW,WAAW;;EAGnE,KAAK,YAAY,iBAAiB,WAAW,WAAW,WAAW;;;;;;;;;;;;;;;;;;;;;;EAuBnE,QAAQ,UAAU,iBAAiB,WAAW,YAC5C,UAAU,GACV,WAAW,gBACT,WACA,WACA,GACA,6BAA6B,WAAW,eAAe,WAAW,WAAW,OAE9E,iBAAiB,WAAW,WAAW,YAAY,OAAO,UAAU;EACvE,QAAQ,UAAU,iBAAiB,WAAW,YAC5C,UAAU,GACV,UAAU,gBAAgB,WAAW,WAAW,GAAG,oBAClD,iBAAiB,WAAW,WAAW,YAAY,OAAO,cAAc;;;;;;;;;;;;EAa3E,OAAO,eAAe,WAAW,WAAW,eACvC,QAAQ,SACV,iBAAiB,WAAW,WAAW,WAAW;;EAGrD,OAAO,oBAAoB,YAAY,WAAW,WAAW,WAAW;;EAGxE,SAAS,QAAQ,YAAY,WAAW,WAAW,WAAW;;;;;;;;;;;;;;EAe9D,UAAU,aAAa,kBACrB,KAAK,KAAK,oBAAoB,WAAW,eAAe,OACvD,QAAQ,mBAAmB;;;;;;;;;;;;;;EAe9B,QAAQ,eAAe,WAAW,WAAW,eACxC,QAAQ,SACV,mBAAmB,WAAW,WAAW;;;;;;;;;;;;EAa5C,OAAO,MAAM,oBAAoB,WAAW,aAAa,QAAQ,gBAAgB,WAAW;;EAG5F,WAAW,KAAK,QAAQ,WAAW,aAAa,QAAQ,gBAAgB,WAAW;;EAGnF,OAAO,KAAK,QAAQ,WAAW,aAAa;;;;;;;;;EAU5C,OAAO,OAAO,oBAAoB,WAAW,aAAa,QAAQ,gBAAgB,WAAW;;;;;EAM7F,UAAU,OAAO,WAAW,WAAW,aAAa,oBAAoB,gBAAgB,WAAW;;;;;EAMnG,YAAY,OAAO,WAAW,WAAW,aAAa;;;;;;;;EAStD,OAAO;IACL,QAAQ,YAAY,WAAW;IAC/B,QAAQ,WAAW,WAAW;IAC9B,OAAO,YAAY,WAAW;MAC5B,QAAQ,gBAAgB,WAAW;;;;;EAMvC,UAAU,MAAM,YAAY,WAAW,eAAe,oBAAoB,gBAAgB,WAAW;;;;;EAMrG,YAAY,MAAM,YAAY,WAAW,eAAe;;;;;EAMxD,aAAa,oBAAoB,gBAAgB,WAAW;;;;;EAM5D,eAAe;;;;;;;;;;;EAYf,SAAS;;;;;;;;;;;;;;;;KC9UC,aAAa,kBAAkB,SAAS,2BAGxC,oBAAoB,qBAAqB,mBAAmB;EACpE,aAAa;IAEX,iBAAiB,WAAW;;;;UAOjB,cAAc,kBAAkB;;WAEtC,UAAU;;;;;;;WAOV,UAAU;;;;;;;;;;;;;;;;;;;;;;;iBA0BL,OAAO,kBAAkB,aAAa,SAAS,cAAc,aAAa,aAAa"}
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`. Shared by {@link getIndexForField} (relation loader,
45
- * single-field lookups) and {@link IdbStoreAccessorImpl} (top-level scans,
46
- * which need the whole map to probe combined filter expressions).
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)) && isIndexableEqValue(filter.value)) return {
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)) && isIndexableEqValue(expr.value)) {
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: foldRemainder(rest)
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) || !isIndexableEqValue(expr.value)) return null;
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, foldRemainder(flat.filter((_, j) => j !== orIdx)));
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 a frozen {@link IdbAggregateSelector} marker; the actual
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 (isScalar) return rows.map((row) => ({
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, modelName, scalarData, item.relation, item.mutation, defaultsCache);
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, modelName, scalarData, item.relation, item.mutation, defaultsCache);
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, parentModelName, scalarData, relation, mutation, defaultsCache) {
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), parentModelName, contract);
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, parentModelName, contract);
1064
+ copyRelatedValuesToParent(relation, scalarData, relatedRow);
1075
1065
  }
1076
- function copyRelatedValuesToParent(relation, scalarData, relatedRow, _parentModelName, _contract) {
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 getOnDeleteForDeleteRelation(contract, modelName, def) {
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 (!isDeleteEnforcementRelation(contract, mName, def)) continue;
1422
- const action = getOnUpdateForRelation(contract, mName, def);
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 (!isDeleteEnforcementRelation(contract, modelName, def)) continue;
1413
+ if (!isChildEnforcementRelation(contract, modelName, def)) continue;
1430
1414
  if (!def.localFields.some((f) => f in data)) continue;
1431
- const action = getOnUpdateForRelation(contract, modelName, def);
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
- * Unlike `validateScalarFks`'s compound-FK restriction, a compound (multi-
1459
- * field) relation is safe to cascade here: both `localFields` values come
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 (!isDeleteEnforcementRelation(contract, modelName, def)) continue;
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 = getOnUpdateForRelation(contract, modelName, def);
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 (i.e. the `onUpdate` collector found more than just the
1654
- * model's own store) — callers use that flag to choose between the existing
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
- const { executor, contract, modelName, filters, data } = options;
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
- * Always goes through the transaction scope — unlike single-row `update()`,
1715
- * which only needs one when there's a scalar FK field or `onUpdate`
1716
- * enforcement to apply. A bulk scan-write's affected row SET isn't knowable
1717
- * until it actually runs, and observing that result INSIDE the same
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) => isDeleteEnforcementRelation(contract, modelName, 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 (!isDeleteEnforcementRelation(contract, mName, def)) continue;
1801
- const action = getOnDeleteForDeleteRelation(contract, mName, def);
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 (!isDeleteEnforcementRelation(contract, modelName, def)) continue;
1835
- const action = getOnDeleteForDeleteRelation(contract, modelName, def);
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
- if (typeof IDBKeyRange !== "undefined") {
2426
- const hint = extractIndexEqualityHint(combined, fieldToIndexMap ?? buildFieldToIndexMap(this.#contract, this.#storeName), getKeyPath(this.#contract, this.#modelName));
2427
- if (hint !== null) {
2428
- const { indexName, value, remainingFilter } = hint;
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-C8yXJp6R.mjs.map
2510
+ //# sourceMappingURL=idb-orm-kDKFzR3S.mjs.map