@notionhq/custom-blocks-host 0.1.30 → 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 -19
- package/dist/createCustomBlockHost.d.ts +8 -12
- package/dist/createCustomBlockHost.d.ts.map +1 -1
- package/dist/createCustomBlockHost.js +138 -162
- 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 +5 -30
- package/dist/lifecycle/initializationController.d.ts.map +1 -1
- package/dist/lifecycle/initializationController.js +90 -42
- package/dist/lifecycle/types.d.ts +14 -8
- 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/dist/protocol/messages/init.d.ts +1 -1
- package/package.json +1 -1
- package/src/createCustomBlockHost.ts +176 -187
- package/src/index.ts +2 -1
- package/src/lifecycle/initErrors.ts +6 -0
- package/src/lifecycle/initializationController.ts +100 -77
- package/src/lifecycle/types.ts +20 -11
- package/src/messages/sandboxMessageRouter.ts +7 -7
package/README.md
CHANGED
|
@@ -47,16 +47,20 @@ import { createCustomBlockHost } from "@notionhq/custom-blocks-host"
|
|
|
47
47
|
const host = createCustomBlockHost({
|
|
48
48
|
iframe,
|
|
49
49
|
sandboxOrigin: "https://example.com",
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
50
|
+
minBridgeProtocolVersion: 3,
|
|
51
|
+
getInitialState: async () => ({
|
|
52
|
+
status: "success",
|
|
53
|
+
initialState: {
|
|
54
|
+
theme,
|
|
55
|
+
contrastMode,
|
|
56
|
+
blockId,
|
|
57
|
+
parent,
|
|
58
|
+
page,
|
|
59
|
+
currentUser,
|
|
60
|
+
manifest,
|
|
61
|
+
dataSources: { bindings },
|
|
62
|
+
},
|
|
63
|
+
}),
|
|
60
64
|
handlers: {
|
|
61
65
|
queryDataSource: async message => ({
|
|
62
66
|
status: "success",
|
|
@@ -113,7 +117,26 @@ initial page fails initialization with `context_unavailable`. If page context be
|
|
|
113
117
|
after initialization, the host stops and reports the same error instead of sending an unsupported
|
|
114
118
|
`pageChanged` message.
|
|
115
119
|
|
|
116
|
-
|
|
120
|
+
`getInitialState` runs after the sandbox sends `connect`. It returns a promise for the initial state
|
|
121
|
+
result. The host listens immediately and sends `init` only after `getInitialState` resolves. Await
|
|
122
|
+
record and binding loads inside `getInitialState`.
|
|
123
|
+
|
|
124
|
+
`getInitialState` selects the manifest that the host uses. The host uses this manifest even when
|
|
125
|
+
`connect` supplies another. Production hosts can select persisted definitions. Preview hosts can
|
|
126
|
+
select sandbox manifests. A successful result must include a manifest. If no manifest is available,
|
|
127
|
+
return `manifest_unavailable`.
|
|
128
|
+
|
|
129
|
+
`getInitialState` can return `{ status: "error", error }` to stop initialization. The host converts
|
|
130
|
+
a thrown error or rejected promise to `unknown_error`. If preparation exceeds
|
|
131
|
+
`initialStateTimeoutMs`, the host reports `initial_state_timeout` and stops the handshake.
|
|
132
|
+
|
|
133
|
+
`no_connect` means the host did not receive `connect`. `no_init_result` means the sandbox did not
|
|
134
|
+
acknowledge `init`. The SDK waits for the host without its own timeout.
|
|
135
|
+
|
|
136
|
+
The host discards `getInitialState` results after a timeout or stop, including a stop caused by an
|
|
137
|
+
iframe reload.
|
|
138
|
+
|
|
139
|
+
- `onManifest` provides the selected manifest when `getInitialState` resolves successfully, before
|
|
117
140
|
binding validation and `onInitialized`. Use it to configure bindings even when initialization
|
|
118
141
|
cannot yet succeed.
|
|
119
142
|
|
|
@@ -122,9 +145,6 @@ after initialization, the host stops and reports the same error instead of sendi
|
|
|
122
145
|
- `onFailure` reports a handshake failure, timeout, unavailable page context, or iframe reload. It
|
|
123
146
|
can run after initialization succeeds.
|
|
124
147
|
|
|
125
|
-
The host uses the manifest from `connect`, or `initialState.manifest` if `connect` omits it. If
|
|
126
|
-
neither exists, initialization fails with `manifest_unavailable`.
|
|
127
|
-
|
|
128
148
|
`queryDataSource` is required. Other request handlers are optional. Every request handler may return
|
|
129
149
|
its result synchronously or as a promise. Results use a `status` discriminator: successful results
|
|
130
150
|
carry their response payload, while errors carry `{ code, message, isRetryable }`.
|
|
@@ -157,12 +177,13 @@ The runtime validates option names, supported sort types, and the limit of ten u
|
|
|
157
177
|
properties before it invokes the handler. Sorts retain their array order. Extra fields and mixed
|
|
158
178
|
filter types fail protocol validation.
|
|
159
179
|
|
|
160
|
-
|
|
161
|
-
|
|
180
|
+
Derive bindings from the successful `connect` message inside `getInitialState` and return them in
|
|
181
|
+
`initialState.dataSources`.
|
|
162
182
|
|
|
163
183
|
Set `sandboxOrigin` to the iframe's exact origin. The runtime targets outbound messages to that
|
|
164
|
-
origin and accepts inbound messages only from that iframe and origin. The runtime
|
|
165
|
-
`noConnectTimeoutMs` and `noInitResultTimeoutMs`;
|
|
184
|
+
origin and accepts inbound messages only from that iframe and origin. The runtime accepts
|
|
185
|
+
`noConnectTimeoutMs`, `initialStateTimeoutMs`, and `noInitResultTimeoutMs`; all default to five
|
|
186
|
+
seconds.
|
|
166
187
|
|
|
167
188
|
### Initialization and malformed messages
|
|
168
189
|
|
|
@@ -215,7 +236,6 @@ The returned handle exposes:
|
|
|
215
236
|
- `setDataSources()`
|
|
216
237
|
- `setCurrentUser()`
|
|
217
238
|
- `refreshQuery()`
|
|
218
|
-
- `post()`
|
|
219
239
|
- `stop()`
|
|
220
240
|
|
|
221
241
|
State updates are sent after `initResult.success`. Updates made earlier are held until
|
|
@@ -1,30 +1,25 @@
|
|
|
1
|
-
import {
|
|
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";
|
|
8
7
|
import type { NotionUser } from "./protocol/users/user.js";
|
|
9
|
-
import type { CustomBlockHostDataSourcesPayload,
|
|
8
|
+
import type { CustomBlockHostDataSourcesPayload, CustomBlockHostGetInitialState } from "./lifecycle/types.js";
|
|
10
9
|
import type { CustomBlockHostFailure, CustomBlockHostHandlers, CustomBlockHostLogDirection } from "./messages/types.js";
|
|
11
10
|
export type CustomBlockHostOptions = {
|
|
12
11
|
iframe: HTMLIFrameElement;
|
|
13
12
|
sandboxOrigin: string;
|
|
14
13
|
/**
|
|
15
|
-
* Minimum sandbox protocol version to accept. Defaults to the host runtime
|
|
16
|
-
* values retire older SDKs; lower values cannot enable
|
|
17
|
-
* supports.
|
|
14
|
+
* Minimum sandbox protocol version to accept. Defaults to the host runtime
|
|
15
|
+
* minimum. Higher values retire older SDKs; lower values cannot enable
|
|
16
|
+
* protocol versions the runtime no longer supports.
|
|
18
17
|
*/
|
|
19
18
|
minBridgeProtocolVersion?: number;
|
|
20
19
|
noConnectTimeoutMs?: number;
|
|
21
20
|
noInitResultTimeoutMs?: number;
|
|
22
|
-
initialState: CustomBlockHostInitialState;
|
|
23
21
|
handlers: CustomBlockHostHandlers;
|
|
24
|
-
/**
|
|
25
|
-
* Called as soon as the `manifest` is available after `connect`, before `onInitialized` and
|
|
26
|
-
* binding validation.
|
|
27
|
-
*/
|
|
22
|
+
/** Called as soon as the `manifest` is available after `connect`, before `onInitialized` and binding validation. */
|
|
28
23
|
onManifest?: (manifest: CustomBlockManifest) => void;
|
|
29
24
|
/** Callback fired when the initialization handshake with the sandbox completed successfully. */
|
|
30
25
|
onInitialized?: (state: {
|
|
@@ -39,10 +34,11 @@ export type CustomBlockHostOptions = {
|
|
|
39
34
|
*/
|
|
40
35
|
onFailure?: (error: CustomBlockHostFailure) => void;
|
|
41
36
|
onLog?: (direction: CustomBlockHostLogDirection, payload: unknown) => void;
|
|
37
|
+
getInitialState: CustomBlockHostGetInitialState;
|
|
38
|
+
initialStateTimeoutMs?: number;
|
|
42
39
|
};
|
|
43
40
|
export type CustomBlockHostHandle = {
|
|
44
41
|
stop: () => void;
|
|
45
|
-
post: (message: HostToSandboxMessage) => void;
|
|
46
42
|
setTheme: (theme: NotionTheme) => void;
|
|
47
43
|
setContrastMode: (contrastMode: NotionContrastMode) => void;
|
|
48
44
|
setParent: (parent: NotionParent) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../src/createCustomBlockHost.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../src/createCustomBlockHost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAA;AAEtF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AACjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AAcvF,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,8BAA8B,EAG9B,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,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,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;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,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,CA8WvB"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { DEFAULT_CONTRAST_MODE, } from "./protocol/contrast.js";
|
|
2
1
|
import { LATEST_BRIDGE_PROTOCOL_VERSION } from "./protocol/protocolVersion.js";
|
|
3
2
|
import { initErrorForFailureReason } from "./lifecycle/initErrors.js";
|
|
4
3
|
import { createInitializationController } from "./lifecycle/initializationController.js";
|
|
@@ -8,43 +7,68 @@ import { MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION } from "./protocolVersion.js
|
|
|
8
7
|
import { QuerySubscriptions, } from "./queries/querySubscriptions.js";
|
|
9
8
|
import { validateQueryDataSourceFilter } from "./queries/validateQueryDataSourceFilter.js";
|
|
10
9
|
import { validateQueryDataSourceSorts } from "./queries/validateQueryDataSourceSorts.js";
|
|
10
|
+
import { unreachable } from "./utils.js";
|
|
11
11
|
// Bridge protocol version that introduced page: { type: "unavailable" }.
|
|
12
12
|
// Earlier SDKs cannot represent this context, so the host reports context_unavailable.
|
|
13
13
|
const BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE = 5;
|
|
14
14
|
export function createCustomBlockHost(options) {
|
|
15
|
-
const { iframe,
|
|
16
|
-
// Callers can retire older SDKs, but cannot restore support for protocols
|
|
17
|
-
// implements. Omission uses the runtime minimum.
|
|
15
|
+
const { iframe, getInitialState, handlers, onManifest, onInitialized, onFailure, onLog, sandboxOrigin, minBridgeProtocolVersion: configuredMinBridgeProtocolVersion, noConnectTimeoutMs, initialStateTimeoutMs, noInitResultTimeoutMs, } = options;
|
|
16
|
+
// Callers can retire older SDKs, but cannot restore support for protocols
|
|
17
|
+
// the runtime no longer implements. Omission uses the runtime minimum.
|
|
18
18
|
const minBridgeProtocolVersion = Math.max(configuredMinBridgeProtocolVersion ??
|
|
19
19
|
MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION, MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION);
|
|
20
|
-
let
|
|
21
|
-
let
|
|
22
|
-
let
|
|
23
|
-
let page = initialState.page;
|
|
24
|
-
let dataSources = initialState.dataSources;
|
|
20
|
+
let pendingState = {};
|
|
21
|
+
let hasPreparedInitialState = false;
|
|
22
|
+
let manifest;
|
|
25
23
|
let activeDataSources;
|
|
26
24
|
let sandboxBridgeProtocolVersion = LATEST_BRIDGE_PROTOCOL_VERSION;
|
|
27
|
-
let manifest = initialState.manifest;
|
|
28
|
-
let currentUser = initialState.currentUser;
|
|
29
|
-
let pendingThemeChange = false;
|
|
30
|
-
let pendingContrastModeChange = false;
|
|
31
|
-
let pendingParentChange = false;
|
|
32
|
-
let pendingPageChange = false;
|
|
33
|
-
let pendingDataSourcesChange;
|
|
34
|
-
let pendingCurrentUserChange = false;
|
|
35
25
|
const querySubscriptions = new QuerySubscriptions();
|
|
36
26
|
function emit(direction, payload) {
|
|
37
27
|
onLog?.(direction, payload);
|
|
38
28
|
}
|
|
39
|
-
function
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
function postToSandbox(message) {
|
|
30
|
+
const status = initializationController.status;
|
|
31
|
+
switch (status) {
|
|
32
|
+
case "waitingForConnect":
|
|
33
|
+
case "preparingInit":
|
|
34
|
+
case "waitingForInitResult":
|
|
35
|
+
case "initialized":
|
|
36
|
+
case "error":
|
|
37
|
+
break;
|
|
38
|
+
// A stopped host must not send messages, including pending responses.
|
|
39
|
+
// Return from the function to skip delivery below.
|
|
40
|
+
case "stopped":
|
|
41
|
+
return;
|
|
42
|
+
default:
|
|
43
|
+
return unreachable(status);
|
|
43
44
|
}
|
|
44
45
|
const sandboxMessage = normalizeHostToSandboxMessage(message, sandboxBridgeProtocolVersion);
|
|
45
46
|
emit("out", sandboxMessage);
|
|
46
47
|
iframe.contentWindow?.postMessage(sandboxMessage, sandboxOrigin);
|
|
47
48
|
}
|
|
49
|
+
function sendInitMessage(message) {
|
|
50
|
+
postToSandbox(message);
|
|
51
|
+
}
|
|
52
|
+
function sendNack(message) {
|
|
53
|
+
postToSandbox(message);
|
|
54
|
+
}
|
|
55
|
+
function sendInitializedMessage(message) {
|
|
56
|
+
const status = initializationController.status;
|
|
57
|
+
switch (status) {
|
|
58
|
+
case "initialized":
|
|
59
|
+
postToSandbox(message);
|
|
60
|
+
return;
|
|
61
|
+
// Send state updates and handler results only after initialization succeeds.
|
|
62
|
+
case "waitingForConnect":
|
|
63
|
+
case "preparingInit":
|
|
64
|
+
case "waitingForInitResult":
|
|
65
|
+
case "error":
|
|
66
|
+
case "stopped":
|
|
67
|
+
return;
|
|
68
|
+
default:
|
|
69
|
+
unreachable(status);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
48
72
|
/**
|
|
49
73
|
* Invokes a one-shot handler and posts its result with the request's ID.
|
|
50
74
|
* Each result type is named `<request type>Result` by the bridge protocol.
|
|
@@ -59,7 +83,7 @@ export function createCustomBlockHost(options) {
|
|
|
59
83
|
requestId: message.requestId,
|
|
60
84
|
...response,
|
|
61
85
|
};
|
|
62
|
-
|
|
86
|
+
sendInitializedMessage(result);
|
|
63
87
|
}
|
|
64
88
|
function postQueryDataSourceResult(args) {
|
|
65
89
|
const message = args.response.status === "error"
|
|
@@ -76,7 +100,7 @@ export function createCustomBlockHost(options) {
|
|
|
76
100
|
items: args.response.items,
|
|
77
101
|
hasMore: args.response.hasMore,
|
|
78
102
|
};
|
|
79
|
-
|
|
103
|
+
sendInitializedMessage(message);
|
|
80
104
|
}
|
|
81
105
|
async function handleQuery(message, token = querySubscriptions.subscribe(message)) {
|
|
82
106
|
const collectionSchema = getCollectionSchema(activeDataSources, message.dataSourceId);
|
|
@@ -99,88 +123,65 @@ export function createCustomBlockHost(options) {
|
|
|
99
123
|
response,
|
|
100
124
|
});
|
|
101
125
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
126
|
+
function updateHostState(update) {
|
|
127
|
+
if (initializationController.status === "stopped" ||
|
|
128
|
+
initializationController.status === "error") {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
Object.assign(pendingState, update);
|
|
132
|
+
flushPendingHostStateChanges();
|
|
133
|
+
}
|
|
107
134
|
function flushPendingHostStateChanges() {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
135
|
+
// Keep updates queued until the sandbox acknowledges initialization.
|
|
136
|
+
// Updates received before init is sent are included in its payload instead.
|
|
137
|
+
if (initializationController.status !== "initialized") {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const changes = pendingState;
|
|
141
|
+
pendingState = {};
|
|
142
|
+
if (changes.theme !== undefined) {
|
|
143
|
+
sendInitializedMessage({ type: "themeChanged", theme: changes.theme });
|
|
111
144
|
}
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
|
|
145
|
+
if (changes.contrastMode !== undefined) {
|
|
146
|
+
sendInitializedMessage({
|
|
147
|
+
type: "contrastModeChanged",
|
|
148
|
+
contrastMode: changes.contrastMode,
|
|
149
|
+
});
|
|
115
150
|
}
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
post({ type: "parentChanged", parent });
|
|
151
|
+
if (changes.parent !== undefined) {
|
|
152
|
+
sendInitializedMessage({ type: "parentChanged", parent: changes.parent });
|
|
119
153
|
}
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
post({ type: "pageChanged", page });
|
|
154
|
+
if (changes.page !== undefined) {
|
|
155
|
+
sendInitializedMessage({ type: "pageChanged", page: changes.page });
|
|
123
156
|
}
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
pendingDataSourcesChange = undefined;
|
|
127
|
-
post({
|
|
157
|
+
if (changes.dataSources !== undefined) {
|
|
158
|
+
sendInitializedMessage({
|
|
128
159
|
type: "dataSourcesChanged",
|
|
129
|
-
dataSources:
|
|
160
|
+
dataSources: changes.dataSources,
|
|
130
161
|
});
|
|
131
162
|
}
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
|
|
163
|
+
if (changes.currentUser !== undefined) {
|
|
164
|
+
sendInitializedMessage({
|
|
165
|
+
type: "currentUserChanged",
|
|
166
|
+
currentUser: changes.currentUser,
|
|
167
|
+
});
|
|
135
168
|
}
|
|
136
169
|
}
|
|
137
170
|
const initializationController = createInitializationController({
|
|
138
|
-
|
|
139
|
-
getState: () => ({
|
|
140
|
-
theme,
|
|
141
|
-
contrastMode,
|
|
142
|
-
parent,
|
|
143
|
-
page,
|
|
144
|
-
currentUser,
|
|
145
|
-
}),
|
|
146
|
-
prepareInit(connect) {
|
|
171
|
+
async prepareInit(connect) {
|
|
147
172
|
sandboxBridgeProtocolVersion = connect.bridgeProtocolVersion;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
status: "error",
|
|
153
|
-
error: initErrorForFailureReason("context_unavailable"),
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
manifest = connect.manifest ?? initialState.manifest;
|
|
157
|
-
if (manifest === undefined) {
|
|
158
|
-
return {
|
|
159
|
-
status: "error",
|
|
160
|
-
error: initErrorForFailureReason("manifest_unavailable"),
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
onManifest?.(manifest);
|
|
164
|
-
const resolvedDataSources = typeof dataSources === "function" ? dataSources(connect) : dataSources;
|
|
165
|
-
const bindingErrorCode = getBindingErrorCode(manifest, resolvedDataSources);
|
|
166
|
-
if (bindingErrorCode !== undefined) {
|
|
167
|
-
return {
|
|
168
|
-
status: "error",
|
|
169
|
-
error: initErrorForFailureReason(bindingErrorCode),
|
|
170
|
-
};
|
|
173
|
+
const result = await getInitialState(connect);
|
|
174
|
+
if (initializationController.status !== "preparingInit") {
|
|
175
|
+
return result;
|
|
171
176
|
}
|
|
172
|
-
|
|
173
|
-
return {
|
|
174
|
-
status: "success",
|
|
175
|
-
manifest,
|
|
176
|
-
dataSources: resolvedDataSources,
|
|
177
|
-
};
|
|
177
|
+
return prepareInitialState(result);
|
|
178
178
|
},
|
|
179
|
-
|
|
179
|
+
sendInitMessage,
|
|
180
180
|
warn: payload => emit("warn", payload),
|
|
181
181
|
flushPendingHostStateChanges,
|
|
182
182
|
minBridgeProtocolVersion,
|
|
183
183
|
noConnectTimeoutMs,
|
|
184
|
+
initialStateTimeoutMs,
|
|
184
185
|
noInitResultTimeoutMs,
|
|
185
186
|
stopHostWithFailure: stopWithFailure,
|
|
186
187
|
onInitResult(message) {
|
|
@@ -190,12 +191,39 @@ export function createCustomBlockHost(options) {
|
|
|
190
191
|
},
|
|
191
192
|
onFailure,
|
|
192
193
|
});
|
|
194
|
+
function prepareInitialState(result) {
|
|
195
|
+
if (result.status === "error") {
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
198
|
+
manifest = result.initialState.manifest;
|
|
199
|
+
onManifest?.(manifest);
|
|
200
|
+
const initialState = { ...result.initialState, ...pendingState };
|
|
201
|
+
pendingState = {};
|
|
202
|
+
if (sandboxBridgeProtocolVersion <
|
|
203
|
+
BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE &&
|
|
204
|
+
initialState.page.type === "unavailable") {
|
|
205
|
+
return {
|
|
206
|
+
status: "error",
|
|
207
|
+
error: initErrorForFailureReason("context_unavailable"),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const bindingErrorCode = getBindingErrorCode(initialState.manifest, initialState.dataSources);
|
|
211
|
+
if (bindingErrorCode !== undefined) {
|
|
212
|
+
return {
|
|
213
|
+
status: "error",
|
|
214
|
+
error: initErrorForFailureReason(bindingErrorCode),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
activeDataSources = initialState.dataSources;
|
|
218
|
+
hasPreparedInitialState = true;
|
|
219
|
+
return { status: "success", initialState };
|
|
220
|
+
}
|
|
193
221
|
const onMessage = createSandboxMessageRouter({
|
|
194
222
|
iframe,
|
|
195
223
|
sandboxOrigin,
|
|
196
224
|
getInitStatus: () => initializationController.status,
|
|
197
225
|
emit,
|
|
198
|
-
|
|
226
|
+
sendNack,
|
|
199
227
|
onInvalidConnect: initializationController.handleInvalidConnect,
|
|
200
228
|
onInvalidInitResult: initializationController.handleInvalidInitResult,
|
|
201
229
|
onConnect: initializationController.handleConnect,
|
|
@@ -225,98 +253,46 @@ export function createCustomBlockHost(options) {
|
|
|
225
253
|
}
|
|
226
254
|
return {
|
|
227
255
|
stop,
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
theme = nextTheme;
|
|
231
|
-
if (initializationController.status !== "success") {
|
|
232
|
-
if (initializationController.status === "waitingForInitResult") {
|
|
233
|
-
pendingThemeChange = true;
|
|
234
|
-
}
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
const message = { type: "themeChanged", theme };
|
|
238
|
-
post(message);
|
|
256
|
+
setTheme(theme) {
|
|
257
|
+
updateHostState({ theme });
|
|
239
258
|
},
|
|
240
|
-
setContrastMode(
|
|
241
|
-
contrastMode
|
|
242
|
-
if (initializationController.status !== "success") {
|
|
243
|
-
if (initializationController.status === "waitingForInitResult") {
|
|
244
|
-
pendingContrastModeChange = true;
|
|
245
|
-
}
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
const message = {
|
|
249
|
-
type: "contrastModeChanged",
|
|
250
|
-
contrastMode,
|
|
251
|
-
};
|
|
252
|
-
post(message);
|
|
259
|
+
setContrastMode(contrastMode) {
|
|
260
|
+
updateHostState({ contrastMode });
|
|
253
261
|
},
|
|
254
|
-
setParent(
|
|
255
|
-
parent
|
|
256
|
-
if (initializationController.status !== "success") {
|
|
257
|
-
if (initializationController.status === "waitingForInitResult") {
|
|
258
|
-
pendingParentChange = true;
|
|
259
|
-
}
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
const message = { type: "parentChanged", parent };
|
|
263
|
-
post(message);
|
|
262
|
+
setParent(parent) {
|
|
263
|
+
updateHostState({ parent });
|
|
264
264
|
},
|
|
265
|
-
setPage(
|
|
266
|
-
page
|
|
267
|
-
if (nextPage.type === "unavailable" &&
|
|
265
|
+
setPage(page) {
|
|
266
|
+
if (page.type === "unavailable" &&
|
|
268
267
|
sandboxBridgeProtocolVersion <
|
|
269
268
|
BRIDGE_PROTOCOL_VERSION_INTRODUCING_UNAVAILABLE_PAGE &&
|
|
270
|
-
(initializationController.status === "
|
|
271
|
-
|
|
269
|
+
((initializationController.status === "preparingInit" &&
|
|
270
|
+
hasPreparedInitialState) ||
|
|
271
|
+
initializationController.status === "waitingForInitResult" ||
|
|
272
|
+
initializationController.status === "initialized")) {
|
|
272
273
|
stopWithFailure(initErrorForFailureReason("context_unavailable"));
|
|
273
274
|
return;
|
|
274
275
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
const message = { type: "pageChanged", page };
|
|
282
|
-
post(message);
|
|
276
|
+
updateHostState({ page });
|
|
277
|
+
},
|
|
278
|
+
setCurrentUser(currentUser) {
|
|
279
|
+
updateHostState({ currentUser });
|
|
283
280
|
},
|
|
284
|
-
setDataSources(
|
|
281
|
+
setDataSources(dataSources) {
|
|
285
282
|
if (initializationController.hasConnectedSandbox &&
|
|
286
283
|
manifest !== undefined &&
|
|
287
|
-
getBindingErrorCode(manifest,
|
|
284
|
+
getBindingErrorCode(manifest, dataSources) !== undefined) {
|
|
288
285
|
emit("warn", {
|
|
289
286
|
message: "Ignored invalid data source bindings update",
|
|
290
|
-
payload:
|
|
287
|
+
payload: dataSources,
|
|
291
288
|
});
|
|
292
289
|
return;
|
|
293
290
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
if (initializationController.status !== "success") {
|
|
297
|
-
if (initializationController.status === "waitingForInitResult") {
|
|
298
|
-
pendingDataSourcesChange = nextDataSources;
|
|
299
|
-
}
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
const message = {
|
|
303
|
-
type: "dataSourcesChanged",
|
|
304
|
-
dataSources: nextDataSources,
|
|
305
|
-
};
|
|
306
|
-
post(message);
|
|
307
|
-
},
|
|
308
|
-
setCurrentUser(nextCurrentUser) {
|
|
309
|
-
currentUser = nextCurrentUser;
|
|
310
|
-
if (initializationController.status !== "success") {
|
|
311
|
-
if (initializationController.status === "waitingForInitResult") {
|
|
312
|
-
pendingCurrentUserChange = true;
|
|
313
|
-
}
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
post({ type: "currentUserChanged", currentUser });
|
|
291
|
+
activeDataSources = dataSources;
|
|
292
|
+
updateHostState({ dataSources });
|
|
317
293
|
},
|
|
318
294
|
refreshQuery({ dataSourceId }) {
|
|
319
|
-
if (initializationController.status !== "
|
|
295
|
+
if (initializationController.status !== "initialized") {
|
|
320
296
|
return;
|
|
321
297
|
}
|
|
322
298
|
const refreshes = querySubscriptions.refresh(dataSourceId);
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type { UpdatePageMessage } from "./protocol/messages/updatePage.js";
|
|
|
9
9
|
export type { NotionPagePropertyValue, NotionPagePropertyWriteMap, NotionRichTextItem, } from "./protocol/pages/page.js";
|
|
10
10
|
export { type AutoBindableProperty, getAutoBoundPropertyIdsByKey, } from "./autoBindProperties.js";
|
|
11
11
|
export { type CustomBlockHostHandle, type CustomBlockHostOptions, createCustomBlockHost, } from "./createCustomBlockHost.js";
|
|
12
|
-
export type { ConnectSuccessMessage, CustomBlockHostDataSourcesPayload,
|
|
12
|
+
export type { ConnectSuccessMessage, CustomBlockHostDataSourcesPayload, CustomBlockHostGetInitialState, CustomBlockHostInitialState, CustomBlockHostInitialStateResult, } from "./lifecycle/types.js";
|
|
13
13
|
export type { CreatePageMessageInput, CustomBlockHostCreatePageResult, CustomBlockHostFailure, CustomBlockHostGetPageResult, CustomBlockHostGetUserResult, CustomBlockHostHandlers, CustomBlockHostListUsersResult, CustomBlockHostLogDirection, CustomBlockHostOpenPageResult, CustomBlockHostUpdatePageResult, GetPageMessageInput, GetUserMessageInput, ListUsersMessageInput, OpenPageMessageInput, QueryDataSourceMessageInput, ResizeMessageInput, UpdatePageMessageInput, } from "./messages/types.js";
|
|
14
14
|
export type { CustomBlockHostQueryDataSourceResult } from "./queries/types.js";
|
|
15
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,YAAY,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,YAAY,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,YAAY,EAAE,gBAAgB,EAAE,MAAM,wDAAwD,CAAA;AAC9F,YAAY,EAAE,eAAe,EAAE,MAAM,uDAAuD,CAAA;AAC5F,YAAY,EAAE,sBAAsB,EAAE,MAAM,8DAA8D,CAAA;AAC1G,YAAY,EAAE,aAAa,EAAE,MAAM,qDAAqD,CAAA;AACxF,YAAY,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,YAAY,EACX,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,GAClB,MAAM,gDAAgD,CAAA;AACvD,OAAO,EACN,KAAK,oBAAoB,EACzB,4BAA4B,GAC5B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,qBAAqB,GACrB,MAAM,4BAA4B,CAAA;AACnC,YAAY,EACX,qBAAqB,EACrB,iCAAiC,EACjC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,YAAY,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,YAAY,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,YAAY,EAAE,gBAAgB,EAAE,MAAM,wDAAwD,CAAA;AAC9F,YAAY,EAAE,eAAe,EAAE,MAAM,uDAAuD,CAAA;AAC5F,YAAY,EAAE,sBAAsB,EAAE,MAAM,8DAA8D,CAAA;AAC1G,YAAY,EAAE,aAAa,EAAE,MAAM,qDAAqD,CAAA;AACxF,YAAY,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,YAAY,EACX,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,GAClB,MAAM,gDAAgD,CAAA;AACvD,OAAO,EACN,KAAK,oBAAoB,EACzB,4BAA4B,GAC5B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,qBAAqB,GACrB,MAAM,4BAA4B,CAAA;AACnC,YAAY,EACX,qBAAqB,EACrB,iCAAiC,EACjC,8BAA8B,EAC9B,2BAA2B,EAC3B,iCAAiC,GACjC,MAAM,sBAAsB,CAAA;AAC7B,YAAY,EACX,sBAAsB,EACtB,+BAA+B,EAC/B,sBAAsB,EACtB,4BAA4B,EAC5B,4BAA4B,EAC5B,uBAAuB,EACvB,8BAA8B,EAC9B,2BAA2B,EAC3B,6BAA6B,EAC7B,+BAA+B,EAC/B,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,2BAA2B,EAC3B,kBAAkB,EAClB,sBAAsB,GACtB,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EAAE,oCAAoC,EAAE,MAAM,oBAAoB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initErrors.d.ts","sourceRoot":"","sources":["../../src/lifecycle/initErrors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,wBAAwB,EACxB,wBAAwB,EACxB,MAAM,mDAAmD,CAAA;AAK1D;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACxC,MAAM,EAAE,wBAAwB,EAChC,IAAI,GAAE;IACL,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,4BAA4B,CAAC,EAAE,MAAM,CAAA;CAChC,GACJ,wBAAwB,
|
|
1
|
+
{"version":3,"file":"initErrors.d.ts","sourceRoot":"","sources":["../../src/lifecycle/initErrors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,wBAAwB,EACxB,wBAAwB,EACxB,MAAM,mDAAmD,CAAA;AAK1D;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACxC,MAAM,EAAE,wBAAwB,EAChC,IAAI,GAAE;IACL,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,4BAA4B,CAAC,EAAE,MAAM,CAAA;CAChC,GACJ,wBAAwB,CAqJ1B"}
|
|
@@ -77,6 +77,12 @@ export function initErrorForFailureReason(reason, data = {}) {
|
|
|
77
77
|
message: "Current user is unavailable.",
|
|
78
78
|
isRetryable: true,
|
|
79
79
|
};
|
|
80
|
+
case "initial_state_timeout":
|
|
81
|
+
return {
|
|
82
|
+
code: reason,
|
|
83
|
+
message: "Host did not prepare initial state before the timeout.",
|
|
84
|
+
isRetryable: true,
|
|
85
|
+
};
|
|
80
86
|
case "missing_data_source_binding":
|
|
81
87
|
return {
|
|
82
88
|
code: reason,
|
|
@@ -1,40 +1,16 @@
|
|
|
1
|
-
import type { NotionContrastMode } from "../protocol/contrast.js";
|
|
2
|
-
import type { NotionBlockId } from "../protocol/ids.js";
|
|
3
|
-
import type { CustomBlockManifest } from "../protocol/manifest.js";
|
|
4
1
|
import type { ConnectMessage } from "../protocol/messages/connect.js";
|
|
5
|
-
import type {
|
|
6
|
-
import type { CustomBlockInitErrorInfo } from "../protocol/messages/init.js";
|
|
2
|
+
import type { CustomBlockInitErrorInfo, InitMessage } from "../protocol/messages/init.js";
|
|
7
3
|
import type { InitResultMessage } from "../protocol/messages/initResult.js";
|
|
8
|
-
import type { CustomBlockPage } from "../protocol/pages/page.js";
|
|
9
|
-
import type { NotionParent } from "../protocol/parent.js";
|
|
10
|
-
import type { NotionTheme } from "../protocol/theme.js";
|
|
11
|
-
import type { NotionUser } from "../protocol/users/user.js";
|
|
12
4
|
import type { CustomBlockHostFailure } from "../messages/types.js";
|
|
13
|
-
import type { ConnectSuccessMessage,
|
|
14
|
-
type InitializationState = {
|
|
15
|
-
theme: NotionTheme;
|
|
16
|
-
contrastMode: NotionContrastMode;
|
|
17
|
-
parent: NotionParent;
|
|
18
|
-
page: CustomBlockPage;
|
|
19
|
-
currentUser: NotionUser;
|
|
20
|
-
};
|
|
21
|
-
type InitializationPreparation = {
|
|
22
|
-
status: "success";
|
|
23
|
-
manifest: CustomBlockManifest;
|
|
24
|
-
dataSources: CustomBlockHostDataSourcesPayload;
|
|
25
|
-
} | {
|
|
26
|
-
status: "error";
|
|
27
|
-
error: CustomBlockInitErrorInfo;
|
|
28
|
-
};
|
|
5
|
+
import type { ConnectSuccessMessage, CustomBlockHostInitialStateResult, InitStatus } from "./types.js";
|
|
29
6
|
export type InitializationControllerOptions = {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
prepareInit: (connect: ConnectSuccessMessage) => InitializationPreparation;
|
|
33
|
-
post: (message: HostToSandboxMessage) => void;
|
|
7
|
+
prepareInit: (connect: ConnectSuccessMessage) => Promise<CustomBlockHostInitialStateResult>;
|
|
8
|
+
sendInitMessage: (message: InitMessage) => void;
|
|
34
9
|
warn: (payload: unknown) => void;
|
|
35
10
|
flushPendingHostStateChanges: () => void;
|
|
36
11
|
minBridgeProtocolVersion: number;
|
|
37
12
|
noConnectTimeoutMs?: number;
|
|
13
|
+
initialStateTimeoutMs?: number;
|
|
38
14
|
noInitResultTimeoutMs?: number;
|
|
39
15
|
stopHostWithFailure: (error: CustomBlockHostFailure) => void;
|
|
40
16
|
onInitResult?: (message: InitResultMessage) => void;
|
|
@@ -58,5 +34,4 @@ export type InitializationController = {
|
|
|
58
34
|
stop: () => void;
|
|
59
35
|
};
|
|
60
36
|
export declare function createInitializationController(options: InitializationControllerOptions): InitializationController;
|
|
61
|
-
export {};
|
|
62
37
|
//# sourceMappingURL=initializationController.d.ts.map
|