@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
|
@@ -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,207 @@
|
|
|
1
|
+
import { InvalidPersistedCollectionConfigError } from "@tanstack/db-sqlite-persistence-core";
|
|
2
|
+
function assertTransactionCallbackHasDriverArg(fn) {
|
|
3
|
+
if (fn.length > 0) {
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
throw new InvalidPersistedCollectionConfigError(
|
|
7
|
+
`SQLiteDriver.transaction callback must accept the transaction driver argument`
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
function isTauriSQLiteDatabaseLike(value) {
|
|
11
|
+
const candidate = value;
|
|
12
|
+
return typeof value === `object` && value !== null && typeof candidate.path === `string` && typeof candidate.execute === `function` && typeof candidate.select === `function`;
|
|
13
|
+
}
|
|
14
|
+
function normalizeQueryRows(rows, sql) {
|
|
15
|
+
if (Array.isArray(rows)) {
|
|
16
|
+
return rows;
|
|
17
|
+
}
|
|
18
|
+
throw new InvalidPersistedCollectionConfigError(
|
|
19
|
+
`Unsupported Tauri SQL query result shape for SQL "${sql}"`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
function convertSqlitePlaceholdersToTauri(sql) {
|
|
23
|
+
let result = ``;
|
|
24
|
+
let parameterIndex = 1;
|
|
25
|
+
let inSingleQuote = false;
|
|
26
|
+
let inDoubleQuote = false;
|
|
27
|
+
let inLineComment = false;
|
|
28
|
+
let inBlockComment = false;
|
|
29
|
+
for (let index = 0; index < sql.length; index++) {
|
|
30
|
+
const currentChar = sql[index];
|
|
31
|
+
const nextChar = sql[index + 1];
|
|
32
|
+
if (inLineComment) {
|
|
33
|
+
result += currentChar;
|
|
34
|
+
if (currentChar === `
|
|
35
|
+
`) {
|
|
36
|
+
inLineComment = false;
|
|
37
|
+
}
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (inBlockComment) {
|
|
41
|
+
result += currentChar;
|
|
42
|
+
if (currentChar === `*` && nextChar === `/`) {
|
|
43
|
+
result += `/`;
|
|
44
|
+
index++;
|
|
45
|
+
inBlockComment = false;
|
|
46
|
+
}
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (!inSingleQuote && !inDoubleQuote) {
|
|
50
|
+
if (currentChar === `-` && nextChar === `-`) {
|
|
51
|
+
result += `--`;
|
|
52
|
+
index++;
|
|
53
|
+
inLineComment = true;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (currentChar === `/` && nextChar === `*`) {
|
|
57
|
+
result += `/*`;
|
|
58
|
+
index++;
|
|
59
|
+
inBlockComment = true;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (currentChar === `'` && !inDoubleQuote) {
|
|
64
|
+
result += currentChar;
|
|
65
|
+
if (inSingleQuote && nextChar === `'`) {
|
|
66
|
+
result += `'`;
|
|
67
|
+
index++;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
inSingleQuote = !inSingleQuote;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (currentChar === `"` && !inSingleQuote) {
|
|
74
|
+
result += currentChar;
|
|
75
|
+
if (inDoubleQuote && nextChar === `"`) {
|
|
76
|
+
result += `"`;
|
|
77
|
+
index++;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
inDoubleQuote = !inDoubleQuote;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (currentChar === `?` && !inSingleQuote && !inDoubleQuote) {
|
|
84
|
+
result += `$${String(parameterIndex)}`;
|
|
85
|
+
parameterIndex++;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
result += currentChar;
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
class TauriSQLiteDriver {
|
|
93
|
+
constructor(options) {
|
|
94
|
+
this.queue = Promise.resolve();
|
|
95
|
+
this.nextSavepointId = 1;
|
|
96
|
+
if (!isTauriSQLiteDatabaseLike(options.database)) {
|
|
97
|
+
throw new InvalidPersistedCollectionConfigError(
|
|
98
|
+
`Tauri SQLite database object must provide execute/select methods`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
this.database = options.database;
|
|
102
|
+
}
|
|
103
|
+
async exec(sql) {
|
|
104
|
+
await this.enqueue(async () => {
|
|
105
|
+
await this.executeStatement(sql);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async query(sql, params = []) {
|
|
109
|
+
return this.enqueue(async () => {
|
|
110
|
+
const rows = await this.database.select(
|
|
111
|
+
convertSqlitePlaceholdersToTauri(sql),
|
|
112
|
+
params.length > 0 ? [...params] : void 0
|
|
113
|
+
);
|
|
114
|
+
return normalizeQueryRows(rows, sql);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
async run(sql, params = []) {
|
|
118
|
+
await this.enqueue(async () => {
|
|
119
|
+
await this.executeStatement(sql, params);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
async transaction(fn) {
|
|
123
|
+
assertTransactionCallbackHasDriverArg(fn);
|
|
124
|
+
return this.transactionWithDriver(fn);
|
|
125
|
+
}
|
|
126
|
+
async transactionWithDriver(fn) {
|
|
127
|
+
return this.enqueue(async () => {
|
|
128
|
+
await this.executeStatement(`BEGIN IMMEDIATE`);
|
|
129
|
+
const transactionDriver = this.createTransactionDriver();
|
|
130
|
+
try {
|
|
131
|
+
const result = await fn(transactionDriver);
|
|
132
|
+
await this.executeStatement(`COMMIT`);
|
|
133
|
+
return result;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
try {
|
|
136
|
+
await this.executeStatement(`ROLLBACK`);
|
|
137
|
+
} catch {
|
|
138
|
+
}
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async close() {
|
|
144
|
+
if (typeof this.database.close !== `function`) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
await Promise.resolve(this.database.close(this.database.path));
|
|
148
|
+
}
|
|
149
|
+
getDatabase() {
|
|
150
|
+
return this.database;
|
|
151
|
+
}
|
|
152
|
+
async executeStatement(sql, params = []) {
|
|
153
|
+
await this.database.execute(
|
|
154
|
+
convertSqlitePlaceholdersToTauri(sql),
|
|
155
|
+
params.length > 0 ? [...params] : void 0
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
enqueue(operation) {
|
|
159
|
+
const queuedOperation = this.queue.then(operation, operation);
|
|
160
|
+
this.queue = queuedOperation.then(
|
|
161
|
+
() => void 0,
|
|
162
|
+
() => void 0
|
|
163
|
+
);
|
|
164
|
+
return queuedOperation;
|
|
165
|
+
}
|
|
166
|
+
createTransactionDriver() {
|
|
167
|
+
const transactionDriver = {
|
|
168
|
+
exec: async (sql) => {
|
|
169
|
+
await this.executeStatement(sql);
|
|
170
|
+
},
|
|
171
|
+
query: async (sql, params = []) => {
|
|
172
|
+
const rows = await this.database.select(
|
|
173
|
+
convertSqlitePlaceholdersToTauri(sql),
|
|
174
|
+
params.length > 0 ? [...params] : void 0
|
|
175
|
+
);
|
|
176
|
+
return normalizeQueryRows(rows, sql);
|
|
177
|
+
},
|
|
178
|
+
run: async (sql, params = []) => {
|
|
179
|
+
await this.executeStatement(sql, params);
|
|
180
|
+
},
|
|
181
|
+
transaction: async (fn) => {
|
|
182
|
+
assertTransactionCallbackHasDriverArg(fn);
|
|
183
|
+
return this.runNestedTransaction(transactionDriver, fn);
|
|
184
|
+
},
|
|
185
|
+
transactionWithDriver: async (fn) => this.runNestedTransaction(transactionDriver, fn)
|
|
186
|
+
};
|
|
187
|
+
return transactionDriver;
|
|
188
|
+
}
|
|
189
|
+
async runNestedTransaction(transactionDriver, fn) {
|
|
190
|
+
const savepointName = `tsdb_sp_${this.nextSavepointId}`;
|
|
191
|
+
this.nextSavepointId++;
|
|
192
|
+
await this.executeStatement(`SAVEPOINT ${savepointName}`);
|
|
193
|
+
try {
|
|
194
|
+
const result = await fn(transactionDriver);
|
|
195
|
+
await this.executeStatement(`RELEASE SAVEPOINT ${savepointName}`);
|
|
196
|
+
return result;
|
|
197
|
+
} catch (error) {
|
|
198
|
+
await this.executeStatement(`ROLLBACK TO SAVEPOINT ${savepointName}`);
|
|
199
|
+
await this.executeStatement(`RELEASE SAVEPOINT ${savepointName}`);
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
export {
|
|
205
|
+
TauriSQLiteDriver
|
|
206
|
+
};
|
|
207
|
+
//# sourceMappingURL=tauri-sql-driver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tauri-sql-driver.js","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":[],"mappings":";AAaA,SAAS,sCACP,IACM;AACN,MAAI,GAAG,SAAS,GAAG;AACjB;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EAAA;AAEJ;AAEA,SAAS,0BACP,OACkC;AAClC,QAAM,YAAY;AAClB,SACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAO,UAAU,SAAS,YAC1B,OAAO,UAAU,YAAY,cAC7B,OAAO,UAAU,WAAW;AAEhC;AAEA,SAAS,mBACP,MACA,KACqB;AACrB,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,IAAI;AAAA,IACR,qDAAqD,GAAG;AAAA,EAAA;AAE5D;AAEA,SAAS,iCAAiC,KAAqB;AAC7D,MAAI,SAAS;AACb,MAAI,iBAAiB;AACrB,MAAI,gBAAgB;AACpB,MAAI,gBAAgB;AACpB,MAAI,gBAAgB;AACpB,MAAI,iBAAiB;AAErB,WAAS,QAAQ,GAAG,QAAQ,IAAI,QAAQ,SAAS;AAC/C,UAAM,cAAc,IAAI,KAAK;AAC7B,UAAM,WAAW,IAAI,QAAQ,CAAC;AAE9B,QAAI,eAAe;AACjB,gBAAU;AACV,UAAI,gBAAgB;AAAA,GAAM;AACxB,wBAAgB;AAAA,MAClB;AACA;AAAA,IACF;AAEA,QAAI,gBAAgB;AAClB,gBAAU;AACV,UAAI,gBAAgB,OAAO,aAAa,KAAK;AAC3C,kBAAU;AACV;AACA,yBAAiB;AAAA,MACnB;AACA;AAAA,IACF;AAEA,QAAI,CAAC,iBAAiB,CAAC,eAAe;AACpC,UAAI,gBAAgB,OAAO,aAAa,KAAK;AAC3C,kBAAU;AACV;AACA,wBAAgB;AAChB;AAAA,MACF;AAEA,UAAI,gBAAgB,OAAO,aAAa,KAAK;AAC3C,kBAAU;AACV;AACA,yBAAiB;AACjB;AAAA,MACF;AAAA,IACF;AAEA,QAAI,gBAAgB,OAAO,CAAC,eAAe;AACzC,gBAAU;AACV,UAAI,iBAAiB,aAAa,KAAK;AACrC,kBAAU;AACV;AACA;AAAA,MACF;AACA,sBAAgB,CAAC;AACjB;AAAA,IACF;AAEA,QAAI,gBAAgB,OAAO,CAAC,eAAe;AACzC,gBAAU;AACV,UAAI,iBAAiB,aAAa,KAAK;AACrC,kBAAU;AACV;AACA;AAAA,MACF;AACA,sBAAgB,CAAC;AACjB;AAAA,IACF;AAEA,QAAI,gBAAgB,OAAO,CAAC,iBAAiB,CAAC,eAAe;AAC3D,gBAAU,IAAI,OAAO,cAAc,CAAC;AACpC;AACA;AAAA,IACF;AAEA,cAAU;AAAA,EACZ;AAEA,SAAO;AACT;AAEO,MAAM,kBAA0C;AAAA,EAKrD,YAAY,SAAmC;AAH/C,SAAQ,QAAuB,QAAQ,QAAA;AACvC,SAAQ,kBAAkB;AAGxB,QAAI,CAAC,0BAA0B,QAAQ,QAAQ,GAAG;AAChD,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AAEA,SAAK,WAAW,QAAQ;AAAA,EAC1B;AAAA,EAEA,MAAM,KAAK,KAA4B;AACrC,UAAM,KAAK,QAAQ,YAAY;AAC7B,YAAM,KAAK,iBAAiB,GAAG;AAAA,IACjC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MACJ,KACA,SAAiC,IACH;AAC9B,WAAO,KAAK,QAAQ,YAAY;AAC9B,YAAM,OAAO,MAAM,KAAK,SAAS;AAAA,QAC/B,iCAAiC,GAAG;AAAA,QACpC,OAAO,SAAS,IAAI,CAAC,GAAG,MAAM,IAAI;AAAA,MAAA;AAEpC,aAAO,mBAAyB,MAAM,GAAG;AAAA,IAC3C,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,IAAI,KAAa,SAAiC,IAAmB;AACzE,UAAM,KAAK,QAAQ,YAAY;AAC7B,YAAM,KAAK,iBAAiB,KAAK,MAAM;AAAA,IACzC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,YACJ,IACkB;AAClB,0CAAsC,EAAE;AACxC,WAAO,KAAK,sBAAsB,EAAE;AAAA,EACtC;AAAA,EAEA,MAAM,sBACJ,IACkB;AAClB,WAAO,KAAK,QAAQ,YAAY;AAC9B,YAAM,KAAK,iBAAiB,iBAAiB;AAC7C,YAAM,oBAAoB,KAAK,wBAAA;AAE/B,UAAI;AACF,cAAM,SAAS,MAAM,GAAG,iBAAiB;AACzC,cAAM,KAAK,iBAAiB,QAAQ;AACpC,eAAO;AAAA,MACT,SAAS,OAAO;AACd,YAAI;AACF,gBAAM,KAAK,iBAAiB,UAAU;AAAA,QACxC,QAAQ;AAAA,QAER;AACA,cAAM;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,OAAO,KAAK,SAAS,UAAU,YAAY;AAC7C;AAAA,IACF;AAEA,UAAM,QAAQ,QAAQ,KAAK,SAAS,MAAM,KAAK,SAAS,IAAI,CAAC;AAAA,EAC/D;AAAA,EAEA,cAAuC;AACrC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,iBACZ,KACA,SAAiC,IAClB;AACf,UAAM,KAAK,SAAS;AAAA,MAClB,iCAAiC,GAAG;AAAA,MACpC,OAAO,SAAS,IAAI,CAAC,GAAG,MAAM,IAAI;AAAA,IAAA;AAAA,EAEtC;AAAA,EAEQ,QACN,WACkB;AAClB,UAAM,kBAAkB,KAAK,MAAM,KAAK,WAAW,SAAS;AAC5D,SAAK,QAAQ,gBAAgB;AAAA,MAC3B,MAAM;AAAA,MACN,MAAM;AAAA,IAAA;AAER,WAAO;AAAA,EACT;AAAA,EAEQ,0BAAwC;AAC9C,UAAM,oBAAkC;AAAA,MACtC,MAAM,OAAO,QAAQ;AACnB,cAAM,KAAK,iBAAiB,GAAG;AAAA,MACjC;AAAA,MACA,OAAO,OACL,KACA,SAAiC,OACA;AACjC,cAAM,OAAO,MAAM,KAAK,SAAS;AAAA,UAC/B,iCAAiC,GAAG;AAAA,UACpC,OAAO,SAAS,IAAI,CAAC,GAAG,MAAM,IAAI;AAAA,QAAA;AAEpC,eAAO,mBAAyB,MAAM,GAAG;AAAA,MAC3C;AAAA,MACA,KAAK,OAAO,KAAK,SAAS,OAAO;AAC/B,cAAM,KAAK,iBAAiB,KAAK,MAAM;AAAA,MACzC;AAAA,MACA,aAAa,OACX,OACqB;AACrB,8CAAsC,EAAE;AACxC,eAAO,KAAK,qBAAqB,mBAAmB,EAAE;AAAA,MACxD;AAAA,MACA,uBAAuB,OACrB,OACqB,KAAK,qBAAqB,mBAAmB,EAAE;AAAA,IAAA;AAGxE,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,qBACZ,mBACA,IACkB;AAClB,UAAM,gBAAgB,WAAW,KAAK,eAAe;AACrD,SAAK;AACL,UAAM,KAAK,iBAAiB,aAAa,aAAa,EAAE;AAExD,QAAI;AACF,YAAM,SAAS,MAAM,GAAG,iBAAiB;AACzC,YAAM,KAAK,iBAAiB,qBAAqB,aAAa,EAAE;AAChE,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,KAAK,iBAAiB,yBAAyB,aAAa,EAAE;AACpE,YAAM,KAAK,iBAAiB,qBAAqB,aAAa,EAAE;AAChE,YAAM;AAAA,IACR;AAAA,EACF;AACF;"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { TauriSQLitePersistenceOptions as TauriSQLitePersistenceOptionsBase, TauriSQLiteSchemaMismatchPolicy as TauriSQLiteSchemaMismatchPolicyBase } from './tauri-persistence.js';
|
|
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.js';
|
|
6
|
+
export declare function createTauriSQLitePersistence<T extends object, TKey extends string | number = string | number>(options: TauriSQLitePersistenceOptions): PersistedCollectionPersistence<T, TKey>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createTauriSQLitePersistence as createTauriSQLitePersistence$1 } from "./tauri-persistence.js";
|
|
2
|
+
function createTauriSQLitePersistence(options) {
|
|
3
|
+
return createTauriSQLitePersistence$1(options);
|
|
4
|
+
}
|
|
5
|
+
export {
|
|
6
|
+
createTauriSQLitePersistence
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=tauri.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tauri.js","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,+BAA2B,OAAO;AAC3C;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tanstack/tauri-db-sqlite-persistence",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Tauri SQLite persisted collection adapter for TanStack DB",
|
|
5
|
+
"author": "TanStack Team",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/TanStack/db.git",
|
|
10
|
+
"directory": "packages/tauri-db-sqlite-persistence"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://tanstack.com/db",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"sqlite",
|
|
15
|
+
"tauri",
|
|
16
|
+
"persistence",
|
|
17
|
+
"typescript"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "vite build",
|
|
21
|
+
"dev": "vite build --watch",
|
|
22
|
+
"lint": "eslint . --fix",
|
|
23
|
+
"test": "vitest --run",
|
|
24
|
+
"test:e2e": "pnpm --filter @tanstack/db-ivm build && pnpm --filter @tanstack/db build && pnpm --filter @tanstack/db-sqlite-persistence-core build && pnpm --filter @tanstack/tauri-db-sqlite-persistence build && vitest --config vitest.e2e.config.ts --run"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"main": "dist/cjs/index.cjs",
|
|
28
|
+
"module": "dist/esm/index.js",
|
|
29
|
+
"types": "dist/esm/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"import": {
|
|
33
|
+
"types": "./dist/esm/index.d.ts",
|
|
34
|
+
"default": "./dist/esm/index.js"
|
|
35
|
+
},
|
|
36
|
+
"require": {
|
|
37
|
+
"types": "./dist/cjs/index.d.cts",
|
|
38
|
+
"default": "./dist/cjs/index.cjs"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"./tauri": {
|
|
42
|
+
"import": {
|
|
43
|
+
"types": "./dist/esm/tauri.d.ts",
|
|
44
|
+
"default": "./dist/esm/tauri.js"
|
|
45
|
+
},
|
|
46
|
+
"require": {
|
|
47
|
+
"types": "./dist/cjs/tauri.d.cts",
|
|
48
|
+
"default": "./dist/cjs/tauri.cjs"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"./package.json": "./package.json"
|
|
52
|
+
},
|
|
53
|
+
"sideEffects": false,
|
|
54
|
+
"files": [
|
|
55
|
+
"dist",
|
|
56
|
+
"src"
|
|
57
|
+
],
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@tanstack/db-sqlite-persistence-core": "workspace:*"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"@tauri-apps/plugin-sql": "^2.3.2",
|
|
63
|
+
"typescript": ">=4.7"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@tauri-apps/plugin-sql": "^2.3.2",
|
|
67
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
68
|
+
"@vitest/coverage-istanbul": "^3.2.4",
|
|
69
|
+
"better-sqlite3": "^12.6.2"
|
|
70
|
+
}
|
|
71
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { createTauriSQLitePersistence } from './tauri'
|
|
2
|
+
export type {
|
|
3
|
+
TauriSQLiteDatabaseLike,
|
|
4
|
+
TauriSQLitePersistenceOptions,
|
|
5
|
+
TauriSQLiteSchemaMismatchPolicy,
|
|
6
|
+
} from './tauri'
|
|
7
|
+
export { persistedCollectionOptions } from '@tanstack/db-sqlite-persistence-core'
|
|
8
|
+
export type {
|
|
9
|
+
PersistedCollectionCoordinator,
|
|
10
|
+
PersistedCollectionPersistence,
|
|
11
|
+
} from '@tanstack/db-sqlite-persistence-core'
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SingleProcessCoordinator,
|
|
3
|
+
createSQLiteCorePersistenceAdapter,
|
|
4
|
+
} from '@tanstack/db-sqlite-persistence-core'
|
|
5
|
+
import { TauriSQLiteDriver } from './tauri-sql-driver'
|
|
6
|
+
import type {
|
|
7
|
+
PersistedCollectionCoordinator,
|
|
8
|
+
PersistedCollectionMode,
|
|
9
|
+
PersistedCollectionPersistence,
|
|
10
|
+
SQLiteCoreAdapterOptions,
|
|
11
|
+
SQLiteDriver,
|
|
12
|
+
} from '@tanstack/db-sqlite-persistence-core'
|
|
13
|
+
import type { TauriSQLiteDatabaseLike } from './tauri-sql-driver'
|
|
14
|
+
|
|
15
|
+
export type { TauriSQLiteDatabaseLike } from './tauri-sql-driver'
|
|
16
|
+
|
|
17
|
+
type TauriSQLiteCoreSchemaMismatchPolicy =
|
|
18
|
+
| `sync-present-reset`
|
|
19
|
+
| `sync-absent-error`
|
|
20
|
+
| `reset`
|
|
21
|
+
|
|
22
|
+
export type TauriSQLiteSchemaMismatchPolicy =
|
|
23
|
+
| TauriSQLiteCoreSchemaMismatchPolicy
|
|
24
|
+
| `throw`
|
|
25
|
+
|
|
26
|
+
type TauriSQLitePersistenceBaseOptions = Omit<
|
|
27
|
+
SQLiteCoreAdapterOptions,
|
|
28
|
+
`driver` | `schemaVersion` | `schemaMismatchPolicy`
|
|
29
|
+
> & {
|
|
30
|
+
database: TauriSQLiteDatabaseLike
|
|
31
|
+
coordinator?: PersistedCollectionCoordinator
|
|
32
|
+
schemaMismatchPolicy?: TauriSQLiteSchemaMismatchPolicy
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type TauriSQLitePersistenceOptions = TauriSQLitePersistenceBaseOptions
|
|
36
|
+
|
|
37
|
+
const tauriDriverCache = new WeakMap<
|
|
38
|
+
TauriSQLiteDatabaseLike,
|
|
39
|
+
TauriSQLiteDriver
|
|
40
|
+
>()
|
|
41
|
+
|
|
42
|
+
function normalizeSchemaMismatchPolicy(
|
|
43
|
+
policy: TauriSQLiteSchemaMismatchPolicy,
|
|
44
|
+
): TauriSQLiteCoreSchemaMismatchPolicy {
|
|
45
|
+
if (policy === `throw`) {
|
|
46
|
+
return `sync-absent-error`
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return policy
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function resolveSchemaMismatchPolicy(
|
|
53
|
+
explicitPolicy: TauriSQLiteSchemaMismatchPolicy | undefined,
|
|
54
|
+
mode: PersistedCollectionMode,
|
|
55
|
+
): TauriSQLiteCoreSchemaMismatchPolicy {
|
|
56
|
+
if (explicitPolicy) {
|
|
57
|
+
return normalizeSchemaMismatchPolicy(explicitPolicy)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return mode === `sync-present` ? `sync-present-reset` : `sync-absent-error`
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function createAdapterCacheKey(
|
|
64
|
+
schemaMismatchPolicy: TauriSQLiteCoreSchemaMismatchPolicy,
|
|
65
|
+
schemaVersion: number | undefined,
|
|
66
|
+
): string {
|
|
67
|
+
const schemaVersionKey =
|
|
68
|
+
schemaVersion === undefined ? `schema:default` : `schema:${schemaVersion}`
|
|
69
|
+
return `${schemaMismatchPolicy}|${schemaVersionKey}`
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function createInternalSQLiteDriver(
|
|
73
|
+
options: TauriSQLitePersistenceOptions,
|
|
74
|
+
): SQLiteDriver {
|
|
75
|
+
const cachedDriver = tauriDriverCache.get(options.database)
|
|
76
|
+
if (cachedDriver) {
|
|
77
|
+
return cachedDriver
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const driver = new TauriSQLiteDriver({
|
|
81
|
+
database: options.database,
|
|
82
|
+
})
|
|
83
|
+
tauriDriverCache.set(options.database, driver)
|
|
84
|
+
return driver
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function resolveAdapterBaseOptions(
|
|
88
|
+
options: TauriSQLitePersistenceOptions,
|
|
89
|
+
): Omit<
|
|
90
|
+
SQLiteCoreAdapterOptions,
|
|
91
|
+
`driver` | `schemaVersion` | `schemaMismatchPolicy`
|
|
92
|
+
> {
|
|
93
|
+
return {
|
|
94
|
+
appliedTxPruneMaxRows: options.appliedTxPruneMaxRows,
|
|
95
|
+
appliedTxPruneMaxAgeSeconds: options.appliedTxPruneMaxAgeSeconds,
|
|
96
|
+
pullSinceReloadThreshold: options.pullSinceReloadThreshold,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function createTauriSQLitePersistence<
|
|
101
|
+
T extends object,
|
|
102
|
+
TKey extends string | number = string | number,
|
|
103
|
+
>(
|
|
104
|
+
options: TauriSQLitePersistenceOptions,
|
|
105
|
+
): PersistedCollectionPersistence<T, TKey> {
|
|
106
|
+
const { coordinator, schemaMismatchPolicy } = options
|
|
107
|
+
const driver = createInternalSQLiteDriver(options)
|
|
108
|
+
const adapterBaseOptions = resolveAdapterBaseOptions(options)
|
|
109
|
+
const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator()
|
|
110
|
+
const adapterCache = new Map<
|
|
111
|
+
string,
|
|
112
|
+
ReturnType<
|
|
113
|
+
typeof createSQLiteCorePersistenceAdapter<
|
|
114
|
+
Record<string, unknown>,
|
|
115
|
+
string | number
|
|
116
|
+
>
|
|
117
|
+
>
|
|
118
|
+
>()
|
|
119
|
+
|
|
120
|
+
const getAdapterForCollection = (
|
|
121
|
+
mode: PersistedCollectionMode,
|
|
122
|
+
schemaVersion: number | undefined,
|
|
123
|
+
) => {
|
|
124
|
+
const resolvedSchemaMismatchPolicy = resolveSchemaMismatchPolicy(
|
|
125
|
+
schemaMismatchPolicy,
|
|
126
|
+
mode,
|
|
127
|
+
)
|
|
128
|
+
const cacheKey = createAdapterCacheKey(
|
|
129
|
+
resolvedSchemaMismatchPolicy,
|
|
130
|
+
schemaVersion,
|
|
131
|
+
)
|
|
132
|
+
const cachedAdapter = adapterCache.get(cacheKey)
|
|
133
|
+
if (cachedAdapter) {
|
|
134
|
+
return cachedAdapter
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const adapter = createSQLiteCorePersistenceAdapter<
|
|
138
|
+
Record<string, unknown>,
|
|
139
|
+
string | number
|
|
140
|
+
>({
|
|
141
|
+
...adapterBaseOptions,
|
|
142
|
+
driver,
|
|
143
|
+
schemaMismatchPolicy: resolvedSchemaMismatchPolicy,
|
|
144
|
+
...(schemaVersion === undefined ? {} : { schemaVersion }),
|
|
145
|
+
})
|
|
146
|
+
adapterCache.set(cacheKey, adapter)
|
|
147
|
+
return adapter
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const createCollectionPersistence = (
|
|
151
|
+
mode: PersistedCollectionMode,
|
|
152
|
+
schemaVersion: number | undefined,
|
|
153
|
+
): PersistedCollectionPersistence<T, TKey> => ({
|
|
154
|
+
adapter: getAdapterForCollection(
|
|
155
|
+
mode,
|
|
156
|
+
schemaVersion,
|
|
157
|
+
) as unknown as PersistedCollectionPersistence<T, TKey>[`adapter`],
|
|
158
|
+
coordinator: resolvedCoordinator,
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
const defaultPersistence = createCollectionPersistence(
|
|
162
|
+
`sync-absent`,
|
|
163
|
+
undefined,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
...defaultPersistence,
|
|
168
|
+
resolvePersistenceForCollection: ({ mode, schemaVersion }) =>
|
|
169
|
+
createCollectionPersistence(mode, schemaVersion),
|
|
170
|
+
resolvePersistenceForMode: (mode) =>
|
|
171
|
+
createCollectionPersistence(mode, undefined),
|
|
172
|
+
}
|
|
173
|
+
}
|