@notionhq/custom-blocks 0.0.77 → 0.0.79
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 +68 -34
- package/README.md +1 -1
- package/bin/src/bridge/manifest.js +3 -3
- package/dist/bridge/SandboxBridge.d.ts +8 -13
- package/dist/bridge/SandboxBridge.d.ts.map +1 -1
- package/dist/bridge/SandboxBridge.js +110 -39
- 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} +14 -12
- package/dist/bridge/messages/connect.d.ts.map +1 -0
- package/dist/bridge/messages/connect.js +38 -0
- 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 +21 -6
- 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 +7 -4
- package/dist/host/createCustomBlockHost.d.ts.map +1 -1
- package/dist/host/createCustomBlockHost.js +215 -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/{ready.d.ts → protocolVersion.d.ts} +1 -1
- package/dist/host/lifecycle/protocolVersion.d.ts.map +1 -0
- 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 +10 -8
- 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 +138 -44
- 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/connect.ts +42 -0
- 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 +265 -45
- 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
- package/dist/bridge/messages/ready.js +0 -43
- package/dist/host/lifecycle/ready.d.ts.map +0 -1
- package/src/bridge/messages/ready.ts +0 -48
- /package/dist/host/lifecycle/{ready.js → protocolVersion.js} +0 -0
- /package/src/host/lifecycle/{ready.ts → protocolVersion.ts} +0 -0
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,23 +28,23 @@ 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"
|
|
31
34
|
import type { UpdatePageResultMessage } from "../bridge/messages/updatePageResult.js"
|
|
32
35
|
import type { CustomBlockPage } from "../bridge/pages/page.js"
|
|
33
36
|
import type { NotionParent } from "../bridge/parent.js"
|
|
37
|
+
import { CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION } from "../bridge/SandboxBridge.js"
|
|
34
38
|
import type { NotionTheme } from "../bridge/theme.js"
|
|
35
39
|
import type { NotionUser } from "../bridge/users/user.js"
|
|
36
40
|
import { unreachable } from "../utils.js"
|
|
37
41
|
import { initErrorForFailureReason } from "./lifecycle/initErrors.js"
|
|
38
|
-
import { isValidBridgeProtocolVersion } from "./lifecycle/
|
|
42
|
+
import { isValidBridgeProtocolVersion } from "./lifecycle/protocolVersion.js"
|
|
39
43
|
import type {
|
|
44
|
+
ConnectSuccessMessage,
|
|
40
45
|
CustomBlockHostDataSourcesPayload,
|
|
41
46
|
CustomBlockHostInitialState,
|
|
42
47
|
InitStatus,
|
|
43
|
-
ReadySuccessMessage,
|
|
44
48
|
} from "./lifecycle/types.js"
|
|
45
49
|
import { getInvalidSandboxMessageResponse } from "./messages/invalidSandboxMessage.js"
|
|
46
50
|
import type {
|
|
@@ -53,11 +57,13 @@ import type { CustomBlockHostQueryDataSourceResult } from "./queries/types.js"
|
|
|
53
57
|
export type CustomBlockHostOptions = {
|
|
54
58
|
iframe: HTMLIFrameElement
|
|
55
59
|
targetOrigin?: string
|
|
56
|
-
minBridgeProtocolVersion
|
|
57
|
-
|
|
60
|
+
minBridgeProtocolVersion: number
|
|
61
|
+
noConnectTimeoutMs?: number
|
|
62
|
+
noInitResultTimeoutMs?: number
|
|
58
63
|
initialState: CustomBlockHostInitialState
|
|
59
64
|
handlers: CustomBlockHostHandlers
|
|
60
|
-
|
|
65
|
+
onConnect?: (message: ConnectSuccessMessage) => void
|
|
66
|
+
onInitResult?: (message: InitResultMessage) => void
|
|
61
67
|
onLog?: (direction: CustomBlockHostLogDirection, payload: unknown) => void
|
|
62
68
|
}
|
|
63
69
|
|
|
@@ -75,8 +81,11 @@ export type CustomBlockHostHandle = {
|
|
|
75
81
|
}) => void
|
|
76
82
|
}
|
|
77
83
|
|
|
78
|
-
const
|
|
79
|
-
const
|
|
84
|
+
const DEFAULT_NO_CONNECT_TIMEOUT_MS = 5_000
|
|
85
|
+
const DEFAULT_NO_INIT_RESULT_TIMEOUT_MS = 5_000
|
|
86
|
+
const initializationIdentitySchema = v.object({
|
|
87
|
+
initializationId: v.string(),
|
|
88
|
+
})
|
|
80
89
|
|
|
81
90
|
export function createCustomBlockHost(
|
|
82
91
|
options: CustomBlockHostOptions,
|
|
@@ -85,20 +94,35 @@ export function createCustomBlockHost(
|
|
|
85
94
|
iframe,
|
|
86
95
|
initialState,
|
|
87
96
|
handlers,
|
|
88
|
-
|
|
97
|
+
onConnect,
|
|
98
|
+
onInitResult,
|
|
89
99
|
onLog,
|
|
90
100
|
targetOrigin = "*",
|
|
91
|
-
minBridgeProtocolVersion
|
|
92
|
-
|
|
101
|
+
minBridgeProtocolVersion: configuredMinBridgeProtocolVersion,
|
|
102
|
+
noConnectTimeoutMs = DEFAULT_NO_CONNECT_TIMEOUT_MS,
|
|
103
|
+
noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS,
|
|
93
104
|
} = options
|
|
105
|
+
const minBridgeProtocolVersion = Math.max(
|
|
106
|
+
configuredMinBridgeProtocolVersion ?? CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION,
|
|
107
|
+
CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION,
|
|
108
|
+
)
|
|
94
109
|
|
|
95
110
|
let theme = initialState.theme
|
|
96
111
|
let parent = initialState.parent
|
|
97
112
|
let page = initialState.page
|
|
98
113
|
let dataSources = initialState.dataSources
|
|
114
|
+
let manifest = initialState.manifest
|
|
99
115
|
let currentUser = initialState.currentUser
|
|
100
|
-
let initStatus: InitStatus = "
|
|
101
|
-
let
|
|
116
|
+
let initStatus: InitStatus = "waitingForConnect"
|
|
117
|
+
let noConnectTimeoutId: number | undefined
|
|
118
|
+
let noInitResultTimeoutId: number | undefined
|
|
119
|
+
let activeInitializationId: string | undefined
|
|
120
|
+
let connectedSandbox: ConnectSuccessMessage | undefined
|
|
121
|
+
let pendingThemeChange = false
|
|
122
|
+
let pendingParentChange = false
|
|
123
|
+
let pendingPageChange = false
|
|
124
|
+
let pendingDataSourcesChange: CustomBlockHostDataSourcesPayload | undefined
|
|
125
|
+
let pendingCurrentUserChange = false
|
|
102
126
|
const querySubscriptions = new QuerySubscriptions()
|
|
103
127
|
|
|
104
128
|
function emit(direction: CustomBlockHostLogDirection, payload: unknown) {
|
|
@@ -132,58 +156,104 @@ export function createCustomBlockHost(
|
|
|
132
156
|
post(message)
|
|
133
157
|
}
|
|
134
158
|
|
|
135
|
-
function
|
|
136
|
-
if (
|
|
137
|
-
window.clearTimeout(
|
|
138
|
-
|
|
159
|
+
function clearNoConnectTimeout() {
|
|
160
|
+
if (noConnectTimeoutId !== undefined) {
|
|
161
|
+
window.clearTimeout(noConnectTimeoutId)
|
|
162
|
+
noConnectTimeoutId = undefined
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function clearNoInitResultTimeout() {
|
|
167
|
+
if (noInitResultTimeoutId !== undefined) {
|
|
168
|
+
window.clearTimeout(noInitResultTimeoutId)
|
|
169
|
+
noInitResultTimeoutId = undefined
|
|
139
170
|
}
|
|
140
171
|
}
|
|
141
172
|
|
|
142
173
|
function resolveDataSourcesPayload(
|
|
143
|
-
|
|
174
|
+
connect: ConnectSuccessMessage,
|
|
144
175
|
): CustomBlockHostDataSourcesPayload {
|
|
145
|
-
return typeof dataSources === "function"
|
|
176
|
+
return typeof dataSources === "function"
|
|
177
|
+
? dataSources(connect)
|
|
178
|
+
: dataSources
|
|
146
179
|
}
|
|
147
180
|
|
|
148
|
-
function sendInit(
|
|
149
|
-
if (initStatus !== "
|
|
181
|
+
function sendInit(connect: ConnectSuccessMessage): boolean {
|
|
182
|
+
if (initStatus !== "waitingForConnect") {
|
|
150
183
|
return false
|
|
151
184
|
}
|
|
152
|
-
|
|
153
|
-
|
|
185
|
+
onConnect?.(connect)
|
|
186
|
+
manifest = connect.manifest ?? initialState.manifest
|
|
187
|
+
const resolvedDataSources = resolveDataSourcesPayload(connect)
|
|
188
|
+
const bindingErrorCode = getBindingErrorCode(manifest, resolvedDataSources)
|
|
189
|
+
if (bindingErrorCode !== undefined) {
|
|
190
|
+
sendInitError(
|
|
191
|
+
connect.initializationId,
|
|
192
|
+
initErrorForFailureReason(bindingErrorCode),
|
|
193
|
+
)
|
|
194
|
+
return false
|
|
195
|
+
}
|
|
196
|
+
initStatus = "waitingForInitResult"
|
|
197
|
+
activeInitializationId = connect.initializationId
|
|
198
|
+
clearNoConnectTimeout()
|
|
154
199
|
const message: InitMessage = {
|
|
155
200
|
type: "init",
|
|
201
|
+
initializationId: connect.initializationId,
|
|
156
202
|
status: "success",
|
|
157
203
|
theme,
|
|
158
204
|
blockId: initialState.blockId,
|
|
159
205
|
parent,
|
|
160
206
|
page,
|
|
161
|
-
|
|
207
|
+
manifest,
|
|
208
|
+
dataSources: resolvedDataSources,
|
|
162
209
|
currentUser,
|
|
163
210
|
}
|
|
164
211
|
post(message)
|
|
212
|
+
noInitResultTimeoutId = window.setTimeout(() => {
|
|
213
|
+
if (initStatus !== "waitingForInitResult") {
|
|
214
|
+
return
|
|
215
|
+
}
|
|
216
|
+
initStatus = "error"
|
|
217
|
+
emit("warn", {
|
|
218
|
+
message: "Sandbox did not send initResult",
|
|
219
|
+
initializationId: connect.initializationId,
|
|
220
|
+
})
|
|
221
|
+
}, noInitResultTimeoutMs)
|
|
165
222
|
return true
|
|
166
223
|
}
|
|
167
224
|
|
|
168
|
-
function sendInitError(
|
|
169
|
-
|
|
225
|
+
function sendInitError(
|
|
226
|
+
initializationId: string,
|
|
227
|
+
error: CustomBlockInitErrorInfo,
|
|
228
|
+
) {
|
|
229
|
+
if (initStatus !== "waitingForConnect") {
|
|
170
230
|
return
|
|
171
231
|
}
|
|
172
232
|
initStatus = "error"
|
|
173
|
-
|
|
233
|
+
activeInitializationId = initializationId
|
|
234
|
+
clearNoConnectTimeout()
|
|
174
235
|
post({
|
|
175
236
|
type: "init",
|
|
237
|
+
initializationId,
|
|
176
238
|
status: "error",
|
|
177
239
|
error,
|
|
178
240
|
})
|
|
179
241
|
}
|
|
180
242
|
|
|
181
243
|
function onIframeLoad() {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
244
|
+
clearNoConnectTimeout()
|
|
245
|
+
if (initStatus !== "waitingForConnect") {
|
|
246
|
+
// Only set the timeout if the iframe loaded before the `connect` message was sent.
|
|
247
|
+
return
|
|
248
|
+
}
|
|
249
|
+
noConnectTimeoutId = window.setTimeout(() => {
|
|
250
|
+
if (initStatus !== "waitingForConnect") {
|
|
251
|
+
// Only do anything if the iframe loaded before the `connect` message was sent.
|
|
252
|
+
return
|
|
253
|
+
}
|
|
254
|
+
initStatus = "error"
|
|
255
|
+
emit("warn", initErrorForFailureReason("no_connect"))
|
|
256
|
+
}, noConnectTimeoutMs)
|
|
187
257
|
}
|
|
188
258
|
|
|
189
259
|
async function handleQuery(message: QueryDataSourceMessage) {
|
|
@@ -263,13 +333,13 @@ export function createCustomBlockHost(
|
|
|
263
333
|
post(result)
|
|
264
334
|
}
|
|
265
335
|
|
|
266
|
-
function
|
|
336
|
+
function handleConnect(message: ConnectMessage) {
|
|
267
337
|
// Verify the bridge protocol version is valid.
|
|
268
338
|
if (!isValidBridgeProtocolVersion(message.bridgeProtocolVersion)) {
|
|
269
339
|
const initError = initErrorForFailureReason("invalid_protocol_version", {
|
|
270
340
|
currentBridgeProtocolVersion: message.bridgeProtocolVersion,
|
|
271
341
|
})
|
|
272
|
-
sendInitError(initError)
|
|
342
|
+
sendInitError(message.initializationId, initError)
|
|
273
343
|
return
|
|
274
344
|
}
|
|
275
345
|
if (message.bridgeProtocolVersion < minBridgeProtocolVersion) {
|
|
@@ -280,19 +350,68 @@ export function createCustomBlockHost(
|
|
|
280
350
|
minBridgeProtocolVersion,
|
|
281
351
|
},
|
|
282
352
|
)
|
|
283
|
-
sendInitError(initError)
|
|
353
|
+
sendInitError(message.initializationId, initError)
|
|
284
354
|
return
|
|
285
355
|
}
|
|
286
356
|
|
|
287
|
-
// If the sandbox reported an error during `
|
|
357
|
+
// If the sandbox reported an error during `connect`, return it as an `init` error.
|
|
288
358
|
if (message.status === "error") {
|
|
289
|
-
sendInitError(message.error)
|
|
359
|
+
sendInitError(message.initializationId, message.error)
|
|
290
360
|
return
|
|
291
361
|
}
|
|
292
362
|
|
|
293
|
-
if (sendInit(message)) {
|
|
294
|
-
|
|
363
|
+
if (!sendInit(message)) {
|
|
364
|
+
return
|
|
365
|
+
}
|
|
366
|
+
connectedSandbox = message
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Sends any pending host state changes to the sandbox. It is possible for these to change
|
|
371
|
+
* between when the host sends `init` and when the sandbox responds with `initResult`. The host
|
|
372
|
+
* does send any pending state changes until hearing back from the sandbox.
|
|
373
|
+
*/
|
|
374
|
+
function flushPendingHostStateChanges() {
|
|
375
|
+
if (pendingThemeChange) {
|
|
376
|
+
pendingThemeChange = false
|
|
377
|
+
post({ type: "themeChanged", theme })
|
|
378
|
+
}
|
|
379
|
+
if (pendingParentChange) {
|
|
380
|
+
pendingParentChange = false
|
|
381
|
+
post({ type: "parentChanged", parent })
|
|
382
|
+
}
|
|
383
|
+
if (pendingPageChange) {
|
|
384
|
+
pendingPageChange = false
|
|
385
|
+
post({ type: "pageChanged", page })
|
|
386
|
+
}
|
|
387
|
+
if (pendingDataSourcesChange !== undefined) {
|
|
388
|
+
const nextDataSources = pendingDataSourcesChange
|
|
389
|
+
pendingDataSourcesChange = undefined
|
|
390
|
+
post({ type: "dataSourcesChanged", dataSources: nextDataSources })
|
|
391
|
+
}
|
|
392
|
+
if (pendingCurrentUserChange) {
|
|
393
|
+
pendingCurrentUserChange = false
|
|
394
|
+
post({ type: "currentUserChanged", currentUser })
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function handleInitResult(message: InitResultMessage) {
|
|
399
|
+
if (
|
|
400
|
+
initStatus !== "waitingForInitResult" ||
|
|
401
|
+
message.initializationId !== activeInitializationId
|
|
402
|
+
) {
|
|
403
|
+
emit("warn", {
|
|
404
|
+
message: "Ignored stale or unexpected initResult",
|
|
405
|
+
payload: message,
|
|
406
|
+
})
|
|
407
|
+
return
|
|
295
408
|
}
|
|
409
|
+
clearNoInitResultTimeout()
|
|
410
|
+
initStatus = message.status === "success" ? "success" : "error"
|
|
411
|
+
if (initStatus === "success") {
|
|
412
|
+
flushPendingHostStateChanges()
|
|
413
|
+
}
|
|
414
|
+
onInitResult?.(message)
|
|
296
415
|
}
|
|
297
416
|
|
|
298
417
|
function onInvalidSandboxMessage(
|
|
@@ -306,9 +425,19 @@ export function createCustomBlockHost(
|
|
|
306
425
|
})
|
|
307
426
|
const response = getInvalidSandboxMessageResponse(data)
|
|
308
427
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
428
|
+
const parsedIdentity = v.safeParse(initializationIdentitySchema, data)
|
|
429
|
+
const initializationId = parsedIdentity.success
|
|
430
|
+
? parsedIdentity.output.initializationId
|
|
431
|
+
: undefined
|
|
432
|
+
if (
|
|
433
|
+
initStatus === "waitingForConnect" &&
|
|
434
|
+
response.incomingType === "connect" &&
|
|
435
|
+
initializationId !== undefined
|
|
436
|
+
) {
|
|
437
|
+
sendInitError(
|
|
438
|
+
initializationId,
|
|
439
|
+
initErrorForFailureReason("invalid_connect"),
|
|
440
|
+
)
|
|
312
441
|
} else if (response.nack !== undefined) {
|
|
313
442
|
post(response.nack)
|
|
314
443
|
}
|
|
@@ -328,9 +457,24 @@ export function createCustomBlockHost(
|
|
|
328
457
|
const message = parsed.output
|
|
329
458
|
emit("in", message)
|
|
330
459
|
|
|
460
|
+
if (
|
|
461
|
+
message.type !== "connect" &&
|
|
462
|
+
message.type !== "initResult" &&
|
|
463
|
+
initStatus !== "success"
|
|
464
|
+
) {
|
|
465
|
+
emit("warn", {
|
|
466
|
+
message: `Ignored ${message.type} before initialization completed`,
|
|
467
|
+
payload: message,
|
|
468
|
+
})
|
|
469
|
+
return
|
|
470
|
+
}
|
|
471
|
+
|
|
331
472
|
switch (message.type) {
|
|
332
|
-
case "
|
|
333
|
-
|
|
473
|
+
case "connect":
|
|
474
|
+
handleConnect(message)
|
|
475
|
+
return
|
|
476
|
+
case "initResult":
|
|
477
|
+
handleInitResult(message)
|
|
334
478
|
return
|
|
335
479
|
case "queryDataSource":
|
|
336
480
|
void handleQuery(message)
|
|
@@ -367,27 +511,62 @@ export function createCustomBlockHost(
|
|
|
367
511
|
stop() {
|
|
368
512
|
window.removeEventListener("message", onMessage)
|
|
369
513
|
iframe.removeEventListener("load", onIframeLoad)
|
|
370
|
-
|
|
514
|
+
clearNoConnectTimeout()
|
|
515
|
+
clearNoInitResultTimeout()
|
|
371
516
|
querySubscriptions.clear()
|
|
372
517
|
},
|
|
373
518
|
post,
|
|
374
519
|
setTheme(nextTheme) {
|
|
375
520
|
theme = nextTheme
|
|
521
|
+
if (initStatus !== "success") {
|
|
522
|
+
if (initStatus === "waitingForInitResult") {
|
|
523
|
+
pendingThemeChange = true
|
|
524
|
+
}
|
|
525
|
+
return
|
|
526
|
+
}
|
|
376
527
|
const message: ThemeChangedMessage = { type: "themeChanged", theme }
|
|
377
528
|
post(message)
|
|
378
529
|
},
|
|
379
530
|
setParent(nextParent) {
|
|
380
531
|
parent = nextParent
|
|
532
|
+
if (initStatus !== "success") {
|
|
533
|
+
if (initStatus === "waitingForInitResult") {
|
|
534
|
+
pendingParentChange = true
|
|
535
|
+
}
|
|
536
|
+
return
|
|
537
|
+
}
|
|
381
538
|
const message: ParentChangedMessage = { type: "parentChanged", parent }
|
|
382
539
|
post(message)
|
|
383
540
|
},
|
|
384
541
|
setPage(nextPage) {
|
|
385
542
|
page = nextPage
|
|
543
|
+
if (initStatus !== "success") {
|
|
544
|
+
if (initStatus === "waitingForInitResult") {
|
|
545
|
+
pendingPageChange = true
|
|
546
|
+
}
|
|
547
|
+
return
|
|
548
|
+
}
|
|
386
549
|
const message: PageChangedMessage = { type: "pageChanged", page }
|
|
387
550
|
post(message)
|
|
388
551
|
},
|
|
389
552
|
setDataSources(nextDataSources) {
|
|
553
|
+
if (
|
|
554
|
+
connectedSandbox !== undefined &&
|
|
555
|
+
getBindingErrorCode(manifest, nextDataSources) !== undefined
|
|
556
|
+
) {
|
|
557
|
+
emit("warn", {
|
|
558
|
+
message: "Ignored invalid data source bindings update",
|
|
559
|
+
payload: nextDataSources,
|
|
560
|
+
})
|
|
561
|
+
return
|
|
562
|
+
}
|
|
390
563
|
dataSources = nextDataSources
|
|
564
|
+
if (initStatus !== "success") {
|
|
565
|
+
if (initStatus === "waitingForInitResult") {
|
|
566
|
+
pendingDataSourcesChange = nextDataSources
|
|
567
|
+
}
|
|
568
|
+
return
|
|
569
|
+
}
|
|
391
570
|
const message: DataSourcesChangedMessage = {
|
|
392
571
|
type: "dataSourcesChanged",
|
|
393
572
|
dataSources: nextDataSources,
|
|
@@ -396,9 +575,18 @@ export function createCustomBlockHost(
|
|
|
396
575
|
},
|
|
397
576
|
setCurrentUser(nextCurrentUser) {
|
|
398
577
|
currentUser = nextCurrentUser
|
|
578
|
+
if (initStatus !== "success") {
|
|
579
|
+
if (initStatus === "waitingForInitResult") {
|
|
580
|
+
pendingCurrentUserChange = true
|
|
581
|
+
}
|
|
582
|
+
return
|
|
583
|
+
}
|
|
399
584
|
post({ type: "currentUserChanged", currentUser })
|
|
400
585
|
},
|
|
401
586
|
refreshQuery({ dataSourceId, response }) {
|
|
587
|
+
if (initStatus !== "success") {
|
|
588
|
+
return
|
|
589
|
+
}
|
|
402
590
|
const refreshes = querySubscriptions.resolveRefreshes({
|
|
403
591
|
dataSourceId,
|
|
404
592
|
response,
|
|
@@ -413,3 +601,35 @@ export function createCustomBlockHost(
|
|
|
413
601
|
},
|
|
414
602
|
}
|
|
415
603
|
}
|
|
604
|
+
|
|
605
|
+
function getBindingErrorCode(
|
|
606
|
+
manifest: CustomBlockManifest,
|
|
607
|
+
dataSources: CustomBlockHostDataSourcesPayload,
|
|
608
|
+
): CustomBlockInitErrorCode | undefined {
|
|
609
|
+
for (const [dataSourceKey, manifestDataSource] of Object.entries(
|
|
610
|
+
manifest.dataSources,
|
|
611
|
+
)) {
|
|
612
|
+
const binding = dataSources.bindings[dataSourceKey]
|
|
613
|
+
if (
|
|
614
|
+
binding?.collectionPointer === undefined ||
|
|
615
|
+
binding.collectionSchema === undefined
|
|
616
|
+
) {
|
|
617
|
+
return "missing_data_source_binding"
|
|
618
|
+
}
|
|
619
|
+
for (const [propertyKey, manifestProperty] of Object.entries(
|
|
620
|
+
manifestDataSource.properties ?? {},
|
|
621
|
+
)) {
|
|
622
|
+
const propertyId = binding.propertyIdsByKey?.[propertyKey]
|
|
623
|
+
if (propertyId === undefined) {
|
|
624
|
+
return "missing_property_binding"
|
|
625
|
+
}
|
|
626
|
+
if (
|
|
627
|
+
binding.collectionSchema.propertiesById[propertyId]?.type !==
|
|
628
|
+
manifestProperty.type
|
|
629
|
+
) {
|
|
630
|
+
return "invalid_property_binding"
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return undefined
|
|
635
|
+
}
|
|
@@ -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,
|