@notionhq/custom-blocks 0.0.63 → 0.0.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/HOST.md +16 -16
  2. package/README.md +2 -2
  3. package/bin/cli/create.js +3 -3
  4. package/dist/bridge/SandboxBridge.js +17 -17
  5. package/dist/bridge/loadManifest.d.ts +2 -7
  6. package/dist/bridge/loadManifest.d.ts.map +1 -1
  7. package/dist/bridge/loadManifest.js +36 -18
  8. package/dist/bridge/messages/ready.d.ts +1 -0
  9. package/dist/bridge/messages/ready.d.ts.map +1 -1
  10. package/dist/bridge/messages/ready.js +8 -2
  11. package/dist/bridge/messages/sandboxToHost.d.ts +1 -0
  12. package/dist/bridge/messages/sandboxToHost.d.ts.map +1 -1
  13. package/dist/host/createCustomBlockHost.d.ts.map +1 -1
  14. package/dist/host/createCustomBlockHost.js +24 -13
  15. package/dist/host/lifecycle/initErrors.d.ts +4 -1
  16. package/dist/host/lifecycle/initErrors.d.ts.map +1 -1
  17. package/dist/host/lifecycle/initErrors.js +16 -5
  18. package/dist/host/lifecycle/ready.d.ts +1 -7
  19. package/dist/host/lifecycle/ready.d.ts.map +1 -1
  20. package/dist/host/lifecycle/ready.js +1 -22
  21. package/dist/init.js +1 -1
  22. package/dist/react/NotionCustomBlock.css +47 -47
  23. package/dist/react/NotionCustomBlock.js +4 -4
  24. package/dist/utils.js +1 -1
  25. package/dist/version.d.ts +1 -1
  26. package/dist/version.d.ts.map +1 -1
  27. package/dist/version.js +1 -1
  28. package/docs/block-location.md +1 -1
  29. package/docs/manifest.md +2 -2
  30. package/docs/pages.md +1 -1
  31. package/package.json +1 -1
  32. package/src/bridge/SandboxBridge.ts +17 -17
  33. package/src/bridge/loadManifest.ts +44 -23
  34. package/src/bridge/messages/ready.ts +9 -2
  35. package/src/host/createCustomBlockHost.ts +30 -14
  36. package/src/host/lifecycle/initErrors.ts +19 -4
  37. package/src/host/lifecycle/ready.ts +1 -34
  38. package/src/init.ts +1 -1
  39. package/src/react/NotionCustomBlock.css +47 -47
  40. package/src/react/NotionCustomBlock.tsx +14 -10
  41. package/src/utils.ts +1 -1
  42. package/src/version.ts +1 -1
package/HOST.md CHANGED
@@ -49,7 +49,7 @@ Related types: `CustomBlockHostOptions`, `CustomBlockHostInitialState`, `CustomB
49
49
 
50
50
  The bridge is established with a handshake: the sandbox posts a `ready` when `initCustomBlock()` starts, and the host validates it and replies exactly once with `init`. Hosts should log and ignore duplicate `ready` messages instead of re-running the handshake. After that, narrower messages stream live state updates.
51
51
 
52
- The sandbox starts by sending `ready`. It declares the bridge protocol version, includes the custom blocks package semver as `sdkVersion` when available, and tells the host whether manifest discovery produced a usable manifest. Hosts should reject init when `ready` reports a manifest error, echoing the sandbox's error code:
52
+ The sandbox starts by sending `ready`. It declares the bridge protocol version, includes the custom blocks package semver as `sdkVersion` when available, and tells the host whether manifest discovery produced a usable manifest. Hosts should reject init when `ready` reports a manifest error, forwarding the sandbox's full error payload unchanged:
53
53
 
54
54
  ```ts
55
55
  // sandbox → host
@@ -59,7 +59,7 @@ The sandbox starts by sending `ready`. It declares the bridge protocol version,
59
59
  { type: "ready", status: "success", bridgeProtocolVersion, sdkVersion, manifest: null }
60
60
 
61
61
  // sandbox → host, manifest failed to load or validate
62
- { type: "ready", status: "error", bridgeProtocolVersion, sdkVersion, error: { code: "manifest_invalid", message: "..." } }
62
+ { type: "ready", status: "error", bridgeProtocolVersion, sdkVersion, error: { code: "manifest_invalid", message: "...", isRetryable: false } }
63
63
  ```
64
64
 
65
65
  The host replies with exactly one `init`, which has two statuses. On success it carries the full initial state — `theme`, `blockId`, `parent`, `page`, `currentUser` (the `NotionUser` shape from the user result messages), and `dataSources: { bindings }`:
@@ -150,21 +150,21 @@ Messages sent from the sandbox to the host. Parse `window` `message` events with
150
150
 
151
151
  Messages sent from the host to the sandbox. Same `{ wire type, type / schema, behavior }` shape; the type / schema column points to the payload shape.
152
152
 
153
- | Wire type | Type / schema | Behavior |
154
- | ----------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
155
- | `init` | `InitMessage` / `initMessageSchema` | Sent exactly once in response to `ready`; carries the success/error payload described under [Lifecycle](#lifecycle). |
156
- | `themeChanged` | `ThemeChangedMessage` / `themeChangedMessageSchema` | Replaces the current theme. |
157
- | `parentChanged` | `ParentChangedMessage` / `parentChangedMessageSchema` | Replaces the custom block parent without disturbing theme, block ID, page ID, user, or query state. |
158
- | `pageChanged` | `PageChangedMessage` / `pageChangedMessageSchema` | Replaces the nearest page ancestor without disturbing theme, block ID, parent, user, or query state. |
159
- | `currentUserChanged` | `CurrentUserChangedMessage` / `currentUserChangedMessageSchema` | Replaces the current viewer record. Send when any viewer field changes (name, avatar, email). |
160
- | `dataSourcesChanged` | `DataSourcesChangedMessage` / `dataSourcesChangedMessageSchema` | Replaces data-source bindings; the sandbox preserves cached query state for keys that still exist and drops removed keys. |
153
+ | Wire type | Type / schema | Behavior |
154
+ | ----------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
155
+ | `init` | `InitMessage` / `initMessageSchema` | Sent exactly once in response to `ready`; carries the success/error payload described under [Lifecycle](#lifecycle). |
156
+ | `themeChanged` | `ThemeChangedMessage` / `themeChangedMessageSchema` | Replaces the current theme. |
157
+ | `parentChanged` | `ParentChangedMessage` / `parentChangedMessageSchema` | Replaces the custom block parent without disturbing theme, block ID, page ID, user, or query state. |
158
+ | `pageChanged` | `PageChangedMessage` / `pageChangedMessageSchema` | Replaces the nearest page ancestor without disturbing theme, block ID, parent, user, or query state. |
159
+ | `currentUserChanged` | `CurrentUserChangedMessage` / `currentUserChangedMessageSchema` | Replaces the current viewer record. Send when any viewer field changes (name, avatar, email). |
160
+ | `dataSourcesChanged` | `DataSourcesChangedMessage` / `dataSourcesChangedMessageSchema` | Replaces data-source bindings; the sandbox preserves cached query state for keys that still exist and drops removed keys. |
161
161
  | `queryDataSourceResult` | `QueryDataSourceResultMessage` / `queryDataSourceResultMessageSchema` | `requestId` / `snapshotId`-keyed response with `status: "success"`, `items`, and `hasMore`, or `status: "error"` and `error`. |
162
- | `createPageResult` | `CreatePageResultMessage` / `createPageResultMessageSchema` | `requestId`-keyed page response with `status: "success"` or `status: "error"`. |
163
- | `getPageResult` | `GetPageResultMessage` / `getPageResultMessageSchema` | Same success/error shape as `createPageResult`. |
164
- | `updatePageResult` | `UpdatePageResultMessage` / `updatePageResultMessageSchema` | Same success/error shape as `createPageResult`. |
165
- | `getUserResult` | `GetUserResultMessage` / `getUserResultMessageSchema` | `requestId`-keyed user response with `status: "success"` or `status: "error"`. |
166
- | `listUsersResult` | `ListUsersResultMessage` / `listUsersResultMessageSchema` | Same success/error shape as `getUserResult`. |
167
- | `invalidSandboxMessage` | `InvalidSandboxMessage` / `invalidSandboxMessageSchema` | Host-side NACK for a sandbox message it could not parse. |
162
+ | `createPageResult` | `CreatePageResultMessage` / `createPageResultMessageSchema` | `requestId`-keyed page response with `status: "success"` or `status: "error"`. |
163
+ | `getPageResult` | `GetPageResultMessage` / `getPageResultMessageSchema` | Same success/error shape as `createPageResult`. |
164
+ | `updatePageResult` | `UpdatePageResultMessage` / `updatePageResultMessageSchema` | Same success/error shape as `createPageResult`. |
165
+ | `getUserResult` | `GetUserResultMessage` / `getUserResultMessageSchema` | `requestId`-keyed user response with `status: "success"` or `status: "error"`. |
166
+ | `listUsersResult` | `ListUsersResultMessage` / `listUsersResultMessageSchema` | Same success/error shape as `getUserResult`. |
167
+ | `invalidSandboxMessage` | `InvalidSandboxMessage` / `invalidSandboxMessageSchema` | Host-side NACK for a sandbox message it could not parse. |
168
168
 
169
169
  `HostToSandboxMessage` / `hostToSandboxMessageSchema` is the discriminated union over all of the above.
170
170
 
package/README.md CHANGED
@@ -3,9 +3,9 @@
3
3
  > [!NOTE]
4
4
  > Nothing to see here. This is **extreme** alpha and currently only works for an unreleased product.
5
5
 
6
- SDK for building Notion custom view blocks.
6
+ SDK for building Notion custom blocks.
7
7
 
8
- A custom view runs as a sandboxed `<iframe>` inside a Notion block on iOS, Android, and desktop. The only channel between your view and Notion is a `postMessage` bridge — this SDK wraps it in typed React hooks plus a small framework-neutral runtime API for non-React renderers.
8
+ A custom block runs as a sandboxed `<iframe>` inside a Notion block on iOS, Android, and desktop. The only channel between your block and Notion is a `postMessage` bridge — this SDK wraps it in typed React hooks plus a small framework-neutral runtime API for non-React renderers.
9
9
 
10
10
  > **Pre-release.** Breaking changes may land at any time before 1.0.
11
11
 
package/bin/cli/create.js CHANGED
@@ -69,7 +69,7 @@ function createCustomBlock(pageId) {
69
69
  *
70
70
  * - No `--parent` → create a private page, then a block under it.
71
71
  * - Page `--parent` → create a block under that page.
72
- * - Database/data source `--parent` → a custom *view* under a collection. The
72
+ * - Database/data source `--parent` → a custom block as a collection view. The
73
73
  * public API has no `custom` view type and the parent-nesting the backend
74
74
  * uses isn't derivable from this repo, so we error rather than guess.
75
75
  */
@@ -82,8 +82,8 @@ export function create(options) {
82
82
  const parentUuid = formatUuid(extractId(options.parent));
83
83
  const kind = classifyParent(parentUuid);
84
84
  if (kind === "database" || kind === "data_source") {
85
- console.error(`Error: --parent ${parentUuid} is a ${kind.replace("_", " ")}. Creating a custom view under a collection isn't supported yet.\n`);
86
- console.error(" The public API has no `custom` view type; the backend does the parent nesting. Create the view in Notion, then run `ncblock attach <block-id-or-url>`.");
85
+ console.error(`Error: --parent ${parentUuid} is a ${kind.replace("_", " ")}. Creating a custom block as a collection view isn't supported yet.\n`);
86
+ console.error(" The public API has no `custom` view type; the backend does the parent nesting. Create the custom block in Notion, then run `ncblock attach <block-id-or-url>`.");
87
87
  process.exit(1);
88
88
  }
89
89
  pageId = parentUuid;
@@ -1,6 +1,6 @@
1
1
  import * as v from "valibot";
2
2
  import { unreachable } from "../utils.js";
3
- import { NCBLOCK_SDK_VERSION } from "../version.js";
3
+ import { CUSTOM_BLOCKS_SDK_VERSION } from "../version.js";
4
4
  import { resolveDataSources } from "./dataSources/resolve.js";
5
5
  import { resolvePropertyWriteMapForDataSource } from "./dataSources/resolveProperty.js";
6
6
  import { createEmptyDataSourceQueryState, } from "./hostState.js";
@@ -43,7 +43,7 @@ export class SandboxBridge {
43
43
  }
44
44
  };
45
45
  this.handleMessage = (event) => {
46
- console.debug("[notion-custom-sdk] incoming postMessage", {
46
+ console.debug("[custom-blocks-sdk] incoming postMessage", {
47
47
  data: event.data,
48
48
  fromParent: event.source === window.parent,
49
49
  });
@@ -53,7 +53,7 @@ export class SandboxBridge {
53
53
  this.logMessage("received", event.data);
54
54
  const parsed = v.safeParse(hostToSandboxMessageSchema, event.data);
55
55
  if (!parsed.success) {
56
- console.warn("[notion-custom-sdk] ignoring malformed host message", parsed.issues);
56
+ console.warn("[custom-blocks-sdk] ignoring malformed host message", parsed.issues);
57
57
  const incomingType = readIncomingType(event.data);
58
58
  // Loop guard: never NACK a NACK. Excludes both directions —
59
59
  // `invalidSandboxMessage` is what the host normally sends, but
@@ -75,7 +75,7 @@ export class SandboxBridge {
75
75
  // visibility and stop. We don't retry as the sandbox can't recover from a host-side parse
76
76
  // failure on its own.
77
77
  if (message.type === "invalidSandboxMessage") {
78
- console.warn("[notion-custom-sdk] host reported invalid sandbox message:", message.reason);
78
+ console.warn("[custom-blocks-sdk] host reported invalid sandbox message:", message.reason);
79
79
  return;
80
80
  }
81
81
  // `init` is the only message valid before initialization. Handle it up
@@ -88,7 +88,7 @@ export class SandboxBridge {
88
88
  // below. Reading `this.hostState` repeatedly would re-widen it.
89
89
  const hostState = this.hostState;
90
90
  if (hostState.status !== "initialized") {
91
- console.warn(`[notion-custom-sdk] ignoring ${message.type} before init`);
91
+ console.warn(`[custom-blocks-sdk] ignoring ${message.type} before init`);
92
92
  return;
93
93
  }
94
94
  switch (message.type) {
@@ -157,7 +157,7 @@ export class SandboxBridge {
157
157
  ? { status: "success", page: message.page }
158
158
  : { status: "error", error: message.error };
159
159
  if (!this.pendingCreatePage.resolve(message.requestId, result)) {
160
- console.warn(`[notion-custom-sdk] createPageResult for unknown requestId ${message.requestId}`);
160
+ console.warn(`[custom-blocks-sdk] createPageResult for unknown requestId ${message.requestId}`);
161
161
  }
162
162
  return;
163
163
  }
@@ -166,7 +166,7 @@ export class SandboxBridge {
166
166
  ? { status: "success", page: message.page }
167
167
  : { status: "error", error: message.error };
168
168
  if (!this.pendingGetPage.resolve(message.requestId, result)) {
169
- console.warn(`[notion-custom-sdk] getPageResult for unknown requestId ${message.requestId}`);
169
+ console.warn(`[custom-blocks-sdk] getPageResult for unknown requestId ${message.requestId}`);
170
170
  }
171
171
  return;
172
172
  }
@@ -175,7 +175,7 @@ export class SandboxBridge {
175
175
  ? { status: "success", user: message.user }
176
176
  : { status: "error", error: message.error };
177
177
  if (!this.pendingGetUser.resolve(message.requestId, result)) {
178
- console.warn(`[notion-custom-sdk] getUserResult for unknown requestId ${message.requestId}`);
178
+ console.warn(`[custom-blocks-sdk] getUserResult for unknown requestId ${message.requestId}`);
179
179
  }
180
180
  return;
181
181
  }
@@ -184,7 +184,7 @@ export class SandboxBridge {
184
184
  ? { status: "success", list: message.list }
185
185
  : { status: "error", error: message.error };
186
186
  if (!this.pendingListUsers.resolve(message.requestId, result)) {
187
- console.warn(`[notion-custom-sdk] listUsersResult for unknown requestId ${message.requestId}`);
187
+ console.warn(`[custom-blocks-sdk] listUsersResult for unknown requestId ${message.requestId}`);
188
188
  }
189
189
  return;
190
190
  }
@@ -193,7 +193,7 @@ export class SandboxBridge {
193
193
  ? { status: "success", page: message.page }
194
194
  : { status: "error", error: message.error };
195
195
  if (!this.pendingUpdatePage.resolve(message.requestId, result)) {
196
- console.warn(`[notion-custom-sdk] updatePageResult for unknown requestId ${message.requestId}`);
196
+ console.warn(`[custom-blocks-sdk] updatePageResult for unknown requestId ${message.requestId}`);
197
197
  }
198
198
  return;
199
199
  }
@@ -300,7 +300,7 @@ export class SandboxBridge {
300
300
  return;
301
301
  }
302
302
  if (this.hasSentReady) {
303
- console.warn("[notion-custom-sdk] ignoring duplicate ready message");
303
+ console.warn("[custom-blocks-sdk] ignoring duplicate ready message");
304
304
  return;
305
305
  }
306
306
  const { manifest, error } = manifestResult;
@@ -311,20 +311,20 @@ export class SandboxBridge {
311
311
  type: "ready",
312
312
  status: "error",
313
313
  bridgeProtocolVersion: CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION,
314
- sdkVersion: NCBLOCK_SDK_VERSION,
314
+ sdkVersion: CUSTOM_BLOCKS_SDK_VERSION,
315
315
  error,
316
316
  }
317
317
  : {
318
318
  type: "ready",
319
319
  status: "success",
320
320
  bridgeProtocolVersion: CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION,
321
- sdkVersion: NCBLOCK_SDK_VERSION,
321
+ sdkVersion: CUSTOM_BLOCKS_SDK_VERSION,
322
322
  manifest,
323
323
  };
324
324
  this.postToHost(readyMessage);
325
325
  }
326
326
  postToHost(message) {
327
- console.debug("[notion-custom-sdk] outbound postMessage", message);
327
+ console.debug("[custom-blocks-sdk] outbound postMessage", message);
328
328
  this.logMessage("sent", message);
329
329
  window.parent.postMessage(message, "*");
330
330
  }
@@ -361,7 +361,7 @@ export class SandboxBridge {
361
361
  // because not every consumer renders the error UI, and we want the failure visible in
362
362
  // the browser console either way. We deliberately don't NACK the host or post anything
363
363
  // back over the bridge since the host has already given up.
364
- console.error(`[notion-custom-sdk] host reported init error (${message.error.code}): ${message.error.message}`);
364
+ console.error(`[custom-blocks-sdk] host reported init error (${message.error.code}): ${message.error.message}`);
365
365
  if (this.rejectInit) {
366
366
  this.rejectInit(new CustomBlockInitError(message.error));
367
367
  this.resolveInit = undefined;
@@ -705,11 +705,11 @@ function resolveDataSourceQueryLimit(limit) {
705
705
  return DEFAULT_DATA_SOURCE_QUERY_LIMIT;
706
706
  }
707
707
  if (!Number.isFinite(limit) || !Number.isInteger(limit) || limit < 1) {
708
- console.warn(`[notion-custom-sdk] useDataSource limit must be a positive integer; using ${DEFAULT_DATA_SOURCE_QUERY_LIMIT}.`);
708
+ console.warn(`[custom-blocks-sdk] useDataSource limit must be a positive integer; using ${DEFAULT_DATA_SOURCE_QUERY_LIMIT}.`);
709
709
  return DEFAULT_DATA_SOURCE_QUERY_LIMIT;
710
710
  }
711
711
  if (limit > MAX_DATA_SOURCE_QUERY_LIMIT) {
712
- console.warn(`[notion-custom-sdk] useDataSource limit is capped at ${MAX_DATA_SOURCE_QUERY_LIMIT}.`);
712
+ console.warn(`[custom-blocks-sdk] useDataSource limit is capped at ${MAX_DATA_SOURCE_QUERY_LIMIT}.`);
713
713
  return MAX_DATA_SOURCE_QUERY_LIMIT;
714
714
  }
715
715
  return limit;
@@ -1,12 +1,7 @@
1
- import * as v from "valibot";
1
+ import type { CustomBlockErrorInfo } from "./errors.js";
2
2
  import { type CustomBlockManifest } from "./manifest.js";
3
- export declare const customBlockReadyErrorCodeSchema: v.StringSchema<undefined>;
4
3
  export type CustomBlockReadyErrorCode = "manifest_unavailable" | "manifest_invalid" | (string & {});
5
- export declare const customBlockReadyErrorSchema: v.ObjectSchema<{
6
- readonly code: v.StringSchema<undefined>;
7
- readonly message: v.StringSchema<undefined>;
8
- }, undefined>;
9
- export type CustomBlockReadyError = v.InferOutput<typeof customBlockReadyErrorSchema>;
4
+ export type CustomBlockReadyError = CustomBlockErrorInfo<CustomBlockReadyErrorCode>;
10
5
  export type ManifestLoadResult = {
11
6
  manifest: CustomBlockManifest | null;
12
7
  error?: undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"loadManifest.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/bridge/loadManifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,KAAK,mBAAmB,EAAkB,MAAM,eAAe,CAAA;AAMxE,eAAO,MAAM,+BAA+B,2BAAa,CAAA;AAEzD,MAAM,MAAM,yBAAyB,GAClC,sBAAsB,GACtB,kBAAkB,GAClB,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;AAEhB,eAAO,MAAM,2BAA2B;;;aAGtC,CAAA;AAEF,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,WAAW,CAChD,OAAO,2BAA2B,CAClC,CAAA;AAED,MAAM,MAAM,kBAAkB,GAC3B;IACA,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAA;IACpC,KAAK,CAAC,EAAE,SAAS,CAAA;CAChB,GACD;IACA,QAAQ,EAAE,IAAI,CAAA;IACd,KAAK,EAAE,qBAAqB,CAAA;CAC3B,CAAA;AAEJ;;;;;;GAMG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAgDhE"}
1
+ {"version":3,"file":"loadManifest.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/bridge/loadManifest.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,EAAE,KAAK,mBAAmB,EAAkB,MAAM,eAAe,CAAA;AAIxE,MAAM,MAAM,yBAAyB,GAClC,sBAAsB,GACtB,kBAAkB,GAClB,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;AAEhB,MAAM,MAAM,qBAAqB,GAChC,oBAAoB,CAAC,yBAAyB,CAAC,CAAA;AAEhD,MAAM,MAAM,kBAAkB,GAC3B;IACA,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAA;IACpC,KAAK,CAAC,EAAE,SAAS,CAAA;CAChB,GACD;IACA,QAAQ,EAAE,IAAI,CAAA;IACd,KAAK,EAAE,qBAAqB,CAAA;CAC3B,CAAA;AAEJ;;;;;;GAMG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAkEhE"}
@@ -1,13 +1,6 @@
1
1
  import * as v from "valibot";
2
2
  import { manifestSchema } from "./manifest.js";
3
3
  const MANIFEST_URL = "custom_blocks.json";
4
- // The schema accepts any string code. The type lists known codes for
5
- // autocomplete, with an open string fallback for newer senders.
6
- export const customBlockReadyErrorCodeSchema = v.string();
7
- export const customBlockReadyErrorSchema = v.object({
8
- code: customBlockReadyErrorCodeSchema,
9
- message: v.string(),
10
- });
11
4
  /**
12
5
  * Attempts to load a `custom_blocks.json` manifest co-located with the bundle.
13
6
  * A missing manifest means the block has no declared data requirements.
@@ -18,10 +11,10 @@ export const customBlockReadyErrorSchema = v.object({
18
11
  export async function loadManifest() {
19
12
  if (typeof fetch !== "function") {
20
13
  const message = `No fetch API available; cannot load ${MANIFEST_URL}.`;
21
- console.warn(`[notion-custom-sdk] ${message}`);
14
+ console.warn(`[custom-blocks-sdk] ${message}`);
22
15
  return {
23
16
  manifest: null,
24
- error: { code: "manifest_unavailable", message },
17
+ error: { code: "manifest_unavailable", message, isRetryable: true },
25
18
  };
26
19
  }
27
20
  let response;
@@ -30,23 +23,30 @@ export async function loadManifest() {
30
23
  }
31
24
  catch (error) {
32
25
  const message = `Could not fetch ${MANIFEST_URL}.`;
33
- console.warn(`[notion-custom-sdk] ${message}`, error);
26
+ console.warn(`[custom-blocks-sdk] ${message}`, error);
34
27
  return {
35
28
  manifest: null,
36
- error: { code: "manifest_unavailable", message },
29
+ // Consider this retryable because it may have just been a network issue. Because `init`
30
+ // errors are considered terminal, the host should "retry" by reloading the iframe and
31
+ // sending another `ready` message.
32
+ error: { code: "manifest_unavailable", message, isRetryable: true },
37
33
  };
38
34
  }
39
35
  if (response.status === 404) {
40
36
  const message = `No manifest found at ${MANIFEST_URL} (status ${response.status}).`;
41
- console.warn(`[notion-custom-sdk] ${message}`);
37
+ console.warn(`[custom-blocks-sdk] ${message}`);
42
38
  return { manifest: null };
43
39
  }
44
40
  if (!response.ok) {
45
41
  const message = `Could not fetch ${MANIFEST_URL} (status ${response.status}).`;
46
- console.warn(`[notion-custom-sdk] ${message}`);
42
+ console.warn(`[custom-blocks-sdk] ${message}`);
47
43
  return {
48
44
  manifest: null,
49
- error: { code: "manifest_unavailable", message },
45
+ error: {
46
+ code: "manifest_unavailable",
47
+ message,
48
+ isRetryable: isRetryableManifestHttpStatus(response.status),
49
+ },
50
50
  };
51
51
  }
52
52
  let json;
@@ -55,14 +55,32 @@ export async function loadManifest() {
55
55
  }
56
56
  catch (error) {
57
57
  const message = `Manifest at ${MANIFEST_URL} was not valid JSON.`;
58
- console.warn(`[notion-custom-sdk] ${message}`, error);
59
- return { manifest: null, error: { code: "manifest_invalid", message } };
58
+ console.warn(`[custom-blocks-sdk] ${message}`, error);
59
+ return {
60
+ manifest: null,
61
+ // Consider invalid manifests as unrecoverable errors.
62
+ error: { code: "manifest_invalid", message, isRetryable: false },
63
+ };
60
64
  }
61
65
  const parsed = v.safeParse(manifestSchema, json);
62
66
  if (!parsed.success) {
63
67
  const message = `Manifest at ${MANIFEST_URL} did not match schema.`;
64
- console.warn(`[notion-custom-sdk] ${message}`, parsed.issues);
65
- return { manifest: null, error: { code: "manifest_invalid", message } };
68
+ console.warn(`[custom-blocks-sdk] ${message}`, parsed.issues);
69
+ return {
70
+ manifest: null,
71
+ // Consider invalid manifests as unrecoverable errors.
72
+ error: { code: "manifest_invalid", message, isRetryable: false },
73
+ };
66
74
  }
67
75
  return { manifest: parsed.output };
68
76
  }
77
+ function isRetryableManifestHttpStatus(status) {
78
+ // Consider these statuses retryable because they may reflect transient failures.
79
+ return (status === 408 || // Request Timeout
80
+ status === 425 || // Too Early
81
+ status === 500 || // Internal Server Error
82
+ status === 502 || // Bad Gateway
83
+ status === 503 || // Service Unavailable
84
+ status === 504 // Gateway Timeout
85
+ );
86
+ }
@@ -39,6 +39,7 @@ export declare const readyMessageSchema: v.UnionSchema<[v.ObjectSchema<{
39
39
  readonly error: v.ObjectSchema<{
40
40
  readonly code: v.StringSchema<undefined>;
41
41
  readonly message: v.StringSchema<undefined>;
42
+ readonly isRetryable: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
42
43
  }, undefined>;
43
44
  }, undefined>], undefined>;
44
45
  export type ReadyMessage = v.InferOutput<typeof readyMessageSchema>;
@@ -1 +1 @@
1
- {"version":3,"file":"ready.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/bridge/messages/ready.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAmB5B;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;;IAG7B;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BASH,CAAA;AAEF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,kBAAkB,CAAC,CAAA"}
1
+ {"version":3,"file":"ready.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/bridge/messages/ready.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AA0B5B;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;;IAG7B;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BASH,CAAA;AAEF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,kBAAkB,CAAC,CAAA"}
@@ -1,5 +1,4 @@
1
1
  import * as v from "valibot";
2
- import { customBlockReadyErrorSchema } from "../loadManifest.js";
3
2
  import { manifestSchema } from "../manifest.js";
4
3
  const readyMessageCommonEntries = {
5
4
  type: v.literal("ready"),
@@ -15,6 +14,13 @@ const readyMessageCommonEntries = {
15
14
  */
16
15
  sdkVersion: v.string(),
17
16
  };
17
+ // TODO(custom-blocks): Replace with `customBlockErrorInfoSchema` which makes `isRetryable` required
18
+ // once min supported bridge protocol version is bumped to 3.
19
+ const readyErrorSchema = v.object({
20
+ code: v.string(),
21
+ message: v.string(),
22
+ isRetryable: v.optional(v.boolean(), false),
23
+ });
18
24
  /**
19
25
  * First message the sandbox sends after mount, kicking off the bridge handshake. The host replies
20
26
  * with `init`.
@@ -31,7 +37,7 @@ export const readyMessageSchema = v.union([
31
37
  }),
32
38
  v.object({
33
39
  status: v.literal("error"),
34
- error: customBlockReadyErrorSchema,
40
+ error: readyErrorSchema,
35
41
  ...readyMessageCommonEntries,
36
42
  }),
37
43
  ]);
@@ -35,6 +35,7 @@ export declare const sandboxToHostMessageSchema: v.UnionSchema<[v.UnionSchema<[v
35
35
  readonly error: v.ObjectSchema<{
36
36
  readonly code: v.StringSchema<undefined>;
37
37
  readonly message: v.StringSchema<undefined>;
38
+ readonly isRetryable: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
38
39
  }, undefined>;
39
40
  }, undefined>], undefined>, v.ObjectSchema<{
40
41
  readonly type: v.LiteralSchema<"queryDataSource", undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"sandboxToHost.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/bridge/messages/sandboxToHost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAW5B;;;GAGG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAUrC,CAAA;AAEF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,WAAW,CAC/C,OAAO,0BAA0B,CACjC,CAAA"}
1
+ {"version":3,"file":"sandboxToHost.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/bridge/messages/sandboxToHost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAW5B;;;GAGG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAUrC,CAAA;AAEF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,WAAW,CAC/C,OAAO,0BAA0B,CACjC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/host/createCustomBlockHost.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAY1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAA;AAkB/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAIzD,OAAO,KAAK,EACX,iCAAiC,EACjC,2BAA2B,EAE3B,mBAAmB,EACnB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,KAAK,EACX,uBAAuB,EACvB,2BAA2B,EAC3B,MAAM,qBAAqB,CAAA;AAE5B,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,oBAAoB,CAAA;AAE9E,MAAM,MAAM,sBAAsB,GAAG;IACpC,MAAM,EAAE,iBAAiB,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,YAAY,EAAE,2BAA2B,CAAA;IACzC,QAAQ,EAAE,uBAAuB,CAAA;IACjC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAA;IAChD,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,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;QACpB,YAAY,EAAE,kBAAkB,CAAA;QAChC,QAAQ,EAAE,oCAAoC,CAAA;KAC9C,KAAK,IAAI,CAAA;CACV,CAAA;AAKD,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,sBAAsB,GAC7B,qBAAqB,CAuTvB"}
1
+ {"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/host/createCustomBlockHost.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAY1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAA;AAkB/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAIzD,OAAO,KAAK,EACX,iCAAiC,EACjC,2BAA2B,EAE3B,mBAAmB,EACnB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,KAAK,EACX,uBAAuB,EACvB,2BAA2B,EAC3B,MAAM,qBAAqB,CAAA;AAE5B,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,oBAAoB,CAAA;AAE9E,MAAM,MAAM,sBAAsB,GAAG;IACpC,MAAM,EAAE,iBAAiB,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,YAAY,EAAE,2BAA2B,CAAA;IACzC,QAAQ,EAAE,uBAAuB,CAAA;IACjC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAA;IAChD,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,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;QACpB,YAAY,EAAE,kBAAkB,CAAA;QAChC,QAAQ,EAAE,oCAAoC,CAAA;KAC9C,KAAK,IAAI,CAAA;CACV,CAAA;AAKD,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,sBAAsB,GAC7B,qBAAqB,CAuUvB"}
@@ -2,7 +2,7 @@ import * as v from "valibot";
2
2
  import { sandboxToHostMessageSchema } from "../bridge/messages/sandboxToHost.js";
3
3
  import { unreachable } from "../utils.js";
4
4
  import { initErrorForFailureReason } from "./lifecycle/initErrors.js";
5
- import { getReadyInitFailure } from "./lifecycle/ready.js";
5
+ import { isValidBridgeProtocolVersion } from "./lifecycle/ready.js";
6
6
  import { getInvalidSandboxMessageResponse } from "./messages/invalidSandboxMessage.js";
7
7
  import { QuerySubscriptions } from "./queries/querySubscriptions.js";
8
8
  const DEFAULT_NO_READY_TIMEOUT_MS = 5000;
@@ -65,26 +65,23 @@ export function createCustomBlockHost(options) {
65
65
  post(message);
66
66
  return true;
67
67
  }
68
- function sendInitError(code, message) {
68
+ function sendInitError(error) {
69
69
  if (initStatus !== "pending") {
70
70
  return;
71
71
  }
72
72
  initStatus = "error";
73
73
  clearNoReadyTimeout();
74
- const error = initErrorForFailureReason(code);
75
74
  post({
76
75
  type: "init",
77
76
  status: "error",
78
- error: {
79
- ...error,
80
- message: message ?? error.message,
81
- },
77
+ error,
82
78
  });
83
79
  }
84
80
  function onIframeLoad() {
85
81
  clearNoReadyTimeout();
86
82
  noReadyTimeoutId = window.setTimeout(() => {
87
- sendInitError("no_ready");
83
+ const initError = initErrorForFailureReason("no_ready");
84
+ sendInitError(initError);
88
85
  }, noReadyTimeoutMs);
89
86
  }
90
87
  async function handleQuery(message) {
@@ -157,12 +154,25 @@ export function createCustomBlockHost(options) {
157
154
  post(result);
158
155
  }
159
156
  function handleReady(message) {
160
- const failure = getReadyInitFailure(message, minBridgeProtocolVersion);
161
- if (failure !== undefined) {
162
- sendInitError(failure.code, failure.message);
157
+ // Verify the bridge protocol version is valid.
158
+ if (!isValidBridgeProtocolVersion(message.bridgeProtocolVersion)) {
159
+ const initError = initErrorForFailureReason("invalid_protocol_version", {
160
+ currentBridgeProtocolVersion: message.bridgeProtocolVersion,
161
+ });
162
+ sendInitError(initError);
163
+ return;
164
+ }
165
+ if (message.bridgeProtocolVersion < minBridgeProtocolVersion) {
166
+ const initError = initErrorForFailureReason("unsupported_protocol_version", {
167
+ currentBridgeProtocolVersion: message.bridgeProtocolVersion,
168
+ minBridgeProtocolVersion,
169
+ });
170
+ sendInitError(initError);
163
171
  return;
164
172
  }
165
- if (message.status !== "success") {
173
+ // If the sandbox reported an error during `ready`, return it as an `init` error.
174
+ if (message.status === "error") {
175
+ sendInitError(message.error);
166
176
  return;
167
177
  }
168
178
  if (sendInit(message)) {
@@ -177,7 +187,8 @@ export function createCustomBlockHost(options) {
177
187
  });
178
188
  const response = getInvalidSandboxMessageResponse(data);
179
189
  if (initStatus === "pending" && response.incomingType === "ready") {
180
- sendInitError("invalid_ready");
190
+ const initError = initErrorForFailureReason("invalid_ready");
191
+ sendInitError(initError);
181
192
  }
182
193
  else if (response.nack !== undefined) {
183
194
  post(response.nack);
@@ -6,5 +6,8 @@ import type { CustomBlockInitErrorCode, CustomBlockInitErrorInfo } from "../../b
6
6
  * `ready` message. `isRetryable` means a fresh iframe / `init` attempt could plausibly succeed
7
7
  * without changing block code or host configuration.
8
8
  */
9
- export declare function initErrorForFailureReason(reason: CustomBlockInitErrorCode): CustomBlockInitErrorInfo;
9
+ export declare function initErrorForFailureReason(reason: CustomBlockInitErrorCode, data?: {
10
+ minBridgeProtocolVersion?: number;
11
+ currentBridgeProtocolVersion?: number;
12
+ }): CustomBlockInitErrorInfo;
10
13
  //# sourceMappingURL=initErrors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"initErrors.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/host/lifecycle/initErrors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,wBAAwB,EACxB,wBAAwB,EACxB,MAAM,+BAA+B,CAAA;AAKtC;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACxC,MAAM,EAAE,wBAAwB,GAC9B,wBAAwB,CAkH1B"}
1
+ {"version":3,"file":"initErrors.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/host/lifecycle/initErrors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,wBAAwB,EACxB,wBAAwB,EACxB,MAAM,+BAA+B,CAAA;AAKtC;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACxC,MAAM,EAAE,wBAAwB,EAChC,IAAI,GAAE;IACL,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,4BAA4B,CAAC,EAAE,MAAM,CAAA;CAChC,GACJ,wBAAwB,CA6H1B"}
@@ -6,7 +6,7 @@ const UNKNOWN_ERROR_MESSAGE = "Unexpected error while initializing custom block.
6
6
  * `ready` message. `isRetryable` means a fresh iframe / `init` attempt could plausibly succeed
7
7
  * without changing block code or host configuration.
8
8
  */
9
- export function initErrorForFailureReason(reason) {
9
+ export function initErrorForFailureReason(reason, data = {}) {
10
10
  switch (reason) {
11
11
  case "no_ready":
12
12
  return {
@@ -34,20 +34,31 @@ export function initErrorForFailureReason(reason) {
34
34
  // The sandbox must be updated to a valid manifest.
35
35
  isRetryable: false,
36
36
  };
37
- case "invalid_protocol_version":
37
+ case "invalid_protocol_version": {
38
+ const versionText = data.currentBridgeProtocolVersion
39
+ ? `: ${data.currentBridgeProtocolVersion}`
40
+ : ".";
38
41
  return {
39
42
  code: reason,
40
- message: "Sandbox reported an invalid bridge protocol version.",
43
+ message: `Sandbox reported an invalid bridge protocol version ${versionText}`,
41
44
  // The sandbox must be updated to a supported protocol version.
42
45
  isRetryable: false,
43
46
  };
44
- case "unsupported_protocol_version":
47
+ }
48
+ case "unsupported_protocol_version": {
49
+ const currentVersionText = data.currentBridgeProtocolVersion
50
+ ? `${data.currentBridgeProtocolVersion} `
51
+ : "";
52
+ const minVersionText = data.minBridgeProtocolVersion
53
+ ? ` Minimum supported version is ${data.minBridgeProtocolVersion}.`
54
+ : "";
45
55
  return {
46
56
  code: reason,
47
- message: "Sandbox bridge protocol version is not supported.",
57
+ message: `Sandbox bridge protocol version ${currentVersionText}is not supported.${minVersionText}`,
48
58
  // The sandbox must be updated to a supported protocol version.
49
59
  isRetryable: false,
50
60
  };
61
+ }
51
62
  case "context_unavailable":
52
63
  return {
53
64
  code: reason,
@@ -1,8 +1,2 @@
1
- import type { CustomBlockInitErrorCode } from "../../bridge/messages/init.js";
2
- import type { ReadyMessage } from "../../bridge/messages/ready.js";
3
- export type ReadyInitFailure = {
4
- code: CustomBlockInitErrorCode;
5
- message?: string;
6
- };
7
- export declare function getReadyInitFailure(message: ReadyMessage, minBridgeProtocolVersion: number): ReadyInitFailure | undefined;
1
+ export declare function isValidBridgeProtocolVersion(version: number): boolean;
8
2
  //# sourceMappingURL=ready.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ready.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/host/lifecycle/ready.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAElE,MAAM,MAAM,gBAAgB,GAAG;IAC9B,IAAI,EAAE,wBAAwB,CAAA;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,YAAY,EACrB,wBAAwB,EAAE,MAAM,GAC9B,gBAAgB,GAAG,SAAS,CAoB9B"}
1
+ {"version":3,"file":"ready.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/host/lifecycle/ready.ts"],"names":[],"mappings":"AAAA,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAErE"}
@@ -1,24 +1,3 @@
1
- export function getReadyInitFailure(message, minBridgeProtocolVersion) {
2
- if (!isValidBridgeProtocolVersion(message.bridgeProtocolVersion)) {
3
- return {
4
- code: "invalid_protocol_version",
5
- message: `Sandbox reported invalid bridge protocol version ${message.bridgeProtocolVersion}.`,
6
- };
7
- }
8
- if (message.bridgeProtocolVersion < minBridgeProtocolVersion) {
9
- return {
10
- code: "unsupported_protocol_version",
11
- message: `Sandbox bridge protocol version ${message.bridgeProtocolVersion} is below the host minimum ${minBridgeProtocolVersion}.`,
12
- };
13
- }
14
- if (message.status === "error") {
15
- return {
16
- code: message.error.code,
17
- message: message.error.message,
18
- };
19
- }
20
- return undefined;
21
- }
22
- function isValidBridgeProtocolVersion(version) {
1
+ export function isValidBridgeProtocolVersion(version) {
23
2
  return Number.isFinite(version) && Number.isInteger(version) && version >= 1;
24
3
  }
package/dist/init.js CHANGED
@@ -56,7 +56,7 @@ export function initCustomBlock(opts = {}) {
56
56
  throw error;
57
57
  }
58
58
  if (message.status === "error") {
59
- console.error(`[notion-custom-sdk] host reported init error (${message.error.code}): ${message.error.message}`);
59
+ console.error(`[custom-blocks-sdk] host reported init error (${message.error.code}): ${message.error.message}`);
60
60
  throw new CustomBlockInitError(message.error);
61
61
  }
62
62
  const hostState = customBlockHost.getState();