@notionhq/custom-blocks 0.0.77 → 0.0.79

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 (100) hide show
  1. package/HOST.md +68 -34
  2. package/README.md +1 -1
  3. package/bin/src/bridge/manifest.js +3 -3
  4. package/dist/bridge/SandboxBridge.d.ts +8 -13
  5. package/dist/bridge/SandboxBridge.d.ts.map +1 -1
  6. package/dist/bridge/SandboxBridge.js +110 -39
  7. package/dist/bridge/dataSources/resolve.d.ts +3 -4
  8. package/dist/bridge/dataSources/resolve.d.ts.map +1 -1
  9. package/dist/bridge/dataSources/resolve.js +2 -12
  10. package/dist/bridge/hostState.d.ts +2 -0
  11. package/dist/bridge/hostState.d.ts.map +1 -1
  12. package/dist/bridge/loadManifest.d.ts +4 -4
  13. package/dist/bridge/loadManifest.d.ts.map +1 -1
  14. package/dist/bridge/loadManifest.js +2 -2
  15. package/dist/bridge/manifest.d.ts +3 -3
  16. package/dist/bridge/manifest.js +3 -3
  17. package/dist/bridge/messages/{ready.d.ts → connect.d.ts} +14 -12
  18. package/dist/bridge/messages/connect.d.ts.map +1 -0
  19. package/dist/bridge/messages/connect.js +38 -0
  20. package/dist/bridge/messages/hostToSandbox.d.ts +21 -0
  21. package/dist/bridge/messages/hostToSandbox.d.ts.map +1 -1
  22. package/dist/bridge/messages/init.d.ts +35 -6
  23. package/dist/bridge/messages/init.d.ts.map +1 -1
  24. package/dist/bridge/messages/init.js +8 -4
  25. package/dist/bridge/messages/initResult.d.ts +29 -0
  26. package/dist/bridge/messages/initResult.d.ts.map +1 -0
  27. package/dist/bridge/messages/initResult.js +25 -0
  28. package/dist/bridge/messages/sandboxToHost.d.ts +21 -6
  29. package/dist/bridge/messages/sandboxToHost.d.ts.map +1 -1
  30. package/dist/bridge/messages/sandboxToHost.js +4 -2
  31. package/dist/bridge/sandboxClient.d.ts +2 -4
  32. package/dist/bridge/sandboxClient.d.ts.map +1 -1
  33. package/dist/bridge/sandboxClient.js +2 -5
  34. package/dist/customBlock.d.ts +1 -1
  35. package/dist/customBlock.d.ts.map +1 -1
  36. package/dist/customBlock.js +1 -1
  37. package/dist/host/createCustomBlockHost.d.ts +7 -4
  38. package/dist/host/createCustomBlockHost.d.ts.map +1 -1
  39. package/dist/host/createCustomBlockHost.js +215 -38
  40. package/dist/host/lifecycle/initErrors.d.ts +1 -1
  41. package/dist/host/lifecycle/initErrors.d.ts.map +1 -1
  42. package/dist/host/lifecycle/initErrors.js +25 -7
  43. package/dist/host/lifecycle/{ready.d.ts → protocolVersion.d.ts} +1 -1
  44. package/dist/host/lifecycle/protocolVersion.d.ts.map +1 -0
  45. package/dist/host/lifecycle/types.d.ts +6 -4
  46. package/dist/host/lifecycle/types.d.ts.map +1 -1
  47. package/dist/host/messages/invalidSandboxMessage.js +0 -3
  48. package/dist/host.d.ts +5 -3
  49. package/dist/host.d.ts.map +1 -1
  50. package/dist/host.js +2 -1
  51. package/dist/index.d.ts +2 -2
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +1 -1
  54. package/dist/init.d.ts +7 -7
  55. package/dist/init.d.ts.map +1 -1
  56. package/dist/init.js +70 -50
  57. package/dist/react/index.d.ts +1 -1
  58. package/dist/react/index.d.ts.map +1 -1
  59. package/dist/react/standalonePreview.d.ts.map +1 -1
  60. package/dist/react/standalonePreview.js +2 -0
  61. package/dist/react/useCustomBlockInit.d.ts +5 -6
  62. package/dist/react/useCustomBlockInit.d.ts.map +1 -1
  63. package/dist/react/useCustomBlockInit.js +3 -19
  64. package/dist/react/useRuntimeState.d.ts +4 -3
  65. package/dist/react/useRuntimeState.d.ts.map +1 -1
  66. package/dist/react/useRuntimeState.js +3 -2
  67. package/dist/version.js +1 -1
  68. package/docs/data-sources.md +10 -8
  69. package/docs/errors.md +5 -1
  70. package/docs/lifecycle.md +19 -31
  71. package/docs/manifest.md +1 -1
  72. package/package.json +1 -1
  73. package/src/bridge/SandboxBridge.ts +138 -44
  74. package/src/bridge/dataSources/resolve.ts +3 -14
  75. package/src/bridge/hostState.ts +2 -0
  76. package/src/bridge/loadManifest.ts +6 -6
  77. package/src/bridge/manifest.ts +3 -3
  78. package/src/bridge/messages/connect.ts +42 -0
  79. package/src/bridge/messages/init.ts +24 -10
  80. package/src/bridge/messages/initResult.ts +37 -0
  81. package/src/bridge/messages/sandboxToHost.ts +4 -2
  82. package/src/bridge/sandboxClient.ts +3 -8
  83. package/src/customBlock.ts +2 -2
  84. package/src/host/createCustomBlockHost.ts +265 -45
  85. package/src/host/lifecycle/initErrors.ts +25 -7
  86. package/src/host/lifecycle/types.ts +13 -4
  87. package/src/host/messages/invalidSandboxMessage.ts +0 -3
  88. package/src/host.ts +13 -2
  89. package/src/index.ts +3 -1
  90. package/src/init.ts +73 -60
  91. package/src/react/index.ts +0 -1
  92. package/src/react/standalonePreview.ts +2 -0
  93. package/src/react/useCustomBlockInit.ts +6 -27
  94. package/src/react/useRuntimeState.ts +4 -3
  95. package/dist/bridge/messages/ready.d.ts.map +0 -1
  96. package/dist/bridge/messages/ready.js +0 -43
  97. package/dist/host/lifecycle/ready.d.ts.map +0 -1
  98. package/src/bridge/messages/ready.ts +0 -48
  99. /package/dist/host/lifecycle/{ready.js → protocolVersion.js} +0 -0
  100. /package/src/host/lifecycle/{ready.ts → protocolVersion.ts} +0 -0
package/dist/index.d.ts CHANGED
@@ -22,8 +22,8 @@ export type { NotionCreatePagePosition } from "./bridge/messages/createPage.js";
22
22
  export type { CustomBlockCreatePageErrorCode, CustomBlockCreatePageErrorInfo, } from "./bridge/messages/createPageResult.js";
23
23
  export type { CustomBlockGetPageErrorCode, CustomBlockGetPageErrorInfo, } from "./bridge/messages/getPage.js";
24
24
  export type { CustomBlockGetUserErrorCode, CustomBlockGetUserErrorInfo, } from "./bridge/messages/getUser.js";
25
- export type { CustomBlockInitErrorCode, CustomBlockInitErrorInfo, } from "./bridge/messages/init.js";
26
- export { CustomBlockInitError } from "./bridge/messages/init.js";
25
+ export type { CustomBlockInitErrorCode, CustomBlockInitErrorInfo, CustomBlockInitializationErrorCode, CustomBlockInitializationErrorInfo, } from "./bridge/messages/init.js";
26
+ export { CustomBlockInitializationError } from "./bridge/messages/init.js";
27
27
  export type { CustomBlockListUsersErrorCode, CustomBlockListUsersErrorInfo, } from "./bridge/messages/listUsers.js";
28
28
  export type { CustomBlockQueryDataSourceErrorCode, CustomBlockQueryDataSourceErrorInfo, } from "./bridge/messages/queryDataSourceResult.js";
29
29
  export type { CustomBlockUpdatePageErrorCode, CustomBlockUpdatePageErrorInfo, } from "./bridge/messages/updatePageResult.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,YAAY,EACX,sBAAsB,EACtB,gBAAgB,GAChB,MAAM,oCAAoC,CAAA;AAC3C,YAAY,EACX,oBAAoB,EACpB,+BAA+B,EAC/B,gCAAgC,GAChC,MAAM,wCAAwC,CAAA;AAC/C,YAAY,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAA;AACpF,YAAY,EACX,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,GAClB,MAAM,mCAAmC,CAAA;AAC1C,YAAY,EACX,wCAAwC,EACxC,wCAAwC,GACxC,MAAM,gCAAgC,CAAA;AACvC,YAAY,EACX,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,GACjB,MAAM,wCAAwC,CAAA;AAC/C,OAAO,EACN,2BAA2B,EAC3B,qBAAqB,GACrB,MAAM,wCAAwC,CAAA;AAC/C,YAAY,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAA;AAChF,YAAY,EACX,4BAA4B,EAC5B,4BAA4B,GAC5B,MAAM,yCAAyC,CAAA;AAChD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC9D,YAAY,EACX,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,aAAa,GACb,MAAM,iBAAiB,CAAA;AACxB,YAAY,EACX,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,GAChB,MAAM,sBAAsB,CAAA;AAC7B,YAAY,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAA;AAC/E,YAAY,EACX,8BAA8B,EAC9B,8BAA8B,GAC9B,MAAM,uCAAuC,CAAA;AAC9C,YAAY,EACX,2BAA2B,EAC3B,2BAA2B,GAC3B,MAAM,8BAA8B,CAAA;AACrC,YAAY,EACX,2BAA2B,EAC3B,2BAA2B,GAC3B,MAAM,8BAA8B,CAAA;AACrC,YAAY,EACX,wBAAwB,EACxB,wBAAwB,GACxB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAChE,YAAY,EACX,6BAA6B,EAC7B,6BAA6B,GAC7B,MAAM,gCAAgC,CAAA;AACvC,YAAY,EACX,mCAAmC,EACnC,mCAAmC,GACnC,MAAM,4CAA4C,CAAA;AACnD,YAAY,EACX,8BAA8B,EAC9B,8BAA8B,GAC9B,MAAM,uCAAuC,CAAA;AAC9C,YAAY,EACX,UAAU,EACV,eAAe,EACf,cAAc,EACd,YAAY,EACZ,0BAA0B,EAC1B,4BAA4B,EAC5B,uBAAuB,EACvB,0BAA0B,GAC1B,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAA;AACxD,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,EAAE,KAAK,gBAAgB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACrE,OAAO,EACN,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,eAAe,EACf,gBAAgB,GAChB,MAAM,WAAW,CAAA;AAClB,cAAc,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,YAAY,EACX,sBAAsB,EACtB,gBAAgB,GAChB,MAAM,oCAAoC,CAAA;AAC3C,YAAY,EACX,oBAAoB,EACpB,+BAA+B,EAC/B,gCAAgC,GAChC,MAAM,wCAAwC,CAAA;AAC/C,YAAY,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAA;AACpF,YAAY,EACX,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,GAClB,MAAM,mCAAmC,CAAA;AAC1C,YAAY,EACX,wCAAwC,EACxC,wCAAwC,GACxC,MAAM,gCAAgC,CAAA;AACvC,YAAY,EACX,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,GACjB,MAAM,wCAAwC,CAAA;AAC/C,OAAO,EACN,2BAA2B,EAC3B,qBAAqB,GACrB,MAAM,wCAAwC,CAAA;AAC/C,YAAY,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAA;AAChF,YAAY,EACX,4BAA4B,EAC5B,4BAA4B,GAC5B,MAAM,yCAAyC,CAAA;AAChD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC9D,YAAY,EACX,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,aAAa,GACb,MAAM,iBAAiB,CAAA;AACxB,YAAY,EACX,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,GAChB,MAAM,sBAAsB,CAAA;AAC7B,YAAY,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAA;AAC/E,YAAY,EACX,8BAA8B,EAC9B,8BAA8B,GAC9B,MAAM,uCAAuC,CAAA;AAC9C,YAAY,EACX,2BAA2B,EAC3B,2BAA2B,GAC3B,MAAM,8BAA8B,CAAA;AACrC,YAAY,EACX,2BAA2B,EAC3B,2BAA2B,GAC3B,MAAM,8BAA8B,CAAA;AACrC,YAAY,EACX,wBAAwB,EACxB,wBAAwB,EACxB,kCAAkC,EAClC,kCAAkC,GAClC,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,8BAA8B,EAAE,MAAM,2BAA2B,CAAA;AAC1E,YAAY,EACX,6BAA6B,EAC7B,6BAA6B,GAC7B,MAAM,gCAAgC,CAAA;AACvC,YAAY,EACX,mCAAmC,EACnC,mCAAmC,GACnC,MAAM,4CAA4C,CAAA;AACnD,YAAY,EACX,8BAA8B,EAC9B,8BAA8B,GAC9B,MAAM,uCAAuC,CAAA;AAC9C,YAAY,EACX,UAAU,EACV,eAAe,EACf,cAAc,EACd,YAAY,EACZ,0BAA0B,EAC1B,4BAA4B,EAC5B,uBAAuB,EACvB,0BAA0B,GAC1B,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAA;AACxD,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,EAAE,KAAK,gBAAgB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACrE,OAAO,EACN,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,eAAe,EACf,gBAAgB,GAChB,MAAM,WAAW,CAAA;AAClB,cAAc,YAAY,CAAA"}
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * should import hooks and components from `@notionhq/custom-blocks/react`.
8
8
  */
9
9
  export { NOTION_BUILTIN_PROPERTY_IDS, NOTION_PROPERTY_TYPES, } from "./bridge/dataSources/propertySchema.js";
10
- export { CustomBlockInitError } from "./bridge/messages/init.js";
10
+ export { CustomBlockInitializationError } from "./bridge/messages/init.js";
11
11
  export { pages, users } from "./bridge/sandboxClient.js";
12
12
  export { customBlock } from "./customBlock.js";
13
13
  export { initCustomBlock, NotInIframeError, } from "./init.js";
package/dist/init.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import type { NotionDataSource } from "./bridge/dataSources/dataSource.js";
2
2
  import type { NotionBlockId } from "./bridge/ids.js";
3
- import { type CustomBlockInitErrorCode } from "./bridge/messages/init.js";
3
+ import { CustomBlockInitializationError } from "./bridge/messages/init.js";
4
4
  import type { CustomBlockPage } from "./bridge/pages/page.js";
5
5
  import type { NotionParent } from "./bridge/parent.js";
6
6
  import type { NotionTheme } from "./bridge/theme.js";
7
7
  import type { NotionUser } from "./bridge/users/user.js";
8
8
  /**
9
- * The payload sent by the host in the `init` message in response to the sandbox's `ready` message.
9
+ * The payload sent by the host in the `init` message in response to the sandbox's `connect` message.
10
10
  */
11
11
  export type CustomBlockInitPayload = {
12
12
  theme: NotionTheme;
@@ -22,10 +22,9 @@ export type CustomBlockInitPayload = {
22
22
  * `<NotionCustomBlock>` catches this specifically and falls back to a standalone preview with a
23
23
  * warning banner. Direct callers can `instanceof` it to apply their own policy.
24
24
  */
25
- export declare class NotInIframeError extends Error {
25
+ export declare class NotInIframeError extends CustomBlockInitializationError {
26
26
  constructor(message?: string);
27
- code: CustomBlockInitErrorCode;
28
- isRetryable: boolean;
27
+ code: "not_in_iframe";
29
28
  }
30
29
  /**
31
30
  * Options for {@link initCustomBlock}.
@@ -41,9 +40,10 @@ export type InitCustomBlockOptions = {
41
40
  };
42
41
  /**
43
42
  * Performs the SDK <-> host handshake: loads `custom_blocks.json`, posts
44
- * `ready`, then awaits the host's `init` message. Resolves with that payload.
43
+ * `connect`, then awaits the host's `init` message. Resolves after the sandbox
44
+ * applies the payload and returns `initResult.success`.
45
45
  *
46
- * Rejects with a `CustomBlockInitError` if the host doesn't respond inside `timeoutMs`.
46
+ * Rejects with a `CustomBlockInitializationError` if initialization fails.
47
47
  *
48
48
  * Idempotent: subsequent calls return the same promise as the first and ignore any new options.
49
49
  * Mount your React tree (or call any SDK hook / `customBlock.subscribe`) only after the
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/init.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAA;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,OAAO,EAEN,KAAK,wBAAwB,EAE7B,MAAM,2BAA2B,CAAA;AAClC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAExD;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACpC,KAAK,EAAE,WAAW,CAAA;IAClB,OAAO,EAAE,aAAa,CAAA;IACtB,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,eAAe,CAAA;IACrB,WAAW,EAAE,UAAU,CAAA;IACvB,WAAW,EAAE,gBAAgB,EAAE,CAAA;CAC/B,CAAA;AAED;;;;;GAKG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;gBAC9B,OAAO,GAAE,MAA8B;IAOnD,IAAI,EAAE,wBAAwB,CAAA;IAC9B,WAAW,EAAE,OAAO,CAAA;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACpC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AASD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC9B,IAAI,GAAE,sBAA2B,GAC/B,OAAO,CAAC,sBAAsB,CAAC,CAyDjC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/init.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAA;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,OAAO,EAAE,8BAA8B,EAAE,MAAM,2BAA2B,CAAA;AAC1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAGxD;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACpC,KAAK,EAAE,WAAW,CAAA;IAClB,OAAO,EAAE,aAAa,CAAA;IACtB,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,eAAe,CAAA;IACrB,WAAW,EAAE,UAAU,CAAA;IACvB,WAAW,EAAE,gBAAgB,EAAE,CAAA;CAC/B,CAAA;AAED;;;;;GAKG;AACH,qBAAa,gBAAiB,SAAQ,8BAA8B;gBACvD,OAAO,GAAE,MAA8B;IAS3C,IAAI,EAAE,eAAe,CAAA;CAC7B;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACpC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AASD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC9B,IAAI,GAAE,sBAA2B,GAC/B,OAAO,CAAC,sBAAsB,CAAC,CAKjC"}
package/dist/init.js CHANGED
@@ -1,18 +1,21 @@
1
1
  import { loadManifest } from "./bridge/loadManifest.js";
2
- import { CustomBlockInitError, } from "./bridge/messages/init.js";
2
+ import { CustomBlockInitializationError } from "./bridge/messages/init.js";
3
3
  import { customBlockHost } from "./bridge/sandboxClient.js";
4
+ import { unreachable } from "./utils.js";
4
5
  /**
5
6
  * Error thrown when the SDK is loaded in a top-level standalone window with no parent frame.
6
7
  * `postMessage` would just hit the same window and the handshake can never complete.
7
8
  * `<NotionCustomBlock>` catches this specifically and falls back to a standalone preview with a
8
9
  * warning banner. Direct callers can `instanceof` it to apply their own policy.
9
10
  */
10
- export class NotInIframeError extends Error {
11
+ export class NotInIframeError extends CustomBlockInitializationError {
11
12
  constructor(message = NOT_IN_IFRAME_MESSAGE) {
12
- super(message);
13
+ super({
14
+ code: "not_in_iframe",
15
+ message,
16
+ isRetryable: false,
17
+ });
13
18
  this.name = "NotInIframeError";
14
- this.code = "not_in_iframe";
15
- this.isRetryable = false;
16
19
  }
17
20
  }
18
21
  const DEFAULT_INIT_TIMEOUT_MS = 15000;
@@ -20,9 +23,10 @@ const NOT_IN_IFRAME_MESSAGE = "<NotionCustomBlock> only works inside an iframe
20
23
  let initPromise;
21
24
  /**
22
25
  * Performs the SDK <-> host handshake: loads `custom_blocks.json`, posts
23
- * `ready`, then awaits the host's `init` message. Resolves with that payload.
26
+ * `connect`, then awaits the host's `init` message. Resolves after the sandbox
27
+ * applies the payload and returns `initResult.success`.
24
28
  *
25
- * Rejects with a `CustomBlockInitError` if the host doesn't respond inside `timeoutMs`.
29
+ * Rejects with a `CustomBlockInitializationError` if initialization fails.
26
30
  *
27
31
  * Idempotent: subsequent calls return the same promise as the first and ignore any new options.
28
32
  * Mount your React tree (or call any SDK hook / `customBlock.subscribe`) only after the
@@ -30,54 +34,70 @@ let initPromise;
30
34
  */
31
35
  export function initCustomBlock(opts = {}) {
32
36
  if (initPromise === undefined) {
33
- initPromise = (async () => {
34
- // Fail fast with a typed error when rendered as a standalone tab and not in a parent frame.
35
- // Otherwise, it would eventually hit the timeout, since `postMessage` to `window.parent`
36
- // would just hit the same window and never arrive.
37
- if (typeof window !== "undefined" && window.parent === window) {
38
- throw new NotInIframeError();
39
- }
40
- // Load the manifest and send it to the host.
41
- const manifestResult = await loadManifest();
42
- customBlockHost.sendReady(manifestResult);
43
- const timeoutMs = opts.timeoutMs ?? DEFAULT_INIT_TIMEOUT_MS;
44
- let message;
45
- try {
46
- message = await customBlockHost.awaitInit(AbortSignal.timeout(timeoutMs));
47
- }
48
- catch (error) {
49
- if (isTimeoutError(error)) {
50
- throw new CustomBlockInitError({
51
- code: "init_timeout",
52
- message: "Host did not respond to init before the timeout.",
53
- isRetryable: true,
54
- });
55
- }
56
- throw error;
57
- }
58
- if (message.status === "error") {
59
- console.error(`[custom-blocks-sdk] host reported init error (${message.error.code}): ${message.error.message}`);
60
- throw new CustomBlockInitError(message.error);
61
- }
62
- const hostState = customBlockHost.getState();
63
- if (hostState.status !== "initialized") {
64
- throw new CustomBlockInitError({
37
+ initPromise = initializeCustomBlock(opts);
38
+ }
39
+ return initPromise;
40
+ }
41
+ async function initializeCustomBlock(opts) {
42
+ try {
43
+ // Fail fast with a typed error when rendered as a standalone tab and not in a parent frame.
44
+ // Otherwise, it would eventually hit the timeout, since `postMessage` to `window.parent`
45
+ // would just hit the same window and never arrive.
46
+ if (typeof window !== "undefined" && window.parent === window) {
47
+ throw new NotInIframeError();
48
+ }
49
+ // Load the manifest and send it to the host.
50
+ const manifestResult = await loadManifest();
51
+ customBlockHost.sendConnect(manifestResult);
52
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_INIT_TIMEOUT_MS;
53
+ await awaitHostInitWithTimeout(timeoutMs);
54
+ const hostState = customBlockHost.getState();
55
+ switch (hostState.status) {
56
+ case "initialized":
57
+ return {
58
+ theme: hostState.theme,
59
+ blockId: hostState.blockId,
60
+ parent: hostState.parent,
61
+ page: hostState.page,
62
+ currentUser: hostState.currentUser,
63
+ dataSources: hostState.dataSources,
64
+ };
65
+ case "uninitialized":
66
+ throw new CustomBlockInitializationError({
65
67
  code: "context_unavailable",
66
68
  message: "Host block payload is unavailable.",
67
69
  isRetryable: true,
68
70
  });
69
- }
70
- return {
71
- theme: message.theme,
72
- blockId: hostState.blockId,
73
- parent: hostState.parent,
74
- page: hostState.page,
75
- currentUser: message.currentUser,
76
- dataSources: hostState.dataSources,
77
- };
78
- })();
71
+ default:
72
+ return unreachable(hostState);
73
+ }
74
+ }
75
+ catch (error) {
76
+ if (error instanceof CustomBlockInitializationError) {
77
+ throw error;
78
+ }
79
+ const message = error instanceof Error ? error.message : String(error);
80
+ throw new CustomBlockInitializationError({
81
+ code: "unknown_error",
82
+ message,
83
+ isRetryable: false,
84
+ });
85
+ }
86
+ }
87
+ async function awaitHostInitWithTimeout(timeoutMs) {
88
+ try {
89
+ await customBlockHost.awaitInit(AbortSignal.timeout(timeoutMs));
90
+ }
91
+ catch (error) {
92
+ if (isTimeoutError(error)) {
93
+ throw new CustomBlockInitializationError({
94
+ code: "init_timeout",
95
+ message: "Host did not respond to init before the timeout.",
96
+ isRetryable: true,
97
+ });
98
+ }
99
+ throw error;
79
100
  }
80
- return initPromise;
81
101
  }
82
102
  function isTimeoutError(error) {
83
103
  return (typeof error === "object" &&
@@ -8,7 +8,7 @@
8
8
  */
9
9
  export { NotionCustomBlock, type NotionCustomBlockProps, } from "./NotionCustomBlock.js";
10
10
  export { useCustomBlockAutoResize } from "./useCustomBlockAutoResize.js";
11
- export { type CustomBlockInitFailure, type UseCustomBlockInitResult, useCustomBlockInit, } from "./useCustomBlockInit.js";
11
+ export { type UseCustomBlockInitResult, useCustomBlockInit, } from "./useCustomBlockInit.js";
12
12
  export { useDataSource } from "./useDataSource.js";
13
13
  export { useBlockId, useCurrentUser, useManifest, usePage, useParent, useTheme, } from "./useRuntimeState.js";
14
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACN,iBAAiB,EACjB,KAAK,sBAAsB,GAC3B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AACxE,OAAO,EACN,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,kBAAkB,GAClB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EACN,UAAU,EACV,cAAc,EACd,WAAW,EACX,OAAO,EACP,SAAS,EACT,QAAQ,GACR,MAAM,sBAAsB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACN,iBAAiB,EACjB,KAAK,sBAAsB,GAC3B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AACxE,OAAO,EACN,KAAK,wBAAwB,EAC7B,kBAAkB,GAClB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EACN,UAAU,EACV,cAAc,EACd,WAAW,EACX,OAAO,EACP,SAAS,EACT,QAAQ,GACR,MAAM,sBAAsB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"standalonePreview.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/react/standalonePreview.ts"],"names":[],"mappings":"AAOA,wBAAgB,0BAA0B,SAoBzC"}
1
+ {"version":3,"file":"standalonePreview.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/react/standalonePreview.ts"],"names":[],"mappings":"AAOA,wBAAgB,0BAA0B,SAsBzC"}
@@ -6,11 +6,13 @@ const previewPageId = v.parse(notionPageIdSchema, "");
6
6
  export function seedStandalonePreviewState() {
7
7
  customBlockHost.setMockState({
8
8
  type: "init",
9
+ initializationId: "standalone-preview",
9
10
  status: "success",
10
11
  theme: "light",
11
12
  blockId: previewBlockId,
12
13
  parent: { type: "page_id", page_id: previewPageId },
13
14
  page: { id: previewPageId, parent: { type: "workspace", workspace: true } },
15
+ manifest: { version: 1, dataSources: {} },
14
16
  dataSources: { bindings: {} },
15
17
  currentUser: {
16
18
  object: "user",
@@ -1,13 +1,12 @@
1
- import { CustomBlockInitError } from "../bridge/messages/init.js";
2
- import { type CustomBlockInitPayload, type InitCustomBlockOptions, NotInIframeError } from "../init.js";
3
- export type CustomBlockInitFailure = CustomBlockInitError | NotInIframeError;
1
+ import type { CustomBlockInitializationError } from "../bridge/messages/init.js";
2
+ import { type CustomBlockInitPayload, type InitCustomBlockOptions } from "../init.js";
4
3
  /**
5
4
  * Discriminated state returned by {@link useCustomBlockInit}.
6
5
  *
7
6
  * Branch on `isLoaded`/`error`:
8
7
  * - `{ isLoaded: false, error: undefined }` — handshake in progress.
9
- * - `{ isLoaded: false, error: CustomBlockInitFailure }` — handshake failed (most commonly a
10
- * `CustomBlockInitError` with code `init_timeout` because the host never sent `init`).
8
+ * - `{ isLoaded: false, error: CustomBlockInitializationError }` — handshake failed (most commonly a
9
+ * `CustomBlockInitializationError` with code `init_timeout` because the host never sent `init`).
11
10
  * - `{ isLoaded: true, initial }` — handshake complete; safe to render
12
11
  * children that call `useTheme`, `useBlockId`, etc.
13
12
  */
@@ -16,7 +15,7 @@ export type UseCustomBlockInitResult = {
16
15
  error: undefined;
17
16
  } | {
18
17
  isLoaded: false;
19
- error: CustomBlockInitFailure;
18
+ error: CustomBlockInitializationError;
20
19
  } | {
21
20
  isLoaded: true;
22
21
  error: undefined;
@@ -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,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"}
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,KAAK,EAAE,8BAA8B,EAAE,MAAM,4BAA4B,CAAA;AAChF,OAAO,EACN,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAE3B,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;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,8BAA8B,CAAA;CAAE,GAC1D;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,CA2B1B"}
@@ -1,6 +1,5 @@
1
1
  import { useEffect, useState } from "react";
2
- import { CustomBlockInitError } from "../bridge/messages/init.js";
3
- import { initCustomBlock, NotInIframeError, } from "../init.js";
2
+ import { initCustomBlock, } from "../init.js";
4
3
  /**
5
4
  * React wrapper around {@link initCustomBlock}. Kicks off the SDK ↔ host
6
5
  * handshake on mount and returns a discriminated state object so the rest of
@@ -29,12 +28,9 @@ export function useCustomBlockInit(opts) {
29
28
  if (!cancelled) {
30
29
  setState({ isLoaded: true, error: undefined, initial });
31
30
  }
32
- }, err => {
31
+ }, error => {
33
32
  if (!cancelled) {
34
- setState({
35
- isLoaded: false,
36
- error: normalizeInitError(err),
37
- });
33
+ setState({ isLoaded: false, error });
38
34
  }
39
35
  });
40
36
  return () => {
@@ -46,15 +42,3 @@ export function useCustomBlockInit(opts) {
46
42
  }, []);
47
43
  return state;
48
44
  }
49
- function normalizeInitError(error) {
50
- if (error instanceof CustomBlockInitError ||
51
- error instanceof NotInIframeError) {
52
- return error;
53
- }
54
- const message = error instanceof Error ? error.message : String(error);
55
- return new CustomBlockInitError({
56
- code: "unknown_error",
57
- message,
58
- isRetryable: false,
59
- });
60
- }
@@ -54,13 +54,14 @@ export declare function useTheme(): NotionTheme;
54
54
  */
55
55
  export declare function useCurrentUser(): NotionUser;
56
56
  /**
57
- * Returns the author-declared manifest loaded from `custom_blocks.json` — the semantic
57
+ * Returns the authoritative manifest received from the host — the semantic
58
58
  * data-source keys plus their declared `name`, `description`, and property declarations.
59
- * `null` when the block ships no manifest.
59
+ *
60
+ * Throws if called before `initCustomBlock` has resolved.
60
61
  *
61
62
  * This is the configuration the block declared, not host-resolved bindings: use
62
63
  * `useDataSource(key)` for rows and resolved schema. Handy for enumerating the declared
63
64
  * data-source keys (e.g. picking a default key, or rendering a key switcher).
64
65
  */
65
- export declare function useManifest(): CustomBlockManifest | null;
66
+ export declare function useManifest(): CustomBlockManifest;
66
67
  //# sourceMappingURL=useRuntimeState.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useRuntimeState.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/react/useRuntimeState.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAGzD;;;;;;;GAOG;AACH,wBAAgB,UAAU,IAAI,aAAa,CAE1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,IAAI,YAAY,CAExC;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,IAAI,eAAe,CAEzC;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,IAAI,WAAW,CAEtC;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,IAAI,UAAU,CAE3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,IAAI,mBAAmB,GAAG,IAAI,CAExD"}
1
+ {"version":3,"file":"useRuntimeState.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/react/useRuntimeState.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAGzD;;;;;;;GAOG;AACH,wBAAgB,UAAU,IAAI,aAAa,CAE1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,IAAI,YAAY,CAExC;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,IAAI,eAAe,CAEzC;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,IAAI,WAAW,CAEtC;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,IAAI,UAAU,CAE3C;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,IAAI,mBAAmB,CAEjD"}
@@ -60,9 +60,10 @@ export function useCurrentUser() {
60
60
  return useSyncExternalStore(customBlock.subscribe, customBlock.getCurrentUser);
61
61
  }
62
62
  /**
63
- * Returns the author-declared manifest loaded from `custom_blocks.json` — the semantic
63
+ * Returns the authoritative manifest received from the host — the semantic
64
64
  * data-source keys plus their declared `name`, `description`, and property declarations.
65
- * `null` when the block ships no manifest.
65
+ *
66
+ * Throws if called before `initCustomBlock` has resolved.
66
67
  *
67
68
  * This is the configuration the block declared, not host-resolved bindings: use
68
69
  * `useDataSource(key)` for rows and resolved schema. Handy for enumerating the declared
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 CUSTOM_BLOCKS_SDK_VERSION = "0.0.77"
7
+ export const CUSTOM_BLOCKS_SDK_VERSION = "0.0.79"
@@ -61,24 +61,26 @@ Query failures follow the SDK's [error-handling contract](./errors.md).
61
61
  ### `useManifest()`
62
62
 
63
63
  ```ts
64
- function useManifest(): CustomBlockManifest | null;
64
+ function useManifest(): CustomBlockManifest;
65
65
  ```
66
66
 
67
- Returns the author-declared manifest loaded from `custom_blocks.json` the semantic data-source keys plus their declared `name`, `description`, and property declarations. `null` when the block ships no manifest. This is the configuration the block _declared_, not host-resolved bindings; use `useDataSource(key)` for rows and resolved schema. Handy for enumerating declared keys:
67
+ Returns the authoritative block manifest received from the host during initialization, including the semantic data source keys plus their declared `name`, `description`, and property declarations. Handy for enumerating declared keys:
68
68
 
69
69
  ```tsx
70
70
  const manifest = useManifest();
71
- const keys = Object.keys(manifest?.dataSources ?? {});
71
+ const keys = Object.keys(manifest.dataSources);
72
72
  const activeKey = keys[0] ?? "default";
73
73
  ```
74
74
 
75
+ This API throws if called before initialization. This does not include host-resolved bindings nor does it include any actual data rows.
76
+
75
77
  ### `customBlock.getManifest()`
76
78
 
77
79
  ```ts
78
- function customBlock.getManifest(): CustomBlockManifest | null;
80
+ function customBlock.getManifest(): CustomBlockManifest;
79
81
  ```
80
82
 
81
- Framework-neutral getter for the same manifest returned by `useManifest()`. The manifest is static for the lifetime of the sandbox, so there is nothing to subscribe to.
83
+ Framework-neutral getter for the same manifest returned by `useManifest()`. Throws if called before `initCustomBlock()` resolves. The manifest is static for the lifetime of the sandbox, so there is nothing to subscribe to.
82
84
 
83
85
  ```ts
84
86
  await initCustomBlock();
@@ -113,8 +115,8 @@ export function ScoreList() {
113
115
  if (error) return <div role="alert">Couldn't load: {error.message}</div>;
114
116
  if (isLoading && items.length === 0) return <div>Loading…</div>;
115
117
 
116
- const ready = items.filter(isComplete);
117
- if (ready.length === 0) {
118
+ const displayItems = items.filter(isComplete);
119
+ if (displayItems.length === 0) {
118
120
  return (
119
121
  <div>
120
122
  Map a data source with key <code>{KEY}</code> exposing <code>name</code>{" "}
@@ -126,7 +128,7 @@ export function ScoreList() {
126
128
  return (
127
129
  <div>
128
130
  <ul>
129
- {ready.map((item) => (
131
+ {displayItems.map((item) => (
130
132
  <li key={item.id}>
131
133
  {String(item.propertiesByKey.name)} —{" "}
132
134
  {Number(item.propertiesByKey.score)}
package/docs/errors.md CHANGED
@@ -35,7 +35,11 @@ Each API narrows `code` to its own open string union. The known literals provide
35
35
 
36
36
  ## Initialization errors
37
37
 
38
- Unlike request helpers, `initCustomBlock` rejects with `CustomBlockInitError` when the host rejects initialization. Use `error instanceof CustomBlockInitError`, then inspect its `code` and `isRetryable` fields. Its structured payload is available as `CustomBlockInitErrorInfo`, with `code` narrowed to `CustomBlockInitErrorCode`.
38
+ Unlike request helpers, `initCustomBlock` rejects with `CustomBlockInitializationError`. Its `code` type is `CustomBlockInitializationErrorCode`, which covers failures reported by either side of the handshake plus SDK-local timeout and iframe errors.
39
+
40
+ `CustomBlockInitializationErrorInfo` is the matching `{ code, message, isRetryable }` object type.
41
+
42
+ Host implementers use the narrower message-specific types: `CustomBlockInitErrorInfo` for `init.error` and `CustomBlockInitResultErrorInfo` for `initResult.error`.
39
43
 
40
44
  See [Lifecycle and initialization](./lifecycle.md) for initialization-specific error codes.
41
45
 
package/docs/lifecycle.md CHANGED
@@ -4,9 +4,15 @@ 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 `CustomBlockInitPayload` — `await` it before mounting React so hooks always see populated state.
7
+ `initCustomBlock()` completes a three-message handshake:
8
8
 
9
- - Default `timeoutMs` is 15000; rejects with `CustomBlockInitError` code `init_timeout` if the host doesn't respond.
9
+ 1. The SDK sends `connect` with a new initialization ID and some version info.
10
+ 2. The host replies with `init`, echoing that initialization ID and providing the manifest, block context, current user, and data source bindings.
11
+ 3. The SDK confirms that state is compatible and sends `initResult` with the same initialization ID.
12
+
13
+ The promise resolves only after the SDK sends `initResult.success`. Await it before mounting React so hooks always see initialized state.
14
+
15
+ - Rejects with `CustomBlockInitializationError` code `init_timeout` if the host doesn't respond.
10
16
  - 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.
11
17
  - After init, `*Changed` events (e.g. `themeChanged`, `parentChanged`, `dataSourcesChanged`) push updates and the relevant hooks re-render.
12
18
  - `initCustomBlock` is idempotent; subsequent calls return the same promise.
@@ -44,7 +50,7 @@ function useCustomBlockInit(
44
50
 
45
51
  type UseCustomBlockInitResult =
46
52
  | { isLoaded: false; error: undefined }
47
- | { isLoaded: false; error: CustomBlockInitFailure }
53
+ | { isLoaded: false; error: CustomBlockInitializationError }
48
54
  | { isLoaded: true; error: undefined; initial: CustomBlockInitPayload };
49
55
  ```
50
56
 
@@ -69,39 +75,21 @@ function initCustomBlock(
69
75
  type InitCustomBlockOptions = { timeoutMs?: number };
70
76
  ```
71
77
 
72
- The lower-level promise API. `<NotionCustomBlock>` and `useCustomBlockInit` both call it for you. Reach for it directly only when you want to `await` init at module scope (e.g. before `ReactDOM.createRoot`). If the host replies with an init error, the promise rejects with `CustomBlockInitError`.
78
+ The lower-level promise API. `<NotionCustomBlock>` and `useCustomBlockInit` both call it for you. Reach for it directly only when you want to `await` init at module scope (e.g. before `ReactDOM.createRoot`). Expected failures reject with `CustomBlockInitializationError`.
73
79
 
74
- ### `CustomBlockInitError`
80
+ ### `CustomBlockInitializationError`
75
81
 
76
- ```ts
77
- class CustomBlockInitError extends Error {
78
- code: CustomBlockInitErrorCode;
79
- isRetryable: boolean;
80
- }
82
+ The code tells you where initialization failed:
81
83
 
82
- type CustomBlockInitErrorCode =
83
- | "no_ready"
84
- | "invalid_ready"
85
- | "manifest_unavailable"
86
- | "manifest_invalid"
87
- | "invalid_protocol_version"
88
- | "unsupported_protocol_version"
89
- | "context_unavailable"
90
- | "current_user_unavailable"
91
- | "missing_data_source_binding"
92
- | "data_source_unavailable"
93
- | "missing_property_binding"
94
- | "invalid_property_binding"
95
- | "not_in_iframe"
96
- | "init_timeout"
97
- | "unknown_error";
98
- ```
84
+ - `CustomBlockInitErrorCode`: the host rejected initialization in `init.error`.
85
+ - `CustomBlockInitResultErrorCode`: the SDK could not apply the host's state and reported why in `initResult.error`.
86
+ - `not_in_iframe` and `init_timeout`: the SDK failed before the handshake completed.
99
87
 
100
- Thrown when the host rejects initialization instead of returning the initial theme, block location, current user, and data sources. Use `error instanceof CustomBlockInitError`, `error.code`, and `error.isRetryable` to branch on host-reported setup failures. See [Error handling](./errors.md) for the shared structured error contract.
88
+ All code types are open string unions, so keep a default branch. Use `isRetryable` to decide whether offering a retry makes sense, and treat `message` as display text rather than a stable identifier. See [Error handling](./errors.md) for the shared error contract.
101
89
 
102
90
  ### `customBlock`
103
91
 
104
- Framework-neutral runtime APIs for renderers that do not use React hooks. `customBlock.getState()` returns a `CustomBlockState` snapshot that hides internal query cache details. Initialized-only getters (`getTheme`, `getBlockId`, `getParent`, `getPage`, and `getCurrentUser`) throw until `initCustomBlock()` resolves. `getManifest()` is not gated on init — it returns the declared manifest (or `null`) regardless.
92
+ Framework-neutral runtime APIs for renderers that do not use React hooks. `customBlock.getState()` returns a `CustomBlockState` snapshot that hides internal query cache details. Initialized-only getters (e.g. `getTheme`, `getBlockId`, `getParent`) throw until `initCustomBlock()` resolves.
105
93
 
106
94
  `customBlock` covers runtime state and sizing. Row querying still goes through `useDataSource`, while imperative APIs such as `pages.*` and `users.*` are already framework-neutral functions.
107
95
 
@@ -127,7 +115,7 @@ stopAutoResize();
127
115
 
128
116
  ### `NotInIframeError`
129
117
 
130
- Thrown when `initCustomBlock` is called in a top-level tab (no parent frame). It has `code: "not_in_iframe"` and `isRetryable: false`. `<NotionCustomBlock>` catches it and falls back to a standalone preview with a warning banner; direct callers can `instanceof NotInIframeError` to apply their own policy.
118
+ Thrown when `initCustomBlock` is called in a top-level tab (no parent frame). It extends `CustomBlockInitializationError`, has `code: "not_in_iframe"` and `isRetryable: false`, and can be detected specifically with `instanceof NotInIframeError`. `<NotionCustomBlock>` catches it and falls back to a standalone preview with a warning banner.
131
119
 
132
120
  ### `useCustomBlockAutoResize({ enabled? })`
133
121
 
@@ -154,7 +142,7 @@ function App() {
154
142
  Press `\` while focused in a custom block to toggle a debug overlay that replaces the block's children with a `<pre>` log of every `postMessage` sent and received over the bridge. Each line is formatted as:
155
143
 
156
144
  ```
157
- [ISO timestamp] sent/received: {"type":"ready", …}
145
+ [ISO timestamp] sent/received: {"type":"connect", …}
158
146
  ```
159
147
 
160
148
  The log is intentionally plain — no filtering or decoration — so it can be copied and pasted directly to a local coding agent for debugging.
package/docs/manifest.md CHANGED
@@ -18,7 +18,7 @@ A custom block declares its required data sources in `custom_blocks.json` at the
18
18
  }
19
19
  ```
20
20
 
21
- `initCustomBlock()` fetches the manifest and forwards it with `ready`. The `notionCustomBlock()` Vite plugin from `@notionhq/custom-blocks/vite` serves it in dev and emits it into `dist/` on build. If the file is missing, the SDK sends `ready` with `status: "success"` and `manifest: null`, which means the block has no declared data requirements. If the file is unavailable for another reason or invalid, the SDK sends `ready` with `status: "error"` and an `error` payload. Hosts should reject `init` by forwarding that full error payload unchanged.
21
+ `initCustomBlock()` fetches the manifest and forwards it with `connect`. The `notionCustomBlock()` Vite plugin from `@notionhq/custom-blocks/vite` serves it in dev and emits it into `dist/` on build. If the file is missing, the SDK omits `manifest` from `connect`. If the file is unavailable for another reason or invalid, the SDK sends `connect` with `status: "error"` and an `error` payload. The host returns its authoritative manifest in `init`. The SDK uses that manifest even when it differs from the manifest sent in `connect`.
22
22
 
23
23
  ## Vite plugin
24
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notionhq/custom-blocks",
3
- "version": "0.0.77",
3
+ "version": "0.0.79",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "publishConfig": {