@trestleinc/replicate 1.1.1 → 1.1.2-preview.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 (91) hide show
  1. package/README.md +395 -146
  2. package/dist/client/index.d.ts +311 -19
  3. package/dist/client/index.js +4027 -0
  4. package/dist/component/_generated/api.d.ts +13 -17
  5. package/dist/component/_generated/api.js +24 -4
  6. package/dist/component/_generated/component.d.ts +79 -77
  7. package/dist/component/_generated/component.js +1 -0
  8. package/dist/component/_generated/dataModel.d.ts +12 -15
  9. package/dist/component/_generated/dataModel.js +1 -0
  10. package/dist/component/_generated/server.d.ts +19 -22
  11. package/dist/component/_generated/server.js +65 -1
  12. package/dist/component/_virtual/rolldown_runtime.js +18 -0
  13. package/dist/component/convex.config.d.ts +6 -2
  14. package/dist/component/convex.config.js +7 -3
  15. package/dist/component/logger.d.ts +10 -6
  16. package/dist/component/logger.js +25 -28
  17. package/dist/component/public.d.ts +70 -61
  18. package/dist/component/public.js +311 -295
  19. package/dist/component/schema.d.ts +53 -45
  20. package/dist/component/schema.js +26 -32
  21. package/dist/component/shared/types.d.ts +9 -0
  22. package/dist/component/shared/types.js +15 -0
  23. package/dist/server/index.d.ts +134 -13
  24. package/dist/server/index.js +368 -0
  25. package/dist/shared/index.d.ts +27 -3
  26. package/dist/shared/index.js +1 -2
  27. package/package.json +34 -29
  28. package/src/client/collection.ts +339 -306
  29. package/src/client/errors.ts +9 -9
  30. package/src/client/index.ts +13 -32
  31. package/src/client/logger.ts +2 -2
  32. package/src/client/merge.ts +37 -34
  33. package/src/client/persistence/custom.ts +84 -0
  34. package/src/client/persistence/index.ts +9 -46
  35. package/src/client/persistence/indexeddb.ts +111 -84
  36. package/src/client/persistence/memory.ts +3 -3
  37. package/src/client/persistence/sqlite/browser.ts +168 -0
  38. package/src/client/persistence/sqlite/native.ts +29 -0
  39. package/src/client/persistence/sqlite/schema.ts +124 -0
  40. package/src/client/persistence/types.ts +32 -28
  41. package/src/client/prose-schema.ts +55 -0
  42. package/src/client/prose.ts +28 -25
  43. package/src/client/replicate.ts +5 -5
  44. package/src/client/services/cursor.ts +109 -0
  45. package/src/component/_generated/component.ts +31 -29
  46. package/src/component/convex.config.ts +2 -2
  47. package/src/component/logger.ts +7 -7
  48. package/src/component/public.ts +225 -237
  49. package/src/component/schema.ts +18 -15
  50. package/src/server/builder.ts +20 -7
  51. package/src/server/index.ts +3 -5
  52. package/src/server/schema.ts +5 -5
  53. package/src/server/storage.ts +113 -59
  54. package/src/shared/index.ts +5 -5
  55. package/src/shared/types.ts +51 -14
  56. package/dist/client/collection.d.ts +0 -96
  57. package/dist/client/errors.d.ts +0 -59
  58. package/dist/client/logger.d.ts +0 -2
  59. package/dist/client/merge.d.ts +0 -77
  60. package/dist/client/persistence/adapters/index.d.ts +0 -8
  61. package/dist/client/persistence/adapters/opsqlite.d.ts +0 -46
  62. package/dist/client/persistence/adapters/sqljs.d.ts +0 -83
  63. package/dist/client/persistence/index.d.ts +0 -49
  64. package/dist/client/persistence/indexeddb.d.ts +0 -17
  65. package/dist/client/persistence/memory.d.ts +0 -16
  66. package/dist/client/persistence/sqlite-browser.d.ts +0 -51
  67. package/dist/client/persistence/sqlite-level.d.ts +0 -63
  68. package/dist/client/persistence/sqlite-rn.d.ts +0 -36
  69. package/dist/client/persistence/sqlite.d.ts +0 -47
  70. package/dist/client/persistence/types.d.ts +0 -42
  71. package/dist/client/prose.d.ts +0 -56
  72. package/dist/client/replicate.d.ts +0 -40
  73. package/dist/client/services/checkpoint.d.ts +0 -18
  74. package/dist/client/services/reconciliation.d.ts +0 -24
  75. package/dist/index.js +0 -1618
  76. package/dist/server/builder.d.ts +0 -94
  77. package/dist/server/schema.d.ts +0 -27
  78. package/dist/server/storage.d.ts +0 -80
  79. package/dist/server.js +0 -281
  80. package/dist/shared/types.d.ts +0 -50
  81. package/dist/shared/types.js +0 -6
  82. package/dist/shared.js +0 -6
  83. package/src/client/persistence/adapters/index.ts +0 -8
  84. package/src/client/persistence/adapters/opsqlite.ts +0 -54
  85. package/src/client/persistence/adapters/sqljs.ts +0 -128
  86. package/src/client/persistence/sqlite-browser.ts +0 -107
  87. package/src/client/persistence/sqlite-level.ts +0 -407
  88. package/src/client/persistence/sqlite-rn.ts +0 -44
  89. package/src/client/persistence/sqlite.ts +0 -160
  90. package/src/client/services/checkpoint.ts +0 -86
  91. package/src/client/services/reconciliation.ts +0 -108
@@ -1,37 +1,37 @@
1
- import { Data } from 'effect';
1
+ import { Data } from "effect";
2
2
 
3
- export class NetworkError extends Data.TaggedError('NetworkError')<{
3
+ export class NetworkError extends Data.TaggedError("NetworkError")<{
4
4
  readonly cause: unknown;
5
5
  readonly retryable: true;
6
6
  readonly operation: string;
7
7
  }> {}
8
8
 
9
- export class IDBError extends Data.TaggedError('IDBError')<{
10
- readonly operation: 'get' | 'set' | 'delete' | 'clear';
9
+ export class IDBError extends Data.TaggedError("IDBError")<{
10
+ readonly operation: "get" | "set" | "delete" | "clear";
11
11
  readonly store?: string;
12
12
  readonly key?: string;
13
13
  readonly cause: unknown;
14
14
  }> {}
15
15
 
16
- export class IDBWriteError extends Data.TaggedError('IDBWriteError')<{
16
+ export class IDBWriteError extends Data.TaggedError("IDBWriteError")<{
17
17
  readonly key: string;
18
18
  readonly value: unknown;
19
19
  readonly cause: unknown;
20
20
  }> {}
21
21
 
22
- export class ReconciliationError extends Data.TaggedError('ReconciliationError')<{
22
+ export class ReconciliationError extends Data.TaggedError("ReconciliationError")<{
23
23
  readonly collection: string;
24
24
  readonly reason: string;
25
25
  readonly cause?: unknown;
26
26
  }> {}
27
27
 
28
- export class ProseError extends Data.TaggedError('ProseError')<{
28
+ export class ProseError extends Data.TaggedError("ProseError")<{
29
29
  readonly documentId: string;
30
30
  readonly field: string;
31
31
  readonly collection: string;
32
32
  }> {}
33
33
 
34
- export class CollectionNotReadyError extends Data.TaggedError('CollectionNotReadyError')<{
34
+ export class CollectionNotReadyError extends Data.TaggedError("CollectionNotReadyError")<{
35
35
  readonly collection: string;
36
36
  readonly reason: string;
37
37
  }> {}
@@ -40,6 +40,6 @@ export class CollectionNotReadyError extends Data.TaggedError('CollectionNotRead
40
40
  export class NonRetriableError extends Error {
41
41
  constructor(message: string) {
42
42
  super(message);
43
- this.name = 'NonRetriableError';
43
+ this.name = "NonRetriableError";
44
44
  }
45
45
  }
@@ -1,8 +1,9 @@
1
1
  export {
2
- convexCollectionOptions,
3
- type ConvexCollection,
2
+ collection,
4
3
  type EditorBinding,
5
- } from '$/client/collection.js';
4
+ type ConvexCollection,
5
+ type Materialized,
6
+ } from "$/client/collection";
6
7
 
7
8
  import {
8
9
  NetworkError,
@@ -12,7 +13,7 @@ import {
12
13
  ProseError,
13
14
  CollectionNotReadyError,
14
15
  NonRetriableError,
15
- } from '$/client/errors.js';
16
+ } from "$/client/errors";
16
17
 
17
18
  export const errors = {
18
19
  Network: NetworkError,
@@ -24,34 +25,14 @@ export const errors = {
24
25
  NonRetriable: NonRetriableError,
25
26
  } as const;
26
27
 
27
- import { extract } from '$/client/merge.js';
28
-
29
- export const prose = {
30
- extract,
31
- } as const;
32
-
33
- export {
34
- persistence,
35
- type Persistence,
36
- type PersistenceProvider,
37
- type KeyValueStore,
38
- type SqlitePersistenceOptions,
39
- type SqliteAdapter,
40
- type SqlJsStatic,
41
- } from '$/client/persistence/index.js';
28
+ import type { ProseValue } from "$/shared/types";
29
+ import { extract } from "$/client/merge";
30
+ import { prose as proseSchema } from "$/client/prose-schema";
42
31
 
43
- import {
44
- SqlJsAdapter,
45
- OPSqliteAdapter,
46
- } from '$/client/persistence/adapters/index.js';
32
+ function empty(): ProseValue {
33
+ return { type: "doc", content: [] } as unknown as ProseValue;
34
+ }
47
35
 
48
- export const adapters = {
49
- sqljs: SqlJsAdapter,
50
- opsqlite: OPSqliteAdapter,
51
- } as const;
36
+ export const prose = Object.assign(proseSchema, { extract, empty });
52
37
 
53
- export type {
54
- SqlJsDatabase,
55
- SqlJsAdapterOptions,
56
- OPSQLiteDatabase,
57
- } from '$/client/persistence/adapters/index.js';
38
+ export { persistence, type StorageAdapter, type Persistence } from "$/client/persistence/index";
@@ -1,5 +1,5 @@
1
- import { type Logger, getLogger as getLogTapeLogger } from '@logtape/logtape';
1
+ import { type Logger, getLogger as getLogTapeLogger } from "@logtape/logtape";
2
2
 
3
3
  export function getLogger(category: string[]): Logger {
4
- return getLogTapeLogger(['replicate', ...category]);
4
+ return getLogTapeLogger(["replicate", ...category]);
5
5
  }
@@ -4,11 +4,11 @@
4
4
  * Provides document creation, state encoding, and merge operations.
5
5
  */
6
6
 
7
- import * as Y from 'yjs';
8
- import { getLogger } from '$/client/logger.js';
9
- import type { KeyValueStore } from '$/client/persistence/types.js';
7
+ import * as Y from "yjs";
8
+ import { getLogger } from "$/client/logger";
9
+ import type { KeyValueStore } from "$/client/persistence/types";
10
10
 
11
- const logger = getLogger(['replicate', 'merge']);
11
+ const logger = getLogger(["replicate", "merge"]);
12
12
 
13
13
  /**
14
14
  * Create a Yjs document with a persistent clientId.
@@ -24,7 +24,7 @@ export async function createYjsDocument(collection: string, kv: KeyValueStore):
24
24
  if (!clientId) {
25
25
  clientId = Math.floor(Math.random() * 2147483647);
26
26
  await kv.set(clientIdKey, clientId);
27
- logger.info('Generated new Yjs clientID', { collection, clientId });
27
+ logger.info("Generated new Yjs clientID", { collection, clientId });
28
28
  }
29
29
 
30
30
  const ydoc = new Y.Doc({
@@ -32,7 +32,7 @@ export async function createYjsDocument(collection: string, kv: KeyValueStore):
32
32
  clientID: clientId,
33
33
  } as any);
34
34
 
35
- logger.info('Created Yjs document', { collection, clientId });
35
+ logger.info("Created Yjs document", { collection, clientId });
36
36
  return ydoc;
37
37
  }
38
38
 
@@ -65,7 +65,7 @@ export function yjsTransact<A>(doc: Y.Doc, fn: () => A, origin?: string): A {
65
65
  export function transactWithDelta<A>(
66
66
  doc: Y.Doc,
67
67
  fn: () => A,
68
- origin?: string
68
+ origin?: string,
69
69
  ): { result: A; delta: Uint8Array } {
70
70
  const beforeVector = Y.encodeStateVector(doc);
71
71
  const result = doc.transact(fn, origin);
@@ -88,10 +88,10 @@ export function transactWithDelta<A>(
88
88
  * and have these properties regardless of which module instance created them.
89
89
  */
90
90
  function isYjsAbstractType(value: unknown): boolean {
91
- if (value === null || typeof value !== 'object') return false;
91
+ if (value === null || typeof value !== "object") return false;
92
92
  const v = value as Record<string, unknown>;
93
93
  // AbstractType has: doc (Doc|null), _map (Map), _eH (event handler)
94
- return '_map' in v && '_eH' in v && 'doc' in v;
94
+ return "_map" in v && "_eH" in v && "doc" in v;
95
95
  }
96
96
 
97
97
  /**
@@ -101,7 +101,7 @@ function isYjsAbstractType(value: unknown): boolean {
101
101
  function isYMap(value: unknown): boolean {
102
102
  if (!isYjsAbstractType(value)) return false;
103
103
  const v = value as Record<string, unknown>;
104
- return typeof v.keys === 'function' && typeof v.get === 'function';
104
+ return typeof v.keys === "function" && typeof v.get === "function";
105
105
  }
106
106
 
107
107
  /**
@@ -110,7 +110,7 @@ function isYMap(value: unknown): boolean {
110
110
  function isYArray(value: unknown): boolean {
111
111
  if (!isYjsAbstractType(value)) return false;
112
112
  const v = value as Record<string, unknown>;
113
- return typeof v.toArray === 'function' && typeof v.get !== 'function';
113
+ return typeof v.toArray === "function" && typeof v.get !== "function";
114
114
  }
115
115
 
116
116
  /**
@@ -121,7 +121,7 @@ function isYXmlFragment(value: unknown): value is Y.XmlFragment {
121
121
  if (!isYjsAbstractType(value)) return false;
122
122
  const v = value as Record<string, unknown>;
123
123
  // XmlFragment has toArray() but NOT keys() - keys() is unique to Y.Map
124
- return typeof v.toArray === 'function' && typeof v.keys !== 'function';
124
+ return typeof v.toArray === "function" && typeof v.keys !== "function";
125
125
  }
126
126
 
127
127
  /**
@@ -131,7 +131,7 @@ function isYXmlFragment(value: unknown): value is Y.XmlFragment {
131
131
  function serialize(value: unknown): unknown {
132
132
  // Primitives pass through
133
133
  if (value === null || value === undefined) return value;
134
- if (typeof value !== 'object') return value;
134
+ if (typeof value !== "object") return value;
135
135
 
136
136
  // Check for XmlFragment first (converts to ProseMirror JSON)
137
137
  if (isYXmlFragment(value)) {
@@ -188,7 +188,7 @@ export function extractItem<T>(ymap: Y.Map<unknown>, key: string): T | null {
188
188
  return null;
189
189
  }
190
190
 
191
- import type { XmlFragmentJSON, XmlNodeJSON } from '$/shared/types.js';
191
+ import type { XmlFragmentJSON, XmlNodeJSON } from "$/shared/types";
192
192
 
193
193
  /**
194
194
  * Check if a value looks like ProseMirror/BlockNote JSON document.
@@ -196,10 +196,10 @@ import type { XmlFragmentJSON, XmlNodeJSON } from '$/shared/types.js';
196
196
  */
197
197
  export function isDoc(value: unknown): value is XmlFragmentJSON {
198
198
  return (
199
- typeof value === 'object' &&
200
- value !== null &&
201
- 'type' in value &&
202
- (value as { type: unknown }).type === 'doc'
199
+ typeof value === "object"
200
+ && value !== null
201
+ && "type" in value
202
+ && (value as { type: unknown }).type === "doc"
203
203
  );
204
204
  }
205
205
 
@@ -212,11 +212,12 @@ export function fragmentToJSON(fragment: Y.XmlFragment): XmlFragmentJSON {
212
212
  for (const child of fragment.toArray()) {
213
213
  if (child instanceof Y.XmlElement) {
214
214
  content.push(xmlElementToJSON(child));
215
- } else if (child instanceof Y.XmlText) {
215
+ }
216
+ else if (child instanceof Y.XmlText) {
216
217
  const textContent = xmlTextToJSON(child);
217
218
  if (textContent.length > 0) {
218
219
  content.push({
219
- type: 'paragraph',
220
+ type: "paragraph",
220
221
  content: textContent,
221
222
  });
222
223
  }
@@ -224,8 +225,8 @@ export function fragmentToJSON(fragment: Y.XmlFragment): XmlFragmentJSON {
224
225
  }
225
226
 
226
227
  return {
227
- type: 'doc',
228
- content: content.length > 0 ? content : [{ type: 'paragraph' }],
228
+ type: "doc",
229
+ content: content.length > 0 ? content : [{ type: "paragraph" }],
229
230
  };
230
231
  }
231
232
 
@@ -243,7 +244,8 @@ function xmlElementToJSON(element: Y.XmlElement): XmlNodeJSON {
243
244
  for (const child of element.toArray()) {
244
245
  if (child instanceof Y.XmlElement) {
245
246
  content.push(xmlElementToJSON(child));
246
- } else if (child instanceof Y.XmlText) {
247
+ }
248
+ else if (child instanceof Y.XmlText) {
247
249
  content.push(...xmlTextToJSON(child));
248
250
  }
249
251
  }
@@ -260,16 +262,16 @@ function xmlTextToJSON(text: Y.XmlText): XmlNodeJSON[] {
260
262
  const delta = text.toDelta();
261
263
 
262
264
  for (const op of delta) {
263
- if (typeof op.insert === 'string') {
265
+ if (typeof op.insert === "string") {
264
266
  const node: XmlNodeJSON = {
265
- type: 'text',
267
+ type: "text",
266
268
  text: op.insert,
267
269
  };
268
270
 
269
271
  if (op.attributes && Object.keys(op.attributes).length > 0) {
270
272
  node.marks = Object.entries(op.attributes).map(([type, attrs]) => ({
271
273
  type,
272
- attrs: typeof attrs === 'object' ? (attrs as Record<string, unknown>) : undefined,
274
+ attrs: typeof attrs === "object" ? (attrs as Record<string, unknown>) : undefined,
273
275
  }));
274
276
  }
275
277
 
@@ -296,23 +298,23 @@ export function fragmentFromJSON(fragment: Y.XmlFragment, json: XmlFragmentJSON)
296
298
  * Handles various content structures defensively for search and display.
297
299
  */
298
300
  export function extract(content: unknown): string {
299
- if (!content || typeof content !== 'object') return '';
301
+ if (!content || typeof content !== "object") return "";
300
302
 
301
303
  const doc = content as { content?: unknown; type?: string };
302
304
 
303
305
  // Handle XmlFragmentJSON format - content must be an array
304
- if (!doc.content || !Array.isArray(doc.content)) return '';
306
+ if (!doc.content || !Array.isArray(doc.content)) return "";
305
307
 
306
308
  return doc.content
307
309
  .map((block: { content?: unknown }) => {
308
- if (!block.content || !Array.isArray(block.content)) return '';
309
- return block.content.map((node: { text?: string }) => node.text || '').join('');
310
+ if (!block.content || !Array.isArray(block.content)) return "";
311
+ return block.content.map((node: { text?: string }) => node.text || "").join("");
310
312
  })
311
- .join(' ');
313
+ .join(" ");
312
314
  }
313
315
 
314
316
  function appendNodeToFragment(parent: Y.XmlFragment | Y.XmlElement, node: XmlNodeJSON): void {
315
- if (node.type === 'text') {
317
+ if (node.type === "text") {
316
318
  const text = new Y.XmlText();
317
319
  if (node.text) {
318
320
  const attrs: Record<string, unknown> = {};
@@ -324,7 +326,8 @@ function appendNodeToFragment(parent: Y.XmlFragment | Y.XmlElement, node: XmlNod
324
326
  text.insert(0, node.text, Object.keys(attrs).length > 0 ? attrs : undefined);
325
327
  }
326
328
  parent.insert(parent.length, [text]);
327
- } else {
329
+ }
330
+ else {
328
331
  const element = new Y.XmlElement(node.type);
329
332
 
330
333
  if (node.attrs) {
@@ -358,7 +361,7 @@ export function serializeYMapValue(value: unknown): unknown {
358
361
  export function getFragmentFromYMap(
359
362
  ymap: Y.Map<unknown>,
360
363
  documentId: string,
361
- field: string
364
+ field: string,
362
365
  ): Y.XmlFragment | null {
363
366
  const doc = ymap.get(documentId);
364
367
  if (!isYMap(doc)) {
@@ -0,0 +1,84 @@
1
+ import * as Y from "yjs";
2
+ import type { StorageAdapter, Persistence, PersistenceProvider, KeyValueStore } from "./types.js";
3
+
4
+ const SNAPSHOT_PREFIX = "snapshot:";
5
+ const UPDATE_PREFIX = "update:";
6
+ const META_PREFIX = "meta:";
7
+
8
+ class AdapterKeyValueStore implements KeyValueStore {
9
+ constructor(private adapter: StorageAdapter) {}
10
+
11
+ async get<T>(key: string): Promise<T | undefined> {
12
+ const data = await this.adapter.get(`${META_PREFIX}${key}`);
13
+ if (!data) return undefined;
14
+ return JSON.parse(new TextDecoder().decode(data)) as T;
15
+ }
16
+
17
+ async set<T>(key: string, value: T): Promise<void> {
18
+ await this.adapter.set(`${META_PREFIX}${key}`, new TextEncoder().encode(JSON.stringify(value)));
19
+ }
20
+
21
+ async del(key: string): Promise<void> {
22
+ await this.adapter.delete(`${META_PREFIX}${key}`);
23
+ }
24
+ }
25
+
26
+ class AdapterPersistenceProvider implements PersistenceProvider {
27
+ private updateHandler: (update: Uint8Array, origin: unknown) => void;
28
+ private updateCounter = 0;
29
+ readonly whenSynced: Promise<void>;
30
+
31
+ constructor(
32
+ private adapter: StorageAdapter,
33
+ private collection: string,
34
+ private ydoc: Y.Doc,
35
+ ) {
36
+ this.whenSynced = this.loadState();
37
+
38
+ this.updateHandler = (update: Uint8Array, origin: unknown) => {
39
+ if (origin !== "custom") {
40
+ void this.saveUpdate(update);
41
+ }
42
+ };
43
+ this.ydoc.on("update", this.updateHandler);
44
+ }
45
+
46
+ private async loadState(): Promise<void> {
47
+ const snapshotData = await this.adapter.get(`${SNAPSHOT_PREFIX}${this.collection}`);
48
+ if (snapshotData) {
49
+ Y.applyUpdate(this.ydoc, snapshotData, "custom");
50
+ }
51
+
52
+ const updateKeys = await this.adapter.keys(`${UPDATE_PREFIX}${this.collection}:`);
53
+ const sortedKeys = updateKeys.sort();
54
+
55
+ for (const key of sortedKeys) {
56
+ const updateData = await this.adapter.get(key);
57
+ if (updateData) {
58
+ Y.applyUpdate(this.ydoc, updateData, "custom");
59
+ const seq = parseInt(key.split(":").pop() || "0", 10);
60
+ if (seq > this.updateCounter) {
61
+ this.updateCounter = seq;
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ private async saveUpdate(update: Uint8Array): Promise<void> {
68
+ this.updateCounter++;
69
+ const paddedCounter = String(this.updateCounter).padStart(10, "0");
70
+ await this.adapter.set(`${UPDATE_PREFIX}${this.collection}:${paddedCounter}`, update);
71
+ }
72
+
73
+ destroy(): void {
74
+ this.ydoc.off("update", this.updateHandler);
75
+ }
76
+ }
77
+
78
+ export function createCustomPersistence(adapter: StorageAdapter): Persistence {
79
+ return {
80
+ createDocPersistence: (collection: string, ydoc: Y.Doc) =>
81
+ new AdapterPersistenceProvider(adapter, collection, ydoc),
82
+ kv: new AdapterKeyValueStore(adapter),
83
+ };
84
+ }
@@ -1,54 +1,17 @@
1
- /**
2
- * Persistence layer exports.
3
- *
4
- * Provides swappable storage backends for Y.Doc and key-value data.
5
- */
6
- export type { Persistence, PersistenceProvider, KeyValueStore } from './types.js';
7
- export type { SqlitePersistenceOptions } from './sqlite.js';
8
- export type { SqlJsStatic } from './sqlite-browser.js';
9
- export type { SqliteAdapter } from './sqlite-level.js';
1
+ export type { StorageAdapter, Persistence } from "./types.js";
10
2
 
11
- // Internal imports for the persistence object
12
- import { indexeddbPersistence } from './indexeddb.js';
13
- import { memoryPersistence } from './memory.js';
14
- import { sqlitePersistence } from './sqlite.js';
15
- import { createBrowserSqlitePersistence } from './sqlite-browser.js';
16
- import { createReactNativeSqlitePersistence } from './sqlite-rn.js';
3
+ import { memoryPersistence } from "./memory.js";
4
+ import { createBrowserSqlitePersistence } from "./sqlite/browser.js";
5
+ import { createNativeSqlitePersistence } from "./sqlite/native.js";
6
+ import { createIndexedDBPersistence } from "./indexeddb.js";
7
+ import { createCustomPersistence } from "./custom.js";
17
8
 
18
- /**
19
- * Persistence API - nested object pattern for ergonomic access.
20
- *
21
- * @example
22
- * ```typescript
23
- * import { persistence } from '@trestleinc/replicate/client';
24
- *
25
- * // Browser SQLite (recommended for web)
26
- * const p = await persistence.sqlite.browser(SQL, 'myapp');
27
- *
28
- * // React Native SQLite
29
- * const p = await persistence.sqlite.native(db, 'myapp');
30
- *
31
- * // IndexedDB fallback
32
- * const p = persistence.indexeddb('myapp');
33
- *
34
- * // In-memory (testing)
35
- * const p = persistence.memory();
36
- * ```
37
- */
38
9
  export const persistence = {
39
- /** IndexedDB-backed persistence (browser) */
40
- indexeddb: indexeddbPersistence,
41
-
42
- /** In-memory persistence (testing/ephemeral) */
43
10
  memory: memoryPersistence,
44
-
45
- /** SQLite persistence variants */
46
11
  sqlite: {
47
- /** Browser SQLite with OPFS (sql.js) */
48
12
  browser: createBrowserSqlitePersistence,
49
- /** React Native SQLite (op-sqlite) */
50
- native: createReactNativeSqlitePersistence,
51
- /** Custom SQLite adapter */
52
- create: sqlitePersistence,
13
+ native: createNativeSqlitePersistence,
53
14
  },
15
+ indexeddb: createIndexedDBPersistence,
16
+ custom: createCustomPersistence,
54
17
  } as const;