@deepfates/lync 0.3.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/LICENSE +21 -0
- package/README.md +374 -0
- package/bin/lync.js +2 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +5 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/index.d.ts +11 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +404 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/sync.d.ts +42 -0
- package/dist/cli/sync.d.ts.map +1 -0
- package/dist/cli/sync.js +232 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/client/create.d.ts +10 -0
- package/dist/client/create.d.ts.map +1 -0
- package/dist/client/create.js +50 -0
- package/dist/client/create.js.map +1 -0
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +3 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/testing.d.ts +14 -0
- package/dist/client/testing.d.ts.map +1 -0
- package/dist/client/testing.js +19 -0
- package/dist/client/testing.js.map +1 -0
- package/dist/client/types.d.ts +49 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +39 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.d.ts +119 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +686 -0
- package/dist/events.js.map +1 -0
- package/dist/file-log.d.ts +25 -0
- package/dist/file-log.d.ts.map +1 -0
- package/dist/file-log.js +97 -0
- package/dist/file-log.js.map +1 -0
- package/dist/idb-log.d.ts +21 -0
- package/dist/idb-log.d.ts.map +1 -0
- package/dist/idb-log.js +114 -0
- package/dist/idb-log.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/indexes/entries.d.ts +6 -0
- package/dist/indexes/entries.d.ts.map +1 -0
- package/dist/indexes/entries.js +7 -0
- package/dist/indexes/entries.js.map +1 -0
- package/dist/indexes/index.d.ts +3 -0
- package/dist/indexes/index.d.ts.map +1 -0
- package/dist/indexes/index.js +3 -0
- package/dist/indexes/index.js.map +1 -0
- package/dist/indexes/memory.d.ts +4 -0
- package/dist/indexes/memory.d.ts.map +1 -0
- package/dist/indexes/memory.js +193 -0
- package/dist/indexes/memory.js.map +1 -0
- package/dist/indexes/types.d.ts +70 -0
- package/dist/indexes/types.d.ts.map +1 -0
- package/dist/indexes/types.js +2 -0
- package/dist/indexes/types.js.map +1 -0
- package/dist/json.d.ts +3 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +15 -0
- package/dist/json.js.map +1 -0
- package/dist/looms.d.ts +25 -0
- package/dist/looms.d.ts.map +1 -0
- package/dist/looms.js +359 -0
- package/dist/looms.js.map +1 -0
- package/dist/memory-log.d.ts +5 -0
- package/dist/memory-log.d.ts.map +1 -0
- package/dist/memory-log.js +7 -0
- package/dist/memory-log.js.map +1 -0
- package/dist/memory.d.ts +4 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +255 -0
- package/dist/memory.js.map +1 -0
- package/dist/profiles/text-story.d.ts +26 -0
- package/dist/profiles/text-story.d.ts.map +1 -0
- package/dist/profiles/text-story.js +55 -0
- package/dist/profiles/text-story.js.map +1 -0
- package/dist/references.d.ts +14 -0
- package/dist/references.d.ts.map +1 -0
- package/dist/references.js +109 -0
- package/dist/references.js.map +1 -0
- package/dist/relay/attach.d.ts +24 -0
- package/dist/relay/attach.d.ts.map +1 -0
- package/dist/relay/attach.js +54 -0
- package/dist/relay/attach.js.map +1 -0
- package/dist/relay/index.d.ts +4 -0
- package/dist/relay/index.d.ts.map +1 -0
- package/dist/relay/index.js +4 -0
- package/dist/relay/index.js.map +1 -0
- package/dist/relay/relay.d.ts +80 -0
- package/dist/relay/relay.d.ts.map +1 -0
- package/dist/relay/relay.js +329 -0
- package/dist/relay/relay.js.map +1 -0
- package/dist/relay/serve.d.ts +18 -0
- package/dist/relay/serve.d.ts.map +1 -0
- package/dist/relay/serve.js +37 -0
- package/dist/relay/serve.js.map +1 -0
- package/dist/sha256.d.ts +2 -0
- package/dist/sha256.d.ts.map +1 -0
- package/dist/sha256.js +84 -0
- package/dist/sha256.js.map +1 -0
- package/dist/store.d.ts +97 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +250 -0
- package/dist/store.js.map +1 -0
- package/dist/sync-protocol.d.ts +80 -0
- package/dist/sync-protocol.d.ts.map +1 -0
- package/dist/sync-protocol.js +135 -0
- package/dist/sync-protocol.js.map +1 -0
- package/dist/synced-store.d.ts +68 -0
- package/dist/synced-store.d.ts.map +1 -0
- package/dist/synced-store.js +340 -0
- package/dist/synced-store.js.map +1 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/uuid.d.ts +9 -0
- package/dist/uuid.d.ts.map +1 -0
- package/dist/uuid.js +25 -0
- package/dist/uuid.js.map +1 -0
- package/dist/views.d.ts +78 -0
- package/dist/views.d.ts.map +1 -0
- package/dist/views.js +192 -0
- package/dist/views.js.map +1 -0
- package/package.json +183 -0
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { LyncEventBody } from "./events.js";
|
|
2
|
+
export interface StoredEvent {
|
|
3
|
+
body: LyncEventBody;
|
|
4
|
+
bytes: string;
|
|
5
|
+
root: string;
|
|
6
|
+
}
|
|
7
|
+
export type AppendResult = {
|
|
8
|
+
status: "added";
|
|
9
|
+
event: StoredEvent;
|
|
10
|
+
} | {
|
|
11
|
+
status: "duplicate";
|
|
12
|
+
event: StoredEvent;
|
|
13
|
+
} | {
|
|
14
|
+
status: "buffered";
|
|
15
|
+
missingParent: string;
|
|
16
|
+
bytes: string;
|
|
17
|
+
} | {
|
|
18
|
+
status: "conflict";
|
|
19
|
+
event: StoredEvent;
|
|
20
|
+
conflictWith: StoredEvent;
|
|
21
|
+
} | {
|
|
22
|
+
status: "garbage";
|
|
23
|
+
reason: string;
|
|
24
|
+
bytes: string;
|
|
25
|
+
};
|
|
26
|
+
export interface EventStore {
|
|
27
|
+
append(ev: LyncEventBody): Promise<AppendResult>;
|
|
28
|
+
union(line: string): Promise<AppendResult>;
|
|
29
|
+
byId(id: string): Promise<StoredEvent | null>;
|
|
30
|
+
byRoot(rootId: string): Promise<StoredEvent[]>;
|
|
31
|
+
subscribe(rootId: string, listener: (ev: StoredEvent) => void): () => void;
|
|
32
|
+
roots(kind?: "lync/loom" | "lync/index"): Promise<StoredEvent[]>;
|
|
33
|
+
exportRootBytes?(rootId: string): Promise<string>;
|
|
34
|
+
diagnostics?(): Promise<EventStoreDiagnostics>;
|
|
35
|
+
}
|
|
36
|
+
export interface EventStoreDiagnostics {
|
|
37
|
+
events: number;
|
|
38
|
+
conflicts: number;
|
|
39
|
+
pending: number;
|
|
40
|
+
garbage: number;
|
|
41
|
+
}
|
|
42
|
+
export interface StoreRecord {
|
|
43
|
+
id: string;
|
|
44
|
+
root: string;
|
|
45
|
+
kind: string;
|
|
46
|
+
at: string;
|
|
47
|
+
bytes: string;
|
|
48
|
+
}
|
|
49
|
+
export interface ConflictRecord {
|
|
50
|
+
id: string;
|
|
51
|
+
digest: string;
|
|
52
|
+
root: string;
|
|
53
|
+
bytes: string;
|
|
54
|
+
}
|
|
55
|
+
export interface PendingRecord {
|
|
56
|
+
missingParent: string;
|
|
57
|
+
digest: string;
|
|
58
|
+
bytes: string;
|
|
59
|
+
}
|
|
60
|
+
export interface GarbageRecord {
|
|
61
|
+
reason: string;
|
|
62
|
+
bytes: string;
|
|
63
|
+
}
|
|
64
|
+
export declare abstract class BaseEventStore implements EventStore {
|
|
65
|
+
protected readonly events: Map<string, StoredEvent>;
|
|
66
|
+
protected readonly conflicts: Map<string, ConflictRecord>;
|
|
67
|
+
protected readonly pending: Map<string, PendingRecord>;
|
|
68
|
+
protected readonly garbage: GarbageRecord[];
|
|
69
|
+
private readonly listeners;
|
|
70
|
+
append(ev: LyncEventBody): Promise<AppendResult>;
|
|
71
|
+
union(line: string): Promise<AppendResult>;
|
|
72
|
+
byId(id: string): Promise<StoredEvent | null>;
|
|
73
|
+
byRoot(rootId: string): Promise<StoredEvent[]>;
|
|
74
|
+
subscribe(rootId: string, listener: (ev: StoredEvent) => void): () => void;
|
|
75
|
+
roots(kind?: "lync/loom" | "lync/index"): Promise<StoredEvent[]>;
|
|
76
|
+
exportRootBytes(rootId: string): Promise<string>;
|
|
77
|
+
diagnostics(): Promise<EventStoreDiagnostics>;
|
|
78
|
+
protected loadRecords(records: {
|
|
79
|
+
events?: StoreRecord[];
|
|
80
|
+
conflicts?: ConflictRecord[];
|
|
81
|
+
pending?: PendingRecord[];
|
|
82
|
+
garbage?: GarbageRecord[];
|
|
83
|
+
}): Promise<void>;
|
|
84
|
+
protected dumpRecords(): {
|
|
85
|
+
events: StoreRecord[];
|
|
86
|
+
conflicts: ConflictRecord[];
|
|
87
|
+
pending: PendingRecord[];
|
|
88
|
+
garbage: GarbageRecord[];
|
|
89
|
+
};
|
|
90
|
+
protected persist(): Promise<void>;
|
|
91
|
+
private ingest;
|
|
92
|
+
private drain;
|
|
93
|
+
private emit;
|
|
94
|
+
private isConflicted;
|
|
95
|
+
}
|
|
96
|
+
export declare function serializeLyncEvent(ev: LyncEventBody): string;
|
|
97
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { parseLyncFiles } from "./events.js";
|
|
2
|
+
import { sha256Hex } from "./sha256.js";
|
|
3
|
+
export class BaseEventStore {
|
|
4
|
+
events = new Map();
|
|
5
|
+
conflicts = new Map();
|
|
6
|
+
pending = new Map();
|
|
7
|
+
garbage = [];
|
|
8
|
+
listeners = new Map();
|
|
9
|
+
async append(ev) {
|
|
10
|
+
return this.ingest(serializeLyncEvent(ev), false);
|
|
11
|
+
}
|
|
12
|
+
async union(line) {
|
|
13
|
+
return this.ingest(line, true);
|
|
14
|
+
}
|
|
15
|
+
async byId(id) {
|
|
16
|
+
return this.isConflicted(id) ? null : (this.events.get(id) ?? null);
|
|
17
|
+
}
|
|
18
|
+
async byRoot(rootId) {
|
|
19
|
+
return [...this.events.values()]
|
|
20
|
+
.filter((event) => event.root === rootId && !this.isConflicted(event.body.id))
|
|
21
|
+
.sort(compareStored);
|
|
22
|
+
}
|
|
23
|
+
subscribe(rootId, listener) {
|
|
24
|
+
const set = this.listeners.get(rootId) ?? new Set();
|
|
25
|
+
set.add(listener);
|
|
26
|
+
this.listeners.set(rootId, set);
|
|
27
|
+
return () => set.delete(listener);
|
|
28
|
+
}
|
|
29
|
+
async roots(kind) {
|
|
30
|
+
return [...this.events.values()]
|
|
31
|
+
.filter((event) => !this.isConflicted(event.body.id))
|
|
32
|
+
.filter((event) => event.body.id === event.root)
|
|
33
|
+
.filter((event) => event.body.kind === "lync/loom" || event.body.kind === "lync/index")
|
|
34
|
+
.filter((event) => kind === undefined || event.body.kind === kind)
|
|
35
|
+
.sort(compareStored);
|
|
36
|
+
}
|
|
37
|
+
async exportRootBytes(rootId) {
|
|
38
|
+
const events = await this.byRoot(rootId);
|
|
39
|
+
return events.map((event) => event.bytes).join("\n") + (events.length ? "\n" : "");
|
|
40
|
+
}
|
|
41
|
+
async diagnostics() {
|
|
42
|
+
return {
|
|
43
|
+
events: this.events.size,
|
|
44
|
+
conflicts: this.conflicts.size,
|
|
45
|
+
pending: this.pending.size,
|
|
46
|
+
garbage: this.garbage.length,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async loadRecords(records) {
|
|
50
|
+
for (const record of records.events ?? []) {
|
|
51
|
+
const parsed = parseStoredLine(record.bytes);
|
|
52
|
+
if (parsed.ok)
|
|
53
|
+
this.events.set(parsed.event.body.id, { ...parsed.event, root: record.root });
|
|
54
|
+
}
|
|
55
|
+
for (const record of records.conflicts ?? []) {
|
|
56
|
+
this.conflicts.set(conflictKey(record.id, record.digest), record);
|
|
57
|
+
}
|
|
58
|
+
for (const record of records.pending ?? []) {
|
|
59
|
+
this.pending.set(pendingKey(record.missingParent, record.digest), record);
|
|
60
|
+
}
|
|
61
|
+
this.garbage.push(...(records.garbage ?? []));
|
|
62
|
+
}
|
|
63
|
+
dumpRecords() {
|
|
64
|
+
return {
|
|
65
|
+
events: [...this.events.values()].map((event) => ({
|
|
66
|
+
id: event.body.id,
|
|
67
|
+
root: event.root,
|
|
68
|
+
kind: event.body.kind,
|
|
69
|
+
at: event.body.at,
|
|
70
|
+
bytes: event.bytes,
|
|
71
|
+
})),
|
|
72
|
+
conflicts: [...this.conflicts.values()],
|
|
73
|
+
pending: [...this.pending.values()],
|
|
74
|
+
garbage: [...this.garbage],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async persist() { }
|
|
78
|
+
async ingest(line, allowBuffer) {
|
|
79
|
+
const parsed = parseStoredLine(line);
|
|
80
|
+
if (!parsed.ok) {
|
|
81
|
+
const result = { status: "garbage", reason: parsed.reason, bytes: line };
|
|
82
|
+
this.garbage.push({ reason: parsed.reason, bytes: line });
|
|
83
|
+
await this.persist();
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
const body = parsed.event.body;
|
|
87
|
+
const known = validateKnownLyncEvent(body);
|
|
88
|
+
if (known !== null) {
|
|
89
|
+
const result = { status: "garbage", reason: known, bytes: line };
|
|
90
|
+
this.garbage.push({ reason: known, bytes: line });
|
|
91
|
+
await this.persist();
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
const parent = body.parents[0];
|
|
95
|
+
const root = body.parents.length === 0
|
|
96
|
+
? body.id
|
|
97
|
+
: this.events.get(parent)?.root;
|
|
98
|
+
if (root === undefined) {
|
|
99
|
+
if (!allowBuffer) {
|
|
100
|
+
const result = { status: "garbage", reason: `missing parent: ${parent}`, bytes: line };
|
|
101
|
+
this.garbage.push({ reason: result.reason, bytes: line });
|
|
102
|
+
await this.persist();
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
const record = { missingParent: parent, digest: bodyDigest(parsed.bodyBytes), bytes: line };
|
|
106
|
+
this.pending.set(pendingKey(record.missingParent, record.digest), record);
|
|
107
|
+
await this.persist();
|
|
108
|
+
return { status: "buffered", missingParent: parent, bytes: line };
|
|
109
|
+
}
|
|
110
|
+
const event = { ...parsed.event, root };
|
|
111
|
+
const existing = this.events.get(body.id);
|
|
112
|
+
if (existing) {
|
|
113
|
+
if (stripSplice(existing.bytes) === stripSplice(line)) {
|
|
114
|
+
if (isRicherLine(line, existing.bytes)) {
|
|
115
|
+
this.events.set(body.id, event);
|
|
116
|
+
await this.persist();
|
|
117
|
+
}
|
|
118
|
+
return { status: "duplicate", event: this.events.get(body.id) };
|
|
119
|
+
}
|
|
120
|
+
const digest = bodyDigest(parsed.bodyBytes);
|
|
121
|
+
const existingDigest = bodyDigest(new TextEncoder().encode(stripSplice(existing.bytes)));
|
|
122
|
+
this.conflicts.set(conflictKey(existing.body.id, existingDigest), {
|
|
123
|
+
id: existing.body.id,
|
|
124
|
+
digest: existingDigest,
|
|
125
|
+
root: existing.root,
|
|
126
|
+
bytes: existing.bytes,
|
|
127
|
+
});
|
|
128
|
+
this.conflicts.set(conflictKey(body.id, digest), { id: body.id, digest, root, bytes: line });
|
|
129
|
+
await this.persist();
|
|
130
|
+
return { status: "conflict", event, conflictWith: existing };
|
|
131
|
+
}
|
|
132
|
+
this.events.set(body.id, event);
|
|
133
|
+
await this.persist();
|
|
134
|
+
this.emit(event);
|
|
135
|
+
await this.drain(body.id);
|
|
136
|
+
return { status: "added", event };
|
|
137
|
+
}
|
|
138
|
+
async drain(parentId) {
|
|
139
|
+
let progressed = true;
|
|
140
|
+
while (progressed) {
|
|
141
|
+
progressed = false;
|
|
142
|
+
for (const record of [...this.pending.values()].filter((item) => item.missingParent === parentId)) {
|
|
143
|
+
this.pending.delete(pendingKey(record.missingParent, record.digest));
|
|
144
|
+
await this.ingest(record.bytes, true);
|
|
145
|
+
progressed = true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
emit(event) {
|
|
150
|
+
for (const listener of this.listeners.get(event.root) ?? [])
|
|
151
|
+
listener(event);
|
|
152
|
+
}
|
|
153
|
+
isConflicted(id) {
|
|
154
|
+
for (const conflict of this.conflicts.values())
|
|
155
|
+
if (conflict.id === id)
|
|
156
|
+
return true;
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export function serializeLyncEvent(ev) {
|
|
161
|
+
const fields = [
|
|
162
|
+
["v", ev.v],
|
|
163
|
+
["id", ev.id],
|
|
164
|
+
["kind", ev.kind],
|
|
165
|
+
["at", ev.at],
|
|
166
|
+
["author", ev.author],
|
|
167
|
+
["parents", ev.parents],
|
|
168
|
+
["payload", ev.payload],
|
|
169
|
+
];
|
|
170
|
+
if (ev.marked !== undefined)
|
|
171
|
+
fields.push(["marked", ev.marked]);
|
|
172
|
+
if (ev.critical !== undefined)
|
|
173
|
+
fields.push(["critical", ev.critical]);
|
|
174
|
+
return `{${fields.map(([key, value]) => `${JSON.stringify(key)}:${JSON.stringify(value)}`).join(",")}}`;
|
|
175
|
+
}
|
|
176
|
+
function parseStoredLine(line) {
|
|
177
|
+
const normalized = line.endsWith("\n") ? line.slice(0, -1) : line;
|
|
178
|
+
const parsed = parseLyncFiles([{ file: "<store>", bytes: `${normalized}\n` }]);
|
|
179
|
+
const diagnostic = parsed.lines[0];
|
|
180
|
+
if (!diagnostic?.event || (diagnostic.class !== "accepted" && diagnostic.class !== "nonconforming")) {
|
|
181
|
+
return { ok: false, reason: diagnostic?.reason ?? "unparseable lync line" };
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
ok: true,
|
|
185
|
+
event: { body: diagnostic.event, bytes: normalized, root: diagnostic.event.id },
|
|
186
|
+
bodyBytes: diagnostic.bodyBytes ?? new TextEncoder().encode(stripSplice(normalized)),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function validateKnownLyncEvent(body) {
|
|
190
|
+
if (!body.kind.startsWith("lync/"))
|
|
191
|
+
return null;
|
|
192
|
+
if (body.kind === "lync/loom" || body.kind === "lync/index") {
|
|
193
|
+
return body.parents.length === 0 ? null : `${body.kind} must not have parents`;
|
|
194
|
+
}
|
|
195
|
+
if (body.kind === "lync/turn") {
|
|
196
|
+
if (body.parents.length !== 1)
|
|
197
|
+
return "lync/turn must have exactly one parent";
|
|
198
|
+
if (!Number.isInteger(body.payload.ordinal) || body.payload.ordinal < 0) {
|
|
199
|
+
return "lync/turn payload.ordinal must be a non-negative integer";
|
|
200
|
+
}
|
|
201
|
+
if (!("payload" in body.payload))
|
|
202
|
+
return "lync/turn payload.payload is required";
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
if (body.kind === "lync/loom-meta") {
|
|
206
|
+
if (body.parents.length !== 1)
|
|
207
|
+
return "lync/loom-meta must have exactly one parent";
|
|
208
|
+
if (!("meta" in body.payload))
|
|
209
|
+
return "lync/loom-meta payload.meta is required";
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
if (body.kind === "lync/index-meta") {
|
|
213
|
+
if (body.parents.length !== 1)
|
|
214
|
+
return "lync/index-meta must have exactly one parent";
|
|
215
|
+
if (!("meta" in body.payload))
|
|
216
|
+
return "lync/index-meta payload.meta is required";
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
if (body.kind === "lync/index-entry") {
|
|
220
|
+
if (body.parents.length !== 1)
|
|
221
|
+
return "lync/index-entry must have exactly one parent";
|
|
222
|
+
if (typeof body.payload.loomId !== "string")
|
|
223
|
+
return "lync/index-entry payload.loomId is required";
|
|
224
|
+
if ("ordinal" in body.payload && (!Number.isInteger(body.payload.ordinal) || body.payload.ordinal < 0)) {
|
|
225
|
+
return "lync/index-entry payload.ordinal must be a non-negative integer";
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
function compareStored(a, b) {
|
|
232
|
+
return a.body.at.localeCompare(b.body.at) || a.body.id.localeCompare(b.body.id);
|
|
233
|
+
}
|
|
234
|
+
function stripSplice(line) {
|
|
235
|
+
return line.replace(/,"digest":"sha256:[0-9a-f]{64}"(?:,"sig":"[A-Za-z0-9+/]+={0,2}")?}$/, "}");
|
|
236
|
+
}
|
|
237
|
+
function bodyDigest(bytes) {
|
|
238
|
+
return sha256Hex(bytes);
|
|
239
|
+
}
|
|
240
|
+
function conflictKey(id, digest) {
|
|
241
|
+
return `${id}\0${digest}`;
|
|
242
|
+
}
|
|
243
|
+
function pendingKey(missingParent, digest) {
|
|
244
|
+
return `${missingParent}\0${digest}`;
|
|
245
|
+
}
|
|
246
|
+
function isRicherLine(candidate, current) {
|
|
247
|
+
const score = (line) => (line.includes(',"digest"') ? 1 : 0) + (line.includes(',"sig"') ? 1 : 0);
|
|
248
|
+
return score(candidate) > score(current);
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The lync line-sync protocol: a dumb event-union relay.
|
|
3
|
+
*
|
|
4
|
+
* Events are immutable and merge is union by id, so the protocol has no
|
|
5
|
+
* merge logic at all — it moves canonical line bytes between peers and lets
|
|
6
|
+
* union make redundancy harmless. Five frame kinds:
|
|
7
|
+
*
|
|
8
|
+
* client → server {"t":"sub", "root": string, "since": number}
|
|
9
|
+
* server → client {"t":"ev", "root": string, "seq": number, "line": string, "gen"?: string}
|
|
10
|
+
* server → client {"t":"live", "root": string, "seq": number, "gen"?: string}
|
|
11
|
+
* client → server {"t":"ev", "root": string, "line": string}
|
|
12
|
+
* either direction {"t":"presence", "root": string, "data"?: unknown}
|
|
13
|
+
* either direction {"t":"err", "root"?: string, "reason": string, "detail"?: string}
|
|
14
|
+
*
|
|
15
|
+
* `seq` is the server's own per-root arrival counter — a resume cursor, not
|
|
16
|
+
* event order. The server echoes accepted events to every subscriber of the
|
|
17
|
+
* root, sender included; echoes are duplicate no-ops under union and still
|
|
18
|
+
* advance the cursor. This module is pure: frame codecs and guards only.
|
|
19
|
+
*
|
|
20
|
+
* `gen` is the server's log GENERATION: a random id minted every time a room
|
|
21
|
+
* is recovered from disk (so every server restart is a new generation). A
|
|
22
|
+
* cursor is only meaningful inside the generation that issued it — a broadcast
|
|
23
|
+
* whose disk write failed still consumes a seq, so after a restart the
|
|
24
|
+
* recovered log can sit BEHIND a client's saved cursor and the client would
|
|
25
|
+
* silently skip the next persisted event forever. A client that sees `gen`
|
|
26
|
+
* change from what its cursor was saved under must reset to 0 and resync from
|
|
27
|
+
* scratch; union makes the re-download a harmless set of duplicate no-ops.
|
|
28
|
+
* The field is additive: frames without it (old servers) decode fine, and old
|
|
29
|
+
* clients ignore it.
|
|
30
|
+
*/
|
|
31
|
+
export interface SubFrame {
|
|
32
|
+
t: "sub";
|
|
33
|
+
root: string;
|
|
34
|
+
since: number;
|
|
35
|
+
}
|
|
36
|
+
export interface EvFrame {
|
|
37
|
+
t: "ev";
|
|
38
|
+
root: string;
|
|
39
|
+
line: string;
|
|
40
|
+
seq?: number;
|
|
41
|
+
/** Server log generation (see module doc). Absent from old servers and client→server frames. */
|
|
42
|
+
gen?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface LiveFrame {
|
|
45
|
+
t: "live";
|
|
46
|
+
root: string;
|
|
47
|
+
seq: number;
|
|
48
|
+
/** Server log generation (see module doc). Absent from old servers. */
|
|
49
|
+
gen?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface PresenceFrame {
|
|
52
|
+
t: "presence";
|
|
53
|
+
root: string;
|
|
54
|
+
data?: unknown;
|
|
55
|
+
}
|
|
56
|
+
export interface ErrFrame {
|
|
57
|
+
t: "err";
|
|
58
|
+
root?: string;
|
|
59
|
+
reason: string;
|
|
60
|
+
detail?: string;
|
|
61
|
+
}
|
|
62
|
+
export type SyncFrame = SubFrame | EvFrame | LiveFrame | PresenceFrame | ErrFrame;
|
|
63
|
+
/**
|
|
64
|
+
* A resume cursor / sequence number: a nonnegative integer. Fractional or
|
|
65
|
+
* non-finite values must never pass — they index into backlogs downstream.
|
|
66
|
+
*/
|
|
67
|
+
export declare function isCursor(value: unknown): value is number;
|
|
68
|
+
export declare function encodeFrame(frame: SyncFrame): string;
|
|
69
|
+
/**
|
|
70
|
+
* Decode one frame. Returns an ErrFrame (never throws) on anything
|
|
71
|
+
* malformed, so transport code stays loud without try/catch pyramids.
|
|
72
|
+
*/
|
|
73
|
+
export declare function decodeFrame(raw: string | Uint8Array): SyncFrame;
|
|
74
|
+
/**
|
|
75
|
+
* Extract the event id from a canonical line without trusting anything else
|
|
76
|
+
* in it. The relay never parses beyond this. Returns undefined when no id
|
|
77
|
+
* can be extracted — callers surface that loudly, never drop it silently.
|
|
78
|
+
*/
|
|
79
|
+
export declare function extractLineId(line: string): string | undefined;
|
|
80
|
+
//# sourceMappingURL=sync-protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-protocol.d.ts","sourceRoot":"","sources":["../src/sync-protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,KAAK,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,OAAO;IACtB,CAAC,EAAE,IAAI,CAAC;IACR,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gGAAgG;IAChG,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,SAAS;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,CAAC,EAAE,UAAU,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,KAAK,CAAC;IACT,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,aAAa,GAAG,QAAQ,CAAC;AAIlF;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAExD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAEpD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAsE/D;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAU9D"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The lync line-sync protocol: a dumb event-union relay.
|
|
3
|
+
*
|
|
4
|
+
* Events are immutable and merge is union by id, so the protocol has no
|
|
5
|
+
* merge logic at all — it moves canonical line bytes between peers and lets
|
|
6
|
+
* union make redundancy harmless. Five frame kinds:
|
|
7
|
+
*
|
|
8
|
+
* client → server {"t":"sub", "root": string, "since": number}
|
|
9
|
+
* server → client {"t":"ev", "root": string, "seq": number, "line": string, "gen"?: string}
|
|
10
|
+
* server → client {"t":"live", "root": string, "seq": number, "gen"?: string}
|
|
11
|
+
* client → server {"t":"ev", "root": string, "line": string}
|
|
12
|
+
* either direction {"t":"presence", "root": string, "data"?: unknown}
|
|
13
|
+
* either direction {"t":"err", "root"?: string, "reason": string, "detail"?: string}
|
|
14
|
+
*
|
|
15
|
+
* `seq` is the server's own per-root arrival counter — a resume cursor, not
|
|
16
|
+
* event order. The server echoes accepted events to every subscriber of the
|
|
17
|
+
* root, sender included; echoes are duplicate no-ops under union and still
|
|
18
|
+
* advance the cursor. This module is pure: frame codecs and guards only.
|
|
19
|
+
*
|
|
20
|
+
* `gen` is the server's log GENERATION: a random id minted every time a room
|
|
21
|
+
* is recovered from disk (so every server restart is a new generation). A
|
|
22
|
+
* cursor is only meaningful inside the generation that issued it — a broadcast
|
|
23
|
+
* whose disk write failed still consumes a seq, so after a restart the
|
|
24
|
+
* recovered log can sit BEHIND a client's saved cursor and the client would
|
|
25
|
+
* silently skip the next persisted event forever. A client that sees `gen`
|
|
26
|
+
* change from what its cursor was saved under must reset to 0 and resync from
|
|
27
|
+
* scratch; union makes the re-download a harmless set of duplicate no-ops.
|
|
28
|
+
* The field is additive: frames without it (old servers) decode fine, and old
|
|
29
|
+
* clients ignore it.
|
|
30
|
+
*/
|
|
31
|
+
const FRAME_KINDS = new Set(["sub", "ev", "live", "presence", "err"]);
|
|
32
|
+
/**
|
|
33
|
+
* A resume cursor / sequence number: a nonnegative integer. Fractional or
|
|
34
|
+
* non-finite values must never pass — they index into backlogs downstream.
|
|
35
|
+
*/
|
|
36
|
+
export function isCursor(value) {
|
|
37
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
38
|
+
}
|
|
39
|
+
export function encodeFrame(frame) {
|
|
40
|
+
return JSON.stringify(frame);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Decode one frame. Returns an ErrFrame (never throws) on anything
|
|
44
|
+
* malformed, so transport code stays loud without try/catch pyramids.
|
|
45
|
+
*/
|
|
46
|
+
export function decodeFrame(raw) {
|
|
47
|
+
const text = typeof raw === "string" ? raw : new TextDecoder().decode(raw);
|
|
48
|
+
let value;
|
|
49
|
+
try {
|
|
50
|
+
value = JSON.parse(text);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
return { t: "err", reason: "malformed-frame", detail: String(error) };
|
|
54
|
+
}
|
|
55
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
56
|
+
return { t: "err", reason: "malformed-frame", detail: "frame is not an object" };
|
|
57
|
+
}
|
|
58
|
+
const frame = value;
|
|
59
|
+
if (typeof frame.t !== "string" || !FRAME_KINDS.has(frame.t)) {
|
|
60
|
+
return { t: "err", reason: "unknown-frame-kind", detail: String(frame.t) };
|
|
61
|
+
}
|
|
62
|
+
switch (frame.t) {
|
|
63
|
+
case "sub":
|
|
64
|
+
// Cursors are array indices and resume positions: a fractional or
|
|
65
|
+
// non-finite `since` silently skips the backlog downstream (lines[0.5]
|
|
66
|
+
// is undefined), so anything but a nonnegative integer is malformed.
|
|
67
|
+
if (typeof frame.root !== "string" || !isCursor(frame.since)) {
|
|
68
|
+
return { t: "err", reason: "malformed-sub" };
|
|
69
|
+
}
|
|
70
|
+
return { t: "sub", root: frame.root, since: frame.since };
|
|
71
|
+
case "ev":
|
|
72
|
+
if (typeof frame.root !== "string" || typeof frame.line !== "string") {
|
|
73
|
+
return { t: "err", reason: "malformed-ev" };
|
|
74
|
+
}
|
|
75
|
+
if (frame.seq !== undefined && !isCursor(frame.seq)) {
|
|
76
|
+
return { t: "err", reason: "malformed-ev", detail: "seq must be a nonnegative integer" };
|
|
77
|
+
}
|
|
78
|
+
// gen is additive: absence is fine (old peers). Present-but-not-a-string
|
|
79
|
+
// is malformed — a client resetting its cursor over garbage would be
|
|
80
|
+
// acting on noise.
|
|
81
|
+
if (frame.gen !== undefined && typeof frame.gen !== "string") {
|
|
82
|
+
return { t: "err", reason: "malformed-ev", detail: "gen must be a string" };
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
t: "ev",
|
|
86
|
+
root: frame.root,
|
|
87
|
+
line: frame.line,
|
|
88
|
+
...(frame.seq !== undefined ? { seq: frame.seq } : {}),
|
|
89
|
+
...(frame.gen !== undefined ? { gen: frame.gen } : {}),
|
|
90
|
+
};
|
|
91
|
+
case "live":
|
|
92
|
+
if (typeof frame.root !== "string" || !isCursor(frame.seq)) {
|
|
93
|
+
return { t: "err", reason: "malformed-live" };
|
|
94
|
+
}
|
|
95
|
+
if (frame.gen !== undefined && typeof frame.gen !== "string") {
|
|
96
|
+
return { t: "err", reason: "malformed-live", detail: "gen must be a string" };
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
t: "live",
|
|
100
|
+
root: frame.root,
|
|
101
|
+
seq: frame.seq,
|
|
102
|
+
...(frame.gen !== undefined ? { gen: frame.gen } : {}),
|
|
103
|
+
};
|
|
104
|
+
case "presence":
|
|
105
|
+
if (typeof frame.root !== "string") {
|
|
106
|
+
return { t: "err", reason: "malformed-presence" };
|
|
107
|
+
}
|
|
108
|
+
return { t: "presence", root: frame.root, data: frame.data };
|
|
109
|
+
default:
|
|
110
|
+
return {
|
|
111
|
+
t: "err",
|
|
112
|
+
reason: typeof frame.reason === "string" ? frame.reason : "unspecified",
|
|
113
|
+
...(typeof frame.root === "string" ? { root: frame.root } : {}),
|
|
114
|
+
...(typeof frame.detail === "string" ? { detail: frame.detail } : {}),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Extract the event id from a canonical line without trusting anything else
|
|
120
|
+
* in it. The relay never parses beyond this. Returns undefined when no id
|
|
121
|
+
* can be extracted — callers surface that loudly, never drop it silently.
|
|
122
|
+
*/
|
|
123
|
+
export function extractLineId(line) {
|
|
124
|
+
try {
|
|
125
|
+
const value = JSON.parse(line);
|
|
126
|
+
if (typeof value === "object" && value !== null && typeof value.id === "string") {
|
|
127
|
+
return value.id;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=sync-protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-protocol.js","sourceRoot":"","sources":["../src/sync-protocol.ts"],"names":[],"mappings":"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"}
|