@notionhq/custom-blocks-host 0.3.8 → 0.4.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 (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +143 -225
  3. package/THIRD_PARTY_LICENSES.md +9 -0
  4. package/dist/autoBindProperties.d.ts +5 -2
  5. package/dist/autoBindProperties.d.ts.map +1 -1
  6. package/dist/autoBindProperties.js +5 -2
  7. package/dist/browser.js +1 -1
  8. package/dist/compatibility/addMetadataReadSchemasForProtocol3To4.d.ts +1215 -0
  9. package/dist/compatibility/addMetadataReadSchemasForProtocol3To4.d.ts.map +1 -0
  10. package/dist/compatibility/addMetadataReadSchemasForProtocol3To4.js +27 -0
  11. package/dist/compatibility/normalizeHostToSandboxMessage.d.ts +8 -0
  12. package/dist/compatibility/normalizeHostToSandboxMessage.d.ts.map +1 -0
  13. package/dist/compatibility/normalizeHostToSandboxMessage.js +39 -0
  14. package/dist/compatibility/normalizePageResultsForProtocol3To5.d.ts +11 -0
  15. package/dist/compatibility/normalizePageResultsForProtocol3To5.d.ts.map +1 -0
  16. package/dist/compatibility/normalizePageResultsForProtocol3To5.js +18 -0
  17. package/dist/compatibility/normalizePageResultsForProtocol6.d.ts +9 -0
  18. package/dist/compatibility/normalizePageResultsForProtocol6.d.ts.map +1 -0
  19. package/dist/compatibility/normalizePageResultsForProtocol6.js +18 -0
  20. package/dist/compatibility/normalizeSandboxToHostMessage.d.ts +1091 -0
  21. package/dist/compatibility/normalizeSandboxToHostMessage.d.ts.map +1 -0
  22. package/dist/compatibility/normalizeSandboxToHostMessage.js +9 -0
  23. package/dist/compatibility/pageForProtocol3To5.d.ts +159 -0
  24. package/dist/compatibility/pageForProtocol3To5.d.ts.map +1 -0
  25. package/dist/{lifecycle/legacyPage.js → compatibility/pageForProtocol3To5.js} +50 -9
  26. package/dist/compatibility/pageResult.d.ts +11 -0
  27. package/dist/compatibility/pageResult.d.ts.map +1 -0
  28. package/dist/compatibility/pageWithNullTextFallbackProperties.d.ts +9 -0
  29. package/dist/compatibility/pageWithNullTextFallbackProperties.d.ts.map +1 -0
  30. package/dist/compatibility/pageWithNullTextFallbackProperties.js +46 -0
  31. package/dist/createCustomBlockHost.d.ts +18 -19
  32. package/dist/createCustomBlockHost.d.ts.map +1 -1
  33. package/dist/createCustomBlockHost.js +103 -38
  34. package/dist/index.d.ts +2 -19
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +1 -2
  37. package/dist/{localBlockUrl.d.ts → isAllowedLocalPreviewUrl.d.ts} +2 -2
  38. package/dist/isAllowedLocalPreviewUrl.d.ts.map +1 -0
  39. package/dist/{localBlockUrl.js → isAllowedLocalPreviewUrl.js} +3 -7
  40. package/dist/lifecycle/initErrors.d.ts +1 -0
  41. package/dist/lifecycle/initErrors.d.ts.map +1 -1
  42. package/dist/lifecycle/initErrors.js +7 -0
  43. package/dist/lifecycle/initializationController.d.ts +6 -4
  44. package/dist/lifecycle/initializationController.d.ts.map +1 -1
  45. package/dist/lifecycle/initializationController.js +24 -15
  46. package/dist/lifecycle/resolveManifest.d.ts +13 -0
  47. package/dist/lifecycle/resolveManifest.d.ts.map +1 -0
  48. package/dist/lifecycle/resolveManifest.js +21 -0
  49. package/dist/lifecycle/types.d.ts +1637 -17
  50. package/dist/lifecycle/types.d.ts.map +1 -1
  51. package/dist/lifecycle/types.js +18 -0
  52. package/dist/messages/sandboxMessageRouter.d.ts.map +1 -1
  53. package/dist/messages/sandboxMessageRouter.js +2 -2
  54. package/dist/protocol/dataSources/dataSourcePage.d.ts +3 -4
  55. package/dist/protocol/dataSources/dataSourcePage.js +2 -3
  56. package/dist/protocol/index.d.ts +1 -0
  57. package/dist/protocol/index.js +1 -0
  58. package/dist/protocol/isLocalBlockServerUrl.d.ts +2 -0
  59. package/dist/protocol/isLocalBlockServerUrl.js +9 -0
  60. package/dist/protocol/messages/connect.d.ts +2 -2
  61. package/dist/protocol/messages/connect.js +2 -2
  62. package/dist/protocol/messages/createPageResult.d.ts +2 -2
  63. package/dist/protocol/messages/createPageResult.js +3 -3
  64. package/dist/protocol/messages/getPage.d.ts +2 -2
  65. package/dist/protocol/messages/getPage.js +3 -3
  66. package/dist/protocol/messages/hostToSandbox.d.ts +3 -3
  67. package/dist/protocol/messages/hostToSandbox.js +10 -10
  68. package/dist/protocol/messages/init.d.ts +1 -1
  69. package/dist/protocol/messages/queryDataSourceResult.d.ts +3 -3
  70. package/dist/protocol/messages/queryDataSourceResult.js +6 -3
  71. package/dist/protocol/messages/updatePageResult.d.ts +2 -2
  72. package/dist/protocol/messages/updatePageResult.js +3 -3
  73. package/dist/protocol/pages/page.d.ts +2 -2
  74. package/dist/protocol/pages/page.js +1 -1
  75. package/dist/protocol/protocolVersion.d.ts +3 -2
  76. package/dist/protocol/protocolVersion.js +4 -2
  77. package/dist/protocol.d.ts +9 -0
  78. package/dist/protocol.d.ts.map +1 -0
  79. package/dist/protocol.js +8 -0
  80. package/docs/bridge/changelog.md +67 -0
  81. package/docs/bridge/protocol.md +182 -0
  82. package/docs/bridge/versioning.md +38 -0
  83. package/docs/compatibility.md +68 -0
  84. package/docs/handlers.md +199 -0
  85. package/docs/lifecycle.md +117 -0
  86. package/package.json +11 -3
  87. package/src/autoBindProperties.ts +5 -2
  88. package/src/compatibility/addMetadataReadSchemasForProtocol3To4.ts +36 -0
  89. package/src/compatibility/normalizeHostToSandboxMessage.ts +54 -0
  90. package/src/compatibility/normalizePageResultsForProtocol3To5.ts +28 -0
  91. package/src/compatibility/normalizePageResultsForProtocol6.ts +23 -0
  92. package/src/compatibility/normalizeSandboxToHostMessage.ts +10 -0
  93. package/src/{lifecycle/legacyPage.ts → compatibility/pageForProtocol3To5.ts} +52 -11
  94. package/src/compatibility/pageResult.ts +18 -0
  95. package/src/compatibility/pageWithNullTextFallbackProperties.ts +53 -0
  96. package/src/createCustomBlockHost.ts +152 -62
  97. package/src/index.ts +4 -51
  98. package/src/{localBlockUrl.ts → isAllowedLocalPreviewUrl.ts} +4 -7
  99. package/src/lifecycle/initErrors.ts +9 -0
  100. package/src/lifecycle/initializationController.ts +34 -19
  101. package/src/lifecycle/resolveManifest.ts +31 -0
  102. package/src/lifecycle/types.ts +84 -16
  103. package/src/messages/sandboxMessageRouter.ts +2 -2
  104. package/src/protocol.ts +8 -0
  105. package/dist/lifecycle/legacyPage.d.ts +0 -70
  106. package/dist/lifecycle/legacyPage.d.ts.map +0 -1
  107. package/dist/lifecycle/loadCustomBlockManifest.d.ts +0 -16
  108. package/dist/lifecycle/loadCustomBlockManifest.d.ts.map +0 -1
  109. package/dist/lifecycle/loadCustomBlockManifest.js +0 -65
  110. package/dist/lifecycle/manifestOptions.d.ts +0 -17
  111. package/dist/lifecycle/manifestOptions.d.ts.map +0 -1
  112. package/dist/lifecycle/normalizeHostToSandboxMessage.d.ts +0 -6
  113. package/dist/lifecycle/normalizeHostToSandboxMessage.d.ts.map +0 -1
  114. package/dist/lifecycle/normalizeHostToSandboxMessage.js +0 -16
  115. package/dist/lifecycle/normalizePage.d.ts +0 -7
  116. package/dist/lifecycle/normalizePage.d.ts.map +0 -1
  117. package/dist/lifecycle/normalizePage.js +0 -22
  118. package/dist/localBlockUrl.d.ts.map +0 -1
  119. package/src/lifecycle/loadCustomBlockManifest.ts +0 -89
  120. package/src/lifecycle/manifestOptions.ts +0 -19
  121. package/src/lifecycle/normalizeHostToSandboxMessage.ts +0 -29
  122. package/src/lifecycle/normalizePage.ts +0 -35
  123. /package/dist/{lifecycle/manifestOptions.js → compatibility/pageResult.js} +0 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Notion Labs, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,259 +1,177 @@
1
1
  # @notionhq/custom-blocks-host
2
2
 
3
- The shared host connects an embedding application to a custom block sandbox. It loads the manifest,
4
- prepares initialization, validates messages, routes requests, manages query subscriptions, and
5
- cleans up the connection.
3
+ The host manages the bridge between your application and a custom block iframe. Your application
4
+ owns the iframe, permissions, data access, persistence, analytics, and UI. Block authors use
5
+ `@notionhq/custom-blocks`.
6
6
 
7
- The embedding application creates the iframe and supplies permissions, record access, bindings,
8
- persistence, analytics, and UI. Both Notion and the dev shell use this package.
7
+ Create the host before navigating the iframe. Pass the full block URL as `sandboxUrl`, and call
8
+ `stop()` when removing the iframe.
9
9
 
10
- ## `createCustomBlockHost()`
10
+ For configuration and lifecycle details, see [Initialization and lifecycle](docs/lifecycle.md).
11
11
 
12
- Create a host before navigating its iframe to the block URL. Set `sandboxOrigin` to that URL's exact
13
- origin. The host accepts messages only from that iframe and origin, and targets outbound messages to
14
- the same origin.
12
+ ## Create a host
13
+
14
+ Install `@notionhq/custom-blocks-host` in your embedding application. Run this TypeScript through
15
+ your browser bundler after the document body exists.
16
+
17
+ This example embeds a block with no data sources and supplies fixed demo context. Replace
18
+ `https://blocks.example.com/hello` with your block's URL. Serve the block from a different origin
19
+ than the embedding application.
15
20
 
16
21
  ```ts
17
22
  import { createCustomBlockHost } from "@notionhq/custom-blocks-host"
18
23
 
19
- const host = createCustomBlockHost({
20
- iframe,
21
- sandboxOrigin: "https://example.com",
22
- manifestSource: "persisted",
23
- getPersistedManifest: async ({ signal }) => loadSavedManifest(signal),
24
- prepareInitialState: async ({ manifest, signal }) => ({
25
- status: "success",
26
- initialState: {
27
- theme,
28
- contrastMode,
29
- blockId,
30
- parent,
31
- page,
32
- currentUser,
33
- dataSources: {
34
- bindings: await loadBindings(manifest, signal),
35
- },
36
- },
37
- }),
38
- handlers: {
39
- queryDataSource: async message => ({
24
+ function mountCustomBlock(container: HTMLElement, blockUrl: URL): () => void {
25
+ if (
26
+ !["http:", "https:"].includes(blockUrl.protocol) ||
27
+ blockUrl.origin === window.location.origin
28
+ ) {
29
+ throw new Error("The block needs an HTTP or HTTPS URL on a separate origin")
30
+ }
31
+
32
+ const wrapper = document.createElement("section")
33
+ const status = document.createElement("p")
34
+ status.setAttribute("role", "status")
35
+ status.textContent = "Loading block…"
36
+ const iframe = document.createElement("iframe")
37
+ iframe.title = "Hello custom block"
38
+ iframe.setAttribute("sandbox", "allow-scripts allow-same-origin")
39
+ iframe.allow = "clipboard-write"
40
+ iframe.style.width = "100%"
41
+ iframe.style.height = "300px"
42
+ iframe.style.visibility = "hidden"
43
+ wrapper.append(status, iframe)
44
+ container.append(wrapper)
45
+
46
+ function setHeight(height: number) {
47
+ iframe.style.height = `${Math.min(1200, Math.max(120, height))}px`
48
+ }
49
+
50
+ const host = createCustomBlockHost({
51
+ iframe,
52
+ sandboxUrl: blockUrl.href,
53
+ manifestSource: "host",
54
+ getPersistedManifest: async () => ({
40
55
  status: "success",
41
- items: await queryRows(message),
42
- hasMore: false,
56
+ manifest: { version: 1, dataSources: {} },
43
57
  }),
44
- resize: message => resizeIframe(message.height),
45
- },
46
- onInitialized: state => showBlock(state),
47
- onConnectionFailure: error => showHostError(error),
48
- onQueryUnsubscribed: undefined,
49
- onLog: (direction, payload) => console.log(direction, payload),
50
- })
51
- ```
52
-
53
- ### Manifest and initial state
54
-
55
- The host starts loading the manifest when it is created. `manifestSource` determines where the
56
- manifest comes from:
57
-
58
- - `"persisted"` requires `getPersistedManifest({ signal })`. The callback supplies a saved block
59
- definition or a dev-shell worker registry manifest.
60
- - `"local"` fetches `/manifest` from `sandboxOrigin`. The URL must pass
61
- [`isLocalCustomBlockUrl({ url, hostOrigin })`](#local-block-urls), including its origin-isolation
62
- check. Requests omit credentials, reject redirects, bypass caches, and time out after five
63
- seconds. This source has no callback or persisted fallback. Local refers to the block server, not
64
- the host's environment.
65
-
66
- Hosts and SDKs use one [manifest schema](../protocol/README.md#manifests), including optional block
67
- metadata. The host validates and normalizes the manifest, then calls
68
- `prepareInitialState({ manifest, signal })` once. The embedding application uses that manifest to
69
- prepare context and bindings, and returns them in `initialState`. The host includes the manifest
70
- with that state in `init`.
71
-
72
- Manifest loading and state preparation do not wait for `connect`. The host listens concurrently and
73
- sends `init` after both preparation and connection succeed. If preparation fails before `connect`,
74
- the host retains the failure and sends `init.error` when the sandbox connects.
75
-
76
- Manifest failures produce `manifest_unavailable`. Exceptions during state preparation produce
77
- `unknown_error`. `onLog("warn", payload)` receives diagnostic details. User-facing manifest errors
78
- remain generic. Stopping the host cancels preparation without logging a cancellation warning.
79
-
80
- The embedding application can load saved definitions separately during local preview, for example to
81
- restore saved bindings after preview ends. Local initialization does not wait for those reads.
58
+ prepareInitialState: async () => ({
59
+ status: "success",
60
+ initialState: {
61
+ theme: "light",
62
+ contrastMode: "standard",
63
+ blockId: "11111111-1111-4111-8111-111111111111",
64
+ parent: {
65
+ type: "page_id",
66
+ page_id: "22222222-2222-4222-8222-222222222222",
67
+ },
68
+ page: {
69
+ type: "page",
70
+ id: "22222222-2222-4222-8222-222222222222",
71
+ parent: { type: "workspace", workspace: true },
72
+ },
73
+ currentUser: {
74
+ object: "user",
75
+ id: "33333333-3333-4333-8333-333333333333",
76
+ name: "Demo viewer",
77
+ avatar_url: null,
78
+ type: "person",
79
+ person: { email: "viewer@example.com" },
80
+ },
81
+ dataSources: { bindings: {} },
82
+ },
83
+ }),
84
+ handlers: {
85
+ queryDataSource: () => ({
86
+ status: "error",
87
+ error: {
88
+ code: "unknown_data_source_key",
89
+ message: "This demo has no data sources.",
90
+ isRetryable: false,
91
+ },
92
+ }),
93
+ resize: message => setHeight(message.height),
94
+ },
95
+ onStatusChange: state => {
96
+ if (state.status === "initialized") {
97
+ setHeight(state.initialHeight ?? 300)
98
+ status.hidden = true
99
+ iframe.style.visibility = "visible"
100
+ } else if (state.status === "failed") {
101
+ iframe.style.visibility = "hidden"
102
+ status.hidden = false
103
+ status.textContent = `Block failed: ${state.error.code}`
104
+ }
105
+ },
106
+ onQueryUnsubscribed: undefined,
107
+ })
82
108
 
83
- ### Connection lifecycle
109
+ // The host must listen before the block starts its handshake.
110
+ iframe.src = blockUrl.href
84
111
 
85
- Each host handles one handshake with one iframe:
112
+ return () => {
113
+ host.stop()
114
+ wrapper.remove()
115
+ }
116
+ }
86
117
 
87
- ```text
88
- sandbox connect → host init → sandbox initResult
118
+ const dispose = mountCustomBlock(document.body, new URL("https://blocks.example.com/hello"))
119
+ window.addEventListener("pagehide", dispose, { once: true })
89
120
  ```
90
121
 
91
- The host accepts the first `connect` attempt. Later attempts do not restart initialization. Live
92
- updates wait for a matching `initResult.success`.
93
-
94
- The first observed iframe `load` is allowed. A second load stops the host and reports retryable
95
- `iframe_reloaded`. Create a new host and iframe to retry initialization or replace the block URL.
96
-
97
- Stopping removes listeners, cancels preparation, clears timers, ends query subscriptions, and
98
- prevents further outbound messages, including pending responses. Call `stop()` when the iframe is
99
- removed.
100
-
101
- Load-event cleanup does not isolate documents before that event. A replacement document at the same
102
- origin can receive messages and invoke handlers until the host stops.
103
-
104
- All handshake timeouts default to five seconds:
105
-
106
- | Option | Waits for | Failure code |
107
- | ----------------------- | --------------------------------------- | ----------------------- |
108
- | `noConnectTimeoutMs` | `connect` after the iframe's first load | `no_connect` |
109
- | `initialStateTimeoutMs` | State preparation after `connect` | `initial_state_timeout` |
110
- | `noInitResultTimeoutMs` | `initResult` after `init` | `no_init_result` |
111
-
112
- The local manifest fetch has its own five-second timeout, starting when the host is created. The SDK
113
- waits for `init` without its own timeout. Handshake failures abort preparation and suppress late
114
- results. Asynchronous callbacks must respect their `signal`.
115
-
116
- `minBridgeProtocolVersion` defaults to the runtime's minimum supported version. A higher value
117
- rejects SDKs below that minimum. A lower value cannot enable unsupported versions. The host does not
118
- enforce a maximum protocol version.
119
-
120
- Unavailable page context requires protocol 5. With protocols 3 and 4, an unavailable page produces
121
- `context_unavailable` during initialization or ends an active connection.
122
+ You should see “Loading block…” until the block completes initialization, then the block iframe. A
123
+ connection failure displays its error code. The example limits the iframe height to 120–1,200 CSS
124
+ pixels.
122
125
 
123
- ### Callbacks
126
+ Call `dispose()` when your application removes the embed. To retry a failed connection or change the
127
+ block URL, dispose of the current embed and call `mountCustomBlock()` again.
124
128
 
125
- - `onInitialized({ manifest, initialHeight })` runs after the sandbox accepts initialization. The
126
- height is optional.
127
- - `onConnectionFailure(error)` reports a handshake failure, timeout, unavailable page context, or
128
- iframe reload. The error contains `{ phase, source, code, message, isRetryable }`. `phase` is
129
- `connect`, `init`, `initResult`, or `runtime`. `source` identifies the host or sandbox that
130
- reported the failure. This callback can run after initialization succeeds.
131
- - `onQueryUnsubscribed(subscriptionId)` releases application resources when a subscription ends. The
132
- host removes the subscription itself. The callback runs for unsubscribe, replacement, terminal
133
- error, or host stop, once per ended subscription.
134
- - `onLog(direction, payload)` receives bridge messages and warnings. The embedding application
135
- chooses where to record them.
129
+ For a production integration, replace the demo context and manifest with your application's values.
130
+ Supply data-source bindings and handlers for the APIs your block uses. Your application must enforce
131
+ permissions and record access.
136
132
 
137
- ### Request types
133
+ To connect workspace data, see [Connect a data source](docs/handlers.md#connect-a-data-source).
138
134
 
139
- `queryDataSource` is required. Other request handlers are optional. Handlers return a result
140
- synchronously or as a promise. Successful results carry their response payload. Errors carry
141
- `{ code, message, isRetryable }`; error codes are open strings.
135
+ ## Guides and reference
142
136
 
143
- TypeScript infers handler request types. Separately defined handlers can import request and
144
- page-property types from `@notionhq/custom-blocks-host`.
137
+ - [Initialization and lifecycle](docs/lifecycle.md): manifests, context, timeouts, live state, and
138
+ cleanup.
139
+ - [Handlers and data](docs/handlers.md): application handlers, data-source integration, queries, and
140
+ navigation.
141
+ - [Host compatibility](docs/compatibility.md): supported versions and response conversions.
145
142
 
146
- Request types such as `GetPageMessage` describe validated messages. Corresponding `*MessageInput`
147
- types accept plain string IDs for callers that construct requests. Initial state, live setters, and
148
- handler results also accept plain string IDs. The runtime validates these values and applies schema
149
- defaults. ID brands distinguish ID kinds in TypeScript; they do not check record existence or
150
- access. The embedding application enforces permissions and record access.
143
+ The runtime handles the wire handshake and message validation. The shared protocol references cover
144
+ payload semantics, version rules, and contract history:
151
145
 
152
- ### Query requests
146
+ - [Wire protocol](docs/bridge/protocol.md)
147
+ - [Protocol versioning](docs/bridge/versioning.md)
148
+ - [Protocol changelog](docs/bridge/changelog.md)
153
149
 
154
- The SDK resolves semantic data-source and property keys to raw IDs before sending requests. The host
155
- validates filters and sorts against actual database properties in `collectionSchema.propertiesById`
156
- before invoking `queryDataSource`. Status group names and option names must match the bound schema.
157
- Sorts retain their order and contain at most ten unique properties.
150
+ ## Protocol schemas and types
158
151
 
159
- ### External navigation
152
+ The `/protocol` entrypoint exposes the protocol definitions bundled with this host release. It
153
+ provides message schemas and types, data shapes, IDs, error definitions, and protocol-version
154
+ constants.
160
155
 
161
- The `openExternalUrl` handler receives an `AbortSignal` and a normalized, absolute HTTP or HTTPS URL
162
- without credentials, limited to 2,048 JavaScript code units before and after normalization. Adapters
163
- display the exact URL, obtain approval, and navigate only after approval. They may apply additional
164
- URL restrictions and should dismiss confirmation UI when the signal aborts on stop or iframe reload.
165
-
166
- Each iframe permits one pending external-navigation request. A different request receives
167
- `pending_navigation`; a duplicate of the active request is ignored. Every completed request is
168
- forgotten, so a subsequent request requires fresh approval. Results are suppressed after stop. The
169
- runtime redacts `openExternalUrl.url` from `onLog` payloads.
170
-
171
- ### Initialization and malformed messages
172
-
173
- The host validates inbound messages, correlates handshake IDs, and rejects malformed requests.
174
- Malformed messages do not restart initialization. The host logs and ignores stale valid `initResult`
175
- messages.
176
-
177
- See the protocol's [initialization contract](../protocol/README.md#initialization) and
178
- [validation and error rules](../protocol/README.md#validation-and-errors) for message shapes and
179
- failure behavior.
180
-
181
- ## Updating live state
182
-
183
- The returned handle exposes `setTheme()`, `setContrastMode()`, `setParent()`, `setPage()`,
184
- `setDataSources()`, `setCurrentUser()`, `refreshQuery()`, and `stop()`.
185
-
186
- State updates made during initialization are held until `initResult.success`. Updates after that
187
- point are sent to the sandbox.
188
-
189
- `refreshQuery()` re-runs `queryDataSource` for each active subscription with the supplied data
190
- source ID, preserving its filter, sorts, and limit. Overlapping refreshes queue one follow-up query.
191
- The running query delivers its result before the follow-up starts.
192
-
193
- Unsubscribe, replacement, and host stop discard queued refreshes and suppress late results.
194
- Non-retryable query errors also end the subscription. Retryable errors retain it. A failed query can
195
- end its subscription while the connection stays active.
196
-
197
- ## Local block URLs
198
-
199
- `isLocalCustomBlockUrl({ url, hostOrigin }: { url: URL; hostOrigin: string })` accepts HTTP(S) URLs
200
- on `localhost`, any `.localhost` subdomain, or `127.0.0.1`, without a username or password. It
201
- rejects URLs on the embedding page's origin and requires a nonempty `hostOrigin`. Pass the embedding
202
- page's serialized origin, such as `window.location.origin`.
203
-
204
- Both local manifest fetching and the dev shell use this policy. Existing callers must add the
205
- required `hostOrigin` argument when adopting this version.
206
-
207
- ## `getAutoBoundPropertyIdsByKey()`
208
-
209
- `getAutoBoundPropertyIdsByKey()` matches manifest properties to database properties for initial
210
- bindings. A candidate must have the same property type and either:
211
-
212
- - An ID that exactly equals the manifest key, without trimming or case folding.
213
- - A display name that equals the manifest display name after trimming and lowercasing.
214
-
215
- Exactly one candidate produces a binding. Zero or multiple candidates leave the property unmapped.
156
+ Import these definitions through the host package to use the protocol version shipped with that
157
+ host:
216
158
 
217
159
  ```ts
218
- import { getAutoBoundPropertyIdsByKey } from "@notionhq/custom-blocks-host"
219
-
220
- const propertyIdsByKey = getAutoBoundPropertyIdsByKey({
221
- manifestProperties: {
222
- title: { name: "Task", type: "title" },
223
- estimate: { name: "Estimate", type: "number" },
224
- },
225
- propertySchemasById: {
226
- title: { name: "Different label", type: "title" },
227
- "property-1": { name: " estimate ", type: "number" },
228
- },
229
- })
230
-
231
- // { title: "title", estimate: "property-1" }
160
+ import {
161
+ sandboxToHostMessageSchema,
162
+ type SandboxToHostMessage,
163
+ } from "@notionhq/custom-blocks-host/protocol"
232
164
  ```
233
165
 
234
- ## Standalone browser module
235
-
236
- The published package includes `dist/browser.js`, a single ES module with all dependencies. Load
237
- this file directly when the browser must not fetch additional modules, such as under an exact-URL
238
- Content Security Policy.
239
-
240
- ## Wire protocol
166
+ `LATEST_BRIDGE_PROTOCOL_VERSION` identifies the latest bridge protocol version defined by the
167
+ bundled protocol.
241
168
 
242
- The host package bundles the shared [protocol](../protocol/README.md) and exposes its public types.
243
- Use the host APIs instead of posting bridge messages directly or importing protocol implementation
244
- files. Runtime source imports schemas from the workspace protocol package; the publish build bundles
245
- that dependency into the host package.
169
+ Host APIs, configuration, handler results, and `*MessageInput` types are available from
170
+ `@notionhq/custom-blocks-host`.
246
171
 
247
- ## Date conversion
172
+ These schemas validate wire payloads. Your application still enforces permissions.
248
173
 
249
- `toNotionDateValue()` converts `NotionDateInput` into the four date variants in
250
- `CustomBlockDateValue`, including reminders. Conversion failures throw `InvalidNotionDateError`.
251
-
252
- ## Other exports
253
-
254
- - `LATEST_BRIDGE_PROTOCOL_VERSION`: the latest bridge protocol version bundled with this host
255
- package.
256
- - `isLocalCustomBlockUrl()`: checks whether a URL can serve a local custom block over HTTP or HTTPS.
174
+ ## Standalone browser module
257
175
 
258
- The package also exports types for host configuration, request handlers, messages, and page
259
- properties.
176
+ `dist/browser.js` is one ES module with all dependencies. Use it when the browser must not fetch
177
+ additional modules, such as under an exact-URL Content Security Policy.
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) Fabian Hiller
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -4,8 +4,8 @@ export type AutoBindableProperty = {
4
4
  type: NotionPropertyType;
5
5
  };
6
6
  /**
7
- * Finds property IDs for a manifest's properties without changing the
8
- * manifest's semantic keys.
7
+ * Returns a map from manifest property keys to database property IDs.
8
+ * Unmapped keys are omitted.
9
9
  *
10
10
  * For each manifest property:
11
11
  * 1. Consider only schema properties of the same type.
@@ -13,6 +13,9 @@ export type AutoBindableProperty = {
13
13
  * (exact string: no trim, no case folding) or its display name equals
14
14
  * the manifest display name after `trim()` and lowercasing.
15
15
  * 3. Bind when there is exactly one candidate. Otherwise leave it unmapped.
16
+ *
17
+ * An ID match has no priority over a display name match. If they identify two
18
+ * different properties of the same type, the manifest property stays unmapped.
16
19
  */
17
20
  export declare function getAutoBoundPropertyIdsByKey(args: {
18
21
  manifestProperties: Readonly<Record<string, AutoBindableProperty>>;
@@ -1 +1 @@
1
- {"version":3,"file":"autoBindProperties.d.ts","sourceRoot":"","sources":["../src/autoBindProperties.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAA;AAExG,MAAM,MAAM,oBAAoB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,kBAAkB,CAAA;CACxB,CAAA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE;IAClD,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAA;IAClE,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAA;CACnE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkBzB"}
1
+ {"version":3,"file":"autoBindProperties.d.ts","sourceRoot":"","sources":["../src/autoBindProperties.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAA;AAExG,MAAM,MAAM,oBAAoB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,kBAAkB,CAAA;CACxB,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE;IAClD,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAA;IAClE,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAA;CACnE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkBzB"}
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Finds property IDs for a manifest's properties without changing the
3
- * manifest's semantic keys.
2
+ * Returns a map from manifest property keys to database property IDs.
3
+ * Unmapped keys are omitted.
4
4
  *
5
5
  * For each manifest property:
6
6
  * 1. Consider only schema properties of the same type.
@@ -8,6 +8,9 @@
8
8
  * (exact string: no trim, no case folding) or its display name equals
9
9
  * the manifest display name after `trim()` and lowercasing.
10
10
  * 3. Bind when there is exactly one candidate. Otherwise leave it unmapped.
11
+ *
12
+ * An ID match has no priority over a display name match. If they identify two
13
+ * different properties of the same type, the manifest property stays unmapped.
11
14
  */
12
15
  export function getAutoBoundPropertyIdsByKey(args) {
13
16
  const { manifestProperties, propertySchemasById } = args;