@notionhq/custom-blocks 0.0.76 → 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 +68 -37
- package/bin/src/bridge/manifest.js +3 -3
- package/dist/bridge/SandboxBridge.d.ts +6 -12
- package/dist/bridge/SandboxBridge.d.ts.map +1 -1
- package/dist/bridge/SandboxBridge.js +123 -71
- 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 +3 -2
- 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 +24 -79
- package/dist/bridge/messages/hostToSandbox.d.ts.map +1 -1
- package/dist/bridge/messages/hostToSandbox.js +2 -3
- 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/queryDataSource.d.ts +3 -7
- package/dist/bridge/messages/queryDataSource.d.ts.map +1 -1
- package/dist/bridge/messages/queryDataSource.js +3 -7
- package/dist/bridge/messages/queryDataSourceResult.d.ts +4 -240
- package/dist/bridge/messages/queryDataSourceResult.d.ts.map +1 -1
- package/dist/bridge/messages/queryDataSourceResult.js +4 -22
- package/dist/bridge/messages/sandboxToHost.d.ts +21 -7
- 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 +227 -58
- 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/queries/querySubscriptions.d.ts +10 -10
- package/dist/host/queries/querySubscriptions.d.ts.map +1 -1
- package/dist/host/queries/querySubscriptions.js +35 -19
- package/dist/host/queries/types.d.ts +5 -4
- package/dist/host/queries/types.d.ts.map +1 -1
- 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 +151 -77
- package/src/bridge/dataSources/resolve.ts +3 -14
- package/src/bridge/hostState.ts +3 -2
- 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/hostToSandbox.ts +2 -6
- package/src/bridge/messages/init.ts +24 -10
- package/src/bridge/messages/initResult.ts +37 -0
- package/src/bridge/messages/queryDataSource.ts +3 -7
- package/src/bridge/messages/queryDataSourceResult.ts +4 -24
- 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 +269 -64
- 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/queries/querySubscriptions.ts +58 -39
- package/src/host/queries/types.ts +10 -11
- 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
|
@@ -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) {
|
|
@@ -111,83 +128,135 @@ export function createCustomBlockHost(
|
|
|
111
128
|
}
|
|
112
129
|
|
|
113
130
|
function postQueryDataSourceResult(args: {
|
|
114
|
-
|
|
115
|
-
snapshotId: string
|
|
131
|
+
subscriptionId: string
|
|
116
132
|
response: CustomBlockHostQueryDataSourceResult
|
|
117
133
|
}) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
134
|
+
const message: QueryDataSourceResultMessage =
|
|
135
|
+
args.response.status === "error"
|
|
136
|
+
? {
|
|
137
|
+
type: "queryDataSourceResult",
|
|
138
|
+
subscriptionId: args.subscriptionId,
|
|
139
|
+
status: "error",
|
|
140
|
+
error: args.response.error,
|
|
141
|
+
}
|
|
142
|
+
: {
|
|
143
|
+
type: "queryDataSourceResult",
|
|
144
|
+
subscriptionId: args.subscriptionId,
|
|
145
|
+
status: "success",
|
|
146
|
+
items: args.response.items,
|
|
147
|
+
hasMore: args.response.hasMore,
|
|
148
|
+
}
|
|
128
149
|
post(message)
|
|
129
150
|
}
|
|
130
151
|
|
|
131
|
-
function
|
|
132
|
-
if (
|
|
133
|
-
window.clearTimeout(
|
|
134
|
-
|
|
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
|
|
135
163
|
}
|
|
136
164
|
}
|
|
137
165
|
|
|
138
166
|
function resolveDataSourcesPayload(
|
|
139
|
-
|
|
167
|
+
connect: ConnectSuccessMessage,
|
|
140
168
|
): CustomBlockHostDataSourcesPayload {
|
|
141
|
-
return typeof dataSources === "function"
|
|
169
|
+
return typeof dataSources === "function"
|
|
170
|
+
? dataSources(connect)
|
|
171
|
+
: dataSources
|
|
142
172
|
}
|
|
143
173
|
|
|
144
|
-
function sendInit(
|
|
145
|
-
if (initStatus !== "
|
|
174
|
+
function sendInit(connect: ConnectSuccessMessage): boolean {
|
|
175
|
+
if (initStatus !== "waitingForConnect") {
|
|
146
176
|
return false
|
|
147
177
|
}
|
|
148
|
-
|
|
149
|
-
|
|
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()
|
|
150
192
|
const message: InitMessage = {
|
|
151
193
|
type: "init",
|
|
194
|
+
initializationId: connect.initializationId,
|
|
152
195
|
status: "success",
|
|
153
196
|
theme,
|
|
154
197
|
blockId: initialState.blockId,
|
|
155
198
|
parent,
|
|
156
199
|
page,
|
|
157
|
-
|
|
200
|
+
manifest,
|
|
201
|
+
dataSources: resolvedDataSources,
|
|
158
202
|
currentUser,
|
|
159
203
|
}
|
|
160
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)
|
|
161
215
|
return true
|
|
162
216
|
}
|
|
163
217
|
|
|
164
|
-
function sendInitError(
|
|
165
|
-
|
|
218
|
+
function sendInitError(
|
|
219
|
+
initializationId: string,
|
|
220
|
+
error: CustomBlockInitErrorInfo,
|
|
221
|
+
) {
|
|
222
|
+
if (initStatus !== "waitingForConnect") {
|
|
166
223
|
return
|
|
167
224
|
}
|
|
168
225
|
initStatus = "error"
|
|
169
|
-
|
|
226
|
+
activeInitializationId = initializationId
|
|
227
|
+
clearNoConnectTimeout()
|
|
170
228
|
post({
|
|
171
229
|
type: "init",
|
|
230
|
+
initializationId,
|
|
172
231
|
status: "error",
|
|
173
232
|
error,
|
|
174
233
|
})
|
|
175
234
|
}
|
|
176
235
|
|
|
177
236
|
function onIframeLoad() {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
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)
|
|
183
250
|
}
|
|
184
251
|
|
|
185
252
|
async function handleQuery(message: QueryDataSourceMessage) {
|
|
186
|
-
querySubscriptions.track(message)
|
|
253
|
+
const token = querySubscriptions.track(message)
|
|
187
254
|
const response = await handlers.queryDataSource(message)
|
|
255
|
+
if (!querySubscriptions.isCurrent(message.subscriptionId, token)) {
|
|
256
|
+
return
|
|
257
|
+
}
|
|
188
258
|
postQueryDataSourceResult({
|
|
189
|
-
|
|
190
|
-
snapshotId: message.snapshotId,
|
|
259
|
+
subscriptionId: message.subscriptionId,
|
|
191
260
|
response,
|
|
192
261
|
})
|
|
193
262
|
}
|
|
@@ -257,13 +326,13 @@ export function createCustomBlockHost(
|
|
|
257
326
|
post(result)
|
|
258
327
|
}
|
|
259
328
|
|
|
260
|
-
function
|
|
329
|
+
function handleConnect(message: ConnectMessage) {
|
|
261
330
|
// Verify the bridge protocol version is valid.
|
|
262
331
|
if (!isValidBridgeProtocolVersion(message.bridgeProtocolVersion)) {
|
|
263
332
|
const initError = initErrorForFailureReason("invalid_protocol_version", {
|
|
264
333
|
currentBridgeProtocolVersion: message.bridgeProtocolVersion,
|
|
265
334
|
})
|
|
266
|
-
sendInitError(initError)
|
|
335
|
+
sendInitError(message.initializationId, initError)
|
|
267
336
|
return
|
|
268
337
|
}
|
|
269
338
|
if (message.bridgeProtocolVersion < minBridgeProtocolVersion) {
|
|
@@ -274,19 +343,68 @@ export function createCustomBlockHost(
|
|
|
274
343
|
minBridgeProtocolVersion,
|
|
275
344
|
},
|
|
276
345
|
)
|
|
277
|
-
sendInitError(initError)
|
|
346
|
+
sendInitError(message.initializationId, initError)
|
|
278
347
|
return
|
|
279
348
|
}
|
|
280
349
|
|
|
281
|
-
// If the sandbox reported an error during `
|
|
350
|
+
// If the sandbox reported an error during `connect`, return it as an `init` error.
|
|
282
351
|
if (message.status === "error") {
|
|
283
|
-
sendInitError(message.error)
|
|
352
|
+
sendInitError(message.initializationId, message.error)
|
|
284
353
|
return
|
|
285
354
|
}
|
|
286
355
|
|
|
287
|
-
if (sendInit(message)) {
|
|
288
|
-
|
|
356
|
+
if (!sendInit(message)) {
|
|
357
|
+
return
|
|
289
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 })
|
|
388
|
+
}
|
|
389
|
+
}
|
|
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)
|
|
290
408
|
}
|
|
291
409
|
|
|
292
410
|
function onInvalidSandboxMessage(
|
|
@@ -300,10 +418,7 @@ export function createCustomBlockHost(
|
|
|
300
418
|
})
|
|
301
419
|
const response = getInvalidSandboxMessageResponse(data)
|
|
302
420
|
|
|
303
|
-
if (
|
|
304
|
-
const initError = initErrorForFailureReason("invalid_ready")
|
|
305
|
-
sendInitError(initError)
|
|
306
|
-
} else if (response.nack !== undefined) {
|
|
421
|
+
if (response.nack !== undefined) {
|
|
307
422
|
post(response.nack)
|
|
308
423
|
}
|
|
309
424
|
}
|
|
@@ -322,9 +437,24 @@ export function createCustomBlockHost(
|
|
|
322
437
|
const message = parsed.output
|
|
323
438
|
emit("in", message)
|
|
324
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
|
+
|
|
325
452
|
switch (message.type) {
|
|
326
|
-
case "
|
|
327
|
-
|
|
453
|
+
case "connect":
|
|
454
|
+
handleConnect(message)
|
|
455
|
+
return
|
|
456
|
+
case "initResult":
|
|
457
|
+
handleInitResult(message)
|
|
328
458
|
return
|
|
329
459
|
case "queryDataSource":
|
|
330
460
|
void handleQuery(message)
|
|
@@ -361,26 +491,62 @@ export function createCustomBlockHost(
|
|
|
361
491
|
stop() {
|
|
362
492
|
window.removeEventListener("message", onMessage)
|
|
363
493
|
iframe.removeEventListener("load", onIframeLoad)
|
|
364
|
-
|
|
494
|
+
clearNoConnectTimeout()
|
|
495
|
+
clearNoInitResultTimeout()
|
|
496
|
+
querySubscriptions.clear()
|
|
365
497
|
},
|
|
366
498
|
post,
|
|
367
499
|
setTheme(nextTheme) {
|
|
368
500
|
theme = nextTheme
|
|
501
|
+
if (initStatus !== "success") {
|
|
502
|
+
if (initStatus === "waitingForInitResult") {
|
|
503
|
+
pendingThemeChange = true
|
|
504
|
+
}
|
|
505
|
+
return
|
|
506
|
+
}
|
|
369
507
|
const message: ThemeChangedMessage = { type: "themeChanged", theme }
|
|
370
508
|
post(message)
|
|
371
509
|
},
|
|
372
510
|
setParent(nextParent) {
|
|
373
511
|
parent = nextParent
|
|
512
|
+
if (initStatus !== "success") {
|
|
513
|
+
if (initStatus === "waitingForInitResult") {
|
|
514
|
+
pendingParentChange = true
|
|
515
|
+
}
|
|
516
|
+
return
|
|
517
|
+
}
|
|
374
518
|
const message: ParentChangedMessage = { type: "parentChanged", parent }
|
|
375
519
|
post(message)
|
|
376
520
|
},
|
|
377
521
|
setPage(nextPage) {
|
|
378
522
|
page = nextPage
|
|
523
|
+
if (initStatus !== "success") {
|
|
524
|
+
if (initStatus === "waitingForInitResult") {
|
|
525
|
+
pendingPageChange = true
|
|
526
|
+
}
|
|
527
|
+
return
|
|
528
|
+
}
|
|
379
529
|
const message: PageChangedMessage = { type: "pageChanged", page }
|
|
380
530
|
post(message)
|
|
381
531
|
},
|
|
382
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
|
+
}
|
|
383
543
|
dataSources = nextDataSources
|
|
544
|
+
if (initStatus !== "success") {
|
|
545
|
+
if (initStatus === "waitingForInitResult") {
|
|
546
|
+
pendingDataSourcesChange = nextDataSources
|
|
547
|
+
}
|
|
548
|
+
return
|
|
549
|
+
}
|
|
384
550
|
const message: DataSourcesChangedMessage = {
|
|
385
551
|
type: "dataSourcesChanged",
|
|
386
552
|
dataSources: nextDataSources,
|
|
@@ -389,22 +555,61 @@ export function createCustomBlockHost(
|
|
|
389
555
|
},
|
|
390
556
|
setCurrentUser(nextCurrentUser) {
|
|
391
557
|
currentUser = nextCurrentUser
|
|
558
|
+
if (initStatus !== "success") {
|
|
559
|
+
if (initStatus === "waitingForInitResult") {
|
|
560
|
+
pendingCurrentUserChange = true
|
|
561
|
+
}
|
|
562
|
+
return
|
|
563
|
+
}
|
|
392
564
|
post({ type: "currentUserChanged", currentUser })
|
|
393
565
|
},
|
|
394
566
|
refreshQuery({ dataSourceId, response }) {
|
|
395
|
-
|
|
567
|
+
if (initStatus !== "success") {
|
|
568
|
+
return
|
|
569
|
+
}
|
|
570
|
+
const refreshes = querySubscriptions.resolveRefreshes({
|
|
396
571
|
dataSourceId,
|
|
397
572
|
response,
|
|
398
573
|
})
|
|
399
|
-
if (
|
|
574
|
+
if (refreshes.length === 0) {
|
|
400
575
|
emit("warn", `No active query exists for data source "${dataSourceId}"`)
|
|
401
576
|
return
|
|
402
577
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
response: refresh.response,
|
|
407
|
-
})
|
|
578
|
+
for (const refresh of refreshes) {
|
|
579
|
+
postQueryDataSourceResult(refresh)
|
|
580
|
+
}
|
|
408
581
|
},
|
|
409
582
|
}
|
|
410
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
|
}
|