Conversation
… readability and maintainability
…improved parameter handling
…ating related functions
…structuring output
… and cleaning up options
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR introduces schema versioning and migrations support to the Prisma IDB generator. It adds automatic schema hash computation and drift detection, enables version-based database initialization with sequential migration application, and refactors generator file creators to use options objects for improved maintainability. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as PrismaIDBClient
participant IDB as IndexedDB
participant MetaStore as _idb_meta Store
participant MigrationSvc as Migration Service
Client->>IDB: openDB("prisma-idb")
IDB-->>Client: db handle
Client->>MetaStore: read "schemaHash"
MetaStore-->>Client: stored hash (or null)
alt Schema Hash Mismatch
Client->>IDB: close db
Client->>MigrationSvc: check DROP_DB_ON_SCHEMA_VERSION_MISMATCH
alt Drop DB
MigrationSvc->>IDB: deleteDB("prisma-idb")
MigrationSvc->>Client: recreate client recursively
else Throw Error
MigrationSvc-->>Client: throw mismatch error with instructions
end
else Hash Match or No Prior Hash
Client->>IDB: initialize (upgrade callback)
IDB->>MigrationSvc: switchOnVersion(oldVersion)
MigrationSvc->>MigrationSvc: call migrateV1(), migrateV2(), ... migrateVn()
MigrationSvc-->>IDB: create/update stores via each migration
IDB-->>Client: db ready
Client->>MetaStore: write IDB_SCHEMA_HASH to "schemaHash"
MetaStore-->>Client: stored
Client->>Client: set instance and complete
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (9)
packages/generator/src/fileCreators/idb-interface/create.ts (1)
8-24: ThehasMigrations = falsebranch is no longer safe.The paired client template now always accesses
_idb_metafor schema-hash checks, so this helper can still emit aPrismaIDBSchemawithout that store. That leaves an easy path for future callers or tests to generate a schema type that diverges from the client template. Prefer generating_idb_metaunconditionally here, or derive the flag from the exact same condition used by the client generator.Also applies to: 57-62
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/generator/src/fileCreators/idb-interface/create.ts` around lines 8 - 24, The current conditional default hasMigrations = false allows createPrismaIDBSchema to emit a PrismaIDBSchema without the _idb_meta store while the paired client template always expects _idb_meta; update create.ts so that _idb_meta is generated unconditionally (remove or ignore hasMigrations when deciding to include _idb_meta) or else compute hasMigrations using the exact same condition the client generator uses so both sides match; refer to the options variable and the hasMigrations binding in this file and ensure the logic that builds the schema models always includes a model named "_idb_meta" (or derives hasMigrations from the client generator condition) to keep schema and client in sync.packages/generator/src/generator.ts (1)
84-92: Consider an options bag forgenerateMigrationstoo.This is now the only orchestration call here still carrying seven positional arguments, several of them same-type strings/booleans. Converting it to an object would make future migration additions much harder to misorder.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/generator/src/generator.ts` around lines 84 - 92, Refactor the generateMigrations API to accept a single options object instead of seven positional args: replace the call that currently passes (options.schemaPath, outputPath, filteredModels, options.dmmf.datamodel.enums, outboxSync, outboxModelName, versionMetaModelName) with a single object literal e.g. { schemaPath: options.schemaPath, outputPath, models: filteredModels, enums: options.dmmf.datamodel.enums, outboxSync, outboxModelName, versionMetaModelName }, then update the generateMigrations function signature and its type definitions to destructure those named properties (schemaPath, outputPath, models, enums, outboxSync, outboxModelName, versionMetaModelName) and update any other call sites to the new object-shape to avoid misordering of same-typed params.packages/generator/src/helpers/migrations.ts (1)
68-80: Minor: Redundant filter condition.Line 77 explicitly excludes
migration_lock.toml, but this is already excluded by theentry.isDirectory()check sincemigration_lock.tomlis a file. The explicit check is harmless but unnecessary.♻️ Simplified filter
return fs .readdirSync(migrationsDir, { withFileTypes: true }) - .filter((entry) => entry.isDirectory() && entry.name !== "migration_lock.toml") + .filter((entry) => entry.isDirectory()) .map((entry) => entry.name) .sort();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/generator/src/helpers/migrations.ts` around lines 68 - 80, In listMigrationFolders, remove the redundant check excluding "migration_lock.toml": the filter should only test entry.isDirectory() (in the readdirSync(...).filter callback) since files like migration_lock.toml are already excluded; update the filter used in listMigrationFolders to only keep directories and leave the subsequent map/sort logic unchanged.packages/generator/src/fileCreators/migrations/createMigrationFunction.ts (1)
64-135: Add exhaustiveness check forMigrationOpswitch.The switch statement covers all 8 current
MigrationOptypes, but lacks adefaultcase. If a new operation type is added toMigrationOp, this will silently skip it without warning.♻️ Add exhaustiveness check
case "removeEnumValue": writer.writeLine(`// [auto] removed enum value: ${op.enumName}.${op.value}`); writer.writeLine(`// TODO: Existing records using "${op.value}" may need to be updated.`); // ... existing code ... writer.blankLine(); break; + + default: { + const _exhaustive: never = op; + throw new Error(`Unhandled migration op type: ${(_exhaustive as MigrationOp).type}`); + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/generator/src/fileCreators/migrations/createMigrationFunction.ts` around lines 64 - 135, The switch in writeOp over MigrationOp is missing an exhaustive/default branch; add a default case that forces a compile-time exhaustiveness check and runtime error for unknown op types (e.g., default: { const _exhaustive: never = op; throw new Error(`Unhandled MigrationOp type: ${(op as any)?.type}`); } or call an assertUnreachable(op) helper) so new MigrationOp variants fail fast at build/runtime; update the writeOp function to include this default branch referencing the MigrationOp union.packages/generator/src/fileCreators/prisma-idb-client/classes/PrismaIDBClient.ts (2)
136-157: Fallthrough comment placement in generated switch.The generated switch uses intentional fallthrough for sequential migrations, but the fallthrough comment (line 147) appears only once after all cases. For clarity in the generated code, consider adding a fallthrough comment after each case.
♻️ Improved fallthrough comments
writer.write(`switch (oldVersion) `).block(() => { for (let i = 0; i < migrationInfo.currentVersion; i++) { writer.writeLine(`// eslint-disable-next-line `@typescript-eslint/no-explicit-any``); writer.writeLine(`case ${i}: await migrateV${i + 1}(db as any, tx as any);`); + writer.writeLine(`// falls through`); } - writer.writeLine(`// fallthrough intentional — runs all pending migrations in sequence`); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/generator/src/fileCreators/prisma-idb-client/classes/PrismaIDBClient.ts` around lines 136 - 157, The generated switch in the upgrade callback (inside openDB<PrismaIDBSchema>("prisma-idb", CURRENT_VERSION, ...)) places a single fallthrough comment after all cases which obscures intent; modify the loop that emits migration cases in PrismaIDBClient.ts so that after each emitted case line (case ${i}: await migrateV${i + 1}(...);) you also emit a per-case fallthrough comment (e.g., // fallthrough intentional — continue to next migration) so every migration case documents the intentional fallthrough; update the code that writes the cases (the for loop that writes `case ${i}: await migrateV${i + 1}(db as any, tx as any);`) to append a writer.writeLine(...) immediately after each case emission instead of a single comment after the loop.
614-621:resetDatabasemay leave stale schema hash.The
resetDatabasemethod (lines 614-621) deletes the database and re-initializes, but does not update theschemaHashin_idb_meta. After reset, the nextcreateClient()call will find no stored hash (undefined), which is handled correctly by line 101'sstoredHash !== undefinedcheck.However, this means the hash won't be persisted until
createClient()is called again. Consider whether this is the intended behavior or ifresetDatabaseshould also persist the hash.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/generator/src/fileCreators/prisma-idb-client/classes/PrismaIDBClient.ts` around lines 614 - 621, The resetDatabase method currently deletes the DB and re-initializes but does not persist the schema hash into _idb_meta, leaving a stale/absent schemaHash for subsequent createClient() calls; update the addResetDatabaseMethod implementation for the resetDatabase method to, after calling PrismaIDBClient.instance.initialize(), also write the current schema hash into the _idb_meta entry (e.g., set _idb_meta.schemaHash to the active schema hash and persist it via the same internal helper used elsewhere to store meta) so that subsequent createClient() sees a persisted schemaHash; reference resetDatabase, _idb_meta, schemaHash and PrismaIDBClient.instance.initialize() when making the change.packages/generator/src/fileCreators/migrations/createDmmfSnapshot.ts (1)
41-58: Minor: Consider simplifying type wrapping for cleaner output.The current parentheses wrapping (lines 55-56) produces valid but verbose types like
((string)[]) | null. For cleaner generated code, you could conditionally add parentheses only when necessary.♻️ Optional simplification
- if (isList) tsType = `(${tsType})[]`; - if (!isRequired) tsType = `(${tsType}) | null`; + if (isList) tsType = `${tsType}[]`; + if (!isRequired) tsType = `${tsType} | null`;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/generator/src/fileCreators/migrations/createDmmfSnapshot.ts` around lines 41 - 58, prismaTypeToTs currently always wraps the computed tsType when making lists and nullable types, leading to redundant parentheses like ((string)[]) | null; change the wrapping logic in prismaTypeToTs so parentheses are only added when necessary: when creating a list, only wrap tsType as `(${tsType})[]` if the base tsType contains a union (`' | '`), otherwise use `${tsType}[]`; when marking nullable, only wrap the type in parentheses if it contains a union (`' | '`), otherwise use `${tsType} | null`. Update the logic in prismaTypeToTs to apply these conditional parentheses rules (refer to the isList and isRequired handling in prismaTypeToTs).packages/generator/src/fileCreators/migrations/create.ts (2)
30-38: Consider using an options object for better maintainability.The function has 7 parameters, which can make call sites harder to read and maintain. Given that the PR commits mention "options-style function signatures," consider aligning this function with that pattern.
♻️ Suggested refactor
+export interface GenerateMigrationsOptions { + schemaPath: string; + outputPath: string; + filteredModels: readonly DMMF.Model[]; + enums: readonly DMMF.DatamodelEnum[]; + outboxSync: boolean; + outboxModelName: string; + versionMetaModelName: string; +} + -export async function generateMigrations( - schemaPath: string, - outputPath: string, - filteredModels: readonly DMMF.Model[], - enums: readonly DMMF.DatamodelEnum[], - outboxSync: boolean, - outboxModelName: string, - versionMetaModelName: string -): Promise<MigrationGenerationResult> { +export async function generateMigrations( + options: GenerateMigrationsOptions +): Promise<MigrationGenerationResult> { + const { + schemaPath, + outputPath, + filteredModels, + enums, + outboxSync, + outboxModelName, + versionMetaModelName, + } = options;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/generator/src/fileCreators/migrations/create.ts` around lines 30 - 38, The generateMigrations function has a long positional parameter list; refactor it to accept a single options object (e.g. define a MigrationOptions/type with properties schemaPath, outputPath, filteredModels, enums, outboxSync, outboxModelName, versionMetaModelName) and update the generateMigrations signature to take that options object and use destructuring inside the function; update all call sites to pass an object instead of positional args (or add an overload/compat shim if needed for backward compatibility) and keep the Promise<MigrationGenerationResult> return type unchanged.
84-100: Simplify redundant option-setting logic.The current flow sets initial values at lines 85-91, then overrides them at lines 94-100. Tracing through both branches shows:
dmmfOptions.outboxSyncends up asoutboxSyncregardless ofisFirstMigrationdmmfOptions.includeMetaStoreends up astruefor all versions (V1 via line 85/90, V2+ via line 99)This can be simplified:
♻️ Simplified logic
const isFirstMigration = version === 1; - const includeMetaStore = isFirstMigration; const dmmfOptions = { - outboxSync: outboxSync && isFirstMigration, + outboxSync, outboxModelName, versionMetaModelName, - includeMetaStore, + includeMetaStore: true, // Meta store exists for all versions }; - // For V2+, outbox/meta stores already exist from V1 — include them in schema - if (outboxSync && !isFirstMigration) { - dmmfOptions.outboxSync = true; - } - // _idb_meta always exists after V1 - if (!isFirstMigration) { - dmmfOptions.includeMetaStore = true; - }Note: If you apply this, you'll need to update line 120 to pass
isFirstMigrationdirectly instead ofincludeMetaStore.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/generator/src/fileCreators/migrations/create.ts` around lines 84 - 100, The dmmfOptions construction is redundant: set dmmfOptions.outboxSync to the incoming outboxSync value (no conditional on isFirstMigration) and set dmmfOptions.includeMetaStore to true (it is always true after V1 or for V1), keep outboxModelName and versionMetaModelName as-is, and remove the later conditional overrides that reassign those fields; also update the downstream call that currently passes includeMetaStore to instead pass the original isFirstMigration value so callers still receive the migration-version flag.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/pidb-kanban-example/src/lib/prisma-idb/client/prisma-idb-client.ts`:
- Around line 456-468: The IndexedDB upgrade callback adds the "_idb_meta" store
but the file still uses the legacy hardcoded IDB_VERSION (IDB_VERSION = 1), so
existing DBs won't trigger upgrade; fix by bumping the IDB version constant
(IDB_VERSION) to a higher integer or switch the generated client to use the
migration-based CURRENT_VERSION path used by the generator, ensuring the
openDB(...) call's upgrade() runs and creates "_idb_meta" (referencing openDB,
upgrade, IDB_VERSION and the "_idb_meta" store) and update any schema-drift
check that reads client._db.get("_idb_meta", "schemaHash") to match the new
versioning approach.
In `@apps/usage/src/lib/prisma-idb/client/prisma-idb-client.ts`:
- Around line 105-109: When the blocking() callback closes the underlying
connection it must also clear the cached singleton so future createClient()
calls don't return a stale PrismaIDBClient with a closed _db; update the
blocking() implementation on the PrismaIDBClient to set the static instance
(e.g., PrismaIDBClient.instance or whatever singleton variable is used) to
undefined/null after calling db.close(), ensuring subsequent createClient()
constructs a fresh client with a valid _db.
- Around line 56-67: The generated PrismaIDB client currently reads from the
"_idb_meta" store immediately after initialize() and caches the singleton before
blocking() can close the connection, causing missing-store and closed-instance
bugs; update the generator template (PrismaIDBClient.ts) to (1) bump IDB_VERSION
and add an upgrade migration that ensures the "_idb_meta" object store is
created on existing databases so the stored schemaHash lookup succeeds, and (2)
move singleton caching to occur only after the drift check and after setting up
blocking(), or detect when client._db.close() runs inside blocking() and
invalidate the cached singleton (e.g., clear the cached PrismaIDBClient) so
subsequent PrismaIDBClient.createClient() returns a fresh, open connection;
ensure the logic still respects DROP_DB_ON_SCHEMA_VERSION_MISMATCH and the
IDB_SCHEMA_HASH comparison.
In `@packages/generator/src/fileCreators/migrations/create.ts`:
- Around line 58-64: Wrap the existing read/parse logic that reads
snapshotJsonPath and pushes into snapshots in a try-catch around
fs.readFileSync(...) and JSON.parse(...); on JSON.parse failure catch the error,
throw or surface a new error that includes snapshotJsonPath and the original
error message (or log it) so callers see "corrupted snapshot.json at
{snapshotJsonPath}: {originalError}" and avoid an unhelpful stacktrace; keep the
fallback branch that pushes { ...currentSnapshot, version: i + 1 } unchanged for
the non-existent-file case.
---
Nitpick comments:
In `@packages/generator/src/fileCreators/idb-interface/create.ts`:
- Around line 8-24: The current conditional default hasMigrations = false allows
createPrismaIDBSchema to emit a PrismaIDBSchema without the _idb_meta store
while the paired client template always expects _idb_meta; update create.ts so
that _idb_meta is generated unconditionally (remove or ignore hasMigrations when
deciding to include _idb_meta) or else compute hasMigrations using the exact
same condition the client generator uses so both sides match; refer to the
options variable and the hasMigrations binding in this file and ensure the logic
that builds the schema models always includes a model named "_idb_meta" (or
derives hasMigrations from the client generator condition) to keep schema and
client in sync.
In `@packages/generator/src/fileCreators/migrations/create.ts`:
- Around line 30-38: The generateMigrations function has a long positional
parameter list; refactor it to accept a single options object (e.g. define a
MigrationOptions/type with properties schemaPath, outputPath, filteredModels,
enums, outboxSync, outboxModelName, versionMetaModelName) and update the
generateMigrations signature to take that options object and use destructuring
inside the function; update all call sites to pass an object instead of
positional args (or add an overload/compat shim if needed for backward
compatibility) and keep the Promise<MigrationGenerationResult> return type
unchanged.
- Around line 84-100: The dmmfOptions construction is redundant: set
dmmfOptions.outboxSync to the incoming outboxSync value (no conditional on
isFirstMigration) and set dmmfOptions.includeMetaStore to true (it is always
true after V1 or for V1), keep outboxModelName and versionMetaModelName as-is,
and remove the later conditional overrides that reassign those fields; also
update the downstream call that currently passes includeMetaStore to instead
pass the original isFirstMigration value so callers still receive the
migration-version flag.
In `@packages/generator/src/fileCreators/migrations/createDmmfSnapshot.ts`:
- Around line 41-58: prismaTypeToTs currently always wraps the computed tsType
when making lists and nullable types, leading to redundant parentheses like
((string)[]) | null; change the wrapping logic in prismaTypeToTs so parentheses
are only added when necessary: when creating a list, only wrap tsType as
`(${tsType})[]` if the base tsType contains a union (`' | '`), otherwise use
`${tsType}[]`; when marking nullable, only wrap the type in parentheses if it
contains a union (`' | '`), otherwise use `${tsType} | null`. Update the logic
in prismaTypeToTs to apply these conditional parentheses rules (refer to the
isList and isRequired handling in prismaTypeToTs).
In `@packages/generator/src/fileCreators/migrations/createMigrationFunction.ts`:
- Around line 64-135: The switch in writeOp over MigrationOp is missing an
exhaustive/default branch; add a default case that forces a compile-time
exhaustiveness check and runtime error for unknown op types (e.g., default: {
const _exhaustive: never = op; throw new Error(`Unhandled MigrationOp type:
${(op as any)?.type}`); } or call an assertUnreachable(op) helper) so new
MigrationOp variants fail fast at build/runtime; update the writeOp function to
include this default branch referencing the MigrationOp union.
In
`@packages/generator/src/fileCreators/prisma-idb-client/classes/PrismaIDBClient.ts`:
- Around line 136-157: The generated switch in the upgrade callback (inside
openDB<PrismaIDBSchema>("prisma-idb", CURRENT_VERSION, ...)) places a single
fallthrough comment after all cases which obscures intent; modify the loop that
emits migration cases in PrismaIDBClient.ts so that after each emitted case line
(case ${i}: await migrateV${i + 1}(...);) you also emit a per-case fallthrough
comment (e.g., // fallthrough intentional — continue to next migration) so every
migration case documents the intentional fallthrough; update the code that
writes the cases (the for loop that writes `case ${i}: await migrateV${i + 1}(db
as any, tx as any);`) to append a writer.writeLine(...) immediately after each
case emission instead of a single comment after the loop.
- Around line 614-621: The resetDatabase method currently deletes the DB and
re-initializes but does not persist the schema hash into _idb_meta, leaving a
stale/absent schemaHash for subsequent createClient() calls; update the
addResetDatabaseMethod implementation for the resetDatabase method to, after
calling PrismaIDBClient.instance.initialize(), also write the current schema
hash into the _idb_meta entry (e.g., set _idb_meta.schemaHash to the active
schema hash and persist it via the same internal helper used elsewhere to store
meta) so that subsequent createClient() sees a persisted schemaHash; reference
resetDatabase, _idb_meta, schemaHash and PrismaIDBClient.instance.initialize()
when making the change.
In `@packages/generator/src/generator.ts`:
- Around line 84-92: Refactor the generateMigrations API to accept a single
options object instead of seven positional args: replace the call that currently
passes (options.schemaPath, outputPath, filteredModels,
options.dmmf.datamodel.enums, outboxSync, outboxModelName, versionMetaModelName)
with a single object literal e.g. { schemaPath: options.schemaPath, outputPath,
models: filteredModels, enums: options.dmmf.datamodel.enums, outboxSync,
outboxModelName, versionMetaModelName }, then update the generateMigrations
function signature and its type definitions to destructure those named
properties (schemaPath, outputPath, models, enums, outboxSync, outboxModelName,
versionMetaModelName) and update any other call sites to the new object-shape to
avoid misordering of same-typed params.
In `@packages/generator/src/helpers/migrations.ts`:
- Around line 68-80: In listMigrationFolders, remove the redundant check
excluding "migration_lock.toml": the filter should only test entry.isDirectory()
(in the readdirSync(...).filter callback) since files like migration_lock.toml
are already excluded; update the filter used in listMigrationFolders to only
keep directories and leave the subsequent map/sort logic unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: c9317fa9-a427-4deb-8816-11e68b768954
⛔ Files ignored due to path filters (2)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamltests/schema-gen/tests/projection/__snapshots__/generator.test.ts.snapis excluded by!**/*.snap
📒 Files selected for processing (48)
apps/pidb-kanban-example/src/lib/prisma-idb/client/idb-interface.tsapps/pidb-kanban-example/src/lib/prisma-idb/client/idb-schema-hash.tsapps/pidb-kanban-example/src/lib/prisma-idb/client/index.tsapps/pidb-kanban-example/src/lib/prisma-idb/client/prisma-idb-client.tsapps/usage/src/lib/prisma-idb/client/idb-interface.tsapps/usage/src/lib/prisma-idb/client/idb-schema-hash.tsapps/usage/src/lib/prisma-idb/client/index.tsapps/usage/src/lib/prisma-idb/client/prisma-idb-client.tspackage.jsonpackages/generator/src/fileCreators/apply-pull/create.tspackages/generator/src/fileCreators/batch-processor/create.tspackages/generator/src/fileCreators/idb-interface/create.tspackages/generator/src/fileCreators/idb-schema-hash/create.tspackages/generator/src/fileCreators/idb-utils/create.tspackages/generator/src/fileCreators/index/create-client-index.tspackages/generator/src/fileCreators/migrations/create.tspackages/generator/src/fileCreators/migrations/createDmmfSnapshot.tspackages/generator/src/fileCreators/migrations/createMigrationFunction.tspackages/generator/src/fileCreators/prisma-idb-client/classes/PrismaIDBClient.tspackages/generator/src/fileCreators/prisma-idb-client/create.tspackages/generator/src/fileCreators/scoped-schema/create.tspackages/generator/src/fileCreators/validators/create.tspackages/generator/src/generator.tspackages/generator/src/helpers/migrations.tspackages/generator/src/helpers/parseGeneratorConfig.tspackages/generator/src/helpers/utils.tspnpm-workspace.yamltests/.gitignoretests/schema-gen/.gitignoretests/schema-gen/package.jsontests/schema-gen/tests/migration/migrations.test.tstests/schema-gen/tests/migration/schemas/v0-base.prismatests/schema-gen/tests/migration/schemas/v1-new-field.prismatests/schema-gen/tests/migration/schemas/v2-new-model.prismatests/schema-gen/tests/projection/generator.test.tstests/schema-gen/tests/projection/schemas/invalid/autoincrement-id.prismatests/schema-gen/tests/projection/schemas/invalid/changelog-manually-included.prismatests/schema-gen/tests/projection/schemas/invalid/extra-change-operation-enum-value.prismatests/schema-gen/tests/projection/schemas/invalid/extra-changelog-field.prismatests/schema-gen/tests/projection/schemas/invalid/invalid-change-operation-enum-value.prismatests/schema-gen/tests/projection/schemas/invalid/invalid-changelog-operation-field-type.prismatests/schema-gen/tests/projection/schemas/invalid/missing-change-operation-enum.prismatests/schema-gen/tests/projection/schemas/invalid/missing-changelog.prismatests/schema-gen/tests/projection/schemas/invalid/no-root-model-with-sync.prismatests/schema-gen/tests/projection/schemas/invalid/ownership-cycle-ambiguous.prismatests/schema-gen/tests/projection/schemas/invalid/ownership-cycle-self.prismatests/schema-gen/tests/projection/schemas/invalid/wrong-changelog-id-default.prismatests/schema-gen/tests/projection/schemas/valid/user-board-todo.prisma
💤 Files with no reviewable changes (1)
- tests/.gitignore
| const db = await openDB<PrismaIDBSchema>("prisma-idb", IDB_VERSION, { | ||
| upgrade(db) { | ||
| db.createObjectStore("Board", { keyPath: ["id"] }); | ||
| db.createObjectStore("Todo", { keyPath: ["id"] }); | ||
| const UserStore = db.createObjectStore("User", { keyPath: ["id"] }); | ||
| UserStore.createIndex("emailIndex", ["email"], { unique: true }); | ||
| db.createObjectStore("OutboxEvent", { keyPath: ["id"] }); | ||
| db.createObjectStore("VersionMeta", { keyPath: ["model", "key"] }); | ||
| db.createObjectStore("_idb_meta"); | ||
| }, | ||
| blocking() { | ||
| db.close(); | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
files=(
apps/pidb-kanban-example/src/lib/prisma-idb/client/prisma-idb-client.ts
)
if [ -f packages/generator/src/fileCreators/prisma-idb-client/classes/PrismaIDBClient.ts ]; then
files+=(packages/generator/src/fileCreators/prisma-idb-client/classes/PrismaIDBClient.ts)
fi
rg -n -C2 'const IDB_VERSION =|CURRENT_VERSION|get\("_idb_meta"|put\("_idb_meta"|createObjectStore\("_idb_meta"' "${files[@]}"Repository: prisma-idb/idb-client-generator
Length of output: 5112
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for version-related constants and logic in the generator
rg -n 'IDB_VERSION|CURRENT_VERSION|version.*=|increment.*version' \
packages/generator/src/fileCreators/prisma-idb-client/ -A 2 -B 2 | head -100Repository: prisma-idb/idb-client-generator
Length of output: 3503
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check the full PrismaIDBClient.ts generator to understand version handling
wc -l packages/generator/src/fileCreators/prisma-idb-client/classes/PrismaIDBClient.tsRepository: prisma-idb/idb-client-generator
Length of output: 159
🏁 Script executed:
#!/bin/bash
set -eulo pipefail
# Search for where IDB_VERSION or version constants are written in the generator
rg -n 'writeLine.*VERSION|IDB_VERSION|const.*VERSION' \
packages/generator/src/fileCreators/prisma-idb-client/classes/PrismaIDBClient.tsRepository: prisma-idb/idb-client-generator
Length of output: 403
Fix IndexedDB schema upgrade path: bump version when introducing _idb_meta store.
The generated code adds _idb_meta store in the upgrade() callback (line 464) but uses the legacy path with hardcoded IDB_VERSION = 1 (line 19). Existing users with a v1 database will not trigger the upgrade callback, so _idb_meta is never created. The subsequent schema-drift check at line 57 (await client._db.get("_idb_meta", "schemaHash")) fails instead of detecting drift.
The generator has two code paths: a migration-based path using CURRENT_VERSION (when migration info is available) and a legacy path using hardcoded IDB_VERSION = 1. The generated example uses the legacy path. When adding new object stores, ensure the version is incremented in the generator's version declaration logic, or migrate to the migration-based upgrade callback in packages/generator/src/fileCreators/prisma-idb-client/classes/PrismaIDBClient.ts.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/pidb-kanban-example/src/lib/prisma-idb/client/prisma-idb-client.ts`
around lines 456 - 468, The IndexedDB upgrade callback adds the "_idb_meta"
store but the file still uses the legacy hardcoded IDB_VERSION (IDB_VERSION =
1), so existing DBs won't trigger upgrade; fix by bumping the IDB version
constant (IDB_VERSION) to a higher integer or switch the generated client to use
the migration-based CURRENT_VERSION path used by the generator, ensuring the
openDB(...) call's upgrade() runs and creates "_idb_meta" (referencing openDB,
upgrade, IDB_VERSION and the "_idb_meta" store) and update any schema-drift
check that reads client._db.get("_idb_meta", "schemaHash") to match the new
versioning approach.
| const storedHash = await client._db.get("_idb_meta", "schemaHash"); | ||
| if (storedHash !== undefined && storedHash !== IDB_SCHEMA_HASH) { | ||
| client._db.close(); | ||
| if (!DROP_DB_ON_SCHEMA_VERSION_MISMATCH) { | ||
| throw new Error( | ||
| `IDB schema mismatch: stored hash "${storedHash}" does not match expected "${IDB_SCHEMA_HASH}". Set dropDbOnSchemaVersionMismatch = true in your generator config to automatically reset the database.` | ||
| ); | ||
| } | ||
| await deleteDB("prisma-idb"); | ||
| return PrismaIDBClient.createClient(); | ||
| } | ||
| await client._db.put("_idb_meta", IDB_SCHEMA_HASH, "schemaHash"); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect generated clients/templates for DB versioning and meta-store upgrade handling.
fd 'prisma-idb-client\.ts$' . -t f | while read -r file; do
echo "=== $file ==="
rg -n -C2 'const IDB_VERSION|openDB<|upgrade\(|oldVersion|_idb_meta|schemaHash' "$file"
doneRepository: prisma-idb/idb-client-generator
Length of output: 2586
🏁 Script executed:
# Get the full blocking() callback implementation and singleton handling
sed -n '105,120p' apps/usage/src/lib/prisma-idb/client/prisma-idb-client.tsRepository: prisma-idb/idb-client-generator
Length of output: 841
🏁 Script executed:
# Check for generator templates that produce this file
fd -t f 'prisma-idb-client' packages/generator/src --type fRepository: prisma-idb/idb-client-generator
Length of output: 57
🏁 Script executed:
# Verify if this is indeed a generated file by checking for generation markers
head -20 apps/usage/src/lib/prisma-idb/client/prisma-idb-client.tsRepository: prisma-idb/idb-client-generator
Length of output: 881
🏁 Script executed:
# Search for the generator template that produces prisma-idb-client.ts
fd -t f 'prisma-idb-client' packages/generator/srcRepository: prisma-idb/idb-client-generator
Length of output: 57
🏁 Script executed:
# Look for the template file by pattern
find packages/generator/src -name '*prisma-idb-client*' -o -name '*client.ts*' | head -20Repository: prisma-idb/idb-client-generator
Length of output: 128
🏁 Script executed:
# Check the generator directory structure
tree -L 3 packages/generator/srcRepository: prisma-idb/idb-client-generator
Length of output: 1180
🏁 Script executed:
# Inspect the generator template for prisma-idb-client
cat -n packages/generator/src/fileCreators/prisma-idb-client/create.ts | head -150Repository: prisma-idb/idb-client-generator
Length of output: 5287
🏁 Script executed:
# Search for upgrade and blocking callback in the template
rg -n -A 5 -B 2 'upgrade\(|blocking\(' packages/generator/src/fileCreators/prisma-idb-client/Repository: prisma-idb/idb-client-generator
Length of output: 4279
🏁 Script executed:
# Check for any version bump logic or migration handling in the generator
rg -n 'IDB_VERSION|oldVersion' packages/generator/src/Repository: prisma-idb/idb-client-generator
Length of output: 686
🏁 Script executed:
# Check the complete initialization and singleton caching logic
sed -n '40,75p' apps/usage/src/lib/prisma-idb/client/prisma-idb-client.tsRepository: prisma-idb/idb-client-generator
Length of output: 1622
🏁 Script executed:
# Verify the learning about generated code and confirm the fix location
head -5 packages/generator/src/fileCreators/prisma-idb-client/classes/PrismaIDBClient.tsRepository: prisma-idb/idb-client-generator
Length of output: 358
Fix _idb_meta store initialization for existing databases and singleton caching after blocking() closes the connection.
Lines 56–67 access _idb_meta immediately after initialize(), but the store is only created in the upgrade() callback. Since IDB_VERSION remains 1, existing user databases won't re-run the upgrade and will lack the _idb_meta store, causing a missing-store error before the drift check executes.
Additionally, the singleton is cached (line 69) before blocking() fires. When another tab requests the database, blocking() closes the connection, leaving the cached instance in a closed state. Subsequent createClient() calls return a closed database.
Both issues originate in the generator template at packages/generator/src/fileCreators/prisma-idb-client/classes/PrismaIDBClient.ts and must be fixed there:
- Add a version bump and migration to safely create
_idb_metain existing databases. - Detect and invalidate the singleton when
blocking()closes the database, or defer singleton caching until after the drift check andblocking()setup is complete.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/usage/src/lib/prisma-idb/client/prisma-idb-client.ts` around lines 56 -
67, The generated PrismaIDB client currently reads from the "_idb_meta" store
immediately after initialize() and caches the singleton before blocking() can
close the connection, causing missing-store and closed-instance bugs; update the
generator template (PrismaIDBClient.ts) to (1) bump IDB_VERSION and add an
upgrade migration that ensures the "_idb_meta" object store is created on
existing databases so the stored schemaHash lookup succeeds, and (2) move
singleton caching to occur only after the drift check and after setting up
blocking(), or detect when client._db.close() runs inside blocking() and
invalidate the cached singleton (e.g., clear the cached PrismaIDBClient) so
subsequent PrismaIDBClient.createClient() returns a fresh, open connection;
ensure the logic still respects DROP_DB_ON_SCHEMA_VERSION_MISMATCH and the
IDB_SCHEMA_HASH comparison.
| blocking() { | ||
| db.close(); | ||
| }, | ||
| }); | ||
| this._db = db; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether any close/blocking path clears the cached singleton.
fd 'prisma-idb-client\.ts$' . -t f | while read -r file; do
echo "=== $file ==="
rg -n -C3 'private static instance|createClient\(|blocking\(|close\(|instance\s*=' "$file"
doneRepository: prisma-idb/idb-client-generator
Length of output: 3804
Clear the cached singleton instance when blocking() closes the database.
The blocking() callback closes the underlying connection but doesn't clear the static instance variable. The next createClient() call returns the same stale PrismaIDBClient with a closed _db, causing operations to fail.
Suggested fix
- private static instance: PrismaIDBClient;
+ private static instance: PrismaIDBClient | undefined;
...
blocking() {
db.close();
+ if (PrismaIDBClient.instance?._db === db) {
+ PrismaIDBClient.instance = undefined;
+ }
},🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/usage/src/lib/prisma-idb/client/prisma-idb-client.ts` around lines 105 -
109, When the blocking() callback closes the underlying connection it must also
clear the cached singleton so future createClient() calls don't return a stale
PrismaIDBClient with a closed _db; update the blocking() implementation on the
PrismaIDBClient to set the static instance (e.g., PrismaIDBClient.instance or
whatever singleton variable is used) to undefined/null after calling db.close(),
ensuring subsequent createClient() constructs a fresh client with a valid _db.
| if (fs.existsSync(snapshotJsonPath)) { | ||
| const json = fs.readFileSync(snapshotJsonPath, "utf-8"); | ||
| snapshots.push(JSON.parse(json) as Snapshot); | ||
| } else { | ||
| // No existing snapshot — use current DMMF with this version number | ||
| snapshots.push({ ...currentSnapshot, version: i + 1 }); | ||
| } |
There was a problem hiding this comment.
Add error handling for corrupted snapshot files.
If snapshot.json exists but contains invalid JSON, JSON.parse will throw an unhelpful error. Consider wrapping with try-catch to provide actionable feedback.
🛡️ Suggested fix
if (fs.existsSync(snapshotJsonPath)) {
const json = fs.readFileSync(snapshotJsonPath, "utf-8");
- snapshots.push(JSON.parse(json) as Snapshot);
+ try {
+ snapshots.push(JSON.parse(json) as Snapshot);
+ } catch (e) {
+ throw new Error(
+ `Failed to parse snapshot.json for migration "${folderName}": ${e instanceof Error ? e.message : e}`
+ );
+ }
} else {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (fs.existsSync(snapshotJsonPath)) { | |
| const json = fs.readFileSync(snapshotJsonPath, "utf-8"); | |
| snapshots.push(JSON.parse(json) as Snapshot); | |
| } else { | |
| // No existing snapshot — use current DMMF with this version number | |
| snapshots.push({ ...currentSnapshot, version: i + 1 }); | |
| } | |
| if (fs.existsSync(snapshotJsonPath)) { | |
| const json = fs.readFileSync(snapshotJsonPath, "utf-8"); | |
| try { | |
| snapshots.push(JSON.parse(json) as Snapshot); | |
| } catch (e) { | |
| throw new Error( | |
| `Failed to parse snapshot.json for migration "${folderName}": ${e instanceof Error ? e.message : e}` | |
| ); | |
| } | |
| } else { | |
| // No existing snapshot — use current DMMF with this version number | |
| snapshots.push({ ...currentSnapshot, version: i + 1 }); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/generator/src/fileCreators/migrations/create.ts` around lines 58 -
64, Wrap the existing read/parse logic that reads snapshotJsonPath and pushes
into snapshots in a try-catch around fs.readFileSync(...) and JSON.parse(...);
on JSON.parse failure catch the error, throw or surface a new error that
includes snapshotJsonPath and the original error message (or log it) so callers
see "corrupted snapshot.json at {snapshotJsonPath}: {originalError}" and avoid
an unhelpful stacktrace; keep the fallback branch that pushes {
...currentSnapshot, version: i + 1 } unchanged for the non-existent-file case.
Description
Handle migrations
Fixes #21
Type of change
How Has This Been Tested?
tests/schema-gen/tests/migration/migrations.test.tsSummary by CodeRabbit
Release Notes
New Features
Chores