@defensestation/ysync-go 0.1.0-dev.12.d04ad84
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 +145 -0
- package/dist/client/connect-yjs-provider.d.ts +107 -0
- package/dist/client/connect-yjs-provider.d.ts.map +1 -0
- package/dist/client/connect-yjs-provider.js +333 -0
- package/dist/client/connect-yjs-provider.js.map +1 -0
- package/dist/client/id.d.ts +2 -0
- package/dist/client/id.d.ts.map +1 -0
- package/dist/client/id.js +21 -0
- package/dist/client/id.js.map +1 -0
- package/dist/client/provider-status.d.ts +8 -0
- package/dist/client/provider-status.d.ts.map +1 -0
- package/dist/client/provider-status.js +2 -0
- package/dist/client/provider-status.js.map +1 -0
- package/dist/client/websocket-yjs-provider.d.ts +51 -0
- package/dist/client/websocket-yjs-provider.d.ts.map +1 -0
- package/dist/client/websocket-yjs-provider.js +270 -0
- package/dist/client/websocket-yjs-provider.js.map +1 -0
- package/dist/compaction/cli.d.ts +3 -0
- package/dist/compaction/cli.d.ts.map +1 -0
- package/dist/compaction/cli.js +124 -0
- package/dist/compaction/cli.js.map +1 -0
- package/dist/compaction/compaction.d.ts +67 -0
- package/dist/compaction/compaction.d.ts.map +1 -0
- package/dist/compaction/compaction.js +153 -0
- package/dist/compaction/compaction.js.map +1 -0
- package/dist/compaction/index.d.ts +3 -0
- package/dist/compaction/index.d.ts.map +1 -0
- package/dist/compaction/index.js +2 -0
- package/dist/compaction/index.js.map +1 -0
- package/dist/gen/ysync/v1/sync_pb.d.ts +630 -0
- package/dist/gen/ysync/v1/sync_pb.d.ts.map +1 -0
- package/dist/gen/ysync/v1/sync_pb.js +133 -0
- package/dist/gen/ysync/v1/sync_pb.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// Compaction adapter for ysync-go servers.
|
|
2
|
+
//
|
|
3
|
+
// The server's update log grows until the host folds it (see the repository
|
|
4
|
+
// README's Compaction section). This module implements the recommended
|
|
5
|
+
// policy — once a document exceeds `threshold` log records, fold everything
|
|
6
|
+
// except the newest `keep` records into one snapshot — using **real Yjs** to
|
|
7
|
+
// build the snapshot. That matters: Go-side merges corrupt documents with
|
|
8
|
+
// nested XML content (BlockNote, ProseMirror, Tiptap); see
|
|
9
|
+
// adapters/yjs/ygo/README.md and its xmlrepro/ cross-implementation check.
|
|
10
|
+
//
|
|
11
|
+
// The functions are runtime-agnostic on purpose: call `compactDocuments`
|
|
12
|
+
// from an AWS Lambda handler, a Cloud Run job, a Kubernetes CronJob (the
|
|
13
|
+
// bundled `ysync-compact` CLI), or any Node 18+ process with `fetch`.
|
|
14
|
+
//
|
|
15
|
+
// Security: `CompactDocument` rewrites stored history, so gate it with
|
|
16
|
+
// `Authz.CanCompact` on the server and give **only** this job's identity
|
|
17
|
+
// that permission. Pass the credential via `headers`.
|
|
18
|
+
import * as Y from "yjs";
|
|
19
|
+
const DEFAULT_THRESHOLD = 200;
|
|
20
|
+
const DEFAULT_KEEP = 50;
|
|
21
|
+
/**
|
|
22
|
+
* Compacts one document if its update log exceeds the policy threshold:
|
|
23
|
+
* lists the stored log, replays all but the newest `keep` records into a
|
|
24
|
+
* fresh Y.Doc, encodes the state with `Y.encodeStateAsUpdate`, and calls
|
|
25
|
+
* `CompactDocument` for the covered sequence. Idempotent — re-running
|
|
26
|
+
* converges to the same folded log, and updates pushed concurrently are
|
|
27
|
+
* unaffected (they sort after `compactThroughSequence`).
|
|
28
|
+
*/
|
|
29
|
+
export async function compactDocument(options, docId) {
|
|
30
|
+
if (!docId) {
|
|
31
|
+
throw new Error("docId is required");
|
|
32
|
+
}
|
|
33
|
+
const threshold = options.threshold ?? DEFAULT_THRESHOLD;
|
|
34
|
+
const keep = options.keep ?? DEFAULT_KEEP;
|
|
35
|
+
if (threshold < 1 || keep < 0) {
|
|
36
|
+
throw new Error(`invalid policy: threshold ${threshold}, keep ${keep}`);
|
|
37
|
+
}
|
|
38
|
+
const listed = (await rpc(options, "ListDocumentUpdates", { docId }));
|
|
39
|
+
const updates = listed.updates ?? [];
|
|
40
|
+
const skip = {
|
|
41
|
+
docId,
|
|
42
|
+
compacted: false,
|
|
43
|
+
records: updates.length,
|
|
44
|
+
};
|
|
45
|
+
if (updates.length <= threshold) {
|
|
46
|
+
return skip;
|
|
47
|
+
}
|
|
48
|
+
// Records arrive in sequence order; the first may already be a snapshot
|
|
49
|
+
// from a prior run. Folding only that snapshot again would be a no-op.
|
|
50
|
+
const cutoff = updates.length - keep;
|
|
51
|
+
if (cutoff < 1 || (cutoff === 1 && updates[0]?.isSnapshot)) {
|
|
52
|
+
return skip;
|
|
53
|
+
}
|
|
54
|
+
const target = updates[cutoff - 1];
|
|
55
|
+
if (target?.sequence === undefined) {
|
|
56
|
+
throw new Error(`${docId}: log record ${cutoff - 1} has no sequence`);
|
|
57
|
+
}
|
|
58
|
+
const doc = new Y.Doc();
|
|
59
|
+
for (const record of updates.slice(0, cutoff)) {
|
|
60
|
+
if (record.yjsUpdate) {
|
|
61
|
+
Y.applyUpdate(doc, fromBase64(record.yjsUpdate));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const snapshot = Y.encodeStateAsUpdate(doc);
|
|
65
|
+
doc.destroy();
|
|
66
|
+
await rpc(options, "CompactDocument", {
|
|
67
|
+
docId,
|
|
68
|
+
compactUpdate: toBase64(snapshot),
|
|
69
|
+
compactThroughSequence: target.sequence,
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
docId,
|
|
73
|
+
compacted: true,
|
|
74
|
+
records: updates.length,
|
|
75
|
+
foldedRecords: cutoff,
|
|
76
|
+
foldedThroughSequence: String(target.sequence),
|
|
77
|
+
snapshotBytes: snapshot.byteLength,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Compacts many documents sequentially (compaction is background work;
|
|
82
|
+
* predictable server load beats speed here). Per-document failures are
|
|
83
|
+
* collected in `errors` instead of aborting the run.
|
|
84
|
+
*/
|
|
85
|
+
export async function compactDocuments(options, docIds) {
|
|
86
|
+
const run = {
|
|
87
|
+
results: [],
|
|
88
|
+
errors: [],
|
|
89
|
+
compacted: 0,
|
|
90
|
+
skipped: 0,
|
|
91
|
+
};
|
|
92
|
+
for (const docId of docIds) {
|
|
93
|
+
try {
|
|
94
|
+
const result = await compactDocument(options, docId);
|
|
95
|
+
run.results.push(result);
|
|
96
|
+
if (result.compacted) {
|
|
97
|
+
run.compacted++;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
run.skipped++;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
run.errors.push({ docId, error });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return run;
|
|
108
|
+
}
|
|
109
|
+
/** Plain Connect JSON: POST /<service>/<method> with a JSON body. */
|
|
110
|
+
async function rpc(options, method, body) {
|
|
111
|
+
const headers = typeof options.headers === "function"
|
|
112
|
+
? await options.headers()
|
|
113
|
+
: (options.headers ?? {});
|
|
114
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
115
|
+
const base = options.baseUrl.replace(/\/+$/, "");
|
|
116
|
+
const response = await doFetch(`${base}/ysync.v1.YjsSyncService/${method}`, {
|
|
117
|
+
method: "POST",
|
|
118
|
+
headers: { "content-type": "application/json", ...headers },
|
|
119
|
+
body: JSON.stringify(body),
|
|
120
|
+
});
|
|
121
|
+
if (!response.ok) {
|
|
122
|
+
const detail = await response.text().catch(() => "");
|
|
123
|
+
throw new Error(`${method}: HTTP ${response.status}${detail ? `: ${detail}` : ""}`);
|
|
124
|
+
}
|
|
125
|
+
return response.json();
|
|
126
|
+
}
|
|
127
|
+
function nodeBuffer() {
|
|
128
|
+
return globalThis.Buffer;
|
|
129
|
+
}
|
|
130
|
+
function toBase64(bytes) {
|
|
131
|
+
const buffer = nodeBuffer();
|
|
132
|
+
if (buffer) {
|
|
133
|
+
return buffer.from(bytes).toString("base64");
|
|
134
|
+
}
|
|
135
|
+
let binary = "";
|
|
136
|
+
for (const byte of bytes) {
|
|
137
|
+
binary += String.fromCharCode(byte);
|
|
138
|
+
}
|
|
139
|
+
return btoa(binary);
|
|
140
|
+
}
|
|
141
|
+
function fromBase64(encoded) {
|
|
142
|
+
const buffer = nodeBuffer();
|
|
143
|
+
if (buffer) {
|
|
144
|
+
return Uint8Array.from(buffer.from(encoded, "base64"));
|
|
145
|
+
}
|
|
146
|
+
const binary = atob(encoded);
|
|
147
|
+
const out = new Uint8Array(binary.length);
|
|
148
|
+
for (let i = 0; i < binary.length; i++) {
|
|
149
|
+
out[i] = binary.charCodeAt(i);
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=compaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction.js","sourceRoot":"","sources":["../../src/compaction/compaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compaction/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACpE,YAAY,EACV,uBAAuB,EACvB,gBAAgB,EAChB,sBAAsB,EACtB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compaction/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
|