@firtoz/drizzle-sqlite-wasm 1.1.1 → 2.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 +33 -0
- package/README.md +21 -4
- package/dist/chunk-7TDQNWT6.js +181 -0
- package/dist/chunk-7TDQNWT6.js.map +1 -0
- package/dist/{chunk-FRONXNEA.js → chunk-BQBL6E44.js} +2 -2
- package/dist/chunk-BQBL6E44.js.map +1 -0
- package/dist/{chunk-H2F2HZ2A.js → chunk-EQOHJW3Q.js} +3 -3
- package/dist/{chunk-H2F2HZ2A.js.map → chunk-EQOHJW3Q.js.map} +1 -1
- package/dist/{chunk-7JJHY44Q.js → chunk-NNPU7YTX.js} +3 -3
- package/dist/{chunk-7JJHY44Q.js.map → chunk-NNPU7YTX.js.map} +1 -1
- package/dist/{chunk-WFFFP6DB.js → chunk-QOFRLODK.js} +74 -19
- package/dist/chunk-QOFRLODK.js.map +1 -0
- package/dist/{chunk-BZVMUTJ7.js → chunk-VLFDMAFH.js} +2 -2
- package/dist/chunk-VLFDMAFH.js.map +1 -0
- package/dist/collections/sqlite-collection.d.ts +0 -1
- package/dist/collections/sqlite-collection.js +1 -1
- package/dist/collections/synced-sqlite-collection.js +2 -2
- package/dist/context/DrizzleSqliteProvider.d.ts +46 -4
- package/dist/context/DrizzleSqliteProvider.js +4 -4
- package/dist/context/useDrizzleSqlite.d.ts +4 -1
- package/dist/context/useDrizzleSqlite.js +5 -5
- package/dist/drizzle/worker.js +1 -1
- package/dist/hooks/useDrizzleSqliteDb.d.ts +35 -7
- package/dist/hooks/useDrizzleSqliteDb.js +2 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +6 -7
- package/dist/index.js.map +1 -1
- package/dist/worker/schema.d.ts +4 -4
- package/dist/worker/sqlite.worker.js +1 -1
- package/dist/worker/sqlite.worker.js.map +1 -1
- package/package.json +10 -10
- package/src/collections/sqlite-collection.ts +0 -2
- package/src/drizzle/worker.ts +1 -4
- package/src/hooks/useDrizzleSqliteDb.ts +133 -15
- package/src/index.ts +9 -12
- package/src/worker/sqlite.worker.ts +4 -1
- package/dist/chunk-AEYHRJVN.js +0 -130
- package/dist/chunk-AEYHRJVN.js.map +0 -1
- package/dist/chunk-BZVMUTJ7.js.map +0 -1
- package/dist/chunk-FRONXNEA.js.map +0 -1
- package/dist/chunk-WFFFP6DB.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @firtoz/drizzle-sqlite-wasm
|
|
2
2
|
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- [`c7bf9d5`](https://github.com/firtoz/fullstack-toolkit/commit/c7bf9d57094b9d4348410ed0d824eb8416639469) Thanks [@firtoz](https://github.com/firtoz)! - **@firtoz/drizzle-sqlite-wasm:** `DrizzleSqliteProvider` now requires a `loadingFallback` and gates `children` until the worker and migrations are ready; optional `errorFallback` and `data-testid="sqlite-db-error"`. `useDrizzleSqliteDb` exposes `sessionStatus` and `sessionError` with a per-`dbName` `readyPromise`. Collections are created only in the ready subtree, with checkpoint closing over the real client (no provider checkpoint ref). Exports `DrizzleSqliteSessionStatus`. Document `key={dbName}` (or a composite) when switching databases. Removes the `drizzleCollectionOptions` entry-point alias: import `sqliteCollectionOptions` (or deep import `@firtoz/drizzle-sqlite-wasm/sqliteCollectionOptions`) instead of the former `./drizzleCollectionOptions` subpath.
|
|
8
|
+
|
|
9
|
+
- [`1e057aa`](https://github.com/firtoz/fullstack-toolkit/commit/1e057aad4b252223f4269a9bc6bd01a744cf56a8) Thanks [@firtoz](https://github.com/firtoz)! - **@firtoz/router-toolkit:** Stop re-exporting `@firtoz/maybe-error` from the package entry so `.d.ts` matches runtime. `@firtoz/maybe-error` is now a regular dependency; import `success`, `fail`, `MaybeError`, `exhaustiveGuard`, and related symbols from `@firtoz/maybe-error` directly.
|
|
10
|
+
|
|
11
|
+
**@firtoz/hono-fetcher** and **@firtoz/worker-helper:** Regenerate root `index.d.ts` after tsup so type-only symbols use `export type { ... }`, for compatibility with stricter consumer compiler settings.
|
|
12
|
+
|
|
13
|
+
**@firtoz/drizzle-sqlite-wasm:** Remove re-exports of `@firtoz/drizzle-utils` (`syncableTable`, `makeId`, branded/schema types, `SQLOperation`, `SQLInterceptor`) from the package entry. Import those from `@firtoz/drizzle-utils` directly.
|
|
14
|
+
|
|
15
|
+
**@firtoz/drizzle-durable-sqlite:** Stop re-exporting `SQLOperation` and `SQLInterceptor` from the package entry; import them from `@firtoz/drizzle-utils` when needed.
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- [`c7bf9d5`](https://github.com/firtoz/fullstack-toolkit/commit/c7bf9d57094b9d4348410ed0d824eb8416639469) Thanks [@firtoz](https://github.com/firtoz)! - Use `OpfsDb` when the constructor is present instead of `"opfs" in sqlite3`, because sqlite-wasm removes the `opfs` helper namespace after init in non-test builds (which incorrectly forced a transient database). Wire WAL checkpoint through a ref so collections created before the worker client is ready still flush to OPFS after mutations.
|
|
20
|
+
|
|
21
|
+
- Updated dependencies [[`1e057aa`](https://github.com/firtoz/fullstack-toolkit/commit/1e057aad4b252223f4269a9bc6bd01a744cf56a8)]:
|
|
22
|
+
- @firtoz/worker-helper@1.6.2
|
|
23
|
+
- @firtoz/collection-sync@6.0.2
|
|
24
|
+
|
|
25
|
+
## 1.1.2
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- Updated dependencies [[`7c4983f`](https://github.com/firtoz/fullstack-toolkit/commit/7c4983fd27adb9709ee844547259e0f22040fded), [`80a7aa6`](https://github.com/firtoz/fullstack-toolkit/commit/80a7aa6abbca3b7ef621e6dd92f51954bee84b43)]:
|
|
30
|
+
- @firtoz/drizzle-utils@1.3.1
|
|
31
|
+
- @firtoz/db-helpers@2.2.1
|
|
32
|
+
- @firtoz/maybe-error@1.6.1
|
|
33
|
+
- @firtoz/worker-helper@1.6.1
|
|
34
|
+
- @firtoz/collection-sync@6.0.1
|
|
35
|
+
|
|
3
36
|
## 1.1.1
|
|
4
37
|
|
|
5
38
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -70,6 +70,7 @@ function App() {
|
|
|
70
70
|
dbName="my-app"
|
|
71
71
|
schema={schema}
|
|
72
72
|
migrations={migrations}
|
|
73
|
+
loadingFallback={<div>Loading database…</div>}
|
|
73
74
|
>
|
|
74
75
|
<TodoApp />
|
|
75
76
|
</DrizzleSqliteProvider>
|
|
@@ -77,6 +78,22 @@ function App() {
|
|
|
77
78
|
}
|
|
78
79
|
```
|
|
79
80
|
|
|
81
|
+
### Database session and loading
|
|
82
|
+
|
|
83
|
+
- **When `dbName` or `workerOpenOptions` change:** the provider’s `children` and collections are for the new file (previous in-provider UI state from the old file does not carry over). State **above** the provider (layout, URL-driven shell, etc.) is unchanged—handle that in your app if it should follow the new database.
|
|
84
|
+
- **`loadingFallback`:** The worker and migrations run asynchronously. Until the session is ready, the provider renders **`loadingFallback`** (not the main `children`). Put routes that use `useDrizzleSqlite` / `useSqliteCollection` *inside* the provider, but they only mount in the “ready” phase—use this slot for “Loading…”, skeletons, or a minimal shell. Keep app chrome (nav, layout) **above** the gated region if you want it to stay stable.
|
|
85
|
+
- **Errors:** If migrations fail, the provider can render `errorFallback` (or a default error area with `data-testid="sqlite-db-error"` for tests).
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// useDrizzleSqliteDb without the provider: gate on sessionStatus before using drizzle / createCollection
|
|
89
|
+
import { useDrizzleSqliteDb } from "@firtoz/drizzle-sqlite-wasm";
|
|
90
|
+
// …
|
|
91
|
+
const { drizzle, readyPromise, sessionStatus } = useDrizzleSqliteDb(...);
|
|
92
|
+
if (sessionStatus !== "ready") {
|
|
93
|
+
return <p>Loading…</p>;
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
80
97
|
### 4. Use in Components
|
|
81
98
|
|
|
82
99
|
```typescript
|
|
@@ -190,7 +207,7 @@ export default defineConfig({
|
|
|
190
207
|
3. **Worker Format**: Ensures all workers are treated as ES modules
|
|
191
208
|
4. **Exclude from Optimization**: Prevents Vite from pre-bundling sqlite-wasm, which can break worker creation
|
|
192
209
|
|
|
193
|
-
For a complete example, see [`tests/test-playground/vite.config.ts`](../../tests/test-playground/vite.config.ts) in the repository.
|
|
210
|
+
For a complete example, see [`tests/test-playground-collections/vite.config.ts`](../../tests/test-playground-collections/vite.config.ts) in the repository.
|
|
194
211
|
|
|
195
212
|
### Webpack 5+
|
|
196
213
|
|
|
@@ -236,11 +253,11 @@ Create TanStack DB collections backed by SQLite:
|
|
|
236
253
|
```typescript
|
|
237
254
|
import { createCollection } from "@tanstack/db";
|
|
238
255
|
import type { DrizzleSqliteTableCollection } from "@firtoz/drizzle-utils";
|
|
239
|
-
import {
|
|
256
|
+
import { sqliteCollectionOptions } from "@firtoz/drizzle-sqlite-wasm";
|
|
240
257
|
import * as schema from "./schema";
|
|
241
258
|
|
|
242
259
|
const collection = createCollection(
|
|
243
|
-
|
|
260
|
+
sqliteCollectionOptions({
|
|
244
261
|
drizzle,
|
|
245
262
|
tableName: "todos",
|
|
246
263
|
readyPromise,
|
|
@@ -682,7 +699,7 @@ import {
|
|
|
682
699
|
## Examples
|
|
683
700
|
|
|
684
701
|
Check out the test playground for complete examples:
|
|
685
|
-
- `tests/test-playground/e2e/`
|
|
702
|
+
- `tests/test-playground-collections/e2e/` — E2E tests (collections / SQLite demos)
|
|
686
703
|
|
|
687
704
|
## Dependencies
|
|
688
705
|
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { useDrizzleSqliteDb } from './chunk-QOFRLODK.js';
|
|
2
|
+
import { sqliteCollectionOptions } from './chunk-VLFDMAFH.js';
|
|
3
|
+
import { createContext, useMemo, useEffect, useRef, useState, useCallback } from 'react';
|
|
4
|
+
import { createCollection } from '@tanstack/db';
|
|
5
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var DrizzleSqliteContext = (
|
|
8
|
+
// biome-ignore lint/suspicious/noExplicitAny: Context needs to accept any schema type
|
|
9
|
+
createContext(null)
|
|
10
|
+
);
|
|
11
|
+
function DrizzleSqliteSessionBody({
|
|
12
|
+
children,
|
|
13
|
+
interceptor,
|
|
14
|
+
drizzle,
|
|
15
|
+
readyPromise,
|
|
16
|
+
sqliteClient,
|
|
17
|
+
debug,
|
|
18
|
+
enableCheckpoint,
|
|
19
|
+
syncMode
|
|
20
|
+
}) {
|
|
21
|
+
const interceptorRef = useRef(interceptor);
|
|
22
|
+
interceptorRef.current = interceptor;
|
|
23
|
+
const [collectionCacheEpoch, setCollectionCacheEpoch] = useState(0);
|
|
24
|
+
const collections = useMemo(
|
|
25
|
+
() => /* @__PURE__ */ new Map(),
|
|
26
|
+
[]
|
|
27
|
+
);
|
|
28
|
+
const stableInterceptor = useMemo(
|
|
29
|
+
() => interceptor ? { onOperation: (op) => interceptorRef.current?.onOperation?.(op) } : void 0,
|
|
30
|
+
[!!interceptor]
|
|
31
|
+
);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
collections.clear();
|
|
34
|
+
setCollectionCacheEpoch((e) => e + 1);
|
|
35
|
+
}, [drizzle, enableCheckpoint, syncMode, debug]);
|
|
36
|
+
const getCollection = useCallback(
|
|
37
|
+
(tableName) => {
|
|
38
|
+
const cacheKey = `${collectionCacheEpoch}:${String(tableName)}`;
|
|
39
|
+
if (!collections.has(cacheKey)) {
|
|
40
|
+
const options = sqliteCollectionOptions({
|
|
41
|
+
drizzle,
|
|
42
|
+
tableName,
|
|
43
|
+
readyPromise,
|
|
44
|
+
syncMode,
|
|
45
|
+
checkpoint: enableCheckpoint ? () => sqliteClient.checkpoint() : void 0,
|
|
46
|
+
interceptor: stableInterceptor,
|
|
47
|
+
debug
|
|
48
|
+
});
|
|
49
|
+
const collection = createCollection(options);
|
|
50
|
+
collections.set(cacheKey, { collection, refCount: 0 });
|
|
51
|
+
}
|
|
52
|
+
return collections.get(cacheKey).collection;
|
|
53
|
+
},
|
|
54
|
+
[
|
|
55
|
+
collectionCacheEpoch,
|
|
56
|
+
collections,
|
|
57
|
+
drizzle,
|
|
58
|
+
readyPromise,
|
|
59
|
+
syncMode,
|
|
60
|
+
enableCheckpoint,
|
|
61
|
+
sqliteClient,
|
|
62
|
+
stableInterceptor,
|
|
63
|
+
debug
|
|
64
|
+
]
|
|
65
|
+
);
|
|
66
|
+
const incrementRefCount = useCallback(
|
|
67
|
+
(tableName) => {
|
|
68
|
+
const k = `${collectionCacheEpoch}:${String(tableName)}`;
|
|
69
|
+
const entry = collections.get(k);
|
|
70
|
+
if (entry) {
|
|
71
|
+
entry.refCount++;
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
[collectionCacheEpoch, collections]
|
|
75
|
+
);
|
|
76
|
+
const decrementRefCount = useCallback(
|
|
77
|
+
(tableName) => {
|
|
78
|
+
const k = `${collectionCacheEpoch}:${String(tableName)}`;
|
|
79
|
+
const entry = collections.get(k);
|
|
80
|
+
if (entry) {
|
|
81
|
+
entry.refCount--;
|
|
82
|
+
if (entry.refCount <= 0) {
|
|
83
|
+
collections.delete(k);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
[collectionCacheEpoch, collections]
|
|
88
|
+
);
|
|
89
|
+
const contextValue = useMemo(
|
|
90
|
+
() => ({
|
|
91
|
+
drizzle,
|
|
92
|
+
readyPromise,
|
|
93
|
+
sqliteClient,
|
|
94
|
+
collectionCacheEpoch,
|
|
95
|
+
getCollection,
|
|
96
|
+
incrementRefCount,
|
|
97
|
+
decrementRefCount
|
|
98
|
+
}),
|
|
99
|
+
[
|
|
100
|
+
drizzle,
|
|
101
|
+
readyPromise,
|
|
102
|
+
sqliteClient,
|
|
103
|
+
collectionCacheEpoch,
|
|
104
|
+
getCollection,
|
|
105
|
+
incrementRefCount,
|
|
106
|
+
decrementRefCount
|
|
107
|
+
]
|
|
108
|
+
);
|
|
109
|
+
return /* @__PURE__ */ jsx(DrizzleSqliteContext.Provider, { value: contextValue, children });
|
|
110
|
+
}
|
|
111
|
+
function DrizzleSqliteProvider({
|
|
112
|
+
children,
|
|
113
|
+
worker,
|
|
114
|
+
dbName,
|
|
115
|
+
schema,
|
|
116
|
+
migrations,
|
|
117
|
+
debug,
|
|
118
|
+
enableCheckpoint = false,
|
|
119
|
+
loadingFallback,
|
|
120
|
+
errorFallback,
|
|
121
|
+
syncMode = "eager",
|
|
122
|
+
interceptor,
|
|
123
|
+
workerOpenOptions
|
|
124
|
+
}) {
|
|
125
|
+
const readySubtreeKey = useMemo(
|
|
126
|
+
() => `drizzle-sqlite:${dbName}:${JSON.stringify(workerOpenOptions ?? null)}`,
|
|
127
|
+
[dbName, workerOpenOptions]
|
|
128
|
+
);
|
|
129
|
+
const session = useDrizzleSqliteDb(
|
|
130
|
+
worker,
|
|
131
|
+
dbName,
|
|
132
|
+
schema,
|
|
133
|
+
migrations,
|
|
134
|
+
debug,
|
|
135
|
+
interceptor,
|
|
136
|
+
workerOpenOptions
|
|
137
|
+
);
|
|
138
|
+
const { drizzle, readyPromise, sqliteClient, sessionStatus } = session;
|
|
139
|
+
if (sessionStatus === "error") {
|
|
140
|
+
return /* @__PURE__ */ jsx(Fragment, { children: errorFallback ?? /* @__PURE__ */ jsx("div", { role: "alert", "data-testid": "sqlite-db-error", children: session.sessionError.message }) });
|
|
141
|
+
}
|
|
142
|
+
if (sessionStatus !== "ready" || !sqliteClient) {
|
|
143
|
+
return /* @__PURE__ */ jsx(Fragment, { children: loadingFallback });
|
|
144
|
+
}
|
|
145
|
+
return /* @__PURE__ */ jsx(
|
|
146
|
+
DrizzleSqliteSessionBody,
|
|
147
|
+
{
|
|
148
|
+
interceptor,
|
|
149
|
+
debug,
|
|
150
|
+
enableCheckpoint,
|
|
151
|
+
syncMode,
|
|
152
|
+
drizzle,
|
|
153
|
+
readyPromise,
|
|
154
|
+
sqliteClient,
|
|
155
|
+
children
|
|
156
|
+
},
|
|
157
|
+
readySubtreeKey
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
function useSqliteCollection(context, tableName) {
|
|
161
|
+
const { collection, unsubscribe } = useMemo(() => {
|
|
162
|
+
const col = context.getCollection(tableName);
|
|
163
|
+
context.incrementRefCount(tableName);
|
|
164
|
+
return {
|
|
165
|
+
collection: col,
|
|
166
|
+
unsubscribe: () => {
|
|
167
|
+
context.decrementRefCount(tableName);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}, [context, tableName, context.collectionCacheEpoch]);
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
return () => {
|
|
173
|
+
unsubscribe();
|
|
174
|
+
};
|
|
175
|
+
}, [unsubscribe]);
|
|
176
|
+
return collection;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export { DrizzleSqliteContext, DrizzleSqliteProvider, useSqliteCollection };
|
|
180
|
+
//# sourceMappingURL=chunk-7TDQNWT6.js.map
|
|
181
|
+
//# sourceMappingURL=chunk-7TDQNWT6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context/DrizzleSqliteProvider.tsx"],"names":[],"mappings":"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","file":"chunk-7TDQNWT6.js","sourcesContent":["import type { PropsWithChildren, ReactNode } from \"react\";\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport type { SqliteRemoteDatabase } from \"drizzle-orm/sqlite-proxy\";\nimport {\n\tcreateCollection,\n\ttype Collection,\n\ttype InferSchemaOutput,\n\ttype UtilsRecord,\n} from \"@tanstack/db\";\nimport {\n\ttype AnyDrizzleDatabase,\n\ttype ValidTableNames,\n\ttype DrizzleSchema,\n\tsqliteCollectionOptions,\n} from \"../collections/sqlite-collection\";\nimport type { SQLInterceptor } from \"@firtoz/drizzle-utils\";\nimport { useDrizzleSqliteDb } from \"../hooks/useDrizzleSqliteDb\";\nimport type { DurableSqliteMigrationConfig } from \"../migration/migrator\";\nimport type { SqliteWasmWorkerOpenOptions } from \"../worker/sqlite-open-options\";\nimport type { ISqliteWorkerClient } from \"../worker/manager\";\nimport type {\n\tIdOf,\n\tGetTableFromSchema,\n\tInferCollectionFromTable,\n} from \"@firtoz/drizzle-utils\";\n\n/** @internal */\ninterface CollectionCacheEntry {\n\t// biome-ignore lint/suspicious/noExplicitAny: Cache needs to store collections of various types\n\tcollection: Collection<any, string>;\n\trefCount: number;\n}\n\ntype SqliteCollection<\n\tTSchema extends Record<string, unknown>,\n\tTTableName extends string & ValidTableNames<TSchema>,\n> = Collection<\n\tInferSchemaOutput<GetTableFromSchema<TSchema, TTableName>[\"$inferSelect\"]>,\n\tIdOf<GetTableFromSchema<TSchema, TTableName>>,\n\t// biome-ignore lint/suspicious/noExplicitAny: We need to use any here to match the Collection type\n\tany,\n\t// biome-ignore lint/suspicious/noExplicitAny: We need to use any here to match the Collection type\n\tany,\n\tOmit<GetTableFromSchema<TSchema, TTableName>[\"$inferInsert\"], \"id\"> & {\n\t\tid?: IdOf<GetTableFromSchema<TSchema, TTableName>>;\n\t}\n>;\n\n/**\n * Exposed only when `sessionStatus === \"ready\"`. `useDrizzleSqlite` and\n * `useSqliteCollection` require this value — they must run under the\n * ready subtree of {@link DrizzleSqliteProvider} (i.e. not during loading fallback).\n */\nexport type DrizzleSqliteContextValue<TSchema extends Record<string, unknown>> =\n\t{\n\t\tdrizzle: SqliteRemoteDatabase<TSchema>;\n\t\treadyPromise: Promise<void>;\n\t\t/**\n\t\t * Worker client for this DB. Present whenever the ready subtree is mounted\n\t\t * (including SSR no-op client).\n\t\t */\n\t\tsqliteClient: ISqliteWorkerClient;\n\t\t/** Bumps when collection caches must be discarded (drizzle/options identity change). */\n\t\tcollectionCacheEpoch: number;\n\t\tgetCollection: <TTableName extends string & ValidTableNames<TSchema>>(\n\t\t\ttableName: TTableName,\n\t\t) => SqliteCollection<TSchema, TTableName>;\n\t\tincrementRefCount: (tableName: string) => void;\n\t\tdecrementRefCount: (tableName: string) => void;\n\t};\n\nexport const DrizzleSqliteContext =\n\t// biome-ignore lint/suspicious/noExplicitAny: Context needs to accept any schema type\n\tcreateContext<DrizzleSqliteContextValue<any> | null>(null);\n\ntype DrizzleSqliteProviderProps<TSchema extends Record<string, unknown>> =\n\tPropsWithChildren<{\n\t\tworker: new () => Worker;\n\t\t/**\n\t\t * File / logical name for the OPFS (or in-memory) DB. The ready subtree remounts when\n\t\t * this or `workerOpenOptions` (serialized) changes, so in-tree state resets for a\n\t\t * new file/session.\n\t\t */\n\t\tdbName: string;\n\t\tschema: TSchema;\n\t\tmigrations: DurableSqliteMigrationConfig;\n\t\tdebug?: boolean;\n\t\tenableCheckpoint?: boolean;\n\t\t/**\n\t\t * Shown on the client while the worker is starting and migrations are running\n\t\t * (`sessionStatus` is not `\"ready\"`). Not shown for SSR (session is `ready` with a no-op client).\n\t\t */\n\t\tloadingFallback: ReactNode;\n\t\t/**\n\t\t * Optional UI when migrations fail. Defaults to a short error message in development.\n\t\t */\n\t\terrorFallback?: ReactNode;\n\t\t/**\n\t\t * Sync mode: 'eager' (immediate) or 'on-demand' (lazy)\n\t\t */\n\t\tsyncMode?: \"eager\" | \"on-demand\";\n\t\t/**\n\t\t * Optional interceptor for tracking SQLite operations (for testing/debugging).\n\t\t * Inline `{ onOperation }` objects are fine: the provider keeps a stable wrapper so\n\t\t * collection caches and context are not invalidated on every parent re-render. You\n\t\t * can still pass a module-level or `useMemo`d object if you prefer.\n\t\t */\n\t\tinterceptor?: SQLInterceptor;\n\t\t/**\n\t\t * Worker DB pragmas on first open of `dbName` this session (see `useDrizzleSqliteDb`).\n\t\t * If this changes for the same `dbName`, the global worker may still return the existing\n\t\t * open DB — use a distinct `dbName` or remount the worker session if you need new pragmas.\n\t\t */\n\t\tworkerOpenOptions?: SqliteWasmWorkerOpenOptions;\n\t}>;\n\ntype DrizzleSqliteSessionBodyProps<TSchema extends Record<string, unknown>> = {\n\tchildren: ReactNode;\n\tinterceptor: SQLInterceptor | undefined;\n\tdebug: boolean | undefined;\n\tenableCheckpoint: boolean;\n\tsyncMode: \"eager\" | \"on-demand\";\n\tdrizzle: SqliteRemoteDatabase<TSchema>;\n\treadyPromise: Promise<void>;\n\tsqliteClient: ISqliteWorkerClient;\n};\n\n/**\n * One mounted provider subtree = one open DB. The parent {@link DrizzleSqliteProvider} gives\n * this node a `key` derived from `dbName` + `workerOpenOptions` so when the logical file (or\n * open options) changes, in-memory React state and collection caches reset.\n */\nfunction DrizzleSqliteSessionBody<TSchema extends Record<string, unknown>>({\n\tchildren,\n\tinterceptor,\n\tdrizzle,\n\treadyPromise,\n\tsqliteClient,\n\tdebug,\n\tenableCheckpoint,\n\tsyncMode,\n}: DrizzleSqliteSessionBodyProps<TSchema>) {\n\tconst interceptorRef = useRef(interceptor);\n\tinterceptorRef.current = interceptor;\n\n\tconst [collectionCacheEpoch, setCollectionCacheEpoch] = useState(0);\n\tconst collections = useMemo(\n\t\t() => new Map<string, CollectionCacheEntry>(),\n\t\t[],\n\t);\n\n\t/**\n\t * Stable `onOperation` identity when an interceptor is enabled, so `getCollection` does\n\t * not churn on unrelated re-renders (object props are usually a new reference each time).\n\t */\n\tconst stableInterceptor: SQLInterceptor | undefined = useMemo(\n\t\t() =>\n\t\t\tinterceptor\n\t\t\t\t? { onOperation: (op) => interceptorRef.current?.onOperation?.(op) }\n\t\t\t\t: undefined,\n\t\t[!!interceptor],\n\t);\n\n\t// When `drizzle` or options that feed `sqliteCollectionOptions` / TanStack sync change,\n\t// drop cached `createCollection` instances. This `useEffect` **setup** runs *after* the\n\t// commit that already has the new `drizzle` / flags; we `clear` + bump `collectionCacheEpoch`\n\t// so the next `getCollection` / `useSqliteCollection` pass uses a new key and repopulates\n\t// the map with up-to-date options.\n\t//\n\t// `useEffect` **cleanup** (`return () => { ... }`, if we added one) would run *after* a\n\t// *later* commit, immediately **before** the next time this setup runs (next dep change) or\n\t// **on unmount**—not “before the tree sees new values”. Props for a given render are fixed\n\t// for that render; this setup always sees the latest `drizzle` / options from the render\n\t// that just committed. We only need the setup here; the Map is not shared across\n\t// unmounted instances, so we do not rely on cleanup for invalidation.\n\tuseEffect(() => {\n\t\tcollections.clear();\n\t\tsetCollectionCacheEpoch((e) => e + 1);\n\t}, [drizzle, enableCheckpoint, syncMode, debug]);\n\n\tconst getCollection = useCallback(\n\t\t<TTableName extends string & ValidTableNames<TSchema>>(\n\t\t\ttableName: TTableName,\n\t\t): SqliteCollection<TSchema, TTableName> => {\n\t\t\tconst cacheKey = `${collectionCacheEpoch}:${String(tableName)}`;\n\t\t\tif (!collections.has(cacheKey)) {\n\t\t\t\tconst options = sqliteCollectionOptions({\n\t\t\t\t\tdrizzle,\n\t\t\t\t\ttableName: tableName as string &\n\t\t\t\t\t\tValidTableNames<DrizzleSchema<AnyDrizzleDatabase>>,\n\t\t\t\t\treadyPromise,\n\t\t\t\t\tsyncMode,\n\t\t\t\t\tcheckpoint: enableCheckpoint\n\t\t\t\t\t\t? () => sqliteClient.checkpoint()\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\tinterceptor: stableInterceptor,\n\t\t\t\t\tdebug,\n\t\t\t\t});\n\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: Table type degenerates through AnyDrizzleDatabase; collection is re-typed on cache retrieval\n\t\t\t\tconst collection = createCollection(options as any) as Collection<\n\t\t\t\t\tRecord<string, unknown>,\n\t\t\t\t\tstring,\n\t\t\t\t\tUtilsRecord\n\t\t\t\t>;\n\t\t\t\tcollections.set(cacheKey, { collection, refCount: 0 });\n\t\t\t}\n\t\t\t// biome-ignore lint/style/noNonNullAssertion: We just ensured the collection exists\n\t\t\treturn collections.get(cacheKey)!\n\t\t\t\t.collection as unknown as SqliteCollection<TSchema, TTableName>;\n\t\t},\n\t\t[\n\t\t\tcollectionCacheEpoch,\n\t\t\tcollections,\n\t\t\tdrizzle,\n\t\t\treadyPromise,\n\t\t\tsyncMode,\n\t\t\tenableCheckpoint,\n\t\t\tsqliteClient,\n\t\t\tstableInterceptor,\n\t\t\tdebug,\n\t\t],\n\t);\n\n\tconst incrementRefCount: DrizzleSqliteContextValue<TSchema>[\"incrementRefCount\"] =\n\t\tuseCallback(\n\t\t\t(tableName: string) => {\n\t\t\t\tconst k = `${collectionCacheEpoch}:${String(tableName)}`;\n\t\t\t\tconst entry = collections.get(k);\n\t\t\t\tif (entry) {\n\t\t\t\t\tentry.refCount++;\n\t\t\t\t}\n\t\t\t},\n\t\t\t[collectionCacheEpoch, collections],\n\t\t);\n\n\tconst decrementRefCount: DrizzleSqliteContextValue<TSchema>[\"decrementRefCount\"] =\n\t\tuseCallback(\n\t\t\t(tableName: string) => {\n\t\t\t\tconst k = `${collectionCacheEpoch}:${String(tableName)}`;\n\t\t\t\tconst entry = collections.get(k);\n\t\t\t\tif (entry) {\n\t\t\t\t\tentry.refCount--;\n\t\t\t\t\tif (entry.refCount <= 0) {\n\t\t\t\t\t\tcollections.delete(k);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t[collectionCacheEpoch, collections],\n\t\t);\n\n\tconst contextValue: DrizzleSqliteContextValue<TSchema> = useMemo(\n\t\t() => ({\n\t\t\tdrizzle,\n\t\t\treadyPromise,\n\t\t\tsqliteClient,\n\t\t\tcollectionCacheEpoch,\n\t\t\tgetCollection,\n\t\t\tincrementRefCount,\n\t\t\tdecrementRefCount,\n\t\t}),\n\t\t[\n\t\t\tdrizzle,\n\t\t\treadyPromise,\n\t\t\tsqliteClient,\n\t\t\tcollectionCacheEpoch,\n\t\t\tgetCollection,\n\t\t\tincrementRefCount,\n\t\t\tdecrementRefCount,\n\t\t],\n\t);\n\n\treturn (\n\t\t<DrizzleSqliteContext.Provider value={contextValue}>\n\t\t\t{children}\n\t\t</DrizzleSqliteContext.Provider>\n\t);\n}\n\n/**\n * Provides a single SQLite+Worker session for `dbName`. Children that call\n * {@link useDrizzleSqlite} or {@link useSqliteCollection} are only mounted\n * after migrations succeed (see `loadingFallback` while not ready).\n *\n * **Session identity** is `dbName` + `workerOpenOptions` (the ready subtree `key` is derived\n * internally; you do not need to set `key` on this component for normal DB switching).\n */\nexport function DrizzleSqliteProvider<TSchema extends Record<string, unknown>>({\n\tchildren,\n\tworker,\n\tdbName,\n\tschema,\n\tmigrations,\n\tdebug,\n\tenableCheckpoint = false,\n\tloadingFallback,\n\terrorFallback,\n\tsyncMode = \"eager\",\n\tinterceptor,\n\tworkerOpenOptions,\n}: DrizzleSqliteProviderProps<TSchema>) {\n\t/** Drives a fresh `DrizzleSqliteSessionBody` + `children` when the DB file or open options change. */\n\tconst readySubtreeKey = useMemo(\n\t\t() =>\n\t\t\t`drizzle-sqlite:${dbName}:${JSON.stringify(workerOpenOptions ?? null)}`,\n\t\t[dbName, workerOpenOptions],\n\t);\n\n\tconst session = useDrizzleSqliteDb(\n\t\tworker,\n\t\tdbName,\n\t\tschema,\n\t\tmigrations,\n\t\tdebug,\n\t\tinterceptor,\n\t\tworkerOpenOptions,\n\t);\n\tconst { drizzle, readyPromise, sqliteClient, sessionStatus } = session;\n\n\tif (sessionStatus === \"error\") {\n\t\treturn (\n\t\t\t<>\n\t\t\t\t{errorFallback ?? (\n\t\t\t\t\t<div role=\"alert\" data-testid=\"sqlite-db-error\">\n\t\t\t\t\t\t{session.sessionError.message}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</>\n\t\t);\n\t}\n\n\tif (sessionStatus !== \"ready\" || !sqliteClient) {\n\t\treturn <>{loadingFallback}</>;\n\t}\n\n\treturn (\n\t\t<DrizzleSqliteSessionBody\n\t\t\tkey={readySubtreeKey}\n\t\t\tinterceptor={interceptor}\n\t\t\tdebug={debug}\n\t\t\tenableCheckpoint={enableCheckpoint}\n\t\t\tsyncMode={syncMode}\n\t\t\tdrizzle={drizzle}\n\t\t\treadyPromise={readyPromise}\n\t\t\tsqliteClient={sqliteClient}\n\t\t>\n\t\t\t{children}\n\t\t</DrizzleSqliteSessionBody>\n\t);\n}\n\n// Hook that components use to get a collection with automatic ref counting\nexport function useSqliteCollection<\n\tTSchema extends Record<string, unknown>,\n\tTTableName extends string & ValidTableNames<TSchema>,\n>(\n\tcontext: DrizzleSqliteContextValue<TSchema>,\n\ttableName: TTableName,\n): InferCollectionFromTable<GetTableFromSchema<TSchema, TTableName>> {\n\tconst { collection, unsubscribe } = useMemo(() => {\n\t\tconst col = context.getCollection(tableName);\n\t\tcontext.incrementRefCount(tableName);\n\n\t\treturn {\n\t\t\tcollection: col,\n\t\t\tunsubscribe: () => {\n\t\t\t\tcontext.decrementRefCount(tableName);\n\t\t\t},\n\t\t};\n\t\t// Re-bind when a new table collection is required (epoch bump or context swap)\n\t}, [context, tableName, context.collectionCacheEpoch]);\n\n\t// Cleanup on unmount\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tunsubscribe();\n\t\t};\n\t}, [unsubscribe]);\n\n\treturn collection as unknown as InferCollectionFromTable<\n\t\tGetTableFromSchema<TSchema, TTableName>\n\t>;\n}\n"]}
|
|
@@ -119,5 +119,5 @@ var createInstrumentedDrizzle = (client, config = {}, interceptor) => {
|
|
|
119
119
|
};
|
|
120
120
|
|
|
121
121
|
export { createInstrumentedDrizzle, drizzleSqliteWasmWorker };
|
|
122
|
-
//# sourceMappingURL=chunk-
|
|
123
|
-
//# sourceMappingURL=chunk-
|
|
122
|
+
//# sourceMappingURL=chunk-BQBL6E44.js.map
|
|
123
|
+
//# sourceMappingURL=chunk-BQBL6E44.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/drizzle/worker.ts"],"names":["drizzleSqliteProxy"],"mappings":"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","file":"chunk-BQBL6E44.js","sourcesContent":["import type { DrizzleConfig } from \"drizzle-orm\";\nimport { drizzle as drizzleSqliteProxy } from \"drizzle-orm/sqlite-proxy\";\nimport type { ISqliteWorkerClient } from \"../worker/client\";\nimport type { SQLInterceptor, SQLOperation } from \"@firtoz/drizzle-utils\";\n\nexport const drizzleSqliteWasmWorker = <\n\tTSchema extends Record<string, unknown> = Record<string, never>,\n>(\n\tclient: ISqliteWorkerClient,\n\tconfig: DrizzleConfig<TSchema> = {},\n) => {\n\treturn drizzleSqliteProxy<TSchema>(async (sql, params, method) => {\n\t\treturn new Promise<{ rows: unknown[] }>((resolve, reject) => {\n\t\t\tclient.performRemoteCallback(\n\t\t\t\t{\n\t\t\t\t\tsql,\n\t\t\t\t\tparams,\n\t\t\t\t\tmethod,\n\t\t\t\t},\n\t\t\t\tresolve,\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t}, config);\n};\n\n/**\n * Creates an instrumented Drizzle instance that logs all SQL queries.\n * This wraps the standard drizzleSqliteWasmWorker to intercept every query.\n */\nexport const createInstrumentedDrizzle = <\n\tTSchema extends Record<string, unknown> = Record<string, never>,\n>(\n\tclient: ISqliteWorkerClient,\n\tconfig: DrizzleConfig<TSchema> = {},\n\tinterceptor?: SQLInterceptor,\n) => {\n\treturn drizzleSqliteProxy<TSchema>(async (sql, params, method) => {\n\t\tconst startTime = Date.now();\n\n\t\tconst result = await new Promise<{ rows: unknown[] }>((resolve, reject) => {\n\t\t\tclient.performRemoteCallback(\n\t\t\t\t{\n\t\t\t\t\tsql,\n\t\t\t\t\tparams,\n\t\t\t\t\tmethod,\n\t\t\t\t},\n\t\t\t\tresolve,\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\n\t\t// Log the operation if interceptor is provided\n\t\tif (interceptor?.onOperation) {\n\t\t\t// Parse SQL to determine context\n\t\t\tconst sqlLower = sql.toLowerCase().trim();\n\t\t\tlet context = \"Direct Drizzle query\";\n\n\t\t\tif (sqlLower.startsWith(\"select\")) {\n\t\t\t\t// Extract table name from SELECT query\n\t\t\t\tconst fromMatch = sql.match(/from\\s+[\"']?(\\w+)[\"']?/i);\n\t\t\t\tconst tableName = fromMatch?.[1] || \"unknown\";\n\n\t\t\t\t// Check for LIMIT/OFFSET - handle both literal values and ? placeholders\n\t\t\t\t// Drizzle uses parameterized queries, so we need to check for `limit ?` style\n\t\t\t\tconst hasLimit = /limit\\s+(\\d+|\\?|\\$\\d+)/i.test(sql);\n\t\t\t\tconst hasOffset = /offset\\s+(\\d+|\\?|\\$\\d+)/i.test(sql);\n\t\t\t\tconst hasOrderBy = /order\\s+by/i.test(sql);\n\n\t\t\t\tif (hasLimit || hasOffset) {\n\t\t\t\t\t// Extract actual values from params if using placeholders\n\t\t\t\t\t// Drizzle typically puts LIMIT as second-to-last param, OFFSET as last\n\t\t\t\t\tlet limitVal = \"?\";\n\t\t\t\t\tlet offsetVal = \"0\";\n\n\t\t\t\t\t// Try to extract literal values first\n\t\t\t\t\tconst literalLimit = sql.match(/limit\\s+(\\d+)/i);\n\t\t\t\t\tconst literalOffset = sql.match(/offset\\s+(\\d+)/i);\n\n\t\t\t\t\tif (literalLimit) {\n\t\t\t\t\t\tlimitVal = literalLimit[1];\n\t\t\t\t\t} else if (params && params.length > 0) {\n\t\t\t\t\t\t// For parameterized queries, try to infer from params\n\t\t\t\t\t\t// LIMIT/OFFSET are usually at the end\n\t\t\t\t\t\tif (hasLimit && hasOffset && params.length >= 2) {\n\t\t\t\t\t\t\tlimitVal = String(params[params.length - 2]);\n\t\t\t\t\t\t\toffsetVal = String(params[params.length - 1]);\n\t\t\t\t\t\t} else if (hasLimit && params.length >= 1) {\n\t\t\t\t\t\t\tlimitVal = String(params[params.length - 1]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (literalOffset) {\n\t\t\t\t\t\toffsetVal = literalOffset[1];\n\t\t\t\t\t}\n\n\t\t\t\t\tcontext = `SELECT with LIMIT ${limitVal} OFFSET ${offsetVal}`;\n\t\t\t\t} else if (hasOrderBy) {\n\t\t\t\t\tcontext = `SELECT all from ${tableName} (ordered)`;\n\t\t\t\t} else {\n\t\t\t\t\tcontext = `SELECT all from ${tableName}`;\n\t\t\t\t}\n\n\t\t\t\tconst operation: SQLOperation = {\n\t\t\t\t\ttype: \"raw-query\",\n\t\t\t\t\tsql,\n\t\t\t\t\tparams: params as unknown[],\n\t\t\t\t\tmethod,\n\t\t\t\t\trowCount: result.rows?.length ?? 0,\n\t\t\t\t\tcontext,\n\t\t\t\t\ttimestamp: startTime,\n\t\t\t\t};\n\t\t\t\tinterceptor.onOperation(operation);\n\t\t\t} else if (sqlLower.startsWith(\"insert\")) {\n\t\t\t\tconst intoMatch = sql.match(/into\\s+[\"']?(\\w+)[\"']?/i);\n\t\t\t\tcontext = `INSERT into ${intoMatch?.[1] || \"unknown\"}`;\n\n\t\t\t\tconst operation: SQLOperation = {\n\t\t\t\t\ttype: \"raw-query\",\n\t\t\t\t\tsql,\n\t\t\t\t\tparams: params as unknown[],\n\t\t\t\t\tmethod,\n\t\t\t\t\trowCount: 0,\n\t\t\t\t\tcontext,\n\t\t\t\t\ttimestamp: startTime,\n\t\t\t\t};\n\t\t\t\tinterceptor.onOperation(operation);\n\t\t\t} else if (sqlLower.startsWith(\"update\")) {\n\t\t\t\tconst tableMatch = sql.match(/update\\s+[\"']?(\\w+)[\"']?/i);\n\t\t\t\tcontext = `UPDATE ${tableMatch?.[1] || \"unknown\"}`;\n\n\t\t\t\tconst operation: SQLOperation = {\n\t\t\t\t\ttype: \"raw-query\",\n\t\t\t\t\tsql,\n\t\t\t\t\tparams: params as unknown[],\n\t\t\t\t\tmethod,\n\t\t\t\t\trowCount: 0,\n\t\t\t\t\tcontext,\n\t\t\t\t\ttimestamp: startTime,\n\t\t\t\t};\n\t\t\t\tinterceptor.onOperation(operation);\n\t\t\t} else if (sqlLower.startsWith(\"delete\")) {\n\t\t\t\tconst fromMatch = sql.match(/from\\s+[\"']?(\\w+)[\"']?/i);\n\t\t\t\tcontext = `DELETE from ${fromMatch?.[1] || \"unknown\"}`;\n\n\t\t\t\tconst operation: SQLOperation = {\n\t\t\t\t\ttype: \"raw-query\",\n\t\t\t\t\tsql,\n\t\t\t\t\tparams: params as unknown[],\n\t\t\t\t\tmethod,\n\t\t\t\t\trowCount: 0,\n\t\t\t\t\tcontext,\n\t\t\t\t\ttimestamp: startTime,\n\t\t\t\t};\n\t\t\t\tinterceptor.onOperation(operation);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}, config);\n};\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DrizzleSqliteContext, useSqliteCollection } from './chunk-
|
|
1
|
+
import { DrizzleSqliteContext, useSqliteCollection } from './chunk-7TDQNWT6.js';
|
|
2
2
|
import { useContext } from 'react';
|
|
3
3
|
|
|
4
4
|
function useDrizzleSqlite() {
|
|
@@ -18,5 +18,5 @@ function useDrizzleSqlite() {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export { useDrizzleSqlite };
|
|
21
|
-
//# sourceMappingURL=chunk-
|
|
22
|
-
//# sourceMappingURL=chunk-
|
|
21
|
+
//# sourceMappingURL=chunk-EQOHJW3Q.js.map
|
|
22
|
+
//# sourceMappingURL=chunk-EQOHJW3Q.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context/useDrizzleSqlite.ts"],"names":[],"mappings":";;;AAgBO,SAAS,gBAAA,GAEqB;AACpC,EAAA,MAAM,OAAA,GAAU,UAAA;AAAA,IACf;AAAA,GACD;AAEA,EAAA,IAAI,CAAC,OAAA,EAAS;AACb,IAAA,MAAM,IAAI,KAAA;AAAA,MACT;AAAA,KACD;AAAA,EACD;AAEA,EAAA,OAAO;AAAA,IACN,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,cAAc,OAAA,CAAQ,YAAA;AAAA,IACtB,aAAA,EAAe,CACd,SAAA,KACI,mBAAA,CAAoB,SAAS,SAAS;AAAA,GAC5C;AACD","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/context/useDrizzleSqlite.ts"],"names":[],"mappings":";;;AAgBO,SAAS,gBAAA,GAEqB;AACpC,EAAA,MAAM,OAAA,GAAU,UAAA;AAAA,IACf;AAAA,GACD;AAEA,EAAA,IAAI,CAAC,OAAA,EAAS;AACb,IAAA,MAAM,IAAI,KAAA;AAAA,MACT;AAAA,KACD;AAAA,EACD;AAEA,EAAA,OAAO;AAAA,IACN,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,cAAc,OAAA,CAAQ,YAAA;AAAA,IACtB,aAAA,EAAe,CACd,SAAA,KACI,mBAAA,CAAoB,SAAS,SAAS;AAAA,GAC5C;AACD","file":"chunk-EQOHJW3Q.js","sourcesContent":["import { useContext } from \"react\";\nimport type { DrizzleSqliteContextValue } from \"./DrizzleSqliteProvider\";\nimport {\n\tDrizzleSqliteContext,\n\tuseSqliteCollection,\n} from \"./DrizzleSqliteProvider\";\nimport type { ValidTableNames } from \"../collections/sqlite-collection\";\n\nexport type UseDrizzleSqliteReturn<TSchema extends Record<string, unknown>> = {\n\tdrizzle: DrizzleSqliteContextValue<TSchema>[\"drizzle\"];\n\treadyPromise: DrizzleSqliteContextValue<TSchema>[\"readyPromise\"];\n\tuseCollection: <TTableName extends string & ValidTableNames<TSchema>>(\n\t\ttableName: TTableName,\n\t) => ReturnType<typeof useSqliteCollection<TSchema, TTableName>>;\n};\n\nexport function useDrizzleSqlite<\n\tTSchema extends Record<string, unknown>,\n>(): UseDrizzleSqliteReturn<TSchema> {\n\tconst context = useContext(\n\t\tDrizzleSqliteContext,\n\t) as DrizzleSqliteContextValue<TSchema> | null;\n\n\tif (!context) {\n\t\tthrow new Error(\n\t\t\t\"useDrizzleSqlite must be used within a DrizzleSqliteProvider\",\n\t\t);\n\t}\n\n\treturn {\n\t\tdrizzle: context.drizzle,\n\t\treadyPromise: context.readyPromise,\n\t\tuseCollection: <TTableName extends string & ValidTableNames<TSchema>>(\n\t\t\ttableName: TTableName,\n\t\t) => useSqliteCollection(context, tableName),\n\t};\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { sqliteCollectionOptions } from './chunk-
|
|
1
|
+
import { sqliteCollectionOptions } from './chunk-VLFDMAFH.js';
|
|
2
2
|
import { createSyncedCollection } from '@firtoz/collection-sync';
|
|
3
3
|
|
|
4
4
|
function createSyncedSqliteCollection(config, syncOptions) {
|
|
@@ -15,5 +15,5 @@ function createSyncedSqliteCollection(config, syncOptions) {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export { createSyncedSqliteCollection };
|
|
18
|
-
//# sourceMappingURL=chunk-
|
|
19
|
-
//# sourceMappingURL=chunk-
|
|
18
|
+
//# sourceMappingURL=chunk-NNPU7YTX.js.map
|
|
19
|
+
//# sourceMappingURL=chunk-NNPU7YTX.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/collections/synced-sqlite-collection.ts"],"names":[],"mappings":";;;AAsBO,SAAS,4BAAA,CAKf,QACA,WAAA,EAKC;AAGD,EAAA,MAAM,OAAA,GAAU,wBAAwB,MAAM,CAAA;AAC9C,EAAA,MAAM,EAAE,UAAA,EAAY,MAAA,EAAQ,gBAAA,EAAiB,GAAI,sBAAA;AAAA,IAChD,OAAA;AAAA,IACA;AAAA,GACD;AACA,EAAA,OAAO;AAAA,IACN,UAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACD;AACD","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/collections/synced-sqlite-collection.ts"],"names":[],"mappings":";;;AAsBO,SAAS,4BAAA,CAKf,QACA,WAAA,EAKC;AAGD,EAAA,MAAM,OAAA,GAAU,wBAAwB,MAAM,CAAA;AAC9C,EAAA,MAAM,EAAE,UAAA,EAAY,MAAA,EAAQ,gBAAA,EAAiB,GAAI,sBAAA;AAAA,IAChD,OAAA;AAAA,IACA;AAAA,GACD;AACA,EAAA,OAAO;AAAA,IACN,UAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACD;AACD","file":"chunk-NNPU7YTX.js","sourcesContent":["import {\n\tcreateSyncedCollection,\n\ttype SyncableCollectionItem,\n\ttype SyncClientBridge,\n\ttype SyncClientMessage,\n\ttype WithSyncOptions,\n} from \"@firtoz/collection-sync\";\nimport type { Collection } from \"@tanstack/db\";\nimport type { TableWithRequiredFields } from \"@firtoz/drizzle-utils\";\nimport type { InferSelectModel } from \"drizzle-orm\";\nimport type {\n\tAnyDrizzleDatabase,\n\tDrizzleSchema,\n\tDrizzleSqliteCollectionConfig,\n\tValidTableNames,\n} from \"./sqlite-collection\";\nimport { sqliteCollectionOptions } from \"./sqlite-collection\";\n\n/**\n * Like {@link createSyncedCollection} from `@firtoz/collection-sync`, but row type uses Drizzle’s\n * {@link InferSelectModel} so branded columns (e.g. ids) match `$inferSelect`, not Valibot schema output.\n */\nexport function createSyncedSqliteCollection<\n\tconst TDrizzle extends AnyDrizzleDatabase,\n\tconst TTableName extends string & ValidTableNames<DrizzleSchema<TDrizzle>>,\n\tTTable extends DrizzleSchema<TDrizzle>[TTableName] & TableWithRequiredFields,\n>(\n\tconfig: DrizzleSqliteCollectionConfig<TDrizzle, TTableName>,\n\tsyncOptions?: WithSyncOptions,\n): {\n\tcollection: Collection<InferSelectModel<TTable>>;\n\tbridge: SyncClientBridge<InferSelectModel<TTable> & SyncableCollectionItem>;\n\tsetTransportSend: (send: (msg: SyncClientMessage) => void) => void;\n} {\n\ttype TRow = InferSelectModel<TTable>;\n\ttype TBridgeItem = TRow & SyncableCollectionItem;\n\tconst options = sqliteCollectionOptions(config);\n\tconst { collection, bridge, setTransportSend } = createSyncedCollection(\n\t\toptions,\n\t\tsyncOptions,\n\t);\n\treturn {\n\t\tcollection: collection as unknown as Collection<TRow>,\n\t\tbridge: bridge as unknown as SyncClientBridge<TBridgeItem>,\n\t\tsetTransportSend,\n\t};\n}\n"]}
|
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
import { customSqliteMigrate } from './chunk-NSPVPJKE.js';
|
|
2
2
|
import { isSqliteWorkerInitialized, initializeSqliteWorker } from './chunk-FIVEPVOM.js';
|
|
3
|
-
import { createInstrumentedDrizzle, drizzleSqliteWasmWorker } from './chunk-
|
|
3
|
+
import { createInstrumentedDrizzle, drizzleSqliteWasmWorker } from './chunk-BQBL6E44.js';
|
|
4
4
|
import { useRef, useState, useMemo, useEffect } from 'react';
|
|
5
5
|
|
|
6
|
+
function isNodeTestRuntime() {
|
|
7
|
+
if (typeof window !== "undefined") {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
if (typeof process === "undefined" || typeof process.env === "undefined") {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const e = process.env;
|
|
14
|
+
if (e.NODE_ENV === "test") {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
if (e.VITEST !== void 0) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
function toDrizzleSqliteSessionError(caught) {
|
|
23
|
+
const message = caught instanceof Error ? caught.message : typeof caught === "string" ? caught : "Database error";
|
|
24
|
+
return { kind: "migration_failed", message, original: caught };
|
|
25
|
+
}
|
|
6
26
|
var useDrizzleSqliteDb = (WorkerConstructor, dbName, schema, migrations, debug, interceptor, workerOpenOptions) => {
|
|
7
27
|
const resolveRef = useRef(null);
|
|
8
28
|
const rejectRef = useRef(null);
|
|
29
|
+
const [internalSession, setInternalSession] = useState(
|
|
30
|
+
() => ({ status: "connecting", error: null })
|
|
31
|
+
);
|
|
9
32
|
const [sqliteClient, setSqliteClient] = useState(
|
|
10
33
|
null
|
|
11
34
|
);
|
|
@@ -15,20 +38,27 @@ var useDrizzleSqliteDb = (WorkerConstructor, dbName, schema, migrations, debug,
|
|
|
15
38
|
resolveRef.current = resolve;
|
|
16
39
|
rejectRef.current = reject;
|
|
17
40
|
});
|
|
18
|
-
}, []);
|
|
41
|
+
}, [dbName]);
|
|
19
42
|
useEffect(() => {
|
|
20
43
|
if (typeof window === "undefined") {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
44
|
+
if (isNodeTestRuntime()) {
|
|
45
|
+
setInternalSession({ status: "ready", error: null });
|
|
46
|
+
setSqliteClient({
|
|
47
|
+
performRemoteCallback: () => {
|
|
48
|
+
},
|
|
49
|
+
checkpoint: () => Promise.resolve(),
|
|
50
|
+
onStarted: () => {
|
|
51
|
+
},
|
|
52
|
+
terminate: () => {
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
queueMicrotask(() => {
|
|
56
|
+
resolveRef.current?.();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
30
59
|
return;
|
|
31
60
|
}
|
|
61
|
+
setInternalSession({ status: "connecting", error: null });
|
|
32
62
|
let mounted = true;
|
|
33
63
|
const init = async () => {
|
|
34
64
|
if (!isSqliteWorkerInitialized()) {
|
|
@@ -97,6 +127,9 @@ var useDrizzleSqliteDb = (WorkerConstructor, dbName, schema, migrations, debug,
|
|
|
97
127
|
return drizzleSqliteWasmWorker(client, { schema });
|
|
98
128
|
}, [schema, dbName, !!interceptor]);
|
|
99
129
|
useEffect(() => {
|
|
130
|
+
if (typeof window === "undefined") {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
100
133
|
if (!sqliteClient) {
|
|
101
134
|
if (debug) {
|
|
102
135
|
console.log(`[DEBUG] ${dbName} - waiting for sqliteClient...`);
|
|
@@ -104,21 +137,43 @@ var useDrizzleSqliteDb = (WorkerConstructor, dbName, schema, migrations, debug,
|
|
|
104
137
|
return;
|
|
105
138
|
}
|
|
106
139
|
sqliteClient.onStarted(async () => {
|
|
140
|
+
if (typeof window === "undefined") {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
107
143
|
try {
|
|
144
|
+
setInternalSession({ status: "connecting", error: null });
|
|
108
145
|
await customSqliteMigrate(drizzle, migrations);
|
|
109
146
|
resolveRef.current?.();
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
147
|
+
setInternalSession({ status: "ready", error: null });
|
|
148
|
+
} catch (caught) {
|
|
149
|
+
console.error(`Migration error for ${dbName}:`, caught);
|
|
150
|
+
const err = toDrizzleSqliteSessionError(caught);
|
|
151
|
+
setInternalSession({ status: "error", error: err });
|
|
152
|
+
rejectRef.current?.(caught);
|
|
113
153
|
}
|
|
114
154
|
});
|
|
115
155
|
return () => {
|
|
116
156
|
sqliteClient.terminate();
|
|
117
157
|
};
|
|
118
|
-
}, [sqliteClient, drizzle, migrations, dbName]);
|
|
119
|
-
|
|
158
|
+
}, [sqliteClient, drizzle, migrations, dbName, debug]);
|
|
159
|
+
if (internalSession.status === "error") {
|
|
160
|
+
return {
|
|
161
|
+
drizzle,
|
|
162
|
+
readyPromise,
|
|
163
|
+
sqliteClient,
|
|
164
|
+
sessionStatus: "error",
|
|
165
|
+
sessionError: internalSession.error
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
drizzle,
|
|
170
|
+
readyPromise,
|
|
171
|
+
sqliteClient,
|
|
172
|
+
sessionStatus: internalSession.status,
|
|
173
|
+
sessionError: null
|
|
174
|
+
};
|
|
120
175
|
};
|
|
121
176
|
|
|
122
|
-
export { useDrizzleSqliteDb };
|
|
123
|
-
//# sourceMappingURL=chunk-
|
|
124
|
-
//# sourceMappingURL=chunk-
|
|
177
|
+
export { toDrizzleSqliteSessionError, useDrizzleSqliteDb };
|
|
178
|
+
//# sourceMappingURL=chunk-QOFRLODK.js.map
|
|
179
|
+
//# sourceMappingURL=chunk-QOFRLODK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useDrizzleSqliteDb.ts"],"names":[],"mappings":";;;;;AAyBA,SAAS,iBAAA,GAA6B;AACrC,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAClC,IAAA,OAAO,KAAA;AAAA,EACR;AACA,EAAA,IAAI,OAAO,OAAA,KAAY,WAAA,IAAe,OAAO,OAAA,CAAQ,QAAQ,WAAA,EAAa;AACzE,IAAA,OAAO,KAAA;AAAA,EACR;AACA,EAAA,MAAM,IAAI,OAAA,CAAQ,GAAA;AAClB,EAAA,IAAI,CAAA,CAAE,aAAa,MAAA,EAAQ;AAC1B,IAAA,OAAO,IAAA;AAAA,EACR;AAEA,EAAA,IAAI,CAAA,CAAE,WAAW,MAAA,EAAW;AAC3B,IAAA,OAAO,IAAA;AAAA,EACR;AACA,EAAA,OAAO,KAAA;AACR;AAsBO,SAAS,4BACf,MAAA,EAC4B;AAC5B,EAAA,MAAM,OAAA,GACL,kBAAkB,KAAA,GACf,MAAA,CAAO,UACP,OAAO,MAAA,KAAW,WACjB,MAAA,GACA,gBAAA;AACL,EAAA,OAAO,EAAE,IAAA,EAAM,kBAAA,EAAoB,OAAA,EAAS,UAAU,MAAA,EAAO;AAC9D;AAsBO,IAAM,kBAAA,GAAqB,CACjC,iBAAA,EACA,MAAA,EACA,QACA,UAAA,EACA,KAAA,EAEA,aAKA,iBAAA,KACuC;AACvC,EAAA,MAAM,UAAA,GAAa,OAA4B,IAAI,CAAA;AACnD,EAAA,MAAM,SAAA,GAAY,OAA0C,IAAI,CAAA;AAIhE,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,QAAA;AAAA,IAC7C,OAAO,EAAE,MAAA,EAAQ,YAAA,EAAc,OAAO,IAAA,EAAK;AAAA,GAC5C;AACA,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,QAAA;AAAA,IACvC;AAAA,GACD;AACA,EAAA,MAAM,eAAA,GAAkB,OAAmC,IAAI,CAAA;AAG/D,EAAA,MAAM,YAAA,GAAe,QAAQ,MAAM;AAClC,IAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AAC7C,MAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AACrB,MAAA,SAAA,CAAU,OAAA,GAAU,MAAA;AAAA,IACrB,CAAC,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,EAAA,SAAA,CAAU,MAAM;AACf,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAClC,MAAA,IAAI,mBAAkB,EAAG;AAExB,QAAA,kBAAA,CAAmB,EAAE,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,MAAM,CAAA;AACnD,QAAA,eAAA,CAAgB;AAAA,UACf,uBAAuB,MAAM;AAAA,UAAC,CAAA;AAAA,UAC9B,UAAA,EAAY,MAAM,OAAA,CAAQ,OAAA,EAAQ;AAAA,UAClC,WAAW,MAAM;AAAA,UAAC,CAAA;AAAA,UAClB,WAAW,MAAM;AAAA,UAAC;AAAA,SAClB,CAAA;AACD,QAAA,cAAA,CAAe,MAAM;AACpB,UAAA,UAAA,CAAW,OAAA,IAAU;AAAA,QACtB,CAAC,CAAA;AAAA,MACF;AACA,MAAA;AAAA,IACD;AAEA,IAAA,kBAAA,CAAmB,EAAE,MAAA,EAAQ,YAAA,EAAc,KAAA,EAAO,MAAM,CAAA;AAExD,IAAA,IAAI,OAAA,GAAU,IAAA;AAEd,IAAA,MAAM,OAAO,YAAY;AAExB,MAAA,IAAI,CAAC,2BAA0B,EAAG;AACjC,QAAA,MAAM,uBAAuB,iBAAiB,CAAA;AAAA,MAC/C;AAGA,MAAA,MAAM,EAAE,sBAAA,EAAuB,GAAI,MAAM,OACxC,4BACD,CAAA;AACA,MAAA,MAAM,UAAU,sBAAA,EAAuB;AACvC,MAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,aAAA,CAAc,QAAQ,iBAAiB,CAAA;AAEtE,MAAA,IAAI,OAAA,EAAS;AACZ,QAAA,eAAA,CAAgB,OAAA,GAAU,QAAA;AAC1B,QAAA,eAAA,CAAgB,QAAQ,CAAA;AAAA,MACzB;AAAA,IACD,CAAA;AAEA,IAAA,IAAA,EAAK;AAEL,IAAA,OAAO,MAAM;AACZ,MAAA,OAAA,GAAU,KAAA;AAAA,IACX,CAAA;AAAA,EACD,CAAA,EAAG,CAAC,MAAA,EAAQ,iBAAA,EAAmB,iBAAiB,CAAC,CAAA;AAGjD,EAAA,MAAM,cAAA,GAAiB,OAAO,WAAW,CAAA;AACzC,EAAA,cAAA,CAAe,OAAA,GAAU,WAAA;AAIzB,EAAA,MAAM,OAAA,GAAU,QAAQ,MAAM;AAC7B,IAAA,IAAI,KAAA,EAAO;AACV,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,QAAA,EAAW,MAAM,CAAA,iCAAA,CAAmC,CAAA;AAAA,IACjE;AAEA,IAAA,MAAM,MAAA,GAA8B;AAAA,MACnC,qBAAA,EAAuB,CAAC,IAAA,EAAM,OAAA,EAAS,MAAA,KAAW;AACjD,QAAA,MAAM,eAAe,eAAA,CAAgB,OAAA;AACrC,QAAA,IAAI,CAAC,YAAA,EAAc;AAClB,UAAA,OAAA,CAAQ,KAAA;AAAA,YACP,WAAW,MAAM,CAAA,uDAAA;AAAA,WAClB;AACA,UAAA,MAAA;AAAA,YACC,IAAI,KAAA,CAAM,CAAA,SAAA,EAAY,MAAM,CAAA,mCAAA,CAAqC;AAAA,WAClE;AACA,UAAA;AAAA,QACD;AACA,QAAA,YAAA,CAAa,qBAAA,CAAsB,IAAA,EAAM,OAAA,EAAS,MAAM,CAAA;AAAA,MACzD,CAAA;AAAA,MACA,SAAA,EAAW,CAAC,QAAA,KAAa;AACxB,QAAA,MAAM,eAAe,eAAA,CAAgB,OAAA;AACrC,QAAA,IAAI,CAAC,YAAA,EAAc;AAClB,UAAA,OAAA,CAAQ,IAAA;AAAA,YACP,WAAW,MAAM,CAAA,2CAAA;AAAA,WAClB;AACA,UAAA;AAAA,QACD;AACA,QAAA,YAAA,CAAa,UAAU,QAAQ,CAAA;AAAA,MAChC,CAAA;AAAA,MACA,WAAW,MAAM;AAChB,QAAA,eAAA,CAAgB,SAAS,SAAA,EAAU;AAAA,MACpC,CAAA;AAAA,MACA,YAAY,MAAM;AACjB,QAAA,OAAO,eAAA,CAAgB,OAAA,EAAS,UAAA,EAAW,IAAK,QAAQ,OAAA,EAAQ;AAAA,MACjE;AAAA,KACD;AAIA,IAAA,MAAM,kBAAA,GAAqC;AAAA,MAC1C,aAAa,CAAC,EAAA,KAAO,cAAA,CAAe,OAAA,EAAS,cAAc,EAAE;AAAA,KAC9D;AAGA,IAAA,IAAI,WAAA,EAAa;AAChB,MAAA,OAAO,yBAAA;AAAA,QACN,MAAA;AAAA,QACA,EAAE,MAAA,EAAO;AAAA,QACT;AAAA,OACD;AAAA,IACD;AAEA,IAAA,OAAO,uBAAA,CAAiC,MAAA,EAAQ,EAAE,MAAA,EAAQ,CAAA;AAAA,EAC3D,GAAG,CAAC,MAAA,EAAQ,QAAQ,CAAC,CAAC,WAAW,CAAC,CAAA;AAElC,EAAA,SAAA,CAAU,MAAM;AACf,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAClC,MAAA;AAAA,IACD;AACA,IAAA,IAAI,CAAC,YAAA,EAAc;AAClB,MAAA,IAAI,KAAA,EAAO;AACV,QAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,QAAA,EAAW,MAAM,CAAA,8BAAA,CAAgC,CAAA;AAAA,MAC9D;AACA,MAAA;AAAA,IACD;AAEA,IAAA,YAAA,CAAa,UAAU,YAAY;AAClC,MAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAClC,QAAA;AAAA,MACD;AACA,MAAA,IAAI;AACH,QAAA,kBAAA,CAAmB,EAAE,MAAA,EAAQ,YAAA,EAAc,KAAA,EAAO,MAAM,CAAA;AACxD,QAAA,MAAM,mBAAA,CAAoB,SAAS,UAAU,CAAA;AAC7C,QAAA,UAAA,CAAW,OAAA,IAAU;AACrB,QAAA,kBAAA,CAAmB,EAAE,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,MAAM,CAAA;AAAA,MACpD,SAAS,MAAA,EAAQ;AAChB,QAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,oBAAA,EAAuB,MAAM,CAAA,CAAA,CAAA,EAAK,MAAM,CAAA;AACtD,QAAA,MAAM,GAAA,GAAM,4BAA4B,MAAM,CAAA;AAC9C,QAAA,kBAAA,CAAmB,EAAE,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,KAAK,CAAA;AAClD,QAAA,SAAA,CAAU,UAAU,MAAM,CAAA;AAAA,MAC3B;AAAA,IACD,CAAC,CAAA;AAED,IAAA,OAAO,MAAM;AACZ,MAAA,YAAA,CAAa,SAAA,EAAU;AAAA,IACxB,CAAA;AAAA,EACD,GAAG,CAAC,YAAA,EAAc,SAAS,UAAA,EAAY,MAAA,EAAQ,KAAK,CAAC,CAAA;AAErD,EAAA,IAAI,eAAA,CAAgB,WAAW,OAAA,EAAS;AACvC,IAAA,OAAO;AAAA,MACN,OAAA;AAAA,MACA,YAAA;AAAA,MACA,YAAA;AAAA,MACA,aAAA,EAAe,OAAA;AAAA,MACf,cAAc,eAAA,CAAgB;AAAA,KAC/B;AAAA,EACD;AACA,EAAA,OAAO;AAAA,IACN,OAAA;AAAA,IACA,YAAA;AAAA,IACA,YAAA;AAAA,IACA,eAAe,eAAA,CAAgB,MAAA;AAAA,IAC/B,YAAA,EAAc;AAAA,GACf;AACD","file":"chunk-QOFRLODK.js","sourcesContent":["import { useEffect, useMemo, useRef, useState } from \"react\";\nimport type { SqliteRemoteDatabase } from \"drizzle-orm/sqlite-proxy\";\nimport {\n\tcustomSqliteMigrate,\n\ttype DurableSqliteMigrationConfig,\n} from \"../migration/migrator\";\nimport {\n\tdrizzleSqliteWasmWorker,\n\tcreateInstrumentedDrizzle,\n} from \"../drizzle/worker\";\nimport type { ISqliteWorkerClient } from \"../worker/manager\";\nimport {\n\tinitializeSqliteWorker,\n\tisSqliteWorkerInitialized,\n} from \"../worker/global-manager\";\nimport type { SQLInterceptor } from \"@firtoz/drizzle-utils\";\nimport type { SqliteWasmWorkerOpenOptions } from \"../worker/sqlite-open-options\";\n\n/**\n * `useEffect` can run in Node (e.g. unit tests) with no `window`. We only install the no-op\n * DB stub there when a test runner is active — not for arbitrary headless Node usage.\n *\n * Playwright E2E is irrelevant here: the app under test runs in a real browser (`window` exists),\n * so the normal client path is used, not this stub.\n */\nfunction isNodeTestRuntime(): boolean {\n\tif (typeof window !== \"undefined\") {\n\t\treturn false;\n\t}\n\tif (typeof process === \"undefined\" || typeof process.env === \"undefined\") {\n\t\treturn false;\n\t}\n\tconst e = process.env;\n\tif (e.NODE_ENV === \"test\") {\n\t\treturn true;\n\t}\n\t// Vitest sets this; avoids relying on NODE_ENV alone\n\tif (e.VITEST !== undefined) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/**\n * `connecting` — no worker client yet, or migrations not finished.\n * `ready` — migrations applied, `readyPromise` resolved.\n * `error` — migration (or init) failed; see `sessionError`.\n */\nexport type DrizzleSqliteSessionStatus = \"connecting\" | \"ready\" | \"error\";\n\n/**\n * Error payload when the hook reports `sessionStatus: \"error\"`.\n * Add further `| { kind: … }` members later; discriminate on `kind` in UI or logging.\n */\nexport type DrizzleSqliteSessionError = {\n\tkind: \"migration_failed\";\n\t/** String for display or logs */\n\tmessage: string;\n\t/** The value that was thrown (often an `Error`) */\n\toriginal: unknown;\n};\n\n/** Normalises `catch` bindings so UI can rely on a stable shape. */\nexport function toDrizzleSqliteSessionError(\n\tcaught: unknown,\n): DrizzleSqliteSessionError {\n\tconst message =\n\t\tcaught instanceof Error\n\t\t\t? caught.message\n\t\t\t: typeof caught === \"string\"\n\t\t\t\t? caught\n\t\t\t\t: \"Database error\";\n\treturn { kind: \"migration_failed\", message, original: caught };\n}\n\ntype InternalSessionState =\n\t| { status: \"connecting\" | \"ready\"; error: null }\n\t| { status: \"error\"; error: DrizzleSqliteSessionError };\n\nexport type UseDrizzleSqliteDbResult<TSchema extends Record<string, unknown>> =\n\t| {\n\t\t\tdrizzle: SqliteRemoteDatabase<TSchema>;\n\t\t\treadyPromise: Promise<void>;\n\t\t\tsqliteClient: ISqliteWorkerClient | null;\n\t\t\tsessionStatus: \"connecting\" | \"ready\";\n\t\t\tsessionError: null;\n\t }\n\t| {\n\t\t\tdrizzle: SqliteRemoteDatabase<TSchema>;\n\t\t\treadyPromise: Promise<void>;\n\t\t\tsqliteClient: ISqliteWorkerClient | null;\n\t\t\tsessionStatus: \"error\";\n\t\t\tsessionError: DrizzleSqliteSessionError;\n\t };\n\nexport const useDrizzleSqliteDb = <TSchema extends Record<string, unknown>>(\n\tWorkerConstructor: new () => Worker,\n\tdbName: string,\n\tschema: TSchema,\n\tmigrations: DurableSqliteMigrationConfig,\n\tdebug?: boolean,\n\t/** Optional interceptor to log ALL SQL queries (including direct Drizzle queries) */\n\tinterceptor?: SQLInterceptor,\n\t/**\n\t * Pragmas applied when the worker first opens this `dbName` in the session.\n\t * Ignored if that database was already started (same global worker + dbName).\n\t */\n\tworkerOpenOptions?: SqliteWasmWorkerOpenOptions,\n): UseDrizzleSqliteDbResult<TSchema> => {\n\tconst resolveRef = useRef<null | (() => void)>(null);\n\tconst rejectRef = useRef<null | ((error: unknown) => void)>(null);\n\t// \"ready\" = migrations done and `readyPromise` resolved (or the rare Node `useEffect` stub).\n\t// Start as \"connecting\" on every first paint so we do not show \"ready\" and then go backward\n\t// to \"connecting\" when the first client `useEffect` runs.\n\tconst [internalSession, setInternalSession] = useState<InternalSessionState>(\n\t\t() => ({ status: \"connecting\", error: null }),\n\t);\n\tconst [sqliteClient, setSqliteClient] = useState<ISqliteWorkerClient | null>(\n\t\tnull,\n\t);\n\tconst sqliteClientRef = useRef<ISqliteWorkerClient | null>(null);\n\n\t/** New promise per logical DB open so a resolved session does not mask the next `dbName`. */\n\tconst readyPromise = useMemo(() => {\n\t\treturn new Promise<void>((resolve, reject) => {\n\t\t\tresolveRef.current = resolve;\n\t\t\trejectRef.current = reject;\n\t\t});\n\t}, [dbName]);\n\n\t// Initialize the global manager and get db instance\n\tuseEffect(() => {\n\t\tif (typeof window === \"undefined\") {\n\t\t\tif (isNodeTestRuntime()) {\n\t\t\t\t// e.g. Vitest without jsdom: no worker; unlock `ready` + `readyPromise` for the test tree\n\t\t\t\tsetInternalSession({ status: \"ready\", error: null });\n\t\t\t\tsetSqliteClient({\n\t\t\t\t\tperformRemoteCallback: () => {},\n\t\t\t\t\tcheckpoint: () => Promise.resolve(),\n\t\t\t\t\tonStarted: () => {},\n\t\t\t\t\tterminate: () => {},\n\t\t\t\t});\n\t\t\t\tqueueMicrotask(() => {\n\t\t\t\t\tresolveRef.current?.();\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tsetInternalSession({ status: \"connecting\", error: null });\n\n\t\tlet mounted = true;\n\n\t\tconst init = async () => {\n\t\t\t// Initialize manager if not already initialized\n\t\t\tif (!isSqliteWorkerInitialized()) {\n\t\t\t\tawait initializeSqliteWorker(WorkerConstructor);\n\t\t\t}\n\n\t\t\t// Get manager and create db instance\n\t\t\tconst { getSqliteWorkerManager } = await import(\n\t\t\t\t\"../worker/global-manager\"\n\t\t\t);\n\t\t\tconst manager = getSqliteWorkerManager();\n\t\t\tconst instance = await manager.getDbInstance(dbName, workerOpenOptions);\n\n\t\t\tif (mounted) {\n\t\t\t\tsqliteClientRef.current = instance;\n\t\t\t\tsetSqliteClient(instance);\n\t\t\t}\n\t\t};\n\n\t\tinit();\n\n\t\treturn () => {\n\t\t\tmounted = false;\n\t\t};\n\t}, [dbName, WorkerConstructor, workerOpenOptions]);\n\n\t// Store interceptor in a ref to avoid recreating drizzle on interceptor changes\n\tconst interceptorRef = useRef(interceptor);\n\tinterceptorRef.current = interceptor;\n\n\t// Create drizzle instance with a callback-based approach that waits for the client\n\t// Use instrumented version if interceptor is provided to log ALL queries\n\tconst drizzle = useMemo(() => {\n\t\tif (debug) {\n\t\t\tconsole.log(`[DEBUG] ${dbName} - creating drizzle proxy wrapper`);\n\t\t}\n\n\t\tconst client: ISqliteWorkerClient = {\n\t\t\tperformRemoteCallback: (data, resolve, reject) => {\n\t\t\t\tconst actualClient = sqliteClientRef.current;\n\t\t\t\tif (!actualClient) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`[DEBUG] ${dbName} - performRemoteCallback called but no sqliteClient yet`,\n\t\t\t\t\t);\n\t\t\t\t\treject(\n\t\t\t\t\t\tnew Error(`Database ${dbName} not ready yet - still initializing`),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tactualClient.performRemoteCallback(data, resolve, reject);\n\t\t\t},\n\t\t\tonStarted: (callback) => {\n\t\t\t\tconst actualClient = sqliteClientRef.current;\n\t\t\t\tif (!actualClient) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[DEBUG] ${dbName} - onStarted called but no sqliteClient yet`,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tactualClient.onStarted(callback);\n\t\t\t},\n\t\t\tterminate: () => {\n\t\t\t\tsqliteClientRef.current?.terminate();\n\t\t\t},\n\t\t\tcheckpoint: () => {\n\t\t\t\treturn sqliteClientRef.current?.checkpoint() ?? Promise.resolve();\n\t\t\t},\n\t\t};\n\n\t\t// Use instrumented version if interceptor is provided\n\t\t// Use a wrapper that accesses the ref so interceptor changes don't recreate drizzle\n\t\tconst interceptorWrapper: SQLInterceptor = {\n\t\t\tonOperation: (op) => interceptorRef.current?.onOperation?.(op),\n\t\t};\n\n\t\t// Always use instrumented if initial interceptor was provided\n\t\tif (interceptor) {\n\t\t\treturn createInstrumentedDrizzle<TSchema>(\n\t\t\t\tclient,\n\t\t\t\t{ schema },\n\t\t\t\tinterceptorWrapper,\n\t\t\t);\n\t\t}\n\n\t\treturn drizzleSqliteWasmWorker<TSchema>(client, { schema });\n\t}, [schema, dbName, !!interceptor]); // Only recreate if interceptor presence changes, not on every render\n\n\tuseEffect(() => {\n\t\tif (typeof window === \"undefined\") {\n\t\t\treturn;\n\t\t}\n\t\tif (!sqliteClient) {\n\t\t\tif (debug) {\n\t\t\t\tconsole.log(`[DEBUG] ${dbName} - waiting for sqliteClient...`);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tsqliteClient.onStarted(async () => {\n\t\t\tif (typeof window === \"undefined\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsetInternalSession({ status: \"connecting\", error: null });\n\t\t\t\tawait customSqliteMigrate(drizzle, migrations);\n\t\t\t\tresolveRef.current?.();\n\t\t\t\tsetInternalSession({ status: \"ready\", error: null });\n\t\t\t} catch (caught) {\n\t\t\t\tconsole.error(`Migration error for ${dbName}:`, caught);\n\t\t\t\tconst err = toDrizzleSqliteSessionError(caught);\n\t\t\t\tsetInternalSession({ status: \"error\", error: err });\n\t\t\t\trejectRef.current?.(caught);\n\t\t\t}\n\t\t});\n\n\t\treturn () => {\n\t\t\tsqliteClient.terminate();\n\t\t};\n\t}, [sqliteClient, drizzle, migrations, dbName, debug]);\n\n\tif (internalSession.status === \"error\") {\n\t\treturn {\n\t\t\tdrizzle,\n\t\t\treadyPromise,\n\t\t\tsqliteClient,\n\t\t\tsessionStatus: \"error\" as const,\n\t\t\tsessionError: internalSession.error,\n\t\t};\n\t}\n\treturn {\n\t\tdrizzle,\n\t\treadyPromise,\n\t\tsqliteClient,\n\t\tsessionStatus: internalSession.status,\n\t\tsessionError: null,\n\t};\n};\n"]}
|
|
@@ -45,5 +45,5 @@ function sqliteCollectionOptions(config) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
export { sqliteCollectionOptions };
|
|
48
|
-
//# sourceMappingURL=chunk-
|
|
49
|
-
//# sourceMappingURL=chunk-
|
|
48
|
+
//# sourceMappingURL=chunk-VLFDMAFH.js.map
|
|
49
|
+
//# sourceMappingURL=chunk-VLFDMAFH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/collections/sqlite-collection.ts"],"names":[],"mappings":";;;AA6EO,SAAS,wBAKf,MAAA,EACiC;AACjC,EAAA,MAAM,YAAY,MAAA,CAAO,SAAA;AAGzB,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,OAAA,EAAS,CAAA,CAAE,WAAW,SAAS,CAAA;AAEpD,EAAA,MAAM,MAAA,GAAS,CACd,IAAA,KACmB,IAAA,CAA8B,EAAA;AAElD,EAAA,MAAM,UAAU,4BAAA,CAA6B;AAAA,IAC5C,SAAS,MAAA,CAAO,OAAA;AAAA,IAChB,KAAA;AAAA,IACA,WAAW,MAAA,CAAO,SAAA;AAAA,IAClB,OAAO,MAAA,CAAO,KAAA;AAAA,IACd,YAAY,MAAA,CAAO,UAAA;AAAA,IACnB,aAAa,MAAA,CAAO,WAAA;AAAA,IACpB,UAAA,EAAY;AAAA,GACZ,CAAA;AAED,EAAA,MAAM,cAAA,GAAyC;AAAA,IAC9C,KAAA;AAAA,IACA,cAAc,MAAA,CAAO,YAAA;AAAA,IACrB,UAAU,MAAA,CAAO,QAAA;AAAA,IACjB,OAAO,MAAA,CAAO,KAAA;AAAA,IACd,mBAAmB,CAAC,IAAA,KAAS,MAAA,CAAO,MAAA,CAAO,IAAI,CAAC;AAAA,GACjD;AAEA,EAAA,MAAM,UAAA,GAAa,kBAAA,CAAmB,cAAA,EAAgB,OAAO,CAAA;AAE7D,EAAA,MAAM,MAAA,GAAS,gCAAgC,KAAK,CAAA;AAEpD,EAAA,MAAM,mBAAmB,sBAAA,CAAuB;AAAA,IAC/C,MAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA,QAAA,EAAU,MAAA,CAAO,KAAA,GACd,OAAO,MAAA,KAAW;AAClB,MAAA,OAAA,CAAQ,GAAA,CAAI,YAAY,MAAM,CAAA;AAE9B,MAAA,MAAM,UAAA,CAAW,SAAU,MAAM,CAAA;AAAA,IAClC,CAAA,GACC,MAAA;AAAA,IACH,QAAA,EAAU,MAAA,CAAO,KAAA,GACd,OAAO,MAAA,KAAW;AAClB,MAAA,OAAA,CAAQ,GAAA,CAAI,YAAY,MAAM,CAAA;AAE9B,MAAA,MAAM,UAAA,CAAW,SAAU,MAAM,CAAA;AAAA,IAClC,CAAA,GACC,MAAA;AAAA,IACH,QAAA,EAAU,MAAA,CAAO,KAAA,GACd,OAAO,MAAA,KAAW;AAClB,MAAA,OAAA,CAAQ,GAAA,CAAI,YAAY,MAAM,CAAA;AAE9B,MAAA,MAAM,UAAA,CAAW,SAAU,MAAM,CAAA;AAAA,IAClC,CAAA,GACC,MAAA;AAAA,IACH,UAAU,MAAA,CAAO;AAAA,GACjB,CAAA;AAED,EAAA,OAAO,gBAAA;AACR","file":"chunk-VLFDMAFH.js","sourcesContent":["import type {\n\tInferSchemaOutput,\n\tSyncMode,\n\tCollectionConfig,\n} from \"@tanstack/db\";\nimport type { Table } from \"drizzle-orm\";\nimport type { BaseSQLiteDatabase } from \"drizzle-orm/sqlite-core\";\nimport type { CollectionUtils } from \"@firtoz/db-helpers\";\nimport type {\n\tSelectSchema,\n\tInsertToSelectSchema,\n\tTableWithRequiredFields,\n\tBaseSyncConfig,\n\tIdOf,\n} from \"@firtoz/drizzle-utils\";\nimport {\n\tcreateSyncFunction,\n\tcreateInsertSchemaWithIdDefault,\n\tcreateCollectionConfig,\n\tcreateSqliteTableSyncBackend,\n\ttype SQLInterceptor,\n} from \"@firtoz/drizzle-utils\";\n\nexport type AnyDrizzleDatabase = BaseSQLiteDatabase<\n\t\"async\",\n\t// biome-ignore lint/suspicious/noExplicitAny: We really want to use any here.\n\tany,\n\tRecord<string, unknown>\n>;\n\nexport type DrizzleSchema<TDrizzle extends AnyDrizzleDatabase> =\n\tTDrizzle[\"_\"][\"fullSchema\"];\n\nexport interface DrizzleSqliteCollectionConfig<\n\tTDrizzle extends AnyDrizzleDatabase,\n\tTTableName extends ValidTableNames<DrizzleSchema<TDrizzle>>,\n> {\n\tdrizzle: TDrizzle;\n\ttableName: ValidTableNames<DrizzleSchema<TDrizzle>> extends never\n\t\t? {\n\t\t\t\t$error: \"The schema needs to include at least one table that uses the syncableTable function.\";\n\t\t\t}\n\t\t: TTableName;\n\treadyPromise: Promise<void>;\n\tsyncMode?: SyncMode;\n\t/**\n\t * Enable debug logging for query execution and mutations\n\t */\n\tdebug?: boolean;\n\t/**\n\t * Optional callback to checkpoint the database after mutations\n\t * This ensures WAL is flushed to the main database file for OPFS persistence\n\t */\n\tcheckpoint?: () => Promise<void>;\n\t/**\n\t * Optional interceptor for tracking SQLite operations (for testing/debugging)\n\t */\n\tinterceptor?: SQLInterceptor;\n}\n\nexport type ValidTableNames<TSchema extends Record<string, unknown>> = {\n\t[K in keyof TSchema]: TSchema[K] extends TableWithRequiredFields ? K : never;\n}[keyof TSchema];\n\nexport type SqliteCollectionConfig<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 function sqliteCollectionOptions<\n\tconst TDrizzle extends AnyDrizzleDatabase,\n\tconst TTableName extends string & ValidTableNames<DrizzleSchema<TDrizzle>>,\n\tTTable extends DrizzleSchema<TDrizzle>[TTableName] & TableWithRequiredFields,\n>(\n\tconfig: DrizzleSqliteCollectionConfig<TDrizzle, TTableName>,\n): SqliteCollectionConfig<TTable> {\n\tconst tableName = config.tableName as string &\n\t\tValidTableNames<DrizzleSchema<TDrizzle>>;\n\n\tconst table = config.drizzle?._.fullSchema[tableName] as TTable;\n\n\tconst getKey = (\n\t\titem: InferSchemaOutput<SelectSchema<TTable>>,\n\t): IdOf<TTable> => (item as { id: IdOf<TTable> }).id;\n\n\tconst backend = createSqliteTableSyncBackend({\n\t\tdrizzle: config.drizzle,\n\t\ttable,\n\t\ttableName: config.tableName as string,\n\t\tdebug: config.debug,\n\t\tcheckpoint: config.checkpoint,\n\t\tinterceptor: config.interceptor,\n\t\tdriverMode: \"async\",\n\t});\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};\n\n\tconst syncResult = createSyncFunction(baseSyncConfig, backend);\n\n\tconst schema = createInsertSchemaWithIdDefault(table);\n\n\tconst collectionConfig = createCollectionConfig({\n\t\tschema,\n\t\tgetKey,\n\t\tsyncResult,\n\t\tonInsert: config.debug\n\t\t\t? async (params) => {\n\t\t\t\t\tconsole.log(\"onInsert\", params);\n\t\t\t\t\t// biome-ignore lint/style/noNonNullAssertion: onInsert is always defined in createSyncFunction\n\t\t\t\t\tawait syncResult.onInsert!(params);\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tonUpdate: config.debug\n\t\t\t? async (params) => {\n\t\t\t\t\tconsole.log(\"onUpdate\", params);\n\t\t\t\t\t// biome-ignore lint/style/noNonNullAssertion: onUpdate is always defined in createSyncFunction\n\t\t\t\t\tawait syncResult.onUpdate!(params);\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tonDelete: config.debug\n\t\t\t? async (params) => {\n\t\t\t\t\tconsole.log(\"onDelete\", params);\n\t\t\t\t\t// biome-ignore lint/style/noNonNullAssertion: onDelete is always defined in createSyncFunction\n\t\t\t\t\tawait syncResult.onDelete!(params);\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tsyncMode: config.syncMode,\n\t});\n\n\treturn collectionConfig;\n}\n"]}
|
|
@@ -3,7 +3,6 @@ import { Table } from 'drizzle-orm';
|
|
|
3
3
|
import { BaseSQLiteDatabase } from 'drizzle-orm/sqlite-core';
|
|
4
4
|
import { CollectionUtils } from '@firtoz/db-helpers';
|
|
5
5
|
import { TableWithRequiredFields, SQLInterceptor, SelectSchema, IdOf, InsertToSelectSchema } from '@firtoz/drizzle-utils';
|
|
6
|
-
export { SQLInterceptor, SQLOperation } from '@firtoz/drizzle-utils';
|
|
7
6
|
|
|
8
7
|
type AnyDrizzleDatabase = BaseSQLiteDatabase<"async", any, Record<string, unknown>>;
|
|
9
8
|
type DrizzleSchema<TDrizzle extends AnyDrizzleDatabase> = TDrizzle["_"]["fullSchema"];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createSyncedSqliteCollection } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
export { createSyncedSqliteCollection } from '../chunk-NNPU7YTX.js';
|
|
2
|
+
import '../chunk-VLFDMAFH.js';
|
|
3
3
|
//# sourceMappingURL=synced-sqlite-collection.js.map
|
|
4
4
|
//# sourceMappingURL=synced-sqlite-collection.js.map
|