@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
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { defineTable, mutationGeneric, queryGeneric } from "convex/server";
|
|
3
|
+
|
|
4
|
+
//#region src/shared/types.ts
|
|
5
|
+
const SIZE_MULTIPLIERS = {
|
|
6
|
+
kb: 1024,
|
|
7
|
+
mb: 1024 ** 2,
|
|
8
|
+
gb: 1024 ** 3
|
|
9
|
+
};
|
|
10
|
+
const DURATION_MULTIPLIERS = {
|
|
11
|
+
m: 6e4,
|
|
12
|
+
h: 36e5,
|
|
13
|
+
d: 864e5
|
|
14
|
+
};
|
|
15
|
+
function parseSize(s) {
|
|
16
|
+
const match = /^(\d+)(kb|mb|gb)$/i.exec(s);
|
|
17
|
+
if (!match) throw new Error(`Invalid size: ${s}`);
|
|
18
|
+
const [, num, unit] = match;
|
|
19
|
+
return parseInt(num) * SIZE_MULTIPLIERS[unit.toLowerCase()];
|
|
20
|
+
}
|
|
21
|
+
function parseDuration(s) {
|
|
22
|
+
const match = /^(\d+)(m|h|d)$/i.exec(s);
|
|
23
|
+
if (!match) throw new Error(`Invalid duration: ${s}`);
|
|
24
|
+
const [, num, unit] = match;
|
|
25
|
+
return parseInt(num) * DURATION_MULTIPLIERS[unit.toLowerCase()];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/server/storage.ts
|
|
30
|
+
const BYTES_PER_MB = 1024 * 1024;
|
|
31
|
+
const MS_PER_HOUR = 3600 * 1e3;
|
|
32
|
+
const DEFAULT_SIZE_THRESHOLD_5MB = 5 * BYTES_PER_MB;
|
|
33
|
+
const DEFAULT_PEER_TIMEOUT_24H = 24 * MS_PER_HOUR;
|
|
34
|
+
var Replicate = class {
|
|
35
|
+
sizeThreshold;
|
|
36
|
+
peerTimeout;
|
|
37
|
+
constructor(component, collectionName, compaction) {
|
|
38
|
+
this.component = component;
|
|
39
|
+
this.collectionName = collectionName;
|
|
40
|
+
this.sizeThreshold = compaction?.sizeThreshold ? parseSize(compaction.sizeThreshold) : DEFAULT_SIZE_THRESHOLD_5MB;
|
|
41
|
+
this.peerTimeout = compaction?.peerTimeout ? parseDuration(compaction.peerTimeout) : DEFAULT_PEER_TIMEOUT_24H;
|
|
42
|
+
}
|
|
43
|
+
createStreamQuery(opts) {
|
|
44
|
+
const component = this.component;
|
|
45
|
+
const collection = this.collectionName;
|
|
46
|
+
return queryGeneric({
|
|
47
|
+
args: {
|
|
48
|
+
cursor: v.number(),
|
|
49
|
+
limit: v.optional(v.number()),
|
|
50
|
+
sizeThreshold: v.optional(v.number())
|
|
51
|
+
},
|
|
52
|
+
returns: v.object({
|
|
53
|
+
changes: v.array(v.object({
|
|
54
|
+
documentId: v.string(),
|
|
55
|
+
crdtBytes: v.bytes(),
|
|
56
|
+
seq: v.number(),
|
|
57
|
+
operationType: v.string()
|
|
58
|
+
})),
|
|
59
|
+
cursor: v.number(),
|
|
60
|
+
hasMore: v.boolean(),
|
|
61
|
+
compact: v.optional(v.string())
|
|
62
|
+
}),
|
|
63
|
+
handler: async (ctx, args) => {
|
|
64
|
+
if (opts?.evalRead) await opts.evalRead(ctx, collection);
|
|
65
|
+
const result = await ctx.runQuery(component.public.stream, {
|
|
66
|
+
collection,
|
|
67
|
+
cursor: args.cursor,
|
|
68
|
+
limit: args.limit,
|
|
69
|
+
sizeThreshold: args.sizeThreshold
|
|
70
|
+
});
|
|
71
|
+
if (opts?.onStream) await opts.onStream(ctx, result);
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
createSSRQuery(opts) {
|
|
77
|
+
const collection = this.collectionName;
|
|
78
|
+
const component = this.component;
|
|
79
|
+
return queryGeneric({
|
|
80
|
+
args: {},
|
|
81
|
+
returns: v.object({
|
|
82
|
+
documents: v.any(),
|
|
83
|
+
cursor: v.optional(v.number()),
|
|
84
|
+
count: v.number(),
|
|
85
|
+
crdtBytes: v.optional(v.bytes())
|
|
86
|
+
}),
|
|
87
|
+
handler: async (ctx) => {
|
|
88
|
+
if (opts?.evalRead) await opts.evalRead(ctx, collection);
|
|
89
|
+
let docs = await ctx.db.query(collection).collect();
|
|
90
|
+
if (opts?.transform) docs = await opts.transform(docs);
|
|
91
|
+
const response = {
|
|
92
|
+
documents: docs,
|
|
93
|
+
count: docs.length
|
|
94
|
+
};
|
|
95
|
+
if (opts?.includeCRDTState) {
|
|
96
|
+
const crdtState = await ctx.runQuery(component.public.getInitialState, { collection });
|
|
97
|
+
if (crdtState) {
|
|
98
|
+
response.crdtBytes = crdtState.crdtBytes;
|
|
99
|
+
response.cursor = crdtState.cursor;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return response;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
createInsertMutation(opts) {
|
|
107
|
+
const component = this.component;
|
|
108
|
+
const collection = this.collectionName;
|
|
109
|
+
return mutationGeneric({
|
|
110
|
+
args: {
|
|
111
|
+
documentId: v.string(),
|
|
112
|
+
crdtBytes: v.bytes(),
|
|
113
|
+
materializedDoc: v.any()
|
|
114
|
+
},
|
|
115
|
+
returns: v.object({
|
|
116
|
+
success: v.boolean(),
|
|
117
|
+
seq: v.number()
|
|
118
|
+
}),
|
|
119
|
+
handler: async (ctx, args) => {
|
|
120
|
+
const doc = args.materializedDoc;
|
|
121
|
+
if (opts?.evalWrite) await opts.evalWrite(ctx, doc);
|
|
122
|
+
const result = await ctx.runMutation(component.public.insertDocument, {
|
|
123
|
+
collection,
|
|
124
|
+
documentId: args.documentId,
|
|
125
|
+
crdtBytes: args.crdtBytes
|
|
126
|
+
});
|
|
127
|
+
await ctx.db.insert(collection, {
|
|
128
|
+
id: args.documentId,
|
|
129
|
+
...args.materializedDoc,
|
|
130
|
+
timestamp: Date.now()
|
|
131
|
+
});
|
|
132
|
+
if (opts?.onInsert) await opts.onInsert(ctx, doc);
|
|
133
|
+
return {
|
|
134
|
+
success: true,
|
|
135
|
+
seq: result.seq
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
createUpdateMutation(opts) {
|
|
141
|
+
const component = this.component;
|
|
142
|
+
const collection = this.collectionName;
|
|
143
|
+
return mutationGeneric({
|
|
144
|
+
args: {
|
|
145
|
+
documentId: v.string(),
|
|
146
|
+
crdtBytes: v.bytes(),
|
|
147
|
+
materializedDoc: v.any()
|
|
148
|
+
},
|
|
149
|
+
returns: v.object({
|
|
150
|
+
success: v.boolean(),
|
|
151
|
+
seq: v.number()
|
|
152
|
+
}),
|
|
153
|
+
handler: async (ctx, args) => {
|
|
154
|
+
const doc = args.materializedDoc;
|
|
155
|
+
if (opts?.evalWrite) await opts.evalWrite(ctx, doc);
|
|
156
|
+
const result = await ctx.runMutation(component.public.updateDocument, {
|
|
157
|
+
collection,
|
|
158
|
+
documentId: args.documentId,
|
|
159
|
+
crdtBytes: args.crdtBytes
|
|
160
|
+
});
|
|
161
|
+
const existing = await ctx.db.query(collection).withIndex("by_doc_id", (q) => q.eq("id", args.documentId)).first();
|
|
162
|
+
if (existing) await ctx.db.patch(existing._id, {
|
|
163
|
+
...args.materializedDoc,
|
|
164
|
+
timestamp: Date.now()
|
|
165
|
+
});
|
|
166
|
+
if (opts?.onUpdate) await opts.onUpdate(ctx, doc);
|
|
167
|
+
return {
|
|
168
|
+
success: true,
|
|
169
|
+
seq: result.seq
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
createRemoveMutation(opts) {
|
|
175
|
+
const component = this.component;
|
|
176
|
+
const collection = this.collectionName;
|
|
177
|
+
return mutationGeneric({
|
|
178
|
+
args: {
|
|
179
|
+
documentId: v.string(),
|
|
180
|
+
crdtBytes: v.bytes()
|
|
181
|
+
},
|
|
182
|
+
returns: v.object({
|
|
183
|
+
success: v.boolean(),
|
|
184
|
+
seq: v.number()
|
|
185
|
+
}),
|
|
186
|
+
handler: async (ctx, args) => {
|
|
187
|
+
const documentId = args.documentId;
|
|
188
|
+
if (opts?.evalRemove) await opts.evalRemove(ctx, documentId);
|
|
189
|
+
const result = await ctx.runMutation(component.public.deleteDocument, {
|
|
190
|
+
collection,
|
|
191
|
+
documentId,
|
|
192
|
+
crdtBytes: args.crdtBytes
|
|
193
|
+
});
|
|
194
|
+
const existing = await ctx.db.query(collection).withIndex("by_doc_id", (q) => q.eq("id", documentId)).first();
|
|
195
|
+
if (existing) await ctx.db.delete(existing._id);
|
|
196
|
+
if (opts?.onRemove) await opts.onRemove(ctx, documentId);
|
|
197
|
+
return {
|
|
198
|
+
success: true,
|
|
199
|
+
seq: result.seq
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
createMarkMutation(opts) {
|
|
205
|
+
const component = this.component;
|
|
206
|
+
const collection = this.collectionName;
|
|
207
|
+
return mutationGeneric({
|
|
208
|
+
args: {
|
|
209
|
+
peerId: v.string(),
|
|
210
|
+
syncedSeq: v.number()
|
|
211
|
+
},
|
|
212
|
+
returns: v.null(),
|
|
213
|
+
handler: async (ctx, args) => {
|
|
214
|
+
if (opts?.evalWrite) await opts.evalWrite(ctx, args.peerId);
|
|
215
|
+
await ctx.runMutation(component.public.mark, {
|
|
216
|
+
collection,
|
|
217
|
+
peerId: args.peerId,
|
|
218
|
+
syncedSeq: args.syncedSeq
|
|
219
|
+
});
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
createCompactMutation(opts) {
|
|
225
|
+
const component = this.component;
|
|
226
|
+
const collection = this.collectionName;
|
|
227
|
+
return mutationGeneric({
|
|
228
|
+
args: {
|
|
229
|
+
documentId: v.string(),
|
|
230
|
+
snapshotBytes: v.bytes(),
|
|
231
|
+
stateVector: v.bytes(),
|
|
232
|
+
peerTimeout: v.optional(v.number())
|
|
233
|
+
},
|
|
234
|
+
returns: v.object({
|
|
235
|
+
success: v.boolean(),
|
|
236
|
+
removed: v.number(),
|
|
237
|
+
retained: v.number()
|
|
238
|
+
}),
|
|
239
|
+
handler: async (ctx, args) => {
|
|
240
|
+
if (opts?.evalWrite) await opts.evalWrite(ctx, args.documentId);
|
|
241
|
+
return await ctx.runMutation(component.public.compact, {
|
|
242
|
+
collection,
|
|
243
|
+
documentId: args.documentId,
|
|
244
|
+
snapshotBytes: args.snapshotBytes,
|
|
245
|
+
stateVector: args.stateVector,
|
|
246
|
+
peerTimeout: args.peerTimeout
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
createRecoveryQuery(opts) {
|
|
252
|
+
const component = this.component;
|
|
253
|
+
const collection = this.collectionName;
|
|
254
|
+
return queryGeneric({
|
|
255
|
+
args: { clientStateVector: v.bytes() },
|
|
256
|
+
returns: v.object({
|
|
257
|
+
diff: v.optional(v.bytes()),
|
|
258
|
+
serverStateVector: v.bytes(),
|
|
259
|
+
cursor: v.number()
|
|
260
|
+
}),
|
|
261
|
+
handler: async (ctx, args) => {
|
|
262
|
+
if (opts?.evalRead) await opts.evalRead(ctx, collection);
|
|
263
|
+
return await ctx.runQuery(component.public.recovery, {
|
|
264
|
+
collection,
|
|
265
|
+
clientStateVector: args.clientStateVector
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
//#endregion
|
|
273
|
+
//#region src/server/builder.ts
|
|
274
|
+
/**
|
|
275
|
+
* Create a replicate function bound to your component. Call this once in your
|
|
276
|
+
* convex/replicate.ts file, then use the returned function for all collections.
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```typescript
|
|
280
|
+
* // convex/replicate.ts (create once)
|
|
281
|
+
* import { replicate } from '@trestleinc/replicate/server';
|
|
282
|
+
* import { components } from './_generated/api';
|
|
283
|
+
*
|
|
284
|
+
* export const tasks = replicate(components.replicate)<Task>({ collection: 'tasks' });
|
|
285
|
+
*
|
|
286
|
+
* // Or bind once and reuse:
|
|
287
|
+
* const r = replicate(components.replicate);
|
|
288
|
+
* export const tasks = r<Task>({ collection: 'tasks' });
|
|
289
|
+
* export const notebooks = r<Notebook>({ collection: 'notebooks' });
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
function replicate(component) {
|
|
293
|
+
return function boundReplicate(config) {
|
|
294
|
+
return replicateInternal(component, config);
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Internal implementation for replicate.
|
|
299
|
+
*/
|
|
300
|
+
function replicateInternal(component, config) {
|
|
301
|
+
const storage = new Replicate(component, config.collection, config.compaction);
|
|
302
|
+
return {
|
|
303
|
+
__collection: config.collection,
|
|
304
|
+
stream: storage.createStreamQuery({
|
|
305
|
+
evalRead: config.hooks?.evalRead,
|
|
306
|
+
onStream: config.hooks?.onStream
|
|
307
|
+
}),
|
|
308
|
+
material: storage.createSSRQuery({
|
|
309
|
+
evalRead: config.hooks?.evalRead,
|
|
310
|
+
transform: config.hooks?.transform
|
|
311
|
+
}),
|
|
312
|
+
recovery: storage.createRecoveryQuery({ evalRead: config.hooks?.evalRead }),
|
|
313
|
+
insert: storage.createInsertMutation({
|
|
314
|
+
evalWrite: config.hooks?.evalWrite,
|
|
315
|
+
onInsert: config.hooks?.onInsert
|
|
316
|
+
}),
|
|
317
|
+
update: storage.createUpdateMutation({
|
|
318
|
+
evalWrite: config.hooks?.evalWrite,
|
|
319
|
+
onUpdate: config.hooks?.onUpdate
|
|
320
|
+
}),
|
|
321
|
+
remove: storage.createRemoveMutation({
|
|
322
|
+
evalRemove: config.hooks?.evalRemove,
|
|
323
|
+
onRemove: config.hooks?.onRemove
|
|
324
|
+
}),
|
|
325
|
+
mark: storage.createMarkMutation({ evalWrite: config.hooks?.evalMark }),
|
|
326
|
+
compact: storage.createCompactMutation({ evalWrite: config.hooks?.evalCompact })
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
//#endregion
|
|
331
|
+
//#region src/server/schema.ts
|
|
332
|
+
const prose = () => v.object({
|
|
333
|
+
type: v.literal("doc"),
|
|
334
|
+
content: v.optional(v.array(v.any()))
|
|
335
|
+
});
|
|
336
|
+
/**
|
|
337
|
+
* Define a table with automatic timestamp field for replication.
|
|
338
|
+
* All replicated tables must have an `id` field and define a `by_doc_id` index.
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```typescript
|
|
342
|
+
* // convex/schema.ts
|
|
343
|
+
* export default defineSchema({
|
|
344
|
+
* tasks: table(
|
|
345
|
+
* { id: v.string(), text: v.string(), isCompleted: v.boolean() },
|
|
346
|
+
* (t) => t.index('by_doc_id', ['id']).index('by_completed', ['isCompleted'])
|
|
347
|
+
* ),
|
|
348
|
+
* });
|
|
349
|
+
* ```
|
|
350
|
+
*/
|
|
351
|
+
function table(userFields, applyIndexes) {
|
|
352
|
+
const tbl = defineTable({
|
|
353
|
+
...userFields,
|
|
354
|
+
timestamp: v.number()
|
|
355
|
+
});
|
|
356
|
+
if (applyIndexes) return applyIndexes(tbl);
|
|
357
|
+
return tbl;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
//#endregion
|
|
361
|
+
//#region src/server/index.ts
|
|
362
|
+
const schema = {
|
|
363
|
+
table,
|
|
364
|
+
prose
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
//#endregion
|
|
368
|
+
export { replicate, schema };
|
package/dist/shared/index.d.ts
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
|
+
//#region src/shared/types.d.ts
|
|
2
|
+
|
|
3
|
+
/** ProseMirror-compatible JSON for XmlFragment serialization */
|
|
4
|
+
interface XmlFragmentJSON {
|
|
5
|
+
type: "doc";
|
|
6
|
+
content?: XmlNodeJSON[];
|
|
7
|
+
}
|
|
8
|
+
declare const PROSE_BRAND: unique symbol;
|
|
1
9
|
/**
|
|
2
|
-
*
|
|
10
|
+
* Branded prose type for Zod schemas.
|
|
11
|
+
* Extends XmlFragmentJSON with a unique brand for type-level detection.
|
|
12
|
+
* Use the `prose()` helper from `@trestleinc/replicate/client` to create this type.
|
|
3
13
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
14
|
+
interface ProseValue extends XmlFragmentJSON {
|
|
15
|
+
readonly [PROSE_BRAND]: typeof PROSE_BRAND;
|
|
16
|
+
}
|
|
17
|
+
/** ProseMirror node structure */
|
|
18
|
+
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
|
+
//#endregion
|
|
29
|
+
export { type ProseValue, type XmlFragmentJSON, type XmlNodeJSON };
|
package/dist/shared/index.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { OperationType };
|
|
1
|
+
export { };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trestleinc/replicate",
|
|
3
|
-
"version": "1.1.0",
|
|
3
|
+
"version": "1.1.2-preview.0",
|
|
4
4
|
"description": "Offline-first data replication with Yjs CRDTs and Convex",
|
|
5
5
|
"repository": "github:trestleinc/replicate",
|
|
6
6
|
"homepage": "https://github.com/trestleinc/replicate#readme",
|
|
@@ -19,21 +19,29 @@
|
|
|
19
19
|
"type": "module",
|
|
20
20
|
"private": false,
|
|
21
21
|
"resolutions": {
|
|
22
|
-
"convex": "
|
|
22
|
+
"convex": "1.31.2",
|
|
23
|
+
"@standard-schema/spec": "1.1.0",
|
|
24
|
+
"zod": "4.2.1"
|
|
23
25
|
},
|
|
24
26
|
"overrides": {
|
|
25
|
-
"convex": "
|
|
27
|
+
"convex": "1.31.2",
|
|
28
|
+
"@standard-schema/spec": "1.1.0",
|
|
29
|
+
"zod": "4.2.1"
|
|
26
30
|
},
|
|
27
31
|
"scripts": {
|
|
28
|
-
"build": "
|
|
29
|
-
"dev": "
|
|
30
|
-
"clean": "rm -rf dist"
|
|
32
|
+
"build": "tsdown",
|
|
33
|
+
"dev": "tsdown --watch",
|
|
34
|
+
"clean": "rm -rf dist",
|
|
35
|
+
"lint": "eslint .",
|
|
36
|
+
"lint:fix": "eslint . --fix",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"test:watch": "vitest"
|
|
31
39
|
},
|
|
32
40
|
"exports": {
|
|
33
41
|
".": {
|
|
34
42
|
"import": {
|
|
35
43
|
"types": "./dist/shared/index.d.ts",
|
|
36
|
-
"default": "./dist/shared.js"
|
|
44
|
+
"default": "./dist/shared/index.js"
|
|
37
45
|
}
|
|
38
46
|
},
|
|
39
47
|
"./package.json": "./package.json",
|
|
@@ -41,13 +49,13 @@
|
|
|
41
49
|
"import": {
|
|
42
50
|
"@convex-dev/component-source": "./src/client/index.ts",
|
|
43
51
|
"types": "./dist/client/index.d.ts",
|
|
44
|
-
"default": "./dist/index.js"
|
|
52
|
+
"default": "./dist/client/index.js"
|
|
45
53
|
}
|
|
46
54
|
},
|
|
47
55
|
"./server": {
|
|
48
56
|
"import": {
|
|
49
57
|
"types": "./dist/server/index.d.ts",
|
|
50
|
-
"default": "./dist/server.js"
|
|
58
|
+
"default": "./dist/server/index.js"
|
|
51
59
|
}
|
|
52
60
|
},
|
|
53
61
|
"./convex.config": {
|
|
@@ -65,16 +73,13 @@
|
|
|
65
73
|
"LICENSE"
|
|
66
74
|
],
|
|
67
75
|
"dependencies": {
|
|
68
|
-
"@effect/platform": "0.
|
|
69
|
-
"@logtape/logtape": "1.
|
|
70
|
-
"
|
|
71
|
-
"browser-level": "3.0.0",
|
|
72
|
-
"effect": "3.19.12",
|
|
73
|
-
"y-indexeddb": "9.0.12",
|
|
74
|
-
"y-leveldb": "0.2.0"
|
|
76
|
+
"@effect/platform": "0.94.0",
|
|
77
|
+
"@logtape/logtape": "1.3.5",
|
|
78
|
+
"effect": "3.19.13"
|
|
75
79
|
},
|
|
76
80
|
"peerDependencies": {
|
|
77
|
-
"@
|
|
81
|
+
"@standard-schema/spec": "^1.1.0",
|
|
82
|
+
"@tanstack/db": "^0.5.15",
|
|
78
83
|
"convex": "^1.31.0",
|
|
79
84
|
"lib0": "^0.2.0",
|
|
80
85
|
"yjs": "^13.6.0"
|
|
@@ -91,27 +96,27 @@
|
|
|
91
96
|
"@convex-dev/eslint-plugin": "1.1.1",
|
|
92
97
|
"@edge-runtime/vm": "5.0.0",
|
|
93
98
|
"@microsoft/api-extractor": "7.55.2",
|
|
94
|
-
"@
|
|
95
|
-
"@rsbuild/plugin-type-check": "1.3.2",
|
|
96
|
-
"@rslib/core": "0.18.4",
|
|
99
|
+
"@standard-schema/spec": "1.1.0",
|
|
97
100
|
"@stylistic/eslint-plugin": "5.6.1",
|
|
98
|
-
"@tanstack/db": "0.5.
|
|
99
|
-
"@types/node": "25.0.
|
|
101
|
+
"@tanstack/db": "0.5.15",
|
|
102
|
+
"@types/node": "25.0.3",
|
|
100
103
|
"@types/sql.js": "1.4.9",
|
|
101
|
-
"convex": "1.31.
|
|
104
|
+
"convex": "1.31.2",
|
|
102
105
|
"dotenv": "17.2.3",
|
|
103
106
|
"eslint": "9.39.2",
|
|
104
107
|
"fake-indexeddb": "6.2.5",
|
|
105
108
|
"globals": "16.5.0",
|
|
106
|
-
"lib0": "0.2.
|
|
109
|
+
"lib0": "0.2.116",
|
|
110
|
+
"publint": "0.3.16",
|
|
111
|
+
"tsdown": "0.18.2",
|
|
107
112
|
"typescript": "5.9.3",
|
|
108
|
-
"typescript-eslint": "8.50.
|
|
109
|
-
"yjs": "13.6.
|
|
110
|
-
"zod": "4.1
|
|
113
|
+
"typescript-eslint": "8.50.1",
|
|
114
|
+
"yjs": "13.6.28",
|
|
115
|
+
"zod": "4.2.1"
|
|
111
116
|
},
|
|
112
|
-
"main": "./dist/shared.js",
|
|
117
|
+
"main": "./dist/shared/index.js",
|
|
113
118
|
"types": "./dist/shared/index.d.ts",
|
|
114
|
-
"module": "./dist/shared.js",
|
|
119
|
+
"module": "./dist/shared/index.js",
|
|
115
120
|
"publishConfig": {
|
|
116
121
|
"access": "public"
|
|
117
122
|
}
|