@tanstack/powersync-db-collection 0.1.37 → 0.1.38

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.
@@ -0,0 +1,219 @@
1
+ function compileSQLite(options, compileOptions) {
2
+ const { where, orderBy, limit } = options;
3
+ const params = [];
4
+ const result = { params };
5
+ if (where) {
6
+ result.where = compileExpression(where, params, compileOptions);
7
+ }
8
+ if (orderBy) {
9
+ result.orderBy = compileOrderBy(orderBy, params, compileOptions);
10
+ }
11
+ if (limit !== void 0) {
12
+ result.limit = limit;
13
+ }
14
+ return result;
15
+ }
16
+ function quoteIdentifier(name) {
17
+ const escaped = name.replace(/"/g, `""`);
18
+ return `"${escaped}"`;
19
+ }
20
+ function compileExpression(exp, params, compileOptions) {
21
+ switch (exp.type) {
22
+ case `val`:
23
+ params.push(exp.value);
24
+ return `?`;
25
+ case `ref`: {
26
+ if (exp.path.length !== 1) {
27
+ throw new Error(
28
+ `SQLite compiler doesn't support nested properties: ${exp.path.join(`.`)}`
29
+ );
30
+ }
31
+ const columnName = exp.path[0];
32
+ if (compileOptions?.jsonColumn && columnName !== `id`) {
33
+ return `json_extract(${compileOptions.jsonColumn}, '$.${columnName}')`;
34
+ }
35
+ return quoteIdentifier(columnName);
36
+ }
37
+ case `func`:
38
+ return compileFunction(exp, params, compileOptions);
39
+ default:
40
+ throw new Error(`Unknown expression type: ${exp.type}`);
41
+ }
42
+ }
43
+ function compileOrderBy(orderBy, params, compileOptions) {
44
+ const clauses = orderBy.map(
45
+ (clause) => compileOrderByClause(clause, params, compileOptions)
46
+ );
47
+ return clauses.join(`, `);
48
+ }
49
+ function compileOrderByClause(clause, params, compileOptions) {
50
+ const { expression, compareOptions } = clause;
51
+ let sql = compileExpression(expression, params, compileOptions);
52
+ if (compareOptions.direction === `desc`) {
53
+ sql = `${sql} DESC`;
54
+ }
55
+ if (compareOptions.nulls === `first`) {
56
+ sql = `${sql} NULLS FIRST`;
57
+ } else {
58
+ sql = `${sql} NULLS LAST`;
59
+ }
60
+ return sql;
61
+ }
62
+ function isNullValue(exp) {
63
+ return exp.type === `val` && (exp.value === null || exp.value === void 0);
64
+ }
65
+ function compileFunction(exp, params, compileOptions) {
66
+ const { name, args } = exp;
67
+ if (isComparisonOp(name)) {
68
+ const hasNullArg = args.some((arg) => isNullValue(arg));
69
+ if (hasNullArg) {
70
+ throw new Error(
71
+ `Cannot use null/undefined with '${name}' operator. Use isNull() to check for null values.`
72
+ );
73
+ }
74
+ }
75
+ const compiledArgs = args.map(
76
+ (arg) => compileExpression(arg, params, compileOptions)
77
+ );
78
+ switch (name) {
79
+ // Binary comparison operators
80
+ case `eq`:
81
+ case `gt`:
82
+ case `gte`:
83
+ case `lt`:
84
+ case `lte`: {
85
+ if (compiledArgs.length !== 2) {
86
+ throw new Error(`${name} expects 2 arguments`);
87
+ }
88
+ const opSymbol = getComparisonOp(name);
89
+ return `${compiledArgs[0]} ${opSymbol} ${compiledArgs[1]}`;
90
+ }
91
+ // Logical operators
92
+ case `and`:
93
+ case `or`: {
94
+ if (compiledArgs.length < 2) {
95
+ throw new Error(`${name} expects at least 2 arguments`);
96
+ }
97
+ const opKeyword = name === `and` ? `AND` : `OR`;
98
+ return compiledArgs.map((arg) => `(${arg})`).join(` ${opKeyword} `);
99
+ }
100
+ case `not`: {
101
+ if (compiledArgs.length !== 1) {
102
+ throw new Error(`not expects 1 argument`);
103
+ }
104
+ const arg = args[0];
105
+ if (arg && arg.type === `func`) {
106
+ if (arg.name === `isNull` || arg.name === `isUndefined`) {
107
+ const innerArg = compileExpression(
108
+ arg.args[0],
109
+ params,
110
+ compileOptions
111
+ );
112
+ return `${innerArg} IS NOT NULL`;
113
+ }
114
+ }
115
+ return `NOT (${compiledArgs[0]})`;
116
+ }
117
+ // Null checking
118
+ case `isNull`:
119
+ case `isUndefined`: {
120
+ if (compiledArgs.length !== 1) {
121
+ throw new Error(`${name} expects 1 argument`);
122
+ }
123
+ return `${compiledArgs[0]} IS NULL`;
124
+ }
125
+ // IN operator
126
+ case `in`: {
127
+ if (compiledArgs.length !== 2) {
128
+ throw new Error(`in expects 2 arguments (column and array)`);
129
+ }
130
+ const lastParamIndex = params.length - 1;
131
+ const arrayValue = params[lastParamIndex];
132
+ if (!Array.isArray(arrayValue)) {
133
+ throw new Error(`in operator requires an array value`);
134
+ }
135
+ params.pop();
136
+ const placeholders = arrayValue.map(() => {
137
+ params.push(arrayValue[params.length - lastParamIndex]);
138
+ return `?`;
139
+ });
140
+ params.length = lastParamIndex;
141
+ for (const val of arrayValue) {
142
+ params.push(val);
143
+ }
144
+ return `${compiledArgs[0]} IN (${placeholders.join(`, `)})`;
145
+ }
146
+ // String operators
147
+ case `like`: {
148
+ if (compiledArgs.length !== 2) {
149
+ throw new Error(`like expects 2 arguments`);
150
+ }
151
+ return `${compiledArgs[0]} LIKE ${compiledArgs[1]}`;
152
+ }
153
+ case `ilike`: {
154
+ if (compiledArgs.length !== 2) {
155
+ throw new Error(`ilike expects 2 arguments`);
156
+ }
157
+ return `${compiledArgs[0]} LIKE ${compiledArgs[1]} COLLATE NOCASE`;
158
+ }
159
+ // String case functions
160
+ case `upper`: {
161
+ if (compiledArgs.length !== 1) {
162
+ throw new Error(`upper expects 1 argument`);
163
+ }
164
+ return `UPPER(${compiledArgs[0]})`;
165
+ }
166
+ case `lower`: {
167
+ if (compiledArgs.length !== 1) {
168
+ throw new Error(`lower expects 1 argument`);
169
+ }
170
+ return `LOWER(${compiledArgs[0]})`;
171
+ }
172
+ case `length`: {
173
+ if (compiledArgs.length !== 1) {
174
+ throw new Error(`length expects 1 argument`);
175
+ }
176
+ return `LENGTH(${compiledArgs[0]})`;
177
+ }
178
+ case `concat`: {
179
+ if (compiledArgs.length < 1) {
180
+ throw new Error(`concat expects at least 1 argument`);
181
+ }
182
+ return `CONCAT(${compiledArgs.join(`, `)})`;
183
+ }
184
+ case `add`: {
185
+ if (compiledArgs.length !== 2) {
186
+ throw new Error(`add expects 2 arguments`);
187
+ }
188
+ return `${compiledArgs[0]} + ${compiledArgs[1]}`;
189
+ }
190
+ // Null fallback
191
+ case `coalesce`: {
192
+ if (compiledArgs.length < 1) {
193
+ throw new Error(`coalesce expects at least 1 argument`);
194
+ }
195
+ return `COALESCE(${compiledArgs.join(`, `)})`;
196
+ }
197
+ default:
198
+ throw new Error(
199
+ `Operator '${name}' is not supported in PowerSync on-demand sync. Supported operators: eq, gt, gte, lt, lte, and, or, not, isNull, in, like, ilike, upper, lower, length, concat, add, coalesce`
200
+ );
201
+ }
202
+ }
203
+ function isComparisonOp(name) {
204
+ return [`eq`, `gt`, `gte`, `lt`, `lte`, `like`, `ilike`].includes(name);
205
+ }
206
+ function getComparisonOp(name) {
207
+ const ops = {
208
+ eq: `=`,
209
+ gt: `>`,
210
+ gte: `>=`,
211
+ lt: `<`,
212
+ lte: `<=`
213
+ };
214
+ return ops[name];
215
+ }
216
+ export {
217
+ compileSQLite
218
+ };
219
+ //# sourceMappingURL=sqlite-compiler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-compiler.js","sources":["../../src/sqlite-compiler.ts"],"sourcesContent":["import type { IR, LoadSubsetOptions } from '@tanstack/db'\n\n/**\n * Result of compiling LoadSubsetOptions to SQLite\n */\nexport interface SQLiteCompiledQuery {\n /** The WHERE clause (without \"WHERE\" keyword), e.g., \"price > ?\" */\n where?: string\n /** The ORDER BY clause (without \"ORDER BY\" keyword), e.g., \"price DESC\" */\n orderBy?: string\n /** The LIMIT value */\n limit?: number\n /** Parameter values in order, to be passed to SQLite query */\n params: Array<unknown>\n}\n\n/**\n * Options for controlling how SQL is compiled.\n */\nexport interface CompileSQLiteOptions {\n /**\n * When set, column references emit `json_extract(<jsonColumn>, '$.<columnName>')`\n * instead of `\"<columnName>\"`. The `id` column is excluded since it's stored\n * as a direct column in the tracked table.\n */\n jsonColumn?: string\n}\n\n/**\n * Compiles TanStack DB LoadSubsetOptions to SQLite query components.\n *\n * @example\n * ```typescript\n * const compiled = compileSQLite({\n * where: { type: 'func', name: 'gt', args: [\n * { type: 'ref', path: ['price'] },\n * { type: 'val', value: 100 }\n * ]},\n * orderBy: [{ expression: { type: 'ref', path: ['price'] }, compareOptions: { direction: 'desc', nulls: 'last' } }],\n * limit: 50\n * })\n * // Result: { where: '\"price\" > ?', orderBy: '\"price\" DESC', limit: 50, params: [100] }\n * ```\n */\nexport function compileSQLite(\n options: LoadSubsetOptions,\n compileOptions?: CompileSQLiteOptions,\n): SQLiteCompiledQuery {\n const { where, orderBy, limit } = options\n\n const params: Array<unknown> = []\n const result: SQLiteCompiledQuery = { params }\n\n if (where) {\n result.where = compileExpression(where, params, compileOptions)\n }\n\n if (orderBy) {\n result.orderBy = compileOrderBy(orderBy, params, compileOptions)\n }\n\n if (limit !== undefined) {\n result.limit = limit\n }\n\n return result\n}\n\n/**\n * Quote SQLite identifiers to handle column names correctly.\n * SQLite uses double quotes for identifiers.\n */\nfunction quoteIdentifier(name: string): string {\n // Escape any double quotes in the name by doubling them\n const escaped = name.replace(/\"/g, `\"\"`)\n return `\"${escaped}\"`\n}\n\n/**\n * Compiles a BasicExpression to a SQL string, mutating the params array.\n */\nfunction compileExpression(\n exp: IR.BasicExpression<unknown>,\n params: Array<unknown>,\n compileOptions?: CompileSQLiteOptions,\n): string {\n switch (exp.type) {\n case `val`:\n params.push(exp.value)\n return `?`\n case `ref`: {\n if (exp.path.length !== 1) {\n throw new Error(\n `SQLite compiler doesn't support nested properties: ${exp.path.join(`.`)}`,\n )\n }\n const columnName = exp.path[0]!\n if (compileOptions?.jsonColumn && columnName !== `id`) {\n return `json_extract(${compileOptions.jsonColumn}, '$.${columnName}')`\n }\n return quoteIdentifier(columnName)\n }\n case `func`:\n return compileFunction(exp, params, compileOptions)\n default:\n throw new Error(`Unknown expression type: ${(exp as any).type}`)\n }\n}\n\n/**\n * Compiles an OrderBy array to a SQL ORDER BY clause.\n */\nfunction compileOrderBy(\n orderBy: IR.OrderBy,\n params: Array<unknown>,\n compileOptions?: CompileSQLiteOptions,\n): string {\n const clauses = orderBy.map((clause: IR.OrderByClause) =>\n compileOrderByClause(clause, params, compileOptions),\n )\n return clauses.join(`, `)\n}\n\n/**\n * Compiles a single OrderByClause to SQL.\n */\nfunction compileOrderByClause(\n clause: IR.OrderByClause,\n params: Array<unknown>,\n compileOptions?: CompileSQLiteOptions,\n): string {\n const { expression, compareOptions } = clause\n let sql = compileExpression(expression, params, compileOptions)\n\n if (compareOptions.direction === `desc`) {\n sql = `${sql} DESC`\n }\n\n // SQLite supports NULLS FIRST/LAST (since 3.30.0)\n if (compareOptions.nulls === `first`) {\n sql = `${sql} NULLS FIRST`\n } else {\n // Default to NULLS LAST (nulls === 'last')\n sql = `${sql} NULLS LAST`\n }\n\n return sql\n}\n\n/**\n * Check if a BasicExpression represents a null/undefined value\n */\nfunction isNullValue(exp: IR.BasicExpression<unknown>): boolean {\n return exp.type === `val` && (exp.value === null || exp.value === undefined)\n}\n\n/**\n * Compiles a function expression (operator) to SQL.\n */\nfunction compileFunction(\n exp: IR.Func<unknown>,\n params: Array<unknown>,\n compileOptions?: CompileSQLiteOptions,\n): string {\n const { name, args } = exp\n\n // Check for null values in comparison operators\n if (isComparisonOp(name)) {\n const hasNullArg = args.some((arg: IR.BasicExpression) => isNullValue(arg))\n if (hasNullArg) {\n throw new Error(\n `Cannot use null/undefined with '${name}' operator. ` +\n `Use isNull() to check for null values.`,\n )\n }\n }\n\n // Compile arguments\n const compiledArgs = args.map((arg: IR.BasicExpression) =>\n compileExpression(arg, params, compileOptions),\n )\n\n // Handle different operator types\n switch (name) {\n // Binary comparison operators\n case `eq`:\n case `gt`:\n case `gte`:\n case `lt`:\n case `lte`: {\n if (compiledArgs.length !== 2) {\n throw new Error(`${name} expects 2 arguments`)\n }\n const opSymbol = getComparisonOp(name)\n return `${compiledArgs[0]} ${opSymbol} ${compiledArgs[1]}`\n }\n\n // Logical operators\n case `and`:\n case `or`: {\n if (compiledArgs.length < 2) {\n throw new Error(`${name} expects at least 2 arguments`)\n }\n const opKeyword = name === `and` ? `AND` : `OR`\n return compiledArgs\n .map((arg: string) => `(${arg})`)\n .join(` ${opKeyword} `)\n }\n\n case `not`: {\n if (compiledArgs.length !== 1) {\n throw new Error(`not expects 1 argument`)\n }\n // Check if argument is isNull/isUndefined for IS NOT NULL\n const arg = args[0]\n if (arg && arg.type === `func`) {\n if (arg.name === `isNull` || arg.name === `isUndefined`) {\n const innerArg = compileExpression(\n arg.args[0]!,\n params,\n compileOptions,\n )\n return `${innerArg} IS NOT NULL`\n }\n }\n return `NOT (${compiledArgs[0]})`\n }\n\n // Null checking\n case `isNull`:\n case `isUndefined`: {\n if (compiledArgs.length !== 1) {\n throw new Error(`${name} expects 1 argument`)\n }\n return `${compiledArgs[0]} IS NULL`\n }\n\n // IN operator\n case `in`: {\n if (compiledArgs.length !== 2) {\n throw new Error(`in expects 2 arguments (column and array)`)\n }\n // The second argument should be an array value\n // We need to handle this specially - expand the array into multiple placeholders\n const lastParamIndex = params.length - 1\n const arrayValue = params[lastParamIndex]\n\n if (!Array.isArray(arrayValue)) {\n throw new Error(`in operator requires an array value`)\n }\n\n // Remove the array param and add individual values\n params.pop()\n const placeholders = arrayValue.map(() => {\n params.push(arrayValue[params.length - lastParamIndex])\n return `?`\n })\n\n // Re-add individual values properly\n params.length = lastParamIndex // Reset to before array\n for (const val of arrayValue) {\n params.push(val)\n }\n\n return `${compiledArgs[0]} IN (${placeholders.join(`, `)})`\n }\n\n // String operators\n case `like`: {\n if (compiledArgs.length !== 2) {\n throw new Error(`like expects 2 arguments`)\n }\n return `${compiledArgs[0]} LIKE ${compiledArgs[1]}`\n }\n\n case `ilike`: {\n if (compiledArgs.length !== 2) {\n throw new Error(`ilike expects 2 arguments`)\n }\n return `${compiledArgs[0]} LIKE ${compiledArgs[1]} COLLATE NOCASE`\n }\n\n // String case functions\n case `upper`: {\n if (compiledArgs.length !== 1) {\n throw new Error(`upper expects 1 argument`)\n }\n return `UPPER(${compiledArgs[0]})`\n }\n\n case `lower`: {\n if (compiledArgs.length !== 1) {\n throw new Error(`lower expects 1 argument`)\n }\n return `LOWER(${compiledArgs[0]})`\n }\n\n case `length`: {\n if (compiledArgs.length !== 1) {\n throw new Error(`length expects 1 argument`)\n }\n return `LENGTH(${compiledArgs[0]})`\n }\n\n case `concat`: {\n if (compiledArgs.length < 1) {\n throw new Error(`concat expects at least 1 argument`)\n }\n return `CONCAT(${compiledArgs.join(`, `)})`\n }\n\n case `add`: {\n if (compiledArgs.length !== 2) {\n throw new Error(`add expects 2 arguments`)\n }\n return `${compiledArgs[0]} + ${compiledArgs[1]}`\n }\n\n // Null fallback\n case `coalesce`: {\n if (compiledArgs.length < 1) {\n throw new Error(`coalesce expects at least 1 argument`)\n }\n return `COALESCE(${compiledArgs.join(`, `)})`\n }\n\n default:\n throw new Error(\n `Operator '${name}' is not supported in PowerSync on-demand sync. ` +\n `Supported operators: eq, gt, gte, lt, lte, and, or, not, isNull, in, like, ilike, upper, lower, length, concat, add, coalesce`,\n )\n }\n}\n\n/**\n * Check if operator is a comparison operator\n */\nfunction isComparisonOp(name: string): boolean {\n return [`eq`, `gt`, `gte`, `lt`, `lte`, `like`, `ilike`].includes(name)\n}\n\n/**\n * Get the SQL symbol for a comparison operator\n */\nfunction getComparisonOp(name: string): string {\n const ops: Record<string, string> = {\n eq: `=`,\n gt: `>`,\n gte: `>=`,\n lt: `<`,\n lte: `<=`,\n }\n return ops[name]!\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/powersync-db-collection",
3
- "version": "0.1.37",
3
+ "version": "0.1.38",
4
4
  "description": "PowerSync collection for TanStack DB",
5
5
  "author": "POWERSYNC",
6
6
  "license": "MIT",
@@ -47,19 +47,20 @@
47
47
  ],
48
48
  "dependencies": {
49
49
  "@standard-schema/spec": "^1.1.0",
50
- "@tanstack/store": "^0.8.0",
50
+ "@tanstack/store": "^0.9.2",
51
51
  "debug": "^4.4.3",
52
52
  "p-defer": "^4.0.1",
53
- "@tanstack/db": "0.5.33"
53
+ "@tanstack/db": "0.6.0"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "@powersync/common": "^1.41.0"
57
57
  },
58
58
  "devDependencies": {
59
- "@powersync/common": "^1.46.0",
60
- "@powersync/node": "^0.15.2",
59
+ "@powersync/common": "1.49.0",
60
+ "@powersync/node": "0.18.1",
61
61
  "@types/debug": "^4.1.12",
62
- "@vitest/coverage-istanbul": "^3.2.4"
62
+ "@vitest/coverage-istanbul": "^3.2.4",
63
+ "better-sqlite3": "^12.6.2"
63
64
  },
64
65
  "scripts": {
65
66
  "build": "vite build",
@@ -2,8 +2,10 @@ import type { AbstractPowerSyncDatabase, Table } from '@powersync/common'
2
2
  import type { StandardSchemaV1 } from '@standard-schema/spec'
3
3
  import type {
4
4
  BaseCollectionConfig,
5
+ CleanupFn,
5
6
  CollectionConfig,
6
7
  InferSchemaOutput,
8
+ LoadSubsetOptions,
7
9
  } from '@tanstack/db'
8
10
  import type {
9
11
  AnyTableColumnType,
@@ -162,12 +164,48 @@ export type ConfigWithArbitraryCollectionTypes<
162
164
  StandardSchemaV1.InferOutput<TSchema>
163
165
  >
164
166
  }
167
+ /**
168
+ * Eager sync mode hooks.
169
+ * Called once when the collection sync starts and stops.
170
+ */
171
+ export type EagerSyncHooks = {
172
+ syncMode?: 'eager'
173
+ /**
174
+ * Called when the collection sync starts.
175
+ * Use this to set up external data sources (e.g. subscribing to a sync stream).
176
+ *
177
+ * @returns A cleanup function that is called when the collection sync is cleaned up.
178
+ */
179
+ onLoad?: () => CleanupFn | void | Promise<CleanupFn | void>
180
+ onLoadSubset?: never
181
+ }
182
+
183
+ /**
184
+ * On-demand sync mode hooks.
185
+ * Called each time a subset is loaded or unloaded in response to live query changes.
186
+ */
187
+ export type OnDemandSyncHooks = {
188
+ syncMode: 'on-demand'
189
+ onLoad?: never
190
+ /**
191
+ * Called when a subset of data is requested by a live query.
192
+ * Use this to set up external data sources for the requested subset
193
+ * (e.g. subscribing to a sync stream with parameters derived from the query predicate).
194
+ *
195
+ * @returns A cleanup function that is called when the subset is unloaded.
196
+ */
197
+
198
+ onLoadSubset?: (
199
+ options: LoadSubsetOptions,
200
+ ) => CleanupFn | void | Promise<CleanupFn | void>
201
+ }
202
+
165
203
  export type BasePowerSyncCollectionConfig<
166
204
  TTable extends Table = Table,
167
205
  TSchema extends StandardSchemaV1 = never,
168
206
  > = Omit<
169
207
  BaseCollectionConfig<ExtractedTable<TTable>, string, TSchema>,
170
- `onInsert` | `onUpdate` | `onDelete` | `getKey`
208
+ `onInsert` | `onUpdate` | `onDelete` | `getKey` | `syncMode`
171
209
  > & {
172
210
  /** The PowerSync schema Table definition */
173
211
  table: TTable
@@ -186,7 +224,7 @@ export type BasePowerSyncCollectionConfig<
186
224
  * streaming of initial results, at the cost of more query calls.
187
225
  */
188
226
  syncBatchSize?: number
189
- }
227
+ } & (EagerSyncHooks | OnDemandSyncHooks)
190
228
 
191
229
  /**
192
230
  * Configuration interface for PowerSync collection options.
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './definitions'
2
2
  export * from './powersync'
3
3
  export * from './PowerSyncTransactor'
4
+ export * from './sqlite-compiler'