@firtoz/drizzle-indexeddb 4.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +9 -1
- package/dist/bin/generate-migrations.d.ts +15 -0
- package/dist/bin/generate-migrations.js +184 -0
- package/dist/bin/generate-migrations.js.map +1 -0
- package/dist/chunk-5KCMKETG.js +212 -0
- package/dist/chunk-5KCMKETG.js.map +1 -0
- package/dist/chunk-7X4EIKN4.js +128 -0
- package/dist/chunk-7X4EIKN4.js.map +1 -0
- package/dist/chunk-CPLA7X66.js +129 -0
- package/dist/chunk-CPLA7X66.js.map +1 -0
- package/dist/chunk-HJFI7QKW.js +28 -0
- package/dist/chunk-HJFI7QKW.js.map +1 -0
- package/dist/chunk-JVUF63L6.js +27 -0
- package/dist/chunk-JVUF63L6.js.map +1 -0
- package/dist/chunk-OSOLYU2M.js +126 -0
- package/dist/chunk-OSOLYU2M.js.map +1 -0
- package/dist/chunk-WMCUJFEC.js +311 -0
- package/dist/chunk-WMCUJFEC.js.map +1 -0
- package/dist/chunk-Y6XE3FVT.js +147 -0
- package/dist/chunk-Y6XE3FVT.js.map +1 -0
- package/dist/collections/drizzle-indexeddb-collection.d.ts +62 -0
- package/dist/collections/drizzle-indexeddb-collection.js +3 -0
- package/dist/collections/drizzle-indexeddb-collection.js.map +1 -0
- package/dist/context/DrizzleIndexedDBProvider.d.ts +35 -0
- package/dist/context/DrizzleIndexedDBProvider.js +7 -0
- package/dist/context/DrizzleIndexedDBProvider.js.map +1 -0
- package/dist/context/useDrizzleIndexedDB.d.ts +17 -0
- package/dist/context/useDrizzleIndexedDB.js +8 -0
- package/dist/context/useDrizzleIndexedDB.js.map +1 -0
- package/dist/function-migrator.d.ts +56 -0
- package/dist/function-migrator.js +5 -0
- package/dist/function-migrator.js.map +1 -0
- package/dist/idb-interceptor.d.ts +68 -0
- package/dist/idb-interceptor.js +3 -0
- package/dist/idb-interceptor.js.map +1 -0
- package/dist/idb-operations.d.ts +13 -0
- package/dist/idb-operations.js +4 -0
- package/dist/idb-operations.js.map +1 -0
- package/dist/idb-types.d.ts +82 -0
- package/dist/idb-types.js +3 -0
- package/dist/idb-types.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/instrumented-idb-database.d.ts +17 -0
- package/dist/instrumented-idb-database.js +4 -0
- package/dist/instrumented-idb-database.js.map +1 -0
- package/dist/native-idb-database.d.ts +9 -0
- package/dist/native-idb-database.js +3 -0
- package/dist/native-idb-database.js.map +1 -0
- package/dist/standalone-collection.d.ts +154 -0
- package/dist/standalone-collection.js +7 -0
- package/dist/standalone-collection.js.map +1 -0
- package/package.json +31 -26
- package/src/collections/drizzle-indexeddb-collection.ts +4 -4
- package/src/context/DrizzleIndexedDBProvider.tsx +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @firtoz/drizzle-indexeddb
|
|
2
2
|
|
|
3
|
+
## 5.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`c3a3cc7`](https://github.com/firtoz/fullstack-toolkit/commit/c3a3cc778ba9ce4b5efe1bcdd8d541f46dec3bfd) Thanks [@firtoz](https://github.com/firtoz)! - Publish compiled ESM and TypeScript declarations from `dist/` for each package (`tsup`, `prepack` build). `exports`, `main`, and `types` resolve to `dist/`; CLI bins point at built JS with a Node shebang where applicable. Shared `scripts/tsup-lib.ts` discovers `src` entries and externals; workspace test apps map `@firtoz/*` to package sources in `tsconfig` for accurate generics during typecheck. `@firtoz/socka` is published under the scoped name with the same `dist/` layout.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- [`f78c988`](https://github.com/firtoz/fullstack-toolkit/commit/f78c988d37a9cc48490ee3372dccc14a42810bfe) Thanks [@firtoz](https://github.com/firtoz)! - Improve READMEs: npm shields, contextual stack badges, clearer taglines, and new docs for `@firtoz/idb-collections` and `@firtoz/collection-sync`. Align `@firtoz/db-helpers` copy with published `dist/` builds.
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [[`c3a3cc7`](https://github.com/firtoz/fullstack-toolkit/commit/c3a3cc778ba9ce4b5efe1bcdd8d541f46dec3bfd), [`f78c988`](https://github.com/firtoz/fullstack-toolkit/commit/f78c988d37a9cc48490ee3372dccc14a42810bfe)]:
|
|
14
|
+
- @firtoz/db-helpers@2.2.0
|
|
15
|
+
- @firtoz/drizzle-utils@1.3.0
|
|
16
|
+
- @firtoz/idb-collections@0.3.0
|
|
17
|
+
- @firtoz/maybe-error@1.6.0
|
|
18
|
+
|
|
19
|
+
## 4.0.1
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- [#70](https://github.com/firtoz/fullstack-toolkit/pull/70) [`138c394`](https://github.com/firtoz/fullstack-toolkit/commit/138c3944b491ebf2e76b7f2c00d651fd5d788bac) Thanks [@firtoz](https://github.com/firtoz)! - Raise TanStack DB peer range to `>=0.6.3` where applicable. `createGenericCollectionConfig` now sets `defaultIndexType: BasicIndex` and `autoIndex: "eager"` so Drizzle-backed collections match pre-0.6 indexing defaults for `orderBy`/`limit` live queries. Re-enable `DeduplicatedLoadSubset` (`USE_DEDUPE`) with `@tanstack/db` 0.6.4.
|
|
24
|
+
|
|
25
|
+
- Updated dependencies [[`138c394`](https://github.com/firtoz/fullstack-toolkit/commit/138c3944b491ebf2e76b7f2c00d651fd5d788bac)]:
|
|
26
|
+
- @firtoz/db-helpers@2.1.1
|
|
27
|
+
- @firtoz/drizzle-utils@1.2.1
|
|
28
|
+
- @firtoz/idb-collections@0.2.3
|
|
29
|
+
|
|
3
30
|
## 4.0.0
|
|
4
31
|
|
|
5
32
|
### Major Changes
|
package/README.md
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# @firtoz/drizzle-indexeddb
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@firtoz/drizzle-indexeddb)
|
|
4
|
+
[](https://www.npmjs.com/package/@firtoz/drizzle-indexeddb)
|
|
5
|
+
[](https://github.com/firtoz/fullstack-toolkit/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://orm.drizzle.team/)
|
|
9
|
+
[](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)
|
|
10
|
+
|
|
11
|
+
**Drizzle-shaped schemas and migrations on top of IndexedDB** — TanStack DB collections in the browser with React hooks and generated migration functions (SQLite-flavored Drizzle types today).
|
|
4
12
|
|
|
5
13
|
> **⚠️ Early WIP Notice:** This package is in very early development and is **not production-ready**. It is TypeScript-only and may have breaking changes. While I (the maintainer) have limited time, I'm open to PRs for features, bug fixes, or additional support (like JS builds). Please feel free to try it out and contribute! See [CONTRIBUTING.md](../../CONTRIBUTING.md) for details.
|
|
6
14
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI tool to generate IndexedDB migrations from Drizzle schema snapshots
|
|
4
|
+
* Run after `drizzle-kit generate` to create executable migration files
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* bun drizzle-indexeddb-generate
|
|
8
|
+
*/
|
|
9
|
+
interface GenerateOptions {
|
|
10
|
+
drizzleDir?: string;
|
|
11
|
+
outputDir?: string;
|
|
12
|
+
}
|
|
13
|
+
declare function generateIndexedDBMigrations(options?: GenerateOptions): void;
|
|
14
|
+
|
|
15
|
+
export { generateIndexedDBMigrations };
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
|
|
3
|
+
import { resolve, join } from 'path';
|
|
4
|
+
import { defineCommand, runMain } from 'citty';
|
|
5
|
+
|
|
6
|
+
function generateMigration(_entry, snapshot, prevSnapshot) {
|
|
7
|
+
const operations = [];
|
|
8
|
+
const currentTables = snapshot.tables || {};
|
|
9
|
+
const previousTables = prevSnapshot?.tables || {};
|
|
10
|
+
for (const [tableName, tableDef] of Object.entries(currentTables)) {
|
|
11
|
+
if (!previousTables[tableName]) {
|
|
12
|
+
const pkColumn = Object.values(
|
|
13
|
+
tableDef.columns
|
|
14
|
+
).find((col) => col.primaryKey);
|
|
15
|
+
const indexes = [];
|
|
16
|
+
for (const [indexName, indexDef] of Object.entries(
|
|
17
|
+
tableDef.indexes || {}
|
|
18
|
+
)) {
|
|
19
|
+
indexes.push({
|
|
20
|
+
name: indexName,
|
|
21
|
+
keyPath: indexDef.columns.length === 1 ? indexDef.columns[0] : indexDef.columns,
|
|
22
|
+
unique: indexDef.isUnique
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
operations.push({
|
|
26
|
+
type: "createTable",
|
|
27
|
+
name: tableName,
|
|
28
|
+
keyPath: pkColumn?.name,
|
|
29
|
+
autoIncrement: pkColumn?.autoincrement ?? false,
|
|
30
|
+
indexes: indexes.length > 0 ? indexes : void 0
|
|
31
|
+
});
|
|
32
|
+
} else {
|
|
33
|
+
const prevTableDef = previousTables[tableName];
|
|
34
|
+
const newIndexes = tableDef.indexes || {};
|
|
35
|
+
const oldIndexes = prevTableDef.indexes || {};
|
|
36
|
+
for (const [indexName, indexDef] of Object.entries(newIndexes)) {
|
|
37
|
+
if (!oldIndexes[indexName]) {
|
|
38
|
+
operations.push({
|
|
39
|
+
type: "createIndex",
|
|
40
|
+
tableName,
|
|
41
|
+
indexName,
|
|
42
|
+
keyPath: indexDef.columns.length === 1 ? indexDef.columns[0] : indexDef.columns,
|
|
43
|
+
unique: indexDef.isUnique
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
for (const indexName of Object.keys(oldIndexes)) {
|
|
48
|
+
if (!newIndexes[indexName]) {
|
|
49
|
+
operations.push({
|
|
50
|
+
type: "deleteIndex",
|
|
51
|
+
tableName,
|
|
52
|
+
indexName
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
for (const tableName of Object.keys(previousTables)) {
|
|
59
|
+
if (!currentTables[tableName]) {
|
|
60
|
+
operations.push({
|
|
61
|
+
type: "deleteTable",
|
|
62
|
+
name: tableName
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return operations;
|
|
67
|
+
}
|
|
68
|
+
function migrationToCode(migration, idx, tag) {
|
|
69
|
+
const migrationName = tag.replace(/^\d+_/, "").replace(/_/g, " ");
|
|
70
|
+
const lines = [];
|
|
71
|
+
lines.push(
|
|
72
|
+
`import type { Migration } from "@firtoz/drizzle-indexeddb";`,
|
|
73
|
+
``,
|
|
74
|
+
`/**`,
|
|
75
|
+
` * Migration: ${migrationName}`,
|
|
76
|
+
` * Generated from: ${tag}`,
|
|
77
|
+
` */`,
|
|
78
|
+
`export const migrate_${idx.toString().padStart(4, "0")}: Migration = ${JSON.stringify(migration, null, " ")};`
|
|
79
|
+
);
|
|
80
|
+
return lines.join("\n");
|
|
81
|
+
}
|
|
82
|
+
function generateIndexedDBMigrations(options = {}) {
|
|
83
|
+
const cwd = process.cwd();
|
|
84
|
+
const drizzleDir = resolve(cwd, options.drizzleDir || "./drizzle");
|
|
85
|
+
const metaDir = join(drizzleDir, "meta");
|
|
86
|
+
const journalPath = join(metaDir, "_journal.json");
|
|
87
|
+
const outputDir = resolve(
|
|
88
|
+
cwd,
|
|
89
|
+
options.outputDir || join(drizzleDir, "indexeddb-migrations")
|
|
90
|
+
);
|
|
91
|
+
const startTime = performance.now();
|
|
92
|
+
console.log(`[drizzle-indexeddb] Starting migration generation...`);
|
|
93
|
+
if (!existsSync(journalPath)) {
|
|
94
|
+
console.error(
|
|
95
|
+
`[drizzle-indexeddb] Error: Journal not found at ${journalPath}`
|
|
96
|
+
);
|
|
97
|
+
console.error(
|
|
98
|
+
`[drizzle-indexeddb] Make sure to run 'drizzle-kit generate' first`
|
|
99
|
+
);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
const journalContent = readFileSync(journalPath, "utf-8");
|
|
103
|
+
const journal = JSON.parse(journalContent);
|
|
104
|
+
console.log(`[drizzle-indexeddb] Found ${journal.entries.length} migrations`);
|
|
105
|
+
if (!existsSync(outputDir)) {
|
|
106
|
+
mkdirSync(outputDir, { recursive: true });
|
|
107
|
+
console.log(`[drizzle-indexeddb] Created output directory: ${outputDir}`);
|
|
108
|
+
}
|
|
109
|
+
const migrationImports = [];
|
|
110
|
+
const migrationNames = [];
|
|
111
|
+
const snapshots = [];
|
|
112
|
+
for (const entry of journal.entries) {
|
|
113
|
+
const fileName = `${entry.idx.toString().padStart(4, "0")}_snapshot.json`;
|
|
114
|
+
const snapshotPath = join(metaDir, fileName);
|
|
115
|
+
if (!existsSync(snapshotPath)) {
|
|
116
|
+
console.error(
|
|
117
|
+
`[drizzle-indexeddb] Error: Snapshot not found at ${snapshotPath}`
|
|
118
|
+
);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
const snapshotContent = readFileSync(snapshotPath, "utf-8");
|
|
122
|
+
const snapshot = JSON.parse(snapshotContent);
|
|
123
|
+
snapshots.push(snapshot);
|
|
124
|
+
const prevSnapshot = entry.idx > 0 ? snapshots[entry.idx - 1] : null;
|
|
125
|
+
const migration = generateMigration(entry, snapshot, prevSnapshot);
|
|
126
|
+
const migrationCode = migrationToCode(migration, entry.idx, entry.tag);
|
|
127
|
+
const migrationFileName = `${entry.tag}.ts`;
|
|
128
|
+
const migrationPath = join(outputDir, migrationFileName);
|
|
129
|
+
writeFileSync(migrationPath, migrationCode, "utf-8");
|
|
130
|
+
const migrationName = `migrate_${entry.idx.toString().padStart(4, "0")}`;
|
|
131
|
+
migrationImports.push(`import { ${migrationName} } from './${entry.tag}';`);
|
|
132
|
+
migrationNames.push(migrationName);
|
|
133
|
+
}
|
|
134
|
+
const indexContent = `import type { Migration } from "@firtoz/drizzle-indexeddb";
|
|
135
|
+
|
|
136
|
+
${migrationImports.join("\n")}
|
|
137
|
+
|
|
138
|
+
export const migrations: Migration[] = [
|
|
139
|
+
${migrationNames.join(",\n ")}
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
export default migrations;
|
|
143
|
+
`;
|
|
144
|
+
writeFileSync(join(outputDir, "index.ts"), indexContent, "utf-8");
|
|
145
|
+
console.log(`[drizzle-indexeddb] \u2713 Generated ${join(outputDir, "index.ts")}`);
|
|
146
|
+
const endTime = performance.now();
|
|
147
|
+
const totalTime = endTime - startTime;
|
|
148
|
+
console.log(`[drizzle-indexeddb] Migrations: ${migrationNames.join(", ")}`);
|
|
149
|
+
console.log(
|
|
150
|
+
`[drizzle-indexeddb] \u2713 Complete! Generated ${journal.entries.length} migrations in ${totalTime.toFixed(2)}ms`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
var main = defineCommand({
|
|
154
|
+
meta: {
|
|
155
|
+
name: "drizzle-indexeddb-generate",
|
|
156
|
+
description: "Generate IndexedDB migrations from Drizzle schema"
|
|
157
|
+
},
|
|
158
|
+
args: {
|
|
159
|
+
drizzleDir: {
|
|
160
|
+
type: "string",
|
|
161
|
+
alias: "d",
|
|
162
|
+
default: "./drizzle",
|
|
163
|
+
description: "Path to Drizzle directory"
|
|
164
|
+
},
|
|
165
|
+
outputDir: {
|
|
166
|
+
type: "string",
|
|
167
|
+
alias: "o",
|
|
168
|
+
description: "Path to output directory (default: <drizzle-dir>/indexeddb-migrations)"
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
run({ args }) {
|
|
172
|
+
generateIndexedDBMigrations({
|
|
173
|
+
drizzleDir: args.drizzleDir,
|
|
174
|
+
outputDir: args.outputDir
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
if (import.meta.main) {
|
|
179
|
+
runMain(main);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export { generateIndexedDBMigrations };
|
|
183
|
+
//# sourceMappingURL=generate-migrations.js.map
|
|
184
|
+
//# sourceMappingURL=generate-migrations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/bin/generate-migrations.ts"],"names":[],"mappings":"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","file":"generate-migrations.js","sourcesContent":["#!/usr/bin/env node\n/**\n * CLI tool to generate IndexedDB migrations from Drizzle schema snapshots\n * Run after `drizzle-kit generate` to create executable migration files\n *\n * Usage:\n * bun drizzle-indexeddb-generate\n */\n\nimport { readFileSync, writeFileSync, mkdirSync, existsSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { defineCommand, runMain } from \"citty\";\nimport type {\n\tJournalEntry,\n\tJournal,\n\tSnapshot,\n\tTableDefinition,\n\tColumnDefinition,\n\tIndexDefinition,\n} from \"@firtoz/drizzle-utils\";\nimport type { Migration, MigrationOperation } from \"../function-migrator\";\n\ninterface GenerateOptions {\n\tdrizzleDir?: string;\n\toutputDir?: string;\n}\n\nfunction generateMigration(\n\t_entry: JournalEntry,\n\tsnapshot: Snapshot,\n\tprevSnapshot: Snapshot | null,\n): Migration {\n\tconst operations: MigrationOperation[] = [];\n\n\tconst currentTables: Record<string, TableDefinition> = snapshot.tables || {};\n\tconst previousTables: Record<string, TableDefinition> =\n\t\tprevSnapshot?.tables || {};\n\n\t// Find new tables\n\tfor (const [tableName, tableDef] of Object.entries(currentTables)) {\n\t\tif (!previousTables[tableName]) {\n\t\t\t// Find primary key\n\t\t\tconst pkColumn = Object.values(\n\t\t\t\ttableDef.columns as Record<string, ColumnDefinition>,\n\t\t\t).find((col) => col.primaryKey);\n\n\t\t\tconst indexes: Array<{\n\t\t\t\tname: string;\n\t\t\t\tkeyPath: string | string[];\n\t\t\t\tunique?: boolean;\n\t\t\t}> = [];\n\n\t\t\t// Collect indexes\n\t\t\tfor (const [indexName, indexDef] of Object.entries(\n\t\t\t\ttableDef.indexes || {},\n\t\t\t)) {\n\t\t\t\tindexes.push({\n\t\t\t\t\tname: indexName,\n\t\t\t\t\tkeyPath:\n\t\t\t\t\t\tindexDef.columns.length === 1\n\t\t\t\t\t\t\t? indexDef.columns[0]\n\t\t\t\t\t\t\t: indexDef.columns,\n\t\t\t\t\tunique: indexDef.isUnique,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\toperations.push({\n\t\t\t\ttype: \"createTable\",\n\t\t\t\tname: tableName,\n\t\t\t\tkeyPath: pkColumn?.name,\n\t\t\t\tautoIncrement: pkColumn?.autoincrement ?? false,\n\t\t\t\tindexes: indexes.length > 0 ? indexes : undefined,\n\t\t\t});\n\t\t} else {\n\t\t\t// Table exists, check for index changes\n\t\t\tconst prevTableDef: TableDefinition = previousTables[tableName];\n\t\t\tconst newIndexes: Record<string, IndexDefinition> =\n\t\t\t\ttableDef.indexes || {};\n\t\t\tconst oldIndexes: Record<string, IndexDefinition> =\n\t\t\t\tprevTableDef.indexes || {};\n\n\t\t\t// Add new indexes\n\t\t\tfor (const [indexName, indexDef] of Object.entries(newIndexes)) {\n\t\t\t\tif (!oldIndexes[indexName]) {\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: \"createIndex\",\n\t\t\t\t\t\ttableName,\n\t\t\t\t\t\tindexName,\n\t\t\t\t\t\tkeyPath:\n\t\t\t\t\t\t\tindexDef.columns.length === 1\n\t\t\t\t\t\t\t\t? indexDef.columns[0]\n\t\t\t\t\t\t\t\t: indexDef.columns,\n\t\t\t\t\t\tunique: indexDef.isUnique,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Delete removed indexes\n\t\t\tfor (const indexName of Object.keys(oldIndexes)) {\n\t\t\t\tif (!newIndexes[indexName]) {\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: \"deleteIndex\",\n\t\t\t\t\t\ttableName,\n\t\t\t\t\t\tindexName,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Find deleted tables\n\tfor (const tableName of Object.keys(previousTables)) {\n\t\tif (!currentTables[tableName]) {\n\t\t\toperations.push({\n\t\t\t\ttype: \"deleteTable\",\n\t\t\t\tname: tableName,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn operations;\n}\n\nfunction migrationToCode(\n\tmigration: Migration,\n\tidx: number,\n\ttag: string,\n): string {\n\tconst migrationName = tag.replace(/^\\d+_/, \"\").replace(/_/g, \" \");\n\tconst lines: string[] = [];\n\n\tlines.push(\n\t\t`import type { Migration } from \"@firtoz/drizzle-indexeddb\";`,\n\t\t``,\n\t\t`/**`,\n\t\t` * Migration: ${migrationName}`,\n\t\t` * Generated from: ${tag}`,\n\t\t` */`,\n\t\t`export const migrate_${idx.toString().padStart(4, \"0\")}: Migration = ${JSON.stringify(migration, null, \"\\t\")};`,\n\t);\n\n\treturn lines.join(\"\\n\");\n}\n\nexport function generateIndexedDBMigrations(\n\toptions: GenerateOptions = {},\n): void {\n\tconst cwd = process.cwd();\n\tconst drizzleDir = resolve(cwd, options.drizzleDir || \"./drizzle\");\n\tconst metaDir = join(drizzleDir, \"meta\");\n\tconst journalPath = join(metaDir, \"_journal.json\");\n\tconst outputDir = resolve(\n\t\tcwd,\n\t\toptions.outputDir || join(drizzleDir, \"indexeddb-migrations\"),\n\t);\n\n\tconst startTime = performance.now();\n\tconsole.log(`[drizzle-indexeddb] Starting migration generation...`);\n\n\t// Read the journal\n\tif (!existsSync(journalPath)) {\n\t\tconsole.error(\n\t\t\t`[drizzle-indexeddb] Error: Journal not found at ${journalPath}`,\n\t\t);\n\t\tconsole.error(\n\t\t\t`[drizzle-indexeddb] Make sure to run 'drizzle-kit generate' first`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\tconst journalContent = readFileSync(journalPath, \"utf-8\");\n\tconst journal: Journal = JSON.parse(journalContent);\n\n\tconsole.log(`[drizzle-indexeddb] Found ${journal.entries.length} migrations`);\n\n\t// Create output directory\n\tif (!existsSync(outputDir)) {\n\t\tmkdirSync(outputDir, { recursive: true });\n\t\tconsole.log(`[drizzle-indexeddb] Created output directory: ${outputDir}`);\n\t}\n\n\tconst migrationImports: string[] = [];\n\tconst migrationNames: string[] = [];\n\n\t// Load all snapshots and generate migrations\n\tconst snapshots: Snapshot[] = [];\n\n\tfor (const entry of journal.entries) {\n\t\tconst fileName = `${entry.idx.toString().padStart(4, \"0\")}_snapshot.json`;\n\t\tconst snapshotPath = join(metaDir, fileName);\n\n\t\t// Load snapshot\n\t\tif (!existsSync(snapshotPath)) {\n\t\t\tconsole.error(\n\t\t\t\t`[drizzle-indexeddb] Error: Snapshot not found at ${snapshotPath}`,\n\t\t\t);\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\tconst snapshotContent = readFileSync(snapshotPath, \"utf-8\");\n\t\tconst snapshot: Snapshot = JSON.parse(snapshotContent);\n\t\tsnapshots.push(snapshot);\n\n\t\t// Generate migration\n\t\tconst prevSnapshot = entry.idx > 0 ? snapshots[entry.idx - 1] : null;\n\t\tconst migration = generateMigration(entry, snapshot, prevSnapshot);\n\t\tconst migrationCode = migrationToCode(migration, entry.idx, entry.tag);\n\n\t\tconst migrationFileName = `${entry.tag}.ts`;\n\t\tconst migrationPath = join(outputDir, migrationFileName);\n\t\twriteFileSync(migrationPath, migrationCode, \"utf-8\");\n\n\t\t// Add to index imports\n\t\tconst migrationName = `migrate_${entry.idx.toString().padStart(4, \"0\")}`;\n\t\tmigrationImports.push(`import { ${migrationName} } from './${entry.tag}';`);\n\t\tmigrationNames.push(migrationName);\n\t}\n\n\t// Generate index.ts for migrations\n\tconst indexContent = `import type { Migration } from \"@firtoz/drizzle-indexeddb\";\n\n${migrationImports.join(\"\\n\")}\n\nexport const migrations: Migration[] = [\n\\t${migrationNames.join(\",\\n\\t\")}\n];\n\nexport default migrations;\n`;\n\n\twriteFileSync(join(outputDir, \"index.ts\"), indexContent, \"utf-8\");\n\tconsole.log(`[drizzle-indexeddb] ✓ Generated ${join(outputDir, \"index.ts\")}`);\n\n\tconst endTime = performance.now();\n\tconst totalTime = endTime - startTime;\n\n\tconsole.log(`[drizzle-indexeddb] Migrations: ${migrationNames.join(\", \")}`);\n\tconsole.log(\n\t\t`[drizzle-indexeddb] ✓ Complete! Generated ${journal.entries.length} migrations in ${totalTime.toFixed(2)}ms`,\n\t);\n}\n\n// CLI entry point\nconst main = defineCommand({\n\tmeta: {\n\t\tname: \"drizzle-indexeddb-generate\",\n\t\tdescription: \"Generate IndexedDB migrations from Drizzle schema\",\n\t},\n\targs: {\n\t\tdrizzleDir: {\n\t\t\ttype: \"string\",\n\t\t\talias: \"d\",\n\t\t\tdefault: \"./drizzle\",\n\t\t\tdescription: \"Path to Drizzle directory\",\n\t\t},\n\t\toutputDir: {\n\t\t\ttype: \"string\",\n\t\t\talias: \"o\",\n\t\t\tdescription:\n\t\t\t\t\"Path to output directory (default: <drizzle-dir>/indexeddb-migrations)\",\n\t\t},\n\t},\n\trun({ args }) {\n\t\tgenerateIndexedDBMigrations({\n\t\t\tdrizzleDir: args.drizzleDir,\n\t\t\toutputDir: args.outputDir,\n\t\t});\n\t},\n});\n\n// Only run CLI when executed directly (not when imported)\nif (import.meta.main) {\n\trunMain(main);\n}\n"]}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { parseOrderByExpression } from '@tanstack/db';
|
|
2
|
+
import { createSyncFunction, createInsertSchemaWithDefaults, createCollectionConfig } from '@firtoz/drizzle-utils';
|
|
3
|
+
import { evaluateExpression } from '@firtoz/db-helpers';
|
|
4
|
+
import { tryExtractIndexedQuery } from '@firtoz/idb-collections';
|
|
5
|
+
|
|
6
|
+
// src/collections/drizzle-indexeddb-collection.ts
|
|
7
|
+
function discoverIndexes(db, storeName) {
|
|
8
|
+
const indexes = db.getStoreIndexes(storeName);
|
|
9
|
+
const indexMap = {};
|
|
10
|
+
for (const index of indexes) {
|
|
11
|
+
if (typeof index.keyPath === "string") {
|
|
12
|
+
indexMap[index.keyPath] = index.name;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return indexMap;
|
|
16
|
+
}
|
|
17
|
+
function drizzleIndexedDBCollectionOptions(config) {
|
|
18
|
+
let discoveredIndexes = {};
|
|
19
|
+
let indexesDiscovered = false;
|
|
20
|
+
const table = config.table;
|
|
21
|
+
const discoverIndexesOnce = async () => {
|
|
22
|
+
await config.readyPromise;
|
|
23
|
+
const db = config.indexedDBRef.current;
|
|
24
|
+
if (!db) {
|
|
25
|
+
throw new Error("Database not ready");
|
|
26
|
+
}
|
|
27
|
+
if (!indexesDiscovered) {
|
|
28
|
+
discoveredIndexes = discoverIndexes(db, config.storeName);
|
|
29
|
+
indexesDiscovered = true;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const backend = {
|
|
33
|
+
initialLoad: async () => {
|
|
34
|
+
const db = config.indexedDBRef.current;
|
|
35
|
+
if (!db) {
|
|
36
|
+
throw new Error("Database not ready");
|
|
37
|
+
}
|
|
38
|
+
await discoverIndexesOnce();
|
|
39
|
+
const items = await db.getAll(config.storeName);
|
|
40
|
+
return items;
|
|
41
|
+
},
|
|
42
|
+
loadSubset: async (options) => {
|
|
43
|
+
const db = config.indexedDBRef.current;
|
|
44
|
+
if (!db) {
|
|
45
|
+
throw new Error("Database not ready");
|
|
46
|
+
}
|
|
47
|
+
if (!indexesDiscovered) {
|
|
48
|
+
discoveredIndexes = discoverIndexes(db, config.storeName);
|
|
49
|
+
indexesDiscovered = true;
|
|
50
|
+
}
|
|
51
|
+
let items;
|
|
52
|
+
let combinedWhere = options.where;
|
|
53
|
+
if (options.cursor?.whereFrom) {
|
|
54
|
+
if (combinedWhere) {
|
|
55
|
+
combinedWhere = {
|
|
56
|
+
type: "func",
|
|
57
|
+
name: "and",
|
|
58
|
+
args: [combinedWhere, options.cursor.whereFrom]
|
|
59
|
+
};
|
|
60
|
+
} else {
|
|
61
|
+
combinedWhere = options.cursor.whereFrom;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const indexedQuery = combinedWhere ? tryExtractIndexedQuery(combinedWhere, discoveredIndexes, config.debug) : null;
|
|
65
|
+
if (indexedQuery) {
|
|
66
|
+
items = await db.getAllByIndex(
|
|
67
|
+
config.storeName,
|
|
68
|
+
indexedQuery.indexName,
|
|
69
|
+
indexedQuery.keyRange
|
|
70
|
+
);
|
|
71
|
+
} else {
|
|
72
|
+
items = await db.getAll(config.storeName);
|
|
73
|
+
if (combinedWhere) {
|
|
74
|
+
const whereExpression = combinedWhere;
|
|
75
|
+
items = items.filter(
|
|
76
|
+
(item) => evaluateExpression(
|
|
77
|
+
whereExpression,
|
|
78
|
+
item
|
|
79
|
+
)
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (options.orderBy) {
|
|
84
|
+
const sorts = parseOrderByExpression(options.orderBy);
|
|
85
|
+
items.sort((a, b) => {
|
|
86
|
+
for (const sort of sorts) {
|
|
87
|
+
let aValue = a;
|
|
88
|
+
let bValue = b;
|
|
89
|
+
for (const fieldName of sort.field) {
|
|
90
|
+
aValue = aValue?.[fieldName];
|
|
91
|
+
bValue = bValue?.[fieldName];
|
|
92
|
+
}
|
|
93
|
+
if (aValue < bValue) {
|
|
94
|
+
return sort.direction === "asc" ? -1 : 1;
|
|
95
|
+
}
|
|
96
|
+
if (aValue > bValue) {
|
|
97
|
+
return sort.direction === "asc" ? 1 : -1;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return 0;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (options.offset !== void 0 && options.offset > 0) {
|
|
104
|
+
items = items.slice(options.offset);
|
|
105
|
+
}
|
|
106
|
+
if (options.limit !== void 0) {
|
|
107
|
+
items = items.slice(0, options.limit);
|
|
108
|
+
}
|
|
109
|
+
return items;
|
|
110
|
+
},
|
|
111
|
+
handleInsert: async (itemsToInsert) => {
|
|
112
|
+
const db = config.indexedDBRef.current;
|
|
113
|
+
if (!db) {
|
|
114
|
+
throw new Error("Database not ready");
|
|
115
|
+
}
|
|
116
|
+
await db.add(config.storeName, itemsToInsert);
|
|
117
|
+
return itemsToInsert;
|
|
118
|
+
},
|
|
119
|
+
handleUpdate: async (mutations) => {
|
|
120
|
+
const db = config.indexedDBRef.current;
|
|
121
|
+
if (!db) {
|
|
122
|
+
throw new Error("Database not ready");
|
|
123
|
+
}
|
|
124
|
+
const results = [];
|
|
125
|
+
const itemsToUpdate = [];
|
|
126
|
+
for (const mutation of mutations) {
|
|
127
|
+
const existing = await db.get(
|
|
128
|
+
config.storeName,
|
|
129
|
+
mutation.key
|
|
130
|
+
);
|
|
131
|
+
if (existing) {
|
|
132
|
+
const updateTime = /* @__PURE__ */ new Date();
|
|
133
|
+
const updatedItem = {
|
|
134
|
+
...existing,
|
|
135
|
+
...mutation.changes,
|
|
136
|
+
updatedAt: updateTime
|
|
137
|
+
};
|
|
138
|
+
itemsToUpdate.push(updatedItem);
|
|
139
|
+
results.push(
|
|
140
|
+
updatedItem
|
|
141
|
+
);
|
|
142
|
+
} else {
|
|
143
|
+
results.push(mutation.original);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (itemsToUpdate.length > 0) {
|
|
147
|
+
await db.put(config.storeName, itemsToUpdate);
|
|
148
|
+
}
|
|
149
|
+
return results;
|
|
150
|
+
},
|
|
151
|
+
handleBatchPut: async (itemsToPut) => {
|
|
152
|
+
const db = config.indexedDBRef.current;
|
|
153
|
+
if (!db) {
|
|
154
|
+
throw new Error("Database not ready");
|
|
155
|
+
}
|
|
156
|
+
const now = /* @__PURE__ */ new Date();
|
|
157
|
+
const rows = itemsToPut.map(
|
|
158
|
+
(row) => ({
|
|
159
|
+
...row,
|
|
160
|
+
updatedAt: now
|
|
161
|
+
})
|
|
162
|
+
);
|
|
163
|
+
await db.put(config.storeName, rows);
|
|
164
|
+
},
|
|
165
|
+
handleDelete: async (mutations) => {
|
|
166
|
+
const db = config.indexedDBRef.current;
|
|
167
|
+
if (!db) {
|
|
168
|
+
throw new Error("Database not ready");
|
|
169
|
+
}
|
|
170
|
+
const keysToDelete = mutations.map((m) => m.key);
|
|
171
|
+
await db.delete(config.storeName, keysToDelete);
|
|
172
|
+
},
|
|
173
|
+
handleTruncate: async () => {
|
|
174
|
+
const db = config.indexedDBRef.current;
|
|
175
|
+
if (!db) {
|
|
176
|
+
throw new Error("Database not ready");
|
|
177
|
+
}
|
|
178
|
+
await db.clear(config.storeName);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const wrappedBackend = {
|
|
182
|
+
...backend,
|
|
183
|
+
initialLoad: async () => {
|
|
184
|
+
if (config.syncMode === "eager" || !config.syncMode) {
|
|
185
|
+
return await backend.initialLoad();
|
|
186
|
+
}
|
|
187
|
+
await discoverIndexesOnce();
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
const getKey = (item) => item.id;
|
|
192
|
+
const baseSyncConfig = {
|
|
193
|
+
table,
|
|
194
|
+
readyPromise: config.readyPromise,
|
|
195
|
+
syncMode: config.syncMode,
|
|
196
|
+
debug: config.debug,
|
|
197
|
+
getSyncPersistKey: (item) => String(getKey(item)),
|
|
198
|
+
...config.deferLocalPersistence !== void 0 ? { deferLocalPersistence: config.deferLocalPersistence } : {}
|
|
199
|
+
};
|
|
200
|
+
const syncResult = createSyncFunction(baseSyncConfig, wrappedBackend);
|
|
201
|
+
const schema = createInsertSchemaWithDefaults(table);
|
|
202
|
+
return createCollectionConfig({
|
|
203
|
+
schema,
|
|
204
|
+
getKey,
|
|
205
|
+
syncResult,
|
|
206
|
+
syncMode: config.syncMode
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export { drizzleIndexedDBCollectionOptions };
|
|
211
|
+
//# sourceMappingURL=chunk-5KCMKETG.js.map
|
|
212
|
+
//# sourceMappingURL=chunk-5KCMKETG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/collections/drizzle-indexeddb-collection.ts"],"names":[],"mappings":"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","file":"chunk-5KCMKETG.js","sourcesContent":["import type {\n\tCollection,\n\tCollectionConfig,\n\tInferSchemaInput,\n\tInferSchemaOutput,\n\tSyncMode,\n} from \"@tanstack/db\";\nimport type { IR } from \"@tanstack/db\";\nimport { parseOrderByExpression } from \"@tanstack/db\";\nimport type { Table } from \"drizzle-orm\";\n\nimport {\n\ttype IdOf,\n\ttype SelectSchema,\n\ttype InsertToSelectSchema,\n\ttype TableWithRequiredFields,\n\ttype BaseSyncConfig,\n\ttype SyncBackend,\n\tcreateSyncFunction,\n\tcreateInsertSchemaWithDefaults,\n\tcreateCollectionConfig,\n} from \"@firtoz/drizzle-utils\";\nimport { evaluateExpression, type CollectionUtils } from \"@firtoz/db-helpers\";\nimport { tryExtractIndexedQuery } from \"@firtoz/idb-collections\";\n\nimport type { IDBDatabaseLike } from \"../idb-types\";\n\n// biome-ignore lint/suspicious/noExplicitAny: intentional\ntype AnyId = IdOf<any>;\n\n/**\n * Type for items stored in IndexedDB (must have required sync fields)\n */\nexport type DrizzleIndexedDBSyncItem = {\n\tid: AnyId;\n\tcreatedAt: Date;\n\tupdatedAt: Date;\n\tdeletedAt: Date | null;\n\t[key: string]: unknown;\n};\n\nexport interface DrizzleIndexedDBCollectionConfig<TTable extends Table> {\n\t/**\n\t * Ref to the IndexedDB database instance\n\t */\n\tindexedDBRef: React.RefObject<IDBDatabaseLike | null>;\n\t/**\n\t * The Drizzle table definition (used for schema and type inference only)\n\t */\n\ttable: TTable;\n\t/**\n\t * The name of the IndexedDB object store (should match the table name)\n\t */\n\tstoreName: string;\n\t/**\n\t * Promise that resolves when the database is ready\n\t */\n\treadyPromise: Promise<void>;\n\t/**\n\t * Sync mode: 'eager' (immediate) or 'on-demand'\n\t */\n\tsyncMode?: SyncMode;\n\t/**\n\t * Enable debug logging for index discovery and query optimization\n\t */\n\tdebug?: boolean;\n\t/**\n\t * When set, local mutations confirm TanStack sync immediately and persist to IndexedDB on a\n\t * coalesced timer (`createGenericSyncFunction` in `@firtoz/db-helpers`).\n\t */\n\tdeferLocalPersistence?: boolean | { flushIntervalMs?: number };\n}\n\nexport type DrizzleIndexedDBCollectionConfigResult<TTable extends Table> = Omit<\n\tCollectionConfig<\n\t\tInferSchemaOutput<SelectSchema<TTable>>,\n\t\tIdOf<TTable>,\n\t\tInsertToSelectSchema<TTable>,\n\t\tCollectionUtils<InferSchemaOutput<SelectSchema<TTable>>>\n\t>,\n\t\"utils\"\n> & {\n\tschema: InsertToSelectSchema<TTable>;\n\tutils: CollectionUtils<InferSchemaOutput<SelectSchema<TTable>>>;\n};\n\nexport type DrizzleIndexedDBCollection<TTable extends TableWithRequiredFields> =\n\tCollection<\n\t\tInferSchemaOutput<SelectSchema<TTable>>,\n\t\tIdOf<TTable>,\n\t\tCollectionUtils<InferSchemaOutput<SelectSchema<TTable>>>,\n\t\tInsertToSelectSchema<TTable>,\n\t\tInferSchemaInput<InsertToSelectSchema<TTable>>\n\t>;\n\n/**\n * Auto-discovers indexes from the IndexedDB store.\n * Returns a map of field names to index names for single-column indexes.\n */\nfunction discoverIndexes(\n\tdb: IDBDatabaseLike,\n\tstoreName: string,\n): Record<string, string> {\n\tconst indexes = db.getStoreIndexes(storeName);\n\tconst indexMap: Record<string, string> = {};\n\n\tfor (const index of indexes) {\n\t\tif (typeof index.keyPath === \"string\") {\n\t\t\tindexMap[index.keyPath] = index.name;\n\t\t}\n\t}\n\n\treturn indexMap;\n}\n\n/**\n * Creates a TanStack DB collection config for IndexedDB backed by Drizzle ORM.\n */\nexport function drizzleIndexedDBCollectionOptions<const TTable extends Table>(\n\tconfig: DrizzleIndexedDBCollectionConfig<TTable>,\n): DrizzleIndexedDBCollectionConfigResult<TTable> {\n\tlet discoveredIndexes: Record<string, string> = {};\n\tlet indexesDiscovered = false;\n\n\tconst table = config.table;\n\n\tconst discoverIndexesOnce = async () => {\n\t\tawait config.readyPromise;\n\n\t\tconst db = config.indexedDBRef.current;\n\t\tif (!db) {\n\t\t\tthrow new Error(\"Database not ready\");\n\t\t}\n\n\t\tif (!indexesDiscovered) {\n\t\t\tdiscoveredIndexes = discoverIndexes(db, config.storeName);\n\n\t\t\tindexesDiscovered = true;\n\t\t}\n\t};\n\n\tconst backend: SyncBackend<TTable> = {\n\t\tinitialLoad: async () => {\n\t\t\tconst db = config.indexedDBRef.current;\n\t\t\tif (!db) {\n\t\t\t\tthrow new Error(\"Database not ready\");\n\t\t\t}\n\n\t\t\tawait discoverIndexesOnce();\n\n\t\t\tconst items = await db.getAll<DrizzleIndexedDBSyncItem>(config.storeName);\n\n\t\t\treturn items as unknown as InferSchemaOutput<SelectSchema<TTable>>[];\n\t\t},\n\t\tloadSubset: async (options) => {\n\t\t\tconst db = config.indexedDBRef.current;\n\t\t\tif (!db) {\n\t\t\t\tthrow new Error(\"Database not ready\");\n\t\t\t}\n\n\t\t\tif (!indexesDiscovered) {\n\t\t\t\tdiscoveredIndexes = discoverIndexes(db, config.storeName);\n\t\t\t\tindexesDiscovered = true;\n\t\t\t}\n\n\t\t\tlet items: DrizzleIndexedDBSyncItem[];\n\n\t\t\tlet combinedWhere = options.where;\n\t\t\tif (options.cursor?.whereFrom) {\n\t\t\t\tif (combinedWhere) {\n\t\t\t\t\tcombinedWhere = {\n\t\t\t\t\t\ttype: \"func\",\n\t\t\t\t\t\tname: \"and\",\n\t\t\t\t\t\targs: [combinedWhere, options.cursor.whereFrom],\n\t\t\t\t\t} as IR.Func;\n\t\t\t\t} else {\n\t\t\t\t\tcombinedWhere = options.cursor.whereFrom;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst indexedQuery = combinedWhere\n\t\t\t\t? tryExtractIndexedQuery(combinedWhere, discoveredIndexes, config.debug)\n\t\t\t\t: null;\n\n\t\t\tif (indexedQuery) {\n\t\t\t\titems = await db.getAllByIndex<DrizzleIndexedDBSyncItem>(\n\t\t\t\t\tconfig.storeName,\n\t\t\t\t\tindexedQuery.indexName,\n\t\t\t\t\tindexedQuery.keyRange,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\titems = await db.getAll<DrizzleIndexedDBSyncItem>(config.storeName);\n\n\t\t\t\tif (combinedWhere) {\n\t\t\t\t\tconst whereExpression = combinedWhere;\n\t\t\t\t\titems = items.filter((item) =>\n\t\t\t\t\t\tevaluateExpression(\n\t\t\t\t\t\t\twhereExpression,\n\t\t\t\t\t\t\titem as Record<string, unknown>,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options.orderBy) {\n\t\t\t\tconst sorts = parseOrderByExpression(options.orderBy);\n\t\t\t\titems.sort((a, b) => {\n\t\t\t\t\tfor (const sort of sorts) {\n\t\t\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: Need any for dynamic field access\n\t\t\t\t\t\tlet aValue: any = a;\n\t\t\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: Need any for dynamic field access\n\t\t\t\t\t\tlet bValue: any = b;\n\t\t\t\t\t\tfor (const fieldName of sort.field) {\n\t\t\t\t\t\t\taValue = aValue?.[fieldName];\n\t\t\t\t\t\t\tbValue = bValue?.[fieldName];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (aValue < bValue) {\n\t\t\t\t\t\t\treturn sort.direction === \"asc\" ? -1 : 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (aValue > bValue) {\n\t\t\t\t\t\t\treturn sort.direction === \"asc\" ? 1 : -1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn 0;\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (options.offset !== undefined && options.offset > 0) {\n\t\t\t\titems = items.slice(options.offset);\n\t\t\t}\n\n\t\t\tif (options.limit !== undefined) {\n\t\t\t\titems = items.slice(0, options.limit);\n\t\t\t}\n\n\t\t\treturn items as unknown as InferSchemaOutput<SelectSchema<TTable>>[];\n\t\t},\n\n\t\thandleInsert: async (itemsToInsert) => {\n\t\t\tconst db = config.indexedDBRef.current;\n\t\t\tif (!db) {\n\t\t\t\tthrow new Error(\"Database not ready\");\n\t\t\t}\n\n\t\t\tawait db.add(config.storeName, itemsToInsert);\n\n\t\t\treturn itemsToInsert;\n\t\t},\n\n\t\thandleUpdate: async (mutations) => {\n\t\t\tconst db = config.indexedDBRef.current;\n\n\t\t\tif (!db) {\n\t\t\t\tthrow new Error(\"Database not ready\");\n\t\t\t}\n\n\t\t\tconst results: Array<InferSchemaOutput<SelectSchema<TTable>>> = [];\n\t\t\tconst itemsToUpdate: DrizzleIndexedDBSyncItem[] = [];\n\n\t\t\tfor (const mutation of mutations) {\n\t\t\t\tconst existing = await db.get<DrizzleIndexedDBSyncItem>(\n\t\t\t\t\tconfig.storeName,\n\t\t\t\t\tmutation.key,\n\t\t\t\t);\n\n\t\t\t\tif (existing) {\n\t\t\t\t\tconst updateTime = new Date();\n\t\t\t\t\tconst updatedItem = {\n\t\t\t\t\t\t...existing,\n\t\t\t\t\t\t...mutation.changes,\n\t\t\t\t\t\tupdatedAt: updateTime,\n\t\t\t\t\t} as DrizzleIndexedDBSyncItem;\n\n\t\t\t\t\titemsToUpdate.push(updatedItem);\n\t\t\t\t\tresults.push(\n\t\t\t\t\t\tupdatedItem as unknown as InferSchemaOutput<SelectSchema<TTable>>,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tresults.push(mutation.original);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (itemsToUpdate.length > 0) {\n\t\t\t\tawait db.put(config.storeName, itemsToUpdate);\n\t\t\t}\n\n\t\t\treturn results;\n\t\t},\n\n\t\thandleBatchPut: async (itemsToPut) => {\n\t\t\tconst db = config.indexedDBRef.current;\n\t\t\tif (!db) {\n\t\t\t\tthrow new Error(\"Database not ready\");\n\t\t\t}\n\t\t\tconst now = new Date();\n\t\t\tconst rows = (itemsToPut as unknown as DrizzleIndexedDBSyncItem[]).map(\n\t\t\t\t(row) => ({\n\t\t\t\t\t...row,\n\t\t\t\t\tupdatedAt: now,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait db.put(config.storeName, rows);\n\t\t},\n\n\t\thandleDelete: async (mutations) => {\n\t\t\tconst db = config.indexedDBRef.current;\n\n\t\t\tif (!db) {\n\t\t\t\tthrow new Error(\"Database not ready\");\n\t\t\t}\n\n\t\t\tconst keysToDelete: IDBValidKey[] = mutations.map((m) => m.key);\n\n\t\t\tawait db.delete(config.storeName, keysToDelete);\n\t\t},\n\n\t\thandleTruncate: async () => {\n\t\t\tconst db = config.indexedDBRef.current;\n\n\t\t\tif (!db) {\n\t\t\t\tthrow new Error(\"Database not ready\");\n\t\t\t}\n\n\t\t\tawait db.clear(config.storeName);\n\t\t},\n\t};\n\n\tconst wrappedBackend: SyncBackend<TTable> = {\n\t\t...backend,\n\t\tinitialLoad: async () => {\n\t\t\tif (config.syncMode === \"eager\" || !config.syncMode) {\n\t\t\t\treturn await backend.initialLoad();\n\t\t\t}\n\n\t\t\tawait discoverIndexesOnce();\n\n\t\t\treturn [];\n\t\t},\n\t};\n\n\tconst getKey = (\n\t\titem: InferSchemaOutput<SelectSchema<TTable>>,\n\t): IdOf<TTable> => (item as { id: IdOf<TTable> }).id;\n\n\tconst baseSyncConfig: BaseSyncConfig<TTable> = {\n\t\ttable,\n\t\treadyPromise: config.readyPromise,\n\t\tsyncMode: config.syncMode,\n\t\tdebug: config.debug,\n\t\tgetSyncPersistKey: (item) => String(getKey(item)),\n\t\t...(config.deferLocalPersistence !== undefined\n\t\t\t? { deferLocalPersistence: config.deferLocalPersistence }\n\t\t\t: {}),\n\t};\n\n\tconst syncResult = createSyncFunction(baseSyncConfig, wrappedBackend);\n\n\tconst schema = createInsertSchemaWithDefaults(table);\n\n\treturn createCollectionConfig({\n\t\tschema,\n\t\tgetKey,\n\t\tsyncResult,\n\t\tsyncMode: config.syncMode,\n\t});\n}\n"]}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { openIndexedDb } from './chunk-JVUF63L6.js';
|
|
2
|
+
import { exhaustiveGuard } from '@firtoz/maybe-error';
|
|
3
|
+
|
|
4
|
+
var MIGRATIONS_STORE = "__drizzle_migrations";
|
|
5
|
+
function executeMigrationOperation(db, op) {
|
|
6
|
+
switch (op.type) {
|
|
7
|
+
case "createTable": {
|
|
8
|
+
if (!db.hasStore(op.name)) {
|
|
9
|
+
db.createStore(op.name, {
|
|
10
|
+
keyPath: op.keyPath,
|
|
11
|
+
autoIncrement: op.autoIncrement
|
|
12
|
+
});
|
|
13
|
+
if (op.indexes) {
|
|
14
|
+
for (const idx of op.indexes) {
|
|
15
|
+
db.createIndex(op.name, idx.name, idx.keyPath, {
|
|
16
|
+
unique: idx.unique
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
case "deleteTable": {
|
|
24
|
+
if (db.hasStore(op.name)) {
|
|
25
|
+
db.deleteStore(op.name);
|
|
26
|
+
}
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
case "createIndex": {
|
|
30
|
+
if (db.hasStore(op.tableName)) {
|
|
31
|
+
db.createIndex(op.tableName, op.indexName, op.keyPath, {
|
|
32
|
+
unique: op.unique
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
case "deleteIndex": {
|
|
38
|
+
if (db.hasStore(op.tableName)) {
|
|
39
|
+
db.deleteIndex(op.tableName, op.indexName);
|
|
40
|
+
}
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
default:
|
|
44
|
+
exhaustiveGuard(op);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function executeMigration(db, migration) {
|
|
48
|
+
for (const op of migration) {
|
|
49
|
+
executeMigrationOperation(db, op);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function migrateIndexedDBWithFunctions(dbName, migrations, debug = false, dbCreator) {
|
|
53
|
+
if (debug) {
|
|
54
|
+
console.log(`[IndexedDB] Starting migration for ${dbName}`);
|
|
55
|
+
}
|
|
56
|
+
const targetVersion = migrations.length + 1;
|
|
57
|
+
let db = await openIndexedDb(dbName, dbCreator);
|
|
58
|
+
const currentVersion = db.version;
|
|
59
|
+
if (debug) {
|
|
60
|
+
console.log(
|
|
61
|
+
`[IndexedDB] Current version: ${currentVersion}, Target: ${targetVersion}`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
if (currentVersion >= targetVersion) {
|
|
65
|
+
const applied = await getAppliedMigrations(db);
|
|
66
|
+
if (applied.length === migrations.length) {
|
|
67
|
+
if (debug) {
|
|
68
|
+
console.log("[IndexedDB] Already up to date");
|
|
69
|
+
}
|
|
70
|
+
return db;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const appliedMigrations = await getAppliedMigrations(db);
|
|
74
|
+
const appliedSet = new Set(appliedMigrations.map((m) => m.id));
|
|
75
|
+
const pendingMigrations = migrations.map((migration, idx) => ({ migration, idx })).filter(({ idx }) => !appliedSet.has(idx));
|
|
76
|
+
if (pendingMigrations.length === 0) {
|
|
77
|
+
if (debug) {
|
|
78
|
+
console.log("[IndexedDB] No pending migrations");
|
|
79
|
+
}
|
|
80
|
+
return db;
|
|
81
|
+
}
|
|
82
|
+
if (debug) {
|
|
83
|
+
console.log(
|
|
84
|
+
`[IndexedDB] ${pendingMigrations.length} pending migrations to apply`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
db.close();
|
|
88
|
+
await openIndexedDb(dbName, dbCreator, {
|
|
89
|
+
version: targetVersion,
|
|
90
|
+
onUpgrade: (upgradeDb) => {
|
|
91
|
+
if (!upgradeDb.hasStore(MIGRATIONS_STORE)) {
|
|
92
|
+
upgradeDb.createStore(MIGRATIONS_STORE, {
|
|
93
|
+
keyPath: "id",
|
|
94
|
+
autoIncrement: false
|
|
95
|
+
});
|
|
96
|
+
if (debug) {
|
|
97
|
+
console.log("[IndexedDB] Created migrations store");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
for (const { migration, idx } of pendingMigrations) {
|
|
101
|
+
if (debug) {
|
|
102
|
+
console.log(`[IndexedDB] Running migration ${idx}`);
|
|
103
|
+
}
|
|
104
|
+
executeMigration(upgradeDb, migration);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
db = await openIndexedDb(dbName, dbCreator);
|
|
109
|
+
for (const { idx } of pendingMigrations) {
|
|
110
|
+
await db.add(MIGRATIONS_STORE, [{ id: idx, appliedAt: Date.now() }]);
|
|
111
|
+
}
|
|
112
|
+
if (debug) {
|
|
113
|
+
console.log(
|
|
114
|
+
`[IndexedDB] Applied ${pendingMigrations.length} migrations, now at version ${targetVersion}`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
return db;
|
|
118
|
+
}
|
|
119
|
+
async function getAppliedMigrations(db) {
|
|
120
|
+
if (!db.hasStore(MIGRATIONS_STORE)) {
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
return db.getAll(MIGRATIONS_STORE);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export { migrateIndexedDBWithFunctions };
|
|
127
|
+
//# sourceMappingURL=chunk-7X4EIKN4.js.map
|
|
128
|
+
//# sourceMappingURL=chunk-7X4EIKN4.js.map
|