@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.
Files changed (142) hide show
  1. package/README.md +204 -0
  2. package/dist/autoBindProperties.d.ts +21 -0
  3. package/dist/autoBindProperties.d.ts.map +1 -0
  4. package/dist/autoBindProperties.js +38 -0
  5. package/dist/createCustomBlockHost.d.ts +46 -0
  6. package/dist/createCustomBlockHost.d.ts.map +1 -0
  7. package/dist/createCustomBlockHost.js +433 -0
  8. package/dist/index.d.ts +6 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +2 -0
  11. package/dist/lifecycle/initErrors.d.ts +13 -0
  12. package/dist/lifecycle/initErrors.d.ts.map +1 -0
  13. package/dist/lifecycle/initErrors.js +152 -0
  14. package/dist/lifecycle/initializationController.d.ts +54 -0
  15. package/dist/lifecycle/initializationController.d.ts.map +1 -0
  16. package/dist/lifecycle/initializationController.js +182 -0
  17. package/dist/lifecycle/protocolVersion.d.ts +2 -0
  18. package/dist/lifecycle/protocolVersion.d.ts.map +1 -0
  19. package/dist/lifecycle/protocolVersion.js +3 -0
  20. package/dist/lifecycle/types.d.ts +28 -0
  21. package/dist/lifecycle/types.d.ts.map +1 -0
  22. package/dist/lifecycle/types.js +1 -0
  23. package/dist/messages/invalidSandboxMessage.d.ts +7 -0
  24. package/dist/messages/invalidSandboxMessage.d.ts.map +1 -0
  25. package/dist/messages/invalidSandboxMessage.js +21 -0
  26. package/dist/messages/types.d.ts +29 -0
  27. package/dist/messages/types.d.ts.map +1 -0
  28. package/dist/messages/types.js +1 -0
  29. package/dist/protocol/contrast.d.ts +4 -0
  30. package/dist/protocol/contrast.js +3 -0
  31. package/dist/protocol/dataSources/dataSource.d.ts +731 -0
  32. package/dist/protocol/dataSources/dataSource.js +37 -0
  33. package/dist/protocol/dataSources/dataSourcePage.d.ts +72 -0
  34. package/dist/protocol/dataSources/dataSourcePage.js +12 -0
  35. package/dist/protocol/dataSources/dataSourceValue.d.ts +66 -0
  36. package/dist/protocol/dataSources/dataSourceValue.js +14 -0
  37. package/dist/protocol/dataSources/dateValue.d.ts +157 -0
  38. package/dist/protocol/dataSources/dateValue.js +59 -0
  39. package/dist/protocol/dataSources/errors.d.ts +8 -0
  40. package/dist/protocol/dataSources/errors.js +7 -0
  41. package/dist/protocol/dataSources/propertySchema.d.ts +197 -0
  42. package/dist/protocol/dataSources/propertySchema.js +165 -0
  43. package/dist/protocol/dataSources/recordPointer.d.ts +9 -0
  44. package/dist/protocol/dataSources/recordPointer.js +8 -0
  45. package/dist/protocol/errors.d.ts +12 -0
  46. package/dist/protocol/errors.js +6 -0
  47. package/dist/protocol/ids.d.ts +31 -0
  48. package/dist/protocol/ids.js +26 -0
  49. package/dist/protocol/incomingType.d.ts +9 -0
  50. package/dist/protocol/incomingType.js +17 -0
  51. package/dist/protocol/index.d.ts +48 -0
  52. package/dist/protocol/index.js +48 -0
  53. package/dist/protocol/manifest.d.ts +60 -0
  54. package/dist/protocol/manifest.js +32 -0
  55. package/dist/protocol/messagePayload.d.ts +19 -0
  56. package/dist/protocol/messagePayload.js +1 -0
  57. package/dist/protocol/messages/connect.d.ts +47 -0
  58. package/dist/protocol/messages/connect.js +38 -0
  59. package/dist/protocol/messages/contrastModeChanged.d.ts +7 -0
  60. package/dist/protocol/messages/contrastModeChanged.js +7 -0
  61. package/dist/protocol/messages/createPage.d.ts +200 -0
  62. package/dist/protocol/messages/createPage.js +52 -0
  63. package/dist/protocol/messages/createPageResult.d.ts +213 -0
  64. package/dist/protocol/messages/createPageResult.js +28 -0
  65. package/dist/protocol/messages/currentUserChanged.d.ts +18 -0
  66. package/dist/protocol/messages/currentUserChanged.js +9 -0
  67. package/dist/protocol/messages/dataSourcesChanged.d.ts +157 -0
  68. package/dist/protocol/messages/dataSourcesChanged.js +13 -0
  69. package/dist/protocol/messages/getPage.d.ts +218 -0
  70. package/dist/protocol/messages/getPage.js +33 -0
  71. package/dist/protocol/messages/getUser.d.ts +44 -0
  72. package/dist/protocol/messages/getUser.js +32 -0
  73. package/dist/protocol/messages/hostToSandbox.d.ts +1167 -0
  74. package/dist/protocol/messages/hostToSandbox.js +34 -0
  75. package/dist/protocol/messages/init.d.ts +259 -0
  76. package/dist/protocol/messages/init.js +56 -0
  77. package/dist/protocol/messages/initResult.d.ts +37 -0
  78. package/dist/protocol/messages/initResult.js +28 -0
  79. package/dist/protocol/messages/invalidHostMessage.d.ts +14 -0
  80. package/dist/protocol/messages/invalidHostMessage.js +13 -0
  81. package/dist/protocol/messages/invalidSandboxMessage.d.ts +14 -0
  82. package/dist/protocol/messages/invalidSandboxMessage.js +13 -0
  83. package/dist/protocol/messages/listUsers.d.ts +52 -0
  84. package/dist/protocol/messages/listUsers.js +33 -0
  85. package/dist/protocol/messages/pageChanged.d.ts +27 -0
  86. package/dist/protocol/messages/pageChanged.js +9 -0
  87. package/dist/protocol/messages/parentChanged.d.ts +24 -0
  88. package/dist/protocol/messages/parentChanged.js +9 -0
  89. package/dist/protocol/messages/queryDataSource.d.ts +985 -0
  90. package/dist/protocol/messages/queryDataSource.js +123 -0
  91. package/dist/protocol/messages/queryDataSourceResult.d.ts +93 -0
  92. package/dist/protocol/messages/queryDataSourceResult.js +28 -0
  93. package/dist/protocol/messages/resize.d.ts +11 -0
  94. package/dist/protocol/messages/resize.js +10 -0
  95. package/dist/protocol/messages/sandboxToHost.d.ts +744 -0
  96. package/dist/protocol/messages/sandboxToHost.js +27 -0
  97. package/dist/protocol/messages/themeChanged.d.ts +10 -0
  98. package/dist/protocol/messages/themeChanged.js +10 -0
  99. package/dist/protocol/messages/updatePage.d.ts +180 -0
  100. package/dist/protocol/messages/updatePage.js +22 -0
  101. package/dist/protocol/messages/updatePageResult.d.ts +212 -0
  102. package/dist/protocol/messages/updatePageResult.js +27 -0
  103. package/dist/protocol/pages/page.d.ts +597 -0
  104. package/dist/protocol/pages/page.js +212 -0
  105. package/dist/protocol/parent.d.ts +28 -0
  106. package/dist/protocol/parent.js +12 -0
  107. package/dist/protocol/protocolVersion.d.ts +6 -0
  108. package/dist/protocol/protocolVersion.js +6 -0
  109. package/dist/protocol/theme.d.ts +3 -0
  110. package/dist/protocol/theme.js +2 -0
  111. package/dist/protocol/users/user.d.ts +32 -0
  112. package/dist/protocol/users/user.js +20 -0
  113. package/dist/queries/querySubscriptions.d.ts +17 -0
  114. package/dist/queries/querySubscriptions.d.ts.map +1 -0
  115. package/dist/queries/querySubscriptions.js +38 -0
  116. package/dist/queries/types.d.ts +4 -0
  117. package/dist/queries/types.d.ts.map +1 -0
  118. package/dist/queries/types.js +1 -0
  119. package/dist/queries/validateQueryDataSourceFilter.d.ts +5 -0
  120. package/dist/queries/validateQueryDataSourceFilter.d.ts.map +1 -0
  121. package/dist/queries/validateQueryDataSourceFilter.js +72 -0
  122. package/dist/queries/validateQueryDataSourceSorts.d.ts +8 -0
  123. package/dist/queries/validateQueryDataSourceSorts.d.ts.map +1 -0
  124. package/dist/queries/validateQueryDataSourceSorts.js +47 -0
  125. package/dist/utils.d.ts +5 -0
  126. package/dist/utils.d.ts.map +1 -0
  127. package/dist/utils.js +6 -0
  128. package/package.json +26 -0
  129. package/src/autoBindProperties.ts +63 -0
  130. package/src/createCustomBlockHost.ts +605 -0
  131. package/src/index.ts +25 -0
  132. package/src/lifecycle/initErrors.ts +165 -0
  133. package/src/lifecycle/initializationController.ts +290 -0
  134. package/src/lifecycle/protocolVersion.ts +3 -0
  135. package/src/lifecycle/types.ts +40 -0
  136. package/src/messages/invalidSandboxMessage.ts +35 -0
  137. package/src/messages/types.ts +80 -0
  138. package/src/queries/querySubscriptions.ts +57 -0
  139. package/src/queries/types.ts +10 -0
  140. package/src/queries/validateQueryDataSourceFilter.ts +101 -0
  141. package/src/queries/validateQueryDataSourceSorts.ts +69 -0
  142. package/src/utils.ts +8 -0
@@ -0,0 +1,433 @@
1
+ import { DEFAULT_CONTRAST_MODE, } from "./protocol/contrast.js";
2
+ import { sandboxToHostMessageSchema } from "./protocol/messages/sandboxToHost.js";
3
+ import { CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION } from "./protocol/protocolVersion.js";
4
+ import * as v from "valibot";
5
+ import { initErrorForFailureReason } from "./lifecycle/initErrors.js";
6
+ import { createInitializationController } from "./lifecycle/initializationController.js";
7
+ import { getInvalidSandboxMessageResponse } from "./messages/invalidSandboxMessage.js";
8
+ import { QuerySubscriptions, } from "./queries/querySubscriptions.js";
9
+ import { validateQueryDataSourceFilter } from "./queries/validateQueryDataSourceFilter.js";
10
+ import { validateQueryDataSourceSorts } from "./queries/validateQueryDataSourceSorts.js";
11
+ import { unreachable } from "./utils.js";
12
+ const initializationIdentitySchema = v.object({
13
+ initializationId: v.string(),
14
+ });
15
+ export function createCustomBlockHost(options) {
16
+ const { iframe, initialState, handlers, onConnect, onInitResult, onIgnoredConnect, onLog, sandboxOrigin, minBridgeProtocolVersion: configuredMinBridgeProtocolVersion, noConnectTimeoutMs, noInitResultTimeoutMs, } = options;
17
+ const minBridgeProtocolVersion = Math.max(configuredMinBridgeProtocolVersion ?? CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION, CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION);
18
+ let theme = initialState.theme;
19
+ let contrastMode = initialState.contrastMode ?? DEFAULT_CONTRAST_MODE;
20
+ let parent = initialState.parent;
21
+ let page = initialState.page;
22
+ let dataSources = initialState.dataSources;
23
+ let activeDataSources;
24
+ let manifest = initialState.manifest;
25
+ let currentUser = initialState.currentUser;
26
+ let pendingThemeChange = false;
27
+ let pendingContrastModeChange = false;
28
+ let pendingParentChange = false;
29
+ let pendingPageChange = false;
30
+ let pendingDataSourcesChange;
31
+ let pendingCurrentUserChange = false;
32
+ const querySubscriptions = new QuerySubscriptions();
33
+ function emit(direction, payload) {
34
+ onLog?.(direction, payload);
35
+ }
36
+ function post(message) {
37
+ emit("out", message);
38
+ iframe.contentWindow?.postMessage(message, sandboxOrigin);
39
+ }
40
+ function postQueryDataSourceResult(args) {
41
+ const message = args.response.status === "error"
42
+ ? {
43
+ type: "queryDataSourceResult",
44
+ subscriptionId: args.subscriptionId,
45
+ status: "error",
46
+ error: args.response.error,
47
+ }
48
+ : {
49
+ type: "queryDataSourceResult",
50
+ subscriptionId: args.subscriptionId,
51
+ status: "success",
52
+ items: args.response.items,
53
+ hasMore: args.response.hasMore,
54
+ };
55
+ post(message);
56
+ }
57
+ async function handleQuery(message, token = querySubscriptions.track(message)) {
58
+ const collectionSchema = getCollectionSchema(activeDataSources, message.dataSourceId);
59
+ const filterError = validateQueryDataSourceFilter(message.filter, collectionSchema);
60
+ if (filterError !== undefined) {
61
+ if (querySubscriptions.isCurrent(message.subscriptionId, token)) {
62
+ postQueryDataSourceResult({
63
+ subscriptionId: message.subscriptionId,
64
+ response: { status: "error", error: filterError },
65
+ });
66
+ }
67
+ return;
68
+ }
69
+ const sortError = validateQueryDataSourceSorts(message.sorts, collectionSchema);
70
+ if (sortError !== undefined) {
71
+ if (querySubscriptions.isCurrent(message.subscriptionId, token)) {
72
+ postQueryDataSourceResult({
73
+ subscriptionId: message.subscriptionId,
74
+ response: { status: "error", error: sortError },
75
+ });
76
+ }
77
+ return;
78
+ }
79
+ const response = await handlers.queryDataSource(message);
80
+ if (!querySubscriptions.isCurrent(message.subscriptionId, token)) {
81
+ return;
82
+ }
83
+ postQueryDataSourceResult({
84
+ subscriptionId: message.subscriptionId,
85
+ response,
86
+ });
87
+ }
88
+ async function handleCreatePage(message) {
89
+ if (handlers.createPage === undefined) {
90
+ return;
91
+ }
92
+ const response = await handlers.createPage(message);
93
+ const result = {
94
+ type: "createPageResult",
95
+ requestId: message.requestId,
96
+ ...response,
97
+ };
98
+ post(result);
99
+ }
100
+ async function handleGetPage(message) {
101
+ if (handlers.getPage === undefined) {
102
+ return;
103
+ }
104
+ const response = await handlers.getPage(message);
105
+ const result = {
106
+ type: "getPageResult",
107
+ requestId: message.requestId,
108
+ ...response,
109
+ };
110
+ post(result);
111
+ }
112
+ async function handleGetUser(message) {
113
+ if (handlers.getUser === undefined) {
114
+ return;
115
+ }
116
+ const response = await handlers.getUser(message);
117
+ const result = {
118
+ type: "getUserResult",
119
+ requestId: message.requestId,
120
+ ...response,
121
+ };
122
+ post(result);
123
+ }
124
+ async function handleListUsers(message) {
125
+ if (handlers.listUsers === undefined) {
126
+ return;
127
+ }
128
+ const response = await handlers.listUsers(message);
129
+ const result = {
130
+ type: "listUsersResult",
131
+ requestId: message.requestId,
132
+ ...response,
133
+ };
134
+ post(result);
135
+ }
136
+ async function handleUpdatePage(message) {
137
+ if (handlers.updatePage === undefined) {
138
+ return;
139
+ }
140
+ const response = await handlers.updatePage(message);
141
+ const result = {
142
+ type: "updatePageResult",
143
+ requestId: message.requestId,
144
+ ...response,
145
+ };
146
+ post(result);
147
+ }
148
+ /**
149
+ * Sends any pending host state changes to the sandbox. It is possible for these to change
150
+ * between when the host sends `init` and when the sandbox responds with `initResult`. The host
151
+ * does send any pending state changes until hearing back from the sandbox.
152
+ */
153
+ function flushPendingHostStateChanges() {
154
+ if (pendingThemeChange) {
155
+ pendingThemeChange = false;
156
+ post({ type: "themeChanged", theme });
157
+ }
158
+ if (pendingContrastModeChange) {
159
+ pendingContrastModeChange = false;
160
+ post({ type: "contrastModeChanged", contrastMode });
161
+ }
162
+ if (pendingParentChange) {
163
+ pendingParentChange = false;
164
+ post({ type: "parentChanged", parent });
165
+ }
166
+ if (pendingPageChange) {
167
+ pendingPageChange = false;
168
+ post({ type: "pageChanged", page });
169
+ }
170
+ if (pendingDataSourcesChange !== undefined) {
171
+ const nextDataSources = pendingDataSourcesChange;
172
+ pendingDataSourcesChange = undefined;
173
+ post({ type: "dataSourcesChanged", dataSources: nextDataSources });
174
+ }
175
+ if (pendingCurrentUserChange) {
176
+ pendingCurrentUserChange = false;
177
+ post({ type: "currentUserChanged", currentUser });
178
+ }
179
+ }
180
+ const initializationController = createInitializationController({
181
+ blockId: initialState.blockId,
182
+ getState: () => ({
183
+ theme,
184
+ contrastMode,
185
+ parent,
186
+ page,
187
+ currentUser,
188
+ }),
189
+ prepareInit(connect) {
190
+ manifest = connect.manifest ?? initialState.manifest;
191
+ const resolvedDataSources = typeof dataSources === "function" ? dataSources(connect) : dataSources;
192
+ const bindingErrorCode = getBindingErrorCode(manifest, resolvedDataSources);
193
+ if (bindingErrorCode !== undefined) {
194
+ return {
195
+ status: "error",
196
+ error: initErrorForFailureReason(bindingErrorCode),
197
+ };
198
+ }
199
+ activeDataSources = resolvedDataSources;
200
+ return {
201
+ status: "success",
202
+ manifest,
203
+ dataSources: resolvedDataSources,
204
+ };
205
+ },
206
+ post,
207
+ warn: payload => emit("warn", payload),
208
+ flushPendingHostStateChanges,
209
+ minBridgeProtocolVersion,
210
+ noConnectTimeoutMs,
211
+ noInitResultTimeoutMs,
212
+ onConnect,
213
+ onInitResult,
214
+ onIgnoredConnect,
215
+ });
216
+ function onInvalidSandboxMessage(data, issues) {
217
+ emit("warn", {
218
+ message: "Ignored unsupported sandbox message",
219
+ payload: data,
220
+ issues,
221
+ });
222
+ const response = getInvalidSandboxMessageResponse(data);
223
+ const parsedIdentity = v.safeParse(initializationIdentitySchema, data);
224
+ const initializationId = parsedIdentity.success
225
+ ? parsedIdentity.output.initializationId
226
+ : undefined;
227
+ if (response.incomingType === "connect" &&
228
+ initializationId !== undefined &&
229
+ initializationController.handleInvalidConnect(initializationId)) {
230
+ return;
231
+ }
232
+ if (response.incomingType === "initResult" &&
233
+ initializationController.handleInvalidInitResult()) {
234
+ if (response.nack !== undefined) {
235
+ post(response.nack);
236
+ }
237
+ return;
238
+ }
239
+ if (response.nack !== undefined) {
240
+ post(response.nack);
241
+ }
242
+ }
243
+ function onMessage(event) {
244
+ // Ignore messages from windows other than this host's iframe.
245
+ if (event.source !== iframe.contentWindow) {
246
+ return;
247
+ }
248
+ // Ignore messages that do not come from the configured sandbox origin.
249
+ if (event.origin !== sandboxOrigin) {
250
+ return;
251
+ }
252
+ const parsed = v.safeParse(sandboxToHostMessageSchema, event.data);
253
+ if (!parsed.success) {
254
+ onInvalidSandboxMessage(event.data, parsed.issues);
255
+ return;
256
+ }
257
+ const message = parsed.output;
258
+ emit("in", message);
259
+ if (message.type !== "connect" &&
260
+ message.type !== "initResult" &&
261
+ initializationController.status !== "success") {
262
+ // The SDK reports resize as soon as it renders, before the
263
+ // handshake completes, so an early resize is routine.
264
+ if (message.type !== "resize") {
265
+ emit("warn", {
266
+ message: `Ignored ${message.type} before initialization completed`,
267
+ payload: message,
268
+ });
269
+ }
270
+ return;
271
+ }
272
+ switch (message.type) {
273
+ case "connect":
274
+ initializationController.handleConnect(message);
275
+ return;
276
+ case "initResult":
277
+ initializationController.handleInitResult(message);
278
+ return;
279
+ case "queryDataSource":
280
+ void handleQuery(message);
281
+ return;
282
+ case "createPage":
283
+ void handleCreatePage(message);
284
+ return;
285
+ case "getPage":
286
+ void handleGetPage(message);
287
+ return;
288
+ case "getUser":
289
+ void handleGetUser(message);
290
+ return;
291
+ case "listUsers":
292
+ void handleListUsers(message);
293
+ return;
294
+ case "updatePage":
295
+ void handleUpdatePage(message);
296
+ return;
297
+ case "resize":
298
+ handlers.resize?.(message);
299
+ return;
300
+ case "invalidHostMessage":
301
+ return;
302
+ default:
303
+ unreachable(message);
304
+ }
305
+ }
306
+ window.addEventListener("message", onMessage);
307
+ iframe.addEventListener("load", initializationController.onIframeLoad);
308
+ return {
309
+ stop() {
310
+ window.removeEventListener("message", onMessage);
311
+ iframe.removeEventListener("load", initializationController.onIframeLoad);
312
+ initializationController.stop();
313
+ querySubscriptions.clear();
314
+ },
315
+ post,
316
+ setTheme(nextTheme) {
317
+ theme = nextTheme;
318
+ if (initializationController.status !== "success") {
319
+ if (initializationController.status === "waitingForInitResult") {
320
+ pendingThemeChange = true;
321
+ }
322
+ return;
323
+ }
324
+ const message = { type: "themeChanged", theme };
325
+ post(message);
326
+ },
327
+ setContrastMode(nextContrastMode) {
328
+ contrastMode = nextContrastMode;
329
+ if (initializationController.status !== "success") {
330
+ if (initializationController.status === "waitingForInitResult") {
331
+ pendingContrastModeChange = true;
332
+ }
333
+ return;
334
+ }
335
+ const message = {
336
+ type: "contrastModeChanged",
337
+ contrastMode,
338
+ };
339
+ post(message);
340
+ },
341
+ setParent(nextParent) {
342
+ parent = nextParent;
343
+ if (initializationController.status !== "success") {
344
+ if (initializationController.status === "waitingForInitResult") {
345
+ pendingParentChange = true;
346
+ }
347
+ return;
348
+ }
349
+ const message = { type: "parentChanged", parent };
350
+ post(message);
351
+ },
352
+ setPage(nextPage) {
353
+ page = nextPage;
354
+ if (initializationController.status !== "success") {
355
+ if (initializationController.status === "waitingForInitResult") {
356
+ pendingPageChange = true;
357
+ }
358
+ return;
359
+ }
360
+ const message = { type: "pageChanged", page };
361
+ post(message);
362
+ },
363
+ setDataSources(nextDataSources) {
364
+ if (initializationController.hasConnectedSandbox &&
365
+ getBindingErrorCode(manifest, nextDataSources) !== undefined) {
366
+ emit("warn", {
367
+ message: "Ignored invalid data source bindings update",
368
+ payload: nextDataSources,
369
+ });
370
+ return;
371
+ }
372
+ dataSources = nextDataSources;
373
+ activeDataSources = nextDataSources;
374
+ if (initializationController.status !== "success") {
375
+ if (initializationController.status === "waitingForInitResult") {
376
+ pendingDataSourcesChange = nextDataSources;
377
+ }
378
+ return;
379
+ }
380
+ const message = {
381
+ type: "dataSourcesChanged",
382
+ dataSources: nextDataSources,
383
+ };
384
+ post(message);
385
+ },
386
+ setCurrentUser(nextCurrentUser) {
387
+ currentUser = nextCurrentUser;
388
+ if (initializationController.status !== "success") {
389
+ if (initializationController.status === "waitingForInitResult") {
390
+ pendingCurrentUserChange = true;
391
+ }
392
+ return;
393
+ }
394
+ post({ type: "currentUserChanged", currentUser });
395
+ },
396
+ refreshQuery({ dataSourceId }) {
397
+ if (initializationController.status !== "success") {
398
+ return;
399
+ }
400
+ const refreshes = querySubscriptions.refresh(dataSourceId);
401
+ if (refreshes.length === 0) {
402
+ emit("warn", `No active query exists for data source "${dataSourceId}"`);
403
+ return;
404
+ }
405
+ for (const refresh of refreshes) {
406
+ void handleQuery(refresh.message, refresh.token);
407
+ }
408
+ },
409
+ };
410
+ }
411
+ function getCollectionSchema(dataSources, dataSourceId) {
412
+ return Object.values(dataSources?.bindings ?? {}).find(binding => binding.collectionPointer?.id === dataSourceId)?.collectionSchema;
413
+ }
414
+ function getBindingErrorCode(manifest, dataSources) {
415
+ for (const [dataSourceKey, manifestDataSource] of Object.entries(manifest.dataSources)) {
416
+ const binding = dataSources.bindings[dataSourceKey];
417
+ if (binding?.collectionPointer === undefined ||
418
+ binding.collectionSchema === undefined) {
419
+ return "missing_data_source_binding";
420
+ }
421
+ for (const [propertyKey, manifestProperty] of Object.entries(manifestDataSource.properties ?? {})) {
422
+ const propertyId = binding.propertyIdsByKey?.[propertyKey];
423
+ if (propertyId === undefined) {
424
+ return "missing_property_binding";
425
+ }
426
+ if (binding.collectionSchema.propertiesById[propertyId]?.type !==
427
+ manifestProperty.type) {
428
+ return "invalid_property_binding";
429
+ }
430
+ }
431
+ }
432
+ return undefined;
433
+ }
@@ -0,0 +1,6 @@
1
+ export { type AutoBindableProperty, getAutoBoundPropertyIdsByKey, } from "./autoBindProperties.js";
2
+ export { type CustomBlockHostHandle, type CustomBlockHostOptions, createCustomBlockHost, } from "./createCustomBlockHost.js";
3
+ export type { ConnectSuccessMessage, CustomBlockHostDataSourcesPayload, CustomBlockHostInitialDataSources, CustomBlockHostInitialState, } from "./lifecycle/types.js";
4
+ export type { CustomBlockHostCreatePageResult, CustomBlockHostGetPageResult, CustomBlockHostGetUserResult, CustomBlockHostHandlers, CustomBlockHostListUsersResult, CustomBlockHostLogDirection, CustomBlockHostUpdatePageResult, } from "./messages/types.js";
5
+ export type { CustomBlockHostQueryDataSourceResult } from "./queries/types.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,oBAAoB,EACzB,4BAA4B,GAC5B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,qBAAqB,GACrB,MAAM,4BAA4B,CAAA;AACnC,YAAY,EACX,qBAAqB,EACrB,iCAAiC,EACjC,iCAAiC,EACjC,2BAA2B,GAC3B,MAAM,sBAAsB,CAAA;AAC7B,YAAY,EACX,+BAA+B,EAC/B,4BAA4B,EAC5B,4BAA4B,EAC5B,uBAAuB,EACvB,8BAA8B,EAC9B,2BAA2B,EAC3B,+BAA+B,GAC/B,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EAAE,oCAAoC,EAAE,MAAM,oBAAoB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { getAutoBoundPropertyIdsByKey, } from "./autoBindProperties.js";
2
+ export { createCustomBlockHost, } from "./createCustomBlockHost.js";
@@ -0,0 +1,13 @@
1
+ import type { CustomBlockInitErrorCode, CustomBlockInitErrorInfo } from "../protocol/messages/init.js";
2
+ /**
3
+ * Returns the error info for a given `init` failure code.
4
+ *
5
+ * `init` handshakes are one-shot: after an `init` error, this host instance will not answer another
6
+ * `connect` message. `isRetryable` means a fresh iframe / `init` attempt could plausibly succeed
7
+ * without changing block code or host configuration.
8
+ */
9
+ export declare function initErrorForFailureReason(reason: CustomBlockInitErrorCode, data?: {
10
+ minBridgeProtocolVersion?: number;
11
+ currentBridgeProtocolVersion?: number;
12
+ }): CustomBlockInitErrorInfo;
13
+ //# sourceMappingURL=initErrors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initErrors.d.ts","sourceRoot":"","sources":["../../src/lifecycle/initErrors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,wBAAwB,EACxB,wBAAwB,EACxB,MAAM,mDAAmD,CAAA;AAK1D;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACxC,MAAM,EAAE,wBAAwB,EAChC,IAAI,GAAE;IACL,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,4BAA4B,CAAC,EAAE,MAAM,CAAA;CAChC,GACJ,wBAAwB,CA+I1B"}
@@ -0,0 +1,152 @@
1
+ const UNKNOWN_ERROR_MESSAGE = "Unexpected error while initializing custom block.";
2
+ /**
3
+ * Returns the error info for a given `init` failure code.
4
+ *
5
+ * `init` handshakes are one-shot: after an `init` error, this host instance will not answer another
6
+ * `connect` message. `isRetryable` means a fresh iframe / `init` attempt could plausibly succeed
7
+ * without changing block code or host configuration.
8
+ */
9
+ export function initErrorForFailureReason(reason, data = {}) {
10
+ switch (reason) {
11
+ case "no_connect":
12
+ return {
13
+ code: reason,
14
+ message: "Sandbox did not send a connect message.",
15
+ isRetryable: true,
16
+ };
17
+ case "invalid_connect_payload":
18
+ return {
19
+ code: reason,
20
+ message: "Sandbox sent an invalid connect message.",
21
+ // The sandbox must be updated to send a valid connect message.
22
+ isRetryable: false,
23
+ };
24
+ case "manifest_unavailable":
25
+ return {
26
+ code: reason,
27
+ message: "The custom block manifest is unavailable.",
28
+ isRetryable: true,
29
+ };
30
+ case "manifest_invalid":
31
+ return {
32
+ code: reason,
33
+ message: "Sandbox sent an invalid manifest.",
34
+ // The sandbox must be updated to a valid manifest.
35
+ isRetryable: false,
36
+ };
37
+ case "invalid_protocol_version": {
38
+ const versionText = data.currentBridgeProtocolVersion
39
+ ? `: ${data.currentBridgeProtocolVersion}`
40
+ : ".";
41
+ return {
42
+ code: reason,
43
+ message: `Sandbox reported an invalid bridge protocol version ${versionText}`,
44
+ // The sandbox must be updated to a supported protocol version.
45
+ isRetryable: false,
46
+ };
47
+ }
48
+ case "unsupported_protocol_version": {
49
+ const currentVersionText = data.currentBridgeProtocolVersion
50
+ ? `${data.currentBridgeProtocolVersion} `
51
+ : "";
52
+ const minVersionText = data.minBridgeProtocolVersion
53
+ ? ` Minimum supported version is ${data.minBridgeProtocolVersion}.`
54
+ : "";
55
+ return {
56
+ code: reason,
57
+ message: `Sandbox bridge protocol version ${currentVersionText}is not supported.${minVersionText}`,
58
+ // The sandbox must be updated to a supported protocol version.
59
+ isRetryable: false,
60
+ };
61
+ }
62
+ case "context_unavailable":
63
+ return {
64
+ code: reason,
65
+ message: "Host page context is unavailable.",
66
+ isRetryable: true,
67
+ };
68
+ case "current_user_unavailable":
69
+ return {
70
+ code: reason,
71
+ message: "Current user is unavailable.",
72
+ isRetryable: true,
73
+ };
74
+ case "missing_data_source_binding":
75
+ return {
76
+ code: reason,
77
+ message: "A required data source is not mapped.",
78
+ // Host configuration must be changed before this block can initialize.
79
+ isRetryable: false,
80
+ };
81
+ case "data_source_unavailable":
82
+ return {
83
+ code: reason,
84
+ message: "A mapped data source is unavailable.",
85
+ // This error combines some retryable errors (e.g. data source failed to load) with
86
+ // some non-retryable errors (e.g. data source does not exist or user does not have
87
+ // access). Fallback to assuming the error is retryable.
88
+ isRetryable: true,
89
+ };
90
+ case "missing_property_binding":
91
+ return {
92
+ code: reason,
93
+ message: "A required data source property is not mapped.",
94
+ // Host configuration must be changed before this block can initialize.
95
+ isRetryable: false,
96
+ };
97
+ case "invalid_property_binding":
98
+ return {
99
+ code: reason,
100
+ message: "A mapped data source property is missing or incompatible.",
101
+ // The bound property's schema must be updated or the binding must be updated to
102
+ // a property with a compatible schema.
103
+ isRetryable: false,
104
+ };
105
+ case "no_init_result":
106
+ return {
107
+ code: reason,
108
+ message: "Sandbox did not acknowledge initialization.",
109
+ isRetryable: true,
110
+ };
111
+ case "invalid_init_result_payload":
112
+ return {
113
+ code: reason,
114
+ message: "Sandbox sent an invalid initialization result.",
115
+ isRetryable: false,
116
+ };
117
+ case "invalid_init_bindings":
118
+ return {
119
+ code: reason,
120
+ message: "Sandbox rejected the host-provided data source bindings.",
121
+ isRetryable: false,
122
+ };
123
+ case "not_in_iframe":
124
+ return {
125
+ code: reason,
126
+ message: "<NotionCustomBlock> only works inside an iframe.",
127
+ // The block must be embedded in an iframe.
128
+ isRetryable: false,
129
+ };
130
+ case "init_timeout":
131
+ return {
132
+ code: reason,
133
+ message: "Host did not respond to init before the timeout.",
134
+ isRetryable: true,
135
+ };
136
+ case "unknown_error":
137
+ return {
138
+ code: reason,
139
+ message: UNKNOWN_ERROR_MESSAGE,
140
+ // Treat unknown errors as non-retryable.
141
+ isRetryable: false,
142
+ };
143
+ default:
144
+ console.warn(`Unexpected custom block init error: ${reason}`);
145
+ return {
146
+ code: reason,
147
+ message: UNKNOWN_ERROR_MESSAGE,
148
+ // Treat unknown error codes as non-retryable.
149
+ isRetryable: false,
150
+ };
151
+ }
152
+ }
@@ -0,0 +1,54 @@
1
+ import type { NotionContrastMode } from "../protocol/contrast.js";
2
+ import type { NotionBlockId } from "../protocol/ids.js";
3
+ import type { CustomBlockManifest } from "../protocol/manifest.js";
4
+ import type { ConnectMessage } from "../protocol/messages/connect.js";
5
+ import type { HostToSandboxMessage } from "../protocol/messages/hostToSandbox.js";
6
+ import type { CustomBlockInitErrorInfo } from "../protocol/messages/init.js";
7
+ import type { InitResultMessage } from "../protocol/messages/initResult.js";
8
+ import type { CustomBlockPage } from "../protocol/pages/page.js";
9
+ import type { NotionParent } from "../protocol/parent.js";
10
+ import type { NotionTheme } from "../protocol/theme.js";
11
+ import type { NotionUser } from "../protocol/users/user.js";
12
+ import type { ConnectSuccessMessage, CustomBlockHostDataSourcesPayload, InitStatus } from "./types.js";
13
+ type InitializationState = {
14
+ theme: NotionTheme;
15
+ contrastMode: NotionContrastMode;
16
+ parent: NotionParent;
17
+ page: CustomBlockPage;
18
+ currentUser: NotionUser;
19
+ };
20
+ type InitializationPreparation = {
21
+ status: "success";
22
+ manifest: CustomBlockManifest;
23
+ dataSources: CustomBlockHostDataSourcesPayload;
24
+ } | {
25
+ status: "error";
26
+ error: CustomBlockInitErrorInfo;
27
+ };
28
+ export type InitializationControllerOptions = {
29
+ blockId: NotionBlockId;
30
+ getState: () => InitializationState;
31
+ prepareInit: (connect: ConnectSuccessMessage) => InitializationPreparation;
32
+ post: (message: HostToSandboxMessage) => void;
33
+ warn: (payload: unknown) => void;
34
+ flushPendingHostStateChanges: () => void;
35
+ minBridgeProtocolVersion: number;
36
+ noConnectTimeoutMs?: number;
37
+ noInitResultTimeoutMs?: number;
38
+ onConnect?: (message: ConnectSuccessMessage) => void;
39
+ onInitResult?: (message: InitResultMessage) => void;
40
+ onIgnoredConnect?: (message: ConnectMessage) => void;
41
+ };
42
+ export type InitializationController = {
43
+ readonly status: InitStatus;
44
+ readonly hasConnectedSandbox: boolean;
45
+ handleConnect: (message: ConnectMessage) => void;
46
+ handleInitResult: (message: InitResultMessage) => void;
47
+ handleInvalidConnect: (initializationId: string) => boolean;
48
+ handleInvalidInitResult: () => boolean;
49
+ onIframeLoad: () => void;
50
+ stop: () => void;
51
+ };
52
+ export declare function createInitializationController(options: InitializationControllerOptions): InitializationController;
53
+ export {};
54
+ //# sourceMappingURL=initializationController.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initializationController.d.ts","sourceRoot":"","sources":["../../src/lifecycle/initializationController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAA;AACtF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAA;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAC1F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4DAA4D,CAAA;AACtG,OAAO,KAAK,EACX,wBAAwB,EAExB,MAAM,mDAAmD,CAAA;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAGhF,OAAO,KAAK,EACX,qBAAqB,EACrB,iCAAiC,EACjC,UAAU,EACV,MAAM,YAAY,CAAA;AAKnB,KAAK,mBAAmB,GAAG;IAC1B,KAAK,EAAE,WAAW,CAAA;IAClB,YAAY,EAAE,kBAAkB,CAAA;IAChC,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,eAAe,CAAA;IACrB,WAAW,EAAE,UAAU,CAAA;CACvB,CAAA;AAED,KAAK,yBAAyB,GAC3B;IACA,MAAM,EAAE,SAAS,CAAA;IACjB,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,WAAW,EAAE,iCAAiC,CAAA;CAC7C,GACD;IACA,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,wBAAwB,CAAA;CAC9B,CAAA;AAEJ,MAAM,MAAM,+BAA+B,GAAG;IAC7C,OAAO,EAAE,aAAa,CAAA;IACtB,QAAQ,EAAE,MAAM,mBAAmB,CAAA;IACnC,WAAW,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,yBAAyB,CAAA;IAC1E,IAAI,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAC7C,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAChC,4BAA4B,EAAE,MAAM,IAAI,CAAA;IACxC,wBAAwB,EAAE,MAAM,CAAA;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAA;IACpD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACnD,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;CACpD,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG;IACtC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;IAC3B,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAA;IACrC,aAAa,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;IAChD,gBAAgB,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACtD,oBAAoB,EAAE,CAAC,gBAAgB,EAAE,MAAM,KAAK,OAAO,CAAA;IAC3D,uBAAuB,EAAE,MAAM,OAAO,CAAA;IACtC,YAAY,EAAE,MAAM,IAAI,CAAA;IACxB,IAAI,EAAE,MAAM,IAAI,CAAA;CAChB,CAAA;AAED,wBAAgB,8BAA8B,CAC7C,OAAO,EAAE,+BAA+B,GACtC,wBAAwB,CAyN1B"}