@notionhq/custom-blocks 0.0.58

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 (298) hide show
  1. package/HOST.md +171 -0
  2. package/README.md +57 -0
  3. package/bin/cli/attach.js +180 -0
  4. package/bin/cli/cli.js +248 -0
  5. package/bin/cli/create.js +106 -0
  6. package/bin/cli/datasources.js +255 -0
  7. package/bin/cli/deploy.js +109 -0
  8. package/bin/cli/ids.js +13 -0
  9. package/bin/cli/ntn.js +95 -0
  10. package/bin/cli/pullData.js +76 -0
  11. package/bin/cli/pullManifest.js +158 -0
  12. package/bin/cli/target.js +95 -0
  13. package/bin/src/bridge/dataSources/propertySchema.js +148 -0
  14. package/bin/src/bridge/manifest.js +40 -0
  15. package/dist/autoResize.d.ts +4 -0
  16. package/dist/autoResize.d.ts.map +1 -0
  17. package/dist/autoResize.js +71 -0
  18. package/dist/bridge/SandboxBridge.d.ts +92 -0
  19. package/dist/bridge/SandboxBridge.d.ts.map +1 -0
  20. package/dist/bridge/SandboxBridge.js +718 -0
  21. package/dist/bridge/dataSources/dataSource.d.ts +732 -0
  22. package/dist/bridge/dataSources/dataSource.d.ts.map +1 -0
  23. package/dist/bridge/dataSources/dataSource.js +37 -0
  24. package/dist/bridge/dataSources/dataSourcePage.d.ts +118 -0
  25. package/dist/bridge/dataSources/dataSourcePage.d.ts.map +1 -0
  26. package/dist/bridge/dataSources/dataSourcePage.js +14 -0
  27. package/dist/bridge/dataSources/dataSourceValue.d.ts +67 -0
  28. package/dist/bridge/dataSources/dataSourceValue.d.ts.map +1 -0
  29. package/dist/bridge/dataSources/dataSourceValue.js +14 -0
  30. package/dist/bridge/dataSources/dateValue.d.ts +158 -0
  31. package/dist/bridge/dataSources/dateValue.d.ts.map +1 -0
  32. package/dist/bridge/dataSources/dateValue.js +59 -0
  33. package/dist/bridge/dataSources/errors.d.ts +6 -0
  34. package/dist/bridge/dataSources/errors.d.ts.map +1 -0
  35. package/dist/bridge/dataSources/errors.js +4 -0
  36. package/dist/bridge/dataSources/propertySchema.d.ts +191 -0
  37. package/dist/bridge/dataSources/propertySchema.d.ts.map +1 -0
  38. package/dist/bridge/dataSources/propertySchema.js +148 -0
  39. package/dist/bridge/dataSources/recordPointer.d.ts +10 -0
  40. package/dist/bridge/dataSources/recordPointer.d.ts.map +1 -0
  41. package/dist/bridge/dataSources/recordPointer.js +8 -0
  42. package/dist/bridge/dataSources/resolve.d.ts +22 -0
  43. package/dist/bridge/dataSources/resolve.d.ts.map +1 -0
  44. package/dist/bridge/dataSources/resolve.js +65 -0
  45. package/dist/bridge/dataSources/resolveProperty.d.ts +41 -0
  46. package/dist/bridge/dataSources/resolveProperty.d.ts.map +1 -0
  47. package/dist/bridge/dataSources/resolveProperty.js +73 -0
  48. package/dist/bridge/errors.d.ts +12 -0
  49. package/dist/bridge/errors.d.ts.map +1 -0
  50. package/dist/bridge/errors.js +6 -0
  51. package/dist/bridge/hostState.d.ts +65 -0
  52. package/dist/bridge/hostState.d.ts.map +1 -0
  53. package/dist/bridge/hostState.js +65 -0
  54. package/dist/bridge/ids.d.ts +39 -0
  55. package/dist/bridge/ids.d.ts.map +1 -0
  56. package/dist/bridge/ids.js +5 -0
  57. package/dist/bridge/incomingType.d.ts +10 -0
  58. package/dist/bridge/incomingType.d.ts.map +1 -0
  59. package/dist/bridge/incomingType.js +17 -0
  60. package/dist/bridge/loadManifest.d.ts +25 -0
  61. package/dist/bridge/loadManifest.d.ts.map +1 -0
  62. package/dist/bridge/loadManifest.js +68 -0
  63. package/dist/bridge/manifest.d.ts +66 -0
  64. package/dist/bridge/manifest.d.ts.map +1 -0
  65. package/dist/bridge/manifest.js +40 -0
  66. package/dist/bridge/messages/createPage.d.ts +225 -0
  67. package/dist/bridge/messages/createPage.d.ts.map +1 -0
  68. package/dist/bridge/messages/createPage.js +46 -0
  69. package/dist/bridge/messages/createPageResult.d.ts +211 -0
  70. package/dist/bridge/messages/createPageResult.d.ts.map +1 -0
  71. package/dist/bridge/messages/createPageResult.js +28 -0
  72. package/dist/bridge/messages/currentUserChanged.d.ts +19 -0
  73. package/dist/bridge/messages/currentUserChanged.d.ts.map +1 -0
  74. package/dist/bridge/messages/currentUserChanged.js +9 -0
  75. package/dist/bridge/messages/dataSourcesChanged.d.ts +158 -0
  76. package/dist/bridge/messages/dataSourcesChanged.d.ts.map +1 -0
  77. package/dist/bridge/messages/dataSourcesChanged.js +13 -0
  78. package/dist/bridge/messages/getPage.d.ts +216 -0
  79. package/dist/bridge/messages/getPage.d.ts.map +1 -0
  80. package/dist/bridge/messages/getPage.js +33 -0
  81. package/dist/bridge/messages/getUser.d.ts +45 -0
  82. package/dist/bridge/messages/getUser.d.ts.map +1 -0
  83. package/dist/bridge/messages/getUser.js +32 -0
  84. package/dist/bridge/messages/hostToSandbox.d.ts +1089 -0
  85. package/dist/bridge/messages/hostToSandbox.d.ts.map +1 -0
  86. package/dist/bridge/messages/hostToSandbox.js +32 -0
  87. package/dist/bridge/messages/init.d.ts +209 -0
  88. package/dist/bridge/messages/init.d.ts.map +1 -0
  89. package/dist/bridge/messages/init.js +47 -0
  90. package/dist/bridge/messages/invalidHostMessage.d.ts +15 -0
  91. package/dist/bridge/messages/invalidHostMessage.d.ts.map +1 -0
  92. package/dist/bridge/messages/invalidHostMessage.js +13 -0
  93. package/dist/bridge/messages/invalidSandboxMessage.d.ts +15 -0
  94. package/dist/bridge/messages/invalidSandboxMessage.d.ts.map +1 -0
  95. package/dist/bridge/messages/invalidSandboxMessage.js +13 -0
  96. package/dist/bridge/messages/listUsers.d.ts +53 -0
  97. package/dist/bridge/messages/listUsers.d.ts.map +1 -0
  98. package/dist/bridge/messages/listUsers.js +33 -0
  99. package/dist/bridge/messages/pageChanged.d.ts +12 -0
  100. package/dist/bridge/messages/pageChanged.d.ts.map +1 -0
  101. package/dist/bridge/messages/pageChanged.js +9 -0
  102. package/dist/bridge/messages/parentChanged.d.ts +22 -0
  103. package/dist/bridge/messages/parentChanged.d.ts.map +1 -0
  104. package/dist/bridge/messages/parentChanged.js +9 -0
  105. package/dist/bridge/messages/queryDataSource.d.ts +20 -0
  106. package/dist/bridge/messages/queryDataSource.d.ts.map +1 -0
  107. package/dist/bridge/messages/queryDataSource.js +19 -0
  108. package/dist/bridge/messages/queryDataSourceResult.d.ts +90 -0
  109. package/dist/bridge/messages/queryDataSourceResult.d.ts.map +1 -0
  110. package/dist/bridge/messages/queryDataSourceResult.js +21 -0
  111. package/dist/bridge/messages/ready.d.ts +45 -0
  112. package/dist/bridge/messages/ready.d.ts.map +1 -0
  113. package/dist/bridge/messages/ready.js +37 -0
  114. package/dist/bridge/messages/resize.d.ts +12 -0
  115. package/dist/bridge/messages/resize.d.ts.map +1 -0
  116. package/dist/bridge/messages/resize.js +10 -0
  117. package/dist/bridge/messages/sandboxToHost.d.ts +410 -0
  118. package/dist/bridge/messages/sandboxToHost.d.ts.map +1 -0
  119. package/dist/bridge/messages/sandboxToHost.js +25 -0
  120. package/dist/bridge/messages/themeChanged.d.ts +11 -0
  121. package/dist/bridge/messages/themeChanged.d.ts.map +1 -0
  122. package/dist/bridge/messages/themeChanged.js +10 -0
  123. package/dist/bridge/messages/updatePage.d.ts +171 -0
  124. package/dist/bridge/messages/updatePage.d.ts.map +1 -0
  125. package/dist/bridge/messages/updatePage.js +15 -0
  126. package/dist/bridge/messages/updatePageResult.d.ts +210 -0
  127. package/dist/bridge/messages/updatePageResult.d.ts.map +1 -0
  128. package/dist/bridge/messages/updatePageResult.js +27 -0
  129. package/dist/bridge/pages/page.d.ts +600 -0
  130. package/dist/bridge/pages/page.d.ts.map +1 -0
  131. package/dist/bridge/pages/page.js +217 -0
  132. package/dist/bridge/parent.d.ts +25 -0
  133. package/dist/bridge/parent.d.ts.map +1 -0
  134. package/dist/bridge/parent.js +11 -0
  135. package/dist/bridge/pendingRequests.d.ts +14 -0
  136. package/dist/bridge/pendingRequests.d.ts.map +1 -0
  137. package/dist/bridge/pendingRequests.js +27 -0
  138. package/dist/bridge/sandboxClient.d.ts +63 -0
  139. package/dist/bridge/sandboxClient.d.ts.map +1 -0
  140. package/dist/bridge/sandboxClient.js +92 -0
  141. package/dist/bridge/theme.d.ts +4 -0
  142. package/dist/bridge/theme.d.ts.map +1 -0
  143. package/dist/bridge/theme.js +2 -0
  144. package/dist/bridge/users/user.d.ts +36 -0
  145. package/dist/bridge/users/user.d.ts.map +1 -0
  146. package/dist/bridge/users/user.js +19 -0
  147. package/dist/customBlock.d.ts +36 -0
  148. package/dist/customBlock.d.ts.map +1 -0
  149. package/dist/customBlock.js +65 -0
  150. package/dist/host/createCustomBlockHost.d.ts +34 -0
  151. package/dist/host/createCustomBlockHost.d.ts.map +1 -0
  152. package/dist/host/createCustomBlockHost.js +278 -0
  153. package/dist/host/lifecycle/initErrors.d.ts +10 -0
  154. package/dist/host/lifecycle/initErrors.d.ts.map +1 -0
  155. package/dist/host/lifecycle/initErrors.js +123 -0
  156. package/dist/host/lifecycle/ready.d.ts +8 -0
  157. package/dist/host/lifecycle/ready.d.ts.map +1 -0
  158. package/dist/host/lifecycle/ready.js +24 -0
  159. package/dist/host/lifecycle/types.d.ts +24 -0
  160. package/dist/host/lifecycle/types.d.ts.map +1 -0
  161. package/dist/host/lifecycle/types.js +1 -0
  162. package/dist/host/messages/invalidSandboxMessage.d.ts +7 -0
  163. package/dist/host/messages/invalidSandboxMessage.d.ts.map +1 -0
  164. package/dist/host/messages/invalidSandboxMessage.js +24 -0
  165. package/dist/host/messages/types.d.ts +60 -0
  166. package/dist/host/messages/types.d.ts.map +1 -0
  167. package/dist/host/messages/types.js +1 -0
  168. package/dist/host/queries/querySubscriptions.d.ts +20 -0
  169. package/dist/host/queries/querySubscriptions.d.ts.map +1 -0
  170. package/dist/host/queries/querySubscriptions.js +29 -0
  171. package/dist/host/queries/types.d.ts +9 -0
  172. package/dist/host/queries/types.d.ts.map +1 -0
  173. package/dist/host/queries/types.js +1 -0
  174. package/dist/host.d.ts +73 -0
  175. package/dist/host.d.ts.map +1 -0
  176. package/dist/host.js +42 -0
  177. package/dist/index.d.ts +37 -0
  178. package/dist/index.d.ts.map +1 -0
  179. package/dist/index.js +14 -0
  180. package/dist/init.d.ts +53 -0
  181. package/dist/init.d.ts.map +1 -0
  182. package/dist/init.js +87 -0
  183. package/dist/react/DebugMessageLog.d.ts +2 -0
  184. package/dist/react/DebugMessageLog.d.ts.map +1 -0
  185. package/dist/react/DebugMessageLog.js +22 -0
  186. package/dist/react/NotionCustomBlock.css +110 -0
  187. package/dist/react/NotionCustomBlock.d.ts +50 -0
  188. package/dist/react/NotionCustomBlock.d.ts.map +1 -0
  189. package/dist/react/NotionCustomBlock.js +81 -0
  190. package/dist/react/index.d.ts +14 -0
  191. package/dist/react/index.d.ts.map +1 -0
  192. package/dist/react/index.js +13 -0
  193. package/dist/react/standalonePreview.d.ts +2 -0
  194. package/dist/react/standalonePreview.d.ts.map +1 -0
  195. package/dist/react/standalonePreview.js +26 -0
  196. package/dist/react/useCustomBlockAutoResize.d.ts +30 -0
  197. package/dist/react/useCustomBlockAutoResize.d.ts.map +1 -0
  198. package/dist/react/useCustomBlockAutoResize.js +35 -0
  199. package/dist/react/useCustomBlockInit.d.ts +43 -0
  200. package/dist/react/useCustomBlockInit.d.ts.map +1 -0
  201. package/dist/react/useCustomBlockInit.js +60 -0
  202. package/dist/react/useDataSource.d.ts +13 -0
  203. package/dist/react/useDataSource.d.ts.map +1 -0
  204. package/dist/react/useDataSource.js +37 -0
  205. package/dist/react/useHostState.d.ts +2 -0
  206. package/dist/react/useHostState.d.ts.map +1 -0
  207. package/dist/react/useHostState.js +5 -0
  208. package/dist/react/useRuntimeState.d.ts +66 -0
  209. package/dist/react/useRuntimeState.d.ts.map +1 -0
  210. package/dist/react/useRuntimeState.js +73 -0
  211. package/dist/types.d.ts +154 -0
  212. package/dist/types.d.ts.map +1 -0
  213. package/dist/types.js +1 -0
  214. package/dist/utils.d.ts +9 -0
  215. package/dist/utils.d.ts.map +1 -0
  216. package/dist/utils.js +10 -0
  217. package/dist/version.d.ts +9 -0
  218. package/dist/version.d.ts.map +1 -0
  219. package/dist/version.js +7 -0
  220. package/docs/block-location.md +83 -0
  221. package/docs/data-sources.md +186 -0
  222. package/docs/lifecycle.md +172 -0
  223. package/docs/manifest.md +42 -0
  224. package/docs/pages.md +143 -0
  225. package/docs/users.md +92 -0
  226. package/package.json +69 -0
  227. package/src/autoResize.ts +84 -0
  228. package/src/bridge/SandboxBridge.ts +921 -0
  229. package/src/bridge/dataSources/dataSource.ts +63 -0
  230. package/src/bridge/dataSources/dataSourcePage.ts +71 -0
  231. package/src/bridge/dataSources/dataSourceValue.ts +19 -0
  232. package/src/bridge/dataSources/dateValue.ts +96 -0
  233. package/src/bridge/dataSources/errors.ts +14 -0
  234. package/src/bridge/dataSources/propertySchema.ts +186 -0
  235. package/src/bridge/dataSources/recordPointer.ts +13 -0
  236. package/src/bridge/dataSources/resolve.ts +99 -0
  237. package/src/bridge/dataSources/resolveProperty.ts +130 -0
  238. package/src/bridge/errors.ts +13 -0
  239. package/src/bridge/hostState.ts +154 -0
  240. package/src/bridge/ids.ts +58 -0
  241. package/src/bridge/incomingType.ts +19 -0
  242. package/src/bridge/loadManifest.ts +89 -0
  243. package/src/bridge/manifest.ts +53 -0
  244. package/src/bridge/messages/createPage.ts +67 -0
  245. package/src/bridge/messages/createPageResult.ts +52 -0
  246. package/src/bridge/messages/currentUserChanged.ts +14 -0
  247. package/src/bridge/messages/dataSourcesChanged.ts +18 -0
  248. package/src/bridge/messages/getPage.ts +52 -0
  249. package/src/bridge/messages/getUser.ts +48 -0
  250. package/src/bridge/messages/hostToSandbox.ts +37 -0
  251. package/src/bridge/messages/init.ts +81 -0
  252. package/src/bridge/messages/invalidHostMessage.ts +16 -0
  253. package/src/bridge/messages/invalidSandboxMessage.ts +18 -0
  254. package/src/bridge/messages/listUsers.ts +49 -0
  255. package/src/bridge/messages/pageChanged.ts +12 -0
  256. package/src/bridge/messages/parentChanged.ts +14 -0
  257. package/src/bridge/messages/queryDataSource.ts +24 -0
  258. package/src/bridge/messages/queryDataSourceResult.ts +38 -0
  259. package/src/bridge/messages/ready.ts +41 -0
  260. package/src/bridge/messages/resize.ts +13 -0
  261. package/src/bridge/messages/sandboxToHost.ts +30 -0
  262. package/src/bridge/messages/themeChanged.ts +15 -0
  263. package/src/bridge/messages/updatePage.ts +22 -0
  264. package/src/bridge/messages/updatePageResult.ts +49 -0
  265. package/src/bridge/pages/page.ts +281 -0
  266. package/src/bridge/parent.ts +27 -0
  267. package/src/bridge/pendingRequests.ts +28 -0
  268. package/src/bridge/sandboxClient.ts +137 -0
  269. package/src/bridge/theme.ts +5 -0
  270. package/src/bridge/users/user.ts +31 -0
  271. package/src/css.d.ts +1 -0
  272. package/src/customBlock.ts +102 -0
  273. package/src/host/createCustomBlockHost.ts +392 -0
  274. package/src/host/lifecycle/initErrors.ts +132 -0
  275. package/src/host/lifecycle/ready.ts +36 -0
  276. package/src/host/lifecycle/types.ts +28 -0
  277. package/src/host/messages/invalidSandboxMessage.ts +38 -0
  278. package/src/host/messages/types.ts +67 -0
  279. package/src/host/queries/querySubscriptions.ts +52 -0
  280. package/src/host/queries/types.ts +16 -0
  281. package/src/host.ts +167 -0
  282. package/src/index.ts +115 -0
  283. package/src/init.ts +143 -0
  284. package/src/react/DebugMessageLog.tsx +30 -0
  285. package/src/react/NotionCustomBlock.css +110 -0
  286. package/src/react/NotionCustomBlock.tsx +151 -0
  287. package/src/react/index.ts +28 -0
  288. package/src/react/standalonePreview.ts +28 -0
  289. package/src/react/useCustomBlockAutoResize.ts +46 -0
  290. package/src/react/useCustomBlockInit.ts +91 -0
  291. package/src/react/useDataSource.ts +47 -0
  292. package/src/react/useHostState.ts +9 -0
  293. package/src/react/useRuntimeState.ts +85 -0
  294. package/src/types.ts +175 -0
  295. package/src/utils.ts +13 -0
  296. package/src/version.ts +8 -0
  297. package/vite-plugin/index.d.ts +46 -0
  298. package/vite-plugin/index.js +115 -0
@@ -0,0 +1,392 @@
1
+ import * as v from "valibot"
2
+ import type { NotionDataSourceId } from "../bridge/ids.js"
3
+ import type { CreatePageMessage } from "../bridge/messages/createPage.js"
4
+ import type { CreatePageResultMessage } from "../bridge/messages/createPageResult.js"
5
+ import type { DataSourcesChangedMessage } from "../bridge/messages/dataSourcesChanged.js"
6
+ import type {
7
+ GetPageMessage,
8
+ GetPageResultMessage,
9
+ } from "../bridge/messages/getPage.js"
10
+ import type {
11
+ GetUserMessage,
12
+ GetUserResultMessage,
13
+ } from "../bridge/messages/getUser.js"
14
+ import type { HostToSandboxMessage } from "../bridge/messages/hostToSandbox.js"
15
+ import type {
16
+ CustomBlockInitErrorCode,
17
+ InitMessage,
18
+ } from "../bridge/messages/init.js"
19
+ import type {
20
+ ListUsersMessage,
21
+ ListUsersResultMessage,
22
+ } from "../bridge/messages/listUsers.js"
23
+ import type { PageChangedMessage } from "../bridge/messages/pageChanged.js"
24
+ import type { ParentChangedMessage } from "../bridge/messages/parentChanged.js"
25
+ import type { QueryDataSourceMessage } from "../bridge/messages/queryDataSource.js"
26
+ import type { QueryDataSourceResultMessage } from "../bridge/messages/queryDataSourceResult.js"
27
+ import type { ReadyMessage } from "../bridge/messages/ready.js"
28
+ import { sandboxToHostMessageSchema } from "../bridge/messages/sandboxToHost.js"
29
+ import type { ThemeChangedMessage } from "../bridge/messages/themeChanged.js"
30
+ import type { UpdatePageMessage } from "../bridge/messages/updatePage.js"
31
+ import type { UpdatePageResultMessage } from "../bridge/messages/updatePageResult.js"
32
+ import type { CustomBlockPage } from "../bridge/pages/page.js"
33
+ import type { NotionParent } from "../bridge/parent.js"
34
+ import type { NotionTheme } from "../bridge/theme.js"
35
+ import type { NotionUser } from "../bridge/users/user.js"
36
+ import { unreachable } from "../utils.js"
37
+ import { initErrorForFailureReason } from "./lifecycle/initErrors.js"
38
+ import { getReadyInitFailure } from "./lifecycle/ready.js"
39
+ import type {
40
+ CustomBlockHostDataSourcesPayload,
41
+ CustomBlockHostInitialState,
42
+ InitStatus,
43
+ ReadySuccessMessage,
44
+ } from "./lifecycle/types.js"
45
+ import { getInvalidSandboxMessageResponse } from "./messages/invalidSandboxMessage.js"
46
+ import type {
47
+ CustomBlockHostHandlers,
48
+ CustomBlockHostLogDirection,
49
+ } from "./messages/types.js"
50
+ import { QuerySubscriptions } from "./queries/querySubscriptions.js"
51
+ import type { CustomBlockHostQueryDataSourceResult } from "./queries/types.js"
52
+
53
+ export type CustomBlockHostOptions = {
54
+ iframe: HTMLIFrameElement
55
+ targetOrigin?: string
56
+ minBridgeProtocolVersion?: number
57
+ noReadyTimeoutMs?: number
58
+ initialState: CustomBlockHostInitialState
59
+ handlers: CustomBlockHostHandlers
60
+ onReady?: (message: ReadySuccessMessage) => void
61
+ onLog?: (direction: CustomBlockHostLogDirection, payload: unknown) => void
62
+ }
63
+
64
+ export type CustomBlockHostHandle = {
65
+ stop: () => void
66
+ post: (message: HostToSandboxMessage) => void
67
+ setTheme: (theme: NotionTheme) => void
68
+ setParent: (parent: NotionParent) => void
69
+ setPage: (page: CustomBlockPage) => void
70
+ setDataSources: (dataSources: CustomBlockHostDataSourcesPayload) => void
71
+ setCurrentUser: (currentUser: NotionUser) => void
72
+ refreshQuery: (args: {
73
+ dataSourceId: NotionDataSourceId
74
+ response: CustomBlockHostQueryDataSourceResult
75
+ }) => void
76
+ }
77
+
78
+ const DEFAULT_NO_READY_TIMEOUT_MS = 5_000
79
+ const DEFAULT_MIN_BRIDGE_PROTOCOL_VERSION = 2
80
+
81
+ export function createCustomBlockHost(
82
+ options: CustomBlockHostOptions,
83
+ ): CustomBlockHostHandle {
84
+ const {
85
+ iframe,
86
+ initialState,
87
+ handlers,
88
+ onReady,
89
+ onLog,
90
+ targetOrigin = "*",
91
+ minBridgeProtocolVersion = DEFAULT_MIN_BRIDGE_PROTOCOL_VERSION,
92
+ noReadyTimeoutMs = DEFAULT_NO_READY_TIMEOUT_MS,
93
+ } = options
94
+
95
+ let theme = initialState.theme
96
+ let parent = initialState.parent
97
+ let page = initialState.page
98
+ let dataSources = initialState.dataSources
99
+ let currentUser = initialState.currentUser
100
+ let initStatus: InitStatus = "pending"
101
+ let noReadyTimeoutId: number | undefined
102
+ const querySubscriptions = new QuerySubscriptions()
103
+
104
+ function emit(direction: CustomBlockHostLogDirection, payload: unknown) {
105
+ onLog?.(direction, payload)
106
+ }
107
+
108
+ function post(message: HostToSandboxMessage) {
109
+ emit("out", message)
110
+ iframe.contentWindow?.postMessage(message, targetOrigin)
111
+ }
112
+
113
+ function postQueryDataSourceResult(args: {
114
+ requestId: string
115
+ snapshotId: string
116
+ response: CustomBlockHostQueryDataSourceResult
117
+ }) {
118
+ const message: QueryDataSourceResultMessage = {
119
+ type: "queryDataSourceResult",
120
+ requestId: args.requestId,
121
+ snapshotId: args.snapshotId,
122
+ items: args.response.items,
123
+ hasMore: args.response.hasMore ?? false,
124
+ error: args.response.error,
125
+ }
126
+ post(message)
127
+ }
128
+
129
+ function clearNoReadyTimeout() {
130
+ if (noReadyTimeoutId !== undefined) {
131
+ window.clearTimeout(noReadyTimeoutId)
132
+ noReadyTimeoutId = undefined
133
+ }
134
+ }
135
+
136
+ function resolveDataSourcesPayload(
137
+ ready: ReadySuccessMessage,
138
+ ): CustomBlockHostDataSourcesPayload {
139
+ return typeof dataSources === "function" ? dataSources(ready) : dataSources
140
+ }
141
+
142
+ function sendInit(ready: ReadySuccessMessage): boolean {
143
+ if (initStatus !== "pending") {
144
+ return false
145
+ }
146
+ initStatus = "success"
147
+ clearNoReadyTimeout()
148
+ const message: InitMessage = {
149
+ type: "init",
150
+ status: "success",
151
+ theme,
152
+ blockId: initialState.blockId,
153
+ parent,
154
+ page,
155
+ dataSources: resolveDataSourcesPayload(ready),
156
+ currentUser,
157
+ }
158
+ post(message)
159
+ return true
160
+ }
161
+
162
+ function sendInitError(code: CustomBlockInitErrorCode, message?: string) {
163
+ if (initStatus !== "pending") {
164
+ return
165
+ }
166
+ initStatus = "error"
167
+ clearNoReadyTimeout()
168
+ const error = initErrorForFailureReason(code)
169
+ post({
170
+ type: "init",
171
+ status: "error",
172
+ error: {
173
+ ...error,
174
+ message: message ?? error.message,
175
+ },
176
+ })
177
+ }
178
+
179
+ function onIframeLoad() {
180
+ clearNoReadyTimeout()
181
+ noReadyTimeoutId = window.setTimeout(() => {
182
+ sendInitError("no_ready")
183
+ }, noReadyTimeoutMs)
184
+ }
185
+
186
+ async function handleQuery(message: QueryDataSourceMessage) {
187
+ querySubscriptions.track(message)
188
+ const response = await handlers.queryDataSource(message)
189
+ postQueryDataSourceResult({
190
+ requestId: message.requestId,
191
+ snapshotId: message.snapshotId,
192
+ response,
193
+ })
194
+ }
195
+
196
+ async function handleCreatePage(message: CreatePageMessage) {
197
+ if (handlers.createPage === undefined) {
198
+ return
199
+ }
200
+ const response = await handlers.createPage(message)
201
+ const result: CreatePageResultMessage = {
202
+ type: "createPageResult",
203
+ requestId: message.requestId,
204
+ ...response,
205
+ }
206
+ post(result)
207
+ }
208
+
209
+ async function handleGetPage(message: GetPageMessage) {
210
+ if (handlers.getPage === undefined) {
211
+ return
212
+ }
213
+ const response = await handlers.getPage(message)
214
+ const result: GetPageResultMessage = {
215
+ type: "getPageResult",
216
+ requestId: message.requestId,
217
+ ...response,
218
+ }
219
+ post(result)
220
+ }
221
+
222
+ async function handleGetUser(message: GetUserMessage) {
223
+ if (handlers.getUser === undefined) {
224
+ return
225
+ }
226
+ const response = await handlers.getUser(message)
227
+ const result: GetUserResultMessage = {
228
+ type: "getUserResult",
229
+ requestId: message.requestId,
230
+ ...response,
231
+ }
232
+ post(result)
233
+ }
234
+
235
+ async function handleListUsers(message: ListUsersMessage) {
236
+ if (handlers.listUsers === undefined) {
237
+ return
238
+ }
239
+ const response = await handlers.listUsers(message)
240
+ const result: ListUsersResultMessage = {
241
+ type: "listUsersResult",
242
+ requestId: message.requestId,
243
+ ...response,
244
+ }
245
+ post(result)
246
+ }
247
+
248
+ async function handleUpdatePage(message: UpdatePageMessage) {
249
+ if (handlers.updatePage === undefined) {
250
+ return
251
+ }
252
+ const response = await handlers.updatePage(message)
253
+ const result: UpdatePageResultMessage = {
254
+ type: "updatePageResult",
255
+ requestId: message.requestId,
256
+ ...response,
257
+ }
258
+ post(result)
259
+ }
260
+
261
+ function handleReady(message: ReadyMessage) {
262
+ const failure = getReadyInitFailure(message, minBridgeProtocolVersion)
263
+ if (failure !== undefined) {
264
+ sendInitError(failure.code, failure.message)
265
+ return
266
+ }
267
+ if (message.status !== "success") {
268
+ return
269
+ }
270
+ if (sendInit(message)) {
271
+ onReady?.(message)
272
+ }
273
+ }
274
+
275
+ function onInvalidSandboxMessage(
276
+ data: unknown,
277
+ issues: readonly v.BaseIssue<unknown>[],
278
+ ) {
279
+ emit("warn", {
280
+ message: "Ignored unsupported sandbox message",
281
+ payload: data,
282
+ issues,
283
+ })
284
+ const response = getInvalidSandboxMessageResponse(data)
285
+
286
+ if (initStatus === "pending" && response.incomingType === "ready") {
287
+ sendInitError("invalid_ready")
288
+ } else if (response.nack !== undefined) {
289
+ post(response.nack)
290
+ }
291
+ }
292
+
293
+ function onMessage(event: MessageEvent) {
294
+ if (event.source !== iframe.contentWindow) {
295
+ return
296
+ }
297
+
298
+ const parsed = v.safeParse(sandboxToHostMessageSchema, event.data)
299
+ if (!parsed.success) {
300
+ onInvalidSandboxMessage(event.data, parsed.issues)
301
+ return
302
+ }
303
+
304
+ const message = parsed.output
305
+ emit("in", message)
306
+
307
+ switch (message.type) {
308
+ case "ready":
309
+ handleReady(message)
310
+ return
311
+ case "queryDataSource":
312
+ void handleQuery(message)
313
+ return
314
+ case "createPage":
315
+ void handleCreatePage(message)
316
+ return
317
+ case "getPage":
318
+ void handleGetPage(message)
319
+ return
320
+ case "getUser":
321
+ void handleGetUser(message)
322
+ return
323
+ case "listUsers":
324
+ void handleListUsers(message)
325
+ return
326
+ case "updatePage":
327
+ void handleUpdatePage(message)
328
+ return
329
+ case "resize":
330
+ handlers.resize?.(message)
331
+ return
332
+ case "invalidHostMessage":
333
+ return
334
+ default:
335
+ unreachable(message)
336
+ }
337
+ }
338
+
339
+ window.addEventListener("message", onMessage)
340
+ iframe.addEventListener("load", onIframeLoad)
341
+
342
+ return {
343
+ stop() {
344
+ window.removeEventListener("message", onMessage)
345
+ iframe.removeEventListener("load", onIframeLoad)
346
+ clearNoReadyTimeout()
347
+ },
348
+ post,
349
+ setTheme(nextTheme) {
350
+ theme = nextTheme
351
+ const message: ThemeChangedMessage = { type: "themeChanged", theme }
352
+ post(message)
353
+ },
354
+ setParent(nextParent) {
355
+ parent = nextParent
356
+ const message: ParentChangedMessage = { type: "parentChanged", parent }
357
+ post(message)
358
+ },
359
+ setPage(nextPage) {
360
+ page = nextPage
361
+ const message: PageChangedMessage = { type: "pageChanged", page }
362
+ post(message)
363
+ },
364
+ setDataSources(nextDataSources) {
365
+ dataSources = nextDataSources
366
+ const message: DataSourcesChangedMessage = {
367
+ type: "dataSourcesChanged",
368
+ dataSources: nextDataSources,
369
+ }
370
+ post(message)
371
+ },
372
+ setCurrentUser(nextCurrentUser) {
373
+ currentUser = nextCurrentUser
374
+ post({ type: "currentUserChanged", currentUser })
375
+ },
376
+ refreshQuery({ dataSourceId, response }) {
377
+ const refresh = querySubscriptions.resolveRefresh({
378
+ dataSourceId,
379
+ response,
380
+ })
381
+ if (refresh.status === "missing") {
382
+ emit("warn", `No active query exists for data source "${dataSourceId}"`)
383
+ return
384
+ }
385
+ postQueryDataSourceResult({
386
+ requestId: refresh.requestId,
387
+ snapshotId: refresh.snapshotId,
388
+ response: refresh.response,
389
+ })
390
+ },
391
+ }
392
+ }
@@ -0,0 +1,132 @@
1
+ import type {
2
+ CustomBlockInitErrorCode,
3
+ CustomBlockInitErrorInfo,
4
+ } from "../../bridge/messages/init.js"
5
+
6
+ const UNKNOWN_ERROR_MESSAGE =
7
+ "Unexpected error while initializing custom block."
8
+
9
+ /**
10
+ * Returns the error info for a given `init` failure code.
11
+ *
12
+ * `init` handshakes are one-shot: after an `init` error, this host instance will not answer another
13
+ * `ready` message. `isRetryable` means a fresh iframe / `init` attempt could plausibly succeed
14
+ * without changing block code or host configuration.
15
+ */
16
+ export function initErrorForFailureReason(
17
+ reason: CustomBlockInitErrorCode,
18
+ ): CustomBlockInitErrorInfo {
19
+ switch (reason) {
20
+ case "no_ready":
21
+ return {
22
+ code: reason,
23
+ message: "Sandbox did not send a ready message.",
24
+ isRetryable: true,
25
+ }
26
+ case "invalid_ready":
27
+ return {
28
+ code: reason,
29
+ message: "Sandbox sent an invalid ready message.",
30
+ // The sandbox must be updated to send a valid ready message.
31
+ isRetryable: false,
32
+ }
33
+ case "manifest_unavailable":
34
+ return {
35
+ code: reason,
36
+ message: "Sandbox could not load its manifest.",
37
+ isRetryable: true,
38
+ }
39
+ case "manifest_invalid":
40
+ return {
41
+ code: reason,
42
+ message: "Sandbox sent an invalid manifest.",
43
+ // The sandbox must be updated to a valid manifest.
44
+ isRetryable: false,
45
+ }
46
+ case "invalid_protocol_version":
47
+ return {
48
+ code: reason,
49
+ message: "Sandbox reported an invalid bridge protocol version.",
50
+ // The sandbox must be updated to a supported protocol version.
51
+ isRetryable: false,
52
+ }
53
+ case "unsupported_protocol_version":
54
+ return {
55
+ code: reason,
56
+ message: "Sandbox bridge protocol version is not supported.",
57
+ // The sandbox must be updated to a supported protocol version.
58
+ isRetryable: false,
59
+ }
60
+ case "context_unavailable":
61
+ return {
62
+ code: reason,
63
+ message: "Host page context is unavailable.",
64
+ isRetryable: true,
65
+ }
66
+ case "current_user_unavailable":
67
+ return {
68
+ code: reason,
69
+ message: "Current user is unavailable.",
70
+ isRetryable: true,
71
+ }
72
+ case "missing_data_source_binding":
73
+ return {
74
+ code: reason,
75
+ message: "A required data source is not mapped.",
76
+ // Host configuration must be changed before this block can initialize.
77
+ isRetryable: false,
78
+ }
79
+ case "data_source_unavailable":
80
+ return {
81
+ code: reason,
82
+ message: "A mapped data source is unavailable.",
83
+ // This error combines some retryable errors (e.g. data source failed to load) with
84
+ // some non-retryable errors (e.g. data source does not exist or user does not have
85
+ // access). Fallback to assuming the error is retryable.
86
+ isRetryable: true,
87
+ }
88
+ case "missing_property_binding":
89
+ return {
90
+ code: reason,
91
+ message: "A required data source property is not mapped.",
92
+ // Host configuration must be changed before this block can initialize.
93
+ isRetryable: false,
94
+ }
95
+ case "invalid_property_binding":
96
+ return {
97
+ code: reason,
98
+ message: "A mapped data source property is missing or incompatible.",
99
+ // The bound property's schema must be updated or the binding must be updated to
100
+ // a property with a compatible schema.
101
+ isRetryable: false,
102
+ }
103
+ case "not_in_iframe":
104
+ return {
105
+ code: reason,
106
+ message: "<NotionCustomBlock> only works inside an iframe.",
107
+ // The block must be embedded in an iframe.
108
+ isRetryable: false,
109
+ }
110
+ case "init_timeout":
111
+ return {
112
+ code: reason,
113
+ message: "Host did not respond to init before the timeout.",
114
+ isRetryable: true,
115
+ }
116
+ case "unknown_error":
117
+ return {
118
+ code: reason,
119
+ message: UNKNOWN_ERROR_MESSAGE,
120
+ // Treat unknown errors as non-retryable.
121
+ isRetryable: false,
122
+ }
123
+ default:
124
+ console.warn(`Unexpected custom block init error: ${reason}`)
125
+ return {
126
+ code: reason,
127
+ message: UNKNOWN_ERROR_MESSAGE,
128
+ // Treat unknown error codes as non-retryable.
129
+ isRetryable: false,
130
+ }
131
+ }
132
+ }
@@ -0,0 +1,36 @@
1
+ import type { CustomBlockInitErrorCode } from "../../bridge/messages/init.js"
2
+ import type { ReadyMessage } from "../../bridge/messages/ready.js"
3
+
4
+ export type ReadyInitFailure = {
5
+ code: CustomBlockInitErrorCode
6
+ message?: string
7
+ }
8
+
9
+ export function getReadyInitFailure(
10
+ message: ReadyMessage,
11
+ minBridgeProtocolVersion: number,
12
+ ): ReadyInitFailure | undefined {
13
+ if (!isValidBridgeProtocolVersion(message.bridgeProtocolVersion)) {
14
+ return {
15
+ code: "invalid_protocol_version",
16
+ message: `Sandbox reported invalid bridge protocol version ${message.bridgeProtocolVersion}.`,
17
+ }
18
+ }
19
+ if (message.bridgeProtocolVersion < minBridgeProtocolVersion) {
20
+ return {
21
+ code: "unsupported_protocol_version",
22
+ message: `Sandbox bridge protocol version ${message.bridgeProtocolVersion} is below the host minimum ${minBridgeProtocolVersion}.`,
23
+ }
24
+ }
25
+ if (message.status === "error") {
26
+ return {
27
+ code: message.error.code,
28
+ message: message.error.message,
29
+ }
30
+ }
31
+ return undefined
32
+ }
33
+
34
+ function isValidBridgeProtocolVersion(version: number): boolean {
35
+ return Number.isFinite(version) && Number.isInteger(version) && version >= 1
36
+ }
@@ -0,0 +1,28 @@
1
+ import type { NotionDataSourceBindings } from "../../bridge/dataSources/dataSource.js"
2
+ import type { NotionBlockId } from "../../bridge/ids.js"
3
+ import type { ReadyMessage } from "../../bridge/messages/ready.js"
4
+ import type { CustomBlockPage } from "../../bridge/pages/page.js"
5
+ import type { NotionParent } from "../../bridge/parent.js"
6
+ import type { NotionTheme } from "../../bridge/theme.js"
7
+ import type { NotionUser } from "../../bridge/users/user.js"
8
+
9
+ export type ReadySuccessMessage = Extract<ReadyMessage, { status: "success" }>
10
+
11
+ export type InitStatus = "pending" | "success" | "error"
12
+
13
+ export type CustomBlockHostDataSourcesPayload = {
14
+ bindings: NotionDataSourceBindings
15
+ }
16
+
17
+ export type CustomBlockHostInitialDataSources =
18
+ | CustomBlockHostDataSourcesPayload
19
+ | ((ready: ReadySuccessMessage) => CustomBlockHostDataSourcesPayload)
20
+
21
+ export type CustomBlockHostInitialState = {
22
+ theme: NotionTheme
23
+ blockId: NotionBlockId
24
+ parent: NotionParent
25
+ page: CustomBlockPage
26
+ dataSources: CustomBlockHostInitialDataSources
27
+ currentUser: NotionUser
28
+ }
@@ -0,0 +1,38 @@
1
+ import { readIncomingType } from "../../bridge/incomingType.js"
2
+ import type { InvalidSandboxMessage } from "../../bridge/messages/invalidSandboxMessage.js"
3
+
4
+ export type InvalidSandboxMessageResponse = {
5
+ incomingType: string | undefined
6
+ nack?: InvalidSandboxMessage
7
+ }
8
+
9
+ export function getInvalidSandboxMessageResponse(
10
+ data: unknown,
11
+ ): InvalidSandboxMessageResponse {
12
+ const incomingType = readIncomingType(data)
13
+ if (
14
+ incomingType === "invalidHostMessage" ||
15
+ incomingType === "invalidSandboxMessage"
16
+ ) {
17
+ return { incomingType }
18
+ }
19
+ return {
20
+ incomingType,
21
+ nack: {
22
+ type: "invalidSandboxMessage",
23
+ reason: nackReasonForInvalidSandboxMessage(incomingType),
24
+ },
25
+ }
26
+ }
27
+
28
+ function nackReasonForInvalidSandboxMessage(
29
+ incomingType: string | undefined,
30
+ ): string {
31
+ if (incomingType === "ready") {
32
+ return "Invalid manifest"
33
+ }
34
+ if (incomingType !== undefined) {
35
+ return `Unsupported sandbox message of type "${incomingType}"`
36
+ }
37
+ return "Unsupported sandbox message (no `type` field or non-object payload)"
38
+ }
@@ -0,0 +1,67 @@
1
+ import type { CreatePageMessage } from "../../bridge/messages/createPage.js"
2
+ import type { CustomBlockCreatePageErrorInfo } from "../../bridge/messages/createPageResult.js"
3
+ import type {
4
+ CustomBlockGetPageErrorInfo,
5
+ GetPageMessage,
6
+ } from "../../bridge/messages/getPage.js"
7
+ import type {
8
+ CustomBlockGetUserErrorInfo,
9
+ GetUserMessage,
10
+ } from "../../bridge/messages/getUser.js"
11
+ import type {
12
+ CustomBlockListUsersErrorInfo,
13
+ ListUsersMessage,
14
+ } from "../../bridge/messages/listUsers.js"
15
+ import type { QueryDataSourceMessage } from "../../bridge/messages/queryDataSource.js"
16
+ import type { ResizeMessage } from "../../bridge/messages/resize.js"
17
+ import type { UpdatePageMessage } from "../../bridge/messages/updatePage.js"
18
+ import type { CustomBlockUpdatePageErrorInfo } from "../../bridge/messages/updatePageResult.js"
19
+ import type { NotionPage } from "../../bridge/pages/page.js"
20
+ import type { NotionUser, NotionUserList } from "../../bridge/users/user.js"
21
+ import type { CustomBlockHostQueryDataSourceResult } from "../queries/types.js"
22
+
23
+ type MaybePromise<T> = T | Promise<T>
24
+
25
+ export type CustomBlockHostCreatePageResult =
26
+ | { status: "success"; page: NotionPage }
27
+ | { status: "error"; error: CustomBlockCreatePageErrorInfo }
28
+
29
+ export type CustomBlockHostGetPageResult =
30
+ | { status: "success"; page: NotionPage }
31
+ | { status: "error"; error: CustomBlockGetPageErrorInfo }
32
+
33
+ export type CustomBlockHostUpdatePageResult =
34
+ | { status: "success"; page: NotionPage }
35
+ | { status: "error"; error: CustomBlockUpdatePageErrorInfo }
36
+
37
+ export type CustomBlockHostGetUserResult =
38
+ | { status: "success"; user: NotionUser }
39
+ | { status: "error"; error: CustomBlockGetUserErrorInfo }
40
+
41
+ export type CustomBlockHostListUsersResult =
42
+ | { status: "success"; list: NotionUserList }
43
+ | { status: "error"; error: CustomBlockListUsersErrorInfo }
44
+
45
+ export type CustomBlockHostHandlers = {
46
+ queryDataSource: (
47
+ message: QueryDataSourceMessage,
48
+ ) => MaybePromise<CustomBlockHostQueryDataSourceResult>
49
+ createPage?: (
50
+ message: CreatePageMessage,
51
+ ) => MaybePromise<CustomBlockHostCreatePageResult>
52
+ getPage?: (
53
+ message: GetPageMessage,
54
+ ) => MaybePromise<CustomBlockHostGetPageResult>
55
+ getUser?: (
56
+ message: GetUserMessage,
57
+ ) => MaybePromise<CustomBlockHostGetUserResult>
58
+ listUsers?: (
59
+ message: ListUsersMessage,
60
+ ) => MaybePromise<CustomBlockHostListUsersResult>
61
+ updatePage?: (
62
+ message: UpdatePageMessage,
63
+ ) => MaybePromise<CustomBlockHostUpdatePageResult>
64
+ resize?: (message: ResizeMessage) => void
65
+ }
66
+
67
+ export type CustomBlockHostLogDirection = "in" | "out" | "warn"