@notionhq/custom-blocks-host 0.5.2 → 0.7.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 (90) 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 +100 -15
  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/pages/toPageWithNullTextFallbackProperties.d.ts.map +1 -1
  11. package/dist/compatibility/pages/toPageWithNullTextFallbackProperties.js +0 -1
  12. package/dist/compatibility/protocol3To4/addMetadataReadSchemasForProtocol3To4.d.ts +15 -34
  13. package/dist/compatibility/protocol3To4/addMetadataReadSchemasForProtocol3To4.d.ts.map +1 -1
  14. package/dist/compatibility/protocol3To5/toPageForProtocol3To5.d.ts.map +1 -1
  15. package/dist/compatibility/protocol3To5/toPageForProtocol3To5.js +2 -1
  16. package/dist/compatibility/protocol3To5/toPropertyValueForProtocol3To5.d.ts.map +1 -1
  17. package/dist/compatibility/protocol3To5/toPropertyValueForProtocol3To5.js +0 -2
  18. package/dist/compatibility/protocol3To5/toQueryPageForProtocol3To5.d.ts +8 -8
  19. package/dist/compatibility/protocol6/normalizePageResultsForProtocol6.d.ts.map +1 -1
  20. package/dist/compatibility/protocol6/normalizePageResultsForProtocol6.js +9 -2
  21. package/dist/compatibility/protocol7To8/normalizePageResultsForProtocol7To8.d.ts +4 -0
  22. package/dist/compatibility/protocol7To8/normalizePageResultsForProtocol7To8.d.ts.map +1 -0
  23. package/dist/compatibility/protocol7To8/normalizePageResultsForProtocol7To8.js +16 -0
  24. package/dist/context/initialState.d.ts +39 -38
  25. package/dist/context/initialState.d.ts.map +1 -1
  26. package/dist/createCustomBlockHost.d.ts.map +1 -1
  27. package/dist/createCustomBlockHost.js +1 -0
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/manifest/types.d.ts +13 -2
  31. package/dist/manifest/types.d.ts.map +1 -1
  32. package/dist/messages/createSandboxMessageRouter.d.ts +3 -2
  33. package/dist/messages/createSandboxMessageRouter.d.ts.map +1 -1
  34. package/dist/messages/createSandboxMessageRouter.js +7 -1
  35. package/dist/messages/invalidNativeIconColor.d.ts +5 -0
  36. package/dist/messages/invalidNativeIconColor.d.ts.map +1 -0
  37. package/dist/messages/invalidNativeIconColor.js +34 -0
  38. package/dist/pages/types.d.ts +2 -0
  39. package/dist/pages/types.d.ts.map +1 -1
  40. package/dist/protocol/compatibility/protocol3To5/bridgePage.d.ts +7 -5
  41. package/dist/protocol/dataSources/bridgeDataSourceBinding.d.ts +0 -4
  42. package/dist/protocol/dataSources/bridgeDataSourceBindings.d.ts +0 -4
  43. package/dist/protocol/dataSources/collectionSchema.d.ts +0 -4
  44. package/dist/protocol/dataSources/messages/dataSourcesChanged.d.ts +0 -4
  45. package/dist/protocol/dataSources/messages/queryDataSource.d.ts +14 -12
  46. package/dist/protocol/dataSources/notionDataSource.d.ts +0 -8
  47. package/dist/protocol/dataSources/properties/propertySchema.d.ts +3 -7
  48. package/dist/protocol/dataSources/properties/propertySchema.js +1 -3
  49. package/dist/protocol/handshake/connect.d.ts +13 -2
  50. package/dist/protocol/handshake/init.d.ts +13 -6
  51. package/dist/protocol/icons.d.ts +17 -0
  52. package/dist/protocol/icons.js +27 -0
  53. package/dist/protocol/index.d.ts +6 -5
  54. package/dist/protocol/index.js +4 -3
  55. package/dist/protocol/manifest/capabilities.d.ts +19 -8
  56. package/dist/protocol/manifest/capabilities.js +9 -3
  57. package/dist/protocol/manifest/manifest.d.ts +27 -4
  58. package/dist/protocol/manifest/manifest.js +6 -0
  59. package/dist/protocol/messages/hostToSandbox.d.ts +103 -83
  60. package/dist/protocol/messages/sandboxToHost.d.ts +100 -15
  61. package/dist/protocol/pages/bridgePage.d.ts +7 -6
  62. package/dist/protocol/pages/bridgePage.js +0 -1
  63. package/dist/protocol/pages/messages/createPage.d.ts +102 -26
  64. package/dist/protocol/pages/messages/createPage.js +3 -1
  65. package/dist/protocol/pages/messages/getPage.d.ts +21 -17
  66. package/dist/protocol/pages/messages/updatePage.d.ts +29 -23
  67. package/dist/protocol/pages/pageAppearance.d.ts +36 -1
  68. package/dist/protocol/pages/pageAppearance.js +9 -0
  69. package/dist/protocol/pages/properties/bridgePagePropertyMaps.d.ts +0 -8
  70. package/dist/protocol/pages/properties/bridgePagePropertyValue.d.ts +0 -4
  71. package/dist/protocol/pages/properties/bridgePagePropertyValue.js +0 -5
  72. package/dist/protocol/utils/protocolVersion.d.ts +2 -1
  73. package/dist/protocol/utils/protocolVersion.js +3 -1
  74. package/docs/bridge/changelog.md +21 -1
  75. package/docs/bridge/protocol.md +20 -3
  76. package/docs/bridge/versioning.md +1 -0
  77. package/docs/compatibility.md +15 -7
  78. package/package.json +1 -1
  79. package/src/compatibility/normalizeHostToSandboxMessage.ts +5 -0
  80. package/src/compatibility/pages/toIconForProtocol3To8.ts +17 -0
  81. package/src/compatibility/pages/toPageWithNullTextFallbackProperties.ts +0 -1
  82. package/src/compatibility/protocol3To5/toPageForProtocol3To5.ts +2 -1
  83. package/src/compatibility/protocol3To5/toPropertyValueForProtocol3To5.ts +0 -2
  84. package/src/compatibility/protocol6/normalizePageResultsForProtocol6.ts +11 -2
  85. package/src/compatibility/protocol7To8/normalizePageResultsForProtocol7To8.ts +22 -0
  86. package/src/createCustomBlockHost.ts +1 -0
  87. package/src/index.ts +1 -0
  88. package/src/messages/createSandboxMessageRouter.ts +18 -2
  89. package/src/messages/invalidNativeIconColor.ts +43 -0
  90. package/src/pages/types.ts +3 -0
@@ -185,9 +185,15 @@ filter: {
185
185
 
186
186
  ## Capabilities
187
187
 
188
- `manifest.capabilities` lists `{ capability, resource }` declarations. Both fields are open strings,
189
- so a newer vocabulary never makes an older receiver reject a manifest. An omitted `capabilities`
190
- marks a block that predates approval.
188
+ `manifest.capabilities` lists `{ capability, resource }` declarations. `capability` is an open
189
+ string and `resource` is an open string or a non-empty array of them, so a newer vocabulary never
190
+ makes an older receiver reject a manifest. An array declares the capability on each element.
191
+ Receivers never split a resource string on commas. An omitted `capabilities` marks a block that
192
+ predates approval.
193
+
194
+ Page capability resources can name one manifest data source as `data_source:<key>`. The key is the
195
+ semantic key from `manifest.dataSources`; hosts resolve it to the bound data source when they record
196
+ and check grants. Bare `data_source` covers every declared key.
191
197
 
192
198
  The `capability_not_granted` error code is reserved for a request that the viewer's grants do not
193
199
  cover, with `isRetryable: false`. No adapter sends it yet, and no receiver restricts requests by
@@ -220,3 +226,14 @@ or `""` when absent. Hosts do not evaluate formulas or rollups.
220
226
  The host converts responses to the sandbox's declared protocol. See
221
227
  [versioning and compatibility](versioning.md) for protocol version rules. The host's
222
228
  [compatibility reference](../compatibility.md) describes older response formats.
229
+
230
+ ### Native page icon compatibility
231
+
232
+ Protocol 9 and later use native icon objects. Protocols 3–8 receive external SVG URLs in page
233
+ operation results. Unified query results in protocols 6–8 also use external SVG URLs; flat query
234
+ results in protocols 3–5 omit icon fields. Native colors accept any string in responses and write
235
+ inputs, so newer host colors do not require an SDK update.
236
+
237
+ `createPage` accepts native icons, emoji, custom emoji, and URL-backed icons and covers. The
238
+ application adapter checks submitted image URLs against the block's network permissions before
239
+ 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 10. 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notionhq/custom-blocks-host",
3
- "version": "0.5.2",
3
+ "version": "0.7.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
+ }
@@ -41,7 +41,6 @@ export function toPageWithNullTextFallbackProperties(
41
41
  case "last_edited_time":
42
42
  case "created_by":
43
43
  case "last_edited_by":
44
- case "last_visited_time":
45
44
  case undefined:
46
45
  return [name, property]
47
46
  default:
@@ -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,
@@ -68,8 +68,6 @@ export function toPropertyValueForProtocol3To5(
68
68
  return toDateTimeValueForProtocol3To5(property.created_time)
69
69
  case "last_edited_time":
70
70
  return toDateTimeValueForProtocol3To5(property.last_edited_time)
71
- case "last_visited_time":
72
- return toDateTimeValueForProtocol3To5(property.last_visited_time)
73
71
  case "created_by":
74
72
  return property.created_by ? [property.created_by] : undefined
75
73
  case "last_edited_by":
@@ -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
+ }
@@ -603,6 +603,7 @@ export function createCustomBlockHost(
603
603
  getInitStatus: () => initializationController.status,
604
604
  emit,
605
605
  sendNack,
606
+ sendPageMutationError: postToSandbox,
606
607
  onInvalidConnect: initializationController.handleInvalidConnect,
607
608
  onInvalidInitResult: initializationController.handleInvalidInitResult,
608
609
  onConnect: initializationController.handleConnect,
package/src/index.ts CHANGED
@@ -44,6 +44,7 @@ export type {
44
44
  export type {
45
45
  CreatePageMessageInput,
46
46
  CustomBlockHostOpenPageResult,
47
+ CustomBlockHostPageIcon,
47
48
  CustomBlockHostPageResult,
48
49
  GetPageMessageInput,
49
50
  OpenPageMessageInput,
@@ -6,10 +6,16 @@ import type { InitResultMessage } from "@notionhq/custom-blocks-protocol/handsha
6
6
  import { isInitHandshakeMessage } from "@notionhq/custom-blocks-protocol/handshake/isInitHandshakeMessage.js"
7
7
  import type { InvalidSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/invalidSandboxMessage.js"
8
8
  import type { OpenExternalUrlMessage } from "@notionhq/custom-blocks-protocol/navigation/openExternalUrl.js"
9
- import type { CreatePageMessage } from "@notionhq/custom-blocks-protocol/pages/messages/createPage.js"
9
+ import type {
10
+ CreatePageMessage,
11
+ CreatePageResultMessage,
12
+ } from "@notionhq/custom-blocks-protocol/pages/messages/createPage.js"
10
13
  import type { GetPageMessage } from "@notionhq/custom-blocks-protocol/pages/messages/getPage.js"
11
14
  import type { OpenPageMessage } from "@notionhq/custom-blocks-protocol/pages/messages/openPage.js"
12
- import type { UpdatePageMessage } from "@notionhq/custom-blocks-protocol/pages/messages/updatePage.js"
15
+ import type {
16
+ UpdatePageMessage,
17
+ UpdatePageResultMessage,
18
+ } from "@notionhq/custom-blocks-protocol/pages/messages/updatePage.js"
13
19
  import type { GetUserMessage } from "@notionhq/custom-blocks-protocol/users/messages/getUser.js"
14
20
  import type { ListUsersMessage } from "@notionhq/custom-blocks-protocol/users/messages/listUsers.js"
15
21
  import * as v from "valibot"
@@ -20,6 +26,7 @@ import { invalidInitResultPayloadError } from "../lifecycle/errors/invalidInitRe
20
26
  import type { InitStatus } from "../lifecycle/types.js"
21
27
  import { unreachable } from "../utils/unreachable.js"
22
28
  import { getInvalidSandboxMessageResponse } from "./getInvalidSandboxMessageResponse.js"
29
+ import { getInvalidNativeIconColorResponse } from "./invalidNativeIconColor.js"
23
30
  import type { CustomBlockHostLogDirection } from "./logging.js"
24
31
 
25
32
  const initializationIdentitySchema = v.object({
@@ -38,6 +45,9 @@ export type SandboxMessageRouterOptions = {
38
45
  getInitStatus: () => InitStatus
39
46
  emit: (direction: CustomBlockHostLogDirection, payload: unknown) => void
40
47
  sendNack: (message: InvalidSandboxMessage) => void
48
+ sendPageMutationError: (
49
+ message: UpdatePageResultMessage | CreatePageResultMessage,
50
+ ) => void
41
51
  onInvalidConnect: InitializationController["handleInvalidConnect"]
42
52
  onInvalidInitResult: InitializationController["handleInvalidInitResult"]
43
53
  onConnect: (message: ConnectMessage) => void
@@ -65,6 +75,7 @@ export function createSandboxMessageRouter(
65
75
  getInitStatus,
66
76
  emit,
67
77
  sendNack,
78
+ sendPageMutationError,
68
79
  onInvalidConnect,
69
80
  onInvalidInitResult,
70
81
  onConnect,
@@ -85,6 +96,11 @@ export function createSandboxMessageRouter(
85
96
  data: unknown,
86
97
  issues: readonly v.BaseIssue<unknown>[],
87
98
  ) {
99
+ const iconError = getInvalidNativeIconColorResponse(data)
100
+ if (iconError !== undefined && getInitStatus() === "initialized") {
101
+ sendPageMutationError(iconError)
102
+ return
103
+ }
88
104
  emit("warn", {
89
105
  message: "Ignored unsupported sandbox message",
90
106
  payload: redactExternalUrl(data),
@@ -0,0 +1,43 @@
1
+ import { notionIconColorSchema } from "@notionhq/custom-blocks-protocol/icons.js"
2
+ import type { CreatePageResultMessage } from "@notionhq/custom-blocks-protocol/pages/messages/createPage.js"
3
+ import type { UpdatePageResultMessage } from "@notionhq/custom-blocks-protocol/pages/messages/updatePage.js"
4
+ import * as v from "valibot"
5
+
6
+ const nativeIconMutationSchema = v.object({
7
+ type: v.picklist(["updatePage", "createPage"]),
8
+ requestId: v.string(),
9
+ icon: v.object({
10
+ type: v.literal("icon"),
11
+ icon: v.object({ color: v.unknown() }),
12
+ }),
13
+ })
14
+
15
+ /** Reject malformed colors with a correlated result before invoking a page mutation handler. */
16
+ export function getInvalidNativeIconColorResponse(
17
+ data: unknown,
18
+ ): UpdatePageResultMessage | CreatePageResultMessage | undefined {
19
+ const parsed = v.safeParse(nativeIconMutationSchema, data)
20
+ if (!parsed.success) {
21
+ return
22
+ }
23
+ const color = parsed.output.icon.icon.color
24
+ if (
25
+ color === undefined ||
26
+ v.safeParse(notionIconColorSchema, color).success
27
+ ) {
28
+ return
29
+ }
30
+ return {
31
+ type:
32
+ parsed.output.type === "createPage"
33
+ ? "createPageResult"
34
+ : "updatePageResult",
35
+ requestId: parsed.output.requestId,
36
+ status: "error",
37
+ error: {
38
+ code: "invalid_icon_color",
39
+ message: "Use a supported Notion icon color.",
40
+ isRetryable: false,
41
+ },
42
+ }
43
+ }
@@ -3,6 +3,7 @@ import type { createPageMessageSchema } from "@notionhq/custom-blocks-protocol/p
3
3
  import type { getPageMessageSchema } from "@notionhq/custom-blocks-protocol/pages/messages/getPage.js"
4
4
  import type { openPageMessageSchema } from "@notionhq/custom-blocks-protocol/pages/messages/openPage.js"
5
5
  import type { updatePageMessageSchema } from "@notionhq/custom-blocks-protocol/pages/messages/updatePage.js"
6
+ import type { notionPageIconSchema } from "@notionhq/custom-blocks-protocol/pages/pageAppearance.js"
6
7
  import type { InferInput } from "valibot"
7
8
  import type { CustomBlockHostResult } from "../messages/result.js"
8
9
 
@@ -20,3 +21,5 @@ export type CustomBlockHostPageResult = CustomBlockHostResult<{
20
21
  export type CustomBlockHostOpenPageResult = CustomBlockHostResult<{
21
22
  pageId: string
22
23
  }>
24
+
25
+ export type CustomBlockHostPageIcon = InferInput<typeof notionPageIconSchema>