@notionhq/custom-blocks-host 0.1.7 → 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 +30 -12
- package/dist/createCustomBlockHost.d.ts +13 -11
- package/dist/createCustomBlockHost.d.ts.map +1 -1
- package/dist/createCustomBlockHost.js +20 -6
- 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/dist/protocol/index.d.ts +1 -0
- package/dist/protocol/index.js +1 -0
- package/dist/protocol/messages/sandboxToHost.d.ts +3 -0
- package/dist/protocol/messages/sandboxToHost.js +2 -0
- package/dist/protocol/messages/unsubscribeDataSourceQuery.d.ts +10 -0
- package/dist/protocol/messages/unsubscribeDataSourceQuery.js +9 -0
- package/dist/queries/querySubscriptions.d.ts +3 -2
- package/dist/queries/querySubscriptions.d.ts.map +1 -1
- package/dist/queries/querySubscriptions.js +5 -2
- package/package.json +1 -1
- package/src/createCustomBlockHost.ts +33 -19
- package/src/lifecycle/initializationController.ts +3 -6
- package/src/lifecycle/types.ts +5 -1
- package/src/queries/querySubscriptions.ts +6 -2
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
|
-
`connect` arrives that the settled host cannot serve: the iframe document was
|
|
92
|
-
replaced, or the document's first `connect` came after the no-connect timeout.
|
|
93
|
-
Recreate the host and the iframe together so the document runs a fresh
|
|
94
|
-
handshake against current host state.
|
|
93
|
+
Each host instance initializes once:
|
|
95
94
|
|
|
96
|
-
`
|
|
97
|
-
|
|
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.
|
|
107
|
+
|
|
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
|
|
@@ -179,6 +192,11 @@ held until initialization completes. `refreshQuery()` re-runs
|
|
|
179
192
|
`queryDataSource` for every active subscription with the supplied data source
|
|
180
193
|
ID, preserving each subscription's filter, sorts, and limit.
|
|
181
194
|
|
|
195
|
+
The runtime removes a subscription when it receives
|
|
196
|
+
`unsubscribeDataSourceQuery`. Repeated unsubscribe messages are safe. The
|
|
197
|
+
runtime also ignores results from a query that was unsubscribed while its
|
|
198
|
+
handler was running.
|
|
199
|
+
|
|
182
200
|
Call `stop()` when the iframe is removed. It detaches listeners, clears
|
|
183
201
|
timeouts, and discards active query subscriptions.
|
|
184
202
|
|
|
@@ -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;
|
|
@@ -54,7 +54,7 @@ export function createCustomBlockHost(options) {
|
|
|
54
54
|
};
|
|
55
55
|
post(message);
|
|
56
56
|
}
|
|
57
|
-
async function handleQuery(message, token = querySubscriptions.
|
|
57
|
+
async function handleQuery(message, token = querySubscriptions.subscribe(message)) {
|
|
58
58
|
const collectionSchema = getCollectionSchema(activeDataSources, message.dataSourceId);
|
|
59
59
|
const filterError = validateQueryDataSourceFilter(message.filter, collectionSchema);
|
|
60
60
|
if (filterError !== undefined) {
|
|
@@ -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) {
|
|
@@ -282,6 +292,9 @@ export function createCustomBlockHost(options) {
|
|
|
282
292
|
case "queryDataSource":
|
|
283
293
|
void handleQuery(message);
|
|
284
294
|
return;
|
|
295
|
+
case "unsubscribeDataSourceQuery":
|
|
296
|
+
querySubscriptions.unsubscribe(message.subscriptionId);
|
|
297
|
+
return;
|
|
285
298
|
case "createPage":
|
|
286
299
|
void handleCreatePage(message);
|
|
287
300
|
return;
|
|
@@ -313,7 +326,7 @@ export function createCustomBlockHost(options) {
|
|
|
313
326
|
window.removeEventListener("message", onMessage);
|
|
314
327
|
iframe.removeEventListener("load", initializationController.onIframeLoad);
|
|
315
328
|
initializationController.stop();
|
|
316
|
-
querySubscriptions.
|
|
329
|
+
querySubscriptions.unsubscribeAll();
|
|
317
330
|
},
|
|
318
331
|
post,
|
|
319
332
|
setTheme(nextTheme) {
|
|
@@ -365,6 +378,7 @@ export function createCustomBlockHost(options) {
|
|
|
365
378
|
},
|
|
366
379
|
setDataSources(nextDataSources) {
|
|
367
380
|
if (initializationController.hasConnectedSandbox &&
|
|
381
|
+
manifest !== undefined &&
|
|
368
382
|
getBindingErrorCode(manifest, nextDataSources) !== undefined) {
|
|
369
383
|
emit("warn", {
|
|
370
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/dist/protocol/index.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ export * from "./messages/queryDataSourceResult.js";
|
|
|
39
39
|
export * from "./messages/resize.js";
|
|
40
40
|
export * from "./messages/sandboxToHost.js";
|
|
41
41
|
export * from "./messages/themeChanged.js";
|
|
42
|
+
export * from "./messages/unsubscribeDataSourceQuery.js";
|
|
42
43
|
export * from "./messages/updatePage.js";
|
|
43
44
|
export * from "./messages/updatePageResult.js";
|
|
44
45
|
export * from "./pages/page.js";
|
package/dist/protocol/index.js
CHANGED
|
@@ -39,6 +39,7 @@ export * from "./messages/queryDataSourceResult.js";
|
|
|
39
39
|
export * from "./messages/resize.js";
|
|
40
40
|
export * from "./messages/sandboxToHost.js";
|
|
41
41
|
export * from "./messages/themeChanged.js";
|
|
42
|
+
export * from "./messages/unsubscribeDataSourceQuery.js";
|
|
42
43
|
export * from "./messages/updatePage.js";
|
|
43
44
|
export * from "./messages/updatePageResult.js";
|
|
44
45
|
export * from "./pages/page.js";
|
|
@@ -411,6 +411,9 @@ export declare const sandboxToHostMessageSchema: v.VariantSchema<"type", [v.Vari
|
|
|
411
411
|
readonly propertyId: v.StringSchema<undefined>;
|
|
412
412
|
readonly direction: v.PicklistSchema<["ascending", "descending"], undefined>;
|
|
413
413
|
}, undefined>, undefined>, undefined>;
|
|
414
|
+
}, undefined>, v.ObjectSchema<{
|
|
415
|
+
readonly type: v.LiteralSchema<"unsubscribeDataSourceQuery", undefined>;
|
|
416
|
+
readonly subscriptionId: v.StringSchema<undefined>;
|
|
414
417
|
}, undefined>, v.ObjectSchema<{
|
|
415
418
|
readonly type: v.LiteralSchema<"createPage", undefined>;
|
|
416
419
|
readonly requestId: v.StringSchema<undefined>;
|
|
@@ -8,6 +8,7 @@ import { invalidHostMessageSchema } from "./invalidHostMessage.js";
|
|
|
8
8
|
import { listUsersMessageSchema } from "./listUsers.js";
|
|
9
9
|
import { queryDataSourceMessageSchema } from "./queryDataSource.js";
|
|
10
10
|
import { resizeMessageSchema } from "./resize.js";
|
|
11
|
+
import { unsubscribeDataSourceQueryMessageSchema } from "./unsubscribeDataSourceQuery.js";
|
|
11
12
|
import { updatePageMessageSchema } from "./updatePage.js";
|
|
12
13
|
/**
|
|
13
14
|
* Discriminated union of every message the sandbox is allowed to send the host. Useful for hosts
|
|
@@ -17,6 +18,7 @@ export const sandboxToHostMessageSchema = v.variant("type", [
|
|
|
17
18
|
connectMessageSchema,
|
|
18
19
|
initResultMessageSchema,
|
|
19
20
|
queryDataSourceMessageSchema,
|
|
21
|
+
unsubscribeDataSourceQueryMessageSchema,
|
|
20
22
|
createPageMessageSchema,
|
|
21
23
|
getPageMessageSchema,
|
|
22
24
|
getUserMessageSchema,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as v from "valibot";
|
|
2
|
+
/**
|
|
3
|
+
* Tells the host to stop an active data source query subscription.
|
|
4
|
+
*/
|
|
5
|
+
export declare const unsubscribeDataSourceQueryMessageSchema: v.ObjectSchema<{
|
|
6
|
+
readonly type: v.LiteralSchema<"unsubscribeDataSourceQuery", undefined>;
|
|
7
|
+
/** The subscription to remove from the host. */
|
|
8
|
+
readonly subscriptionId: v.StringSchema<undefined>;
|
|
9
|
+
}, undefined>;
|
|
10
|
+
export type UnsubscribeDataSourceQueryMessage = v.InferOutput<typeof unsubscribeDataSourceQueryMessageSchema>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as v from "valibot";
|
|
2
|
+
/**
|
|
3
|
+
* Tells the host to stop an active data source query subscription.
|
|
4
|
+
*/
|
|
5
|
+
export const unsubscribeDataSourceQueryMessageSchema = v.object({
|
|
6
|
+
type: v.literal("unsubscribeDataSourceQuery"),
|
|
7
|
+
/** The subscription to remove from the host. */
|
|
8
|
+
subscriptionId: v.string(),
|
|
9
|
+
});
|
|
@@ -9,9 +9,10 @@ export type QuerySubscriptionToken = {
|
|
|
9
9
|
};
|
|
10
10
|
export declare class QuerySubscriptions {
|
|
11
11
|
private readonly bySubscriptionId;
|
|
12
|
-
|
|
12
|
+
subscribe(message: QueryDataSourceMessage): QuerySubscriptionToken;
|
|
13
13
|
isCurrent(subscriptionId: string, token: QuerySubscriptionToken): boolean;
|
|
14
|
+
unsubscribe(subscriptionId: string): void;
|
|
14
15
|
refresh(dataSourceId: NotionDataSourceId): QueryRefresh[];
|
|
15
|
-
|
|
16
|
+
unsubscribeAll(): void;
|
|
16
17
|
}
|
|
17
18
|
//# sourceMappingURL=querySubscriptions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"querySubscriptions.d.ts","sourceRoot":"","sources":["../../src/queries/querySubscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AACjF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8DAA8D,CAAA;AAE1G,MAAM,MAAM,YAAY,GAAG;IAC1B,OAAO,EAAE,sBAAsB,CAAA;IAC/B,KAAK,EAAE,sBAAsB,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACpC,QAAQ,CAAC,IAAI,EAAE,wBAAwB,CAAA;CACvC,CAAA;AAOD,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA4C;IAE7E,
|
|
1
|
+
{"version":3,"file":"querySubscriptions.d.ts","sourceRoot":"","sources":["../../src/queries/querySubscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AACjF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8DAA8D,CAAA;AAE1G,MAAM,MAAM,YAAY,GAAG;IAC1B,OAAO,EAAE,sBAAsB,CAAA;IAC/B,KAAK,EAAE,sBAAsB,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACpC,QAAQ,CAAC,IAAI,EAAE,wBAAwB,CAAA;CACvC,CAAA;AAOD,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA4C;IAE7E,SAAS,CAAC,OAAO,EAAE,sBAAsB,GAAG,sBAAsB,CASjE;IAED,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAExE;IAED,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAExC;IAED,OAAO,CAAC,YAAY,EAAE,kBAAkB,GAAG,YAAY,EAAE,CAgBxD;IAED,cAAc,SAEb;CACD"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export class QuerySubscriptions {
|
|
2
2
|
bySubscriptionId = new Map();
|
|
3
|
-
|
|
3
|
+
subscribe(message) {
|
|
4
4
|
const token = {
|
|
5
5
|
_tag: "QuerySubscriptionToken",
|
|
6
6
|
};
|
|
@@ -13,6 +13,9 @@ export class QuerySubscriptions {
|
|
|
13
13
|
isCurrent(subscriptionId, token) {
|
|
14
14
|
return this.bySubscriptionId.get(subscriptionId)?.token === token;
|
|
15
15
|
}
|
|
16
|
+
unsubscribe(subscriptionId) {
|
|
17
|
+
this.bySubscriptionId.delete(subscriptionId);
|
|
18
|
+
}
|
|
16
19
|
refresh(dataSourceId) {
|
|
17
20
|
const refreshes = [];
|
|
18
21
|
for (const queryState of this.bySubscriptionId.values()) {
|
|
@@ -30,7 +33,7 @@ export class QuerySubscriptions {
|
|
|
30
33
|
}
|
|
31
34
|
return refreshes;
|
|
32
35
|
}
|
|
33
|
-
|
|
36
|
+
unsubscribeAll() {
|
|
34
37
|
this.bySubscriptionId.clear();
|
|
35
38
|
}
|
|
36
39
|
}
|
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,
|
|
@@ -171,7 +171,7 @@ export function createCustomBlockHost(
|
|
|
171
171
|
|
|
172
172
|
async function handleQuery(
|
|
173
173
|
message: QueryDataSourceMessage,
|
|
174
|
-
token: QuerySubscriptionToken = querySubscriptions.
|
|
174
|
+
token: QuerySubscriptionToken = querySubscriptions.subscribe(message),
|
|
175
175
|
) {
|
|
176
176
|
const collectionSchema = getCollectionSchema(
|
|
177
177
|
activeDataSources,
|
|
@@ -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
|
|
|
@@ -435,6 +445,9 @@ export function createCustomBlockHost(
|
|
|
435
445
|
case "queryDataSource":
|
|
436
446
|
void handleQuery(message)
|
|
437
447
|
return
|
|
448
|
+
case "unsubscribeDataSourceQuery":
|
|
449
|
+
querySubscriptions.unsubscribe(message.subscriptionId)
|
|
450
|
+
return
|
|
438
451
|
case "createPage":
|
|
439
452
|
void handleCreatePage(message)
|
|
440
453
|
return
|
|
@@ -468,7 +481,7 @@ export function createCustomBlockHost(
|
|
|
468
481
|
window.removeEventListener("message", onMessage)
|
|
469
482
|
iframe.removeEventListener("load", initializationController.onIframeLoad)
|
|
470
483
|
initializationController.stop()
|
|
471
|
-
querySubscriptions.
|
|
484
|
+
querySubscriptions.unsubscribeAll()
|
|
472
485
|
},
|
|
473
486
|
post,
|
|
474
487
|
setTheme(nextTheme) {
|
|
@@ -521,6 +534,7 @@ export function createCustomBlockHost(
|
|
|
521
534
|
setDataSources(nextDataSources) {
|
|
522
535
|
if (
|
|
523
536
|
initializationController.hasConnectedSandbox &&
|
|
537
|
+
manifest !== undefined &&
|
|
524
538
|
getBindingErrorCode(manifest, nextDataSources) !== undefined
|
|
525
539
|
) {
|
|
526
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
|
}
|
|
@@ -18,7 +18,7 @@ type QuerySubscriptionState = {
|
|
|
18
18
|
export class QuerySubscriptions {
|
|
19
19
|
private readonly bySubscriptionId = new Map<string, QuerySubscriptionState>()
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
subscribe(message: QueryDataSourceMessage): QuerySubscriptionToken {
|
|
22
22
|
const token: QuerySubscriptionToken = {
|
|
23
23
|
_tag: "QuerySubscriptionToken",
|
|
24
24
|
}
|
|
@@ -33,6 +33,10 @@ export class QuerySubscriptions {
|
|
|
33
33
|
return this.bySubscriptionId.get(subscriptionId)?.token === token
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
unsubscribe(subscriptionId: string): void {
|
|
37
|
+
this.bySubscriptionId.delete(subscriptionId)
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
refresh(dataSourceId: NotionDataSourceId): QueryRefresh[] {
|
|
37
41
|
const refreshes: QueryRefresh[] = []
|
|
38
42
|
for (const queryState of this.bySubscriptionId.values()) {
|
|
@@ -51,7 +55,7 @@ export class QuerySubscriptions {
|
|
|
51
55
|
return refreshes
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
|
|
58
|
+
unsubscribeAll() {
|
|
55
59
|
this.bySubscriptionId.clear()
|
|
56
60
|
}
|
|
57
61
|
}
|