@ifc-lite/collab 0.2.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 +373 -0
- package/README.md +92 -0
- package/dist/awareness/agent.d.ts +36 -0
- package/dist/awareness/agent.d.ts.map +1 -0
- package/dist/awareness/agent.js +39 -0
- package/dist/awareness/agent.js.map +1 -0
- package/dist/awareness/color.d.ts +31 -0
- package/dist/awareness/color.d.ts.map +1 -0
- package/dist/awareness/color.js +61 -0
- package/dist/awareness/color.js.map +1 -0
- package/dist/awareness/index.d.ts +6 -0
- package/dist/awareness/index.d.ts.map +1 -0
- package/dist/awareness/index.js +9 -0
- package/dist/awareness/index.js.map +1 -0
- package/dist/awareness/overlay.d.ts +59 -0
- package/dist/awareness/overlay.d.ts.map +1 -0
- package/dist/awareness/overlay.js +110 -0
- package/dist/awareness/overlay.js.map +1 -0
- package/dist/awareness/presence.d.ts +95 -0
- package/dist/awareness/presence.d.ts.map +1 -0
- package/dist/awareness/presence.js +114 -0
- package/dist/awareness/presence.js.map +1 -0
- package/dist/awareness/render.d.ts +58 -0
- package/dist/awareness/render.d.ts.map +1 -0
- package/dist/awareness/render.js +66 -0
- package/dist/awareness/render.js.map +1 -0
- package/dist/branch/branch-tree.d.ts +40 -0
- package/dist/branch/branch-tree.d.ts.map +1 -0
- package/dist/branch/branch-tree.js +66 -0
- package/dist/branch/branch-tree.js.map +1 -0
- package/dist/branch/branch.d.ts +44 -0
- package/dist/branch/branch.d.ts.map +1 -0
- package/dist/branch/branch.js +109 -0
- package/dist/branch/branch.js.map +1 -0
- package/dist/branch/history-automerge.d.ts +31 -0
- package/dist/branch/history-automerge.d.ts.map +1 -0
- package/dist/branch/history-automerge.js +237 -0
- package/dist/branch/history-automerge.js.map +1 -0
- package/dist/branch/history.d.ts +147 -0
- package/dist/branch/history.d.ts.map +1 -0
- package/dist/branch/history.js +223 -0
- package/dist/branch/history.js.map +1 -0
- package/dist/branch/index.d.ts +5 -0
- package/dist/branch/index.d.ts.map +1 -0
- package/dist/branch/index.js +8 -0
- package/dist/branch/index.js.map +1 -0
- package/dist/conflicts/detector.d.ts +52 -0
- package/dist/conflicts/detector.d.ts.map +1 -0
- package/dist/conflicts/detector.js +226 -0
- package/dist/conflicts/detector.js.map +1 -0
- package/dist/conflicts/index.d.ts +3 -0
- package/dist/conflicts/index.d.ts.map +1 -0
- package/dist/conflicts/index.js +6 -0
- package/dist/conflicts/index.js.map +1 -0
- package/dist/conflicts/ui-bridge.d.ts +80 -0
- package/dist/conflicts/ui-bridge.d.ts.map +1 -0
- package/dist/conflicts/ui-bridge.js +126 -0
- package/dist/conflicts/ui-bridge.js.map +1 -0
- package/dist/doc/entity.d.ts +84 -0
- package/dist/doc/entity.d.ts.map +1 -0
- package/dist/doc/entity.js +345 -0
- package/dist/doc/entity.js.map +1 -0
- package/dist/doc/geometry.d.ts +39 -0
- package/dist/doc/geometry.d.ts.map +1 -0
- package/dist/doc/geometry.js +99 -0
- package/dist/doc/geometry.js.map +1 -0
- package/dist/doc/index.d.ts +8 -0
- package/dist/doc/index.d.ts.map +1 -0
- package/dist/doc/index.js +11 -0
- package/dist/doc/index.js.map +1 -0
- package/dist/doc/migration-ifc4-to-ifc4x3.d.ts +21 -0
- package/dist/doc/migration-ifc4-to-ifc4x3.d.ts.map +1 -0
- package/dist/doc/migration-ifc4-to-ifc4x3.js +55 -0
- package/dist/doc/migration-ifc4-to-ifc4x3.js.map +1 -0
- package/dist/doc/relationship.d.ts +27 -0
- package/dist/doc/relationship.d.ts.map +1 -0
- package/dist/doc/relationship.js +110 -0
- package/dist/doc/relationship.js.map +1 -0
- package/dist/doc/schema-version.d.ts +47 -0
- package/dist/doc/schema-version.d.ts.map +1 -0
- package/dist/doc/schema-version.js +41 -0
- package/dist/doc/schema-version.js.map +1 -0
- package/dist/doc/schema.d.ts +131 -0
- package/dist/doc/schema.d.ts.map +1 -0
- package/dist/doc/schema.js +117 -0
- package/dist/doc/schema.js.map +1 -0
- package/dist/doc/units.d.ts +45 -0
- package/dist/doc/units.d.ts.map +1 -0
- package/dist/doc/units.js +120 -0
- package/dist/doc/units.js.map +1 -0
- package/dist/federation/bridge.d.ts +34 -0
- package/dist/federation/bridge.d.ts.map +1 -0
- package/dist/federation/bridge.js +52 -0
- package/dist/federation/bridge.js.map +1 -0
- package/dist/federation/index.d.ts +4 -0
- package/dist/federation/index.d.ts.map +1 -0
- package/dist/federation/index.js +7 -0
- package/dist/federation/index.js.map +1 -0
- package/dist/federation/resolver.d.ts +58 -0
- package/dist/federation/resolver.d.ts.map +1 -0
- package/dist/federation/resolver.js +43 -0
- package/dist/federation/resolver.js.map +1 -0
- package/dist/federation/session.d.ts +79 -0
- package/dist/federation/session.d.ts.map +1 -0
- package/dist/federation/session.js +126 -0
- package/dist/federation/session.js.map +1 -0
- package/dist/geometry/blob-store.d.ts +106 -0
- package/dist/geometry/blob-store.d.ts.map +1 -0
- package/dist/geometry/blob-store.js +266 -0
- package/dist/geometry/blob-store.js.map +1 -0
- package/dist/geometry/csg.d.ts +63 -0
- package/dist/geometry/csg.d.ts.map +1 -0
- package/dist/geometry/csg.js +101 -0
- package/dist/geometry/csg.js.map +1 -0
- package/dist/geometry/determinism.d.ts +45 -0
- package/dist/geometry/determinism.d.ts.map +1 -0
- package/dist/geometry/determinism.js +92 -0
- package/dist/geometry/determinism.js.map +1 -0
- package/dist/geometry/gc.d.ts +63 -0
- package/dist/geometry/gc.d.ts.map +1 -0
- package/dist/geometry/gc.js +109 -0
- package/dist/geometry/gc.js.map +1 -0
- package/dist/geometry/index.d.ts +6 -0
- package/dist/geometry/index.d.ts.map +1 -0
- package/dist/geometry/index.js +9 -0
- package/dist/geometry/index.js.map +1 -0
- package/dist/geometry/parametric.d.ts +92 -0
- package/dist/geometry/parametric.d.ts.map +1 -0
- package/dist/geometry/parametric.js +200 -0
- package/dist/geometry/parametric.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/mutations/bind.d.ts +56 -0
- package/dist/mutations/bind.d.ts.map +1 -0
- package/dist/mutations/bind.js +68 -0
- package/dist/mutations/bind.js.map +1 -0
- package/dist/mutations/index.d.ts +2 -0
- package/dist/mutations/index.d.ts.map +1 -0
- package/dist/mutations/index.js +5 -0
- package/dist/mutations/index.js.map +1 -0
- package/dist/perf/benchmark.d.ts +25 -0
- package/dist/perf/benchmark.d.ts.map +1 -0
- package/dist/perf/benchmark.js +97 -0
- package/dist/perf/benchmark.js.map +1 -0
- package/dist/perf/index.d.ts +3 -0
- package/dist/perf/index.d.ts.map +1 -0
- package/dist/perf/index.js +6 -0
- package/dist/perf/index.js.map +1 -0
- package/dist/perf/latency.d.ts +39 -0
- package/dist/perf/latency.d.ts.map +1 -0
- package/dist/perf/latency.js +79 -0
- package/dist/perf/latency.js.map +1 -0
- package/dist/privacy.d.ts +45 -0
- package/dist/privacy.d.ts.map +1 -0
- package/dist/privacy.js +66 -0
- package/dist/privacy.js.map +1 -0
- package/dist/providers/indexeddb.d.ts +37 -0
- package/dist/providers/indexeddb.d.ts.map +1 -0
- package/dist/providers/indexeddb.js +45 -0
- package/dist/providers/indexeddb.js.map +1 -0
- package/dist/providers/webrtc.d.ts +40 -0
- package/dist/providers/webrtc.d.ts.map +1 -0
- package/dist/providers/webrtc.js +81 -0
- package/dist/providers/webrtc.js.map +1 -0
- package/dist/providers/websocket.d.ts +45 -0
- package/dist/providers/websocket.d.ts.map +1 -0
- package/dist/providers/websocket.js +56 -0
- package/dist/providers/websocket.js.map +1 -0
- package/dist/security/e2e.d.ts +54 -0
- package/dist/security/e2e.d.ts.map +1 -0
- package/dist/security/e2e.js +147 -0
- package/dist/security/e2e.js.map +1 -0
- package/dist/security/index.d.ts +2 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +5 -0
- package/dist/security/index.js.map +1 -0
- package/dist/session.d.ts +79 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +112 -0
- package/dist/session.js.map +1 -0
- package/dist/snapshot/from-ifcx.d.ts +24 -0
- package/dist/snapshot/from-ifcx.d.ts.map +1 -0
- package/dist/snapshot/from-ifcx.js +93 -0
- package/dist/snapshot/from-ifcx.js.map +1 -0
- package/dist/snapshot/index.d.ts +6 -0
- package/dist/snapshot/index.d.ts.map +1 -0
- package/dist/snapshot/index.js +9 -0
- package/dist/snapshot/index.js.map +1 -0
- package/dist/snapshot/layers.d.ts +56 -0
- package/dist/snapshot/layers.d.ts.map +1 -0
- package/dist/snapshot/layers.js +82 -0
- package/dist/snapshot/layers.js.map +1 -0
- package/dist/snapshot/minimal-layer.d.ts +40 -0
- package/dist/snapshot/minimal-layer.d.ts.map +1 -0
- package/dist/snapshot/minimal-layer.js +123 -0
- package/dist/snapshot/minimal-layer.js.map +1 -0
- package/dist/snapshot/to-ifcx.d.ts +26 -0
- package/dist/snapshot/to-ifcx.d.ts.map +1 -0
- package/dist/snapshot/to-ifcx.js +54 -0
- package/dist/snapshot/to-ifcx.js.map +1 -0
- package/dist/snapshot/worker.d.ts +45 -0
- package/dist/snapshot/worker.d.ts.map +1 -0
- package/dist/snapshot/worker.js +73 -0
- package/dist/snapshot/worker.js.map +1 -0
- package/dist/sync/room.d.ts +15 -0
- package/dist/sync/room.d.ts.map +1 -0
- package/dist/sync/room.js +20 -0
- package/dist/sync/room.js.map +1 -0
- package/dist/undo.d.ts +25 -0
- package/dist/undo.d.ts.map +1 -0
- package/dist/undo.js +37 -0
- package/dist/undo.js.map +1 -0
- package/dist/viewer-bridge.d.ts +27 -0
- package/dist/viewer-bridge.d.ts.map +1 -0
- package/dist/viewer-bridge.js +82 -0
- package/dist/viewer-bridge.js.map +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network-latency simulation (spec §15 §18).
|
|
3
|
+
*
|
|
4
|
+
* Helpers for benchmarking how a CRDT pipeline behaves under delay,
|
|
5
|
+
* jitter, packet loss, or partition. The simulator wraps a pair of
|
|
6
|
+
* Y.Docs with a queued, time-bucketed update channel — call
|
|
7
|
+
* `flushUntil(t)` to advance time and dispatch any updates whose
|
|
8
|
+
* delivery time has arrived.
|
|
9
|
+
*
|
|
10
|
+
* This is benchmark-only: production sync goes through providers.
|
|
11
|
+
*/
|
|
12
|
+
import * as Y from 'yjs';
|
|
13
|
+
export interface LatencyParams {
|
|
14
|
+
/** Mean one-way latency in ms (default 50). */
|
|
15
|
+
baseMs?: number;
|
|
16
|
+
/** Random jitter ±jitterMs added to each delivery (default 0). */
|
|
17
|
+
jitterMs?: number;
|
|
18
|
+
/** Probability [0..1] that a delivery is dropped (default 0). */
|
|
19
|
+
dropRate?: number;
|
|
20
|
+
/** Override the random source (default Math.random). */
|
|
21
|
+
random?: () => number;
|
|
22
|
+
}
|
|
23
|
+
export interface LatencyChannel {
|
|
24
|
+
/** Inject the docs' initial state vectors into each other. */
|
|
25
|
+
initialSync(): void;
|
|
26
|
+
/** Advance simulated time and deliver any updates due at-or-before `t`. */
|
|
27
|
+
flushUntil(t: number): void;
|
|
28
|
+
/** Total updates that have been delivered. */
|
|
29
|
+
delivered(): number;
|
|
30
|
+
/** Total updates that were dropped. */
|
|
31
|
+
dropped(): number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build a one-way-symmetric latency channel between `a` and `b`. Both
|
|
35
|
+
* docs subscribe to each other's `update` events; outbound updates are
|
|
36
|
+
* queued with simulated arrival time and dispatched on `flushUntil`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createLatencyChannel(a: Y.Doc, b: Y.Doc, params?: LatencyParams): LatencyChannel;
|
|
39
|
+
//# sourceMappingURL=latency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"latency.d.ts","sourceRoot":"","sources":["../../src/perf/latency.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;CACvB;AAQD,MAAM,WAAW,cAAc;IAC7B,8DAA8D;IAC9D,WAAW,IAAI,IAAI,CAAC;IACpB,2EAA2E;IAC3E,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,8CAA8C;IAC9C,SAAS,IAAI,MAAM,CAAC;IACpB,uCAAuC;IACvC,OAAO,IAAI,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,CAAC,EAAE,CAAC,CAAC,GAAG,EACR,CAAC,EAAE,CAAC,CAAC,GAAG,EACR,MAAM,GAAE,aAAkB,GACzB,cAAc,CAyDhB"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Network-latency simulation (spec §15 §18).
|
|
6
|
+
*
|
|
7
|
+
* Helpers for benchmarking how a CRDT pipeline behaves under delay,
|
|
8
|
+
* jitter, packet loss, or partition. The simulator wraps a pair of
|
|
9
|
+
* Y.Docs with a queued, time-bucketed update channel — call
|
|
10
|
+
* `flushUntil(t)` to advance time and dispatch any updates whose
|
|
11
|
+
* delivery time has arrived.
|
|
12
|
+
*
|
|
13
|
+
* This is benchmark-only: production sync goes through providers.
|
|
14
|
+
*/
|
|
15
|
+
import * as Y from 'yjs';
|
|
16
|
+
/**
|
|
17
|
+
* Build a one-way-symmetric latency channel between `a` and `b`. Both
|
|
18
|
+
* docs subscribe to each other's `update` events; outbound updates are
|
|
19
|
+
* queued with simulated arrival time and dispatched on `flushUntil`.
|
|
20
|
+
*/
|
|
21
|
+
export function createLatencyChannel(a, b, params = {}) {
|
|
22
|
+
const baseMs = params.baseMs ?? 50;
|
|
23
|
+
const jitterMs = params.jitterMs ?? 0;
|
|
24
|
+
const dropRate = params.dropRate ?? 0;
|
|
25
|
+
const rand = params.random ?? Math.random;
|
|
26
|
+
let now = 0;
|
|
27
|
+
let delivered = 0;
|
|
28
|
+
let dropped = 0;
|
|
29
|
+
const queue = [];
|
|
30
|
+
const arrivalTime = () => {
|
|
31
|
+
const jitter = jitterMs > 0 ? (rand() * 2 - 1) * jitterMs : 0;
|
|
32
|
+
return now + baseMs + jitter;
|
|
33
|
+
};
|
|
34
|
+
const onA = (update, origin) => {
|
|
35
|
+
if (origin === 'sim')
|
|
36
|
+
return;
|
|
37
|
+
if (rand() < dropRate) {
|
|
38
|
+
dropped += 1;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
queue.push({ toClient: 'b', arrivesAt: arrivalTime(), payload: update });
|
|
42
|
+
};
|
|
43
|
+
const onB = (update, origin) => {
|
|
44
|
+
if (origin === 'sim')
|
|
45
|
+
return;
|
|
46
|
+
if (rand() < dropRate) {
|
|
47
|
+
dropped += 1;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
queue.push({ toClient: 'a', arrivesAt: arrivalTime(), payload: update });
|
|
51
|
+
};
|
|
52
|
+
a.on('update', onA);
|
|
53
|
+
b.on('update', onB);
|
|
54
|
+
return {
|
|
55
|
+
initialSync() {
|
|
56
|
+
const aSv = Y.encodeStateVector(a);
|
|
57
|
+
const bSv = Y.encodeStateVector(b);
|
|
58
|
+
Y.applyUpdate(b, Y.encodeStateAsUpdate(a, bSv), 'sim');
|
|
59
|
+
Y.applyUpdate(a, Y.encodeStateAsUpdate(b, aSv), 'sim');
|
|
60
|
+
},
|
|
61
|
+
flushUntil(t) {
|
|
62
|
+
now = t;
|
|
63
|
+
// Deliver in time order. We sort once per call rather than
|
|
64
|
+
// maintaining a heap — tests typically have a few hundred entries.
|
|
65
|
+
queue.sort((x, y) => x.arrivesAt - y.arrivesAt);
|
|
66
|
+
while (queue.length > 0 && queue[0].arrivesAt <= now) {
|
|
67
|
+
const item = queue.shift();
|
|
68
|
+
if (item.toClient === 'a')
|
|
69
|
+
Y.applyUpdate(a, item.payload, 'sim');
|
|
70
|
+
else
|
|
71
|
+
Y.applyUpdate(b, item.payload, 'sim');
|
|
72
|
+
delivered += 1;
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
delivered: () => delivered,
|
|
76
|
+
dropped: () => dropped,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=latency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"latency.js","sourceRoot":"","sources":["../../src/perf/latency.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Privacy / GDPR helpers (spec §14).
|
|
3
|
+
*
|
|
4
|
+
* `exportAndLeave(session)` runs the user's "give me a copy of
|
|
5
|
+
* everything and forget me" flow:
|
|
6
|
+
* 1. Snapshot the current Y.Doc to IFCX.
|
|
7
|
+
* 2. Clear our awareness state so no stale presence lingers.
|
|
8
|
+
* 3. Dispose the session (closes provider, undo manager, conflict
|
|
9
|
+
* detector, presence, IDB persistence).
|
|
10
|
+
*
|
|
11
|
+
* The actual server-side hard-delete (room teardown, blob removal,
|
|
12
|
+
* audit-log redaction) is the deployer's responsibility because the
|
|
13
|
+
* client doesn't have that capability — but we expose hooks so apps
|
|
14
|
+
* can trigger it from inside the same call.
|
|
15
|
+
*/
|
|
16
|
+
import type { IfcxFile } from '@ifc-lite/ifcx';
|
|
17
|
+
import type { CollabSession } from './session.js';
|
|
18
|
+
import { type SnapshotOptions } from './snapshot/to-ifcx.js';
|
|
19
|
+
export interface ExportAndLeaveOptions {
|
|
20
|
+
/** Forwarded to `snapshotToIfcx`. */
|
|
21
|
+
snapshot?: SnapshotOptions;
|
|
22
|
+
/**
|
|
23
|
+
* Optional remote hard-delete trigger. The promise's resolve value
|
|
24
|
+
* is bubbled up in `serverDeletion`.
|
|
25
|
+
*/
|
|
26
|
+
serverDelete?: () => Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export interface ExportAndLeaveResult {
|
|
29
|
+
ifcx: IfcxFile;
|
|
30
|
+
serverDeletion: 'skipped' | 'ok' | 'failed';
|
|
31
|
+
serverDeletionError?: unknown;
|
|
32
|
+
}
|
|
33
|
+
export declare function exportAndLeave(session: CollabSession, options?: ExportAndLeaveOptions): Promise<ExportAndLeaveResult>;
|
|
34
|
+
/**
|
|
35
|
+
* Strip user-attributable metadata from a Y.Doc: per-entity `meta` keys
|
|
36
|
+
* `createdBy` / `lastEditedBy` are blanked. Useful for "anonymise this
|
|
37
|
+
* project" exports. Returns the number of entities touched.
|
|
38
|
+
*
|
|
39
|
+
* Note: this does NOT touch the underlying CRDT struct authorship
|
|
40
|
+
* (clientID is intrinsic to Yjs's merge semantics and removing it
|
|
41
|
+
* would corrupt the doc). For a proper anonymised export, snapshot
|
|
42
|
+
* to IFCX first — the IFCX serialiser drops clientIDs by design.
|
|
43
|
+
*/
|
|
44
|
+
export declare function redactAuthorMeta(session: CollabSession): number;
|
|
45
|
+
//# sourceMappingURL=privacy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../src/privacy.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7E,MAAM,WAAW,qBAAqB;IACpC,qCAAqC;IACrC,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,QAAQ,CAAC;IACf,cAAc,EAAE,SAAS,GAAG,IAAI,GAAG,QAAQ,CAAC;IAC5C,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CA4B/B;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAqB/D"}
|
package/dist/privacy.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import { snapshotToIfcx } from './snapshot/to-ifcx.js';
|
|
5
|
+
export async function exportAndLeave(session, options = {}) {
|
|
6
|
+
const ifcx = snapshotToIfcx(session.doc, options.snapshot);
|
|
7
|
+
// Clear our presence so peers see us leave cleanly.
|
|
8
|
+
try {
|
|
9
|
+
session.presence.setStatus('offline');
|
|
10
|
+
session.presence.patch({ selection: [], cursor3d: undefined, cursor2d: undefined });
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
// Presence channel may already be torn down by the time we get
|
|
14
|
+
// here; non-fatal.
|
|
15
|
+
void err;
|
|
16
|
+
}
|
|
17
|
+
let serverDeletion = 'skipped';
|
|
18
|
+
let serverDeletionError;
|
|
19
|
+
if (options.serverDelete) {
|
|
20
|
+
try {
|
|
21
|
+
await options.serverDelete();
|
|
22
|
+
serverDeletion = 'ok';
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
serverDeletion = 'failed';
|
|
26
|
+
serverDeletionError = err;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
session.dispose();
|
|
30
|
+
return { ifcx, serverDeletion, serverDeletionError };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Strip user-attributable metadata from a Y.Doc: per-entity `meta` keys
|
|
34
|
+
* `createdBy` / `lastEditedBy` are blanked. Useful for "anonymise this
|
|
35
|
+
* project" exports. Returns the number of entities touched.
|
|
36
|
+
*
|
|
37
|
+
* Note: this does NOT touch the underlying CRDT struct authorship
|
|
38
|
+
* (clientID is intrinsic to Yjs's merge semantics and removing it
|
|
39
|
+
* would corrupt the doc). For a proper anonymised export, snapshot
|
|
40
|
+
* to IFCX first — the IFCX serialiser drops clientIDs by design.
|
|
41
|
+
*/
|
|
42
|
+
export function redactAuthorMeta(session) {
|
|
43
|
+
const ents = session.doc.getMap('entities');
|
|
44
|
+
let touched = 0;
|
|
45
|
+
session.doc.transact(() => {
|
|
46
|
+
ents.forEach((entUntyped) => {
|
|
47
|
+
const entity = entUntyped;
|
|
48
|
+
const meta = entity.get('meta');
|
|
49
|
+
if (!meta)
|
|
50
|
+
return;
|
|
51
|
+
let changed = false;
|
|
52
|
+
if (meta.has('createdBy')) {
|
|
53
|
+
meta.set('createdBy', null);
|
|
54
|
+
changed = true;
|
|
55
|
+
}
|
|
56
|
+
if (meta.has('lastEditedBy')) {
|
|
57
|
+
meta.set('lastEditedBy', null);
|
|
58
|
+
changed = true;
|
|
59
|
+
}
|
|
60
|
+
if (changed)
|
|
61
|
+
touched += 1;
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
return touched;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=privacy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privacy.js","sourceRoot":"","sources":["../src/privacy.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAoB/D,OAAO,EAAE,cAAc,EAAwB,MAAM,uBAAuB,CAAC;AAkB7E,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAsB,EACtB,UAAiC,EAAE;IAEnC,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE3D,oDAAoD;IACpD,IAAI,CAAC;QACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACtC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;IACtF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,+DAA+D;QAC/D,mBAAmB;QACnB,KAAK,GAAG,CAAC;IACX,CAAC;IAED,IAAI,cAAc,GAA2C,SAAS,CAAC;IACvE,IAAI,mBAA4B,CAAC;IACjC,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,YAAY,EAAE,CAAC;YAC7B,cAAc,GAAG,IAAI,CAAC;QACxB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,cAAc,GAAG,QAAQ,CAAC;YAC1B,mBAAmB,GAAG,GAAG,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,OAAO,CAAC,OAAO,EAAE,CAAC;IAElB,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,mBAAmB,EAAE,CAAC;AACvD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAsB;IACrD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC5C,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE;QACxB,IAAI,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE;YAC1B,MAAM,MAAM,GAAG,UAAwC,CAAC;YACxD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAA2C,CAAC;YAC1E,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC1B,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;gBAC5B,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;YACD,IAAI,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;gBAC7B,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;gBAC/B,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;YACD,IAAI,OAAO;gBAAE,OAAO,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IndexedDB provider — local-first persistence per spec §12.1.
|
|
3
|
+
*
|
|
4
|
+
* Always-on: every CollabSession mirrors its Y.Doc to IndexedDB on each
|
|
5
|
+
* update. Reload restores last state instantly; sync resumes from saved
|
|
6
|
+
* state vector.
|
|
7
|
+
*
|
|
8
|
+
* Loaded lazily so non-browser environments (Node tests, server) don't
|
|
9
|
+
* trip over the missing `indexedDB` global.
|
|
10
|
+
*/
|
|
11
|
+
import type * as Y from 'yjs';
|
|
12
|
+
export interface IndexedDbProvider {
|
|
13
|
+
readonly roomId: string;
|
|
14
|
+
/** Resolves once the local state has been loaded into the Y.Doc. */
|
|
15
|
+
readonly whenSynced: Promise<void>;
|
|
16
|
+
destroy(): void;
|
|
17
|
+
/** Wipe persisted state for this room. */
|
|
18
|
+
clearData(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export interface IndexedDbOptions {
|
|
21
|
+
/** Override the IDB database name (defaults to `roomId`). */
|
|
22
|
+
dbName?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Create an IndexedDB provider for `doc` keyed by `roomId`.
|
|
26
|
+
*
|
|
27
|
+
* Throws if `indexedDB` is unavailable. In Node tests, prefer to omit the
|
|
28
|
+
* provider entirely or point the session at a `MemoryProvider`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createIndexedDbProvider(doc: Y.Doc, roomId: string, options?: IndexedDbOptions): Promise<IndexedDbProvider>;
|
|
31
|
+
/**
|
|
32
|
+
* In-memory provider. Useful for tests and ephemeral rooms. Functionally
|
|
33
|
+
* a no-op; included so callers can ask for `provider: 'memory'` and still
|
|
34
|
+
* get a `whenSynced` promise.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createMemoryProvider(doc: Y.Doc, roomId: string): IndexedDbProvider;
|
|
37
|
+
//# sourceMappingURL=indexeddb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexeddb.d.ts","sourceRoot":"","sources":["../../src/providers/indexeddb.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH,OAAO,KAAK,KAAK,CAAC,MAAM,KAAK,CAAC;AAE9B,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,OAAO,IAAI,IAAI,CAAC;IAChB,0CAA0C;IAC1C,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,CAAC,CAAC,GAAG,EACV,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,iBAAiB,CAAC,CAwB5B;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,iBAAiB,CAQlF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Create an IndexedDB provider for `doc` keyed by `roomId`.
|
|
6
|
+
*
|
|
7
|
+
* Throws if `indexedDB` is unavailable. In Node tests, prefer to omit the
|
|
8
|
+
* provider entirely or point the session at a `MemoryProvider`.
|
|
9
|
+
*/
|
|
10
|
+
export async function createIndexedDbProvider(doc, roomId, options = {}) {
|
|
11
|
+
if (typeof indexedDB === 'undefined') {
|
|
12
|
+
throw new Error('@ifc-lite/collab: indexedDB is not available; createIndexedDbProvider requires a browser environment');
|
|
13
|
+
}
|
|
14
|
+
// Dynamic import keeps `y-indexeddb` out of Node bundle paths.
|
|
15
|
+
const { IndexeddbPersistence } = await import('y-indexeddb');
|
|
16
|
+
const persistence = new IndexeddbPersistence(options.dbName ?? roomId, doc);
|
|
17
|
+
const whenSynced = new Promise((resolve) => {
|
|
18
|
+
persistence.once('synced', () => resolve());
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
roomId,
|
|
22
|
+
whenSynced,
|
|
23
|
+
destroy: () => {
|
|
24
|
+
persistence.destroy();
|
|
25
|
+
},
|
|
26
|
+
clearData: async () => {
|
|
27
|
+
await persistence.clearData();
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* In-memory provider. Useful for tests and ephemeral rooms. Functionally
|
|
33
|
+
* a no-op; included so callers can ask for `provider: 'memory'` and still
|
|
34
|
+
* get a `whenSynced` promise.
|
|
35
|
+
*/
|
|
36
|
+
export function createMemoryProvider(doc, roomId) {
|
|
37
|
+
void doc;
|
|
38
|
+
return {
|
|
39
|
+
roomId,
|
|
40
|
+
whenSynced: Promise.resolve(),
|
|
41
|
+
destroy: () => { },
|
|
42
|
+
clearData: async () => { },
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=indexeddb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexeddb.js","sourceRoot":"","sources":["../../src/providers/indexeddb.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AA6B/D;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,GAAU,EACV,MAAc,EACd,UAA4B,EAAE;IAE9B,IAAI,OAAO,SAAS,KAAK,WAAW,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,sGAAsG,CACvG,CAAC;IACJ,CAAC;IACD,+DAA+D;IAC/D,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAG,IAAI,oBAAoB,CAAC,OAAO,CAAC,MAAM,IAAI,MAAM,EAAE,GAAG,CAAC,CAAC;IAE5E,MAAM,UAAU,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC/C,WAAW,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,MAAM;QACN,UAAU;QACV,OAAO,EAAE,GAAG,EAAE;YACZ,WAAW,CAAC,OAAO,EAAE,CAAC;QACxB,CAAC;QACD,SAAS,EAAE,KAAK,IAAI,EAAE;YACpB,MAAM,WAAW,CAAC,SAAS,EAAE,CAAC;QAChC,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAU,EAAE,MAAc;IAC7D,KAAK,GAAG,CAAC;IACT,OAAO;QACL,MAAM;QACN,UAAU,EAAE,OAAO,CAAC,OAAO,EAAE;QAC7B,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC;QACjB,SAAS,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;KAC1B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebRTC provider (spec §8.1).
|
|
3
|
+
*
|
|
4
|
+
* `y-webrtc` lets two peers sync directly without going through a
|
|
5
|
+
* central server — useful for offline-first / P2P use cases. This
|
|
6
|
+
* module wraps it so consumers don't import `y-webrtc` directly and
|
|
7
|
+
* the API matches our other providers.
|
|
8
|
+
*/
|
|
9
|
+
import type * as Y from 'yjs';
|
|
10
|
+
import type { Awareness } from 'y-protocols/awareness';
|
|
11
|
+
export type WebRtcStatus = 'connecting' | 'connected' | 'disconnected';
|
|
12
|
+
export interface WebRtcProviderOptions {
|
|
13
|
+
/** ICE / signaling URLs (default y-webrtc's public list). */
|
|
14
|
+
signaling?: string[];
|
|
15
|
+
/** Optional shared password — peers must agree. */
|
|
16
|
+
password?: string;
|
|
17
|
+
/** Awareness instance to share over the same transport. */
|
|
18
|
+
awareness?: Awareness;
|
|
19
|
+
/** Max simultaneous WebRTC connections. */
|
|
20
|
+
maxConns?: number;
|
|
21
|
+
/** Keep BroadcastChannel intra-browser sync (default true). */
|
|
22
|
+
filterBcConns?: boolean;
|
|
23
|
+
/** Connect on construction (default true). */
|
|
24
|
+
connect?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface WebRtcProvider {
|
|
27
|
+
readonly roomId: string;
|
|
28
|
+
readonly status: WebRtcStatus;
|
|
29
|
+
onStatus(listener: (s: WebRtcStatus) => void): () => void;
|
|
30
|
+
whenSynced: Promise<void>;
|
|
31
|
+
connect(): void;
|
|
32
|
+
disconnect(): void;
|
|
33
|
+
destroy(): void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Build a WebRTC provider. `y-webrtc` is loaded lazily so consumers
|
|
37
|
+
* who don't use this provider don't pay its bundle cost.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createWebRtcProvider(doc: Y.Doc, roomId: string, options?: WebRtcProviderOptions): Promise<WebRtcProvider>;
|
|
40
|
+
//# sourceMappingURL=webrtc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webrtc.d.ts","sourceRoot":"","sources":["../../src/providers/webrtc.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AAEH,OAAO,KAAK,KAAK,CAAC,MAAM,KAAK,CAAC;AAC9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,WAAW,GAAG,cAAc,CAAC;AAEvE,MAAM,WAAW,qBAAqB;IACpC,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC1D,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,OAAO,IAAI,IAAI,CAAC;IAChB,UAAU,IAAI,IAAI,CAAC;IACnB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,CAAC,CAAC,GAAG,EACV,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,cAAc,CAAC,CAqFzB"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Build a WebRTC provider. `y-webrtc` is loaded lazily so consumers
|
|
6
|
+
* who don't use this provider don't pay its bundle cost.
|
|
7
|
+
*/
|
|
8
|
+
export async function createWebRtcProvider(doc, roomId, options = {}) {
|
|
9
|
+
const mod = await import('y-webrtc').catch(() => null);
|
|
10
|
+
if (!mod) {
|
|
11
|
+
throw new Error('@ifc-lite/collab: y-webrtc is not installed. Run `pnpm add y-webrtc` and retry.');
|
|
12
|
+
}
|
|
13
|
+
const { WebrtcProvider } = mod;
|
|
14
|
+
// Honor `connect: false` end-to-end. Previously the option was only
|
|
15
|
+
// reflected in the local status state, not passed to WebrtcProvider,
|
|
16
|
+
// so the transport still auto-connected and violated the API contract.
|
|
17
|
+
const autoConnect = options.connect !== false;
|
|
18
|
+
const provider = new WebrtcProvider(roomId, doc, {
|
|
19
|
+
signaling: options.signaling,
|
|
20
|
+
password: options.password,
|
|
21
|
+
awareness: options.awareness,
|
|
22
|
+
maxConns: options.maxConns ?? 20,
|
|
23
|
+
filterBcConns: options.filterBcConns ?? true,
|
|
24
|
+
connect: autoConnect,
|
|
25
|
+
});
|
|
26
|
+
// Track whether the consumer has manually disconnected so transient
|
|
27
|
+
// y-webrtc events (e.g. a `peers` update with count=0) can't flip us
|
|
28
|
+
// back to `connecting`. Manual disconnect stays disconnected until
|
|
29
|
+
// `connect()` is called again.
|
|
30
|
+
let manuallyDisconnected = !autoConnect;
|
|
31
|
+
let status = autoConnect ? 'connecting' : 'disconnected';
|
|
32
|
+
const listeners = new Set();
|
|
33
|
+
const setStatus = (s) => {
|
|
34
|
+
if (s === status)
|
|
35
|
+
return;
|
|
36
|
+
status = s;
|
|
37
|
+
listeners.forEach((l) => l(s));
|
|
38
|
+
};
|
|
39
|
+
// y-webrtc emits 'peers' events when connections come up / go down.
|
|
40
|
+
provider.on('peers', (info) => {
|
|
41
|
+
if (manuallyDisconnected)
|
|
42
|
+
return;
|
|
43
|
+
const i = info;
|
|
44
|
+
const total = (i.webrtcPeers?.length ?? 0) + (i.bcPeers?.length ?? 0);
|
|
45
|
+
setStatus(total > 0 ? 'connected' : 'connecting');
|
|
46
|
+
});
|
|
47
|
+
provider.on('synced', () => {
|
|
48
|
+
if (manuallyDisconnected)
|
|
49
|
+
return;
|
|
50
|
+
setStatus('connected');
|
|
51
|
+
});
|
|
52
|
+
const whenSynced = new Promise((resolve) => {
|
|
53
|
+
provider.once('synced', () => resolve());
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
roomId,
|
|
57
|
+
get status() {
|
|
58
|
+
return status;
|
|
59
|
+
},
|
|
60
|
+
onStatus(listener) {
|
|
61
|
+
listeners.add(listener);
|
|
62
|
+
listener(status);
|
|
63
|
+
return () => listeners.delete(listener);
|
|
64
|
+
},
|
|
65
|
+
whenSynced,
|
|
66
|
+
connect() {
|
|
67
|
+
manuallyDisconnected = false;
|
|
68
|
+
provider.connect();
|
|
69
|
+
setStatus('connecting');
|
|
70
|
+
},
|
|
71
|
+
disconnect() {
|
|
72
|
+
manuallyDisconnected = true;
|
|
73
|
+
provider.disconnect();
|
|
74
|
+
setStatus('disconnected');
|
|
75
|
+
},
|
|
76
|
+
destroy() {
|
|
77
|
+
provider.destroy();
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=webrtc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webrtc.js","sourceRoot":"","sources":["../../src/providers/webrtc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Websocket provider — wraps `y-websocket` with a typed status observable
|
|
3
|
+
* and reconnect/backoff that we control.
|
|
4
|
+
*
|
|
5
|
+
* Spec §8.1.
|
|
6
|
+
*/
|
|
7
|
+
import type * as Y from 'yjs';
|
|
8
|
+
import type { Awareness } from 'y-protocols/awareness';
|
|
9
|
+
export type WebSocketStatus = 'connecting' | 'connected' | 'offline' | 'error';
|
|
10
|
+
export interface WebSocketProviderOptions {
|
|
11
|
+
/** Custom WebSocket implementation (e.g. `ws` in Node tests). */
|
|
12
|
+
WebSocketPolyfill?: unknown;
|
|
13
|
+
/** Bearer token attached as `?token=` for the server to validate. */
|
|
14
|
+
token?: string;
|
|
15
|
+
/** Awareness instance to share over the same transport. */
|
|
16
|
+
awareness?: Awareness;
|
|
17
|
+
/** Disable connect on construction; call `connect()` explicitly. */
|
|
18
|
+
connect?: boolean;
|
|
19
|
+
/** Extra params appended to the connect URL. */
|
|
20
|
+
params?: Record<string, string>;
|
|
21
|
+
/**
|
|
22
|
+
* Disable the BroadcastChannel intra-browser shortcut. When two tabs
|
|
23
|
+
* open the same room in the same browser, y-websocket normally syncs
|
|
24
|
+
* them via BroadcastChannel and never round-trips through the server.
|
|
25
|
+
* Set true to force every edit through the websocket — useful for
|
|
26
|
+
* demos / debugging server-side flows.
|
|
27
|
+
*/
|
|
28
|
+
disableBc?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface WebSocketProvider {
|
|
31
|
+
readonly roomId: string;
|
|
32
|
+
readonly status: WebSocketStatus;
|
|
33
|
+
onStatus(listener: (status: WebSocketStatus) => void): () => void;
|
|
34
|
+
/** Resolves on the first successful sync. */
|
|
35
|
+
whenSynced: Promise<void>;
|
|
36
|
+
connect(): void;
|
|
37
|
+
disconnect(): void;
|
|
38
|
+
destroy(): void;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Create a websocket provider. Returns a thin wrapper so consumers don't
|
|
42
|
+
* import `y-websocket` directly and can swap providers transparently.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createWebSocketProvider(doc: Y.Doc, roomId: string, serverUrl: string, options?: WebSocketProviderOptions): Promise<WebSocketProvider>;
|
|
45
|
+
//# sourceMappingURL=websocket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../src/providers/websocket.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AAEH,OAAO,KAAK,KAAK,CAAC,MAAM,KAAK,CAAC;AAC9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AAE/E,MAAM,WAAW,wBAAwB;IACvC,iEAAiE;IACjE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAClE,6CAA6C;IAC7C,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,OAAO,IAAI,IAAI,CAAC;IAChB,UAAU,IAAI,IAAI,CAAC;IACnB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,CAAC,CAAC,GAAG,EACV,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,iBAAiB,CAAC,CAoD5B"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Create a websocket provider. Returns a thin wrapper so consumers don't
|
|
6
|
+
* import `y-websocket` directly and can swap providers transparently.
|
|
7
|
+
*/
|
|
8
|
+
export async function createWebSocketProvider(doc, roomId, serverUrl, options = {}) {
|
|
9
|
+
const { WebsocketProvider } = await import('y-websocket');
|
|
10
|
+
const params = { ...(options.params ?? {}) };
|
|
11
|
+
if (options.token)
|
|
12
|
+
params.token = options.token;
|
|
13
|
+
const provider = new WebsocketProvider(serverUrl, roomId, doc, {
|
|
14
|
+
WebSocketPolyfill: options.WebSocketPolyfill,
|
|
15
|
+
awareness: options.awareness,
|
|
16
|
+
connect: options.connect ?? true,
|
|
17
|
+
disableBc: options.disableBc === true,
|
|
18
|
+
params,
|
|
19
|
+
});
|
|
20
|
+
let status = 'connecting';
|
|
21
|
+
const listeners = new Set();
|
|
22
|
+
const setStatus = (s) => {
|
|
23
|
+
if (status === s)
|
|
24
|
+
return;
|
|
25
|
+
status = s;
|
|
26
|
+
listeners.forEach((l) => l(s));
|
|
27
|
+
};
|
|
28
|
+
provider.on('status', (event) => {
|
|
29
|
+
setStatus(event.status === 'connected'
|
|
30
|
+
? 'connected'
|
|
31
|
+
: event.status === 'connecting'
|
|
32
|
+
? 'connecting'
|
|
33
|
+
: 'offline');
|
|
34
|
+
});
|
|
35
|
+
provider.on('connection-error', () => setStatus('error'));
|
|
36
|
+
provider.on('connection-close', () => setStatus('offline'));
|
|
37
|
+
const whenSynced = new Promise((resolve) => {
|
|
38
|
+
if (provider.synced)
|
|
39
|
+
return resolve();
|
|
40
|
+
provider.once('sync', () => resolve());
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
roomId,
|
|
44
|
+
get status() { return status; },
|
|
45
|
+
onStatus(listener) {
|
|
46
|
+
listeners.add(listener);
|
|
47
|
+
listener(status);
|
|
48
|
+
return () => listeners.delete(listener);
|
|
49
|
+
},
|
|
50
|
+
whenSynced,
|
|
51
|
+
connect: () => provider.connect(),
|
|
52
|
+
disconnect: () => provider.disconnect(),
|
|
53
|
+
destroy: () => provider.destroy(),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=websocket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocket.js","sourceRoot":"","sources":["../../src/providers/websocket.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AA8C/D;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,GAAU,EACV,MAAc,EACd,SAAiB,EACjB,UAAoC,EAAE;IAEtC,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IAE1D,MAAM,MAAM,GAA2B,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;IACrE,IAAI,OAAO,CAAC,KAAK;QAAE,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAEhD,MAAM,QAAQ,GAAG,IAAI,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE;QAC7D,iBAAiB,EAAE,OAAO,CAAC,iBAA0B;QACrD,SAAS,EAAE,OAAO,CAAC,SAAkB;QACrC,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,IAAI;QAChC,SAAS,EAAE,OAAO,CAAC,SAAS,KAAK,IAAI;QACrC,MAAM;KACP,CAAC,CAAC;IAEH,IAAI,MAAM,GAAoB,YAAY,CAAC;IAC3C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAgC,CAAC;IAC1D,MAAM,SAAS,GAAG,CAAC,CAAkB,EAAE,EAAE;QACvC,IAAI,MAAM,KAAK,CAAC;YAAE,OAAO;QACzB,MAAM,GAAG,CAAC,CAAC;QACX,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC,CAAC;IAEF,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAA8D,EAAE,EAAE;QACvF,SAAS,CACP,KAAK,CAAC,MAAM,KAAK,WAAW;YAC1B,CAAC,CAAC,WAAW;YACb,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,YAAY;gBAC7B,CAAC,CAAC,YAAY;gBACd,CAAC,CAAC,SAAS,CAChB,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,QAAQ,CAAC,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1D,QAAQ,CAAC,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC;IAE5D,MAAM,UAAU,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC/C,IAAI,QAAQ,CAAC,MAAM;YAAE,OAAO,OAAO,EAAE,CAAC;QACtC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,MAAM;QACN,IAAI,MAAM,KAAK,OAAO,MAAM,CAAC,CAAC,CAAC;QAC/B,QAAQ,CAAC,QAAQ;YACf,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACxB,QAAQ,CAAC,MAAM,CAAC,CAAC;YACjB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QACD,UAAU;QACV,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE;QACjC,UAAU,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE;QACvC,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE;KAClC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export interface RoomKey {
|
|
2
|
+
/** Key version — 1 for AES-GCM-256, monotonic for rotations. */
|
|
3
|
+
version: number;
|
|
4
|
+
/** The opaque WebCrypto handle. */
|
|
5
|
+
key: CryptoKey;
|
|
6
|
+
/** Created-at timestamp in ms. */
|
|
7
|
+
createdAt: number;
|
|
8
|
+
}
|
|
9
|
+
export interface DeriveOptions {
|
|
10
|
+
/** PBKDF2 iterations. Default 200_000. */
|
|
11
|
+
iterations?: number;
|
|
12
|
+
/** Hash. Default 'SHA-256'. */
|
|
13
|
+
hash?: 'SHA-256' | 'SHA-384' | 'SHA-512';
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Derive a room key from a shared password + salt. Salt is typically
|
|
17
|
+
* the room ID hashed; never reuse the same `(password, salt)` pair
|
|
18
|
+
* across rooms.
|
|
19
|
+
*/
|
|
20
|
+
export declare function deriveRoomKey(password: string, salt: Uint8Array, options?: DeriveOptions): Promise<RoomKey>;
|
|
21
|
+
/** Generate a random AES-GCM-256 room key (no password). */
|
|
22
|
+
export declare function generateRoomKey(): Promise<RoomKey>;
|
|
23
|
+
/** Export the raw key bytes so apps can persist or rewrap them. */
|
|
24
|
+
export declare function exportRoomKey(key: RoomKey): Promise<{
|
|
25
|
+
version: number;
|
|
26
|
+
bytes: Uint8Array;
|
|
27
|
+
}>;
|
|
28
|
+
/** Re-import a previously exported key. */
|
|
29
|
+
export declare function importRoomKey(version: number, bytes: Uint8Array): Promise<RoomKey>;
|
|
30
|
+
/** Encrypt a plaintext frame. Adds a 13-byte header (`[ver][iv]`). */
|
|
31
|
+
export declare function encryptFrame(plaintext: Uint8Array, key: RoomKey): Promise<Uint8Array>;
|
|
32
|
+
/**
|
|
33
|
+
* Decrypt a frame using `keys`. Tries each key in order so callers can
|
|
34
|
+
* pass `[currentKey, ...recentKeys]` to gracefully decode in-flight
|
|
35
|
+
* frames around a rotation.
|
|
36
|
+
*/
|
|
37
|
+
export declare function decryptFrame(frame: Uint8Array, keys: readonly RoomKey[]): Promise<Uint8Array>;
|
|
38
|
+
export interface KeyRingOptions {
|
|
39
|
+
/** Keep retired keys for this long after rotation, ms. Default 30_000. */
|
|
40
|
+
gracePeriodMs?: number;
|
|
41
|
+
/** Override `Date.now`. */
|
|
42
|
+
now?: () => number;
|
|
43
|
+
}
|
|
44
|
+
export interface KeyRing {
|
|
45
|
+
current(): RoomKey;
|
|
46
|
+
/** Rotate: new key becomes current; previous key is retained for grace. */
|
|
47
|
+
rotate(next: RoomKey): void;
|
|
48
|
+
/** All keys — current + still-in-grace ones. Most-recent first. */
|
|
49
|
+
active(): RoomKey[];
|
|
50
|
+
/** Drop keys that are past grace. */
|
|
51
|
+
prune(): number;
|
|
52
|
+
}
|
|
53
|
+
export declare function createKeyRing(initial: RoomKey, options?: KeyRingOptions): KeyRing;
|
|
54
|
+
//# sourceMappingURL=e2e.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e2e.d.ts","sourceRoot":"","sources":["../../src/security/e2e.ts"],"names":[],"mappings":"AAyDA,MAAM,WAAW,OAAO;IACtB,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,GAAG,EAAE,SAAS,CAAC;IACf,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;CAC1C;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAED,4DAA4D;AAC5D,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAOxD;AAED,mEAAmE;AACnE,wBAAsB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,CAAC,CAGjG;AAED,2CAA2C;AAC3C,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,OAAO,CAAC,CASlB;AAED,sEAAsE;AACtE,wBAAsB,YAAY,CAAC,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAa3F;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAyBnG;AAMD,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,OAAO,IAAI,OAAO,CAAC;IACnB,2EAA2E;IAC3E,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5B,mEAAmE;IACnE,MAAM,IAAI,OAAO,EAAE,CAAC;IACpB,qCAAqC;IACrC,KAAK,IAAI,MAAM,CAAC;CACjB;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CA6BrF"}
|