@notionhq/custom-blocks 0.0.62 → 0.0.64

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 (44) hide show
  1. package/HOST.md +4 -4
  2. package/README.md +2 -2
  3. package/bin/cli/create.js +3 -3
  4. package/dist/bridge/SandboxBridge.js +17 -17
  5. package/dist/bridge/loadManifest.js +6 -6
  6. package/dist/bridge/messages/hostToSandbox.d.ts +32 -0
  7. package/dist/bridge/messages/hostToSandbox.d.ts.map +1 -1
  8. package/dist/bridge/messages/init.d.ts +16 -0
  9. package/dist/bridge/messages/init.d.ts.map +1 -1
  10. package/dist/bridge/messages/pageChanged.d.ts +16 -0
  11. package/dist/bridge/messages/pageChanged.d.ts.map +1 -1
  12. package/dist/bridge/pages/page.d.ts +24 -0
  13. package/dist/bridge/pages/page.d.ts.map +1 -1
  14. package/dist/bridge/pages/page.js +9 -0
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/init.d.ts +2 -2
  18. package/dist/init.d.ts.map +1 -1
  19. package/dist/init.js +1 -1
  20. package/dist/react/NotionCustomBlock.css +47 -47
  21. package/dist/react/NotionCustomBlock.js +4 -4
  22. package/dist/react/standalonePreview.js +1 -1
  23. package/dist/react/useCustomBlockInit.d.ts +2 -2
  24. package/dist/react/useCustomBlockInit.d.ts.map +1 -1
  25. package/dist/utils.js +1 -1
  26. package/dist/version.d.ts +1 -1
  27. package/dist/version.d.ts.map +1 -1
  28. package/dist/version.js +1 -1
  29. package/docs/block-location.md +17 -3
  30. package/docs/lifecycle.md +3 -3
  31. package/docs/manifest.md +1 -1
  32. package/docs/pages.md +1 -1
  33. package/package.json +1 -1
  34. package/src/bridge/SandboxBridge.ts +17 -17
  35. package/src/bridge/loadManifest.ts +6 -6
  36. package/src/bridge/pages/page.ts +9 -0
  37. package/src/index.ts +1 -1
  38. package/src/init.ts +4 -4
  39. package/src/react/NotionCustomBlock.css +47 -47
  40. package/src/react/NotionCustomBlock.tsx +14 -10
  41. package/src/react/standalonePreview.ts +1 -1
  42. package/src/react/useCustomBlockInit.ts +2 -2
  43. package/src/utils.ts +1 -1
  44. package/src/version.ts +1 -1
@@ -1,27 +1,27 @@
1
- .ncblock-standalone-banner {
2
- --ncblock-standalone-banner-background: #fff8e1;
3
- --ncblock-standalone-banner-border: #f0d77b;
4
- --ncblock-standalone-banner-text: #5d4200;
1
+ .custom-blocks-standalone-banner {
2
+ --custom-blocks-standalone-banner-background: #fff8e1;
3
+ --custom-blocks-standalone-banner-border: #f0d77b;
4
+ --custom-blocks-standalone-banner-text: #5d4200;
5
5
 
6
6
  padding: 8px 12px;
7
- background: var(--ncblock-standalone-banner-background);
8
- color: var(--ncblock-standalone-banner-text);
9
- border-bottom: 1px solid var(--ncblock-standalone-banner-border);
7
+ background: var(--custom-blocks-standalone-banner-background);
8
+ color: var(--custom-blocks-standalone-banner-text);
9
+ border-bottom: 1px solid var(--custom-blocks-standalone-banner-border);
10
10
  font-size: 13px;
11
11
  font-family: system-ui, sans-serif;
12
12
  line-height: 1.4;
13
13
  }
14
14
 
15
- .ncblock-default-init-error {
16
- --ncblock-init-error-background: #f7f7f5;
17
- --ncblock-init-error-card-background: #ffffff;
18
- --ncblock-init-error-border: #e5e5e1;
19
- --ncblock-init-error-text: #1f1f1d;
20
- --ncblock-init-error-muted-text: #5f5e5b;
21
- --ncblock-init-error-eyebrow-text: #787774;
22
- --ncblock-init-error-pre-background: #f1f1ef;
23
- --ncblock-init-error-pre-text: #37352f;
24
- --ncblock-init-error-shadow: rgba(0, 0, 0, 0.08);
15
+ .custom-blocks-default-init-error {
16
+ --custom-blocks-init-error-background: #f7f7f5;
17
+ --custom-blocks-init-error-card-background: #ffffff;
18
+ --custom-blocks-init-error-border: #e5e5e1;
19
+ --custom-blocks-init-error-text: #1f1f1d;
20
+ --custom-blocks-init-error-muted-text: #5f5e5b;
21
+ --custom-blocks-init-error-eyebrow-text: #787774;
22
+ --custom-blocks-init-error-pre-background: #f1f1ef;
23
+ --custom-blocks-init-error-pre-text: #37352f;
24
+ --custom-blocks-init-error-shadow: rgba(0, 0, 0, 0.08);
25
25
 
26
26
  box-sizing: border-box;
27
27
  min-height: 240px;
@@ -29,81 +29,81 @@
29
29
  align-items: center;
30
30
  justify-content: center;
31
31
  padding: 24px;
32
- background: var(--ncblock-init-error-background);
33
- color: var(--ncblock-init-error-text);
32
+ background: var(--custom-blocks-init-error-background);
33
+ color: var(--custom-blocks-init-error-text);
34
34
  font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
35
35
  color-scheme: light;
36
36
  }
37
37
 
38
- .ncblock-default-init-error__card {
38
+ .custom-blocks-default-init-error__card {
39
39
  box-sizing: border-box;
40
40
  width: 100%;
41
41
  max-width: 420px;
42
42
  padding: 20px;
43
- border: 1px solid var(--ncblock-init-error-border);
43
+ border: 1px solid var(--custom-blocks-init-error-border);
44
44
  border-radius: 12px;
45
- background: var(--ncblock-init-error-card-background);
46
- box-shadow: 0 8px 24px var(--ncblock-init-error-shadow);
45
+ background: var(--custom-blocks-init-error-card-background);
46
+ box-shadow: 0 8px 24px var(--custom-blocks-init-error-shadow);
47
47
  }
48
48
 
49
- .ncblock-default-init-error__eyebrow {
49
+ .custom-blocks-default-init-error__eyebrow {
50
50
  margin-bottom: 8px;
51
51
  font-size: 11px;
52
52
  font-weight: 600;
53
53
  letter-spacing: 0.08em;
54
54
  text-transform: uppercase;
55
- color: var(--ncblock-init-error-eyebrow-text);
55
+ color: var(--custom-blocks-init-error-eyebrow-text);
56
56
  }
57
57
 
58
- .ncblock-default-init-error__title {
58
+ .custom-blocks-default-init-error__title {
59
59
  margin: 0;
60
60
  font-size: 18px;
61
61
  line-height: 1.3;
62
62
  font-weight: 650;
63
63
  }
64
64
 
65
- .ncblock-default-init-error__body {
65
+ .custom-blocks-default-init-error__body {
66
66
  margin: 8px 0 0;
67
67
  font-size: 14px;
68
68
  line-height: 1.5;
69
- color: var(--ncblock-init-error-muted-text);
69
+ color: var(--custom-blocks-init-error-muted-text);
70
70
  }
71
71
 
72
- .ncblock-default-init-error__details {
72
+ .custom-blocks-default-init-error__details {
73
73
  margin-top: 14px;
74
74
  font-size: 12px;
75
- color: var(--ncblock-init-error-muted-text);
75
+ color: var(--custom-blocks-init-error-muted-text);
76
76
  }
77
77
 
78
- .ncblock-default-init-error__pre {
78
+ .custom-blocks-default-init-error__pre {
79
79
  margin: 8px 0 0;
80
80
  padding: 10px;
81
81
  overflow: auto;
82
82
  border-radius: 8px;
83
- background: var(--ncblock-init-error-pre-background);
84
- color: var(--ncblock-init-error-pre-text);
83
+ background: var(--custom-blocks-init-error-pre-background);
84
+ color: var(--custom-blocks-init-error-pre-text);
85
85
  font-size: 12px;
86
86
  line-height: 1.4;
87
87
  white-space: pre-wrap;
88
88
  }
89
89
 
90
90
  @media (prefers-color-scheme: dark) {
91
- .ncblock-standalone-banner {
92
- --ncblock-standalone-banner-background: #3b2f0b;
93
- --ncblock-standalone-banner-border: #6f5713;
94
- --ncblock-standalone-banner-text: #f5d36c;
91
+ .custom-blocks-standalone-banner {
92
+ --custom-blocks-standalone-banner-background: #3b2f0b;
93
+ --custom-blocks-standalone-banner-border: #6f5713;
94
+ --custom-blocks-standalone-banner-text: #f5d36c;
95
95
  }
96
96
 
97
- .ncblock-default-init-error {
98
- --ncblock-init-error-background: #191918;
99
- --ncblock-init-error-card-background: #20201f;
100
- --ncblock-init-error-border: #373633;
101
- --ncblock-init-error-text: #f1f1ef;
102
- --ncblock-init-error-muted-text: #b9b8b3;
103
- --ncblock-init-error-eyebrow-text: #9b9a97;
104
- --ncblock-init-error-pre-background: #2a2a28;
105
- --ncblock-init-error-pre-text: #e8e8e4;
106
- --ncblock-init-error-shadow: rgba(0, 0, 0, 0.35);
97
+ .custom-blocks-default-init-error {
98
+ --custom-blocks-init-error-background: #191918;
99
+ --custom-blocks-init-error-card-background: #20201f;
100
+ --custom-blocks-init-error-border: #373633;
101
+ --custom-blocks-init-error-text: #f1f1ef;
102
+ --custom-blocks-init-error-muted-text: #b9b8b3;
103
+ --custom-blocks-init-error-eyebrow-text: #9b9a97;
104
+ --custom-blocks-init-error-pre-background: #2a2a28;
105
+ --custom-blocks-init-error-pre-text: #e8e8e4;
106
+ --custom-blocks-init-error-shadow: rgba(0, 0, 0, 0.35);
107
107
 
108
108
  color-scheme: dark;
109
109
  }
@@ -45,7 +45,7 @@ export function NotionCustomBlock({ children, timeoutMs, fallback = null, errorF
45
45
  if (!isStandalone) {
46
46
  return;
47
47
  }
48
- console.warn(`[notion-custom-sdk] ${init.error?.message}`);
48
+ console.warn(`[custom-blocks-sdk] ${init.error?.message}`);
49
49
  seedStandalonePreviewState();
50
50
  }, [isStandalone, init.error]);
51
51
  if (debugOpen) {
@@ -65,7 +65,7 @@ export function NotionCustomBlock({ children, timeoutMs, fallback = null, errorF
65
65
  if (host.status !== "initialized") {
66
66
  return _jsx(_Fragment, { children: fallback });
67
67
  }
68
- return (_jsxs(_Fragment, { children: [_jsx("div", { role: "status", className: "ncblock-standalone-banner", children: "Notion host not detected \u2014 running in standalone preview. SDK hooks return placeholder values until embedded in Notion." }), children] }));
68
+ return (_jsxs(_Fragment, { children: [_jsx("div", { role: "status", className: "custom-blocks-standalone-banner", children: "Notion host not detected \u2014 running in standalone preview. SDK hooks return placeholder values until embedded in Notion." }), children] }));
69
69
  }
70
70
  if (!init.isLoaded) {
71
71
  return _jsx(_Fragment, { children: fallback });
@@ -75,7 +75,7 @@ export function NotionCustomBlock({ children, timeoutMs, fallback = null, errorF
75
75
  // TODO(custom-blocks): Align the styling of this with the host-side error states.
76
76
  function DefaultInitErrorFallback({ error }) {
77
77
  useEffect(() => {
78
- console.warn(`[notion-custom-sdk] Custom block init failed: ${error.message}`);
78
+ console.warn(`[custom-blocks-sdk] Custom block init failed: ${error.message}`);
79
79
  }, [error]);
80
- return (_jsx("div", { role: "alert", className: "ncblock-default-init-error", children: _jsxs("div", { className: "ncblock-default-init-error__card", children: [_jsx("div", { className: "ncblock-default-init-error__eyebrow", children: "Custom block" }), _jsx("h2", { className: "ncblock-default-init-error__title", children: "Couldn't connect to Notion" }), _jsx("p", { className: "ncblock-default-init-error__body", children: "This custom block loaded, but the setup handshake didn't finish. Try again from Notion. If this keeps happening, check the browser console for SDK details." }), _jsxs("details", { className: "ncblock-default-init-error__details", children: [_jsx("summary", { children: "Developer details" }), _jsx("pre", { className: "ncblock-default-init-error__pre", children: error.message })] })] }) }));
80
+ return (_jsx("div", { role: "alert", className: "custom-blocks-default-init-error", children: _jsxs("div", { className: "custom-blocks-default-init-error__card", children: [_jsx("div", { className: "custom-blocks-default-init-error__eyebrow", children: "Custom block" }), _jsx("h2", { className: "custom-blocks-default-init-error__title", children: "Couldn't connect to Notion" }), _jsx("p", { className: "custom-blocks-default-init-error__body", children: "This custom block loaded, but the setup handshake didn't finish. Try again from Notion. If this keeps happening, check the browser console for SDK details." }), _jsxs("details", { className: "custom-blocks-default-init-error__details", children: [_jsx("summary", { children: "Developer details" }), _jsx("pre", { className: "custom-blocks-default-init-error__pre", children: error.message })] })] }) }));
81
81
  }
@@ -10,7 +10,7 @@ export function seedStandalonePreviewState() {
10
10
  theme: "light",
11
11
  blockId: previewBlockId,
12
12
  parent: { type: "page_id", page_id: previewPageId },
13
- page: { id: previewPageId },
13
+ page: { id: previewPageId, parent: { type: "workspace", workspace: true } },
14
14
  dataSources: { bindings: {} },
15
15
  currentUser: {
16
16
  object: "user",
@@ -1,5 +1,5 @@
1
1
  import { CustomBlockInitError } from "../bridge/messages/init.js";
2
- import { type CustomBlockInitial, type InitCustomBlockOptions, NotInIframeError } from "../init.js";
2
+ import { type CustomBlockInitPayload, type InitCustomBlockOptions, NotInIframeError } from "../init.js";
3
3
  export type CustomBlockInitFailure = CustomBlockInitError | NotInIframeError;
4
4
  /**
5
5
  * Discriminated state returned by {@link useCustomBlockInit}.
@@ -20,7 +20,7 @@ export type UseCustomBlockInitResult = {
20
20
  } | {
21
21
  isLoaded: true;
22
22
  error: undefined;
23
- initial: CustomBlockInitial;
23
+ initial: CustomBlockInitPayload;
24
24
  };
25
25
  /**
26
26
  * React wrapper around {@link initCustomBlock}. Kicks off the SDK ↔ host
@@ -1 +1 @@
1
- {"version":3,"file":"useCustomBlockInit.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/react/useCustomBlockInit.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AACjE,OAAO,EACN,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAE3B,gBAAgB,EAChB,MAAM,YAAY,CAAA;AAEnB,MAAM,MAAM,sBAAsB,GAAG,oBAAoB,GAAG,gBAAgB,CAAA;AAE5E;;;;;;;;;GASG;AACH,MAAM,MAAM,wBAAwB,GACjC;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GACrC;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,sBAAsB,CAAA;CAAE,GAClD;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE,CAAA;AAEpE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CACjC,IAAI,CAAC,EAAE,sBAAsB,GAC3B,wBAAwB,CA8B1B"}
1
+ {"version":3,"file":"useCustomBlockInit.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/react/useCustomBlockInit.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AACjE,OAAO,EACN,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAE3B,gBAAgB,EAChB,MAAM,YAAY,CAAA;AAEnB,MAAM,MAAM,sBAAsB,GAAG,oBAAoB,GAAG,gBAAgB,CAAA;AAE5E;;;;;;;;;GASG;AACH,MAAM,MAAM,wBAAwB,GACjC;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GACrC;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,sBAAsB,CAAA;CAAE,GAClD;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,sBAAsB,CAAA;CAAE,CAAA;AAExE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CACjC,IAAI,CAAC,EAAE,sBAAsB,GAC3B,wBAAwB,CA8B1B"}
package/dist/utils.js CHANGED
@@ -6,5 +6,5 @@
6
6
  * are covered when using discriminated unions.
7
7
  */
8
8
  export function unreachable(value) {
9
- throw new Error(`[notion-custom-sdk] Unexpected value encountered: ${JSON.stringify(value)}`);
9
+ throw new Error(`[custom-blocks-sdk] Unexpected value encountered: ${JSON.stringify(value)}`);
10
10
  }
package/dist/version.d.ts CHANGED
@@ -5,5 +5,5 @@
5
5
  * This checked-in value intentionally stays generic for local development. The SDK publish build
6
6
  * overwrites the compiled runtime module with package.json's semver before publishing.
7
7
  */
8
- export declare const NCBLOCK_SDK_VERSION: string;
8
+ export declare const CUSTOM_BLOCKS_SDK_VERSION: string;
9
9
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/version.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAgB,CAAA"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/version.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAgB,CAAA"}
package/dist/version.js CHANGED
@@ -4,4 +4,4 @@
4
4
  *
5
5
  * WARNING: Generated during SDK publish. Do not edit in the published package.
6
6
  */
7
- export const NCBLOCK_SDK_VERSION = "0.0.62"
7
+ export const CUSTOM_BLOCKS_SDK_VERSION = "0.0.64"
@@ -46,7 +46,7 @@ For non-React renderers, use `customBlock.getParent()` after `initCustomBlock()`
46
46
  To identify custom blocks in agent-owned instruction content:
47
47
 
48
48
  ```tsx
49
- import { useParent } from "ncblock"
49
+ import { useParent } from "@notionhq/custom-blocks"
50
50
 
51
51
  export function AgentInstructionBadge() {
52
52
  const parent = useParent()
@@ -63,12 +63,26 @@ export function AgentInstructionBadge() {
63
63
 
64
64
  Returns the nearest enclosing `page` / `collection_view_page` ancestor.
65
65
 
66
- Re-renders when the host sends a block location update.
66
+ Re-renders when the host sends a block location update — including when the containing page itself moves (its `parent` changes) without the page ID changing.
67
67
 
68
68
  ```ts
69
- function usePage(): { id: NotionPageId };
69
+ function usePage(): CustomBlockPage;
70
+
71
+ type CustomBlockPage = {
72
+ id: NotionPageId;
73
+ parent: NotionParent; // the containing page's own parent
74
+ };
70
75
  ```
71
76
 
77
+ `page.parent` allows determining if the custom block is in a freestanding page or part of a database. Don't confuse it with `useParent()`, which is the custom block's own parent.
78
+
79
+ ```tsx
80
+ const page = usePage();
81
+ const isInsideDatabaseRow = page.parent.type === "data_source_id";
82
+ ```
83
+
84
+ For anything beyond location — title, icon, properties — fetch the page through the pages API: `pages.get(page.id)`.
85
+
72
86
  For non-React renderers, use `customBlock.getPage()` after `initCustomBlock()` resolves.
73
87
 
74
88
  ### `useTheme()`
package/docs/lifecycle.md CHANGED
@@ -4,7 +4,7 @@ The SDK ↔ host handshake, the React wrapper that runs it, and the auto-resize
4
4
 
5
5
  ## Handshake
6
6
 
7
- `initCustomBlock()` posts `ready` to `window.parent` and awaits the host's `init` (theme, block ID/parent/page ID, current user, and `dataSources: { bindings }` keyed by semantic data-source key, which the SDK resolves against the manifest). The promise resolves with the normalized initial state, captured in `CustomBlockInitial` — `await` it before mounting React so hooks always see populated state.
7
+ `initCustomBlock()` posts `ready` to `window.parent` and awaits the host's `init` (theme, block ID/parent/page ID, current user, and `dataSources: { bindings }` keyed by semantic data-source key, which the SDK resolves against the manifest). The promise resolves with the normalized initial state, captured in `CustomBlockInitPayload` — `await` it before mounting React so hooks always see populated state.
8
8
 
9
9
  - Default `timeoutMs` is 15000; rejects with `CustomBlockInitError` code `init_timeout` if the host doesn't respond.
10
10
  - In a top-level browser tab (no parent frame), rejects with `NotInIframeError` code `not_in_iframe`. `<NotionCustomBlock>` catches this, seeds placeholders, and renders `children` behind a warning banner so dev-time previews still work.
@@ -45,7 +45,7 @@ function useCustomBlockInit(
45
45
  type UseCustomBlockInitResult =
46
46
  | { isLoaded: false; error: undefined }
47
47
  | { isLoaded: false; error: CustomBlockInitFailure }
48
- | { isLoaded: true; error: undefined; initial: CustomBlockInitial };
48
+ | { isLoaded: true; error: undefined; initial: CustomBlockInitPayload };
49
49
  ```
50
50
 
51
51
  React wrapper around `initCustomBlock` for templates that prefer not to use top-level `await`. Multiple components calling it share the same handshake.
@@ -64,7 +64,7 @@ function Root() {
64
64
  ```ts
65
65
  function initCustomBlock(
66
66
  opts?: InitCustomBlockOptions,
67
- ): Promise<CustomBlockInitial>;
67
+ ): Promise<CustomBlockInitPayload>;
68
68
 
69
69
  type InitCustomBlockOptions = { timeoutMs?: number };
70
70
  ```
package/docs/manifest.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Manifest
2
2
 
3
- A custom view declares its required data sources in `custom_blocks.json` at the project root. Notion uses the manifest to know what semantic keys the block expects, what shape each property should be, and what to show when an admin is configuring the block.
3
+ A custom block declares its required data sources in `custom_blocks.json` at the project root. Notion uses the manifest to know what semantic keys the block expects, what shape each property should be, and what to show when an admin is configuring the block.
4
4
 
5
5
  ```json
6
6
  {
package/docs/pages.md CHANGED
@@ -53,7 +53,7 @@ type CreatePageParent =
53
53
  | { type: "data_source_key"; key: string };
54
54
  ```
55
55
 
56
- `type: "data_source_key"` is the recommended form inside a custom view. Pass the semantic key you declared in `custom_blocks.json` (e.g. `"tasks"`) and the SDK looks up the corresponding data source for you. The other two variants exist for the rarer case where you already have a raw Notion ID in hand.
56
+ `type: "data_source_key"` is the recommended form inside a custom block. Pass the semantic key you declared in `custom_blocks.json` (e.g. `"tasks"`) and the SDK looks up the corresponding data source for you. The other two variants exist for the rarer case where you already have a raw Notion ID in hand.
57
57
 
58
58
  ### Property keys
59
59
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notionhq/custom-blocks",
3
- "version": "0.0.62",
3
+ "version": "0.0.64",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -13,7 +13,7 @@ import type {
13
13
  UseDataSourceOptions,
14
14
  } from "../types.js"
15
15
  import { unreachable } from "../utils.js"
16
- import { NCBLOCK_SDK_VERSION } from "../version.js"
16
+ import { CUSTOM_BLOCKS_SDK_VERSION } from "../version.js"
17
17
  import type {
18
18
  NotionDataSource,
19
19
  NotionDataSourceBindings,
@@ -167,7 +167,7 @@ export class SandboxBridge {
167
167
  return
168
168
  }
169
169
  if (this.hasSentReady) {
170
- console.warn("[notion-custom-sdk] ignoring duplicate ready message")
170
+ console.warn("[custom-blocks-sdk] ignoring duplicate ready message")
171
171
  return
172
172
  }
173
173
  const { manifest, error } = manifestResult
@@ -179,21 +179,21 @@ export class SandboxBridge {
179
179
  type: "ready",
180
180
  status: "error",
181
181
  bridgeProtocolVersion: CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION,
182
- sdkVersion: NCBLOCK_SDK_VERSION,
182
+ sdkVersion: CUSTOM_BLOCKS_SDK_VERSION,
183
183
  error,
184
184
  }
185
185
  : {
186
186
  type: "ready",
187
187
  status: "success",
188
188
  bridgeProtocolVersion: CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION,
189
- sdkVersion: NCBLOCK_SDK_VERSION,
189
+ sdkVersion: CUSTOM_BLOCKS_SDK_VERSION,
190
190
  manifest,
191
191
  }
192
192
  this.postToHost(readyMessage)
193
193
  }
194
194
 
195
195
  private postToHost(message: unknown) {
196
- console.debug("[notion-custom-sdk] outbound postMessage", message)
196
+ console.debug("[custom-blocks-sdk] outbound postMessage", message)
197
197
  this.logMessage("sent", message)
198
198
  window.parent.postMessage(message, "*")
199
199
  }
@@ -205,7 +205,7 @@ export class SandboxBridge {
205
205
  }
206
206
 
207
207
  private handleMessage = (event: MessageEvent) => {
208
- console.debug("[notion-custom-sdk] incoming postMessage", {
208
+ console.debug("[custom-blocks-sdk] incoming postMessage", {
209
209
  data: event.data,
210
210
  fromParent: event.source === window.parent,
211
211
  })
@@ -217,7 +217,7 @@ export class SandboxBridge {
217
217
  const parsed = v.safeParse(hostToSandboxMessageSchema, event.data)
218
218
  if (!parsed.success) {
219
219
  console.warn(
220
- "[notion-custom-sdk] ignoring malformed host message",
220
+ "[custom-blocks-sdk] ignoring malformed host message",
221
221
  parsed.issues,
222
222
  )
223
223
  const incomingType = readIncomingType(event.data)
@@ -245,7 +245,7 @@ export class SandboxBridge {
245
245
  // failure on its own.
246
246
  if (message.type === "invalidSandboxMessage") {
247
247
  console.warn(
248
- "[notion-custom-sdk] host reported invalid sandbox message:",
248
+ "[custom-blocks-sdk] host reported invalid sandbox message:",
249
249
  message.reason,
250
250
  )
251
251
  return
@@ -263,7 +263,7 @@ export class SandboxBridge {
263
263
  const hostState = this.hostState
264
264
 
265
265
  if (hostState.status !== "initialized") {
266
- console.warn(`[notion-custom-sdk] ignoring ${message.type} before init`)
266
+ console.warn(`[custom-blocks-sdk] ignoring ${message.type} before init`)
267
267
  return
268
268
  }
269
269
 
@@ -340,7 +340,7 @@ export class SandboxBridge {
340
340
  : { status: "error", error: message.error }
341
341
  if (!this.pendingCreatePage.resolve(message.requestId, result)) {
342
342
  console.warn(
343
- `[notion-custom-sdk] createPageResult for unknown requestId ${message.requestId}`,
343
+ `[custom-blocks-sdk] createPageResult for unknown requestId ${message.requestId}`,
344
344
  )
345
345
  }
346
346
  return
@@ -353,7 +353,7 @@ export class SandboxBridge {
353
353
  : { status: "error", error: message.error }
354
354
  if (!this.pendingGetPage.resolve(message.requestId, result)) {
355
355
  console.warn(
356
- `[notion-custom-sdk] getPageResult for unknown requestId ${message.requestId}`,
356
+ `[custom-blocks-sdk] getPageResult for unknown requestId ${message.requestId}`,
357
357
  )
358
358
  }
359
359
  return
@@ -366,7 +366,7 @@ export class SandboxBridge {
366
366
  : { status: "error", error: message.error }
367
367
  if (!this.pendingGetUser.resolve(message.requestId, result)) {
368
368
  console.warn(
369
- `[notion-custom-sdk] getUserResult for unknown requestId ${message.requestId}`,
369
+ `[custom-blocks-sdk] getUserResult for unknown requestId ${message.requestId}`,
370
370
  )
371
371
  }
372
372
  return
@@ -379,7 +379,7 @@ export class SandboxBridge {
379
379
  : { status: "error", error: message.error }
380
380
  if (!this.pendingListUsers.resolve(message.requestId, result)) {
381
381
  console.warn(
382
- `[notion-custom-sdk] listUsersResult for unknown requestId ${message.requestId}`,
382
+ `[custom-blocks-sdk] listUsersResult for unknown requestId ${message.requestId}`,
383
383
  )
384
384
  }
385
385
  return
@@ -392,7 +392,7 @@ export class SandboxBridge {
392
392
  : { status: "error", error: message.error }
393
393
  if (!this.pendingUpdatePage.resolve(message.requestId, result)) {
394
394
  console.warn(
395
- `[notion-custom-sdk] updatePageResult for unknown requestId ${message.requestId}`,
395
+ `[custom-blocks-sdk] updatePageResult for unknown requestId ${message.requestId}`,
396
396
  )
397
397
  }
398
398
  return
@@ -492,7 +492,7 @@ export class SandboxBridge {
492
492
  // the browser console either way. We deliberately don't NACK the host or post anything
493
493
  // back over the bridge since the host has already given up.
494
494
  console.error(
495
- `[notion-custom-sdk] host reported init error (${message.error.code}): ${message.error.message}`,
495
+ `[custom-blocks-sdk] host reported init error (${message.error.code}): ${message.error.message}`,
496
496
  )
497
497
  if (this.rejectInit) {
498
498
  this.rejectInit(new CustomBlockInitError(message.error))
@@ -900,13 +900,13 @@ function resolveDataSourceQueryLimit(limit: number | undefined): number {
900
900
  }
901
901
  if (!Number.isFinite(limit) || !Number.isInteger(limit) || limit < 1) {
902
902
  console.warn(
903
- `[notion-custom-sdk] useDataSource limit must be a positive integer; using ${DEFAULT_DATA_SOURCE_QUERY_LIMIT}.`,
903
+ `[custom-blocks-sdk] useDataSource limit must be a positive integer; using ${DEFAULT_DATA_SOURCE_QUERY_LIMIT}.`,
904
904
  )
905
905
  return DEFAULT_DATA_SOURCE_QUERY_LIMIT
906
906
  }
907
907
  if (limit > MAX_DATA_SOURCE_QUERY_LIMIT) {
908
908
  console.warn(
909
- `[notion-custom-sdk] useDataSource limit is capped at ${MAX_DATA_SOURCE_QUERY_LIMIT}.`,
909
+ `[custom-blocks-sdk] useDataSource limit is capped at ${MAX_DATA_SOURCE_QUERY_LIMIT}.`,
910
910
  )
911
911
  return MAX_DATA_SOURCE_QUERY_LIMIT
912
912
  }
@@ -41,7 +41,7 @@ export type ManifestLoadResult =
41
41
  export async function loadManifest(): Promise<ManifestLoadResult> {
42
42
  if (typeof fetch !== "function") {
43
43
  const message = `No fetch API available; cannot load ${MANIFEST_URL}.`
44
- console.warn(`[notion-custom-sdk] ${message}`)
44
+ console.warn(`[custom-blocks-sdk] ${message}`)
45
45
  return {
46
46
  manifest: null,
47
47
  error: { code: "manifest_unavailable", message },
@@ -52,7 +52,7 @@ export async function loadManifest(): Promise<ManifestLoadResult> {
52
52
  response = await fetch(MANIFEST_URL, { credentials: "omit" })
53
53
  } catch (error) {
54
54
  const message = `Could not fetch ${MANIFEST_URL}.`
55
- console.warn(`[notion-custom-sdk] ${message}`, error)
55
+ console.warn(`[custom-blocks-sdk] ${message}`, error)
56
56
  return {
57
57
  manifest: null,
58
58
  error: { code: "manifest_unavailable", message },
@@ -60,12 +60,12 @@ export async function loadManifest(): Promise<ManifestLoadResult> {
60
60
  }
61
61
  if (response.status === 404) {
62
62
  const message = `No manifest found at ${MANIFEST_URL} (status ${response.status}).`
63
- console.warn(`[notion-custom-sdk] ${message}`)
63
+ console.warn(`[custom-blocks-sdk] ${message}`)
64
64
  return { manifest: null }
65
65
  }
66
66
  if (!response.ok) {
67
67
  const message = `Could not fetch ${MANIFEST_URL} (status ${response.status}).`
68
- console.warn(`[notion-custom-sdk] ${message}`)
68
+ console.warn(`[custom-blocks-sdk] ${message}`)
69
69
  return {
70
70
  manifest: null,
71
71
  error: { code: "manifest_unavailable", message },
@@ -76,13 +76,13 @@ export async function loadManifest(): Promise<ManifestLoadResult> {
76
76
  json = await response.json()
77
77
  } catch (error) {
78
78
  const message = `Manifest at ${MANIFEST_URL} was not valid JSON.`
79
- console.warn(`[notion-custom-sdk] ${message}`, error)
79
+ console.warn(`[custom-blocks-sdk] ${message}`, error)
80
80
  return { manifest: null, error: { code: "manifest_invalid", message } }
81
81
  }
82
82
  const parsed = v.safeParse(manifestSchema, json)
83
83
  if (!parsed.success) {
84
84
  const message = `Manifest at ${MANIFEST_URL} did not match schema.`
85
- console.warn(`[notion-custom-sdk] ${message}`, parsed.issues)
85
+ console.warn(`[custom-blocks-sdk] ${message}`, parsed.issues)
86
86
  return { manifest: null, error: { code: "manifest_invalid", message } }
87
87
  }
88
88
  return { manifest: parsed.output }
@@ -11,8 +11,17 @@ import { notionParentSchema } from "../parent.js"
11
11
 
12
12
  export type { NotionPageId } from "../ids.js"
13
13
 
14
+ /**
15
+ * The custom block's nearest enclosing page ancestor, carried in `init.page` and `pageChanged`.
16
+ */
14
17
  export const customBlockPageSchema = v.object({
15
18
  id: notionPageIdSchema,
19
+ /**
20
+ * The containing page's own parent. It lets a block distinguish a freestanding page from a
21
+ * database row without a round trip. Not to be confused with the top-level `parent` runtime
22
+ * value, which is the custom block's own parent.
23
+ */
24
+ parent: notionParentSchema,
16
25
  })
17
26
 
18
27
  export type CustomBlockPage = v.InferOutput<typeof customBlockPageSchema>
package/src/index.ts CHANGED
@@ -108,7 +108,7 @@ export { pages, users } from "./bridge/sandboxClient.js"
108
108
  export type { NotionTheme } from "./bridge/theme.js"
109
109
  export { type CustomBlockState, customBlock } from "./customBlock.js"
110
110
  export {
111
- type CustomBlockInitial,
111
+ type CustomBlockInitPayload,
112
112
  type InitCustomBlockOptions,
113
113
  initCustomBlock,
114
114
  NotInIframeError,
package/src/init.ts CHANGED
@@ -15,7 +15,7 @@ import type { NotionUser } from "./bridge/users/user.js"
15
15
  /**
16
16
  * The payload sent by the host in the `init` message in response to the sandbox's `ready` message.
17
17
  */
18
- export type CustomBlockInitial = {
18
+ export type CustomBlockInitPayload = {
19
19
  theme: NotionTheme
20
20
  blockId: NotionBlockId
21
21
  parent: NotionParent
@@ -60,7 +60,7 @@ const DEFAULT_INIT_TIMEOUT_MS = 15_000
60
60
  const NOT_IN_IFRAME_MESSAGE =
61
61
  "<NotionCustomBlock> only works inside an iframe — use the dev shell or deploy to Notion."
62
62
 
63
- let initPromise: Promise<CustomBlockInitial> | undefined
63
+ let initPromise: Promise<CustomBlockInitPayload> | undefined
64
64
 
65
65
  /**
66
66
  * Performs the SDK <-> host handshake: loads `custom_blocks.json`, posts
@@ -74,7 +74,7 @@ let initPromise: Promise<CustomBlockInitial> | undefined
74
74
  */
75
75
  export function initCustomBlock(
76
76
  opts: InitCustomBlockOptions = {},
77
- ): Promise<CustomBlockInitial> {
77
+ ): Promise<CustomBlockInitPayload> {
78
78
  if (initPromise === undefined) {
79
79
  initPromise = (async () => {
80
80
  // Fail fast with a typed error when rendered as a standalone tab and not in a parent frame.
@@ -106,7 +106,7 @@ export function initCustomBlock(
106
106
  }
107
107
  if (message.status === "error") {
108
108
  console.error(
109
- `[notion-custom-sdk] host reported init error (${message.error.code}): ${message.error.message}`,
109
+ `[custom-blocks-sdk] host reported init error (${message.error.code}): ${message.error.message}`,
110
110
  )
111
111
  throw new CustomBlockInitError(message.error)
112
112
  }