@notionhq/custom-blocks-host 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +204 -0
- package/dist/autoBindProperties.d.ts +21 -0
- package/dist/autoBindProperties.d.ts.map +1 -0
- package/dist/autoBindProperties.js +38 -0
- package/dist/createCustomBlockHost.d.ts +46 -0
- package/dist/createCustomBlockHost.d.ts.map +1 -0
- package/dist/createCustomBlockHost.js +433 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/lifecycle/initErrors.d.ts +13 -0
- package/dist/lifecycle/initErrors.d.ts.map +1 -0
- package/dist/lifecycle/initErrors.js +152 -0
- package/dist/lifecycle/initializationController.d.ts +54 -0
- package/dist/lifecycle/initializationController.d.ts.map +1 -0
- package/dist/lifecycle/initializationController.js +182 -0
- package/dist/lifecycle/protocolVersion.d.ts +2 -0
- package/dist/lifecycle/protocolVersion.d.ts.map +1 -0
- package/dist/lifecycle/protocolVersion.js +3 -0
- package/dist/lifecycle/types.d.ts +28 -0
- package/dist/lifecycle/types.d.ts.map +1 -0
- package/dist/lifecycle/types.js +1 -0
- package/dist/messages/invalidSandboxMessage.d.ts +7 -0
- package/dist/messages/invalidSandboxMessage.d.ts.map +1 -0
- package/dist/messages/invalidSandboxMessage.js +21 -0
- package/dist/messages/types.d.ts +29 -0
- package/dist/messages/types.d.ts.map +1 -0
- package/dist/messages/types.js +1 -0
- package/dist/protocol/contrast.d.ts +4 -0
- package/dist/protocol/contrast.js +3 -0
- package/dist/protocol/dataSources/dataSource.d.ts +731 -0
- package/dist/protocol/dataSources/dataSource.js +37 -0
- package/dist/protocol/dataSources/dataSourcePage.d.ts +72 -0
- package/dist/protocol/dataSources/dataSourcePage.js +12 -0
- package/dist/protocol/dataSources/dataSourceValue.d.ts +66 -0
- package/dist/protocol/dataSources/dataSourceValue.js +14 -0
- package/dist/protocol/dataSources/dateValue.d.ts +157 -0
- package/dist/protocol/dataSources/dateValue.js +59 -0
- package/dist/protocol/dataSources/errors.d.ts +8 -0
- package/dist/protocol/dataSources/errors.js +7 -0
- package/dist/protocol/dataSources/propertySchema.d.ts +197 -0
- package/dist/protocol/dataSources/propertySchema.js +165 -0
- package/dist/protocol/dataSources/recordPointer.d.ts +9 -0
- package/dist/protocol/dataSources/recordPointer.js +8 -0
- package/dist/protocol/errors.d.ts +12 -0
- package/dist/protocol/errors.js +6 -0
- package/dist/protocol/ids.d.ts +31 -0
- package/dist/protocol/ids.js +26 -0
- package/dist/protocol/incomingType.d.ts +9 -0
- package/dist/protocol/incomingType.js +17 -0
- package/dist/protocol/index.d.ts +48 -0
- package/dist/protocol/index.js +48 -0
- package/dist/protocol/manifest.d.ts +60 -0
- package/dist/protocol/manifest.js +32 -0
- package/dist/protocol/messagePayload.d.ts +19 -0
- package/dist/protocol/messagePayload.js +1 -0
- package/dist/protocol/messages/connect.d.ts +47 -0
- package/dist/protocol/messages/connect.js +38 -0
- package/dist/protocol/messages/contrastModeChanged.d.ts +7 -0
- package/dist/protocol/messages/contrastModeChanged.js +7 -0
- package/dist/protocol/messages/createPage.d.ts +200 -0
- package/dist/protocol/messages/createPage.js +52 -0
- package/dist/protocol/messages/createPageResult.d.ts +213 -0
- package/dist/protocol/messages/createPageResult.js +28 -0
- package/dist/protocol/messages/currentUserChanged.d.ts +18 -0
- package/dist/protocol/messages/currentUserChanged.js +9 -0
- package/dist/protocol/messages/dataSourcesChanged.d.ts +157 -0
- package/dist/protocol/messages/dataSourcesChanged.js +13 -0
- package/dist/protocol/messages/getPage.d.ts +218 -0
- package/dist/protocol/messages/getPage.js +33 -0
- package/dist/protocol/messages/getUser.d.ts +44 -0
- package/dist/protocol/messages/getUser.js +32 -0
- package/dist/protocol/messages/hostToSandbox.d.ts +1167 -0
- package/dist/protocol/messages/hostToSandbox.js +34 -0
- package/dist/protocol/messages/init.d.ts +259 -0
- package/dist/protocol/messages/init.js +56 -0
- package/dist/protocol/messages/initResult.d.ts +37 -0
- package/dist/protocol/messages/initResult.js +28 -0
- package/dist/protocol/messages/invalidHostMessage.d.ts +14 -0
- package/dist/protocol/messages/invalidHostMessage.js +13 -0
- package/dist/protocol/messages/invalidSandboxMessage.d.ts +14 -0
- package/dist/protocol/messages/invalidSandboxMessage.js +13 -0
- package/dist/protocol/messages/listUsers.d.ts +52 -0
- package/dist/protocol/messages/listUsers.js +33 -0
- package/dist/protocol/messages/pageChanged.d.ts +27 -0
- package/dist/protocol/messages/pageChanged.js +9 -0
- package/dist/protocol/messages/parentChanged.d.ts +24 -0
- package/dist/protocol/messages/parentChanged.js +9 -0
- package/dist/protocol/messages/queryDataSource.d.ts +985 -0
- package/dist/protocol/messages/queryDataSource.js +123 -0
- package/dist/protocol/messages/queryDataSourceResult.d.ts +93 -0
- package/dist/protocol/messages/queryDataSourceResult.js +28 -0
- package/dist/protocol/messages/resize.d.ts +11 -0
- package/dist/protocol/messages/resize.js +10 -0
- package/dist/protocol/messages/sandboxToHost.d.ts +744 -0
- package/dist/protocol/messages/sandboxToHost.js +27 -0
- package/dist/protocol/messages/themeChanged.d.ts +10 -0
- package/dist/protocol/messages/themeChanged.js +10 -0
- package/dist/protocol/messages/updatePage.d.ts +180 -0
- package/dist/protocol/messages/updatePage.js +22 -0
- package/dist/protocol/messages/updatePageResult.d.ts +212 -0
- package/dist/protocol/messages/updatePageResult.js +27 -0
- package/dist/protocol/pages/page.d.ts +597 -0
- package/dist/protocol/pages/page.js +212 -0
- package/dist/protocol/parent.d.ts +28 -0
- package/dist/protocol/parent.js +12 -0
- package/dist/protocol/protocolVersion.d.ts +6 -0
- package/dist/protocol/protocolVersion.js +6 -0
- package/dist/protocol/theme.d.ts +3 -0
- package/dist/protocol/theme.js +2 -0
- package/dist/protocol/users/user.d.ts +32 -0
- package/dist/protocol/users/user.js +20 -0
- package/dist/queries/querySubscriptions.d.ts +17 -0
- package/dist/queries/querySubscriptions.d.ts.map +1 -0
- package/dist/queries/querySubscriptions.js +38 -0
- package/dist/queries/types.d.ts +4 -0
- package/dist/queries/types.d.ts.map +1 -0
- package/dist/queries/types.js +1 -0
- package/dist/queries/validateQueryDataSourceFilter.d.ts +5 -0
- package/dist/queries/validateQueryDataSourceFilter.d.ts.map +1 -0
- package/dist/queries/validateQueryDataSourceFilter.js +72 -0
- package/dist/queries/validateQueryDataSourceSorts.d.ts +8 -0
- package/dist/queries/validateQueryDataSourceSorts.d.ts.map +1 -0
- package/dist/queries/validateQueryDataSourceSorts.js +47 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +6 -0
- package/package.json +26 -0
- package/src/autoBindProperties.ts +63 -0
- package/src/createCustomBlockHost.ts +605 -0
- package/src/index.ts +25 -0
- package/src/lifecycle/initErrors.ts +165 -0
- package/src/lifecycle/initializationController.ts +290 -0
- package/src/lifecycle/protocolVersion.ts +3 -0
- package/src/lifecycle/types.ts +40 -0
- package/src/messages/invalidSandboxMessage.ts +35 -0
- package/src/messages/types.ts +80 -0
- package/src/queries/querySubscriptions.ts +57 -0
- package/src/queries/types.ts +10 -0
- package/src/queries/validateQueryDataSourceFilter.ts +101 -0
- package/src/queries/validateQueryDataSourceSorts.ts +69 -0
- package/src/utils.ts +8 -0
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_CONTRAST_MODE,
|
|
3
|
+
type NotionContrastMode,
|
|
4
|
+
} from "@notionhq/custom-blocks-protocol/contrast.js"
|
|
5
|
+
import type { NotionDataSourceId } from "@notionhq/custom-blocks-protocol/ids.js"
|
|
6
|
+
import type { CustomBlockManifest } from "@notionhq/custom-blocks-protocol/manifest.js"
|
|
7
|
+
import type { ConnectMessage } from "@notionhq/custom-blocks-protocol/messages/connect.js"
|
|
8
|
+
import type { ContrastModeChangedMessage } from "@notionhq/custom-blocks-protocol/messages/contrastModeChanged.js"
|
|
9
|
+
import type { CreatePageMessage } from "@notionhq/custom-blocks-protocol/messages/createPage.js"
|
|
10
|
+
import type { CreatePageResultMessage } from "@notionhq/custom-blocks-protocol/messages/createPageResult.js"
|
|
11
|
+
import type { DataSourcesChangedMessage } from "@notionhq/custom-blocks-protocol/messages/dataSourcesChanged.js"
|
|
12
|
+
import type {
|
|
13
|
+
GetPageMessage,
|
|
14
|
+
GetPageResultMessage,
|
|
15
|
+
} from "@notionhq/custom-blocks-protocol/messages/getPage.js"
|
|
16
|
+
import type {
|
|
17
|
+
GetUserMessage,
|
|
18
|
+
GetUserResultMessage,
|
|
19
|
+
} from "@notionhq/custom-blocks-protocol/messages/getUser.js"
|
|
20
|
+
import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
|
|
21
|
+
import type { CustomBlockInitErrorCode } from "@notionhq/custom-blocks-protocol/messages/init.js"
|
|
22
|
+
import type { InitResultMessage } from "@notionhq/custom-blocks-protocol/messages/initResult.js"
|
|
23
|
+
import type {
|
|
24
|
+
ListUsersMessage,
|
|
25
|
+
ListUsersResultMessage,
|
|
26
|
+
} from "@notionhq/custom-blocks-protocol/messages/listUsers.js"
|
|
27
|
+
import type { PageChangedMessage } from "@notionhq/custom-blocks-protocol/messages/pageChanged.js"
|
|
28
|
+
import type { ParentChangedMessage } from "@notionhq/custom-blocks-protocol/messages/parentChanged.js"
|
|
29
|
+
import type { QueryDataSourceMessage } from "@notionhq/custom-blocks-protocol/messages/queryDataSource.js"
|
|
30
|
+
import type { QueryDataSourceResultMessage } from "@notionhq/custom-blocks-protocol/messages/queryDataSourceResult.js"
|
|
31
|
+
import { sandboxToHostMessageSchema } from "@notionhq/custom-blocks-protocol/messages/sandboxToHost.js"
|
|
32
|
+
import type { ThemeChangedMessage } from "@notionhq/custom-blocks-protocol/messages/themeChanged.js"
|
|
33
|
+
import type { UpdatePageMessage } from "@notionhq/custom-blocks-protocol/messages/updatePage.js"
|
|
34
|
+
import type { UpdatePageResultMessage } from "@notionhq/custom-blocks-protocol/messages/updatePageResult.js"
|
|
35
|
+
import type { CustomBlockPage } from "@notionhq/custom-blocks-protocol/pages/page.js"
|
|
36
|
+
import type { NotionParent } from "@notionhq/custom-blocks-protocol/parent.js"
|
|
37
|
+
import { CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION } from "@notionhq/custom-blocks-protocol/protocolVersion.js"
|
|
38
|
+
import type { NotionTheme } from "@notionhq/custom-blocks-protocol/theme.js"
|
|
39
|
+
import type { NotionUser } from "@notionhq/custom-blocks-protocol/users/user.js"
|
|
40
|
+
import * as v from "valibot"
|
|
41
|
+
import { initErrorForFailureReason } from "./lifecycle/initErrors.js"
|
|
42
|
+
import { createInitializationController } from "./lifecycle/initializationController.js"
|
|
43
|
+
import type {
|
|
44
|
+
ConnectSuccessMessage,
|
|
45
|
+
CustomBlockHostDataSourcesPayload,
|
|
46
|
+
CustomBlockHostInitialState,
|
|
47
|
+
} from "./lifecycle/types.js"
|
|
48
|
+
import { getInvalidSandboxMessageResponse } from "./messages/invalidSandboxMessage.js"
|
|
49
|
+
import type {
|
|
50
|
+
CustomBlockHostHandlers,
|
|
51
|
+
CustomBlockHostLogDirection,
|
|
52
|
+
} from "./messages/types.js"
|
|
53
|
+
import {
|
|
54
|
+
QuerySubscriptions,
|
|
55
|
+
type QuerySubscriptionToken,
|
|
56
|
+
} from "./queries/querySubscriptions.js"
|
|
57
|
+
import type { CustomBlockHostQueryDataSourceResult } from "./queries/types.js"
|
|
58
|
+
import { validateQueryDataSourceFilter } from "./queries/validateQueryDataSourceFilter.js"
|
|
59
|
+
import { validateQueryDataSourceSorts } from "./queries/validateQueryDataSourceSorts.js"
|
|
60
|
+
import { unreachable } from "./utils.js"
|
|
61
|
+
|
|
62
|
+
export type CustomBlockHostOptions = {
|
|
63
|
+
iframe: HTMLIFrameElement
|
|
64
|
+
sandboxOrigin: string
|
|
65
|
+
minBridgeProtocolVersion: number
|
|
66
|
+
noConnectTimeoutMs?: number
|
|
67
|
+
noInitResultTimeoutMs?: number
|
|
68
|
+
initialState: CustomBlockHostInitialState
|
|
69
|
+
handlers: CustomBlockHostHandlers
|
|
70
|
+
onConnect?: (message: ConnectSuccessMessage) => void
|
|
71
|
+
onInitResult?: (message: InitResultMessage) => void
|
|
72
|
+
/**
|
|
73
|
+
* A `connect` arrived after this host's initialization settled — the
|
|
74
|
+
* document was replaced, or its first `connect` came after the host
|
|
75
|
+
* stopped waiting. Either way the host cannot serve it: the embedder
|
|
76
|
+
* should recreate host and iframe together so the document can run a
|
|
77
|
+
* fresh handshake.
|
|
78
|
+
*/
|
|
79
|
+
onIgnoredConnect?: (message: ConnectMessage) => void
|
|
80
|
+
onLog?: (direction: CustomBlockHostLogDirection, payload: unknown) => void
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type CustomBlockHostHandle = {
|
|
84
|
+
stop: () => void
|
|
85
|
+
post: (message: HostToSandboxMessage) => void
|
|
86
|
+
setTheme: (theme: NotionTheme) => void
|
|
87
|
+
setContrastMode: (contrastMode: NotionContrastMode) => void
|
|
88
|
+
setParent: (parent: NotionParent) => void
|
|
89
|
+
setPage: (page: CustomBlockPage) => void
|
|
90
|
+
setDataSources: (dataSources: CustomBlockHostDataSourcesPayload) => void
|
|
91
|
+
setCurrentUser: (currentUser: NotionUser) => void
|
|
92
|
+
refreshQuery: (args: { dataSourceId: NotionDataSourceId }) => void
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const initializationIdentitySchema = v.object({
|
|
96
|
+
initializationId: v.string(),
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
export function createCustomBlockHost(
|
|
100
|
+
options: CustomBlockHostOptions,
|
|
101
|
+
): CustomBlockHostHandle {
|
|
102
|
+
const {
|
|
103
|
+
iframe,
|
|
104
|
+
initialState,
|
|
105
|
+
handlers,
|
|
106
|
+
onConnect,
|
|
107
|
+
onInitResult,
|
|
108
|
+
onIgnoredConnect,
|
|
109
|
+
onLog,
|
|
110
|
+
sandboxOrigin,
|
|
111
|
+
minBridgeProtocolVersion: configuredMinBridgeProtocolVersion,
|
|
112
|
+
noConnectTimeoutMs,
|
|
113
|
+
noInitResultTimeoutMs,
|
|
114
|
+
} = options
|
|
115
|
+
const minBridgeProtocolVersion = Math.max(
|
|
116
|
+
configuredMinBridgeProtocolVersion ?? CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION,
|
|
117
|
+
CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
let theme = initialState.theme
|
|
121
|
+
let contrastMode = initialState.contrastMode ?? DEFAULT_CONTRAST_MODE
|
|
122
|
+
let parent = initialState.parent
|
|
123
|
+
let page = initialState.page
|
|
124
|
+
let dataSources = initialState.dataSources
|
|
125
|
+
let activeDataSources: CustomBlockHostDataSourcesPayload | undefined
|
|
126
|
+
let manifest = initialState.manifest
|
|
127
|
+
let currentUser = initialState.currentUser
|
|
128
|
+
let pendingThemeChange = false
|
|
129
|
+
let pendingContrastModeChange = false
|
|
130
|
+
let pendingParentChange = false
|
|
131
|
+
let pendingPageChange = false
|
|
132
|
+
let pendingDataSourcesChange: CustomBlockHostDataSourcesPayload | undefined
|
|
133
|
+
let pendingCurrentUserChange = false
|
|
134
|
+
const querySubscriptions = new QuerySubscriptions()
|
|
135
|
+
|
|
136
|
+
function emit(direction: CustomBlockHostLogDirection, payload: unknown) {
|
|
137
|
+
onLog?.(direction, payload)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function post(message: HostToSandboxMessage) {
|
|
141
|
+
emit("out", message)
|
|
142
|
+
iframe.contentWindow?.postMessage(message, sandboxOrigin)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function postQueryDataSourceResult(args: {
|
|
146
|
+
subscriptionId: string
|
|
147
|
+
response: CustomBlockHostQueryDataSourceResult
|
|
148
|
+
}) {
|
|
149
|
+
const message: QueryDataSourceResultMessage =
|
|
150
|
+
args.response.status === "error"
|
|
151
|
+
? {
|
|
152
|
+
type: "queryDataSourceResult",
|
|
153
|
+
subscriptionId: args.subscriptionId,
|
|
154
|
+
status: "error",
|
|
155
|
+
error: args.response.error,
|
|
156
|
+
}
|
|
157
|
+
: {
|
|
158
|
+
type: "queryDataSourceResult",
|
|
159
|
+
subscriptionId: args.subscriptionId,
|
|
160
|
+
status: "success",
|
|
161
|
+
items: args.response.items,
|
|
162
|
+
hasMore: args.response.hasMore,
|
|
163
|
+
}
|
|
164
|
+
post(message)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function handleQuery(
|
|
168
|
+
message: QueryDataSourceMessage,
|
|
169
|
+
token: QuerySubscriptionToken = querySubscriptions.track(message),
|
|
170
|
+
) {
|
|
171
|
+
const collectionSchema = getCollectionSchema(
|
|
172
|
+
activeDataSources,
|
|
173
|
+
message.dataSourceId,
|
|
174
|
+
)
|
|
175
|
+
const filterError = validateQueryDataSourceFilter(
|
|
176
|
+
message.filter,
|
|
177
|
+
collectionSchema,
|
|
178
|
+
)
|
|
179
|
+
if (filterError !== undefined) {
|
|
180
|
+
if (querySubscriptions.isCurrent(message.subscriptionId, token)) {
|
|
181
|
+
postQueryDataSourceResult({
|
|
182
|
+
subscriptionId: message.subscriptionId,
|
|
183
|
+
response: { status: "error", error: filterError },
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
const sortError = validateQueryDataSourceSorts(
|
|
189
|
+
message.sorts,
|
|
190
|
+
collectionSchema,
|
|
191
|
+
)
|
|
192
|
+
if (sortError !== undefined) {
|
|
193
|
+
if (querySubscriptions.isCurrent(message.subscriptionId, token)) {
|
|
194
|
+
postQueryDataSourceResult({
|
|
195
|
+
subscriptionId: message.subscriptionId,
|
|
196
|
+
response: { status: "error", error: sortError },
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
const response = await handlers.queryDataSource(message)
|
|
202
|
+
if (!querySubscriptions.isCurrent(message.subscriptionId, token)) {
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
postQueryDataSourceResult({
|
|
206
|
+
subscriptionId: message.subscriptionId,
|
|
207
|
+
response,
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function handleCreatePage(message: CreatePageMessage) {
|
|
212
|
+
if (handlers.createPage === undefined) {
|
|
213
|
+
return
|
|
214
|
+
}
|
|
215
|
+
const response = await handlers.createPage(message)
|
|
216
|
+
const result: CreatePageResultMessage = {
|
|
217
|
+
type: "createPageResult",
|
|
218
|
+
requestId: message.requestId,
|
|
219
|
+
...response,
|
|
220
|
+
}
|
|
221
|
+
post(result)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function handleGetPage(message: GetPageMessage) {
|
|
225
|
+
if (handlers.getPage === undefined) {
|
|
226
|
+
return
|
|
227
|
+
}
|
|
228
|
+
const response = await handlers.getPage(message)
|
|
229
|
+
const result: GetPageResultMessage = {
|
|
230
|
+
type: "getPageResult",
|
|
231
|
+
requestId: message.requestId,
|
|
232
|
+
...response,
|
|
233
|
+
}
|
|
234
|
+
post(result)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async function handleGetUser(message: GetUserMessage) {
|
|
238
|
+
if (handlers.getUser === undefined) {
|
|
239
|
+
return
|
|
240
|
+
}
|
|
241
|
+
const response = await handlers.getUser(message)
|
|
242
|
+
const result: GetUserResultMessage = {
|
|
243
|
+
type: "getUserResult",
|
|
244
|
+
requestId: message.requestId,
|
|
245
|
+
...response,
|
|
246
|
+
}
|
|
247
|
+
post(result)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function handleListUsers(message: ListUsersMessage) {
|
|
251
|
+
if (handlers.listUsers === undefined) {
|
|
252
|
+
return
|
|
253
|
+
}
|
|
254
|
+
const response = await handlers.listUsers(message)
|
|
255
|
+
const result: ListUsersResultMessage = {
|
|
256
|
+
type: "listUsersResult",
|
|
257
|
+
requestId: message.requestId,
|
|
258
|
+
...response,
|
|
259
|
+
}
|
|
260
|
+
post(result)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async function handleUpdatePage(message: UpdatePageMessage) {
|
|
264
|
+
if (handlers.updatePage === undefined) {
|
|
265
|
+
return
|
|
266
|
+
}
|
|
267
|
+
const response = await handlers.updatePage(message)
|
|
268
|
+
const result: UpdatePageResultMessage = {
|
|
269
|
+
type: "updatePageResult",
|
|
270
|
+
requestId: message.requestId,
|
|
271
|
+
...response,
|
|
272
|
+
}
|
|
273
|
+
post(result)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Sends any pending host state changes to the sandbox. It is possible for these to change
|
|
278
|
+
* between when the host sends `init` and when the sandbox responds with `initResult`. The host
|
|
279
|
+
* does send any pending state changes until hearing back from the sandbox.
|
|
280
|
+
*/
|
|
281
|
+
function flushPendingHostStateChanges() {
|
|
282
|
+
if (pendingThemeChange) {
|
|
283
|
+
pendingThemeChange = false
|
|
284
|
+
post({ type: "themeChanged", theme })
|
|
285
|
+
}
|
|
286
|
+
if (pendingContrastModeChange) {
|
|
287
|
+
pendingContrastModeChange = false
|
|
288
|
+
post({ type: "contrastModeChanged", contrastMode })
|
|
289
|
+
}
|
|
290
|
+
if (pendingParentChange) {
|
|
291
|
+
pendingParentChange = false
|
|
292
|
+
post({ type: "parentChanged", parent })
|
|
293
|
+
}
|
|
294
|
+
if (pendingPageChange) {
|
|
295
|
+
pendingPageChange = false
|
|
296
|
+
post({ type: "pageChanged", page })
|
|
297
|
+
}
|
|
298
|
+
if (pendingDataSourcesChange !== undefined) {
|
|
299
|
+
const nextDataSources = pendingDataSourcesChange
|
|
300
|
+
pendingDataSourcesChange = undefined
|
|
301
|
+
post({ type: "dataSourcesChanged", dataSources: nextDataSources })
|
|
302
|
+
}
|
|
303
|
+
if (pendingCurrentUserChange) {
|
|
304
|
+
pendingCurrentUserChange = false
|
|
305
|
+
post({ type: "currentUserChanged", currentUser })
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const initializationController = createInitializationController({
|
|
310
|
+
blockId: initialState.blockId,
|
|
311
|
+
getState: () => ({
|
|
312
|
+
theme,
|
|
313
|
+
contrastMode,
|
|
314
|
+
parent,
|
|
315
|
+
page,
|
|
316
|
+
currentUser,
|
|
317
|
+
}),
|
|
318
|
+
prepareInit(connect) {
|
|
319
|
+
manifest = connect.manifest ?? initialState.manifest
|
|
320
|
+
const resolvedDataSources =
|
|
321
|
+
typeof dataSources === "function" ? dataSources(connect) : dataSources
|
|
322
|
+
const bindingErrorCode = getBindingErrorCode(
|
|
323
|
+
manifest,
|
|
324
|
+
resolvedDataSources,
|
|
325
|
+
)
|
|
326
|
+
if (bindingErrorCode !== undefined) {
|
|
327
|
+
return {
|
|
328
|
+
status: "error",
|
|
329
|
+
error: initErrorForFailureReason(bindingErrorCode),
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
activeDataSources = resolvedDataSources
|
|
333
|
+
return {
|
|
334
|
+
status: "success",
|
|
335
|
+
manifest,
|
|
336
|
+
dataSources: resolvedDataSources,
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
post,
|
|
340
|
+
warn: payload => emit("warn", payload),
|
|
341
|
+
flushPendingHostStateChanges,
|
|
342
|
+
minBridgeProtocolVersion,
|
|
343
|
+
noConnectTimeoutMs,
|
|
344
|
+
noInitResultTimeoutMs,
|
|
345
|
+
onConnect,
|
|
346
|
+
onInitResult,
|
|
347
|
+
onIgnoredConnect,
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
function onInvalidSandboxMessage(
|
|
351
|
+
data: unknown,
|
|
352
|
+
issues: readonly v.BaseIssue<unknown>[],
|
|
353
|
+
) {
|
|
354
|
+
emit("warn", {
|
|
355
|
+
message: "Ignored unsupported sandbox message",
|
|
356
|
+
payload: data,
|
|
357
|
+
issues,
|
|
358
|
+
})
|
|
359
|
+
const response = getInvalidSandboxMessageResponse(data)
|
|
360
|
+
|
|
361
|
+
const parsedIdentity = v.safeParse(initializationIdentitySchema, data)
|
|
362
|
+
const initializationId = parsedIdentity.success
|
|
363
|
+
? parsedIdentity.output.initializationId
|
|
364
|
+
: undefined
|
|
365
|
+
if (
|
|
366
|
+
response.incomingType === "connect" &&
|
|
367
|
+
initializationId !== undefined &&
|
|
368
|
+
initializationController.handleInvalidConnect(initializationId)
|
|
369
|
+
) {
|
|
370
|
+
return
|
|
371
|
+
}
|
|
372
|
+
if (
|
|
373
|
+
response.incomingType === "initResult" &&
|
|
374
|
+
initializationController.handleInvalidInitResult()
|
|
375
|
+
) {
|
|
376
|
+
if (response.nack !== undefined) {
|
|
377
|
+
post(response.nack)
|
|
378
|
+
}
|
|
379
|
+
return
|
|
380
|
+
}
|
|
381
|
+
if (response.nack !== undefined) {
|
|
382
|
+
post(response.nack)
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function onMessage(event: MessageEvent) {
|
|
387
|
+
// Ignore messages from windows other than this host's iframe.
|
|
388
|
+
if (event.source !== iframe.contentWindow) {
|
|
389
|
+
return
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Ignore messages that do not come from the configured sandbox origin.
|
|
393
|
+
if (event.origin !== sandboxOrigin) {
|
|
394
|
+
return
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const parsed = v.safeParse(sandboxToHostMessageSchema, event.data)
|
|
398
|
+
if (!parsed.success) {
|
|
399
|
+
onInvalidSandboxMessage(event.data, parsed.issues)
|
|
400
|
+
return
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const message = parsed.output
|
|
404
|
+
emit("in", message)
|
|
405
|
+
|
|
406
|
+
if (
|
|
407
|
+
message.type !== "connect" &&
|
|
408
|
+
message.type !== "initResult" &&
|
|
409
|
+
initializationController.status !== "success"
|
|
410
|
+
) {
|
|
411
|
+
// The SDK reports resize as soon as it renders, before the
|
|
412
|
+
// handshake completes, so an early resize is routine.
|
|
413
|
+
if (message.type !== "resize") {
|
|
414
|
+
emit("warn", {
|
|
415
|
+
message: `Ignored ${message.type} before initialization completed`,
|
|
416
|
+
payload: message,
|
|
417
|
+
})
|
|
418
|
+
}
|
|
419
|
+
return
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
switch (message.type) {
|
|
423
|
+
case "connect":
|
|
424
|
+
initializationController.handleConnect(message)
|
|
425
|
+
return
|
|
426
|
+
case "initResult":
|
|
427
|
+
initializationController.handleInitResult(message)
|
|
428
|
+
return
|
|
429
|
+
case "queryDataSource":
|
|
430
|
+
void handleQuery(message)
|
|
431
|
+
return
|
|
432
|
+
case "createPage":
|
|
433
|
+
void handleCreatePage(message)
|
|
434
|
+
return
|
|
435
|
+
case "getPage":
|
|
436
|
+
void handleGetPage(message)
|
|
437
|
+
return
|
|
438
|
+
case "getUser":
|
|
439
|
+
void handleGetUser(message)
|
|
440
|
+
return
|
|
441
|
+
case "listUsers":
|
|
442
|
+
void handleListUsers(message)
|
|
443
|
+
return
|
|
444
|
+
case "updatePage":
|
|
445
|
+
void handleUpdatePage(message)
|
|
446
|
+
return
|
|
447
|
+
case "resize":
|
|
448
|
+
handlers.resize?.(message)
|
|
449
|
+
return
|
|
450
|
+
case "invalidHostMessage":
|
|
451
|
+
return
|
|
452
|
+
default:
|
|
453
|
+
unreachable(message)
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
window.addEventListener("message", onMessage)
|
|
458
|
+
iframe.addEventListener("load", initializationController.onIframeLoad)
|
|
459
|
+
|
|
460
|
+
return {
|
|
461
|
+
stop() {
|
|
462
|
+
window.removeEventListener("message", onMessage)
|
|
463
|
+
iframe.removeEventListener("load", initializationController.onIframeLoad)
|
|
464
|
+
initializationController.stop()
|
|
465
|
+
querySubscriptions.clear()
|
|
466
|
+
},
|
|
467
|
+
post,
|
|
468
|
+
setTheme(nextTheme) {
|
|
469
|
+
theme = nextTheme
|
|
470
|
+
if (initializationController.status !== "success") {
|
|
471
|
+
if (initializationController.status === "waitingForInitResult") {
|
|
472
|
+
pendingThemeChange = true
|
|
473
|
+
}
|
|
474
|
+
return
|
|
475
|
+
}
|
|
476
|
+
const message: ThemeChangedMessage = { type: "themeChanged", theme }
|
|
477
|
+
post(message)
|
|
478
|
+
},
|
|
479
|
+
setContrastMode(nextContrastMode) {
|
|
480
|
+
contrastMode = nextContrastMode
|
|
481
|
+
if (initializationController.status !== "success") {
|
|
482
|
+
if (initializationController.status === "waitingForInitResult") {
|
|
483
|
+
pendingContrastModeChange = true
|
|
484
|
+
}
|
|
485
|
+
return
|
|
486
|
+
}
|
|
487
|
+
const message: ContrastModeChangedMessage = {
|
|
488
|
+
type: "contrastModeChanged",
|
|
489
|
+
contrastMode,
|
|
490
|
+
}
|
|
491
|
+
post(message)
|
|
492
|
+
},
|
|
493
|
+
setParent(nextParent) {
|
|
494
|
+
parent = nextParent
|
|
495
|
+
if (initializationController.status !== "success") {
|
|
496
|
+
if (initializationController.status === "waitingForInitResult") {
|
|
497
|
+
pendingParentChange = true
|
|
498
|
+
}
|
|
499
|
+
return
|
|
500
|
+
}
|
|
501
|
+
const message: ParentChangedMessage = { type: "parentChanged", parent }
|
|
502
|
+
post(message)
|
|
503
|
+
},
|
|
504
|
+
setPage(nextPage) {
|
|
505
|
+
page = nextPage
|
|
506
|
+
if (initializationController.status !== "success") {
|
|
507
|
+
if (initializationController.status === "waitingForInitResult") {
|
|
508
|
+
pendingPageChange = true
|
|
509
|
+
}
|
|
510
|
+
return
|
|
511
|
+
}
|
|
512
|
+
const message: PageChangedMessage = { type: "pageChanged", page }
|
|
513
|
+
post(message)
|
|
514
|
+
},
|
|
515
|
+
setDataSources(nextDataSources) {
|
|
516
|
+
if (
|
|
517
|
+
initializationController.hasConnectedSandbox &&
|
|
518
|
+
getBindingErrorCode(manifest, nextDataSources) !== undefined
|
|
519
|
+
) {
|
|
520
|
+
emit("warn", {
|
|
521
|
+
message: "Ignored invalid data source bindings update",
|
|
522
|
+
payload: nextDataSources,
|
|
523
|
+
})
|
|
524
|
+
return
|
|
525
|
+
}
|
|
526
|
+
dataSources = nextDataSources
|
|
527
|
+
activeDataSources = nextDataSources
|
|
528
|
+
if (initializationController.status !== "success") {
|
|
529
|
+
if (initializationController.status === "waitingForInitResult") {
|
|
530
|
+
pendingDataSourcesChange = nextDataSources
|
|
531
|
+
}
|
|
532
|
+
return
|
|
533
|
+
}
|
|
534
|
+
const message: DataSourcesChangedMessage = {
|
|
535
|
+
type: "dataSourcesChanged",
|
|
536
|
+
dataSources: nextDataSources,
|
|
537
|
+
}
|
|
538
|
+
post(message)
|
|
539
|
+
},
|
|
540
|
+
setCurrentUser(nextCurrentUser) {
|
|
541
|
+
currentUser = nextCurrentUser
|
|
542
|
+
if (initializationController.status !== "success") {
|
|
543
|
+
if (initializationController.status === "waitingForInitResult") {
|
|
544
|
+
pendingCurrentUserChange = true
|
|
545
|
+
}
|
|
546
|
+
return
|
|
547
|
+
}
|
|
548
|
+
post({ type: "currentUserChanged", currentUser })
|
|
549
|
+
},
|
|
550
|
+
refreshQuery({ dataSourceId }) {
|
|
551
|
+
if (initializationController.status !== "success") {
|
|
552
|
+
return
|
|
553
|
+
}
|
|
554
|
+
const refreshes = querySubscriptions.refresh(dataSourceId)
|
|
555
|
+
if (refreshes.length === 0) {
|
|
556
|
+
emit("warn", `No active query exists for data source "${dataSourceId}"`)
|
|
557
|
+
return
|
|
558
|
+
}
|
|
559
|
+
for (const refresh of refreshes) {
|
|
560
|
+
void handleQuery(refresh.message, refresh.token)
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function getCollectionSchema(
|
|
567
|
+
dataSources: CustomBlockHostDataSourcesPayload | undefined,
|
|
568
|
+
dataSourceId: NotionDataSourceId,
|
|
569
|
+
) {
|
|
570
|
+
return Object.values(dataSources?.bindings ?? {}).find(
|
|
571
|
+
binding => binding.collectionPointer?.id === dataSourceId,
|
|
572
|
+
)?.collectionSchema
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function getBindingErrorCode(
|
|
576
|
+
manifest: CustomBlockManifest,
|
|
577
|
+
dataSources: CustomBlockHostDataSourcesPayload,
|
|
578
|
+
): CustomBlockInitErrorCode | undefined {
|
|
579
|
+
for (const [dataSourceKey, manifestDataSource] of Object.entries(
|
|
580
|
+
manifest.dataSources,
|
|
581
|
+
)) {
|
|
582
|
+
const binding = dataSources.bindings[dataSourceKey]
|
|
583
|
+
if (
|
|
584
|
+
binding?.collectionPointer === undefined ||
|
|
585
|
+
binding.collectionSchema === undefined
|
|
586
|
+
) {
|
|
587
|
+
return "missing_data_source_binding"
|
|
588
|
+
}
|
|
589
|
+
for (const [propertyKey, manifestProperty] of Object.entries(
|
|
590
|
+
manifestDataSource.properties ?? {},
|
|
591
|
+
)) {
|
|
592
|
+
const propertyId = binding.propertyIdsByKey?.[propertyKey]
|
|
593
|
+
if (propertyId === undefined) {
|
|
594
|
+
return "missing_property_binding"
|
|
595
|
+
}
|
|
596
|
+
if (
|
|
597
|
+
binding.collectionSchema.propertiesById[propertyId]?.type !==
|
|
598
|
+
manifestProperty.type
|
|
599
|
+
) {
|
|
600
|
+
return "invalid_property_binding"
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
return undefined
|
|
605
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export {
|
|
2
|
+
type AutoBindableProperty,
|
|
3
|
+
getAutoBoundPropertyIdsByKey,
|
|
4
|
+
} from "./autoBindProperties.js"
|
|
5
|
+
export {
|
|
6
|
+
type CustomBlockHostHandle,
|
|
7
|
+
type CustomBlockHostOptions,
|
|
8
|
+
createCustomBlockHost,
|
|
9
|
+
} from "./createCustomBlockHost.js"
|
|
10
|
+
export type {
|
|
11
|
+
ConnectSuccessMessage,
|
|
12
|
+
CustomBlockHostDataSourcesPayload,
|
|
13
|
+
CustomBlockHostInitialDataSources,
|
|
14
|
+
CustomBlockHostInitialState,
|
|
15
|
+
} from "./lifecycle/types.js"
|
|
16
|
+
export type {
|
|
17
|
+
CustomBlockHostCreatePageResult,
|
|
18
|
+
CustomBlockHostGetPageResult,
|
|
19
|
+
CustomBlockHostGetUserResult,
|
|
20
|
+
CustomBlockHostHandlers,
|
|
21
|
+
CustomBlockHostListUsersResult,
|
|
22
|
+
CustomBlockHostLogDirection,
|
|
23
|
+
CustomBlockHostUpdatePageResult,
|
|
24
|
+
} from "./messages/types.js"
|
|
25
|
+
export type { CustomBlockHostQueryDataSourceResult } from "./queries/types.js"
|