@notionhq/custom-blocks-host 0.2.20 → 0.3.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.
- package/README.md +170 -239
- package/dist/browser.js +1 -1
- package/dist/createCustomBlockHost.d.ts +5 -5
- package/dist/createCustomBlockHost.d.ts.map +1 -1
- package/dist/createCustomBlockHost.js +54 -15
- package/dist/ids.d.ts +3 -0
- package/dist/ids.d.ts.map +1 -0
- package/dist/ids.js +5 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/lifecycle/loadCustomBlockManifest.d.ts +16 -0
- package/dist/lifecycle/loadCustomBlockManifest.d.ts.map +1 -0
- package/dist/lifecycle/loadCustomBlockManifest.js +65 -0
- package/dist/lifecycle/manifestOptions.d.ts +17 -0
- package/dist/lifecycle/manifestOptions.d.ts.map +1 -0
- package/dist/lifecycle/manifestOptions.js +1 -0
- package/dist/lifecycle/types.d.ts +14 -2
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/lifecycle/types.js +1 -1
- package/dist/protocol/dataSources/dataSourcePage.d.ts +160 -1
- package/dist/protocol/dataSources/dataSourcePage.js +8 -1
- package/dist/protocol/dataSources/propertySchema.d.ts +1 -0
- package/dist/protocol/messages/hostToSandbox.d.ts +159 -2
- package/dist/protocol/messages/queryDataSourceResult.d.ts +159 -2
- package/dist/protocol/messages/queryDataSourceResult.js +5 -2
- package/dist/protocol/protocolVersion.d.ts +2 -1
- package/dist/protocol/protocolVersion.js +4 -1
- package/package.json +1 -1
- package/src/createCustomBlockHost.ts +64 -23
- package/src/ids.ts +9 -0
- package/src/index.ts +12 -1
- package/src/lifecycle/loadCustomBlockManifest.ts +89 -0
- package/src/lifecycle/manifestOptions.ts +19 -0
- package/src/lifecycle/types.ts +17 -5
package/README.md
CHANGED
|
@@ -1,51 +1,17 @@
|
|
|
1
1
|
# @notionhq/custom-blocks-host
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
this package.
|
|
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.
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
The published package includes `dist/browser.js`, a single ES module containing the host runtime
|
|
11
|
-
and all dependencies. Load this file directly when a browser
|
|
12
|
-
must not fetch additional modules, such as under an exact-URL Content Security Policy.
|
|
13
|
-
|
|
14
|
-
## `getAutoBoundPropertyIdsByKey()`
|
|
15
|
-
|
|
16
|
-
`getAutoBoundPropertyIdsByKey()` matches manifest properties to schema properties for initial
|
|
17
|
-
property binding. For each manifest property:
|
|
18
|
-
|
|
19
|
-
1. Consider only schema properties of the same type.
|
|
20
|
-
2. A schema property is a candidate if its ID equals the manifest key (exact string: no trim, no
|
|
21
|
-
case folding) or its display name equals the manifest display name after `trim()` and
|
|
22
|
-
lowercasing.
|
|
23
|
-
3. Bind when there is exactly one candidate. Otherwise leave it unmapped.
|
|
24
|
-
|
|
25
|
-
Wrong-type properties never enter the candidate set.
|
|
26
|
-
|
|
27
|
-
```ts
|
|
28
|
-
import { getAutoBoundPropertyIdsByKey } from "@notionhq/custom-blocks-host"
|
|
29
|
-
|
|
30
|
-
const propertyIdsByKey = getAutoBoundPropertyIdsByKey({
|
|
31
|
-
manifestProperties: {
|
|
32
|
-
title: { name: "Task", type: "title" },
|
|
33
|
-
estimate: { name: "Estimate", type: "number" },
|
|
34
|
-
},
|
|
35
|
-
propertySchemasById: {
|
|
36
|
-
title: { name: "Different label", type: "title" },
|
|
37
|
-
"property-1": { name: " estimate ", type: "number" },
|
|
38
|
-
},
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
// { title: "title", estimate: "property-1" }
|
|
42
|
-
```
|
|
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.
|
|
43
9
|
|
|
44
10
|
## `createCustomBlockHost()`
|
|
45
11
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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.
|
|
49
15
|
|
|
50
16
|
```ts
|
|
51
17
|
import { createCustomBlockHost } from "@notionhq/custom-blocks-host"
|
|
@@ -53,8 +19,9 @@ import { createCustomBlockHost } from "@notionhq/custom-blocks-host"
|
|
|
53
19
|
const host = createCustomBlockHost({
|
|
54
20
|
iframe,
|
|
55
21
|
sandboxOrigin: "https://example.com",
|
|
56
|
-
|
|
57
|
-
|
|
22
|
+
manifestSource: "persisted",
|
|
23
|
+
getPersistedManifest: async ({ signal }) => loadSavedManifest(signal),
|
|
24
|
+
prepareInitialState: async ({ manifest, signal }) => ({
|
|
58
25
|
status: "success",
|
|
59
26
|
initialState: {
|
|
60
27
|
theme,
|
|
@@ -63,8 +30,9 @@ const host = createCustomBlockHost({
|
|
|
63
30
|
parent,
|
|
64
31
|
page,
|
|
65
32
|
currentUser,
|
|
66
|
-
|
|
67
|
-
|
|
33
|
+
dataSources: {
|
|
34
|
+
bindings: await loadBindings(manifest, signal),
|
|
35
|
+
},
|
|
68
36
|
},
|
|
69
37
|
}),
|
|
70
38
|
handlers: {
|
|
@@ -73,232 +41,195 @@ const host = createCustomBlockHost({
|
|
|
73
41
|
items: await queryRows(message),
|
|
74
42
|
hasMore: false,
|
|
75
43
|
}),
|
|
76
|
-
createPage: async message => createPage(message),
|
|
77
|
-
getPage: async message => getPage(message),
|
|
78
|
-
openPage: async message => openPage(message),
|
|
79
|
-
updatePage: async message => updatePage(message),
|
|
80
|
-
getUser: async message => getUser(message),
|
|
81
|
-
listUsers: async message => listUsers(message),
|
|
82
44
|
resize: message => resizeIframe(message.height),
|
|
83
45
|
},
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
},
|
|
87
|
-
onInitialized: state => {
|
|
88
|
-
console.log("Initialized manifest", state.manifest)
|
|
89
|
-
},
|
|
90
|
-
onConnectionFailure: error => {
|
|
91
|
-
showHostError(error)
|
|
92
|
-
},
|
|
46
|
+
onInitialized: state => showBlock(state),
|
|
47
|
+
onConnectionFailure: error => showHostError(error),
|
|
93
48
|
onQueryUnsubscribed: undefined,
|
|
94
|
-
onLog: (direction, payload) =>
|
|
95
|
-
console.log(direction, payload)
|
|
96
|
-
},
|
|
49
|
+
onLog: (direction, payload) => console.log(direction, payload),
|
|
97
50
|
})
|
|
98
51
|
```
|
|
99
52
|
|
|
100
|
-
|
|
101
|
-
supports. Set a higher value to retire older SDKs. Lower values cannot enable versions the runtime
|
|
102
|
-
no longer supports. The host does not enforce a maximum protocol version.
|
|
103
|
-
|
|
104
|
-
Each host handles one initialization handshake with one sandbox iframe:
|
|
105
|
-
|
|
106
|
-
- Create the host before navigating the iframe to the sandbox URL.
|
|
107
|
-
- The host accepts the first `connect` attempt and ignores subsequent `connect` messages, including
|
|
108
|
-
malformed ones. A connection after an initialization timeout does not restart it.
|
|
109
|
-
- The first observed iframe `load` is allowed, including when initialization completes before it.
|
|
110
|
-
- A second observed `load` stops the host and reports retryable `iframe_reloaded` through
|
|
111
|
-
`onConnectionFailure`. A reload or navigation can replace the sandbox document, so the host ends
|
|
112
|
-
the connection instead of continuing to serve the replacement.
|
|
113
|
-
- Stopping removes message and load listeners, clears initialization timers, ends query
|
|
114
|
-
subscriptions, and prevents further outbound messages, including pending responses.
|
|
115
|
-
- To retry or apply a deployment or configuration change, the embedder creates a new host and
|
|
116
|
-
iframe. The host does not restart automatically.
|
|
117
|
-
|
|
118
|
-
The stop takes effect at the second observed load. It does not prevent navigation or isolate the
|
|
119
|
-
original document before that event: a replacement document at the same origin can receive messages
|
|
120
|
-
and invoke handlers until the host stops.
|
|
121
|
-
|
|
122
|
-
SDKs using protocol 5 or later support unavailable page context. For older SDKs, an unavailable
|
|
123
|
-
initial page fails initialization with `context_unavailable`. If page context becomes unavailable
|
|
124
|
-
after initialization, the host stops and reports the same error instead of sending an unsupported
|
|
125
|
-
`pageChanged` message.
|
|
126
|
-
|
|
127
|
-
`getInitialState` runs after the sandbox sends `connect`. It returns a promise for the initial state
|
|
128
|
-
result. The host listens immediately and sends `init` only after `getInitialState` resolves. Await
|
|
129
|
-
record and binding loads inside `getInitialState`.
|
|
130
|
-
|
|
131
|
-
`getInitialState` selects the manifest that the host uses. The host uses this manifest even when
|
|
132
|
-
`connect` supplies another. Production hosts can select persisted definitions. Preview hosts can
|
|
133
|
-
select sandbox manifests. A successful result must include a manifest. If no manifest is available,
|
|
134
|
-
return `manifest_unavailable`.
|
|
135
|
-
|
|
136
|
-
`getInitialState` can return `{ status: "error", error }` to stop initialization. The host converts
|
|
137
|
-
a thrown error or rejected promise to `unknown_error`. If preparation exceeds
|
|
138
|
-
`initialStateTimeoutMs`, the host reports `initial_state_timeout` and stops the handshake.
|
|
139
|
-
|
|
140
|
-
`no_connect` means the host did not receive `connect`. `no_init_result` means the sandbox did not
|
|
141
|
-
acknowledge `init`. The SDK waits for the host without its own timeout.
|
|
142
|
-
|
|
143
|
-
The host discards `getInitialState` results after a timeout or stop, including a stop caused by an
|
|
144
|
-
iframe reload.
|
|
145
|
-
|
|
146
|
-
- `onManifest` provides the selected manifest when `getInitialState` resolves successfully, before
|
|
147
|
-
binding validation and `onInitialized`. Use it to configure bindings even when initialization
|
|
148
|
-
cannot yet succeed.
|
|
149
|
-
|
|
150
|
-
- `onInitialized` reports success after the sandbox accepts initialization. It provides the selected
|
|
151
|
-
`manifest` and the sandbox's optional `initialHeight`.
|
|
152
|
-
- `onConnectionFailure` reports a handshake failure, timeout, unavailable page context, or iframe
|
|
153
|
-
reload. It receives `{ phase, source, code, message, isRetryable }`. The phase identifies
|
|
154
|
-
`connect`, `init`, `initResult`, or `runtime`; the source identifies whether the host detected the
|
|
155
|
-
failure or the sandbox reported it. It can run after initialization succeeds.
|
|
156
|
-
|
|
157
|
-
- `onQueryUnsubscribed` reports when a query subscription ends. Use it to release per-query
|
|
158
|
-
resources, such as listeners or timers. The host removes the subscription itself. A query failure
|
|
159
|
-
can end one subscription while the connection remains active.
|
|
160
|
-
|
|
161
|
-
`queryDataSource` is required. Other request handlers are optional. Every request handler may return
|
|
162
|
-
its result synchronously or as a promise. Results use a `status` discriminator: successful results
|
|
163
|
-
carry their response payload, while errors carry `{ code, message, isRetryable }`.
|
|
53
|
+
### Manifest and initial state
|
|
164
54
|
|
|
165
|
-
|
|
55
|
+
The host starts loading the manifest when it is created. `manifestSource` determines where the
|
|
56
|
+
manifest comes from:
|
|
166
57
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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()`](#local-block-urls) and differ from the embedding page's origin.
|
|
62
|
+
Requests omit credentials, reject redirects, bypass caches, and time out after five seconds. This
|
|
63
|
+
source has no callback or persisted fallback. Local refers to the block server, not the host's
|
|
64
|
+
environment.
|
|
170
65
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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`.
|
|
176
71
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
incoming messages before it calls handlers. Your implementation remains responsible for permissions
|
|
181
|
-
and record access.
|
|
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.
|
|
182
75
|
|
|
183
|
-
|
|
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.
|
|
184
79
|
|
|
185
|
-
The
|
|
186
|
-
|
|
187
|
-
`collectionSchema.propertiesById`. This map must contain only actual database properties. The SDK
|
|
188
|
-
adds synthetic metadata to its separate read map, `propertySchemasById`.
|
|
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.
|
|
189
82
|
|
|
190
|
-
|
|
191
|
-
`collectionSchema` payloads during initialization and binding updates. Query validation still uses
|
|
192
|
-
the actual database schema supplied by the host. Protocol 5 SDKs receive that schema directly and
|
|
193
|
-
add the metadata read schemas themselves.
|
|
83
|
+
### Connection lifecycle
|
|
194
84
|
|
|
195
|
-
|
|
196
|
-
properties before it invokes the handler. It checks group names against the current bound schema.
|
|
197
|
-
Sorts retain their array order. Extra fields and mixed filter types fail protocol validation.
|
|
85
|
+
Each host handles one handshake with one iframe:
|
|
198
86
|
|
|
199
|
-
|
|
200
|
-
|
|
87
|
+
```text
|
|
88
|
+
sandbox connect → host init → sandbox initResult
|
|
89
|
+
```
|
|
201
90
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
`noConnectTimeoutMs`, `initialStateTimeoutMs`, and `noInitResultTimeoutMs`. All default to five
|
|
205
|
-
seconds.
|
|
91
|
+
The host accepts the first `connect` attempt. Later attempts do not restart initialization. Live
|
|
92
|
+
updates wait for a matching `initResult.success`.
|
|
206
93
|
|
|
207
|
-
|
|
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.
|
|
208
96
|
|
|
209
|
-
|
|
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.
|
|
210
100
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
```
|
|
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.
|
|
214
103
|
|
|
215
|
-
|
|
216
|
-
it into `initResult`. The host sends live state changes only after it receives a matching
|
|
217
|
-
`initResult.success`.
|
|
104
|
+
All handshake timeouts default to five seconds:
|
|
218
105
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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` |
|
|
222
111
|
|
|
223
|
-
The
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
NACK in response to a NACK.
|
|
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`.
|
|
227
115
|
|
|
228
|
-
|
|
229
|
-
|
|
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.
|
|
230
119
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
unsupported numeric protocol version takes precedence over `invalid_connect_payload`.
|
|
234
|
-
- The host expects `initResult` and receives a malformed `initResult` with the active
|
|
235
|
-
`initializationId`. The host sends a NACK and fails initialization with
|
|
236
|
-
`invalid_init_result_payload`. This error code describes a local host failure. The host does not
|
|
237
|
-
send it in `initResult.error`.
|
|
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.
|
|
238
122
|
|
|
239
|
-
|
|
240
|
-
timeout. Malformed messages do not restart initialization after it succeeds or fails.
|
|
123
|
+
### Callbacks
|
|
241
124
|
|
|
242
|
-
|
|
243
|
-
|
|
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.
|
|
136
|
+
|
|
137
|
+
### Request types
|
|
244
138
|
|
|
245
|
-
|
|
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.
|
|
142
|
+
|
|
143
|
+
TypeScript infers handler request types. Separately defined handlers can import request and
|
|
144
|
+
page-property types from `@notionhq/custom-blocks-host`.
|
|
145
|
+
|
|
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.
|
|
151
|
+
|
|
152
|
+
### Query requests
|
|
153
|
+
|
|
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.
|
|
158
|
+
|
|
159
|
+
The SDK exposes synthetic metadata through its separate `propertySchemasById` read map. For
|
|
160
|
+
protocols 3 and 4, the host adds synthetic metadata to outgoing `collectionSchema` payloads.
|
|
161
|
+
Protocol 5 SDKs add those read schemas themselves. Host query validation always uses the actual
|
|
162
|
+
database schema.
|
|
163
|
+
|
|
164
|
+
### Initialization and malformed messages
|
|
165
|
+
|
|
166
|
+
The host validates inbound messages, correlates handshake IDs, and rejects malformed requests.
|
|
167
|
+
Malformed messages do not restart initialization. The host logs and ignores stale valid `initResult`
|
|
168
|
+
messages.
|
|
169
|
+
|
|
170
|
+
See the protocol's [initialization contract](../protocol/README.md#initialization) and
|
|
171
|
+
[validation and error rules](../protocol/README.md#validation-and-errors) for message shapes and
|
|
172
|
+
failure behavior.
|
|
246
173
|
|
|
247
174
|
## Updating live state
|
|
248
175
|
|
|
249
|
-
The returned handle exposes
|
|
250
|
-
|
|
251
|
-
- `setTheme()`
|
|
252
|
-
- `setContrastMode()`
|
|
253
|
-
- `setParent()`
|
|
254
|
-
- `setPage()`
|
|
255
|
-
- `setDataSources()`
|
|
256
|
-
- `setCurrentUser()`
|
|
257
|
-
- `refreshQuery()`
|
|
258
|
-
- `stop()`
|
|
259
|
-
|
|
260
|
-
State updates are sent after `initResult.success`. Updates made earlier are held until
|
|
261
|
-
initialization completes. `refreshQuery()` re-runs `queryDataSource` for every active subscription
|
|
262
|
-
with the supplied data source ID, preserving each subscription's filter, sorts, and limit. If that
|
|
263
|
-
query is already running, refresh requests coalesce into one follow-up query. The running query
|
|
264
|
-
delivers its snapshot before the follow-up starts, so continuous changes do not starve updates.
|
|
265
|
-
Unsubscribing, replacing the query, or stopping the host discards its queued refresh.
|
|
266
|
-
|
|
267
|
-
After `stop()`, the host drops outbound messages, including pending request responses. The runtime
|
|
268
|
-
removes a subscription when it receives `unsubscribeDataSourceQuery`. Repeated unsubscribe messages
|
|
269
|
-
are safe. The runtime also ignores results from a query that was unsubscribed while its handler was
|
|
270
|
-
running.
|
|
271
|
-
|
|
272
|
-
A non-retryable query error ends the subscription and discards any queued refresh. This includes
|
|
273
|
-
validation errors before the query handler runs. Retryable errors retain the subscription.
|
|
274
|
-
|
|
275
|
-
Use `onQueryUnsubscribed(subscriptionId)` to release adapter resources such as collection listeners.
|
|
276
|
-
The callback runs when a subscription ends through an explicit unsubscribe, replacement, terminal
|
|
277
|
-
error, or host stop. Repeated removal of the same subscription does not repeat the callback. A late
|
|
278
|
-
result from a replaced query cannot remove its replacement. The sandbox still receives the terminal
|
|
279
|
-
error result.
|
|
280
|
-
|
|
281
|
-
Call `stop()` when the iframe is removed. It detaches listeners, clears timeouts, and discards
|
|
282
|
-
active query subscriptions.
|
|
176
|
+
The returned handle exposes `setTheme()`, `setContrastMode()`, `setParent()`, `setPage()`,
|
|
177
|
+
`setDataSources()`, `setCurrentUser()`, `refreshQuery()`, and `stop()`.
|
|
283
178
|
|
|
284
|
-
|
|
179
|
+
State updates made during initialization are held until `initResult.success`. Updates after that
|
|
180
|
+
point are sent to the sandbox.
|
|
181
|
+
|
|
182
|
+
`refreshQuery()` re-runs `queryDataSource` for each active subscription with the supplied data
|
|
183
|
+
source ID, preserving its filter, sorts, and limit. Overlapping refreshes queue one follow-up query.
|
|
184
|
+
The running query delivers its result before the follow-up starts.
|
|
185
|
+
|
|
186
|
+
Unsubscribe, replacement, and host stop discard queued refreshes and suppress late results.
|
|
187
|
+
Non-retryable query errors also end the subscription. Retryable errors retain it. A failed query can
|
|
188
|
+
end its subscription while the connection stays active.
|
|
189
|
+
|
|
190
|
+
## Local block URLs
|
|
191
|
+
|
|
192
|
+
`isLocalCustomBlockUrl(url: URL)` accepts HTTP(S) URLs on `localhost`, any `.localhost` subdomain,
|
|
193
|
+
or `127.0.0.1`, without a username or password. Both the shared host and the dev shell use this
|
|
194
|
+
policy. The helper does not enforce origin isolation. Local manifest fetching separately rejects the
|
|
195
|
+
embedding page's own origin.
|
|
196
|
+
|
|
197
|
+
## `getAutoBoundPropertyIdsByKey()`
|
|
198
|
+
|
|
199
|
+
`getAutoBoundPropertyIdsByKey()` matches manifest properties to database properties for initial
|
|
200
|
+
bindings. A candidate must have the same property type and either:
|
|
201
|
+
|
|
202
|
+
- An ID that exactly equals the manifest key, without trimming or case folding.
|
|
203
|
+
- A display name that equals the manifest display name after trimming and lowercasing.
|
|
285
204
|
|
|
286
|
-
|
|
287
|
-
initialization handshake, correlates requests, manages query subscriptions, and emits bridge NACKs.
|
|
288
|
-
Use its state-update methods and handlers instead of posting bridge messages directly.
|
|
205
|
+
Exactly one candidate produces a binding. Zero or multiple candidates leave the property unmapped.
|
|
289
206
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
Consumers should use the host APIs above instead of importing protocol implementation files.
|
|
207
|
+
```ts
|
|
208
|
+
import { getAutoBoundPropertyIdsByKey } from "@notionhq/custom-blocks-host"
|
|
293
209
|
|
|
294
|
-
|
|
295
|
-
|
|
210
|
+
const propertyIdsByKey = getAutoBoundPropertyIdsByKey({
|
|
211
|
+
manifestProperties: {
|
|
212
|
+
title: { name: "Task", type: "title" },
|
|
213
|
+
estimate: { name: "Estimate", type: "number" },
|
|
214
|
+
},
|
|
215
|
+
propertySchemasById: {
|
|
216
|
+
title: { name: "Different label", type: "title" },
|
|
217
|
+
"property-1": { name: " estimate ", type: "number" },
|
|
218
|
+
},
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
// { title: "title", estimate: "property-1" }
|
|
222
|
+
```
|
|
296
223
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
224
|
+
## Standalone browser module
|
|
225
|
+
|
|
226
|
+
The published package includes `dist/browser.js`, a single ES module with all dependencies. Load
|
|
227
|
+
this file directly when the browser must not fetch additional modules, such as under an exact-URL
|
|
228
|
+
Content Security Policy.
|
|
229
|
+
|
|
230
|
+
## Wire protocol
|
|
301
231
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
232
|
+
The host package bundles the shared [protocol](../protocol/README.md) and exposes its public types.
|
|
233
|
+
Use the host APIs instead of posting bridge messages directly or importing protocol implementation
|
|
234
|
+
files. Runtime source imports schemas from the workspace protocol package; the publish build bundles
|
|
235
|
+
that dependency into the host package.
|