@twip/web 0.1.0-alpha.1 → 0.1.0-pre.1

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.
Files changed (179) hide show
  1. package/README.md +35 -0
  2. package/dist/THIRD-PARTY-NOTICES.md +1009 -221
  3. package/dist/commands.d.ts +637 -0
  4. package/dist/commands.js +188 -0
  5. package/dist/editor.d.ts +218 -0
  6. package/dist/editor.js +460 -0
  7. package/dist/errors.d.ts +21 -0
  8. package/dist/errors.js +26 -0
  9. package/dist/events.d.ts +126 -0
  10. package/dist/events.js +1 -0
  11. package/dist/fonts/google.d.ts +3 -0
  12. package/dist/fonts/google.js +39 -0
  13. package/dist/fonts/library.d.ts +65 -0
  14. package/dist/fonts/library.js +130 -0
  15. package/dist/host/a11y.d.ts +3 -0
  16. package/dist/host/a11y.js +62 -0
  17. package/dist/host/chrome.d.ts +28 -0
  18. package/dist/host/chrome.js +144 -0
  19. package/dist/host/clipboard.d.ts +16 -0
  20. package/dist/host/clipboard.js +128 -0
  21. package/dist/host/dom.d.ts +45 -0
  22. package/dist/host/dom.js +122 -0
  23. package/dist/host/frames.d.ts +14 -0
  24. package/dist/host/frames.js +87 -0
  25. package/dist/host/gpu.d.ts +58 -0
  26. package/dist/host/gpu.js +79 -0
  27. package/dist/host/ime.d.ts +3 -0
  28. package/dist/host/ime.js +11 -0
  29. package/dist/host/input.d.ts +12 -0
  30. package/dist/host/input.js +86 -0
  31. package/dist/host/keys.d.ts +16 -0
  32. package/dist/host/keys.js +27 -0
  33. package/dist/host/mount.d.ts +11 -0
  34. package/dist/host/mount.js +72 -0
  35. package/dist/host/pointer.d.ts +4 -0
  36. package/dist/host/pointer.js +171 -0
  37. package/dist/host/scroll.d.ts +14 -0
  38. package/dist/host/scroll.js +95 -0
  39. package/dist/index.d.ts +27 -98
  40. package/dist/index.js +10 -0
  41. package/dist/review.d.ts +156 -0
  42. package/dist/review.js +38 -0
  43. package/dist/services/accessibility.d.ts +52 -0
  44. package/dist/services/accessibility.js +71 -0
  45. package/dist/services/dialogs.d.ts +181 -0
  46. package/dist/services/dialogs.js +18 -0
  47. package/dist/services/elements.d.ts +146 -0
  48. package/dist/services/elements.js +18 -0
  49. package/dist/services/find.d.ts +50 -0
  50. package/dist/services/find.js +12 -0
  51. package/dist/services/menus.d.ts +82 -0
  52. package/dist/services/menus.js +8 -0
  53. package/dist/services/navigation.d.ts +58 -0
  54. package/dist/services/navigation.js +12 -0
  55. package/dist/services/ruler.d.ts +95 -0
  56. package/dist/services/ruler.js +16 -0
  57. package/dist/services/spelling.d.ts +34 -0
  58. package/dist/services/spelling.js +14 -0
  59. package/dist/services/view.d.ts +70 -0
  60. package/dist/services/view.js +19 -0
  61. package/dist/state.d.ts +483 -0
  62. package/dist/state.js +1 -0
  63. package/dist/transport/direct.d.ts +31 -0
  64. package/dist/transport/direct.js +405 -0
  65. package/dist/transport/module.d.ts +51 -0
  66. package/dist/transport/module.js +17 -0
  67. package/dist/transport/protocol.d.ts +265 -0
  68. package/dist/transport/protocol.js +132 -0
  69. package/dist/transport/worker.d.ts +54 -0
  70. package/dist/transport/worker.js +203 -0
  71. package/dist/twip-wasm.js +1 -1
  72. package/dist/twip_wasm.wasm +0 -0
  73. package/dist/types.d.ts +134 -0
  74. package/dist/types.js +16 -0
  75. package/dist/worker/entry.d.ts +1 -0
  76. package/dist/worker/entry.js +53 -0
  77. package/dist/worker.d.ts +1 -0
  78. package/dist/worker.js +2 -0
  79. package/package.json +12 -7
  80. package/dist/AccessibleNode.d.ts +0 -51
  81. package/dist/AccessibleNode.mjs +0 -177
  82. package/dist/Alignment.d.ts +0 -38
  83. package/dist/Alignment.mjs +0 -90
  84. package/dist/BorderEdges.d.ts +0 -50
  85. package/dist/BorderEdges.mjs +0 -114
  86. package/dist/BorderStyle.d.ts +0 -27
  87. package/dist/BorderStyle.mjs +0 -81
  88. package/dist/Bytes.d.ts +0 -19
  89. package/dist/Bytes.mjs +0 -76
  90. package/dist/CaretPosition.d.ts +0 -34
  91. package/dist/CaretPosition.mjs +0 -148
  92. package/dist/CellAlignment.d.ts +0 -25
  93. package/dist/CellAlignment.mjs +0 -75
  94. package/dist/CellShift.d.ts +0 -30
  95. package/dist/CellShift.mjs +0 -78
  96. package/dist/Clipboard.d.ts +0 -22
  97. package/dist/Clipboard.mjs +0 -90
  98. package/dist/ColumnSide.d.ts +0 -24
  99. package/dist/ColumnSide.mjs +0 -72
  100. package/dist/ControlKind.d.ts +0 -28
  101. package/dist/ControlKind.mjs +0 -84
  102. package/dist/ControlLock.d.ts +0 -42
  103. package/dist/ControlLock.mjs +0 -96
  104. package/dist/DocumentStatus.d.ts +0 -34
  105. package/dist/DocumentStatus.mjs +0 -148
  106. package/dist/Editor.d.ts +0 -773
  107. package/dist/Editor.mjs +0 -2269
  108. package/dist/EditorMode.d.ts +0 -34
  109. package/dist/EditorMode.mjs +0 -84
  110. package/dist/Element.d.ts +0 -57
  111. package/dist/Element.mjs +0 -201
  112. package/dist/ElementKind.d.ts +0 -28
  113. package/dist/ElementKind.mjs +0 -78
  114. package/dist/ElementList.d.ts +0 -20
  115. package/dist/ElementList.mjs +0 -71
  116. package/dist/ElementText.d.ts +0 -34
  117. package/dist/ElementText.mjs +0 -84
  118. package/dist/Event.d.ts +0 -45
  119. package/dist/Event.mjs +0 -159
  120. package/dist/EventKind.d.ts +0 -39
  121. package/dist/EventKind.mjs +0 -93
  122. package/dist/EventList.d.ts +0 -20
  123. package/dist/EventList.mjs +0 -71
  124. package/dist/Fonts.d.ts +0 -29
  125. package/dist/Fonts.mjs +0 -107
  126. package/dist/Highlight.d.ts +0 -41
  127. package/dist/Highlight.mjs +0 -117
  128. package/dist/MarginPreset.d.ts +0 -38
  129. package/dist/MarginPreset.mjs +0 -90
  130. package/dist/MarkSeverity.d.ts +0 -25
  131. package/dist/MarkSeverity.mjs +0 -75
  132. package/dist/Modifiers.d.ts +0 -34
  133. package/dist/Modifiers.mjs +0 -148
  134. package/dist/OpenError.d.ts +0 -30
  135. package/dist/OpenError.mjs +0 -78
  136. package/dist/Orientation.d.ts +0 -30
  137. package/dist/Orientation.mjs +0 -78
  138. package/dist/PageNumber.d.ts +0 -34
  139. package/dist/PageNumber.mjs +0 -84
  140. package/dist/PaperSize.d.ts +0 -50
  141. package/dist/PaperSize.mjs +0 -108
  142. package/dist/ParagraphBorders.d.ts +0 -34
  143. package/dist/ParagraphBorders.mjs +0 -84
  144. package/dist/PictureWrap.d.ts +0 -34
  145. package/dist/PictureWrap.mjs +0 -90
  146. package/dist/PointerKind.d.ts +0 -23
  147. package/dist/PointerKind.mjs +0 -75
  148. package/dist/Rect.d.ts +0 -34
  149. package/dist/Rect.mjs +0 -148
  150. package/dist/Request.d.ts +0 -55
  151. package/dist/Request.mjs +0 -207
  152. package/dist/RequestKind.d.ts +0 -45
  153. package/dist/RequestKind.mjs +0 -105
  154. package/dist/RequestList.d.ts +0 -20
  155. package/dist/RequestList.mjs +0 -71
  156. package/dist/RowHeightRule.d.ts +0 -34
  157. package/dist/RowHeightRule.mjs +0 -84
  158. package/dist/RowSide.d.ts +0 -24
  159. package/dist/RowSide.mjs +0 -72
  160. package/dist/SaveError.d.ts +0 -24
  161. package/dist/SaveError.mjs +0 -72
  162. package/dist/SelectionInfo.d.ts +0 -39
  163. package/dist/SelectionInfo.mjs +0 -166
  164. package/dist/SelectionKind.d.ts +0 -37
  165. package/dist/SelectionKind.mjs +0 -93
  166. package/dist/TableAlignment.d.ts +0 -25
  167. package/dist/TableAlignment.mjs +0 -75
  168. package/dist/TableSelection.d.ts +0 -38
  169. package/dist/TableSelection.mjs +0 -90
  170. package/dist/TableWidthUnit.d.ts +0 -31
  171. package/dist/TableWidthUnit.mjs +0 -81
  172. package/dist/TextUnit.d.ts +0 -25
  173. package/dist/TextUnit.mjs +0 -75
  174. package/dist/diplomat-runtime.d.ts +0 -15
  175. package/dist/diplomat-runtime.mjs +0 -786
  176. package/dist/diplomat-wasm.mjs +0 -18
  177. package/dist/gpu.d.ts +0 -15
  178. package/dist/gpu.mjs +0 -42
  179. package/dist/index.mjs +0 -101
@@ -0,0 +1,265 @@
1
+ import { EditorError, type ErrorCode } from '../errors.js';
2
+ import type { TwipEvents } from '../events.js';
3
+ import type { FaceInfo } from '../fonts/library.js';
4
+ import type { AccessibleNode } from '../services/accessibility.js';
5
+ import type { Warning } from '../types.js';
6
+ /** Structured call numbers: `TwipMethod` in `twip.h`, the C ABI's `Method`. The two lists must match exactly. */
7
+ export declare const Method: {
8
+ readonly Run: 1;
9
+ readonly Batch: 2;
10
+ readonly State: 3;
11
+ readonly Catalog: 4;
12
+ readonly NewDocument: 5;
13
+ readonly SetReadOnly: 6;
14
+ readonly SetMode: 7;
15
+ readonly SetAuthor: 8;
16
+ readonly ContextMenu: 9;
17
+ readonly Invoke: 10;
18
+ readonly DialogOpen: 11;
19
+ readonly DialogUpdate: 12;
20
+ readonly DialogApply: 13;
21
+ readonly Find: 14;
22
+ readonly FindAll: 15;
23
+ readonly SelectMatch: 16;
24
+ readonly Replace: 17;
25
+ readonly ReplaceAll: 18;
26
+ readonly SpellingAddDictionary: 19;
27
+ readonly SpellingSetEnabled: 20;
28
+ readonly SpellingSetLanguage: 21;
29
+ readonly SpellingIgnore: 22;
30
+ readonly SpellingAdd: 23;
31
+ readonly SpellingLoadUserWords: 24;
32
+ readonly SpellingSuggestions: 25;
33
+ readonly SpellingNext: 26;
34
+ readonly Headings: 27;
35
+ readonly SelectHeading: 28;
36
+ readonly Styles: 29;
37
+ readonly Revisions: 30;
38
+ readonly Comments: 31;
39
+ readonly People: 32;
40
+ readonly Elements: 33;
41
+ readonly Element: 34;
42
+ readonly Structure: 35;
43
+ readonly SelectElement: 36;
44
+ readonly DataParts: 37;
45
+ readonly AccessibleTree: 38;
46
+ readonly ShortcutsRegister: 39;
47
+ readonly ShortcutsUnregister: 40;
48
+ readonly ShortcutsLookup: 41;
49
+ readonly GesturesBindings: 42;
50
+ readonly GesturesSet: 43;
51
+ readonly GesturesReset: 44;
52
+ readonly WordCount: 45;
53
+ readonly Copy: 46;
54
+ readonly Paste: 47;
55
+ readonly FontFamilies: 48;
56
+ readonly FontSetSubstitutions: 49;
57
+ readonly FontSetDefault: 50;
58
+ readonly FontMarkUnresolved: 51;
59
+ readonly HostSetBackground: 52;
60
+ readonly HostSetMarkupPaneWidth: 53;
61
+ readonly HostSetRulers: 54;
62
+ readonly HostSetRulerUnit: 55;
63
+ readonly HostSetTabSelector: 56;
64
+ readonly HostRulerView: 57;
65
+ readonly HostCommentAnchors: 58;
66
+ readonly HostHitTest: 59;
67
+ readonly HostDragOver: 60;
68
+ readonly HostDragLeave: 61;
69
+ readonly HostDrop: 62;
70
+ readonly HostRevealSelection: 63;
71
+ readonly HostVisiblePages: 64;
72
+ readonly StackBalloons: 65;
73
+ readonly OpenCommit: 66;
74
+ readonly OpenDiscard: 67;
75
+ readonly DialogSelectIssue: 68;
76
+ readonly FontSetResolver: 69;
77
+ readonly Placeholders: 70;
78
+ readonly HostSetCaretBlink: 71;
79
+ readonly HostFrameStats: 72;
80
+ readonly HostSetOverlayPalette: 73;
81
+ readonly HostSetPaintBudget: 74;
82
+ readonly HostSetLeaders: 75;
83
+ };
84
+ /** A structured call number. */
85
+ export type Method = (typeof Method)[keyof typeof Method];
86
+ /** What the engine sends: callback kinds of the C ABI. */
87
+ export declare const Kind: {
88
+ readonly Event: 1;
89
+ readonly State: 2;
90
+ readonly RequestFrame: 3;
91
+ readonly SetTimer: 4;
92
+ readonly FontRequest: 5;
93
+ readonly HostRequest: 6;
94
+ };
95
+ /** A pointer shape over the document: the engine's `Cursor`. */
96
+ export type Cursor = 'default' | 'text' | 'pointer' | 'move' | 'resizeHorizontal' | 'resizeVertical' | 'resizeNorthWestSouthEast' | 'resizeNorthEastSouthWest' | 'crosshair' | 'notAllowed' | 'copy';
97
+ /** What a copy puts on the clipboard: the engine's `ClipboardData`. */
98
+ export interface ClipboardData {
99
+ text: string;
100
+ html?: string;
101
+ /** The engine's own copy, for a paste back into it. */
102
+ internal?: string;
103
+ }
104
+ /** Something only the view showing a session can do: the {@link Kind.HostRequest} payload. Content CSS px. */
105
+ export type HostRequest = {
106
+ type: 'cursor';
107
+ cursor: Cursor;
108
+ }
109
+ /** Send every pointer event to the view until `release`. */
110
+ | {
111
+ type: 'capture';
112
+ } | {
113
+ type: 'release';
114
+ }
115
+ /** A cut or copy from the keyboard, for the system clipboard. */
116
+ | {
117
+ type: 'clipboard';
118
+ data: ClipboardData;
119
+ }
120
+ /** The caret moved: for the IME and the system caret. */
121
+ | {
122
+ type: 'caret';
123
+ x: number;
124
+ y: number;
125
+ height: number;
126
+ visible: boolean;
127
+ }
128
+ /** Scroll the viewport's top-left to this point, after the next frame sizes the content (a zoom). */
129
+ | {
130
+ type: 'scroll';
131
+ x: number;
132
+ y: number;
133
+ }
134
+ /** Scroll so this rectangle shows. */
135
+ | {
136
+ type: 'scroll_to';
137
+ x: number;
138
+ y: number;
139
+ width: number;
140
+ height: number;
141
+ }
142
+ /** The accessibility tree changed: mirror it. */
143
+ | {
144
+ type: 'a11y';
145
+ tree: AccessibleNode;
146
+ };
147
+ /** One engine-to-SDK message. `payload` is the decoded JSON, or undefined for kinds without one. */
148
+ export interface Message {
149
+ kind: number;
150
+ sessionId: number;
151
+ payload: unknown;
152
+ }
153
+ /** Raw input, forwarded to the engine as typed calls (no JSON). */
154
+ export type Input = {
155
+ type: 'key';
156
+ key: number;
157
+ mods: number;
158
+ } | {
159
+ type: 'text';
160
+ text: string;
161
+ } | {
162
+ type: 'composition';
163
+ phase: number;
164
+ text: string;
165
+ } | {
166
+ type: 'focus';
167
+ focused: boolean;
168
+ } | {
169
+ type: 'surface';
170
+ width: number;
171
+ height: number;
172
+ dpr: number;
173
+ } | {
174
+ type: 'scroll';
175
+ x: number;
176
+ y: number;
177
+ } | {
178
+ type: 'wheel';
179
+ dx: number;
180
+ dy: number;
181
+ mods: number;
182
+ } | {
183
+ type: 'pointer';
184
+ kind: number;
185
+ x: number;
186
+ y: number;
187
+ button: number;
188
+ clicks: number;
189
+ mods: number;
190
+ };
191
+ /** What a frame reports: the content's size, CSS px, and the pages laid out so far. */
192
+ export interface FrameInfo {
193
+ contentWidth: number;
194
+ contentHeight: number;
195
+ pages: number;
196
+ }
197
+ /** How a canvas gets its frames: the best of the page's WebGPU, WebGL 2 and CPU pixels. */
198
+ export type Backend = 'webgpu' | 'webgl' | 'cpu';
199
+ /** The engine, behind one ordered queue. Every method runs after every earlier call on the same transport. */
200
+ export interface Transport {
201
+ /** Creates a session over the shared engine. */
202
+ createSession(options: object): Promise<number>;
203
+ /** Closes a session; later calls with its id fail `destroyed`. */
204
+ closeSession(session: number): Promise<void>;
205
+ /** A structured call: request and response are the engine's JSON shapes. */
206
+ call(session: number, method: Method, request?: unknown): Promise<unknown>;
207
+ /** Opens a document; resolves with the read's warnings. */
208
+ open(session: number, bytes: Uint8Array, signal?: AbortSignal): Promise<Warning[]>;
209
+ /**
210
+ * Reads a document without replacing the open one, for an open that may still be aborted: resolves with a token
211
+ * for {@link Transport.openCommit} or {@link Transport.openDiscard}.
212
+ */
213
+ openPrepare(session: number, bytes: Uint8Array): Promise<number>;
214
+ /** Replaces the open document with a prepared one; resolves with the read's warnings. */
215
+ openCommit(session: number, token: number): Promise<Warning[]>;
216
+ /** Drops a prepared document: the open one stays. */
217
+ openDiscard(session: number, token: number): Promise<void>;
218
+ /** Exports the document. */
219
+ export(session: number, options: {
220
+ format: 'docx' | 'pdf';
221
+ }, signal?: AbortSignal): Promise<{
222
+ bytes: Uint8Array;
223
+ revision: number;
224
+ warnings: Warning[];
225
+ }>;
226
+ /** Adds a font to the shared library. */
227
+ addFont(bytes: Uint8Array): Promise<FaceInfo[]>;
228
+ /** Forwards input; a key resolves with its outcome (0 unhandled, 1 handled, 2 and up a host key). */
229
+ input(session: number, input: Input): Promise<number>;
230
+ /**
231
+ * Gives a session the canvas its frames go to, on the best backend the page has. Rejects `unsupported` when the
232
+ * canvas can't be drawn at all.
233
+ */
234
+ attachCanvas(session: number, canvas: HTMLCanvasElement): Promise<Backend>;
235
+ /**
236
+ * Draws a frame of `width` x `height` device pixels into the session's canvas (sizing it), or resolves null when
237
+ * there is no canvas or nothing to draw.
238
+ */
239
+ frame(session: number, width: number, height: number): Promise<FrameInfo | null>;
240
+ /** Receives a session's messages, after the operation that caused them and before its promise settles. */
241
+ listen(session: number, listener: (message: Message) => void): () => void;
242
+ }
243
+ /** The engine's error shape. */
244
+ interface WireError {
245
+ code: ErrorCode;
246
+ message: string;
247
+ details?: unknown;
248
+ }
249
+ /** Turns a response envelope (`{ ok }` or `{ error }`) into its value, or throws its error. */
250
+ export declare function unwrap(response: unknown): unknown;
251
+ /** The engine's error shape as an {@link EditorError}. */
252
+ export declare function toError(error: WireError): EditorError;
253
+ /** An {@link EditorError} in the engine's error shape. */
254
+ export declare function fromError(error: EditorError): WireError;
255
+ /**
256
+ * An engine event as its public type and payload. Events travel as `{ type, ...fields }`; an event whose payload
257
+ * isn't an object carries it as `value`, and `error` carries the error shape plus `fatal`.
258
+ */
259
+ export declare function decodeEvent(wire: unknown): {
260
+ type: keyof TwipEvents;
261
+ event: unknown;
262
+ };
263
+ /** The inverse of {@link decodeEvent}. */
264
+ export declare function encodeEvent(type: keyof TwipEvents, event: unknown): unknown;
265
+ export {};
@@ -0,0 +1,132 @@
1
+ // The private boundary between the public classes and the engine. The engine may run on this thread
2
+ // (DirectTransport) or in the page's engine worker; both deliver the same message stream and run operations in one
3
+ // ordered queue, so the public classes don't know which one they talk to.
4
+ import { EditorError } from '../errors.js';
5
+ /** Structured call numbers: `TwipMethod` in `twip.h`, the C ABI's `Method`. The two lists must match exactly. */
6
+ export const Method = {
7
+ Run: 1,
8
+ Batch: 2,
9
+ State: 3,
10
+ Catalog: 4,
11
+ NewDocument: 5,
12
+ SetReadOnly: 6,
13
+ SetMode: 7,
14
+ SetAuthor: 8,
15
+ ContextMenu: 9,
16
+ Invoke: 10,
17
+ DialogOpen: 11,
18
+ DialogUpdate: 12,
19
+ DialogApply: 13,
20
+ Find: 14,
21
+ FindAll: 15,
22
+ SelectMatch: 16,
23
+ Replace: 17,
24
+ ReplaceAll: 18,
25
+ SpellingAddDictionary: 19,
26
+ SpellingSetEnabled: 20,
27
+ SpellingSetLanguage: 21,
28
+ SpellingIgnore: 22,
29
+ SpellingAdd: 23,
30
+ SpellingLoadUserWords: 24,
31
+ SpellingSuggestions: 25,
32
+ SpellingNext: 26,
33
+ Headings: 27,
34
+ SelectHeading: 28,
35
+ Styles: 29,
36
+ Revisions: 30,
37
+ Comments: 31,
38
+ People: 32,
39
+ Elements: 33,
40
+ Element: 34,
41
+ Structure: 35,
42
+ SelectElement: 36,
43
+ DataParts: 37,
44
+ AccessibleTree: 38,
45
+ ShortcutsRegister: 39,
46
+ ShortcutsUnregister: 40,
47
+ ShortcutsLookup: 41,
48
+ GesturesBindings: 42,
49
+ GesturesSet: 43,
50
+ GesturesReset: 44,
51
+ WordCount: 45,
52
+ Copy: 46,
53
+ Paste: 47,
54
+ FontFamilies: 48,
55
+ FontSetSubstitutions: 49,
56
+ FontSetDefault: 50,
57
+ FontMarkUnresolved: 51,
58
+ HostSetBackground: 52,
59
+ HostSetMarkupPaneWidth: 53,
60
+ HostSetRulers: 54,
61
+ HostSetRulerUnit: 55,
62
+ HostSetTabSelector: 56,
63
+ HostRulerView: 57,
64
+ HostCommentAnchors: 58,
65
+ HostHitTest: 59,
66
+ HostDragOver: 60,
67
+ HostDragLeave: 61,
68
+ HostDrop: 62,
69
+ HostRevealSelection: 63,
70
+ HostVisiblePages: 64,
71
+ StackBalloons: 65,
72
+ OpenCommit: 66,
73
+ OpenDiscard: 67,
74
+ DialogSelectIssue: 68,
75
+ FontSetResolver: 69,
76
+ Placeholders: 70,
77
+ HostSetCaretBlink: 71,
78
+ HostFrameStats: 72,
79
+ HostSetOverlayPalette: 73,
80
+ HostSetPaintBudget: 74,
81
+ HostSetLeaders: 75,
82
+ };
83
+ /** What the engine sends: callback kinds of the C ABI. */
84
+ export const Kind = {
85
+ Event: 1,
86
+ State: 2,
87
+ RequestFrame: 3,
88
+ SetTimer: 4,
89
+ FontRequest: 5,
90
+ HostRequest: 6,
91
+ };
92
+ /** Turns a response envelope (`{ ok }` or `{ error }`) into its value, or throws its error. */
93
+ export function unwrap(response) {
94
+ const envelope = response;
95
+ if (envelope && typeof envelope === 'object' && 'error' in envelope && envelope.error)
96
+ throw toError(envelope.error);
97
+ if (envelope && typeof envelope === 'object' && 'ok' in envelope)
98
+ return envelope.ok;
99
+ throw new EditorError('internal', `malformed engine response: ${JSON.stringify(response)}`);
100
+ }
101
+ /** The engine's error shape as an {@link EditorError}. */
102
+ export function toError(error) {
103
+ return new EditorError(error.code, error.message, error.details == null ? undefined : { details: error.details });
104
+ }
105
+ /** An {@link EditorError} in the engine's error shape. */
106
+ export function fromError(error) {
107
+ return error.details === undefined
108
+ ? { code: error.code, message: error.message }
109
+ : { code: error.code, message: error.message, details: error.details };
110
+ }
111
+ /**
112
+ * An engine event as its public type and payload. Events travel as `{ type, ...fields }`; an event whose payload
113
+ * isn't an object carries it as `value`, and `error` carries the error shape plus `fatal`.
114
+ */
115
+ export function decodeEvent(wire) {
116
+ const { type, ...fields } = wire;
117
+ if (type === 'error') {
118
+ const { fatal, ...error } = fields;
119
+ const e = toError(error);
120
+ return { type, event: fatal ? new EditorError(e.code, e.message, { details: e.details, fatal }) : e };
121
+ }
122
+ return { type, event: 'value' in fields ? fields.value : fields };
123
+ }
124
+ /** The events whose payload always travels as `value`, an object or not: the engine's optional payloads. */
125
+ const valueEvents = new Set(['tooltip']);
126
+ /** The inverse of {@link decodeEvent}. */
127
+ export function encodeEvent(type, event) {
128
+ if (event instanceof EditorError)
129
+ return { type, ...fromError(event), fatal: event.fatal };
130
+ const spread = event !== null && typeof event === 'object' && !Array.isArray(event) && !valueEvents.has(type);
131
+ return spread ? { type, ...event } : { type, value: event };
132
+ }
@@ -0,0 +1,54 @@
1
+ import { EditorError } from '../errors.js';
2
+ import type { FaceInfo } from '../fonts/library.js';
3
+ import type { Warning } from '../types.js';
4
+ import { type Backend, type FrameInfo, type Input, type Message, Method, type Transport } from './protocol.js';
5
+ /** A call to the worker: a {@link Transport} method and its arguments (without the signal), or an abort. */
6
+ export interface Request {
7
+ id: number;
8
+ op: keyof Transport | 'abort';
9
+ args: unknown[];
10
+ }
11
+ /** The worker's answer to a request. */
12
+ export type Reply = {
13
+ id: number;
14
+ ok: unknown;
15
+ } | {
16
+ id: number;
17
+ error: {
18
+ code: EditorError['code'];
19
+ message: string;
20
+ details?: unknown;
21
+ };
22
+ fatal: boolean;
23
+ };
24
+ /**
25
+ * The mode `execution` asks for. `'auto'` is the worker where the page supports it, and the main thread otherwise;
26
+ * `'worker'` where it doesn't is `unsupported`.
27
+ */
28
+ export declare function chooseExecution(execution?: 'auto' | 'main' | 'worker'): 'main' | 'worker';
29
+ /** The page's one worker transport. */
30
+ export declare function workerTransport(): WorkerTransport;
31
+ /** The {@link Transport} that posts every call to the page's engine worker. */
32
+ export declare class WorkerTransport implements Transport {
33
+ #private;
34
+ createSession(options: object): Promise<number>;
35
+ closeSession(session: number): Promise<void>;
36
+ call(session: number, method: Method, request?: unknown): Promise<unknown>;
37
+ open(session: number, bytes: Uint8Array, signal?: AbortSignal): Promise<Warning[]>;
38
+ openPrepare(session: number, bytes: Uint8Array): Promise<number>;
39
+ openCommit(session: number, token: number): Promise<Warning[]>;
40
+ openDiscard(session: number, token: number): Promise<void>;
41
+ export(session: number, options: {
42
+ format: 'docx' | 'pdf';
43
+ }, signal?: AbortSignal): Promise<{
44
+ bytes: Uint8Array;
45
+ revision: number;
46
+ warnings: Warning[];
47
+ }>;
48
+ addFont(bytes: Uint8Array): Promise<FaceInfo[]>;
49
+ input(session: number, input: Input): Promise<number>;
50
+ /** Moves the canvas to the worker, which draws the session's frames into it from then on. */
51
+ attachCanvas(session: number, canvas: HTMLCanvasElement): Promise<Backend>;
52
+ frame(session: number, width: number, height: number): Promise<FrameInfo | null>;
53
+ listen(session: number, listener: (message: Message) => void): () => void;
54
+ }
@@ -0,0 +1,203 @@
1
+ // The engine in the page's engine worker (src/worker/entry.ts). One worker per page hosts every editor's session, so
2
+ // fonts, dictionaries and the internal clipboard are shared. Calls are posted in submission order and the worker runs
3
+ // them through one queue (a DirectTransport there), so the ordering is the direct transport's. Each operation's
4
+ // messages arrive before its reply and are dispatched in arrival order, so a state message reaches the session's
5
+ // cache before any event of the same drain.
6
+ import { EditorError, unsupported } from '../errors.js';
7
+ import { live } from '../host/dom.js';
8
+ import { Method, toError } from './protocol.js';
9
+ let modules;
10
+ /** Whether this page can run the engine in a worker: module workers and `transferControlToOffscreen`. */
11
+ function workerSupported() {
12
+ if (typeof Worker === 'undefined' || typeof HTMLCanvasElement === 'undefined')
13
+ return false;
14
+ if (typeof HTMLCanvasElement.prototype.transferControlToOffscreen !== 'function')
15
+ return false;
16
+ if (modules === undefined) {
17
+ // A browser with module workers reads `type`; the URL doesn't parse, so no worker starts.
18
+ modules = false;
19
+ try {
20
+ new Worker('http://[', {
21
+ get type() {
22
+ modules = true;
23
+ return 'module';
24
+ },
25
+ });
26
+ }
27
+ catch {
28
+ // Expected: the URL.
29
+ }
30
+ }
31
+ return modules;
32
+ }
33
+ /**
34
+ * The mode `execution` asks for. `'auto'` is the worker where the page supports it, and the main thread otherwise;
35
+ * `'worker'` where it doesn't is `unsupported`.
36
+ */
37
+ export function chooseExecution(execution = 'auto') {
38
+ if (execution === 'main')
39
+ return 'main';
40
+ if (workerSupported())
41
+ return 'worker';
42
+ if (execution === 'worker')
43
+ throw unsupported("execution: 'worker' on this browser");
44
+ return 'main';
45
+ }
46
+ let shared;
47
+ /** The page's one worker transport. */
48
+ export function workerTransport() {
49
+ return (shared ??= new WorkerTransport());
50
+ }
51
+ /** The {@link Transport} that posts every call to the page's engine worker. */
52
+ export class WorkerTransport {
53
+ #worker;
54
+ #next = 1;
55
+ #calls = new Map();
56
+ #listeners = new Map();
57
+ /** Open sessions: the worker stops when the last one closes and no call is in flight. */
58
+ #sessions = new Set();
59
+ /** The font library's changes, replayed into a restarted worker so the page's fonts survive it. */
60
+ #library = [];
61
+ /** Set when the worker failed: it can't be trusted again. */
62
+ #fault;
63
+ createSession(options) {
64
+ return this.#request('createSession', [options]).then((id) => (this.#sessions.add(id), id));
65
+ }
66
+ closeSession(session) {
67
+ return this.#request('closeSession', [session]).finally(() => {
68
+ this.#listeners.delete(session);
69
+ this.#sessions.delete(session);
70
+ this.#stopWhenIdle();
71
+ });
72
+ }
73
+ call(session, method, request) {
74
+ return this.#request('call', [session, method, request], { library: session === 0 && method !== Method.FontFamilies });
75
+ }
76
+ open(session, bytes, signal) {
77
+ return this.#request('open', [session, bytes], { signal });
78
+ }
79
+ openPrepare(session, bytes) {
80
+ return this.#request('openPrepare', [session, bytes]);
81
+ }
82
+ openCommit(session, token) {
83
+ return this.#request('openCommit', [session, token]);
84
+ }
85
+ openDiscard(session, token) {
86
+ return this.#request('openDiscard', [session, token]);
87
+ }
88
+ export(session, options, signal) {
89
+ return this.#request('export', [session, options], { signal });
90
+ }
91
+ addFont(bytes) {
92
+ return this.#request('addFont', [bytes], { library: true });
93
+ }
94
+ input(session, input) {
95
+ return this.#request('input', [session, input]);
96
+ }
97
+ /** Moves the canvas to the worker, which draws the session's frames into it from then on. */
98
+ attachCanvas(session, canvas) {
99
+ const offscreen = canvas.transferControlToOffscreen();
100
+ return this.#request('attachCanvas', [session, offscreen], { transfer: [offscreen] });
101
+ }
102
+ frame(session, width, height) {
103
+ return this.#request('frame', [session, width, height]);
104
+ }
105
+ listen(session, listener) {
106
+ let set = this.#listeners.get(session);
107
+ if (!set)
108
+ this.#listeners.set(session, (set = new Set()));
109
+ set.add(listener);
110
+ return () => set.delete(listener);
111
+ }
112
+ #request(op, args, how = {}) {
113
+ if (this.#fault)
114
+ return Promise.reject(this.#fault);
115
+ const { signal } = how;
116
+ if (signal?.aborted)
117
+ return Promise.reject(new EditorError('aborted', 'the operation was aborted'));
118
+ let worker;
119
+ try {
120
+ worker = this.#start();
121
+ }
122
+ catch (cause) {
123
+ return Promise.reject(this.#failed(cause));
124
+ }
125
+ const request = { id: this.#next++, op, args };
126
+ // Replayed with no reply to wait for (id 0).
127
+ if (how.library)
128
+ this.#library.push({ ...request, id: 0 });
129
+ return new Promise((resolve, reject) => {
130
+ const abort = () => worker.postMessage({ id: request.id, op: 'abort', args: [] });
131
+ signal?.addEventListener('abort', abort, { once: true });
132
+ this.#calls.set(request.id, {
133
+ resolve: (value) => (signal?.removeEventListener('abort', abort), resolve(value)),
134
+ reject: (error) => (signal?.removeEventListener('abort', abort), reject(error)),
135
+ });
136
+ worker.postMessage(request, { transfer: how.transfer ?? [] });
137
+ });
138
+ }
139
+ #start() {
140
+ if (this.#worker)
141
+ return this.#worker;
142
+ // The worker is beside this file's directory, in dist/. Written out in one expression, as bundlers recognise
143
+ // it: they build the worker and its imports into the application.
144
+ const worker = new Worker(new URL('../worker.js', import.meta.url), { type: 'module', name: 'twip-engine' });
145
+ live.workers++;
146
+ worker.onmessage = ({ data }) => this.#receive(data);
147
+ worker.onerror = (e) => {
148
+ e.preventDefault();
149
+ this.#failed(e.message || 'the engine worker failed to start');
150
+ };
151
+ for (const request of this.#library)
152
+ worker.postMessage(request);
153
+ return (this.#worker = worker);
154
+ }
155
+ #receive(data) {
156
+ if ('kind' in data) {
157
+ for (const listener of [...(this.#listeners.get(data.sessionId) ?? [])]) {
158
+ try {
159
+ listener(data);
160
+ }
161
+ catch (e) {
162
+ globalThis.reportError?.(e);
163
+ }
164
+ }
165
+ return;
166
+ }
167
+ const call = this.#calls.get(data.id);
168
+ if (!call)
169
+ return;
170
+ this.#calls.delete(data.id);
171
+ if ('error' in data) {
172
+ const e = toError(data.error);
173
+ call.reject(data.fatal ? new EditorError(e.code, e.message, { details: e.details, fatal: true }) : e);
174
+ }
175
+ else
176
+ call.resolve(data.ok);
177
+ // After the caller's reaction, which counts a session just created.
178
+ queueMicrotask(() => this.#stopWhenIdle());
179
+ }
180
+ /** No editor left: the worker goes, and the next call starts a new one. */
181
+ #stopWhenIdle() {
182
+ if (this.#sessions.size || this.#calls.size || !this.#worker)
183
+ return;
184
+ this.#stop();
185
+ }
186
+ #stop() {
187
+ this.#worker?.terminate();
188
+ if (this.#worker)
189
+ live.workers--;
190
+ this.#worker = undefined;
191
+ }
192
+ /** The worker failed: every call in flight and every later one rejects, and the next editor gets a new worker. */
193
+ #failed(cause) {
194
+ this.#fault ??= new EditorError('internal', `the engine worker stopped: ${String(cause)}`, { fatal: true, cause });
195
+ if (shared === this)
196
+ shared = undefined;
197
+ for (const call of this.#calls.values())
198
+ call.reject(this.#fault);
199
+ this.#calls.clear();
200
+ this.#stop();
201
+ return this.#fault;
202
+ }
203
+ }