@tanstack/db-sqlite-persistence-core 0.2.21 → 0.4.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 (58) hide show
  1. package/README.md +258 -7
  2. package/dist/cjs/broadcast-coordinator.cjs +1689 -0
  3. package/dist/cjs/broadcast-coordinator.cjs.map +1 -0
  4. package/dist/cjs/broadcast-coordinator.d.cts +116 -0
  5. package/dist/cjs/errors.cjs +73 -0
  6. package/dist/cjs/errors.cjs.map +1 -1
  7. package/dist/cjs/errors.d.cts +44 -0
  8. package/dist/cjs/index.cjs +19 -0
  9. package/dist/cjs/index.cjs.map +1 -1
  10. package/dist/cjs/index.d.cts +2 -0
  11. package/dist/cjs/persisted.cjs +1840 -399
  12. package/dist/cjs/persisted.cjs.map +1 -1
  13. package/dist/cjs/persisted.d.cts +177 -11
  14. package/dist/cjs/remote-subset-owner.cjs +20 -0
  15. package/dist/cjs/remote-subset-owner.cjs.map +1 -0
  16. package/dist/cjs/remote-subset-owner.d.cts +7 -0
  17. package/dist/cjs/remote-subset-wire.cjs +681 -0
  18. package/dist/cjs/remote-subset-wire.cjs.map +1 -0
  19. package/dist/cjs/remote-subset-wire.d.cts +59 -0
  20. package/dist/cjs/sqlite-core-adapter.cjs +969 -364
  21. package/dist/cjs/sqlite-core-adapter.cjs.map +1 -1
  22. package/dist/cjs/sqlite-core-adapter.d.cts +45 -1
  23. package/dist/cjs/sqlite-value.cjs +31 -0
  24. package/dist/cjs/sqlite-value.cjs.map +1 -0
  25. package/dist/cjs/sqlite-value.d.cts +10 -0
  26. package/dist/esm/broadcast-coordinator.d.ts +116 -0
  27. package/dist/esm/broadcast-coordinator.js +1689 -0
  28. package/dist/esm/broadcast-coordinator.js.map +1 -0
  29. package/dist/esm/errors.d.ts +44 -0
  30. package/dist/esm/errors.js +74 -1
  31. package/dist/esm/errors.js.map +1 -1
  32. package/dist/esm/index.d.ts +2 -0
  33. package/dist/esm/index.js +21 -2
  34. package/dist/esm/index.js.map +1 -1
  35. package/dist/esm/persisted.d.ts +177 -11
  36. package/dist/esm/persisted.js +1842 -401
  37. package/dist/esm/persisted.js.map +1 -1
  38. package/dist/esm/remote-subset-owner.d.ts +7 -0
  39. package/dist/esm/remote-subset-owner.js +20 -0
  40. package/dist/esm/remote-subset-owner.js.map +1 -0
  41. package/dist/esm/remote-subset-wire.d.ts +59 -0
  42. package/dist/esm/remote-subset-wire.js +681 -0
  43. package/dist/esm/remote-subset-wire.js.map +1 -0
  44. package/dist/esm/sqlite-core-adapter.d.ts +45 -1
  45. package/dist/esm/sqlite-core-adapter.js +955 -350
  46. package/dist/esm/sqlite-core-adapter.js.map +1 -1
  47. package/dist/esm/sqlite-value.d.ts +10 -0
  48. package/dist/esm/sqlite-value.js +31 -0
  49. package/dist/esm/sqlite-value.js.map +1 -0
  50. package/package.json +12 -2
  51. package/src/broadcast-coordinator.ts +2456 -0
  52. package/src/errors.ts +120 -0
  53. package/src/index.ts +2 -0
  54. package/src/persisted.ts +2690 -444
  55. package/src/remote-subset-owner.ts +36 -0
  56. package/src/remote-subset-wire.ts +946 -0
  57. package/src/sqlite-core-adapter.ts +1297 -410
  58. package/src/sqlite-value.ts +30 -0
@@ -1,15 +1,25 @@
1
+ import { TransportedLoadSubsetOptions } from './remote-subset-wire.js';
2
+ import { RemoteSubsetOwner } from './remote-subset-owner.js';
1
3
  import { StandardSchemaV1 } from '@standard-schema/spec';
2
- import { CollectionConfig, LoadSubsetOptions, PendingMutation, SyncConfig, UtilsRecord } from '@tanstack/db';
4
+ import { CollectionConfig, InferSchemaOutput, LoadSubsetOptions, PendingMutation, SyncConfig, SyncPersistenceKeySetEvidence, SyncPersistenceScanOptions, UtilsRecord } from '@tanstack/db';
3
5
  export type PersistedMutationEnvelope = {
4
6
  mutationId: string;
5
7
  type: `insert`;
6
8
  key: string | number;
7
9
  value: Record<string, unknown>;
10
+ /** Persisted row metadata, not optimistic-transaction metadata. */
11
+ metadata?: unknown;
12
+ /** Whether this envelope replaces or deletes the persisted row metadata. */
13
+ metadataChanged?: boolean;
8
14
  } | {
9
15
  mutationId: string;
10
16
  type: `update`;
11
17
  key: string | number;
12
18
  value: Record<string, unknown>;
19
+ /** Persisted row metadata, not optimistic-transaction metadata. */
20
+ metadata?: unknown;
21
+ /** Whether this envelope replaces or deletes the persisted row metadata. */
22
+ metadataChanged?: boolean;
13
23
  } | {
14
24
  mutationId: string;
15
25
  type: `delete`;
@@ -52,17 +62,35 @@ export type TxCommitted = {
52
62
  export type EnsureRemoteSubsetRequest = {
53
63
  type: `rpc:ensureRemoteSubset:req`;
54
64
  rpcId: string;
55
- options: LoadSubsetOptions;
65
+ acquisitionId: string;
66
+ options: TransportedLoadSubsetOptions;
56
67
  };
57
68
  export type EnsureRemoteSubsetResponse = {
58
69
  type: `rpc:ensureRemoteSubset:res`;
59
70
  rpcId: string;
60
71
  ok: true;
72
+ leaderId: string;
61
73
  } | {
62
74
  type: `rpc:ensureRemoteSubset:res`;
63
75
  rpcId: string;
64
76
  ok: false;
65
77
  error: string;
78
+ retryable?: true;
79
+ };
80
+ export type ReleaseRemoteSubsetRequest = {
81
+ type: `rpc:releaseRemoteSubset:req`;
82
+ rpcId: string;
83
+ acquisitionId: string;
84
+ };
85
+ export type ReleaseRemoteSubsetResponse = {
86
+ type: `rpc:releaseRemoteSubset:res`;
87
+ rpcId: string;
88
+ ok: true;
89
+ } | {
90
+ type: `rpc:releaseRemoteSubset:res`;
91
+ rpcId: string;
92
+ ok: false;
93
+ error: string;
66
94
  };
67
95
  export type ApplyLocalMutationsRequest = {
68
96
  type: `rpc:applyLocalMutations:req`;
@@ -84,6 +112,42 @@ export type ApplyLocalMutationsResponse = {
84
112
  ok: false;
85
113
  code: `NOT_LEADER` | `VALIDATION_ERROR` | `CONFLICT` | `TIMEOUT`;
86
114
  error: string;
115
+ } | {
116
+ type: `rpc:applyLocalMutations:res`;
117
+ rpcId: string;
118
+ ok: false;
119
+ code: `PERSISTENCE_ERROR`;
120
+ error: string;
121
+ sourceCode?: string | number;
122
+ path?: string | ReadonlyArray<string | number>;
123
+ };
124
+ export type ApplyCommittedTxRequest = {
125
+ type: `rpc:applyCommittedTx:req`;
126
+ rpcId: string;
127
+ envelopeId: string;
128
+ tx: PersistedTx;
129
+ };
130
+ export type ApplyCommittedTxResponse = {
131
+ type: `rpc:applyCommittedTx:res`;
132
+ rpcId: string;
133
+ ok: true;
134
+ term: number;
135
+ seq: number;
136
+ latestRowVersion: number;
137
+ } | {
138
+ type: `rpc:applyCommittedTx:res`;
139
+ rpcId: string;
140
+ ok: false;
141
+ code: `NOT_LEADER` | `CONFLICT` | `TIMEOUT`;
142
+ error: string;
143
+ } | {
144
+ type: `rpc:applyCommittedTx:res`;
145
+ rpcId: string;
146
+ ok: false;
147
+ code: `PERSISTENCE_ERROR`;
148
+ error: string;
149
+ sourceCode?: string | number;
150
+ path?: string | ReadonlyArray<string | number>;
87
151
  };
88
152
  export type PullSinceRequest = {
89
153
  type: `rpc:pullSince:req`;
@@ -157,8 +221,17 @@ export type PersistedScannedRow<T extends object = Record<string, unknown>, TKey
157
221
  value: T;
158
222
  metadata?: unknown;
159
223
  };
160
- export type PersistedRowScanOptions = {
161
- metadataOnly?: boolean;
224
+ export type PersistedRowScanOptions = SyncPersistenceScanOptions;
225
+ export type PersistedKeySetEvidence = SyncPersistenceKeySetEvidence;
226
+ export type PersistencePullSinceResult = {
227
+ latestRowVersion: number;
228
+ requiresFullReload: true;
229
+ } | {
230
+ latestRowVersion: number;
231
+ requiresFullReload: false;
232
+ changedKeys: Array<string | number>;
233
+ deletedKeys: Array<string | number>;
234
+ deltas?: Array<ReplayableTxDelta<Record<string, unknown>, string | number>>;
162
235
  };
163
236
  export type PersistedTx<T extends object = Record<string, unknown>, TKey extends string | number = string | number> = {
164
237
  txId: string;
@@ -186,6 +259,18 @@ export type PersistedTx<T extends object = Record<string, unknown>, TKey extends
186
259
  rowMetadataMutations?: Array<PersistedRowMetadataMutation<TKey>>;
187
260
  collectionMetadataMutations?: Array<PersistedCollectionMetadataMutation>;
188
261
  };
262
+ /**
263
+ * Opaque identity shared by every adapter over the same physical SQLite driver.
264
+ * The core adapter uses it to serialize non-preemptible logical operations
265
+ * while alternating one regular operation between queued hydrations.
266
+ *
267
+ * Delegating drivers must forward this property before they are passed to a
268
+ * SQLite persistence adapter. A driver may also brand each returned Promise
269
+ * with the same key for late capability discovery, but a wrapper must return
270
+ * that exact Promise: discovering the key after a call cannot retroactively
271
+ * schedule the wrapper's first logical operation.
272
+ */
273
+ export declare const SQLITE_DRIVER_SHARED_LOGICAL_SCHEDULING_KEY: unique symbol;
189
274
  export interface PersistenceAdapter {
190
275
  loadSubset: (collectionId: string, options: LoadSubsetOptions, ctx?: {
191
276
  requiredIndexSignatures?: ReadonlyArray<string>;
@@ -194,6 +279,25 @@ export interface PersistenceAdapter {
194
279
  value: Record<string, unknown>;
195
280
  metadata?: unknown;
196
281
  }>>;
282
+ loadResumeSnapshot: (collectionId: string, ctx?: {
283
+ requiredIndexSignatures?: ReadonlyArray<string>;
284
+ includeRows?: boolean;
285
+ }) => Promise<{
286
+ rows: Array<{
287
+ key: string | number;
288
+ value: Record<string, unknown>;
289
+ metadata?: unknown;
290
+ }>;
291
+ keySet?: PersistedKeySetEvidence;
292
+ collectionMetadata: Array<{
293
+ key: string;
294
+ value: unknown;
295
+ }>;
296
+ latestTerm: number;
297
+ latestSeq: number;
298
+ latestRowVersion: number;
299
+ resetEpoch: number;
300
+ }>;
197
301
  applyCommittedTx: (collectionId: string, tx: PersistedTx) => Promise<void>;
198
302
  loadCollectionMetadata?: (collectionId: string) => Promise<Array<{
199
303
  key: string;
@@ -207,24 +311,56 @@ export interface PersistenceAdapter {
207
311
  latestSeq: number;
208
312
  latestRowVersion: number;
209
313
  }>;
314
+ /**
315
+ * Runs one complete logical hydrate as a non-preemptible scheduler unit.
316
+ * The callback must use the supplied unscheduled adapter for all nested
317
+ * persistence work and must not retain it after the callback settles.
318
+ */
319
+ runInHydrationScope?: <T>(task: (adapter: HydrationPersistenceAdapter) => Promise<T>) => Promise<T>;
320
+ /** Schedule one regular operation before it acquires external writer locks. */
321
+ runInRegularScope?: <T>(task: (adapter: HydrationPersistenceAdapter) => Promise<T>) => Promise<T>;
322
+ /** Whether hydration scopes currently enter a shared driver scheduler. */
323
+ isHydrationScopeScheduled?: () => boolean;
210
324
  }
325
+ export type HydrationPersistenceAdapter = PersistenceAdapter & {
326
+ pullSince?: (collectionId: string, fromRowVersion: number) => Promise<PersistencePullSinceResult>;
327
+ };
328
+ export type { RemoteSubsetOwner } from './remote-subset-owner.js';
211
329
  export interface SQLiteDriver {
330
+ readonly [SQLITE_DRIVER_SHARED_LOGICAL_SCHEDULING_KEY]?: object;
331
+ /** Host-specific limit on bound parameters in one query, when lower than SQLite's. */
332
+ readonly maxBoundParameters?: number;
212
333
  exec: (sql: string) => Promise<void>;
213
334
  query: <T>(sql: string, params?: ReadonlyArray<unknown>) => Promise<ReadonlyArray<T>>;
214
335
  run: (sql: string, params?: ReadonlyArray<unknown>) => Promise<void>;
215
336
  transaction: <T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>) => Promise<T>;
216
337
  transactionWithDriver?: <T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>) => Promise<T>;
217
338
  }
339
+ /**
340
+ * Forwards a driver's shared logical scheduling identity to a transparent
341
+ * delegating driver before the wrapper is used by a persistence adapter.
342
+ */
343
+ export declare function forwardSQLiteDriverSharedLogicalScheduling<TDriver extends SQLiteDriver>(source: SQLiteDriver, wrapper: TDriver): TDriver;
218
344
  export interface PersistedCollectionCoordinator {
219
345
  getNodeId: () => string;
346
+ setAdapterForCollection?: (collectionId: string, adapter: PersistenceAdapter) => void;
220
347
  subscribe: (collectionId: string, onMessage: (message: ProtocolEnvelope<unknown>) => void) => () => void;
221
348
  publish: (collectionId: string, message: ProtocolEnvelope<unknown>) => void;
222
349
  isLeader: (collectionId: string) => boolean;
223
350
  ensureLeadership: (collectionId: string) => Promise<void>;
224
- requestEnsureRemoteSubset?: (collectionId: string, options: LoadSubsetOptions) => Promise<void>;
225
- requestEnsurePersistedIndex: (collectionId: string, signature: string, spec: PersistedIndexSpec) => Promise<void>;
351
+ requestEnsureRemoteSubset: (collectionId: string, options: LoadSubsetOptions) => Promise<void>;
352
+ requestReleaseRemoteSubset: (collectionId: string, options: LoadSubsetOptions) => Promise<void>;
353
+ registerRemoteSubsetOwner: (collectionId: string, owner: RemoteSubsetOwner) => () => void;
354
+ /**
355
+ * Requests leader-side index creation. The scoped adapter is leader-local
356
+ * and is never serialized to a follower. `localEnsureCompleted` lets the
357
+ * built-in leader avoid repeating successful local work.
358
+ */
359
+ requestEnsurePersistedIndex: (collectionId: string, signature: string, spec: PersistedIndexSpec, scopedAdapter?: HydrationPersistenceAdapter, localEnsureCompleted?: boolean) => Promise<void>;
226
360
  requestApplyLocalMutations?: (collectionId: string, mutations: Array<PersistedMutationEnvelope>) => Promise<ApplyLocalMutationsResponse>;
227
- pullSince?: (collectionId: string, fromRowVersion: number) => Promise<PullSinceResponse>;
361
+ requestApplyCommittedTx: (collectionId: string, tx: PersistedTx, scopedAdapter?: HydrationPersistenceAdapter) => Promise<ApplyCommittedTxResponse>;
362
+ /** The scoped adapter is leader-local and is never serialized to a follower. */
363
+ pullSince?: (collectionId: string, fromRowVersion: number, scopedAdapter?: HydrationPersistenceAdapter) => Promise<PullSinceResponse>;
228
364
  }
229
365
  export interface PersistedCollectionPersistence {
230
366
  adapter: PersistenceAdapter;
@@ -248,16 +384,26 @@ export interface PersistedCollectionUtils extends UtilsRecord {
248
384
  mutations: Array<PendingMutation<Record<string, unknown>>>;
249
385
  }) => Promise<void> | void;
250
386
  getLeadershipState?: () => PersistedCollectionLeadershipState;
387
+ /** Hydrate once without acquiring a new ongoing subset lease. */
251
388
  forceReloadSubset?: (options: LoadSubsetOptions) => Promise<void> | void;
252
389
  }
390
+ export type PersistedInitialRenderOptions = {
391
+ strategy: `network-first`;
392
+ /** Time to prefer network before restored rows may render; defaults to 3s. */
393
+ networkTimeoutMs?: number;
394
+ };
253
395
  export type PersistedSyncWrappedOptions<T extends object, TKey extends string | number, TSchema extends StandardSchemaV1 = never, TUtils extends UtilsRecord = UtilsRecord> = CollectionConfig<T, TKey, TSchema, TUtils> & {
254
396
  sync: SyncConfig<T, TKey>;
255
397
  persistence: PersistedCollectionPersistence;
256
398
  schemaVersion?: number;
399
+ /** Opt into network-first rendering with a persisted fallback. */
400
+ initialRender?: PersistedInitialRenderOptions;
257
401
  };
258
402
  export type PersistedLocalOnlyOptions<T extends object, TKey extends string | number, TSchema extends StandardSchemaV1 = never, TUtils extends UtilsRecord = UtilsRecord> = Omit<CollectionConfig<T, TKey, TSchema, TUtils>, `sync`> & {
259
403
  persistence: PersistedCollectionPersistence;
260
404
  schemaVersion?: number;
405
+ /** Opt into network-first rendering with a persisted fallback. */
406
+ initialRender?: PersistedInitialRenderOptions;
261
407
  };
262
408
  type PersistedSyncOptionsResult<T extends object, TKey extends string | number, TSchema extends StandardSchemaV1, TUtils extends UtilsRecord> = CollectionConfig<T, TKey, TSchema, TUtils> & {
263
409
  persistence: PersistedResolvedPersistence;
@@ -273,21 +419,41 @@ type PersistedLocalOnlyOptionsResult<T extends object, TKey extends string | num
273
419
  */
274
420
  export declare class SingleProcessCoordinator implements PersistedCollectionCoordinator {
275
421
  private readonly nodeId;
422
+ private readonly collectionAdapters;
423
+ private readonly remoteSubsetOwners;
424
+ private readonly remoteSubsetAcquisitions;
276
425
  constructor(nodeId?: string);
277
426
  getNodeId(): string;
278
427
  subscribe(): () => void;
279
428
  publish(): void;
280
429
  isLeader(): boolean;
281
430
  ensureLeadership(): Promise<void>;
282
- requestEnsureRemoteSubset(): Promise<void>;
431
+ requestEnsureRemoteSubset(collectionId: string, options: LoadSubsetOptions): Promise<void>;
432
+ requestReleaseRemoteSubset(collectionId: string, options: LoadSubsetOptions): Promise<void>;
433
+ registerRemoteSubsetOwner(collectionId: string, owner: RemoteSubsetOwner): () => void;
283
434
  requestEnsurePersistedIndex(): Promise<void>;
435
+ setAdapterForCollection(collectionId: string, adapter: PersistenceAdapter): void;
436
+ requestApplyCommittedTx(collectionId: string, tx: PersistedTx): Promise<ApplyCommittedTxResponse>;
284
437
  pullSince(): Promise<PullSinceResponse>;
285
438
  }
286
439
  export declare function validatePersistedCollectionCoordinator(coordinator: PersistedCollectionCoordinator): void;
287
440
  export type PersistedCollectionMode = `sync-present` | `sync-absent`;
288
- export declare function persistedCollectionOptions<T extends object, TKey extends string | number, TSchema extends StandardSchemaV1 = never, TUtils extends UtilsRecord = UtilsRecord>(options: PersistedSyncWrappedOptions<T, TKey, TSchema, TUtils>): PersistedSyncOptionsResult<T, TKey, TSchema, TUtils>;
289
- export declare function persistedCollectionOptions<T extends object, TKey extends string | number, TSchema extends StandardSchemaV1 = never, TUtils extends UtilsRecord = UtilsRecord>(options: PersistedLocalOnlyOptions<T, TKey, TSchema, TUtils>): PersistedLocalOnlyOptionsResult<T, TKey, TSchema, TUtils>;
441
+ export declare function persistedCollectionOptions<TSchema extends StandardSchemaV1, TKey extends string | number, TUtils extends UtilsRecord = UtilsRecord>(options: PersistedSyncWrappedOptions<InferSchemaOutput<TSchema>, TKey, TSchema, TUtils> & {
442
+ schema: TSchema;
443
+ }): PersistedSyncOptionsResult<InferSchemaOutput<TSchema>, TKey, TSchema, TUtils> & {
444
+ schema: TSchema;
445
+ };
446
+ export declare function persistedCollectionOptions<TSchema extends StandardSchemaV1, TKey extends string | number, TUtils extends UtilsRecord = UtilsRecord>(options: PersistedLocalOnlyOptions<InferSchemaOutput<TSchema>, TKey, TSchema, TUtils> & {
447
+ schema: TSchema;
448
+ }): PersistedLocalOnlyOptionsResult<InferSchemaOutput<TSchema>, TKey, TSchema, TUtils> & {
449
+ schema: TSchema;
450
+ };
451
+ export declare function persistedCollectionOptions<T extends object, TKey extends string | number, TSchema extends StandardSchemaV1 = never, TUtils extends UtilsRecord = UtilsRecord>(options: PersistedSyncWrappedOptions<T, TKey, TSchema, TUtils> & {
452
+ schema?: never;
453
+ }): PersistedSyncOptionsResult<T, TKey, TSchema, TUtils>;
454
+ export declare function persistedCollectionOptions<T extends object, TKey extends string | number, TSchema extends StandardSchemaV1 = never, TUtils extends UtilsRecord = UtilsRecord>(options: PersistedLocalOnlyOptions<T, TKey, TSchema, TUtils> & {
455
+ schema?: never;
456
+ }): PersistedLocalOnlyOptionsResult<T, TKey, TSchema, TUtils>;
290
457
  export declare function encodePersistedStorageKey(key: string | number): string;
291
458
  export declare function decodePersistedStorageKey(encoded: string): string | number;
292
459
  export declare function createPersistedTableName(collectionId: string, prefix?: `c` | `t`): string;
293
- export {};