@fileverse-dev/dsheet 4.1.3 → 4.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{constants-5ZxHFftQ.js → constants-8Ij49Lxv.js} +79 -22
- package/dist/constants.d.ts +2 -2
- package/dist/constants.js +3 -3
- package/dist/editor/components/editor-workbook-controls.d.ts +11 -0
- package/dist/editor/components/editor-workbook-controls.test.d.ts +1 -0
- package/dist/editor/components/editor-workbook.d.ts +5 -1
- package/dist/editor/components/permission-chip-model.d.ts +34 -0
- package/dist/editor/components/permission-chip.d.ts +5 -1
- package/dist/editor/components/permission-chip.test.d.ts +1 -0
- package/dist/editor/contexts/editor-context.d.ts +11 -2
- package/dist/editor/dsheet-editor.d.ts +3 -2
- package/dist/editor/hooks/collaboration-lifecycle.d.ts +35 -0
- package/dist/editor/hooks/collaboration-lifecycle.test.d.ts +1 -0
- package/dist/editor/hooks/use-collab-awareness.d.ts +3 -6
- package/dist/editor/hooks/use-editor-data.d.ts +2 -2
- package/dist/editor/hooks/use-editor-sync.d.ts +6 -2
- package/dist/editor/types.d.ts +29 -3
- package/dist/editor/utils/detach-workbook-data.d.ts +8 -0
- package/dist/editor/utils/detach-workbook-data.test.d.ts +1 -0
- package/dist/editor/utils/editor-handle.d.ts +11 -0
- package/dist/editor/utils/editor-handle.test.d.ts +1 -0
- package/dist/{index-C-29uy7d.js → index-CRzwkPJq.js} +23763 -23293
- package/dist/index.d.ts +3 -1
- package/dist/index.es.js +55 -51
- package/dist/persistence-utils-BXFajzvq.js +98 -0
- package/dist/persistence-utils.d.ts +6 -0
- package/dist/persistence-utils.test.d.ts +1 -0
- package/dist/persistence.js +1 -1
- package/dist/sheet-engine/react/components/Toolbar/index.d.ts +1 -0
- package/dist/sheet-engine/react/components/Workbook/index.d.ts +1 -0
- package/dist/style.css +1 -1
- package/dist/sync-local/SyncManager.d.ts +54 -9
- package/dist/sync-local/crypto/room-key.d.ts +2 -0
- package/dist/sync-local/crypto/room-key.test.d.ts +1 -0
- package/dist/sync-local/floor.d.ts +8 -0
- package/dist/sync-local/floor.test.d.ts +1 -0
- package/dist/sync-local/index.d.ts +1 -1
- package/dist/sync-local/presence.d.ts +13 -0
- package/dist/sync-local/presence.test.d.ts +1 -0
- package/dist/sync-local/session-tools.d.ts +31 -0
- package/dist/sync-local/session-tools.test.d.ts +1 -0
- package/dist/sync-local/socketClient.d.ts +75 -8
- package/dist/sync-local/types/index.d.ts +111 -15
- package/dist/sync-local/useSyncManager.d.ts +4 -0
- package/dist/{use-xlsx-import-impl-DB8XoHgC.js → use-xlsx-import-impl-tzKWGisi.js} +3 -3
- package/dist/{xlsx-defined-names-Cz6m4fA3.js → xlsx-defined-names-CflXuPTO.js} +1 -1
- package/dist/{xlsx-export-impl-C1LcpSSe.js → xlsx-export-impl-CkJLfQwm.js} +3 -3
- package/dist/{xlsx-hyperlink-inline-iNImgoxT.js → xlsx-hyperlink-inline-DWLdqDYy.js} +1 -1
- package/package.json +1 -1
- package/dist/persistence-utils-DsM_MOB1.js +0 -89
- package/dist/sync-local/utils/objectToFile.d.ts +0 -1
|
@@ -9,35 +9,66 @@ 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 healBackoffUntil;
|
|
17
|
+
private staleAckRetries;
|
|
18
|
+
private encryptMirror;
|
|
19
|
+
private fileKeyEpoch;
|
|
20
|
+
private mirrorIdleTimer;
|
|
21
|
+
private readonly MIRROR_IDLE_MS;
|
|
12
22
|
private isOwner;
|
|
23
|
+
private joinOnly;
|
|
13
24
|
private updateQueue;
|
|
14
|
-
private uncommittedUpdatesIdList;
|
|
15
25
|
private contentTobeAppliedQueue;
|
|
16
26
|
private isProcessing;
|
|
17
|
-
|
|
18
|
-
private
|
|
27
|
+
private syncId;
|
|
28
|
+
private floor;
|
|
29
|
+
private updatesSinceSnapshot;
|
|
30
|
+
private isAuthoringSnapshot;
|
|
31
|
+
private readonly SNAPSHOT_THRESHOLD;
|
|
32
|
+
private tailCompactTimer;
|
|
33
|
+
private gapCatchUpTimer;
|
|
34
|
+
private gapCatchUpInFlight;
|
|
35
|
+
private gapCatchUpCooldownUntil;
|
|
36
|
+
private readonly TAIL_COMPACT_PAGES;
|
|
37
|
+
private readonly TAIL_COMPACT_ROWS;
|
|
19
38
|
private flushTimer;
|
|
20
39
|
private readonly FLUSH_INTERVAL_MS;
|
|
21
40
|
private readonly MAX_QUEUE_SIZE;
|
|
22
41
|
private _awarenessUpdateHandler;
|
|
23
42
|
private ydoc;
|
|
24
|
-
private servicesRef;
|
|
25
43
|
private callbacksRef;
|
|
26
44
|
private onLocalUpdate?;
|
|
27
45
|
constructor(config: SyncManagerConfig, onCollabStateChange: (state: CollabState) => void);
|
|
28
46
|
/** Called by useSyncManager on every render to keep refs fresh */
|
|
29
|
-
updateRefs(
|
|
47
|
+
updateRefs(_services: CollabServices | undefined, callbacks: CollabCallbacks | undefined, onLocalUpdate?: (updatedDocContent: string, updateChunk: string) => void): void;
|
|
30
48
|
get isConnected(): boolean;
|
|
31
49
|
get isReady(): boolean;
|
|
32
50
|
get awareness(): Awareness | null;
|
|
33
51
|
get status(): CollabStatus;
|
|
34
52
|
get collabState(): CollabState;
|
|
53
|
+
private beginSyncAttempt;
|
|
54
|
+
private isCurrentSyncAttempt;
|
|
35
55
|
private send;
|
|
36
56
|
private runExitActions;
|
|
37
57
|
private runEntryActions;
|
|
38
58
|
connect(config: CollabConnectionConfig): Promise<void>;
|
|
39
59
|
disconnect(): Promise<void>;
|
|
60
|
+
/** Owner rename mid-session — see SocketClient.updateDocumentMeta. */
|
|
61
|
+
updateTitle(args: {
|
|
62
|
+
encryptedTitle: string;
|
|
63
|
+
documentTitle: string;
|
|
64
|
+
}): Promise<void>;
|
|
40
65
|
terminateSession(): Promise<void>;
|
|
66
|
+
/** In-place roomKey migration on CUTOVER: pause dispatch, swap the key, re-auth the same
|
|
67
|
+
* socket, re-broadcast awareness, then flush. See SocketClient.rekey for the wire side. */
|
|
68
|
+
rekey(newRoomKey: string, newAppLock?: string, newEditUcan?: string): Promise<void>;
|
|
69
|
+
private onDecryptMiss;
|
|
70
|
+
private runGapCatchUp;
|
|
71
|
+
private scheduleGapCatchUp;
|
|
41
72
|
enqueueLocalUpdate(update: Uint8Array): void;
|
|
42
73
|
private flushUpdates;
|
|
43
74
|
private awaitFlush;
|
|
@@ -45,21 +76,35 @@ export declare class SyncManager {
|
|
|
45
76
|
* Fire-and-forget: merge all queued updates, encrypt, and emit via Socket.IO
|
|
46
77
|
* without awaiting the server ACK. The server broadcasts to peers immediately
|
|
47
78
|
* (before MongoDB write), so content reaches observers in near-real-time.
|
|
48
|
-
* ACK callback handles updateId tracking and auto-commit asynchronously.
|
|
49
79
|
*/
|
|
50
80
|
private sendUpdateBatch;
|
|
81
|
+
private sendUpdateBatchAttempt;
|
|
82
|
+
private isRevocationResponse;
|
|
83
|
+
private isSessionTerminatedResponse;
|
|
84
|
+
private isSessionNotFoundResponse;
|
|
85
|
+
private classifyRevocation;
|
|
86
|
+
private handleWriteRevocation;
|
|
87
|
+
private surfaceSessionTerminated;
|
|
88
|
+
private maybeAuthorSnapshotAfterSend;
|
|
89
|
+
private scheduleMirror;
|
|
90
|
+
private authorMirror;
|
|
91
|
+
buildPendingBeaconPayload(): string | null;
|
|
92
|
+
fireBeacon(): void;
|
|
51
93
|
forceCleanup(): void;
|
|
52
94
|
private handleConnectionError;
|
|
53
95
|
private handleReconnection;
|
|
54
96
|
private connectSocket;
|
|
55
|
-
private
|
|
97
|
+
private tryDecrypt;
|
|
98
|
+
private decodeInto;
|
|
99
|
+
private catchUpFloor;
|
|
100
|
+
private authorSnapshot;
|
|
101
|
+
private hydrate;
|
|
102
|
+
private maybeCompactTail;
|
|
56
103
|
private initializeAwareness;
|
|
57
104
|
private cleanupAwareness;
|
|
58
|
-
private commitLocalContents;
|
|
59
105
|
private broadcastLocalContents;
|
|
60
106
|
private processUpdateQueue;
|
|
61
107
|
private processNextUpdate;
|
|
62
|
-
private processCommit;
|
|
63
108
|
private handleRemoteContentUpdate;
|
|
64
109
|
private applyRemoteYjsUpdate;
|
|
65
110
|
private applyQueuedRemoteContents;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as Y from 'yjs';
|
|
2
|
+
export declare function advanceFloor(floor: number, pageSeqsAscending: number[]): number;
|
|
3
|
+
export declare function shouldAuthorSnapshot(p: {
|
|
4
|
+
canAuthor: boolean;
|
|
5
|
+
updatesSinceLastSnapshot: number;
|
|
6
|
+
threshold: number;
|
|
7
|
+
}): boolean;
|
|
8
|
+
export declare function computeLocalOnlyUpdate(ydoc: Y.Doc, serverMerged: Uint8Array | null): Uint8Array | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { useSyncManager } from './useSyncManager';
|
|
2
2
|
export { SyncManager } from './SyncManager';
|
|
3
|
-
export type { SyncManagerConfig, CollabConnectionConfig, CollabServices, CollabCallbacks, CollaborationProps, CollabSessionMeta, CollabStatus, CollabState, CollabError, CollabErrorCode, CollabEvent, CollabContext,
|
|
3
|
+
export type { SyncManagerConfig, CollabConnectionConfig, CollabServices, CollabCallbacks, CollaborationProps, CollabSessionMeta, CollabStatus, CollabState, CollabError, CollabErrorCode, CollabEvent, CollabContext, } from './types';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CollabUser } from './types';
|
|
2
|
+
|
|
3
|
+
export interface AwarenessIdentity {
|
|
4
|
+
name: string;
|
|
5
|
+
color: string;
|
|
6
|
+
isEns: string | boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare const PLACEHOLDER_COLOR = "#9CA3AF";
|
|
9
|
+
export declare const COLLAB_PRESENCE_COLORS: readonly ["#30bced", "#6eeb83", "#fa69d1", "#ecd444", "#ee6352", "#db3041", "#0ad7f2", "#1bff39"];
|
|
10
|
+
export declare function buildIdentityMap(states: Map<number, Record<string, unknown>>): Map<string, AwarenessIdentity>;
|
|
11
|
+
export declare function mergePresence(roomMembers: string[], identityBySocketId: Map<string, AwarenessIdentity>): CollabUser[];
|
|
12
|
+
export declare function assignSessionColors(collaborators: CollabUser[], colorByName: Map<string, string>): CollabUser[];
|
|
13
|
+
export declare function identitySignature(roomMembers: string[], identityBySocketId: Map<string, AwarenessIdentity>): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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
|
+
dsheetId: 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
|
+
dsheetId: string;
|
|
28
|
+
newRoomKey: string;
|
|
29
|
+
state: Uint8Array;
|
|
30
|
+
ownerAuth: CollabConnectionConfig;
|
|
31
|
+
}): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ISocketInitConfig,
|
|
1
|
+
import { ISocketInitConfig, SocketStatusEnum, SendUpdateResponse, HydrationResponse, SnapshotResponse, AckResponse } from './types';
|
|
2
2
|
import { Awareness } from 'y-protocols/awareness.js';
|
|
3
3
|
|
|
4
4
|
interface ISocketClientConfig {
|
|
@@ -8,6 +8,37 @@ interface ISocketClientConfig {
|
|
|
8
8
|
ownerEdSecret?: string;
|
|
9
9
|
contractAddress?: string;
|
|
10
10
|
ownerAddress?: string;
|
|
11
|
+
editLock?: string;
|
|
12
|
+
encryptedTitle?: string;
|
|
13
|
+
identityToken?: string;
|
|
14
|
+
editUcan?: string;
|
|
15
|
+
refreshEditClaim?: () => Promise<{
|
|
16
|
+
status: 'ok';
|
|
17
|
+
token: string;
|
|
18
|
+
} | {
|
|
19
|
+
status: 'demoted';
|
|
20
|
+
} | {
|
|
21
|
+
status: 'unavailable';
|
|
22
|
+
}>;
|
|
23
|
+
onRotationPrepare?: (inner: {
|
|
24
|
+
epoch: number;
|
|
25
|
+
gp: string;
|
|
26
|
+
appLock?: string;
|
|
27
|
+
}) => Promise<string | {
|
|
28
|
+
roomKey: string;
|
|
29
|
+
editUcan?: string;
|
|
30
|
+
} | null>;
|
|
31
|
+
onEpochAvailable?: (data: {
|
|
32
|
+
roomId: string;
|
|
33
|
+
epoch: number;
|
|
34
|
+
payload: string;
|
|
35
|
+
}) => void | Promise<void>;
|
|
36
|
+
onCutover?: (data: {
|
|
37
|
+
roomId: string;
|
|
38
|
+
epoch: number;
|
|
39
|
+
}) => void | Promise<void>;
|
|
40
|
+
actorHandle?: string;
|
|
41
|
+
joinOnly?: boolean;
|
|
11
42
|
onHandshakeData?: (response: {
|
|
12
43
|
data: AckResponse;
|
|
13
44
|
roomKey: string;
|
|
@@ -20,6 +51,8 @@ interface ISocketClientConfig {
|
|
|
20
51
|
}
|
|
21
52
|
export declare class SocketClient {
|
|
22
53
|
private _socketUrl;
|
|
54
|
+
private _restBase;
|
|
55
|
+
private _lastSessionToken;
|
|
23
56
|
private _socket;
|
|
24
57
|
private _webSocketStatus;
|
|
25
58
|
private _isIntentionalDisconnect;
|
|
@@ -27,38 +60,72 @@ export declare class SocketClient {
|
|
|
27
60
|
get status(): SocketStatusEnum;
|
|
28
61
|
private _websocketServiceDid;
|
|
29
62
|
private roomId;
|
|
30
|
-
roomMembers:
|
|
63
|
+
roomMembers: string[];
|
|
64
|
+
private _lastPresenceSignature;
|
|
65
|
+
private _presenceColorByName;
|
|
66
|
+
private _onPresenceChange?;
|
|
67
|
+
private _pendingAwarenessUpdates;
|
|
31
68
|
private collaborationKeyPair;
|
|
32
69
|
private ownerKeyPair?;
|
|
33
70
|
private contractAddress?;
|
|
34
71
|
private ownerUcan?;
|
|
35
72
|
private collaborationUcan?;
|
|
36
73
|
private ownerAddress?;
|
|
74
|
+
private editLock?;
|
|
75
|
+
private encryptedTitle?;
|
|
76
|
+
private identityToken?;
|
|
77
|
+
private editUcan?;
|
|
78
|
+
private refreshEditClaim?;
|
|
79
|
+
private lastGoodEditUcan?;
|
|
80
|
+
private actorHandle?;
|
|
81
|
+
private joinOnly?;
|
|
37
82
|
private roomKey;
|
|
38
83
|
private roomInfo?;
|
|
39
84
|
private awareness;
|
|
40
85
|
private connectionAttemptErrorCount;
|
|
41
86
|
private _onHandshakeData;
|
|
87
|
+
private _onEpochAvailable;
|
|
88
|
+
private _onCutover;
|
|
89
|
+
private _rotating;
|
|
42
90
|
constructor(config: ISocketClientConfig);
|
|
43
91
|
registerAwareness(awareness: Awareness): void;
|
|
92
|
+
private _stampSocketId;
|
|
44
93
|
private _emitWithAck;
|
|
45
94
|
private _fetchRoomMembers;
|
|
95
|
+
private _recomputePresence;
|
|
46
96
|
sendUpdate({ update }: {
|
|
47
97
|
update: string;
|
|
48
98
|
}): Promise<SendUpdateResponse>;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
99
|
+
fetchHydrationRange(sinceSeq?: number): Promise<HydrationResponse>;
|
|
100
|
+
sendSnapshot({ data, floorSeq, publishedMarker, }: {
|
|
101
|
+
data: string;
|
|
102
|
+
floorSeq: number;
|
|
103
|
+
publishedMarker?: string | null;
|
|
104
|
+
}): Promise<SnapshotResponse>;
|
|
105
|
+
sendMirrorSnapshot({ data, fileKeyEpoch, }: {
|
|
106
|
+
data: string;
|
|
107
|
+
fileKeyEpoch: number;
|
|
108
|
+
}): Promise<AckResponse<any>>;
|
|
109
|
+
setDocumentMeta(): Promise<void>;
|
|
110
|
+
/** Owner rename mid-session: refresh the connect-frozen title artifacts (so
|
|
111
|
+
* every later /auth re-sends current values), persist server-side, and let
|
|
112
|
+
* the server broadcast /document/meta_update to room peers. */
|
|
113
|
+
updateDocumentMeta(args: {
|
|
114
|
+
encryptedTitle: string;
|
|
115
|
+
documentTitle: string;
|
|
116
|
+
}): Promise<void>;
|
|
55
117
|
broadcastAwareness(awarenessUpdate: string): Promise<void>;
|
|
56
118
|
disconnect: () => void;
|
|
119
|
+
reauth: () => void;
|
|
120
|
+
rekey(newRoomKey: string, newAppLock?: string, newEditUcan?: string): Promise<void>;
|
|
121
|
+
ackEpochLoaded(documentId: string, epoch: number): Promise<void>;
|
|
57
122
|
terminateSession: () => Promise<void>;
|
|
123
|
+
flushBeacon(mergedUpdate: string): void;
|
|
58
124
|
private getCollaborationKeyPair;
|
|
59
125
|
private isUcanValid;
|
|
60
126
|
private getOwnerToken;
|
|
61
127
|
private buildSessionToken;
|
|
128
|
+
private _authenticate;
|
|
62
129
|
private _handleHandShake;
|
|
63
130
|
private _handleAwarenessUpdate;
|
|
64
131
|
connectSocket(config: ISocketInitConfig): Promise<void>;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import * as Y from 'yjs';
|
|
2
|
+
/** dSheet presence identity. Cell coordinates stay in Yjs awareness and are
|
|
3
|
+
* intentionally excluded from the authoritative socket roster signature. */
|
|
2
4
|
export interface CollabUser {
|
|
3
|
-
clientId: number;
|
|
5
|
+
clientId: string | number;
|
|
4
6
|
name: string;
|
|
5
7
|
color: string;
|
|
6
|
-
isEns: boolean;
|
|
8
|
+
isEns: string | boolean;
|
|
9
|
+
isPlaceholder?: boolean;
|
|
7
10
|
cell?: {
|
|
8
11
|
r: number;
|
|
9
12
|
c: number;
|
|
@@ -16,6 +19,60 @@ export interface CollabConnectionConfig {
|
|
|
16
19
|
roomId: string;
|
|
17
20
|
wsUrl: string;
|
|
18
21
|
isOwner: boolean;
|
|
22
|
+
/** Host signal: doc is in an active live-collaboration context (shared / peers present). Keeps the socket connected. */
|
|
23
|
+
livePresence?: boolean;
|
|
24
|
+
/** Host signal: connect once on open to initialise durability even without an edit. */
|
|
25
|
+
connectOnOpen?: boolean;
|
|
26
|
+
/** Host signal (owner-only): wrapped roomKey bytes (owner-lock construction), uploaded as the server editLock recovery artifact. Opaque to the package. */
|
|
27
|
+
editLock?: string;
|
|
28
|
+
/** Host signal (owner-only): the document title encrypted with the roomKey, uploaded as the server title recovery artifact. Opaque to the package. */
|
|
29
|
+
encryptedTitle?: string;
|
|
30
|
+
/** Host signal (owner-only): Ed25519 UCAN proving the owner identity, sent on /auth so the server binds the CRYPTOGRAPHICALLY PROVEN owner DID (not a bare asserted string). Opaque to the package. */
|
|
31
|
+
identityToken?: string;
|
|
32
|
+
/** Host signal (non-owner editor): the gate-minted edit-admission UCAN, forwarded on /auth so the
|
|
33
|
+
* server admits this connection as a GP-rail editor. Opaque to the package. */
|
|
34
|
+
editUcan?: string;
|
|
35
|
+
/** Host signal (non-owner editor): re-mints a CURRENT-epoch editUcan. Called at every /auth
|
|
36
|
+
* (incl. reconnect after a force-drop). Tri-state: `ok` (fresh token; overrides the static
|
|
37
|
+
* `editUcan`), `demoted` (no longer an editor → terminal, drop to viewer), or `unavailable`
|
|
38
|
+
* (transient/network → retry on the last-good claim). Opaque to the package. */
|
|
39
|
+
refreshEditClaim?: () => Promise<{
|
|
40
|
+
status: 'ok';
|
|
41
|
+
token: string;
|
|
42
|
+
} | {
|
|
43
|
+
status: 'demoted';
|
|
44
|
+
} | {
|
|
45
|
+
status: 'unavailable';
|
|
46
|
+
}>;
|
|
47
|
+
/** Host signal: resolves the rotated roomKey for the given epoch payload (GP: gate release
|
|
48
|
+
* unwrap of `gp`; workspace: app-key unwrap of `appLock`). Returns the new base64 roomKey —
|
|
49
|
+
* or, for a gate-admitted editor, `{ roomKey, editUcan }`: the same gate release that unwraps
|
|
50
|
+
* the relay also mints the new-epoch editUcan, and the cutover re-auth must present THAT token
|
|
51
|
+
* (a re-mint through the host's fresh-open flow can race the deferred publish and hand back a
|
|
52
|
+
* stale-epoch claim). `null` if this actor can't resolve it (falls back to the decrypt-miss
|
|
53
|
+
* self-heal path). Opaque to the package. */
|
|
54
|
+
onRotationPrepare?: (inner: {
|
|
55
|
+
epoch: number;
|
|
56
|
+
gp: string;
|
|
57
|
+
appLock?: string;
|
|
58
|
+
}) => Promise<string | {
|
|
59
|
+
roomKey: string;
|
|
60
|
+
editUcan?: string;
|
|
61
|
+
} | null>;
|
|
62
|
+
/** Host signal: an anonymous per-connection actor handle (public rail). Record-only; opaque. */
|
|
63
|
+
actorHandle?: string;
|
|
64
|
+
/** Host signal (non-creator workspace member): join-only. The server never creates or
|
|
65
|
+
* binds a session for this connection and caps the role at editor; every handshake
|
|
66
|
+
* rejection resolves to a quiet terminated state ('ROOM_NOT_ESTABLISHED' when the room
|
|
67
|
+
* was never established, 'JOIN_REJECTED' otherwise) instead of an error. */
|
|
68
|
+
joinOnly?: boolean;
|
|
69
|
+
/** Host signal (editor): produces the view-plane mirror ciphertext from the live Yjs state. The app
|
|
70
|
+
* AES-GCM-encrypts `{ file: base64(yjsUpdate), source }` under the fileKey in the publish wire
|
|
71
|
+
* format. Absent ⇒ the mirror is not written. The package never sees the fileKey or the crypto. */
|
|
72
|
+
encryptMirror?: (yjsUpdate: Uint8Array) => Promise<string>;
|
|
73
|
+
/** Host signal: the fileKey's rotation epoch (the gate `currentEpoch` / fileKey version — NOT
|
|
74
|
+
* `editGrantEpoch`; default 0 for non-rotating plain-public docs). */
|
|
75
|
+
fileKeyEpoch?: number;
|
|
19
76
|
ownerEdSecret?: string;
|
|
20
77
|
contractAddress?: string;
|
|
21
78
|
ownerAddress?: string;
|
|
@@ -28,21 +85,18 @@ export interface CollabConnectionConfig {
|
|
|
28
85
|
/** Session metadata — changes to these update awareness, NOT reconnect */
|
|
29
86
|
export interface CollabSessionMeta {
|
|
30
87
|
username: string;
|
|
88
|
+
/** Spreadsheet-only awareness color used by Fortune cell cursors. */
|
|
31
89
|
color?: string;
|
|
32
90
|
isEns?: boolean;
|
|
33
91
|
}
|
|
34
|
-
|
|
35
|
-
export
|
|
36
|
-
commitToStorage?: (file: File) => Promise<string>;
|
|
37
|
-
fetchFromStorage?: (cid: string) => Promise<any>;
|
|
38
|
-
}
|
|
39
|
-
export type CollabErrorCode = 'CONNECTION_FAILED' | 'AUTH_FAILED' | 'SYNC_FAILED' | 'TIMEOUT' | 'POISONED_ROOM' | 'UNKNOWN';
|
|
92
|
+
export type CollabServices = Record<string, never>;
|
|
93
|
+
export type CollabErrorCode = 'CONNECTION_FAILED' | 'AUTH_FAILED' | 'SYNC_FAILED' | 'TIMEOUT' | 'UNKNOWN';
|
|
40
94
|
export type CollabError = {
|
|
41
95
|
code: CollabErrorCode;
|
|
42
96
|
message: string;
|
|
43
97
|
recoverable: boolean;
|
|
44
98
|
};
|
|
45
|
-
export type CollabStatus = 'idle' | 'connecting' | 'syncing' | 'ready' | 'reconnecting' | 'error' | 'terminated';
|
|
99
|
+
export type CollabStatus = 'idle' | 'connecting' | 'syncing' | 'ready' | 'reconnecting' | 'rotating' | 'error' | 'terminated';
|
|
46
100
|
export type CollabState = {
|
|
47
101
|
status: 'idle';
|
|
48
102
|
} | {
|
|
@@ -56,6 +110,10 @@ export type CollabState = {
|
|
|
56
110
|
status: 'reconnecting';
|
|
57
111
|
attempt: number;
|
|
58
112
|
maxAttempts: number;
|
|
113
|
+
}
|
|
114
|
+
/** roomKey migration in flight — distinct from `terminated` so consumers don't mistake it for a kick. */
|
|
115
|
+
| {
|
|
116
|
+
status: 'rotating';
|
|
59
117
|
} | {
|
|
60
118
|
status: 'error';
|
|
61
119
|
error: CollabError;
|
|
@@ -84,6 +142,10 @@ export type CollabEvent = {
|
|
|
84
142
|
} | {
|
|
85
143
|
type: 'SESSION_TERMINATED';
|
|
86
144
|
reason?: string;
|
|
145
|
+
}
|
|
146
|
+
/** roomKey cutover in progress; no-op on status — lets the manager gate re-entrancy itself. */
|
|
147
|
+
| {
|
|
148
|
+
type: 'CUTOVER';
|
|
87
149
|
} | {
|
|
88
150
|
type: 'RESET';
|
|
89
151
|
};
|
|
@@ -103,6 +165,13 @@ export interface CollabCallbacks {
|
|
|
103
165
|
data: AckResponse;
|
|
104
166
|
roomKey: string;
|
|
105
167
|
}) => void;
|
|
168
|
+
/** Live rename from the room owner; title is roomKey-encrypted. */
|
|
169
|
+
onTitleUpdate?: (encryptedTitle: string | null) => void;
|
|
170
|
+
/** Decrypt-miss self-heal: resolves the CURRENT-epoch roomKey (same resolution the host
|
|
171
|
+
* uses for `onRotationPrepare`, minus the relay payload — re-read the blob's
|
|
172
|
+
* `wrappedRoomKey` / appLock). Returns the new base64 roomKey, or `null` if this actor
|
|
173
|
+
* can't resolve one (the miss is then a real revocation, not a rotation lag). */
|
|
174
|
+
onRotationSelfHeal?: () => Promise<string | null>;
|
|
106
175
|
}
|
|
107
176
|
/** Discriminated union — TypeScript enforces config+services only when enabled */
|
|
108
177
|
export type CollaborationProps = {
|
|
@@ -138,7 +207,9 @@ export declare enum ServerErrorCode {
|
|
|
138
207
|
NOT_AUTHENTICATED = "NOT_AUTHENTICATED",
|
|
139
208
|
DB_ERROR = "DB_ERROR",
|
|
140
209
|
INTERNAL_ERROR = "INTERNAL_ERROR",
|
|
141
|
-
|
|
210
|
+
JOIN_DISABLED = "JOIN_DISABLED",
|
|
211
|
+
EDIT_REVOKED = "EDIT_REVOKED",
|
|
212
|
+
ROOM_NOT_ESTABLISHED = "ROOM_NOT_ESTABLISHED"
|
|
142
213
|
}
|
|
143
214
|
export interface AckResponse<T = Record<string, any>> {
|
|
144
215
|
status: boolean;
|
|
@@ -156,11 +227,30 @@ export interface SendUpdateResponse extends AckResponse<{
|
|
|
156
227
|
createdAt: number;
|
|
157
228
|
}> {
|
|
158
229
|
}
|
|
159
|
-
export interface
|
|
160
|
-
|
|
161
|
-
createdAt: number;
|
|
230
|
+
export interface HydrationRow {
|
|
231
|
+
id: string;
|
|
162
232
|
documentId: string;
|
|
163
|
-
|
|
233
|
+
seq: number;
|
|
234
|
+
data: string;
|
|
235
|
+
updateType: string;
|
|
236
|
+
committed?: boolean;
|
|
237
|
+
commitCid?: string | null;
|
|
238
|
+
createdAt?: number;
|
|
239
|
+
sessionDid?: string;
|
|
240
|
+
publishedMarker?: string | null;
|
|
241
|
+
floorSeq?: number | null;
|
|
242
|
+
}
|
|
243
|
+
export interface HydrationResponse extends AckResponse<{
|
|
244
|
+
history: HydrationRow[];
|
|
245
|
+
total: number;
|
|
246
|
+
snapshot: HydrationRow | null;
|
|
247
|
+
nextSeq: number | null;
|
|
248
|
+
hasMore: boolean;
|
|
249
|
+
}> {
|
|
250
|
+
}
|
|
251
|
+
export interface SnapshotResponse extends AckResponse<{
|
|
252
|
+
id: string;
|
|
253
|
+
seq: number;
|
|
164
254
|
}> {
|
|
165
255
|
}
|
|
166
256
|
export interface ISocketInitConfig {
|
|
@@ -168,7 +258,7 @@ export interface ISocketInitConfig {
|
|
|
168
258
|
onDisconnect: () => void;
|
|
169
259
|
onSocketDropped: () => void;
|
|
170
260
|
onError: (err: Error) => void;
|
|
171
|
-
onHandShakeError: (err: Error, statusCode?: number) => void;
|
|
261
|
+
onHandShakeError: (err: Error, statusCode?: number, errorCode?: ServerErrorCode) => void;
|
|
172
262
|
onContentUpdate: (data: {
|
|
173
263
|
id: string;
|
|
174
264
|
data: string;
|
|
@@ -182,6 +272,8 @@ export interface ISocketInitConfig {
|
|
|
182
272
|
};
|
|
183
273
|
roomId: string;
|
|
184
274
|
}) => void;
|
|
275
|
+
onPresenceChange?: (collaborators: CollabUser[]) => void;
|
|
276
|
+
onTitleUpdate?: (encryptedTitle: string | null) => void;
|
|
185
277
|
onSessionTerminated: (data: {
|
|
186
278
|
roomId: string;
|
|
187
279
|
}) => void;
|
|
@@ -208,5 +300,9 @@ export interface IAuthArgs {
|
|
|
208
300
|
contractAddress?: string;
|
|
209
301
|
sessionDid?: string;
|
|
210
302
|
roomInfo?: string;
|
|
303
|
+
identityToken?: string;
|
|
304
|
+
editUcan?: string;
|
|
305
|
+
actorHandle?: string;
|
|
306
|
+
joinOnly?: boolean;
|
|
211
307
|
appType?: AppType;
|
|
212
308
|
}
|
|
@@ -5,6 +5,10 @@ export declare const useSyncManager: (config: SyncManagerConfig) => {
|
|
|
5
5
|
connect: (connectConfig: CollabConnectionConfig) => void;
|
|
6
6
|
disconnect: () => void;
|
|
7
7
|
terminateSession: () => void;
|
|
8
|
+
updateTitle: (args: {
|
|
9
|
+
encryptedTitle: string;
|
|
10
|
+
documentTitle: string;
|
|
11
|
+
}) => void;
|
|
8
12
|
isReady: boolean;
|
|
9
13
|
isSyncing: boolean;
|
|
10
14
|
awareness: import('y-protocols/awareness.js').Awareness | null;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as BZ from "yjs";
|
|
2
|
-
import { i as Yl, a as ul, s as eZ, r as hl, y as Il, b as pl, m as Rl } from "./index-
|
|
2
|
+
import { i as Yl, a as ul, s as eZ, r as hl, y as Il, b as pl, m as Rl } from "./index-CRzwkPJq.js";
|
|
3
3
|
import { g as sl } from "./executeStringFunction-adhsb5GH.js";
|
|
4
4
|
import { Workbook as al } from "exceljs";
|
|
5
5
|
import Kl from "luckyexcel";
|
|
6
|
-
import { e as Jl, b as xl, n as Ml } from "./xlsx-hyperlink-inline-
|
|
7
|
-
import { parseDefinedNamesFromXlsxBuffer as Sl, excelEntriesToDefinedNames as OZ } from "./xlsx-defined-names-
|
|
6
|
+
import { e as Jl, b as xl, n as Ml } from "./xlsx-hyperlink-inline-DWLdqDYy.js";
|
|
7
|
+
import { parseDefinedNamesFromXlsxBuffer as Sl, excelEntriesToDefinedNames as OZ } from "./xlsx-defined-names-CflXuPTO.js";
|
|
8
8
|
import { toast as fZ } from "@fileverse/ui";
|
|
9
9
|
var Zl = { exports: {} };
|
|
10
10
|
(function(c) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { utils as F, write as it } from "xlsx-js-style";
|
|
2
2
|
import { Workbook as ct } from "exceljs";
|
|
3
3
|
import { toast as nt } from "@fileverse/ui";
|
|
4
|
-
import { J as ft, c as X, g as ut, a as pt } from "./xlsx-hyperlink-inline-
|
|
4
|
+
import { J as ft, c as X, g as ut, a as pt } from "./xlsx-hyperlink-inline-DWLdqDYy.js";
|
|
5
5
|
import { b as dt } from "./executeStringFunction-adhsb5GH.js";
|
|
6
|
-
import { e as ht } from "./index-
|
|
6
|
+
import { e as ht } from "./index-CRzwkPJq.js";
|
|
7
7
|
const mt = {
|
|
8
8
|
1: "thin",
|
|
9
9
|
2: "hair",
|
|
@@ -810,7 +810,7 @@ const Y = (e) => {
|
|
|
810
810
|
D
|
|
811
811
|
);
|
|
812
812
|
});
|
|
813
|
-
const { applyDefinedNamesToExcelWorkbook: ot } = await import("./xlsx-defined-names-
|
|
813
|
+
const { applyDefinedNamesToExcelWorkbook: ot } = await import("./xlsx-defined-names-CflXuPTO.js");
|
|
814
814
|
ot(
|
|
815
815
|
j,
|
|
816
816
|
(T = (a = (d = e.current).getWorkbookContext) == null ? void 0 : a.call(d)) == null ? void 0 : T.definedNames,
|