@trestleinc/replicate 1.1.0 → 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.
- package/README.md +446 -260
- package/dist/client/index.d.ts +311 -19
- package/dist/client/index.js +4027 -0
- package/dist/component/_generated/api.d.ts +13 -17
- package/dist/component/_generated/api.js +24 -4
- package/dist/component/_generated/component.d.ts +79 -77
- package/dist/component/_generated/component.js +1 -0
- package/dist/component/_generated/dataModel.d.ts +12 -15
- package/dist/component/_generated/dataModel.js +1 -0
- package/dist/component/_generated/server.d.ts +19 -22
- package/dist/component/_generated/server.js +65 -1
- package/dist/component/_virtual/rolldown_runtime.js +18 -0
- package/dist/component/convex.config.d.ts +6 -2
- package/dist/component/convex.config.js +7 -3
- package/dist/component/logger.d.ts +10 -6
- package/dist/component/logger.js +25 -28
- package/dist/component/public.d.ts +70 -61
- package/dist/component/public.js +311 -295
- package/dist/component/schema.d.ts +53 -45
- package/dist/component/schema.js +26 -32
- package/dist/component/shared/types.d.ts +9 -0
- package/dist/component/shared/types.js +15 -0
- package/dist/server/index.d.ts +134 -13
- package/dist/server/index.js +368 -0
- package/dist/shared/index.d.ts +27 -3
- package/dist/shared/index.js +1 -2
- package/package.json +34 -29
- package/src/client/collection.ts +339 -306
- package/src/client/errors.ts +9 -9
- package/src/client/index.ts +13 -32
- package/src/client/logger.ts +2 -2
- package/src/client/merge.ts +37 -34
- package/src/client/persistence/custom.ts +84 -0
- package/src/client/persistence/index.ts +9 -46
- package/src/client/persistence/indexeddb.ts +111 -84
- package/src/client/persistence/memory.ts +3 -3
- package/src/client/persistence/sqlite/browser.ts +168 -0
- package/src/client/persistence/sqlite/native.ts +29 -0
- package/src/client/persistence/sqlite/schema.ts +124 -0
- package/src/client/persistence/types.ts +32 -28
- package/src/client/prose-schema.ts +55 -0
- package/src/client/prose.ts +28 -25
- package/src/client/replicate.ts +5 -5
- package/src/client/services/cursor.ts +109 -0
- package/src/component/_generated/component.ts +31 -29
- package/src/component/convex.config.ts +2 -2
- package/src/component/logger.ts +7 -7
- package/src/component/public.ts +225 -237
- package/src/component/schema.ts +18 -15
- package/src/server/builder.ts +20 -7
- package/src/server/index.ts +3 -5
- package/src/server/schema.ts +5 -5
- package/src/server/storage.ts +113 -59
- package/src/shared/index.ts +5 -5
- package/src/shared/types.ts +51 -14
- package/dist/client/collection.d.ts +0 -96
- package/dist/client/errors.d.ts +0 -59
- package/dist/client/logger.d.ts +0 -2
- package/dist/client/merge.d.ts +0 -77
- package/dist/client/persistence/adapters/index.d.ts +0 -8
- package/dist/client/persistence/adapters/opsqlite.d.ts +0 -46
- package/dist/client/persistence/adapters/sqljs.d.ts +0 -83
- package/dist/client/persistence/index.d.ts +0 -49
- package/dist/client/persistence/indexeddb.d.ts +0 -17
- package/dist/client/persistence/memory.d.ts +0 -16
- package/dist/client/persistence/sqlite-browser.d.ts +0 -51
- package/dist/client/persistence/sqlite-level.d.ts +0 -63
- package/dist/client/persistence/sqlite-rn.d.ts +0 -36
- package/dist/client/persistence/sqlite.d.ts +0 -47
- package/dist/client/persistence/types.d.ts +0 -42
- package/dist/client/prose.d.ts +0 -56
- package/dist/client/replicate.d.ts +0 -40
- package/dist/client/services/checkpoint.d.ts +0 -18
- package/dist/client/services/reconciliation.d.ts +0 -24
- package/dist/index.js +0 -1620
- package/dist/server/builder.d.ts +0 -94
- package/dist/server/schema.d.ts +0 -27
- package/dist/server/storage.d.ts +0 -80
- package/dist/server.js +0 -281
- package/dist/shared/types.d.ts +0 -50
- package/dist/shared/types.js +0 -6
- package/dist/shared.js +0 -6
- package/src/client/persistence/adapters/index.ts +0 -8
- package/src/client/persistence/adapters/opsqlite.ts +0 -54
- package/src/client/persistence/adapters/sqljs.ts +0 -128
- package/src/client/persistence/sqlite-browser.ts +0 -107
- package/src/client/persistence/sqlite-level.ts +0 -407
- package/src/client/persistence/sqlite-rn.ts +0 -44
- package/src/client/persistence/sqlite.ts +0 -161
- package/src/client/services/checkpoint.ts +0 -86
- package/src/client/services/reconciliation.ts +0 -108
package/dist/server/builder.d.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import type { GenericMutationCtx, GenericQueryCtx, GenericDataModel } from 'convex/server';
|
|
2
|
-
/**
|
|
3
|
-
* Configuration for replicate handlers (without component - used with factory pattern).
|
|
4
|
-
*/
|
|
5
|
-
export interface ReplicateConfig<T extends object> {
|
|
6
|
-
collection: string;
|
|
7
|
-
/** Size threshold for auto-compaction (default: 5MB). Set to 0 to disable. */
|
|
8
|
-
compaction?: {
|
|
9
|
-
threshold?: number;
|
|
10
|
-
};
|
|
11
|
-
hooks?: {
|
|
12
|
-
evalRead?: (ctx: GenericQueryCtx<GenericDataModel>, collection: string) => void | Promise<void>;
|
|
13
|
-
evalWrite?: (ctx: GenericMutationCtx<GenericDataModel>, doc: T) => void | Promise<void>;
|
|
14
|
-
evalRemove?: (ctx: GenericMutationCtx<GenericDataModel>, docId: string) => void | Promise<void>;
|
|
15
|
-
onStream?: (ctx: GenericQueryCtx<GenericDataModel>, result: any) => void | Promise<void>;
|
|
16
|
-
onInsert?: (ctx: GenericMutationCtx<GenericDataModel>, doc: T) => void | Promise<void>;
|
|
17
|
-
onUpdate?: (ctx: GenericMutationCtx<GenericDataModel>, doc: T) => void | Promise<void>;
|
|
18
|
-
onRemove?: (ctx: GenericMutationCtx<GenericDataModel>, docId: string) => void | Promise<void>;
|
|
19
|
-
transform?: (docs: T[]) => T[] | Promise<T[]>;
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Create a replicate function bound to your component. Call this once in your
|
|
24
|
-
* convex/replicate.ts file, then use the returned function for all collections.
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```typescript
|
|
28
|
-
* // convex/replicate.ts (create once)
|
|
29
|
-
* import { replicate } from '@trestleinc/replicate/server';
|
|
30
|
-
* import { components } from './_generated/api';
|
|
31
|
-
*
|
|
32
|
-
* export const tasks = replicate(components.replicate)<Task>({ collection: 'tasks' });
|
|
33
|
-
*
|
|
34
|
-
* // Or bind once and reuse:
|
|
35
|
-
* const r = replicate(components.replicate);
|
|
36
|
-
* export const tasks = r<Task>({ collection: 'tasks' });
|
|
37
|
-
* export const notebooks = r<Notebook>({ collection: 'notebooks' });
|
|
38
|
-
* ```
|
|
39
|
-
*/
|
|
40
|
-
export declare function replicate(component: any): <T extends object>(config: ReplicateConfig<T>) => {
|
|
41
|
-
stream: import("convex/server").RegisteredQuery<"public", {
|
|
42
|
-
limit?: number | undefined;
|
|
43
|
-
vector?: ArrayBuffer | undefined;
|
|
44
|
-
checkpoint: {
|
|
45
|
-
lastModified: number;
|
|
46
|
-
};
|
|
47
|
-
}, Promise<any>>;
|
|
48
|
-
material: import("convex/server").RegisteredQuery<"public", {}, Promise<{
|
|
49
|
-
documents: T[];
|
|
50
|
-
checkpoint?: {
|
|
51
|
-
lastModified: number;
|
|
52
|
-
} | undefined;
|
|
53
|
-
count: number;
|
|
54
|
-
crdtBytes?: ArrayBuffer;
|
|
55
|
-
}>>;
|
|
56
|
-
recovery: import("convex/server").RegisteredQuery<"public", {
|
|
57
|
-
clientStateVector: ArrayBuffer;
|
|
58
|
-
}, Promise<any>>;
|
|
59
|
-
insert: import("convex/server").RegisteredMutation<"public", {
|
|
60
|
-
documentId: string;
|
|
61
|
-
crdtBytes: ArrayBuffer;
|
|
62
|
-
materializedDoc: any;
|
|
63
|
-
}, Promise<{
|
|
64
|
-
success: boolean;
|
|
65
|
-
metadata: {
|
|
66
|
-
documentId: string;
|
|
67
|
-
timestamp: number;
|
|
68
|
-
collection: string;
|
|
69
|
-
};
|
|
70
|
-
}>>;
|
|
71
|
-
update: import("convex/server").RegisteredMutation<"public", {
|
|
72
|
-
documentId: string;
|
|
73
|
-
crdtBytes: ArrayBuffer;
|
|
74
|
-
materializedDoc: any;
|
|
75
|
-
}, Promise<{
|
|
76
|
-
success: boolean;
|
|
77
|
-
metadata: {
|
|
78
|
-
documentId: string;
|
|
79
|
-
timestamp: number;
|
|
80
|
-
collection: string;
|
|
81
|
-
};
|
|
82
|
-
}>>;
|
|
83
|
-
remove: import("convex/server").RegisteredMutation<"public", {
|
|
84
|
-
documentId: string;
|
|
85
|
-
crdtBytes: ArrayBuffer;
|
|
86
|
-
}, Promise<{
|
|
87
|
-
success: boolean;
|
|
88
|
-
metadata: {
|
|
89
|
-
documentId: string;
|
|
90
|
-
timestamp: number;
|
|
91
|
-
collection: string;
|
|
92
|
-
};
|
|
93
|
-
}>>;
|
|
94
|
-
};
|
package/dist/server/schema.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/** Fields automatically added to replicated tables */
|
|
2
|
-
export type ReplicationFields = {
|
|
3
|
-
timestamp: number;
|
|
4
|
-
};
|
|
5
|
-
export declare const prose: () => import("convex/values").VObject<{
|
|
6
|
-
content?: any[] | undefined;
|
|
7
|
-
type: "doc";
|
|
8
|
-
}, {
|
|
9
|
-
type: import("convex/values").VLiteral<"doc", "required">;
|
|
10
|
-
content: import("convex/values").VArray<any[] | undefined, import("convex/values").VAny<any, "required", string>, "optional">;
|
|
11
|
-
}, "required", "type" | "content">;
|
|
12
|
-
/**
|
|
13
|
-
* Define a table with automatic timestamp field for replication.
|
|
14
|
-
* All replicated tables must have an `id` field and define a `by_doc_id` index.
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```typescript
|
|
18
|
-
* // convex/schema.ts
|
|
19
|
-
* export default defineSchema({
|
|
20
|
-
* tasks: table(
|
|
21
|
-
* { id: v.string(), text: v.string(), isCompleted: v.boolean() },
|
|
22
|
-
* (t) => t.index('by_doc_id', ['id']).index('by_completed', ['isCompleted'])
|
|
23
|
-
* ),
|
|
24
|
-
* });
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
export declare function table(userFields: Record<string, any>, applyIndexes?: (table: any) => any): any;
|
package/dist/server/storage.d.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import type { GenericMutationCtx, GenericQueryCtx, GenericDataModel } from 'convex/server';
|
|
2
|
-
export declare class Replicate<T extends object> {
|
|
3
|
-
component: any;
|
|
4
|
-
collectionName: string;
|
|
5
|
-
private options?;
|
|
6
|
-
constructor(component: any, collectionName: string, options?: {
|
|
7
|
-
threshold?: number;
|
|
8
|
-
} | undefined);
|
|
9
|
-
createStreamQuery(opts?: {
|
|
10
|
-
evalRead?: (ctx: GenericQueryCtx<GenericDataModel>, collection: string) => void | Promise<void>;
|
|
11
|
-
onStream?: (ctx: GenericQueryCtx<GenericDataModel>, result: any) => void | Promise<void>;
|
|
12
|
-
}): import("convex/server").RegisteredQuery<"public", {
|
|
13
|
-
limit?: number | undefined;
|
|
14
|
-
vector?: ArrayBuffer | undefined;
|
|
15
|
-
checkpoint: {
|
|
16
|
-
lastModified: number;
|
|
17
|
-
};
|
|
18
|
-
}, Promise<any>>;
|
|
19
|
-
createSSRQuery(opts?: {
|
|
20
|
-
evalRead?: (ctx: GenericQueryCtx<GenericDataModel>, collection: string) => void | Promise<void>;
|
|
21
|
-
transform?: (docs: T[]) => T[] | Promise<T[]>;
|
|
22
|
-
includeCRDTState?: boolean;
|
|
23
|
-
}): import("convex/server").RegisteredQuery<"public", {}, Promise<{
|
|
24
|
-
documents: T[];
|
|
25
|
-
checkpoint?: {
|
|
26
|
-
lastModified: number;
|
|
27
|
-
};
|
|
28
|
-
count: number;
|
|
29
|
-
crdtBytes?: ArrayBuffer;
|
|
30
|
-
}>>;
|
|
31
|
-
createInsertMutation(opts?: {
|
|
32
|
-
evalWrite?: (ctx: GenericMutationCtx<GenericDataModel>, doc: T) => void | Promise<void>;
|
|
33
|
-
onInsert?: (ctx: GenericMutationCtx<GenericDataModel>, doc: T) => void | Promise<void>;
|
|
34
|
-
}): import("convex/server").RegisteredMutation<"public", {
|
|
35
|
-
documentId: string;
|
|
36
|
-
crdtBytes: ArrayBuffer;
|
|
37
|
-
materializedDoc: any;
|
|
38
|
-
}, Promise<{
|
|
39
|
-
success: boolean;
|
|
40
|
-
metadata: {
|
|
41
|
-
documentId: string;
|
|
42
|
-
timestamp: number;
|
|
43
|
-
collection: string;
|
|
44
|
-
};
|
|
45
|
-
}>>;
|
|
46
|
-
createUpdateMutation(opts?: {
|
|
47
|
-
evalWrite?: (ctx: GenericMutationCtx<GenericDataModel>, doc: T) => void | Promise<void>;
|
|
48
|
-
onUpdate?: (ctx: GenericMutationCtx<GenericDataModel>, doc: T) => void | Promise<void>;
|
|
49
|
-
}): import("convex/server").RegisteredMutation<"public", {
|
|
50
|
-
documentId: string;
|
|
51
|
-
crdtBytes: ArrayBuffer;
|
|
52
|
-
materializedDoc: any;
|
|
53
|
-
}, Promise<{
|
|
54
|
-
success: boolean;
|
|
55
|
-
metadata: {
|
|
56
|
-
documentId: string;
|
|
57
|
-
timestamp: number;
|
|
58
|
-
collection: string;
|
|
59
|
-
};
|
|
60
|
-
}>>;
|
|
61
|
-
createRemoveMutation(opts?: {
|
|
62
|
-
evalRemove?: (ctx: GenericMutationCtx<GenericDataModel>, docId: string) => void | Promise<void>;
|
|
63
|
-
onRemove?: (ctx: GenericMutationCtx<GenericDataModel>, docId: string) => void | Promise<void>;
|
|
64
|
-
}): import("convex/server").RegisteredMutation<"public", {
|
|
65
|
-
documentId: string;
|
|
66
|
-
crdtBytes: ArrayBuffer;
|
|
67
|
-
}, Promise<{
|
|
68
|
-
success: boolean;
|
|
69
|
-
metadata: {
|
|
70
|
-
documentId: string;
|
|
71
|
-
timestamp: number;
|
|
72
|
-
collection: string;
|
|
73
|
-
};
|
|
74
|
-
}>>;
|
|
75
|
-
createRecoveryQuery(opts?: {
|
|
76
|
-
evalRead?: (ctx: GenericQueryCtx<GenericDataModel>, collection: string) => void | Promise<void>;
|
|
77
|
-
}): import("convex/server").RegisteredQuery<"public", {
|
|
78
|
-
clientStateVector: ArrayBuffer;
|
|
79
|
-
}, Promise<any>>;
|
|
80
|
-
}
|
package/dist/server.js
DELETED
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
import { v } from "convex/values";
|
|
2
|
-
import { defineTable, mutationGeneric, queryGeneric } from "convex/server";
|
|
3
|
-
class Replicate {
|
|
4
|
-
component;
|
|
5
|
-
collectionName;
|
|
6
|
-
options;
|
|
7
|
-
constructor(component, collectionName, options){
|
|
8
|
-
this.component = component;
|
|
9
|
-
this.collectionName = collectionName;
|
|
10
|
-
this.options = options;
|
|
11
|
-
}
|
|
12
|
-
createStreamQuery(opts) {
|
|
13
|
-
const component = this.component;
|
|
14
|
-
const collection = this.collectionName;
|
|
15
|
-
return queryGeneric({
|
|
16
|
-
args: {
|
|
17
|
-
checkpoint: v.object({
|
|
18
|
-
lastModified: v.number()
|
|
19
|
-
}),
|
|
20
|
-
limit: v.optional(v.number()),
|
|
21
|
-
vector: v.optional(v.bytes())
|
|
22
|
-
},
|
|
23
|
-
returns: v.object({
|
|
24
|
-
changes: v.array(v.object({
|
|
25
|
-
documentId: v.optional(v.string()),
|
|
26
|
-
crdtBytes: v.bytes(),
|
|
27
|
-
version: v.number(),
|
|
28
|
-
timestamp: v.number(),
|
|
29
|
-
operationType: v.string()
|
|
30
|
-
})),
|
|
31
|
-
checkpoint: v.object({
|
|
32
|
-
lastModified: v.number()
|
|
33
|
-
}),
|
|
34
|
-
hasMore: v.boolean()
|
|
35
|
-
}),
|
|
36
|
-
handler: async (ctx, args)=>{
|
|
37
|
-
if (opts?.evalRead) await opts.evalRead(ctx, collection);
|
|
38
|
-
const result = await ctx.runQuery(component.public.stream, {
|
|
39
|
-
collection,
|
|
40
|
-
checkpoint: args.checkpoint,
|
|
41
|
-
limit: args.limit,
|
|
42
|
-
vector: args.vector
|
|
43
|
-
});
|
|
44
|
-
if (opts?.onStream) await opts.onStream(ctx, result);
|
|
45
|
-
return result;
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
createSSRQuery(opts) {
|
|
50
|
-
const collection = this.collectionName;
|
|
51
|
-
const component = this.component;
|
|
52
|
-
return queryGeneric({
|
|
53
|
-
args: {},
|
|
54
|
-
returns: v.object({
|
|
55
|
-
documents: v.any(),
|
|
56
|
-
checkpoint: v.optional(v.object({
|
|
57
|
-
lastModified: v.number()
|
|
58
|
-
})),
|
|
59
|
-
count: v.number(),
|
|
60
|
-
crdtBytes: v.optional(v.bytes())
|
|
61
|
-
}),
|
|
62
|
-
handler: async (ctx)=>{
|
|
63
|
-
if (opts?.evalRead) await opts.evalRead(ctx, collection);
|
|
64
|
-
let docs = await ctx.db.query(collection).collect();
|
|
65
|
-
if (opts?.transform) docs = await opts.transform(docs);
|
|
66
|
-
const latestTimestamp = docs.length > 0 ? Math.max(...docs.map((doc)=>doc.timestamp || 0)) : 0;
|
|
67
|
-
const response = {
|
|
68
|
-
documents: docs,
|
|
69
|
-
checkpoint: latestTimestamp > 0 ? {
|
|
70
|
-
lastModified: latestTimestamp
|
|
71
|
-
} : void 0,
|
|
72
|
-
count: docs.length
|
|
73
|
-
};
|
|
74
|
-
if (opts?.includeCRDTState) {
|
|
75
|
-
const crdtState = await ctx.runQuery(component.public.getInitialState, {
|
|
76
|
-
collection
|
|
77
|
-
});
|
|
78
|
-
if (crdtState) {
|
|
79
|
-
response.crdtBytes = crdtState.crdtBytes;
|
|
80
|
-
response.checkpoint = crdtState.checkpoint;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return response;
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
createInsertMutation(opts) {
|
|
88
|
-
const component = this.component;
|
|
89
|
-
const collection = this.collectionName;
|
|
90
|
-
const threshold = this.options?.threshold;
|
|
91
|
-
return mutationGeneric({
|
|
92
|
-
args: {
|
|
93
|
-
documentId: v.string(),
|
|
94
|
-
crdtBytes: v.bytes(),
|
|
95
|
-
materializedDoc: v.any()
|
|
96
|
-
},
|
|
97
|
-
returns: v.object({
|
|
98
|
-
success: v.boolean(),
|
|
99
|
-
metadata: v.any()
|
|
100
|
-
}),
|
|
101
|
-
handler: async (ctx, args)=>{
|
|
102
|
-
const doc = args.materializedDoc;
|
|
103
|
-
if (opts?.evalWrite) await opts.evalWrite(ctx, doc);
|
|
104
|
-
const version = Date.now();
|
|
105
|
-
await ctx.runMutation(component.public.insertDocument, {
|
|
106
|
-
collection,
|
|
107
|
-
documentId: args.documentId,
|
|
108
|
-
crdtBytes: args.crdtBytes,
|
|
109
|
-
version,
|
|
110
|
-
threshold
|
|
111
|
-
});
|
|
112
|
-
await ctx.db.insert(collection, {
|
|
113
|
-
id: args.documentId,
|
|
114
|
-
...args.materializedDoc,
|
|
115
|
-
timestamp: Date.now()
|
|
116
|
-
});
|
|
117
|
-
if (opts?.onInsert) await opts.onInsert(ctx, doc);
|
|
118
|
-
return {
|
|
119
|
-
success: true,
|
|
120
|
-
metadata: {
|
|
121
|
-
documentId: args.documentId,
|
|
122
|
-
timestamp: Date.now(),
|
|
123
|
-
collection
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
createUpdateMutation(opts) {
|
|
130
|
-
const component = this.component;
|
|
131
|
-
const collection = this.collectionName;
|
|
132
|
-
const threshold = this.options?.threshold;
|
|
133
|
-
return mutationGeneric({
|
|
134
|
-
args: {
|
|
135
|
-
documentId: v.string(),
|
|
136
|
-
crdtBytes: v.bytes(),
|
|
137
|
-
materializedDoc: v.any()
|
|
138
|
-
},
|
|
139
|
-
returns: v.object({
|
|
140
|
-
success: v.boolean(),
|
|
141
|
-
metadata: v.any()
|
|
142
|
-
}),
|
|
143
|
-
handler: async (ctx, args)=>{
|
|
144
|
-
const doc = args.materializedDoc;
|
|
145
|
-
if (opts?.evalWrite) await opts.evalWrite(ctx, doc);
|
|
146
|
-
const version = Date.now();
|
|
147
|
-
await ctx.runMutation(component.public.updateDocument, {
|
|
148
|
-
collection,
|
|
149
|
-
documentId: args.documentId,
|
|
150
|
-
crdtBytes: args.crdtBytes,
|
|
151
|
-
version,
|
|
152
|
-
threshold
|
|
153
|
-
});
|
|
154
|
-
const existing = await ctx.db.query(collection).withIndex('by_doc_id', (q)=>q.eq('id', args.documentId)).first();
|
|
155
|
-
if (existing) await ctx.db.patch(collection, existing._id, {
|
|
156
|
-
...args.materializedDoc,
|
|
157
|
-
timestamp: Date.now()
|
|
158
|
-
});
|
|
159
|
-
if (opts?.onUpdate) await opts.onUpdate(ctx, doc);
|
|
160
|
-
return {
|
|
161
|
-
success: true,
|
|
162
|
-
metadata: {
|
|
163
|
-
documentId: args.documentId,
|
|
164
|
-
timestamp: Date.now(),
|
|
165
|
-
collection
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
createRemoveMutation(opts) {
|
|
172
|
-
const component = this.component;
|
|
173
|
-
const collection = this.collectionName;
|
|
174
|
-
const threshold = this.options?.threshold;
|
|
175
|
-
return mutationGeneric({
|
|
176
|
-
args: {
|
|
177
|
-
documentId: v.string(),
|
|
178
|
-
crdtBytes: v.bytes()
|
|
179
|
-
},
|
|
180
|
-
returns: v.object({
|
|
181
|
-
success: v.boolean(),
|
|
182
|
-
metadata: v.any()
|
|
183
|
-
}),
|
|
184
|
-
handler: async (ctx, args)=>{
|
|
185
|
-
const documentId = args.documentId;
|
|
186
|
-
if (opts?.evalRemove) await opts.evalRemove(ctx, documentId);
|
|
187
|
-
const version = Date.now();
|
|
188
|
-
await ctx.runMutation(component.public.deleteDocument, {
|
|
189
|
-
collection,
|
|
190
|
-
documentId: documentId,
|
|
191
|
-
crdtBytes: args.crdtBytes,
|
|
192
|
-
version,
|
|
193
|
-
threshold
|
|
194
|
-
});
|
|
195
|
-
const existing = await ctx.db.query(collection).withIndex('by_doc_id', (q)=>q.eq('id', documentId)).first();
|
|
196
|
-
if (existing) await ctx.db.delete(collection, existing._id);
|
|
197
|
-
if (opts?.onRemove) await opts.onRemove(ctx, documentId);
|
|
198
|
-
return {
|
|
199
|
-
success: true,
|
|
200
|
-
metadata: {
|
|
201
|
-
documentId: documentId,
|
|
202
|
-
timestamp: Date.now(),
|
|
203
|
-
collection
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
createRecoveryQuery(opts) {
|
|
210
|
-
const component = this.component;
|
|
211
|
-
const collection = this.collectionName;
|
|
212
|
-
return queryGeneric({
|
|
213
|
-
args: {
|
|
214
|
-
clientStateVector: v.bytes()
|
|
215
|
-
},
|
|
216
|
-
returns: v.object({
|
|
217
|
-
diff: v.optional(v.bytes()),
|
|
218
|
-
serverStateVector: v.bytes()
|
|
219
|
-
}),
|
|
220
|
-
handler: async (ctx, args)=>{
|
|
221
|
-
if (opts?.evalRead) await opts.evalRead(ctx, collection);
|
|
222
|
-
return await ctx.runQuery(component.public.recovery, {
|
|
223
|
-
collection,
|
|
224
|
-
clientStateVector: args.clientStateVector
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
function replicate(component) {
|
|
231
|
-
return function(config) {
|
|
232
|
-
return replicateInternal(component, config);
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
function replicateInternal(component, config) {
|
|
236
|
-
const storage = new Replicate(component, config.collection, {
|
|
237
|
-
threshold: config.compaction?.threshold
|
|
238
|
-
});
|
|
239
|
-
return {
|
|
240
|
-
stream: storage.createStreamQuery({
|
|
241
|
-
evalRead: config.hooks?.evalRead,
|
|
242
|
-
onStream: config.hooks?.onStream
|
|
243
|
-
}),
|
|
244
|
-
material: storage.createSSRQuery({
|
|
245
|
-
evalRead: config.hooks?.evalRead,
|
|
246
|
-
transform: config.hooks?.transform
|
|
247
|
-
}),
|
|
248
|
-
recovery: storage.createRecoveryQuery({
|
|
249
|
-
evalRead: config.hooks?.evalRead
|
|
250
|
-
}),
|
|
251
|
-
insert: storage.createInsertMutation({
|
|
252
|
-
evalWrite: config.hooks?.evalWrite,
|
|
253
|
-
onInsert: config.hooks?.onInsert
|
|
254
|
-
}),
|
|
255
|
-
update: storage.createUpdateMutation({
|
|
256
|
-
evalWrite: config.hooks?.evalWrite,
|
|
257
|
-
onUpdate: config.hooks?.onUpdate
|
|
258
|
-
}),
|
|
259
|
-
remove: storage.createRemoveMutation({
|
|
260
|
-
evalRemove: config.hooks?.evalRemove,
|
|
261
|
-
onRemove: config.hooks?.onRemove
|
|
262
|
-
})
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
const prose = ()=>v.object({
|
|
266
|
-
type: v.literal('doc'),
|
|
267
|
-
content: v.optional(v.array(v.any()))
|
|
268
|
-
});
|
|
269
|
-
function table(userFields, applyIndexes) {
|
|
270
|
-
const tbl = defineTable({
|
|
271
|
-
...userFields,
|
|
272
|
-
timestamp: v.number()
|
|
273
|
-
});
|
|
274
|
-
if (applyIndexes) return applyIndexes(tbl);
|
|
275
|
-
return tbl;
|
|
276
|
-
}
|
|
277
|
-
const schema = {
|
|
278
|
-
table: table,
|
|
279
|
-
prose: prose
|
|
280
|
-
};
|
|
281
|
-
export { replicate, schema };
|
package/dist/shared/types.d.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared types for @trestleinc/replicate
|
|
3
|
-
*
|
|
4
|
-
* These types are used across client, server, and component code.
|
|
5
|
-
* They are safe to import in any environment (browser, Node.js, Convex).
|
|
6
|
-
*/
|
|
7
|
-
/** Marker used during insert/update to signal a fragment field */
|
|
8
|
-
export interface FragmentValue {
|
|
9
|
-
__xmlFragment: true;
|
|
10
|
-
content?: XmlFragmentJSON;
|
|
11
|
-
}
|
|
12
|
-
/** ProseMirror-compatible JSON for XmlFragment serialization */
|
|
13
|
-
export interface XmlFragmentJSON {
|
|
14
|
-
type: "doc";
|
|
15
|
-
content?: XmlNodeJSON[];
|
|
16
|
-
}
|
|
17
|
-
/** ProseMirror node structure */
|
|
18
|
-
export interface XmlNodeJSON {
|
|
19
|
-
type: string;
|
|
20
|
-
attrs?: Record<string, unknown>;
|
|
21
|
-
content?: XmlNodeJSON[];
|
|
22
|
-
text?: string;
|
|
23
|
-
marks?: {
|
|
24
|
-
type: string;
|
|
25
|
-
attrs?: Record<string, unknown>;
|
|
26
|
-
}[];
|
|
27
|
-
}
|
|
28
|
-
/** Operation type for streaming changes */
|
|
29
|
-
export declare enum OperationType {
|
|
30
|
-
Delta = "delta",
|
|
31
|
-
Snapshot = "snapshot"
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Extract field names from T where the value type is XmlFragmentJSON.
|
|
35
|
-
* Used for type-safe prose field configuration.
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* ```typescript
|
|
39
|
-
* interface Notebook {
|
|
40
|
-
* id: string;
|
|
41
|
-
* title: string;
|
|
42
|
-
* content: XmlFragmentJSON;
|
|
43
|
-
* }
|
|
44
|
-
*
|
|
45
|
-
* type Fields = ProseFields<Notebook>; // 'content'
|
|
46
|
-
* ```
|
|
47
|
-
*/
|
|
48
|
-
export type ProseFields<T> = {
|
|
49
|
-
[K in keyof T]: T[K] extends XmlFragmentJSON ? K : never;
|
|
50
|
-
}[keyof T];
|
package/dist/shared/types.js
DELETED
package/dist/shared.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
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';
|
|
@@ -1,54 +0,0 @@
|
|
|
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
|
-
/**
|
|
19
|
-
* Interface for op-sqlite Database.
|
|
20
|
-
* Consumer must install @op-engineering/op-sqlite and pass a Database instance.
|
|
21
|
-
*/
|
|
22
|
-
export interface OPSQLiteDatabase {
|
|
23
|
-
execute(sql: string, params?: unknown[]): Promise<{ rows: Record<string, unknown>[] }>;
|
|
24
|
-
close(): void;
|
|
25
|
-
}
|
|
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 class OPSqliteAdapter implements SqliteAdapter {
|
|
40
|
-
private db: OPSQLiteDatabase;
|
|
41
|
-
|
|
42
|
-
constructor(db: OPSQLiteDatabase) {
|
|
43
|
-
this.db = db;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async execute(sql: string, params?: unknown[]): Promise<{ rows: Record<string, unknown>[] }> {
|
|
47
|
-
const result = await this.db.execute(sql, params);
|
|
48
|
-
return { rows: result.rows || [] };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
close(): void {
|
|
52
|
-
this.db.close();
|
|
53
|
-
}
|
|
54
|
-
}
|