@tanstack/db-sqlite-persistence-core 0.2.23 → 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 +1782 -394
  12. package/dist/cjs/persisted.cjs.map +1 -1
  13. package/dist/cjs/persisted.d.cts +176 -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 +176 -11
  36. package/dist/esm/persisted.js +1784 -396
  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 +2617 -442
  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;
@@ -251,14 +387,23 @@ export interface PersistedCollectionUtils extends UtilsRecord {
251
387
  /** Hydrate once without acquiring a new ongoing subset lease. */
252
388
  forceReloadSubset?: (options: LoadSubsetOptions) => Promise<void> | void;
253
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
+ };
254
395
  export type PersistedSyncWrappedOptions<T extends object, TKey extends string | number, TSchema extends StandardSchemaV1 = never, TUtils extends UtilsRecord = UtilsRecord> = CollectionConfig<T, TKey, TSchema, TUtils> & {
255
396
  sync: SyncConfig<T, TKey>;
256
397
  persistence: PersistedCollectionPersistence;
257
398
  schemaVersion?: number;
399
+ /** Opt into network-first rendering with a persisted fallback. */
400
+ initialRender?: PersistedInitialRenderOptions;
258
401
  };
259
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`> & {
260
403
  persistence: PersistedCollectionPersistence;
261
404
  schemaVersion?: number;
405
+ /** Opt into network-first rendering with a persisted fallback. */
406
+ initialRender?: PersistedInitialRenderOptions;
262
407
  };
263
408
  type PersistedSyncOptionsResult<T extends object, TKey extends string | number, TSchema extends StandardSchemaV1, TUtils extends UtilsRecord> = CollectionConfig<T, TKey, TSchema, TUtils> & {
264
409
  persistence: PersistedResolvedPersistence;
@@ -274,21 +419,41 @@ type PersistedLocalOnlyOptionsResult<T extends object, TKey extends string | num
274
419
  */
275
420
  export declare class SingleProcessCoordinator implements PersistedCollectionCoordinator {
276
421
  private readonly nodeId;
422
+ private readonly collectionAdapters;
423
+ private readonly remoteSubsetOwners;
424
+ private readonly remoteSubsetAcquisitions;
277
425
  constructor(nodeId?: string);
278
426
  getNodeId(): string;
279
427
  subscribe(): () => void;
280
428
  publish(): void;
281
429
  isLeader(): boolean;
282
430
  ensureLeadership(): Promise<void>;
283
- 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;
284
434
  requestEnsurePersistedIndex(): Promise<void>;
435
+ setAdapterForCollection(collectionId: string, adapter: PersistenceAdapter): void;
436
+ requestApplyCommittedTx(collectionId: string, tx: PersistedTx): Promise<ApplyCommittedTxResponse>;
285
437
  pullSince(): Promise<PullSinceResponse>;
286
438
  }
287
439
  export declare function validatePersistedCollectionCoordinator(coordinator: PersistedCollectionCoordinator): void;
288
440
  export type PersistedCollectionMode = `sync-present` | `sync-absent`;
289
- 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>;
290
- 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>;
291
457
  export declare function encodePersistedStorageKey(key: string | number): string;
292
458
  export declare function decodePersistedStorageKey(encoded: string): string | number;
293
459
  export declare function createPersistedTableName(collectionId: string, prefix?: `c` | `t`): string;
294
- export {};