@casualoffice/sheets 0.9.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.
Files changed (92) hide show
  1. package/LICENSE +200 -0
  2. package/dist/embed/embed-runtime.js +537 -0
  3. package/dist/embed/embed.html +29 -0
  4. package/dist/embed/parser.worker.js +48474 -0
  5. package/dist/embed.cjs +225 -0
  6. package/dist/embed.cjs.map +1 -0
  7. package/dist/embed.d.cts +100 -0
  8. package/dist/embed.d.ts +100 -0
  9. package/dist/embed.js +204 -0
  10. package/dist/embed.js.map +1 -0
  11. package/dist/index.cjs +1549 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.d.cts +9 -0
  14. package/dist/index.d.ts +9 -0
  15. package/dist/index.js +1530 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/parser.worker.cjs +48469 -0
  18. package/dist/parser.worker.cjs.map +1 -0
  19. package/dist/parser.worker.js +48474 -0
  20. package/dist/parser.worker.js.map +1 -0
  21. package/dist/protocol--KyBQUjU.d.cts +171 -0
  22. package/dist/protocol-cEzy7S0i.d.ts +171 -0
  23. package/dist/sheets.cjs +677 -0
  24. package/dist/sheets.cjs.map +1 -0
  25. package/dist/sheets.d.cts +177 -0
  26. package/dist/sheets.d.ts +177 -0
  27. package/dist/sheets.js +658 -0
  28. package/dist/sheets.js.map +1 -0
  29. package/dist/signing.cjs +706 -0
  30. package/dist/signing.cjs.map +1 -0
  31. package/dist/signing.d.cts +141 -0
  32. package/dist/signing.d.ts +141 -0
  33. package/dist/signing.js +683 -0
  34. package/dist/signing.js.map +1 -0
  35. package/dist/styles.cjs +10 -0
  36. package/dist/styles.cjs.map +1 -0
  37. package/dist/styles.d.cts +2 -0
  38. package/dist/styles.d.ts +2 -0
  39. package/dist/styles.js +8 -0
  40. package/dist/styles.js.map +1 -0
  41. package/dist/types-s_O0u6Cg.d.cts +90 -0
  42. package/dist/types-s_O0u6Cg.d.ts +90 -0
  43. package/dist/univer.cjs +220 -0
  44. package/dist/univer.cjs.map +1 -0
  45. package/dist/univer.d.cts +60 -0
  46. package/dist/univer.d.ts +60 -0
  47. package/dist/univer.js +187 -0
  48. package/dist/univer.js.map +1 -0
  49. package/dist/xlsx.cjs +3388 -0
  50. package/dist/xlsx.cjs.map +1 -0
  51. package/dist/xlsx.d.cts +383 -0
  52. package/dist/xlsx.d.ts +383 -0
  53. package/dist/xlsx.js +3383 -0
  54. package/dist/xlsx.js.map +1 -0
  55. package/package.json +293 -0
  56. package/src/embed/EmbedHostTransport.ts +226 -0
  57. package/src/embed/EmbedTransport.ts +323 -0
  58. package/src/embed/EmbedTransport.unit.test.ts +161 -0
  59. package/src/embed/index.ts +40 -0
  60. package/src/embed/protocol.ts +258 -0
  61. package/src/embed-runtime/embed.html +29 -0
  62. package/src/embed-runtime/index.tsx +440 -0
  63. package/src/index.ts +16 -0
  64. package/src/sheets/CasualSheets.tsx +319 -0
  65. package/src/sheets/CasualSheetsIframe.tsx +220 -0
  66. package/src/sheets/api.ts +108 -0
  67. package/src/sheets/index.ts +11 -0
  68. package/src/signing/SigningPane.tsx +374 -0
  69. package/src/signing/SigningProvider.tsx +126 -0
  70. package/src/signing/captures.tsx +316 -0
  71. package/src/signing/controller.ts +151 -0
  72. package/src/signing/controller.unit.test.ts +133 -0
  73. package/src/signing/index.ts +44 -0
  74. package/src/signing/types.ts +89 -0
  75. package/src/styles.ts +16 -0
  76. package/src/univer/index.ts +17 -0
  77. package/src/univer/lazy-plugins.ts +280 -0
  78. package/src/xlsx/_perf.ts +14 -0
  79. package/src/xlsx/_snapshot-constants.ts +15 -0
  80. package/src/xlsx/comments-resource.ts +209 -0
  81. package/src/xlsx/constants.ts +9 -0
  82. package/src/xlsx/data-validation-resource.ts +219 -0
  83. package/src/xlsx/import.ts +35 -0
  84. package/src/xlsx/index.ts +40 -0
  85. package/src/xlsx/page-setup-resource.ts +205 -0
  86. package/src/xlsx/parse-impl.ts +418 -0
  87. package/src/xlsx/parse-in-worker.ts +82 -0
  88. package/src/xlsx/parser.worker.ts +39 -0
  89. package/src/xlsx/passthrough-resource.ts +175 -0
  90. package/src/xlsx/pivot-passthrough.ts +359 -0
  91. package/src/xlsx/style-mapping.ts +171 -0
  92. package/src/xlsx/tables-resource.ts +211 -0
@@ -0,0 +1,323 @@
1
+ /**
2
+ * EmbedTransport — postMessage bridge for the iframe-embedded
3
+ * editor. Validates origin, dispatches envelopes to handlers,
4
+ * sends responses back to the host. Pure TypeScript — no React.
5
+ *
6
+ * Wire shape defined in
7
+ * `docs/internal/13-iframe-protocol.md` + `./protocol.ts`.
8
+ *
9
+ * Lifetime: constructed once when the /embed page mounts.
10
+ * Destroyed when the page unloads. Concurrent calls into the same
11
+ * handler are the host's responsibility (well-behaved hosts
12
+ * serialise requests by id).
13
+ */
14
+
15
+ import {
16
+ isCasualEnvelope,
17
+ type CasualApp,
18
+ type CasualEnvelope,
19
+ type EditorHelloData,
20
+ type HostHelloData,
21
+ type LoadResponseData,
22
+ type LoadRequestData,
23
+ type SaveRequestData,
24
+ type SaveResponseData,
25
+ type SelectionChangedData,
26
+ type TelemetryEventData,
27
+ type CommandSetReadOnlyData,
28
+ type CommandSetThemeData,
29
+ type CommandSetLocaleData,
30
+ type CommandSetViewModeData,
31
+ type CommandExecuteData,
32
+ type SelectionFormatStateData,
33
+ type CasualErrorData,
34
+ type SignatureRequestData,
35
+ type SignatureRequestAckData,
36
+ type SignatureFieldSignedData,
37
+ type SignatureCompleteData,
38
+ type SignatureCancelData,
39
+ } from './protocol';
40
+
41
+ export interface EmbedTransportOptions {
42
+ app: CasualApp;
43
+ /** Origin allowed to send messages. Required — no `*` default. */
44
+ hostOrigin: string;
45
+ /** Editor build identity, surfaced in the editor.hello handshake. */
46
+ version: string;
47
+ commit: string;
48
+ /** Editor-side capabilities advertised at handshake. */
49
+ capabilities: string[];
50
+ /** Optional injection for `window.parent` — tests pass a stub. */
51
+ parentWindow?: {
52
+ postMessage: (msg: unknown, targetOrigin: string, transfer?: Transferable[]) => void;
53
+ };
54
+ /** Optional injection for the window receiving messages — tests pass a stub. */
55
+ hostWindow?: Pick<Window, 'addEventListener' | 'removeEventListener'>;
56
+ }
57
+
58
+ export interface EmbedTransportHandlers {
59
+ /** Host → editor handshake. Editor responds with `editor.ready`. */
60
+ onHostHello?: (data: HostHelloData) => void | Promise<void>;
61
+ /** Host → editor: command.* messages. */
62
+ onCommandSetReadOnly?: (data: CommandSetReadOnlyData) => void | Promise<void>;
63
+ onCommandSetTheme?: (data: CommandSetThemeData) => void | Promise<void>;
64
+ onCommandSetLocale?: (data: CommandSetLocaleData) => void | Promise<void>;
65
+ /** Host → editor: switch chrome density (preview ↔ editor). */
66
+ onCommandSetViewMode?: (data: CommandSetViewModeData) => void | Promise<void>;
67
+ /** Host → editor: execute a formatting / navigation command (bold,
68
+ * italic, undo, …) against the active selection. v0.6+. */
69
+ onCommandExecute?: (data: CommandExecuteData) => void | Promise<void>;
70
+ onCommandFocus?: () => void | Promise<void>;
71
+ onCommandSave?: () => void | Promise<void>;
72
+ onCommandLoad?: () => void | Promise<void>;
73
+ /** Host → editor signing session. Editor responds with `signature.request.ack`. */
74
+ onSignatureRequest?: (
75
+ data: SignatureRequestData,
76
+ ) => SignatureRequestAckData | Promise<SignatureRequestAckData>;
77
+ onSignatureCancel?: (data: SignatureCancelData) => void | Promise<void>;
78
+ /** Host → editor: response to a prior load.request the editor issued. */
79
+ onLoadResponse?: (id: string, data: LoadResponseData) => void;
80
+ /** Host → editor: response to a prior save.request the editor issued. */
81
+ onSaveResponse?: (id: string, data: SaveResponseData) => void;
82
+ }
83
+
84
+ export class EmbedTransport {
85
+ private readonly opts: Required<Omit<EmbedTransportOptions, 'parentWindow' | 'hostWindow'>> & {
86
+ parentWindow: NonNullable<EmbedTransportOptions['parentWindow']>;
87
+ hostWindow: NonNullable<EmbedTransportOptions['hostWindow']>;
88
+ };
89
+ private handlers: EmbedTransportHandlers = {};
90
+ private destroyed = false;
91
+ private pendingRequests = new Map<string, (env: CasualEnvelope) => void>();
92
+
93
+ constructor(opts: EmbedTransportOptions) {
94
+ this.opts = {
95
+ ...opts,
96
+ parentWindow:
97
+ opts.parentWindow ??
98
+ (typeof window !== 'undefined'
99
+ ? (window.parent as unknown as NonNullable<EmbedTransportOptions['parentWindow']>)
100
+ : { postMessage: () => undefined }),
101
+ hostWindow:
102
+ opts.hostWindow ??
103
+ (typeof window !== 'undefined'
104
+ ? window
105
+ : ({
106
+ addEventListener: () => undefined,
107
+ removeEventListener: () => undefined,
108
+ } as unknown as NonNullable<EmbedTransportOptions['hostWindow']>)),
109
+ };
110
+ this.boundMessage = this.boundMessage.bind(this);
111
+ this.opts.hostWindow.addEventListener('message', this.boundMessage as EventListener);
112
+ }
113
+
114
+ /** Replaces the handler set. Idempotent — call multiple times. */
115
+ on(handlers: EmbedTransportHandlers): void {
116
+ this.handlers = { ...this.handlers, ...handlers };
117
+ }
118
+
119
+ /** Editor → Host: announce ourselves. Call once after handlers are wired. */
120
+ sendHello(): void {
121
+ const data: EditorHelloData = {
122
+ capabilities: this.opts.capabilities,
123
+ version: this.opts.version,
124
+ commit: this.opts.commit,
125
+ };
126
+ this.post('casual.hello', data);
127
+ }
128
+
129
+ /** Editor → Host: editor is ready to receive commands. */
130
+ sendReady(): void {
131
+ this.post('casual.ready', {});
132
+ }
133
+
134
+ /** Editor → Host: ask the host to supply document bytes for `docId`. */
135
+ async requestLoad(docId: string, timeoutMs = 30000): Promise<LoadResponseData> {
136
+ return this.request<LoadResponseData>(
137
+ 'casual.load.request',
138
+ { docId } as LoadRequestData,
139
+ timeoutMs,
140
+ );
141
+ }
142
+
143
+ /** Editor → Host: ask the host to persist `bytes`. */
144
+ async requestSave(req: SaveRequestData, timeoutMs = 30000): Promise<SaveResponseData> {
145
+ return this.request<SaveResponseData>('casual.save.request', req, timeoutMs, [req.bytes]);
146
+ }
147
+
148
+ /** Editor → Host: selection moved. Fire-and-forget. */
149
+ sendSelectionChanged(data: SelectionChangedData): void {
150
+ this.post('casual.selection.changed', data);
151
+ }
152
+
153
+ /** Editor → Host: format flags on the active cell changed (bold,
154
+ * italic, …). Drive's custom toolbar mirrors this state in its
155
+ * "pressed" indicators. v0.6+. */
156
+ sendSelectionFormatState(data: SelectionFormatStateData): void {
157
+ this.post('casual.selection.format-state', data);
158
+ }
159
+
160
+ /** Editor → Host: a noteworthy event. */
161
+ sendTelemetry(data: TelemetryEventData): void {
162
+ this.post('casual.telemetry.event', data);
163
+ }
164
+
165
+ /** Editor → Host: per-field progress during a signing session. */
166
+ sendSignatureFieldSigned(data: SignatureFieldSignedData): void {
167
+ // Bytes ride the transfer list.
168
+ this.post('casual.signature.field.signed', data, [data.bytes]);
169
+ }
170
+
171
+ /** Editor → Host: signing session is finished. */
172
+ sendSignatureComplete(data: SignatureCompleteData): void {
173
+ this.post('casual.signature.complete', data, [data.bytes]);
174
+ }
175
+
176
+ /** Editor → Host: editor-side cancel of a signing session. */
177
+ sendSignatureCancel(reason: SignatureCancelData['reason']): void {
178
+ this.post('casual.signature.cancel', { reason });
179
+ }
180
+
181
+ /** Editor → Host: fatal boot / load error. Host surfaces via wrapper
182
+ * `onError` callback. Fire-and-forget. */
183
+ sendError(data: CasualErrorData): void {
184
+ this.post('casual.error', data);
185
+ }
186
+
187
+ /** Tear down listeners. Idempotent. */
188
+ destroy(): void {
189
+ if (this.destroyed) return;
190
+ this.opts.hostWindow.removeEventListener('message', this.boundMessage as EventListener);
191
+ this.pendingRequests.clear();
192
+ this.destroyed = true;
193
+ }
194
+
195
+ // ---------------------------------------------------------------
196
+ // Internals
197
+ // ---------------------------------------------------------------
198
+
199
+ private boundMessage(ev: Event): void {
200
+ // MessageEvent isn't always available on the typing side when
201
+ // a stub window is injected; cast pragmatically.
202
+ const msg = ev as unknown as MessageEvent;
203
+ if (msg.origin && msg.origin !== this.opts.hostOrigin) return;
204
+ if (!isCasualEnvelope(msg.data)) return;
205
+ void this.dispatch(msg.data as CasualEnvelope);
206
+ }
207
+
208
+ private async dispatch(env: CasualEnvelope): Promise<void> {
209
+ // Responses to outbound requests route through the correlation map.
210
+ if (env.id && this.pendingRequests.has(env.id)) {
211
+ const resolve = this.pendingRequests.get(env.id)!;
212
+ this.pendingRequests.delete(env.id);
213
+ resolve(env);
214
+ return;
215
+ }
216
+
217
+ switch (env.type) {
218
+ case 'casual.hello':
219
+ await this.handlers.onHostHello?.(env.data as HostHelloData);
220
+ this.sendReady();
221
+ return;
222
+ case 'casual.command.setReadOnly':
223
+ await this.handlers.onCommandSetReadOnly?.(env.data as CommandSetReadOnlyData);
224
+ return;
225
+ case 'casual.command.setTheme':
226
+ await this.handlers.onCommandSetTheme?.(env.data as CommandSetThemeData);
227
+ return;
228
+ case 'casual.command.setLocale':
229
+ await this.handlers.onCommandSetLocale?.(env.data as CommandSetLocaleData);
230
+ return;
231
+ case 'casual.command.set.viewmode':
232
+ await this.handlers.onCommandSetViewMode?.(env.data as CommandSetViewModeData);
233
+ return;
234
+ case 'casual.command.execute':
235
+ await this.handlers.onCommandExecute?.(env.data as CommandExecuteData);
236
+ return;
237
+ case 'casual.command.focus':
238
+ await this.handlers.onCommandFocus?.();
239
+ return;
240
+ case 'casual.command.save':
241
+ await this.handlers.onCommandSave?.();
242
+ return;
243
+ case 'casual.command.load':
244
+ await this.handlers.onCommandLoad?.();
245
+ return;
246
+ case 'casual.signature.request': {
247
+ const ack = (await this.handlers.onSignatureRequest?.(
248
+ env.data as SignatureRequestData,
249
+ )) ?? {
250
+ ok: false,
251
+ code: 'unhandled',
252
+ };
253
+ if (env.id) {
254
+ this.postReply(env.id, 'casual.signature.request.ack', ack);
255
+ }
256
+ return;
257
+ }
258
+ case 'casual.signature.cancel':
259
+ await this.handlers.onSignatureCancel?.(env.data as SignatureCancelData);
260
+ return;
261
+ default:
262
+ // Unknown envelope — silently drop per the forward-compat
263
+ // convention in the protocol doc.
264
+ return;
265
+ }
266
+ }
267
+
268
+ private post(type: string, data: unknown, transfer?: Transferable[]): void {
269
+ const env: CasualEnvelope = { type, app: this.opts.app, v: 1, data };
270
+ try {
271
+ this.opts.parentWindow.postMessage(env, this.opts.hostOrigin, transfer);
272
+ } catch {
273
+ // Cross-origin postMessage can throw if the parent went away;
274
+ // silently swallow — the next user action will retry.
275
+ }
276
+ }
277
+
278
+ private postReply(id: string, type: string, data: unknown): void {
279
+ const env: CasualEnvelope = { type, app: this.opts.app, id, v: 1, data };
280
+ try {
281
+ this.opts.parentWindow.postMessage(env, this.opts.hostOrigin);
282
+ } catch {
283
+ // see post()
284
+ }
285
+ }
286
+
287
+ private async request<T>(
288
+ type: string,
289
+ data: unknown,
290
+ timeoutMs: number,
291
+ transfer?: Transferable[],
292
+ ): Promise<T> {
293
+ const id = newRequestId();
294
+ return new Promise<T>((resolve, reject) => {
295
+ const timer =
296
+ timeoutMs > 0
297
+ ? setTimeout(() => {
298
+ this.pendingRequests.delete(id);
299
+ reject(new Error(`Embed request ${type} timed out after ${timeoutMs}ms`));
300
+ }, timeoutMs)
301
+ : null;
302
+ this.pendingRequests.set(id, (env) => {
303
+ if (timer) clearTimeout(timer);
304
+ resolve(env.data as T);
305
+ });
306
+ const env: CasualEnvelope = { type, app: this.opts.app, id, v: 1, data };
307
+ try {
308
+ this.opts.parentWindow.postMessage(env, this.opts.hostOrigin, transfer);
309
+ } catch (err) {
310
+ if (timer) clearTimeout(timer);
311
+ this.pendingRequests.delete(id);
312
+ reject(err);
313
+ }
314
+ });
315
+ }
316
+ }
317
+
318
+ function newRequestId(): string {
319
+ // 8 hex chars is plenty — IDs only need to be unique within a
320
+ // single editor session. Math.random is fine; the host can't
321
+ // use these for security.
322
+ return Math.random().toString(16).slice(2, 10);
323
+ }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * EmbedTransport contract tests. Mirrors the document/ repo's
3
+ * EmbedTransport.test.ts — wire shape is uniform across docs and
4
+ * sheet, only the `app` discriminator differs. When you change
5
+ * EmbedTransport.ts here, change it there too.
6
+ */
7
+ import { strict as assert } from 'node:assert';
8
+ import { test } from 'node:test';
9
+
10
+ import { EmbedTransport, isCasualEnvelope } from './';
11
+ import type { CasualEnvelope } from './';
12
+
13
+ function fakeHostWindow() {
14
+ let handler: ((ev: unknown) => void) | null = null;
15
+ return {
16
+ addEventListener(_type: string, h: EventListenerOrEventListenerObject) {
17
+ handler = h as (ev: unknown) => void;
18
+ },
19
+ removeEventListener() {
20
+ handler = null;
21
+ },
22
+ fire(ev: unknown) {
23
+ if (handler) handler(ev);
24
+ },
25
+ };
26
+ }
27
+
28
+ function fakeParent() {
29
+ const sent: Array<{ msg: unknown; origin: string; transfer?: Transferable[] }> = [];
30
+ return {
31
+ sent,
32
+ postMessage(msg: unknown, origin: string, transfer?: Transferable[]) {
33
+ sent.push({ msg, origin, transfer });
34
+ },
35
+ };
36
+ }
37
+
38
+ test('isCasualEnvelope accepts a well-formed sheet envelope', () => {
39
+ assert.equal(
40
+ isCasualEnvelope({
41
+ type: 'casual.hello',
42
+ app: 'sheet',
43
+ v: 1,
44
+ data: {},
45
+ } satisfies CasualEnvelope),
46
+ true,
47
+ );
48
+ });
49
+
50
+ test('isCasualEnvelope rejects foreign types', () => {
51
+ assert.equal(isCasualEnvelope({ type: 'other.hello', app: 'sheet', v: 1, data: {} }), false);
52
+ });
53
+
54
+ test('isCasualEnvelope rejects bad app values', () => {
55
+ assert.equal(isCasualEnvelope({ type: 'casual.hello', app: 'pdf', v: 1, data: {} }), false);
56
+ });
57
+
58
+ test('EmbedTransport drops messages from disallowed origins', async () => {
59
+ const host = fakeHostWindow();
60
+ const parent = fakeParent();
61
+ let called = false;
62
+ const transport = new EmbedTransport({
63
+ app: 'sheet',
64
+ hostOrigin: 'https://drive.example',
65
+ version: '1.0.0',
66
+ commit: 'abc',
67
+ capabilities: [],
68
+ parentWindow: parent,
69
+ hostWindow: host,
70
+ });
71
+ transport.on({
72
+ onHostHello: () => {
73
+ called = true;
74
+ },
75
+ });
76
+ host.fire({
77
+ origin: 'https://evil.example',
78
+ data: { type: 'casual.hello', app: 'sheet', v: 1, data: { capabilities: [] } },
79
+ });
80
+ await new Promise((r) => setTimeout(r, 0));
81
+ assert.equal(called, false);
82
+ });
83
+
84
+ test('EmbedTransport routes valid host.hello + replies with editor.ready', async () => {
85
+ const host = fakeHostWindow();
86
+ const parent = fakeParent();
87
+ let received: unknown = null;
88
+ const transport = new EmbedTransport({
89
+ app: 'sheet',
90
+ hostOrigin: 'https://drive.example',
91
+ version: '1.0.0',
92
+ commit: 'abc',
93
+ capabilities: [],
94
+ parentWindow: parent,
95
+ hostWindow: host,
96
+ });
97
+ transport.on({
98
+ onHostHello: (data) => {
99
+ received = data;
100
+ },
101
+ });
102
+ host.fire({
103
+ origin: 'https://drive.example',
104
+ data: {
105
+ type: 'casual.hello',
106
+ app: 'sheet',
107
+ v: 1,
108
+ data: { capabilities: ['saveDocument'] },
109
+ },
110
+ });
111
+ await new Promise((r) => setTimeout(r, 0));
112
+ assert.deepEqual(received, { capabilities: ['saveDocument'] });
113
+ const lastSent = parent.sent[parent.sent.length - 1].msg as CasualEnvelope;
114
+ assert.equal(lastSent.type, 'casual.ready');
115
+ assert.equal(lastSent.app, 'sheet');
116
+ });
117
+
118
+ test('sendHello emits an editor.hello envelope', () => {
119
+ const host = fakeHostWindow();
120
+ const parent = fakeParent();
121
+ const transport = new EmbedTransport({
122
+ app: 'sheet',
123
+ hostOrigin: 'https://drive.example',
124
+ version: '1.2.3',
125
+ commit: 'deadbee',
126
+ capabilities: ['save', 'load'],
127
+ parentWindow: parent,
128
+ hostWindow: host,
129
+ });
130
+ transport.sendHello();
131
+ assert.equal(parent.sent.length, 1);
132
+ const env = parent.sent[0].msg as CasualEnvelope;
133
+ assert.equal(env.type, 'casual.hello');
134
+ assert.equal((env.data as { version: string }).version, '1.2.3');
135
+ assert.deepEqual((env.data as { capabilities: string[] }).capabilities, ['save', 'load']);
136
+ });
137
+
138
+ test('destroy detaches the listener', () => {
139
+ const host = fakeHostWindow();
140
+ const transport = new EmbedTransport({
141
+ app: 'sheet',
142
+ hostOrigin: 'https://drive.example',
143
+ version: '1.0.0',
144
+ commit: 'abc',
145
+ capabilities: [],
146
+ parentWindow: fakeParent(),
147
+ hostWindow: host,
148
+ });
149
+ let called = false;
150
+ transport.on({
151
+ onHostHello: () => {
152
+ called = true;
153
+ },
154
+ });
155
+ transport.destroy();
156
+ host.fire({
157
+ origin: 'https://drive.example',
158
+ data: { type: 'casual.hello', app: 'sheet', v: 1, data: { capabilities: [] } },
159
+ });
160
+ assert.equal(called, false);
161
+ });
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Embed — iframe delivery surface.
3
+ *
4
+ * EmbedTransport bridges postMessage to React handlers. The
5
+ * envelope shapes (`./protocol.ts`) mirror the documentation
6
+ * contract in `docs/internal/13-iframe-protocol.md` and the
7
+ * signing payloads from `../signing/types.ts`.
8
+ */
9
+
10
+ export {
11
+ EmbedTransport,
12
+ type EmbedTransportHandlers,
13
+ type EmbedTransportOptions,
14
+ } from './EmbedTransport';
15
+ export {
16
+ isCasualEnvelope,
17
+ type CasualApp,
18
+ type CasualEnvelope,
19
+ type EditorHelloData,
20
+ type HostHelloData,
21
+ type LoadRequestData,
22
+ type LoadResponseData,
23
+ type LoadResponseDataOk,
24
+ type LoadResponseDataErr,
25
+ type SaveRequestData,
26
+ type SaveResponseData,
27
+ type SaveResponseDataOk,
28
+ type SaveResponseDataErr,
29
+ type SelectionChangedData,
30
+ type TelemetryEventData,
31
+ type LockLostData,
32
+ type CommandSetReadOnlyData,
33
+ type CommandSetThemeData,
34
+ type CommandSetLocaleData,
35
+ type SignatureRequestData,
36
+ type SignatureRequestAckData,
37
+ type SignatureFieldSignedData,
38
+ type SignatureCompleteData,
39
+ type SignatureCancelData,
40
+ } from './protocol';