@notionhq/custom-blocks 0.1.45 → 0.1.47

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 (51) hide show
  1. package/README.md +3 -4
  2. package/dist/bridge/SandboxBridge.d.ts +1 -1
  3. package/dist/bridge/SandboxBridge.d.ts.map +1 -1
  4. package/dist/bridge/SandboxBridge.js +3 -20
  5. package/dist/bridge/sandboxClient.d.ts +1 -1
  6. package/dist/bridge/sandboxClient.d.ts.map +1 -1
  7. package/dist/bridge/sandboxClient.js +2 -2
  8. package/dist/customBlock.d.ts +76 -3
  9. package/dist/customBlock.d.ts.map +1 -1
  10. package/dist/customBlock.js +120 -2
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/init.d.ts +2 -14
  14. package/dist/init.d.ts.map +1 -1
  15. package/dist/init.js +7 -30
  16. package/dist/protocol/index.d.ts +3 -2
  17. package/dist/protocol/index.js +3 -2
  18. package/dist/protocol/messages/init.d.ts +2 -2
  19. package/dist/protocol/messages/isInitHandshakeMessage.d.ts +4 -0
  20. package/dist/protocol/messages/isInitHandshakeMessage.js +11 -0
  21. package/dist/protocol/{messagePayload.d.ts → messages/messagePayload.d.ts} +1 -1
  22. package/dist/react/NotionCustomBlock.d.ts +3 -5
  23. package/dist/react/NotionCustomBlock.d.ts.map +1 -1
  24. package/dist/react/NotionCustomBlock.js +3 -4
  25. package/dist/react/useCustomBlockInit.d.ts +4 -4
  26. package/dist/react/useCustomBlockInit.d.ts.map +1 -1
  27. package/dist/react/useCustomBlockInit.js +3 -4
  28. package/dist/react/useRuntimeState.d.ts +3 -5
  29. package/dist/react/useRuntimeState.d.ts.map +1 -1
  30. package/dist/react/useRuntimeState.js +10 -12
  31. package/dist/types.d.ts +1 -1
  32. package/dist/types.d.ts.map +1 -1
  33. package/dist/version.js +1 -1
  34. package/docs/block-location.md +51 -55
  35. package/docs/data-sources.md +3 -3
  36. package/docs/errors.md +1 -1
  37. package/docs/lifecycle.md +60 -25
  38. package/docs/users.md +4 -3
  39. package/package.json +1 -1
  40. package/src/bridge/SandboxBridge.ts +3 -23
  41. package/src/bridge/sandboxClient.ts +2 -2
  42. package/src/customBlock.ts +139 -3
  43. package/src/index.ts +0 -1
  44. package/src/init.ts +7 -52
  45. package/src/react/NotionCustomBlock.tsx +4 -6
  46. package/src/react/useCustomBlockInit.ts +5 -12
  47. package/src/react/useRuntimeState.ts +25 -12
  48. package/src/types.ts +1 -1
  49. /package/dist/protocol/{incomingType.d.ts → messages/incomingType.d.ts} +0 -0
  50. /package/dist/protocol/{incomingType.js → messages/incomingType.js} +0 -0
  51. /package/dist/protocol/{messagePayload.js → messages/messagePayload.js} +0 -0
package/docs/users.md CHANGED
@@ -29,14 +29,15 @@ For non-React renderers, read the same state through `customBlock` after initial
29
29
 
30
30
  ```ts
31
31
  await initCustomBlock()
32
- const me = customBlock.getCurrentUser()
33
32
 
34
- const unsubscribe = customBlock.subscribe(() => {
35
- const nextMe = customBlock.getCurrentUser()
33
+ const unsubscribe = customBlock.subscribeToCurrentUser(nextMe => {
36
34
  // Update your renderer with nextMe.
37
35
  })
38
36
  ```
39
37
 
38
+ Call `unsubscribe()` when your renderer is removed. The callback receives the initial profile and later changes.
39
+ See [context subscriptions](./block-location.md#reading-values-and-following-changes) for initialization and equality rules.
40
+
40
41
  ## Listing users
41
42
 
42
43
  `users.list(input?)` returns workspace users visible to the current custom block, mirroring Notion's [`GET /v1/users`](https://developers.notion.com/reference/get-users) shape.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notionhq/custom-blocks",
3
- "version": "0.1.45",
3
+ "version": "0.1.47",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -5,7 +5,6 @@ import type {
5
5
  NotionDataSourceBindings,
6
6
  } from "@notionhq/custom-blocks-protocol/dataSources/dataSource.js"
7
7
  import type { NotionPageId } from "@notionhq/custom-blocks-protocol/ids.js"
8
- import { readIncomingType } from "@notionhq/custom-blocks-protocol/incomingType.js"
9
8
  import type { CustomBlockManifest } from "@notionhq/custom-blocks-protocol/manifest.js"
10
9
  import type { ConnectMessage } from "@notionhq/custom-blocks-protocol/messages/connect.js"
11
10
  import type {
@@ -18,6 +17,7 @@ import {
18
17
  hostToSandboxMessageSchema,
19
18
  type PrimeableHostToSandboxMessage,
20
19
  } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
20
+ import { readIncomingType } from "@notionhq/custom-blocks-protocol/messages/incomingType.js"
21
21
  import {
22
22
  CustomBlockInitializationError,
23
23
  type InitMessage,
@@ -173,28 +173,8 @@ export class SandboxBridge {
173
173
  return () => this.messageLogListeners.delete(listener)
174
174
  }
175
175
 
176
- awaitInit(signal?: AbortSignal): Promise<void> {
177
- if (!signal) {
178
- return this.initMessage
179
- }
180
- return new Promise((resolve, reject) => {
181
- if (signal.aborted) {
182
- reject(signal.reason)
183
- return
184
- }
185
- const onAbort = () => reject(signal.reason)
186
- signal.addEventListener("abort", onAbort, { once: true })
187
- this.initMessage.then(
188
- () => {
189
- signal.removeEventListener("abort", onAbort)
190
- resolve()
191
- },
192
- err => {
193
- signal.removeEventListener("abort", onAbort)
194
- reject(err)
195
- },
196
- )
197
- })
176
+ awaitInit(): Promise<void> {
177
+ return this.initMessage
198
178
  }
199
179
 
200
180
  sendConnect(manifestResult: ManifestLoadResult) {
@@ -42,8 +42,8 @@ export const customBlockHost = {
42
42
  getBridge().sendConnect(manifestResult)
43
43
  },
44
44
 
45
- awaitInit: (signal?: AbortSignal): Promise<void> => {
46
- return getBridge().awaitInit(signal)
45
+ awaitInit: (): Promise<void> => {
46
+ return getBridge().awaitInit()
47
47
  },
48
48
 
49
49
  subscribe: (listener: () => void) => {
@@ -12,8 +12,10 @@ import type {
12
12
  CustomBlockHostState,
13
13
  InitializedHostState,
14
14
  } from "./bridge/hostState.js"
15
+ import { notifyListener } from "./bridge/notifyListener.js"
15
16
  import { customBlockHost } from "./bridge/sandboxClient.js"
16
17
 
18
+ /** @deprecated Use individual context types and getters after `initCustomBlock` resolves. */
17
19
  export type CustomBlockState =
18
20
  | {
19
21
  status: "uninitialized"
@@ -31,47 +33,144 @@ export type CustomBlockState =
31
33
  dataSources: NotionDataSource[]
32
34
  }
33
35
 
36
+ type Unsubscribe = () => void
37
+
34
38
  /**
35
- * Framework-neutral runtime state APIs for a custom block. Call `initCustomBlock`
36
- * before reading initialized values, and use `subscribe` to react to host pushes.
39
+ * Read block and app context with getters, or follow changes with subscriptions.
40
+ * Getters throw if called before `initCustomBlock()` completes. Subscriptions
41
+ * can be registered before initialization and deliver the initial value once
42
+ * initialization completes, followed by changes to that value.
43
+ *
44
+ * Each subscription returns an unsubscribe function. Call it when the
45
+ * subscription is no longer needed.
37
46
  */
38
47
  export const customBlock = {
39
- subscribe(listener: () => void): () => void {
48
+ /** @deprecated Use an individual subscription method, such as `subscribeToTheme()` or `subscribeToCurrentUser()`. */
49
+ subscribe(listener: () => void): Unsubscribe {
40
50
  return customBlockHost.subscribe(listener)
41
51
  },
42
52
 
53
+ /** @deprecated Use an individual getter method, such as `getTheme()` or `getCurrentUser()`, after `initCustomBlock()` resolves. */
43
54
  getState(): CustomBlockState {
44
55
  return toPublicState(customBlockHost.getState())
45
56
  },
46
57
 
58
+ /**
59
+ * Get the current user viewing the block.
60
+ * Throws if called before SDK initialization completes.
61
+ */
47
62
  getCurrentUser(): NotionUser {
48
63
  return getInitializedHostState("getCurrentUser").currentUser
49
64
  },
50
65
 
66
+ /**
67
+ * Subscribes to the current `currentUser` and future changes.
68
+ * Waits for initialization before delivering the initial value.
69
+ */
70
+ subscribeToCurrentUser(listener: (value: NotionUser) => void): Unsubscribe {
71
+ return subscribeToContext({ key: "currentUser", listener })
72
+ },
73
+
74
+ /**
75
+ * Get the host's current theme.
76
+ * Throws if called before SDK initialization completes.
77
+ */
51
78
  getTheme(): NotionTheme {
52
79
  return getInitializedHostState("getTheme").theme
53
80
  },
54
81
 
82
+ /**
83
+ * Subscribes to the current `theme` and future changes.
84
+ * Waits for initialization before delivering the initial value.
85
+ */
86
+ subscribeToTheme(listener: (value: NotionTheme) => void): Unsubscribe {
87
+ return subscribeToContext({ key: "theme", listener })
88
+ },
89
+
90
+ /**
91
+ * Get the host's current contrast mode.
92
+ * Throws if called before SDK initialization completes.
93
+ */
55
94
  getContrastMode(): NotionContrastMode {
56
95
  return getInitializedHostState("getContrastMode").contrastMode
57
96
  },
58
97
 
98
+ /**
99
+ * Subscribes to the current `contrastMode` and future changes.
100
+ * Waits for initialization before delivering the initial value.
101
+ */
102
+ subscribeToContrastMode(
103
+ listener: (value: NotionContrastMode) => void,
104
+ ): Unsubscribe {
105
+ return subscribeToContext({ key: "contrastMode", listener })
106
+ },
107
+
108
+ /**
109
+ * Get the custom block's ID. The ID is stable for the lifetime of the block.
110
+ * Throws if called before SDK initialization completes.
111
+ */
59
112
  getBlockId(): NotionBlockId {
60
113
  return getInitializedHostState("getBlockId").blockId
61
114
  },
62
115
 
116
+ /**
117
+ * Delivers the block ID once. The ID is stable for the lifetime of the block.
118
+ * Waits for initialization before delivering the initial value.
119
+ */
120
+ subscribeToBlockId(listener: (value: NotionBlockId) => void): Unsubscribe {
121
+ return subscribeToContext({ key: "blockId", listener })
122
+ },
123
+
124
+ /**
125
+ * Get the custom block's parent.
126
+ * Throws if called before SDK initialization completes.
127
+ */
63
128
  getParent(): NotionParent {
64
129
  return getInitializedHostState("getParent").parent
65
130
  },
66
131
 
132
+ /**
133
+ * Subscribes to the current `parent` and future changes.
134
+ * Waits for initialization before delivering the initial value.
135
+ */
136
+ subscribeToParent(listener: (value: NotionParent) => void): Unsubscribe {
137
+ return subscribeToContext({ key: "parent", listener })
138
+ },
139
+
140
+ /**
141
+ * Get the containing page and its parent.
142
+ * Throws if called before SDK initialization completes.
143
+ */
67
144
  getPage(): CustomBlockPage {
68
145
  return getInitializedHostState("getPage").page
69
146
  },
70
147
 
148
+ /**
149
+ * Subscribes to the current `page` and future changes.
150
+ * Waits for initialization before delivering the initial value.
151
+ */
152
+ subscribeToPage(listener: (value: CustomBlockPage) => void): Unsubscribe {
153
+ return subscribeToContext({ key: "page", listener })
154
+ },
155
+
156
+ /**
157
+ * Get the block's declared data source manifest.
158
+ * Throws if called before SDK initialization completes.
159
+ */
71
160
  getManifest(): CustomBlockManifest {
72
161
  return getInitializedHostState("getManifest").manifest
73
162
  },
74
163
 
164
+ /**
165
+ * Subscribes to the current `manifest` and future changes.
166
+ * Waits for initialization before delivering the initial value.
167
+ */
168
+ subscribeToManifest(
169
+ listener: (value: CustomBlockManifest) => void,
170
+ ): Unsubscribe {
171
+ return subscribeToContext({ key: "manifest", listener })
172
+ },
173
+
75
174
  autoResize,
76
175
 
77
176
  /**
@@ -119,3 +218,40 @@ function getInitializedHostState(methodName: string): InitializedHostState {
119
218
  }
120
219
  return hostState
121
220
  }
221
+
222
+ /**
223
+ * Subscribe to one context value. Wait for initialization before delivering
224
+ * the initial value, then deliver updates when that value changes.
225
+ * Return a function that stops delivery, including before initialization.
226
+ */
227
+ function subscribeToContext<K extends keyof InitializedHostState>(args: {
228
+ key: K
229
+ listener: (value: InitializedHostState[K]) => void
230
+ }): Unsubscribe {
231
+ const { key, listener } = args
232
+ let active = true
233
+ let delivered = false
234
+ let previous: InitializedHostState[K] | undefined
235
+ const deliverIfChanged = () => {
236
+ const state = customBlockHost.getState()
237
+ if (!active || state.status !== "initialized") {
238
+ return
239
+ }
240
+ const next = state[key]
241
+ // Use referential equality to keep the implementation simple.
242
+ // Callbacks can receive different objects with the same contents.
243
+ // We have not benchmarked this choice. Deep equality may also be suitable.
244
+ if (delivered && Object.is(previous, next)) {
245
+ return
246
+ }
247
+ previous = next
248
+ delivered = true
249
+ notifyListener(() => listener(next))
250
+ }
251
+ const unsubscribe = customBlockHost.subscribe(deliverIfChanged)
252
+ deliverIfChanged()
253
+ return () => {
254
+ active = false
255
+ unsubscribe()
256
+ }
257
+ }
package/src/index.ts CHANGED
@@ -101,7 +101,6 @@ export { pages, users } from "./bridge/sandboxClient.js"
101
101
  export { type CustomBlockState, customBlock } from "./customBlock.js"
102
102
  export {
103
103
  type CustomBlockInitPayload,
104
- type InitCustomBlockOptions,
105
104
  initCustomBlock,
106
105
  NotInIframeError,
107
106
  } from "./init.js"
package/src/init.ts CHANGED
@@ -42,21 +42,6 @@ export class NotInIframeError extends CustomBlockInitializationError {
42
42
  declare code: "not_in_iframe"
43
43
  }
44
44
 
45
- /**
46
- * Options for {@link initCustomBlock}.
47
- */
48
- export type InitCustomBlockOptions = {
49
- /**
50
- * How long to wait for the host's `init` response before rejecting with an error.
51
- *
52
- * @default 15000 - Intentionally longer than Notion's 10s host-dependency watchdog
53
- * so host-owned init errors can arrive before the SDK's generic fallback appears.
54
- */
55
- timeoutMs?: number
56
- }
57
-
58
- const DEFAULT_INIT_TIMEOUT_MS = 15_000
59
-
60
45
  const NOT_IN_IFRAME_MESSAGE =
61
46
  "<NotionCustomBlock> only works inside an iframe — use the dev shell or deploy to Notion."
62
47
 
@@ -69,15 +54,13 @@ let initPromise: Promise<CustomBlockInitPayload> | undefined
69
54
  * user, data sources) once the host has initialized the block. Rejects with a
70
55
  * `CustomBlockInitializationError` if initialization fails.
71
56
  *
72
- * Idempotent: subsequent calls return the same promise as the first and ignore any new options.
57
+ * Idempotent: subsequent calls return the same promise as the first.
73
58
  * Mount your React tree (or call any SDK hook) only after the returned promise resolves.
74
59
  */
75
- export function initCustomBlock(
76
- opts: InitCustomBlockOptions = {},
77
- ): Promise<CustomBlockInitPayload> {
60
+ export function initCustomBlock(): Promise<CustomBlockInitPayload> {
78
61
  if (initPromise === undefined) {
79
62
  customBlockHost.start()
80
- initPromise = performHandshake(opts)
63
+ initPromise = performHandshake()
81
64
  }
82
65
  return initPromise
83
66
  }
@@ -93,13 +76,11 @@ export function initCustomBlock(
93
76
  * Resolves with the `init` payload after the process above. Rejects with a
94
77
  * `CustomBlockInitializationError` if any step fails.
95
78
  */
96
- async function performHandshake(
97
- opts: InitCustomBlockOptions,
98
- ): Promise<CustomBlockInitPayload> {
79
+ async function performHandshake(): Promise<CustomBlockInitPayload> {
99
80
  try {
100
81
  // Fail fast with a typed error when rendered as a standalone tab and not in a parent frame.
101
- // Otherwise, it would eventually hit the timeout, since `postMessage` to `window.parent`
102
- // would just hit the same window and never arrive.
82
+ // Otherwise, `postMessage` to `window.parent` would just hit the same window and never
83
+ // arrive.
103
84
  if (typeof window !== "undefined" && window.parent === window) {
104
85
  throw new NotInIframeError()
105
86
  }
@@ -108,8 +89,7 @@ async function performHandshake(
108
89
  const manifestResult = await attemptToLoadSelfHostedManifest()
109
90
  customBlockHost.sendConnect(manifestResult)
110
91
 
111
- const timeoutMs = opts.timeoutMs ?? DEFAULT_INIT_TIMEOUT_MS
112
- await awaitHostInitWithTimeout(timeoutMs)
92
+ await customBlockHost.awaitInit()
113
93
 
114
94
  const hostState = customBlockHost.getState()
115
95
  switch (hostState.status) {
@@ -144,28 +124,3 @@ async function performHandshake(
144
124
  })
145
125
  }
146
126
  }
147
-
148
- async function awaitHostInitWithTimeout(timeoutMs: number): Promise<void> {
149
- try {
150
- await customBlockHost.awaitInit(AbortSignal.timeout(timeoutMs))
151
- } catch (error) {
152
- if (isTimeoutError(error)) {
153
- throw new CustomBlockInitializationError({
154
- code: "init_timeout",
155
- message:
156
- "Host did not respond to the `connect` message before the timeout.",
157
- isRetryable: true,
158
- })
159
- }
160
- throw error
161
- }
162
- }
163
-
164
- function isTimeoutError(error: unknown): boolean {
165
- return (
166
- typeof error === "object" &&
167
- error !== null &&
168
- "name" in error &&
169
- (error.name === "TimeoutError" || error.name === "AbortError")
170
- )
171
- }
@@ -1,7 +1,7 @@
1
1
  import type { CustomBlockInitializationError } from "@notionhq/custom-blocks-protocol/messages/init.js"
2
2
  import { type ReactNode, useEffect, useLayoutEffect, useState } from "react"
3
3
  import { customBlockHost } from "../bridge/sandboxClient.js"
4
- import { type InitCustomBlockOptions, NotInIframeError } from "../init.js"
4
+ import { NotInIframeError } from "../init.js"
5
5
  import { DebugMessageLog } from "./DebugMessageLog.js"
6
6
  import { seedStandalonePreviewState } from "./standalonePreview.js"
7
7
  import { useCustomBlockAutoResize } from "./useCustomBlockAutoResize.js"
@@ -12,7 +12,7 @@ import "./NotionCustomBlock.css"
12
12
  /**
13
13
  * Props accepted by {@link NotionCustomBlock}.
14
14
  */
15
- export type NotionCustomBlockProps = InitCustomBlockOptions & {
15
+ export type NotionCustomBlockProps = {
16
16
  children: ReactNode
17
17
  /**
18
18
  * Rendered while the SDK ↔ host handshake is in progress. Defaults to
@@ -37,8 +37,7 @@ export type NotionCustomBlockProps = InitCustomBlockOptions & {
37
37
 
38
38
  /**
39
39
  * Top-level wrapper that runs the SDK ↔ host handshake and gates `children`
40
- * until it resolves. Passes `timeoutMs` straight through to
41
- * {@link initCustomBlock}.
40
+ * until it resolves.
42
41
  *
43
42
  * Templates that prefer not to write a `Root` gating component (or top-level
44
43
  * `await`) can mount their app entirely inside this provider:
@@ -56,12 +55,11 @@ export type NotionCustomBlockProps = InitCustomBlockOptions & {
56
55
  */
57
56
  export function NotionCustomBlock({
58
57
  children,
59
- timeoutMs,
60
58
  fallback = null,
61
59
  errorFallback,
62
60
  autoResize = true,
63
61
  }: NotionCustomBlockProps) {
64
- const init = useCustomBlockInit({ timeoutMs })
62
+ const init = useCustomBlockInit()
65
63
  useCustomBlockAutoResize({ enabled: init.isLoaded && autoResize })
66
64
  // True if the block has no host (i.e. it's running in standalone preview).
67
65
  const isStandalone = init.error instanceof NotInIframeError
@@ -1,18 +1,14 @@
1
1
  import type { CustomBlockInitializationError } from "@notionhq/custom-blocks-protocol/messages/init.js"
2
2
  import { useEffect, useState } from "react"
3
- import {
4
- type CustomBlockInitPayload,
5
- type InitCustomBlockOptions,
6
- initCustomBlock,
7
- } from "../init.js"
3
+ import { type CustomBlockInitPayload, initCustomBlock } from "../init.js"
8
4
 
9
5
  /**
10
6
  * Discriminated state returned by {@link useCustomBlockInit}.
11
7
  *
12
8
  * Branch on `isLoaded`/`error`:
13
9
  * - `{ isLoaded: false, error: undefined }` — handshake in progress.
14
- * - `{ isLoaded: false, error: CustomBlockInitializationError }` — handshake failed (most commonly a
15
- * `CustomBlockInitializationError` with code `init_timeout` because the host never sent `init`).
10
+ * - `{ isLoaded: false, error: CustomBlockInitializationError }` — handshake failed because
11
+ * the host or SDK reported a terminal initialization error.
16
12
  * - `{ isLoaded: true, initial }` — handshake complete; safe to render
17
13
  * children that call `useTheme`, `useBlockId`, etc.
18
14
  */
@@ -38,17 +34,14 @@ export type UseCustomBlockInitResult =
38
34
  * return <App />
39
35
  * }
40
36
  */
41
- export function useCustomBlockInit(
42
- opts?: InitCustomBlockOptions,
43
- ): UseCustomBlockInitResult {
37
+ export function useCustomBlockInit(): UseCustomBlockInitResult {
44
38
  const [state, setState] = useState<UseCustomBlockInitResult>({
45
39
  isLoaded: false,
46
40
  error: undefined,
47
41
  })
48
- // biome-ignore lint/correctness/useExhaustiveDependencies(opts): Initialization caches its first options and promise. Later options are ignored, so changes should not resubscribe to the same promise.
49
42
  useEffect(() => {
50
43
  let cancelled = false
51
- initCustomBlock(opts).then(
44
+ initCustomBlock().then(
52
45
  initial => {
53
46
  if (!cancelled) {
54
47
  setState({ isLoaded: true, error: undefined, initial })
@@ -17,7 +17,10 @@ import { customBlock } from "../customBlock.js"
17
17
  * const blockId = useBlockId()
18
18
  */
19
19
  export function useBlockId(): NotionBlockId {
20
- return useSyncExternalStore(customBlock.subscribe, customBlock.getBlockId)
20
+ return useSyncExternalStore(
21
+ customBlock.subscribeToBlockId,
22
+ customBlock.getBlockId,
23
+ )
21
24
  }
22
25
 
23
26
  /**
@@ -30,7 +33,10 @@ export function useBlockId(): NotionBlockId {
30
33
  * const parent = useParent()
31
34
  */
32
35
  export function useParent(): NotionParent {
33
- return useSyncExternalStore(customBlock.subscribe, customBlock.getParent)
36
+ return useSyncExternalStore(
37
+ customBlock.subscribeToParent,
38
+ customBlock.getParent,
39
+ )
34
40
  }
35
41
 
36
42
  /**
@@ -43,11 +49,11 @@ export function useParent(): NotionParent {
43
49
  * const page = usePage()
44
50
  */
45
51
  export function usePage(): CustomBlockPage {
46
- return useSyncExternalStore(customBlock.subscribe, customBlock.getPage)
52
+ return useSyncExternalStore(customBlock.subscribeToPage, customBlock.getPage)
47
53
  }
48
54
 
49
55
  /**
50
- * Returns the host's current theme. Re-renders on every `themeChanged` message from the host.
56
+ * Returns the host's current theme. Re-renders when the theme changes.
51
57
  *
52
58
  * Throws if called before `initCustomBlock` has resolved.
53
59
  *
@@ -55,12 +61,14 @@ export function usePage(): CustomBlockPage {
55
61
  * const theme = useTheme()
56
62
  */
57
63
  export function useTheme(): NotionTheme {
58
- return useSyncExternalStore(customBlock.subscribe, customBlock.getTheme)
64
+ return useSyncExternalStore(
65
+ customBlock.subscribeToTheme,
66
+ customBlock.getTheme,
67
+ )
59
68
  }
60
69
 
61
70
  /**
62
- * Returns the host's contrast preference. Re-renders on every protocol-v3
63
- * `contrastModeChanged` message from the host.
71
+ * Returns the host's contrast preference. Re-renders when the contrast preference changes.
64
72
  *
65
73
  * Throws if called before `initCustomBlock` has resolved.
66
74
  *
@@ -69,14 +77,13 @@ export function useTheme(): NotionTheme {
69
77
  */
70
78
  export function useContrastMode(): NotionContrastMode {
71
79
  return useSyncExternalStore(
72
- customBlock.subscribe,
80
+ customBlock.subscribeToContrastMode,
73
81
  customBlock.getContrastMode,
74
82
  )
75
83
  }
76
84
 
77
85
  /**
78
- * Returns the viewing user's Notion profile. Re-renders whenever the host sends a
79
- * `currentUserChanged` message.
86
+ * Returns the viewing user's Notion profile. Re-renders when the profile data changes.
80
87
  *
81
88
  * Throws if called before `initCustomBlock` has resolved. `await` it before mounting.
82
89
  *
@@ -85,7 +92,10 @@ export function useContrastMode(): NotionContrastMode {
85
92
  * console.log(me.id, me.name, me.person.email)
86
93
  */
87
94
  export function useCurrentUser(): NotionUser {
88
- return useSyncExternalStore(customBlock.subscribe, customBlock.getCurrentUser)
95
+ return useSyncExternalStore(
96
+ customBlock.subscribeToCurrentUser,
97
+ customBlock.getCurrentUser,
98
+ )
89
99
  }
90
100
 
91
101
  /**
@@ -101,5 +111,8 @@ export function useCurrentUser(): NotionUser {
101
111
  * resolved property IDs and schemas.
102
112
  */
103
113
  export function useManifest(): CustomBlockManifest {
104
- return useSyncExternalStore(customBlock.subscribe, customBlock.getManifest)
114
+ return useSyncExternalStore(
115
+ customBlock.subscribeToManifest,
116
+ customBlock.getManifest,
117
+ )
105
118
  }
package/src/types.ts CHANGED
@@ -5,7 +5,6 @@ import type {
5
5
  NotionDataSourceId,
6
6
  NotionPageId,
7
7
  } from "@notionhq/custom-blocks-protocol/ids.js"
8
- import type { BridgeMessagePayload } from "@notionhq/custom-blocks-protocol/messagePayload.js"
9
8
  import type { NotionCreatePagePosition } from "@notionhq/custom-blocks-protocol/messages/createPage.js"
10
9
  import type {
11
10
  CreatePageResultMessage,
@@ -24,6 +23,7 @@ import type {
24
23
  ListUsersMessage,
25
24
  ListUsersResultMessage,
26
25
  } from "@notionhq/custom-blocks-protocol/messages/listUsers.js"
26
+ import type { BridgeMessagePayload } from "@notionhq/custom-blocks-protocol/messages/messagePayload.js"
27
27
  import type {
28
28
  CustomBlockCheckboxFilterOperator,
29
29
  CustomBlockContainsFilterOperator,