edytor 4.0.0 → 6.0.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/README.md +2 -2
- package/dist/block/block.svelte.js +6 -21
- package/dist/block/inlineBlock.svelte.js +4 -6
- package/dist/clipboard/moveText.js +6 -8
- package/dist/cloudflare/DocumentRoom.d.ts +10 -3
- package/dist/cloudflare/index.d.ts +1 -1
- package/dist/cloudflare/room/access.d.ts +8 -3
- package/dist/cloudflare/room/access.js +44 -20
- package/dist/cloudflare/room/admission.js +28 -5
- package/dist/cloudflare/room/comments.js +28 -1
- package/dist/cloudflare/room/context.d.ts +106 -109
- package/dist/cloudflare/room/context.js +10 -3
- package/dist/cloudflare/room/forged.d.ts +14 -0
- package/dist/cloudflare/room/forged.js +30 -2
- package/dist/cloudflare/room/frames.d.ts +4 -0
- package/dist/cloudflare/room/frames.js +5 -1
- package/dist/cloudflare/room/purge.js +2 -1
- package/dist/cloudflare/room/replicas.d.ts +4 -2
- package/dist/cloudflare/room/replicas.js +8 -3
- package/dist/cloudflare/room/suggest.d.ts +7 -0
- package/dist/cloudflare/room/suggest.js +143 -0
- package/dist/cloudflare/routeDocumentSocket.d.ts +10 -1
- package/dist/cloudflare/routeDocumentSocket.js +5 -3
- package/dist/components/Content.svelte +9 -1
- package/dist/crdt/anchors.js +3 -2
- package/dist/crdt/attribution/attribution.d.ts +3 -0
- package/dist/crdt/attribution/attribution.js +3 -3
- package/dist/crdt/doc/funnel.d.ts +1 -1
- package/dist/crdt/doc/funnel.js +5 -2
- package/dist/crdt/doc/gate.d.ts +1 -1
- package/dist/crdt/doc/history.d.ts +6 -0
- package/dist/crdt/doc/history.js +79 -59
- package/dist/crdt/doc/ops.d.ts +81 -0
- package/dist/crdt/doc/ops.js +1898 -0
- package/dist/crdt/doc/reads.d.ts +2 -2
- package/dist/crdt/doc/reads.js +18 -28
- package/dist/crdt/doc/suggest.d.ts +167 -0
- package/dist/crdt/doc/suggest.js +579 -0
- package/dist/crdt/document.d.ts +10 -1
- package/dist/crdt/document.js +19 -11
- package/dist/crdt/edytor-doc.d.ts +294 -272
- package/dist/crdt/edytor-doc.js +123 -108
- package/dist/crdt/engine-api.d.ts +8 -2
- package/dist/crdt/engine.d.ts +2 -1
- package/dist/crdt/engine.js +3 -1
- package/dist/crdt/flow.d.ts +5 -47
- package/dist/crdt/flow.js +3 -269
- package/dist/crdt/index.d.ts +102 -108
- package/dist/crdt/index.js +3 -0
- package/dist/crdt/operations.d.ts +13 -4
- package/dist/crdt/parts.d.ts +43 -0
- package/dist/crdt/parts.js +44 -0
- package/dist/crdt/placement/display.d.ts +1 -1
- package/dist/crdt/placement/display.js +3 -3
- package/dist/crdt/placement/keys.d.ts +5 -0
- package/dist/crdt/placement/keys.js +5 -0
- package/dist/crdt/placement/resolve.d.ts +1 -1
- package/dist/crdt/placement/resolve.js +2 -1
- package/dist/crdt/protocol.d.ts +1 -1
- package/dist/crdt/protocol.js +1 -1
- package/dist/crdt/protocols/auth.d.ts +11 -1
- package/dist/crdt/protocols/auth.js +17 -1
- package/dist/crdt/protocols/awareness.js +46 -81
- package/dist/crdt/protocols/comments.d.ts +16 -1
- package/dist/crdt/protocols/comments.js +9 -1
- package/dist/crdt/providers/index.d.ts +12 -22
- package/dist/crdt/providers/index.js +3 -1
- package/dist/crdt/providers/indexeddb.d.ts +6 -10
- package/dist/crdt/providers/indexeddb.js +10 -16
- package/dist/crdt/providers/room.d.ts +11 -0
- package/dist/crdt/providers/room.js +26 -12
- package/dist/crdt/providers/websocket.d.ts +8 -12
- package/dist/crdt/providers/websocket.js +17 -19
- package/dist/crdt/purge.d.ts +7 -1
- package/dist/crdt/purge.js +32 -2
- package/dist/crdt/rangeDelete.d.ts +2 -47
- package/dist/crdt/rangeDelete.js +1 -169
- package/dist/crdt/restore.d.ts +2 -1
- package/dist/crdt/restore.js +9 -0
- package/dist/crdt/schema.d.ts +9 -1
- package/dist/crdt/schema.js +13 -3
- package/dist/crdt/structs.d.ts +2 -0
- package/dist/crdt/structs.js +2 -0
- package/dist/crdt/text/index/build.d.ts +125 -0
- package/dist/crdt/text/index/build.js +2895 -0
- package/dist/crdt/text/index/shared.d.ts +2 -6
- package/dist/crdt/text/index/shared.js +19 -19
- package/dist/crdt/text/model.d.ts +10 -0
- package/dist/crdt/text/model.js +10 -4
- package/dist/crdt/text/runs.d.ts +43 -42
- package/dist/crdt/text/runs.js +2 -165
- package/dist/crdt/vendor/yjs/UPSTREAM.md +83 -0
- package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
- package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +6 -3
- package/dist/crdt/vendor/yjs/dts/ynode.d.ts +13 -2
- package/dist/crdt/vendor/yjs/src/index.js +1 -1
- package/dist/crdt/vendor/yjs/src/structs/Item.js +44 -5
- package/dist/crdt/vendor/yjs/src/ynode.js +29 -7
- package/dist/edytor.svelte.d.ts +1 -1
- package/dist/edytor.svelte.js +100 -75
- package/dist/events/beforeInputCommands.d.ts +2 -2
- package/dist/events/domTextOffset.d.ts +2 -0
- package/dist/events/domTextOffset.js +8 -0
- package/dist/events/nativeInteractiveControl.d.ts +11 -1
- package/dist/events/nativeInteractiveControl.js +41 -1
- package/dist/events/onBeforeInput.js +3 -3
- package/dist/events/onFocus.js +9 -11
- package/dist/export/forms.d.ts +75 -75
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -1
- package/dist/labels.d.ts +8 -0
- package/dist/labels.js +6 -1
- package/dist/plugins/comments/CommentsController.svelte.d.ts +5 -1
- package/dist/plugins/comments/CommentsController.svelte.js +14 -4
- package/dist/plugins/find/search.js +2 -2
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/index.js +1 -0
- package/dist/plugins/richtext/RichTextPlugin.svelte +63 -61
- package/dist/plugins/richtext/richTextOperations.js +26 -32
- package/dist/plugins/suggestions/SuggestionBars.svelte +40 -23
- package/dist/plugins/suggestions/SuggestionBars.svelte.d.ts +3 -1
- package/dist/plugins/suggestions/SuggestionRuns.svelte +77 -0
- package/dist/plugins/suggestions/SuggestionRuns.svelte.d.ts +9 -0
- package/dist/plugins/suggestions/shared.svelte.d.ts +58 -0
- package/dist/plugins/suggestions/shared.svelte.js +421 -0
- package/dist/plugins/suggestions/suggestionsPlugin.d.ts +15 -4
- package/dist/selection/domSelection.d.ts +2 -0
- package/dist/selection/domSelection.js +6 -2
- package/dist/selection/pointer.svelte.js +75 -48
- package/dist/selection/replaceSelection.js +2 -6
- package/dist/selection/selection.svelte.d.ts +3 -3
- package/dist/selection/selection.svelte.js +39 -32
- package/dist/selection/selection.utils.d.ts +14 -0
- package/dist/selection/selection.utils.js +33 -11
- package/dist/session/commands.js +21 -11
- package/dist/session/composition.svelte.d.ts +5 -3
- package/dist/session/composition.svelte.js +21 -17
- package/dist/session/handles.d.ts +4 -17
- package/dist/session/handles.js +14 -44
- package/dist/session/navigation.js +6 -9
- package/dist/session/selection.d.ts +11 -5
- package/dist/session/selection.js +14 -48
- package/dist/session/suggestions.svelte.d.ts +38 -9
- package/dist/session/suggestions.svelte.js +37 -18
- package/dist/session/virtual.d.ts +1 -1
- package/dist/surface/attributes.d.ts +5 -0
- package/dist/surface/attributes.js +2 -2
- package/dist/surface/cells.d.ts +2 -26
- package/dist/surface/cells.js +2 -33
- package/dist/surface/observer.svelte.d.ts +6 -1
- package/dist/surface/observer.svelte.js +143 -111
- package/dist/surface/pin.svelte.d.ts +9 -9
- package/dist/surface/pin.svelte.js +15 -22
- package/dist/surface/projector.svelte.js +10 -16
- package/dist/text/text.svelte.js +14 -19
- package/dist/themes/notion.css +29 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +7 -0
- package/package.json +3 -1
- package/dist/crdt/doc/capability.d.ts +0 -56
- package/dist/crdt/doc/capability.js +0 -211
- package/dist/crdt/doc/content.d.ts +0 -16
- package/dist/crdt/doc/content.js +0 -77
- package/dist/crdt/doc/delete.d.ts +0 -15
- package/dist/crdt/doc/delete.js +0 -84
- package/dist/crdt/doc/layout.d.ts +0 -9
- package/dist/crdt/doc/layout.js +0 -178
- package/dist/crdt/doc/meta.d.ts +0 -19
- package/dist/crdt/doc/meta.js +0 -199
- package/dist/crdt/doc/moves.d.ts +0 -21
- package/dist/crdt/doc/moves.js +0 -274
- package/dist/crdt/doc/split-merge.d.ts +0 -10
- package/dist/crdt/doc/split-merge.js +0 -179
- package/dist/crdt/doc/steps.d.ts +0 -65
- package/dist/crdt/doc/steps.js +0 -311
- package/dist/crdt/text/index/cache.d.ts +0 -16
- package/dist/crdt/text/index/cache.js +0 -175
- package/dist/crdt/text/index/claims.d.ts +0 -18
- package/dist/crdt/text/index/claims.js +0 -225
- package/dist/crdt/text/index/fold.d.ts +0 -21
- package/dist/crdt/text/index/fold.js +0 -656
- package/dist/crdt/text/index/layout.d.ts +0 -24
- package/dist/crdt/text/index/layout.js +0 -271
- package/dist/crdt/text/index/placement.d.ts +0 -47
- package/dist/crdt/text/index/placement.js +0 -741
- package/dist/crdt/text/index/records.d.ts +0 -13
- package/dist/crdt/text/index/records.js +0 -139
- package/dist/crdt/text/index/report.d.ts +0 -18
- package/dist/crdt/text/index/report.js +0 -337
- package/dist/crdt/text/index/state.d.ts +0 -197
- package/dist/crdt/text/index/state.js +0 -116
- package/dist/crdt/text/index/streams.d.ts +0 -25
- package/dist/crdt/text/index/streams.js +0 -248
|
@@ -14,10 +14,20 @@ import * as decoding from 'lib0-v14/decoding';
|
|
|
14
14
|
export declare const messagePermissionDenied = 0;
|
|
15
15
|
/** The denial of a read-only socket's write: that socket still syncs (not a terminal refusal). */
|
|
16
16
|
export declare const READ_ONLY_DENIAL = "read-only";
|
|
17
|
+
/** The denial of what a suggest-only socket wrote that is no suggestion's: it still syncs. */
|
|
18
|
+
export declare const SUGGEST_ONLY_DENIAL = "suggest-only";
|
|
17
19
|
export declare const writePermissionDenied: (encoder: encoding.Encoder, reason: string) => void;
|
|
18
20
|
/** The socket may read but not write: sent when it joins, before any write is refused. */
|
|
19
21
|
export declare const messageReadOnly = 1;
|
|
20
22
|
export declare const writeReadOnly: (encoder: encoding.Encoder) => void;
|
|
23
|
+
/**
|
|
24
|
+
* The socket's access (the string that follows: `write`, `suggest` or
|
|
25
|
+
* `comment`), sent when it joins, before anything else it reads; a
|
|
26
|
+
* `comment` one's after its read-only notice (a `read` one has only the
|
|
27
|
+
* notice).
|
|
28
|
+
*/
|
|
29
|
+
export declare const messageAccess = 2;
|
|
30
|
+
export declare const writeAccess: (encoder: encoding.Encoder, access: string) => void;
|
|
21
31
|
export type PermissionDeniedHandler = (ydoc: unknown, reason: string) => void;
|
|
22
32
|
/**
|
|
23
33
|
* Decode one auth payload. Returns the auth message subtype so the caller
|
|
@@ -25,4 +35,4 @@ export type PermissionDeniedHandler = (ydoc: unknown, reason: string) => void;
|
|
|
25
35
|
* do not speak is protocol skew, and this stack's contract is that skew is
|
|
26
36
|
* reported, not silently dropped (same rule as unknown sync subtypes).
|
|
27
37
|
*/
|
|
28
|
-
export declare const readAuthMessage: (decoder: decoding.Decoder, y: unknown, permissionDeniedHandler: PermissionDeniedHandler, readOnlyHandler?: (ydoc: unknown) => void) => number;
|
|
38
|
+
export declare const readAuthMessage: (decoder: decoding.Decoder, y: unknown, permissionDeniedHandler: PermissionDeniedHandler, readOnlyHandler?: (ydoc: unknown) => void, accessHandler?: (access: string) => void) => number;
|
|
@@ -14,6 +14,8 @@ import * as decoding from 'lib0-v14/decoding';
|
|
|
14
14
|
export const messagePermissionDenied = 0;
|
|
15
15
|
/** The denial of a read-only socket's write: that socket still syncs (not a terminal refusal). */
|
|
16
16
|
export const READ_ONLY_DENIAL = 'read-only';
|
|
17
|
+
/** The denial of what a suggest-only socket wrote that is no suggestion's: it still syncs. */
|
|
18
|
+
export const SUGGEST_ONLY_DENIAL = 'suggest-only';
|
|
17
19
|
export const writePermissionDenied = (encoder, reason) => {
|
|
18
20
|
encoding.writeVarUint(encoder, messagePermissionDenied);
|
|
19
21
|
encoding.writeVarString(encoder, reason);
|
|
@@ -23,13 +25,24 @@ export const messageReadOnly = 1;
|
|
|
23
25
|
export const writeReadOnly = (encoder) => {
|
|
24
26
|
encoding.writeVarUint(encoder, messageReadOnly);
|
|
25
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* The socket's access (the string that follows: `write`, `suggest` or
|
|
30
|
+
* `comment`), sent when it joins, before anything else it reads; a
|
|
31
|
+
* `comment` one's after its read-only notice (a `read` one has only the
|
|
32
|
+
* notice).
|
|
33
|
+
*/
|
|
34
|
+
export const messageAccess = 2;
|
|
35
|
+
export const writeAccess = (encoder, access) => {
|
|
36
|
+
encoding.writeVarUint(encoder, messageAccess);
|
|
37
|
+
encoding.writeVarString(encoder, access);
|
|
38
|
+
};
|
|
26
39
|
/**
|
|
27
40
|
* Decode one auth payload. Returns the auth message subtype so the caller
|
|
28
41
|
* can surface unknown subtypes — a valid envelope carrying an auth type we
|
|
29
42
|
* do not speak is protocol skew, and this stack's contract is that skew is
|
|
30
43
|
* reported, not silently dropped (same rule as unknown sync subtypes).
|
|
31
44
|
*/
|
|
32
|
-
export const readAuthMessage = (decoder, y, permissionDeniedHandler, readOnlyHandler) => {
|
|
45
|
+
export const readAuthMessage = (decoder, y, permissionDeniedHandler, readOnlyHandler, accessHandler) => {
|
|
33
46
|
const authMessageType = decoding.readVarUint(decoder);
|
|
34
47
|
switch (authMessageType) {
|
|
35
48
|
case messagePermissionDenied:
|
|
@@ -38,6 +51,9 @@ export const readAuthMessage = (decoder, y, permissionDeniedHandler, readOnlyHan
|
|
|
38
51
|
case messageReadOnly:
|
|
39
52
|
// Unclaimed, it is returned like any subtype the caller does not speak.
|
|
40
53
|
readOnlyHandler?.(y);
|
|
54
|
+
break;
|
|
55
|
+
case messageAccess:
|
|
56
|
+
accessHandler?.(decoding.readVarString(decoder));
|
|
41
57
|
}
|
|
42
58
|
return authMessageType;
|
|
43
59
|
};
|
|
@@ -94,41 +94,19 @@ export class Awareness extends IsolatedObservable {
|
|
|
94
94
|
}
|
|
95
95
|
setLocalState(state) {
|
|
96
96
|
const clientID = this.clientID;
|
|
97
|
-
const currLocalMeta = this.meta.get(clientID);
|
|
98
|
-
const clock = currLocalMeta === undefined ? 0 : currLocalMeta.clock + 1;
|
|
99
97
|
const prevState = this.states.get(clientID);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
put(this, clientID, (this.meta.get(clientID)?.clock ?? -1) + 1, state, time.getUnixTime());
|
|
99
|
+
const changes = noChanges();
|
|
100
|
+
if (state === null)
|
|
101
|
+
changes.removed.push(clientID);
|
|
102
|
+
else if (prevState == null)
|
|
103
|
+
changes.added.push(clientID);
|
|
103
104
|
else {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
clock,
|
|
108
|
-
lastUpdated: time.getUnixTime()
|
|
109
|
-
});
|
|
110
|
-
const added = [];
|
|
111
|
-
const updated = [];
|
|
112
|
-
const filteredUpdated = [];
|
|
113
|
-
const removed = [];
|
|
114
|
-
if (state === null) {
|
|
115
|
-
removed.push(clientID);
|
|
105
|
+
changes.updated.push(clientID);
|
|
106
|
+
if (!jsonEquals(prevState, state))
|
|
107
|
+
changes.changed.push(clientID);
|
|
116
108
|
}
|
|
117
|
-
|
|
118
|
-
if (state != null) {
|
|
119
|
-
added.push(clientID);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
updated.push(clientID);
|
|
124
|
-
if (!jsonEquals(prevState, state)) {
|
|
125
|
-
filteredUpdated.push(clientID);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
if (added.length > 0 || filteredUpdated.length > 0 || removed.length > 0) {
|
|
129
|
-
this.emit('change', [{ added, updated: filteredUpdated, removed }, 'local']);
|
|
130
|
-
}
|
|
131
|
-
this.emit('update', [{ added, updated, removed }, 'local']);
|
|
109
|
+
emitChanges(this, changes, 'local', true);
|
|
132
110
|
}
|
|
133
111
|
setLocalStateField(field, value) {
|
|
134
112
|
const state = this.getLocalState();
|
|
@@ -143,30 +121,40 @@ export class Awareness extends IsolatedObservable {
|
|
|
143
121
|
return this.states;
|
|
144
122
|
}
|
|
145
123
|
}
|
|
124
|
+
const noChanges = () => ({ added: [], updated: [], changed: [], removed: [] });
|
|
125
|
+
/** Write client `clientID`'s `state` (`null`: removed) at `clock`, heard `now`. */
|
|
126
|
+
const put = (awareness, clientID, clock, state, now) => {
|
|
127
|
+
if (state === null)
|
|
128
|
+
awareness.states.delete(clientID);
|
|
129
|
+
else
|
|
130
|
+
awareness.states.set(clientID, state);
|
|
131
|
+
awareness.meta.set(clientID, { clock, lastUpdated: now });
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Emit a change: `change` when a client was added, removed or changed its
|
|
135
|
+
* state, `update` when one was touched at all (`always`: even when none was).
|
|
136
|
+
*/
|
|
137
|
+
const emitChanges = (awareness, { added, updated, changed, removed }, origin, always = false) => {
|
|
138
|
+
if (added.length > 0 || changed.length > 0 || removed.length > 0)
|
|
139
|
+
awareness.emit('change', [{ added, updated: changed, removed }, origin]);
|
|
140
|
+
if (always || added.length > 0 || updated.length > 0 || removed.length > 0)
|
|
141
|
+
awareness.emit('update', [{ added, updated, removed }, origin]);
|
|
142
|
+
};
|
|
146
143
|
/**
|
|
147
144
|
* Mark (remote) clients as inactive and remove them from the list of active
|
|
148
145
|
* peers. This change is propagated to remote clients.
|
|
149
146
|
*/
|
|
150
147
|
export const removeAwarenessStates = (awareness, clients, origin) => {
|
|
151
|
-
const
|
|
152
|
-
for (
|
|
153
|
-
const clientID = clients[i];
|
|
148
|
+
const changes = noChanges();
|
|
149
|
+
for (const clientID of clients) {
|
|
154
150
|
if (awareness.states.has(clientID)) {
|
|
155
151
|
awareness.states.delete(clientID);
|
|
156
|
-
if (clientID === awareness.clientID)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
clock: curMeta.clock + 1,
|
|
160
|
-
lastUpdated: time.getUnixTime()
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
removed.push(clientID);
|
|
152
|
+
if (clientID === awareness.clientID)
|
|
153
|
+
put(awareness, clientID, awareness.meta.get(clientID).clock + 1, null, time.getUnixTime());
|
|
154
|
+
changes.removed.push(clientID);
|
|
164
155
|
}
|
|
165
156
|
}
|
|
166
|
-
|
|
167
|
-
awareness.emit('change', [{ added: [], updated: [], removed }, origin]);
|
|
168
|
-
awareness.emit('update', [{ added: [], updated: [], removed }, origin]);
|
|
169
|
-
}
|
|
157
|
+
emitChanges(awareness, changes, origin);
|
|
170
158
|
};
|
|
171
159
|
/** The update carrying `clients`' entries of `awareness` (their states in `states`). */
|
|
172
160
|
export const encodeAwarenessUpdate = (awareness, clients, states = awareness.states) => writeAwarenessEntries(clients.map((clientID) => ({
|
|
@@ -202,36 +190,19 @@ export const writeAwarenessEntries = (entries) => encoding.encode((encoder) => {
|
|
|
202
190
|
*/
|
|
203
191
|
export const applyAwarenessUpdate = (awareness, update, origin) => {
|
|
204
192
|
const timestamp = time.getUnixTime();
|
|
205
|
-
const
|
|
206
|
-
const updated =
|
|
207
|
-
const
|
|
208
|
-
const removed = [];
|
|
209
|
-
for (const entry of readAwarenessEntries(update)) {
|
|
210
|
-
const { clientID, state } = entry;
|
|
211
|
-
let { clock } = entry;
|
|
193
|
+
const changes = noChanges();
|
|
194
|
+
const { added, updated, changed, removed } = changes;
|
|
195
|
+
for (const { clientID, clock, state } of readAwarenessEntries(update)) {
|
|
212
196
|
const clientMeta = awareness.meta.get(clientID);
|
|
213
197
|
const prevState = awareness.states.get(clientID);
|
|
214
198
|
const currClock = clientMeta === undefined ? 0 : clientMeta.clock;
|
|
215
199
|
if (currClock < clock ||
|
|
216
200
|
(currClock === clock && state === null && awareness.states.has(clientID))) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
clock++;
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
awareness.states.delete(clientID);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
awareness.states.set(clientID, state);
|
|
230
|
-
}
|
|
231
|
-
awareness.meta.set(clientID, {
|
|
232
|
-
clock,
|
|
233
|
-
lastUpdated: timestamp
|
|
234
|
-
});
|
|
201
|
+
// Never let a remote client remove this local state: keep it and
|
|
202
|
+
// broadcast that this client still exists, by increasing the clock.
|
|
203
|
+
const local = awareness.getLocalState();
|
|
204
|
+
const keep = state === null && clientID === awareness.clientID && local != null;
|
|
205
|
+
put(awareness, clientID, keep ? clock + 1 : clock, keep ? local : state, timestamp);
|
|
235
206
|
if (clientMeta === undefined && state !== null) {
|
|
236
207
|
added.push(clientID);
|
|
237
208
|
}
|
|
@@ -239,17 +210,11 @@ export const applyAwarenessUpdate = (awareness, update, origin) => {
|
|
|
239
210
|
removed.push(clientID);
|
|
240
211
|
}
|
|
241
212
|
else if (state !== null) {
|
|
242
|
-
if (!jsonEquals(state, prevState))
|
|
243
|
-
|
|
244
|
-
}
|
|
213
|
+
if (!jsonEquals(state, prevState))
|
|
214
|
+
changed.push(clientID);
|
|
245
215
|
updated.push(clientID);
|
|
246
216
|
}
|
|
247
217
|
}
|
|
248
218
|
}
|
|
249
|
-
|
|
250
|
-
awareness.emit('change', [{ added, updated: filteredUpdated, removed }, origin]);
|
|
251
|
-
}
|
|
252
|
-
if (added.length > 0 || updated.length > 0 || removed.length > 0) {
|
|
253
|
-
awareness.emit('update', [{ added, updated, removed }, origin]);
|
|
254
|
-
}
|
|
219
|
+
emitChanges(awareness, changes, origin);
|
|
255
220
|
};
|
|
@@ -67,6 +67,16 @@ export type CommentRequest = {
|
|
|
67
67
|
block?: string | null;
|
|
68
68
|
/** The first comment's id (default: one the store makes). */
|
|
69
69
|
comment?: string;
|
|
70
|
+
/**
|
|
71
|
+
* The text it is made on, as anchors (`DocAnchor`s, each span's
|
|
72
|
+
* ends): the room writes its anchor mark over them for a user who
|
|
73
|
+
* may only comment (`room.access.comment`); a writer's view writes
|
|
74
|
+
* it itself.
|
|
75
|
+
*/
|
|
76
|
+
anchors?: {
|
|
77
|
+
start: unknown;
|
|
78
|
+
end: unknown;
|
|
79
|
+
}[];
|
|
70
80
|
} | {
|
|
71
81
|
op: 'reply';
|
|
72
82
|
thread: string;
|
|
@@ -85,11 +95,16 @@ export type CommentRequest = {
|
|
|
85
95
|
thread: string;
|
|
86
96
|
comment?: string;
|
|
87
97
|
};
|
|
88
|
-
/**
|
|
98
|
+
/**
|
|
99
|
+
* Who asks: a verified user, read-only or not; a moderator may delete
|
|
100
|
+
* anyone's comment; `anchors`: the store writes a new thread's anchor (a
|
|
101
|
+
* user who may only comment).
|
|
102
|
+
*/
|
|
89
103
|
export type CommentActor = {
|
|
90
104
|
user: string;
|
|
91
105
|
readOnly?: boolean;
|
|
92
106
|
moderator?: boolean;
|
|
107
|
+
anchors?: boolean;
|
|
93
108
|
};
|
|
94
109
|
/** What one request changed (the room broadcasts it, `onComment` receives it). */
|
|
95
110
|
export type CommentChange = {
|
|
@@ -62,13 +62,21 @@ export const parseCommentRequest = (value) => {
|
|
|
62
62
|
return null;
|
|
63
63
|
if (v.block !== undefined && v.block !== null && typeof v.block !== 'string')
|
|
64
64
|
return null;
|
|
65
|
+
if (v.anchors !== undefined &&
|
|
66
|
+
(!Array.isArray(v.anchors) ||
|
|
67
|
+
v.anchors.length > 64 ||
|
|
68
|
+
v.anchors.some((a) => typeof a?.start !== 'object' || typeof a?.end !== 'object')))
|
|
69
|
+
return null;
|
|
65
70
|
return {
|
|
66
71
|
op: 'add',
|
|
67
72
|
thread: v.thread,
|
|
68
73
|
body: v.body,
|
|
69
74
|
...(v.quote === undefined ? {} : { quote: v.quote }),
|
|
70
75
|
...(v.block === undefined ? {} : { block: v.block }),
|
|
71
|
-
...(v.comment === undefined ? {} : { comment: v.comment })
|
|
76
|
+
...(v.comment === undefined ? {} : { comment: v.comment }),
|
|
77
|
+
...(v.anchors === undefined
|
|
78
|
+
? {}
|
|
79
|
+
: { anchors: v.anchors })
|
|
72
80
|
};
|
|
73
81
|
case 'reply':
|
|
74
82
|
if (typeof v.body !== 'string')
|
|
@@ -47,6 +47,8 @@ export type EdytorSyncPayload = {
|
|
|
47
47
|
* websocket sync's local store. `document.attachSync` passes it.
|
|
48
48
|
*/
|
|
49
49
|
attach?: (sync: EdytorSync) => EdytorSyncCleanup | void;
|
|
50
|
+
/** What the room says the document's user may do (`EdytorDocument.access`). */
|
|
51
|
+
access?: (access: 'write' | 'suggest' | 'comment' | 'read') => void;
|
|
50
52
|
};
|
|
51
53
|
export type EdytorSyncCleanup = () => void | Promise<void>;
|
|
52
54
|
export type EdytorSync = ((payload: EdytorSyncPayload) => void | EdytorSyncCleanup) & {
|
|
@@ -145,17 +147,12 @@ export declare const bindProviders: (Y: EngineApi) => {
|
|
|
145
147
|
loadError: unknown;
|
|
146
148
|
_storeTimeout: number;
|
|
147
149
|
_storeTimeoutId: ReturnType<typeof setTimeout> | null;
|
|
148
|
-
|
|
150
|
+
_unlisten: () => void;
|
|
149
151
|
awareness: Awareness;
|
|
150
152
|
bcconnected: boolean;
|
|
151
153
|
disableBc: boolean;
|
|
152
154
|
_ownsAwareness: boolean;
|
|
153
155
|
_bcSubscriber: (data: ArrayBuffer | Uint8Array, origin: unknown) => void;
|
|
154
|
-
_awarenessUpdateHandler: (updates: {
|
|
155
|
-
added: number[];
|
|
156
|
-
updated: number[];
|
|
157
|
-
removed: number[];
|
|
158
|
-
}, origin: unknown) => void;
|
|
159
156
|
get synced(): boolean;
|
|
160
157
|
messageHandlers: Record<number, import("./room.js").RoomMessageHandler</*elided*/ any>>;
|
|
161
158
|
readMessage: (buf: Uint8Array, emitSynced: boolean) => import("lib0-v14/encoding").Encoder;
|
|
@@ -163,7 +160,7 @@ export declare const bindProviders: (Y: EngineApi) => {
|
|
|
163
160
|
connectBc(): void;
|
|
164
161
|
disconnectBc(): void;
|
|
165
162
|
destroy(): Promise<void>;
|
|
166
|
-
emit<N extends "failed" | "
|
|
163
|
+
emit<N extends "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "load-error">(name: N, args: Parameters<{
|
|
167
164
|
synced: (provider: /*elided*/ any) => void;
|
|
168
165
|
'protocol-mismatch': (mismatch: import("./room.js").ProtocolMismatch, provider: /*elided*/ any) => void;
|
|
169
166
|
'load-error': (error: unknown, provider: /*elided*/ any) => void;
|
|
@@ -172,7 +169,7 @@ export declare const bindProviders: (Y: EngineApi) => {
|
|
|
172
169
|
failed: (error: unknown, provider: /*elided*/ any) => void;
|
|
173
170
|
}[N]>): void;
|
|
174
171
|
_observers: Map<string, Set<any>>;
|
|
175
|
-
on<NAME extends "failed" | "
|
|
172
|
+
on<NAME extends "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "load-error">(name: NAME, f: {
|
|
176
173
|
synced: (provider: /*elided*/ any) => void;
|
|
177
174
|
'protocol-mismatch': (mismatch: import("./room.js").ProtocolMismatch, provider: /*elided*/ any) => void;
|
|
178
175
|
'load-error': (error: unknown, provider: /*elided*/ any) => void;
|
|
@@ -187,7 +184,7 @@ export declare const bindProviders: (Y: EngineApi) => {
|
|
|
187
184
|
'schema-mismatch': (detail: import("./room.js").SchemaMismatchDetail, provider: /*elided*/ any) => void;
|
|
188
185
|
failed: (error: unknown, provider: /*elided*/ any) => void;
|
|
189
186
|
}[NAME];
|
|
190
|
-
once<NAME extends "failed" | "
|
|
187
|
+
once<NAME extends "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "load-error">(name: NAME, f: {
|
|
191
188
|
synced: (provider: /*elided*/ any) => void;
|
|
192
189
|
'protocol-mismatch': (mismatch: import("./room.js").ProtocolMismatch, provider: /*elided*/ any) => void;
|
|
193
190
|
'load-error': (error: unknown, provider: /*elided*/ any) => void;
|
|
@@ -195,7 +192,7 @@ export declare const bindProviders: (Y: EngineApi) => {
|
|
|
195
192
|
'schema-mismatch': (detail: import("./room.js").SchemaMismatchDetail, provider: /*elided*/ any) => void;
|
|
196
193
|
failed: (error: unknown, provider: /*elided*/ any) => void;
|
|
197
194
|
}[NAME]): void;
|
|
198
|
-
off<NAME extends "failed" | "
|
|
195
|
+
off<NAME extends "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "load-error">(name: NAME, f: {
|
|
199
196
|
synced: (provider: /*elided*/ any) => void;
|
|
200
197
|
'protocol-mismatch': (mismatch: import("./room.js").ProtocolMismatch, provider: /*elided*/ any) => void;
|
|
201
198
|
'load-error': (error: unknown, provider: /*elided*/ any) => void;
|
|
@@ -256,14 +253,7 @@ export declare const bindProviders: (Y: EngineApi) => {
|
|
|
256
253
|
_resync: ReturnType<typeof setInterval> | undefined;
|
|
257
254
|
_connectTimer: ReturnType<typeof setTimeout> | undefined;
|
|
258
255
|
_checkInterval: ReturnType<typeof setInterval>;
|
|
259
|
-
|
|
260
|
-
local: boolean;
|
|
261
|
-
}) => void;
|
|
262
|
-
_awarenessUpdateHandler: (updates: {
|
|
263
|
-
added: number[];
|
|
264
|
-
updated: number[];
|
|
265
|
-
removed: number[];
|
|
266
|
-
}, origin: unknown) => void;
|
|
256
|
+
_unlisten: () => void;
|
|
267
257
|
get url(): string;
|
|
268
258
|
get unsaved(): number;
|
|
269
259
|
get saved(): boolean;
|
|
@@ -273,11 +263,11 @@ export declare const bindProviders: (Y: EngineApi) => {
|
|
|
273
263
|
destroy(): void;
|
|
274
264
|
disconnect(): void;
|
|
275
265
|
connect(): void;
|
|
276
|
-
emit<N extends "refused" | "status" | "failed" | "
|
|
266
|
+
emit<N extends "refused" | "status" | "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "access" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: N, args: Parameters<WebsocketProviderEvents[N]>): void;
|
|
277
267
|
_observers: Map<string, Set<any>>;
|
|
278
|
-
on<NAME extends "refused" | "status" | "failed" | "
|
|
279
|
-
once<NAME extends "refused" | "status" | "failed" | "
|
|
280
|
-
off<NAME extends "refused" | "status" | "failed" | "
|
|
268
|
+
on<NAME extends "refused" | "status" | "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "access" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: NAME, f: WebsocketProviderEvents[NAME]): WebsocketProviderEvents[NAME];
|
|
269
|
+
once<NAME extends "refused" | "status" | "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "access" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: NAME, f: WebsocketProviderEvents[NAME]): void;
|
|
270
|
+
off<NAME extends "refused" | "status" | "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "access" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: NAME, f: WebsocketProviderEvents[NAME]): void;
|
|
281
271
|
};
|
|
282
272
|
};
|
|
283
273
|
watchComments: (server: string | null, roomName: string, listener: (message: import("../protocol.js").CommentMessage) => void) => (() => void);
|
|
@@ -38,7 +38,7 @@ export const bindProviders = (Y) => {
|
|
|
38
38
|
const room = `${serverUrl}/${roomName}`;
|
|
39
39
|
const persistName = options.persist === false ? undefined : (options.persistName ?? `edytor:${room}`);
|
|
40
40
|
const sync = (payload) => {
|
|
41
|
-
const { doc, awareness, synced, failed, attach, armBound, holdBound } = payload;
|
|
41
|
+
const { doc, awareness, synced, failed, attach, armBound, holdBound, access } = payload;
|
|
42
42
|
const { params, WebSocketPolyfill, maxBackoffTime, connectTimeout, disableBc, onExpired, maxFrameBytes } = options;
|
|
43
43
|
const local = persistName !== undefined && typeof indexedDB !== 'undefined'
|
|
44
44
|
? localSync(persistName, { disableBc })
|
|
@@ -54,6 +54,8 @@ export const bindProviders = (Y) => {
|
|
|
54
54
|
});
|
|
55
55
|
if (onExpired)
|
|
56
56
|
provider.on('expired', (state) => onExpired(state));
|
|
57
|
+
if (access)
|
|
58
|
+
provider.on('access', access);
|
|
57
59
|
provider.on('synced', (isSynced) => {
|
|
58
60
|
if (isSynced)
|
|
59
61
|
synced(provider);
|
|
@@ -45,17 +45,13 @@ export declare const bindIndexeddbProvider: (Y: EngineApi) => {
|
|
|
45
45
|
loadError: unknown;
|
|
46
46
|
_storeTimeout: number;
|
|
47
47
|
_storeTimeoutId: ReturnType<typeof setTimeout> | null;
|
|
48
|
-
|
|
48
|
+
/** @internal Removes the document and presence listeners (`room.listen`). */
|
|
49
|
+
_unlisten: () => void;
|
|
49
50
|
awareness: Awareness;
|
|
50
51
|
bcconnected: boolean;
|
|
51
52
|
disableBc: boolean;
|
|
52
53
|
_ownsAwareness: boolean;
|
|
53
54
|
_bcSubscriber: (data: ArrayBuffer | Uint8Array, origin: unknown) => void;
|
|
54
|
-
_awarenessUpdateHandler: (updates: {
|
|
55
|
-
added: number[];
|
|
56
|
-
updated: number[];
|
|
57
|
-
removed: number[];
|
|
58
|
-
}, origin: unknown) => void;
|
|
59
55
|
/** The lifetime hydration claim. */
|
|
60
56
|
get synced(): boolean;
|
|
61
57
|
/**
|
|
@@ -78,7 +74,7 @@ export declare const bindIndexeddbProvider: (Y: EngineApi) => {
|
|
|
78
74
|
* close the DB. Does NOT remove stored data — `clearDocument` does.
|
|
79
75
|
*/
|
|
80
76
|
destroy(): Promise<void>;
|
|
81
|
-
emit<N extends "failed" | "
|
|
77
|
+
emit<N extends "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "load-error">(name: N, args: Parameters<{
|
|
82
78
|
synced: (provider: /*elided*/ any) => void;
|
|
83
79
|
'protocol-mismatch': (mismatch: ProtocolMismatch, provider: /*elided*/ any) => void;
|
|
84
80
|
/** Fired when loading persisted state failed (e.g. a generation mismatch). `whenSynced` rejects. */
|
|
@@ -101,7 +97,7 @@ export declare const bindIndexeddbProvider: (Y: EngineApi) => {
|
|
|
101
97
|
failed: (error: unknown, provider: /*elided*/ any) => void;
|
|
102
98
|
}[N]>): void;
|
|
103
99
|
_observers: Map<string, Set<any>>;
|
|
104
|
-
on<NAME extends "failed" | "
|
|
100
|
+
on<NAME extends "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "load-error">(name: NAME, f: {
|
|
105
101
|
synced: (provider: /*elided*/ any) => void;
|
|
106
102
|
'protocol-mismatch': (mismatch: ProtocolMismatch, provider: /*elided*/ any) => void;
|
|
107
103
|
/** Fired when loading persisted state failed (e.g. a generation mismatch). `whenSynced` rejects. */
|
|
@@ -144,7 +140,7 @@ export declare const bindIndexeddbProvider: (Y: EngineApi) => {
|
|
|
144
140
|
*/
|
|
145
141
|
failed: (error: unknown, provider: /*elided*/ any) => void;
|
|
146
142
|
}[NAME];
|
|
147
|
-
once<NAME extends "failed" | "
|
|
143
|
+
once<NAME extends "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "load-error">(name: NAME, f: {
|
|
148
144
|
synced: (provider: /*elided*/ any) => void;
|
|
149
145
|
'protocol-mismatch': (mismatch: ProtocolMismatch, provider: /*elided*/ any) => void;
|
|
150
146
|
/** Fired when loading persisted state failed (e.g. a generation mismatch). `whenSynced` rejects. */
|
|
@@ -166,7 +162,7 @@ export declare const bindIndexeddbProvider: (Y: EngineApi) => {
|
|
|
166
162
|
*/
|
|
167
163
|
failed: (error: unknown, provider: /*elided*/ any) => void;
|
|
168
164
|
}[NAME]): void;
|
|
169
|
-
off<NAME extends "failed" | "
|
|
165
|
+
off<NAME extends "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "load-error">(name: NAME, f: {
|
|
170
166
|
synced: (provider: /*elided*/ any) => void;
|
|
171
167
|
'protocol-mismatch': (mismatch: ProtocolMismatch, provider: /*elided*/ any) => void;
|
|
172
168
|
/** Fired when loading persisted state failed (e.g. a generation mismatch). `whenSynced` rejects. */
|
|
@@ -41,7 +41,7 @@ import { IsolatedObservable } from '../protocols/observable.js';
|
|
|
41
41
|
import { bindSync } from '../protocols/sync.js';
|
|
42
42
|
import { generationDbName } from '../protocols/envelope.js';
|
|
43
43
|
import { CUSTOM as customStoreName, encodeRow, lastKey, openContainer, readRow, rtop, snapshotRow, storesOf, UPDATES as updatesStoreName, verifyOrStamp } from './container.js';
|
|
44
|
-
import { beginDestroy, bindRoomProtocol, tabs, emitFailed, initLifecycle, markSynced, quarantined } from './room.js';
|
|
44
|
+
import { beginDestroy, bindRoomProtocol, tabs, emitFailed, fault, initLifecycle, markSynced, quarantined } from './room.js';
|
|
45
45
|
export const PREFERRED_TRIM_SIZE = 500;
|
|
46
46
|
/**
|
|
47
47
|
* Bind the IndexedDB persistence provider to the vendored v14 engine module.
|
|
@@ -175,13 +175,13 @@ export const bindIndexeddbProvider = (Y) => {
|
|
|
175
175
|
loadError = null;
|
|
176
176
|
_storeTimeout;
|
|
177
177
|
_storeTimeoutId;
|
|
178
|
-
|
|
178
|
+
/** @internal Removes the document and presence listeners (`room.listen`). */
|
|
179
|
+
_unlisten;
|
|
179
180
|
awareness;
|
|
180
181
|
bcconnected = false;
|
|
181
182
|
disableBc;
|
|
182
183
|
_ownsAwareness;
|
|
183
184
|
_bcSubscriber;
|
|
184
|
-
_awarenessUpdateHandler;
|
|
185
185
|
constructor(name, doc, options = {}) {
|
|
186
186
|
super();
|
|
187
187
|
this.doc = doc;
|
|
@@ -196,8 +196,6 @@ export const bindIndexeddbProvider = (Y) => {
|
|
|
196
196
|
this._db = openContainer(this.dbName);
|
|
197
197
|
this.destroy = this.destroy.bind(this);
|
|
198
198
|
initLifecycle(this, this.destroy, () => room.depart(this));
|
|
199
|
-
this._bcSubscriber = room.bcSubscriber(this);
|
|
200
|
-
this._awarenessUpdateHandler = room.awarenessUpdateHandler(this);
|
|
201
199
|
// Fail closed: no sync, no BC room join, whenSynced rejects, and
|
|
202
200
|
// the terminal 'failed' fires — the provider can never reach
|
|
203
201
|
// synced once loading its persisted state has failed.
|
|
@@ -216,7 +214,7 @@ export const bindIndexeddbProvider = (Y) => {
|
|
|
216
214
|
// A read-only doc's pre-hydration state is never persisted.
|
|
217
215
|
const beforeApplyUpdatesCallback = (updatesStore) => {
|
|
218
216
|
if (!quarantined(doc)) {
|
|
219
|
-
rtop(updatesStore.add(encodeRow(Y.encodeStateAsUpdate(doc)))).catch((error) => this
|
|
217
|
+
rtop(updatesStore.add(encodeRow(Y.encodeStateAsUpdate(doc)))).catch((error) => fault(this, error));
|
|
220
218
|
}
|
|
221
219
|
};
|
|
222
220
|
// Hydrated: join the room and claim `synced` (lifetime).
|
|
@@ -243,7 +241,7 @@ export const bindIndexeddbProvider = (Y) => {
|
|
|
243
241
|
.catch(onLoadError);
|
|
244
242
|
this._storeTimeout = 1000;
|
|
245
243
|
this._storeTimeoutId = null;
|
|
246
|
-
|
|
244
|
+
const storeUpdate = (update, origin) => {
|
|
247
245
|
// Outbound quarantine: a read-only doc is never persisted
|
|
248
246
|
// (`broadcastUpdate` applies the same rule to the room).
|
|
249
247
|
if (origin === this || quarantined(doc))
|
|
@@ -253,9 +251,7 @@ export const bindIndexeddbProvider = (Y) => {
|
|
|
253
251
|
const [updatesStore] = storesOf(this.db, [updatesStoreName]);
|
|
254
252
|
// Storage failures must surface — never an unobserved
|
|
255
253
|
// rejection (the error channel observes every failure mode).
|
|
256
|
-
rtop(updatesStore.add(encodeRow(update))).catch((error) =>
|
|
257
|
-
this.emit('message-error', [error, this]);
|
|
258
|
-
});
|
|
254
|
+
rtop(updatesStore.add(encodeRow(update))).catch((error) => fault(this, error));
|
|
259
255
|
if (++this._dbsize >= PREFERRED_TRIM_SIZE) {
|
|
260
256
|
if (this._storeTimeoutId !== null) {
|
|
261
257
|
clearTimeout(this._storeTimeoutId);
|
|
@@ -264,7 +260,7 @@ export const bindIndexeddbProvider = (Y) => {
|
|
|
264
260
|
this._storeTimeoutId = null;
|
|
265
261
|
// The timed caller has no promise consumer — surface
|
|
266
262
|
// compaction/storage failures on the error channel.
|
|
267
|
-
storeState(this, false).catch((error) => this
|
|
263
|
+
storeState(this, false).catch((error) => fault(this, error));
|
|
268
264
|
}, this._storeTimeout);
|
|
269
265
|
}
|
|
270
266
|
}
|
|
@@ -272,14 +268,13 @@ export const bindIndexeddbProvider = (Y) => {
|
|
|
272
268
|
// A synchronous transact failure (e.g. a handle closed
|
|
273
269
|
// mid-session) still lands on the error channel rather
|
|
274
270
|
// than propagating into the doc's update dispatch.
|
|
275
|
-
this
|
|
271
|
+
fault(this, error);
|
|
276
272
|
}
|
|
277
273
|
}
|
|
278
274
|
// Broadcast the update to other tabs
|
|
279
275
|
room.broadcastUpdate(this, update);
|
|
280
276
|
};
|
|
281
|
-
|
|
282
|
-
this.awareness.on('update', this._awarenessUpdateHandler);
|
|
277
|
+
this._unlisten = room.listen(this, storeUpdate);
|
|
283
278
|
const onDocDestroy = () => void this.destroy();
|
|
284
279
|
destroyWithDoc.set(this, onDocDestroy);
|
|
285
280
|
doc.on('destroy', onDocDestroy);
|
|
@@ -324,11 +319,10 @@ export const bindIndexeddbProvider = (Y) => {
|
|
|
324
319
|
if (this._storeTimeoutId) {
|
|
325
320
|
clearTimeout(this._storeTimeoutId);
|
|
326
321
|
}
|
|
327
|
-
this.
|
|
322
|
+
this._unlisten();
|
|
328
323
|
const onDocDestroy = destroyWithDoc.get(this);
|
|
329
324
|
if (onDocDestroy)
|
|
330
325
|
this.doc.off('destroy', onDocDestroy);
|
|
331
|
-
this.awareness.off('update', this._awarenessUpdateHandler);
|
|
332
326
|
this.disconnectBc();
|
|
333
327
|
if (this._ownsAwareness) {
|
|
334
328
|
this.awareness.destroy();
|
|
@@ -164,6 +164,14 @@ export declare const onPageLeave: (fn: () => void, phase: PageLeavePhase) => (()
|
|
|
164
164
|
export declare const initLifecycle: (host: LifecycleHost, destroy: () => unknown, depart?: () => void) => void;
|
|
165
165
|
/** The first sync — lifetime. Returns `true` only the first time. */
|
|
166
166
|
export declare const markSynced: (host: LifecycleHost) => boolean;
|
|
167
|
+
/** Report `error` on `provider`'s error channel (`'message-error'`): a frame, a payload or a storage failure. */
|
|
168
|
+
export declare const fault: (provider: {
|
|
169
|
+
emit?(event: "message-error", args: [unknown, unknown]): void;
|
|
170
|
+
}, error: unknown) => void;
|
|
171
|
+
/** A message type no handler claims inside a valid envelope: protocol skew, reported, never dropped silently. */
|
|
172
|
+
export declare const unknownType: (provider: {
|
|
173
|
+
emit?(event: "message-error", args: [unknown, unknown]): void;
|
|
174
|
+
}, what: string, type: number) => void;
|
|
167
175
|
/**
|
|
168
176
|
* Terminal `'failed'` emission (the sync-failure contract): fires at
|
|
169
177
|
* most once per provider, only while it has never synced — destroy before
|
|
@@ -298,5 +306,8 @@ export declare const bindRoomProtocol: <P extends RoomProvider<P>>(syncProtocol:
|
|
|
298
306
|
depart: (provider: P) => void;
|
|
299
307
|
connectBc: (provider: P & BcMember) => void;
|
|
300
308
|
disconnectBc: (provider: P & BcMember) => void;
|
|
309
|
+
listen: (provider: P & BcMember, onUpdate: (update: Uint8Array, origin: unknown, doc?: unknown, transaction?: {
|
|
310
|
+
local: boolean;
|
|
311
|
+
}) => void) => (() => void);
|
|
301
312
|
};
|
|
302
313
|
export {};
|