@tanstack/tauri-db-sqlite-persistence 0.1.2
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/README.md +96 -0
- package/dist/cjs/index.cjs +10 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/index.d.cts +4 -0
- package/dist/cjs/tauri-persistence.cjs +86 -0
- package/dist/cjs/tauri-persistence.cjs.map +1 -0
- package/dist/cjs/tauri-persistence.d.cts +12 -0
- package/dist/cjs/tauri-sql-driver.cjs +207 -0
- package/dist/cjs/tauri-sql-driver.cjs.map +1 -0
- package/dist/cjs/tauri-sql-driver.d.cts +24 -0
- package/dist/cjs/tauri.cjs +8 -0
- package/dist/cjs/tauri.cjs.map +1 -0
- package/dist/cjs/tauri.d.cts +6 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/tauri-persistence.d.ts +12 -0
- package/dist/esm/tauri-persistence.js +86 -0
- package/dist/esm/tauri-persistence.js.map +1 -0
- package/dist/esm/tauri-sql-driver.d.ts +24 -0
- package/dist/esm/tauri-sql-driver.js +207 -0
- package/dist/esm/tauri-sql-driver.js.map +1 -0
- package/dist/esm/tauri.d.ts +6 -0
- package/dist/esm/tauri.js +8 -0
- package/dist/esm/tauri.js.map +1 -0
- package/package.json +71 -0
- package/src/index.ts +11 -0
- package/src/tauri-persistence.ts +173 -0
- package/src/tauri-sql-driver.ts +290 -0
- package/src/tauri.ts +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# @tanstack/tauri-db-sqlite-persistence
|
|
2
|
+
|
|
3
|
+
Thin SQLite persistence for Tauri apps using `@tauri-apps/plugin-sql`.
|
|
4
|
+
|
|
5
|
+
## Public API
|
|
6
|
+
|
|
7
|
+
- `createTauriSQLitePersistence(...)`
|
|
8
|
+
- `persistedCollectionOptions(...)` (re-exported from core)
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm add @tanstack/tauri-db-sqlite-persistence @tauri-apps/plugin-sql
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Consumer-side Tauri setup
|
|
17
|
+
|
|
18
|
+
Install the official SQL plugin in your Tauri app:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cd src-tauri
|
|
22
|
+
cargo add tauri-plugin-sql --features sqlite
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Register the plugin in `src-tauri/src/main.rs`:
|
|
26
|
+
|
|
27
|
+
```rust
|
|
28
|
+
fn main() {
|
|
29
|
+
tauri::Builder::default()
|
|
30
|
+
.plugin(tauri_plugin_sql::Builder::default().build())
|
|
31
|
+
.run(tauri::generate_context!())
|
|
32
|
+
.expect("error while running tauri application");
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Enable the SQL permissions in `src-tauri/capabilities/default.json`:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"permissions": ["core:default", "sql:default", "sql:allow-execute"]
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Quick start
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import Database from '@tauri-apps/plugin-sql'
|
|
48
|
+
import { createCollection } from '@tanstack/db'
|
|
49
|
+
import {
|
|
50
|
+
createTauriSQLitePersistence,
|
|
51
|
+
persistedCollectionOptions,
|
|
52
|
+
} from '@tanstack/tauri-db-sqlite-persistence'
|
|
53
|
+
|
|
54
|
+
type Todo = {
|
|
55
|
+
id: string
|
|
56
|
+
title: string
|
|
57
|
+
completed: boolean
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const database = await Database.load(`sqlite:tanstack-db.sqlite`)
|
|
61
|
+
|
|
62
|
+
const persistence = createTauriSQLitePersistence({
|
|
63
|
+
database,
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
export const todosCollection = createCollection(
|
|
67
|
+
persistedCollectionOptions<Todo, string>({
|
|
68
|
+
id: `todos`,
|
|
69
|
+
getKey: (todo) => todo.id,
|
|
70
|
+
persistence,
|
|
71
|
+
schemaVersion: 1,
|
|
72
|
+
}),
|
|
73
|
+
)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Notes
|
|
77
|
+
|
|
78
|
+
- `createTauriSQLitePersistence` is shared across collections.
|
|
79
|
+
- Reuse a single `Database.load('sqlite:...')` handle per SQLite file when using
|
|
80
|
+
this package. Opening multiple plugin handles to the same file can reintroduce
|
|
81
|
+
SQLite locking behavior outside this package's serialized transaction queue.
|
|
82
|
+
- Mode defaults (`sync-present` vs `sync-absent`) are inferred from whether a
|
|
83
|
+
`sync` config is present in `persistedCollectionOptions`.
|
|
84
|
+
- This package expects a database handle created by
|
|
85
|
+
`@tauri-apps/plugin-sql`, typically from `Database.load('sqlite:...')`.
|
|
86
|
+
- The database path is resolved by Tauri's SQL plugin, not by this package.
|
|
87
|
+
- This package does not publish or require package-specific Rust code. Only the
|
|
88
|
+
app-level Tauri SQL plugin registration shown above is required.
|
|
89
|
+
|
|
90
|
+
## Testing
|
|
91
|
+
|
|
92
|
+
- `pnpm --filter @tanstack/tauri-db-sqlite-persistence test`
|
|
93
|
+
runs the driver and shared adapter contract tests.
|
|
94
|
+
- `pnpm --filter @tanstack/tauri-db-sqlite-persistence test:e2e`
|
|
95
|
+
builds the repo-local Tauri harness and runs the persisted collection
|
|
96
|
+
conformance suite inside a real Tauri runtime.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const tauri = require("./tauri.cjs");
|
|
4
|
+
const dbSqlitePersistenceCore = require("@tanstack/db-sqlite-persistence-core");
|
|
5
|
+
exports.createTauriSQLitePersistence = tauri.createTauriSQLitePersistence;
|
|
6
|
+
Object.defineProperty(exports, "persistedCollectionOptions", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: () => dbSqlitePersistenceCore.persistedCollectionOptions
|
|
9
|
+
});
|
|
10
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createTauriSQLitePersistence } from './tauri.cjs';
|
|
2
|
+
export type { TauriSQLiteDatabaseLike, TauriSQLitePersistenceOptions, TauriSQLiteSchemaMismatchPolicy, } from './tauri.cjs';
|
|
3
|
+
export { persistedCollectionOptions } from '@tanstack/db-sqlite-persistence-core';
|
|
4
|
+
export type { PersistedCollectionCoordinator, PersistedCollectionPersistence, } from '@tanstack/db-sqlite-persistence-core';
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const dbSqlitePersistenceCore = require("@tanstack/db-sqlite-persistence-core");
|
|
4
|
+
const tauriSqlDriver = require("./tauri-sql-driver.cjs");
|
|
5
|
+
const tauriDriverCache = /* @__PURE__ */ new WeakMap();
|
|
6
|
+
function normalizeSchemaMismatchPolicy(policy) {
|
|
7
|
+
if (policy === `throw`) {
|
|
8
|
+
return `sync-absent-error`;
|
|
9
|
+
}
|
|
10
|
+
return policy;
|
|
11
|
+
}
|
|
12
|
+
function resolveSchemaMismatchPolicy(explicitPolicy, mode) {
|
|
13
|
+
if (explicitPolicy) {
|
|
14
|
+
return normalizeSchemaMismatchPolicy(explicitPolicy);
|
|
15
|
+
}
|
|
16
|
+
return mode === `sync-present` ? `sync-present-reset` : `sync-absent-error`;
|
|
17
|
+
}
|
|
18
|
+
function createAdapterCacheKey(schemaMismatchPolicy, schemaVersion) {
|
|
19
|
+
const schemaVersionKey = schemaVersion === void 0 ? `schema:default` : `schema:${schemaVersion}`;
|
|
20
|
+
return `${schemaMismatchPolicy}|${schemaVersionKey}`;
|
|
21
|
+
}
|
|
22
|
+
function createInternalSQLiteDriver(options) {
|
|
23
|
+
const cachedDriver = tauriDriverCache.get(options.database);
|
|
24
|
+
if (cachedDriver) {
|
|
25
|
+
return cachedDriver;
|
|
26
|
+
}
|
|
27
|
+
const driver = new tauriSqlDriver.TauriSQLiteDriver({
|
|
28
|
+
database: options.database
|
|
29
|
+
});
|
|
30
|
+
tauriDriverCache.set(options.database, driver);
|
|
31
|
+
return driver;
|
|
32
|
+
}
|
|
33
|
+
function resolveAdapterBaseOptions(options) {
|
|
34
|
+
return {
|
|
35
|
+
appliedTxPruneMaxRows: options.appliedTxPruneMaxRows,
|
|
36
|
+
appliedTxPruneMaxAgeSeconds: options.appliedTxPruneMaxAgeSeconds,
|
|
37
|
+
pullSinceReloadThreshold: options.pullSinceReloadThreshold
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function createTauriSQLitePersistence(options) {
|
|
41
|
+
const { coordinator, schemaMismatchPolicy } = options;
|
|
42
|
+
const driver = createInternalSQLiteDriver(options);
|
|
43
|
+
const adapterBaseOptions = resolveAdapterBaseOptions(options);
|
|
44
|
+
const resolvedCoordinator = coordinator ?? new dbSqlitePersistenceCore.SingleProcessCoordinator();
|
|
45
|
+
const adapterCache = /* @__PURE__ */ new Map();
|
|
46
|
+
const getAdapterForCollection = (mode, schemaVersion) => {
|
|
47
|
+
const resolvedSchemaMismatchPolicy = resolveSchemaMismatchPolicy(
|
|
48
|
+
schemaMismatchPolicy,
|
|
49
|
+
mode
|
|
50
|
+
);
|
|
51
|
+
const cacheKey = createAdapterCacheKey(
|
|
52
|
+
resolvedSchemaMismatchPolicy,
|
|
53
|
+
schemaVersion
|
|
54
|
+
);
|
|
55
|
+
const cachedAdapter = adapterCache.get(cacheKey);
|
|
56
|
+
if (cachedAdapter) {
|
|
57
|
+
return cachedAdapter;
|
|
58
|
+
}
|
|
59
|
+
const adapter = dbSqlitePersistenceCore.createSQLiteCorePersistenceAdapter({
|
|
60
|
+
...adapterBaseOptions,
|
|
61
|
+
driver,
|
|
62
|
+
schemaMismatchPolicy: resolvedSchemaMismatchPolicy,
|
|
63
|
+
...schemaVersion === void 0 ? {} : { schemaVersion }
|
|
64
|
+
});
|
|
65
|
+
adapterCache.set(cacheKey, adapter);
|
|
66
|
+
return adapter;
|
|
67
|
+
};
|
|
68
|
+
const createCollectionPersistence = (mode, schemaVersion) => ({
|
|
69
|
+
adapter: getAdapterForCollection(
|
|
70
|
+
mode,
|
|
71
|
+
schemaVersion
|
|
72
|
+
),
|
|
73
|
+
coordinator: resolvedCoordinator
|
|
74
|
+
});
|
|
75
|
+
const defaultPersistence = createCollectionPersistence(
|
|
76
|
+
`sync-absent`,
|
|
77
|
+
void 0
|
|
78
|
+
);
|
|
79
|
+
return {
|
|
80
|
+
...defaultPersistence,
|
|
81
|
+
resolvePersistenceForCollection: ({ mode, schemaVersion }) => createCollectionPersistence(mode, schemaVersion),
|
|
82
|
+
resolvePersistenceForMode: (mode) => createCollectionPersistence(mode, void 0)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
exports.createTauriSQLitePersistence = createTauriSQLitePersistence;
|
|
86
|
+
//# sourceMappingURL=tauri-persistence.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tauri-persistence.cjs","sources":["../../src/tauri-persistence.ts"],"sourcesContent":["import {\n SingleProcessCoordinator,\n createSQLiteCorePersistenceAdapter,\n} from '@tanstack/db-sqlite-persistence-core'\nimport { TauriSQLiteDriver } from './tauri-sql-driver'\nimport type {\n PersistedCollectionCoordinator,\n PersistedCollectionMode,\n PersistedCollectionPersistence,\n SQLiteCoreAdapterOptions,\n SQLiteDriver,\n} from '@tanstack/db-sqlite-persistence-core'\nimport type { TauriSQLiteDatabaseLike } from './tauri-sql-driver'\n\nexport type { TauriSQLiteDatabaseLike } from './tauri-sql-driver'\n\ntype TauriSQLiteCoreSchemaMismatchPolicy =\n | `sync-present-reset`\n | `sync-absent-error`\n | `reset`\n\nexport type TauriSQLiteSchemaMismatchPolicy =\n | TauriSQLiteCoreSchemaMismatchPolicy\n | `throw`\n\ntype TauriSQLitePersistenceBaseOptions = Omit<\n SQLiteCoreAdapterOptions,\n `driver` | `schemaVersion` | `schemaMismatchPolicy`\n> & {\n database: TauriSQLiteDatabaseLike\n coordinator?: PersistedCollectionCoordinator\n schemaMismatchPolicy?: TauriSQLiteSchemaMismatchPolicy\n}\n\nexport type TauriSQLitePersistenceOptions = TauriSQLitePersistenceBaseOptions\n\nconst tauriDriverCache = new WeakMap<\n TauriSQLiteDatabaseLike,\n TauriSQLiteDriver\n>()\n\nfunction normalizeSchemaMismatchPolicy(\n policy: TauriSQLiteSchemaMismatchPolicy,\n): TauriSQLiteCoreSchemaMismatchPolicy {\n if (policy === `throw`) {\n return `sync-absent-error`\n }\n\n return policy\n}\n\nfunction resolveSchemaMismatchPolicy(\n explicitPolicy: TauriSQLiteSchemaMismatchPolicy | undefined,\n mode: PersistedCollectionMode,\n): TauriSQLiteCoreSchemaMismatchPolicy {\n if (explicitPolicy) {\n return normalizeSchemaMismatchPolicy(explicitPolicy)\n }\n\n return mode === `sync-present` ? `sync-present-reset` : `sync-absent-error`\n}\n\nfunction createAdapterCacheKey(\n schemaMismatchPolicy: TauriSQLiteCoreSchemaMismatchPolicy,\n schemaVersion: number | undefined,\n): string {\n const schemaVersionKey =\n schemaVersion === undefined ? `schema:default` : `schema:${schemaVersion}`\n return `${schemaMismatchPolicy}|${schemaVersionKey}`\n}\n\nfunction createInternalSQLiteDriver(\n options: TauriSQLitePersistenceOptions,\n): SQLiteDriver {\n const cachedDriver = tauriDriverCache.get(options.database)\n if (cachedDriver) {\n return cachedDriver\n }\n\n const driver = new TauriSQLiteDriver({\n database: options.database,\n })\n tauriDriverCache.set(options.database, driver)\n return driver\n}\n\nfunction resolveAdapterBaseOptions(\n options: TauriSQLitePersistenceOptions,\n): Omit<\n SQLiteCoreAdapterOptions,\n `driver` | `schemaVersion` | `schemaMismatchPolicy`\n> {\n return {\n appliedTxPruneMaxRows: options.appliedTxPruneMaxRows,\n appliedTxPruneMaxAgeSeconds: options.appliedTxPruneMaxAgeSeconds,\n pullSinceReloadThreshold: options.pullSinceReloadThreshold,\n }\n}\n\nexport function createTauriSQLitePersistence<\n T extends object,\n TKey extends string | number = string | number,\n>(\n options: TauriSQLitePersistenceOptions,\n): PersistedCollectionPersistence<T, TKey> {\n const { coordinator, schemaMismatchPolicy } = options\n const driver = createInternalSQLiteDriver(options)\n const adapterBaseOptions = resolveAdapterBaseOptions(options)\n const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator()\n const adapterCache = new Map<\n string,\n ReturnType<\n typeof createSQLiteCorePersistenceAdapter<\n Record<string, unknown>,\n string | number\n >\n >\n >()\n\n const getAdapterForCollection = (\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ) => {\n const resolvedSchemaMismatchPolicy = resolveSchemaMismatchPolicy(\n schemaMismatchPolicy,\n mode,\n )\n const cacheKey = createAdapterCacheKey(\n resolvedSchemaMismatchPolicy,\n schemaVersion,\n )\n const cachedAdapter = adapterCache.get(cacheKey)\n if (cachedAdapter) {\n return cachedAdapter\n }\n\n const adapter = createSQLiteCorePersistenceAdapter<\n Record<string, unknown>,\n string | number\n >({\n ...adapterBaseOptions,\n driver,\n schemaMismatchPolicy: resolvedSchemaMismatchPolicy,\n ...(schemaVersion === undefined ? {} : { schemaVersion }),\n })\n adapterCache.set(cacheKey, adapter)\n return adapter\n }\n\n const createCollectionPersistence = (\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ): PersistedCollectionPersistence<T, TKey> => ({\n adapter: getAdapterForCollection(\n mode,\n schemaVersion,\n ) as unknown as PersistedCollectionPersistence<T, TKey>[`adapter`],\n coordinator: resolvedCoordinator,\n })\n\n const defaultPersistence = createCollectionPersistence(\n `sync-absent`,\n undefined,\n )\n\n return {\n ...defaultPersistence,\n resolvePersistenceForCollection: ({ mode, schemaVersion }) =>\n createCollectionPersistence(mode, schemaVersion),\n resolvePersistenceForMode: (mode) =>\n createCollectionPersistence(mode, undefined),\n }\n}\n"],"names":["TauriSQLiteDriver","SingleProcessCoordinator","createSQLiteCorePersistenceAdapter"],"mappings":";;;;AAoCA,MAAM,uCAAuB,QAAA;AAK7B,SAAS,8BACP,QACqC;AACrC,MAAI,WAAW,SAAS;AACtB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,4BACP,gBACA,MACqC;AACrC,MAAI,gBAAgB;AAClB,WAAO,8BAA8B,cAAc;AAAA,EACrD;AAEA,SAAO,SAAS,iBAAiB,uBAAuB;AAC1D;AAEA,SAAS,sBACP,sBACA,eACQ;AACR,QAAM,mBACJ,kBAAkB,SAAY,mBAAmB,UAAU,aAAa;AAC1E,SAAO,GAAG,oBAAoB,IAAI,gBAAgB;AACpD;AAEA,SAAS,2BACP,SACc;AACd,QAAM,eAAe,iBAAiB,IAAI,QAAQ,QAAQ;AAC1D,MAAI,cAAc;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,IAAIA,iCAAkB;AAAA,IACnC,UAAU,QAAQ;AAAA,EAAA,CACnB;AACD,mBAAiB,IAAI,QAAQ,UAAU,MAAM;AAC7C,SAAO;AACT;AAEA,SAAS,0BACP,SAIA;AACA,SAAO;AAAA,IACL,uBAAuB,QAAQ;AAAA,IAC/B,6BAA6B,QAAQ;AAAA,IACrC,0BAA0B,QAAQ;AAAA,EAAA;AAEtC;AAEO,SAAS,6BAId,SACyC;AACzC,QAAM,EAAE,aAAa,qBAAA,IAAyB;AAC9C,QAAM,SAAS,2BAA2B,OAAO;AACjD,QAAM,qBAAqB,0BAA0B,OAAO;AAC5D,QAAM,sBAAsB,eAAe,IAAIC,iDAAA;AAC/C,QAAM,mCAAmB,IAAA;AAUzB,QAAM,0BAA0B,CAC9B,MACA,kBACG;AACH,UAAM,+BAA+B;AAAA,MACnC;AAAA,MACA;AAAA,IAAA;AAEF,UAAM,WAAW;AAAA,MACf;AAAA,MACA;AAAA,IAAA;AAEF,UAAM,gBAAgB,aAAa,IAAI,QAAQ;AAC/C,QAAI,eAAe;AACjB,aAAO;AAAA,IACT;AAEA,UAAM,UAAUC,wBAAAA,mCAGd;AAAA,MACA,GAAG;AAAA,MACH;AAAA,MACA,sBAAsB;AAAA,MACtB,GAAI,kBAAkB,SAAY,CAAA,IAAK,EAAE,cAAA;AAAA,IAAc,CACxD;AACD,iBAAa,IAAI,UAAU,OAAO;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,8BAA8B,CAClC,MACA,mBAC6C;AAAA,IAC7C,SAAS;AAAA,MACP;AAAA,MACA;AAAA,IAAA;AAAA,IAEF,aAAa;AAAA,EAAA;AAGf,QAAM,qBAAqB;AAAA,IACzB;AAAA,IACA;AAAA,EAAA;AAGF,SAAO;AAAA,IACL,GAAG;AAAA,IACH,iCAAiC,CAAC,EAAE,MAAM,oBACxC,4BAA4B,MAAM,aAAa;AAAA,IACjD,2BAA2B,CAAC,SAC1B,4BAA4B,MAAM,MAAS;AAAA,EAAA;AAEjD;;"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PersistedCollectionCoordinator, PersistedCollectionPersistence, SQLiteCoreAdapterOptions } from '@tanstack/db-sqlite-persistence-core';
|
|
2
|
+
import { TauriSQLiteDatabaseLike } from './tauri-sql-driver.cjs';
|
|
3
|
+
export type { TauriSQLiteDatabaseLike } from './tauri-sql-driver.cjs';
|
|
4
|
+
type TauriSQLiteCoreSchemaMismatchPolicy = `sync-present-reset` | `sync-absent-error` | `reset`;
|
|
5
|
+
export type TauriSQLiteSchemaMismatchPolicy = TauriSQLiteCoreSchemaMismatchPolicy | `throw`;
|
|
6
|
+
type TauriSQLitePersistenceBaseOptions = Omit<SQLiteCoreAdapterOptions, `driver` | `schemaVersion` | `schemaMismatchPolicy`> & {
|
|
7
|
+
database: TauriSQLiteDatabaseLike;
|
|
8
|
+
coordinator?: PersistedCollectionCoordinator;
|
|
9
|
+
schemaMismatchPolicy?: TauriSQLiteSchemaMismatchPolicy;
|
|
10
|
+
};
|
|
11
|
+
export type TauriSQLitePersistenceOptions = TauriSQLitePersistenceBaseOptions;
|
|
12
|
+
export declare function createTauriSQLitePersistence<T extends object, TKey extends string | number = string | number>(options: TauriSQLitePersistenceOptions): PersistedCollectionPersistence<T, TKey>;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const dbSqlitePersistenceCore = require("@tanstack/db-sqlite-persistence-core");
|
|
4
|
+
function assertTransactionCallbackHasDriverArg(fn) {
|
|
5
|
+
if (fn.length > 0) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
|
|
9
|
+
`SQLiteDriver.transaction callback must accept the transaction driver argument`
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
function isTauriSQLiteDatabaseLike(value) {
|
|
13
|
+
const candidate = value;
|
|
14
|
+
return typeof value === `object` && value !== null && typeof candidate.path === `string` && typeof candidate.execute === `function` && typeof candidate.select === `function`;
|
|
15
|
+
}
|
|
16
|
+
function normalizeQueryRows(rows, sql) {
|
|
17
|
+
if (Array.isArray(rows)) {
|
|
18
|
+
return rows;
|
|
19
|
+
}
|
|
20
|
+
throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
|
|
21
|
+
`Unsupported Tauri SQL query result shape for SQL "${sql}"`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
function convertSqlitePlaceholdersToTauri(sql) {
|
|
25
|
+
let result = ``;
|
|
26
|
+
let parameterIndex = 1;
|
|
27
|
+
let inSingleQuote = false;
|
|
28
|
+
let inDoubleQuote = false;
|
|
29
|
+
let inLineComment = false;
|
|
30
|
+
let inBlockComment = false;
|
|
31
|
+
for (let index = 0; index < sql.length; index++) {
|
|
32
|
+
const currentChar = sql[index];
|
|
33
|
+
const nextChar = sql[index + 1];
|
|
34
|
+
if (inLineComment) {
|
|
35
|
+
result += currentChar;
|
|
36
|
+
if (currentChar === `
|
|
37
|
+
`) {
|
|
38
|
+
inLineComment = false;
|
|
39
|
+
}
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (inBlockComment) {
|
|
43
|
+
result += currentChar;
|
|
44
|
+
if (currentChar === `*` && nextChar === `/`) {
|
|
45
|
+
result += `/`;
|
|
46
|
+
index++;
|
|
47
|
+
inBlockComment = false;
|
|
48
|
+
}
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (!inSingleQuote && !inDoubleQuote) {
|
|
52
|
+
if (currentChar === `-` && nextChar === `-`) {
|
|
53
|
+
result += `--`;
|
|
54
|
+
index++;
|
|
55
|
+
inLineComment = true;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (currentChar === `/` && nextChar === `*`) {
|
|
59
|
+
result += `/*`;
|
|
60
|
+
index++;
|
|
61
|
+
inBlockComment = true;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (currentChar === `'` && !inDoubleQuote) {
|
|
66
|
+
result += currentChar;
|
|
67
|
+
if (inSingleQuote && nextChar === `'`) {
|
|
68
|
+
result += `'`;
|
|
69
|
+
index++;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
inSingleQuote = !inSingleQuote;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (currentChar === `"` && !inSingleQuote) {
|
|
76
|
+
result += currentChar;
|
|
77
|
+
if (inDoubleQuote && nextChar === `"`) {
|
|
78
|
+
result += `"`;
|
|
79
|
+
index++;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
inDoubleQuote = !inDoubleQuote;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (currentChar === `?` && !inSingleQuote && !inDoubleQuote) {
|
|
86
|
+
result += `$${String(parameterIndex)}`;
|
|
87
|
+
parameterIndex++;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
result += currentChar;
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
class TauriSQLiteDriver {
|
|
95
|
+
constructor(options) {
|
|
96
|
+
this.queue = Promise.resolve();
|
|
97
|
+
this.nextSavepointId = 1;
|
|
98
|
+
if (!isTauriSQLiteDatabaseLike(options.database)) {
|
|
99
|
+
throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
|
|
100
|
+
`Tauri SQLite database object must provide execute/select methods`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
this.database = options.database;
|
|
104
|
+
}
|
|
105
|
+
async exec(sql) {
|
|
106
|
+
await this.enqueue(async () => {
|
|
107
|
+
await this.executeStatement(sql);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
async query(sql, params = []) {
|
|
111
|
+
return this.enqueue(async () => {
|
|
112
|
+
const rows = await this.database.select(
|
|
113
|
+
convertSqlitePlaceholdersToTauri(sql),
|
|
114
|
+
params.length > 0 ? [...params] : void 0
|
|
115
|
+
);
|
|
116
|
+
return normalizeQueryRows(rows, sql);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
async run(sql, params = []) {
|
|
120
|
+
await this.enqueue(async () => {
|
|
121
|
+
await this.executeStatement(sql, params);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
async transaction(fn) {
|
|
125
|
+
assertTransactionCallbackHasDriverArg(fn);
|
|
126
|
+
return this.transactionWithDriver(fn);
|
|
127
|
+
}
|
|
128
|
+
async transactionWithDriver(fn) {
|
|
129
|
+
return this.enqueue(async () => {
|
|
130
|
+
await this.executeStatement(`BEGIN IMMEDIATE`);
|
|
131
|
+
const transactionDriver = this.createTransactionDriver();
|
|
132
|
+
try {
|
|
133
|
+
const result = await fn(transactionDriver);
|
|
134
|
+
await this.executeStatement(`COMMIT`);
|
|
135
|
+
return result;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
try {
|
|
138
|
+
await this.executeStatement(`ROLLBACK`);
|
|
139
|
+
} catch {
|
|
140
|
+
}
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async close() {
|
|
146
|
+
if (typeof this.database.close !== `function`) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
await Promise.resolve(this.database.close(this.database.path));
|
|
150
|
+
}
|
|
151
|
+
getDatabase() {
|
|
152
|
+
return this.database;
|
|
153
|
+
}
|
|
154
|
+
async executeStatement(sql, params = []) {
|
|
155
|
+
await this.database.execute(
|
|
156
|
+
convertSqlitePlaceholdersToTauri(sql),
|
|
157
|
+
params.length > 0 ? [...params] : void 0
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
enqueue(operation) {
|
|
161
|
+
const queuedOperation = this.queue.then(operation, operation);
|
|
162
|
+
this.queue = queuedOperation.then(
|
|
163
|
+
() => void 0,
|
|
164
|
+
() => void 0
|
|
165
|
+
);
|
|
166
|
+
return queuedOperation;
|
|
167
|
+
}
|
|
168
|
+
createTransactionDriver() {
|
|
169
|
+
const transactionDriver = {
|
|
170
|
+
exec: async (sql) => {
|
|
171
|
+
await this.executeStatement(sql);
|
|
172
|
+
},
|
|
173
|
+
query: async (sql, params = []) => {
|
|
174
|
+
const rows = await this.database.select(
|
|
175
|
+
convertSqlitePlaceholdersToTauri(sql),
|
|
176
|
+
params.length > 0 ? [...params] : void 0
|
|
177
|
+
);
|
|
178
|
+
return normalizeQueryRows(rows, sql);
|
|
179
|
+
},
|
|
180
|
+
run: async (sql, params = []) => {
|
|
181
|
+
await this.executeStatement(sql, params);
|
|
182
|
+
},
|
|
183
|
+
transaction: async (fn) => {
|
|
184
|
+
assertTransactionCallbackHasDriverArg(fn);
|
|
185
|
+
return this.runNestedTransaction(transactionDriver, fn);
|
|
186
|
+
},
|
|
187
|
+
transactionWithDriver: async (fn) => this.runNestedTransaction(transactionDriver, fn)
|
|
188
|
+
};
|
|
189
|
+
return transactionDriver;
|
|
190
|
+
}
|
|
191
|
+
async runNestedTransaction(transactionDriver, fn) {
|
|
192
|
+
const savepointName = `tsdb_sp_${this.nextSavepointId}`;
|
|
193
|
+
this.nextSavepointId++;
|
|
194
|
+
await this.executeStatement(`SAVEPOINT ${savepointName}`);
|
|
195
|
+
try {
|
|
196
|
+
const result = await fn(transactionDriver);
|
|
197
|
+
await this.executeStatement(`RELEASE SAVEPOINT ${savepointName}`);
|
|
198
|
+
return result;
|
|
199
|
+
} catch (error) {
|
|
200
|
+
await this.executeStatement(`ROLLBACK TO SAVEPOINT ${savepointName}`);
|
|
201
|
+
await this.executeStatement(`RELEASE SAVEPOINT ${savepointName}`);
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
exports.TauriSQLiteDriver = TauriSQLiteDriver;
|
|
207
|
+
//# sourceMappingURL=tauri-sql-driver.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tauri-sql-driver.cjs","sources":["../../src/tauri-sql-driver.ts"],"sourcesContent":["import { InvalidPersistedCollectionConfigError } from '@tanstack/db-sqlite-persistence-core'\nimport type { SQLiteDriver } from '@tanstack/db-sqlite-persistence-core'\nimport type Database from '@tauri-apps/plugin-sql'\n\nexport type TauriSQLiteDatabaseLike = Pick<\n Database,\n `execute` | `select` | `close` | `path`\n>\n\nexport type TauriSQLiteDriverOptions = {\n database: TauriSQLiteDatabaseLike\n}\n\nfunction assertTransactionCallbackHasDriverArg(\n fn: (transactionDriver: SQLiteDriver) => Promise<unknown>,\n): void {\n if (fn.length > 0) {\n return\n }\n\n throw new InvalidPersistedCollectionConfigError(\n `SQLiteDriver.transaction callback must accept the transaction driver argument`,\n )\n}\n\nfunction isTauriSQLiteDatabaseLike(\n value: unknown,\n): value is TauriSQLiteDatabaseLike {\n const candidate = value as Partial<TauriSQLiteDatabaseLike>\n return (\n typeof value === `object` &&\n value !== null &&\n typeof candidate.path === `string` &&\n typeof candidate.execute === `function` &&\n typeof candidate.select === `function`\n )\n}\n\nfunction normalizeQueryRows<TRow>(\n rows: unknown,\n sql: string,\n): ReadonlyArray<TRow> {\n if (Array.isArray(rows)) {\n return rows as ReadonlyArray<TRow>\n }\n\n throw new InvalidPersistedCollectionConfigError(\n `Unsupported Tauri SQL query result shape for SQL \"${sql}\"`,\n )\n}\n\nfunction convertSqlitePlaceholdersToTauri(sql: string): string {\n let result = ``\n let parameterIndex = 1\n let inSingleQuote = false\n let inDoubleQuote = false\n let inLineComment = false\n let inBlockComment = false\n\n for (let index = 0; index < sql.length; index++) {\n const currentChar = sql[index]\n const nextChar = sql[index + 1]\n\n if (inLineComment) {\n result += currentChar\n if (currentChar === `\\n`) {\n inLineComment = false\n }\n continue\n }\n\n if (inBlockComment) {\n result += currentChar\n if (currentChar === `*` && nextChar === `/`) {\n result += `/`\n index++\n inBlockComment = false\n }\n continue\n }\n\n if (!inSingleQuote && !inDoubleQuote) {\n if (currentChar === `-` && nextChar === `-`) {\n result += `--`\n index++\n inLineComment = true\n continue\n }\n\n if (currentChar === `/` && nextChar === `*`) {\n result += `/*`\n index++\n inBlockComment = true\n continue\n }\n }\n\n if (currentChar === `'` && !inDoubleQuote) {\n result += currentChar\n if (inSingleQuote && nextChar === `'`) {\n result += `'`\n index++\n continue\n }\n inSingleQuote = !inSingleQuote\n continue\n }\n\n if (currentChar === `\"` && !inSingleQuote) {\n result += currentChar\n if (inDoubleQuote && nextChar === `\"`) {\n result += `\"`\n index++\n continue\n }\n inDoubleQuote = !inDoubleQuote\n continue\n }\n\n if (currentChar === `?` && !inSingleQuote && !inDoubleQuote) {\n result += `$${String(parameterIndex)}`\n parameterIndex++\n continue\n }\n\n result += currentChar\n }\n\n return result\n}\n\nexport class TauriSQLiteDriver implements SQLiteDriver {\n private readonly database: TauriSQLiteDatabaseLike\n private queue: Promise<void> = Promise.resolve()\n private nextSavepointId = 1\n\n constructor(options: TauriSQLiteDriverOptions) {\n if (!isTauriSQLiteDatabaseLike(options.database)) {\n throw new InvalidPersistedCollectionConfigError(\n `Tauri SQLite database object must provide execute/select methods`,\n )\n }\n\n this.database = options.database\n }\n\n async exec(sql: string): Promise<void> {\n await this.enqueue(async () => {\n await this.executeStatement(sql)\n })\n }\n\n async query<TRow>(\n sql: string,\n params: ReadonlyArray<unknown> = [],\n ): Promise<ReadonlyArray<TRow>> {\n return this.enqueue(async () => {\n const rows = await this.database.select<ReadonlyArray<TRow>>(\n convertSqlitePlaceholdersToTauri(sql),\n params.length > 0 ? [...params] : undefined,\n )\n return normalizeQueryRows<TRow>(rows, sql)\n })\n }\n\n async run(sql: string, params: ReadonlyArray<unknown> = []): Promise<void> {\n await this.enqueue(async () => {\n await this.executeStatement(sql, params)\n })\n }\n\n async transaction<TReturn>(\n fn: (transactionDriver: SQLiteDriver) => Promise<TReturn>,\n ): Promise<TReturn> {\n assertTransactionCallbackHasDriverArg(fn)\n return this.transactionWithDriver(fn)\n }\n\n async transactionWithDriver<TReturn>(\n fn: (transactionDriver: SQLiteDriver) => Promise<TReturn>,\n ): Promise<TReturn> {\n return this.enqueue(async () => {\n await this.executeStatement(`BEGIN IMMEDIATE`)\n const transactionDriver = this.createTransactionDriver()\n\n try {\n const result = await fn(transactionDriver)\n await this.executeStatement(`COMMIT`)\n return result\n } catch (error) {\n try {\n await this.executeStatement(`ROLLBACK`)\n } catch {\n // Keep the original transaction error as the primary failure.\n }\n throw error\n }\n })\n }\n\n async close(): Promise<void> {\n if (typeof this.database.close !== `function`) {\n return\n }\n\n await Promise.resolve(this.database.close(this.database.path))\n }\n\n getDatabase(): TauriSQLiteDatabaseLike {\n return this.database\n }\n\n private async executeStatement(\n sql: string,\n params: ReadonlyArray<unknown> = [],\n ): Promise<void> {\n await this.database.execute(\n convertSqlitePlaceholdersToTauri(sql),\n params.length > 0 ? [...params] : undefined,\n )\n }\n\n private enqueue<TReturn>(\n operation: () => Promise<TReturn>,\n ): Promise<TReturn> {\n const queuedOperation = this.queue.then(operation, operation)\n this.queue = queuedOperation.then(\n () => undefined,\n () => undefined,\n )\n return queuedOperation\n }\n\n private createTransactionDriver(): SQLiteDriver {\n const transactionDriver: SQLiteDriver = {\n exec: async (sql) => {\n await this.executeStatement(sql)\n },\n query: async <TRow>(\n sql: string,\n params: ReadonlyArray<unknown> = [],\n ): Promise<ReadonlyArray<TRow>> => {\n const rows = await this.database.select<ReadonlyArray<TRow>>(\n convertSqlitePlaceholdersToTauri(sql),\n params.length > 0 ? [...params] : undefined,\n )\n return normalizeQueryRows<TRow>(rows, sql)\n },\n run: async (sql, params = []) => {\n await this.executeStatement(sql, params)\n },\n transaction: async <TReturn>(\n fn: (transactionDriver: SQLiteDriver) => Promise<TReturn>,\n ): Promise<TReturn> => {\n assertTransactionCallbackHasDriverArg(fn)\n return this.runNestedTransaction(transactionDriver, fn)\n },\n transactionWithDriver: async <TReturn>(\n fn: (transactionDriver: SQLiteDriver) => Promise<TReturn>,\n ): Promise<TReturn> => this.runNestedTransaction(transactionDriver, fn),\n }\n\n return transactionDriver\n }\n\n private async runNestedTransaction<TReturn>(\n transactionDriver: SQLiteDriver,\n fn: (transactionDriver: SQLiteDriver) => Promise<TReturn>,\n ): Promise<TReturn> {\n const savepointName = `tsdb_sp_${this.nextSavepointId}`\n this.nextSavepointId++\n await this.executeStatement(`SAVEPOINT ${savepointName}`)\n\n try {\n const result = await fn(transactionDriver)\n await this.executeStatement(`RELEASE SAVEPOINT ${savepointName}`)\n return result\n } catch (error) {\n await this.executeStatement(`ROLLBACK TO SAVEPOINT ${savepointName}`)\n await this.executeStatement(`RELEASE SAVEPOINT ${savepointName}`)\n throw error\n }\n }\n}\n\nexport function createTauriSQLiteDriver(\n options: TauriSQLiteDriverOptions,\n): TauriSQLiteDriver {\n return new TauriSQLiteDriver(options)\n}\n"],"names":["InvalidPersistedCollectionConfigError"],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { SQLiteDriver } from '@tanstack/db-sqlite-persistence-core';
|
|
2
|
+
import { default as Database } from '@tauri-apps/plugin-sql';
|
|
3
|
+
export type TauriSQLiteDatabaseLike = Pick<Database, `execute` | `select` | `close` | `path`>;
|
|
4
|
+
export type TauriSQLiteDriverOptions = {
|
|
5
|
+
database: TauriSQLiteDatabaseLike;
|
|
6
|
+
};
|
|
7
|
+
export declare class TauriSQLiteDriver implements SQLiteDriver {
|
|
8
|
+
private readonly database;
|
|
9
|
+
private queue;
|
|
10
|
+
private nextSavepointId;
|
|
11
|
+
constructor(options: TauriSQLiteDriverOptions);
|
|
12
|
+
exec(sql: string): Promise<void>;
|
|
13
|
+
query<TRow>(sql: string, params?: ReadonlyArray<unknown>): Promise<ReadonlyArray<TRow>>;
|
|
14
|
+
run(sql: string, params?: ReadonlyArray<unknown>): Promise<void>;
|
|
15
|
+
transaction<TReturn>(fn: (transactionDriver: SQLiteDriver) => Promise<TReturn>): Promise<TReturn>;
|
|
16
|
+
transactionWithDriver<TReturn>(fn: (transactionDriver: SQLiteDriver) => Promise<TReturn>): Promise<TReturn>;
|
|
17
|
+
close(): Promise<void>;
|
|
18
|
+
getDatabase(): TauriSQLiteDatabaseLike;
|
|
19
|
+
private executeStatement;
|
|
20
|
+
private enqueue;
|
|
21
|
+
private createTransactionDriver;
|
|
22
|
+
private runNestedTransaction;
|
|
23
|
+
}
|
|
24
|
+
export declare function createTauriSQLiteDriver(options: TauriSQLiteDriverOptions): TauriSQLiteDriver;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const tauriPersistence = require("./tauri-persistence.cjs");
|
|
4
|
+
function createTauriSQLitePersistence(options) {
|
|
5
|
+
return tauriPersistence.createTauriSQLitePersistence(options);
|
|
6
|
+
}
|
|
7
|
+
exports.createTauriSQLitePersistence = createTauriSQLitePersistence;
|
|
8
|
+
//# sourceMappingURL=tauri.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tauri.cjs","sources":["../../src/tauri.ts"],"sourcesContent":["import { createTauriSQLitePersistence as createPersistence } from './tauri-persistence'\nimport type {\n TauriSQLitePersistenceOptions as TauriSQLitePersistenceOptionsBase,\n TauriSQLiteSchemaMismatchPolicy as TauriSQLiteSchemaMismatchPolicyBase,\n} from './tauri-persistence'\nimport type { PersistedCollectionPersistence } from '@tanstack/db-sqlite-persistence-core'\n\nexport type TauriSQLitePersistenceOptions = TauriSQLitePersistenceOptionsBase\nexport type TauriSQLiteSchemaMismatchPolicy =\n TauriSQLiteSchemaMismatchPolicyBase\nexport type { TauriSQLiteDatabaseLike } from './tauri-persistence'\n\nexport function createTauriSQLitePersistence<\n T extends object,\n TKey extends string | number = string | number,\n>(\n options: TauriSQLitePersistenceOptions,\n): PersistedCollectionPersistence<T, TKey> {\n return createPersistence<T, TKey>(options)\n}\n"],"names":["createPersistence"],"mappings":";;;AAYO,SAAS,6BAId,SACyC;AACzC,SAAOA,iBAAAA,6BAA2B,OAAO;AAC3C;;"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { TauriSQLitePersistenceOptions as TauriSQLitePersistenceOptionsBase, TauriSQLiteSchemaMismatchPolicy as TauriSQLiteSchemaMismatchPolicyBase } from './tauri-persistence.cjs';
|
|
2
|
+
import { PersistedCollectionPersistence } from '@tanstack/db-sqlite-persistence-core';
|
|
3
|
+
export type TauriSQLitePersistenceOptions = TauriSQLitePersistenceOptionsBase;
|
|
4
|
+
export type TauriSQLiteSchemaMismatchPolicy = TauriSQLiteSchemaMismatchPolicyBase;
|
|
5
|
+
export type { TauriSQLiteDatabaseLike } from './tauri-persistence.cjs';
|
|
6
|
+
export declare function createTauriSQLitePersistence<T extends object, TKey extends string | number = string | number>(options: TauriSQLitePersistenceOptions): PersistedCollectionPersistence<T, TKey>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createTauriSQLitePersistence } from './tauri.js';
|
|
2
|
+
export type { TauriSQLiteDatabaseLike, TauriSQLitePersistenceOptions, TauriSQLiteSchemaMismatchPolicy, } from './tauri.js';
|
|
3
|
+
export { persistedCollectionOptions } from '@tanstack/db-sqlite-persistence-core';
|
|
4
|
+
export type { PersistedCollectionCoordinator, PersistedCollectionPersistence, } from '@tanstack/db-sqlite-persistence-core';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PersistedCollectionCoordinator, PersistedCollectionPersistence, SQLiteCoreAdapterOptions } from '@tanstack/db-sqlite-persistence-core';
|
|
2
|
+
import { TauriSQLiteDatabaseLike } from './tauri-sql-driver.js';
|
|
3
|
+
export type { TauriSQLiteDatabaseLike } from './tauri-sql-driver.js';
|
|
4
|
+
type TauriSQLiteCoreSchemaMismatchPolicy = `sync-present-reset` | `sync-absent-error` | `reset`;
|
|
5
|
+
export type TauriSQLiteSchemaMismatchPolicy = TauriSQLiteCoreSchemaMismatchPolicy | `throw`;
|
|
6
|
+
type TauriSQLitePersistenceBaseOptions = Omit<SQLiteCoreAdapterOptions, `driver` | `schemaVersion` | `schemaMismatchPolicy`> & {
|
|
7
|
+
database: TauriSQLiteDatabaseLike;
|
|
8
|
+
coordinator?: PersistedCollectionCoordinator;
|
|
9
|
+
schemaMismatchPolicy?: TauriSQLiteSchemaMismatchPolicy;
|
|
10
|
+
};
|
|
11
|
+
export type TauriSQLitePersistenceOptions = TauriSQLitePersistenceBaseOptions;
|
|
12
|
+
export declare function createTauriSQLitePersistence<T extends object, TKey extends string | number = string | number>(options: TauriSQLitePersistenceOptions): PersistedCollectionPersistence<T, TKey>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { SingleProcessCoordinator, createSQLiteCorePersistenceAdapter } from "@tanstack/db-sqlite-persistence-core";
|
|
2
|
+
import { TauriSQLiteDriver } from "./tauri-sql-driver.js";
|
|
3
|
+
const tauriDriverCache = /* @__PURE__ */ new WeakMap();
|
|
4
|
+
function normalizeSchemaMismatchPolicy(policy) {
|
|
5
|
+
if (policy === `throw`) {
|
|
6
|
+
return `sync-absent-error`;
|
|
7
|
+
}
|
|
8
|
+
return policy;
|
|
9
|
+
}
|
|
10
|
+
function resolveSchemaMismatchPolicy(explicitPolicy, mode) {
|
|
11
|
+
if (explicitPolicy) {
|
|
12
|
+
return normalizeSchemaMismatchPolicy(explicitPolicy);
|
|
13
|
+
}
|
|
14
|
+
return mode === `sync-present` ? `sync-present-reset` : `sync-absent-error`;
|
|
15
|
+
}
|
|
16
|
+
function createAdapterCacheKey(schemaMismatchPolicy, schemaVersion) {
|
|
17
|
+
const schemaVersionKey = schemaVersion === void 0 ? `schema:default` : `schema:${schemaVersion}`;
|
|
18
|
+
return `${schemaMismatchPolicy}|${schemaVersionKey}`;
|
|
19
|
+
}
|
|
20
|
+
function createInternalSQLiteDriver(options) {
|
|
21
|
+
const cachedDriver = tauriDriverCache.get(options.database);
|
|
22
|
+
if (cachedDriver) {
|
|
23
|
+
return cachedDriver;
|
|
24
|
+
}
|
|
25
|
+
const driver = new TauriSQLiteDriver({
|
|
26
|
+
database: options.database
|
|
27
|
+
});
|
|
28
|
+
tauriDriverCache.set(options.database, driver);
|
|
29
|
+
return driver;
|
|
30
|
+
}
|
|
31
|
+
function resolveAdapterBaseOptions(options) {
|
|
32
|
+
return {
|
|
33
|
+
appliedTxPruneMaxRows: options.appliedTxPruneMaxRows,
|
|
34
|
+
appliedTxPruneMaxAgeSeconds: options.appliedTxPruneMaxAgeSeconds,
|
|
35
|
+
pullSinceReloadThreshold: options.pullSinceReloadThreshold
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function createTauriSQLitePersistence(options) {
|
|
39
|
+
const { coordinator, schemaMismatchPolicy } = options;
|
|
40
|
+
const driver = createInternalSQLiteDriver(options);
|
|
41
|
+
const adapterBaseOptions = resolveAdapterBaseOptions(options);
|
|
42
|
+
const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator();
|
|
43
|
+
const adapterCache = /* @__PURE__ */ new Map();
|
|
44
|
+
const getAdapterForCollection = (mode, schemaVersion) => {
|
|
45
|
+
const resolvedSchemaMismatchPolicy = resolveSchemaMismatchPolicy(
|
|
46
|
+
schemaMismatchPolicy,
|
|
47
|
+
mode
|
|
48
|
+
);
|
|
49
|
+
const cacheKey = createAdapterCacheKey(
|
|
50
|
+
resolvedSchemaMismatchPolicy,
|
|
51
|
+
schemaVersion
|
|
52
|
+
);
|
|
53
|
+
const cachedAdapter = adapterCache.get(cacheKey);
|
|
54
|
+
if (cachedAdapter) {
|
|
55
|
+
return cachedAdapter;
|
|
56
|
+
}
|
|
57
|
+
const adapter = createSQLiteCorePersistenceAdapter({
|
|
58
|
+
...adapterBaseOptions,
|
|
59
|
+
driver,
|
|
60
|
+
schemaMismatchPolicy: resolvedSchemaMismatchPolicy,
|
|
61
|
+
...schemaVersion === void 0 ? {} : { schemaVersion }
|
|
62
|
+
});
|
|
63
|
+
adapterCache.set(cacheKey, adapter);
|
|
64
|
+
return adapter;
|
|
65
|
+
};
|
|
66
|
+
const createCollectionPersistence = (mode, schemaVersion) => ({
|
|
67
|
+
adapter: getAdapterForCollection(
|
|
68
|
+
mode,
|
|
69
|
+
schemaVersion
|
|
70
|
+
),
|
|
71
|
+
coordinator: resolvedCoordinator
|
|
72
|
+
});
|
|
73
|
+
const defaultPersistence = createCollectionPersistence(
|
|
74
|
+
`sync-absent`,
|
|
75
|
+
void 0
|
|
76
|
+
);
|
|
77
|
+
return {
|
|
78
|
+
...defaultPersistence,
|
|
79
|
+
resolvePersistenceForCollection: ({ mode, schemaVersion }) => createCollectionPersistence(mode, schemaVersion),
|
|
80
|
+
resolvePersistenceForMode: (mode) => createCollectionPersistence(mode, void 0)
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export {
|
|
84
|
+
createTauriSQLitePersistence
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=tauri-persistence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tauri-persistence.js","sources":["../../src/tauri-persistence.ts"],"sourcesContent":["import {\n SingleProcessCoordinator,\n createSQLiteCorePersistenceAdapter,\n} from '@tanstack/db-sqlite-persistence-core'\nimport { TauriSQLiteDriver } from './tauri-sql-driver'\nimport type {\n PersistedCollectionCoordinator,\n PersistedCollectionMode,\n PersistedCollectionPersistence,\n SQLiteCoreAdapterOptions,\n SQLiteDriver,\n} from '@tanstack/db-sqlite-persistence-core'\nimport type { TauriSQLiteDatabaseLike } from './tauri-sql-driver'\n\nexport type { TauriSQLiteDatabaseLike } from './tauri-sql-driver'\n\ntype TauriSQLiteCoreSchemaMismatchPolicy =\n | `sync-present-reset`\n | `sync-absent-error`\n | `reset`\n\nexport type TauriSQLiteSchemaMismatchPolicy =\n | TauriSQLiteCoreSchemaMismatchPolicy\n | `throw`\n\ntype TauriSQLitePersistenceBaseOptions = Omit<\n SQLiteCoreAdapterOptions,\n `driver` | `schemaVersion` | `schemaMismatchPolicy`\n> & {\n database: TauriSQLiteDatabaseLike\n coordinator?: PersistedCollectionCoordinator\n schemaMismatchPolicy?: TauriSQLiteSchemaMismatchPolicy\n}\n\nexport type TauriSQLitePersistenceOptions = TauriSQLitePersistenceBaseOptions\n\nconst tauriDriverCache = new WeakMap<\n TauriSQLiteDatabaseLike,\n TauriSQLiteDriver\n>()\n\nfunction normalizeSchemaMismatchPolicy(\n policy: TauriSQLiteSchemaMismatchPolicy,\n): TauriSQLiteCoreSchemaMismatchPolicy {\n if (policy === `throw`) {\n return `sync-absent-error`\n }\n\n return policy\n}\n\nfunction resolveSchemaMismatchPolicy(\n explicitPolicy: TauriSQLiteSchemaMismatchPolicy | undefined,\n mode: PersistedCollectionMode,\n): TauriSQLiteCoreSchemaMismatchPolicy {\n if (explicitPolicy) {\n return normalizeSchemaMismatchPolicy(explicitPolicy)\n }\n\n return mode === `sync-present` ? `sync-present-reset` : `sync-absent-error`\n}\n\nfunction createAdapterCacheKey(\n schemaMismatchPolicy: TauriSQLiteCoreSchemaMismatchPolicy,\n schemaVersion: number | undefined,\n): string {\n const schemaVersionKey =\n schemaVersion === undefined ? `schema:default` : `schema:${schemaVersion}`\n return `${schemaMismatchPolicy}|${schemaVersionKey}`\n}\n\nfunction createInternalSQLiteDriver(\n options: TauriSQLitePersistenceOptions,\n): SQLiteDriver {\n const cachedDriver = tauriDriverCache.get(options.database)\n if (cachedDriver) {\n return cachedDriver\n }\n\n const driver = new TauriSQLiteDriver({\n database: options.database,\n })\n tauriDriverCache.set(options.database, driver)\n return driver\n}\n\nfunction resolveAdapterBaseOptions(\n options: TauriSQLitePersistenceOptions,\n): Omit<\n SQLiteCoreAdapterOptions,\n `driver` | `schemaVersion` | `schemaMismatchPolicy`\n> {\n return {\n appliedTxPruneMaxRows: options.appliedTxPruneMaxRows,\n appliedTxPruneMaxAgeSeconds: options.appliedTxPruneMaxAgeSeconds,\n pullSinceReloadThreshold: options.pullSinceReloadThreshold,\n }\n}\n\nexport function createTauriSQLitePersistence<\n T extends object,\n TKey extends string | number = string | number,\n>(\n options: TauriSQLitePersistenceOptions,\n): PersistedCollectionPersistence<T, TKey> {\n const { coordinator, schemaMismatchPolicy } = options\n const driver = createInternalSQLiteDriver(options)\n const adapterBaseOptions = resolveAdapterBaseOptions(options)\n const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator()\n const adapterCache = new Map<\n string,\n ReturnType<\n typeof createSQLiteCorePersistenceAdapter<\n Record<string, unknown>,\n string | number\n >\n >\n >()\n\n const getAdapterForCollection = (\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ) => {\n const resolvedSchemaMismatchPolicy = resolveSchemaMismatchPolicy(\n schemaMismatchPolicy,\n mode,\n )\n const cacheKey = createAdapterCacheKey(\n resolvedSchemaMismatchPolicy,\n schemaVersion,\n )\n const cachedAdapter = adapterCache.get(cacheKey)\n if (cachedAdapter) {\n return cachedAdapter\n }\n\n const adapter = createSQLiteCorePersistenceAdapter<\n Record<string, unknown>,\n string | number\n >({\n ...adapterBaseOptions,\n driver,\n schemaMismatchPolicy: resolvedSchemaMismatchPolicy,\n ...(schemaVersion === undefined ? {} : { schemaVersion }),\n })\n adapterCache.set(cacheKey, adapter)\n return adapter\n }\n\n const createCollectionPersistence = (\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ): PersistedCollectionPersistence<T, TKey> => ({\n adapter: getAdapterForCollection(\n mode,\n schemaVersion,\n ) as unknown as PersistedCollectionPersistence<T, TKey>[`adapter`],\n coordinator: resolvedCoordinator,\n })\n\n const defaultPersistence = createCollectionPersistence(\n `sync-absent`,\n undefined,\n )\n\n return {\n ...defaultPersistence,\n resolvePersistenceForCollection: ({ mode, schemaVersion }) =>\n createCollectionPersistence(mode, schemaVersion),\n resolvePersistenceForMode: (mode) =>\n createCollectionPersistence(mode, undefined),\n }\n}\n"],"names":[],"mappings":";;AAoCA,MAAM,uCAAuB,QAAA;AAK7B,SAAS,8BACP,QACqC;AACrC,MAAI,WAAW,SAAS;AACtB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,4BACP,gBACA,MACqC;AACrC,MAAI,gBAAgB;AAClB,WAAO,8BAA8B,cAAc;AAAA,EACrD;AAEA,SAAO,SAAS,iBAAiB,uBAAuB;AAC1D;AAEA,SAAS,sBACP,sBACA,eACQ;AACR,QAAM,mBACJ,kBAAkB,SAAY,mBAAmB,UAAU,aAAa;AAC1E,SAAO,GAAG,oBAAoB,IAAI,gBAAgB;AACpD;AAEA,SAAS,2BACP,SACc;AACd,QAAM,eAAe,iBAAiB,IAAI,QAAQ,QAAQ;AAC1D,MAAI,cAAc;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,IAAI,kBAAkB;AAAA,IACnC,UAAU,QAAQ;AAAA,EAAA,CACnB;AACD,mBAAiB,IAAI,QAAQ,UAAU,MAAM;AAC7C,SAAO;AACT;AAEA,SAAS,0BACP,SAIA;AACA,SAAO;AAAA,IACL,uBAAuB,QAAQ;AAAA,IAC/B,6BAA6B,QAAQ;AAAA,IACrC,0BAA0B,QAAQ;AAAA,EAAA;AAEtC;AAEO,SAAS,6BAId,SACyC;AACzC,QAAM,EAAE,aAAa,qBAAA,IAAyB;AAC9C,QAAM,SAAS,2BAA2B,OAAO;AACjD,QAAM,qBAAqB,0BAA0B,OAAO;AAC5D,QAAM,sBAAsB,eAAe,IAAI,yBAAA;AAC/C,QAAM,mCAAmB,IAAA;AAUzB,QAAM,0BAA0B,CAC9B,MACA,kBACG;AACH,UAAM,+BAA+B;AAAA,MACnC;AAAA,MACA;AAAA,IAAA;AAEF,UAAM,WAAW;AAAA,MACf;AAAA,MACA;AAAA,IAAA;AAEF,UAAM,gBAAgB,aAAa,IAAI,QAAQ;AAC/C,QAAI,eAAe;AACjB,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,mCAGd;AAAA,MACA,GAAG;AAAA,MACH;AAAA,MACA,sBAAsB;AAAA,MACtB,GAAI,kBAAkB,SAAY,CAAA,IAAK,EAAE,cAAA;AAAA,IAAc,CACxD;AACD,iBAAa,IAAI,UAAU,OAAO;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,8BAA8B,CAClC,MACA,mBAC6C;AAAA,IAC7C,SAAS;AAAA,MACP;AAAA,MACA;AAAA,IAAA;AAAA,IAEF,aAAa;AAAA,EAAA;AAGf,QAAM,qBAAqB;AAAA,IACzB;AAAA,IACA;AAAA,EAAA;AAGF,SAAO;AAAA,IACL,GAAG;AAAA,IACH,iCAAiC,CAAC,EAAE,MAAM,oBACxC,4BAA4B,MAAM,aAAa;AAAA,IACjD,2BAA2B,CAAC,SAC1B,4BAA4B,MAAM,MAAS;AAAA,EAAA;AAEjD;"}
|