@notionhq/custom-blocks-host 0.1.31 → 0.2.0
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 +39 -18
- package/dist/createCustomBlockHost.d.ts +8 -10
- package/dist/createCustomBlockHost.d.ts.map +1 -1
- package/dist/createCustomBlockHost.js +94 -150
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lifecycle/initErrors.d.ts.map +1 -1
- package/dist/lifecycle/initErrors.js +6 -0
- package/dist/lifecycle/initializationController.d.ts +3 -27
- package/dist/lifecycle/initializationController.d.ts.map +1 -1
- package/dist/lifecycle/initializationController.js +89 -46
- package/dist/lifecycle/types.d.ts +13 -7
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/protocol/messages/init.d.ts +1 -1
- package/package.json +1 -1
- package/src/createCustomBlockHost.ts +118 -172
- package/src/index.ts +2 -1
- package/src/lifecycle/initErrors.ts +6 -0
- package/src/lifecycle/initializationController.ts +97 -78
- package/src/lifecycle/types.ts +19 -10
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import { DEFAULT_CONTRAST_MODE } from "../protocol/contrast.js";
|
|
2
|
+
import { unreachable } from "../utils.js";
|
|
1
3
|
import { initErrorForFailureReason } from "./initErrors.js";
|
|
2
4
|
import { isValidBridgeProtocolVersion } from "./protocolVersion.js";
|
|
3
5
|
const DEFAULT_NO_CONNECT_TIMEOUT_MS = 5_000;
|
|
6
|
+
const DEFAULT_INITIAL_STATE_TIMEOUT_MS = 5_000;
|
|
4
7
|
const DEFAULT_NO_INIT_RESULT_TIMEOUT_MS = 5_000;
|
|
5
8
|
export function createInitializationController(options) {
|
|
6
|
-
const {
|
|
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, onFailure, } = options;
|
|
7
10
|
let initStatus = "waitingForConnect";
|
|
8
11
|
let noConnectTimeoutId;
|
|
12
|
+
let initialStateTimeoutId;
|
|
9
13
|
let noInitResultTimeoutId;
|
|
10
14
|
let activeInitializationId;
|
|
11
15
|
let hasObservedIframeLoad = false;
|
|
@@ -16,6 +20,12 @@ export function createInitializationController(options) {
|
|
|
16
20
|
noConnectTimeoutId = undefined;
|
|
17
21
|
}
|
|
18
22
|
}
|
|
23
|
+
function clearInitialStateTimeout() {
|
|
24
|
+
if (initialStateTimeoutId !== undefined) {
|
|
25
|
+
window.clearTimeout(initialStateTimeoutId);
|
|
26
|
+
initialStateTimeoutId = undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
19
29
|
function clearNoInitResultTimeout() {
|
|
20
30
|
if (noInitResultTimeoutId !== undefined) {
|
|
21
31
|
window.clearTimeout(noInitResultTimeoutId);
|
|
@@ -24,12 +34,22 @@ export function createInitializationController(options) {
|
|
|
24
34
|
}
|
|
25
35
|
function sendInitError(args) {
|
|
26
36
|
const { initializationId, error } = args;
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
switch (initStatus) {
|
|
38
|
+
case "waitingForConnect":
|
|
39
|
+
case "preparingInit":
|
|
40
|
+
break;
|
|
41
|
+
case "waitingForInitResult":
|
|
42
|
+
case "initialized":
|
|
43
|
+
case "error":
|
|
44
|
+
case "stopped":
|
|
45
|
+
return;
|
|
46
|
+
default:
|
|
47
|
+
return unreachable(initStatus);
|
|
29
48
|
}
|
|
30
49
|
initStatus = "error";
|
|
31
50
|
activeInitializationId = initializationId;
|
|
32
51
|
clearNoConnectTimeout();
|
|
52
|
+
clearInitialStateTimeout();
|
|
33
53
|
sendInitMessage({
|
|
34
54
|
type: "init",
|
|
35
55
|
initializationId,
|
|
@@ -38,54 +58,78 @@ export function createInitializationController(options) {
|
|
|
38
58
|
});
|
|
39
59
|
onFailure?.(error);
|
|
40
60
|
}
|
|
41
|
-
function prepareAndSendInit(connect) {
|
|
42
|
-
if (initStatus !== "waitingForConnect") {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
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.
|
|
61
|
+
async function prepareAndSendInit(connect) {
|
|
48
62
|
if (initStatus !== "waitingForConnect") {
|
|
49
|
-
return
|
|
63
|
+
return;
|
|
50
64
|
}
|
|
51
|
-
|
|
52
|
-
|
|
65
|
+
initStatus = "preparingInit";
|
|
66
|
+
activeInitializationId = connect.initializationId;
|
|
67
|
+
clearNoConnectTimeout();
|
|
68
|
+
initialStateTimeoutId = window.setTimeout(() => {
|
|
69
|
+
if (initStatus !== "preparingInit") {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const error = initErrorForFailureReason("initial_state_timeout");
|
|
73
|
+
warn({
|
|
74
|
+
...error,
|
|
53
75
|
initializationId: connect.initializationId,
|
|
54
|
-
error: preparation.error,
|
|
55
76
|
});
|
|
56
|
-
|
|
77
|
+
sendInitError({ initializationId: connect.initializationId, error });
|
|
78
|
+
}, initialStateTimeoutMs);
|
|
79
|
+
try {
|
|
80
|
+
const preparation = await prepareInit(connect);
|
|
81
|
+
if (initStatus !== "preparingInit") {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
clearInitialStateTimeout();
|
|
85
|
+
if (preparation.status === "error") {
|
|
86
|
+
sendInitError({
|
|
87
|
+
initializationId: connect.initializationId,
|
|
88
|
+
error: preparation.error,
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
initStatus = "waitingForInitResult";
|
|
93
|
+
const message = {
|
|
94
|
+
type: "init",
|
|
95
|
+
initializationId: connect.initializationId,
|
|
96
|
+
status: "success",
|
|
97
|
+
theme: preparation.initialState.theme,
|
|
98
|
+
contrastMode: preparation.initialState.contrastMode ?? DEFAULT_CONTRAST_MODE,
|
|
99
|
+
blockId: preparation.initialState.blockId,
|
|
100
|
+
parent: preparation.initialState.parent,
|
|
101
|
+
page: preparation.initialState.page,
|
|
102
|
+
currentUser: preparation.initialState.currentUser,
|
|
103
|
+
manifest: preparation.initialState.manifest,
|
|
104
|
+
dataSources: preparation.initialState.dataSources,
|
|
105
|
+
};
|
|
106
|
+
sendInitMessage(message);
|
|
107
|
+
noInitResultTimeoutId = window.setTimeout(() => {
|
|
108
|
+
if (initStatus !== "waitingForInitResult") {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
initStatus = "error";
|
|
112
|
+
const error = initErrorForFailureReason("no_init_result");
|
|
113
|
+
onFailure?.(error);
|
|
114
|
+
warn({
|
|
115
|
+
...error,
|
|
116
|
+
initializationId: connect.initializationId,
|
|
117
|
+
});
|
|
118
|
+
}, noInitResultTimeoutMs);
|
|
57
119
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
clearNoConnectTimeout();
|
|
61
|
-
const state = getState();
|
|
62
|
-
const message = {
|
|
63
|
-
type: "init",
|
|
64
|
-
initializationId: connect.initializationId,
|
|
65
|
-
status: "success",
|
|
66
|
-
theme: state.theme,
|
|
67
|
-
contrastMode: state.contrastMode,
|
|
68
|
-
blockId,
|
|
69
|
-
parent: state.parent,
|
|
70
|
-
page: state.page,
|
|
71
|
-
manifest: preparation.manifest,
|
|
72
|
-
dataSources: preparation.dataSources,
|
|
73
|
-
currentUser: state.currentUser,
|
|
74
|
-
};
|
|
75
|
-
sendInitMessage(message);
|
|
76
|
-
noInitResultTimeoutId = window.setTimeout(() => {
|
|
77
|
-
if (initStatus !== "waitingForInitResult") {
|
|
120
|
+
catch (error) {
|
|
121
|
+
if (initStatus !== "preparingInit") {
|
|
78
122
|
return;
|
|
79
123
|
}
|
|
80
|
-
initStatus = "error";
|
|
81
|
-
const error = initErrorForFailureReason("no_init_result");
|
|
82
124
|
warn({
|
|
83
|
-
message: "
|
|
125
|
+
message: "Failed to prepare custom block initialization",
|
|
126
|
+
error,
|
|
127
|
+
});
|
|
128
|
+
sendInitError({
|
|
84
129
|
initializationId: connect.initializationId,
|
|
130
|
+
error: initErrorForFailureReason("unknown_error"),
|
|
85
131
|
});
|
|
86
|
-
|
|
87
|
-
}, noInitResultTimeoutMs);
|
|
88
|
-
return true;
|
|
132
|
+
}
|
|
89
133
|
}
|
|
90
134
|
function handleConnect(message) {
|
|
91
135
|
if (initStatus === "stopped") {
|
|
@@ -124,9 +168,8 @@ export function createInitializationController(options) {
|
|
|
124
168
|
});
|
|
125
169
|
return;
|
|
126
170
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
171
|
+
hasConnectedSandbox = true;
|
|
172
|
+
void prepareAndSendInit(message);
|
|
130
173
|
}
|
|
131
174
|
function handleInvalidConnect(args) {
|
|
132
175
|
if (initStatus === "stopped") {
|
|
@@ -208,12 +251,11 @@ export function createInitializationController(options) {
|
|
|
208
251
|
return;
|
|
209
252
|
}
|
|
210
253
|
clearNoInitResultTimeout();
|
|
254
|
+
initStatus = message.status === "success" ? "initialized" : "error";
|
|
211
255
|
if (message.status === "success") {
|
|
212
|
-
initStatus = "initialized";
|
|
213
256
|
flushPendingHostStateChanges();
|
|
214
257
|
}
|
|
215
258
|
else {
|
|
216
|
-
initStatus = "error";
|
|
217
259
|
onFailure?.(message.error);
|
|
218
260
|
}
|
|
219
261
|
onInitResult?.(message);
|
|
@@ -221,6 +263,7 @@ export function createInitializationController(options) {
|
|
|
221
263
|
function stop() {
|
|
222
264
|
initStatus = "stopped";
|
|
223
265
|
clearNoConnectTimeout();
|
|
266
|
+
clearInitialStateTimeout();
|
|
224
267
|
clearNoInitResultTimeout();
|
|
225
268
|
}
|
|
226
269
|
return {
|
|
@@ -3,6 +3,7 @@ import type { NotionDataSourceBindings } from "../protocol/dataSources/dataSourc
|
|
|
3
3
|
import type { NotionBlockId } from "../protocol/ids.js";
|
|
4
4
|
import type { CustomBlockManifest } from "../protocol/manifest.js";
|
|
5
5
|
import type { ConnectMessage } from "../protocol/messages/connect.js";
|
|
6
|
+
import type { CustomBlockInitErrorInfo } from "../protocol/messages/init.js";
|
|
6
7
|
import type { CustomBlockPage } from "../protocol/pages/page.js";
|
|
7
8
|
import type { NotionParent } from "../protocol/parent.js";
|
|
8
9
|
import type { NotionTheme } from "../protocol/theme.js";
|
|
@@ -14,6 +15,8 @@ export type ConnectSuccessMessage = Extract<ConnectMessage, {
|
|
|
14
15
|
export type InitStatus =
|
|
15
16
|
/** The host is waiting for the sandbox's first `connect` message. */
|
|
16
17
|
"waitingForConnect"
|
|
18
|
+
/** The host is waiting for `getInitialState` to prepare `init`. */
|
|
19
|
+
| "preparingInit"
|
|
17
20
|
/** The host is waiting for a matching `initResult` after it sends `init`. */
|
|
18
21
|
| "waitingForInitResult"
|
|
19
22
|
/** The host is initialized and can send live updates. */
|
|
@@ -25,7 +28,6 @@ export type InitStatus =
|
|
|
25
28
|
export type CustomBlockHostDataSourcesPayload = {
|
|
26
29
|
bindings: NotionDataSourceBindings;
|
|
27
30
|
};
|
|
28
|
-
export type CustomBlockHostInitialDataSources = CustomBlockHostDataSourcesPayload | ((connect: ConnectSuccessMessage) => CustomBlockHostDataSourcesPayload);
|
|
29
31
|
export type CustomBlockHostInitialState = {
|
|
30
32
|
theme: NotionTheme;
|
|
31
33
|
/**
|
|
@@ -36,12 +38,16 @@ export type CustomBlockHostInitialState = {
|
|
|
36
38
|
blockId: NotionBlockId;
|
|
37
39
|
parent: NotionParent;
|
|
38
40
|
page: CustomBlockPage;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
* requires the sandbox to provide a manifest in `connect`.
|
|
42
|
-
*/
|
|
43
|
-
manifest?: CustomBlockManifest;
|
|
44
|
-
dataSources: CustomBlockHostInitialDataSources;
|
|
41
|
+
manifest: CustomBlockManifest;
|
|
42
|
+
dataSources: CustomBlockHostDataSourcesPayload;
|
|
45
43
|
currentUser: NotionUser;
|
|
46
44
|
};
|
|
45
|
+
export type CustomBlockHostInitialStateResult = {
|
|
46
|
+
status: "success";
|
|
47
|
+
initialState: CustomBlockHostInitialState;
|
|
48
|
+
} | {
|
|
49
|
+
status: "error";
|
|
50
|
+
error: CustomBlockInitErrorInfo;
|
|
51
|
+
};
|
|
52
|
+
export type CustomBlockHostGetInitialState = (connect: ConnectSuccessMessage) => Promise<CustomBlockHostInitialStateResult>;
|
|
47
53
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -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,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,
|
|
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,wBAAwB,EAAE,MAAM,mDAAmD,CAAA;AACjG,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,mEAAmE;GACjE,eAAe;AACjB,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,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,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,WAAW,EAAE,iCAAiC,CAAA;IAC9C,WAAW,EAAE,UAAU,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,iCAAiC,GAC1C;IACA,MAAM,EAAE,SAAS,CAAA;IACjB,YAAY,EAAE,2BAA2B,CAAA;CACxC,GACD;IACA,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,wBAAwB,CAAA;CAC9B,CAAA;AAEJ,MAAM,MAAM,8BAA8B,GAAG,CAC5C,OAAO,EAAE,qBAAqB,KAC1B,OAAO,CAAC,iCAAiC,CAAC,CAAA"}
|
|
@@ -2,7 +2,7 @@ import * as v from "valibot";
|
|
|
2
2
|
import type { CustomBlockErrorCode, CustomBlockErrorInfo } from "../errors.js";
|
|
3
3
|
import type { CustomBlockInitResultErrorCode } from "./initResult.js";
|
|
4
4
|
export declare const customBlockInitErrorCodeSchema: v.StringSchema<undefined>;
|
|
5
|
-
export type CustomBlockInitErrorCode = CustomBlockErrorCode<"manifest_unavailable" | "manifest_invalid" | "invalid_connect_payload" | "invalid_protocol_version" | "unsupported_protocol_version" | "init_timeout" | "context_unavailable" | "current_user_unavailable" | "missing_data_source_binding" | "data_source_unavailable" | "missing_property_binding" | "invalid_property_binding">;
|
|
5
|
+
export type CustomBlockInitErrorCode = CustomBlockErrorCode<"manifest_unavailable" | "manifest_invalid" | "invalid_connect_payload" | "invalid_protocol_version" | "unsupported_protocol_version" | "init_timeout" | "context_unavailable" | "current_user_unavailable" | "initial_state_timeout" | "missing_data_source_binding" | "data_source_unavailable" | "missing_property_binding" | "invalid_property_binding">;
|
|
6
6
|
export type CustomBlockInitErrorInfo = CustomBlockErrorInfo<CustomBlockInitErrorCode>;
|
|
7
7
|
/**
|
|
8
8
|
* Every initialization failure visible to block code. This combines errors
|