@anfenn/dync 1.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/LICENSE +21 -0
- package/README.md +212 -0
- package/dist/capacitor.cjs +228 -0
- package/dist/capacitor.cjs.map +1 -0
- package/dist/capacitor.d.cts +62 -0
- package/dist/capacitor.d.ts +62 -0
- package/dist/capacitor.js +9 -0
- package/dist/capacitor.js.map +1 -0
- package/dist/chunk-LGHOZECP.js +3884 -0
- package/dist/chunk-LGHOZECP.js.map +1 -0
- package/dist/chunk-SQB6E7V2.js +191 -0
- package/dist/chunk-SQB6E7V2.js.map +1 -0
- package/dist/dexie-Bv-fV10P.d.cts +444 -0
- package/dist/dexie-DJFApKsM.d.ts +444 -0
- package/dist/dexie.cjs +381 -0
- package/dist/dexie.cjs.map +1 -0
- package/dist/dexie.d.cts +3 -0
- package/dist/dexie.d.ts +3 -0
- package/dist/dexie.js +343 -0
- package/dist/dexie.js.map +1 -0
- package/dist/expoSqlite.cjs +98 -0
- package/dist/expoSqlite.cjs.map +1 -0
- package/dist/expoSqlite.d.cts +17 -0
- package/dist/expoSqlite.d.ts +17 -0
- package/dist/expoSqlite.js +61 -0
- package/dist/expoSqlite.js.map +1 -0
- package/dist/index.cjs +3916 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/index.shared-CPIge2ZM.d.ts +234 -0
- package/dist/index.shared-YSn6c01d.d.cts +234 -0
- package/dist/node.cjs +126 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +80 -0
- package/dist/node.d.ts +80 -0
- package/dist/node.js +89 -0
- package/dist/node.js.map +1 -0
- package/dist/react/index.cjs +1754 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +40 -0
- package/dist/react/index.d.ts +40 -0
- package/dist/react/index.js +78 -0
- package/dist/react/index.js.map +1 -0
- package/dist/types-CSbIAfu2.d.cts +46 -0
- package/dist/types-CSbIAfu2.d.ts +46 -0
- package/dist/wa-sqlite.cjs +318 -0
- package/dist/wa-sqlite.cjs.map +1 -0
- package/dist/wa-sqlite.d.cts +175 -0
- package/dist/wa-sqlite.d.ts +175 -0
- package/dist/wa-sqlite.js +281 -0
- package/dist/wa-sqlite.js.map +1 -0
- package/package.json +171 -0
- package/src/addVisibilityChangeListener.native.ts +33 -0
- package/src/addVisibilityChangeListener.ts +24 -0
- package/src/capacitor.ts +4 -0
- package/src/core/StateManager.ts +272 -0
- package/src/core/firstLoad.ts +332 -0
- package/src/core/pullOperations.ts +212 -0
- package/src/core/pushOperations.ts +290 -0
- package/src/core/tableEnhancers.ts +457 -0
- package/src/core/types.ts +3 -0
- package/src/createLocalId.native.ts +8 -0
- package/src/createLocalId.ts +6 -0
- package/src/dexie.ts +2 -0
- package/src/expoSqlite.ts +2 -0
- package/src/helpers.ts +87 -0
- package/src/index.native.ts +28 -0
- package/src/index.shared.ts +613 -0
- package/src/index.ts +28 -0
- package/src/logger.ts +26 -0
- package/src/node.ts +4 -0
- package/src/react/index.ts +2 -0
- package/src/react/useDync.ts +156 -0
- package/src/storage/dexie/DexieAdapter.ts +72 -0
- package/src/storage/dexie/DexieQueryContext.ts +14 -0
- package/src/storage/dexie/DexieStorageCollection.ts +124 -0
- package/src/storage/dexie/DexieStorageTable.ts +123 -0
- package/src/storage/dexie/DexieStorageWhereClause.ts +103 -0
- package/src/storage/dexie/helpers.ts +1 -0
- package/src/storage/dexie/index.ts +7 -0
- package/src/storage/memory/MemoryAdapter.ts +55 -0
- package/src/storage/memory/MemoryCollection.ts +215 -0
- package/src/storage/memory/MemoryQueryContext.ts +14 -0
- package/src/storage/memory/MemoryTable.ts +336 -0
- package/src/storage/memory/MemoryWhereClause.ts +134 -0
- package/src/storage/memory/index.ts +7 -0
- package/src/storage/memory/types.ts +24 -0
- package/src/storage/sqlite/SQLiteAdapter.ts +564 -0
- package/src/storage/sqlite/SQLiteCollection.ts +294 -0
- package/src/storage/sqlite/SQLiteTable.ts +604 -0
- package/src/storage/sqlite/SQLiteWhereClause.ts +341 -0
- package/src/storage/sqlite/SqliteQueryContext.ts +30 -0
- package/src/storage/sqlite/drivers/BetterSqlite3Driver.ts +156 -0
- package/src/storage/sqlite/drivers/CapacitorFastSqlDriver.ts +114 -0
- package/src/storage/sqlite/drivers/CapacitorSQLiteDriver.ts +137 -0
- package/src/storage/sqlite/drivers/ExpoSQLiteDriver.native.ts +67 -0
- package/src/storage/sqlite/drivers/WaSqliteDriver.ts +537 -0
- package/src/storage/sqlite/drivers/wa-sqlite-vfs.d.ts +46 -0
- package/src/storage/sqlite/helpers.ts +144 -0
- package/src/storage/sqlite/index.ts +11 -0
- package/src/storage/sqlite/schema.ts +44 -0
- package/src/storage/sqlite/types.ts +164 -0
- package/src/storage/types.ts +112 -0
- package/src/types.ts +186 -0
- package/src/wa-sqlite.ts +4 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import type { StorageCollection, StorageWhereClause } from '../types';
|
|
2
|
+
import { LOCAL_PK } from '../../types';
|
|
3
|
+
import type { SQLiteCollectionState, SQLiteCondition, TableEntry } from './types';
|
|
4
|
+
import { createDefaultState, cloneValue, buildWhereClause } from './helpers';
|
|
5
|
+
import { SQLiteTable } from './SQLiteTable';
|
|
6
|
+
|
|
7
|
+
export class SQLiteCollection<T = any> implements StorageCollection<T> {
|
|
8
|
+
private readonly table: SQLiteTable<T>;
|
|
9
|
+
private readonly state: SQLiteCollectionState<T>;
|
|
10
|
+
|
|
11
|
+
constructor(table: SQLiteTable<T>, state?: Partial<SQLiteCollectionState<T>>) {
|
|
12
|
+
this.table = table;
|
|
13
|
+
const base = createDefaultState<T>();
|
|
14
|
+
this.state = {
|
|
15
|
+
...base,
|
|
16
|
+
...state,
|
|
17
|
+
sqlConditions: state?.sqlConditions ?? base.sqlConditions,
|
|
18
|
+
jsPredicate: state?.jsPredicate,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
getState(): SQLiteCollectionState<T> {
|
|
23
|
+
return { ...this.state, sqlConditions: [...this.state.sqlConditions] };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Add a SQL-expressible condition to this collection
|
|
27
|
+
addSqlCondition(condition: SQLiteCondition): SQLiteCollection<T> {
|
|
28
|
+
return new SQLiteCollection(this.table, {
|
|
29
|
+
...this.state,
|
|
30
|
+
sqlConditions: [...this.state.sqlConditions, condition],
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private replicate(overrides?: Partial<SQLiteCollectionState<T>>): SQLiteCollection<T> {
|
|
35
|
+
return new SQLiteCollection(this.table, {
|
|
36
|
+
...this.state,
|
|
37
|
+
...overrides,
|
|
38
|
+
sqlConditions: overrides?.sqlConditions ?? this.state.sqlConditions,
|
|
39
|
+
jsPredicate: overrides?.jsPredicate !== undefined ? overrides.jsPredicate : this.state.jsPredicate,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private withJsPredicate(predicate: (record: T, key: string, index: number) => boolean): SQLiteCollection<T> {
|
|
44
|
+
const existingPredicate = this.state.jsPredicate;
|
|
45
|
+
const combined = existingPredicate
|
|
46
|
+
? (record: T, key: string, index: number) => existingPredicate(record, key, index) && predicate(record, key, index)
|
|
47
|
+
: predicate;
|
|
48
|
+
return new SQLiteCollection(this.table, {
|
|
49
|
+
...this.state,
|
|
50
|
+
jsPredicate: combined,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private hasJsPredicate(): boolean {
|
|
55
|
+
return this.state.jsPredicate !== undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private resolveOrdering(): { index: string | string[]; direction: 'asc' | 'desc' } {
|
|
59
|
+
const base = this.state.orderBy ?? { index: LOCAL_PK, direction: 'asc' as const };
|
|
60
|
+
const direction = this.state.reverse ? (base.direction === 'asc' ? 'desc' : 'asc') : base.direction;
|
|
61
|
+
return { index: base.index, direction };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Execute a native SQL query with all SQL conditions.
|
|
66
|
+
* If there's a JS predicate, we fetch rows without LIMIT/OFFSET in SQL
|
|
67
|
+
* and apply them after JS filtering.
|
|
68
|
+
*/
|
|
69
|
+
private async executeQuery(
|
|
70
|
+
options: {
|
|
71
|
+
clone?: boolean;
|
|
72
|
+
limitOverride?: number;
|
|
73
|
+
orderByOverride?: { index: string | string[]; direction: 'asc' | 'desc' };
|
|
74
|
+
} = {},
|
|
75
|
+
): Promise<TableEntry<T>[]> {
|
|
76
|
+
const { whereClause, parameters } = buildWhereClause(this.state.sqlConditions);
|
|
77
|
+
const ordering = options.orderByOverride ?? this.resolveOrdering();
|
|
78
|
+
const cloneValues = options.clone !== false;
|
|
79
|
+
const hasJsFilter = this.hasJsPredicate();
|
|
80
|
+
const distinct = this.state.distinct;
|
|
81
|
+
|
|
82
|
+
// If we have a JS predicate, we can't apply LIMIT/OFFSET in SQL
|
|
83
|
+
// because we don't know which rows will pass the JS filter
|
|
84
|
+
const sqlLimit = hasJsFilter ? undefined : (options.limitOverride ?? this.state.limit);
|
|
85
|
+
const sqlOffset = hasJsFilter ? undefined : this.state.offset;
|
|
86
|
+
|
|
87
|
+
const rows = await this.table.queryWithConditions({
|
|
88
|
+
whereClause,
|
|
89
|
+
parameters,
|
|
90
|
+
orderBy: ordering,
|
|
91
|
+
limit: sqlLimit,
|
|
92
|
+
offset: sqlOffset,
|
|
93
|
+
distinct,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
let results: TableEntry<T>[] = rows.map((row) => ({
|
|
97
|
+
key: String((row as any)[LOCAL_PK]),
|
|
98
|
+
value: row,
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
// Apply JS predicate if present
|
|
102
|
+
if (hasJsFilter) {
|
|
103
|
+
const predicate = this.state.jsPredicate!;
|
|
104
|
+
results = results.filter((entry, index) => predicate(entry.value, entry.key, index));
|
|
105
|
+
|
|
106
|
+
// Apply offset/limit in JS since we couldn't apply them in SQL
|
|
107
|
+
const offset = Math.max(0, this.state.offset ?? 0);
|
|
108
|
+
const limit = options.limitOverride ?? this.state.limit;
|
|
109
|
+
if (offset > 0 || limit !== undefined) {
|
|
110
|
+
const end = limit !== undefined ? offset + limit : undefined;
|
|
111
|
+
results = results.slice(offset, end);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Clone values if needed
|
|
116
|
+
if (cloneValues) {
|
|
117
|
+
results = results.map((entry) => ({
|
|
118
|
+
key: entry.key,
|
|
119
|
+
value: this.table.cloneRecord(entry.value),
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return results;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async first(): Promise<T | undefined> {
|
|
127
|
+
const entries = await this.executeQuery({ limitOverride: 1 });
|
|
128
|
+
return entries[0]?.value;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async last(): Promise<T | undefined> {
|
|
132
|
+
return this.replicate({ reverse: !this.state.reverse }).first();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async each(callback: (item: T, index: number) => void | Promise<void>): Promise<void> {
|
|
136
|
+
const entries = await this.executeQuery();
|
|
137
|
+
for (const [index, entry] of entries.entries()) {
|
|
138
|
+
await callback(entry.value, index);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async eachKey(callback: (key: unknown, index: number) => void | Promise<void>): Promise<void> {
|
|
143
|
+
const entries = await this.executeQuery({ clone: false });
|
|
144
|
+
for (const [index, entry] of entries.entries()) {
|
|
145
|
+
await callback(entry.key, index);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async eachPrimaryKey(callback: (key: unknown, index: number) => void | Promise<void>): Promise<void> {
|
|
150
|
+
return this.eachKey(callback);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async eachUniqueKey(callback: (key: unknown, index: number) => void | Promise<void>): Promise<void> {
|
|
154
|
+
const keys = await this.uniqueKeys();
|
|
155
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
156
|
+
await callback(keys[index], index);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async keys(): Promise<unknown[]> {
|
|
161
|
+
// Optimization: use native SQL when no JS filtering needed
|
|
162
|
+
if (!this.hasJsPredicate()) {
|
|
163
|
+
const { whereClause, parameters } = buildWhereClause(this.state.sqlConditions);
|
|
164
|
+
const ordering = this.resolveOrdering();
|
|
165
|
+
return this.table.queryKeysWithConditions({
|
|
166
|
+
whereClause,
|
|
167
|
+
parameters,
|
|
168
|
+
orderBy: ordering,
|
|
169
|
+
limit: this.state.limit,
|
|
170
|
+
offset: this.state.offset,
|
|
171
|
+
distinct: this.state.distinct,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
// Fallback for JS filtering
|
|
175
|
+
const entries = await this.executeQuery({ clone: false });
|
|
176
|
+
return entries.map((entry) => entry.key);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async primaryKeys(): Promise<unknown[]> {
|
|
180
|
+
return this.keys();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async uniqueKeys(): Promise<unknown[]> {
|
|
184
|
+
// Optimization: use native SQL DISTINCT when no JS filtering needed
|
|
185
|
+
if (!this.hasJsPredicate()) {
|
|
186
|
+
const { whereClause, parameters } = buildWhereClause(this.state.sqlConditions);
|
|
187
|
+
const ordering = this.resolveOrdering();
|
|
188
|
+
return this.table.queryKeysWithConditions({
|
|
189
|
+
whereClause,
|
|
190
|
+
parameters,
|
|
191
|
+
orderBy: ordering,
|
|
192
|
+
limit: this.state.limit,
|
|
193
|
+
offset: this.state.offset,
|
|
194
|
+
distinct: true,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
// Fallback for JS filtering
|
|
198
|
+
const keys = await this.keys();
|
|
199
|
+
return [...new Set(keys)];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async count(): Promise<number> {
|
|
203
|
+
// Optimization: use SQL COUNT when no JS filtering needed
|
|
204
|
+
if (!this.hasJsPredicate()) {
|
|
205
|
+
return this.table.countWithConditions({
|
|
206
|
+
whereClause: buildWhereClause(this.state.sqlConditions).whereClause,
|
|
207
|
+
parameters: buildWhereClause(this.state.sqlConditions).parameters,
|
|
208
|
+
distinct: this.state.distinct,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
// Fallback for JS filtering
|
|
212
|
+
const entries = await this.executeQuery({ clone: false });
|
|
213
|
+
return entries.length;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async sortBy(key: string): Promise<T[]> {
|
|
217
|
+
// Use native SQL ORDER BY instead of JS sorting
|
|
218
|
+
const entries = await this.executeQuery({
|
|
219
|
+
orderByOverride: { index: key, direction: 'asc' },
|
|
220
|
+
});
|
|
221
|
+
return entries.map((entry) => entry.value);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
distinct(): StorageCollection<T> {
|
|
225
|
+
return this.replicate({ distinct: true });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
jsFilter(predicate: (item: T) => boolean): StorageCollection<T> {
|
|
229
|
+
return this.withJsPredicate((record: T) => predicate(cloneValue(record)));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
or(index: string): StorageWhereClause<T> {
|
|
233
|
+
return this.table.createWhereClause(index, this);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
clone(_props?: Record<string, unknown>): StorageCollection<T> {
|
|
237
|
+
return this.replicate();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
reverse(): StorageCollection<T> {
|
|
241
|
+
return this.replicate({ reverse: !this.state.reverse });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
offset(offset: number): StorageCollection<T> {
|
|
245
|
+
return this.replicate({ offset });
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
limit(count: number): StorageCollection<T> {
|
|
249
|
+
return this.replicate({ limit: count });
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
toCollection(): StorageCollection<T> {
|
|
253
|
+
return this.replicate();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async delete(): Promise<number> {
|
|
257
|
+
// Optimization: use native SQL DELETE when no JS filtering needed
|
|
258
|
+
if (!this.hasJsPredicate()) {
|
|
259
|
+
const { whereClause, parameters } = buildWhereClause(this.state.sqlConditions);
|
|
260
|
+
return this.table.deleteWithConditions({ whereClause, parameters });
|
|
261
|
+
}
|
|
262
|
+
// Fallback for JS filtering - must iterate and delete one by one
|
|
263
|
+
const entries = await this.executeQuery({ clone: false });
|
|
264
|
+
for (const entry of entries) {
|
|
265
|
+
await this.table.deleteByPrimaryKey(entry.key);
|
|
266
|
+
}
|
|
267
|
+
return entries.length;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async modify(changes: Partial<T> | ((item: T) => void | Promise<void>)): Promise<number> {
|
|
271
|
+
// Optimization: use native SQL UPDATE when changes is an object and no JS filtering
|
|
272
|
+
if (typeof changes !== 'function' && !this.hasJsPredicate()) {
|
|
273
|
+
const { whereClause, parameters } = buildWhereClause(this.state.sqlConditions);
|
|
274
|
+
return this.table.updateWithConditions({ whereClause, parameters, changes });
|
|
275
|
+
}
|
|
276
|
+
// Fallback for function-based changes or JS filtering
|
|
277
|
+
const entries = await this.executeQuery();
|
|
278
|
+
for (const entry of entries) {
|
|
279
|
+
const draft = cloneValue(entry.value);
|
|
280
|
+
if (typeof changes === 'function') {
|
|
281
|
+
await changes(draft);
|
|
282
|
+
} else {
|
|
283
|
+
Object.assign(draft as object, changes);
|
|
284
|
+
}
|
|
285
|
+
await this.table.replaceRecord(draft);
|
|
286
|
+
}
|
|
287
|
+
return entries.length;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async toArray(): Promise<T[]> {
|
|
291
|
+
const entries = await this.executeQuery();
|
|
292
|
+
return entries.map((entry) => entry.value);
|
|
293
|
+
}
|
|
294
|
+
}
|