@trestleinc/replicate 0.1.0 → 1.1.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.
- package/README.md +356 -420
- package/dist/client/collection.d.ts +78 -76
- package/dist/client/errors.d.ts +59 -0
- package/dist/client/index.d.ts +22 -18
- package/dist/client/logger.d.ts +0 -1
- package/dist/client/merge.d.ts +77 -0
- package/dist/client/persistence/adapters/index.d.ts +8 -0
- package/dist/client/persistence/adapters/opsqlite.d.ts +46 -0
- package/dist/client/persistence/adapters/sqljs.d.ts +83 -0
- package/dist/client/persistence/index.d.ts +49 -0
- package/dist/client/persistence/indexeddb.d.ts +17 -0
- package/dist/client/persistence/memory.d.ts +16 -0
- package/dist/client/persistence/sqlite-browser.d.ts +51 -0
- package/dist/client/persistence/sqlite-level.d.ts +63 -0
- package/dist/client/persistence/sqlite-rn.d.ts +36 -0
- package/dist/client/persistence/sqlite.d.ts +47 -0
- package/dist/client/persistence/types.d.ts +42 -0
- package/dist/client/prose.d.ts +56 -0
- package/dist/client/replicate.d.ts +40 -0
- package/dist/client/services/checkpoint.d.ts +18 -0
- package/dist/client/services/reconciliation.d.ts +24 -0
- package/dist/component/_generated/api.d.ts +35 -0
- package/dist/component/_generated/api.js +3 -3
- package/dist/component/_generated/component.d.ts +89 -0
- package/dist/component/_generated/component.js +0 -0
- package/dist/component/_generated/dataModel.d.ts +45 -0
- package/dist/component/_generated/dataModel.js +0 -0
- package/{src → dist}/component/_generated/server.d.ts +9 -38
- package/dist/component/convex.config.d.ts +2 -2
- package/dist/component/convex.config.js +2 -1
- package/dist/component/logger.d.ts +8 -0
- package/dist/component/logger.js +30 -0
- package/dist/component/public.d.ts +36 -61
- package/dist/component/public.js +232 -58
- package/dist/component/schema.d.ts +32 -8
- package/dist/component/schema.js +19 -6
- package/dist/index.js +1553 -308
- package/dist/server/builder.d.ts +94 -0
- package/dist/server/index.d.ts +14 -17
- package/dist/server/schema.d.ts +17 -63
- package/dist/server/storage.d.ts +80 -0
- package/dist/server.js +268 -83
- package/dist/shared/index.d.ts +5 -0
- package/dist/shared/index.js +2 -0
- package/dist/shared/types.d.ts +50 -0
- package/dist/shared/types.js +6 -0
- package/dist/shared.js +6 -0
- package/package.json +59 -49
- package/src/client/collection.ts +877 -450
- package/src/client/errors.ts +45 -0
- package/src/client/index.ts +52 -26
- package/src/client/logger.ts +2 -28
- package/src/client/merge.ts +374 -0
- package/src/client/persistence/adapters/index.ts +8 -0
- package/src/client/persistence/adapters/opsqlite.ts +54 -0
- package/src/client/persistence/adapters/sqljs.ts +128 -0
- package/src/client/persistence/index.ts +54 -0
- package/src/client/persistence/indexeddb.ts +110 -0
- package/src/client/persistence/memory.ts +61 -0
- package/src/client/persistence/sqlite-browser.ts +107 -0
- package/src/client/persistence/sqlite-level.ts +407 -0
- package/src/client/persistence/sqlite-rn.ts +44 -0
- package/src/client/persistence/sqlite.ts +161 -0
- package/src/client/persistence/types.ts +49 -0
- package/src/client/prose.ts +369 -0
- package/src/client/replicate.ts +80 -0
- package/src/client/services/checkpoint.ts +86 -0
- package/src/client/services/reconciliation.ts +108 -0
- package/src/component/_generated/api.ts +52 -0
- package/src/component/_generated/component.ts +103 -0
- package/src/component/_generated/{dataModel.d.ts → dataModel.ts} +1 -1
- package/src/component/_generated/server.ts +161 -0
- package/src/component/convex.config.ts +3 -1
- package/src/component/logger.ts +36 -0
- package/src/component/public.ts +364 -111
- package/src/component/schema.ts +18 -5
- package/src/env.d.ts +31 -0
- package/src/server/builder.ts +85 -0
- package/src/server/index.ts +9 -24
- package/src/server/schema.ts +20 -76
- package/src/server/storage.ts +313 -0
- package/src/shared/index.ts +5 -0
- package/src/shared/types.ts +52 -0
- package/LICENSE.package +0 -201
- package/dist/client/storage.d.ts +0 -143
- package/dist/server/replication.d.ts +0 -122
- package/dist/server/ssr.d.ts +0 -79
- package/dist/ssr.js +0 -19
- package/src/client/storage.ts +0 -206
- package/src/component/_generated/api.d.ts +0 -95
- package/src/component/_generated/api.js +0 -23
- package/src/component/_generated/server.js +0 -90
- package/src/server/replication.ts +0 -244
- package/src/server/ssr.ts +0 -106
|
@@ -1,94 +1,96 @@
|
|
|
1
|
+
import * as Y from 'yjs';
|
|
2
|
+
import type { Persistence } from '$/client/persistence/types.js';
|
|
1
3
|
import type { ConvexClient } from 'convex/browser';
|
|
2
4
|
import type { FunctionReference } from 'convex/server';
|
|
3
5
|
import type { CollectionConfig, Collection } from '@tanstack/db';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
import type { ProseFields } from '$/shared/types.js';
|
|
7
|
+
/** Server-rendered material data for SSR hydration */
|
|
8
|
+
export type Materialized<T> = {
|
|
9
|
+
documents: ReadonlyArray<T>;
|
|
10
|
+
checkpoint?: {
|
|
11
|
+
lastModified: number;
|
|
12
|
+
};
|
|
13
|
+
count?: number;
|
|
14
|
+
crdtBytes?: ArrayBuffer;
|
|
15
|
+
};
|
|
16
|
+
/** Configuration for creating a Convex-backed collection */
|
|
8
17
|
export interface ConvexCollectionOptionsConfig<T extends object> {
|
|
9
|
-
/** Function to extract unique key from items */
|
|
10
18
|
getKey: (item: T) => string | number;
|
|
11
|
-
|
|
12
|
-
initialData?: ReadonlyArray<T>;
|
|
13
|
-
/** Convex client instance */
|
|
19
|
+
material?: Materialized<T>;
|
|
14
20
|
convexClient: ConvexClient;
|
|
15
|
-
/** Convex API functions for this collection */
|
|
16
21
|
api: {
|
|
17
22
|
stream: FunctionReference<'query'>;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
insert: FunctionReference<'mutation'>;
|
|
24
|
+
update: FunctionReference<'mutation'>;
|
|
25
|
+
remove: FunctionReference<'mutation'>;
|
|
26
|
+
recovery: FunctionReference<'query'>;
|
|
27
|
+
material?: FunctionReference<'query'>;
|
|
28
|
+
[key: string]: any;
|
|
21
29
|
};
|
|
22
|
-
|
|
23
|
-
|
|
30
|
+
collection: string;
|
|
31
|
+
/** Fields that contain prose (rich text) content stored as Y.XmlFragment */
|
|
32
|
+
prose: Array<ProseFields<T>>;
|
|
33
|
+
/** Undo capture timeout in ms. Changes within this window merge into one undo. Default: 500 */
|
|
34
|
+
undoCaptureTimeout?: number;
|
|
35
|
+
/** Persistence provider for Y.Doc and key-value storage */
|
|
36
|
+
persistence: Persistence;
|
|
37
|
+
}
|
|
38
|
+
/** Editor binding for BlockNote/TipTap collaboration */
|
|
39
|
+
export interface EditorBinding {
|
|
40
|
+
/** The Y.XmlFragment bound to the editor */
|
|
41
|
+
readonly fragment: Y.XmlFragment;
|
|
42
|
+
/** Provider stub for BlockNote compatibility */
|
|
43
|
+
readonly provider: {
|
|
44
|
+
readonly awareness: null;
|
|
45
|
+
};
|
|
46
|
+
/** Current sync state - true if unsent changes exist */
|
|
47
|
+
readonly pending: boolean;
|
|
48
|
+
/** Subscribe to pending state changes. Returns unsubscribe function. */
|
|
49
|
+
onPendingChange(callback: (pending: boolean) => void): () => void;
|
|
50
|
+
/** Undo the last content edit */
|
|
51
|
+
undo(): void;
|
|
52
|
+
/** Redo the last undone edit */
|
|
53
|
+
redo(): void;
|
|
54
|
+
/** Check if undo is available */
|
|
55
|
+
canUndo(): boolean;
|
|
56
|
+
/** Check if redo is available */
|
|
57
|
+
canRedo(): boolean;
|
|
58
|
+
}
|
|
59
|
+
/** Utilities exposed on collection.utils */
|
|
60
|
+
interface ConvexCollectionUtils<T extends object> {
|
|
61
|
+
/**
|
|
62
|
+
* Get an editor binding for a prose field.
|
|
63
|
+
* Waits for Y.Doc to be ready (IndexedDB loaded) before returning.
|
|
64
|
+
* @param documentId - The document ID
|
|
65
|
+
* @param field - The prose field name (must be in `prose` config)
|
|
66
|
+
* @returns Promise resolving to EditorBinding
|
|
67
|
+
*/
|
|
68
|
+
prose(documentId: string, field: ProseFields<T>): Promise<EditorBinding>;
|
|
69
|
+
}
|
|
70
|
+
/** Extended collection with prose field utilities */
|
|
71
|
+
export interface ConvexCollection<T extends object> extends Collection<T> {
|
|
72
|
+
/** Utilities for prose field operations */
|
|
73
|
+
utils: ConvexCollectionUtils<T>;
|
|
24
74
|
}
|
|
25
75
|
/**
|
|
26
|
-
*
|
|
27
|
-
* No custom wrapper, no special methods - uses built-in transaction system.
|
|
28
|
-
*/
|
|
29
|
-
export type ConvexCollection<T extends object> = Collection<T>;
|
|
30
|
-
/**
|
|
31
|
-
* Step 1: Create TanStack DB CollectionConfig with REAL mutation handlers.
|
|
32
|
-
*
|
|
33
|
-
* This implements the CORRECT pattern:
|
|
34
|
-
* - Uses onInsert/onUpdate/onDelete handlers (not custom wrapper)
|
|
35
|
-
* - Yjs Y.Doc with 'update' event for delta encoding
|
|
36
|
-
* - Stores Y.Map instances (not plain objects) for field-level CRDT
|
|
37
|
-
* - Uses ydoc.transact() to batch changes into single 'update' event
|
|
76
|
+
* Create TanStack DB collection options with Convex + Yjs replication.
|
|
38
77
|
*
|
|
39
78
|
* @example
|
|
40
79
|
* ```typescript
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* collectionName: 'tasks',
|
|
49
|
-
* getKey: (task) => task.id,
|
|
50
|
-
* initialData,
|
|
51
|
-
* })
|
|
52
|
-
* )
|
|
80
|
+
* const options = convexCollectionOptions<Task>({
|
|
81
|
+
* getKey: (t) => t.id,
|
|
82
|
+
* convexClient,
|
|
83
|
+
* api: { stream: api.tasks.stream, insert: api.tasks.insert, ... },
|
|
84
|
+
* collection: 'tasks',
|
|
85
|
+
* });
|
|
86
|
+
* const collection = createCollection(options);
|
|
53
87
|
* ```
|
|
54
88
|
*/
|
|
55
|
-
export declare function convexCollectionOptions<T extends object>({ getKey,
|
|
89
|
+
export declare function convexCollectionOptions<T extends object>({ getKey, material, convexClient, api, collection, prose: proseFields, undoCaptureTimeout, persistence, }: ConvexCollectionOptionsConfig<T>): CollectionConfig<T> & {
|
|
56
90
|
_convexClient: ConvexClient;
|
|
57
|
-
|
|
91
|
+
_collection: string;
|
|
92
|
+
_proseFields: Array<ProseFields<T>>;
|
|
93
|
+
_persistence: Persistence;
|
|
94
|
+
utils: ConvexCollectionUtils<T>;
|
|
58
95
|
};
|
|
59
|
-
|
|
60
|
-
* Step 2: Wrap collection with offline support.
|
|
61
|
-
*
|
|
62
|
-
* This implements the CORRECT pattern:
|
|
63
|
-
* - Wraps collection ONCE with startOfflineExecutor
|
|
64
|
-
* - Returns raw collection (NO CUSTOM WRAPPER)
|
|
65
|
-
* - Uses beforeRetry filter for stale transactions
|
|
66
|
-
* - Connects to Convex connection state for retry triggers
|
|
67
|
-
*
|
|
68
|
-
* Config is automatically extracted from the rawCollection!
|
|
69
|
-
*
|
|
70
|
-
* @example
|
|
71
|
-
* ```typescript
|
|
72
|
-
* import { createCollection } from '@tanstack/react-db'
|
|
73
|
-
* import { convexCollectionOptions, createConvexCollection } from '@trestleinc/convex-replicate-core'
|
|
74
|
-
*
|
|
75
|
-
* // Step 1: Create raw collection with ALL config
|
|
76
|
-
* const rawCollection = createCollection(
|
|
77
|
-
* convexCollectionOptions<Task>({
|
|
78
|
-
* convexClient,
|
|
79
|
-
* api: api.tasks,
|
|
80
|
-
* collectionName: 'tasks',
|
|
81
|
-
* getKey: (task) => task.id,
|
|
82
|
-
* initialData,
|
|
83
|
-
* })
|
|
84
|
-
* )
|
|
85
|
-
*
|
|
86
|
-
* // Step 2: Wrap with offline support - params automatically extracted!
|
|
87
|
-
* const collection = createConvexCollection(rawCollection)
|
|
88
|
-
*
|
|
89
|
-
* // Use like a normal TanStack DB collection
|
|
90
|
-
* const tx = collection.insert({ id: '1', text: 'Buy milk', isCompleted: false })
|
|
91
|
-
* await tx.isPersisted.promise // Built-in promise (not custom awaitReplication)
|
|
92
|
-
* ```
|
|
93
|
-
*/
|
|
94
|
-
export declare function createConvexCollection<T extends object>(rawCollection: Collection<T>): ConvexCollection<T>;
|
|
96
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
declare const NetworkError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
2
|
+
readonly _tag: "NetworkError";
|
|
3
|
+
} & Readonly<A>;
|
|
4
|
+
export declare class NetworkError extends NetworkError_base<{
|
|
5
|
+
readonly cause: unknown;
|
|
6
|
+
readonly retryable: true;
|
|
7
|
+
readonly operation: string;
|
|
8
|
+
}> {
|
|
9
|
+
}
|
|
10
|
+
declare const IDBError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
11
|
+
readonly _tag: "IDBError";
|
|
12
|
+
} & Readonly<A>;
|
|
13
|
+
export declare class IDBError extends IDBError_base<{
|
|
14
|
+
readonly operation: 'get' | 'set' | 'delete' | 'clear';
|
|
15
|
+
readonly store?: string;
|
|
16
|
+
readonly key?: string;
|
|
17
|
+
readonly cause: unknown;
|
|
18
|
+
}> {
|
|
19
|
+
}
|
|
20
|
+
declare const IDBWriteError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
21
|
+
readonly _tag: "IDBWriteError";
|
|
22
|
+
} & Readonly<A>;
|
|
23
|
+
export declare class IDBWriteError extends IDBWriteError_base<{
|
|
24
|
+
readonly key: string;
|
|
25
|
+
readonly value: unknown;
|
|
26
|
+
readonly cause: unknown;
|
|
27
|
+
}> {
|
|
28
|
+
}
|
|
29
|
+
declare const ReconciliationError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
30
|
+
readonly _tag: "ReconciliationError";
|
|
31
|
+
} & Readonly<A>;
|
|
32
|
+
export declare class ReconciliationError extends ReconciliationError_base<{
|
|
33
|
+
readonly collection: string;
|
|
34
|
+
readonly reason: string;
|
|
35
|
+
readonly cause?: unknown;
|
|
36
|
+
}> {
|
|
37
|
+
}
|
|
38
|
+
declare const ProseError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
39
|
+
readonly _tag: "ProseError";
|
|
40
|
+
} & Readonly<A>;
|
|
41
|
+
export declare class ProseError extends ProseError_base<{
|
|
42
|
+
readonly documentId: string;
|
|
43
|
+
readonly field: string;
|
|
44
|
+
readonly collection: string;
|
|
45
|
+
}> {
|
|
46
|
+
}
|
|
47
|
+
declare const CollectionNotReadyError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
48
|
+
readonly _tag: "CollectionNotReadyError";
|
|
49
|
+
} & Readonly<A>;
|
|
50
|
+
export declare class CollectionNotReadyError extends CollectionNotReadyError_base<{
|
|
51
|
+
readonly collection: string;
|
|
52
|
+
readonly reason: string;
|
|
53
|
+
}> {
|
|
54
|
+
}
|
|
55
|
+
/** Error that should not be retried (auth failures, validation errors) */
|
|
56
|
+
export declare class NonRetriableError extends Error {
|
|
57
|
+
constructor(message: string);
|
|
58
|
+
}
|
|
59
|
+
export {};
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export {
|
|
17
|
-
|
|
18
|
-
export
|
|
1
|
+
export { convexCollectionOptions, type ConvexCollection, type EditorBinding, } from '$/client/collection.js';
|
|
2
|
+
import { NetworkError, IDBError, IDBWriteError, ReconciliationError, ProseError, CollectionNotReadyError, NonRetriableError } from '$/client/errors.js';
|
|
3
|
+
export declare const errors: {
|
|
4
|
+
readonly Network: typeof NetworkError;
|
|
5
|
+
readonly IDB: typeof IDBError;
|
|
6
|
+
readonly IDBWrite: typeof IDBWriteError;
|
|
7
|
+
readonly Reconciliation: typeof ReconciliationError;
|
|
8
|
+
readonly Prose: typeof ProseError;
|
|
9
|
+
readonly CollectionNotReady: typeof CollectionNotReadyError;
|
|
10
|
+
readonly NonRetriable: typeof NonRetriableError;
|
|
11
|
+
};
|
|
12
|
+
import { extract } from '$/client/merge.js';
|
|
13
|
+
export declare const prose: {
|
|
14
|
+
readonly extract: typeof extract;
|
|
15
|
+
};
|
|
16
|
+
export { persistence, type Persistence, type PersistenceProvider, type KeyValueStore, type SqlitePersistenceOptions, type SqliteAdapter, type SqlJsStatic, } from '$/client/persistence/index.js';
|
|
17
|
+
import { SqlJsAdapter, OPSqliteAdapter } from '$/client/persistence/adapters/index.js';
|
|
18
|
+
export declare const adapters: {
|
|
19
|
+
readonly sqljs: typeof SqlJsAdapter;
|
|
20
|
+
readonly opsqlite: typeof OPSqliteAdapter;
|
|
21
|
+
};
|
|
22
|
+
export type { SqlJsDatabase, SqlJsAdapterOptions, OPSQLiteDatabase, } from '$/client/persistence/adapters/index.js';
|
package/dist/client/logger.d.ts
CHANGED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge Helpers - Plain functions for Yjs CRDT operations
|
|
3
|
+
*
|
|
4
|
+
* Provides document creation, state encoding, and merge operations.
|
|
5
|
+
*/
|
|
6
|
+
import * as Y from 'yjs';
|
|
7
|
+
import type { KeyValueStore } from '$/client/persistence/types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Create a Yjs document with a persistent clientId.
|
|
10
|
+
* The clientId ensures consistent identity across sessions for CRDT merging.
|
|
11
|
+
*
|
|
12
|
+
* @param collection - The collection name
|
|
13
|
+
* @param kv - Key-value store for persisting the clientId
|
|
14
|
+
*/
|
|
15
|
+
export declare function createYjsDocument(collection: string, kv: KeyValueStore): Promise<Y.Doc>;
|
|
16
|
+
/**
|
|
17
|
+
* Apply a binary update to a Yjs document.
|
|
18
|
+
* Y.applyUpdateV2 is already atomic, no need for transaction wrapper.
|
|
19
|
+
*/
|
|
20
|
+
export declare function applyUpdate(doc: Y.Doc, update: Uint8Array, origin?: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* Get a Y.Map from a Yjs document by name.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getYMap<T = unknown>(doc: Y.Doc, name: string): Y.Map<T>;
|
|
25
|
+
/**
|
|
26
|
+
* Execute a function within a Yjs transaction.
|
|
27
|
+
*/
|
|
28
|
+
export declare function yjsTransact<A>(doc: Y.Doc, fn: () => A, origin?: string): A;
|
|
29
|
+
/**
|
|
30
|
+
* Execute a function within a Yjs transaction and capture the delta.
|
|
31
|
+
* Returns both the function result and a delta containing only the changes made.
|
|
32
|
+
*/
|
|
33
|
+
export declare function transactWithDelta<A>(doc: Y.Doc, fn: () => A, origin?: string): {
|
|
34
|
+
result: A;
|
|
35
|
+
delta: Uint8Array;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Serialize a Y.Map to a plain object.
|
|
39
|
+
*/
|
|
40
|
+
export declare function serializeYMap(ymap: Y.Map<unknown>): Record<string, unknown>;
|
|
41
|
+
/**
|
|
42
|
+
* Extract all items from a Y.Map as plain objects.
|
|
43
|
+
*/
|
|
44
|
+
export declare function extractItems<T>(ymap: Y.Map<unknown>): T[];
|
|
45
|
+
/**
|
|
46
|
+
* Extract a single item from a Y.Map by key.
|
|
47
|
+
*/
|
|
48
|
+
export declare function extractItem<T>(ymap: Y.Map<unknown>, key: string): T | null;
|
|
49
|
+
import type { XmlFragmentJSON } from '$/shared/types.js';
|
|
50
|
+
/**
|
|
51
|
+
* Check if a value looks like ProseMirror/BlockNote JSON document.
|
|
52
|
+
* Used internally to auto-detect prose fields during insert/update.
|
|
53
|
+
*/
|
|
54
|
+
export declare function isDoc(value: unknown): value is XmlFragmentJSON;
|
|
55
|
+
/**
|
|
56
|
+
* Convert a Y.XmlFragment to ProseMirror-compatible JSON.
|
|
57
|
+
*/
|
|
58
|
+
export declare function fragmentToJSON(fragment: Y.XmlFragment): XmlFragmentJSON;
|
|
59
|
+
/**
|
|
60
|
+
* Initialize a Y.XmlFragment from ProseMirror-compatible JSON.
|
|
61
|
+
*/
|
|
62
|
+
export declare function fragmentFromJSON(fragment: Y.XmlFragment, json: XmlFragmentJSON): void;
|
|
63
|
+
/**
|
|
64
|
+
* Extract plain text from ProseMirror/BlockNote JSON content.
|
|
65
|
+
* Handles various content structures defensively for search and display.
|
|
66
|
+
*/
|
|
67
|
+
export declare function extract(content: unknown): string;
|
|
68
|
+
/**
|
|
69
|
+
* Serialize any value, handling Yjs types specially.
|
|
70
|
+
* Uses our custom serialization system that works across module instances.
|
|
71
|
+
*/
|
|
72
|
+
export declare function serializeYMapValue(value: unknown): unknown;
|
|
73
|
+
/**
|
|
74
|
+
* Get a Y.XmlFragment from a document's field.
|
|
75
|
+
* Returns null if the document or field doesn't exist, or if the field is not an XmlFragment.
|
|
76
|
+
*/
|
|
77
|
+
export declare function getFragmentFromYMap(ymap: Y.Map<unknown>, documentId: string, field: string): Y.XmlFragment | null;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite adapter wrappers for different platforms.
|
|
3
|
+
*
|
|
4
|
+
* These are wrapper classes - the consuming app imports and initializes
|
|
5
|
+
* the actual database packages, then passes them to these wrappers.
|
|
6
|
+
*/
|
|
7
|
+
export { SqlJsAdapter, type SqlJsDatabase, type SqlJsAdapterOptions } from './sqljs.js';
|
|
8
|
+
export { OPSqliteAdapter, type OPSQLiteDatabase } from './opsqlite.js';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* op-sqlite adapter wrapper for React Native SQLite.
|
|
3
|
+
*
|
|
4
|
+
* The consuming app imports @op-engineering/op-sqlite and opens the database,
|
|
5
|
+
* then passes it to this wrapper.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { open } from '@op-engineering/op-sqlite';
|
|
10
|
+
* import { OPSqliteAdapter } from '@trestleinc/replicate/client';
|
|
11
|
+
*
|
|
12
|
+
* const db = open({ name: 'myapp.db' });
|
|
13
|
+
* const adapter = new OPSqliteAdapter(db);
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
import type { SqliteAdapter } from '../sqlite-level.js';
|
|
17
|
+
/**
|
|
18
|
+
* Interface for op-sqlite Database.
|
|
19
|
+
* Consumer must install @op-engineering/op-sqlite and pass a Database instance.
|
|
20
|
+
*/
|
|
21
|
+
export interface OPSQLiteDatabase {
|
|
22
|
+
execute(sql: string, params?: unknown[]): Promise<{
|
|
23
|
+
rows: Record<string, unknown>[];
|
|
24
|
+
}>;
|
|
25
|
+
close(): void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Wraps an op-sqlite Database as a SqliteAdapter.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* import { open } from '@op-engineering/op-sqlite';
|
|
33
|
+
* import { OPSqliteAdapter } from '@trestleinc/replicate/client';
|
|
34
|
+
*
|
|
35
|
+
* const db = open({ name: 'myapp.db' });
|
|
36
|
+
* const adapter = new OPSqliteAdapter(db);
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare class OPSqliteAdapter implements SqliteAdapter {
|
|
40
|
+
private db;
|
|
41
|
+
constructor(db: OPSQLiteDatabase);
|
|
42
|
+
execute(sql: string, params?: unknown[]): Promise<{
|
|
43
|
+
rows: Record<string, unknown>[];
|
|
44
|
+
}>;
|
|
45
|
+
close(): void;
|
|
46
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sql.js adapter wrapper for browser SQLite.
|
|
3
|
+
*
|
|
4
|
+
* The consuming app imports sql.js and creates the database,
|
|
5
|
+
* then passes it to this wrapper.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import initSqlJs from 'sql.js';
|
|
10
|
+
* import { SqlJsAdapter } from '@trestleinc/replicate/client';
|
|
11
|
+
*
|
|
12
|
+
* const SQL = await initSqlJs({ locateFile: f => `/wasm/${f}` });
|
|
13
|
+
* const db = new SQL.Database();
|
|
14
|
+
* const adapter = new SqlJsAdapter(db, {
|
|
15
|
+
* onPersist: async (data) => {
|
|
16
|
+
* // Persist to OPFS, localStorage, etc.
|
|
17
|
+
* }
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import type { SqliteAdapter } from '../sqlite-level.js';
|
|
22
|
+
/**
|
|
23
|
+
* Interface for sql.js Database.
|
|
24
|
+
* Consumer must install sql.js and pass a Database instance.
|
|
25
|
+
*/
|
|
26
|
+
export interface SqlJsDatabase {
|
|
27
|
+
run(sql: string, params?: unknown[]): void;
|
|
28
|
+
prepare(sql: string): {
|
|
29
|
+
bind(params?: unknown[]): void;
|
|
30
|
+
step(): boolean;
|
|
31
|
+
getAsObject(): Record<string, unknown>;
|
|
32
|
+
free(): void;
|
|
33
|
+
};
|
|
34
|
+
export(): Uint8Array;
|
|
35
|
+
close(): void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Options for the SqlJsAdapter.
|
|
39
|
+
*/
|
|
40
|
+
export interface SqlJsAdapterOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Callback to persist database after write operations.
|
|
43
|
+
* Called with the exported database bytes.
|
|
44
|
+
*
|
|
45
|
+
* @example OPFS persistence
|
|
46
|
+
* ```typescript
|
|
47
|
+
* onPersist: async (data) => {
|
|
48
|
+
* const root = await navigator.storage.getDirectory();
|
|
49
|
+
* const handle = await root.getFileHandle('myapp.sqlite', { create: true });
|
|
50
|
+
* const writable = await handle.createWritable();
|
|
51
|
+
* await writable.write(data.buffer);
|
|
52
|
+
* await writable.close();
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
onPersist?: (data: Uint8Array) => Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Wraps a sql.js Database as a SqliteAdapter.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* import initSqlJs from 'sql.js';
|
|
64
|
+
* import { SqlJsAdapter } from '@trestleinc/replicate/client';
|
|
65
|
+
*
|
|
66
|
+
* const SQL = await initSqlJs();
|
|
67
|
+
* const db = new SQL.Database();
|
|
68
|
+
* const adapter = new SqlJsAdapter(db);
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export declare class SqlJsAdapter implements SqliteAdapter {
|
|
72
|
+
private db;
|
|
73
|
+
private onPersist?;
|
|
74
|
+
constructor(db: SqlJsDatabase, options?: SqlJsAdapterOptions);
|
|
75
|
+
execute(sql: string, params?: unknown[]): Promise<{
|
|
76
|
+
rows: Record<string, unknown>[];
|
|
77
|
+
}>;
|
|
78
|
+
close(): void;
|
|
79
|
+
/**
|
|
80
|
+
* Persist database using the onPersist callback if provided.
|
|
81
|
+
*/
|
|
82
|
+
private persist;
|
|
83
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
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';
|
|
10
|
+
import { indexeddbPersistence } from './indexeddb.js';
|
|
11
|
+
import { memoryPersistence } from './memory.js';
|
|
12
|
+
import { sqlitePersistence } from './sqlite.js';
|
|
13
|
+
import { createBrowserSqlitePersistence } from './sqlite-browser.js';
|
|
14
|
+
import { createReactNativeSqlitePersistence } from './sqlite-rn.js';
|
|
15
|
+
/**
|
|
16
|
+
* Persistence API - nested object pattern for ergonomic access.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* import { persistence } from '@trestleinc/replicate/client';
|
|
21
|
+
*
|
|
22
|
+
* // Browser SQLite (recommended for web)
|
|
23
|
+
* const p = await persistence.sqlite.browser(SQL, 'myapp');
|
|
24
|
+
*
|
|
25
|
+
* // React Native SQLite
|
|
26
|
+
* const p = await persistence.sqlite.native(db, 'myapp');
|
|
27
|
+
*
|
|
28
|
+
* // IndexedDB fallback
|
|
29
|
+
* const p = persistence.indexeddb('myapp');
|
|
30
|
+
*
|
|
31
|
+
* // In-memory (testing)
|
|
32
|
+
* const p = persistence.memory();
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare const persistence: {
|
|
36
|
+
/** IndexedDB-backed persistence (browser) */
|
|
37
|
+
readonly indexeddb: typeof indexeddbPersistence;
|
|
38
|
+
/** In-memory persistence (testing/ephemeral) */
|
|
39
|
+
readonly memory: typeof memoryPersistence;
|
|
40
|
+
/** SQLite persistence variants */
|
|
41
|
+
readonly sqlite: {
|
|
42
|
+
/** Browser SQLite with OPFS (sql.js) */
|
|
43
|
+
readonly browser: typeof createBrowserSqlitePersistence;
|
|
44
|
+
/** React Native SQLite (op-sqlite) */
|
|
45
|
+
readonly native: typeof createReactNativeSqlitePersistence;
|
|
46
|
+
/** Custom SQLite adapter */
|
|
47
|
+
readonly create: typeof sqlitePersistence;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Persistence } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create an IndexedDB persistence factory.
|
|
4
|
+
*
|
|
5
|
+
* Uses y-indexeddb for Y.Doc persistence and browser-level for metadata storage.
|
|
6
|
+
*
|
|
7
|
+
* @param dbName - Name for the LevelDB database (default: 'replicate-kv')
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* convexCollectionOptions<Task>({
|
|
12
|
+
* // ... other options
|
|
13
|
+
* persistence: indexeddbPersistence(),
|
|
14
|
+
* });
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function indexeddbPersistence(dbName?: string): Persistence;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Persistence } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create an in-memory persistence factory.
|
|
4
|
+
*
|
|
5
|
+
* Useful for testing where you don't want IndexedDB side effects.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* // In tests
|
|
10
|
+
* convexCollectionOptions<Task>({
|
|
11
|
+
* // ... other options
|
|
12
|
+
* persistence: memoryPersistence(),
|
|
13
|
+
* });
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare function memoryPersistence(): Persistence;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser SQLite persistence helper using sql.js and OPFS.
|
|
3
|
+
*
|
|
4
|
+
* Handles all the boilerplate for browser SQLite:
|
|
5
|
+
* - Loading existing database from OPFS
|
|
6
|
+
* - Persisting to OPFS on every write
|
|
7
|
+
* - Creating the SqlJsAdapter
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { createBrowserSqlitePersistence } from '@trestleinc/replicate/client';
|
|
12
|
+
* import initSqlJs from 'sql.js';
|
|
13
|
+
*
|
|
14
|
+
* const SQL = await initSqlJs({ locateFile: f => `https://sql.js.org/dist/${f}` });
|
|
15
|
+
* const persistence = await createBrowserSqlitePersistence(SQL, 'myapp');
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import { type SqlJsDatabase } from './adapters/sqljs.js';
|
|
19
|
+
import type { Persistence } from './types.js';
|
|
20
|
+
/**
|
|
21
|
+
* Interface for the sql.js module (the result of initSqlJs).
|
|
22
|
+
*/
|
|
23
|
+
export interface SqlJsStatic {
|
|
24
|
+
Database: new (data?: ArrayLike<number>) => SqlJsDatabase;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create browser SQLite persistence with OPFS storage.
|
|
28
|
+
*
|
|
29
|
+
* This helper handles all the OPFS boilerplate:
|
|
30
|
+
* - Loads existing database from OPFS on init
|
|
31
|
+
* - Persists to OPFS after every write operation
|
|
32
|
+
*
|
|
33
|
+
* @param SQL - The initialized sql.js module (from `await initSqlJs()`)
|
|
34
|
+
* @param dbName - Name for the database (used for OPFS filename: `{dbName}.sqlite`)
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* import { createBrowserSqlitePersistence } from '@trestleinc/replicate/client';
|
|
39
|
+
* import initSqlJs from 'sql.js';
|
|
40
|
+
*
|
|
41
|
+
* const SQL = await initSqlJs({ locateFile: f => `https://sql.js.org/dist/${f}` });
|
|
42
|
+
* const persistence = await createBrowserSqlitePersistence(SQL, 'intervals');
|
|
43
|
+
*
|
|
44
|
+
* // Use in collection options
|
|
45
|
+
* convexCollectionOptions<Task>({
|
|
46
|
+
* // ...
|
|
47
|
+
* persistence,
|
|
48
|
+
* });
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare function createBrowserSqlitePersistence(SQL: SqlJsStatic, dbName: string): Promise<Persistence>;
|