@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,13 @@
1
+ import * as v from "valibot"
2
+
3
+ export type CustomBlockErrorInfo<TCode extends string = string> = {
4
+ code: TCode
5
+ message: string
6
+ isRetryable: boolean
7
+ }
8
+
9
+ export const customBlockErrorInfoSchema = v.object({
10
+ code: v.string(),
11
+ message: v.string(),
12
+ isRetryable: v.boolean(),
13
+ })
@@ -0,0 +1,154 @@
1
+ import type { NotionDataSource } from "./dataSources/dataSource.js"
2
+ import type {
3
+ NotionDataSourcePage,
4
+ NotionDataSourcePageBridge,
5
+ NotionDataSourcePageUpdateInput,
6
+ NotionDataSourcePageUpdateResult,
7
+ } from "./dataSources/dataSourcePage.js"
8
+ import type { NotionDataSourceValue } from "./dataSources/dataSourceValue.js"
9
+ import type { NotionPropertySchema } from "./dataSources/propertySchema.js"
10
+ import type { NotionBlockId } from "./ids.js"
11
+ import type { CustomBlockQueryDataSourceErrorInfo } from "./messages/queryDataSourceResult.js"
12
+ import type { CustomBlockPage, NotionPageId } from "./pages/page.js"
13
+ import type { NotionParent } from "./parent.js"
14
+ import type { NotionTheme } from "./theme.js"
15
+ import type { NotionUser } from "./users/user.js"
16
+
17
+ export type CustomBlockHostState = UninitializedHostState | InitializedHostState
18
+
19
+ export type UninitializedHostState = {
20
+ status: "uninitialized"
21
+ theme: NotionTheme
22
+ }
23
+
24
+ export type InitializedHostState = {
25
+ status: "initialized"
26
+ theme: NotionTheme
27
+ blockId: NotionBlockId
28
+ parent: NotionParent
29
+ page: CustomBlockPage
30
+ currentUser: NotionUser
31
+ dataSources: NotionDataSource[]
32
+ dataSourceState: Record<string, DataSourceQueryState>
33
+ }
34
+
35
+ export type DataSourceQueryState = {
36
+ /** Latest pages from the host as parsed from the bridge. `propertiesByKey` is derived lazily. */
37
+ items: NotionDataSourcePageBridge[]
38
+ isLoading: boolean
39
+ hasMore: boolean
40
+ error?: CustomBlockQueryDataSourceErrorInfo
41
+ latestRequestId?: string
42
+ latestSnapshotId?: string
43
+ latestLimit?: number
44
+ }
45
+
46
+ export function createEmptyDataSourceQueryState(): DataSourceQueryState {
47
+ return {
48
+ items: [],
49
+ isLoading: false,
50
+ hasMore: false,
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Resolved page + schema views for a single data source, keyed by raw property
56
+ * ID and by user-defined key. Re-derived from `propertyIdsByKey` on every read so
57
+ * renames don't require a re-query.
58
+ */
59
+ export type DataSourceQueryView = {
60
+ items: NotionDataSourcePage[]
61
+ collectionSchema?: NotionDataSource["collectionSchema"]
62
+ propertySchemasById: { [propertyId: string]: NotionPropertySchema }
63
+ propertySchemasByKey: { [key: string]: NotionPropertySchema | undefined }
64
+ isLoading: boolean
65
+ hasMore: boolean
66
+ error?: CustomBlockQueryDataSourceErrorInfo
67
+ }
68
+
69
+ const EMPTY_QUERY_VIEW: DataSourceQueryView = {
70
+ items: [],
71
+ propertySchemasById: {},
72
+ propertySchemasByKey: {},
73
+ isLoading: false,
74
+ hasMore: false,
75
+ }
76
+
77
+ /**
78
+ * Per-row callback that resolves property keys against the data source and forwards the
79
+ * resulting write to the bridge. Injected by callers (typically the SDK singleton in `init.ts`)
80
+ * so this module stays free of bridge-instance dependencies.
81
+ */
82
+ export type UpdateDataSourcePageFn = (args: {
83
+ dataSource: NotionDataSource
84
+ pageId: NotionPageId
85
+ input: NotionDataSourcePageUpdateInput
86
+ }) => Promise<NotionDataSourcePageUpdateResult>
87
+
88
+ export function getDataSourceQueryView(
89
+ hostState: CustomBlockHostState,
90
+ key: string,
91
+ updateDataSourcePage: UpdateDataSourcePageFn,
92
+ ): DataSourceQueryView {
93
+ if (hostState.status !== "initialized") {
94
+ return EMPTY_QUERY_VIEW
95
+ }
96
+
97
+ const dataSource = hostState.dataSources.find(entry => entry.key === key)
98
+ const queryState =
99
+ hostState.dataSourceState[key] ?? createEmptyDataSourceQueryState()
100
+
101
+ if (dataSource === undefined) {
102
+ return {
103
+ items: [],
104
+ collectionSchema: undefined,
105
+ propertySchemasById: {},
106
+ propertySchemasByKey: {},
107
+ isLoading: queryState.isLoading,
108
+ hasMore: queryState.hasMore,
109
+ error: queryState.error,
110
+ }
111
+ }
112
+
113
+ const propertyIdsByKey = dataSource.propertyIdsByKey
114
+ const propertySchemasById = dataSource.propertySchemasById
115
+
116
+ const resolvedItems: NotionDataSourcePage[] = queryState.items.map(entry => {
117
+ const propertiesByKey: {
118
+ [key: string]: NotionDataSourceValue | undefined
119
+ } = {}
120
+ for (const [key, propertyId] of Object.entries(propertyIdsByKey)) {
121
+ propertiesByKey[key] =
122
+ propertyId === undefined ? undefined : entry.propertiesById[propertyId]
123
+ }
124
+ return {
125
+ id: entry.id,
126
+ propertiesById: entry.propertiesById,
127
+ propertiesByKey,
128
+ update: input =>
129
+ updateDataSourcePage({
130
+ dataSource,
131
+ pageId: entry.id,
132
+ input,
133
+ }),
134
+ }
135
+ })
136
+
137
+ const propertySchemasByKey: {
138
+ [key: string]: NotionPropertySchema | undefined
139
+ } = {}
140
+ for (const [key, propertyId] of Object.entries(propertyIdsByKey)) {
141
+ propertySchemasByKey[key] =
142
+ propertyId === undefined ? undefined : propertySchemasById[propertyId]
143
+ }
144
+
145
+ return {
146
+ items: resolvedItems,
147
+ collectionSchema: dataSource.collectionSchema,
148
+ propertySchemasById,
149
+ propertySchemasByKey,
150
+ isLoading: queryState.isLoading,
151
+ hasMore: queryState.hasMore,
152
+ error: queryState.error,
153
+ }
154
+ }
@@ -0,0 +1,58 @@
1
+ import * as v from "valibot"
2
+
3
+ declare const notionDataSourceIdBrand: unique symbol
4
+ declare const notionBlockIdBrand: unique symbol
5
+ declare const notionPageIdBrand: unique symbol
6
+ declare const notionSpaceIdBrand: unique symbol
7
+
8
+ /**
9
+ * Branded Notion block ID. Host payloads and SDK APIs use plain strings at runtime,
10
+ * but the brand keeps block IDs from being accidentally mixed with other IDs in TypeScript.
11
+ */
12
+ export type NotionBlockId = string & {
13
+ readonly [notionBlockIdBrand]: "NotionBlockId"
14
+ }
15
+
16
+ export const notionBlockIdSchema = v.custom<NotionBlockId>(
17
+ value => typeof value === "string",
18
+ "Expected a Notion block ID",
19
+ )
20
+
21
+ /**
22
+ * Branded Notion data source ID. Host payloads and SDK APIs use plain strings at runtime,
23
+ * but the brand keeps data source IDs from being accidentally mixed with other IDs in TypeScript.
24
+ */
25
+ export type NotionDataSourceId = string & {
26
+ readonly [notionDataSourceIdBrand]: "NotionDataSourceId"
27
+ }
28
+
29
+ export const notionDataSourceIdSchema = v.custom<NotionDataSourceId>(
30
+ value => typeof value === "string",
31
+ "Expected a Notion data source ID",
32
+ )
33
+
34
+ /**
35
+ * Branded Notion page ID. Host payloads and SDK APIs use plain strings at runtime,
36
+ * but the brand keeps page IDs from being accidentally mixed with other IDs in TypeScript.
37
+ */
38
+ export type NotionPageId = string & {
39
+ readonly [notionPageIdBrand]: "NotionPageId"
40
+ }
41
+
42
+ export const notionPageIdSchema = v.custom<NotionPageId>(
43
+ value => typeof value === "string",
44
+ "Expected a Notion page ID",
45
+ )
46
+
47
+ /**
48
+ * Branded Notion workspace/space ID. Host payloads and SDK APIs use plain strings at runtime,
49
+ * but the brand keeps space IDs from being accidentally mixed with other IDs in TypeScript.
50
+ */
51
+ export type NotionSpaceId = string & {
52
+ readonly [notionSpaceIdBrand]: "NotionSpaceId"
53
+ }
54
+
55
+ export const notionSpaceIdSchema = v.custom<NotionSpaceId>(
56
+ value => typeof value === "string",
57
+ "Expected a Notion space ID",
58
+ )
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Reads the `type` field off an inbound bridge message, if any.
3
+ *
4
+ * Used by both sides of the bridge after the canonical message schema rejects a payload. We still
5
+ * want to know what type the sender claimed to be sending so we can (a) avoid NACK loops on
6
+ * `invalidHostMessage` / `invalidSandboxMessage` and (b) include the type in the failure reason
7
+ * for debugging.
8
+ */
9
+ export function readIncomingType(data: unknown): string | undefined {
10
+ if (
11
+ typeof data === "object" &&
12
+ data !== null &&
13
+ "type" in data &&
14
+ typeof data.type === "string"
15
+ ) {
16
+ return data.type
17
+ }
18
+ return undefined
19
+ }
@@ -0,0 +1,89 @@
1
+ import * as v from "valibot"
2
+ import { type CustomBlockManifest, manifestSchema } from "./manifest.js"
3
+
4
+ const MANIFEST_URL = "custom_blocks.json"
5
+
6
+ // The schema accepts any string code. The type lists known codes for
7
+ // autocomplete, with an open string fallback for newer senders.
8
+ export const customBlockReadyErrorCodeSchema = v.string()
9
+
10
+ export type CustomBlockReadyErrorCode =
11
+ | "manifest_unavailable"
12
+ | "manifest_invalid"
13
+ | (string & {})
14
+
15
+ export const customBlockReadyErrorSchema = v.object({
16
+ code: customBlockReadyErrorCodeSchema,
17
+ message: v.string(),
18
+ })
19
+
20
+ export type CustomBlockReadyError = v.InferOutput<
21
+ typeof customBlockReadyErrorSchema
22
+ >
23
+
24
+ export type ManifestLoadResult =
25
+ | {
26
+ manifest: CustomBlockManifest | null
27
+ error?: undefined
28
+ }
29
+ | {
30
+ manifest: null
31
+ error: CustomBlockReadyError
32
+ }
33
+
34
+ /**
35
+ * Attempts to load a `custom_blocks.json` manifest co-located with the bundle.
36
+ * A missing manifest means the block has no declared data requirements.
37
+ * Other failures return a structured error so the host can reject the manifest.
38
+ * The SDK validates the manifest locally for author feedback; the host still
39
+ * validates the ready message as the iframe trust boundary.
40
+ */
41
+ export async function loadManifest(): Promise<ManifestLoadResult> {
42
+ if (typeof fetch !== "function") {
43
+ const message = `No fetch API available; cannot load ${MANIFEST_URL}.`
44
+ console.warn(`[notion-custom-sdk] ${message}`)
45
+ return {
46
+ manifest: null,
47
+ error: { code: "manifest_unavailable", message },
48
+ }
49
+ }
50
+ let response: Response
51
+ try {
52
+ response = await fetch(MANIFEST_URL, { credentials: "omit" })
53
+ } catch (error) {
54
+ const message = `Could not fetch ${MANIFEST_URL}.`
55
+ console.warn(`[notion-custom-sdk] ${message}`, error)
56
+ return {
57
+ manifest: null,
58
+ error: { code: "manifest_unavailable", message },
59
+ }
60
+ }
61
+ if (response.status === 404) {
62
+ const message = `No manifest found at ${MANIFEST_URL} (status ${response.status}).`
63
+ console.warn(`[notion-custom-sdk] ${message}`)
64
+ return { manifest: null }
65
+ }
66
+ if (!response.ok) {
67
+ const message = `Could not fetch ${MANIFEST_URL} (status ${response.status}).`
68
+ console.warn(`[notion-custom-sdk] ${message}`)
69
+ return {
70
+ manifest: null,
71
+ error: { code: "manifest_unavailable", message },
72
+ }
73
+ }
74
+ let json: unknown
75
+ try {
76
+ json = await response.json()
77
+ } catch (error) {
78
+ const message = `Manifest at ${MANIFEST_URL} was not valid JSON.`
79
+ console.warn(`[notion-custom-sdk] ${message}`, error)
80
+ return { manifest: null, error: { code: "manifest_invalid", message } }
81
+ }
82
+ const parsed = v.safeParse(manifestSchema, json)
83
+ if (!parsed.success) {
84
+ const message = `Manifest at ${MANIFEST_URL} did not match schema.`
85
+ console.warn(`[notion-custom-sdk] ${message}`, parsed.issues)
86
+ return { manifest: null, error: { code: "manifest_invalid", message } }
87
+ }
88
+ return { manifest: parsed.output }
89
+ }
@@ -0,0 +1,53 @@
1
+ import * as v from "valibot"
2
+ import { notionPropertyTypeSchema } from "./dataSources/propertySchema.js"
3
+
4
+ /**
5
+ * User-authored manifest declaring the data sources the custom block expects.
6
+ * Lives at `custom_blocks.json` in the project root and is forwarded to the host with
7
+ * the bridge `ready` message so the host can pre-bind data sources, surface
8
+ * configuration UI, etc. The `notionCustomBlock` Vite plugin from
9
+ * `@notionhq/custom-blocks/vite` wires the JSON file into the dev server and
10
+ * the build output.
11
+ */
12
+
13
+ /**
14
+ * Decorative icon attached to a manifest data source. Mirrors the
15
+ * `emoji` / `external` icon variants the public Notion API uses, so the host
16
+ * can render a recognizable affordance next to the slot in setup UI.
17
+ */
18
+ export const manifestIconSchema = v.variant("type", [
19
+ v.object({
20
+ type: v.literal("emoji"),
21
+ emoji: v.string(),
22
+ }),
23
+ v.object({
24
+ type: v.literal("external"),
25
+ url: v.string(),
26
+ }),
27
+ ])
28
+
29
+ export type ManifestIcon = v.InferOutput<typeof manifestIconSchema>
30
+
31
+ export const manifestPropertySchema = v.object({
32
+ name: v.string(),
33
+ description: v.optional(v.string()),
34
+ type: notionPropertyTypeSchema,
35
+ })
36
+
37
+ export type ManifestProperty = v.InferOutput<typeof manifestPropertySchema>
38
+
39
+ export const manifestDataSourceSchema = v.object({
40
+ name: v.string(),
41
+ description: v.optional(v.string()),
42
+ icon: v.optional(manifestIconSchema),
43
+ properties: v.optional(v.record(v.string(), manifestPropertySchema), {}),
44
+ })
45
+
46
+ export type ManifestDataSource = v.InferOutput<typeof manifestDataSourceSchema>
47
+
48
+ export const manifestSchema = v.object({
49
+ version: v.literal(1),
50
+ dataSources: v.record(v.string(), manifestDataSourceSchema),
51
+ })
52
+
53
+ export type CustomBlockManifest = v.InferOutput<typeof manifestSchema>
@@ -0,0 +1,67 @@
1
+ import * as v from "valibot"
2
+ import {
3
+ notionBlockIdSchema,
4
+ notionDataSourceIdSchema,
5
+ notionPageIdSchema,
6
+ } from "../ids.js"
7
+ import {
8
+ notionPageCoverSchema,
9
+ notionPageIconSchema,
10
+ notionPagePropertyWriteMapSchema,
11
+ } from "../pages/page.js"
12
+
13
+ /**
14
+ * Describes where the newly created page should be inserted.
15
+ * - `start` / `end` (default): prepend / append to the request's `parent`
16
+ * page's children.
17
+ * - `before` / `after`: insert as a sibling of the anchor block identified by
18
+ * `blockId`, using the anchor's own structural parent. The anchor can be
19
+ * nested anywhere below the page (inside a toggle, column, callout, etc.).
20
+ */
21
+ export const notionCreatePagePositionSchema = v.variant("type", [
22
+ v.object({ type: v.literal("start") }),
23
+ v.object({ type: v.literal("end") }),
24
+ v.object({ type: v.literal("before"), blockId: notionBlockIdSchema }),
25
+ v.object({ type: v.literal("after"), blockId: notionBlockIdSchema }),
26
+ ])
27
+
28
+ export type NotionCreatePagePosition = v.InferOutput<
29
+ typeof notionCreatePagePositionSchema
30
+ >
31
+
32
+ /**
33
+ * Parent accepted by the outbound `createPage` bridge message.
34
+ *
35
+ * Mirrors Notion's public API `POST /v1/pages` parent shape: `page_id` for a page-parented child,
36
+ * `data_source_id` for a database row. `data_source_id` is the internal collection ID.
37
+ *
38
+ * The SDK additionally accepts a higher-level `data_source_key` variant that resolves to
39
+ * `data_source_id` locally, using the `dataSources` mapping.
40
+ */
41
+ export const createPageParentSchema = v.variant("type", [
42
+ v.object({ type: v.literal("page_id"), page_id: notionPageIdSchema }),
43
+ v.object({
44
+ type: v.literal("data_source_id"),
45
+ data_source_id: notionDataSourceIdSchema,
46
+ }),
47
+ ])
48
+
49
+ export type CreatePageMessageParent = v.InferOutput<
50
+ typeof createPageParentSchema
51
+ >
52
+
53
+ /**
54
+ * Message sent by the sandbox to ask the host to create a new page on its behalf.
55
+ * The payload mirrors Notion's public `POST /v1/pages` API.
56
+ */
57
+ export const createPageMessageSchema = v.object({
58
+ type: v.literal("createPage"),
59
+ requestId: v.string(),
60
+ parent: createPageParentSchema,
61
+ properties: notionPagePropertyWriteMapSchema,
62
+ icon: v.optional(notionPageIconSchema),
63
+ cover: v.optional(notionPageCoverSchema),
64
+ position: v.optional(notionCreatePagePositionSchema),
65
+ })
66
+
67
+ export type CreatePageMessage = v.InferOutput<typeof createPageMessageSchema>
@@ -0,0 +1,52 @@
1
+ import * as v from "valibot"
2
+ import type { CustomBlockErrorInfo } from "../errors.js"
3
+ import { notionPageSchema } from "../pages/page.js"
4
+
5
+ // The schema accepts any string code. The type lists known codes for
6
+ // autocomplete, with an open string fallback for newer senders.
7
+ export const customBlockCreatePageErrorCodeSchema = v.string()
8
+
9
+ export type CustomBlockCreatePageErrorCode =
10
+ | "create_page_failed"
11
+ | "invalid_page_parent"
12
+ | "page_access_failed"
13
+ | "collection_access_failed"
14
+ | "unknown_data_source_key"
15
+ | "unmapped_data_source"
16
+ | "unmapped_property"
17
+ | "property_id_mismatch"
18
+ | "duplicate_property"
19
+ | "invalid_property_value"
20
+ | (string & {})
21
+
22
+ export type CustomBlockCreatePageErrorInfo =
23
+ CustomBlockErrorInfo<CustomBlockCreatePageErrorCode>
24
+
25
+ export const customBlockCreatePageErrorInfoSchema = v.object({
26
+ code: customBlockCreatePageErrorCodeSchema,
27
+ message: v.string(),
28
+ isRetryable: v.boolean(),
29
+ })
30
+
31
+ /**
32
+ * Message sent by the host in response to a sandbox `createPage` request.
33
+ */
34
+ export const createPageResultMessageSchema = v.variant("status", [
35
+ v.object({
36
+ type: v.literal("createPageResult"),
37
+ requestId: v.string(),
38
+ status: v.literal("success"),
39
+ /** The newly created page. */
40
+ page: notionPageSchema,
41
+ }),
42
+ v.object({
43
+ type: v.literal("createPageResult"),
44
+ requestId: v.string(),
45
+ status: v.literal("error"),
46
+ error: customBlockCreatePageErrorInfoSchema,
47
+ }),
48
+ ])
49
+
50
+ export type CreatePageResultMessage = v.InferOutput<
51
+ typeof createPageResultMessageSchema
52
+ >
@@ -0,0 +1,14 @@
1
+ import * as v from "valibot"
2
+ import { notionUserSchema } from "../users/user.js"
3
+
4
+ /**
5
+ * Sent by the host when the viewing user's profile changes while the block is mounted.
6
+ */
7
+ export const currentUserChangedMessageSchema = v.object({
8
+ type: v.literal("currentUserChanged"),
9
+ currentUser: notionUserSchema,
10
+ })
11
+
12
+ export type CurrentUserChangedMessage = v.InferOutput<
13
+ typeof currentUserChangedMessageSchema
14
+ >
@@ -0,0 +1,18 @@
1
+ import * as v from "valibot"
2
+ import { notionDataSourceBindingsSchema } from "../dataSources/dataSource.js"
3
+
4
+ /**
5
+ * Message sent by the host whenever the custom block's data-source mapping changes (e.g. a key is
6
+ * added, removed, or remapped). The SDK replaces its list of configured data sources but keeps any
7
+ * in-flight `useDataSource` query state for keys that still exist.
8
+ */
9
+ export const dataSourcesChangedMessageSchema = v.object({
10
+ type: v.literal("dataSourcesChanged"),
11
+ dataSources: v.object({
12
+ bindings: notionDataSourceBindingsSchema,
13
+ }),
14
+ })
15
+
16
+ export type DataSourcesChangedMessage = v.InferOutput<
17
+ typeof dataSourcesChangedMessageSchema
18
+ >
@@ -0,0 +1,52 @@
1
+ import * as v from "valibot"
2
+ import type { CustomBlockErrorInfo } from "../errors.js"
3
+ import { notionPageIdSchema } from "../ids.js"
4
+ import { notionPageSchema } from "../pages/page.js"
5
+
6
+ // The schema accepts any string code. The type lists known codes for
7
+ // autocomplete, with an open string fallback for newer senders.
8
+ export const customBlockGetPageErrorCodeSchema = v.string()
9
+
10
+ export type CustomBlockGetPageErrorCode =
11
+ | "get_page_failed"
12
+ | "page_access_failed"
13
+ | (string & {})
14
+
15
+ export type CustomBlockGetPageErrorInfo =
16
+ CustomBlockErrorInfo<CustomBlockGetPageErrorCode>
17
+
18
+ export const customBlockGetPageErrorInfoSchema = v.object({
19
+ code: customBlockGetPageErrorCodeSchema,
20
+ message: v.string(),
21
+ isRetryable: v.boolean(),
22
+ })
23
+
24
+ /**
25
+ * Sandbox -> host: fetch a page by ID.
26
+ */
27
+ export const getPageMessageSchema = v.object({
28
+ type: v.literal("getPage"),
29
+ requestId: v.string(),
30
+ pageId: notionPageIdSchema,
31
+ })
32
+
33
+ export type GetPageMessage = v.InferOutput<typeof getPageMessageSchema>
34
+
35
+ export const getPageResultMessageSchema = v.variant("status", [
36
+ v.object({
37
+ type: v.literal("getPageResult"),
38
+ requestId: v.string(),
39
+ status: v.literal("success"),
40
+ page: notionPageSchema,
41
+ }),
42
+ v.object({
43
+ type: v.literal("getPageResult"),
44
+ requestId: v.string(),
45
+ status: v.literal("error"),
46
+ error: customBlockGetPageErrorInfoSchema,
47
+ }),
48
+ ])
49
+
50
+ export type GetPageResultMessage = v.InferOutput<
51
+ typeof getPageResultMessageSchema
52
+ >
@@ -0,0 +1,48 @@
1
+ import * as v from "valibot"
2
+ import type { CustomBlockErrorInfo } from "../errors.js"
3
+ import { notionUserSchema } from "../users/user.js"
4
+
5
+ // The schema accepts any string code. The type lists known codes for
6
+ // autocomplete, with an open string fallback for newer senders.
7
+ export const customBlockGetUserErrorCodeSchema = v.string()
8
+
9
+ export type CustomBlockGetUserErrorCode = "get_user_failed" | (string & {})
10
+
11
+ export type CustomBlockGetUserErrorInfo =
12
+ CustomBlockErrorInfo<CustomBlockGetUserErrorCode>
13
+
14
+ export const customBlockGetUserErrorInfoSchema = v.object({
15
+ code: customBlockGetUserErrorCodeSchema,
16
+ message: v.string(),
17
+ isRetryable: v.boolean(),
18
+ })
19
+
20
+ /**
21
+ * Sandbox -> host: fetch a user by ID.
22
+ */
23
+ export const getUserMessageSchema = v.object({
24
+ type: v.literal("getUser"),
25
+ requestId: v.string(),
26
+ userId: v.string(),
27
+ })
28
+
29
+ export type GetUserMessage = v.InferOutput<typeof getUserMessageSchema>
30
+
31
+ export const getUserResultMessageSchema = v.variant("status", [
32
+ v.object({
33
+ type: v.literal("getUserResult"),
34
+ requestId: v.string(),
35
+ status: v.literal("success"),
36
+ user: notionUserSchema,
37
+ }),
38
+ v.object({
39
+ type: v.literal("getUserResult"),
40
+ requestId: v.string(),
41
+ status: v.literal("error"),
42
+ error: customBlockGetUserErrorInfoSchema,
43
+ }),
44
+ ])
45
+
46
+ export type GetUserResultMessage = v.InferOutput<
47
+ typeof getUserResultMessageSchema
48
+ >
@@ -0,0 +1,37 @@
1
+ import * as v from "valibot"
2
+ import { createPageResultMessageSchema } from "./createPageResult.js"
3
+ import { currentUserChangedMessageSchema } from "./currentUserChanged.js"
4
+ import { dataSourcesChangedMessageSchema } from "./dataSourcesChanged.js"
5
+ import { getPageResultMessageSchema } from "./getPage.js"
6
+ import { getUserResultMessageSchema } from "./getUser.js"
7
+ import { initMessageSchema } from "./init.js"
8
+ import { invalidSandboxMessageSchema } from "./invalidSandboxMessage.js"
9
+ import { listUsersResultMessageSchema } from "./listUsers.js"
10
+ import { pageChangedMessageSchema } from "./pageChanged.js"
11
+ import { parentChangedMessageSchema } from "./parentChanged.js"
12
+ import { queryDataSourceResultMessageSchema } from "./queryDataSourceResult.js"
13
+ import { themeChangedMessageSchema } from "./themeChanged.js"
14
+ import { updatePageResultMessageSchema } from "./updatePageResult.js"
15
+
16
+ /**
17
+ * Discriminated union of every message the host is allowed to send the sandbox.
18
+ */
19
+ export const hostToSandboxMessageSchema = v.variant("type", [
20
+ initMessageSchema,
21
+ themeChangedMessageSchema,
22
+ parentChangedMessageSchema,
23
+ pageChangedMessageSchema,
24
+ currentUserChangedMessageSchema,
25
+ dataSourcesChangedMessageSchema,
26
+ queryDataSourceResultMessageSchema,
27
+ createPageResultMessageSchema,
28
+ getPageResultMessageSchema,
29
+ getUserResultMessageSchema,
30
+ listUsersResultMessageSchema,
31
+ updatePageResultMessageSchema,
32
+ invalidSandboxMessageSchema,
33
+ ])
34
+
35
+ export type HostToSandboxMessage = v.InferOutput<
36
+ typeof hostToSandboxMessageSchema
37
+ >