@notionhq/custom-blocks-host 0.2.6 → 0.2.7
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 +19 -5
- package/dist/createCustomBlockHost.d.ts +3 -1
- package/dist/createCustomBlockHost.d.ts.map +1 -1
- package/dist/createCustomBlockHost.js +10 -4
- package/dist/lifecycle/initializationController.d.ts +1 -1
- package/dist/lifecycle/initializationController.d.ts.map +1 -1
- package/dist/lifecycle/initializationController.js +6 -6
- package/dist/queries/querySubscriptions.d.ts +2 -0
- package/dist/queries/querySubscriptions.d.ts.map +1 -1
- package/dist/queries/querySubscriptions.js +11 -2
- package/package.json +1 -1
- package/src/createCustomBlockHost.ts +14 -5
- package/src/lifecycle/initializationController.ts +7 -7
- package/src/queries/querySubscriptions.ts +11 -2
package/README.md
CHANGED
|
@@ -81,9 +81,10 @@ const host = createCustomBlockHost({
|
|
|
81
81
|
onInitialized: state => {
|
|
82
82
|
console.log("Initialized manifest", state.manifest)
|
|
83
83
|
},
|
|
84
|
-
|
|
84
|
+
onConnectionFailure: error => {
|
|
85
85
|
showHostError(error)
|
|
86
86
|
},
|
|
87
|
+
onQueryUnsubscribed: undefined,
|
|
87
88
|
onLog: (direction, payload) => {
|
|
88
89
|
console.log(direction, payload)
|
|
89
90
|
},
|
|
@@ -101,8 +102,8 @@ Each host handles one initialization handshake with one sandbox iframe:
|
|
|
101
102
|
malformed ones. A connection after an initialization timeout does not restart it.
|
|
102
103
|
- The first observed iframe `load` is allowed, including when initialization completes before it.
|
|
103
104
|
- A second observed `load` stops the host and reports retryable `iframe_reloaded` through
|
|
104
|
-
`
|
|
105
|
-
connection instead of continuing to serve the replacement.
|
|
105
|
+
`onConnectionFailure`. A reload or navigation can replace the sandbox document, so the host ends
|
|
106
|
+
the connection instead of continuing to serve the replacement.
|
|
106
107
|
- Stopping removes message and load listeners, clears initialization timers, ends query
|
|
107
108
|
subscriptions, and prevents further outbound messages, including pending responses.
|
|
108
109
|
- To retry or apply a deployment or configuration change, the embedder creates a new host and
|
|
@@ -142,8 +143,12 @@ iframe reload.
|
|
|
142
143
|
|
|
143
144
|
- `onInitialized` reports success after the sandbox accepts initialization. It provides the selected
|
|
144
145
|
`manifest` and the sandbox's optional `initialHeight`.
|
|
145
|
-
- `
|
|
146
|
-
can run after initialization succeeds.
|
|
146
|
+
- `onConnectionFailure` reports a handshake failure, timeout, unavailable page context, or iframe
|
|
147
|
+
reload. It can run after initialization succeeds.
|
|
148
|
+
|
|
149
|
+
- `onQueryUnsubscribed` reports when a query subscription ends. Use it to release per-query
|
|
150
|
+
resources, such as listeners or timers. The host removes the subscription itself. A query failure
|
|
151
|
+
can end one subscription while the connection remains active.
|
|
147
152
|
|
|
148
153
|
`queryDataSource` is required. Other request handlers are optional. Every request handler may return
|
|
149
154
|
its result synchronously or as a promise. Results use a `status` discriminator: successful results
|
|
@@ -256,6 +261,15 @@ removes a subscription when it receives `unsubscribeDataSourceQuery`. Repeated u
|
|
|
256
261
|
are safe. The runtime also ignores results from a query that was unsubscribed while its handler was
|
|
257
262
|
running.
|
|
258
263
|
|
|
264
|
+
A non-retryable query error ends the subscription and discards any queued refresh. This includes
|
|
265
|
+
validation errors before the query handler runs. Retryable errors retain the subscription.
|
|
266
|
+
|
|
267
|
+
Use `onQueryUnsubscribed(subscriptionId)` to release adapter resources such as collection listeners.
|
|
268
|
+
The callback runs when a subscription ends through an explicit unsubscribe, replacement, terminal
|
|
269
|
+
error, or host stop. Repeated removal of the same subscription does not repeat the callback. A late
|
|
270
|
+
result from a replaced query cannot remove its replacement. The sandbox still receives the terminal
|
|
271
|
+
error result.
|
|
272
|
+
|
|
259
273
|
Call `stop()` when the iframe is removed. It detaches listeners, clears timeouts, and discards
|
|
260
274
|
active query subscriptions.
|
|
261
275
|
|
|
@@ -33,7 +33,9 @@ export type CustomBlockHostOptions = {
|
|
|
33
33
|
* before this callback runs.
|
|
34
34
|
* This callback can run after initialization succeeds.
|
|
35
35
|
*/
|
|
36
|
-
|
|
36
|
+
onConnectionFailure: ((error: CustomBlockHostFailure) => void) | undefined;
|
|
37
|
+
/** Releases adapter resources when a query ends, including replacement and terminal errors. */
|
|
38
|
+
onQueryUnsubscribed: ((subscriptionId: string) => void) | undefined;
|
|
37
39
|
onLog?: (direction: CustomBlockHostLogDirection, payload: unknown) => void;
|
|
38
40
|
getInitialState: CustomBlockHostGetInitialState;
|
|
39
41
|
initialStateTimeoutMs?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../src/createCustomBlockHost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,8CAA8C,CAAA;AAI5F,OAAO,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAA;AAClF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AAiBvF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gDAAgD,CAAA;AAC3F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAA;AAEpF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAA;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gDAAgD,CAAA;AACtF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAKzC,OAAO,KAAK,EACX,iCAAiC,EACjC,8BAA8B,EAI9B,MAAM,sBAAsB,CAAA;AAG7B,OAAO,KAAK,EACX,sBAAsB,EACtB,uBAAuB,EACvB,2BAA2B,EAC3B,MAAM,qBAAqB,CAAA;AAe5B,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,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;;;;;OAKG;IACH,
|
|
1
|
+
{"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../src/createCustomBlockHost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,8CAA8C,CAAA;AAI5F,OAAO,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAA;AAClF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AAiBvF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gDAAgD,CAAA;AAC3F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAA;AAEpF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAA;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gDAAgD,CAAA;AACtF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAKzC,OAAO,KAAK,EACX,iCAAiC,EACjC,8BAA8B,EAI9B,MAAM,sBAAsB,CAAA;AAG7B,OAAO,KAAK,EACX,sBAAsB,EACtB,uBAAuB,EACvB,2BAA2B,EAC3B,MAAM,qBAAqB,CAAA;AAe5B,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,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;;;;;OAKG;IACH,mBAAmB,EAAE,CAAC,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAC1E,+FAA+F;IAC/F,mBAAmB,EAAE,CAAC,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACnE,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,2BAA2B,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC1E,eAAe,EAAE,8BAA8B,CAAA;IAC/C,qBAAqB,CAAC,EAAE,MAAM,CAAA;CAC9B,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IACnC,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,KAAK,IAAI,CAAA;IAC/D,eAAe,EAAE,CAChB,YAAY,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,KACrD,IAAI,CAAA;IACT,SAAS,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,KAAK,IAAI,CAAA;IAClE,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,qBAAqB,CAAC,KAAK,IAAI,CAAA;IACjE,cAAc,EAAE,CAAC,WAAW,EAAE,iCAAiC,KAAK,IAAI,CAAA;IACxE,cAAc,EAAE,CAAC,WAAW,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,KAAK,IAAI,CAAA;IAC1E,YAAY,EAAE,CAAC,IAAI,EAAE;QACpB,YAAY,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAA;KACzD,KAAK,IAAI,CAAA;CACV,CAAA;AAQD,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,sBAAsB,GAC7B,qBAAqB,CA8YvB"}
|
|
@@ -19,7 +19,7 @@ import { unreachable } from "./utils.js";
|
|
|
19
19
|
// Earlier SDKs cannot represent this context, so the host reports context_unavailable.
|
|
20
20
|
const BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE = 5;
|
|
21
21
|
export function createCustomBlockHost(options) {
|
|
22
|
-
const { iframe, getInitialState, handlers, onManifest, onInitialized,
|
|
22
|
+
const { iframe, getInitialState, handlers, onManifest, onInitialized, onConnectionFailure, onLog, onQueryUnsubscribed, sandboxOrigin, minBridgeProtocolVersion: configuredMinBridgeProtocolVersion, noConnectTimeoutMs, initialStateTimeoutMs, noInitResultTimeoutMs, } = options;
|
|
23
23
|
// Callers can retire older SDKs, but cannot restore support for protocols
|
|
24
24
|
// the runtime no longer implements. Omission uses the runtime minimum.
|
|
25
25
|
const minBridgeProtocolVersion = Math.max(configuredMinBridgeProtocolVersion ??
|
|
@@ -29,7 +29,9 @@ export function createCustomBlockHost(options) {
|
|
|
29
29
|
let manifest;
|
|
30
30
|
let activeDataSources;
|
|
31
31
|
let sandboxBridgeProtocolVersion = LATEST_BRIDGE_PROTOCOL_VERSION;
|
|
32
|
-
const querySubscriptions = new QuerySubscriptions(
|
|
32
|
+
const querySubscriptions = new QuerySubscriptions(subscriptionId => {
|
|
33
|
+
onQueryUnsubscribed?.(subscriptionId);
|
|
34
|
+
});
|
|
33
35
|
function emit(direction, payload) {
|
|
34
36
|
onLog?.(direction, payload);
|
|
35
37
|
}
|
|
@@ -114,6 +116,7 @@ export function createCustomBlockHost(options) {
|
|
|
114
116
|
const queryError = getQueryValidationError(message, collectionSchema);
|
|
115
117
|
if (queryError !== undefined) {
|
|
116
118
|
if (querySubscriptions.isCurrent(message.subscriptionId, token)) {
|
|
119
|
+
querySubscriptions.unsubscribe(message.subscriptionId);
|
|
117
120
|
postQueryDataSourceResult({
|
|
118
121
|
subscriptionId: message.subscriptionId,
|
|
119
122
|
response: { status: "error", error: queryError },
|
|
@@ -125,6 +128,9 @@ export function createCustomBlockHost(options) {
|
|
|
125
128
|
if (!querySubscriptions.isCurrent(message.subscriptionId, token)) {
|
|
126
129
|
return;
|
|
127
130
|
}
|
|
131
|
+
if (response.status === "error" && !response.error.isRetryable) {
|
|
132
|
+
querySubscriptions.unsubscribe(message.subscriptionId);
|
|
133
|
+
}
|
|
128
134
|
postQueryDataSourceResult({
|
|
129
135
|
subscriptionId: message.subscriptionId,
|
|
130
136
|
response,
|
|
@@ -203,7 +209,7 @@ export function createCustomBlockHost(options) {
|
|
|
203
209
|
onInitialized?.({ manifest, initialHeight: message.initialHeight });
|
|
204
210
|
}
|
|
205
211
|
},
|
|
206
|
-
|
|
212
|
+
onConnectionFailure,
|
|
207
213
|
});
|
|
208
214
|
function prepareInitialState(result) {
|
|
209
215
|
if (result.status === "error") {
|
|
@@ -270,7 +276,7 @@ export function createCustomBlockHost(options) {
|
|
|
270
276
|
function stopWithFailure(error) {
|
|
271
277
|
stop();
|
|
272
278
|
emit("warn", error);
|
|
273
|
-
|
|
279
|
+
onConnectionFailure?.(error);
|
|
274
280
|
}
|
|
275
281
|
return {
|
|
276
282
|
stop,
|
|
@@ -15,7 +15,7 @@ export type InitializationControllerOptions = {
|
|
|
15
15
|
noInitResultTimeoutMs?: number;
|
|
16
16
|
stopHostWithFailure: (error: CustomBlockHostFailure) => void;
|
|
17
17
|
onInitResult?: (message: InitResultMessage) => void;
|
|
18
|
-
|
|
18
|
+
onConnectionFailure: ((error: CustomBlockHostFailure) => void) | undefined;
|
|
19
19
|
};
|
|
20
20
|
export type InitializationController = {
|
|
21
21
|
readonly status: InitStatus;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initializationController.d.ts","sourceRoot":"","sources":["../../src/lifecycle/initializationController.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EACX,wBAAwB,EACxB,iBAAiB,EACjB,MAAM,mDAAmD,CAAA;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAIlE,OAAO,KAAK,EACX,qBAAqB,EACrB,iCAAiC,EACjC,UAAU,EACV,MAAM,YAAY,CAAA;AAMnB,MAAM,MAAM,+BAA+B,GAAG;IAC7C,WAAW,EAAE,CACZ,OAAO,EAAE,qBAAqB,KAC1B,OAAO,CAAC,iCAAiC,CAAC,CAAA;IAC/C,eAAe,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,KAAK,IAAI,CAAA;IACxE,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,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,mBAAmB,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAA;IAC5D,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACnD,
|
|
1
|
+
{"version":3,"file":"initializationController.d.ts","sourceRoot":"","sources":["../../src/lifecycle/initializationController.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EACX,wBAAwB,EACxB,iBAAiB,EACjB,MAAM,mDAAmD,CAAA;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAIlE,OAAO,KAAK,EACX,qBAAqB,EACrB,iCAAiC,EACjC,UAAU,EACV,MAAM,YAAY,CAAA;AAMnB,MAAM,MAAM,+BAA+B,GAAG;IAC7C,WAAW,EAAE,CACZ,OAAO,EAAE,qBAAqB,KAC1B,OAAO,CAAC,iCAAiC,CAAC,CAAA;IAC/C,eAAe,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,KAAK,IAAI,CAAA;IACxE,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,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,mBAAmB,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAA;IAC5D,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACnD,mBAAmB,EAAE,CAAC,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;CAC1E,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,IAAI,EAAE;QAC5B,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAA;QACpC,KAAK,EAAE,wBAAwB,CAAA;QAC/B,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAA;KACzC,KAAK,SAAS,GAAG,MAAM,CAAA;IACxB,uBAAuB,EAAE,CAAC,IAAI,EAAE;QAC/B,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAA;QACpC,KAAK,EAAE,wBAAwB,CAAA;KAC/B,KAAK,IAAI,CAAA;IACV,YAAY,EAAE,MAAM,IAAI,CAAA;IACxB,IAAI,EAAE,MAAM,IAAI,CAAA;CAChB,CAAA;AAED,wBAAgB,8BAA8B,CAC7C,OAAO,EAAE,+BAA+B,GACtC,wBAAwB,CAiU1B"}
|
|
@@ -6,7 +6,7 @@ const DEFAULT_NO_CONNECT_TIMEOUT_MS = 5_000;
|
|
|
6
6
|
const DEFAULT_INITIAL_STATE_TIMEOUT_MS = 5_000;
|
|
7
7
|
const DEFAULT_NO_INIT_RESULT_TIMEOUT_MS = 5_000;
|
|
8
8
|
export function createInitializationController(options) {
|
|
9
|
-
const { prepareInit, sendInitMessage, warn, flushPendingHostStateChanges, minBridgeProtocolVersion, noConnectTimeoutMs = DEFAULT_NO_CONNECT_TIMEOUT_MS, initialStateTimeoutMs = DEFAULT_INITIAL_STATE_TIMEOUT_MS, noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS, stopHostWithFailure, onInitResult,
|
|
9
|
+
const { prepareInit, sendInitMessage, warn, flushPendingHostStateChanges, minBridgeProtocolVersion, noConnectTimeoutMs = DEFAULT_NO_CONNECT_TIMEOUT_MS, initialStateTimeoutMs = DEFAULT_INITIAL_STATE_TIMEOUT_MS, noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS, stopHostWithFailure, onInitResult, onConnectionFailure, } = options;
|
|
10
10
|
let initStatus = "waitingForConnect";
|
|
11
11
|
let noConnectTimeoutId;
|
|
12
12
|
let initialStateTimeoutId;
|
|
@@ -56,7 +56,7 @@ export function createInitializationController(options) {
|
|
|
56
56
|
status: "error",
|
|
57
57
|
error,
|
|
58
58
|
});
|
|
59
|
-
|
|
59
|
+
onConnectionFailure?.(error);
|
|
60
60
|
}
|
|
61
61
|
async function prepareAndSendInit(connect) {
|
|
62
62
|
if (initStatus !== "waitingForConnect") {
|
|
@@ -110,7 +110,7 @@ export function createInitializationController(options) {
|
|
|
110
110
|
}
|
|
111
111
|
initStatus = "error";
|
|
112
112
|
const error = initErrorForFailureReason("no_init_result");
|
|
113
|
-
|
|
113
|
+
onConnectionFailure?.(error);
|
|
114
114
|
warn({
|
|
115
115
|
...error,
|
|
116
116
|
initializationId: connect.initializationId,
|
|
@@ -210,7 +210,7 @@ export function createInitializationController(options) {
|
|
|
210
210
|
clearNoInitResultTimeout();
|
|
211
211
|
initStatus = "error";
|
|
212
212
|
warn(error);
|
|
213
|
-
|
|
213
|
+
onConnectionFailure?.(error);
|
|
214
214
|
}
|
|
215
215
|
function onIframeLoad() {
|
|
216
216
|
if (initStatus === "stopped") {
|
|
@@ -238,7 +238,7 @@ export function createInitializationController(options) {
|
|
|
238
238
|
initStatus = "error";
|
|
239
239
|
const error = initErrorForFailureReason("no_connect");
|
|
240
240
|
warn(error);
|
|
241
|
-
|
|
241
|
+
onConnectionFailure?.(error);
|
|
242
242
|
}, noConnectTimeoutMs);
|
|
243
243
|
}
|
|
244
244
|
function handleInitResult(message) {
|
|
@@ -256,7 +256,7 @@ export function createInitializationController(options) {
|
|
|
256
256
|
flushPendingHostStateChanges();
|
|
257
257
|
}
|
|
258
258
|
else {
|
|
259
|
-
|
|
259
|
+
onConnectionFailure?.(message.error);
|
|
260
260
|
}
|
|
261
261
|
onInitResult?.(message);
|
|
262
262
|
}
|
|
@@ -8,7 +8,9 @@ export type QuerySubscriptionToken = {
|
|
|
8
8
|
readonly _tag: "QuerySubscriptionToken";
|
|
9
9
|
};
|
|
10
10
|
export declare class QuerySubscriptions {
|
|
11
|
+
private readonly onUnsubscribe;
|
|
11
12
|
private readonly bySubscriptionId;
|
|
13
|
+
constructor(onUnsubscribe: (subscriptionId: string) => void);
|
|
12
14
|
subscribe(message: QueryDataSourceMessage): QuerySubscriptionToken;
|
|
13
15
|
isCurrent(subscriptionId: string, token: QuerySubscriptionToken): boolean;
|
|
14
16
|
unsubscribe(subscriptionId: string): void;
|
|
@@ -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;AASD,qBAAa,kBAAkB;
|
|
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;AASD,qBAAa,kBAAkB;IAI7B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAH/B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA4C;IAE7E,YACkB,aAAa,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,EAC7D;IAEJ,SAAS,CAAC,OAAO,EAAE,sBAAsB,GAAG,sBAAsB,CAYjE;IAED,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAExE;IAED,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAIxC;IAED,OAAO,CAAC,YAAY,EAAE,kBAAkB,GAAG,YAAY,EAAE,GAAG,SAAS,CAmBpE;IAED,MAAM,CACL,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,sBAAsB,GAC3B,YAAY,GAAG,SAAS,CAY1B;IAED,cAAc,SAIb;CACD"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
export class QuerySubscriptions {
|
|
2
|
+
onUnsubscribe;
|
|
2
3
|
bySubscriptionId = new Map();
|
|
4
|
+
constructor(onUnsubscribe) {
|
|
5
|
+
this.onUnsubscribe = onUnsubscribe;
|
|
6
|
+
}
|
|
3
7
|
subscribe(message) {
|
|
8
|
+
this.unsubscribe(message.subscriptionId);
|
|
4
9
|
const token = {
|
|
5
10
|
_tag: "QuerySubscriptionToken",
|
|
6
11
|
};
|
|
@@ -16,7 +21,9 @@ export class QuerySubscriptions {
|
|
|
16
21
|
return this.bySubscriptionId.get(subscriptionId)?.token === token;
|
|
17
22
|
}
|
|
18
23
|
unsubscribe(subscriptionId) {
|
|
19
|
-
this.bySubscriptionId.delete(subscriptionId)
|
|
24
|
+
if (this.bySubscriptionId.delete(subscriptionId)) {
|
|
25
|
+
this.onUnsubscribe(subscriptionId);
|
|
26
|
+
}
|
|
20
27
|
}
|
|
21
28
|
refresh(dataSourceId) {
|
|
22
29
|
const refreshes = [];
|
|
@@ -52,6 +59,8 @@ export class QuerySubscriptions {
|
|
|
52
59
|
return { message: state.message, token: state.token };
|
|
53
60
|
}
|
|
54
61
|
unsubscribeAll() {
|
|
55
|
-
this.bySubscriptionId.
|
|
62
|
+
for (const subscriptionId of this.bySubscriptionId.keys()) {
|
|
63
|
+
this.unsubscribe(subscriptionId);
|
|
64
|
+
}
|
|
56
65
|
}
|
|
57
66
|
}
|
package/package.json
CHANGED
|
@@ -83,7 +83,9 @@ export type CustomBlockHostOptions = {
|
|
|
83
83
|
* before this callback runs.
|
|
84
84
|
* This callback can run after initialization succeeds.
|
|
85
85
|
*/
|
|
86
|
-
|
|
86
|
+
onConnectionFailure: ((error: CustomBlockHostFailure) => void) | undefined
|
|
87
|
+
/** Releases adapter resources when a query ends, including replacement and terminal errors. */
|
|
88
|
+
onQueryUnsubscribed: ((subscriptionId: string) => void) | undefined
|
|
87
89
|
onLog?: (direction: CustomBlockHostLogDirection, payload: unknown) => void
|
|
88
90
|
getInitialState: CustomBlockHostGetInitialState
|
|
89
91
|
initialStateTimeoutMs?: number
|
|
@@ -119,8 +121,9 @@ export function createCustomBlockHost(
|
|
|
119
121
|
handlers,
|
|
120
122
|
onManifest,
|
|
121
123
|
onInitialized,
|
|
122
|
-
|
|
124
|
+
onConnectionFailure,
|
|
123
125
|
onLog,
|
|
126
|
+
onQueryUnsubscribed,
|
|
124
127
|
sandboxOrigin,
|
|
125
128
|
minBridgeProtocolVersion: configuredMinBridgeProtocolVersion,
|
|
126
129
|
noConnectTimeoutMs,
|
|
@@ -143,7 +146,9 @@ export function createCustomBlockHost(
|
|
|
143
146
|
let manifest: CustomBlockManifest | undefined
|
|
144
147
|
let activeDataSources: CustomBlockHostNormalizedDataSourcesPayload | undefined
|
|
145
148
|
let sandboxBridgeProtocolVersion = LATEST_BRIDGE_PROTOCOL_VERSION
|
|
146
|
-
const querySubscriptions = new QuerySubscriptions(
|
|
149
|
+
const querySubscriptions = new QuerySubscriptions(subscriptionId => {
|
|
150
|
+
onQueryUnsubscribed?.(subscriptionId)
|
|
151
|
+
})
|
|
147
152
|
|
|
148
153
|
function emit(direction: CustomBlockHostLogDirection, payload: unknown) {
|
|
149
154
|
onLog?.(direction, payload)
|
|
@@ -255,6 +260,7 @@ export function createCustomBlockHost(
|
|
|
255
260
|
const queryError = getQueryValidationError(message, collectionSchema)
|
|
256
261
|
if (queryError !== undefined) {
|
|
257
262
|
if (querySubscriptions.isCurrent(message.subscriptionId, token)) {
|
|
263
|
+
querySubscriptions.unsubscribe(message.subscriptionId)
|
|
258
264
|
postQueryDataSourceResult({
|
|
259
265
|
subscriptionId: message.subscriptionId,
|
|
260
266
|
response: { status: "error", error: queryError },
|
|
@@ -266,6 +272,9 @@ export function createCustomBlockHost(
|
|
|
266
272
|
if (!querySubscriptions.isCurrent(message.subscriptionId, token)) {
|
|
267
273
|
return
|
|
268
274
|
}
|
|
275
|
+
if (response.status === "error" && !response.error.isRetryable) {
|
|
276
|
+
querySubscriptions.unsubscribe(message.subscriptionId)
|
|
277
|
+
}
|
|
269
278
|
postQueryDataSourceResult({
|
|
270
279
|
subscriptionId: message.subscriptionId,
|
|
271
280
|
response,
|
|
@@ -348,7 +357,7 @@ export function createCustomBlockHost(
|
|
|
348
357
|
onInitialized?.({ manifest, initialHeight: message.initialHeight })
|
|
349
358
|
}
|
|
350
359
|
},
|
|
351
|
-
|
|
360
|
+
onConnectionFailure,
|
|
352
361
|
})
|
|
353
362
|
|
|
354
363
|
function prepareInitialState(
|
|
@@ -431,7 +440,7 @@ export function createCustomBlockHost(
|
|
|
431
440
|
function stopWithFailure(error: CustomBlockHostFailure) {
|
|
432
441
|
stop()
|
|
433
442
|
emit("warn", error)
|
|
434
|
-
|
|
443
|
+
onConnectionFailure?.(error)
|
|
435
444
|
}
|
|
436
445
|
|
|
437
446
|
return {
|
|
@@ -33,7 +33,7 @@ export type InitializationControllerOptions = {
|
|
|
33
33
|
noInitResultTimeoutMs?: number
|
|
34
34
|
stopHostWithFailure: (error: CustomBlockHostFailure) => void
|
|
35
35
|
onInitResult?: (message: InitResultMessage) => void
|
|
36
|
-
|
|
36
|
+
onConnectionFailure: ((error: CustomBlockHostFailure) => void) | undefined
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export type InitializationController = {
|
|
@@ -68,7 +68,7 @@ export function createInitializationController(
|
|
|
68
68
|
noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS,
|
|
69
69
|
stopHostWithFailure,
|
|
70
70
|
onInitResult,
|
|
71
|
-
|
|
71
|
+
onConnectionFailure,
|
|
72
72
|
} = options
|
|
73
73
|
|
|
74
74
|
let initStatus: InitStatus = "waitingForConnect"
|
|
@@ -127,7 +127,7 @@ export function createInitializationController(
|
|
|
127
127
|
status: "error",
|
|
128
128
|
error,
|
|
129
129
|
})
|
|
130
|
-
|
|
130
|
+
onConnectionFailure?.(error)
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
async function prepareAndSendInit(connect: ConnectSuccessMessage) {
|
|
@@ -184,7 +184,7 @@ export function createInitializationController(
|
|
|
184
184
|
}
|
|
185
185
|
initStatus = "error"
|
|
186
186
|
const error = initErrorForFailureReason("no_init_result")
|
|
187
|
-
|
|
187
|
+
onConnectionFailure?.(error)
|
|
188
188
|
warn({
|
|
189
189
|
...error,
|
|
190
190
|
initializationId: connect.initializationId,
|
|
@@ -301,7 +301,7 @@ export function createInitializationController(
|
|
|
301
301
|
clearNoInitResultTimeout()
|
|
302
302
|
initStatus = "error"
|
|
303
303
|
warn(error)
|
|
304
|
-
|
|
304
|
+
onConnectionFailure?.(error)
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
function onIframeLoad() {
|
|
@@ -331,7 +331,7 @@ export function createInitializationController(
|
|
|
331
331
|
initStatus = "error"
|
|
332
332
|
const error = initErrorForFailureReason("no_connect")
|
|
333
333
|
warn(error)
|
|
334
|
-
|
|
334
|
+
onConnectionFailure?.(error)
|
|
335
335
|
}, noConnectTimeoutMs)
|
|
336
336
|
}
|
|
337
337
|
|
|
@@ -351,7 +351,7 @@ export function createInitializationController(
|
|
|
351
351
|
if (message.status === "success") {
|
|
352
352
|
flushPendingHostStateChanges()
|
|
353
353
|
} else {
|
|
354
|
-
|
|
354
|
+
onConnectionFailure?.(message.error)
|
|
355
355
|
}
|
|
356
356
|
onInitResult?.(message)
|
|
357
357
|
}
|
|
@@ -20,7 +20,12 @@ type QuerySubscriptionState = {
|
|
|
20
20
|
export class QuerySubscriptions {
|
|
21
21
|
private readonly bySubscriptionId = new Map<string, QuerySubscriptionState>()
|
|
22
22
|
|
|
23
|
+
constructor(
|
|
24
|
+
private readonly onUnsubscribe: (subscriptionId: string) => void,
|
|
25
|
+
) {}
|
|
26
|
+
|
|
23
27
|
subscribe(message: QueryDataSourceMessage): QuerySubscriptionToken {
|
|
28
|
+
this.unsubscribe(message.subscriptionId)
|
|
24
29
|
const token: QuerySubscriptionToken = {
|
|
25
30
|
_tag: "QuerySubscriptionToken",
|
|
26
31
|
}
|
|
@@ -38,7 +43,9 @@ export class QuerySubscriptions {
|
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
unsubscribe(subscriptionId: string): void {
|
|
41
|
-
this.bySubscriptionId.delete(subscriptionId)
|
|
46
|
+
if (this.bySubscriptionId.delete(subscriptionId)) {
|
|
47
|
+
this.onUnsubscribe(subscriptionId)
|
|
48
|
+
}
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
refresh(dataSourceId: NotionDataSourceId): QueryRefresh[] | undefined {
|
|
@@ -80,6 +87,8 @@ export class QuerySubscriptions {
|
|
|
80
87
|
}
|
|
81
88
|
|
|
82
89
|
unsubscribeAll() {
|
|
83
|
-
this.bySubscriptionId.
|
|
90
|
+
for (const subscriptionId of this.bySubscriptionId.keys()) {
|
|
91
|
+
this.unsubscribe(subscriptionId)
|
|
92
|
+
}
|
|
84
93
|
}
|
|
85
94
|
}
|