@notionhq/custom-blocks-host 0.1.30 → 0.1.31
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 +0 -1
- package/dist/createCustomBlockHost.d.ts +0 -2
- package/dist/createCustomBlockHost.d.ts.map +1 -1
- package/dist/createCustomBlockHost.js +62 -30
- package/dist/lifecycle/initializationController.d.ts +2 -3
- package/dist/lifecycle/initializationController.d.ts.map +1 -1
- package/dist/lifecycle/initializationController.js +11 -6
- package/dist/lifecycle/types.d.ts +1 -1
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/messages/sandboxMessageRouter.d.ts +2 -2
- package/dist/messages/sandboxMessageRouter.d.ts.map +1 -1
- package/dist/messages/sandboxMessageRouter.js +5 -5
- package/package.json +1 -1
- package/src/createCustomBlockHost.ts +76 -33
- package/src/lifecycle/initializationController.ts +12 -8
- package/src/lifecycle/types.ts +1 -1
- package/src/messages/sandboxMessageRouter.ts +7 -7
package/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type NotionContrastMode } from "./protocol/contrast.js";
|
|
2
2
|
import type { NotionDataSourceId } from "./protocol/ids.js";
|
|
3
3
|
import type { CustomBlockManifest } from "./protocol/manifest.js";
|
|
4
|
-
import type { HostToSandboxMessage } from "./protocol/messages/hostToSandbox.js";
|
|
5
4
|
import type { CustomBlockPage } from "./protocol/pages/page.js";
|
|
6
5
|
import type { NotionParent } from "./protocol/parent.js";
|
|
7
6
|
import type { NotionTheme } from "./protocol/theme.js";
|
|
@@ -42,7 +41,6 @@ export type CustomBlockHostOptions = {
|
|
|
42
41
|
};
|
|
43
42
|
export type CustomBlockHostHandle = {
|
|
44
43
|
stop: () => void;
|
|
45
|
-
post: (message: HostToSandboxMessage) => void;
|
|
46
44
|
setTheme: (theme: NotionTheme) => void;
|
|
47
45
|
setContrastMode: (contrastMode: NotionContrastMode) => void;
|
|
48
46
|
setParent: (parent: NotionParent) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../src/createCustomBlockHost.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,kBAAkB,EACvB,MAAM,8CAA8C,CAAA;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AACjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;
|
|
1
|
+
{"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../src/createCustomBlockHost.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,kBAAkB,EACvB,MAAM,8CAA8C,CAAA;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AACjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AAmBvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAE9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAIhF,OAAO,KAAK,EACX,iCAAiC,EACjC,2BAA2B,EAC3B,MAAM,sBAAsB,CAAA;AAE7B,OAAO,KAAK,EACX,sBAAsB,EACtB,uBAAuB,EACvB,2BAA2B,EAC3B,MAAM,qBAAqB,CAAA;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,YAAY,EAAE,2BAA2B,CAAA;IACzC,QAAQ,EAAE,uBAAuB,CAAA;IACjC;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,mBAAmB,KAAK,IAAI,CAAA;IACpD,gGAAgG;IAChG,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QACvB,QAAQ,EAAE,mBAAmB,CAAA;QAC7B,aAAa,CAAC,EAAE,MAAM,CAAA;KACtB,KAAK,IAAI,CAAA;IACV;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAA;IACnD,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,2BAA2B,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;CAC1E,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IACnC,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,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;AAyBD,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,sBAAsB,GAC7B,qBAAqB,CA4ZvB"}
|
|
@@ -8,6 +8,7 @@ import { MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION } from "./protocolVersion.js
|
|
|
8
8
|
import { QuerySubscriptions, } from "./queries/querySubscriptions.js";
|
|
9
9
|
import { validateQueryDataSourceFilter } from "./queries/validateQueryDataSourceFilter.js";
|
|
10
10
|
import { validateQueryDataSourceSorts } from "./queries/validateQueryDataSourceSorts.js";
|
|
11
|
+
import { unreachable } from "./utils.js";
|
|
11
12
|
// Bridge protocol version that introduced page: { type: "unavailable" }.
|
|
12
13
|
// Earlier SDKs cannot represent this context, so the host reports context_unavailable.
|
|
13
14
|
const BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE = 5;
|
|
@@ -36,15 +37,47 @@ export function createCustomBlockHost(options) {
|
|
|
36
37
|
function emit(direction, payload) {
|
|
37
38
|
onLog?.(direction, payload);
|
|
38
39
|
}
|
|
39
|
-
function
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
function postToSandbox(message) {
|
|
41
|
+
const status = initializationController.status;
|
|
42
|
+
switch (status) {
|
|
43
|
+
case "waitingForConnect":
|
|
44
|
+
case "waitingForInitResult":
|
|
45
|
+
case "initialized":
|
|
46
|
+
case "error":
|
|
47
|
+
break;
|
|
48
|
+
// A stopped host must not send messages, including pending responses.
|
|
49
|
+
// Return from the function to skip delivery below.
|
|
50
|
+
case "stopped":
|
|
51
|
+
return;
|
|
52
|
+
default:
|
|
53
|
+
return unreachable(status);
|
|
43
54
|
}
|
|
44
55
|
const sandboxMessage = normalizeHostToSandboxMessage(message, sandboxBridgeProtocolVersion);
|
|
45
56
|
emit("out", sandboxMessage);
|
|
46
57
|
iframe.contentWindow?.postMessage(sandboxMessage, sandboxOrigin);
|
|
47
58
|
}
|
|
59
|
+
function sendInitMessage(message) {
|
|
60
|
+
postToSandbox(message);
|
|
61
|
+
}
|
|
62
|
+
function sendNack(message) {
|
|
63
|
+
postToSandbox(message);
|
|
64
|
+
}
|
|
65
|
+
function sendInitializedMessage(message) {
|
|
66
|
+
const status = initializationController.status;
|
|
67
|
+
switch (status) {
|
|
68
|
+
case "initialized":
|
|
69
|
+
postToSandbox(message);
|
|
70
|
+
return;
|
|
71
|
+
// Send state updates and handler results only after initialization succeeds.
|
|
72
|
+
case "waitingForConnect":
|
|
73
|
+
case "waitingForInitResult":
|
|
74
|
+
case "error":
|
|
75
|
+
case "stopped":
|
|
76
|
+
return;
|
|
77
|
+
default:
|
|
78
|
+
unreachable(status);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
48
81
|
/**
|
|
49
82
|
* Invokes a one-shot handler and posts its result with the request's ID.
|
|
50
83
|
* Each result type is named `<request type>Result` by the bridge protocol.
|
|
@@ -59,7 +92,7 @@ export function createCustomBlockHost(options) {
|
|
|
59
92
|
requestId: message.requestId,
|
|
60
93
|
...response,
|
|
61
94
|
};
|
|
62
|
-
|
|
95
|
+
sendInitializedMessage(result);
|
|
63
96
|
}
|
|
64
97
|
function postQueryDataSourceResult(args) {
|
|
65
98
|
const message = args.response.status === "error"
|
|
@@ -76,7 +109,7 @@ export function createCustomBlockHost(options) {
|
|
|
76
109
|
items: args.response.items,
|
|
77
110
|
hasMore: args.response.hasMore,
|
|
78
111
|
};
|
|
79
|
-
|
|
112
|
+
sendInitializedMessage(message);
|
|
80
113
|
}
|
|
81
114
|
async function handleQuery(message, token = querySubscriptions.subscribe(message)) {
|
|
82
115
|
const collectionSchema = getCollectionSchema(activeDataSources, message.dataSourceId);
|
|
@@ -102,36 +135,36 @@ export function createCustomBlockHost(options) {
|
|
|
102
135
|
/**
|
|
103
136
|
* Sends any pending host state changes to the sandbox. It is possible for these to change
|
|
104
137
|
* between when the host sends `init` and when the sandbox responds with `initResult`. The host
|
|
105
|
-
* does send
|
|
138
|
+
* does not send pending state changes until the sandbox responds.
|
|
106
139
|
*/
|
|
107
140
|
function flushPendingHostStateChanges() {
|
|
108
141
|
if (pendingThemeChange) {
|
|
109
142
|
pendingThemeChange = false;
|
|
110
|
-
|
|
143
|
+
sendInitializedMessage({ type: "themeChanged", theme });
|
|
111
144
|
}
|
|
112
145
|
if (pendingContrastModeChange) {
|
|
113
146
|
pendingContrastModeChange = false;
|
|
114
|
-
|
|
147
|
+
sendInitializedMessage({ type: "contrastModeChanged", contrastMode });
|
|
115
148
|
}
|
|
116
149
|
if (pendingParentChange) {
|
|
117
150
|
pendingParentChange = false;
|
|
118
|
-
|
|
151
|
+
sendInitializedMessage({ type: "parentChanged", parent });
|
|
119
152
|
}
|
|
120
153
|
if (pendingPageChange) {
|
|
121
154
|
pendingPageChange = false;
|
|
122
|
-
|
|
155
|
+
sendInitializedMessage({ type: "pageChanged", page });
|
|
123
156
|
}
|
|
124
157
|
if (pendingDataSourcesChange !== undefined) {
|
|
125
158
|
const nextDataSources = pendingDataSourcesChange;
|
|
126
159
|
pendingDataSourcesChange = undefined;
|
|
127
|
-
|
|
160
|
+
sendInitializedMessage({
|
|
128
161
|
type: "dataSourcesChanged",
|
|
129
162
|
dataSources: nextDataSources,
|
|
130
163
|
});
|
|
131
164
|
}
|
|
132
165
|
if (pendingCurrentUserChange) {
|
|
133
166
|
pendingCurrentUserChange = false;
|
|
134
|
-
|
|
167
|
+
sendInitializedMessage({ type: "currentUserChanged", currentUser });
|
|
135
168
|
}
|
|
136
169
|
}
|
|
137
170
|
const initializationController = createInitializationController({
|
|
@@ -176,7 +209,7 @@ export function createCustomBlockHost(options) {
|
|
|
176
209
|
dataSources: resolvedDataSources,
|
|
177
210
|
};
|
|
178
211
|
},
|
|
179
|
-
|
|
212
|
+
sendInitMessage,
|
|
180
213
|
warn: payload => emit("warn", payload),
|
|
181
214
|
flushPendingHostStateChanges,
|
|
182
215
|
minBridgeProtocolVersion,
|
|
@@ -195,7 +228,7 @@ export function createCustomBlockHost(options) {
|
|
|
195
228
|
sandboxOrigin,
|
|
196
229
|
getInitStatus: () => initializationController.status,
|
|
197
230
|
emit,
|
|
198
|
-
|
|
231
|
+
sendNack,
|
|
199
232
|
onInvalidConnect: initializationController.handleInvalidConnect,
|
|
200
233
|
onInvalidInitResult: initializationController.handleInvalidInitResult,
|
|
201
234
|
onConnect: initializationController.handleConnect,
|
|
@@ -225,21 +258,20 @@ export function createCustomBlockHost(options) {
|
|
|
225
258
|
}
|
|
226
259
|
return {
|
|
227
260
|
stop,
|
|
228
|
-
post,
|
|
229
261
|
setTheme(nextTheme) {
|
|
230
262
|
theme = nextTheme;
|
|
231
|
-
if (initializationController.status !== "
|
|
263
|
+
if (initializationController.status !== "initialized") {
|
|
232
264
|
if (initializationController.status === "waitingForInitResult") {
|
|
233
265
|
pendingThemeChange = true;
|
|
234
266
|
}
|
|
235
267
|
return;
|
|
236
268
|
}
|
|
237
269
|
const message = { type: "themeChanged", theme };
|
|
238
|
-
|
|
270
|
+
sendInitializedMessage(message);
|
|
239
271
|
},
|
|
240
272
|
setContrastMode(nextContrastMode) {
|
|
241
273
|
contrastMode = nextContrastMode;
|
|
242
|
-
if (initializationController.status !== "
|
|
274
|
+
if (initializationController.status !== "initialized") {
|
|
243
275
|
if (initializationController.status === "waitingForInitResult") {
|
|
244
276
|
pendingContrastModeChange = true;
|
|
245
277
|
}
|
|
@@ -249,18 +281,18 @@ export function createCustomBlockHost(options) {
|
|
|
249
281
|
type: "contrastModeChanged",
|
|
250
282
|
contrastMode,
|
|
251
283
|
};
|
|
252
|
-
|
|
284
|
+
sendInitializedMessage(message);
|
|
253
285
|
},
|
|
254
286
|
setParent(nextParent) {
|
|
255
287
|
parent = nextParent;
|
|
256
|
-
if (initializationController.status !== "
|
|
288
|
+
if (initializationController.status !== "initialized") {
|
|
257
289
|
if (initializationController.status === "waitingForInitResult") {
|
|
258
290
|
pendingParentChange = true;
|
|
259
291
|
}
|
|
260
292
|
return;
|
|
261
293
|
}
|
|
262
294
|
const message = { type: "parentChanged", parent };
|
|
263
|
-
|
|
295
|
+
sendInitializedMessage(message);
|
|
264
296
|
},
|
|
265
297
|
setPage(nextPage) {
|
|
266
298
|
page = nextPage;
|
|
@@ -268,18 +300,18 @@ export function createCustomBlockHost(options) {
|
|
|
268
300
|
sandboxBridgeProtocolVersion <
|
|
269
301
|
BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE &&
|
|
270
302
|
(initializationController.status === "waitingForInitResult" ||
|
|
271
|
-
initializationController.status === "
|
|
303
|
+
initializationController.status === "initialized")) {
|
|
272
304
|
stopWithFailure(initErrorForFailureReason("context_unavailable"));
|
|
273
305
|
return;
|
|
274
306
|
}
|
|
275
|
-
if (initializationController.status !== "
|
|
307
|
+
if (initializationController.status !== "initialized") {
|
|
276
308
|
if (initializationController.status === "waitingForInitResult") {
|
|
277
309
|
pendingPageChange = true;
|
|
278
310
|
}
|
|
279
311
|
return;
|
|
280
312
|
}
|
|
281
313
|
const message = { type: "pageChanged", page };
|
|
282
|
-
|
|
314
|
+
sendInitializedMessage(message);
|
|
283
315
|
},
|
|
284
316
|
setDataSources(nextDataSources) {
|
|
285
317
|
if (initializationController.hasConnectedSandbox &&
|
|
@@ -293,7 +325,7 @@ export function createCustomBlockHost(options) {
|
|
|
293
325
|
}
|
|
294
326
|
dataSources = nextDataSources;
|
|
295
327
|
activeDataSources = nextDataSources;
|
|
296
|
-
if (initializationController.status !== "
|
|
328
|
+
if (initializationController.status !== "initialized") {
|
|
297
329
|
if (initializationController.status === "waitingForInitResult") {
|
|
298
330
|
pendingDataSourcesChange = nextDataSources;
|
|
299
331
|
}
|
|
@@ -303,20 +335,20 @@ export function createCustomBlockHost(options) {
|
|
|
303
335
|
type: "dataSourcesChanged",
|
|
304
336
|
dataSources: nextDataSources,
|
|
305
337
|
};
|
|
306
|
-
|
|
338
|
+
sendInitializedMessage(message);
|
|
307
339
|
},
|
|
308
340
|
setCurrentUser(nextCurrentUser) {
|
|
309
341
|
currentUser = nextCurrentUser;
|
|
310
|
-
if (initializationController.status !== "
|
|
342
|
+
if (initializationController.status !== "initialized") {
|
|
311
343
|
if (initializationController.status === "waitingForInitResult") {
|
|
312
344
|
pendingCurrentUserChange = true;
|
|
313
345
|
}
|
|
314
346
|
return;
|
|
315
347
|
}
|
|
316
|
-
|
|
348
|
+
sendInitializedMessage({ type: "currentUserChanged", currentUser });
|
|
317
349
|
},
|
|
318
350
|
refreshQuery({ dataSourceId }) {
|
|
319
|
-
if (initializationController.status !== "
|
|
351
|
+
if (initializationController.status !== "initialized") {
|
|
320
352
|
return;
|
|
321
353
|
}
|
|
322
354
|
const refreshes = querySubscriptions.refresh(dataSourceId);
|
|
@@ -2,8 +2,7 @@ import type { NotionContrastMode } from "../protocol/contrast.js";
|
|
|
2
2
|
import type { NotionBlockId } from "../protocol/ids.js";
|
|
3
3
|
import type { CustomBlockManifest } from "../protocol/manifest.js";
|
|
4
4
|
import type { ConnectMessage } from "../protocol/messages/connect.js";
|
|
5
|
-
import type {
|
|
6
|
-
import type { CustomBlockInitErrorInfo } from "../protocol/messages/init.js";
|
|
5
|
+
import type { CustomBlockInitErrorInfo, InitMessage } from "../protocol/messages/init.js";
|
|
7
6
|
import type { InitResultMessage } from "../protocol/messages/initResult.js";
|
|
8
7
|
import type { CustomBlockPage } from "../protocol/pages/page.js";
|
|
9
8
|
import type { NotionParent } from "../protocol/parent.js";
|
|
@@ -30,7 +29,7 @@ export type InitializationControllerOptions = {
|
|
|
30
29
|
blockId: NotionBlockId;
|
|
31
30
|
getState: () => InitializationState;
|
|
32
31
|
prepareInit: (connect: ConnectSuccessMessage) => InitializationPreparation;
|
|
33
|
-
|
|
32
|
+
sendInitMessage: (message: InitMessage) => void;
|
|
34
33
|
warn: (payload: unknown) => void;
|
|
35
34
|
flushPendingHostStateChanges: () => void;
|
|
36
35
|
minBridgeProtocolVersion: number;
|
|
@@ -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,
|
|
1
|
+
{"version":3,"file":"initializationController.d.ts","sourceRoot":"","sources":["../../src/lifecycle/initializationController.ts"],"names":[],"mappings":"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"}
|
|
@@ -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,
|
|
6
|
+
const { blockId, getState, prepareInit, sendInitMessage, warn, flushPendingHostStateChanges, minBridgeProtocolVersion, noConnectTimeoutMs = DEFAULT_NO_CONNECT_TIMEOUT_MS, noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS, stopHostWithFailure, onInitResult, onFailure, } = options;
|
|
7
7
|
let initStatus = "waitingForConnect";
|
|
8
8
|
let noConnectTimeoutId;
|
|
9
9
|
let noInitResultTimeoutId;
|
|
@@ -30,7 +30,7 @@ export function createInitializationController(options) {
|
|
|
30
30
|
initStatus = "error";
|
|
31
31
|
activeInitializationId = initializationId;
|
|
32
32
|
clearNoConnectTimeout();
|
|
33
|
-
|
|
33
|
+
sendInitMessage({
|
|
34
34
|
type: "init",
|
|
35
35
|
initializationId,
|
|
36
36
|
status: "error",
|
|
@@ -38,11 +38,16 @@ export function createInitializationController(options) {
|
|
|
38
38
|
});
|
|
39
39
|
onFailure?.(error);
|
|
40
40
|
}
|
|
41
|
-
function
|
|
41
|
+
function prepareAndSendInit(connect) {
|
|
42
42
|
if (initStatus !== "waitingForConnect") {
|
|
43
43
|
return false;
|
|
44
44
|
}
|
|
45
45
|
const preparation = prepareInit(connect);
|
|
46
|
+
// The embedder can stop the host when it receives the manifest.
|
|
47
|
+
// If that happens, do not continue initialization or start its timer.
|
|
48
|
+
if (initStatus !== "waitingForConnect") {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
46
51
|
if (preparation.status === "error") {
|
|
47
52
|
sendInitError({
|
|
48
53
|
initializationId: connect.initializationId,
|
|
@@ -67,7 +72,7 @@ export function createInitializationController(options) {
|
|
|
67
72
|
dataSources: preparation.dataSources,
|
|
68
73
|
currentUser: state.currentUser,
|
|
69
74
|
};
|
|
70
|
-
|
|
75
|
+
sendInitMessage(message);
|
|
71
76
|
noInitResultTimeoutId = window.setTimeout(() => {
|
|
72
77
|
if (initStatus !== "waitingForInitResult") {
|
|
73
78
|
return;
|
|
@@ -119,7 +124,7 @@ export function createInitializationController(options) {
|
|
|
119
124
|
});
|
|
120
125
|
return;
|
|
121
126
|
}
|
|
122
|
-
if (
|
|
127
|
+
if (prepareAndSendInit(message)) {
|
|
123
128
|
hasConnectedSandbox = true;
|
|
124
129
|
}
|
|
125
130
|
}
|
|
@@ -204,7 +209,7 @@ export function createInitializationController(options) {
|
|
|
204
209
|
}
|
|
205
210
|
clearNoInitResultTimeout();
|
|
206
211
|
if (message.status === "success") {
|
|
207
|
-
initStatus = "
|
|
212
|
+
initStatus = "initialized";
|
|
208
213
|
flushPendingHostStateChanges();
|
|
209
214
|
}
|
|
210
215
|
else {
|
|
@@ -17,7 +17,7 @@ export type InitStatus =
|
|
|
17
17
|
/** The host is waiting for a matching `initResult` after it sends `init`. */
|
|
18
18
|
| "waitingForInitResult"
|
|
19
19
|
/** The host is initialized and can send live updates. */
|
|
20
|
-
| "
|
|
20
|
+
| "initialized"
|
|
21
21
|
/** The host is in an error state after an initialization failure or timeout. */
|
|
22
22
|
| "error"
|
|
23
23
|
/** The host is stopped and no longer sends or accepts sandbox messages. */
|
|
@@ -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,gEAAgE;AAChE,MAAM,MAAM,UAAU;AACrB,qEAAqE;AACnE,mBAAmB;AACrB,6EAA6E;GAC3E,sBAAsB;AACxB,yDAAyD;GACvD,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lifecycle/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAA;AACtF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4DAA4D,CAAA;AAC1G,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAA;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAEhF,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAC1C,cAAc,EACd;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CACrB,CAAA;AAED,gEAAgE;AAChE,MAAM,MAAM,UAAU;AACrB,qEAAqE;AACnE,mBAAmB;AACrB,6EAA6E;GAC3E,sBAAsB;AACxB,yDAAyD;GACvD,aAAa;AACf,gFAAgF;GAC9E,OAAO;AACT,2EAA2E;GACzE,SAAS,CAAA;AAEZ,MAAM,MAAM,iCAAiC,GAAG;IAC/C,QAAQ,EAAE,wBAAwB,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,iCAAiC,GAC1C,iCAAiC,GACjC,CAAC,CAAC,OAAO,EAAE,qBAAqB,KAAK,iCAAiC,CAAC,CAAA;AAE1E,MAAM,MAAM,2BAA2B,GAAG;IACzC,KAAK,EAAE,WAAW,CAAA;IAClB;;;OAGG;IACH,YAAY,CAAC,EAAE,kBAAkB,CAAA;IACjC,OAAO,EAAE,aAAa,CAAA;IACtB,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,eAAe,CAAA;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,mBAAmB,CAAA;IAC9B,WAAW,EAAE,iCAAiC,CAAA;IAC9C,WAAW,EAAE,UAAU,CAAA;CACvB,CAAA"}
|
|
@@ -2,8 +2,8 @@ import type { ConnectMessage } from "../protocol/messages/connect.js";
|
|
|
2
2
|
import type { CreatePageMessage } from "../protocol/messages/createPage.js";
|
|
3
3
|
import type { GetPageMessage } from "../protocol/messages/getPage.js";
|
|
4
4
|
import type { GetUserMessage } from "../protocol/messages/getUser.js";
|
|
5
|
-
import type { HostToSandboxMessage } from "../protocol/messages/hostToSandbox.js";
|
|
6
5
|
import type { InitResultMessage } from "../protocol/messages/initResult.js";
|
|
6
|
+
import type { InvalidSandboxMessage } from "../protocol/messages/invalidSandboxMessage.js";
|
|
7
7
|
import type { ListUsersMessage } from "../protocol/messages/listUsers.js";
|
|
8
8
|
import type { OpenPageMessage } from "../protocol/messages/openPage.js";
|
|
9
9
|
import type { QueryDataSourceMessage } from "../protocol/messages/queryDataSource.js";
|
|
@@ -18,7 +18,7 @@ export type SandboxMessageRouterOptions = {
|
|
|
18
18
|
sandboxOrigin: string;
|
|
19
19
|
getInitStatus: () => InitStatus;
|
|
20
20
|
emit: (direction: CustomBlockHostLogDirection, payload: unknown) => void;
|
|
21
|
-
|
|
21
|
+
sendNack: (message: InvalidSandboxMessage) => void;
|
|
22
22
|
onInvalidConnect: InitializationController["handleInvalidConnect"];
|
|
23
23
|
onInvalidInitResult: InitializationController["handleInvalidInitResult"];
|
|
24
24
|
onConnect: (message: ConnectMessage) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandboxMessageRouter.d.ts","sourceRoot":"","sources":["../../src/messages/sandboxMessageRouter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"sandboxMessageRouter.d.ts","sourceRoot":"","sources":["../../src/messages/sandboxMessageRouter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oEAAoE,CAAA;AAE/G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wDAAwD,CAAA;AAC9F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uDAAuD,CAAA;AAC5F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8DAA8D,CAAA;AAC1G,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qDAAqD,CAAA;AAExF,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,yEAAyE,CAAA;AAChI,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAGhG,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAA;AACxF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAGvD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAA;AAY7D,MAAM,MAAM,2BAA2B,GAAG;IACzC,MAAM,EAAE,iBAAiB,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,UAAU,CAAA;IAC/B,IAAI,EAAE,CAAC,SAAS,EAAE,2BAA2B,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACxE,QAAQ,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAA;IAClD,gBAAgB,EAAE,wBAAwB,CAAC,sBAAsB,CAAC,CAAA;IAClE,mBAAmB,EAAE,wBAAwB,CAAC,yBAAyB,CAAC,CAAA;IACxE,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;IAC5C,YAAY,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;IAClD,iBAAiB,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5E,4BAA4B,EAAE,CAC7B,OAAO,EAAE,iCAAiC,KACtC,IAAI,CAAA;IACT,YAAY,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClE,UAAU,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9D,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5D,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5D,WAAW,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAChE,YAAY,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClE,QAAQ,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;CAC1C,CAAA;AAED,wBAAgB,0BAA0B,CACzC,OAAO,EAAE,2BAA2B,GAClC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAsI/B"}
|
|
@@ -13,7 +13,7 @@ const bridgeProtocolVersionSchema = v.object({
|
|
|
13
13
|
bridgeProtocolVersion: v.union([v.number(), v.nan()]),
|
|
14
14
|
});
|
|
15
15
|
export function createSandboxMessageRouter(options) {
|
|
16
|
-
const { iframe, sandboxOrigin, getInitStatus, emit,
|
|
16
|
+
const { iframe, sandboxOrigin, getInitStatus, emit, sendNack, onInvalidConnect, onInvalidInitResult, onConnect, onInitResult, onQueryDataSource, onUnsubscribeDataSourceQuery, onCreatePage, onGetPage, onOpenPage, onGetUser, onListUsers, onUpdatePage, onResize, } = options;
|
|
17
17
|
function onInvalidSandboxMessage(data, issues) {
|
|
18
18
|
emit("warn", {
|
|
19
19
|
message: "Ignored unsupported sandbox message",
|
|
@@ -47,9 +47,9 @@ export function createSandboxMessageRouter(options) {
|
|
|
47
47
|
error: initErrorForFailureReason("invalid_init_result_payload"),
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
|
-
// Other messages get NACKed indicating they were invalid and skipped
|
|
51
|
-
if (response.nack !== undefined) {
|
|
52
|
-
|
|
50
|
+
// Other messages get NACKed indicating they were invalid and skipped.
|
|
51
|
+
if (response.nack !== undefined && getInitStatus() !== "stopped") {
|
|
52
|
+
sendNack(response.nack);
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
return function onMessage(event) {
|
|
@@ -67,7 +67,7 @@ export function createSandboxMessageRouter(options) {
|
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
69
|
const message = parsed.output;
|
|
70
|
-
if (!isInitHandshakeMessage(message) && getInitStatus() !== "
|
|
70
|
+
if (!isInitHandshakeMessage(message) && getInitStatus() !== "initialized") {
|
|
71
71
|
emit("warn", {
|
|
72
72
|
message: `Ignored ${message.type} before initialization completed`,
|
|
73
73
|
payload: message,
|
package/package.json
CHANGED
|
@@ -8,7 +8,11 @@ import type { CustomBlockManifest } from "@notionhq/custom-blocks-protocol/manif
|
|
|
8
8
|
import type { ContrastModeChangedMessage } from "@notionhq/custom-blocks-protocol/messages/contrastModeChanged.js"
|
|
9
9
|
import type { DataSourcesChangedMessage } from "@notionhq/custom-blocks-protocol/messages/dataSourcesChanged.js"
|
|
10
10
|
import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
|
|
11
|
-
import type {
|
|
11
|
+
import type {
|
|
12
|
+
CustomBlockInitErrorCode,
|
|
13
|
+
InitMessage,
|
|
14
|
+
} from "@notionhq/custom-blocks-protocol/messages/init.js"
|
|
15
|
+
import type { InvalidSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/invalidSandboxMessage.js"
|
|
12
16
|
import type { BridgeMessagePayload } from "@notionhq/custom-blocks-protocol/messages/messagePayload.js"
|
|
13
17
|
import type { PageChangedMessage } from "@notionhq/custom-blocks-protocol/messages/pageChanged.js"
|
|
14
18
|
import type { ParentChangedMessage } from "@notionhq/custom-blocks-protocol/messages/parentChanged.js"
|
|
@@ -45,6 +49,7 @@ import {
|
|
|
45
49
|
import type { CustomBlockHostQueryDataSourceResult } from "./queries/types.js"
|
|
46
50
|
import { validateQueryDataSourceFilter } from "./queries/validateQueryDataSourceFilter.js"
|
|
47
51
|
import { validateQueryDataSourceSorts } from "./queries/validateQueryDataSourceSorts.js"
|
|
52
|
+
import { unreachable } from "./utils.js"
|
|
48
53
|
|
|
49
54
|
// Bridge protocol version that introduced page: { type: "unavailable" }.
|
|
50
55
|
// Earlier SDKs cannot represent this context, so the host reports context_unavailable.
|
|
@@ -85,7 +90,6 @@ export type CustomBlockHostOptions = {
|
|
|
85
90
|
|
|
86
91
|
export type CustomBlockHostHandle = {
|
|
87
92
|
stop: () => void
|
|
88
|
-
post: (message: HostToSandboxMessage) => void
|
|
89
93
|
setTheme: (theme: NotionTheme) => void
|
|
90
94
|
setContrastMode: (contrastMode: NotionContrastMode) => void
|
|
91
95
|
setParent: (parent: NotionParent) => void
|
|
@@ -163,10 +167,20 @@ export function createCustomBlockHost(
|
|
|
163
167
|
onLog?.(direction, payload)
|
|
164
168
|
}
|
|
165
169
|
|
|
166
|
-
function
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
+
function postToSandbox(message: HostToSandboxMessage) {
|
|
171
|
+
const status = initializationController.status
|
|
172
|
+
switch (status) {
|
|
173
|
+
case "waitingForConnect":
|
|
174
|
+
case "waitingForInitResult":
|
|
175
|
+
case "initialized":
|
|
176
|
+
case "error":
|
|
177
|
+
break
|
|
178
|
+
// A stopped host must not send messages, including pending responses.
|
|
179
|
+
// Return from the function to skip delivery below.
|
|
180
|
+
case "stopped":
|
|
181
|
+
return
|
|
182
|
+
default:
|
|
183
|
+
return unreachable(status)
|
|
170
184
|
}
|
|
171
185
|
const sandboxMessage = normalizeHostToSandboxMessage(
|
|
172
186
|
message,
|
|
@@ -176,6 +190,36 @@ export function createCustomBlockHost(
|
|
|
176
190
|
iframe.contentWindow?.postMessage(sandboxMessage, sandboxOrigin)
|
|
177
191
|
}
|
|
178
192
|
|
|
193
|
+
function sendInitMessage(message: InitMessage) {
|
|
194
|
+
postToSandbox(message)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function sendNack(message: InvalidSandboxMessage) {
|
|
198
|
+
postToSandbox(message)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function sendInitializedMessage(
|
|
202
|
+
message: Exclude<
|
|
203
|
+
HostToSandboxMessage,
|
|
204
|
+
{ type: "init" | "invalidSandboxMessage" }
|
|
205
|
+
>,
|
|
206
|
+
) {
|
|
207
|
+
const status = initializationController.status
|
|
208
|
+
switch (status) {
|
|
209
|
+
case "initialized":
|
|
210
|
+
postToSandbox(message)
|
|
211
|
+
return
|
|
212
|
+
// Send state updates and handler results only after initialization succeeds.
|
|
213
|
+
case "waitingForConnect":
|
|
214
|
+
case "waitingForInitResult":
|
|
215
|
+
case "error":
|
|
216
|
+
case "stopped":
|
|
217
|
+
return
|
|
218
|
+
default:
|
|
219
|
+
unreachable(status)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
179
223
|
/**
|
|
180
224
|
* Invokes a one-shot handler and posts its result with the request's ID.
|
|
181
225
|
* Each result type is named `<request type>Result` by the bridge protocol.
|
|
@@ -192,8 +236,8 @@ export function createCustomBlockHost(
|
|
|
192
236
|
type: `${message.type}Result` as const,
|
|
193
237
|
requestId: message.requestId,
|
|
194
238
|
...response,
|
|
195
|
-
} as
|
|
196
|
-
|
|
239
|
+
} as ResultForRequest<OneShotRequest>
|
|
240
|
+
sendInitializedMessage(result)
|
|
197
241
|
}
|
|
198
242
|
|
|
199
243
|
function postQueryDataSourceResult(args: {
|
|
@@ -215,7 +259,7 @@ export function createCustomBlockHost(
|
|
|
215
259
|
items: args.response.items,
|
|
216
260
|
hasMore: args.response.hasMore,
|
|
217
261
|
}
|
|
218
|
-
|
|
262
|
+
sendInitializedMessage(message)
|
|
219
263
|
}
|
|
220
264
|
|
|
221
265
|
async function handleQuery(
|
|
@@ -249,36 +293,36 @@ export function createCustomBlockHost(
|
|
|
249
293
|
/**
|
|
250
294
|
* Sends any pending host state changes to the sandbox. It is possible for these to change
|
|
251
295
|
* between when the host sends `init` and when the sandbox responds with `initResult`. The host
|
|
252
|
-
* does send
|
|
296
|
+
* does not send pending state changes until the sandbox responds.
|
|
253
297
|
*/
|
|
254
298
|
function flushPendingHostStateChanges() {
|
|
255
299
|
if (pendingThemeChange) {
|
|
256
300
|
pendingThemeChange = false
|
|
257
|
-
|
|
301
|
+
sendInitializedMessage({ type: "themeChanged", theme })
|
|
258
302
|
}
|
|
259
303
|
if (pendingContrastModeChange) {
|
|
260
304
|
pendingContrastModeChange = false
|
|
261
|
-
|
|
305
|
+
sendInitializedMessage({ type: "contrastModeChanged", contrastMode })
|
|
262
306
|
}
|
|
263
307
|
if (pendingParentChange) {
|
|
264
308
|
pendingParentChange = false
|
|
265
|
-
|
|
309
|
+
sendInitializedMessage({ type: "parentChanged", parent })
|
|
266
310
|
}
|
|
267
311
|
if (pendingPageChange) {
|
|
268
312
|
pendingPageChange = false
|
|
269
|
-
|
|
313
|
+
sendInitializedMessage({ type: "pageChanged", page })
|
|
270
314
|
}
|
|
271
315
|
if (pendingDataSourcesChange !== undefined) {
|
|
272
316
|
const nextDataSources = pendingDataSourcesChange
|
|
273
317
|
pendingDataSourcesChange = undefined
|
|
274
|
-
|
|
318
|
+
sendInitializedMessage({
|
|
275
319
|
type: "dataSourcesChanged",
|
|
276
320
|
dataSources: nextDataSources,
|
|
277
321
|
})
|
|
278
322
|
}
|
|
279
323
|
if (pendingCurrentUserChange) {
|
|
280
324
|
pendingCurrentUserChange = false
|
|
281
|
-
|
|
325
|
+
sendInitializedMessage({ type: "currentUserChanged", currentUser })
|
|
282
326
|
}
|
|
283
327
|
}
|
|
284
328
|
|
|
@@ -330,7 +374,7 @@ export function createCustomBlockHost(
|
|
|
330
374
|
dataSources: resolvedDataSources,
|
|
331
375
|
}
|
|
332
376
|
},
|
|
333
|
-
|
|
377
|
+
sendInitMessage,
|
|
334
378
|
warn: payload => emit("warn", payload),
|
|
335
379
|
flushPendingHostStateChanges,
|
|
336
380
|
minBridgeProtocolVersion,
|
|
@@ -350,7 +394,7 @@ export function createCustomBlockHost(
|
|
|
350
394
|
sandboxOrigin,
|
|
351
395
|
getInitStatus: () => initializationController.status,
|
|
352
396
|
emit,
|
|
353
|
-
|
|
397
|
+
sendNack,
|
|
354
398
|
onInvalidConnect: initializationController.handleInvalidConnect,
|
|
355
399
|
onInvalidInitResult: initializationController.handleInvalidInitResult,
|
|
356
400
|
onConnect: initializationController.handleConnect,
|
|
@@ -385,21 +429,20 @@ export function createCustomBlockHost(
|
|
|
385
429
|
|
|
386
430
|
return {
|
|
387
431
|
stop,
|
|
388
|
-
post,
|
|
389
432
|
setTheme(nextTheme) {
|
|
390
433
|
theme = nextTheme
|
|
391
|
-
if (initializationController.status !== "
|
|
434
|
+
if (initializationController.status !== "initialized") {
|
|
392
435
|
if (initializationController.status === "waitingForInitResult") {
|
|
393
436
|
pendingThemeChange = true
|
|
394
437
|
}
|
|
395
438
|
return
|
|
396
439
|
}
|
|
397
440
|
const message: ThemeChangedMessage = { type: "themeChanged", theme }
|
|
398
|
-
|
|
441
|
+
sendInitializedMessage(message)
|
|
399
442
|
},
|
|
400
443
|
setContrastMode(nextContrastMode) {
|
|
401
444
|
contrastMode = nextContrastMode
|
|
402
|
-
if (initializationController.status !== "
|
|
445
|
+
if (initializationController.status !== "initialized") {
|
|
403
446
|
if (initializationController.status === "waitingForInitResult") {
|
|
404
447
|
pendingContrastModeChange = true
|
|
405
448
|
}
|
|
@@ -409,18 +452,18 @@ export function createCustomBlockHost(
|
|
|
409
452
|
type: "contrastModeChanged",
|
|
410
453
|
contrastMode,
|
|
411
454
|
}
|
|
412
|
-
|
|
455
|
+
sendInitializedMessage(message)
|
|
413
456
|
},
|
|
414
457
|
setParent(nextParent) {
|
|
415
458
|
parent = nextParent
|
|
416
|
-
if (initializationController.status !== "
|
|
459
|
+
if (initializationController.status !== "initialized") {
|
|
417
460
|
if (initializationController.status === "waitingForInitResult") {
|
|
418
461
|
pendingParentChange = true
|
|
419
462
|
}
|
|
420
463
|
return
|
|
421
464
|
}
|
|
422
465
|
const message: ParentChangedMessage = { type: "parentChanged", parent }
|
|
423
|
-
|
|
466
|
+
sendInitializedMessage(message)
|
|
424
467
|
},
|
|
425
468
|
setPage(nextPage) {
|
|
426
469
|
page = nextPage
|
|
@@ -429,19 +472,19 @@ export function createCustomBlockHost(
|
|
|
429
472
|
sandboxBridgeProtocolVersion <
|
|
430
473
|
BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE &&
|
|
431
474
|
(initializationController.status === "waitingForInitResult" ||
|
|
432
|
-
initializationController.status === "
|
|
475
|
+
initializationController.status === "initialized")
|
|
433
476
|
) {
|
|
434
477
|
stopWithFailure(initErrorForFailureReason("context_unavailable"))
|
|
435
478
|
return
|
|
436
479
|
}
|
|
437
|
-
if (initializationController.status !== "
|
|
480
|
+
if (initializationController.status !== "initialized") {
|
|
438
481
|
if (initializationController.status === "waitingForInitResult") {
|
|
439
482
|
pendingPageChange = true
|
|
440
483
|
}
|
|
441
484
|
return
|
|
442
485
|
}
|
|
443
486
|
const message: PageChangedMessage = { type: "pageChanged", page }
|
|
444
|
-
|
|
487
|
+
sendInitializedMessage(message)
|
|
445
488
|
},
|
|
446
489
|
setDataSources(nextDataSources) {
|
|
447
490
|
if (
|
|
@@ -457,7 +500,7 @@ export function createCustomBlockHost(
|
|
|
457
500
|
}
|
|
458
501
|
dataSources = nextDataSources
|
|
459
502
|
activeDataSources = nextDataSources
|
|
460
|
-
if (initializationController.status !== "
|
|
503
|
+
if (initializationController.status !== "initialized") {
|
|
461
504
|
if (initializationController.status === "waitingForInitResult") {
|
|
462
505
|
pendingDataSourcesChange = nextDataSources
|
|
463
506
|
}
|
|
@@ -467,20 +510,20 @@ export function createCustomBlockHost(
|
|
|
467
510
|
type: "dataSourcesChanged",
|
|
468
511
|
dataSources: nextDataSources,
|
|
469
512
|
}
|
|
470
|
-
|
|
513
|
+
sendInitializedMessage(message)
|
|
471
514
|
},
|
|
472
515
|
setCurrentUser(nextCurrentUser) {
|
|
473
516
|
currentUser = nextCurrentUser
|
|
474
|
-
if (initializationController.status !== "
|
|
517
|
+
if (initializationController.status !== "initialized") {
|
|
475
518
|
if (initializationController.status === "waitingForInitResult") {
|
|
476
519
|
pendingCurrentUserChange = true
|
|
477
520
|
}
|
|
478
521
|
return
|
|
479
522
|
}
|
|
480
|
-
|
|
523
|
+
sendInitializedMessage({ type: "currentUserChanged", currentUser })
|
|
481
524
|
},
|
|
482
525
|
refreshQuery({ dataSourceId }) {
|
|
483
|
-
if (initializationController.status !== "
|
|
526
|
+
if (initializationController.status !== "initialized") {
|
|
484
527
|
return
|
|
485
528
|
}
|
|
486
529
|
const refreshes = querySubscriptions.refresh(dataSourceId)
|
|
@@ -2,7 +2,6 @@ import type { NotionContrastMode } from "@notionhq/custom-blocks-protocol/contra
|
|
|
2
2
|
import type { NotionBlockId } from "@notionhq/custom-blocks-protocol/ids.js"
|
|
3
3
|
import type { CustomBlockManifest } from "@notionhq/custom-blocks-protocol/manifest.js"
|
|
4
4
|
import type { ConnectMessage } from "@notionhq/custom-blocks-protocol/messages/connect.js"
|
|
5
|
-
import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
|
|
6
5
|
import type {
|
|
7
6
|
CustomBlockInitErrorInfo,
|
|
8
7
|
InitMessage,
|
|
@@ -47,7 +46,7 @@ export type InitializationControllerOptions = {
|
|
|
47
46
|
blockId: NotionBlockId
|
|
48
47
|
getState: () => InitializationState
|
|
49
48
|
prepareInit: (connect: ConnectSuccessMessage) => InitializationPreparation
|
|
50
|
-
|
|
49
|
+
sendInitMessage: (message: InitMessage) => void
|
|
51
50
|
warn: (payload: unknown) => void
|
|
52
51
|
flushPendingHostStateChanges: () => void
|
|
53
52
|
minBridgeProtocolVersion: number
|
|
@@ -83,7 +82,7 @@ export function createInitializationController(
|
|
|
83
82
|
blockId,
|
|
84
83
|
getState,
|
|
85
84
|
prepareInit,
|
|
86
|
-
|
|
85
|
+
sendInitMessage,
|
|
87
86
|
warn,
|
|
88
87
|
flushPendingHostStateChanges,
|
|
89
88
|
minBridgeProtocolVersion,
|
|
@@ -126,7 +125,7 @@ export function createInitializationController(
|
|
|
126
125
|
initStatus = "error"
|
|
127
126
|
activeInitializationId = initializationId
|
|
128
127
|
clearNoConnectTimeout()
|
|
129
|
-
|
|
128
|
+
sendInitMessage({
|
|
130
129
|
type: "init",
|
|
131
130
|
initializationId,
|
|
132
131
|
status: "error",
|
|
@@ -135,11 +134,16 @@ export function createInitializationController(
|
|
|
135
134
|
onFailure?.(error)
|
|
136
135
|
}
|
|
137
136
|
|
|
138
|
-
function
|
|
137
|
+
function prepareAndSendInit(connect: ConnectSuccessMessage): boolean {
|
|
139
138
|
if (initStatus !== "waitingForConnect") {
|
|
140
139
|
return false
|
|
141
140
|
}
|
|
142
141
|
const preparation = prepareInit(connect)
|
|
142
|
+
// The embedder can stop the host when it receives the manifest.
|
|
143
|
+
// If that happens, do not continue initialization or start its timer.
|
|
144
|
+
if (initStatus !== "waitingForConnect") {
|
|
145
|
+
return false
|
|
146
|
+
}
|
|
143
147
|
if (preparation.status === "error") {
|
|
144
148
|
sendInitError({
|
|
145
149
|
initializationId: connect.initializationId,
|
|
@@ -164,7 +168,7 @@ export function createInitializationController(
|
|
|
164
168
|
dataSources: preparation.dataSources,
|
|
165
169
|
currentUser: state.currentUser,
|
|
166
170
|
}
|
|
167
|
-
|
|
171
|
+
sendInitMessage(message)
|
|
168
172
|
noInitResultTimeoutId = window.setTimeout(() => {
|
|
169
173
|
if (initStatus !== "waitingForInitResult") {
|
|
170
174
|
return
|
|
@@ -223,7 +227,7 @@ export function createInitializationController(
|
|
|
223
227
|
return
|
|
224
228
|
}
|
|
225
229
|
|
|
226
|
-
if (
|
|
230
|
+
if (prepareAndSendInit(message)) {
|
|
227
231
|
hasConnectedSandbox = true
|
|
228
232
|
}
|
|
229
233
|
}
|
|
@@ -324,7 +328,7 @@ export function createInitializationController(
|
|
|
324
328
|
}
|
|
325
329
|
clearNoInitResultTimeout()
|
|
326
330
|
if (message.status === "success") {
|
|
327
|
-
initStatus = "
|
|
331
|
+
initStatus = "initialized"
|
|
328
332
|
flushPendingHostStateChanges()
|
|
329
333
|
} else {
|
|
330
334
|
initStatus = "error"
|
package/src/lifecycle/types.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type InitStatus =
|
|
|
20
20
|
/** The host is waiting for a matching `initResult` after it sends `init`. */
|
|
21
21
|
| "waitingForInitResult"
|
|
22
22
|
/** The host is initialized and can send live updates. */
|
|
23
|
-
| "
|
|
23
|
+
| "initialized"
|
|
24
24
|
/** The host is in an error state after an initialization failure or timeout. */
|
|
25
25
|
| "error"
|
|
26
26
|
/** The host is stopped and no longer sends or accepts sandbox messages. */
|
|
@@ -2,8 +2,8 @@ import type { ConnectMessage } from "@notionhq/custom-blocks-protocol/messages/c
|
|
|
2
2
|
import type { CreatePageMessage } from "@notionhq/custom-blocks-protocol/messages/createPage.js"
|
|
3
3
|
import type { GetPageMessage } from "@notionhq/custom-blocks-protocol/messages/getPage.js"
|
|
4
4
|
import type { GetUserMessage } from "@notionhq/custom-blocks-protocol/messages/getUser.js"
|
|
5
|
-
import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
|
|
6
5
|
import type { InitResultMessage } from "@notionhq/custom-blocks-protocol/messages/initResult.js"
|
|
6
|
+
import type { InvalidSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/invalidSandboxMessage.js"
|
|
7
7
|
import { isInitHandshakeMessage } from "@notionhq/custom-blocks-protocol/messages/isInitHandshakeMessage.js"
|
|
8
8
|
import type { ListUsersMessage } from "@notionhq/custom-blocks-protocol/messages/listUsers.js"
|
|
9
9
|
import type { OpenPageMessage } from "@notionhq/custom-blocks-protocol/messages/openPage.js"
|
|
@@ -35,7 +35,7 @@ export type SandboxMessageRouterOptions = {
|
|
|
35
35
|
sandboxOrigin: string
|
|
36
36
|
getInitStatus: () => InitStatus
|
|
37
37
|
emit: (direction: CustomBlockHostLogDirection, payload: unknown) => void
|
|
38
|
-
|
|
38
|
+
sendNack: (message: InvalidSandboxMessage) => void
|
|
39
39
|
onInvalidConnect: InitializationController["handleInvalidConnect"]
|
|
40
40
|
onInvalidInitResult: InitializationController["handleInvalidInitResult"]
|
|
41
41
|
onConnect: (message: ConnectMessage) => void
|
|
@@ -61,7 +61,7 @@ export function createSandboxMessageRouter(
|
|
|
61
61
|
sandboxOrigin,
|
|
62
62
|
getInitStatus,
|
|
63
63
|
emit,
|
|
64
|
-
|
|
64
|
+
sendNack,
|
|
65
65
|
onInvalidConnect,
|
|
66
66
|
onInvalidInitResult,
|
|
67
67
|
onConnect,
|
|
@@ -116,9 +116,9 @@ export function createSandboxMessageRouter(
|
|
|
116
116
|
})
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
// Other messages get NACKed indicating they were invalid and skipped
|
|
120
|
-
if (response.nack !== undefined) {
|
|
121
|
-
|
|
119
|
+
// Other messages get NACKed indicating they were invalid and skipped.
|
|
120
|
+
if (response.nack !== undefined && getInitStatus() !== "stopped") {
|
|
121
|
+
sendNack(response.nack)
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
|
|
@@ -139,7 +139,7 @@ export function createSandboxMessageRouter(
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
const message = parsed.output
|
|
142
|
-
if (!isInitHandshakeMessage(message) && getInitStatus() !== "
|
|
142
|
+
if (!isInitHandshakeMessage(message) && getInitStatus() !== "initialized") {
|
|
143
143
|
emit("warn", {
|
|
144
144
|
message: `Ignored ${message.type} before initialization completed`,
|
|
145
145
|
payload: message,
|