@notionhq/custom-blocks-host 0.1.8 → 0.1.9
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 +25 -12
- package/dist/createCustomBlockHost.d.ts +13 -11
- package/dist/createCustomBlockHost.d.ts.map +1 -1
- package/dist/createCustomBlockHost.js +15 -4
- package/dist/lifecycle/initializationController.d.ts +1 -2
- package/dist/lifecycle/initializationController.d.ts.map +1 -1
- package/dist/lifecycle/initializationController.js +2 -3
- package/dist/lifecycle/types.d.ts +5 -1
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/createCustomBlockHost.ts +28 -17
- package/src/lifecycle/initializationController.ts +3 -6
- package/src/lifecycle/types.ts +5 -1
package/README.md
CHANGED
|
@@ -72,29 +72,42 @@ const host = createCustomBlockHost({
|
|
|
72
72
|
listUsers: async (message) => listUsers(message),
|
|
73
73
|
resize: (message) => resizeIframe(message.height),
|
|
74
74
|
},
|
|
75
|
-
|
|
76
|
-
console.log("
|
|
75
|
+
onManifest: (manifest) => {
|
|
76
|
+
console.log("Selected manifest", manifest);
|
|
77
77
|
},
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
onInitialized: (state) => {
|
|
79
|
+
console.log("Initialized manifest", state.manifest);
|
|
80
80
|
},
|
|
81
81
|
onInitializationFailure: (error) => {
|
|
82
82
|
showInitializationError(error);
|
|
83
83
|
},
|
|
84
|
+
onRestartRequired: () => {
|
|
85
|
+
remountHostAndIframe();
|
|
86
|
+
},
|
|
84
87
|
onLog: (direction, payload) => {
|
|
85
88
|
console.log(direction, payload);
|
|
86
89
|
},
|
|
87
90
|
});
|
|
88
91
|
```
|
|
89
92
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
Each host instance initializes once:
|
|
94
|
+
|
|
95
|
+
- `onManifest` provides the selected manifest as soon as it is available after
|
|
96
|
+
`connect`, before binding validation and `onInitialized`. Use it to configure
|
|
97
|
+
bindings even when initialization cannot yet succeed.
|
|
98
|
+
|
|
99
|
+
- `onInitialized` reports success after the sandbox accepts initialization. It
|
|
100
|
+
provides the selected `manifest` and the sandbox's optional `initialHeight`.
|
|
101
|
+
- `onInitializationFailure` reports an initialization error or timeout.
|
|
102
|
+
- `onRestartRequired` reports a connection attempt after the host accepts its
|
|
103
|
+
first connection or stops waiting. Recreate the host and iframe together
|
|
104
|
+
to accept a new connection.
|
|
105
|
+
This can occur after the iframe document changes or a connection arrives
|
|
106
|
+
after a timeout.
|
|
95
107
|
|
|
96
|
-
|
|
97
|
-
|
|
108
|
+
The host uses the manifest from `connect`, or `initialState.manifest` if
|
|
109
|
+
`connect` omits it. If neither exists, initialization fails with
|
|
110
|
+
`manifest_unavailable`.
|
|
98
111
|
|
|
99
112
|
`queryDataSource` is required. Other request handlers are optional. Every
|
|
100
113
|
request handler may return its result synchronously or as a promise. Results
|
|
@@ -157,7 +170,7 @@ failure. It logs and ignores a valid `initResult` with a stale
|
|
|
157
170
|
`initializationId` or an unexpected state.
|
|
158
171
|
|
|
159
172
|
Hosts do not retry malformed messages. A host that receives a `connect` after
|
|
160
|
-
initialization has settled must use `
|
|
173
|
+
initialization has settled must use `onRestartRequired` to recreate the host
|
|
161
174
|
and iframe together.
|
|
162
175
|
|
|
163
176
|
## Updating live state
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { type NotionContrastMode } from "./protocol/contrast.js";
|
|
2
2
|
import type { NotionDataSourceId } from "./protocol/ids.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { CustomBlockManifest } from "./protocol/manifest.js";
|
|
4
4
|
import type { HostToSandboxMessage } from "./protocol/messages/hostToSandbox.js";
|
|
5
|
-
import type { InitResultMessage } from "./protocol/messages/initResult.js";
|
|
6
5
|
import type { CustomBlockPage } from "./protocol/pages/page.js";
|
|
7
6
|
import type { NotionParent } from "./protocol/parent.js";
|
|
8
7
|
import type { NotionTheme } from "./protocol/theme.js";
|
|
9
8
|
import type { NotionUser } from "./protocol/users/user.js";
|
|
10
|
-
import type {
|
|
9
|
+
import type { CustomBlockHostDataSourcesPayload, CustomBlockHostInitialState } from "./lifecycle/types.js";
|
|
11
10
|
import type { CustomBlockHostHandlers, CustomBlockHostInitializationFailure, CustomBlockHostLogDirection } from "./messages/types.js";
|
|
12
11
|
export type CustomBlockHostOptions = {
|
|
13
12
|
iframe: HTMLIFrameElement;
|
|
@@ -17,17 +16,20 @@ export type CustomBlockHostOptions = {
|
|
|
17
16
|
noInitResultTimeoutMs?: number;
|
|
18
17
|
initialState: CustomBlockHostInitialState;
|
|
19
18
|
handlers: CustomBlockHostHandlers;
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
/** Called as soon as the `manifest` is available after `connect`, before `onInitialized` and binding validation. */
|
|
20
|
+
onManifest?: (manifest: CustomBlockManifest) => void;
|
|
21
|
+
/** Callback fired when the initialization handshake with the sandbox completed successfully. */
|
|
22
|
+
onInitialized?: (state: {
|
|
23
|
+
manifest: CustomBlockManifest;
|
|
24
|
+
initialHeight?: number;
|
|
25
|
+
}) => void;
|
|
26
|
+
/** Callback fired when the initialization handshake with the sandbox failed or timed out. */
|
|
22
27
|
onInitializationFailure?: (error: CustomBlockHostInitializationFailure) => void;
|
|
23
28
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* stopped waiting. Either way the host cannot serve it: the embedder
|
|
27
|
-
* should recreate host and iframe together so the document can run a
|
|
28
|
-
* fresh handshake.
|
|
29
|
+
* Callback fired when the current host and iframe must restart.
|
|
30
|
+
* Recreate both to continue.
|
|
29
31
|
*/
|
|
30
|
-
|
|
32
|
+
onRestartRequired?: () => void;
|
|
31
33
|
onLog?: (direction: CustomBlockHostLogDirection, payload: unknown) => void;
|
|
32
34
|
};
|
|
33
35
|
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;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;
|
|
1
|
+
{"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../src/createCustomBlockHost.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,kBAAkB,EACvB,MAAM,8CAA8C,CAAA;AACrD,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;AActG,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,oCAAoC,EACpC,2BAA2B,EAC3B,MAAM,qBAAqB,CAAA;AAU5B,MAAM,MAAM,sBAAsB,GAAG;IACpC,MAAM,EAAE,iBAAiB,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,wBAAwB,EAAE,MAAM,CAAA;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,YAAY,EAAE,2BAA2B,CAAA;IACzC,QAAQ,EAAE,uBAAuB,CAAA;IACjC,oHAAoH;IACpH,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,6FAA6F;IAC7F,uBAAuB,CAAC,EAAE,CACzB,KAAK,EAAE,oCAAoC,KACvC,IAAI,CAAA;IACT;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC9B,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;AAMD,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,sBAAsB,GAC7B,qBAAqB,CA+dvB"}
|
|
@@ -13,7 +13,7 @@ const initializationIdentitySchema = v.object({
|
|
|
13
13
|
initializationId: v.string(),
|
|
14
14
|
});
|
|
15
15
|
export function createCustomBlockHost(options) {
|
|
16
|
-
const { iframe, initialState, handlers,
|
|
16
|
+
const { iframe, initialState, handlers, onManifest, onInitialized, onInitializationFailure, onRestartRequired, onLog, sandboxOrigin, minBridgeProtocolVersion: configuredMinBridgeProtocolVersion, noConnectTimeoutMs, noInitResultTimeoutMs, } = options;
|
|
17
17
|
const minBridgeProtocolVersion = Math.max(configuredMinBridgeProtocolVersion ?? CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION, CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION);
|
|
18
18
|
let theme = initialState.theme;
|
|
19
19
|
let contrastMode = initialState.contrastMode ?? DEFAULT_CONTRAST_MODE;
|
|
@@ -188,6 +188,13 @@ export function createCustomBlockHost(options) {
|
|
|
188
188
|
}),
|
|
189
189
|
prepareInit(connect) {
|
|
190
190
|
manifest = connect.manifest ?? initialState.manifest;
|
|
191
|
+
if (manifest === undefined) {
|
|
192
|
+
return {
|
|
193
|
+
status: "error",
|
|
194
|
+
error: initErrorForFailureReason("manifest_unavailable"),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
onManifest?.(manifest);
|
|
191
198
|
const resolvedDataSources = typeof dataSources === "function" ? dataSources(connect) : dataSources;
|
|
192
199
|
const bindingErrorCode = getBindingErrorCode(manifest, resolvedDataSources);
|
|
193
200
|
if (bindingErrorCode !== undefined) {
|
|
@@ -209,9 +216,12 @@ export function createCustomBlockHost(options) {
|
|
|
209
216
|
minBridgeProtocolVersion,
|
|
210
217
|
noConnectTimeoutMs,
|
|
211
218
|
noInitResultTimeoutMs,
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
219
|
+
onInitResult(message) {
|
|
220
|
+
if (message.status === "success" && manifest !== undefined) {
|
|
221
|
+
onInitialized?.({ manifest, initialHeight: message.initialHeight });
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
onRestartRequired,
|
|
215
225
|
onInitializationFailure,
|
|
216
226
|
});
|
|
217
227
|
function onInvalidSandboxMessage(data, issues) {
|
|
@@ -368,6 +378,7 @@ export function createCustomBlockHost(options) {
|
|
|
368
378
|
},
|
|
369
379
|
setDataSources(nextDataSources) {
|
|
370
380
|
if (initializationController.hasConnectedSandbox &&
|
|
381
|
+
manifest !== undefined &&
|
|
371
382
|
getBindingErrorCode(manifest, nextDataSources) !== undefined) {
|
|
372
383
|
emit("warn", {
|
|
373
384
|
message: "Ignored invalid data source bindings update",
|
|
@@ -36,9 +36,8 @@ export type InitializationControllerOptions = {
|
|
|
36
36
|
minBridgeProtocolVersion: number;
|
|
37
37
|
noConnectTimeoutMs?: number;
|
|
38
38
|
noInitResultTimeoutMs?: number;
|
|
39
|
-
onConnect?: (message: ConnectSuccessMessage) => void;
|
|
40
39
|
onInitResult?: (message: InitResultMessage) => void;
|
|
41
|
-
|
|
40
|
+
onRestartRequired?: () => void;
|
|
42
41
|
onInitializationFailure?: (error: CustomBlockHostInitializationFailure) => void;
|
|
43
42
|
};
|
|
44
43
|
export type InitializationController = {
|
|
@@ -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,oCAAoC,EAAE,MAAM,sBAAsB,CAAA;AAGhF,OAAO,KAAK,EACX,qBAAqB,EACrB,iCAAiC,EACjC,UAAU,EACV,MAAM,YAAY,CAAA;AAKnB,KAAK,mBAAmB,GAAG;IAC1B,KAAK,EAAE,WAAW,CAAA;IAClB,YAAY,EAAE,kBAAkB,CAAA;IAChC,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,eAAe,CAAA;IACrB,WAAW,EAAE,UAAU,CAAA;CACvB,CAAA;AAED,KAAK,yBAAyB,GAC3B;IACA,MAAM,EAAE,SAAS,CAAA;IACjB,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,WAAW,EAAE,iCAAiC,CAAA;CAC7C,GACD;IACA,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,wBAAwB,CAAA;CAC9B,CAAA;AAEJ,MAAM,MAAM,+BAA+B,GAAG;IAC7C,OAAO,EAAE,aAAa,CAAA;IACtB,QAAQ,EAAE,MAAM,mBAAmB,CAAA;IACnC,WAAW,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,yBAAyB,CAAA;IAC1E,IAAI,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAC7C,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAChC,4BAA4B,EAAE,MAAM,IAAI,CAAA;IACxC,wBAAwB,EAAE,MAAM,CAAA;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,
|
|
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,oCAAoC,EAAE,MAAM,sBAAsB,CAAA;AAGhF,OAAO,KAAK,EACX,qBAAqB,EACrB,iCAAiC,EACjC,UAAU,EACV,MAAM,YAAY,CAAA;AAKnB,KAAK,mBAAmB,GAAG;IAC1B,KAAK,EAAE,WAAW,CAAA;IAClB,YAAY,EAAE,kBAAkB,CAAA;IAChC,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,eAAe,CAAA;IACrB,WAAW,EAAE,UAAU,CAAA;CACvB,CAAA;AAED,KAAK,yBAAyB,GAC3B;IACA,MAAM,EAAE,SAAS,CAAA;IACjB,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,WAAW,EAAE,iCAAiC,CAAA;CAC7C,GACD;IACA,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,wBAAwB,CAAA;CAC9B,CAAA;AAEJ,MAAM,MAAM,+BAA+B,GAAG;IAC7C,OAAO,EAAE,aAAa,CAAA;IACtB,QAAQ,EAAE,MAAM,mBAAmB,CAAA;IACnC,WAAW,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,yBAAyB,CAAA;IAC1E,IAAI,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAC7C,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAChC,4BAA4B,EAAE,MAAM,IAAI,CAAA;IACxC,wBAAwB,EAAE,MAAM,CAAA;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACnD,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC9B,uBAAuB,CAAC,EAAE,CACzB,KAAK,EAAE,oCAAoC,KACvC,IAAI,CAAA;CACT,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG;IACtC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;IAC3B,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAA;IACrC,aAAa,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;IAChD,gBAAgB,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACtD,oBAAoB,EAAE,CAAC,gBAAgB,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;IAC5D,uBAAuB,EAAE,MAAM,OAAO,CAAA;IACtC,YAAY,EAAE,MAAM,IAAI,CAAA;IACxB,IAAI,EAAE,MAAM,IAAI,CAAA;CAChB,CAAA;AAED,wBAAgB,8BAA8B,CAC7C,OAAO,EAAE,+BAA+B,GACtC,wBAAwB,CAuO1B"}
|
|
@@ -3,7 +3,7 @@ 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, onInitResult, onRestartRequired, onInitializationFailure, } = options;
|
|
7
7
|
let initStatus = "waitingForConnect";
|
|
8
8
|
let noConnectTimeoutId;
|
|
9
9
|
let noInitResultTimeoutId;
|
|
@@ -40,7 +40,6 @@ export function createInitializationController(options) {
|
|
|
40
40
|
if (initStatus !== "waitingForConnect") {
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
|
-
onConnect?.(connect);
|
|
44
43
|
const preparation = prepareInit(connect);
|
|
45
44
|
if (preparation.status === "error") {
|
|
46
45
|
sendInitError(connect.initializationId, preparation.error);
|
|
@@ -89,7 +88,7 @@ export function createInitializationController(options) {
|
|
|
89
88
|
message: "Ignored connect after initialization settled; recreate the host and iframe to re-initialize",
|
|
90
89
|
initializationId: message.initializationId,
|
|
91
90
|
});
|
|
92
|
-
|
|
91
|
+
onRestartRequired?.();
|
|
93
92
|
return;
|
|
94
93
|
}
|
|
95
94
|
// Verify the bridge protocol version is valid.
|
|
@@ -21,7 +21,11 @@ export type CustomBlockHostInitialState = {
|
|
|
21
21
|
blockId: NotionBlockId;
|
|
22
22
|
parent: NotionParent;
|
|
23
23
|
page: CustomBlockPage;
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* The host's persisted manifest, when one exists. If omitted, the host
|
|
26
|
+
* requires the sandbox to provide a manifest in `connect`.
|
|
27
|
+
*/
|
|
28
|
+
manifest?: CustomBlockManifest;
|
|
25
29
|
dataSources: CustomBlockHostInitialDataSources;
|
|
26
30
|
currentUser: NotionUser;
|
|
27
31
|
};
|
|
@@ -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,GACnB,mBAAmB,GACnB,sBAAsB,GACtB,SAAS,GACT,OAAO,CAAA;AAEV,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;IAElB,YAAY,CAAC,EAAE,kBAAkB,CAAA;IACjC,OAAO,EAAE,aAAa,CAAA;IACtB,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,eAAe,CAAA;IACrB,QAAQ,EAAE,mBAAmB,CAAA;
|
|
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,GACnB,mBAAmB,GACnB,sBAAsB,GACtB,SAAS,GACT,OAAO,CAAA;AAEV,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;IAElB,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"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
} from "@notionhq/custom-blocks-protocol/contrast.js"
|
|
5
5
|
import type { NotionDataSourceId } from "@notionhq/custom-blocks-protocol/ids.js"
|
|
6
6
|
import type { CustomBlockManifest } from "@notionhq/custom-blocks-protocol/manifest.js"
|
|
7
|
-
import type { ConnectMessage } from "@notionhq/custom-blocks-protocol/messages/connect.js"
|
|
8
7
|
import type { ContrastModeChangedMessage } from "@notionhq/custom-blocks-protocol/messages/contrastModeChanged.js"
|
|
9
8
|
import type { CreatePageMessage } from "@notionhq/custom-blocks-protocol/messages/createPage.js"
|
|
10
9
|
import type { CreatePageResultMessage } from "@notionhq/custom-blocks-protocol/messages/createPageResult.js"
|
|
@@ -19,7 +18,6 @@ import type {
|
|
|
19
18
|
} from "@notionhq/custom-blocks-protocol/messages/getUser.js"
|
|
20
19
|
import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
|
|
21
20
|
import type { CustomBlockInitErrorCode } from "@notionhq/custom-blocks-protocol/messages/init.js"
|
|
22
|
-
import type { InitResultMessage } from "@notionhq/custom-blocks-protocol/messages/initResult.js"
|
|
23
21
|
import type {
|
|
24
22
|
ListUsersMessage,
|
|
25
23
|
ListUsersResultMessage,
|
|
@@ -41,7 +39,6 @@ import * as v from "valibot"
|
|
|
41
39
|
import { initErrorForFailureReason } from "./lifecycle/initErrors.js"
|
|
42
40
|
import { createInitializationController } from "./lifecycle/initializationController.js"
|
|
43
41
|
import type {
|
|
44
|
-
ConnectSuccessMessage,
|
|
45
42
|
CustomBlockHostDataSourcesPayload,
|
|
46
43
|
CustomBlockHostInitialState,
|
|
47
44
|
} from "./lifecycle/types.js"
|
|
@@ -68,19 +65,22 @@ export type CustomBlockHostOptions = {
|
|
|
68
65
|
noInitResultTimeoutMs?: number
|
|
69
66
|
initialState: CustomBlockHostInitialState
|
|
70
67
|
handlers: CustomBlockHostHandlers
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
/** Called as soon as the `manifest` is available after `connect`, before `onInitialized` and binding validation. */
|
|
69
|
+
onManifest?: (manifest: CustomBlockManifest) => void
|
|
70
|
+
/** Callback fired when the initialization handshake with the sandbox completed successfully. */
|
|
71
|
+
onInitialized?: (state: {
|
|
72
|
+
manifest: CustomBlockManifest
|
|
73
|
+
initialHeight?: number
|
|
74
|
+
}) => void
|
|
75
|
+
/** Callback fired when the initialization handshake with the sandbox failed or timed out. */
|
|
73
76
|
onInitializationFailure?: (
|
|
74
77
|
error: CustomBlockHostInitializationFailure,
|
|
75
78
|
) => void
|
|
76
79
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* stopped waiting. Either way the host cannot serve it: the embedder
|
|
80
|
-
* should recreate host and iframe together so the document can run a
|
|
81
|
-
* fresh handshake.
|
|
80
|
+
* Callback fired when the current host and iframe must restart.
|
|
81
|
+
* Recreate both to continue.
|
|
82
82
|
*/
|
|
83
|
-
|
|
83
|
+
onRestartRequired?: () => void
|
|
84
84
|
onLog?: (direction: CustomBlockHostLogDirection, payload: unknown) => void
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -107,10 +107,10 @@ export function createCustomBlockHost(
|
|
|
107
107
|
iframe,
|
|
108
108
|
initialState,
|
|
109
109
|
handlers,
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
onManifest,
|
|
111
|
+
onInitialized,
|
|
112
112
|
onInitializationFailure,
|
|
113
|
-
|
|
113
|
+
onRestartRequired,
|
|
114
114
|
onLog,
|
|
115
115
|
sandboxOrigin,
|
|
116
116
|
minBridgeProtocolVersion: configuredMinBridgeProtocolVersion,
|
|
@@ -322,6 +322,13 @@ export function createCustomBlockHost(
|
|
|
322
322
|
}),
|
|
323
323
|
prepareInit(connect) {
|
|
324
324
|
manifest = connect.manifest ?? initialState.manifest
|
|
325
|
+
if (manifest === undefined) {
|
|
326
|
+
return {
|
|
327
|
+
status: "error",
|
|
328
|
+
error: initErrorForFailureReason("manifest_unavailable"),
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
onManifest?.(manifest)
|
|
325
332
|
const resolvedDataSources =
|
|
326
333
|
typeof dataSources === "function" ? dataSources(connect) : dataSources
|
|
327
334
|
const bindingErrorCode = getBindingErrorCode(
|
|
@@ -347,9 +354,12 @@ export function createCustomBlockHost(
|
|
|
347
354
|
minBridgeProtocolVersion,
|
|
348
355
|
noConnectTimeoutMs,
|
|
349
356
|
noInitResultTimeoutMs,
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
357
|
+
onInitResult(message) {
|
|
358
|
+
if (message.status === "success" && manifest !== undefined) {
|
|
359
|
+
onInitialized?.({ manifest, initialHeight: message.initialHeight })
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
onRestartRequired,
|
|
353
363
|
onInitializationFailure,
|
|
354
364
|
})
|
|
355
365
|
|
|
@@ -524,6 +534,7 @@ export function createCustomBlockHost(
|
|
|
524
534
|
setDataSources(nextDataSources) {
|
|
525
535
|
if (
|
|
526
536
|
initializationController.hasConnectedSandbox &&
|
|
537
|
+
manifest !== undefined &&
|
|
527
538
|
getBindingErrorCode(manifest, nextDataSources) !== undefined
|
|
528
539
|
) {
|
|
529
540
|
emit("warn", {
|
|
@@ -53,9 +53,8 @@ export type InitializationControllerOptions = {
|
|
|
53
53
|
minBridgeProtocolVersion: number
|
|
54
54
|
noConnectTimeoutMs?: number
|
|
55
55
|
noInitResultTimeoutMs?: number
|
|
56
|
-
onConnect?: (message: ConnectSuccessMessage) => void
|
|
57
56
|
onInitResult?: (message: InitResultMessage) => void
|
|
58
|
-
|
|
57
|
+
onRestartRequired?: () => void
|
|
59
58
|
onInitializationFailure?: (
|
|
60
59
|
error: CustomBlockHostInitializationFailure,
|
|
61
60
|
) => void
|
|
@@ -85,9 +84,8 @@ export function createInitializationController(
|
|
|
85
84
|
minBridgeProtocolVersion,
|
|
86
85
|
noConnectTimeoutMs = DEFAULT_NO_CONNECT_TIMEOUT_MS,
|
|
87
86
|
noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS,
|
|
88
|
-
onConnect,
|
|
89
87
|
onInitResult,
|
|
90
|
-
|
|
88
|
+
onRestartRequired,
|
|
91
89
|
onInitializationFailure,
|
|
92
90
|
} = options
|
|
93
91
|
|
|
@@ -134,7 +132,6 @@ export function createInitializationController(
|
|
|
134
132
|
if (initStatus !== "waitingForConnect") {
|
|
135
133
|
return false
|
|
136
134
|
}
|
|
137
|
-
onConnect?.(connect)
|
|
138
135
|
const preparation = prepareInit(connect)
|
|
139
136
|
if (preparation.status === "error") {
|
|
140
137
|
sendInitError(connect.initializationId, preparation.error)
|
|
@@ -185,7 +182,7 @@ export function createInitializationController(
|
|
|
185
182
|
"Ignored connect after initialization settled; recreate the host and iframe to re-initialize",
|
|
186
183
|
initializationId: message.initializationId,
|
|
187
184
|
})
|
|
188
|
-
|
|
185
|
+
onRestartRequired?.()
|
|
189
186
|
return
|
|
190
187
|
}
|
|
191
188
|
|
package/src/lifecycle/types.ts
CHANGED
|
@@ -34,7 +34,11 @@ export type CustomBlockHostInitialState = {
|
|
|
34
34
|
blockId: NotionBlockId
|
|
35
35
|
parent: NotionParent
|
|
36
36
|
page: CustomBlockPage
|
|
37
|
-
|
|
37
|
+
/**
|
|
38
|
+
* The host's persisted manifest, when one exists. If omitted, the host
|
|
39
|
+
* requires the sandbox to provide a manifest in `connect`.
|
|
40
|
+
*/
|
|
41
|
+
manifest?: CustomBlockManifest
|
|
38
42
|
dataSources: CustomBlockHostInitialDataSources
|
|
39
43
|
currentUser: NotionUser
|
|
40
44
|
}
|