@notionhq/custom-blocks-host 0.1.26 → 0.1.28
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 +32 -16
- package/dist/createCustomBlockHost.d.ts +6 -5
- package/dist/createCustomBlockHost.d.ts.map +1 -1
- package/dist/createCustomBlockHost.js +38 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lifecycle/initializationController.d.ts +5 -5
- package/dist/lifecycle/initializationController.d.ts.map +1 -1
- package/dist/lifecycle/initializationController.js +31 -19
- package/dist/lifecycle/types.d.ts +12 -1
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/messages/sandboxMessageRouter.js +13 -13
- package/dist/messages/types.d.ts +1 -1
- package/dist/messages/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/createCustomBlockHost.ts +51 -17
- package/src/index.ts +1 -1
- package/src/lifecycle/initializationController.ts +40 -30
- package/src/lifecycle/types.ts +7 -0
- package/src/messages/sandboxMessageRouter.ts +13 -13
- package/src/messages/types.ts +1 -1
package/README.md
CHANGED
|
@@ -77,11 +77,8 @@ const host = createCustomBlockHost({
|
|
|
77
77
|
onInitialized: state => {
|
|
78
78
|
console.log("Initialized manifest", state.manifest)
|
|
79
79
|
},
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
},
|
|
83
|
-
onRestartRequired: () => {
|
|
84
|
-
remountHostAndIframe()
|
|
80
|
+
onFailure: error => {
|
|
81
|
+
showHostError(error)
|
|
85
82
|
},
|
|
86
83
|
onLog: (direction, payload) => {
|
|
87
84
|
console.log(direction, payload)
|
|
@@ -93,7 +90,28 @@ const host = createCustomBlockHost({
|
|
|
93
90
|
supports. Set a higher value to retire older SDKs. Lower values cannot enable versions the runtime
|
|
94
91
|
no longer supports. The host does not enforce a maximum protocol version.
|
|
95
92
|
|
|
96
|
-
Each host
|
|
93
|
+
Each host handles one initialization handshake with one sandbox iframe:
|
|
94
|
+
|
|
95
|
+
- Create the host before navigating the iframe to the sandbox URL.
|
|
96
|
+
- The host accepts the first `connect` attempt and ignores subsequent `connect` messages, including
|
|
97
|
+
malformed ones. A connection after an initialization timeout does not restart it.
|
|
98
|
+
- The first observed iframe `load` is allowed, including when initialization completes before it.
|
|
99
|
+
- A second observed `load` stops the host and reports retryable `iframe_reloaded` through
|
|
100
|
+
`onFailure`. A reload or navigation can replace the sandbox document, so the host ends the
|
|
101
|
+
connection instead of continuing to serve the replacement.
|
|
102
|
+
- Stopping removes message and load listeners, clears initialization timers, ends query
|
|
103
|
+
subscriptions, and prevents further outbound messages, including pending responses.
|
|
104
|
+
- To retry or apply a deployment or configuration change, the embedder creates a new host and
|
|
105
|
+
iframe. The host does not restart automatically.
|
|
106
|
+
|
|
107
|
+
The stop takes effect at the second observed load. It does not prevent navigation or isolate the
|
|
108
|
+
original document before that event: a replacement document at the same origin can receive messages
|
|
109
|
+
and invoke handlers until the host stops.
|
|
110
|
+
|
|
111
|
+
SDKs using protocol 5 or later support unavailable page context. For older SDKs, an unavailable
|
|
112
|
+
initial page fails initialization with `context_unavailable`. If page context becomes unavailable
|
|
113
|
+
after initialization, the host stops and reports the same error instead of sending an unsupported
|
|
114
|
+
`pageChanged` message.
|
|
97
115
|
|
|
98
116
|
- `onManifest` provides the selected manifest as soon as it is available after `connect`, before
|
|
99
117
|
binding validation and `onInitialized`. Use it to configure bindings even when initialization
|
|
@@ -101,10 +119,8 @@ Each host instance initializes once:
|
|
|
101
119
|
|
|
102
120
|
- `onInitialized` reports success after the sandbox accepts initialization. It provides the selected
|
|
103
121
|
`manifest` and the sandbox's optional `initialHeight`.
|
|
104
|
-
- `
|
|
105
|
-
|
|
106
|
-
stops waiting. Recreate the host and iframe together to accept a new connection. This can occur
|
|
107
|
-
after the iframe document changes or a connection arrives after a timeout.
|
|
122
|
+
- `onFailure` reports a handshake failure, timeout, unavailable page context, or iframe reload. It
|
|
123
|
+
can run after initialization succeeds.
|
|
108
124
|
|
|
109
125
|
The host uses the manifest from `connect`, or `initialState.manifest` if `connect` omits it. If
|
|
110
126
|
neither exists, initialization fails with `manifest_unavailable`.
|
|
@@ -145,7 +161,7 @@ filter types fail protocol validation.
|
|
|
145
161
|
host derive bindings from the sandbox-provided manifest.
|
|
146
162
|
|
|
147
163
|
Set `sandboxOrigin` to the iframe's exact origin. The runtime targets outbound messages to that
|
|
148
|
-
origin and
|
|
164
|
+
origin and accepts inbound messages only from that iframe and origin. The runtime also accepts
|
|
149
165
|
`noConnectTimeoutMs` and `noInitResultTimeoutMs`; both default to five seconds.
|
|
150
166
|
|
|
151
167
|
### Initialization and malformed messages
|
|
@@ -186,8 +202,7 @@ timeout. Malformed messages do not restart initialization after it succeeds or f
|
|
|
186
202
|
The host accepts a valid `initResult.error` as a terminal initialization failure. It logs and
|
|
187
203
|
ignores a valid `initResult` with a stale `initializationId` or an unexpected state.
|
|
188
204
|
|
|
189
|
-
|
|
190
|
-
settled must use `onRestartRequired` to recreate the host and iframe together.
|
|
205
|
+
The host does not retry malformed messages.
|
|
191
206
|
|
|
192
207
|
## Updating live state
|
|
193
208
|
|
|
@@ -207,9 +222,10 @@ State updates are sent after `initResult.success`. Updates made earlier are held
|
|
|
207
222
|
initialization completes. `refreshQuery()` re-runs `queryDataSource` for every active subscription
|
|
208
223
|
with the supplied data source ID, preserving each subscription's filter, sorts, and limit.
|
|
209
224
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
while its handler was
|
|
225
|
+
After `stop()`, the host drops outbound messages, including pending request responses. The runtime
|
|
226
|
+
removes a subscription when it receives `unsubscribeDataSourceQuery`. Repeated unsubscribe messages
|
|
227
|
+
are safe. The runtime also ignores results from a query that was unsubscribed while its handler was
|
|
228
|
+
running.
|
|
213
229
|
|
|
214
230
|
Call `stop()` when the iframe is removed. It detaches listeners, clears timeouts, and discards
|
|
215
231
|
active query subscriptions.
|
|
@@ -7,7 +7,7 @@ import type { NotionParent } from "./protocol/parent.js";
|
|
|
7
7
|
import type { NotionTheme } from "./protocol/theme.js";
|
|
8
8
|
import type { NotionUser } from "./protocol/users/user.js";
|
|
9
9
|
import type { CustomBlockHostDataSourcesPayload, CustomBlockHostInitialState } from "./lifecycle/types.js";
|
|
10
|
-
import type {
|
|
10
|
+
import type { CustomBlockHostFailure, CustomBlockHostHandlers, CustomBlockHostLogDirection } from "./messages/types.js";
|
|
11
11
|
export type CustomBlockHostOptions = {
|
|
12
12
|
iframe: HTMLIFrameElement;
|
|
13
13
|
sandboxOrigin: string;
|
|
@@ -31,12 +31,13 @@ export type CustomBlockHostOptions = {
|
|
|
31
31
|
manifest: CustomBlockManifest;
|
|
32
32
|
initialHeight?: number;
|
|
33
33
|
}) => void;
|
|
34
|
-
/** Callback fired when the initialization handshake with the sandbox failed or timed out. */
|
|
35
|
-
onInitializationFailure?: (error: CustomBlockHostInitializationFailure) => void;
|
|
36
34
|
/**
|
|
37
|
-
*
|
|
35
|
+
* Reports a handshake failure, timeout, unavailable page context, or iframe reload.
|
|
36
|
+
* Iframe reloads and unavailable page context for older SDKs stop the host
|
|
37
|
+
* before this callback runs.
|
|
38
|
+
* This callback can run after initialization succeeds.
|
|
38
39
|
*/
|
|
39
|
-
|
|
40
|
+
onFailure?: (error: CustomBlockHostFailure) => void;
|
|
40
41
|
onLog?: (direction: CustomBlockHostLogDirection, payload: unknown) => void;
|
|
41
42
|
};
|
|
42
43
|
export type CustomBlockHostHandle = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../src/createCustomBlockHost.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,kBAAkB,EACvB,MAAM,8CAA8C,CAAA;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AACjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AAavF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4DAA4D,CAAA;AAoBtG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAE9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAIhF,OAAO,KAAK,EACX,iCAAiC,EACjC,2BAA2B,EAC3B,MAAM,sBAAsB,CAAA;AAE7B,OAAO,KAAK,EACX,uBAAuB,EACvB,
|
|
1
|
+
{"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../src/createCustomBlockHost.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,kBAAkB,EACvB,MAAM,8CAA8C,CAAA;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AACjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AAavF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4DAA4D,CAAA;AAoBtG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAE9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAIhF,OAAO,KAAK,EACX,iCAAiC,EACjC,2BAA2B,EAC3B,MAAM,sBAAsB,CAAA;AAE7B,OAAO,KAAK,EACX,sBAAsB,EACtB,uBAAuB,EACvB,2BAA2B,EAC3B,MAAM,qBAAqB,CAAA;AAc5B,MAAM,MAAM,sBAAsB,GAAG;IACpC,MAAM,EAAE,iBAAiB,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,YAAY,EAAE,2BAA2B,CAAA;IACzC,QAAQ,EAAE,uBAAuB,CAAA;IACjC;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,mBAAmB,KAAK,IAAI,CAAA;IACpD,gGAAgG;IAChG,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QACvB,QAAQ,EAAE,mBAAmB,CAAA;QAC7B,aAAa,CAAC,EAAE,MAAM,CAAA;KACtB,KAAK,IAAI,CAAA;IACV;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAA;IACnD,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,2BAA2B,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;CAC1E,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IACnC,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,IAAI,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAC7C,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;IACtC,eAAe,EAAE,CAAC,YAAY,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC3D,SAAS,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAA;IACzC,OAAO,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAA;IACxC,cAAc,EAAE,CAAC,WAAW,EAAE,iCAAiC,KAAK,IAAI,CAAA;IACxE,cAAc,EAAE,CAAC,WAAW,EAAE,UAAU,KAAK,IAAI,CAAA;IACjD,YAAY,EAAE,CAAC,IAAI,EAAE;QAAE,YAAY,EAAE,kBAAkB,CAAA;KAAE,KAAK,IAAI,CAAA;CAClE,CAAA;AAED,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,sBAAsB,GAC7B,qBAAqB,CA+avB"}
|
|
@@ -8,8 +8,11 @@ import { MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION } from "./protocolVersion.js
|
|
|
8
8
|
import { QuerySubscriptions, } from "./queries/querySubscriptions.js";
|
|
9
9
|
import { validateQueryDataSourceFilter } from "./queries/validateQueryDataSourceFilter.js";
|
|
10
10
|
import { validateQueryDataSourceSorts } from "./queries/validateQueryDataSourceSorts.js";
|
|
11
|
+
// Bridge protocol version that introduced page: { type: "unavailable" }.
|
|
12
|
+
// Earlier SDKs cannot represent this context, so the host reports context_unavailable.
|
|
13
|
+
const BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE = 5;
|
|
11
14
|
export function createCustomBlockHost(options) {
|
|
12
|
-
const { iframe, initialState, handlers, onManifest, onInitialized,
|
|
15
|
+
const { iframe, initialState, handlers, onManifest, onInitialized, onFailure, onLog, sandboxOrigin, minBridgeProtocolVersion: configuredMinBridgeProtocolVersion, noConnectTimeoutMs, noInitResultTimeoutMs, } = options;
|
|
13
16
|
// Callers can retire older SDKs, but cannot restore support for protocols the runtime no longer
|
|
14
17
|
// implements. Omission uses the runtime minimum.
|
|
15
18
|
const minBridgeProtocolVersion = Math.max(configuredMinBridgeProtocolVersion ??
|
|
@@ -34,6 +37,10 @@ export function createCustomBlockHost(options) {
|
|
|
34
37
|
onLog?.(direction, payload);
|
|
35
38
|
}
|
|
36
39
|
function post(message) {
|
|
40
|
+
if (initializationController.status === "stopped") {
|
|
41
|
+
// The parent page can stop this host. Do not send messages after the host stops.
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
37
44
|
const sandboxMessage = normalizeHostToSandboxMessage(message, sandboxBridgeProtocolVersion);
|
|
38
45
|
emit("out", sandboxMessage);
|
|
39
46
|
iframe.contentWindow?.postMessage(sandboxMessage, sandboxOrigin);
|
|
@@ -194,6 +201,14 @@ export function createCustomBlockHost(options) {
|
|
|
194
201
|
}),
|
|
195
202
|
prepareInit(connect) {
|
|
196
203
|
sandboxBridgeProtocolVersion = connect.bridgeProtocolVersion;
|
|
204
|
+
if (sandboxBridgeProtocolVersion <
|
|
205
|
+
BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE &&
|
|
206
|
+
page.type === "unavailable") {
|
|
207
|
+
return {
|
|
208
|
+
status: "error",
|
|
209
|
+
error: initErrorForFailureReason("context_unavailable"),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
197
212
|
manifest = connect.manifest ?? initialState.manifest;
|
|
198
213
|
if (manifest === undefined) {
|
|
199
214
|
return {
|
|
@@ -223,13 +238,13 @@ export function createCustomBlockHost(options) {
|
|
|
223
238
|
minBridgeProtocolVersion,
|
|
224
239
|
noConnectTimeoutMs,
|
|
225
240
|
noInitResultTimeoutMs,
|
|
241
|
+
stopHostWithFailure: stopWithFailure,
|
|
226
242
|
onInitResult(message) {
|
|
227
243
|
if (message.status === "success" && manifest !== undefined) {
|
|
228
244
|
onInitialized?.({ manifest, initialHeight: message.initialHeight });
|
|
229
245
|
}
|
|
230
246
|
},
|
|
231
|
-
|
|
232
|
-
onInitializationFailure,
|
|
247
|
+
onFailure,
|
|
233
248
|
});
|
|
234
249
|
const onMessage = createSandboxMessageRouter({
|
|
235
250
|
iframe,
|
|
@@ -253,13 +268,19 @@ export function createCustomBlockHost(options) {
|
|
|
253
268
|
});
|
|
254
269
|
window.addEventListener("message", onMessage);
|
|
255
270
|
iframe.addEventListener("load", initializationController.onIframeLoad);
|
|
271
|
+
function stop() {
|
|
272
|
+
window.removeEventListener("message", onMessage);
|
|
273
|
+
iframe.removeEventListener("load", initializationController.onIframeLoad);
|
|
274
|
+
initializationController.stop();
|
|
275
|
+
querySubscriptions.unsubscribeAll();
|
|
276
|
+
}
|
|
277
|
+
function stopWithFailure(error) {
|
|
278
|
+
stop();
|
|
279
|
+
emit("warn", error);
|
|
280
|
+
onFailure?.(error);
|
|
281
|
+
}
|
|
256
282
|
return {
|
|
257
|
-
stop
|
|
258
|
-
window.removeEventListener("message", onMessage);
|
|
259
|
-
iframe.removeEventListener("load", initializationController.onIframeLoad);
|
|
260
|
-
initializationController.stop();
|
|
261
|
-
querySubscriptions.unsubscribeAll();
|
|
262
|
-
},
|
|
283
|
+
stop,
|
|
263
284
|
post,
|
|
264
285
|
setTheme(nextTheme) {
|
|
265
286
|
theme = nextTheme;
|
|
@@ -299,6 +320,14 @@ export function createCustomBlockHost(options) {
|
|
|
299
320
|
},
|
|
300
321
|
setPage(nextPage) {
|
|
301
322
|
page = nextPage;
|
|
323
|
+
if (nextPage.type === "unavailable" &&
|
|
324
|
+
sandboxBridgeProtocolVersion <
|
|
325
|
+
BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE &&
|
|
326
|
+
(initializationController.status === "waitingForInitResult" ||
|
|
327
|
+
initializationController.status === "success")) {
|
|
328
|
+
stopWithFailure(initErrorForFailureReason("context_unavailable"));
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
302
331
|
if (initializationController.status !== "success") {
|
|
303
332
|
if (initializationController.status === "waitingForInitResult") {
|
|
304
333
|
pendingPageChange = true;
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,6 @@ export type { NotionPagePropertyValue, NotionPagePropertyWriteMap, NotionRichTex
|
|
|
10
10
|
export { type AutoBindableProperty, getAutoBoundPropertyIdsByKey, } from "./autoBindProperties.js";
|
|
11
11
|
export { type CustomBlockHostHandle, type CustomBlockHostOptions, createCustomBlockHost, } from "./createCustomBlockHost.js";
|
|
12
12
|
export type { ConnectSuccessMessage, CustomBlockHostDataSourcesPayload, CustomBlockHostInitialDataSources, CustomBlockHostInitialState, } from "./lifecycle/types.js";
|
|
13
|
-
export type { CreatePageMessageInput, CustomBlockHostCreatePageResult, CustomBlockHostGetPageResult, CustomBlockHostGetUserResult, CustomBlockHostHandlers,
|
|
13
|
+
export type { CreatePageMessageInput, CustomBlockHostCreatePageResult, CustomBlockHostFailure, CustomBlockHostGetPageResult, CustomBlockHostGetUserResult, CustomBlockHostHandlers, CustomBlockHostListUsersResult, CustomBlockHostLogDirection, CustomBlockHostOpenPageResult, CustomBlockHostUpdatePageResult, GetPageMessageInput, GetUserMessageInput, ListUsersMessageInput, OpenPageMessageInput, QueryDataSourceMessageInput, ResizeMessageInput, UpdatePageMessageInput, } from "./messages/types.js";
|
|
14
14
|
export type { CustomBlockHostQueryDataSourceResult } from "./queries/types.js";
|
|
15
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,YAAY,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,YAAY,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,YAAY,EAAE,gBAAgB,EAAE,MAAM,wDAAwD,CAAA;AAC9F,YAAY,EAAE,eAAe,EAAE,MAAM,uDAAuD,CAAA;AAC5F,YAAY,EAAE,sBAAsB,EAAE,MAAM,8DAA8D,CAAA;AAC1G,YAAY,EAAE,aAAa,EAAE,MAAM,qDAAqD,CAAA;AACxF,YAAY,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,YAAY,EACX,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,GAClB,MAAM,gDAAgD,CAAA;AACvD,OAAO,EACN,KAAK,oBAAoB,EACzB,4BAA4B,GAC5B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,qBAAqB,GACrB,MAAM,4BAA4B,CAAA;AACnC,YAAY,EACX,qBAAqB,EACrB,iCAAiC,EACjC,iCAAiC,EACjC,2BAA2B,GAC3B,MAAM,sBAAsB,CAAA;AAC7B,YAAY,EACX,sBAAsB,EACtB,+BAA+B,EAC/B,4BAA4B,EAC5B,4BAA4B,EAC5B,uBAAuB,EACvB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,YAAY,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,YAAY,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,YAAY,EAAE,gBAAgB,EAAE,MAAM,wDAAwD,CAAA;AAC9F,YAAY,EAAE,eAAe,EAAE,MAAM,uDAAuD,CAAA;AAC5F,YAAY,EAAE,sBAAsB,EAAE,MAAM,8DAA8D,CAAA;AAC1G,YAAY,EAAE,aAAa,EAAE,MAAM,qDAAqD,CAAA;AACxF,YAAY,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,YAAY,EACX,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,GAClB,MAAM,gDAAgD,CAAA;AACvD,OAAO,EACN,KAAK,oBAAoB,EACzB,4BAA4B,GAC5B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,qBAAqB,GACrB,MAAM,4BAA4B,CAAA;AACnC,YAAY,EACX,qBAAqB,EACrB,iCAAiC,EACjC,iCAAiC,EACjC,2BAA2B,GAC3B,MAAM,sBAAsB,CAAA;AAC7B,YAAY,EACX,sBAAsB,EACtB,+BAA+B,EAC/B,sBAAsB,EACtB,4BAA4B,EAC5B,4BAA4B,EAC5B,uBAAuB,EACvB,8BAA8B,EAC9B,2BAA2B,EAC3B,6BAA6B,EAC7B,+BAA+B,EAC/B,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,2BAA2B,EAC3B,kBAAkB,EAClB,sBAAsB,GACtB,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EAAE,oCAAoC,EAAE,MAAM,oBAAoB,CAAA"}
|
|
@@ -9,7 +9,7 @@ import type { CustomBlockPage } from "../protocol/pages/page.js";
|
|
|
9
9
|
import type { NotionParent } from "../protocol/parent.js";
|
|
10
10
|
import type { NotionTheme } from "../protocol/theme.js";
|
|
11
11
|
import type { NotionUser } from "../protocol/users/user.js";
|
|
12
|
-
import type {
|
|
12
|
+
import type { CustomBlockHostFailure } from "../messages/types.js";
|
|
13
13
|
import type { ConnectSuccessMessage, CustomBlockHostDataSourcesPayload, InitStatus } from "./types.js";
|
|
14
14
|
type InitializationState = {
|
|
15
15
|
theme: NotionTheme;
|
|
@@ -36,9 +36,9 @@ export type InitializationControllerOptions = {
|
|
|
36
36
|
minBridgeProtocolVersion: number;
|
|
37
37
|
noConnectTimeoutMs?: number;
|
|
38
38
|
noInitResultTimeoutMs?: number;
|
|
39
|
+
stopHostWithFailure: (error: CustomBlockHostFailure) => void;
|
|
39
40
|
onInitResult?: (message: InitResultMessage) => void;
|
|
40
|
-
|
|
41
|
-
onInitializationFailure?: (error: CustomBlockHostInitializationFailure) => void;
|
|
41
|
+
onFailure?: (error: CustomBlockHostFailure) => void;
|
|
42
42
|
};
|
|
43
43
|
export type InitializationController = {
|
|
44
44
|
readonly status: InitStatus;
|
|
@@ -46,10 +46,10 @@ export type InitializationController = {
|
|
|
46
46
|
handleConnect: (message: ConnectMessage) => void;
|
|
47
47
|
handleInitResult: (message: InitResultMessage) => void;
|
|
48
48
|
handleInvalidConnect: (args: {
|
|
49
|
-
initializationId: string;
|
|
49
|
+
initializationId: string | undefined;
|
|
50
50
|
error: CustomBlockInitErrorInfo;
|
|
51
51
|
bridgeProtocolVersion: number | undefined;
|
|
52
|
-
}) => "
|
|
52
|
+
}) => "handled" | "nack";
|
|
53
53
|
handleInvalidInitResult: (args: {
|
|
54
54
|
initializationId: string | undefined;
|
|
55
55
|
error: CustomBlockInitErrorInfo;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initializationController.d.ts","sourceRoot":"","sources":["../../src/lifecycle/initializationController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAA;AACtF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAA;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4DAA4D,CAAA;AACtG,OAAO,KAAK,EACX,wBAAwB,EAExB,MAAM,mDAAmD,CAAA;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAChF,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"initializationController.d.ts","sourceRoot":"","sources":["../../src/lifecycle/initializationController.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,11 +3,12 @@ import { isValidBridgeProtocolVersion } from "./protocolVersion.js";
|
|
|
3
3
|
const DEFAULT_NO_CONNECT_TIMEOUT_MS = 5_000;
|
|
4
4
|
const DEFAULT_NO_INIT_RESULT_TIMEOUT_MS = 5_000;
|
|
5
5
|
export function createInitializationController(options) {
|
|
6
|
-
const { blockId, getState, prepareInit, post, warn, flushPendingHostStateChanges, minBridgeProtocolVersion, noConnectTimeoutMs = DEFAULT_NO_CONNECT_TIMEOUT_MS, noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS,
|
|
6
|
+
const { blockId, getState, prepareInit, post, warn, flushPendingHostStateChanges, minBridgeProtocolVersion, noConnectTimeoutMs = DEFAULT_NO_CONNECT_TIMEOUT_MS, noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS, stopHostWithFailure, onInitResult, onFailure, } = options;
|
|
7
7
|
let initStatus = "waitingForConnect";
|
|
8
8
|
let noConnectTimeoutId;
|
|
9
9
|
let noInitResultTimeoutId;
|
|
10
10
|
let activeInitializationId;
|
|
11
|
+
let hasObservedIframeLoad = false;
|
|
11
12
|
let hasConnectedSandbox = false;
|
|
12
13
|
function clearNoConnectTimeout() {
|
|
13
14
|
if (noConnectTimeoutId !== undefined) {
|
|
@@ -35,7 +36,7 @@ export function createInitializationController(options) {
|
|
|
35
36
|
status: "error",
|
|
36
37
|
error,
|
|
37
38
|
});
|
|
38
|
-
|
|
39
|
+
onFailure?.(error);
|
|
39
40
|
}
|
|
40
41
|
function sendInit(connect) {
|
|
41
42
|
if (initStatus !== "waitingForConnect") {
|
|
@@ -77,22 +78,15 @@ export function createInitializationController(options) {
|
|
|
77
78
|
message: "Sandbox did not send initResult",
|
|
78
79
|
initializationId: connect.initializationId,
|
|
79
80
|
});
|
|
80
|
-
|
|
81
|
+
onFailure?.(error);
|
|
81
82
|
}, noInitResultTimeoutMs);
|
|
82
83
|
return true;
|
|
83
84
|
}
|
|
84
85
|
function handleConnect(message) {
|
|
86
|
+
if (initStatus === "stopped") {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
85
89
|
if (initStatus !== "waitingForConnect") {
|
|
86
|
-
// A sandbox document sends `connect` exactly once, so this connect
|
|
87
|
-
// belongs to a replacement document — or arrived after this host
|
|
88
|
-
// already gave up waiting. The host is single-initialization; the
|
|
89
|
-
// embedder must recreate it together with the iframe for the new
|
|
90
|
-
// document to initialize.
|
|
91
|
-
warn({
|
|
92
|
-
message: "Ignored connect after initialization settled; recreate the host and iframe to re-initialize",
|
|
93
|
-
initializationId: message.initializationId,
|
|
94
|
-
});
|
|
95
|
-
onRestartRequired?.();
|
|
96
90
|
return;
|
|
97
91
|
}
|
|
98
92
|
// Verify the bridge protocol version is valid.
|
|
@@ -130,11 +124,16 @@ export function createInitializationController(options) {
|
|
|
130
124
|
}
|
|
131
125
|
}
|
|
132
126
|
function handleInvalidConnect(args) {
|
|
127
|
+
if (initStatus === "stopped") {
|
|
128
|
+
return "handled";
|
|
129
|
+
}
|
|
133
130
|
if (initStatus !== "waitingForConnect") {
|
|
134
|
-
|
|
135
|
-
return "nack";
|
|
131
|
+
return "handled";
|
|
136
132
|
}
|
|
137
133
|
const { initializationId, bridgeProtocolVersion } = args;
|
|
134
|
+
if (initializationId === undefined) {
|
|
135
|
+
return "nack";
|
|
136
|
+
}
|
|
138
137
|
let error = args.error;
|
|
139
138
|
// Prefer a specific protocol version error over the generic connect payload error
|
|
140
139
|
// when the malformed message includes a numeric version.
|
|
@@ -150,7 +149,7 @@ export function createInitializationController(options) {
|
|
|
150
149
|
}
|
|
151
150
|
}
|
|
152
151
|
sendInitError({ initializationId, error });
|
|
153
|
-
return "
|
|
152
|
+
return "handled";
|
|
154
153
|
}
|
|
155
154
|
function handleInvalidInitResult(args) {
|
|
156
155
|
const { initializationId, error } = args;
|
|
@@ -163,9 +162,21 @@ export function createInitializationController(options) {
|
|
|
163
162
|
clearNoInitResultTimeout();
|
|
164
163
|
initStatus = "error";
|
|
165
164
|
warn(error);
|
|
166
|
-
|
|
165
|
+
onFailure?.(error);
|
|
167
166
|
}
|
|
168
167
|
function onIframeLoad() {
|
|
168
|
+
if (initStatus === "stopped") {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (hasObservedIframeLoad) {
|
|
172
|
+
stopHostWithFailure({
|
|
173
|
+
code: "iframe_reloaded",
|
|
174
|
+
message: "The iframe reloaded. Create a new host and iframe to reconnect.",
|
|
175
|
+
isRetryable: true,
|
|
176
|
+
});
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
hasObservedIframeLoad = true;
|
|
169
180
|
clearNoConnectTimeout();
|
|
170
181
|
if (initStatus !== "waitingForConnect") {
|
|
171
182
|
// Only set the timeout if the iframe loaded before the `connect` message was sent.
|
|
@@ -179,7 +190,7 @@ export function createInitializationController(options) {
|
|
|
179
190
|
initStatus = "error";
|
|
180
191
|
const error = initErrorForFailureReason("no_connect");
|
|
181
192
|
warn(error);
|
|
182
|
-
|
|
193
|
+
onFailure?.(error);
|
|
183
194
|
}, noConnectTimeoutMs);
|
|
184
195
|
}
|
|
185
196
|
function handleInitResult(message) {
|
|
@@ -198,11 +209,12 @@ export function createInitializationController(options) {
|
|
|
198
209
|
}
|
|
199
210
|
else {
|
|
200
211
|
initStatus = "error";
|
|
201
|
-
|
|
212
|
+
onFailure?.(message.error);
|
|
202
213
|
}
|
|
203
214
|
onInitResult?.(message);
|
|
204
215
|
}
|
|
205
216
|
function stop() {
|
|
217
|
+
initStatus = "stopped";
|
|
206
218
|
clearNoConnectTimeout();
|
|
207
219
|
clearNoInitResultTimeout();
|
|
208
220
|
}
|
|
@@ -10,7 +10,18 @@ import type { NotionUser } from "../protocol/users/user.js";
|
|
|
10
10
|
export type ConnectSuccessMessage = Extract<ConnectMessage, {
|
|
11
11
|
status: "success";
|
|
12
12
|
}>;
|
|
13
|
-
|
|
13
|
+
/** Tracks initialization and lifetime for one host instance. */
|
|
14
|
+
export type InitStatus =
|
|
15
|
+
/** The host is waiting for the sandbox's first `connect` message. */
|
|
16
|
+
"waitingForConnect"
|
|
17
|
+
/** The host is waiting for a matching `initResult` after it sends `init`. */
|
|
18
|
+
| "waitingForInitResult"
|
|
19
|
+
/** The host is initialized and can send live updates. */
|
|
20
|
+
| "success"
|
|
21
|
+
/** The host is in an error state after an initialization failure or timeout. */
|
|
22
|
+
| "error"
|
|
23
|
+
/** The host is stopped and no longer sends or accepts sandbox messages. */
|
|
24
|
+
| "stopped";
|
|
14
25
|
export type CustomBlockHostDataSourcesPayload = {
|
|
15
26
|
bindings: NotionDataSourceBindings;
|
|
16
27
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lifecycle/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAA;AACtF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4DAA4D,CAAA;AAC1G,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAA;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAEhF,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAC1C,cAAc,EACd;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CACrB,CAAA;AAED,MAAM,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lifecycle/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAA;AACtF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4DAA4D,CAAA;AAC1G,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAA;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAEhF,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAC1C,cAAc,EACd;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CACrB,CAAA;AAED,gEAAgE;AAChE,MAAM,MAAM,UAAU;AACrB,qEAAqE;AACnE,mBAAmB;AACrB,6EAA6E;GAC3E,sBAAsB;AACxB,yDAAyD;GACvD,SAAS;AACX,gFAAgF;GAC9E,OAAO;AACT,2EAA2E;GACzE,SAAS,CAAA;AAEZ,MAAM,MAAM,iCAAiC,GAAG;IAC/C,QAAQ,EAAE,wBAAwB,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,iCAAiC,GAC1C,iCAAiC,GACjC,CAAC,CAAC,OAAO,EAAE,qBAAqB,KAAK,iCAAiC,CAAC,CAAA;AAE1E,MAAM,MAAM,2BAA2B,GAAG;IACzC,KAAK,EAAE,WAAW,CAAA;IAClB;;;OAGG;IACH,YAAY,CAAC,EAAE,kBAAkB,CAAA;IACjC,OAAO,EAAE,aAAa,CAAA;IACtB,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,eAAe,CAAA;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,mBAAmB,CAAA;IAC9B,WAAW,EAAE,iCAAiC,CAAA;IAC9C,WAAW,EAAE,UAAU,CAAA;CACvB,CAAA"}
|
|
@@ -23,19 +23,19 @@ export function createSandboxMessageRouter(options) {
|
|
|
23
23
|
const response = getInvalidSandboxMessageResponse(data);
|
|
24
24
|
if (response.incomingType === "connect") {
|
|
25
25
|
const parsedIdentity = v.safeParse(initializationIdentitySchema, data);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
const parsedVersion = v.safeParse(bridgeProtocolVersionSchema, data);
|
|
27
|
+
const responseType = onInvalidConnect({
|
|
28
|
+
initializationId: parsedIdentity.success
|
|
29
|
+
? parsedIdentity.output.initializationId
|
|
30
|
+
: undefined,
|
|
31
|
+
error: initErrorForFailureReason("invalid_connect_payload"),
|
|
32
|
+
bridgeProtocolVersion: parsedVersion.success
|
|
33
|
+
? parsedVersion.output.bridgeProtocolVersion
|
|
34
|
+
: undefined,
|
|
35
|
+
});
|
|
36
|
+
if (responseType === "handled") {
|
|
37
|
+
// The controller sent init.error or stopped the host; neither needs a NACK.
|
|
38
|
+
return;
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
if (response.incomingType === "initResult") {
|
package/dist/messages/types.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export type CustomBlockHostHandlers = {
|
|
|
30
30
|
updatePage?: (message: UpdatePageMessage) => MaybePromise<CustomBlockHostUpdatePageResult>;
|
|
31
31
|
resize?: (message: ResizeMessage) => void;
|
|
32
32
|
};
|
|
33
|
-
export type
|
|
33
|
+
export type CustomBlockHostFailure = CustomBlockInitErrorInfo | CustomBlockInitResultErrorInfo;
|
|
34
34
|
export type CustomBlockHostLogDirection = "in" | "out" | "warn";
|
|
35
35
|
export type CreatePageMessageInput = InferInput<typeof createPageMessageSchema>;
|
|
36
36
|
export type GetPageMessageInput = InferInput<typeof getPageMessageSchema>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/messages/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,iBAAiB,EACjB,uBAAuB,EACvB,MAAM,yDAAyD,CAAA;AAChE,OAAO,KAAK,EACX,uBAAuB,EACvB,8BAA8B,EAC9B,MAAM,+DAA+D,CAAA;AACtE,OAAO,KAAK,EACX,2BAA2B,EAC3B,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,MAAM,sDAAsD,CAAA;AAC7D,OAAO,KAAK,EACX,2BAA2B,EAC3B,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,MAAM,sDAAsD,CAAA;AAC7D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mDAAmD,CAAA;AACjG,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,yDAAyD,CAAA;AAC7G,OAAO,KAAK,EACX,6BAA6B,EAC7B,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,EACtB,MAAM,wDAAwD,CAAA;AAC/D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6DAA6D,CAAA;AACvG,OAAO,KAAK,EACX,4BAA4B,EAC5B,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,MAAM,uDAAuD,CAAA;AAC9D,OAAO,KAAK,EACX,sBAAsB,EACtB,4BAA4B,EAC5B,MAAM,8DAA8D,CAAA;AACrE,OAAO,KAAK,EACX,aAAa,EACb,mBAAmB,EACnB,MAAM,qDAAqD,CAAA;AAC5D,OAAO,KAAK,EACX,iBAAiB,EACjB,uBAAuB,EACvB,MAAM,yDAAyD,CAAA;AAChE,OAAO,KAAK,EACX,8BAA8B,EAC9B,uBAAuB,EACvB,MAAM,+DAA+D,CAAA;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,qBAAqB,CAAA;AAE/E,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;AAErC,MAAM,MAAM,+BAA+B,GAAG,oBAAoB,CACjE,uBAAuB,EACvB,8BAA8B,CAC9B,CAAA;AAED,MAAM,MAAM,4BAA4B,GAAG,oBAAoB,CAC9D,oBAAoB,EACpB,2BAA2B,CAC3B,CAAA;AAED,MAAM,MAAM,+BAA+B,GAAG,oBAAoB,CACjE,uBAAuB,EACvB,8BAA8B,CAC9B,CAAA;AAED,MAAM,MAAM,4BAA4B,GAAG,oBAAoB,CAC9D,oBAAoB,EACpB,2BAA2B,CAC3B,CAAA;AAED,MAAM,MAAM,8BAA8B,GAAG,oBAAoB,CAChE,sBAAsB,EACtB,6BAA6B,CAC7B,CAAA;AAED,MAAM,MAAM,6BAA6B,GAAG,oBAAoB,CAC/D,qBAAqB,EACrB,4BAA4B,CAC5B,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACrC,eAAe,EAAE,CAChB,OAAO,EAAE,sBAAsB,KAC3B,YAAY,CAAC,oCAAoC,CAAC,CAAA;IACvD,UAAU,CAAC,EAAE,CACZ,OAAO,EAAE,iBAAiB,KACtB,YAAY,CAAC,+BAA+B,CAAC,CAAA;IAClD,OAAO,CAAC,EAAE,CACT,OAAO,EAAE,cAAc,KACnB,YAAY,CAAC,4BAA4B,CAAC,CAAA;IAC/C,OAAO,CAAC,EAAE,CACT,OAAO,EAAE,cAAc,KACnB,YAAY,CAAC,4BAA4B,CAAC,CAAA;IAC/C,SAAS,CAAC,EAAE,CACX,OAAO,EAAE,gBAAgB,KACrB,YAAY,CAAC,8BAA8B,CAAC,CAAA;IACjD,QAAQ,CAAC,EAAE,CACV,OAAO,EAAE,eAAe,KACpB,YAAY,CAAC,6BAA6B,CAAC,CAAA;IAChD,UAAU,CAAC,EAAE,CACZ,OAAO,EAAE,iBAAiB,KACtB,YAAY,CAAC,+BAA+B,CAAC,CAAA;IAClD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;CACzC,CAAA;AAED,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/messages/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -51,8 +51,8 @@ import type {
|
|
|
51
51
|
} from "./lifecycle/types.js"
|
|
52
52
|
import { createSandboxMessageRouter } from "./messages/sandboxMessageRouter.js"
|
|
53
53
|
import type {
|
|
54
|
+
CustomBlockHostFailure,
|
|
54
55
|
CustomBlockHostHandlers,
|
|
55
|
-
CustomBlockHostInitializationFailure,
|
|
56
56
|
CustomBlockHostLogDirection,
|
|
57
57
|
} from "./messages/types.js"
|
|
58
58
|
import { MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION } from "./protocolVersion.js"
|
|
@@ -64,6 +64,10 @@ import type { CustomBlockHostQueryDataSourceResult } from "./queries/types.js"
|
|
|
64
64
|
import { validateQueryDataSourceFilter } from "./queries/validateQueryDataSourceFilter.js"
|
|
65
65
|
import { validateQueryDataSourceSorts } from "./queries/validateQueryDataSourceSorts.js"
|
|
66
66
|
|
|
67
|
+
// Bridge protocol version that introduced page: { type: "unavailable" }.
|
|
68
|
+
// Earlier SDKs cannot represent this context, so the host reports context_unavailable.
|
|
69
|
+
const BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE = 5
|
|
70
|
+
|
|
67
71
|
export type CustomBlockHostOptions = {
|
|
68
72
|
iframe: HTMLIFrameElement
|
|
69
73
|
sandboxOrigin: string
|
|
@@ -87,14 +91,13 @@ export type CustomBlockHostOptions = {
|
|
|
87
91
|
manifest: CustomBlockManifest
|
|
88
92
|
initialHeight?: number
|
|
89
93
|
}) => void
|
|
90
|
-
/** Callback fired when the initialization handshake with the sandbox failed or timed out. */
|
|
91
|
-
onInitializationFailure?: (
|
|
92
|
-
error: CustomBlockHostInitializationFailure,
|
|
93
|
-
) => void
|
|
94
94
|
/**
|
|
95
|
-
*
|
|
95
|
+
* Reports a handshake failure, timeout, unavailable page context, or iframe reload.
|
|
96
|
+
* Iframe reloads and unavailable page context for older SDKs stop the host
|
|
97
|
+
* before this callback runs.
|
|
98
|
+
* This callback can run after initialization succeeds.
|
|
96
99
|
*/
|
|
97
|
-
|
|
100
|
+
onFailure?: (error: CustomBlockHostFailure) => void
|
|
98
101
|
onLog?: (direction: CustomBlockHostLogDirection, payload: unknown) => void
|
|
99
102
|
}
|
|
100
103
|
|
|
@@ -119,8 +122,7 @@ export function createCustomBlockHost(
|
|
|
119
122
|
handlers,
|
|
120
123
|
onManifest,
|
|
121
124
|
onInitialized,
|
|
122
|
-
|
|
123
|
-
onRestartRequired,
|
|
125
|
+
onFailure,
|
|
124
126
|
onLog,
|
|
125
127
|
sandboxOrigin,
|
|
126
128
|
minBridgeProtocolVersion: configuredMinBridgeProtocolVersion,
|
|
@@ -157,6 +159,10 @@ export function createCustomBlockHost(
|
|
|
157
159
|
}
|
|
158
160
|
|
|
159
161
|
function post(message: HostToSandboxMessage) {
|
|
162
|
+
if (initializationController.status === "stopped") {
|
|
163
|
+
// The parent page can stop this host. Do not send messages after the host stops.
|
|
164
|
+
return
|
|
165
|
+
}
|
|
160
166
|
const sandboxMessage = normalizeHostToSandboxMessage(
|
|
161
167
|
message,
|
|
162
168
|
sandboxBridgeProtocolVersion,
|
|
@@ -340,6 +346,16 @@ export function createCustomBlockHost(
|
|
|
340
346
|
}),
|
|
341
347
|
prepareInit(connect) {
|
|
342
348
|
sandboxBridgeProtocolVersion = connect.bridgeProtocolVersion
|
|
349
|
+
if (
|
|
350
|
+
sandboxBridgeProtocolVersion <
|
|
351
|
+
BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE &&
|
|
352
|
+
page.type === "unavailable"
|
|
353
|
+
) {
|
|
354
|
+
return {
|
|
355
|
+
status: "error",
|
|
356
|
+
error: initErrorForFailureReason("context_unavailable"),
|
|
357
|
+
}
|
|
358
|
+
}
|
|
343
359
|
manifest = connect.manifest ?? initialState.manifest
|
|
344
360
|
if (manifest === undefined) {
|
|
345
361
|
return {
|
|
@@ -373,13 +389,13 @@ export function createCustomBlockHost(
|
|
|
373
389
|
minBridgeProtocolVersion,
|
|
374
390
|
noConnectTimeoutMs,
|
|
375
391
|
noInitResultTimeoutMs,
|
|
392
|
+
stopHostWithFailure: stopWithFailure,
|
|
376
393
|
onInitResult(message) {
|
|
377
394
|
if (message.status === "success" && manifest !== undefined) {
|
|
378
395
|
onInitialized?.({ manifest, initialHeight: message.initialHeight })
|
|
379
396
|
}
|
|
380
397
|
},
|
|
381
|
-
|
|
382
|
-
onInitializationFailure,
|
|
398
|
+
onFailure,
|
|
383
399
|
})
|
|
384
400
|
|
|
385
401
|
const onMessage = createSandboxMessageRouter({
|
|
@@ -407,13 +423,21 @@ export function createCustomBlockHost(
|
|
|
407
423
|
window.addEventListener("message", onMessage)
|
|
408
424
|
iframe.addEventListener("load", initializationController.onIframeLoad)
|
|
409
425
|
|
|
426
|
+
function stop() {
|
|
427
|
+
window.removeEventListener("message", onMessage)
|
|
428
|
+
iframe.removeEventListener("load", initializationController.onIframeLoad)
|
|
429
|
+
initializationController.stop()
|
|
430
|
+
querySubscriptions.unsubscribeAll()
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function stopWithFailure(error: CustomBlockHostFailure) {
|
|
434
|
+
stop()
|
|
435
|
+
emit("warn", error)
|
|
436
|
+
onFailure?.(error)
|
|
437
|
+
}
|
|
438
|
+
|
|
410
439
|
return {
|
|
411
|
-
stop
|
|
412
|
-
window.removeEventListener("message", onMessage)
|
|
413
|
-
iframe.removeEventListener("load", initializationController.onIframeLoad)
|
|
414
|
-
initializationController.stop()
|
|
415
|
-
querySubscriptions.unsubscribeAll()
|
|
416
|
-
},
|
|
440
|
+
stop,
|
|
417
441
|
post,
|
|
418
442
|
setTheme(nextTheme) {
|
|
419
443
|
theme = nextTheme
|
|
@@ -453,6 +477,16 @@ export function createCustomBlockHost(
|
|
|
453
477
|
},
|
|
454
478
|
setPage(nextPage) {
|
|
455
479
|
page = nextPage
|
|
480
|
+
if (
|
|
481
|
+
nextPage.type === "unavailable" &&
|
|
482
|
+
sandboxBridgeProtocolVersion <
|
|
483
|
+
BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE &&
|
|
484
|
+
(initializationController.status === "waitingForInitResult" ||
|
|
485
|
+
initializationController.status === "success")
|
|
486
|
+
) {
|
|
487
|
+
stopWithFailure(initErrorForFailureReason("context_unavailable"))
|
|
488
|
+
return
|
|
489
|
+
}
|
|
456
490
|
if (initializationController.status !== "success") {
|
|
457
491
|
if (initializationController.status === "waitingForInitResult") {
|
|
458
492
|
pendingPageChange = true
|
package/src/index.ts
CHANGED
|
@@ -29,10 +29,10 @@ export type {
|
|
|
29
29
|
export type {
|
|
30
30
|
CreatePageMessageInput,
|
|
31
31
|
CustomBlockHostCreatePageResult,
|
|
32
|
+
CustomBlockHostFailure,
|
|
32
33
|
CustomBlockHostGetPageResult,
|
|
33
34
|
CustomBlockHostGetUserResult,
|
|
34
35
|
CustomBlockHostHandlers,
|
|
35
|
-
CustomBlockHostInitializationFailure,
|
|
36
36
|
CustomBlockHostListUsersResult,
|
|
37
37
|
CustomBlockHostLogDirection,
|
|
38
38
|
CustomBlockHostOpenPageResult,
|
|
@@ -12,7 +12,7 @@ import type { CustomBlockPage } from "@notionhq/custom-blocks-protocol/pages/pag
|
|
|
12
12
|
import type { NotionParent } from "@notionhq/custom-blocks-protocol/parent.js"
|
|
13
13
|
import type { NotionTheme } from "@notionhq/custom-blocks-protocol/theme.js"
|
|
14
14
|
import type { NotionUser } from "@notionhq/custom-blocks-protocol/users/user.js"
|
|
15
|
-
import type {
|
|
15
|
+
import type { CustomBlockHostFailure } from "../messages/types.js"
|
|
16
16
|
import { initErrorForFailureReason } from "./initErrors.js"
|
|
17
17
|
import { isValidBridgeProtocolVersion } from "./protocolVersion.js"
|
|
18
18
|
import type {
|
|
@@ -53,11 +53,9 @@ export type InitializationControllerOptions = {
|
|
|
53
53
|
minBridgeProtocolVersion: number
|
|
54
54
|
noConnectTimeoutMs?: number
|
|
55
55
|
noInitResultTimeoutMs?: number
|
|
56
|
+
stopHostWithFailure: (error: CustomBlockHostFailure) => void
|
|
56
57
|
onInitResult?: (message: InitResultMessage) => void
|
|
57
|
-
|
|
58
|
-
onInitializationFailure?: (
|
|
59
|
-
error: CustomBlockHostInitializationFailure,
|
|
60
|
-
) => void
|
|
58
|
+
onFailure?: (error: CustomBlockHostFailure) => void
|
|
61
59
|
}
|
|
62
60
|
|
|
63
61
|
export type InitializationController = {
|
|
@@ -66,10 +64,10 @@ export type InitializationController = {
|
|
|
66
64
|
handleConnect: (message: ConnectMessage) => void
|
|
67
65
|
handleInitResult: (message: InitResultMessage) => void
|
|
68
66
|
handleInvalidConnect: (args: {
|
|
69
|
-
initializationId: string
|
|
67
|
+
initializationId: string | undefined
|
|
70
68
|
error: CustomBlockInitErrorInfo
|
|
71
69
|
bridgeProtocolVersion: number | undefined
|
|
72
|
-
}) => "
|
|
70
|
+
}) => "handled" | "nack"
|
|
73
71
|
handleInvalidInitResult: (args: {
|
|
74
72
|
initializationId: string | undefined
|
|
75
73
|
error: CustomBlockInitErrorInfo
|
|
@@ -91,15 +89,16 @@ export function createInitializationController(
|
|
|
91
89
|
minBridgeProtocolVersion,
|
|
92
90
|
noConnectTimeoutMs = DEFAULT_NO_CONNECT_TIMEOUT_MS,
|
|
93
91
|
noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS,
|
|
92
|
+
stopHostWithFailure,
|
|
94
93
|
onInitResult,
|
|
95
|
-
|
|
96
|
-
onInitializationFailure,
|
|
94
|
+
onFailure,
|
|
97
95
|
} = options
|
|
98
96
|
|
|
99
97
|
let initStatus: InitStatus = "waitingForConnect"
|
|
100
98
|
let noConnectTimeoutId: number | undefined
|
|
101
99
|
let noInitResultTimeoutId: number | undefined
|
|
102
100
|
let activeInitializationId: string | undefined
|
|
101
|
+
let hasObservedIframeLoad = false
|
|
103
102
|
let hasConnectedSandbox = false
|
|
104
103
|
|
|
105
104
|
function clearNoConnectTimeout() {
|
|
@@ -133,7 +132,7 @@ export function createInitializationController(
|
|
|
133
132
|
status: "error",
|
|
134
133
|
error,
|
|
135
134
|
})
|
|
136
|
-
|
|
135
|
+
onFailure?.(error)
|
|
137
136
|
}
|
|
138
137
|
|
|
139
138
|
function sendInit(connect: ConnectSuccessMessage): boolean {
|
|
@@ -176,24 +175,16 @@ export function createInitializationController(
|
|
|
176
175
|
message: "Sandbox did not send initResult",
|
|
177
176
|
initializationId: connect.initializationId,
|
|
178
177
|
})
|
|
179
|
-
|
|
178
|
+
onFailure?.(error)
|
|
180
179
|
}, noInitResultTimeoutMs)
|
|
181
180
|
return true
|
|
182
181
|
}
|
|
183
182
|
|
|
184
183
|
function handleConnect(message: ConnectMessage) {
|
|
184
|
+
if (initStatus === "stopped") {
|
|
185
|
+
return
|
|
186
|
+
}
|
|
185
187
|
if (initStatus !== "waitingForConnect") {
|
|
186
|
-
// A sandbox document sends `connect` exactly once, so this connect
|
|
187
|
-
// belongs to a replacement document — or arrived after this host
|
|
188
|
-
// already gave up waiting. The host is single-initialization; the
|
|
189
|
-
// embedder must recreate it together with the iframe for the new
|
|
190
|
-
// document to initialize.
|
|
191
|
-
warn({
|
|
192
|
-
message:
|
|
193
|
-
"Ignored connect after initialization settled; recreate the host and iframe to re-initialize",
|
|
194
|
-
initializationId: message.initializationId,
|
|
195
|
-
})
|
|
196
|
-
onRestartRequired?.()
|
|
197
188
|
return
|
|
198
189
|
}
|
|
199
190
|
|
|
@@ -238,16 +229,21 @@ export function createInitializationController(
|
|
|
238
229
|
}
|
|
239
230
|
|
|
240
231
|
function handleInvalidConnect(args: {
|
|
241
|
-
initializationId: string
|
|
232
|
+
initializationId: string | undefined
|
|
242
233
|
error: CustomBlockInitErrorInfo
|
|
243
234
|
bridgeProtocolVersion: number | undefined
|
|
244
|
-
}): "
|
|
235
|
+
}): "handled" | "nack" {
|
|
236
|
+
if (initStatus === "stopped") {
|
|
237
|
+
return "handled"
|
|
238
|
+
}
|
|
245
239
|
if (initStatus !== "waitingForConnect") {
|
|
246
|
-
|
|
247
|
-
return "nack"
|
|
240
|
+
return "handled"
|
|
248
241
|
}
|
|
249
242
|
|
|
250
243
|
const { initializationId, bridgeProtocolVersion } = args
|
|
244
|
+
if (initializationId === undefined) {
|
|
245
|
+
return "nack"
|
|
246
|
+
}
|
|
251
247
|
let error = args.error
|
|
252
248
|
// Prefer a specific protocol version error over the generic connect payload error
|
|
253
249
|
// when the malformed message includes a numeric version.
|
|
@@ -262,7 +258,7 @@ export function createInitializationController(
|
|
|
262
258
|
}
|
|
263
259
|
}
|
|
264
260
|
sendInitError({ initializationId, error })
|
|
265
|
-
return "
|
|
261
|
+
return "handled"
|
|
266
262
|
}
|
|
267
263
|
|
|
268
264
|
function handleInvalidInitResult(args: {
|
|
@@ -281,10 +277,23 @@ export function createInitializationController(
|
|
|
281
277
|
clearNoInitResultTimeout()
|
|
282
278
|
initStatus = "error"
|
|
283
279
|
warn(error)
|
|
284
|
-
|
|
280
|
+
onFailure?.(error)
|
|
285
281
|
}
|
|
286
282
|
|
|
287
283
|
function onIframeLoad() {
|
|
284
|
+
if (initStatus === "stopped") {
|
|
285
|
+
return
|
|
286
|
+
}
|
|
287
|
+
if (hasObservedIframeLoad) {
|
|
288
|
+
stopHostWithFailure({
|
|
289
|
+
code: "iframe_reloaded",
|
|
290
|
+
message:
|
|
291
|
+
"The iframe reloaded. Create a new host and iframe to reconnect.",
|
|
292
|
+
isRetryable: true,
|
|
293
|
+
})
|
|
294
|
+
return
|
|
295
|
+
}
|
|
296
|
+
hasObservedIframeLoad = true
|
|
288
297
|
clearNoConnectTimeout()
|
|
289
298
|
if (initStatus !== "waitingForConnect") {
|
|
290
299
|
// Only set the timeout if the iframe loaded before the `connect` message was sent.
|
|
@@ -298,7 +307,7 @@ export function createInitializationController(
|
|
|
298
307
|
initStatus = "error"
|
|
299
308
|
const error = initErrorForFailureReason("no_connect")
|
|
300
309
|
warn(error)
|
|
301
|
-
|
|
310
|
+
onFailure?.(error)
|
|
302
311
|
}, noConnectTimeoutMs)
|
|
303
312
|
}
|
|
304
313
|
|
|
@@ -319,12 +328,13 @@ export function createInitializationController(
|
|
|
319
328
|
flushPendingHostStateChanges()
|
|
320
329
|
} else {
|
|
321
330
|
initStatus = "error"
|
|
322
|
-
|
|
331
|
+
onFailure?.(message.error)
|
|
323
332
|
}
|
|
324
333
|
onInitResult?.(message)
|
|
325
334
|
}
|
|
326
335
|
|
|
327
336
|
function stop() {
|
|
337
|
+
initStatus = "stopped"
|
|
328
338
|
clearNoConnectTimeout()
|
|
329
339
|
clearNoInitResultTimeout()
|
|
330
340
|
}
|
package/src/lifecycle/types.ts
CHANGED
|
@@ -13,11 +13,18 @@ export type ConnectSuccessMessage = Extract<
|
|
|
13
13
|
{ status: "success" }
|
|
14
14
|
>
|
|
15
15
|
|
|
16
|
+
/** Tracks initialization and lifetime for one host instance. */
|
|
16
17
|
export type InitStatus =
|
|
18
|
+
/** The host is waiting for the sandbox's first `connect` message. */
|
|
17
19
|
| "waitingForConnect"
|
|
20
|
+
/** The host is waiting for a matching `initResult` after it sends `init`. */
|
|
18
21
|
| "waitingForInitResult"
|
|
22
|
+
/** The host is initialized and can send live updates. */
|
|
19
23
|
| "success"
|
|
24
|
+
/** The host is in an error state after an initialization failure or timeout. */
|
|
20
25
|
| "error"
|
|
26
|
+
/** The host is stopped and no longer sends or accepts sandbox messages. */
|
|
27
|
+
| "stopped"
|
|
21
28
|
|
|
22
29
|
export type CustomBlockHostDataSourcesPayload = {
|
|
23
30
|
bindings: NotionDataSourceBindings
|
|
@@ -90,19 +90,19 @@ export function createSandboxMessageRouter(
|
|
|
90
90
|
|
|
91
91
|
if (response.incomingType === "connect") {
|
|
92
92
|
const parsedIdentity = v.safeParse(initializationIdentitySchema, data)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
93
|
+
const parsedVersion = v.safeParse(bridgeProtocolVersionSchema, data)
|
|
94
|
+
const responseType = onInvalidConnect({
|
|
95
|
+
initializationId: parsedIdentity.success
|
|
96
|
+
? parsedIdentity.output.initializationId
|
|
97
|
+
: undefined,
|
|
98
|
+
error: initErrorForFailureReason("invalid_connect_payload"),
|
|
99
|
+
bridgeProtocolVersion: parsedVersion.success
|
|
100
|
+
? parsedVersion.output.bridgeProtocolVersion
|
|
101
|
+
: undefined,
|
|
102
|
+
})
|
|
103
|
+
if (responseType === "handled") {
|
|
104
|
+
// The controller sent init.error or stopped the host; neither needs a NACK.
|
|
105
|
+
return
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
package/src/messages/types.ts
CHANGED
|
@@ -109,7 +109,7 @@ export type CustomBlockHostHandlers = {
|
|
|
109
109
|
resize?: (message: ResizeMessage) => void
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
export type
|
|
112
|
+
export type CustomBlockHostFailure =
|
|
113
113
|
| CustomBlockInitErrorInfo
|
|
114
114
|
| CustomBlockInitResultErrorInfo
|
|
115
115
|
|