@syncular/client 0.1.3 → 0.2.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 (233) hide show
  1. package/README.md +227 -709
  2. package/dist/apply.d.ts +80 -0
  3. package/dist/apply.js +301 -0
  4. package/dist/blob.d.ts +127 -0
  5. package/dist/blob.js +201 -0
  6. package/dist/bun-database.d.ts +22 -0
  7. package/dist/bun-database.js +61 -0
  8. package/dist/client.d.ts +320 -69
  9. package/dist/client.js +1627 -163
  10. package/dist/content-type.d.ts +2 -0
  11. package/dist/content-type.js +2 -0
  12. package/dist/database.d.ts +35 -80
  13. package/dist/database.js +32 -947
  14. package/dist/encryption.d.ts +47 -0
  15. package/dist/encryption.js +75 -0
  16. package/dist/errors.d.ts +8 -22
  17. package/dist/errors.js +10 -207
  18. package/dist/http.d.ts +32 -0
  19. package/dist/http.js +196 -0
  20. package/dist/index.d.ts +28 -16
  21. package/dist/index.js +25 -12
  22. package/dist/invalidation.d.ts +69 -0
  23. package/dist/invalidation.js +84 -0
  24. package/dist/leader-lock.d.ts +28 -0
  25. package/dist/leader-lock.js +38 -0
  26. package/dist/multi-tab.d.ts +134 -0
  27. package/dist/multi-tab.js +399 -0
  28. package/dist/node-database.d.ts +41 -0
  29. package/dist/node-database.js +133 -0
  30. package/dist/outbox.d.ts +56 -0
  31. package/dist/outbox.js +140 -0
  32. package/dist/schema.d.ts +107 -0
  33. package/dist/schema.js +267 -0
  34. package/dist/state.d.ts +40 -0
  35. package/dist/state.js +77 -0
  36. package/dist/transport.d.ts +44 -0
  37. package/dist/transport.js +7 -0
  38. package/dist/wasm-database.d.ts +30 -0
  39. package/dist/wasm-database.js +170 -0
  40. package/dist/window.d.ts +64 -0
  41. package/dist/window.js +0 -0
  42. package/dist/worker-entry.d.ts +16 -2
  43. package/dist/worker-entry.js +300 -456
  44. package/dist/worker-host.d.ts +174 -0
  45. package/dist/worker-host.js +536 -0
  46. package/dist/worker-protocol.d.ts +173 -61
  47. package/dist/worker-protocol.js +7 -16
  48. package/package.json +45 -112
  49. package/src/apply.ts +435 -0
  50. package/src/blob.ts +333 -0
  51. package/src/bun-database.ts +75 -0
  52. package/src/client.ts +2247 -277
  53. package/src/content-type.ts +2 -0
  54. package/src/database.ts +63 -1389
  55. package/src/encryption.ts +123 -0
  56. package/src/errors.ts +11 -265
  57. package/src/http.ts +254 -0
  58. package/src/index.ts +25 -20
  59. package/src/invalidation.ts +128 -0
  60. package/src/leader-lock.ts +68 -0
  61. package/src/multi-tab.ts +550 -0
  62. package/src/node-database.ts +180 -0
  63. package/src/outbox.ts +206 -0
  64. package/src/schema.ts +397 -0
  65. package/src/state.ts +126 -0
  66. package/src/transport.ts +52 -0
  67. package/src/wasm-database.ts +279 -0
  68. package/src/window.ts +0 -0
  69. package/src/worker-entry.ts +391 -545
  70. package/src/worker-host.ts +800 -0
  71. package/src/worker-protocol.ts +204 -99
  72. package/dist/auth-leases.d.ts +0 -11
  73. package/dist/auth-leases.d.ts.map +0 -1
  74. package/dist/auth-leases.js +0 -140
  75. package/dist/auth-leases.js.map +0 -1
  76. package/dist/blob-limits.d.ts +0 -15
  77. package/dist/blob-limits.d.ts.map +0 -1
  78. package/dist/blob-limits.js +0 -66
  79. package/dist/blob-limits.js.map +0 -1
  80. package/dist/bridge-client.d.ts +0 -69
  81. package/dist/bridge-client.d.ts.map +0 -1
  82. package/dist/bridge-client.js +0 -282
  83. package/dist/bridge-client.js.map +0 -1
  84. package/dist/client-config.d.ts +0 -6
  85. package/dist/client-config.d.ts.map +0 -1
  86. package/dist/client-config.js +0 -18
  87. package/dist/client-config.js.map +0 -1
  88. package/dist/client.d.ts.map +0 -1
  89. package/dist/client.js.map +0 -1
  90. package/dist/command-history.d.ts +0 -35
  91. package/dist/command-history.d.ts.map +0 -1
  92. package/dist/command-history.js +0 -378
  93. package/dist/command-history.js.map +0 -1
  94. package/dist/console-diagnostics.d.ts +0 -33
  95. package/dist/console-diagnostics.d.ts.map +0 -1
  96. package/dist/console-diagnostics.js +0 -496
  97. package/dist/console-diagnostics.js.map +0 -1
  98. package/dist/crdt-yjs/index.d.ts +0 -4
  99. package/dist/crdt-yjs/index.d.ts.map +0 -1
  100. package/dist/crdt-yjs/index.js +0 -4
  101. package/dist/crdt-yjs/index.js.map +0 -1
  102. package/dist/crdt-yjs/webview-host-facade.d.ts +0 -126
  103. package/dist/crdt-yjs/webview-host-facade.d.ts.map +0 -1
  104. package/dist/crdt-yjs/webview-host-facade.js +0 -284
  105. package/dist/crdt-yjs/webview-host-facade.js.map +0 -1
  106. package/dist/crdt-yjs/yjs-document-field-adapter.d.ts +0 -153
  107. package/dist/crdt-yjs/yjs-document-field-adapter.d.ts.map +0 -1
  108. package/dist/crdt-yjs/yjs-document-field-adapter.js +0 -406
  109. package/dist/crdt-yjs/yjs-document-field-adapter.js.map +0 -1
  110. package/dist/crdt-yjs/yjs-prosemirror-bridge.d.ts +0 -73
  111. package/dist/crdt-yjs/yjs-prosemirror-bridge.d.ts.map +0 -1
  112. package/dist/crdt-yjs/yjs-prosemirror-bridge.js +0 -169
  113. package/dist/crdt-yjs/yjs-prosemirror-bridge.js.map +0 -1
  114. package/dist/database.d.ts.map +0 -1
  115. package/dist/database.js.map +0 -1
  116. package/dist/diagnostics.d.ts +0 -10
  117. package/dist/diagnostics.d.ts.map +0 -1
  118. package/dist/diagnostics.js +0 -83
  119. package/dist/diagnostics.js.map +0 -1
  120. package/dist/errors.d.ts.map +0 -1
  121. package/dist/errors.js.map +0 -1
  122. package/dist/generated-bridge.d.ts +0 -365
  123. package/dist/generated-bridge.d.ts.map +0 -1
  124. package/dist/generated-bridge.js +0 -250
  125. package/dist/generated-bridge.js.map +0 -1
  126. package/dist/index.d.ts.map +0 -1
  127. package/dist/index.js.map +0 -1
  128. package/dist/mutations.d.ts +0 -72
  129. package/dist/mutations.d.ts.map +0 -1
  130. package/dist/mutations.js +0 -63
  131. package/dist/mutations.js.map +0 -1
  132. package/dist/network.d.ts +0 -3
  133. package/dist/network.d.ts.map +0 -1
  134. package/dist/network.js +0 -17
  135. package/dist/network.js.map +0 -1
  136. package/dist/react/index.d.ts +0 -169
  137. package/dist/react/index.d.ts.map +0 -1
  138. package/dist/react/index.js +0 -628
  139. package/dist/react/index.js.map +0 -1
  140. package/dist/react-native/index.d.ts +0 -35
  141. package/dist/react-native/index.d.ts.map +0 -1
  142. package/dist/react-native/index.js +0 -49
  143. package/dist/react-native/index.js.map +0 -1
  144. package/dist/runtime-contract.d.ts +0 -13
  145. package/dist/runtime-contract.d.ts.map +0 -1
  146. package/dist/runtime-contract.js +0 -24
  147. package/dist/runtime-contract.js.map +0 -1
  148. package/dist/rust-client.d.ts +0 -106
  149. package/dist/rust-client.d.ts.map +0 -1
  150. package/dist/rust-client.js +0 -938
  151. package/dist/rust-client.js.map +0 -1
  152. package/dist/sentry.d.ts +0 -35
  153. package/dist/sentry.d.ts.map +0 -1
  154. package/dist/sentry.js +0 -155
  155. package/dist/sentry.js.map +0 -1
  156. package/dist/sql-safety.d.ts +0 -3
  157. package/dist/sql-safety.d.ts.map +0 -1
  158. package/dist/sql-safety.js +0 -62
  159. package/dist/sql-safety.js.map +0 -1
  160. package/dist/syncular-runtime-artifacts.json +0 -61
  161. package/dist/tauri/index.d.ts +0 -35
  162. package/dist/tauri/index.d.ts.map +0 -1
  163. package/dist/tauri/index.js +0 -114
  164. package/dist/tauri/index.js.map +0 -1
  165. package/dist/types.d.ts +0 -1096
  166. package/dist/types.d.ts.map +0 -1
  167. package/dist/types.js +0 -2
  168. package/dist/types.js.map +0 -1
  169. package/dist/wasm/.syncular-wasm-profile +0 -1
  170. package/dist/wasm/syncular-runtime-artifact.json +0 -21
  171. package/dist/wasm/syncular.d.ts +0 -207
  172. package/dist/wasm/syncular.js +0 -2341
  173. package/dist/wasm/syncular_bg.wasm +0 -0
  174. package/dist/wasm/syncular_bg.wasm.d.ts +0 -97
  175. package/dist/wasm-bindings/runtime-contract.d.ts +0 -22
  176. package/dist/wasm-bindings/runtime-contract.d.ts.map +0 -1
  177. package/dist/wasm-bindings/runtime-contract.js +0 -112
  178. package/dist/wasm-bindings/runtime-contract.js.map +0 -1
  179. package/dist/wasm-core/.syncular-wasm-profile +0 -1
  180. package/dist/wasm-core/syncular-runtime-artifact.json +0 -17
  181. package/dist/wasm-core/syncular.d.ts +0 -162
  182. package/dist/wasm-core/syncular.js +0 -1847
  183. package/dist/wasm-core/syncular_bg.wasm +0 -0
  184. package/dist/wasm-core/syncular_bg.wasm.d.ts +0 -77
  185. package/dist/wasm-perf/.syncular-wasm-profile +0 -1
  186. package/dist/wasm-perf/syncular-runtime-artifact.json +0 -21
  187. package/dist/wasm-perf/syncular.d.ts +0 -207
  188. package/dist/wasm-perf/syncular.js +0 -2341
  189. package/dist/wasm-perf/syncular_bg.wasm +0 -0
  190. package/dist/wasm-perf/syncular_bg.wasm.d.ts +0 -97
  191. package/dist/wasm-runtime.d.ts +0 -23
  192. package/dist/wasm-runtime.d.ts.map +0 -1
  193. package/dist/wasm-runtime.js +0 -69
  194. package/dist/wasm-runtime.js.map +0 -1
  195. package/dist/worker-client.d.ts +0 -123
  196. package/dist/worker-client.d.ts.map +0 -1
  197. package/dist/worker-client.js +0 -1735
  198. package/dist/worker-client.js.map +0 -1
  199. package/dist/worker-entry.d.ts.map +0 -1
  200. package/dist/worker-entry.js.map +0 -1
  201. package/dist/worker-protocol.d.ts.map +0 -1
  202. package/dist/worker-protocol.js.map +0 -1
  203. package/dist/worker-realtime.d.ts +0 -39
  204. package/dist/worker-realtime.d.ts.map +0 -1
  205. package/dist/worker-realtime.js +0 -677
  206. package/dist/worker-realtime.js.map +0 -1
  207. package/src/auth-leases.ts +0 -251
  208. package/src/blob-limits.ts +0 -98
  209. package/src/bridge-client.ts +0 -512
  210. package/src/client-config.ts +0 -29
  211. package/src/command-history.ts +0 -623
  212. package/src/console-diagnostics.ts +0 -617
  213. package/src/crdt-yjs/index.ts +0 -3
  214. package/src/crdt-yjs/webview-host-facade.ts +0 -477
  215. package/src/crdt-yjs/yjs-document-field-adapter.ts +0 -733
  216. package/src/crdt-yjs/yjs-prosemirror-bridge.ts +0 -272
  217. package/src/diagnostics.ts +0 -116
  218. package/src/generated-bridge.ts +0 -741
  219. package/src/mutations.ts +0 -168
  220. package/src/network.ts +0 -32
  221. package/src/react/index.ts +0 -1036
  222. package/src/react-native/index.ts +0 -152
  223. package/src/runtime-contract.ts +0 -48
  224. package/src/rust-client.ts +0 -1491
  225. package/src/sentry.ts +0 -215
  226. package/src/sql-safety.ts +0 -61
  227. package/src/tauri/index.ts +0 -211
  228. package/src/types.ts +0 -1397
  229. package/src/wasm-bindings/generated-wasm-bindings.d.ts +0 -70
  230. package/src/wasm-bindings/runtime-contract.ts +0 -158
  231. package/src/wasm-runtime.ts +0 -145
  232. package/src/worker-client.ts +0 -2289
  233. package/src/worker-realtime.ts +0 -843
package/dist/database.js CHANGED
@@ -1,954 +1,39 @@
1
- import { applyCodecsToDbRow, createColumnCodecsPlugin, createTableColumnCodecsResolver, randomId, } from '@syncular/core';
2
- import { Kysely, CompiledQuery as KyselyCompiledQuery, } from 'kysely';
3
- import { BaseSqliteDialect, BaseSqliteDriver } from 'kysely-generic-sqlite';
4
- import { assertSyncularBlobPayloadLimit, syncularBlobInputSize, } from './blob-limits.js';
5
- import { getSyncularClientStatus, SyncularClientLifecycle, } from './client.js';
6
- import { createSyncularConsoleDiagnosticsPublisher } from './console-diagnostics.js';
7
- import { isSyncularOfflineError } from './errors.js';
8
- import { createMutationsApi, } from './mutations.js';
9
- import { browserSyncularNetworkStatusSource } from './network.js';
10
- import { assertSyncularReadonlySql } from './sql-safety.js';
11
- import { createSyncularWorkerClient } from './worker-client.js';
12
- export async function withSyncularSchemaWrites(database, callback) {
13
- const client = assertUnsafeSqlClient(database.client);
14
- const dialect = createSyncularDialect(client, { unsafeWrites: true });
15
- const db = new Kysely({ dialect });
1
+ /** Attach-schema aliases are code-chosen, never user input — enforce it. */
2
+ export function assertImageAlias(alias) {
3
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(alias)) {
4
+ throw new Error(`invalid sqlite-image alias ${JSON.stringify(alias)}`);
5
+ }
6
+ }
7
+ /**
8
+ * Savepoint-based transaction helper shared by database implementations.
9
+ * `run` executes a parameterless statement.
10
+ */
11
+ export function runTransaction(depthHolder, run, fn) {
12
+ const depth = depthHolder.depth;
13
+ const savepoint = `syncular_sp_${depth}`;
14
+ if (depth === 0)
15
+ run('BEGIN');
16
+ else
17
+ run(`SAVEPOINT ${savepoint}`);
18
+ depthHolder.depth = depth + 1;
16
19
  try {
17
- return await callback(db);
18
- }
19
- finally {
20
- await dialect.destroyLiveQueries();
21
- await db.destroy();
22
- }
23
- }
24
- export async function createSyncularDatabase(options) {
25
- const client = await createSyncularWorkerClient(options);
26
- const dialect = createSyncularDialect(client, {
27
- appTables: options.appTables,
28
- tableConfig: options.tableConfig,
29
- });
30
- const db = new Kysely({
31
- dialect,
32
- plugins: options.codecs
33
- ? [
34
- createColumnCodecsPlugin({
35
- codecs: options.codecs,
36
- dialect: 'sqlite',
37
- }),
38
- ]
39
- : undefined,
40
- });
41
- let closed = false;
42
- const localClientMode = (options.config.mode ?? 'remote') !== 'remote';
43
- const mutationSyncScheduler = createMutationSyncScheduler(client, {
44
- enabled: options.sync?.autoSyncAfterMutation ?? !localClientMode,
45
- debounceMs: options.sync?.mutationSyncDebounceMs ?? 10,
46
- network: options.sync?.network === false
47
- ? undefined
48
- : (options.sync?.network ?? browserSyncularNetworkStatusSource()),
49
- isClosed: () => closed,
50
- });
51
- const blobUploadScheduler = createBlobUploadScheduler(client, {
52
- enabled: options.sync?.autoProcessBlobUploadsAfterStore ?? false,
53
- debounceMs: options.sync?.blobUploadDebounceMs ?? 10,
54
- isClosed: () => closed,
55
- });
56
- const consoleDiagnostics = options.consoleDiagnostics === undefined ||
57
- options.consoleDiagnostics === false
58
- ? undefined
59
- : createSyncularConsoleDiagnosticsPublisher(client, {
60
- ...(options.consoleDiagnostics === true
61
- ? {}
62
- : options.consoleDiagnostics),
63
- config: options.config,
64
- isClosed: () => closed,
65
- });
66
- const mutations = createSyncularMutations({
67
- client,
68
- codecs: options.codecs,
69
- codecDialect: 'sqlite',
70
- tableConfig: options.tableConfig,
71
- readBaseVersion: (args) => readCurrentBaseVersion(client, args),
72
- afterCommit: () => mutationSyncScheduler.schedule(),
73
- });
74
- const leasedMutations = createSyncularMutations({
75
- client,
76
- requireAuthLease: true,
77
- codecs: options.codecs,
78
- codecDialect: 'sqlite',
79
- tableConfig: options.tableConfig,
80
- readBaseVersion: (args) => readCurrentBaseVersion(client, args),
81
- afterCommit: () => mutationSyncScheduler.schedule(),
82
- });
83
- const blobs = createSyncularBlobClient(client, {
84
- diagnostics: options.diagnostics,
85
- limits: options.blobLimits,
86
- afterStore: ({ options }) => {
87
- if (options?.immediate)
88
- return;
89
- blobUploadScheduler.schedule();
90
- },
91
- });
92
- const lifecycle = new SyncularClientLifecycle(client, {
93
- realtime: options.realtime ?? true,
94
- initialSync: options.lifecycle?.initialSync,
95
- syncOnRealtimeConnect: options.lifecycle?.syncOnRealtimeConnect,
96
- pollIntervalMs: options.pollIntervalMs,
97
- network: options.sync?.network,
98
- });
99
- const database = {
100
- db,
101
- client,
102
- dialect,
103
- mutations,
104
- leasedMutations,
105
- blobs,
106
- lifecycle,
107
- live: (query, liveOptions) => dialect.live(query, liveOptions),
108
- start: () => lifecycle.start(),
109
- stop: () => lifecycle.stop(),
110
- sync: () => lifecycle.sync(),
111
- on: (event, listener) => client.addEventListener(event, listener),
112
- getStatus: () => getSyncularClientStatus(client),
113
- setSubscriptions: (subscriptions) => client.setSubscriptions(subscriptions),
114
- resumeFromBackground: (syncOptions) => client.resumeFromBackground(syncOptions),
115
- issueAuthLease: (request) => client.issueAuthLease(request),
116
- upsertAuthLease: (lease) => client.upsertAuthLease(lease),
117
- authLease: (leaseId) => client.authLease(leaseId),
118
- activeAuthLeases: (actorId, nowMs) => client.activeAuthLeases(actorId, nowMs),
119
- diagnosticSnapshot: () => client.diagnosticSnapshot(),
120
- presence: {
121
- get: (scopeKey) => client.getPresence(scopeKey),
122
- join: (scopeKey, metadata) => client.joinPresence(scopeKey, metadata),
123
- leave: (scopeKey) => client.leavePresence(scopeKey),
124
- updateMetadata: (scopeKey, metadata) => client.updatePresenceMetadata(scopeKey, metadata),
125
- onChange: (listener) => client.addPresenceListener(listener),
126
- },
127
- conflicts: {
128
- list: () => client.conflictSummaries(),
129
- retryKeepLocal: (id) => client.retryConflictKeepLocal(id),
130
- resolve: (id, resolution) => client.resolveConflict(id, resolution),
131
- },
132
- async close() {
133
- if (closed)
134
- return;
135
- closed = true;
136
- mutationSyncScheduler.destroy();
137
- blobUploadScheduler.destroy();
138
- consoleDiagnostics?.destroy();
139
- try {
140
- await lifecycle.stop().catch(() => undefined);
141
- await dialect.destroyLiveQueries();
142
- await db.destroy();
143
- }
144
- finally {
145
- await client.close();
146
- }
147
- },
148
- };
149
- try {
150
- if (options.subscriptions) {
151
- await client.setSubscriptions(options.subscriptions);
152
- }
153
- if (options.lifecycle?.autoStart ?? !localClientMode) {
154
- await lifecycle.start();
155
- }
20
+ const result = fn();
21
+ depthHolder.depth = depth;
22
+ if (depth === 0)
23
+ run('COMMIT');
24
+ else
25
+ run(`RELEASE ${savepoint}`);
26
+ return result;
156
27
  }
157
28
  catch (error) {
158
- await database.close().catch(() => undefined);
159
- throw error;
160
- }
161
- return database;
162
- }
163
- function createMutationSyncScheduler(client, options) {
164
- let timer;
165
- let inFlight;
166
- let queued = false;
167
- const isOnline = () => options.network?.isOnline() !== false;
168
- const clear = () => {
169
- if (timer)
170
- clearTimeout(timer);
171
- timer = undefined;
172
- };
173
- const run = async () => {
174
- if (options.isClosed())
175
- return;
176
- if (!isOnline()) {
177
- queued = true;
178
- return;
179
- }
180
- if (inFlight) {
181
- queued = true;
182
- return;
183
- }
184
- inFlight = client
185
- .syncOnce()
186
- .then(() => undefined)
187
- .catch((error) => {
188
- if (!isOnline() || isSyncularOfflineError(error))
189
- queued = true;
190
- })
191
- .finally(() => {
192
- inFlight = undefined;
193
- if (queued && !options.isClosed() && isOnline()) {
194
- queued = false;
195
- schedule();
196
- }
197
- });
198
- await inFlight;
199
- };
200
- const schedule = () => {
201
- if (!options.enabled || options.isClosed())
202
- return;
203
- if (!isOnline()) {
204
- queued = true;
205
- clear();
206
- return;
29
+ depthHolder.depth = depth;
30
+ if (depth === 0) {
31
+ run('ROLLBACK');
207
32
  }
208
- if (inFlight) {
209
- queued = true;
210
- return;
211
- }
212
- clear();
213
- if (options.debounceMs === false || options.debounceMs <= 0) {
214
- queueMicrotask(() => void run());
215
- return;
216
- }
217
- timer = setTimeout(() => void run(), options.debounceMs);
218
- };
219
- const handleOnline = () => {
220
- if (!queued || options.isClosed())
221
- return;
222
- queued = false;
223
- schedule();
224
- };
225
- options.network?.addEventListener?.('online', handleOnline);
226
- return {
227
- schedule,
228
- destroy() {
229
- clear();
230
- queued = false;
231
- options.network?.removeEventListener?.('online', handleOnline);
232
- },
233
- };
234
- }
235
- function createBlobUploadScheduler(client, options) {
236
- let timer;
237
- let inFlight;
238
- let queued = false;
239
- const clear = () => {
240
- if (timer)
241
- clearTimeout(timer);
242
- timer = undefined;
243
- };
244
- const run = async () => {
245
- if (options.isClosed())
246
- return;
247
- if (inFlight) {
248
- queued = true;
249
- return;
250
- }
251
- inFlight = client
252
- .processBlobUploadQueue()
253
- .then(() => undefined)
254
- .catch(() => undefined)
255
- .finally(() => {
256
- inFlight = undefined;
257
- if (queued && !options.isClosed()) {
258
- queued = false;
259
- schedule();
260
- }
261
- });
262
- await inFlight;
263
- };
264
- const schedule = () => {
265
- if (!options.enabled || options.isClosed())
266
- return;
267
- if (inFlight) {
268
- queued = true;
269
- return;
270
- }
271
- clear();
272
- if (options.debounceMs === false || options.debounceMs <= 0) {
273
- queueMicrotask(() => void run());
274
- return;
275
- }
276
- timer = setTimeout(() => void run(), options.debounceMs);
277
- };
278
- return {
279
- schedule,
280
- destroy() {
281
- clear();
282
- queued = false;
283
- },
284
- };
285
- }
286
- export function createSyncularBlobClient(client, hooks = {}) {
287
- const assertRetrieveWithinLimit = (ref) => assertSyncularBlobPayloadLimit({
288
- operation: 'retrieve',
289
- size: ref.size,
290
- limits: hooks.limits,
291
- refHash: ref.hash,
292
- diagnostics: hooks.diagnostics,
293
- });
294
- return {
295
- async store(data, storeOptions) {
296
- assertSyncularBlobPayloadLimit({
297
- operation: 'store',
298
- size: syncularBlobInputSize(data),
299
- limits: hooks.limits,
300
- options: storeOptions,
301
- diagnostics: hooks.diagnostics,
302
- });
303
- const ref = await client.storeBlob(await toUint8Array(data), storeOptions);
304
- await hooks.afterStore?.({
305
- ref,
306
- ...(storeOptions === undefined ? {} : { options: storeOptions }),
307
- });
308
- return ref;
309
- },
310
- async retrieve(ref) {
311
- assertRetrieveWithinLimit(ref);
312
- return client.retrieveBlob(ref);
313
- },
314
- isLocal(hash) {
315
- return client.isBlobLocal(hash);
316
- },
317
- async preload(refs) {
318
- for (const ref of refs)
319
- assertRetrieveWithinLimit(ref);
320
- await Promise.all(refs.map((ref) => client.retrieveBlob(ref)));
321
- },
322
- processUploadQueue(options) {
323
- return client.processBlobUploadQueue(options);
324
- },
325
- getUploadQueueStats() {
326
- return client.blobUploadQueueStats();
327
- },
328
- getCacheStats() {
329
- return client.blobCacheStats();
330
- },
331
- pruneCache(maxBytes) {
332
- return client.pruneBlobCache(maxBytes);
333
- },
334
- clearCache() {
335
- return client.clearBlobCache();
336
- },
337
- };
338
- }
339
- export function createSyncularDialect(client, options = {}) {
340
- const driver = new SyncularDriver(client, options);
341
- const dialect = new BaseSqliteDialect(() => driver);
342
- dialect.live = (query, liveOptions) => driver.live(query, liveOptions);
343
- dialect.destroyLiveQueries = () => driver.destroy();
344
- return dialect;
345
- }
346
- export function createSyncularMutations(options) {
347
- return createMutationsApi(createSyncularCommit(options));
348
- }
349
- export function createSyncularCommit(options) {
350
- const idColumn = options.idColumn ?? 'id';
351
- const versionColumn = options.versionColumn ?? 'server_version';
352
- const omitColumns = options.omitColumns ?? [];
353
- const codecDialect = options.codecDialect ?? 'sqlite';
354
- const resolveTableCodecs = createTableColumnCodecsResolver(options.codecs, {
355
- dialect: codecDialect,
356
- });
357
- return async (fn) => {
358
- const operations = [];
359
- const batch = [];
360
- const localMutations = [];
361
- const txTableCache = new Map();
362
- const makeTxTable = (table) => {
363
- const cached = txTableCache.get(table);
364
- if (cached)
365
- return cached;
366
- const tableConfig = options.tableConfig?.[table];
367
- const tableIdColumn = tableConfig?.primaryKeyColumn ?? idColumn;
368
- const tableVersionColumn = tableConfig?.serverVersionColumn === undefined
369
- ? versionColumn
370
- : tableConfig.serverVersionColumn;
371
- const softDeleteColumn = tableConfig?.softDeleteColumn ?? null;
372
- const crdtYjsFields = serverMergeCrdtYjsFields(tableConfig?.crdtYjsFields ?? []);
373
- const prepareInsert = async (values) => {
374
- const raw = objectRecord(values);
375
- const rawId = raw[tableIdColumn];
376
- const id = typeof rawId === 'string' && rawId ? rawId : randomId();
377
- const row = { ...raw, [tableIdColumn]: id };
378
- const payload = sanitizeOperationPayload(row, {
379
- omit: [
380
- tableIdColumn,
381
- ...(tableVersionColumn ? [tableVersionColumn] : []),
382
- ...omitColumns,
383
- ],
384
- });
385
- const syncPayload = stripCrdtYjsMaterializedPayloadFields(payload, crdtYjsFields);
386
- const localPayload = sanitizeOperationPayload(row, {
387
- omit: [
388
- tableIdColumn,
389
- ...(tableVersionColumn ? [tableVersionColumn] : []),
390
- ],
391
- });
392
- const transformedLocalPayload = await transformCrdtYjsMutationPayload({
393
- client: options.client,
394
- table,
395
- rowId: id,
396
- payload: localPayload,
397
- crdtYjsFields,
398
- });
399
- const localDbPayload = applyCodecsToDbRow(transformedLocalPayload, resolveTableCodecs(table, transformedLocalPayload), codecDialect);
400
- const operation = {
401
- table,
402
- row_id: id,
403
- op: 'upsert',
404
- payload: syncPayload,
405
- base_version: null,
406
- };
407
- return {
408
- id,
409
- operation,
410
- localRow: { ...localDbPayload, [tableIdColumn]: id },
411
- };
412
- };
413
- const tableApi = {
414
- async insert(values) {
415
- const prepared = await prepareInsert(values);
416
- const { id, operation, localRow } = prepared;
417
- operations.push(operation);
418
- batch.push({ operation, localRow });
419
- localMutations.push({ table, rowId: id, op: 'upsert' });
420
- return id;
421
- },
422
- async insertMany(rows) {
423
- if (rows.length === 0) {
424
- throw new Error('insertMany requires at least one row');
425
- }
426
- const ids = [];
427
- for (const row of rows) {
428
- const { id, operation, localRow } = await prepareInsert(row);
429
- ids.push(id);
430
- operations.push(operation);
431
- batch.push({ operation, localRow });
432
- localMutations.push({ table, rowId: id, op: 'upsert' });
433
- }
434
- return ids;
435
- },
436
- async update(id, patch, opts) {
437
- await applyUpsert(id, patch, opts, { mergeExistingLocalRow: true });
438
- },
439
- async delete(id, opts) {
440
- const baseVersion = await resolveBaseVersion({
441
- options,
442
- table,
443
- rowId: id,
444
- idColumn: tableIdColumn,
445
- versionColumn: tableVersionColumn,
446
- explicit: opts?.baseVersion,
447
- });
448
- if (softDeleteColumn) {
449
- const payload = { [softDeleteColumn]: 1 };
450
- const localDbPayload = applyCodecsToDbRow(payload, resolveTableCodecs(table, payload), codecDialect);
451
- const operation = {
452
- table,
453
- row_id: id,
454
- op: 'upsert',
455
- payload,
456
- base_version: baseVersion,
457
- };
458
- operations.push(operation);
459
- batch.push({
460
- operation,
461
- localRow: { ...localDbPayload, [tableIdColumn]: id },
462
- });
463
- localMutations.push({ table, rowId: id, op: 'upsert' });
464
- return;
465
- }
466
- const operation = {
467
- table,
468
- row_id: id,
469
- op: 'delete',
470
- payload: null,
471
- base_version: baseVersion,
472
- };
473
- operations.push(operation);
474
- batch.push({ operation, localRow: null });
475
- localMutations.push({ table, rowId: id, op: 'delete' });
476
- },
477
- async upsert(id, patch, opts) {
478
- await applyUpsert(id, patch, opts, { mergeExistingLocalRow: false });
479
- },
480
- };
481
- const applyUpsert = async (id, patch, opts, behavior) => {
482
- const rawPatch = objectRecord(patch);
483
- const payload = sanitizeOperationPayload(rawPatch, {
484
- omit: [
485
- tableIdColumn,
486
- ...(tableVersionColumn ? [tableVersionColumn] : []),
487
- ...omitColumns,
488
- ],
489
- });
490
- const syncPayload = stripCrdtYjsMaterializedPayloadFields(payload, crdtYjsFields);
491
- const existingLocalRow = behavior.mergeExistingLocalRow
492
- ? await readExistingLocalRow({
493
- client: options.client,
494
- table,
495
- rowId: id,
496
- idColumn: tableIdColumn,
497
- })
498
- : null;
499
- const rawLocalPayload = existingLocalRow
500
- ? { ...existingLocalRow, ...rawPatch }
501
- : rawPatch;
502
- const localPayload = sanitizeOperationPayload(rawLocalPayload, {
503
- omit: [
504
- tableIdColumn,
505
- ...(tableVersionColumn ? [tableVersionColumn] : []),
506
- ],
507
- });
508
- const existingRow = await readCrdtYjsExistingRow({
509
- client: options.client,
510
- table,
511
- rowId: id,
512
- idColumn: tableIdColumn,
513
- crdtYjsFields,
514
- });
515
- const transformedLocalPayload = await transformCrdtYjsMutationPayload({
516
- client: options.client,
517
- table,
518
- rowId: id,
519
- payload: localPayload,
520
- existingRow,
521
- crdtYjsFields,
522
- });
523
- const localDbPayload = applyCodecsToDbRow(transformedLocalPayload, resolveTableCodecs(table, transformedLocalPayload), codecDialect);
524
- const operation = {
525
- table,
526
- row_id: id,
527
- op: 'upsert',
528
- payload: syncPayload,
529
- base_version: await resolveBaseVersion({
530
- options,
531
- table,
532
- rowId: id,
533
- idColumn: tableIdColumn,
534
- versionColumn: tableVersionColumn,
535
- explicit: opts?.baseVersion,
536
- }),
537
- };
538
- operations.push(operation);
539
- batch.push({
540
- operation,
541
- localRow: { ...localDbPayload, [tableIdColumn]: id },
542
- });
543
- localMutations.push({ table, rowId: id, op: 'upsert' });
544
- };
545
- txTableCache.set(table, tableApi);
546
- return tableApi;
547
- };
548
- const tx = new Proxy({}, {
549
- get(_target, prop) {
550
- if (prop === 'then')
551
- return undefined;
552
- if (typeof prop !== 'string')
553
- return undefined;
554
- return makeTxTable(prop);
555
- },
556
- });
557
- const result = await fn(tx);
558
- if (operations.length === 0)
559
- throw new Error('No mutations were enqueued');
560
- const clientCommitId = options.requireAuthLease
561
- ? await requireLeasedMutationClient(options.client).applyLeasedMutationsCommit(batch)
562
- : await options.client.applyMutationsCommit(batch);
563
- const meta = {
564
- operations,
565
- localMutations,
566
- clientCommitIds: [clientCommitId],
567
- };
568
- await options.afterCommit?.(meta);
569
- return {
570
- result,
571
- receipt: { commitId: clientCommitId, clientCommitId },
572
- meta,
573
- };
574
- };
575
- }
576
- function requireLeasedMutationClient(client) {
577
- if (typeof client.applyLeasedMutationsCommit !== 'function') {
578
- throw new Error('Syncular leased mutations require applyLeasedMutationsCommit support');
579
- }
580
- if (typeof client.applyLeasedMutation !== 'function') {
581
- throw new Error('Syncular leased mutations require applyLeasedMutation support');
582
- }
583
- return client;
584
- }
585
- class SyncularDriver extends BaseSqliteDriver {
586
- client;
587
- #listeners = new Map();
588
- #appTables;
589
- #tableConfig;
590
- constructor(client, options = {}) {
591
- super(async () => {
592
- this.conn = new SyncularConnection(client, options.unsafeWrites ?? false);
593
- });
594
- this.client = client;
595
- this.#appTables =
596
- options.appTables == null
597
- ? undefined
598
- : new Set(normalizeLiveQueryTables(options.appTables));
599
- this.#tableConfig = options.tableConfig;
600
- }
601
- async live(query, options) {
602
- const compiled = query.compile();
603
- const tables = normalizeLiveQueryTables(options.tables ?? inferTablesFromCompiledQuery(compiled, this.#appTables), this.#appTables);
604
- if (tables.length === 0) {
605
- throw new Error('Could not infer live query table dependencies. Pass { tables: [...] } explicitly.');
33
+ else {
34
+ run(`ROLLBACK TO ${savepoint}`);
35
+ run(`RELEASE ${savepoint}`);
606
36
  }
607
- const dependencyHints = normalizeLiveQueryDependencyHints(options.dependencyHints ??
608
- inferDependencyHintsFromCompiledQuery(compiled, tables, this.#tableConfig), tables, this.#appTables);
609
- const snapshot = await this.client.subscribeQuery(compiled.sql, compiled.parameters, tables, dependencyHints);
610
- const listener = (event) => {
611
- const typed = event;
612
- options.onChange(typed.rows, { ...typed, initial: false });
613
- };
614
- this.#listeners.set(snapshot.id, listener);
615
- this.client.addLiveQueryListener(snapshot.id, listener);
616
- options.onChange(snapshot.rows, {
617
- queryId: snapshot.id,
618
- version: Date.now(),
619
- changedRows: [],
620
- rows: snapshot.rows,
621
- initial: true,
622
- });
623
- return {
624
- id: snapshot.id,
625
- unsubscribe: () => {
626
- void this.#unsubscribeQuery(snapshot.id).catch(() => undefined);
627
- },
628
- };
629
- }
630
- async beginTransaction(connection) {
631
- await connection.executeQuery(KyselyCompiledQuery.raw('begin'));
632
- }
633
- async destroy() {
634
- const ids = [...this.#listeners.keys()];
635
- for (const id of ids)
636
- this.client.removeLiveQueryListener(id);
637
- this.#listeners.clear();
638
- await Promise.allSettled(ids.map((id) => this.client.unsubscribeQuery(id)));
639
- }
640
- async #unsubscribeQuery(id) {
641
- if (!this.#listeners.has(id))
642
- return;
643
- this.#listeners.delete(id);
644
- this.client.removeLiveQueryListener(id);
645
- await this.client.unsubscribeQuery(id);
646
- }
647
- }
648
- class SyncularConnection {
649
- client;
650
- unsafeWrites;
651
- constructor(client, unsafeWrites) {
652
- this.client = client;
653
- this.unsafeWrites = unsafeWrites;
654
- }
655
- async executeQuery(compiledQuery) {
656
- if (!this.unsafeWrites)
657
- assertSyncularReadonlySql(compiledQuery.sql);
658
- const result = this.unsafeWrites
659
- ? await assertUnsafeSqlClient(this.client).executeUnsafeSql(compiledQuery.sql, compiledQuery.parameters)
660
- : await this.client.executeSql(compiledQuery.sql, compiledQuery.parameters);
661
- return {
662
- rows: result.rows,
663
- numAffectedRows: result.numAffectedRows == null
664
- ? undefined
665
- : BigInt(result.numAffectedRows),
666
- insertId: result.insertId == null ? undefined : BigInt(result.insertId),
667
- };
668
- }
669
- streamQuery(_compiledQuery, _chunkSize) {
670
- throw new Error('syncular sqlite dialect does not support streaming');
671
- }
672
- }
673
- function assertUnsafeSqlClient(client) {
674
- const maybe = client;
675
- if (typeof maybe.executeUnsafeSql === 'function') {
676
- return client;
677
- }
678
- throw new Error('Syncular schema installation requires an internal unsafe SQL client.');
679
- }
680
- function objectRecord(value) {
681
- if (!value || typeof value !== 'object' || Array.isArray(value))
682
- return {};
683
- return { ...value };
684
- }
685
- async function toUint8Array(data) {
686
- if (data instanceof Uint8Array)
687
- return data;
688
- return new Uint8Array(await data.arrayBuffer());
689
- }
690
- function sanitizeOperationPayload(payload, args) {
691
- if (args.omit.length === 0)
692
- return payload;
693
- const omit = new Set(args.omit);
694
- const out = {};
695
- for (const [key, value] of Object.entries(payload)) {
696
- if (!omit.has(key))
697
- out[key] = value;
698
- }
699
- return out;
700
- }
701
- function serverMergeCrdtYjsFields(fields) {
702
- return fields.filter((field) => field.syncMode === undefined || field.syncMode === 'server-merge');
703
- }
704
- async function transformCrdtYjsMutationPayload(args) {
705
- if (args.crdtYjsFields.length === 0 || !('__yjs' in args.payload)) {
706
- return args.payload;
707
- }
708
- return args.client.applyYjsEnvelopeToPayload({
709
- table: args.table,
710
- rowId: args.rowId,
711
- payload: args.payload,
712
- existingRow: args.existingRow ?? null,
713
- rules: args.crdtYjsFields.map((field) => ({
714
- table: args.table,
715
- field: field.field,
716
- stateColumn: field.stateColumn,
717
- containerKey: field.containerKey ?? field.field,
718
- rowIdField: field.rowIdField ?? 'id',
719
- kind: field.kind ?? 'text',
720
- })),
721
- stripEnvelope: true,
722
- });
723
- }
724
- function stripCrdtYjsMaterializedPayloadFields(payload, crdtYjsFields) {
725
- const envelope = objectRecord(payload.__yjs);
726
- if (crdtYjsFields.length === 0 || Object.keys(envelope).length === 0) {
727
- return payload;
728
- }
729
- let next = null;
730
- const ensureNext = () => {
731
- next ??= { ...payload };
732
- return next;
733
- };
734
- for (const field of crdtYjsFields) {
735
- if (!(field.field in envelope))
736
- continue;
737
- delete ensureNext()[field.field];
738
- delete ensureNext()[field.stateColumn];
739
- }
740
- return next ?? payload;
741
- }
742
- async function readCrdtYjsExistingRow(args) {
743
- if (args.crdtYjsFields.length === 0)
744
- return null;
745
- const columns = new Set([args.idColumn]);
746
- for (const field of args.crdtYjsFields) {
747
- columns.add(field.field);
748
- columns.add(field.stateColumn);
749
- }
750
- const result = await args.client.executeSql(`select ${[...columns].map(quoteSqlIdentifier).join(', ')} from ${quoteSqlIdentifier(args.table)} where ${quoteSqlIdentifier(args.idColumn)} = ? limit 1`, [args.rowId]);
751
- return result.rows[0] ?? null;
752
- }
753
- async function readExistingLocalRow(args) {
754
- const result = await args.client.executeSql(`select * from ${quoteSqlIdentifier(args.table)} where ${quoteSqlIdentifier(args.idColumn)} = ? limit 1`, [args.rowId]);
755
- return result.rows[0] ?? null;
756
- }
757
- function quoteSqlIdentifier(identifier) {
758
- if (!identifier)
759
- throw new Error('SQLite identifier cannot be empty');
760
- return `"${identifier.replace(/"/g, '""')}"`;
761
- }
762
- async function readCurrentBaseVersion(client, args) {
763
- const result = await client.executeSql(`select ${quoteSqlIdentifier(args.versionColumn)} as version from ${quoteSqlIdentifier(args.table)} where ${quoteSqlIdentifier(args.idColumn)} = ? limit 1`, [args.rowId]);
764
- return coerceBaseVersion(result.rows[0]?.version);
765
- }
766
- function coerceBaseVersion(value) {
767
- if (value == null)
768
- return null;
769
- const numeric = typeof value === 'number' ? value : Number(value);
770
- if (!Number.isFinite(numeric) || numeric < 0)
771
- return null;
772
- return numeric;
773
- }
774
- async function resolveBaseVersion(args) {
775
- if (args.explicit !== undefined)
776
- return coerceBaseVersion(args.explicit);
777
- if (!args.versionColumn)
778
- return null;
779
- const readBaseVersion = args.options.readBaseVersion ??
780
- ((readArgs) => readCurrentBaseVersion(args.options.client, readArgs));
781
- return readBaseVersion({
782
- table: args.table,
783
- rowId: args.rowId,
784
- idColumn: args.idColumn,
785
- versionColumn: args.versionColumn,
786
- });
787
- }
788
- function inferTablesFromCompiledQuery(query, appTables) {
789
- const tables = new Set();
790
- collectTables(query.query, tables);
791
- return [...tables].filter((table) => appTables == null || appTables.has(table));
792
- }
793
- function inferDependencyHintsFromCompiledQuery(query, tables, tableConfig) {
794
- if (tables.length !== 1)
795
- return [];
796
- const table = tables[0];
797
- const primaryKey = tableConfig?.[table]?.primaryKeyColumn ?? 'id';
798
- const rowIds = primaryKeyEqualityValues(query.query, table, primaryKey);
799
- return rowIds.length > 0 ? [{ table, rowIds }] : [];
800
- }
801
- function normalizeLiveQueryDependencyHints(hints, tables, appTables) {
802
- const observedTables = new Set(tables);
803
- const normalized = [];
804
- for (const hint of hints) {
805
- if (hint.table.length === 0) {
806
- throw new Error('Live query dependency hint tables must not be empty.');
807
- }
808
- if (!observedTables.has(hint.table)) {
809
- throw new Error(`Live query dependency hint table ${hint.table} is not one of the observed tables.`);
810
- }
811
- if (appTables != null && !appTables.has(hint.table)) {
812
- throw new Error(`Live query dependency hint table ${hint.table} is not part of the generated app schema.`);
813
- }
814
- const rowIds = uniqueDefinedStrings(hint.rowIds);
815
- const fields = uniqueDefinedStrings(hint.fields);
816
- normalized.push({
817
- table: hint.table,
818
- ...(rowIds.length > 0 ? { rowIds } : {}),
819
- ...(fields.length > 0 ? { fields } : {}),
820
- });
821
- }
822
- return normalized;
823
- }
824
- function primaryKeyEqualityValues(query, table, primaryKey) {
825
- if (!isOperationNode(query) || query.kind !== 'SelectQueryNode')
826
- return [];
827
- const where = query.where;
828
- if (!isOperationNode(where) || where.kind !== 'WhereNode')
829
- return [];
830
- return collectConjunctivePrimaryKeyValues(where.where, table, primaryKey);
831
- }
832
- function collectConjunctivePrimaryKeyValues(node, table, primaryKey) {
833
- if (!isOperationNode(node))
834
- return [];
835
- if (node.kind === 'AndNode') {
836
- return uniqueStrings([
837
- ...collectConjunctivePrimaryKeyValues(node.left, table, primaryKey),
838
- ...collectConjunctivePrimaryKeyValues(node.right, table, primaryKey),
839
- ]);
840
- }
841
- const equality = primaryKeyEqualityValue(node, table, primaryKey);
842
- return equality == null ? [] : [equality];
843
- }
844
- function primaryKeyEqualityValue(node, table, primaryKey) {
845
- if (!isOperationNode(node) || node.kind !== 'BinaryOperationNode') {
846
- return undefined;
847
- }
848
- const operator = node.operator;
849
- if (!isOperationNode(operator) ||
850
- operator.kind !== 'OperatorNode' ||
851
- operator.operator !== '=') {
852
- return undefined;
853
- }
854
- return (equalityValueForReference(node.leftOperand, node.rightOperand, table, primaryKey) ??
855
- equalityValueForReference(node.rightOperand, node.leftOperand, table, primaryKey));
856
- }
857
- function equalityValueForReference(reference, value, table, primaryKey) {
858
- if (!referenceMatchesColumn(reference, table, primaryKey))
859
- return undefined;
860
- if (!isOperationNode(value) || value.kind !== 'ValueNode')
861
- return undefined;
862
- if (typeof value.value === 'string')
863
- return value.value;
864
- if (typeof value.value === 'number' && Number.isFinite(value.value)) {
865
- return String(value.value);
866
- }
867
- return undefined;
868
- }
869
- function referenceMatchesColumn(reference, table, column) {
870
- if (!isOperationNode(reference) || reference.kind !== 'ReferenceNode') {
871
- return false;
872
- }
873
- if (reference.table != null &&
874
- tableNameFromTableNode(reference.table) !== table) {
875
- return false;
876
- }
877
- const columnNode = reference.column;
878
- if (!isOperationNode(columnNode) || columnNode.kind !== 'ColumnNode') {
879
- return false;
880
- }
881
- const identifier = columnNode.column;
882
- return (isOperationNode(identifier) &&
883
- identifier.kind === 'IdentifierNode' &&
884
- identifier.name === column);
885
- }
886
- function uniqueStrings(values) {
887
- return [...new Set(values)];
888
- }
889
- function uniqueDefinedStrings(values) {
890
- if (values == null)
891
- return [];
892
- const unique = [];
893
- for (const value of values) {
894
- if (value.length === 0) {
895
- throw new Error('Live query dependency hint values must not be empty.');
896
- }
897
- if (!unique.includes(value))
898
- unique.push(value);
899
- }
900
- return unique;
901
- }
902
- function collectTables(node, tables) {
903
- if (Array.isArray(node)) {
904
- for (const item of node)
905
- collectTables(item, tables);
906
- return;
907
- }
908
- if (!isOperationNode(node))
909
- return;
910
- if (node.kind === 'TableNode')
911
- collectTableNode(node, tables);
912
- for (const value of Object.values(node))
913
- collectTables(value, tables);
914
- }
915
- function collectTableNode(node, tables) {
916
- const table = tableNameFromTableNode(node);
917
- if (table != null)
918
- tables.add(table);
919
- }
920
- function tableNameFromTableNode(node) {
921
- if (!isOperationNode(node))
922
- return undefined;
923
- if (node.kind === 'AliasNode')
924
- return tableNameFromTableNode(node.node);
925
- if (node.kind !== 'TableNode')
926
- return undefined;
927
- const table = node.table;
928
- if (!isOperationNode(table) || table.kind !== 'SchemableIdentifierNode') {
929
- return undefined;
930
- }
931
- const identifier = table.identifier;
932
- if (!isOperationNode(identifier) || identifier.kind !== 'IdentifierNode') {
933
- return undefined;
934
- }
935
- return typeof identifier.name === 'string' ? identifier.name : undefined;
936
- }
937
- function isOperationNode(value) {
938
- return Boolean(value && typeof value === 'object' && 'kind' in value);
939
- }
940
- function normalizeLiveQueryTables(tables, appTables) {
941
- const normalized = [];
942
- for (const table of tables) {
943
- if (table.length === 0) {
944
- throw new Error('Live query table dependencies must not contain empty names.');
945
- }
946
- if (appTables != null && !appTables.has(table)) {
947
- throw new Error(`Live query table ${table} is not part of the generated app schema.`);
948
- }
949
- if (!normalized.includes(table))
950
- normalized.push(table);
37
+ throw error;
951
38
  }
952
- return normalized;
953
39
  }
954
- //# sourceMappingURL=database.js.map