@apollohg/react-native-rich-text-editor 2.0.0 → 2.0.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/README.md +8 -116
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/android/build.gradle +3 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextImages.kt +25 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2RenderValidation.kt +1 -0
- package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +101 -33
- package/android/src/main/java/com/apollohg/editor/RenderMediaSpans.kt +15 -3
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +14 -0
- package/android/src/main/java/com/apollohg/editor/viewer/AndroidProseLayoutEngine.kt +1 -1
- package/app.plugin.js +42 -39
- package/dist/AtomHost.js +18 -11
- package/dist/EditorAddon.js +4 -2
- package/dist/EditorAddonNormalization.js +17 -9
- package/dist/EditorMentionThemeNormalization.js +6 -3
- package/dist/EditorMentionThemeValidation.js +28 -11
- package/dist/EditorStyleSheetNormalization.js +85 -38
- package/dist/EditorStyleSheetTypes.d.ts +1 -1
- package/dist/EditorTheme.d.ts +4 -0
- package/dist/EditorTheme.js +5 -3
- package/dist/EditorToolbarComponent.d.ts +1 -1
- package/dist/EditorToolbarComponent.js +1 -1
- package/dist/EditorToolbarRegistry.js +5 -5
- package/dist/EditorToolbarTypes.d.ts +12 -12
- package/dist/EditorToolbarVisuals.js +1 -1
- package/dist/ExternalTextComposition.js +89 -43
- package/dist/ImageLoadingPolicy.d.ts +1 -1
- package/dist/NativeEditorBoundaryError.d.ts +7 -7
- package/dist/NativeEditorBoundaryError.js +9 -5
- package/dist/NativeEditorCollaborationTransport.d.ts +7 -7
- package/dist/NativeEditorCollaborationTransport.js +28 -15
- package/dist/NativeEditorCreateConfig.js +11 -6
- package/dist/NativeEditorCreateJson.js +51 -25
- package/dist/NativeEditorCreateValidation.js +11 -6
- package/dist/NativeEditorDocumentBridge.d.ts +1 -1
- package/dist/NativeEditorDocumentBridge.js +32 -18
- package/dist/NativeEditorDocumentHandle.d.ts +4 -4
- package/dist/NativeEditorDocumentHandle.js +1 -1
- package/dist/NativeEditorLocalAwareness.js +25 -13
- package/dist/NativeEditorNativeModule.d.ts +8 -1
- package/dist/NativeEditorNativeModule.js +7 -5
- package/dist/NativeEditorRenderNormalization.js +66 -25
- package/dist/NativeEditorResultNormalization.js +53 -29
- package/dist/NativeEditorTypes.d.ts +10 -10
- package/dist/NativeEditorV2Decimal.js +6 -3
- package/dist/NativeProseViewer.js +11 -3
- package/dist/ResourceLimits.d.ts +2 -2
- package/dist/ResourceLimits.js +7 -2
- package/dist/RichTextEditor.js +1 -1
- package/dist/RichTextEditorNativeTypes.d.ts +1 -1
- package/dist/RichTextEditorNativeView.d.ts +1 -3
- package/dist/RichTextEditorSerialization.js +24 -14
- package/dist/RichTextEditorTypes.d.ts +1 -1
- package/dist/YjsCollaboration.d.ts +7 -7
- package/dist/YjsCollaboration.js +42 -26
- package/dist/addons.d.ts +4 -4
- package/dist/addons.js +3 -3
- package/dist/atomInstances.js +6 -3
- package/dist/atomUpdates.js +7 -4
- package/dist/atoms.js +20 -12
- package/dist/attributeValidation.js +55 -24
- package/dist/contentExpression.js +80 -42
- package/dist/listMarkers.d.ts +1 -1
- package/dist/listMarkers.js +1 -1
- package/dist/schemaDefinition.d.ts +1 -1
- package/dist/schemaDefinition.js +23 -14
- package/dist/schemaDocument.js +1 -1
- package/dist/schemaDocumentConstruction.js +25 -14
- package/dist/schemaNormalization.js +51 -29
- package/dist/schemaPresets.d.ts +1 -1
- package/dist/schemaPresets.js +9 -4
- package/dist/schemaResolution.js +46 -25
- package/dist/specs/PreparedProseViewerNativeComponent.d.ts +4 -0
- package/dist/useEditorToolbarInteractions.d.ts +2 -2
- package/dist/useEditorToolbarInteractions.js +45 -23
- package/dist/useEditorToolbarItems.d.ts +1 -1
- package/dist/useEditorToolbarItems.js +4 -3
- package/dist/useEditorToolbarPresentation.d.ts +3 -3
- package/dist/useEditorToolbarPresentation.js +4 -4
- package/dist/useEditorToolbarState.d.ts +1 -1
- package/dist/useEditorToolbarState.js +1 -1
- package/dist/useFocusPreservingFrames.js +11 -8
- package/dist/useNativeEditor.d.ts +1 -1
- package/dist/useNativeEditor.js +21 -11
- package/dist/useRichTextEditorCommands.d.ts +2 -2
- package/dist/useRichTextEditorCommands.js +59 -29
- package/dist/useRichTextEditorEvents.d.ts +3 -3
- package/dist/useRichTextEditorEvents.js +75 -32
- package/dist/useRichTextEditorMentions.d.ts +4 -4
- package/dist/useRichTextEditorMentions.js +48 -29
- package/dist/useRichTextEditorPresentation.d.ts +4 -4
- package/dist/useRichTextEditorPresentation.js +25 -23
- package/dist/useRichTextEditorState.d.ts +3 -3
- package/dist/useRichTextEditorState.js +20 -15
- package/dist/useRichTextEditorUpdates.d.ts +1 -1
- package/dist/useRichTextEditorUpdates.js +99 -41
- package/dist/useViewerAtoms.js +63 -36
- package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
- package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
- package/ios/EditorInteractionOverlays.swift +114 -122
- package/ios/EditorLayoutManager.swift +13 -3
- package/ios/EditorStyleSheet.swift +12 -1
- package/ios/EditorTextView+Images.swift +32 -10
- package/ios/EditorTextView.swift +1 -1
- package/ios/EditorV2Adapter+RenderValidation.swift +2 -2
- package/ios/NativeToolbar+ButtonStyle.swift +17 -5
- package/ios/NativeToolbar+Diagnostics.swift +15 -0
- package/ios/RenderBridge+Atoms.swift +0 -1
- package/ios/RenderBridge+Attachments.swift +0 -29
- package/ios/RichTextEditorView.swift +4 -12
- package/ios/Viewer/CoreTextProseLayoutEngine+AttributedText.swift +3 -2
- package/package.json +11 -3
- package/react-native.config.js +1 -1
- package/rust/android/arm64-v8a/libeditor_core.so +0 -0
- package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
- package/rust/android/x86/libeditor_core.so +0 -0
- package/rust/android/x86_64/libeditor_core.so +0 -0
- package/src/AtomHost.tsx +58 -28
- package/src/EditorAddon.ts +18 -8
- package/src/EditorAddonNormalization.ts +40 -11
- package/src/EditorMentionThemeNormalization.ts +15 -4
- package/src/EditorMentionThemeValidation.ts +51 -14
- package/src/EditorStyleSheet.ts +1 -0
- package/src/EditorStyleSheetNormalization.ts +195 -64
- package/src/EditorStyleSheetTypes.ts +1 -1
- package/src/EditorTheme.ts +13 -5
- package/src/EditorToolbarComponent.tsx +2 -1
- package/src/EditorToolbarRegistry.tsx +23 -13
- package/src/EditorToolbarTypes.tsx +12 -12
- package/src/EditorToolbarVisuals.tsx +6 -3
- package/src/EditorUpdateRevision.ts +1 -0
- package/src/ExternalTextComposition.ts +259 -49
- package/src/ImageLoadingPolicy.ts +3 -1
- package/src/NativeEditorBoundaryError.ts +38 -11
- package/src/NativeEditorCollaborationTransport.ts +108 -27
- package/src/NativeEditorCreateConfig.ts +92 -19
- package/src/NativeEditorCreateJson.ts +163 -26
- package/src/NativeEditorCreateValidation.ts +33 -12
- package/src/NativeEditorDocumentBridge.ts +111 -19
- package/src/NativeEditorDocumentHandle.ts +9 -4
- package/src/NativeEditorLocalAwareness.ts +70 -15
- package/src/NativeEditorNativeModule.ts +13 -9
- package/src/NativeEditorRenderNormalization.ts +128 -29
- package/src/NativeEditorResultNormalization.ts +143 -32
- package/src/NativeEditorTypes.ts +12 -11
- package/src/NativeEditorV2Decimal.ts +12 -3
- package/src/NativeProseViewer.tsx +46 -16
- package/src/ResourceLimits.ts +15 -8
- package/src/RichTextEditor.tsx +3 -1
- package/src/RichTextEditorNativeTypes.tsx +1 -1
- package/src/RichTextEditorNativeView.tsx +2 -8
- package/src/RichTextEditorSerialization.tsx +70 -24
- package/src/RichTextEditorTypes.tsx +1 -1
- package/src/YjsCollaboration.ts +143 -47
- package/src/addons.ts +12 -8
- package/src/atomInstances.ts +25 -5
- package/src/atomUpdates.ts +23 -6
- package/src/atoms.ts +85 -18
- package/src/attributeValidation.ts +97 -33
- package/src/contentExpression.ts +218 -63
- package/src/listMarkers.ts +1 -1
- package/src/schemaDefinition.ts +82 -20
- package/src/schemaDocument.ts +15 -1
- package/src/schemaDocumentConstruction.ts +80 -16
- package/src/schemaNormalization.ts +155 -41
- package/src/schemaPresets.ts +37 -31
- package/src/schemaResolution.ts +126 -27
- package/src/specs/PreparedProseViewerNativeComponent.ts +4 -2
- package/src/useEditorToolbarInteractions.tsx +85 -36
- package/src/useEditorToolbarItems.tsx +50 -22
- package/src/useEditorToolbarPresentation.tsx +81 -58
- package/src/useEditorToolbarState.tsx +8 -6
- package/src/useFocusPreservingFrames.ts +43 -15
- package/src/useNativeEditor.ts +111 -51
- package/src/useRichTextEditorCommands.tsx +127 -54
- package/src/useRichTextEditorEvents.tsx +144 -42
- package/src/useRichTextEditorMentions.tsx +118 -37
- package/src/useRichTextEditorPresentation.tsx +128 -114
- package/src/useRichTextEditorState.tsx +76 -40
- package/src/useRichTextEditorUpdates.tsx +207 -56
- package/src/useViewerAtoms.tsx +162 -66
|
@@ -8,7 +8,7 @@ import { NativeEditorBoundaryError } from './NativeEditorBoundaryError';
|
|
|
8
8
|
* Every field is optional; an omitted one uses
|
|
9
9
|
* {@link DEFAULT_EDITOR_IMAGE_LOADING_POLICY}. Each value must be a positive
|
|
10
10
|
* integer no greater than the matching
|
|
11
|
-
* {@link HARD_EDITOR_IMAGE_LOADING_POLICY} ceiling
|
|
11
|
+
* {@link HARD_EDITOR_IMAGE_LOADING_POLICY} ceiling : anything else throws
|
|
12
12
|
* `NativeEditorBoundaryError` (`IMAGE_POLICY_INVALID`).
|
|
13
13
|
*/
|
|
14
14
|
export interface EditorImageLoadingPolicy {
|
|
@@ -71,6 +71,7 @@ function resolveImagePolicyValue(
|
|
|
71
71
|
value: number | undefined
|
|
72
72
|
): number {
|
|
73
73
|
const resolved = value ?? DEFAULT_EDITOR_IMAGE_LOADING_POLICY[name];
|
|
74
|
+
|
|
74
75
|
if (
|
|
75
76
|
!Number.isSafeInteger(resolved) ||
|
|
76
77
|
resolved <= 0 ||
|
|
@@ -83,6 +84,7 @@ function resolveImagePolicyValue(
|
|
|
83
84
|
resolved
|
|
84
85
|
);
|
|
85
86
|
}
|
|
87
|
+
|
|
86
88
|
return resolved;
|
|
87
89
|
}
|
|
88
90
|
|
|
@@ -41,12 +41,12 @@ export const NATIVE_EDITOR_ERROR_DOMAINS = [
|
|
|
41
41
|
/**
|
|
42
42
|
* Which subsystem raised a failure:
|
|
43
43
|
*
|
|
44
|
-
* - `boundary`
|
|
45
|
-
* - `document`
|
|
46
|
-
* - `operation`
|
|
47
|
-
* - `lifecycle`
|
|
48
|
-
* - `snapshot`
|
|
49
|
-
* - `transport`
|
|
44
|
+
* - `boundary` : the value never reached the engine (bad config, oversized input).
|
|
45
|
+
* - `document` : the document or schema was rejected.
|
|
46
|
+
* - `operation` : a mutation could not be applied (stale revision, invalid position).
|
|
47
|
+
* - `lifecycle` : the session was destroyed or is being destroyed.
|
|
48
|
+
* - `snapshot` : a room snapshot could not be exported or restored.
|
|
49
|
+
* - `transport` : collaboration transport failure.
|
|
50
50
|
*/
|
|
51
51
|
export type NativeEditorErrorDomain = (typeof NATIVE_EDITOR_ERROR_DOMAINS)[number];
|
|
52
52
|
|
|
@@ -86,7 +86,7 @@ export interface NativeEditorError {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
/**
|
|
89
|
-
* A value rejected before it reached the engine
|
|
89
|
+
* A value rejected before it reached the engine : an out-of-range resource
|
|
90
90
|
* limit, an invalid image policy, an oversized input. Thrown synchronously by
|
|
91
91
|
* the validating helpers in this package.
|
|
92
92
|
*/
|
|
@@ -111,9 +111,11 @@ export function parseNativeBoundaryError(value: unknown): NativeEditorBoundaryEr
|
|
|
111
111
|
const envelope = value as { error?: Record<string, unknown> };
|
|
112
112
|
const nativeError = envelope?.error;
|
|
113
113
|
const code = nativeError?.code;
|
|
114
|
+
|
|
114
115
|
if (typeof code !== 'string' || typeof nativeError?.message !== 'string') {
|
|
115
116
|
return null;
|
|
116
117
|
}
|
|
118
|
+
|
|
117
119
|
return new NativeEditorBoundaryError(
|
|
118
120
|
code,
|
|
119
121
|
nativeError.message,
|
|
@@ -126,7 +128,10 @@ export function parseNativeBoundaryError(value: unknown): NativeEditorBoundaryEr
|
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
function nullableCanonicalDecimal(value: unknown): string | null | undefined {
|
|
129
|
-
if (value == null)
|
|
131
|
+
if (value == null) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
130
135
|
return normalizeNativeEditorV2U64(value) ?? undefined;
|
|
131
136
|
}
|
|
132
137
|
|
|
@@ -137,12 +142,21 @@ function parseDetails(
|
|
|
137
142
|
if (typeof nativeError.details !== 'object' || Array.isArray(nativeError.details)) {
|
|
138
143
|
return undefined;
|
|
139
144
|
}
|
|
145
|
+
|
|
140
146
|
return nativeError.details as Record<string, unknown>;
|
|
141
147
|
}
|
|
142
|
-
|
|
143
|
-
if (
|
|
148
|
+
|
|
149
|
+
if (nativeError.detailsJson == null) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (typeof nativeError.detailsJson !== 'string') {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
|
|
144
157
|
try {
|
|
145
158
|
const details: unknown = JSON.parse(nativeError.detailsJson);
|
|
159
|
+
|
|
146
160
|
return details != null && typeof details === 'object' && !Array.isArray(details)
|
|
147
161
|
? (details as Record<string, unknown>)
|
|
148
162
|
: undefined;
|
|
@@ -183,9 +197,13 @@ function hasValidKnownDetails(code: string, details: Record<string, unknown> | n
|
|
|
183
197
|
export function normalizeNativeEditorV2Error(value: unknown): NativeEditorError | null {
|
|
184
198
|
const envelope = value as { error?: unknown };
|
|
185
199
|
const nativeError = envelope?.error as Record<string, unknown> | undefined;
|
|
186
|
-
|
|
200
|
+
|
|
201
|
+
if (nativeError == null || typeof nativeError !== 'object') {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
187
204
|
|
|
188
205
|
const { domain, code, message } = nativeError;
|
|
206
|
+
|
|
189
207
|
if (
|
|
190
208
|
typeof domain !== 'string' ||
|
|
191
209
|
!NATIVE_EDITOR_ERROR_DOMAINS.includes(domain as NativeEditorErrorDomain) ||
|
|
@@ -200,6 +218,7 @@ export function normalizeNativeEditorV2Error(value: unknown): NativeEditorError
|
|
|
200
218
|
const limit = nullableCanonicalDecimal(nativeError.limit);
|
|
201
219
|
const actual = nullableCanonicalDecimal(nativeError.actual);
|
|
202
220
|
const details = parseDetails(nativeError);
|
|
221
|
+
|
|
203
222
|
if (
|
|
204
223
|
requestId === undefined ||
|
|
205
224
|
operationIndex === undefined ||
|
|
@@ -242,11 +261,17 @@ export function isNativeEditorV2NonRetryableCode(code: string): boolean {
|
|
|
242
261
|
/** Base class for every typed error raised from a normalized FFI v2 error record. */
|
|
243
262
|
export class NativeEditorErrorBase extends Error {
|
|
244
263
|
readonly domain: NativeEditorErrorDomain;
|
|
264
|
+
|
|
245
265
|
readonly code: NativeEditorBoundaryErrorCode;
|
|
266
|
+
|
|
246
267
|
readonly requestId: string | null;
|
|
268
|
+
|
|
247
269
|
readonly operationIndex: string | null;
|
|
270
|
+
|
|
248
271
|
readonly limit: string | null;
|
|
272
|
+
|
|
249
273
|
readonly actual: string | null;
|
|
274
|
+
|
|
250
275
|
readonly details: Record<string, unknown> | null;
|
|
251
276
|
|
|
252
277
|
constructor(
|
|
@@ -340,6 +365,8 @@ export function nativeEditorV2ErrorToException(
|
|
|
340
365
|
if (isNativeEditorV2NonRetryableCode(error.code)) {
|
|
341
366
|
return new NativeEditorNonRetryableError(error);
|
|
342
367
|
}
|
|
368
|
+
|
|
343
369
|
const ErrorClass = NATIVE_EDITOR_V2_DOMAIN_ERROR_CLASSES[error.domain];
|
|
370
|
+
|
|
344
371
|
return new ErrorClass(error);
|
|
345
372
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
NativeEditorErrorBase,
|
|
2
|
+
type NativeEditorErrorBase,
|
|
3
3
|
nativeEditorV2ErrorToException,
|
|
4
4
|
normalizeNativeEditorV2Error,
|
|
5
5
|
} from './NativeEditorBoundaryError';
|
|
@@ -40,9 +40,9 @@ export type NativeCollaborationProtocolFrame =
|
|
|
40
40
|
/**
|
|
41
41
|
* What the transport does next after an adapter callback:
|
|
42
42
|
*
|
|
43
|
-
* - `continue`
|
|
44
|
-
* - `ready`
|
|
45
|
-
* - `reject`
|
|
43
|
+
* - `continue` : stay in the prelude and await the next frame.
|
|
44
|
+
* - `ready` : the prelude succeeded; release Yjs traffic.
|
|
45
|
+
* - `reject` : abandon this attempt.
|
|
46
46
|
*/
|
|
47
47
|
export type NativeCollaborationProtocolAdapterAction = 'continue' | 'ready' | 'reject';
|
|
48
48
|
|
|
@@ -77,7 +77,7 @@ export interface NativeCollaborationProtocolAdapter {
|
|
|
77
77
|
timeoutMillis?: number;
|
|
78
78
|
/** Close codes that park the Rust transport instead of entering automatic retry. */
|
|
79
79
|
terminalCloseCodes?: readonly number[];
|
|
80
|
-
/** Runs once when the socket opens
|
|
80
|
+
/** Runs once when the socket opens : send credentials here. */
|
|
81
81
|
onOpen(
|
|
82
82
|
context: NativeCollaborationProtocolAdapterContext
|
|
83
83
|
): NativeCollaborationProtocolAdapterResult | Promise<NativeCollaborationProtocolAdapterResult>;
|
|
@@ -90,7 +90,7 @@ export interface NativeCollaborationProtocolAdapter {
|
|
|
90
90
|
|
|
91
91
|
/** Where the native transport connects, and whether it should. */
|
|
92
92
|
export interface NativeCollaborationTransportConfig {
|
|
93
|
-
/** WebSocket endpoint, `ws:` or `wss:`. Treat it as sensitive
|
|
93
|
+
/** WebSocket endpoint, `ws:` or `wss:`. Treat it as sensitive : it may carry credentials. */
|
|
94
94
|
url: string;
|
|
95
95
|
/** Whether to open the connection now. False configures the endpoint without connecting. */
|
|
96
96
|
connect: boolean;
|
|
@@ -98,7 +98,7 @@ export interface NativeCollaborationTransportConfig {
|
|
|
98
98
|
protocolAdapter?: NativeCollaborationProtocolAdapter;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
/** Why the transport woke and what it did
|
|
101
|
+
/** Why the transport woke and what it did : diagnostic only; no contract depends on these. */
|
|
102
102
|
export interface NativeCollaborationTransportDiagnostics {
|
|
103
103
|
/** What woke the transport, e.g. a received message or an elapsed timer. */
|
|
104
104
|
wakeReason: string;
|
|
@@ -197,7 +197,8 @@ export function collaborationProtocolAdapterDescriptor(
|
|
|
197
197
|
) {
|
|
198
198
|
throw invalidV2RequestError('NativeEditorBridge: invalid collaboration protocol adapter');
|
|
199
199
|
}
|
|
200
|
-
|
|
200
|
+
|
|
201
|
+
const protocols = value.protocols.map(protocol => {
|
|
201
202
|
if (
|
|
202
203
|
typeof protocol !== 'string' ||
|
|
203
204
|
protocol.length === 0 ||
|
|
@@ -208,14 +209,18 @@ export function collaborationProtocolAdapterDescriptor(
|
|
|
208
209
|
'NativeEditorBridge: invalid collaboration WebSocket subprotocol'
|
|
209
210
|
);
|
|
210
211
|
}
|
|
212
|
+
|
|
211
213
|
return protocol;
|
|
212
214
|
});
|
|
215
|
+
|
|
213
216
|
if (new Set(protocols).size !== protocols.length) {
|
|
214
217
|
throw invalidV2RequestError(
|
|
215
218
|
'NativeEditorBridge: duplicate collaboration WebSocket subprotocol'
|
|
216
219
|
);
|
|
217
220
|
}
|
|
221
|
+
|
|
218
222
|
const timeoutMillis = value.timeoutMillis;
|
|
223
|
+
|
|
219
224
|
if (
|
|
220
225
|
timeoutMillis !== undefined &&
|
|
221
226
|
(!Number.isSafeInteger(timeoutMillis) ||
|
|
@@ -226,20 +231,24 @@ export function collaborationProtocolAdapterDescriptor(
|
|
|
226
231
|
'NativeEditorBridge: invalid collaboration protocol adapter timeout'
|
|
227
232
|
);
|
|
228
233
|
}
|
|
234
|
+
|
|
229
235
|
const rawTerminalCloseCodes = value.terminalCloseCodes;
|
|
236
|
+
|
|
230
237
|
if (
|
|
231
238
|
rawTerminalCloseCodes !== undefined &&
|
|
232
239
|
(!Array.isArray(rawTerminalCloseCodes) ||
|
|
233
240
|
rawTerminalCloseCodes.some(
|
|
234
|
-
|
|
241
|
+
code => !Number.isSafeInteger(code) || code < 1_000 || code > 4_999
|
|
235
242
|
))
|
|
236
243
|
) {
|
|
237
244
|
throw invalidV2RequestError(
|
|
238
245
|
'NativeEditorBridge: invalid collaboration terminal close code'
|
|
239
246
|
);
|
|
240
247
|
}
|
|
248
|
+
|
|
241
249
|
const terminalCloseCodes =
|
|
242
|
-
rawTerminalCloseCodes === undefined ? undefined : [...rawTerminalCloseCodes];
|
|
250
|
+
rawTerminalCloseCodes === undefined ? undefined : [ ...rawTerminalCloseCodes ];
|
|
251
|
+
|
|
243
252
|
if (
|
|
244
253
|
terminalCloseCodes !== undefined &&
|
|
245
254
|
new Set(terminalCloseCodes).size !== terminalCloseCodes.length
|
|
@@ -248,6 +257,7 @@ export function collaborationProtocolAdapterDescriptor(
|
|
|
248
257
|
'NativeEditorBridge: duplicate collaboration terminal close code'
|
|
249
258
|
);
|
|
250
259
|
}
|
|
260
|
+
|
|
251
261
|
return {
|
|
252
262
|
protocols,
|
|
253
263
|
...(timeoutMillis === undefined ? {} : { timeoutMillis }),
|
|
@@ -268,14 +278,15 @@ export function collaborationTransportWireConfig(
|
|
|
268
278
|
'NativeEditorBridge: invalid collaboration transport configuration'
|
|
269
279
|
);
|
|
270
280
|
}
|
|
281
|
+
|
|
271
282
|
return {
|
|
272
283
|
url: config.url,
|
|
273
284
|
connect: config.connect,
|
|
274
285
|
...(config.protocolAdapter === undefined
|
|
275
286
|
? {}
|
|
276
287
|
: {
|
|
277
|
-
|
|
278
|
-
|
|
288
|
+
protocolAdapter: collaborationProtocolAdapterDescriptor(config.protocolAdapter),
|
|
289
|
+
}),
|
|
279
290
|
};
|
|
280
291
|
}
|
|
281
292
|
|
|
@@ -294,20 +305,24 @@ export function serializeCollaborationProtocolAdapterResult(
|
|
|
294
305
|
'NativeEditorBridge: invalid collaboration protocol adapter result'
|
|
295
306
|
);
|
|
296
307
|
}
|
|
297
|
-
|
|
308
|
+
|
|
309
|
+
const frames = (value.frames ?? []).map((frame: NativeCollaborationProtocolFrame) => {
|
|
298
310
|
if (frame === null || typeof frame !== 'object') {
|
|
299
311
|
throw invalidV2RequestError(
|
|
300
312
|
'NativeEditorBridge: invalid collaboration protocol adapter frame'
|
|
301
313
|
);
|
|
302
314
|
}
|
|
315
|
+
|
|
303
316
|
if (frame.type === 'text' && typeof frame.data === 'string') {
|
|
304
317
|
if (utf8V2JsonByteLength(frame.data) > MAX_COLLABORATION_ADAPTER_FRAME_BYTES) {
|
|
305
318
|
throw invalidV2RequestError(
|
|
306
319
|
'NativeEditorBridge: collaboration protocol adapter frame is too large'
|
|
307
320
|
);
|
|
308
321
|
}
|
|
322
|
+
|
|
309
323
|
return { type: 'text' as const, data: frame.data };
|
|
310
324
|
}
|
|
325
|
+
|
|
311
326
|
if (
|
|
312
327
|
frame.type === 'binary' &&
|
|
313
328
|
frame.data instanceof Uint8Array &&
|
|
@@ -318,10 +333,12 @@ export function serializeCollaborationProtocolAdapterResult(
|
|
|
318
333
|
data: encodeNativeCollaborationProtocolBytes(frame.data),
|
|
319
334
|
};
|
|
320
335
|
}
|
|
336
|
+
|
|
321
337
|
throw invalidV2RequestError(
|
|
322
338
|
'NativeEditorBridge: invalid collaboration protocol adapter frame'
|
|
323
339
|
);
|
|
324
340
|
});
|
|
341
|
+
|
|
325
342
|
return JSON.stringify({
|
|
326
343
|
action: value.action,
|
|
327
344
|
...(frames.length === 0 ? {} : { frames }),
|
|
@@ -331,16 +348,22 @@ export function serializeCollaborationProtocolAdapterResult(
|
|
|
331
348
|
export function normalizeNativeCollaborationTransportDiagnostics(
|
|
332
349
|
value: unknown
|
|
333
350
|
): NativeCollaborationTransportDiagnostics | null {
|
|
334
|
-
if (!isPlainRecord(value))
|
|
351
|
+
if (!isPlainRecord(value)) {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
|
|
335
355
|
const transportState = whitelisted(value.transportState, V2_TRANSPORT_STATES);
|
|
356
|
+
|
|
336
357
|
const nextDeadlineMillis =
|
|
337
358
|
value.nextDeadlineMillis === null
|
|
338
359
|
? null
|
|
339
360
|
: normalizeNativeEditorV2DecimalId(value.nextDeadlineMillis);
|
|
361
|
+
|
|
340
362
|
const remoteCommitApplied = optionalBoolean(value.remoteCommitApplied);
|
|
341
363
|
const peersChanged = optionalBoolean(value.peersChanged);
|
|
342
364
|
const renewedLocal = optionalBoolean(value.renewedLocal);
|
|
343
365
|
const expiredPeerCount = nativeEditorV2U32(value.expiredPeerCount);
|
|
366
|
+
|
|
344
367
|
if (
|
|
345
368
|
typeof value.wakeReason !== 'string' ||
|
|
346
369
|
transportState == null ||
|
|
@@ -352,6 +375,7 @@ export function normalizeNativeCollaborationTransportDiagnostics(
|
|
|
352
375
|
) {
|
|
353
376
|
return null;
|
|
354
377
|
}
|
|
378
|
+
|
|
355
379
|
return {
|
|
356
380
|
wakeReason: value.wakeReason,
|
|
357
381
|
transportState,
|
|
@@ -367,6 +391,7 @@ export const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv
|
|
|
367
391
|
|
|
368
392
|
export function encodeNativeCollaborationProtocolBytes(bytes: Uint8Array): string {
|
|
369
393
|
let encoded = '';
|
|
394
|
+
|
|
370
395
|
for (let index = 0; index < bytes.length; index += 3) {
|
|
371
396
|
const first = bytes[index];
|
|
372
397
|
const second = index + 1 < bytes.length ? bytes[index + 1] : 0;
|
|
@@ -377,39 +402,68 @@ export function encodeNativeCollaborationProtocolBytes(bytes: Uint8Array): strin
|
|
|
377
402
|
encoded += index + 1 < bytes.length ? BASE64_ALPHABET[(value >>> 6) & 63] : '=';
|
|
378
403
|
encoded += index + 2 < bytes.length ? BASE64_ALPHABET[value & 63] : '=';
|
|
379
404
|
}
|
|
405
|
+
|
|
380
406
|
return encoded;
|
|
381
407
|
}
|
|
382
408
|
|
|
383
409
|
export function decodeNativeCollaborationProtocolBytes(value: string): Uint8Array | null {
|
|
384
|
-
if (value.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(value))
|
|
410
|
+
if (value.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(value)) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
|
|
385
414
|
const firstPadding = value.indexOf('=');
|
|
386
|
-
|
|
415
|
+
|
|
416
|
+
if (firstPadding >= 0 && firstPadding < value.length - 2) {
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
|
|
387
420
|
const outputLength =
|
|
388
421
|
(value.length / 4) * 3 - (value.endsWith('==') ? 2 : value.endsWith('=') ? 1 : 0);
|
|
422
|
+
|
|
389
423
|
const bytes = new Uint8Array(outputLength);
|
|
390
424
|
let outputIndex = 0;
|
|
425
|
+
|
|
391
426
|
for (let index = 0; index < value.length; index += 4) {
|
|
392
427
|
const first = BASE64_ALPHABET.indexOf(value[index]);
|
|
393
428
|
const second = BASE64_ALPHABET.indexOf(value[index + 1]);
|
|
394
429
|
const third = value[index + 2] === '=' ? 0 : BASE64_ALPHABET.indexOf(value[index + 2]);
|
|
395
430
|
const fourth = value[index + 3] === '=' ? 0 : BASE64_ALPHABET.indexOf(value[index + 3]);
|
|
396
|
-
|
|
431
|
+
|
|
432
|
+
if (first < 0 || second < 0 || third < 0 || fourth < 0) {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
|
|
397
436
|
const decoded = (first << 18) | (second << 12) | (third << 6) | fourth;
|
|
398
|
-
|
|
399
|
-
if (outputIndex < outputLength)
|
|
400
|
-
|
|
437
|
+
|
|
438
|
+
if (outputIndex < outputLength) {
|
|
439
|
+
bytes[outputIndex++] = (decoded >>> 16) & 0xff;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (outputIndex < outputLength) {
|
|
443
|
+
bytes[outputIndex++] = (decoded >>> 8) & 0xff;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (outputIndex < outputLength) {
|
|
447
|
+
bytes[outputIndex++] = decoded & 0xff;
|
|
448
|
+
}
|
|
401
449
|
}
|
|
450
|
+
|
|
402
451
|
return bytes;
|
|
403
452
|
}
|
|
404
453
|
|
|
405
454
|
export function normalizeNativeCollaborationTransportEvent(
|
|
406
455
|
value: unknown
|
|
407
456
|
): NativeCollaborationTransportEvent | null {
|
|
408
|
-
if (!isPlainRecord(value))
|
|
457
|
+
if (!isPlainRecord(value)) {
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
|
|
409
461
|
const editorId = normalizeNativeEditorV2DecimalId(value.editorId);
|
|
410
462
|
const eventSequence = normalizeNativeEditorV2DecimalId(value.eventSequence);
|
|
463
|
+
|
|
411
464
|
const generation =
|
|
412
465
|
value.generation === null ? null : normalizeNativeEditorV2DecimalId(value.generation);
|
|
466
|
+
|
|
413
467
|
if (
|
|
414
468
|
editorId == null ||
|
|
415
469
|
editorId === '0' ||
|
|
@@ -419,11 +473,16 @@ export function normalizeNativeCollaborationTransportEvent(
|
|
|
419
473
|
) {
|
|
420
474
|
return null;
|
|
421
475
|
}
|
|
476
|
+
|
|
422
477
|
if (value.kind === 'state') {
|
|
423
478
|
const state = normalizeNativeEditorV2StateValue(value.state);
|
|
424
479
|
const peers = normalizeNativeEditorV2PeersValue({ peers: value.peers });
|
|
425
480
|
const diagnostics = normalizeNativeCollaborationTransportDiagnostics(value.diagnostics);
|
|
426
|
-
|
|
481
|
+
|
|
482
|
+
if (state == null || peers == null || diagnostics == null) {
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
|
|
427
486
|
return {
|
|
428
487
|
editorId,
|
|
429
488
|
eventSequence,
|
|
@@ -434,9 +493,14 @@ export function normalizeNativeCollaborationTransportEvent(
|
|
|
434
493
|
diagnostics,
|
|
435
494
|
};
|
|
436
495
|
}
|
|
496
|
+
|
|
437
497
|
if (value.kind === 'error') {
|
|
438
498
|
const error = normalizeNativeEditorV2Error({ error: value.error });
|
|
439
|
-
|
|
499
|
+
|
|
500
|
+
if (error == null) {
|
|
501
|
+
return null;
|
|
502
|
+
}
|
|
503
|
+
|
|
440
504
|
return {
|
|
441
505
|
editorId,
|
|
442
506
|
eventSequence,
|
|
@@ -445,8 +509,10 @@ export function normalizeNativeCollaborationTransportEvent(
|
|
|
445
509
|
error: nativeEditorV2ErrorToException(error),
|
|
446
510
|
};
|
|
447
511
|
}
|
|
512
|
+
|
|
448
513
|
if (value.kind === 'protocolAdapter') {
|
|
449
514
|
const eventId = normalizeNativeEditorV2DecimalId(value.eventId);
|
|
515
|
+
|
|
450
516
|
if (
|
|
451
517
|
generation == null ||
|
|
452
518
|
typeof value.attemptId !== 'string' ||
|
|
@@ -458,6 +524,7 @@ export function normalizeNativeCollaborationTransportEvent(
|
|
|
458
524
|
) {
|
|
459
525
|
return null;
|
|
460
526
|
}
|
|
527
|
+
|
|
461
528
|
const base = {
|
|
462
529
|
editorId,
|
|
463
530
|
eventSequence,
|
|
@@ -465,13 +532,21 @@ export function normalizeNativeCollaborationTransportEvent(
|
|
|
465
532
|
kind: 'protocolAdapter' as const,
|
|
466
533
|
attemptId: value.attemptId,
|
|
467
534
|
eventId,
|
|
468
|
-
negotiatedProtocol: value.negotiatedProtocol
|
|
535
|
+
negotiatedProtocol: value.negotiatedProtocol,
|
|
469
536
|
};
|
|
537
|
+
|
|
470
538
|
if (value.phase === 'open') {
|
|
471
|
-
if (value.frame !== undefined)
|
|
539
|
+
if (value.frame !== undefined) {
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
|
|
472
543
|
return { ...base, phase: 'open' };
|
|
473
544
|
}
|
|
474
|
-
|
|
545
|
+
|
|
546
|
+
if (!isPlainRecord(value.frame) || typeof value.frame.data !== 'string') {
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
|
|
475
550
|
if (value.frame.type === 'text') {
|
|
476
551
|
return {
|
|
477
552
|
...base,
|
|
@@ -479,9 +554,14 @@ export function normalizeNativeCollaborationTransportEvent(
|
|
|
479
554
|
frame: { type: 'text', data: value.frame.data },
|
|
480
555
|
};
|
|
481
556
|
}
|
|
557
|
+
|
|
482
558
|
if (value.frame.type === 'binary') {
|
|
483
559
|
const data = decodeNativeCollaborationProtocolBytes(value.frame.data);
|
|
484
|
-
|
|
560
|
+
|
|
561
|
+
if (data == null) {
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
|
|
485
565
|
return {
|
|
486
566
|
...base,
|
|
487
567
|
phase: 'message',
|
|
@@ -489,5 +569,6 @@ export function normalizeNativeCollaborationTransportEvent(
|
|
|
489
569
|
};
|
|
490
570
|
}
|
|
491
571
|
}
|
|
572
|
+
|
|
492
573
|
return null;
|
|
493
574
|
}
|