@notionhq/custom-blocks-host 0.1.29 → 0.1.31

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 CHANGED
@@ -215,7 +215,6 @@ The returned handle exposes:
215
215
  - `setDataSources()`
216
216
  - `setCurrentUser()`
217
217
  - `refreshQuery()`
218
- - `post()`
219
218
  - `stop()`
220
219
 
221
220
  State updates are sent after `initResult.success`. Updates made earlier are held until
@@ -1,7 +1,6 @@
1
1
  import { type NotionContrastMode } from "./protocol/contrast.js";
2
2
  import type { NotionDataSourceId } from "./protocol/ids.js";
3
3
  import type { CustomBlockManifest } from "./protocol/manifest.js";
4
- import type { HostToSandboxMessage } from "./protocol/messages/hostToSandbox.js";
5
4
  import type { CustomBlockPage } from "./protocol/pages/page.js";
6
5
  import type { NotionParent } from "./protocol/parent.js";
7
6
  import type { NotionTheme } from "./protocol/theme.js";
@@ -42,7 +41,6 @@ export type CustomBlockHostOptions = {
42
41
  };
43
42
  export type CustomBlockHostHandle = {
44
43
  stop: () => void;
45
- post: (message: HostToSandboxMessage) => void;
46
44
  setTheme: (theme: NotionTheme) => void;
47
45
  setContrastMode: (contrastMode: NotionContrastMode) => void;
48
46
  setParent: (parent: NotionParent) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../src/createCustomBlockHost.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,kBAAkB,EACvB,MAAM,8CAA8C,CAAA;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AACjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AAavF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4DAA4D,CAAA;AAoBtG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAE9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAIhF,OAAO,KAAK,EACX,iCAAiC,EACjC,2BAA2B,EAC3B,MAAM,sBAAsB,CAAA;AAE7B,OAAO,KAAK,EACX,sBAAsB,EACtB,uBAAuB,EACvB,2BAA2B,EAC3B,MAAM,qBAAqB,CAAA;AAc5B,MAAM,MAAM,sBAAsB,GAAG;IACpC,MAAM,EAAE,iBAAiB,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,YAAY,EAAE,2BAA2B,CAAA;IACzC,QAAQ,EAAE,uBAAuB,CAAA;IACjC;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,mBAAmB,KAAK,IAAI,CAAA;IACpD,gGAAgG;IAChG,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QACvB,QAAQ,EAAE,mBAAmB,CAAA;QAC7B,aAAa,CAAC,EAAE,MAAM,CAAA;KACtB,KAAK,IAAI,CAAA;IACV;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAA;IACnD,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,2BAA2B,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;CAC1E,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IACnC,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,IAAI,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAC7C,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;IACtC,eAAe,EAAE,CAAC,YAAY,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC3D,SAAS,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAA;IACzC,OAAO,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAA;IACxC,cAAc,EAAE,CAAC,WAAW,EAAE,iCAAiC,KAAK,IAAI,CAAA;IACxE,cAAc,EAAE,CAAC,WAAW,EAAE,UAAU,KAAK,IAAI,CAAA;IACjD,YAAY,EAAE,CAAC,IAAI,EAAE;QAAE,YAAY,EAAE,kBAAkB,CAAA;KAAE,KAAK,IAAI,CAAA;CAClE,CAAA;AAED,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,sBAAsB,GAC7B,qBAAqB,CA+avB"}
1
+ {"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../src/createCustomBlockHost.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,kBAAkB,EACvB,MAAM,8CAA8C,CAAA;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AACjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AAmBvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAE9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAIhF,OAAO,KAAK,EACX,iCAAiC,EACjC,2BAA2B,EAC3B,MAAM,sBAAsB,CAAA;AAE7B,OAAO,KAAK,EACX,sBAAsB,EACtB,uBAAuB,EACvB,2BAA2B,EAC3B,MAAM,qBAAqB,CAAA;AAe5B,MAAM,MAAM,sBAAsB,GAAG;IACpC,MAAM,EAAE,iBAAiB,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,YAAY,EAAE,2BAA2B,CAAA;IACzC,QAAQ,EAAE,uBAAuB,CAAA;IACjC;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,mBAAmB,KAAK,IAAI,CAAA;IACpD,gGAAgG;IAChG,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QACvB,QAAQ,EAAE,mBAAmB,CAAA;QAC7B,aAAa,CAAC,EAAE,MAAM,CAAA;KACtB,KAAK,IAAI,CAAA;IACV;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAA;IACnD,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,2BAA2B,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;CAC1E,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IACnC,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;IACtC,eAAe,EAAE,CAAC,YAAY,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC3D,SAAS,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAA;IACzC,OAAO,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAA;IACxC,cAAc,EAAE,CAAC,WAAW,EAAE,iCAAiC,KAAK,IAAI,CAAA;IACxE,cAAc,EAAE,CAAC,WAAW,EAAE,UAAU,KAAK,IAAI,CAAA;IACjD,YAAY,EAAE,CAAC,IAAI,EAAE;QAAE,YAAY,EAAE,kBAAkB,CAAA;KAAE,KAAK,IAAI,CAAA;CAClE,CAAA;AAyBD,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,sBAAsB,GAC7B,qBAAqB,CA4ZvB"}
@@ -8,6 +8,7 @@ import { MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION } from "./protocolVersion.js
8
8
  import { QuerySubscriptions, } from "./queries/querySubscriptions.js";
9
9
  import { validateQueryDataSourceFilter } from "./queries/validateQueryDataSourceFilter.js";
10
10
  import { validateQueryDataSourceSorts } from "./queries/validateQueryDataSourceSorts.js";
11
+ import { unreachable } from "./utils.js";
11
12
  // Bridge protocol version that introduced page: { type: "unavailable" }.
12
13
  // Earlier SDKs cannot represent this context, so the host reports context_unavailable.
13
14
  const BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE = 5;
@@ -36,15 +37,63 @@ export function createCustomBlockHost(options) {
36
37
  function emit(direction, payload) {
37
38
  onLog?.(direction, payload);
38
39
  }
39
- function post(message) {
40
- if (initializationController.status === "stopped") {
41
- // The parent page can stop this host. Do not send messages after the host stops.
42
- return;
40
+ function postToSandbox(message) {
41
+ const status = initializationController.status;
42
+ switch (status) {
43
+ case "waitingForConnect":
44
+ case "waitingForInitResult":
45
+ case "initialized":
46
+ case "error":
47
+ break;
48
+ // A stopped host must not send messages, including pending responses.
49
+ // Return from the function to skip delivery below.
50
+ case "stopped":
51
+ return;
52
+ default:
53
+ return unreachable(status);
43
54
  }
44
55
  const sandboxMessage = normalizeHostToSandboxMessage(message, sandboxBridgeProtocolVersion);
45
56
  emit("out", sandboxMessage);
46
57
  iframe.contentWindow?.postMessage(sandboxMessage, sandboxOrigin);
47
58
  }
59
+ function sendInitMessage(message) {
60
+ postToSandbox(message);
61
+ }
62
+ function sendNack(message) {
63
+ postToSandbox(message);
64
+ }
65
+ function sendInitializedMessage(message) {
66
+ const status = initializationController.status;
67
+ switch (status) {
68
+ case "initialized":
69
+ postToSandbox(message);
70
+ return;
71
+ // Send state updates and handler results only after initialization succeeds.
72
+ case "waitingForConnect":
73
+ case "waitingForInitResult":
74
+ case "error":
75
+ case "stopped":
76
+ return;
77
+ default:
78
+ unreachable(status);
79
+ }
80
+ }
81
+ /**
82
+ * Invokes a one-shot handler and posts its result with the request's ID.
83
+ * Each result type is named `<request type>Result` by the bridge protocol.
84
+ */
85
+ async function respondToRequest(message, handler) {
86
+ if (handler === undefined) {
87
+ return;
88
+ }
89
+ const response = await handler(message);
90
+ const result = {
91
+ type: `${message.type}Result`,
92
+ requestId: message.requestId,
93
+ ...response,
94
+ };
95
+ sendInitializedMessage(result);
96
+ }
48
97
  function postQueryDataSourceResult(args) {
49
98
  const message = args.response.status === "error"
50
99
  ? {
@@ -60,7 +109,7 @@ export function createCustomBlockHost(options) {
60
109
  items: args.response.items,
61
110
  hasMore: args.response.hasMore,
62
111
  };
63
- post(message);
112
+ sendInitializedMessage(message);
64
113
  }
65
114
  async function handleQuery(message, token = querySubscriptions.subscribe(message)) {
66
115
  const collectionSchema = getCollectionSchema(activeDataSources, message.dataSourceId);
@@ -83,111 +132,39 @@ export function createCustomBlockHost(options) {
83
132
  response,
84
133
  });
85
134
  }
86
- async function handleCreatePage(message) {
87
- if (handlers.createPage === undefined) {
88
- return;
89
- }
90
- const response = await handlers.createPage(message);
91
- const result = {
92
- type: "createPageResult",
93
- requestId: message.requestId,
94
- ...response,
95
- };
96
- post(result);
97
- }
98
- async function handleGetPage(message) {
99
- if (handlers.getPage === undefined) {
100
- return;
101
- }
102
- const response = await handlers.getPage(message);
103
- const result = {
104
- type: "getPageResult",
105
- requestId: message.requestId,
106
- ...response,
107
- };
108
- post(result);
109
- }
110
- async function handleGetUser(message) {
111
- if (handlers.getUser === undefined) {
112
- return;
113
- }
114
- const response = await handlers.getUser(message);
115
- const result = {
116
- type: "getUserResult",
117
- requestId: message.requestId,
118
- ...response,
119
- };
120
- post(result);
121
- }
122
- async function handleListUsers(message) {
123
- if (handlers.listUsers === undefined) {
124
- return;
125
- }
126
- const response = await handlers.listUsers(message);
127
- const result = {
128
- type: "listUsersResult",
129
- requestId: message.requestId,
130
- ...response,
131
- };
132
- post(result);
133
- }
134
- async function handleOpenPage(message) {
135
- if (handlers.openPage === undefined) {
136
- return;
137
- }
138
- const response = await handlers.openPage(message);
139
- const result = {
140
- type: "openPageResult",
141
- requestId: message.requestId,
142
- ...response,
143
- };
144
- post(result);
145
- }
146
- async function handleUpdatePage(message) {
147
- if (handlers.updatePage === undefined) {
148
- return;
149
- }
150
- const response = await handlers.updatePage(message);
151
- const result = {
152
- type: "updatePageResult",
153
- requestId: message.requestId,
154
- ...response,
155
- };
156
- post(result);
157
- }
158
135
  /**
159
136
  * Sends any pending host state changes to the sandbox. It is possible for these to change
160
137
  * between when the host sends `init` and when the sandbox responds with `initResult`. The host
161
- * does send any pending state changes until hearing back from the sandbox.
138
+ * does not send pending state changes until the sandbox responds.
162
139
  */
163
140
  function flushPendingHostStateChanges() {
164
141
  if (pendingThemeChange) {
165
142
  pendingThemeChange = false;
166
- post({ type: "themeChanged", theme });
143
+ sendInitializedMessage({ type: "themeChanged", theme });
167
144
  }
168
145
  if (pendingContrastModeChange) {
169
146
  pendingContrastModeChange = false;
170
- post({ type: "contrastModeChanged", contrastMode });
147
+ sendInitializedMessage({ type: "contrastModeChanged", contrastMode });
171
148
  }
172
149
  if (pendingParentChange) {
173
150
  pendingParentChange = false;
174
- post({ type: "parentChanged", parent });
151
+ sendInitializedMessage({ type: "parentChanged", parent });
175
152
  }
176
153
  if (pendingPageChange) {
177
154
  pendingPageChange = false;
178
- post({ type: "pageChanged", page });
155
+ sendInitializedMessage({ type: "pageChanged", page });
179
156
  }
180
157
  if (pendingDataSourcesChange !== undefined) {
181
158
  const nextDataSources = pendingDataSourcesChange;
182
159
  pendingDataSourcesChange = undefined;
183
- post({
160
+ sendInitializedMessage({
184
161
  type: "dataSourcesChanged",
185
162
  dataSources: nextDataSources,
186
163
  });
187
164
  }
188
165
  if (pendingCurrentUserChange) {
189
166
  pendingCurrentUserChange = false;
190
- post({ type: "currentUserChanged", currentUser });
167
+ sendInitializedMessage({ type: "currentUserChanged", currentUser });
191
168
  }
192
169
  }
193
170
  const initializationController = createInitializationController({
@@ -232,7 +209,7 @@ export function createCustomBlockHost(options) {
232
209
  dataSources: resolvedDataSources,
233
210
  };
234
211
  },
235
- post,
212
+ sendInitMessage,
236
213
  warn: payload => emit("warn", payload),
237
214
  flushPendingHostStateChanges,
238
215
  minBridgeProtocolVersion,
@@ -251,19 +228,19 @@ export function createCustomBlockHost(options) {
251
228
  sandboxOrigin,
252
229
  getInitStatus: () => initializationController.status,
253
230
  emit,
254
- post,
231
+ sendNack,
255
232
  onInvalidConnect: initializationController.handleInvalidConnect,
256
233
  onInvalidInitResult: initializationController.handleInvalidInitResult,
257
234
  onConnect: initializationController.handleConnect,
258
235
  onInitResult: initializationController.handleInitResult,
259
236
  onQueryDataSource: handleQuery,
260
237
  onUnsubscribeDataSourceQuery: message => querySubscriptions.unsubscribe(message.subscriptionId),
261
- onCreatePage: handleCreatePage,
262
- onGetPage: handleGetPage,
263
- onOpenPage: handleOpenPage,
264
- onGetUser: handleGetUser,
265
- onListUsers: handleListUsers,
266
- onUpdatePage: handleUpdatePage,
238
+ onCreatePage: message => respondToRequest(message, handlers.createPage),
239
+ onGetPage: message => respondToRequest(message, handlers.getPage),
240
+ onOpenPage: message => respondToRequest(message, handlers.openPage),
241
+ onGetUser: message => respondToRequest(message, handlers.getUser),
242
+ onListUsers: message => respondToRequest(message, handlers.listUsers),
243
+ onUpdatePage: message => respondToRequest(message, handlers.updatePage),
267
244
  onResize: message => handlers.resize?.(message),
268
245
  });
269
246
  window.addEventListener("message", onMessage);
@@ -281,21 +258,20 @@ export function createCustomBlockHost(options) {
281
258
  }
282
259
  return {
283
260
  stop,
284
- post,
285
261
  setTheme(nextTheme) {
286
262
  theme = nextTheme;
287
- if (initializationController.status !== "success") {
263
+ if (initializationController.status !== "initialized") {
288
264
  if (initializationController.status === "waitingForInitResult") {
289
265
  pendingThemeChange = true;
290
266
  }
291
267
  return;
292
268
  }
293
269
  const message = { type: "themeChanged", theme };
294
- post(message);
270
+ sendInitializedMessage(message);
295
271
  },
296
272
  setContrastMode(nextContrastMode) {
297
273
  contrastMode = nextContrastMode;
298
- if (initializationController.status !== "success") {
274
+ if (initializationController.status !== "initialized") {
299
275
  if (initializationController.status === "waitingForInitResult") {
300
276
  pendingContrastModeChange = true;
301
277
  }
@@ -305,18 +281,18 @@ export function createCustomBlockHost(options) {
305
281
  type: "contrastModeChanged",
306
282
  contrastMode,
307
283
  };
308
- post(message);
284
+ sendInitializedMessage(message);
309
285
  },
310
286
  setParent(nextParent) {
311
287
  parent = nextParent;
312
- if (initializationController.status !== "success") {
288
+ if (initializationController.status !== "initialized") {
313
289
  if (initializationController.status === "waitingForInitResult") {
314
290
  pendingParentChange = true;
315
291
  }
316
292
  return;
317
293
  }
318
294
  const message = { type: "parentChanged", parent };
319
- post(message);
295
+ sendInitializedMessage(message);
320
296
  },
321
297
  setPage(nextPage) {
322
298
  page = nextPage;
@@ -324,18 +300,18 @@ export function createCustomBlockHost(options) {
324
300
  sandboxBridgeProtocolVersion <
325
301
  BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE &&
326
302
  (initializationController.status === "waitingForInitResult" ||
327
- initializationController.status === "success")) {
303
+ initializationController.status === "initialized")) {
328
304
  stopWithFailure(initErrorForFailureReason("context_unavailable"));
329
305
  return;
330
306
  }
331
- if (initializationController.status !== "success") {
307
+ if (initializationController.status !== "initialized") {
332
308
  if (initializationController.status === "waitingForInitResult") {
333
309
  pendingPageChange = true;
334
310
  }
335
311
  return;
336
312
  }
337
313
  const message = { type: "pageChanged", page };
338
- post(message);
314
+ sendInitializedMessage(message);
339
315
  },
340
316
  setDataSources(nextDataSources) {
341
317
  if (initializationController.hasConnectedSandbox &&
@@ -349,7 +325,7 @@ export function createCustomBlockHost(options) {
349
325
  }
350
326
  dataSources = nextDataSources;
351
327
  activeDataSources = nextDataSources;
352
- if (initializationController.status !== "success") {
328
+ if (initializationController.status !== "initialized") {
353
329
  if (initializationController.status === "waitingForInitResult") {
354
330
  pendingDataSourcesChange = nextDataSources;
355
331
  }
@@ -359,20 +335,20 @@ export function createCustomBlockHost(options) {
359
335
  type: "dataSourcesChanged",
360
336
  dataSources: nextDataSources,
361
337
  };
362
- post(message);
338
+ sendInitializedMessage(message);
363
339
  },
364
340
  setCurrentUser(nextCurrentUser) {
365
341
  currentUser = nextCurrentUser;
366
- if (initializationController.status !== "success") {
342
+ if (initializationController.status !== "initialized") {
367
343
  if (initializationController.status === "waitingForInitResult") {
368
344
  pendingCurrentUserChange = true;
369
345
  }
370
346
  return;
371
347
  }
372
- post({ type: "currentUserChanged", currentUser });
348
+ sendInitializedMessage({ type: "currentUserChanged", currentUser });
373
349
  },
374
350
  refreshQuery({ dataSourceId }) {
375
- if (initializationController.status !== "success") {
351
+ if (initializationController.status !== "initialized") {
376
352
  return;
377
353
  }
378
354
  const refreshes = querySubscriptions.refresh(dataSourceId);
@@ -2,8 +2,7 @@ import type { NotionContrastMode } from "../protocol/contrast.js";
2
2
  import type { NotionBlockId } from "../protocol/ids.js";
3
3
  import type { CustomBlockManifest } from "../protocol/manifest.js";
4
4
  import type { ConnectMessage } from "../protocol/messages/connect.js";
5
- import type { HostToSandboxMessage } from "../protocol/messages/hostToSandbox.js";
6
- import type { CustomBlockInitErrorInfo } from "../protocol/messages/init.js";
5
+ import type { CustomBlockInitErrorInfo, InitMessage } from "../protocol/messages/init.js";
7
6
  import type { InitResultMessage } from "../protocol/messages/initResult.js";
8
7
  import type { CustomBlockPage } from "../protocol/pages/page.js";
9
8
  import type { NotionParent } from "../protocol/parent.js";
@@ -30,7 +29,7 @@ export type InitializationControllerOptions = {
30
29
  blockId: NotionBlockId;
31
30
  getState: () => InitializationState;
32
31
  prepareInit: (connect: ConnectSuccessMessage) => InitializationPreparation;
33
- post: (message: HostToSandboxMessage) => void;
32
+ sendInitMessage: (message: InitMessage) => void;
34
33
  warn: (payload: unknown) => void;
35
34
  flushPendingHostStateChanges: () => void;
36
35
  minBridgeProtocolVersion: number;
@@ -1 +1 @@
1
- {"version":3,"file":"initializationController.d.ts","sourceRoot":"","sources":["../../src/lifecycle/initializationController.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"initializationController.d.ts","sourceRoot":"","sources":["../../src/lifecycle/initializationController.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import { isValidBridgeProtocolVersion } from "./protocolVersion.js";
3
3
  const DEFAULT_NO_CONNECT_TIMEOUT_MS = 5_000;
4
4
  const DEFAULT_NO_INIT_RESULT_TIMEOUT_MS = 5_000;
5
5
  export function createInitializationController(options) {
6
- const { blockId, getState, prepareInit, post, warn, flushPendingHostStateChanges, minBridgeProtocolVersion, noConnectTimeoutMs = DEFAULT_NO_CONNECT_TIMEOUT_MS, noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS, stopHostWithFailure, onInitResult, onFailure, } = options;
6
+ const { blockId, getState, prepareInit, sendInitMessage, warn, flushPendingHostStateChanges, minBridgeProtocolVersion, noConnectTimeoutMs = DEFAULT_NO_CONNECT_TIMEOUT_MS, noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS, stopHostWithFailure, onInitResult, onFailure, } = options;
7
7
  let initStatus = "waitingForConnect";
8
8
  let noConnectTimeoutId;
9
9
  let noInitResultTimeoutId;
@@ -30,7 +30,7 @@ export function createInitializationController(options) {
30
30
  initStatus = "error";
31
31
  activeInitializationId = initializationId;
32
32
  clearNoConnectTimeout();
33
- post({
33
+ sendInitMessage({
34
34
  type: "init",
35
35
  initializationId,
36
36
  status: "error",
@@ -38,11 +38,16 @@ export function createInitializationController(options) {
38
38
  });
39
39
  onFailure?.(error);
40
40
  }
41
- function sendInit(connect) {
41
+ function prepareAndSendInit(connect) {
42
42
  if (initStatus !== "waitingForConnect") {
43
43
  return false;
44
44
  }
45
45
  const preparation = prepareInit(connect);
46
+ // The embedder can stop the host when it receives the manifest.
47
+ // If that happens, do not continue initialization or start its timer.
48
+ if (initStatus !== "waitingForConnect") {
49
+ return false;
50
+ }
46
51
  if (preparation.status === "error") {
47
52
  sendInitError({
48
53
  initializationId: connect.initializationId,
@@ -67,7 +72,7 @@ export function createInitializationController(options) {
67
72
  dataSources: preparation.dataSources,
68
73
  currentUser: state.currentUser,
69
74
  };
70
- post(message);
75
+ sendInitMessage(message);
71
76
  noInitResultTimeoutId = window.setTimeout(() => {
72
77
  if (initStatus !== "waitingForInitResult") {
73
78
  return;
@@ -119,7 +124,7 @@ export function createInitializationController(options) {
119
124
  });
120
125
  return;
121
126
  }
122
- if (sendInit(message)) {
127
+ if (prepareAndSendInit(message)) {
123
128
  hasConnectedSandbox = true;
124
129
  }
125
130
  }
@@ -204,7 +209,7 @@ export function createInitializationController(options) {
204
209
  }
205
210
  clearNoInitResultTimeout();
206
211
  if (message.status === "success") {
207
- initStatus = "success";
212
+ initStatus = "initialized";
208
213
  flushPendingHostStateChanges();
209
214
  }
210
215
  else {
@@ -17,7 +17,7 @@ export type InitStatus =
17
17
  /** The host is waiting for a matching `initResult` after it sends `init`. */
18
18
  | "waitingForInitResult"
19
19
  /** The host is initialized and can send live updates. */
20
- | "success"
20
+ | "initialized"
21
21
  /** The host is in an error state after an initialization failure or timeout. */
22
22
  | "error"
23
23
  /** The host is stopped and no longer sends or accepts sandbox messages. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lifecycle/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAA;AACtF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4DAA4D,CAAA;AAC1G,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAA;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAEhF,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAC1C,cAAc,EACd;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CACrB,CAAA;AAED,gEAAgE;AAChE,MAAM,MAAM,UAAU;AACrB,qEAAqE;AACnE,mBAAmB;AACrB,6EAA6E;GAC3E,sBAAsB;AACxB,yDAAyD;GACvD,SAAS;AACX,gFAAgF;GAC9E,OAAO;AACT,2EAA2E;GACzE,SAAS,CAAA;AAEZ,MAAM,MAAM,iCAAiC,GAAG;IAC/C,QAAQ,EAAE,wBAAwB,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,iCAAiC,GAC1C,iCAAiC,GACjC,CAAC,CAAC,OAAO,EAAE,qBAAqB,KAAK,iCAAiC,CAAC,CAAA;AAE1E,MAAM,MAAM,2BAA2B,GAAG;IACzC,KAAK,EAAE,WAAW,CAAA;IAClB;;;OAGG;IACH,YAAY,CAAC,EAAE,kBAAkB,CAAA;IACjC,OAAO,EAAE,aAAa,CAAA;IACtB,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,eAAe,CAAA;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,mBAAmB,CAAA;IAC9B,WAAW,EAAE,iCAAiC,CAAA;IAC9C,WAAW,EAAE,UAAU,CAAA;CACvB,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lifecycle/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAA;AACtF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4DAA4D,CAAA;AAC1G,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAA;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAEhF,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAC1C,cAAc,EACd;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CACrB,CAAA;AAED,gEAAgE;AAChE,MAAM,MAAM,UAAU;AACrB,qEAAqE;AACnE,mBAAmB;AACrB,6EAA6E;GAC3E,sBAAsB;AACxB,yDAAyD;GACvD,aAAa;AACf,gFAAgF;GAC9E,OAAO;AACT,2EAA2E;GACzE,SAAS,CAAA;AAEZ,MAAM,MAAM,iCAAiC,GAAG;IAC/C,QAAQ,EAAE,wBAAwB,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,iCAAiC,GAC1C,iCAAiC,GACjC,CAAC,CAAC,OAAO,EAAE,qBAAqB,KAAK,iCAAiC,CAAC,CAAA;AAE1E,MAAM,MAAM,2BAA2B,GAAG;IACzC,KAAK,EAAE,WAAW,CAAA;IAClB;;;OAGG;IACH,YAAY,CAAC,EAAE,kBAAkB,CAAA;IACjC,OAAO,EAAE,aAAa,CAAA;IACtB,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,eAAe,CAAA;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,mBAAmB,CAAA;IAC9B,WAAW,EAAE,iCAAiC,CAAA;IAC9C,WAAW,EAAE,UAAU,CAAA;CACvB,CAAA"}
@@ -2,8 +2,8 @@ import type { ConnectMessage } from "../protocol/messages/connect.js";
2
2
  import type { CreatePageMessage } from "../protocol/messages/createPage.js";
3
3
  import type { GetPageMessage } from "../protocol/messages/getPage.js";
4
4
  import type { GetUserMessage } from "../protocol/messages/getUser.js";
5
- import type { HostToSandboxMessage } from "../protocol/messages/hostToSandbox.js";
6
5
  import type { InitResultMessage } from "../protocol/messages/initResult.js";
6
+ import type { InvalidSandboxMessage } from "../protocol/messages/invalidSandboxMessage.js";
7
7
  import type { ListUsersMessage } from "../protocol/messages/listUsers.js";
8
8
  import type { OpenPageMessage } from "../protocol/messages/openPage.js";
9
9
  import type { QueryDataSourceMessage } from "../protocol/messages/queryDataSource.js";
@@ -18,7 +18,7 @@ export type SandboxMessageRouterOptions = {
18
18
  sandboxOrigin: string;
19
19
  getInitStatus: () => InitStatus;
20
20
  emit: (direction: CustomBlockHostLogDirection, payload: unknown) => void;
21
- post: (message: HostToSandboxMessage) => void;
21
+ sendNack: (message: InvalidSandboxMessage) => void;
22
22
  onInvalidConnect: InitializationController["handleInvalidConnect"];
23
23
  onInvalidInitResult: InitializationController["handleInvalidInitResult"];
24
24
  onConnect: (message: ConnectMessage) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"sandboxMessageRouter.d.ts","sourceRoot":"","sources":["../../src/messages/sandboxMessageRouter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4DAA4D,CAAA;AACtG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAEhG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wDAAwD,CAAA;AAC9F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uDAAuD,CAAA;AAC5F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8DAA8D,CAAA;AAC1G,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qDAAqD,CAAA;AAExF,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,yEAAyE,CAAA;AAChI,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAGhG,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAA;AACxF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAGvD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAA;AAY7D,MAAM,MAAM,2BAA2B,GAAG;IACzC,MAAM,EAAE,iBAAiB,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,UAAU,CAAA;IAC/B,IAAI,EAAE,CAAC,SAAS,EAAE,2BAA2B,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACxE,IAAI,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAC7C,gBAAgB,EAAE,wBAAwB,CAAC,sBAAsB,CAAC,CAAA;IAClE,mBAAmB,EAAE,wBAAwB,CAAC,yBAAyB,CAAC,CAAA;IACxE,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;IAC5C,YAAY,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;IAClD,iBAAiB,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5E,4BAA4B,EAAE,CAC7B,OAAO,EAAE,iCAAiC,KACtC,IAAI,CAAA;IACT,YAAY,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClE,UAAU,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9D,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5D,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5D,WAAW,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAChE,YAAY,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClE,QAAQ,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;CAC1C,CAAA;AAED,wBAAgB,0BAA0B,CACzC,OAAO,EAAE,2BAA2B,GAClC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAsI/B"}
1
+ {"version":3,"file":"sandboxMessageRouter.d.ts","sourceRoot":"","sources":["../../src/messages/sandboxMessageRouter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oEAAoE,CAAA;AAE/G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wDAAwD,CAAA;AAC9F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uDAAuD,CAAA;AAC5F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8DAA8D,CAAA;AAC1G,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qDAAqD,CAAA;AAExF,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,yEAAyE,CAAA;AAChI,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAGhG,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAA;AACxF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAGvD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAA;AAY7D,MAAM,MAAM,2BAA2B,GAAG;IACzC,MAAM,EAAE,iBAAiB,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,UAAU,CAAA;IAC/B,IAAI,EAAE,CAAC,SAAS,EAAE,2BAA2B,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACxE,QAAQ,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAA;IAClD,gBAAgB,EAAE,wBAAwB,CAAC,sBAAsB,CAAC,CAAA;IAClE,mBAAmB,EAAE,wBAAwB,CAAC,yBAAyB,CAAC,CAAA;IACxE,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;IAC5C,YAAY,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;IAClD,iBAAiB,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5E,4BAA4B,EAAE,CAC7B,OAAO,EAAE,iCAAiC,KACtC,IAAI,CAAA;IACT,YAAY,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClE,UAAU,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9D,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5D,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5D,WAAW,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAChE,YAAY,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClE,QAAQ,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;CAC1C,CAAA;AAED,wBAAgB,0BAA0B,CACzC,OAAO,EAAE,2BAA2B,GAClC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAsI/B"}
@@ -13,7 +13,7 @@ const bridgeProtocolVersionSchema = v.object({
13
13
  bridgeProtocolVersion: v.union([v.number(), v.nan()]),
14
14
  });
15
15
  export function createSandboxMessageRouter(options) {
16
- const { iframe, sandboxOrigin, getInitStatus, emit, post, onInvalidConnect, onInvalidInitResult, onConnect, onInitResult, onQueryDataSource, onUnsubscribeDataSourceQuery, onCreatePage, onGetPage, onOpenPage, onGetUser, onListUsers, onUpdatePage, onResize, } = options;
16
+ const { iframe, sandboxOrigin, getInitStatus, emit, sendNack, onInvalidConnect, onInvalidInitResult, onConnect, onInitResult, onQueryDataSource, onUnsubscribeDataSourceQuery, onCreatePage, onGetPage, onOpenPage, onGetUser, onListUsers, onUpdatePage, onResize, } = options;
17
17
  function onInvalidSandboxMessage(data, issues) {
18
18
  emit("warn", {
19
19
  message: "Ignored unsupported sandbox message",
@@ -47,9 +47,9 @@ export function createSandboxMessageRouter(options) {
47
47
  error: initErrorForFailureReason("invalid_init_result_payload"),
48
48
  });
49
49
  }
50
- // Other messages get NACKed indicating they were invalid and skipped
51
- if (response.nack !== undefined) {
52
- post(response.nack);
50
+ // Other messages get NACKed indicating they were invalid and skipped.
51
+ if (response.nack !== undefined && getInitStatus() !== "stopped") {
52
+ sendNack(response.nack);
53
53
  }
54
54
  }
55
55
  return function onMessage(event) {
@@ -67,7 +67,7 @@ export function createSandboxMessageRouter(options) {
67
67
  return;
68
68
  }
69
69
  const message = parsed.output;
70
- if (!isInitHandshakeMessage(message) && getInitStatus() !== "success") {
70
+ if (!isInitHandshakeMessage(message) && getInitStatus() !== "initialized") {
71
71
  emit("warn", {
72
72
  message: `Ignored ${message.type} before initialization completed`,
73
73
  payload: message,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notionhq/custom-blocks-host",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "description": "Reusable host runtime for Notion custom blocks.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -6,27 +6,14 @@ import type { NotionCollectionSchema } from "@notionhq/custom-blocks-protocol/da
6
6
  import type { NotionDataSourceId } from "@notionhq/custom-blocks-protocol/ids.js"
7
7
  import type { CustomBlockManifest } from "@notionhq/custom-blocks-protocol/manifest.js"
8
8
  import type { ContrastModeChangedMessage } from "@notionhq/custom-blocks-protocol/messages/contrastModeChanged.js"
9
- import type { CreatePageMessage } from "@notionhq/custom-blocks-protocol/messages/createPage.js"
10
- import type { CreatePageResultMessage } from "@notionhq/custom-blocks-protocol/messages/createPageResult.js"
11
9
  import type { DataSourcesChangedMessage } from "@notionhq/custom-blocks-protocol/messages/dataSourcesChanged.js"
12
- import type {
13
- GetPageMessage,
14
- GetPageResultMessage,
15
- } from "@notionhq/custom-blocks-protocol/messages/getPage.js"
16
- import type {
17
- GetUserMessage,
18
- GetUserResultMessage,
19
- } from "@notionhq/custom-blocks-protocol/messages/getUser.js"
20
10
  import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
21
- import type { CustomBlockInitErrorCode } from "@notionhq/custom-blocks-protocol/messages/init.js"
22
- import type {
23
- ListUsersMessage,
24
- ListUsersResultMessage,
25
- } from "@notionhq/custom-blocks-protocol/messages/listUsers.js"
26
11
  import type {
27
- OpenPageMessage,
28
- OpenPageResultMessage,
29
- } from "@notionhq/custom-blocks-protocol/messages/openPage.js"
12
+ CustomBlockInitErrorCode,
13
+ InitMessage,
14
+ } from "@notionhq/custom-blocks-protocol/messages/init.js"
15
+ import type { InvalidSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/invalidSandboxMessage.js"
16
+ import type { BridgeMessagePayload } from "@notionhq/custom-blocks-protocol/messages/messagePayload.js"
30
17
  import type { PageChangedMessage } from "@notionhq/custom-blocks-protocol/messages/pageChanged.js"
31
18
  import type { ParentChangedMessage } from "@notionhq/custom-blocks-protocol/messages/parentChanged.js"
32
19
  import type { QueryDataSourceMessage } from "@notionhq/custom-blocks-protocol/messages/queryDataSource.js"
@@ -34,9 +21,8 @@ import type {
34
21
  CustomBlockQueryDataSourceErrorInfo,
35
22
  QueryDataSourceResultMessage,
36
23
  } from "@notionhq/custom-blocks-protocol/messages/queryDataSourceResult.js"
24
+ import type { SandboxToHostMessage } from "@notionhq/custom-blocks-protocol/messages/sandboxToHost.js"
37
25
  import type { ThemeChangedMessage } from "@notionhq/custom-blocks-protocol/messages/themeChanged.js"
38
- import type { UpdatePageMessage } from "@notionhq/custom-blocks-protocol/messages/updatePage.js"
39
- import type { UpdatePageResultMessage } from "@notionhq/custom-blocks-protocol/messages/updatePageResult.js"
40
26
  import type { CustomBlockPage } from "@notionhq/custom-blocks-protocol/pages/page.js"
41
27
  import type { NotionParent } from "@notionhq/custom-blocks-protocol/parent.js"
42
28
  import { LATEST_BRIDGE_PROTOCOL_VERSION } from "@notionhq/custom-blocks-protocol/protocolVersion.js"
@@ -63,6 +49,7 @@ import {
63
49
  import type { CustomBlockHostQueryDataSourceResult } from "./queries/types.js"
64
50
  import { validateQueryDataSourceFilter } from "./queries/validateQueryDataSourceFilter.js"
65
51
  import { validateQueryDataSourceSorts } from "./queries/validateQueryDataSourceSorts.js"
52
+ import { unreachable } from "./utils.js"
66
53
 
67
54
  // Bridge protocol version that introduced page: { type: "unavailable" }.
68
55
  // Earlier SDKs cannot represent this context, so the host reports context_unavailable.
@@ -103,7 +90,6 @@ export type CustomBlockHostOptions = {
103
90
 
104
91
  export type CustomBlockHostHandle = {
105
92
  stop: () => void
106
- post: (message: HostToSandboxMessage) => void
107
93
  setTheme: (theme: NotionTheme) => void
108
94
  setContrastMode: (contrastMode: NotionContrastMode) => void
109
95
  setParent: (parent: NotionParent) => void
@@ -113,6 +99,29 @@ export type CustomBlockHostHandle = {
113
99
  refreshQuery: (args: { dataSourceId: NotionDataSourceId }) => void
114
100
  }
115
101
 
102
+ /**
103
+ * A request that expects one result correlated by `requestId`.
104
+ * Query subscriptions use `subscriptionId` and can receive repeated results.
105
+ */
106
+ type OneShotRequest = Extract<SandboxToHostMessage, { requestId: string }>
107
+
108
+ /** The matching host result message, named `<request type>Result` by the protocol. */
109
+ type ResultForRequest<Request extends OneShotRequest> = Extract<
110
+ HostToSandboxMessage,
111
+ { type: `${Request["type"]}Result` }
112
+ >
113
+
114
+ /** The result's success or error payload, without the bridge envelope fields. */
115
+ type ResultPayloadForRequest<Request extends OneShotRequest> =
116
+ BridgeMessagePayload<ResultForRequest<Request>>
117
+
118
+ /** Returns one result payload. `respondToRequest` adds the message type and request ID. */
119
+ type OneShotHandler<Request extends OneShotRequest> = (
120
+ message: Request,
121
+ ) =>
122
+ | ResultPayloadForRequest<Request>
123
+ | Promise<ResultPayloadForRequest<Request>>
124
+
116
125
  export function createCustomBlockHost(
117
126
  options: CustomBlockHostOptions,
118
127
  ): CustomBlockHostHandle {
@@ -158,10 +167,20 @@ export function createCustomBlockHost(
158
167
  onLog?.(direction, payload)
159
168
  }
160
169
 
161
- function post(message: HostToSandboxMessage) {
162
- if (initializationController.status === "stopped") {
163
- // The parent page can stop this host. Do not send messages after the host stops.
164
- return
170
+ function postToSandbox(message: HostToSandboxMessage) {
171
+ const status = initializationController.status
172
+ switch (status) {
173
+ case "waitingForConnect":
174
+ case "waitingForInitResult":
175
+ case "initialized":
176
+ case "error":
177
+ break
178
+ // A stopped host must not send messages, including pending responses.
179
+ // Return from the function to skip delivery below.
180
+ case "stopped":
181
+ return
182
+ default:
183
+ return unreachable(status)
165
184
  }
166
185
  const sandboxMessage = normalizeHostToSandboxMessage(
167
186
  message,
@@ -171,6 +190,56 @@ export function createCustomBlockHost(
171
190
  iframe.contentWindow?.postMessage(sandboxMessage, sandboxOrigin)
172
191
  }
173
192
 
193
+ function sendInitMessage(message: InitMessage) {
194
+ postToSandbox(message)
195
+ }
196
+
197
+ function sendNack(message: InvalidSandboxMessage) {
198
+ postToSandbox(message)
199
+ }
200
+
201
+ function sendInitializedMessage(
202
+ message: Exclude<
203
+ HostToSandboxMessage,
204
+ { type: "init" | "invalidSandboxMessage" }
205
+ >,
206
+ ) {
207
+ const status = initializationController.status
208
+ switch (status) {
209
+ case "initialized":
210
+ postToSandbox(message)
211
+ return
212
+ // Send state updates and handler results only after initialization succeeds.
213
+ case "waitingForConnect":
214
+ case "waitingForInitResult":
215
+ case "error":
216
+ case "stopped":
217
+ return
218
+ default:
219
+ unreachable(status)
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Invokes a one-shot handler and posts its result with the request's ID.
225
+ * Each result type is named `<request type>Result` by the bridge protocol.
226
+ */
227
+ async function respondToRequest<Request extends OneShotRequest>(
228
+ message: Request,
229
+ handler: OneShotHandler<Request> | undefined,
230
+ ) {
231
+ if (handler === undefined) {
232
+ return
233
+ }
234
+ const response = await handler(message)
235
+ const result = {
236
+ type: `${message.type}Result` as const,
237
+ requestId: message.requestId,
238
+ ...response,
239
+ } as ResultForRequest<OneShotRequest>
240
+ sendInitializedMessage(result)
241
+ }
242
+
174
243
  function postQueryDataSourceResult(args: {
175
244
  subscriptionId: string
176
245
  response: CustomBlockHostQueryDataSourceResult
@@ -190,7 +259,7 @@ export function createCustomBlockHost(
190
259
  items: args.response.items,
191
260
  hasMore: args.response.hasMore,
192
261
  }
193
- post(message)
262
+ sendInitializedMessage(message)
194
263
  }
195
264
 
196
265
  async function handleQuery(
@@ -221,117 +290,39 @@ export function createCustomBlockHost(
221
290
  })
222
291
  }
223
292
 
224
- async function handleCreatePage(message: CreatePageMessage) {
225
- if (handlers.createPage === undefined) {
226
- return
227
- }
228
- const response = await handlers.createPage(message)
229
- const result: CreatePageResultMessage = {
230
- type: "createPageResult",
231
- requestId: message.requestId,
232
- ...response,
233
- }
234
- post(result)
235
- }
236
-
237
- async function handleGetPage(message: GetPageMessage) {
238
- if (handlers.getPage === undefined) {
239
- return
240
- }
241
- const response = await handlers.getPage(message)
242
- const result: GetPageResultMessage = {
243
- type: "getPageResult",
244
- requestId: message.requestId,
245
- ...response,
246
- }
247
- post(result)
248
- }
249
-
250
- async function handleGetUser(message: GetUserMessage) {
251
- if (handlers.getUser === undefined) {
252
- return
253
- }
254
- const response = await handlers.getUser(message)
255
- const result: GetUserResultMessage = {
256
- type: "getUserResult",
257
- requestId: message.requestId,
258
- ...response,
259
- }
260
- post(result)
261
- }
262
-
263
- async function handleListUsers(message: ListUsersMessage) {
264
- if (handlers.listUsers === undefined) {
265
- return
266
- }
267
- const response = await handlers.listUsers(message)
268
- const result: ListUsersResultMessage = {
269
- type: "listUsersResult",
270
- requestId: message.requestId,
271
- ...response,
272
- }
273
- post(result)
274
- }
275
-
276
- async function handleOpenPage(message: OpenPageMessage) {
277
- if (handlers.openPage === undefined) {
278
- return
279
- }
280
- const response = await handlers.openPage(message)
281
- const result: OpenPageResultMessage = {
282
- type: "openPageResult",
283
- requestId: message.requestId,
284
- ...response,
285
- }
286
- post(result)
287
- }
288
-
289
- async function handleUpdatePage(message: UpdatePageMessage) {
290
- if (handlers.updatePage === undefined) {
291
- return
292
- }
293
- const response = await handlers.updatePage(message)
294
- const result: UpdatePageResultMessage = {
295
- type: "updatePageResult",
296
- requestId: message.requestId,
297
- ...response,
298
- }
299
- post(result)
300
- }
301
-
302
293
  /**
303
294
  * Sends any pending host state changes to the sandbox. It is possible for these to change
304
295
  * between when the host sends `init` and when the sandbox responds with `initResult`. The host
305
- * does send any pending state changes until hearing back from the sandbox.
296
+ * does not send pending state changes until the sandbox responds.
306
297
  */
307
298
  function flushPendingHostStateChanges() {
308
299
  if (pendingThemeChange) {
309
300
  pendingThemeChange = false
310
- post({ type: "themeChanged", theme })
301
+ sendInitializedMessage({ type: "themeChanged", theme })
311
302
  }
312
303
  if (pendingContrastModeChange) {
313
304
  pendingContrastModeChange = false
314
- post({ type: "contrastModeChanged", contrastMode })
305
+ sendInitializedMessage({ type: "contrastModeChanged", contrastMode })
315
306
  }
316
307
  if (pendingParentChange) {
317
308
  pendingParentChange = false
318
- post({ type: "parentChanged", parent })
309
+ sendInitializedMessage({ type: "parentChanged", parent })
319
310
  }
320
311
  if (pendingPageChange) {
321
312
  pendingPageChange = false
322
- post({ type: "pageChanged", page })
313
+ sendInitializedMessage({ type: "pageChanged", page })
323
314
  }
324
315
  if (pendingDataSourcesChange !== undefined) {
325
316
  const nextDataSources = pendingDataSourcesChange
326
317
  pendingDataSourcesChange = undefined
327
- post({
318
+ sendInitializedMessage({
328
319
  type: "dataSourcesChanged",
329
320
  dataSources: nextDataSources,
330
321
  })
331
322
  }
332
323
  if (pendingCurrentUserChange) {
333
324
  pendingCurrentUserChange = false
334
- post({ type: "currentUserChanged", currentUser })
325
+ sendInitializedMessage({ type: "currentUserChanged", currentUser })
335
326
  }
336
327
  }
337
328
 
@@ -383,7 +374,7 @@ export function createCustomBlockHost(
383
374
  dataSources: resolvedDataSources,
384
375
  }
385
376
  },
386
- post,
377
+ sendInitMessage,
387
378
  warn: payload => emit("warn", payload),
388
379
  flushPendingHostStateChanges,
389
380
  minBridgeProtocolVersion,
@@ -403,7 +394,7 @@ export function createCustomBlockHost(
403
394
  sandboxOrigin,
404
395
  getInitStatus: () => initializationController.status,
405
396
  emit,
406
- post,
397
+ sendNack,
407
398
  onInvalidConnect: initializationController.handleInvalidConnect,
408
399
  onInvalidInitResult: initializationController.handleInvalidInitResult,
409
400
  onConnect: initializationController.handleConnect,
@@ -411,12 +402,12 @@ export function createCustomBlockHost(
411
402
  onQueryDataSource: handleQuery,
412
403
  onUnsubscribeDataSourceQuery: message =>
413
404
  querySubscriptions.unsubscribe(message.subscriptionId),
414
- onCreatePage: handleCreatePage,
415
- onGetPage: handleGetPage,
416
- onOpenPage: handleOpenPage,
417
- onGetUser: handleGetUser,
418
- onListUsers: handleListUsers,
419
- onUpdatePage: handleUpdatePage,
405
+ onCreatePage: message => respondToRequest(message, handlers.createPage),
406
+ onGetPage: message => respondToRequest(message, handlers.getPage),
407
+ onOpenPage: message => respondToRequest(message, handlers.openPage),
408
+ onGetUser: message => respondToRequest(message, handlers.getUser),
409
+ onListUsers: message => respondToRequest(message, handlers.listUsers),
410
+ onUpdatePage: message => respondToRequest(message, handlers.updatePage),
420
411
  onResize: message => handlers.resize?.(message),
421
412
  })
422
413
 
@@ -438,21 +429,20 @@ export function createCustomBlockHost(
438
429
 
439
430
  return {
440
431
  stop,
441
- post,
442
432
  setTheme(nextTheme) {
443
433
  theme = nextTheme
444
- if (initializationController.status !== "success") {
434
+ if (initializationController.status !== "initialized") {
445
435
  if (initializationController.status === "waitingForInitResult") {
446
436
  pendingThemeChange = true
447
437
  }
448
438
  return
449
439
  }
450
440
  const message: ThemeChangedMessage = { type: "themeChanged", theme }
451
- post(message)
441
+ sendInitializedMessage(message)
452
442
  },
453
443
  setContrastMode(nextContrastMode) {
454
444
  contrastMode = nextContrastMode
455
- if (initializationController.status !== "success") {
445
+ if (initializationController.status !== "initialized") {
456
446
  if (initializationController.status === "waitingForInitResult") {
457
447
  pendingContrastModeChange = true
458
448
  }
@@ -462,18 +452,18 @@ export function createCustomBlockHost(
462
452
  type: "contrastModeChanged",
463
453
  contrastMode,
464
454
  }
465
- post(message)
455
+ sendInitializedMessage(message)
466
456
  },
467
457
  setParent(nextParent) {
468
458
  parent = nextParent
469
- if (initializationController.status !== "success") {
459
+ if (initializationController.status !== "initialized") {
470
460
  if (initializationController.status === "waitingForInitResult") {
471
461
  pendingParentChange = true
472
462
  }
473
463
  return
474
464
  }
475
465
  const message: ParentChangedMessage = { type: "parentChanged", parent }
476
- post(message)
466
+ sendInitializedMessage(message)
477
467
  },
478
468
  setPage(nextPage) {
479
469
  page = nextPage
@@ -482,19 +472,19 @@ export function createCustomBlockHost(
482
472
  sandboxBridgeProtocolVersion <
483
473
  BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE &&
484
474
  (initializationController.status === "waitingForInitResult" ||
485
- initializationController.status === "success")
475
+ initializationController.status === "initialized")
486
476
  ) {
487
477
  stopWithFailure(initErrorForFailureReason("context_unavailable"))
488
478
  return
489
479
  }
490
- if (initializationController.status !== "success") {
480
+ if (initializationController.status !== "initialized") {
491
481
  if (initializationController.status === "waitingForInitResult") {
492
482
  pendingPageChange = true
493
483
  }
494
484
  return
495
485
  }
496
486
  const message: PageChangedMessage = { type: "pageChanged", page }
497
- post(message)
487
+ sendInitializedMessage(message)
498
488
  },
499
489
  setDataSources(nextDataSources) {
500
490
  if (
@@ -510,7 +500,7 @@ export function createCustomBlockHost(
510
500
  }
511
501
  dataSources = nextDataSources
512
502
  activeDataSources = nextDataSources
513
- if (initializationController.status !== "success") {
503
+ if (initializationController.status !== "initialized") {
514
504
  if (initializationController.status === "waitingForInitResult") {
515
505
  pendingDataSourcesChange = nextDataSources
516
506
  }
@@ -520,20 +510,20 @@ export function createCustomBlockHost(
520
510
  type: "dataSourcesChanged",
521
511
  dataSources: nextDataSources,
522
512
  }
523
- post(message)
513
+ sendInitializedMessage(message)
524
514
  },
525
515
  setCurrentUser(nextCurrentUser) {
526
516
  currentUser = nextCurrentUser
527
- if (initializationController.status !== "success") {
517
+ if (initializationController.status !== "initialized") {
528
518
  if (initializationController.status === "waitingForInitResult") {
529
519
  pendingCurrentUserChange = true
530
520
  }
531
521
  return
532
522
  }
533
- post({ type: "currentUserChanged", currentUser })
523
+ sendInitializedMessage({ type: "currentUserChanged", currentUser })
534
524
  },
535
525
  refreshQuery({ dataSourceId }) {
536
- if (initializationController.status !== "success") {
526
+ if (initializationController.status !== "initialized") {
537
527
  return
538
528
  }
539
529
  const refreshes = querySubscriptions.refresh(dataSourceId)
@@ -2,7 +2,6 @@ import type { NotionContrastMode } from "@notionhq/custom-blocks-protocol/contra
2
2
  import type { NotionBlockId } from "@notionhq/custom-blocks-protocol/ids.js"
3
3
  import type { CustomBlockManifest } from "@notionhq/custom-blocks-protocol/manifest.js"
4
4
  import type { ConnectMessage } from "@notionhq/custom-blocks-protocol/messages/connect.js"
5
- import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
6
5
  import type {
7
6
  CustomBlockInitErrorInfo,
8
7
  InitMessage,
@@ -47,7 +46,7 @@ export type InitializationControllerOptions = {
47
46
  blockId: NotionBlockId
48
47
  getState: () => InitializationState
49
48
  prepareInit: (connect: ConnectSuccessMessage) => InitializationPreparation
50
- post: (message: HostToSandboxMessage) => void
49
+ sendInitMessage: (message: InitMessage) => void
51
50
  warn: (payload: unknown) => void
52
51
  flushPendingHostStateChanges: () => void
53
52
  minBridgeProtocolVersion: number
@@ -83,7 +82,7 @@ export function createInitializationController(
83
82
  blockId,
84
83
  getState,
85
84
  prepareInit,
86
- post,
85
+ sendInitMessage,
87
86
  warn,
88
87
  flushPendingHostStateChanges,
89
88
  minBridgeProtocolVersion,
@@ -126,7 +125,7 @@ export function createInitializationController(
126
125
  initStatus = "error"
127
126
  activeInitializationId = initializationId
128
127
  clearNoConnectTimeout()
129
- post({
128
+ sendInitMessage({
130
129
  type: "init",
131
130
  initializationId,
132
131
  status: "error",
@@ -135,11 +134,16 @@ export function createInitializationController(
135
134
  onFailure?.(error)
136
135
  }
137
136
 
138
- function sendInit(connect: ConnectSuccessMessage): boolean {
137
+ function prepareAndSendInit(connect: ConnectSuccessMessage): boolean {
139
138
  if (initStatus !== "waitingForConnect") {
140
139
  return false
141
140
  }
142
141
  const preparation = prepareInit(connect)
142
+ // The embedder can stop the host when it receives the manifest.
143
+ // If that happens, do not continue initialization or start its timer.
144
+ if (initStatus !== "waitingForConnect") {
145
+ return false
146
+ }
143
147
  if (preparation.status === "error") {
144
148
  sendInitError({
145
149
  initializationId: connect.initializationId,
@@ -164,7 +168,7 @@ export function createInitializationController(
164
168
  dataSources: preparation.dataSources,
165
169
  currentUser: state.currentUser,
166
170
  }
167
- post(message)
171
+ sendInitMessage(message)
168
172
  noInitResultTimeoutId = window.setTimeout(() => {
169
173
  if (initStatus !== "waitingForInitResult") {
170
174
  return
@@ -223,7 +227,7 @@ export function createInitializationController(
223
227
  return
224
228
  }
225
229
 
226
- if (sendInit(message)) {
230
+ if (prepareAndSendInit(message)) {
227
231
  hasConnectedSandbox = true
228
232
  }
229
233
  }
@@ -324,7 +328,7 @@ export function createInitializationController(
324
328
  }
325
329
  clearNoInitResultTimeout()
326
330
  if (message.status === "success") {
327
- initStatus = "success"
331
+ initStatus = "initialized"
328
332
  flushPendingHostStateChanges()
329
333
  } else {
330
334
  initStatus = "error"
@@ -20,7 +20,7 @@ export type InitStatus =
20
20
  /** The host is waiting for a matching `initResult` after it sends `init`. */
21
21
  | "waitingForInitResult"
22
22
  /** The host is initialized and can send live updates. */
23
- | "success"
23
+ | "initialized"
24
24
  /** The host is in an error state after an initialization failure or timeout. */
25
25
  | "error"
26
26
  /** The host is stopped and no longer sends or accepts sandbox messages. */
@@ -2,8 +2,8 @@ import type { ConnectMessage } from "@notionhq/custom-blocks-protocol/messages/c
2
2
  import type { CreatePageMessage } from "@notionhq/custom-blocks-protocol/messages/createPage.js"
3
3
  import type { GetPageMessage } from "@notionhq/custom-blocks-protocol/messages/getPage.js"
4
4
  import type { GetUserMessage } from "@notionhq/custom-blocks-protocol/messages/getUser.js"
5
- import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
6
5
  import type { InitResultMessage } from "@notionhq/custom-blocks-protocol/messages/initResult.js"
6
+ import type { InvalidSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/invalidSandboxMessage.js"
7
7
  import { isInitHandshakeMessage } from "@notionhq/custom-blocks-protocol/messages/isInitHandshakeMessage.js"
8
8
  import type { ListUsersMessage } from "@notionhq/custom-blocks-protocol/messages/listUsers.js"
9
9
  import type { OpenPageMessage } from "@notionhq/custom-blocks-protocol/messages/openPage.js"
@@ -35,7 +35,7 @@ export type SandboxMessageRouterOptions = {
35
35
  sandboxOrigin: string
36
36
  getInitStatus: () => InitStatus
37
37
  emit: (direction: CustomBlockHostLogDirection, payload: unknown) => void
38
- post: (message: HostToSandboxMessage) => void
38
+ sendNack: (message: InvalidSandboxMessage) => void
39
39
  onInvalidConnect: InitializationController["handleInvalidConnect"]
40
40
  onInvalidInitResult: InitializationController["handleInvalidInitResult"]
41
41
  onConnect: (message: ConnectMessage) => void
@@ -61,7 +61,7 @@ export function createSandboxMessageRouter(
61
61
  sandboxOrigin,
62
62
  getInitStatus,
63
63
  emit,
64
- post,
64
+ sendNack,
65
65
  onInvalidConnect,
66
66
  onInvalidInitResult,
67
67
  onConnect,
@@ -116,9 +116,9 @@ export function createSandboxMessageRouter(
116
116
  })
117
117
  }
118
118
 
119
- // Other messages get NACKed indicating they were invalid and skipped
120
- if (response.nack !== undefined) {
121
- post(response.nack)
119
+ // Other messages get NACKed indicating they were invalid and skipped.
120
+ if (response.nack !== undefined && getInitStatus() !== "stopped") {
121
+ sendNack(response.nack)
122
122
  }
123
123
  }
124
124
 
@@ -139,7 +139,7 @@ export function createSandboxMessageRouter(
139
139
  }
140
140
 
141
141
  const message = parsed.output
142
- if (!isInitHandshakeMessage(message) && getInitStatus() !== "success") {
142
+ if (!isInitHandshakeMessage(message) && getInitStatus() !== "initialized") {
143
143
  emit("warn", {
144
144
  message: `Ignored ${message.type} before initialization completed`,
145
145
  payload: message,