@notionhq/custom-blocks 0.0.77 → 0.0.78
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/HOST.md +60 -33
- package/bin/src/bridge/manifest.js +3 -3
- package/dist/bridge/SandboxBridge.d.ts +7 -12
- package/dist/bridge/SandboxBridge.d.ts.map +1 -1
- package/dist/bridge/SandboxBridge.js +109 -38
- package/dist/bridge/dataSources/resolve.d.ts +3 -4
- package/dist/bridge/dataSources/resolve.d.ts.map +1 -1
- package/dist/bridge/dataSources/resolve.js +2 -12
- package/dist/bridge/hostState.d.ts +2 -0
- package/dist/bridge/hostState.d.ts.map +1 -1
- package/dist/bridge/loadManifest.d.ts +4 -4
- package/dist/bridge/loadManifest.d.ts.map +1 -1
- package/dist/bridge/loadManifest.js +2 -2
- package/dist/bridge/manifest.d.ts +3 -3
- package/dist/bridge/manifest.js +3 -3
- package/dist/bridge/messages/{ready.d.ts → connect.d.ts} +13 -11
- package/dist/bridge/messages/connect.d.ts.map +1 -0
- package/dist/bridge/messages/{ready.js → connect.js} +14 -13
- package/dist/bridge/messages/hostToSandbox.d.ts +21 -0
- package/dist/bridge/messages/hostToSandbox.d.ts.map +1 -1
- package/dist/bridge/messages/init.d.ts +35 -6
- package/dist/bridge/messages/init.d.ts.map +1 -1
- package/dist/bridge/messages/init.js +8 -4
- package/dist/bridge/messages/initResult.d.ts +29 -0
- package/dist/bridge/messages/initResult.d.ts.map +1 -0
- package/dist/bridge/messages/initResult.js +25 -0
- package/dist/bridge/messages/sandboxToHost.d.ts +20 -5
- package/dist/bridge/messages/sandboxToHost.d.ts.map +1 -1
- package/dist/bridge/messages/sandboxToHost.js +4 -2
- package/dist/bridge/sandboxClient.d.ts +2 -4
- package/dist/bridge/sandboxClient.d.ts.map +1 -1
- package/dist/bridge/sandboxClient.js +2 -5
- package/dist/customBlock.d.ts +1 -1
- package/dist/customBlock.d.ts.map +1 -1
- package/dist/customBlock.js +1 -1
- package/dist/host/createCustomBlockHost.d.ts +6 -3
- package/dist/host/createCustomBlockHost.d.ts.map +1 -1
- package/dist/host/createCustomBlockHost.js +202 -38
- package/dist/host/lifecycle/initErrors.d.ts +1 -1
- package/dist/host/lifecycle/initErrors.d.ts.map +1 -1
- package/dist/host/lifecycle/initErrors.js +25 -7
- package/dist/host/lifecycle/types.d.ts +6 -4
- package/dist/host/lifecycle/types.d.ts.map +1 -1
- package/dist/host/messages/invalidSandboxMessage.js +0 -3
- package/dist/host.d.ts +5 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +2 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/init.d.ts +7 -7
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +70 -50
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/standalonePreview.d.ts.map +1 -1
- package/dist/react/standalonePreview.js +2 -0
- package/dist/react/useCustomBlockInit.d.ts +5 -6
- package/dist/react/useCustomBlockInit.d.ts.map +1 -1
- package/dist/react/useCustomBlockInit.js +3 -19
- package/dist/react/useRuntimeState.d.ts +4 -3
- package/dist/react/useRuntimeState.d.ts.map +1 -1
- package/dist/react/useRuntimeState.js +3 -2
- package/dist/version.js +1 -1
- package/docs/data-sources.md +7 -5
- package/docs/errors.md +5 -1
- package/docs/lifecycle.md +19 -31
- package/docs/manifest.md +1 -1
- package/package.json +1 -1
- package/src/bridge/SandboxBridge.ts +137 -43
- package/src/bridge/dataSources/resolve.ts +3 -14
- package/src/bridge/hostState.ts +2 -0
- package/src/bridge/loadManifest.ts +6 -6
- package/src/bridge/manifest.ts +3 -3
- package/src/bridge/messages/{ready.ts → connect.ts} +15 -14
- package/src/bridge/messages/init.ts +24 -10
- package/src/bridge/messages/initResult.ts +37 -0
- package/src/bridge/messages/sandboxToHost.ts +4 -2
- package/src/bridge/sandboxClient.ts +3 -8
- package/src/customBlock.ts +2 -2
- package/src/host/createCustomBlockHost.ts +242 -42
- package/src/host/lifecycle/initErrors.ts +25 -7
- package/src/host/lifecycle/types.ts +13 -4
- package/src/host/messages/invalidSandboxMessage.ts +0 -3
- package/src/host.ts +13 -2
- package/src/index.ts +3 -1
- package/src/init.ts +73 -60
- package/src/react/index.ts +0 -1
- package/src/react/standalonePreview.ts +2 -0
- package/src/react/useCustomBlockInit.ts +6 -27
- package/src/react/useRuntimeState.ts +4 -3
- package/dist/bridge/messages/ready.d.ts.map +0 -1
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
getDataSourceQueryView as getDataSourceQueryViewWithBridge,
|
|
18
18
|
} from "./hostState.js"
|
|
19
19
|
import type { ManifestLoadResult } from "./loadManifest.js"
|
|
20
|
-
import type { CustomBlockManifest } from "./manifest.js"
|
|
21
20
|
import type { InitMessage } from "./messages/init.js"
|
|
22
21
|
import { type MessageLogEntry, SandboxBridge } from "./SandboxBridge.js"
|
|
23
22
|
|
|
@@ -26,11 +25,11 @@ export type { MessageLogEntry }
|
|
|
26
25
|
const bridge = new SandboxBridge()
|
|
27
26
|
|
|
28
27
|
export const customBlockHost = {
|
|
29
|
-
|
|
30
|
-
bridge.
|
|
28
|
+
sendConnect: (manifestResult: ManifestLoadResult) => {
|
|
29
|
+
bridge.sendConnect(manifestResult)
|
|
31
30
|
},
|
|
32
31
|
|
|
33
|
-
awaitInit: (signal?: AbortSignal): Promise<
|
|
32
|
+
awaitInit: (signal?: AbortSignal): Promise<void> => {
|
|
34
33
|
return bridge.awaitInit(signal)
|
|
35
34
|
},
|
|
36
35
|
|
|
@@ -42,10 +41,6 @@ export const customBlockHost = {
|
|
|
42
41
|
return bridge.getHostState()
|
|
43
42
|
},
|
|
44
43
|
|
|
45
|
-
getManifest: (): CustomBlockManifest | null => {
|
|
46
|
-
return bridge.getManifest()
|
|
47
|
-
},
|
|
48
|
-
|
|
49
44
|
/**
|
|
50
45
|
* Apply an `init` payload directly, bypassing the postMessage handshake for
|
|
51
46
|
* standalone preview state.
|
package/src/customBlock.ts
CHANGED
|
@@ -57,8 +57,8 @@ export const customBlock = {
|
|
|
57
57
|
return getInitializedHostState("getPage").page
|
|
58
58
|
},
|
|
59
59
|
|
|
60
|
-
getManifest(): CustomBlockManifest
|
|
61
|
-
return
|
|
60
|
+
getManifest(): CustomBlockManifest {
|
|
61
|
+
return getInitializedHostState("getManifest").manifest
|
|
62
62
|
},
|
|
63
63
|
|
|
64
64
|
autoResize,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as v from "valibot"
|
|
2
2
|
import type { NotionDataSourceId } from "../bridge/ids.js"
|
|
3
|
+
import type { CustomBlockManifest } from "../bridge/manifest.js"
|
|
4
|
+
import type { ConnectMessage } from "../bridge/messages/connect.js"
|
|
3
5
|
import type { CreatePageMessage } from "../bridge/messages/createPage.js"
|
|
4
6
|
import type { CreatePageResultMessage } from "../bridge/messages/createPageResult.js"
|
|
5
7
|
import type { DataSourcesChangedMessage } from "../bridge/messages/dataSourcesChanged.js"
|
|
@@ -13,9 +15,11 @@ import type {
|
|
|
13
15
|
} from "../bridge/messages/getUser.js"
|
|
14
16
|
import type { HostToSandboxMessage } from "../bridge/messages/hostToSandbox.js"
|
|
15
17
|
import type {
|
|
18
|
+
CustomBlockInitErrorCode,
|
|
16
19
|
CustomBlockInitErrorInfo,
|
|
17
20
|
InitMessage,
|
|
18
21
|
} from "../bridge/messages/init.js"
|
|
22
|
+
import type { InitResultMessage } from "../bridge/messages/initResult.js"
|
|
19
23
|
import type {
|
|
20
24
|
ListUsersMessage,
|
|
21
25
|
ListUsersResultMessage,
|
|
@@ -24,7 +28,6 @@ import type { PageChangedMessage } from "../bridge/messages/pageChanged.js"
|
|
|
24
28
|
import type { ParentChangedMessage } from "../bridge/messages/parentChanged.js"
|
|
25
29
|
import type { QueryDataSourceMessage } from "../bridge/messages/queryDataSource.js"
|
|
26
30
|
import type { QueryDataSourceResultMessage } from "../bridge/messages/queryDataSourceResult.js"
|
|
27
|
-
import type { ReadyMessage } from "../bridge/messages/ready.js"
|
|
28
31
|
import { sandboxToHostMessageSchema } from "../bridge/messages/sandboxToHost.js"
|
|
29
32
|
import type { ThemeChangedMessage } from "../bridge/messages/themeChanged.js"
|
|
30
33
|
import type { UpdatePageMessage } from "../bridge/messages/updatePage.js"
|
|
@@ -37,10 +40,10 @@ import { unreachable } from "../utils.js"
|
|
|
37
40
|
import { initErrorForFailureReason } from "./lifecycle/initErrors.js"
|
|
38
41
|
import { isValidBridgeProtocolVersion } from "./lifecycle/ready.js"
|
|
39
42
|
import type {
|
|
43
|
+
ConnectSuccessMessage,
|
|
40
44
|
CustomBlockHostDataSourcesPayload,
|
|
41
45
|
CustomBlockHostInitialState,
|
|
42
46
|
InitStatus,
|
|
43
|
-
ReadySuccessMessage,
|
|
44
47
|
} from "./lifecycle/types.js"
|
|
45
48
|
import { getInvalidSandboxMessageResponse } from "./messages/invalidSandboxMessage.js"
|
|
46
49
|
import type {
|
|
@@ -54,10 +57,12 @@ export type CustomBlockHostOptions = {
|
|
|
54
57
|
iframe: HTMLIFrameElement
|
|
55
58
|
targetOrigin?: string
|
|
56
59
|
minBridgeProtocolVersion?: number
|
|
57
|
-
|
|
60
|
+
noConnectTimeoutMs?: number
|
|
61
|
+
noInitResultTimeoutMs?: number
|
|
58
62
|
initialState: CustomBlockHostInitialState
|
|
59
63
|
handlers: CustomBlockHostHandlers
|
|
60
|
-
|
|
64
|
+
onConnect?: (message: ConnectSuccessMessage) => void
|
|
65
|
+
onInitResult?: (message: InitResultMessage) => void
|
|
61
66
|
onLog?: (direction: CustomBlockHostLogDirection, payload: unknown) => void
|
|
62
67
|
}
|
|
63
68
|
|
|
@@ -75,7 +80,8 @@ export type CustomBlockHostHandle = {
|
|
|
75
80
|
}) => void
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
const
|
|
83
|
+
const DEFAULT_NO_CONNECT_TIMEOUT_MS = 5_000
|
|
84
|
+
const DEFAULT_NO_INIT_RESULT_TIMEOUT_MS = 5_000
|
|
79
85
|
const DEFAULT_MIN_BRIDGE_PROTOCOL_VERSION = 2
|
|
80
86
|
|
|
81
87
|
export function createCustomBlockHost(
|
|
@@ -85,20 +91,31 @@ export function createCustomBlockHost(
|
|
|
85
91
|
iframe,
|
|
86
92
|
initialState,
|
|
87
93
|
handlers,
|
|
88
|
-
|
|
94
|
+
onConnect,
|
|
95
|
+
onInitResult,
|
|
89
96
|
onLog,
|
|
90
97
|
targetOrigin = "*",
|
|
91
98
|
minBridgeProtocolVersion = DEFAULT_MIN_BRIDGE_PROTOCOL_VERSION,
|
|
92
|
-
|
|
99
|
+
noConnectTimeoutMs = DEFAULT_NO_CONNECT_TIMEOUT_MS,
|
|
100
|
+
noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS,
|
|
93
101
|
} = options
|
|
94
102
|
|
|
95
103
|
let theme = initialState.theme
|
|
96
104
|
let parent = initialState.parent
|
|
97
105
|
let page = initialState.page
|
|
98
106
|
let dataSources = initialState.dataSources
|
|
107
|
+
let manifest = initialState.manifest
|
|
99
108
|
let currentUser = initialState.currentUser
|
|
100
|
-
let initStatus: InitStatus = "
|
|
101
|
-
let
|
|
109
|
+
let initStatus: InitStatus = "waitingForConnect"
|
|
110
|
+
let noConnectTimeoutId: number | undefined
|
|
111
|
+
let noInitResultTimeoutId: number | undefined
|
|
112
|
+
let activeInitializationId: string | undefined
|
|
113
|
+
let connectedSandbox: ConnectSuccessMessage | undefined
|
|
114
|
+
let pendingThemeChange = false
|
|
115
|
+
let pendingParentChange = false
|
|
116
|
+
let pendingPageChange = false
|
|
117
|
+
let pendingDataSourcesChange: CustomBlockHostDataSourcesPayload | undefined
|
|
118
|
+
let pendingCurrentUserChange = false
|
|
102
119
|
const querySubscriptions = new QuerySubscriptions()
|
|
103
120
|
|
|
104
121
|
function emit(direction: CustomBlockHostLogDirection, payload: unknown) {
|
|
@@ -132,58 +149,104 @@ export function createCustomBlockHost(
|
|
|
132
149
|
post(message)
|
|
133
150
|
}
|
|
134
151
|
|
|
135
|
-
function
|
|
136
|
-
if (
|
|
137
|
-
window.clearTimeout(
|
|
138
|
-
|
|
152
|
+
function clearNoConnectTimeout() {
|
|
153
|
+
if (noConnectTimeoutId !== undefined) {
|
|
154
|
+
window.clearTimeout(noConnectTimeoutId)
|
|
155
|
+
noConnectTimeoutId = undefined
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function clearNoInitResultTimeout() {
|
|
160
|
+
if (noInitResultTimeoutId !== undefined) {
|
|
161
|
+
window.clearTimeout(noInitResultTimeoutId)
|
|
162
|
+
noInitResultTimeoutId = undefined
|
|
139
163
|
}
|
|
140
164
|
}
|
|
141
165
|
|
|
142
166
|
function resolveDataSourcesPayload(
|
|
143
|
-
|
|
167
|
+
connect: ConnectSuccessMessage,
|
|
144
168
|
): CustomBlockHostDataSourcesPayload {
|
|
145
|
-
return typeof dataSources === "function"
|
|
169
|
+
return typeof dataSources === "function"
|
|
170
|
+
? dataSources(connect)
|
|
171
|
+
: dataSources
|
|
146
172
|
}
|
|
147
173
|
|
|
148
|
-
function sendInit(
|
|
149
|
-
if (initStatus !== "
|
|
174
|
+
function sendInit(connect: ConnectSuccessMessage): boolean {
|
|
175
|
+
if (initStatus !== "waitingForConnect") {
|
|
150
176
|
return false
|
|
151
177
|
}
|
|
152
|
-
|
|
153
|
-
|
|
178
|
+
onConnect?.(connect)
|
|
179
|
+
manifest = connect.manifest ?? initialState.manifest
|
|
180
|
+
const resolvedDataSources = resolveDataSourcesPayload(connect)
|
|
181
|
+
const bindingErrorCode = getBindingErrorCode(manifest, resolvedDataSources)
|
|
182
|
+
if (bindingErrorCode !== undefined) {
|
|
183
|
+
sendInitError(
|
|
184
|
+
connect.initializationId,
|
|
185
|
+
initErrorForFailureReason(bindingErrorCode),
|
|
186
|
+
)
|
|
187
|
+
return false
|
|
188
|
+
}
|
|
189
|
+
initStatus = "waitingForInitResult"
|
|
190
|
+
activeInitializationId = connect.initializationId
|
|
191
|
+
clearNoConnectTimeout()
|
|
154
192
|
const message: InitMessage = {
|
|
155
193
|
type: "init",
|
|
194
|
+
initializationId: connect.initializationId,
|
|
156
195
|
status: "success",
|
|
157
196
|
theme,
|
|
158
197
|
blockId: initialState.blockId,
|
|
159
198
|
parent,
|
|
160
199
|
page,
|
|
161
|
-
|
|
200
|
+
manifest,
|
|
201
|
+
dataSources: resolvedDataSources,
|
|
162
202
|
currentUser,
|
|
163
203
|
}
|
|
164
204
|
post(message)
|
|
205
|
+
noInitResultTimeoutId = window.setTimeout(() => {
|
|
206
|
+
if (initStatus !== "waitingForInitResult") {
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
initStatus = "error"
|
|
210
|
+
emit("warn", {
|
|
211
|
+
message: "Sandbox did not send initResult",
|
|
212
|
+
initializationId: connect.initializationId,
|
|
213
|
+
})
|
|
214
|
+
}, noInitResultTimeoutMs)
|
|
165
215
|
return true
|
|
166
216
|
}
|
|
167
217
|
|
|
168
|
-
function sendInitError(
|
|
169
|
-
|
|
218
|
+
function sendInitError(
|
|
219
|
+
initializationId: string,
|
|
220
|
+
error: CustomBlockInitErrorInfo,
|
|
221
|
+
) {
|
|
222
|
+
if (initStatus !== "waitingForConnect") {
|
|
170
223
|
return
|
|
171
224
|
}
|
|
172
225
|
initStatus = "error"
|
|
173
|
-
|
|
226
|
+
activeInitializationId = initializationId
|
|
227
|
+
clearNoConnectTimeout()
|
|
174
228
|
post({
|
|
175
229
|
type: "init",
|
|
230
|
+
initializationId,
|
|
176
231
|
status: "error",
|
|
177
232
|
error,
|
|
178
233
|
})
|
|
179
234
|
}
|
|
180
235
|
|
|
181
236
|
function onIframeLoad() {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
237
|
+
clearNoConnectTimeout()
|
|
238
|
+
if (initStatus !== "waitingForConnect") {
|
|
239
|
+
// Only set the timeout if the iframe loaded before the `connect` message was sent.
|
|
240
|
+
return
|
|
241
|
+
}
|
|
242
|
+
noConnectTimeoutId = window.setTimeout(() => {
|
|
243
|
+
if (initStatus !== "waitingForConnect") {
|
|
244
|
+
// Only do anything if the iframe loaded before the `connect` message was sent.
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
initStatus = "error"
|
|
248
|
+
emit("warn", initErrorForFailureReason("no_connect"))
|
|
249
|
+
}, noConnectTimeoutMs)
|
|
187
250
|
}
|
|
188
251
|
|
|
189
252
|
async function handleQuery(message: QueryDataSourceMessage) {
|
|
@@ -263,13 +326,13 @@ export function createCustomBlockHost(
|
|
|
263
326
|
post(result)
|
|
264
327
|
}
|
|
265
328
|
|
|
266
|
-
function
|
|
329
|
+
function handleConnect(message: ConnectMessage) {
|
|
267
330
|
// Verify the bridge protocol version is valid.
|
|
268
331
|
if (!isValidBridgeProtocolVersion(message.bridgeProtocolVersion)) {
|
|
269
332
|
const initError = initErrorForFailureReason("invalid_protocol_version", {
|
|
270
333
|
currentBridgeProtocolVersion: message.bridgeProtocolVersion,
|
|
271
334
|
})
|
|
272
|
-
sendInitError(initError)
|
|
335
|
+
sendInitError(message.initializationId, initError)
|
|
273
336
|
return
|
|
274
337
|
}
|
|
275
338
|
if (message.bridgeProtocolVersion < minBridgeProtocolVersion) {
|
|
@@ -280,21 +343,70 @@ export function createCustomBlockHost(
|
|
|
280
343
|
minBridgeProtocolVersion,
|
|
281
344
|
},
|
|
282
345
|
)
|
|
283
|
-
sendInitError(initError)
|
|
346
|
+
sendInitError(message.initializationId, initError)
|
|
284
347
|
return
|
|
285
348
|
}
|
|
286
349
|
|
|
287
|
-
// If the sandbox reported an error during `
|
|
350
|
+
// If the sandbox reported an error during `connect`, return it as an `init` error.
|
|
288
351
|
if (message.status === "error") {
|
|
289
|
-
sendInitError(message.error)
|
|
352
|
+
sendInitError(message.initializationId, message.error)
|
|
290
353
|
return
|
|
291
354
|
}
|
|
292
355
|
|
|
293
|
-
if (sendInit(message)) {
|
|
294
|
-
|
|
356
|
+
if (!sendInit(message)) {
|
|
357
|
+
return
|
|
358
|
+
}
|
|
359
|
+
connectedSandbox = message
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Sends any pending host state changes to the sandbox. It is possible for these to change
|
|
364
|
+
* between when the host sends `init` and when the sandbox responds with `initResult`. The host
|
|
365
|
+
* does send any pending state changes until hearing back from the sandbox.
|
|
366
|
+
*/
|
|
367
|
+
function flushPendingHostStateChanges() {
|
|
368
|
+
if (pendingThemeChange) {
|
|
369
|
+
pendingThemeChange = false
|
|
370
|
+
post({ type: "themeChanged", theme })
|
|
371
|
+
}
|
|
372
|
+
if (pendingParentChange) {
|
|
373
|
+
pendingParentChange = false
|
|
374
|
+
post({ type: "parentChanged", parent })
|
|
375
|
+
}
|
|
376
|
+
if (pendingPageChange) {
|
|
377
|
+
pendingPageChange = false
|
|
378
|
+
post({ type: "pageChanged", page })
|
|
379
|
+
}
|
|
380
|
+
if (pendingDataSourcesChange !== undefined) {
|
|
381
|
+
const nextDataSources = pendingDataSourcesChange
|
|
382
|
+
pendingDataSourcesChange = undefined
|
|
383
|
+
post({ type: "dataSourcesChanged", dataSources: nextDataSources })
|
|
384
|
+
}
|
|
385
|
+
if (pendingCurrentUserChange) {
|
|
386
|
+
pendingCurrentUserChange = false
|
|
387
|
+
post({ type: "currentUserChanged", currentUser })
|
|
295
388
|
}
|
|
296
389
|
}
|
|
297
390
|
|
|
391
|
+
function handleInitResult(message: InitResultMessage) {
|
|
392
|
+
if (
|
|
393
|
+
initStatus !== "waitingForInitResult" ||
|
|
394
|
+
message.initializationId !== activeInitializationId
|
|
395
|
+
) {
|
|
396
|
+
emit("warn", {
|
|
397
|
+
message: "Ignored stale or unexpected initResult",
|
|
398
|
+
payload: message,
|
|
399
|
+
})
|
|
400
|
+
return
|
|
401
|
+
}
|
|
402
|
+
clearNoInitResultTimeout()
|
|
403
|
+
initStatus = message.status === "success" ? "success" : "error"
|
|
404
|
+
if (initStatus === "success") {
|
|
405
|
+
flushPendingHostStateChanges()
|
|
406
|
+
}
|
|
407
|
+
onInitResult?.(message)
|
|
408
|
+
}
|
|
409
|
+
|
|
298
410
|
function onInvalidSandboxMessage(
|
|
299
411
|
data: unknown,
|
|
300
412
|
issues: readonly v.BaseIssue<unknown>[],
|
|
@@ -306,10 +418,7 @@ export function createCustomBlockHost(
|
|
|
306
418
|
})
|
|
307
419
|
const response = getInvalidSandboxMessageResponse(data)
|
|
308
420
|
|
|
309
|
-
if (
|
|
310
|
-
const initError = initErrorForFailureReason("invalid_ready")
|
|
311
|
-
sendInitError(initError)
|
|
312
|
-
} else if (response.nack !== undefined) {
|
|
421
|
+
if (response.nack !== undefined) {
|
|
313
422
|
post(response.nack)
|
|
314
423
|
}
|
|
315
424
|
}
|
|
@@ -328,9 +437,24 @@ export function createCustomBlockHost(
|
|
|
328
437
|
const message = parsed.output
|
|
329
438
|
emit("in", message)
|
|
330
439
|
|
|
440
|
+
if (
|
|
441
|
+
message.type !== "connect" &&
|
|
442
|
+
message.type !== "initResult" &&
|
|
443
|
+
initStatus !== "success"
|
|
444
|
+
) {
|
|
445
|
+
emit("warn", {
|
|
446
|
+
message: `Ignored ${message.type} before initialization completed`,
|
|
447
|
+
payload: message,
|
|
448
|
+
})
|
|
449
|
+
return
|
|
450
|
+
}
|
|
451
|
+
|
|
331
452
|
switch (message.type) {
|
|
332
|
-
case "
|
|
333
|
-
|
|
453
|
+
case "connect":
|
|
454
|
+
handleConnect(message)
|
|
455
|
+
return
|
|
456
|
+
case "initResult":
|
|
457
|
+
handleInitResult(message)
|
|
334
458
|
return
|
|
335
459
|
case "queryDataSource":
|
|
336
460
|
void handleQuery(message)
|
|
@@ -367,27 +491,62 @@ export function createCustomBlockHost(
|
|
|
367
491
|
stop() {
|
|
368
492
|
window.removeEventListener("message", onMessage)
|
|
369
493
|
iframe.removeEventListener("load", onIframeLoad)
|
|
370
|
-
|
|
494
|
+
clearNoConnectTimeout()
|
|
495
|
+
clearNoInitResultTimeout()
|
|
371
496
|
querySubscriptions.clear()
|
|
372
497
|
},
|
|
373
498
|
post,
|
|
374
499
|
setTheme(nextTheme) {
|
|
375
500
|
theme = nextTheme
|
|
501
|
+
if (initStatus !== "success") {
|
|
502
|
+
if (initStatus === "waitingForInitResult") {
|
|
503
|
+
pendingThemeChange = true
|
|
504
|
+
}
|
|
505
|
+
return
|
|
506
|
+
}
|
|
376
507
|
const message: ThemeChangedMessage = { type: "themeChanged", theme }
|
|
377
508
|
post(message)
|
|
378
509
|
},
|
|
379
510
|
setParent(nextParent) {
|
|
380
511
|
parent = nextParent
|
|
512
|
+
if (initStatus !== "success") {
|
|
513
|
+
if (initStatus === "waitingForInitResult") {
|
|
514
|
+
pendingParentChange = true
|
|
515
|
+
}
|
|
516
|
+
return
|
|
517
|
+
}
|
|
381
518
|
const message: ParentChangedMessage = { type: "parentChanged", parent }
|
|
382
519
|
post(message)
|
|
383
520
|
},
|
|
384
521
|
setPage(nextPage) {
|
|
385
522
|
page = nextPage
|
|
523
|
+
if (initStatus !== "success") {
|
|
524
|
+
if (initStatus === "waitingForInitResult") {
|
|
525
|
+
pendingPageChange = true
|
|
526
|
+
}
|
|
527
|
+
return
|
|
528
|
+
}
|
|
386
529
|
const message: PageChangedMessage = { type: "pageChanged", page }
|
|
387
530
|
post(message)
|
|
388
531
|
},
|
|
389
532
|
setDataSources(nextDataSources) {
|
|
533
|
+
if (
|
|
534
|
+
connectedSandbox !== undefined &&
|
|
535
|
+
getBindingErrorCode(manifest, nextDataSources) !== undefined
|
|
536
|
+
) {
|
|
537
|
+
emit("warn", {
|
|
538
|
+
message: "Ignored invalid data source bindings update",
|
|
539
|
+
payload: nextDataSources,
|
|
540
|
+
})
|
|
541
|
+
return
|
|
542
|
+
}
|
|
390
543
|
dataSources = nextDataSources
|
|
544
|
+
if (initStatus !== "success") {
|
|
545
|
+
if (initStatus === "waitingForInitResult") {
|
|
546
|
+
pendingDataSourcesChange = nextDataSources
|
|
547
|
+
}
|
|
548
|
+
return
|
|
549
|
+
}
|
|
391
550
|
const message: DataSourcesChangedMessage = {
|
|
392
551
|
type: "dataSourcesChanged",
|
|
393
552
|
dataSources: nextDataSources,
|
|
@@ -396,9 +555,18 @@ export function createCustomBlockHost(
|
|
|
396
555
|
},
|
|
397
556
|
setCurrentUser(nextCurrentUser) {
|
|
398
557
|
currentUser = nextCurrentUser
|
|
558
|
+
if (initStatus !== "success") {
|
|
559
|
+
if (initStatus === "waitingForInitResult") {
|
|
560
|
+
pendingCurrentUserChange = true
|
|
561
|
+
}
|
|
562
|
+
return
|
|
563
|
+
}
|
|
399
564
|
post({ type: "currentUserChanged", currentUser })
|
|
400
565
|
},
|
|
401
566
|
refreshQuery({ dataSourceId, response }) {
|
|
567
|
+
if (initStatus !== "success") {
|
|
568
|
+
return
|
|
569
|
+
}
|
|
402
570
|
const refreshes = querySubscriptions.resolveRefreshes({
|
|
403
571
|
dataSourceId,
|
|
404
572
|
response,
|
|
@@ -413,3 +581,35 @@ export function createCustomBlockHost(
|
|
|
413
581
|
},
|
|
414
582
|
}
|
|
415
583
|
}
|
|
584
|
+
|
|
585
|
+
function getBindingErrorCode(
|
|
586
|
+
manifest: CustomBlockManifest,
|
|
587
|
+
dataSources: CustomBlockHostDataSourcesPayload,
|
|
588
|
+
): CustomBlockInitErrorCode | undefined {
|
|
589
|
+
for (const [dataSourceKey, manifestDataSource] of Object.entries(
|
|
590
|
+
manifest.dataSources,
|
|
591
|
+
)) {
|
|
592
|
+
const binding = dataSources.bindings[dataSourceKey]
|
|
593
|
+
if (
|
|
594
|
+
binding?.collectionPointer === undefined ||
|
|
595
|
+
binding.collectionSchema === undefined
|
|
596
|
+
) {
|
|
597
|
+
return "missing_data_source_binding"
|
|
598
|
+
}
|
|
599
|
+
for (const [propertyKey, manifestProperty] of Object.entries(
|
|
600
|
+
manifestDataSource.properties ?? {},
|
|
601
|
+
)) {
|
|
602
|
+
const propertyId = binding.propertyIdsByKey?.[propertyKey]
|
|
603
|
+
if (propertyId === undefined) {
|
|
604
|
+
return "missing_property_binding"
|
|
605
|
+
}
|
|
606
|
+
if (
|
|
607
|
+
binding.collectionSchema.propertiesById[propertyId]?.type !==
|
|
608
|
+
manifestProperty.type
|
|
609
|
+
) {
|
|
610
|
+
return "invalid_property_binding"
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return undefined
|
|
615
|
+
}
|
|
@@ -10,7 +10,7 @@ const UNKNOWN_ERROR_MESSAGE =
|
|
|
10
10
|
* Returns the error info for a given `init` failure code.
|
|
11
11
|
*
|
|
12
12
|
* `init` handshakes are one-shot: after an `init` error, this host instance will not answer another
|
|
13
|
-
* `
|
|
13
|
+
* `connect` message. `isRetryable` means a fresh iframe / `init` attempt could plausibly succeed
|
|
14
14
|
* without changing block code or host configuration.
|
|
15
15
|
*/
|
|
16
16
|
export function initErrorForFailureReason(
|
|
@@ -21,23 +21,23 @@ export function initErrorForFailureReason(
|
|
|
21
21
|
} = {},
|
|
22
22
|
): CustomBlockInitErrorInfo {
|
|
23
23
|
switch (reason) {
|
|
24
|
-
case "
|
|
24
|
+
case "no_connect":
|
|
25
25
|
return {
|
|
26
26
|
code: reason,
|
|
27
|
-
message: "Sandbox did not send a
|
|
27
|
+
message: "Sandbox did not send a connect message.",
|
|
28
28
|
isRetryable: true,
|
|
29
29
|
}
|
|
30
|
-
case "
|
|
30
|
+
case "invalid_connect":
|
|
31
31
|
return {
|
|
32
32
|
code: reason,
|
|
33
|
-
message: "Sandbox sent an invalid
|
|
34
|
-
// The sandbox must be updated to send a valid
|
|
33
|
+
message: "Sandbox sent an invalid connect message.",
|
|
34
|
+
// The sandbox must be updated to send a valid connect message.
|
|
35
35
|
isRetryable: false,
|
|
36
36
|
}
|
|
37
37
|
case "manifest_unavailable":
|
|
38
38
|
return {
|
|
39
39
|
code: reason,
|
|
40
|
-
message: "
|
|
40
|
+
message: "The custom block manifest is unavailable.",
|
|
41
41
|
isRetryable: true,
|
|
42
42
|
}
|
|
43
43
|
case "manifest_invalid":
|
|
@@ -115,6 +115,24 @@ export function initErrorForFailureReason(
|
|
|
115
115
|
// a property with a compatible schema.
|
|
116
116
|
isRetryable: false,
|
|
117
117
|
}
|
|
118
|
+
case "no_init_result":
|
|
119
|
+
return {
|
|
120
|
+
code: reason,
|
|
121
|
+
message: "Sandbox did not acknowledge initialization.",
|
|
122
|
+
isRetryable: true,
|
|
123
|
+
}
|
|
124
|
+
case "invalid_init_result":
|
|
125
|
+
return {
|
|
126
|
+
code: reason,
|
|
127
|
+
message: "Sandbox sent an invalid initialization result.",
|
|
128
|
+
isRetryable: false,
|
|
129
|
+
}
|
|
130
|
+
case "invalid_init_bindings":
|
|
131
|
+
return {
|
|
132
|
+
code: reason,
|
|
133
|
+
message: "Sandbox rejected the host-provided data source bindings.",
|
|
134
|
+
isRetryable: false,
|
|
135
|
+
}
|
|
118
136
|
case "not_in_iframe":
|
|
119
137
|
return {
|
|
120
138
|
code: reason,
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import type { NotionDataSourceBindings } from "../../bridge/dataSources/dataSource.js"
|
|
2
2
|
import type { NotionBlockId } from "../../bridge/ids.js"
|
|
3
|
-
import type {
|
|
3
|
+
import type { CustomBlockManifest } from "../../bridge/manifest.js"
|
|
4
|
+
import type { ConnectMessage } from "../../bridge/messages/connect.js"
|
|
4
5
|
import type { CustomBlockPage } from "../../bridge/pages/page.js"
|
|
5
6
|
import type { NotionParent } from "../../bridge/parent.js"
|
|
6
7
|
import type { NotionTheme } from "../../bridge/theme.js"
|
|
7
8
|
import type { NotionUser } from "../../bridge/users/user.js"
|
|
8
9
|
|
|
9
|
-
export type
|
|
10
|
+
export type ConnectSuccessMessage = Extract<
|
|
11
|
+
ConnectMessage,
|
|
12
|
+
{ status: "success" }
|
|
13
|
+
>
|
|
10
14
|
|
|
11
|
-
export type InitStatus =
|
|
15
|
+
export type InitStatus =
|
|
16
|
+
| "waitingForConnect"
|
|
17
|
+
| "waitingForInitResult"
|
|
18
|
+
| "success"
|
|
19
|
+
| "error"
|
|
12
20
|
|
|
13
21
|
export type CustomBlockHostDataSourcesPayload = {
|
|
14
22
|
bindings: NotionDataSourceBindings
|
|
@@ -16,13 +24,14 @@ export type CustomBlockHostDataSourcesPayload = {
|
|
|
16
24
|
|
|
17
25
|
export type CustomBlockHostInitialDataSources =
|
|
18
26
|
| CustomBlockHostDataSourcesPayload
|
|
19
|
-
| ((
|
|
27
|
+
| ((connect: ConnectSuccessMessage) => CustomBlockHostDataSourcesPayload)
|
|
20
28
|
|
|
21
29
|
export type CustomBlockHostInitialState = {
|
|
22
30
|
theme: NotionTheme
|
|
23
31
|
blockId: NotionBlockId
|
|
24
32
|
parent: NotionParent
|
|
25
33
|
page: CustomBlockPage
|
|
34
|
+
manifest: CustomBlockManifest
|
|
26
35
|
dataSources: CustomBlockHostInitialDataSources
|
|
27
36
|
currentUser: NotionUser
|
|
28
37
|
}
|
|
@@ -28,9 +28,6 @@ export function getInvalidSandboxMessageResponse(
|
|
|
28
28
|
function nackReasonForInvalidSandboxMessage(
|
|
29
29
|
incomingType: string | undefined,
|
|
30
30
|
): string {
|
|
31
|
-
if (incomingType === "ready") {
|
|
32
|
-
return "Invalid manifest"
|
|
33
|
-
}
|
|
34
31
|
if (incomingType !== undefined) {
|
|
35
32
|
return `Unsupported sandbox message of type "${incomingType}"`
|
|
36
33
|
}
|
package/src/host.ts
CHANGED
|
@@ -38,6 +38,8 @@ export {
|
|
|
38
38
|
notionPageIdSchema,
|
|
39
39
|
} from "./bridge/ids.js"
|
|
40
40
|
export { readIncomingType } from "./bridge/incomingType.js"
|
|
41
|
+
export type { ConnectMessage } from "./bridge/messages/connect.js"
|
|
42
|
+
export { connectMessageSchema } from "./bridge/messages/connect.js"
|
|
41
43
|
export type { CreatePageMessage } from "./bridge/messages/createPage.js"
|
|
42
44
|
export { createPageMessageSchema } from "./bridge/messages/createPage.js"
|
|
43
45
|
export type {
|
|
@@ -90,6 +92,16 @@ export {
|
|
|
90
92
|
customBlockInitErrorInfoSchema,
|
|
91
93
|
initMessageSchema,
|
|
92
94
|
} from "./bridge/messages/init.js"
|
|
95
|
+
export type {
|
|
96
|
+
CustomBlockInitResultErrorCode,
|
|
97
|
+
CustomBlockInitResultErrorInfo,
|
|
98
|
+
InitResultMessage,
|
|
99
|
+
} from "./bridge/messages/initResult.js"
|
|
100
|
+
export {
|
|
101
|
+
customBlockInitResultErrorCodeSchema,
|
|
102
|
+
customBlockInitResultErrorInfoSchema,
|
|
103
|
+
initResultMessageSchema,
|
|
104
|
+
} from "./bridge/messages/initResult.js"
|
|
93
105
|
export type { InvalidHostMessage } from "./bridge/messages/invalidHostMessage.js"
|
|
94
106
|
export { invalidHostMessageSchema } from "./bridge/messages/invalidHostMessage.js"
|
|
95
107
|
export type { InvalidSandboxMessage } from "./bridge/messages/invalidSandboxMessage.js"
|
|
@@ -122,8 +134,6 @@ export {
|
|
|
122
134
|
customBlockQueryDataSourceErrorInfoSchema,
|
|
123
135
|
queryDataSourceResultMessageSchema,
|
|
124
136
|
} from "./bridge/messages/queryDataSourceResult.js"
|
|
125
|
-
export type { ReadyMessage } from "./bridge/messages/ready.js"
|
|
126
|
-
export { readyMessageSchema } from "./bridge/messages/ready.js"
|
|
127
137
|
export type { ResizeMessage } from "./bridge/messages/resize.js"
|
|
128
138
|
export { resizeMessageSchema } from "./bridge/messages/resize.js"
|
|
129
139
|
export type { SandboxToHostMessage } from "./bridge/messages/sandboxToHost.js"
|
|
@@ -151,6 +161,7 @@ export {
|
|
|
151
161
|
createCustomBlockHost,
|
|
152
162
|
} from "./host/createCustomBlockHost.js"
|
|
153
163
|
export type {
|
|
164
|
+
ConnectSuccessMessage,
|
|
154
165
|
CustomBlockHostDataSourcesPayload,
|
|
155
166
|
CustomBlockHostInitialDataSources,
|
|
156
167
|
CustomBlockHostInitialState,
|
package/src/index.ts
CHANGED
|
@@ -79,8 +79,10 @@ export type {
|
|
|
79
79
|
export type {
|
|
80
80
|
CustomBlockInitErrorCode,
|
|
81
81
|
CustomBlockInitErrorInfo,
|
|
82
|
+
CustomBlockInitializationErrorCode,
|
|
83
|
+
CustomBlockInitializationErrorInfo,
|
|
82
84
|
} from "./bridge/messages/init.js"
|
|
83
|
-
export {
|
|
85
|
+
export { CustomBlockInitializationError } from "./bridge/messages/init.js"
|
|
84
86
|
export type {
|
|
85
87
|
CustomBlockListUsersErrorCode,
|
|
86
88
|
CustomBlockListUsersErrorInfo,
|