@fileverse-dev/ddoc 4.2.0-durability-7 → 4.2.0-rotation-2
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/dist/{index-BM0vGxD5.mjs → index-BzWshzFX.mjs} +14544 -14187
- package/dist/{index-Cmue3Umx.mjs → index-CtHz5niS.mjs} +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.es.js +13 -9
- package/dist/{markdown-source-pane-COOcYwSm.mjs → markdown-source-pane-yvufyN4Y.mjs} +2 -2
- package/dist/package/sync-local/SyncManager.d.ts +13 -0
- package/dist/package/sync-local/crypto/room-key.d.ts +2 -0
- package/dist/package/sync-local/session-tools.d.ts +31 -0
- package/dist/package/sync-local/socketClient.d.ts +23 -0
- package/dist/package/sync-local/types/index.d.ts +29 -1
- package/dist/{split-view-toolbar-Da3BRk-r.mjs → split-view-toolbar-CQsqHInM.mjs} +2 -2
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { k as Ql, b as Zl, s as eh } from "./index-
|
|
1
|
+
import { k as Ql, b as Zl, s as eh } from "./index-BzWshzFX.mjs";
|
|
2
2
|
let An = [], Xr = [];
|
|
3
3
|
(() => {
|
|
4
4
|
let s = "lc,34,7n,7,7b,19,,,,2,,2,,,20,b,1c,l,g,,2t,7,2,6,2,2,,4,z,,u,r,2j,b,1m,9,9,,o,4,,9,,3,,5,17,3,3b,f,,w,1j,,,,4,8,4,,3,7,a,2,t,,1m,,,,2,4,8,,9,,a,2,q,,2,2,1l,,4,2,4,2,2,3,3,,u,2,3,,b,2,1l,,4,5,,2,4,,k,2,m,6,,,1m,,,2,,4,8,,7,3,a,2,u,,1n,,,,c,,9,,14,,3,,1l,3,5,3,,4,7,2,b,2,t,,1m,,2,,2,,3,,5,2,7,2,b,2,s,2,1l,2,,,2,4,8,,9,,a,2,t,,20,,4,,2,3,,,8,,29,,2,7,c,8,2q,,2,9,b,6,22,2,r,,,,,,1j,e,,5,,2,5,b,,10,9,,2u,4,,6,,2,2,2,p,2,4,3,g,4,d,,2,2,6,,f,,jj,3,qa,3,t,3,t,2,u,2,1s,2,,7,8,,2,b,9,,19,3,3b,2,y,,3a,3,4,2,9,,6,3,63,2,2,,1m,,,7,,,,,2,8,6,a,2,,1c,h,1r,4,1c,7,,,5,,14,9,c,2,w,4,2,2,,3,1k,,,2,3,,,3,1m,8,2,2,48,3,,d,,7,4,,6,,3,2,5i,1m,,5,ek,,5f,x,2da,3,3x,,2o,w,fe,6,2x,2,n9w,4,,a,w,2,28,2,7k,,3,,4,,p,2,5,,47,2,q,i,d,,12,8,p,b,1a,3,1c,,2,4,2,2,13,,1v,6,2,2,2,2,c,,8,,1b,,1f,,,3,2,2,5,2,,,16,2,8,,6m,,2,,4,,fn4,,kh,g,g,g,a6,2,gt,,6a,,45,5,1ae,3,,2,5,4,14,3,4,,4l,2,fx,4,ar,2,49,b,4w,,1i,f,1k,3,1d,4,2,2,1x,3,10,5,,8,1q,,c,2,1g,9,a,4,2,,2n,3,2,,,2,6,,4g,,3,8,l,2,1l,2,,,,,m,,e,7,3,5,5f,8,2,3,,,n,,29,,2,6,,,2,,,2,,2,6j,,2,4,6,2,,2,r,2,2d,8,2,,,2,2y,,,,2,6,,,2t,3,2,4,,5,77,9,,2,6t,,a,2,,,4,,40,4,2,2,4,,w,a,14,6,2,4,8,,9,6,2,3,1a,d,,2,ba,7,,6,,,2a,m,2,7,,2,,2,3e,6,3,,,2,,7,,,20,2,3,,,,9n,2,f0b,5,1n,7,t4,,1r,4,29,,f5k,2,43q,,,3,4,5,8,8,2,7,u,4,44,3,1iz,1j,4,1e,8,,e,,m,5,,f,11s,7,,h,2,7,,2,,5,79,7,c5,4,15s,7,31,7,240,5,gx7k,2o,3k,6o".split(",").map((e) => e ? parseInt(e, 36) : 1);
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type { DdocExportModalProps, ExportFormatOption, ExportTabOption, } from
|
|
|
14
14
|
export type { VersionTabSnapshot } from './package/components/tabs/utils/version-diff-snapshot';
|
|
15
15
|
export { Editor } from '@tiptap/react';
|
|
16
16
|
export type { CollaborationProps, CollabConnectionConfig, CollabSessionMeta, CollabServices, CollabCallbacks, CollabState, CollabError, CollabErrorCode, CollabStatus, } from './package/sync-local/types';
|
|
17
|
+
export { encryptForRoomKey, decryptForRoomKey, } from './package/sync-local/crypto/room-key';
|
|
18
|
+
export { fetchSessionState, seedSession, } from './package/sync-local/session-tools';
|
|
17
19
|
export type { CommentMutationMeta, SuggestionType } from './package/types';
|
|
18
20
|
export type { IComment } from './package/extensions/comment/comment.ts';
|
|
19
21
|
export type { FontDescriptor } from './package/types';
|
package/dist/index.es.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import { D as
|
|
1
|
+
import { D as a, a as o, E as t, P as d, f as r, i, g as n, l as m, h as E, m as c, e as l, n as p, c as u, d as C, u as f, v as D } from "./index-BzWshzFX.mjs";
|
|
2
2
|
export {
|
|
3
|
-
|
|
3
|
+
a as DdocEditor,
|
|
4
4
|
o as DdocExportModal,
|
|
5
5
|
t as Editor,
|
|
6
6
|
d as PreviewDdocEditor,
|
|
7
7
|
r as buildVersionDiffSnapshot,
|
|
8
|
-
i as
|
|
9
|
-
n as
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
i as decryptForRoomKey,
|
|
9
|
+
n as encryptForRoomKey,
|
|
10
|
+
m as fetchSessionState,
|
|
11
|
+
E as handleContentPrint,
|
|
12
|
+
c as mergeTabAwareYjsUpdates,
|
|
13
|
+
l as sanitizeCustomCss,
|
|
14
|
+
p as seedSession,
|
|
15
|
+
u as useEditorCommands,
|
|
16
|
+
C as useExportHeadlessEditorContent,
|
|
17
|
+
f as useHeadlessEditor,
|
|
18
|
+
D as validateCustomCss
|
|
15
19
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
var Vr = Object.defineProperty;
|
|
2
2
|
var Wr = (t, e, O) => e in t ? Vr(t, e, { enumerable: !0, configurable: !0, writable: !0, value: O }) : t[e] = O;
|
|
3
3
|
var gt = (t, e, O) => Wr(t, typeof e != "symbol" ? e + "" : e, O);
|
|
4
|
-
import { j as Cr } from "./index-
|
|
4
|
+
import { j as Cr } from "./index-BzWshzFX.mjs";
|
|
5
5
|
import { useRef as pe, useEffect as St } from "react";
|
|
6
|
-
import { s as y, A as pO, T as dO, a as Gr, S as et, b as Lr, P as fO, E as j, i as QO, c as _, D as le, k as tt, F as Ar, M as Pt, W as Er, R as Ur, d as mO, N as oe, e as V, f as Mr, g as Ot, h as ce, p as $O, j as Y, t as h, l as Ir, I as rt, L as at, m as ye, n as ue, o as Qe, q as gO, r as te, u as SO, v as Br, w as Nr, x as PO, y as kO, z as Dr, B as Fr, C as Kr, G as Oe, H as ZO, J as Jr, K as kt, O as Hr, Q as ea, U as ta, V as Oa, X as ra, Y as aa, Z as ia, _ as sa, $ as na, a0 as la, a1 as oa, a2 as ca } from "./index-
|
|
6
|
+
import { s as y, A as pO, T as dO, a as Gr, S as et, b as Lr, P as fO, E as j, i as QO, c as _, D as le, k as tt, F as Ar, M as Pt, W as Er, R as Ur, d as mO, N as oe, e as V, f as Mr, g as Ot, h as ce, p as $O, j as Y, t as h, l as Ir, I as rt, L as at, m as ye, n as ue, o as Qe, q as gO, r as te, u as SO, v as Br, w as Nr, x as PO, y as kO, z as Dr, B as Fr, C as Kr, G as Oe, H as ZO, J as Jr, K as kt, O as Hr, Q as ea, U as ta, V as Oa, X as ra, Y as aa, Z as ia, _ as sa, $ as na, a0 as la, a1 as oa, a2 as ca } from "./index-CtHz5niS.mjs";
|
|
7
7
|
class ua {
|
|
8
8
|
/**
|
|
9
9
|
Create a new completion context. (Mostly useful for testing
|
|
@@ -9,6 +9,11 @@ export declare class SyncManager {
|
|
|
9
9
|
private socketClient;
|
|
10
10
|
private roomKey;
|
|
11
11
|
private roomKeyBytes;
|
|
12
|
+
private rotating;
|
|
13
|
+
private roomKeyBytesPrev;
|
|
14
|
+
private pendingRotationKey;
|
|
15
|
+
private healingPromise;
|
|
16
|
+
private staleAckRetries;
|
|
12
17
|
private encryptMirror;
|
|
13
18
|
private fileKeyEpoch;
|
|
14
19
|
private mirrorIdleTimer;
|
|
@@ -51,6 +56,10 @@ export declare class SyncManager {
|
|
|
51
56
|
documentTitle: string;
|
|
52
57
|
}): Promise<void>;
|
|
53
58
|
terminateSession(): Promise<void>;
|
|
59
|
+
/** In-place roomKey migration on CUTOVER: pause dispatch, swap the key, re-auth the same
|
|
60
|
+
* socket, re-broadcast awareness, then flush. See SocketClient.rekey for the wire side. */
|
|
61
|
+
rekey(newRoomKey: string, newAppLock?: string, newEditUcan?: string): Promise<void>;
|
|
62
|
+
private onDecryptMiss;
|
|
54
63
|
enqueueLocalUpdate(update: Uint8Array): void;
|
|
55
64
|
private flushUpdates;
|
|
56
65
|
private awaitFlush;
|
|
@@ -60,9 +69,12 @@ export declare class SyncManager {
|
|
|
60
69
|
* (before MongoDB write), so content reaches observers in near-real-time.
|
|
61
70
|
*/
|
|
62
71
|
private sendUpdateBatch;
|
|
72
|
+
private sendUpdateBatchAttempt;
|
|
63
73
|
private isRevocationResponse;
|
|
74
|
+
private isSessionTerminatedResponse;
|
|
64
75
|
private classifyRevocation;
|
|
65
76
|
private handleWriteRevocation;
|
|
77
|
+
private surfaceSessionTerminated;
|
|
66
78
|
private maybeAuthorSnapshotAfterSend;
|
|
67
79
|
private scheduleMirror;
|
|
68
80
|
private authorMirror;
|
|
@@ -72,6 +84,7 @@ export declare class SyncManager {
|
|
|
72
84
|
private handleConnectionError;
|
|
73
85
|
private handleReconnection;
|
|
74
86
|
private connectSocket;
|
|
87
|
+
private tryDecrypt;
|
|
75
88
|
private decodeInto;
|
|
76
89
|
private catchUpFloor;
|
|
77
90
|
private authorSnapshot;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { CollabConnectionConfig } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Headless hydrate: pulls the OLD session's durable log and decrypts it with `roomKey`.
|
|
5
|
+
* The old session still exists server-side — termination only rejects writes, so a
|
|
6
|
+
* joinOnly read succeeds even after cutover. Mirrors the connect-time hydration pull
|
|
7
|
+
* (SyncManager.catchUpFloor / decodeInto), minus the Y.Doc/floor bookkeeping. Throws on
|
|
8
|
+
* connect/auth/pull failure; an individual undecryptable row is skipped, not fatal.
|
|
9
|
+
*/
|
|
10
|
+
export declare function fetchSessionState(args: {
|
|
11
|
+
ddocId: string;
|
|
12
|
+
roomKey: string;
|
|
13
|
+
ownerAuth: CollabConnectionConfig;
|
|
14
|
+
}): Promise<Uint8Array[]>;
|
|
15
|
+
/**
|
|
16
|
+
* Headless seed: pushes `state` into a brand-new session as a single owner snapshot,
|
|
17
|
+
* encrypted under `newRoomKey`. `floorSeq: 0` — the target session is freshly created by
|
|
18
|
+
* the rotate op and holds no rows yet, so there is nothing to catch up before stamping.
|
|
19
|
+
*
|
|
20
|
+
* Caller ordering: must run AFTER the rotate op has already created the target session.
|
|
21
|
+
* This function's own owner `/auth` is create-or-join — calling it first would create the
|
|
22
|
+
* target session itself and, as an owner /auth, sweep (terminate) any other non-terminated
|
|
23
|
+
* session for this document, which is the OLD session that's supposed to keep draining
|
|
24
|
+
* stragglers during the rotation window. Seeding early collapses that drain early.
|
|
25
|
+
*/
|
|
26
|
+
export declare function seedSession(args: {
|
|
27
|
+
ddocId: string;
|
|
28
|
+
newRoomKey: string;
|
|
29
|
+
state: Uint8Array;
|
|
30
|
+
ownerAuth: CollabConnectionConfig;
|
|
31
|
+
}): Promise<void>;
|
|
@@ -22,6 +22,23 @@ interface ISocketClientConfig {
|
|
|
22
22
|
} | {
|
|
23
23
|
status: 'unavailable';
|
|
24
24
|
}>;
|
|
25
|
+
onRotationPrepare?: (inner: {
|
|
26
|
+
epoch: number;
|
|
27
|
+
gp: string;
|
|
28
|
+
appLock?: string;
|
|
29
|
+
}) => Promise<string | {
|
|
30
|
+
roomKey: string;
|
|
31
|
+
editUcan?: string;
|
|
32
|
+
} | null>;
|
|
33
|
+
onEpochAvailable?: (data: {
|
|
34
|
+
roomId: string;
|
|
35
|
+
epoch: number;
|
|
36
|
+
payload: string;
|
|
37
|
+
}) => void | Promise<void>;
|
|
38
|
+
onCutover?: (data: {
|
|
39
|
+
roomId: string;
|
|
40
|
+
epoch: number;
|
|
41
|
+
}) => void | Promise<void>;
|
|
25
42
|
actorHandle?: string;
|
|
26
43
|
joinOnly?: boolean;
|
|
27
44
|
onHandshakeData?: (response: {
|
|
@@ -70,6 +87,9 @@ export declare class SocketClient {
|
|
|
70
87
|
private awareness;
|
|
71
88
|
private connectionAttemptErrorCount;
|
|
72
89
|
private _onHandshakeData;
|
|
90
|
+
private _onEpochAvailable;
|
|
91
|
+
private _onCutover;
|
|
92
|
+
private _rotating;
|
|
73
93
|
constructor(config: ISocketClientConfig);
|
|
74
94
|
registerAwareness(awareness: Awareness): void;
|
|
75
95
|
private _stampSocketId;
|
|
@@ -100,12 +120,15 @@ export declare class SocketClient {
|
|
|
100
120
|
broadcastAwareness(awarenessUpdate: string): Promise<void>;
|
|
101
121
|
disconnect: () => void;
|
|
102
122
|
reauth: () => void;
|
|
123
|
+
rekey(newRoomKey: string, newAppLock?: string, newEditUcan?: string): Promise<void>;
|
|
124
|
+
ackEpochLoaded(documentId: string, epoch: number): Promise<void>;
|
|
103
125
|
terminateSession: () => Promise<void>;
|
|
104
126
|
flushBeacon(mergedUpdate: string): void;
|
|
105
127
|
private getCollaborationKeyPair;
|
|
106
128
|
private isUcanValid;
|
|
107
129
|
private getOwnerToken;
|
|
108
130
|
private buildSessionToken;
|
|
131
|
+
private _authenticate;
|
|
109
132
|
private _handleHandShake;
|
|
110
133
|
private _handleAwarenessUpdate;
|
|
111
134
|
connectSocket(config: ISocketInitConfig): Promise<void>;
|
|
@@ -36,6 +36,21 @@ export interface CollabConnectionConfig {
|
|
|
36
36
|
} | {
|
|
37
37
|
status: 'unavailable';
|
|
38
38
|
}>;
|
|
39
|
+
/** Host signal: resolves the rotated roomKey for the given epoch payload (GP: gate release
|
|
40
|
+
* unwrap of `gp`; workspace: app-key unwrap of `appLock`). Returns the new base64 roomKey —
|
|
41
|
+
* or, for a gate-admitted editor, `{ roomKey, editUcan }`: the same gate release that unwraps
|
|
42
|
+
* the relay also mints the new-epoch editUcan, and the cutover re-auth must present THAT token
|
|
43
|
+
* (a re-mint through the host's fresh-open flow can race the deferred publish and hand back a
|
|
44
|
+
* stale-epoch claim). `null` if this actor can't resolve it (falls back to the decrypt-miss
|
|
45
|
+
* self-heal path). Opaque to the package. */
|
|
46
|
+
onRotationPrepare?: (inner: {
|
|
47
|
+
epoch: number;
|
|
48
|
+
gp: string;
|
|
49
|
+
appLock?: string;
|
|
50
|
+
}) => Promise<string | {
|
|
51
|
+
roomKey: string;
|
|
52
|
+
editUcan?: string;
|
|
53
|
+
} | null>;
|
|
39
54
|
/** Host signal: an anonymous per-connection actor handle (public rail). Record-only; opaque. */
|
|
40
55
|
actorHandle?: string;
|
|
41
56
|
/** Host signal (non-creator workspace member): join-only. The server never creates or
|
|
@@ -71,7 +86,7 @@ export type CollabError = {
|
|
|
71
86
|
message: string;
|
|
72
87
|
recoverable: boolean;
|
|
73
88
|
};
|
|
74
|
-
export type CollabStatus = 'idle' | 'connecting' | 'syncing' | 'ready' | 'reconnecting' | 'error' | 'terminated';
|
|
89
|
+
export type CollabStatus = 'idle' | 'connecting' | 'syncing' | 'ready' | 'reconnecting' | 'rotating' | 'error' | 'terminated';
|
|
75
90
|
export type CollabState = {
|
|
76
91
|
status: 'idle';
|
|
77
92
|
} | {
|
|
@@ -85,6 +100,10 @@ export type CollabState = {
|
|
|
85
100
|
status: 'reconnecting';
|
|
86
101
|
attempt: number;
|
|
87
102
|
maxAttempts: number;
|
|
103
|
+
}
|
|
104
|
+
/** roomKey migration in flight — distinct from `terminated` so consumers don't mistake it for a kick. */
|
|
105
|
+
| {
|
|
106
|
+
status: 'rotating';
|
|
88
107
|
} | {
|
|
89
108
|
status: 'error';
|
|
90
109
|
error: CollabError;
|
|
@@ -113,6 +132,10 @@ export type CollabEvent = {
|
|
|
113
132
|
} | {
|
|
114
133
|
type: 'SESSION_TERMINATED';
|
|
115
134
|
reason?: string;
|
|
135
|
+
}
|
|
136
|
+
/** roomKey cutover in progress; no-op on status — lets the manager gate re-entrancy itself. */
|
|
137
|
+
| {
|
|
138
|
+
type: 'CUTOVER';
|
|
116
139
|
} | {
|
|
117
140
|
type: 'RESET';
|
|
118
141
|
};
|
|
@@ -134,6 +157,11 @@ export interface CollabCallbacks {
|
|
|
134
157
|
}) => void;
|
|
135
158
|
/** Live rename from the room owner; title is roomKey-encrypted. */
|
|
136
159
|
onTitleUpdate?: (encryptedTitle: string | null) => void;
|
|
160
|
+
/** Decrypt-miss self-heal: resolves the CURRENT-epoch roomKey (same resolution the host
|
|
161
|
+
* uses for `onRotationPrepare`, minus the relay payload — re-read the blob's
|
|
162
|
+
* `wrappedRoomKey` / appLock). Returns the new base64 roomKey, or `null` if this actor
|
|
163
|
+
* can't resolve one (the miss is then a real revocation, not a rotation lag). */
|
|
164
|
+
onRotationSelfHeal?: () => Promise<string | null>;
|
|
137
165
|
}
|
|
138
166
|
/** Discriminated union — TypeScript enforces config+services only when enabled */
|
|
139
167
|
export type CollaborationProps = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { j as d } from "./index-
|
|
1
|
+
import { j as d } from "./index-BzWshzFX.mjs";
|
|
2
2
|
import { useRef as I, useState as N, useEffect as D } from "react";
|
|
3
3
|
import { DynamicDropdown as E, Tooltip as R, IconButton as O } from "@fileverse/ui";
|
|
4
|
-
import { a3 as A, a4 as P, c as y } from "./index-
|
|
4
|
+
import { a3 as A, a4 as P, c as y } from "./index-CtHz5niS.mjs";
|
|
5
5
|
const p = (e, t, o = t) => {
|
|
6
6
|
const { state: s } = e, l = s.changeByRange((n) => {
|
|
7
7
|
const u = s.sliceDoc(n.from, n.to), r = s.sliceDoc(
|