@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.
Files changed (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +212 -0
  3. package/dist/capacitor.cjs +228 -0
  4. package/dist/capacitor.cjs.map +1 -0
  5. package/dist/capacitor.d.cts +62 -0
  6. package/dist/capacitor.d.ts +62 -0
  7. package/dist/capacitor.js +9 -0
  8. package/dist/capacitor.js.map +1 -0
  9. package/dist/chunk-LGHOZECP.js +3884 -0
  10. package/dist/chunk-LGHOZECP.js.map +1 -0
  11. package/dist/chunk-SQB6E7V2.js +191 -0
  12. package/dist/chunk-SQB6E7V2.js.map +1 -0
  13. package/dist/dexie-Bv-fV10P.d.cts +444 -0
  14. package/dist/dexie-DJFApKsM.d.ts +444 -0
  15. package/dist/dexie.cjs +381 -0
  16. package/dist/dexie.cjs.map +1 -0
  17. package/dist/dexie.d.cts +3 -0
  18. package/dist/dexie.d.ts +3 -0
  19. package/dist/dexie.js +343 -0
  20. package/dist/dexie.js.map +1 -0
  21. package/dist/expoSqlite.cjs +98 -0
  22. package/dist/expoSqlite.cjs.map +1 -0
  23. package/dist/expoSqlite.d.cts +17 -0
  24. package/dist/expoSqlite.d.ts +17 -0
  25. package/dist/expoSqlite.js +61 -0
  26. package/dist/expoSqlite.js.map +1 -0
  27. package/dist/index.cjs +3916 -0
  28. package/dist/index.cjs.map +1 -0
  29. package/dist/index.d.cts +8 -0
  30. package/dist/index.d.ts +8 -0
  31. package/dist/index.js +20 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/index.shared-CPIge2ZM.d.ts +234 -0
  34. package/dist/index.shared-YSn6c01d.d.cts +234 -0
  35. package/dist/node.cjs +126 -0
  36. package/dist/node.cjs.map +1 -0
  37. package/dist/node.d.cts +80 -0
  38. package/dist/node.d.ts +80 -0
  39. package/dist/node.js +89 -0
  40. package/dist/node.js.map +1 -0
  41. package/dist/react/index.cjs +1754 -0
  42. package/dist/react/index.cjs.map +1 -0
  43. package/dist/react/index.d.cts +40 -0
  44. package/dist/react/index.d.ts +40 -0
  45. package/dist/react/index.js +78 -0
  46. package/dist/react/index.js.map +1 -0
  47. package/dist/types-CSbIAfu2.d.cts +46 -0
  48. package/dist/types-CSbIAfu2.d.ts +46 -0
  49. package/dist/wa-sqlite.cjs +318 -0
  50. package/dist/wa-sqlite.cjs.map +1 -0
  51. package/dist/wa-sqlite.d.cts +175 -0
  52. package/dist/wa-sqlite.d.ts +175 -0
  53. package/dist/wa-sqlite.js +281 -0
  54. package/dist/wa-sqlite.js.map +1 -0
  55. package/package.json +171 -0
  56. package/src/addVisibilityChangeListener.native.ts +33 -0
  57. package/src/addVisibilityChangeListener.ts +24 -0
  58. package/src/capacitor.ts +4 -0
  59. package/src/core/StateManager.ts +272 -0
  60. package/src/core/firstLoad.ts +332 -0
  61. package/src/core/pullOperations.ts +212 -0
  62. package/src/core/pushOperations.ts +290 -0
  63. package/src/core/tableEnhancers.ts +457 -0
  64. package/src/core/types.ts +3 -0
  65. package/src/createLocalId.native.ts +8 -0
  66. package/src/createLocalId.ts +6 -0
  67. package/src/dexie.ts +2 -0
  68. package/src/expoSqlite.ts +2 -0
  69. package/src/helpers.ts +87 -0
  70. package/src/index.native.ts +28 -0
  71. package/src/index.shared.ts +613 -0
  72. package/src/index.ts +28 -0
  73. package/src/logger.ts +26 -0
  74. package/src/node.ts +4 -0
  75. package/src/react/index.ts +2 -0
  76. package/src/react/useDync.ts +156 -0
  77. package/src/storage/dexie/DexieAdapter.ts +72 -0
  78. package/src/storage/dexie/DexieQueryContext.ts +14 -0
  79. package/src/storage/dexie/DexieStorageCollection.ts +124 -0
  80. package/src/storage/dexie/DexieStorageTable.ts +123 -0
  81. package/src/storage/dexie/DexieStorageWhereClause.ts +103 -0
  82. package/src/storage/dexie/helpers.ts +1 -0
  83. package/src/storage/dexie/index.ts +7 -0
  84. package/src/storage/memory/MemoryAdapter.ts +55 -0
  85. package/src/storage/memory/MemoryCollection.ts +215 -0
  86. package/src/storage/memory/MemoryQueryContext.ts +14 -0
  87. package/src/storage/memory/MemoryTable.ts +336 -0
  88. package/src/storage/memory/MemoryWhereClause.ts +134 -0
  89. package/src/storage/memory/index.ts +7 -0
  90. package/src/storage/memory/types.ts +24 -0
  91. package/src/storage/sqlite/SQLiteAdapter.ts +564 -0
  92. package/src/storage/sqlite/SQLiteCollection.ts +294 -0
  93. package/src/storage/sqlite/SQLiteTable.ts +604 -0
  94. package/src/storage/sqlite/SQLiteWhereClause.ts +341 -0
  95. package/src/storage/sqlite/SqliteQueryContext.ts +30 -0
  96. package/src/storage/sqlite/drivers/BetterSqlite3Driver.ts +156 -0
  97. package/src/storage/sqlite/drivers/CapacitorFastSqlDriver.ts +114 -0
  98. package/src/storage/sqlite/drivers/CapacitorSQLiteDriver.ts +137 -0
  99. package/src/storage/sqlite/drivers/ExpoSQLiteDriver.native.ts +67 -0
  100. package/src/storage/sqlite/drivers/WaSqliteDriver.ts +537 -0
  101. package/src/storage/sqlite/drivers/wa-sqlite-vfs.d.ts +46 -0
  102. package/src/storage/sqlite/helpers.ts +144 -0
  103. package/src/storage/sqlite/index.ts +11 -0
  104. package/src/storage/sqlite/schema.ts +44 -0
  105. package/src/storage/sqlite/types.ts +164 -0
  106. package/src/storage/types.ts +112 -0
  107. package/src/types.ts +186 -0
  108. 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
+ }