@notionhq/custom-blocks-host 0.1.8 → 0.1.9

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
@@ -72,29 +72,42 @@ const host = createCustomBlockHost({
72
72
  listUsers: async (message) => listUsers(message),
73
73
  resize: (message) => resizeIframe(message.height),
74
74
  },
75
- onConnect: (message) => {
76
- console.log("Connected SDK", message.sdkVersion);
75
+ onManifest: (manifest) => {
76
+ console.log("Selected manifest", manifest);
77
77
  },
78
- onIgnoredConnect: () => {
79
- remountHostAndIframe();
78
+ onInitialized: (state) => {
79
+ console.log("Initialized manifest", state.manifest);
80
80
  },
81
81
  onInitializationFailure: (error) => {
82
82
  showInitializationError(error);
83
83
  },
84
+ onRestartRequired: () => {
85
+ remountHostAndIframe();
86
+ },
84
87
  onLog: (direction, payload) => {
85
88
  console.log(direction, payload);
86
89
  },
87
90
  });
88
91
  ```
89
92
 
90
- The host runs one initialization per instance. `onIgnoredConnect` fires when a
91
- `connect` arrives that the settled host cannot serve: the iframe document was
92
- replaced, or the document's first `connect` came after the no-connect timeout.
93
- Recreate the host and the iframe together so the document runs a fresh
94
- handshake against current host state.
93
+ Each host instance initializes once:
94
+
95
+ - `onManifest` provides the selected manifest as soon as it is available after
96
+ `connect`, before binding validation and `onInitialized`. Use it to configure
97
+ bindings even when initialization cannot yet succeed.
98
+
99
+ - `onInitialized` reports success after the sandbox accepts initialization. It
100
+ provides the selected `manifest` and the sandbox's optional `initialHeight`.
101
+ - `onInitializationFailure` reports an initialization error or timeout.
102
+ - `onRestartRequired` reports a connection attempt after the host accepts its
103
+ first connection or stops waiting. Recreate the host and iframe together
104
+ to accept a new connection.
105
+ This can occur after the iframe document changes or a connection arrives
106
+ after a timeout.
95
107
 
96
- `onInitializationFailure` receives structured errors from failed `init`
97
- messages, failed `initResult` messages, and initialization timeouts.
108
+ The host uses the manifest from `connect`, or `initialState.manifest` if
109
+ `connect` omits it. If neither exists, initialization fails with
110
+ `manifest_unavailable`.
98
111
 
99
112
  `queryDataSource` is required. Other request handlers are optional. Every
100
113
  request handler may return its result synchronously or as a promise. Results
@@ -157,7 +170,7 @@ failure. It logs and ignores a valid `initResult` with a stale
157
170
  `initializationId` or an unexpected state.
158
171
 
159
172
  Hosts do not retry malformed messages. A host that receives a `connect` after
160
- initialization has settled must use `onIgnoredConnect` to recreate the host
173
+ initialization has settled must use `onRestartRequired` to recreate the host
161
174
  and iframe together.
162
175
 
163
176
  ## Updating live state
@@ -1,13 +1,12 @@
1
1
  import { type NotionContrastMode } from "./protocol/contrast.js";
2
2
  import type { NotionDataSourceId } from "./protocol/ids.js";
3
- import type { ConnectMessage } from "./protocol/messages/connect.js";
3
+ import type { CustomBlockManifest } from "./protocol/manifest.js";
4
4
  import type { HostToSandboxMessage } from "./protocol/messages/hostToSandbox.js";
5
- import type { InitResultMessage } from "./protocol/messages/initResult.js";
6
5
  import type { CustomBlockPage } from "./protocol/pages/page.js";
7
6
  import type { NotionParent } from "./protocol/parent.js";
8
7
  import type { NotionTheme } from "./protocol/theme.js";
9
8
  import type { NotionUser } from "./protocol/users/user.js";
10
- import type { ConnectSuccessMessage, CustomBlockHostDataSourcesPayload, CustomBlockHostInitialState } from "./lifecycle/types.js";
9
+ import type { CustomBlockHostDataSourcesPayload, CustomBlockHostInitialState } from "./lifecycle/types.js";
11
10
  import type { CustomBlockHostHandlers, CustomBlockHostInitializationFailure, CustomBlockHostLogDirection } from "./messages/types.js";
12
11
  export type CustomBlockHostOptions = {
13
12
  iframe: HTMLIFrameElement;
@@ -17,17 +16,20 @@ export type CustomBlockHostOptions = {
17
16
  noInitResultTimeoutMs?: number;
18
17
  initialState: CustomBlockHostInitialState;
19
18
  handlers: CustomBlockHostHandlers;
20
- onConnect?: (message: ConnectSuccessMessage) => void;
21
- onInitResult?: (message: InitResultMessage) => void;
19
+ /** Called as soon as the `manifest` is available after `connect`, before `onInitialized` and binding validation. */
20
+ onManifest?: (manifest: CustomBlockManifest) => void;
21
+ /** Callback fired when the initialization handshake with the sandbox completed successfully. */
22
+ onInitialized?: (state: {
23
+ manifest: CustomBlockManifest;
24
+ initialHeight?: number;
25
+ }) => void;
26
+ /** Callback fired when the initialization handshake with the sandbox failed or timed out. */
22
27
  onInitializationFailure?: (error: CustomBlockHostInitializationFailure) => void;
23
28
  /**
24
- * A `connect` arrived after this host's initialization settled the
25
- * document was replaced, or its first `connect` came after the host
26
- * stopped waiting. Either way the host cannot serve it: the embedder
27
- * should recreate host and iframe together so the document can run a
28
- * fresh handshake.
29
+ * Callback fired when the current host and iframe must restart.
30
+ * Recreate both to continue.
29
31
  */
30
- onIgnoredConnect?: (message: ConnectMessage) => void;
32
+ onRestartRequired?: () => void;
31
33
  onLog?: (direction: CustomBlockHostLogDirection, payload: unknown) => void;
32
34
  };
33
35
  export type CustomBlockHostHandle = {
@@ -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;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AAEjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAa1F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4DAA4D,CAAA;AAEtG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAahG,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,qBAAqB,EACrB,iCAAiC,EACjC,2BAA2B,EAC3B,MAAM,sBAAsB,CAAA;AAE7B,OAAO,KAAK,EACX,uBAAuB,EACvB,oCAAoC,EACpC,2BAA2B,EAC3B,MAAM,qBAAqB,CAAA;AAU5B,MAAM,MAAM,sBAAsB,GAAG;IACpC,MAAM,EAAE,iBAAiB,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,wBAAwB,EAAE,MAAM,CAAA;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,YAAY,EAAE,2BAA2B,CAAA;IACzC,QAAQ,EAAE,uBAAuB,CAAA;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAA;IACpD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACnD,uBAAuB,CAAC,EAAE,CACzB,KAAK,EAAE,oCAAoC,KACvC,IAAI,CAAA;IACT;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;IACpD,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;AAMD,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,sBAAsB,GAC7B,qBAAqB,CAodvB"}
1
+ {"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../src/createCustomBlockHost.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,kBAAkB,EACvB,MAAM,8CAA8C,CAAA;AACrD,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;AActG,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,uBAAuB,EACvB,oCAAoC,EACpC,2BAA2B,EAC3B,MAAM,qBAAqB,CAAA;AAU5B,MAAM,MAAM,sBAAsB,GAAG;IACpC,MAAM,EAAE,iBAAiB,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,wBAAwB,EAAE,MAAM,CAAA;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,YAAY,EAAE,2BAA2B,CAAA;IACzC,QAAQ,EAAE,uBAAuB,CAAA;IACjC,oHAAoH;IACpH,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,6FAA6F;IAC7F,uBAAuB,CAAC,EAAE,CACzB,KAAK,EAAE,oCAAoC,KACvC,IAAI,CAAA;IACT;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC9B,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;AAMD,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,sBAAsB,GAC7B,qBAAqB,CA+dvB"}
@@ -13,7 +13,7 @@ const initializationIdentitySchema = v.object({
13
13
  initializationId: v.string(),
14
14
  });
15
15
  export function createCustomBlockHost(options) {
16
- const { iframe, initialState, handlers, onConnect, onInitResult, onInitializationFailure, onIgnoredConnect, onLog, sandboxOrigin, minBridgeProtocolVersion: configuredMinBridgeProtocolVersion, noConnectTimeoutMs, noInitResultTimeoutMs, } = options;
16
+ const { iframe, initialState, handlers, onManifest, onInitialized, onInitializationFailure, onRestartRequired, onLog, sandboxOrigin, minBridgeProtocolVersion: configuredMinBridgeProtocolVersion, noConnectTimeoutMs, noInitResultTimeoutMs, } = options;
17
17
  const minBridgeProtocolVersion = Math.max(configuredMinBridgeProtocolVersion ?? CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION, CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION);
18
18
  let theme = initialState.theme;
19
19
  let contrastMode = initialState.contrastMode ?? DEFAULT_CONTRAST_MODE;
@@ -188,6 +188,13 @@ export function createCustomBlockHost(options) {
188
188
  }),
189
189
  prepareInit(connect) {
190
190
  manifest = connect.manifest ?? initialState.manifest;
191
+ if (manifest === undefined) {
192
+ return {
193
+ status: "error",
194
+ error: initErrorForFailureReason("manifest_unavailable"),
195
+ };
196
+ }
197
+ onManifest?.(manifest);
191
198
  const resolvedDataSources = typeof dataSources === "function" ? dataSources(connect) : dataSources;
192
199
  const bindingErrorCode = getBindingErrorCode(manifest, resolvedDataSources);
193
200
  if (bindingErrorCode !== undefined) {
@@ -209,9 +216,12 @@ export function createCustomBlockHost(options) {
209
216
  minBridgeProtocolVersion,
210
217
  noConnectTimeoutMs,
211
218
  noInitResultTimeoutMs,
212
- onConnect,
213
- onInitResult,
214
- onIgnoredConnect,
219
+ onInitResult(message) {
220
+ if (message.status === "success" && manifest !== undefined) {
221
+ onInitialized?.({ manifest, initialHeight: message.initialHeight });
222
+ }
223
+ },
224
+ onRestartRequired,
215
225
  onInitializationFailure,
216
226
  });
217
227
  function onInvalidSandboxMessage(data, issues) {
@@ -368,6 +378,7 @@ export function createCustomBlockHost(options) {
368
378
  },
369
379
  setDataSources(nextDataSources) {
370
380
  if (initializationController.hasConnectedSandbox &&
381
+ manifest !== undefined &&
371
382
  getBindingErrorCode(manifest, nextDataSources) !== undefined) {
372
383
  emit("warn", {
373
384
  message: "Ignored invalid data source bindings update",
@@ -36,9 +36,8 @@ export type InitializationControllerOptions = {
36
36
  minBridgeProtocolVersion: number;
37
37
  noConnectTimeoutMs?: number;
38
38
  noInitResultTimeoutMs?: number;
39
- onConnect?: (message: ConnectSuccessMessage) => void;
40
39
  onInitResult?: (message: InitResultMessage) => void;
41
- onIgnoredConnect?: (message: ConnectMessage) => void;
40
+ onRestartRequired?: () => void;
42
41
  onInitializationFailure?: (error: CustomBlockHostInitializationFailure) => void;
43
42
  };
44
43
  export type InitializationController = {
@@ -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":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAA;AACtF,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,oBAAoB,EAAE,MAAM,4DAA4D,CAAA;AACtG,OAAO,KAAK,EACX,wBAAwB,EAExB,MAAM,mDAAmD,CAAA;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,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;AAChF,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,sBAAsB,CAAA;AAGhF,OAAO,KAAK,EACX,qBAAqB,EACrB,iCAAiC,EACjC,UAAU,EACV,MAAM,YAAY,CAAA;AAKnB,KAAK,mBAAmB,GAAG;IAC1B,KAAK,EAAE,WAAW,CAAA;IAClB,YAAY,EAAE,kBAAkB,CAAA;IAChC,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,eAAe,CAAA;IACrB,WAAW,EAAE,UAAU,CAAA;CACvB,CAAA;AAED,KAAK,yBAAyB,GAC3B;IACA,MAAM,EAAE,SAAS,CAAA;IACjB,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,WAAW,EAAE,iCAAiC,CAAA;CAC7C,GACD;IACA,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,wBAAwB,CAAA;CAC9B,CAAA;AAEJ,MAAM,MAAM,+BAA+B,GAAG;IAC7C,OAAO,EAAE,aAAa,CAAA;IACtB,QAAQ,EAAE,MAAM,mBAAmB,CAAA;IACnC,WAAW,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,yBAAyB,CAAA;IAC1E,IAAI,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAC7C,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAChC,4BAA4B,EAAE,MAAM,IAAI,CAAA;IACxC,wBAAwB,EAAE,MAAM,CAAA;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACnD,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC9B,uBAAuB,CAAC,EAAE,CACzB,KAAK,EAAE,oCAAoC,KACvC,IAAI,CAAA;CACT,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG;IACtC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;IAC3B,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAA;IACrC,aAAa,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;IAChD,gBAAgB,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACtD,oBAAoB,EAAE,CAAC,gBAAgB,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;IAC5D,uBAAuB,EAAE,MAAM,OAAO,CAAA;IACtC,YAAY,EAAE,MAAM,IAAI,CAAA;IACxB,IAAI,EAAE,MAAM,IAAI,CAAA;CAChB,CAAA;AAED,wBAAgB,8BAA8B,CAC7C,OAAO,EAAE,+BAA+B,GACtC,wBAAwB,CAuO1B"}
@@ -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, onConnect, onInitResult, onIgnoredConnect, onInitializationFailure, } = options;
6
+ const { blockId, getState, prepareInit, post, warn, flushPendingHostStateChanges, minBridgeProtocolVersion, noConnectTimeoutMs = DEFAULT_NO_CONNECT_TIMEOUT_MS, noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS, onInitResult, onRestartRequired, onInitializationFailure, } = options;
7
7
  let initStatus = "waitingForConnect";
8
8
  let noConnectTimeoutId;
9
9
  let noInitResultTimeoutId;
@@ -40,7 +40,6 @@ export function createInitializationController(options) {
40
40
  if (initStatus !== "waitingForConnect") {
41
41
  return false;
42
42
  }
43
- onConnect?.(connect);
44
43
  const preparation = prepareInit(connect);
45
44
  if (preparation.status === "error") {
46
45
  sendInitError(connect.initializationId, preparation.error);
@@ -89,7 +88,7 @@ export function createInitializationController(options) {
89
88
  message: "Ignored connect after initialization settled; recreate the host and iframe to re-initialize",
90
89
  initializationId: message.initializationId,
91
90
  });
92
- onIgnoredConnect?.(message);
91
+ onRestartRequired?.();
93
92
  return;
94
93
  }
95
94
  // Verify the bridge protocol version is valid.
@@ -21,7 +21,11 @@ export type CustomBlockHostInitialState = {
21
21
  blockId: NotionBlockId;
22
22
  parent: NotionParent;
23
23
  page: CustomBlockPage;
24
- manifest: CustomBlockManifest;
24
+ /**
25
+ * The host's persisted manifest, when one exists. If omitted, the host
26
+ * requires the sandbox to provide a manifest in `connect`.
27
+ */
28
+ manifest?: CustomBlockManifest;
25
29
  dataSources: CustomBlockHostInitialDataSources;
26
30
  currentUser: NotionUser;
27
31
  };
@@ -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,MAAM,MAAM,UAAU,GACnB,mBAAmB,GACnB,sBAAsB,GACtB,SAAS,GACT,OAAO,CAAA;AAEV,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;IAElB,YAAY,CAAC,EAAE,kBAAkB,CAAA;IACjC,OAAO,EAAE,aAAa,CAAA;IACtB,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,eAAe,CAAA;IACrB,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,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,MAAM,MAAM,UAAU,GACnB,mBAAmB,GACnB,sBAAsB,GACtB,SAAS,GACT,OAAO,CAAA;AAEV,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;IAElB,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notionhq/custom-blocks-host",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Reusable host runtime for Notion custom blocks.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -4,7 +4,6 @@ import {
4
4
  } from "@notionhq/custom-blocks-protocol/contrast.js"
5
5
  import type { NotionDataSourceId } from "@notionhq/custom-blocks-protocol/ids.js"
6
6
  import type { CustomBlockManifest } from "@notionhq/custom-blocks-protocol/manifest.js"
7
- import type { ConnectMessage } from "@notionhq/custom-blocks-protocol/messages/connect.js"
8
7
  import type { ContrastModeChangedMessage } from "@notionhq/custom-blocks-protocol/messages/contrastModeChanged.js"
9
8
  import type { CreatePageMessage } from "@notionhq/custom-blocks-protocol/messages/createPage.js"
10
9
  import type { CreatePageResultMessage } from "@notionhq/custom-blocks-protocol/messages/createPageResult.js"
@@ -19,7 +18,6 @@ import type {
19
18
  } from "@notionhq/custom-blocks-protocol/messages/getUser.js"
20
19
  import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
21
20
  import type { CustomBlockInitErrorCode } from "@notionhq/custom-blocks-protocol/messages/init.js"
22
- import type { InitResultMessage } from "@notionhq/custom-blocks-protocol/messages/initResult.js"
23
21
  import type {
24
22
  ListUsersMessage,
25
23
  ListUsersResultMessage,
@@ -41,7 +39,6 @@ import * as v from "valibot"
41
39
  import { initErrorForFailureReason } from "./lifecycle/initErrors.js"
42
40
  import { createInitializationController } from "./lifecycle/initializationController.js"
43
41
  import type {
44
- ConnectSuccessMessage,
45
42
  CustomBlockHostDataSourcesPayload,
46
43
  CustomBlockHostInitialState,
47
44
  } from "./lifecycle/types.js"
@@ -68,19 +65,22 @@ export type CustomBlockHostOptions = {
68
65
  noInitResultTimeoutMs?: number
69
66
  initialState: CustomBlockHostInitialState
70
67
  handlers: CustomBlockHostHandlers
71
- onConnect?: (message: ConnectSuccessMessage) => void
72
- onInitResult?: (message: InitResultMessage) => void
68
+ /** Called as soon as the `manifest` is available after `connect`, before `onInitialized` and binding validation. */
69
+ onManifest?: (manifest: CustomBlockManifest) => void
70
+ /** Callback fired when the initialization handshake with the sandbox completed successfully. */
71
+ onInitialized?: (state: {
72
+ manifest: CustomBlockManifest
73
+ initialHeight?: number
74
+ }) => void
75
+ /** Callback fired when the initialization handshake with the sandbox failed or timed out. */
73
76
  onInitializationFailure?: (
74
77
  error: CustomBlockHostInitializationFailure,
75
78
  ) => void
76
79
  /**
77
- * A `connect` arrived after this host's initialization settled the
78
- * document was replaced, or its first `connect` came after the host
79
- * stopped waiting. Either way the host cannot serve it: the embedder
80
- * should recreate host and iframe together so the document can run a
81
- * fresh handshake.
80
+ * Callback fired when the current host and iframe must restart.
81
+ * Recreate both to continue.
82
82
  */
83
- onIgnoredConnect?: (message: ConnectMessage) => void
83
+ onRestartRequired?: () => void
84
84
  onLog?: (direction: CustomBlockHostLogDirection, payload: unknown) => void
85
85
  }
86
86
 
@@ -107,10 +107,10 @@ export function createCustomBlockHost(
107
107
  iframe,
108
108
  initialState,
109
109
  handlers,
110
- onConnect,
111
- onInitResult,
110
+ onManifest,
111
+ onInitialized,
112
112
  onInitializationFailure,
113
- onIgnoredConnect,
113
+ onRestartRequired,
114
114
  onLog,
115
115
  sandboxOrigin,
116
116
  minBridgeProtocolVersion: configuredMinBridgeProtocolVersion,
@@ -322,6 +322,13 @@ export function createCustomBlockHost(
322
322
  }),
323
323
  prepareInit(connect) {
324
324
  manifest = connect.manifest ?? initialState.manifest
325
+ if (manifest === undefined) {
326
+ return {
327
+ status: "error",
328
+ error: initErrorForFailureReason("manifest_unavailable"),
329
+ }
330
+ }
331
+ onManifest?.(manifest)
325
332
  const resolvedDataSources =
326
333
  typeof dataSources === "function" ? dataSources(connect) : dataSources
327
334
  const bindingErrorCode = getBindingErrorCode(
@@ -347,9 +354,12 @@ export function createCustomBlockHost(
347
354
  minBridgeProtocolVersion,
348
355
  noConnectTimeoutMs,
349
356
  noInitResultTimeoutMs,
350
- onConnect,
351
- onInitResult,
352
- onIgnoredConnect,
357
+ onInitResult(message) {
358
+ if (message.status === "success" && manifest !== undefined) {
359
+ onInitialized?.({ manifest, initialHeight: message.initialHeight })
360
+ }
361
+ },
362
+ onRestartRequired,
353
363
  onInitializationFailure,
354
364
  })
355
365
 
@@ -524,6 +534,7 @@ export function createCustomBlockHost(
524
534
  setDataSources(nextDataSources) {
525
535
  if (
526
536
  initializationController.hasConnectedSandbox &&
537
+ manifest !== undefined &&
527
538
  getBindingErrorCode(manifest, nextDataSources) !== undefined
528
539
  ) {
529
540
  emit("warn", {
@@ -53,9 +53,8 @@ export type InitializationControllerOptions = {
53
53
  minBridgeProtocolVersion: number
54
54
  noConnectTimeoutMs?: number
55
55
  noInitResultTimeoutMs?: number
56
- onConnect?: (message: ConnectSuccessMessage) => void
57
56
  onInitResult?: (message: InitResultMessage) => void
58
- onIgnoredConnect?: (message: ConnectMessage) => void
57
+ onRestartRequired?: () => void
59
58
  onInitializationFailure?: (
60
59
  error: CustomBlockHostInitializationFailure,
61
60
  ) => void
@@ -85,9 +84,8 @@ export function createInitializationController(
85
84
  minBridgeProtocolVersion,
86
85
  noConnectTimeoutMs = DEFAULT_NO_CONNECT_TIMEOUT_MS,
87
86
  noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS,
88
- onConnect,
89
87
  onInitResult,
90
- onIgnoredConnect,
88
+ onRestartRequired,
91
89
  onInitializationFailure,
92
90
  } = options
93
91
 
@@ -134,7 +132,6 @@ export function createInitializationController(
134
132
  if (initStatus !== "waitingForConnect") {
135
133
  return false
136
134
  }
137
- onConnect?.(connect)
138
135
  const preparation = prepareInit(connect)
139
136
  if (preparation.status === "error") {
140
137
  sendInitError(connect.initializationId, preparation.error)
@@ -185,7 +182,7 @@ export function createInitializationController(
185
182
  "Ignored connect after initialization settled; recreate the host and iframe to re-initialize",
186
183
  initializationId: message.initializationId,
187
184
  })
188
- onIgnoredConnect?.(message)
185
+ onRestartRequired?.()
189
186
  return
190
187
  }
191
188
 
@@ -34,7 +34,11 @@ export type CustomBlockHostInitialState = {
34
34
  blockId: NotionBlockId
35
35
  parent: NotionParent
36
36
  page: CustomBlockPage
37
- manifest: CustomBlockManifest
37
+ /**
38
+ * The host's persisted manifest, when one exists. If omitted, the host
39
+ * requires the sandbox to provide a manifest in `connect`.
40
+ */
41
+ manifest?: CustomBlockManifest
38
42
  dataSources: CustomBlockHostInitialDataSources
39
43
  currentUser: NotionUser
40
44
  }