@anfenn/dync 1.0.18 → 1.0.20

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/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  SqliteQueryContext,
7
7
  SyncAction,
8
8
  createLocalId
9
- } from "./chunk-WZ7V4U6Z.js";
9
+ } from "./chunk-I2KQD4DD.js";
10
10
  import "./chunk-SQB6E7V2.js";
11
11
  export {
12
12
  Dync,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Dync
3
- } from "../chunk-WZ7V4U6Z.js";
3
+ } from "../chunk-I2KQD4DD.js";
4
4
  import "../chunk-SQB6E7V2.js";
5
5
 
6
6
  // src/react/useDync.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anfenn/dync",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "private": false,
5
5
  "description": "Write once, run IndexedDB & SQLite with sync anywhere - React, React Native, Expo, Capacitor, Electron & Node.js",
6
6
  "keywords": [
@@ -14,20 +14,21 @@ export class SQLiteCollection<T = any> implements StorageCollection<T> {
14
14
  this.state = {
15
15
  ...base,
16
16
  ...state,
17
- sqlConditions: state?.sqlConditions ?? base.sqlConditions,
17
+ orGroups: state?.orGroups ?? base.orGroups,
18
18
  jsPredicate: state?.jsPredicate,
19
19
  };
20
20
  }
21
21
 
22
22
  getState(): SQLiteCollectionState<T> {
23
- return { ...this.state, sqlConditions: [...this.state.sqlConditions] };
23
+ return { ...this.state, orGroups: this.state.orGroups.map((g) => [...g]) };
24
24
  }
25
25
 
26
- // Add a SQL-expressible condition to this collection
26
+ // Add a SQL-expressible condition to the current OR group
27
27
  addSqlCondition(condition: SQLiteCondition): SQLiteCollection<T> {
28
+ const newGroups = this.state.orGroups.map((g, i) => (i === this.state.orGroups.length - 1 ? [...g, condition] : g));
28
29
  return new SQLiteCollection(this.table, {
29
30
  ...this.state,
30
- sqlConditions: [...this.state.sqlConditions, condition],
31
+ orGroups: newGroups,
31
32
  });
32
33
  }
33
34
 
@@ -35,7 +36,7 @@ export class SQLiteCollection<T = any> implements StorageCollection<T> {
35
36
  return new SQLiteCollection(this.table, {
36
37
  ...this.state,
37
38
  ...overrides,
38
- sqlConditions: overrides?.sqlConditions ?? this.state.sqlConditions,
39
+ orGroups: overrides?.orGroups ?? this.state.orGroups,
39
40
  jsPredicate: overrides?.jsPredicate !== undefined ? overrides.jsPredicate : this.state.jsPredicate,
40
41
  });
41
42
  }
@@ -73,7 +74,7 @@ export class SQLiteCollection<T = any> implements StorageCollection<T> {
73
74
  orderByOverride?: { index: string | string[]; direction: 'asc' | 'desc' };
74
75
  } = {},
75
76
  ): Promise<TableEntry<T>[]> {
76
- const { whereClause, parameters } = buildWhereClause(this.state.sqlConditions);
77
+ const { whereClause, parameters } = buildWhereClause(this.state.orGroups);
77
78
  const ordering = options.orderByOverride ?? this.resolveOrdering();
78
79
  const cloneValues = options.clone !== false;
79
80
  const hasJsFilter = this.hasJsPredicate();
@@ -160,7 +161,7 @@ export class SQLiteCollection<T = any> implements StorageCollection<T> {
160
161
  async keys(): Promise<unknown[]> {
161
162
  // Optimization: use native SQL when no JS filtering needed
162
163
  if (!this.hasJsPredicate()) {
163
- const { whereClause, parameters } = buildWhereClause(this.state.sqlConditions);
164
+ const { whereClause, parameters } = buildWhereClause(this.state.orGroups);
164
165
  const ordering = this.resolveOrdering();
165
166
  return this.table.queryKeysWithConditions({
166
167
  whereClause,
@@ -183,7 +184,7 @@ export class SQLiteCollection<T = any> implements StorageCollection<T> {
183
184
  async uniqueKeys(): Promise<unknown[]> {
184
185
  // Optimization: use native SQL DISTINCT when no JS filtering needed
185
186
  if (!this.hasJsPredicate()) {
186
- const { whereClause, parameters } = buildWhereClause(this.state.sqlConditions);
187
+ const { whereClause, parameters } = buildWhereClause(this.state.orGroups);
187
188
  const ordering = this.resolveOrdering();
188
189
  return this.table.queryKeysWithConditions({
189
190
  whereClause,
@@ -203,8 +204,8 @@ export class SQLiteCollection<T = any> implements StorageCollection<T> {
203
204
  // Optimization: use SQL COUNT when no JS filtering needed
204
205
  if (!this.hasJsPredicate()) {
205
206
  return this.table.countWithConditions({
206
- whereClause: buildWhereClause(this.state.sqlConditions).whereClause,
207
- parameters: buildWhereClause(this.state.sqlConditions).parameters,
207
+ whereClause: buildWhereClause(this.state.orGroups).whereClause,
208
+ parameters: buildWhereClause(this.state.orGroups).parameters,
208
209
  distinct: this.state.distinct,
209
210
  });
210
211
  }
@@ -229,7 +230,12 @@ export class SQLiteCollection<T = any> implements StorageCollection<T> {
229
230
  }
230
231
 
231
232
  or(index: string): StorageWhereClause<T> {
232
- return this.table.createWhereClause(index, this);
233
+ // Start a new OR group - subsequent conditions will be ORed with previous groups
234
+ const newCollection = new SQLiteCollection(this.table, {
235
+ ...this.state,
236
+ orGroups: [...this.state.orGroups, []],
237
+ });
238
+ return this.table.createWhereClause(index, newCollection);
233
239
  }
234
240
 
235
241
  clone(_props?: Record<string, unknown>): StorageCollection<T> {
@@ -255,7 +261,7 @@ export class SQLiteCollection<T = any> implements StorageCollection<T> {
255
261
  async delete(): Promise<number> {
256
262
  // Optimization: use native SQL DELETE when no JS filtering needed
257
263
  if (!this.hasJsPredicate()) {
258
- const { whereClause, parameters } = buildWhereClause(this.state.sqlConditions);
264
+ const { whereClause, parameters } = buildWhereClause(this.state.orGroups);
259
265
  return this.table.deleteWithConditions({ whereClause, parameters });
260
266
  }
261
267
  // Fallback for JS filtering - must iterate and delete one by one
@@ -269,7 +275,7 @@ export class SQLiteCollection<T = any> implements StorageCollection<T> {
269
275
  async modify(changes: Partial<T> | ((item: T) => void | Promise<void>)): Promise<number> {
270
276
  // Optimization: use native SQL UPDATE when changes is an object and no JS filtering
271
277
  if (typeof changes !== 'function' && !this.hasJsPredicate()) {
272
- const { whereClause, parameters } = buildWhereClause(this.state.sqlConditions);
278
+ const { whereClause, parameters } = buildWhereClause(this.state.orGroups);
273
279
  return this.table.updateWithConditions({ whereClause, parameters, changes });
274
280
  }
275
281
  // Fallback for function-based changes or JS filtering
@@ -4,7 +4,7 @@ export const SQLITE_SCHEMA_VERSION_STATE_KEY = 'sqlite_schema_version';
4
4
  export const DEFAULT_STREAM_BATCH_SIZE = 200;
5
5
 
6
6
  export const createDefaultState = <T>(): SQLiteCollectionState<T> => ({
7
- sqlConditions: [],
7
+ orGroups: [[]],
8
8
  jsPredicate: undefined,
9
9
  orderBy: undefined,
10
10
  reverse: false,
@@ -18,22 +18,34 @@ export interface SQLiteBuiltQuery {
18
18
  parameters: unknown[];
19
19
  }
20
20
 
21
- export const buildWhereClause = (conditions: SQLiteCondition[]): SQLiteBuiltQuery => {
22
- if (conditions.length === 0) {
21
+ export const buildWhereClause = (orGroups: SQLiteCondition[][]): SQLiteBuiltQuery => {
22
+ // Filter out empty groups
23
+ const nonEmptyGroups = orGroups.filter((group) => group.length > 0);
24
+
25
+ if (nonEmptyGroups.length === 0) {
23
26
  return { whereClause: '', parameters: [] };
24
27
  }
25
28
 
26
- const clauses: string[] = [];
29
+ const groupClauses: string[] = [];
27
30
  const parameters: unknown[] = [];
28
31
 
29
- for (const condition of conditions) {
30
- const built = buildCondition(condition);
31
- clauses.push(built.clause);
32
- parameters.push(...built.parameters);
32
+ for (const group of nonEmptyGroups) {
33
+ const conditionClauses: string[] = [];
34
+ for (const condition of group) {
35
+ const built = buildCondition(condition);
36
+ conditionClauses.push(built.clause);
37
+ parameters.push(...built.parameters);
38
+ }
39
+ // AND conditions within a group
40
+ const groupClause = conditionClauses.length === 1 ? conditionClauses[0]! : `(${conditionClauses.join(' AND ')})`;
41
+ groupClauses.push(groupClause);
33
42
  }
34
43
 
44
+ // OR between groups (wrap in parens if multiple groups)
45
+ const whereContent = groupClauses.length === 1 ? groupClauses[0]! : `(${groupClauses.join(' OR ')})`;
46
+
35
47
  return {
36
- whereClause: `WHERE ${clauses.join(' AND ')}`,
48
+ whereClause: `WHERE ${whereContent}`,
37
49
  parameters,
38
50
  };
39
51
  };
@@ -79,8 +79,11 @@ export type SQLiteCondition =
79
79
  | SQLiteConditionOr;
80
80
 
81
81
  export interface SQLiteCollectionState<T> {
82
- /** SQL-expressible WHERE conditions (ANDed together) */
83
- sqlConditions: SQLiteCondition[];
82
+ /**
83
+ * OR groups of SQL conditions. Each group's conditions are ANDed together,
84
+ * then groups are ORed: (A AND B) OR (C) OR (D AND E)
85
+ */
86
+ orGroups: SQLiteCondition[][];
84
87
  /** JavaScript predicate for conditions that can't be expressed in SQL (e.g., arbitrary filter functions) */
85
88
  jsPredicate?: (record: T, key: string, index: number) => boolean;
86
89
  orderBy?: { index: string | string[]; direction: 'asc' | 'desc' };