@notionhq/custom-blocks-host 0.1.30 → 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;AAGvF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4DAA4D,CAAA;AAYtG,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;AAyBD,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,sBAAsB,GAC7B,qBAAqB,CAqXvB"}
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,47 @@ 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
+ }
48
81
  /**
49
82
  * Invokes a one-shot handler and posts its result with the request's ID.
50
83
  * Each result type is named `<request type>Result` by the bridge protocol.
@@ -59,7 +92,7 @@ export function createCustomBlockHost(options) {
59
92
  requestId: message.requestId,
60
93
  ...response,
61
94
  };
62
- post(result);
95
+ sendInitializedMessage(result);
63
96
  }
64
97
  function postQueryDataSourceResult(args) {
65
98
  const message = args.response.status === "error"
@@ -76,7 +109,7 @@ export function createCustomBlockHost(options) {
76
109
  items: args.response.items,
77
110
  hasMore: args.response.hasMore,
78
111
  };
79
- post(message);
112
+ sendInitializedMessage(message);
80
113
  }
81
114
  async function handleQuery(message, token = querySubscriptions.subscribe(message)) {
82
115
  const collectionSchema = getCollectionSchema(activeDataSources, message.dataSourceId);
@@ -102,36 +135,36 @@ export function createCustomBlockHost(options) {
102
135
  /**
103
136
  * Sends any pending host state changes to the sandbox. It is possible for these to change
104
137
  * between when the host sends `init` and when the sandbox responds with `initResult`. The host
105
- * does send any pending state changes until hearing back from the sandbox.
138
+ * does not send pending state changes until the sandbox responds.
106
139
  */
107
140
  function flushPendingHostStateChanges() {
108
141
  if (pendingThemeChange) {
109
142
  pendingThemeChange = false;
110
- post({ type: "themeChanged", theme });
143
+ sendInitializedMessage({ type: "themeChanged", theme });
111
144
  }
112
145
  if (pendingContrastModeChange) {
113
146
  pendingContrastModeChange = false;
114
- post({ type: "contrastModeChanged", contrastMode });
147
+ sendInitializedMessage({ type: "contrastModeChanged", contrastMode });
115
148
  }
116
149
  if (pendingParentChange) {
117
150
  pendingParentChange = false;
118
- post({ type: "parentChanged", parent });
151
+ sendInitializedMessage({ type: "parentChanged", parent });
119
152
  }
120
153
  if (pendingPageChange) {
121
154
  pendingPageChange = false;
122
- post({ type: "pageChanged", page });
155
+ sendInitializedMessage({ type: "pageChanged", page });
123
156
  }
124
157
  if (pendingDataSourcesChange !== undefined) {
125
158
  const nextDataSources = pendingDataSourcesChange;
126
159
  pendingDataSourcesChange = undefined;
127
- post({
160
+ sendInitializedMessage({
128
161
  type: "dataSourcesChanged",
129
162
  dataSources: nextDataSources,
130
163
  });
131
164
  }
132
165
  if (pendingCurrentUserChange) {
133
166
  pendingCurrentUserChange = false;
134
- post({ type: "currentUserChanged", currentUser });
167
+ sendInitializedMessage({ type: "currentUserChanged", currentUser });
135
168
  }
136
169
  }
137
170
  const initializationController = createInitializationController({
@@ -176,7 +209,7 @@ export function createCustomBlockHost(options) {
176
209
  dataSources: resolvedDataSources,
177
210
  };
178
211
  },
179
- post,
212
+ sendInitMessage,
180
213
  warn: payload => emit("warn", payload),
181
214
  flushPendingHostStateChanges,
182
215
  minBridgeProtocolVersion,
@@ -195,7 +228,7 @@ export function createCustomBlockHost(options) {
195
228
  sandboxOrigin,
196
229
  getInitStatus: () => initializationController.status,
197
230
  emit,
198
- post,
231
+ sendNack,
199
232
  onInvalidConnect: initializationController.handleInvalidConnect,
200
233
  onInvalidInitResult: initializationController.handleInvalidInitResult,
201
234
  onConnect: initializationController.handleConnect,
@@ -225,21 +258,20 @@ export function createCustomBlockHost(options) {
225
258
  }
226
259
  return {
227
260
  stop,
228
- post,
229
261
  setTheme(nextTheme) {
230
262
  theme = nextTheme;
231
- if (initializationController.status !== "success") {
263
+ if (initializationController.status !== "initialized") {
232
264
  if (initializationController.status === "waitingForInitResult") {
233
265
  pendingThemeChange = true;
234
266
  }
235
267
  return;
236
268
  }
237
269
  const message = { type: "themeChanged", theme };
238
- post(message);
270
+ sendInitializedMessage(message);
239
271
  },
240
272
  setContrastMode(nextContrastMode) {
241
273
  contrastMode = nextContrastMode;
242
- if (initializationController.status !== "success") {
274
+ if (initializationController.status !== "initialized") {
243
275
  if (initializationController.status === "waitingForInitResult") {
244
276
  pendingContrastModeChange = true;
245
277
  }
@@ -249,18 +281,18 @@ export function createCustomBlockHost(options) {
249
281
  type: "contrastModeChanged",
250
282
  contrastMode,
251
283
  };
252
- post(message);
284
+ sendInitializedMessage(message);
253
285
  },
254
286
  setParent(nextParent) {
255
287
  parent = nextParent;
256
- if (initializationController.status !== "success") {
288
+ if (initializationController.status !== "initialized") {
257
289
  if (initializationController.status === "waitingForInitResult") {
258
290
  pendingParentChange = true;
259
291
  }
260
292
  return;
261
293
  }
262
294
  const message = { type: "parentChanged", parent };
263
- post(message);
295
+ sendInitializedMessage(message);
264
296
  },
265
297
  setPage(nextPage) {
266
298
  page = nextPage;
@@ -268,18 +300,18 @@ export function createCustomBlockHost(options) {
268
300
  sandboxBridgeProtocolVersion <
269
301
  BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE &&
270
302
  (initializationController.status === "waitingForInitResult" ||
271
- initializationController.status === "success")) {
303
+ initializationController.status === "initialized")) {
272
304
  stopWithFailure(initErrorForFailureReason("context_unavailable"));
273
305
  return;
274
306
  }
275
- if (initializationController.status !== "success") {
307
+ if (initializationController.status !== "initialized") {
276
308
  if (initializationController.status === "waitingForInitResult") {
277
309
  pendingPageChange = true;
278
310
  }
279
311
  return;
280
312
  }
281
313
  const message = { type: "pageChanged", page };
282
- post(message);
314
+ sendInitializedMessage(message);
283
315
  },
284
316
  setDataSources(nextDataSources) {
285
317
  if (initializationController.hasConnectedSandbox &&
@@ -293,7 +325,7 @@ export function createCustomBlockHost(options) {
293
325
  }
294
326
  dataSources = nextDataSources;
295
327
  activeDataSources = nextDataSources;
296
- if (initializationController.status !== "success") {
328
+ if (initializationController.status !== "initialized") {
297
329
  if (initializationController.status === "waitingForInitResult") {
298
330
  pendingDataSourcesChange = nextDataSources;
299
331
  }
@@ -303,20 +335,20 @@ export function createCustomBlockHost(options) {
303
335
  type: "dataSourcesChanged",
304
336
  dataSources: nextDataSources,
305
337
  };
306
- post(message);
338
+ sendInitializedMessage(message);
307
339
  },
308
340
  setCurrentUser(nextCurrentUser) {
309
341
  currentUser = nextCurrentUser;
310
- if (initializationController.status !== "success") {
342
+ if (initializationController.status !== "initialized") {
311
343
  if (initializationController.status === "waitingForInitResult") {
312
344
  pendingCurrentUserChange = true;
313
345
  }
314
346
  return;
315
347
  }
316
- post({ type: "currentUserChanged", currentUser });
348
+ sendInitializedMessage({ type: "currentUserChanged", currentUser });
317
349
  },
318
350
  refreshQuery({ dataSourceId }) {
319
- if (initializationController.status !== "success") {
351
+ if (initializationController.status !== "initialized") {
320
352
  return;
321
353
  }
322
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.30",
3
+ "version": "0.1.31",
4
4
  "description": "Reusable host runtime for Notion custom blocks.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -8,7 +8,11 @@ import type { CustomBlockManifest } from "@notionhq/custom-blocks-protocol/manif
8
8
  import type { ContrastModeChangedMessage } from "@notionhq/custom-blocks-protocol/messages/contrastModeChanged.js"
9
9
  import type { DataSourcesChangedMessage } from "@notionhq/custom-blocks-protocol/messages/dataSourcesChanged.js"
10
10
  import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
11
- import type { CustomBlockInitErrorCode } from "@notionhq/custom-blocks-protocol/messages/init.js"
11
+ import type {
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"
12
16
  import type { BridgeMessagePayload } from "@notionhq/custom-blocks-protocol/messages/messagePayload.js"
13
17
  import type { PageChangedMessage } from "@notionhq/custom-blocks-protocol/messages/pageChanged.js"
14
18
  import type { ParentChangedMessage } from "@notionhq/custom-blocks-protocol/messages/parentChanged.js"
@@ -45,6 +49,7 @@ import {
45
49
  import type { CustomBlockHostQueryDataSourceResult } from "./queries/types.js"
46
50
  import { validateQueryDataSourceFilter } from "./queries/validateQueryDataSourceFilter.js"
47
51
  import { validateQueryDataSourceSorts } from "./queries/validateQueryDataSourceSorts.js"
52
+ import { unreachable } from "./utils.js"
48
53
 
49
54
  // Bridge protocol version that introduced page: { type: "unavailable" }.
50
55
  // Earlier SDKs cannot represent this context, so the host reports context_unavailable.
@@ -85,7 +90,6 @@ export type CustomBlockHostOptions = {
85
90
 
86
91
  export type CustomBlockHostHandle = {
87
92
  stop: () => void
88
- post: (message: HostToSandboxMessage) => void
89
93
  setTheme: (theme: NotionTheme) => void
90
94
  setContrastMode: (contrastMode: NotionContrastMode) => void
91
95
  setParent: (parent: NotionParent) => void
@@ -163,10 +167,20 @@ export function createCustomBlockHost(
163
167
  onLog?.(direction, payload)
164
168
  }
165
169
 
166
- function post(message: HostToSandboxMessage) {
167
- if (initializationController.status === "stopped") {
168
- // The parent page can stop this host. Do not send messages after the host stops.
169
- 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)
170
184
  }
171
185
  const sandboxMessage = normalizeHostToSandboxMessage(
172
186
  message,
@@ -176,6 +190,36 @@ export function createCustomBlockHost(
176
190
  iframe.contentWindow?.postMessage(sandboxMessage, sandboxOrigin)
177
191
  }
178
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
+
179
223
  /**
180
224
  * Invokes a one-shot handler and posts its result with the request's ID.
181
225
  * Each result type is named `<request type>Result` by the bridge protocol.
@@ -192,8 +236,8 @@ export function createCustomBlockHost(
192
236
  type: `${message.type}Result` as const,
193
237
  requestId: message.requestId,
194
238
  ...response,
195
- } as HostToSandboxMessage
196
- post(result)
239
+ } as ResultForRequest<OneShotRequest>
240
+ sendInitializedMessage(result)
197
241
  }
198
242
 
199
243
  function postQueryDataSourceResult(args: {
@@ -215,7 +259,7 @@ export function createCustomBlockHost(
215
259
  items: args.response.items,
216
260
  hasMore: args.response.hasMore,
217
261
  }
218
- post(message)
262
+ sendInitializedMessage(message)
219
263
  }
220
264
 
221
265
  async function handleQuery(
@@ -249,36 +293,36 @@ export function createCustomBlockHost(
249
293
  /**
250
294
  * Sends any pending host state changes to the sandbox. It is possible for these to change
251
295
  * between when the host sends `init` and when the sandbox responds with `initResult`. The host
252
- * does send any pending state changes until hearing back from the sandbox.
296
+ * does not send pending state changes until the sandbox responds.
253
297
  */
254
298
  function flushPendingHostStateChanges() {
255
299
  if (pendingThemeChange) {
256
300
  pendingThemeChange = false
257
- post({ type: "themeChanged", theme })
301
+ sendInitializedMessage({ type: "themeChanged", theme })
258
302
  }
259
303
  if (pendingContrastModeChange) {
260
304
  pendingContrastModeChange = false
261
- post({ type: "contrastModeChanged", contrastMode })
305
+ sendInitializedMessage({ type: "contrastModeChanged", contrastMode })
262
306
  }
263
307
  if (pendingParentChange) {
264
308
  pendingParentChange = false
265
- post({ type: "parentChanged", parent })
309
+ sendInitializedMessage({ type: "parentChanged", parent })
266
310
  }
267
311
  if (pendingPageChange) {
268
312
  pendingPageChange = false
269
- post({ type: "pageChanged", page })
313
+ sendInitializedMessage({ type: "pageChanged", page })
270
314
  }
271
315
  if (pendingDataSourcesChange !== undefined) {
272
316
  const nextDataSources = pendingDataSourcesChange
273
317
  pendingDataSourcesChange = undefined
274
- post({
318
+ sendInitializedMessage({
275
319
  type: "dataSourcesChanged",
276
320
  dataSources: nextDataSources,
277
321
  })
278
322
  }
279
323
  if (pendingCurrentUserChange) {
280
324
  pendingCurrentUserChange = false
281
- post({ type: "currentUserChanged", currentUser })
325
+ sendInitializedMessage({ type: "currentUserChanged", currentUser })
282
326
  }
283
327
  }
284
328
 
@@ -330,7 +374,7 @@ export function createCustomBlockHost(
330
374
  dataSources: resolvedDataSources,
331
375
  }
332
376
  },
333
- post,
377
+ sendInitMessage,
334
378
  warn: payload => emit("warn", payload),
335
379
  flushPendingHostStateChanges,
336
380
  minBridgeProtocolVersion,
@@ -350,7 +394,7 @@ export function createCustomBlockHost(
350
394
  sandboxOrigin,
351
395
  getInitStatus: () => initializationController.status,
352
396
  emit,
353
- post,
397
+ sendNack,
354
398
  onInvalidConnect: initializationController.handleInvalidConnect,
355
399
  onInvalidInitResult: initializationController.handleInvalidInitResult,
356
400
  onConnect: initializationController.handleConnect,
@@ -385,21 +429,20 @@ export function createCustomBlockHost(
385
429
 
386
430
  return {
387
431
  stop,
388
- post,
389
432
  setTheme(nextTheme) {
390
433
  theme = nextTheme
391
- if (initializationController.status !== "success") {
434
+ if (initializationController.status !== "initialized") {
392
435
  if (initializationController.status === "waitingForInitResult") {
393
436
  pendingThemeChange = true
394
437
  }
395
438
  return
396
439
  }
397
440
  const message: ThemeChangedMessage = { type: "themeChanged", theme }
398
- post(message)
441
+ sendInitializedMessage(message)
399
442
  },
400
443
  setContrastMode(nextContrastMode) {
401
444
  contrastMode = nextContrastMode
402
- if (initializationController.status !== "success") {
445
+ if (initializationController.status !== "initialized") {
403
446
  if (initializationController.status === "waitingForInitResult") {
404
447
  pendingContrastModeChange = true
405
448
  }
@@ -409,18 +452,18 @@ export function createCustomBlockHost(
409
452
  type: "contrastModeChanged",
410
453
  contrastMode,
411
454
  }
412
- post(message)
455
+ sendInitializedMessage(message)
413
456
  },
414
457
  setParent(nextParent) {
415
458
  parent = nextParent
416
- if (initializationController.status !== "success") {
459
+ if (initializationController.status !== "initialized") {
417
460
  if (initializationController.status === "waitingForInitResult") {
418
461
  pendingParentChange = true
419
462
  }
420
463
  return
421
464
  }
422
465
  const message: ParentChangedMessage = { type: "parentChanged", parent }
423
- post(message)
466
+ sendInitializedMessage(message)
424
467
  },
425
468
  setPage(nextPage) {
426
469
  page = nextPage
@@ -429,19 +472,19 @@ export function createCustomBlockHost(
429
472
  sandboxBridgeProtocolVersion <
430
473
  BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE &&
431
474
  (initializationController.status === "waitingForInitResult" ||
432
- initializationController.status === "success")
475
+ initializationController.status === "initialized")
433
476
  ) {
434
477
  stopWithFailure(initErrorForFailureReason("context_unavailable"))
435
478
  return
436
479
  }
437
- if (initializationController.status !== "success") {
480
+ if (initializationController.status !== "initialized") {
438
481
  if (initializationController.status === "waitingForInitResult") {
439
482
  pendingPageChange = true
440
483
  }
441
484
  return
442
485
  }
443
486
  const message: PageChangedMessage = { type: "pageChanged", page }
444
- post(message)
487
+ sendInitializedMessage(message)
445
488
  },
446
489
  setDataSources(nextDataSources) {
447
490
  if (
@@ -457,7 +500,7 @@ export function createCustomBlockHost(
457
500
  }
458
501
  dataSources = nextDataSources
459
502
  activeDataSources = nextDataSources
460
- if (initializationController.status !== "success") {
503
+ if (initializationController.status !== "initialized") {
461
504
  if (initializationController.status === "waitingForInitResult") {
462
505
  pendingDataSourcesChange = nextDataSources
463
506
  }
@@ -467,20 +510,20 @@ export function createCustomBlockHost(
467
510
  type: "dataSourcesChanged",
468
511
  dataSources: nextDataSources,
469
512
  }
470
- post(message)
513
+ sendInitializedMessage(message)
471
514
  },
472
515
  setCurrentUser(nextCurrentUser) {
473
516
  currentUser = nextCurrentUser
474
- if (initializationController.status !== "success") {
517
+ if (initializationController.status !== "initialized") {
475
518
  if (initializationController.status === "waitingForInitResult") {
476
519
  pendingCurrentUserChange = true
477
520
  }
478
521
  return
479
522
  }
480
- post({ type: "currentUserChanged", currentUser })
523
+ sendInitializedMessage({ type: "currentUserChanged", currentUser })
481
524
  },
482
525
  refreshQuery({ dataSourceId }) {
483
- if (initializationController.status !== "success") {
526
+ if (initializationController.status !== "initialized") {
484
527
  return
485
528
  }
486
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,