@notionhq/custom-blocks-host 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +9 -0
  2. package/dist/browser.js +1 -1
  3. package/dist/compatibility/normalizeHostToSandboxMessage.d.ts.map +1 -1
  4. package/dist/compatibility/normalizeHostToSandboxMessage.js +5 -1
  5. package/dist/compatibility/normalizeSandboxToHostMessage.d.ts +88 -1
  6. package/dist/compatibility/normalizeSandboxToHostMessage.d.ts.map +1 -1
  7. package/dist/compatibility/pages/toIconForProtocol3To8.d.ts +4 -0
  8. package/dist/compatibility/pages/toIconForProtocol3To8.d.ts.map +1 -0
  9. package/dist/compatibility/pages/toIconForProtocol3To8.js +13 -0
  10. package/dist/compatibility/protocol3To5/toPageForProtocol3To5.d.ts.map +1 -1
  11. package/dist/compatibility/protocol3To5/toPageForProtocol3To5.js +2 -1
  12. package/dist/compatibility/protocol3To5/toQueryPageForProtocol3To5.d.ts +8 -8
  13. package/dist/compatibility/protocol6/normalizePageResultsForProtocol6.d.ts.map +1 -1
  14. package/dist/compatibility/protocol6/normalizePageResultsForProtocol6.js +9 -2
  15. package/dist/compatibility/protocol7To8/normalizePageResultsForProtocol7To8.d.ts +4 -0
  16. package/dist/compatibility/protocol7To8/normalizePageResultsForProtocol7To8.d.ts.map +1 -0
  17. package/dist/compatibility/protocol7To8/normalizePageResultsForProtocol7To8.js +16 -0
  18. package/dist/createCustomBlockHost.d.ts.map +1 -1
  19. package/dist/createCustomBlockHost.js +50 -34
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/messages/createSandboxMessageRouter.d.ts +3 -2
  23. package/dist/messages/createSandboxMessageRouter.d.ts.map +1 -1
  24. package/dist/messages/createSandboxMessageRouter.js +7 -1
  25. package/dist/messages/getInvalidSandboxMessageResponse.d.ts.map +1 -1
  26. package/dist/messages/getInvalidSandboxMessageResponse.js +3 -0
  27. package/dist/messages/invalidNativeIconColor.d.ts +5 -0
  28. package/dist/messages/invalidNativeIconColor.d.ts.map +1 -0
  29. package/dist/messages/invalidNativeIconColor.js +34 -0
  30. package/dist/pages/types.d.ts +2 -0
  31. package/dist/pages/types.d.ts.map +1 -1
  32. package/dist/protocol/compatibility/protocol3To5/bridgePage.d.ts +7 -1
  33. package/dist/protocol/dataSources/messages/queryDataSource.d.ts +14 -2
  34. package/dist/protocol/icons.d.ts +17 -0
  35. package/dist/protocol/icons.js +27 -0
  36. package/dist/protocol/index.d.ts +5 -3
  37. package/dist/protocol/index.js +4 -2
  38. package/dist/protocol/messages/hostToSandbox.d.ts +79 -11
  39. package/dist/protocol/messages/invalidHostMessage.d.ts +2 -0
  40. package/dist/protocol/messages/invalidHostMessage.js +2 -0
  41. package/dist/protocol/messages/invalidSandboxMessage.d.ts +2 -0
  42. package/dist/protocol/messages/invalidSandboxMessage.js +2 -0
  43. package/dist/protocol/messages/sandboxToHost.d.ts +88 -1
  44. package/dist/protocol/navigation/openExternalUrl.d.ts +2 -2
  45. package/dist/protocol/pages/bridgePage.d.ts +7 -1
  46. package/dist/protocol/pages/messages/createPage.d.ts +103 -5
  47. package/dist/protocol/pages/messages/createPage.js +3 -1
  48. package/dist/protocol/pages/messages/getPage.d.ts +23 -5
  49. package/dist/protocol/pages/messages/openPage.d.ts +2 -2
  50. package/dist/protocol/pages/messages/updatePage.d.ts +30 -6
  51. package/dist/protocol/pages/pageAppearance.d.ts +36 -1
  52. package/dist/protocol/pages/pageAppearance.js +9 -0
  53. package/dist/protocol/users/messages/getUser.d.ts +2 -2
  54. package/dist/protocol/users/messages/listUsers.d.ts +2 -2
  55. package/dist/protocol/utils/errors.d.ts +2 -0
  56. package/dist/protocol/utils/protocolVersion.d.ts +2 -1
  57. package/dist/protocol/utils/protocolVersion.js +3 -1
  58. package/dist/protocol/utils/readRequestId.d.ts +2 -0
  59. package/dist/protocol/utils/readRequestId.js +7 -0
  60. package/docs/bridge/changelog.md +19 -0
  61. package/docs/bridge/protocol.md +30 -1
  62. package/docs/bridge/versioning.md +1 -0
  63. package/docs/compatibility.md +15 -7
  64. package/docs/handlers.md +6 -3
  65. package/package.json +1 -1
  66. package/src/compatibility/normalizeHostToSandboxMessage.ts +5 -0
  67. package/src/compatibility/pages/toIconForProtocol3To8.ts +17 -0
  68. package/src/compatibility/protocol3To5/toPageForProtocol3To5.ts +2 -1
  69. package/src/compatibility/protocol6/normalizePageResultsForProtocol6.ts +11 -2
  70. package/src/compatibility/protocol7To8/normalizePageResultsForProtocol7To8.ts +22 -0
  71. package/src/createCustomBlockHost.ts +57 -37
  72. package/src/index.ts +1 -0
  73. package/src/messages/createSandboxMessageRouter.ts +18 -2
  74. package/src/messages/getInvalidSandboxMessageResponse.ts +3 -0
  75. package/src/messages/invalidNativeIconColor.ts +43 -0
  76. package/src/pages/types.ts +3 -0
@@ -1,7 +1,7 @@
1
1
  import * as v from "valibot";
2
- import type { CustomBlockCapabilityErrorCode, CustomBlockErrorCode, CustomBlockErrorInfo } from "../../utils/errors.js";
2
+ import type { CustomBlockCapabilityErrorCode, CustomBlockErrorCode, CustomBlockErrorInfo, CustomBlockRequestErrorCode } from "../../utils/errors.js";
3
3
  export declare const customBlockUpdatePageErrorCodeSchema: v.StringSchema<undefined>;
4
- export type CustomBlockUpdatePageErrorCode = CustomBlockErrorCode<"update_page_failed" | "invalid_page_update" | "page_unavailable" | "data_source_access_failed" | "unmapped_property" | "property_id_mismatch" | "duplicate_property" | "invalid_property_value" | CustomBlockCapabilityErrorCode>;
4
+ export type CustomBlockUpdatePageErrorCode = CustomBlockErrorCode<CustomBlockRequestErrorCode | "update_page_failed" | "previous_write_incomplete" | "invalid_page_update" | "invalid_icon_name" | "invalid_icon_color" | "page_unavailable" | "data_source_access_failed" | "unmapped_property" | "property_id_mismatch" | "duplicate_property" | "invalid_property_value" | CustomBlockCapabilityErrorCode>;
5
5
  export type CustomBlockUpdatePageErrorInfo = CustomBlockErrorInfo<CustomBlockUpdatePageErrorCode>;
6
6
  export declare const customBlockUpdatePageErrorInfoSchema: v.ObjectSchema<{
7
7
  readonly code: v.StringSchema<undefined>;
@@ -264,7 +264,13 @@ export declare const updatePageResultMessageSchema: v.VariantSchema<"status", [v
264
264
  readonly last_visited_time: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, null>;
265
265
  readonly created_time: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, null>;
266
266
  readonly last_edited_time: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, null>;
267
- readonly icon: v.OptionalSchema<v.NullableSchema<v.VariantSchema<"type", [v.ObjectSchema<{
267
+ readonly icon: v.OptionalSchema<v.NullableSchema<v.VariantSchema<"type", [v.LooseObjectSchema<{
268
+ readonly type: v.LiteralSchema<"icon", undefined>;
269
+ readonly icon: v.LooseObjectSchema<{
270
+ readonly name: v.StringSchema<undefined>;
271
+ readonly color: v.OptionalSchema<v.GenericSchema<import("../../icons.js").NotionIconColor>, undefined>;
272
+ }, undefined>;
273
+ }, undefined>, v.ObjectSchema<{
268
274
  readonly type: v.LiteralSchema<"emoji", undefined>;
269
275
  readonly emoji: v.StringSchema<undefined>;
270
276
  }, undefined>, v.ObjectSchema<{
@@ -566,7 +572,13 @@ export declare const updatePageResultMessageWithProtocol3To5CompatibilitySchema:
566
572
  readonly last_visited_time: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, null>;
567
573
  readonly created_time: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, null>;
568
574
  readonly last_edited_time: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, null>;
569
- readonly icon: v.OptionalSchema<v.NullableSchema<v.VariantSchema<"type", [v.ObjectSchema<{
575
+ readonly icon: v.OptionalSchema<v.NullableSchema<v.VariantSchema<"type", [v.LooseObjectSchema<{
576
+ readonly type: v.LiteralSchema<"icon", undefined>;
577
+ readonly icon: v.LooseObjectSchema<{
578
+ readonly name: v.StringSchema<undefined>;
579
+ readonly color: v.OptionalSchema<v.GenericSchema<import("../../icons.js").NotionIconColor>, undefined>;
580
+ }, undefined>;
581
+ }, undefined>, v.ObjectSchema<{
570
582
  readonly type: v.LiteralSchema<"emoji", undefined>;
571
583
  readonly emoji: v.StringSchema<undefined>;
572
584
  }, undefined>, v.ObjectSchema<{
@@ -842,7 +854,13 @@ export declare const updatePageResultMessageWithProtocol3To5CompatibilitySchema:
842
854
  }, undefined>], undefined>, undefined>;
843
855
  readonly created_time: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
844
856
  readonly last_edited_time: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
845
- readonly icon: v.OptionalSchema<v.VariantSchema<"type", [v.ObjectSchema<{
857
+ readonly icon: v.OptionalSchema<v.VariantSchema<"type", [v.LooseObjectSchema<{
858
+ readonly type: v.LiteralSchema<"icon", undefined>;
859
+ readonly icon: v.LooseObjectSchema<{
860
+ readonly name: v.StringSchema<undefined>;
861
+ readonly color: v.OptionalSchema<v.GenericSchema<import("../../icons.js").NotionIconColor>, undefined>;
862
+ }, undefined>;
863
+ }, undefined>, v.ObjectSchema<{
846
864
  readonly type: v.LiteralSchema<"emoji", undefined>;
847
865
  readonly emoji: v.StringSchema<undefined>;
848
866
  }, undefined>, v.ObjectSchema<{
@@ -1123,7 +1141,13 @@ export declare const updatePageMessageSchema: v.ObjectSchema<{
1123
1141
  readonly type: v.LiteralSchema<"verification", undefined>;
1124
1142
  readonly verification: v.StringSchema<undefined>;
1125
1143
  }, undefined>], undefined>, undefined>, undefined>;
1126
- readonly icon: v.OptionalSchema<v.VariantSchema<"type", [v.ObjectSchema<{
1144
+ readonly icon: v.OptionalSchema<v.VariantSchema<"type", [v.LooseObjectSchema<{
1145
+ readonly type: v.LiteralSchema<"icon", undefined>;
1146
+ readonly icon: v.LooseObjectSchema<{
1147
+ readonly name: v.StringSchema<undefined>;
1148
+ readonly color: v.OptionalSchema<v.GenericSchema<import("../../icons.js").NotionIconColor>, undefined>;
1149
+ }, undefined>;
1150
+ }, undefined>, v.ObjectSchema<{
1127
1151
  readonly type: v.LiteralSchema<"emoji", undefined>;
1128
1152
  readonly emoji: v.StringSchema<undefined>;
1129
1153
  }, undefined>, v.ObjectSchema<{
@@ -24,7 +24,13 @@ export declare const notionHostedFileSchema: v.ObjectSchema<{
24
24
  readonly expiry_time: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
25
25
  }, undefined>;
26
26
  }, undefined>;
27
- export declare const notionPageIconSchema: v.VariantSchema<"type", [v.ObjectSchema<{
27
+ export declare const notionPageIconSchema: v.VariantSchema<"type", [v.LooseObjectSchema<{
28
+ readonly type: v.LiteralSchema<"icon", undefined>;
29
+ readonly icon: v.LooseObjectSchema<{
30
+ readonly name: v.StringSchema<undefined>;
31
+ readonly color: v.OptionalSchema<v.GenericSchema<import("../icons.js").NotionIconColor>, undefined>;
32
+ }, undefined>;
33
+ }, undefined>, v.ObjectSchema<{
28
34
  readonly type: v.LiteralSchema<"emoji", undefined>;
29
35
  readonly emoji: v.StringSchema<undefined>;
30
36
  }, undefined>, v.ObjectSchema<{
@@ -60,3 +66,32 @@ export declare const notionPageCoverSchema: v.VariantSchema<"type", [v.ObjectSch
60
66
  }, undefined>;
61
67
  }, undefined>], undefined>;
62
68
  export type NotionPageCover = v.InferOutput<typeof notionPageCoverSchema>;
69
+ export declare const notionCreatePageIconSchema: v.VariantSchema<"type", [v.LooseObjectSchema<{
70
+ readonly type: v.LiteralSchema<"icon", undefined>;
71
+ readonly icon: v.LooseObjectSchema<{
72
+ readonly name: v.StringSchema<undefined>;
73
+ readonly color: v.OptionalSchema<v.GenericSchema<import("../icons.js").NotionIconColor>, undefined>;
74
+ }, undefined>;
75
+ }, undefined>, v.ObjectSchema<{
76
+ readonly type: v.LiteralSchema<"emoji", undefined>;
77
+ readonly emoji: v.StringSchema<undefined>;
78
+ }, undefined>, v.ObjectSchema<{
79
+ readonly type: v.LiteralSchema<"custom_emoji", undefined>;
80
+ readonly custom_emoji: v.ObjectSchema<{
81
+ readonly id: v.StringSchema<undefined>;
82
+ readonly name: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
83
+ readonly url: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
84
+ }, undefined>;
85
+ }, undefined>, v.ObjectSchema<{
86
+ readonly type: v.LiteralSchema<"external", undefined>;
87
+ readonly external: v.ObjectSchema<{
88
+ readonly url: v.StringSchema<undefined>;
89
+ }, undefined>;
90
+ }, undefined>, v.ObjectSchema<{
91
+ readonly type: v.LiteralSchema<"file", undefined>;
92
+ readonly file: v.ObjectSchema<{
93
+ readonly url: v.StringSchema<undefined>;
94
+ readonly expiry_time: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
95
+ }, undefined>;
96
+ }, undefined>], undefined>;
97
+ export type NotionCreatePageIcon = v.InferOutput<typeof notionCreatePageIconSchema>;
@@ -1,4 +1,5 @@
1
1
  import * as v from "valibot";
2
+ import { notionNativeIconSchema } from "../icons.js";
2
3
  export const notionEmojiIconSchema = v.object({
3
4
  type: v.literal("emoji"),
4
5
  emoji: v.string(),
@@ -23,6 +24,7 @@ export const notionHostedFileSchema = v.object({
23
24
  }),
24
25
  });
25
26
  export const notionPageIconSchema = v.variant("type", [
27
+ notionNativeIconSchema,
26
28
  notionEmojiIconSchema,
27
29
  notionCustomEmojiIconSchema,
28
30
  notionExternalFileSchema,
@@ -32,3 +34,10 @@ export const notionPageCoverSchema = v.variant("type", [
32
34
  notionExternalFileSchema,
33
35
  notionHostedFileSchema,
34
36
  ]);
37
+ export const notionCreatePageIconSchema = v.variant("type", [
38
+ notionNativeIconSchema,
39
+ notionEmojiIconSchema,
40
+ notionCustomEmojiIconSchema,
41
+ notionExternalFileSchema,
42
+ notionHostedFileSchema,
43
+ ]);
@@ -1,7 +1,7 @@
1
1
  import * as v from "valibot";
2
- import type { CustomBlockCapabilityErrorCode, CustomBlockErrorCode, CustomBlockErrorInfo } from "../../utils/errors.js";
2
+ import type { CustomBlockCapabilityErrorCode, CustomBlockErrorCode, CustomBlockErrorInfo, CustomBlockRequestErrorCode } from "../../utils/errors.js";
3
3
  export declare const customBlockGetUserErrorCodeSchema: v.StringSchema<undefined>;
4
- export type CustomBlockGetUserErrorCode = CustomBlockErrorCode<"get_user_failed" | CustomBlockCapabilityErrorCode>;
4
+ export type CustomBlockGetUserErrorCode = CustomBlockErrorCode<CustomBlockRequestErrorCode | "get_user_failed" | CustomBlockCapabilityErrorCode>;
5
5
  export type CustomBlockGetUserErrorInfo = CustomBlockErrorInfo<CustomBlockGetUserErrorCode>;
6
6
  export declare const customBlockGetUserErrorInfoSchema: v.ObjectSchema<{
7
7
  readonly code: v.StringSchema<undefined>;
@@ -1,7 +1,7 @@
1
1
  import * as v from "valibot";
2
- import type { CustomBlockCapabilityErrorCode, CustomBlockErrorCode, CustomBlockErrorInfo } from "../../utils/errors.js";
2
+ import type { CustomBlockCapabilityErrorCode, CustomBlockErrorCode, CustomBlockErrorInfo, CustomBlockRequestErrorCode } from "../../utils/errors.js";
3
3
  export declare const customBlockListUsersErrorCodeSchema: v.StringSchema<undefined>;
4
- export type CustomBlockListUsersErrorCode = CustomBlockErrorCode<"list_users_failed" | CustomBlockCapabilityErrorCode>;
4
+ export type CustomBlockListUsersErrorCode = CustomBlockErrorCode<CustomBlockRequestErrorCode | "list_users_failed" | CustomBlockCapabilityErrorCode>;
5
5
  export type CustomBlockListUsersErrorInfo = CustomBlockErrorInfo<CustomBlockListUsersErrorCode>;
6
6
  export declare const customBlockListUsersErrorInfoSchema: v.ObjectSchema<{
7
7
  readonly code: v.StringSchema<undefined>;
@@ -16,3 +16,5 @@ export declare const customBlockErrorInfoSchema: v.ObjectSchema<{
16
16
  readonly message: v.StringSchema<undefined>;
17
17
  readonly isRetryable: v.BooleanSchema<undefined>;
18
18
  }, undefined>;
19
+ /** Failures shared by one-shot requests. */
20
+ export type CustomBlockRequestErrorCode = CustomBlockErrorCode<"request_timeout" | "request_send_failed" | "request_disposed" | "invalid_host_message" | "invalid_sandbox_message" | "unsupported_operation">;
@@ -3,6 +3,7 @@
3
3
  * Increase this value for breaking wire changes. Hosts can continue to accept
4
4
  * older versions, so this value does not set the minimum or maximum they accept.
5
5
  */
6
- export declare const LATEST_BRIDGE_PROTOCOL_VERSION = 8;
6
+ export declare const LATEST_BRIDGE_PROTOCOL_VERSION = 9;
7
7
  export declare const UNIFIED_PAGE_PROTOCOL_VERSION = 6;
8
8
  export declare const TEXT_FALLBACK_PROPERTY_PROTOCOL_VERSION = 7;
9
+ export declare const NATIVE_ICON_PROTOCOL_VERSION = 9;
@@ -3,9 +3,11 @@
3
3
  * Increase this value for breaking wire changes. Hosts can continue to accept
4
4
  * older versions, so this value does not set the minimum or maximum they accept.
5
5
  */
6
- export const LATEST_BRIDGE_PROTOCOL_VERSION = 8;
6
+ export const LATEST_BRIDGE_PROTOCOL_VERSION = 9;
7
7
  // Protocol 6 introduces unified pages with name-keyed property objects.
8
8
  // Keep this cutoff fixed when the latest protocol version increases.
9
9
  export const UNIFIED_PAGE_PROTOCOL_VERSION = 6;
10
10
  // Protocol 7 adds typed text fallbacks for previously unsupported page properties.
11
11
  export const TEXT_FALLBACK_PROPERTY_PROTOCOL_VERSION = 7;
12
+ // Protocol 9 adds native icon responses and writes throughout the page API.
13
+ export const NATIVE_ICON_PROTOCOL_VERSION = 9;
@@ -0,0 +1,2 @@
1
+ /** Reads correlation without accepting the rest of an invalid message. */
2
+ export declare function readRequestId(data: unknown): string | undefined;
@@ -0,0 +1,7 @@
1
+ import * as v from "valibot";
2
+ const requestIdentitySchema = v.object({ requestId: v.string() });
3
+ /** Reads correlation without accepting the rest of an invalid message. */
4
+ export function readRequestId(data) {
5
+ const result = v.safeParse(requestIdentitySchema, data);
6
+ return result.success ? result.output.requestId : undefined;
7
+ }
@@ -20,10 +20,22 @@ versions refer to `@notionhq/custom-blocks`.
20
20
  | 6 [‡](#protocol-6-richer-fallback-properties) | `0.3.0` | October 26, 2026 | Typed, display-name-keyed query properties and unified page responses replace the older query representation |
21
21
  | 7 | `0.3.3` | October 30, 2026 | Typed text fallback properties for unique ID, button, formula, location, rollup, and verification |
22
22
  | 8 | `0.4.0` | November 2, 2026 | Local manifests move from `/manifest` to HTML metadata sent in `connect`; `manifestSource` selects the initialization manifest |
23
+ | 9 | Unreleased | — | Native page icons; older protocols receive external SVG URLs |
23
24
 
24
25
  The table lists SDK release boundaries, not complete supported version ranges. Host compatibility
25
26
  depends on `connect.bridgeProtocolVersion`, not the SDK package version.
26
27
 
28
+ ## Protocol 9: native page icons
29
+
30
+ Page operations and query results can return `{ type: "icon", icon: { name, color } }`. Reads and
31
+ writes share a loose shape: names and colors accept strings, color is optional, and additional
32
+ native icon fields are retained. Hosts resolve names and default omitted colors to gray.
33
+
34
+ Protocols 3–8 receive external SVG URLs in page operation results. Protocols 6–8 receive the same
35
+ fallback in query results; protocols 3–5 keep their flat query format without appearance fields. The
36
+ released-SDK compatibility corpus exercises these conversions. Add a frozen protocol-9 reader after
37
+ the SDK release is published.
38
+
27
39
  ## Protocol 8: local manifest declarations
28
40
 
29
41
  The dev shell removes the `/manifest` route and embeds the local manifest in block HTML. The SDK
@@ -41,6 +53,13 @@ from `connect`.
41
53
 
42
54
  ## Changes within a declared version
43
55
 
56
+ ### Protocol 8: correlated request rejections
57
+
58
+ Bridge NACKs (`invalidHostMessage` and `invalidSandboxMessage`) gain an optional string `requestId`.
59
+ Updated SDKs complete the matching request immediately when a rejection can be correlated. Older
60
+ readers ignore the additional field; updated readers continue to accept NACKs without an ID.
61
+ Uncorrelated requests use the SDK's 60-second timeout. This additive change retains protocol 8.
62
+
44
63
  ### Protocol 5: unavailable containing-page context
45
64
 
46
65
  On October 2, 2026, SDK 0.2.17 added `{ type: "unavailable" }` to containing-page context while
@@ -62,7 +62,9 @@ a different origin from its embedding page.
62
62
  - `queryDataSource` is a long-lived subscription; requests and every update use its stable
63
63
  `subscriptionId`.
64
64
  - The initialization lifecycle uses `initializationId`, not `requestId` or `subscriptionId`.
65
- - One-way messages, such as `resize` and bridge NACKs, have no correlation ID.
65
+ - One-way messages, such as `resize`, have no correlation ID.
66
+ - Bridge NACKs include the rejected message's `requestId` when it is a string. The ID identifies the
67
+ original request, including when the rejected message is its response.
66
68
 
67
69
  Hosts key query state by `subscriptionId`, replace state when a request reuses one, and suppress
68
70
  stale asynchronous results.
@@ -82,6 +84,22 @@ On parse failure, the host replies with `invalidSandboxMessage` and the sandbox
82
84
  `invalidHostMessage`, except for the handshake cases below. These replies are negative
83
85
  acknowledgments (NACKs). Receivers do not NACK another NACK.
84
86
 
87
+ A sandbox request with a `requestId` expects a terminal result or correlated rejection. On parse
88
+ failure, read `requestId` separately from the invalid payload. The host includes a readable string
89
+ ID in `invalidSandboxMessage`, even for an unknown request type. The SDK completes the matching
90
+ request with `invalid_sandbox_message`. When the SDK rejects a host response, it completes the
91
+ matching request locally with `invalid_host_message` and includes the ID in `invalidHostMessage`.
92
+ Both errors have `isRetryable: false`.
93
+
94
+ One-shot SDK requests time out after 60 seconds. Missing, unreadable, or unknown IDs do not fail
95
+ unrelated requests. NACK IDs are optional: older readers ignore them, and newer readers accept
96
+ legacy NACKs without IDs. Those requests retain the timeout fallback.
97
+
98
+ Request completion clears its timer, removes the pending entry, and ignores duplicate or late
99
+ results. Disposing the SDK completes pending calls locally with `request_disposed` and clears their
100
+ timers. The SDK does not retry or cancel host work. A timed-out write may still complete; check its
101
+ resulting state before repeating it. Data source subscriptions use a separate lifecycle.
102
+
85
103
  Malformed sandbox messages do not change the host's initialization state or timeout, except as
86
104
  follows:
87
105
 
@@ -202,3 +220,14 @@ or `""` when absent. Hosts do not evaluate formulas or rollups.
202
220
  The host converts responses to the sandbox's declared protocol. See
203
221
  [versioning and compatibility](versioning.md) for protocol version rules. The host's
204
222
  [compatibility reference](../compatibility.md) describes older response formats.
223
+
224
+ ### Native page icon compatibility
225
+
226
+ Protocol 9 and later use native icon objects. Protocols 3–8 receive external SVG URLs in page
227
+ operation results. Unified query results in protocols 6–8 also use external SVG URLs; flat query
228
+ results in protocols 3–5 omit icon fields. Native colors accept any string in responses and write
229
+ inputs, so newer host colors do not require an SDK update.
230
+
231
+ `createPage` accepts native icons, emoji, custom emoji, and URL-backed icons and covers. The
232
+ application adapter checks submitted image URLs against the block's network permissions before
233
+ creating a page.
@@ -17,6 +17,7 @@ conversions.
17
17
 
18
18
  `connect.bridgeProtocolVersion` selects the wire contract. `sdkVersion` is analytics metadata.
19
19
  `LATEST_BRIDGE_PROTOCOL_VERSION` identifies the latest contract defined by the package.
20
+ `NATIVE_ICON_PROTOCOL_VERSION` is 9, the first protocol with native icon responses and writes.
20
21
 
21
22
  ## Protocol export names
22
23
 
@@ -16,7 +16,7 @@ does not guarantee that future features work.
16
16
 
17
17
  `sdkVersion` is analytics metadata. Compatibility behavior depends on the protocol version.
18
18
 
19
- The current SDK declares protocol 8. The shared runtime minimum is 3. The dev shell uses the Worker
19
+ The current SDK declares protocol 9. The shared runtime minimum is 3. The dev shell uses the Worker
20
20
  manifest in host mode and accepts the shared runtime's supported protocol versions. Protocols 7 and
21
21
  8 use the same page and query response formats. Protocol 8's local manifest declaration is selected
22
22
  only in sandbox mode; host mode uses the persisted manifest. See the
@@ -35,12 +35,13 @@ through.
35
35
 
36
36
  ## Current compatibility behavior
37
37
 
38
- | Target | Query results | Page operation results | Binding schemas |
39
- | ------ | --------------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------ |
40
- | 3–4 | Flat ID-keyed values, including synthetic metadata entries | ID-keyed properties with unsupported property objects omitted | Synthetic metadata read schemas restored in outgoing copies |
41
- | 5 | Same query values as protocols 3–4 | Same page format as protocols 3–4 | Actual database schemas. The SDK supplies synthetic read schemas locally |
42
- | 6 | Unified name-keyed properties. Text fallback property objects become `null` | Same unified page format | Actual database schemas |
43
- | 7+ | Unified pages with typed text fallback properties | Same unified page format | Actual database schemas |
38
+ | Target | Query results | Page operation results | Binding schemas | Native icons |
39
+ | ------ | --------------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------- |
40
+ | 3–4 | Flat ID-keyed values, including synthetic metadata entries | ID-keyed properties with unsupported property objects omitted | Synthetic metadata read schemas restored in outgoing copies | External SVG URLs in page operations; icons omitted from flat query results |
41
+ | 5 | Same query values as protocols 3–4 | Same page format as protocols 3–4 | Actual database schemas. The SDK supplies synthetic read schemas locally | Same as protocols 3–4 |
42
+ | 6 | Unified name-keyed properties. Text fallback property objects become `null` | Same unified page format | Actual database schemas | External SVG URLs |
43
+ | 7–8 | Unified pages with typed text fallback properties | Same unified page format | Actual database schemas | External SVG URLs |
44
+ | 9+ | Unified pages with typed text fallback properties | Same unified page format | Actual database schemas | Native icon objects |
44
45
 
45
46
  The synthetic metadata IDs are `created_time`, `last_edited_time`, `created_by`, and
46
47
  `last_edited_by`. They retain metadata meaning if an actual property uses the same raw ID. This
@@ -52,6 +53,13 @@ Release-specific compatibility limits are documented in the changelog entries fo
52
53
  and
53
54
  [protocol 6 text fallback properties](bridge/changelog.md#protocol-6-richer-fallback-properties).
54
55
 
56
+ ### Native page icons
57
+
58
+ Protocol 9 and later receive native icon objects. Protocols 3–8 receive external SVG URLs in page
59
+ operation results, and protocols 6–8 also receive them in query results. Protocols 3–5 use flat
60
+ query results without page appearance fields. Omitted native colors default to gray when producing
61
+ SVG URLs.
62
+
55
63
  ### Unavailable containing-page context
56
64
 
57
65
  Protocols 3–4 receive `context_unavailable` during initialization or stop when containing-page
package/docs/handlers.md CHANGED
@@ -11,9 +11,8 @@ hosts must provide their own permissions, data access, and persistence.
11
11
 
12
12
  ## Handler contracts
13
13
 
14
- `queryDataSource` is required. Other handlers are optional. Omitting a one-shot request handler
15
- sends no response to that request. A handler returns a result synchronously or as a promise. Errors
16
- carry `{ code, message, isRetryable }`, with open string codes.
14
+ `queryDataSource` is required. Other handlers are optional. A handler returns a result synchronously
15
+ or as a promise. Errors carry `{ code, message, isRetryable }`, with open string codes.
17
16
 
18
17
  ### Return bridge data
19
18
 
@@ -28,6 +27,10 @@ Query filters and sorts are validated against the actual bound database schema b
28
27
  `queryDataSource` runs. See the [wire reference](bridge/protocol.md#data-sources) for
29
28
  query semantics.
30
29
 
30
+ For one-shot requests, a missing handler returns `unsupported_operation`. A handler that throws,
31
+ rejects, or returns a malformed result produces `unknown_error`. Exception details go to `onLog`;
32
+ the sandbox receives a generic error. Specific errors returned by handlers pass through unchanged.
33
+
31
34
  ## Connect a data source
32
35
 
33
36
  Connect an embedded block to an in-memory Tasks data source with one numeric Estimate property. This
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notionhq/custom-blocks-host",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Reusable host runtime for Notion custom blocks.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -3,12 +3,14 @@ import type {
3
3
  HostToSandboxMessageWithProtocol3To5Compatibility,
4
4
  } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
5
5
  import {
6
+ NATIVE_ICON_PROTOCOL_VERSION,
6
7
  TEXT_FALLBACK_PROPERTY_PROTOCOL_VERSION,
7
8
  UNIFIED_PAGE_PROTOCOL_VERSION,
8
9
  } from "@notionhq/custom-blocks-protocol/utils/protocolVersion.js"
9
10
  import { addMetadataReadSchemasForProtocol3To4 } from "./protocol3To4/addMetadataReadSchemasForProtocol3To4.js"
10
11
  import { normalizePageResultsForProtocol3To5 } from "./protocol3To5/normalizePageResultsForProtocol3To5.js"
11
12
  import { normalizePageResultsForProtocol6 } from "./protocol6/normalizePageResultsForProtocol6.js"
13
+ import { normalizePageResultsForProtocol7To8 } from "./protocol7To8/normalizePageResultsForProtocol7To8.js"
12
14
 
13
15
  /**
14
16
  * Encodes current host messages for the sandbox's declared protocol. Handlers use
@@ -50,5 +52,8 @@ export function normalizeHostToSandboxMessage(
50
52
  if (bridgeProtocolVersion < TEXT_FALLBACK_PROPERTY_PROTOCOL_VERSION) {
51
53
  return normalizePageResultsForProtocol6(message)
52
54
  }
55
+ if (bridgeProtocolVersion < NATIVE_ICON_PROTOCOL_VERSION) {
56
+ return normalizePageResultsForProtocol7To8(message)
57
+ }
53
58
  return message
54
59
  }
@@ -0,0 +1,17 @@
1
+ import type { NotionPageIcon } from "@notionhq/custom-blocks-protocol/pages/pageAppearance.js"
2
+
3
+ /** Converts native icons for legacy receivers. Delete when the runtime minimum reaches 9. */
4
+ export function toIconForProtocol3To8(
5
+ icon: NotionPageIcon | null,
6
+ ): NotionPageIcon | null {
7
+ if (icon?.type !== "icon") {
8
+ return icon
9
+ }
10
+ const { name, color } = icon.icon
11
+ return {
12
+ type: "external",
13
+ external: {
14
+ url: `https://www.notion.so/icons/${name}_${color ?? "gray"}.svg`,
15
+ },
16
+ }
17
+ }
@@ -1,5 +1,6 @@
1
1
  import type { BridgePageForProtocol3To5 } from "@notionhq/custom-blocks-protocol/compatibility/protocol3To5/bridgePage.js"
2
2
  import type { BridgePage } from "@notionhq/custom-blocks-protocol/pages/bridgePage.js"
3
+ import { toIconForProtocol3To8 } from "../pages/toIconForProtocol3To8.js"
3
4
 
4
5
  /**
5
6
  * Converts current page data to the page-operation format used by protocols 3–5.
@@ -19,7 +20,7 @@ export function toPageForProtocol3To5(
19
20
  property ? [[property.id, property]] : [],
20
21
  ),
21
22
  ),
22
- icon: page.icon ?? undefined,
23
+ icon: toIconForProtocol3To8(page.icon) ?? undefined,
23
24
  cover: page.cover ?? undefined,
24
25
  created_time: page.created_time ?? undefined,
25
26
  last_edited_time: page.last_edited_time ?? undefined,
@@ -1,5 +1,7 @@
1
1
  import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
2
+ import type { BridgePage } from "@notionhq/custom-blocks-protocol/pages/bridgePage.js"
2
3
  import type { SuccessfulPageResult } from "../pages/pageResult.js"
4
+ import { toIconForProtocol3To8 } from "../pages/toIconForProtocol3To8.js"
3
5
  import { toPageWithNullTextFallbackProperties } from "../pages/toPageWithNullTextFallbackProperties.js"
4
6
 
5
7
  /**
@@ -13,11 +15,18 @@ export function normalizePageResultsForProtocol6(
13
15
  if (message.type === "queryDataSourceResult") {
14
16
  return {
15
17
  ...message,
16
- items: message.items.map(toPageWithNullTextFallbackProperties),
18
+ items: message.items.map(toPageForProtocol6),
17
19
  }
18
20
  }
19
21
  return {
20
22
  ...message,
21
- page: toPageWithNullTextFallbackProperties(message.page),
23
+ page: toPageForProtocol6(message.page),
24
+ }
25
+ }
26
+
27
+ function toPageForProtocol6(page: BridgePage): BridgePage {
28
+ return {
29
+ ...toPageWithNullTextFallbackProperties(page),
30
+ icon: toIconForProtocol3To8(page.icon),
22
31
  }
23
32
  }
@@ -0,0 +1,22 @@
1
+ import type { BridgePage } from "@notionhq/custom-blocks-protocol/pages/bridgePage.js"
2
+ import type { SuccessfulPageResult } from "../pages/pageResult.js"
3
+ import { toIconForProtocol3To8 } from "../pages/toIconForProtocol3To8.js"
4
+
5
+ /** Encodes icons for protocols 7–8. Delete when the runtime minimum reaches 9. */
6
+ export function normalizePageResultsForProtocol7To8(
7
+ message: SuccessfulPageResult,
8
+ ): SuccessfulPageResult {
9
+ if (message.type === "queryDataSourceResult") {
10
+ if (!message.items.some(page => page.icon?.type === "icon")) {
11
+ return message
12
+ }
13
+ return { ...message, items: message.items.map(toPageWithoutNativeIcon) }
14
+ }
15
+ const page = toPageWithoutNativeIcon(message.page)
16
+ return page === message.page ? message : { ...message, page }
17
+ }
18
+
19
+ function toPageWithoutNativeIcon(page: BridgePage): BridgePage {
20
+ const icon = toIconForProtocol3To8(page.icon)
21
+ return icon === page.icon ? page : { ...page, icon }
22
+ }