@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.
- package/HOST.md +68 -34
- package/README.md +1 -1
- package/bin/src/bridge/manifest.js +3 -3
- package/dist/bridge/SandboxBridge.d.ts +8 -13
- package/dist/bridge/SandboxBridge.d.ts.map +1 -1
- package/dist/bridge/SandboxBridge.js +110 -39
- package/dist/bridge/dataSources/resolve.d.ts +3 -4
- package/dist/bridge/dataSources/resolve.d.ts.map +1 -1
- package/dist/bridge/dataSources/resolve.js +2 -12
- package/dist/bridge/hostState.d.ts +2 -0
- package/dist/bridge/hostState.d.ts.map +1 -1
- package/dist/bridge/loadManifest.d.ts +4 -4
- package/dist/bridge/loadManifest.d.ts.map +1 -1
- package/dist/bridge/loadManifest.js +2 -2
- package/dist/bridge/manifest.d.ts +3 -3
- package/dist/bridge/manifest.js +3 -3
- package/dist/bridge/messages/{ready.d.ts → connect.d.ts} +14 -12
- package/dist/bridge/messages/connect.d.ts.map +1 -0
- package/dist/bridge/messages/connect.js +38 -0
- package/dist/bridge/messages/hostToSandbox.d.ts +21 -0
- package/dist/bridge/messages/hostToSandbox.d.ts.map +1 -1
- package/dist/bridge/messages/init.d.ts +35 -6
- package/dist/bridge/messages/init.d.ts.map +1 -1
- package/dist/bridge/messages/init.js +8 -4
- package/dist/bridge/messages/initResult.d.ts +29 -0
- package/dist/bridge/messages/initResult.d.ts.map +1 -0
- package/dist/bridge/messages/initResult.js +25 -0
- package/dist/bridge/messages/sandboxToHost.d.ts +21 -6
- package/dist/bridge/messages/sandboxToHost.d.ts.map +1 -1
- package/dist/bridge/messages/sandboxToHost.js +4 -2
- package/dist/bridge/sandboxClient.d.ts +2 -4
- package/dist/bridge/sandboxClient.d.ts.map +1 -1
- package/dist/bridge/sandboxClient.js +2 -5
- package/dist/customBlock.d.ts +1 -1
- package/dist/customBlock.d.ts.map +1 -1
- package/dist/customBlock.js +1 -1
- package/dist/host/createCustomBlockHost.d.ts +7 -4
- package/dist/host/createCustomBlockHost.d.ts.map +1 -1
- package/dist/host/createCustomBlockHost.js +215 -38
- package/dist/host/lifecycle/initErrors.d.ts +1 -1
- package/dist/host/lifecycle/initErrors.d.ts.map +1 -1
- package/dist/host/lifecycle/initErrors.js +25 -7
- package/dist/host/lifecycle/{ready.d.ts → protocolVersion.d.ts} +1 -1
- package/dist/host/lifecycle/protocolVersion.d.ts.map +1 -0
- package/dist/host/lifecycle/types.d.ts +6 -4
- package/dist/host/lifecycle/types.d.ts.map +1 -1
- package/dist/host/messages/invalidSandboxMessage.js +0 -3
- package/dist/host.d.ts +5 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +2 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/init.d.ts +7 -7
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +70 -50
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/standalonePreview.d.ts.map +1 -1
- package/dist/react/standalonePreview.js +2 -0
- package/dist/react/useCustomBlockInit.d.ts +5 -6
- package/dist/react/useCustomBlockInit.d.ts.map +1 -1
- package/dist/react/useCustomBlockInit.js +3 -19
- package/dist/react/useRuntimeState.d.ts +4 -3
- package/dist/react/useRuntimeState.d.ts.map +1 -1
- package/dist/react/useRuntimeState.js +3 -2
- package/dist/version.js +1 -1
- package/docs/data-sources.md +10 -8
- package/docs/errors.md +5 -1
- package/docs/lifecycle.md +19 -31
- package/docs/manifest.md +1 -1
- package/package.json +1 -1
- package/src/bridge/SandboxBridge.ts +138 -44
- package/src/bridge/dataSources/resolve.ts +3 -14
- package/src/bridge/hostState.ts +2 -0
- package/src/bridge/loadManifest.ts +6 -6
- package/src/bridge/manifest.ts +3 -3
- package/src/bridge/messages/connect.ts +42 -0
- package/src/bridge/messages/init.ts +24 -10
- package/src/bridge/messages/initResult.ts +37 -0
- package/src/bridge/messages/sandboxToHost.ts +4 -2
- package/src/bridge/sandboxClient.ts +3 -8
- package/src/customBlock.ts +2 -2
- package/src/host/createCustomBlockHost.ts +265 -45
- package/src/host/lifecycle/initErrors.ts +25 -7
- package/src/host/lifecycle/types.ts +13 -4
- package/src/host/messages/invalidSandboxMessage.ts +0 -3
- package/src/host.ts +13 -2
- package/src/index.ts +3 -1
- package/src/init.ts +73 -60
- package/src/react/index.ts +0 -1
- package/src/react/standalonePreview.ts +2 -0
- package/src/react/useCustomBlockInit.ts +6 -27
- package/src/react/useRuntimeState.ts +4 -3
- package/dist/bridge/messages/ready.d.ts.map +0 -1
- package/dist/bridge/messages/ready.js +0 -43
- package/dist/host/lifecycle/ready.d.ts.map +0 -1
- package/src/bridge/messages/ready.ts +0 -48
- /package/dist/host/lifecycle/{ready.js → protocolVersion.js} +0 -0
- /package/src/host/lifecycle/{ready.ts → protocolVersion.ts} +0 -0
package/HOST.md
CHANGED
|
@@ -18,65 +18,98 @@ import {
|
|
|
18
18
|
|
|
19
19
|
`readIncomingType()` is intentionally best-effort. Use it after validation fails so the host can avoid NACK loops and include a useful reason in `invalidSandboxMessage`.
|
|
20
20
|
|
|
21
|
-
`createCustomBlockHost()` is an optional protocol loop for lightweight hosts. It owns iframe message listener setup, `
|
|
21
|
+
`createCustomBlockHost()` is an optional protocol loop for lightweight hosts. It owns iframe message listener setup, `connect` / `init` / `initResult`, protocol version checks, malformed-message NACKs, request routing, auto-resize callbacks, state-change messages, cleanup, and latest-query refresh bookkeeping. It deliberately does not know about any host's data model, permissions, iframe policy, analytics, or UI state.
|
|
22
22
|
|
|
23
23
|
```ts
|
|
24
24
|
import { createCustomBlockHost } from "@notionhq/custom-blocks/host";
|
|
25
25
|
|
|
26
26
|
const host = createCustomBlockHost({
|
|
27
27
|
iframe,
|
|
28
|
+
minBridgeProtocolVersion: 3,
|
|
28
29
|
initialState: {
|
|
29
30
|
theme,
|
|
30
31
|
blockId,
|
|
31
32
|
parent,
|
|
32
33
|
page,
|
|
33
34
|
currentUser,
|
|
35
|
+
manifest: persistedManifest,
|
|
34
36
|
dataSources: { bindings },
|
|
35
37
|
},
|
|
36
38
|
handlers: {
|
|
37
|
-
queryDataSource: async (message) => ({
|
|
39
|
+
queryDataSource: async (message) => ({
|
|
40
|
+
status: "success",
|
|
41
|
+
items: [],
|
|
42
|
+
hasMore: false,
|
|
43
|
+
}),
|
|
38
44
|
},
|
|
39
45
|
});
|
|
40
46
|
```
|
|
41
47
|
|
|
42
48
|
Use it when the generic protocol loop is helpful. Hosts with more specialized runtime needs can keep using the schemas and message types directly.
|
|
43
49
|
|
|
44
|
-
|
|
50
|
+
Responses return a `status`-discriminated result: `"success"` carries `items` and required `hasMore`; `"error"` carries the structured `error`. Set `hasMore` to `false` when no additional results are available.
|
|
51
|
+
|
|
52
|
+
Related exports: `CustomBlockHostOptions`, `CustomBlockHostInitialState`, `CustomBlockHostInitialDataSources`, `CustomBlockHostDataSourcesPayload`, `ConnectSuccessMessage`, `CustomBlockHostHandlers`, `CustomBlockHostHandle`, `CustomBlockHostLogDirection`, `CustomBlockHostQueryDataSourceResult`, `CustomBlockHostCreatePageResult`, `CustomBlockHostGetPageResult`, `CustomBlockHostUpdatePageResult`, `CustomBlockHostGetUserResult`, and `CustomBlockHostListUsersResult`.
|
|
45
53
|
|
|
46
54
|
## Bridge protocol
|
|
47
55
|
|
|
48
56
|
### Lifecycle
|
|
49
57
|
|
|
50
|
-
|
|
58
|
+
Initialization has three steps. A sandbox-generated `initializationId` identifies the handshake from start to finish. Ignore duplicate messages and messages carrying any other ID.
|
|
51
59
|
|
|
52
|
-
|
|
60
|
+
#### 1. Sandbox sends `connect`
|
|
53
61
|
|
|
54
|
-
|
|
55
|
-
// sandbox → host
|
|
56
|
-
{ type: "ready", status: "success", bridgeProtocolVersion, sdkVersion, manifest: { /* custom_blocks.json */ } }
|
|
62
|
+
`connect` declares the bridge protocol version and SDK package version. A successful message may also include the manifest discovered in the sandbox:
|
|
57
63
|
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
```ts
|
|
65
|
+
// sandbox → host without manifest
|
|
66
|
+
{ type: "connect", initializationId, status: "success", bridgeProtocolVersion, sdkVersion }
|
|
60
67
|
|
|
61
|
-
// sandbox → host
|
|
62
|
-
{ type: "
|
|
68
|
+
// sandbox → host with explicit manifest
|
|
69
|
+
{ type: "connect", initializationId, status: "success", bridgeProtocolVersion, sdkVersion, manifest: { /* ... */ } }
|
|
63
70
|
```
|
|
64
71
|
|
|
65
|
-
|
|
72
|
+
If `connect.status` is `"error"`, reply with `init.error` using the same `initializationId` and forward its complete error payload unchanged.
|
|
73
|
+
|
|
74
|
+
#### 2. Host sends `init`
|
|
75
|
+
|
|
76
|
+
The host sends exactly one `init` containing its authoritative manifest. It may
|
|
77
|
+
use the manifest from `connect` as input, but the manifest it returns may differ;
|
|
78
|
+
the sandbox applies the host's version.
|
|
79
|
+
|
|
80
|
+
`init.success` carries the chosen manifest together with the initial theme, block context, current user, and data-source bindings:
|
|
66
81
|
|
|
67
82
|
```ts
|
|
68
83
|
// host → sandbox
|
|
69
|
-
{ type: "init", status: "success", theme, blockId, parent, page: { id, parent }, currentUser, dataSources: { bindings } }
|
|
84
|
+
{ type: "init", initializationId, status: "success", theme, blockId, parent, page: { id, parent }, currentUser, manifest, dataSources: { bindings } }
|
|
70
85
|
```
|
|
71
86
|
|
|
72
|
-
|
|
87
|
+
If the host cannot initialize the block, send `init.error` instead:
|
|
73
88
|
|
|
74
89
|
```ts
|
|
75
90
|
// host → sandbox
|
|
76
|
-
{ type: "init", status: "error", error: { code: "context_unavailable", message: "...", isRetryable: true } }
|
|
91
|
+
{ type: "init", initializationId, status: "error", error: { code: "context_unavailable", message: "...", isRetryable: true } }
|
|
77
92
|
```
|
|
78
93
|
|
|
79
|
-
|
|
94
|
+
Use `CustomBlockInitErrorCode` / `CustomBlockInitErrorInfo` to construct this error and `customBlockInitErrorCodeSchema` / `customBlockInitErrorInfoSchema` to validate it. `init.error` is terminal; the sandbox does not reply with `initResult`.
|
|
95
|
+
|
|
96
|
+
#### 3. Sandbox sends `initResult`
|
|
97
|
+
|
|
98
|
+
After applying `init.success`, the sandbox acknowledges whether it accepted the manifest and bindings:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
// sandbox → host
|
|
102
|
+
{ type: "initResult", initializationId, status: "success" }
|
|
103
|
+
|
|
104
|
+
// sandbox → host, host supplied bindings the SDK could not apply
|
|
105
|
+
{ type: "initResult", initializationId, status: "error", error: { code: "invalid_init_bindings", message: "...", isRetryable: false } }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Use `CustomBlockInitResultErrorCode` / `CustomBlockInitResultErrorInfo` to construct this error and `customBlockInitResultErrorCodeSchema` / `customBlockInitResultErrorInfoSchema` to validate it.
|
|
109
|
+
|
|
110
|
+
Wait for `initResult.success` before uncovering the iframe or sending live updates. `initResult.error` is terminal.
|
|
111
|
+
|
|
112
|
+
After `initResult.success`, narrower messages update live state without re-running the handshake — `themeChanged`, `parentChanged`, `pageChanged`, `currentUserChanged`, and `dataSourcesChanged`. Each replaces just its slice of state:
|
|
80
113
|
|
|
81
114
|
```ts
|
|
82
115
|
// host → sandbox, any time after init
|
|
@@ -88,7 +121,7 @@ After `init`, narrower messages update live state without re-running the handsha
|
|
|
88
121
|
|
|
89
122
|
### Versioning
|
|
90
123
|
|
|
91
|
-
`
|
|
124
|
+
`connect` includes `bridgeProtocolVersion` (the host <-> sandbox version) and `sdkVersion` (the package semver). Hosts should reject or fail `init` when the sandbox reports a `bridgeProtocolVersion` the host does not implement, but should treat `sdkVersion` as purely analytics metadata.
|
|
92
125
|
|
|
93
126
|
If the sandbox reports a version below the host's supported minimum, reply with `init.status: "error"` and `error.code: "unsupported_protocol_version"`. If the reported version is structurally invalid (not a positive integer — e.g. `0`, negative, fractional, `NaN`, or `Infinity`), reply with `error.code: "invalid_protocol_version"` instead.
|
|
94
127
|
|
|
@@ -142,9 +175,10 @@ Messages sent from the sandbox to the host. Parse `window` `message` events with
|
|
|
142
175
|
|
|
143
176
|
| Wire type | Type / schema | Behavior |
|
|
144
177
|
| -------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
145
|
-
| `
|
|
178
|
+
| `connect` | `ConnectMessage` / `connectMessageSchema` | Starts the one-shot handshake with a sandbox-generated `initializationId`; carries `bridgeProtocolVersion`, `sdkVersion`, and the manifest. |
|
|
179
|
+
| `initResult` | `InitResultMessage` / `initResultMessageSchema` | Echoes the handshake `initializationId` and reports whether the sandbox applied `init.success`. |
|
|
146
180
|
| `queryDataSource` | `QueryDataSourceMessage` / `queryDataSourceMessageSchema` | Starts or updates a `subscriptionId`-keyed subscription for rows in a raw `dataSourceId`. Later results for the same subscription replace its current value. |
|
|
147
|
-
| `createPage` | `CreatePageMessage` / `createPageMessageSchema` | `requestId`-keyed page creation with properties and optional position; parent is `page_id` or `data_source_id`. Create-time icon and cover are not supported.
|
|
181
|
+
| `createPage` | `CreatePageMessage` / `createPageMessageSchema` | `requestId`-keyed page creation with properties and optional position; parent is `page_id` or `data_source_id`. Create-time icon and cover are not supported. |
|
|
148
182
|
| `getPage` | `GetPageMessage` / `getPageMessageSchema` | `requestId`-keyed page fetch by page id. |
|
|
149
183
|
| `updatePage` | `UpdatePageMessage` / `updatePageMessageSchema` | `requestId`-keyed patch (properties, icon, cover, or `archived`). |
|
|
150
184
|
| `getUser` | `GetUserMessage` / `getUserMessageSchema` | `requestId`-keyed user fetch by user id. |
|
|
@@ -158,20 +192,20 @@ Messages sent from the sandbox to the host. Parse `window` `message` events with
|
|
|
158
192
|
|
|
159
193
|
Messages sent from the host to the sandbox. Same `{ wire type, type / schema, behavior }` shape; the type / schema column points to the payload shape.
|
|
160
194
|
|
|
161
|
-
| Wire type | Type / schema | Behavior
|
|
162
|
-
| ----------------------- | --------------------------------------------------------------------- |
|
|
163
|
-
| `init` | `InitMessage` / `initMessageSchema` | Sent exactly once in response to `
|
|
164
|
-
| `themeChanged` | `ThemeChangedMessage` / `themeChangedMessageSchema` | Replaces the current theme.
|
|
165
|
-
| `parentChanged` | `ParentChangedMessage` / `parentChangedMessageSchema` | Replaces the custom block parent without disturbing theme, block ID, page ID, user, or query state.
|
|
166
|
-
| `pageChanged` | `PageChangedMessage` / `pageChangedMessageSchema` | Replaces the nearest page ancestor without disturbing theme, block ID, parent, user, or query state.
|
|
167
|
-
| `currentUserChanged` | `CurrentUserChangedMessage` / `currentUserChangedMessageSchema` | Replaces the current viewer record. Send when any viewer field changes (name, avatar, email).
|
|
168
|
-
| `dataSourcesChanged` | `DataSourcesChangedMessage` / `dataSourcesChangedMessageSchema` | Replaces data-source bindings; the sandbox preserves cached query state for keys that still exist and drops removed keys.
|
|
169
|
-
| `queryDataSourceResult` | `QueryDataSourceResultMessage` / `queryDataSourceResultMessageSchema` | `subscriptionId`-keyed update with `status: "success"`, `items`, and `hasMore`, or `status: "error"` and `error`.
|
|
170
|
-
| `createPageResult` | `CreatePageResultMessage` / `createPageResultMessageSchema` | `requestId`-keyed page response with `status: "success"` or `status: "error"`.
|
|
171
|
-
| `getPageResult` | `GetPageResultMessage` / `getPageResultMessageSchema` | Same success/error shape as `createPageResult`.
|
|
172
|
-
| `updatePageResult` | `UpdatePageResultMessage` / `updatePageResultMessageSchema` | Same success/error shape as `createPageResult`.
|
|
173
|
-
| `getUserResult` | `GetUserResultMessage` / `getUserResultMessageSchema` | `requestId`-keyed user response with `status: "success"` or `status: "error"`.
|
|
174
|
-
| `listUsersResult` | `ListUsersResultMessage` / `listUsersResultMessageSchema` | Same success/error shape as `getUserResult`.
|
|
195
|
+
| Wire type | Type / schema | Behavior |
|
|
196
|
+
| ----------------------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
197
|
+
| `init` | `InitMessage` / `initMessageSchema` | Sent exactly once in response to `connect`; echoes its `initializationId` and carries the success/error payload described under [Lifecycle](#lifecycle). |
|
|
198
|
+
| `themeChanged` | `ThemeChangedMessage` / `themeChangedMessageSchema` | Replaces the current theme. |
|
|
199
|
+
| `parentChanged` | `ParentChangedMessage` / `parentChangedMessageSchema` | Replaces the custom block parent without disturbing theme, block ID, page ID, user, or query state. |
|
|
200
|
+
| `pageChanged` | `PageChangedMessage` / `pageChangedMessageSchema` | Replaces the nearest page ancestor without disturbing theme, block ID, parent, user, or query state. |
|
|
201
|
+
| `currentUserChanged` | `CurrentUserChangedMessage` / `currentUserChangedMessageSchema` | Replaces the current viewer record. Send when any viewer field changes (name, avatar, email). |
|
|
202
|
+
| `dataSourcesChanged` | `DataSourcesChangedMessage` / `dataSourcesChangedMessageSchema` | Replaces data-source bindings; the sandbox preserves cached query state for keys that still exist and drops removed keys. |
|
|
203
|
+
| `queryDataSourceResult` | `QueryDataSourceResultMessage` / `queryDataSourceResultMessageSchema` | `subscriptionId`-keyed update with `status: "success"`, `items`, and `hasMore`, or `status: "error"` and `error`. |
|
|
204
|
+
| `createPageResult` | `CreatePageResultMessage` / `createPageResultMessageSchema` | `requestId`-keyed page response with `status: "success"` or `status: "error"`. |
|
|
205
|
+
| `getPageResult` | `GetPageResultMessage` / `getPageResultMessageSchema` | Same success/error shape as `createPageResult`. |
|
|
206
|
+
| `updatePageResult` | `UpdatePageResultMessage` / `updatePageResultMessageSchema` | Same success/error shape as `createPageResult`. |
|
|
207
|
+
| `getUserResult` | `GetUserResultMessage` / `getUserResultMessageSchema` | `requestId`-keyed user response with `status: "success"` or `status: "error"`. |
|
|
208
|
+
| `listUsersResult` | `ListUsersResultMessage` / `listUsersResultMessageSchema` | Same success/error shape as `getUserResult`. |
|
|
175
209
|
| `invalidSandboxMessage` | `InvalidSandboxMessage` / `invalidSandboxMessageSchema` | Host-side NACK for a sandbox message it could not parse. |
|
|
176
210
|
|
|
177
211
|
`HostToSandboxMessage` / `hostToSandboxMessageSchema` is the discriminated union over all of the above.
|
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ export function App() {
|
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
`<NotionCustomBlock>` runs the SDK ↔ host handshake (
|
|
38
|
+
`<NotionCustomBlock>` runs the SDK ↔ host handshake (`connect` → `init` → `initResult`) and only mounts `children` once it resolves. Inside the wrapper, every hook returns non-nullable values — there's no separate gating component to write. It also runs `useCustomBlockAutoResize` for you by default; pass `autoResize={false}` to opt out.
|
|
39
39
|
|
|
40
40
|
## Reference
|
|
41
41
|
|
|
@@ -2,9 +2,9 @@ import * as v from "valibot";
|
|
|
2
2
|
import { notionPropertyTypeSchema } from "./dataSources/propertySchema.js";
|
|
3
3
|
/**
|
|
4
4
|
* User-authored manifest declaring the data sources the custom block expects.
|
|
5
|
-
* Lives at `custom_blocks.json` in the project root
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Lives at `custom_blocks.json` in the project root. The sandbox may send it in
|
|
6
|
+
* `connect`, and the host returns the authoritative manifest in `init`. The
|
|
7
|
+
* `notionCustomBlock` Vite plugin from
|
|
8
8
|
* `@notionhq/custom-blocks/vite` wires the JSON file into the dev server and
|
|
9
9
|
* the build output.
|
|
10
10
|
*/
|
|
@@ -3,14 +3,13 @@ import type { NotionDataSource } from "./dataSources/dataSource.js";
|
|
|
3
3
|
import type { NotionDataSourcePageUpdateInput, NotionDataSourcePageUpdateResult } from "./dataSources/dataSourcePage.js";
|
|
4
4
|
import { type CustomBlockHostState } from "./hostState.js";
|
|
5
5
|
import type { ManifestLoadResult } from "./loadManifest.js";
|
|
6
|
-
import type { CustomBlockManifest } from "./manifest.js";
|
|
7
6
|
import { type InitMessage } from "./messages/init.js";
|
|
8
7
|
/**
|
|
9
8
|
* Used to ensure that the host and client are using the same version of the bridge protocol. A
|
|
10
9
|
* single host needs to support multiple custom blocks built with different versions of the bridge
|
|
11
10
|
* protocol. Increment this number any time a breaking change is made to the bridge protocol.
|
|
12
11
|
*/
|
|
13
|
-
export declare const CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION =
|
|
12
|
+
export declare const CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION = 3;
|
|
14
13
|
/**
|
|
15
14
|
* A single entry in the bridge message log. Kept intentionally plain so the log
|
|
16
15
|
* is copy-pasteable to a local coding agent without needing extra context.
|
|
@@ -25,36 +24,32 @@ export declare class SandboxBridge {
|
|
|
25
24
|
private listeners;
|
|
26
25
|
private messageLog;
|
|
27
26
|
private messageLogListeners;
|
|
27
|
+
private nextRequestId;
|
|
28
28
|
private readonly pendingCreatePage;
|
|
29
29
|
private readonly pendingGetPage;
|
|
30
30
|
private readonly pendingGetUser;
|
|
31
31
|
private readonly pendingListUsers;
|
|
32
32
|
private readonly pendingUpdatePage;
|
|
33
|
-
private
|
|
33
|
+
private hasSentConnect;
|
|
34
|
+
private hasReceivedInit;
|
|
35
|
+
private initializationId;
|
|
34
36
|
private latestDataSourceBindings;
|
|
37
|
+
private isMockState;
|
|
35
38
|
private resolveInit;
|
|
36
39
|
private rejectInit;
|
|
37
40
|
private readonly initMessage;
|
|
38
|
-
private manifest;
|
|
39
41
|
constructor();
|
|
40
42
|
private static MAX_LOG_ENTRIES;
|
|
41
43
|
private logMessage;
|
|
42
44
|
getMessageLog(): readonly MessageLogEntry[];
|
|
43
45
|
subscribeToMessageLog(listener: () => void): () => boolean;
|
|
44
|
-
awaitInit(signal?: AbortSignal): Promise<
|
|
45
|
-
|
|
46
|
+
awaitInit(signal?: AbortSignal): Promise<void>;
|
|
47
|
+
sendConnect(manifestResult: ManifestLoadResult): void;
|
|
46
48
|
private postToHost;
|
|
47
49
|
private notify;
|
|
48
50
|
private handleMessage;
|
|
49
51
|
subscribe(listener: () => void): () => boolean;
|
|
50
52
|
getHostState(): CustomBlockHostState;
|
|
51
|
-
/**
|
|
52
|
-
* The author-declared manifest loaded from `custom_blocks.json` and forwarded
|
|
53
|
-
* to the host in `ready`. `null` when it failed to load/parse and the host
|
|
54
|
-
* should reject init via `ready.status: "error"`. Static for the lifetime
|
|
55
|
-
* of the sandbox.
|
|
56
|
-
*/
|
|
57
|
-
getManifest(): CustomBlockManifest | null;
|
|
58
53
|
/**
|
|
59
54
|
* Apply an `init` payload as if it had arrived from the host. Lets callers
|
|
60
55
|
* seed the bridge directly (e.g. the React provider's standalone preview
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SandboxBridge.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/bridge/SandboxBridge.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,MAAM,aAAa,CAAA;AAGpB,OAAO,KAAK,EACX,gBAAgB,EAEhB,MAAM,6BAA6B,CAAA;AACpC,OAAO,KAAK,EACX,+BAA+B,EAC/B,gCAAgC,EAChC,MAAM,iCAAiC,CAAA;AAGxC,OAAO,EACN,KAAK,oBAAoB,EAGzB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"SandboxBridge.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/bridge/SandboxBridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,14 +6,14 @@ import { resolvePropertyWriteMapForDataSource } from "./dataSources/resolvePrope
|
|
|
6
6
|
import { createEmptyDataSourceQueryState, } from "./hostState.js";
|
|
7
7
|
import { readIncomingType } from "./incomingType.js";
|
|
8
8
|
import { hostToSandboxMessageSchema } from "./messages/hostToSandbox.js";
|
|
9
|
-
import {
|
|
9
|
+
import { CustomBlockInitializationError, initMessageSchema, } from "./messages/init.js";
|
|
10
10
|
import { PendingRequests } from "./pendingRequests.js";
|
|
11
11
|
/**
|
|
12
12
|
* Used to ensure that the host and client are using the same version of the bridge protocol. A
|
|
13
13
|
* single host needs to support multiple custom blocks built with different versions of the bridge
|
|
14
14
|
* protocol. Increment this number any time a breaking change is made to the bridge protocol.
|
|
15
15
|
*/
|
|
16
|
-
export const CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION =
|
|
16
|
+
export const CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION = 3;
|
|
17
17
|
export class SandboxBridge {
|
|
18
18
|
constructor() {
|
|
19
19
|
this.hostState = {
|
|
@@ -23,18 +23,20 @@ export class SandboxBridge {
|
|
|
23
23
|
this.listeners = new Set();
|
|
24
24
|
this.messageLog = [];
|
|
25
25
|
this.messageLogListeners = new Set();
|
|
26
|
+
this.nextRequestId = 1;
|
|
26
27
|
this.pendingCreatePage = new PendingRequests("custom-block-create-page");
|
|
27
28
|
this.pendingGetPage = new PendingRequests("custom-block-get-page");
|
|
28
29
|
this.pendingGetUser = new PendingRequests("custom-block-get-user");
|
|
29
30
|
this.pendingListUsers = new PendingRequests("custom-block-list-users");
|
|
30
31
|
this.pendingUpdatePage = new PendingRequests("custom-block-update-page");
|
|
31
|
-
this.
|
|
32
|
+
this.hasSentConnect = false;
|
|
33
|
+
this.hasReceivedInit = false;
|
|
32
34
|
this.latestDataSourceBindings = {};
|
|
35
|
+
this.isMockState = false;
|
|
33
36
|
this.initMessage = new Promise((resolve, reject) => {
|
|
34
37
|
this.resolveInit = resolve;
|
|
35
38
|
this.rejectInit = reject;
|
|
36
39
|
});
|
|
37
|
-
this.manifest = null;
|
|
38
40
|
this.notify = () => {
|
|
39
41
|
for (const listener of this.listeners) {
|
|
40
42
|
listener();
|
|
@@ -79,7 +81,16 @@ export class SandboxBridge {
|
|
|
79
81
|
// `init` is the only message valid before initialization. Handle it up
|
|
80
82
|
// front so every later case can assume `status === "initialized"`.
|
|
81
83
|
if (message.type === "init") {
|
|
82
|
-
this.
|
|
84
|
+
if (message.initializationId !== this.initializationId) {
|
|
85
|
+
console.warn(`[custom-blocks-sdk] ignoring init for unknown initializationId ${message.initializationId}`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (this.hasReceivedInit) {
|
|
89
|
+
console.warn("[custom-blocks-sdk] ignoring duplicate init message");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this.hasReceivedInit = true;
|
|
93
|
+
this.applyInit(message, true);
|
|
83
94
|
return;
|
|
84
95
|
}
|
|
85
96
|
// Alias to keep TS's narrowed `InitializedHostState` across the switch
|
|
@@ -127,10 +138,12 @@ export class SandboxBridge {
|
|
|
127
138
|
const dataSources = reuseDataSourcesForUnchangedBindings({
|
|
128
139
|
previousDataSources: hostState.dataSources,
|
|
129
140
|
previousBindings: this.latestDataSourceBindings,
|
|
130
|
-
nextDataSources:
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
141
|
+
nextDataSources: this.isMockState
|
|
142
|
+
? resolveMockDataSources(nextBindings)
|
|
143
|
+
: resolveDataSources({
|
|
144
|
+
manifest: hostState.manifest,
|
|
145
|
+
dataSourceBindings: nextBindings,
|
|
146
|
+
}),
|
|
134
147
|
nextBindings,
|
|
135
148
|
});
|
|
136
149
|
this.latestDataSourceBindings = nextBindings;
|
|
@@ -234,7 +247,7 @@ export class SandboxBridge {
|
|
|
234
247
|
}
|
|
235
248
|
}
|
|
236
249
|
};
|
|
237
|
-
// `
|
|
250
|
+
// `connect` is sent later by `initCustomBlock` (after the manifest fetch
|
|
238
251
|
// resolves). Top-level / no-iframe rejection is handled there too, so
|
|
239
252
|
// the constructor just attaches the listener.
|
|
240
253
|
if (typeof window !== "undefined") {
|
|
@@ -272,42 +285,46 @@ export class SandboxBridge {
|
|
|
272
285
|
}
|
|
273
286
|
const onAbort = () => reject(signal.reason);
|
|
274
287
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
275
|
-
this.initMessage.then(
|
|
288
|
+
this.initMessage.then(() => {
|
|
276
289
|
signal.removeEventListener("abort", onAbort);
|
|
277
|
-
resolve(
|
|
290
|
+
resolve();
|
|
278
291
|
}, err => {
|
|
279
292
|
signal.removeEventListener("abort", onAbort);
|
|
280
293
|
reject(err);
|
|
281
294
|
});
|
|
282
295
|
});
|
|
283
296
|
}
|
|
284
|
-
|
|
297
|
+
sendConnect(manifestResult) {
|
|
285
298
|
if (typeof window === "undefined") {
|
|
286
299
|
return;
|
|
287
300
|
}
|
|
288
|
-
if (this.
|
|
289
|
-
console.warn("[custom-blocks-sdk] ignoring duplicate
|
|
301
|
+
if (this.hasSentConnect) {
|
|
302
|
+
console.warn("[custom-blocks-sdk] ignoring duplicate connect message");
|
|
290
303
|
return;
|
|
291
304
|
}
|
|
292
305
|
const { manifest, error } = manifestResult;
|
|
293
|
-
this.
|
|
294
|
-
|
|
295
|
-
|
|
306
|
+
this.hasSentConnect = true;
|
|
307
|
+
const initializationId = `custom-block-initialization-${this.nextRequestId}`;
|
|
308
|
+
this.nextRequestId += 1;
|
|
309
|
+
this.initializationId = initializationId;
|
|
310
|
+
const connectMessage = error !== undefined
|
|
296
311
|
? {
|
|
297
|
-
type: "
|
|
312
|
+
type: "connect",
|
|
313
|
+
initializationId,
|
|
298
314
|
status: "error",
|
|
299
315
|
bridgeProtocolVersion: CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION,
|
|
300
316
|
sdkVersion: CUSTOM_BLOCKS_SDK_VERSION,
|
|
301
317
|
error,
|
|
302
318
|
}
|
|
303
319
|
: {
|
|
304
|
-
type: "
|
|
320
|
+
type: "connect",
|
|
321
|
+
initializationId,
|
|
305
322
|
status: "success",
|
|
306
323
|
bridgeProtocolVersion: CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION,
|
|
307
324
|
sdkVersion: CUSTOM_BLOCKS_SDK_VERSION,
|
|
308
|
-
manifest,
|
|
325
|
+
...(manifest !== null ? { manifest } : {}),
|
|
309
326
|
};
|
|
310
|
-
this.postToHost(
|
|
327
|
+
this.postToHost(connectMessage);
|
|
311
328
|
}
|
|
312
329
|
postToHost(message) {
|
|
313
330
|
console.debug("[custom-blocks-sdk] outbound postMessage", message);
|
|
@@ -321,15 +338,6 @@ export class SandboxBridge {
|
|
|
321
338
|
getHostState() {
|
|
322
339
|
return this.hostState;
|
|
323
340
|
}
|
|
324
|
-
/**
|
|
325
|
-
* The author-declared manifest loaded from `custom_blocks.json` and forwarded
|
|
326
|
-
* to the host in `ready`. `null` when it failed to load/parse and the host
|
|
327
|
-
* should reject init via `ready.status: "error"`. Static for the lifetime
|
|
328
|
-
* of the sandbox.
|
|
329
|
-
*/
|
|
330
|
-
getManifest() {
|
|
331
|
-
return this.manifest;
|
|
332
|
-
}
|
|
333
341
|
/**
|
|
334
342
|
* Apply an `init` payload as if it had arrived from the host. Lets callers
|
|
335
343
|
* seed the bridge directly (e.g. the React provider's standalone preview
|
|
@@ -337,9 +345,13 @@ export class SandboxBridge {
|
|
|
337
345
|
* of why it's being seeded.
|
|
338
346
|
*/
|
|
339
347
|
setMockState(message) {
|
|
340
|
-
this.
|
|
348
|
+
this.isMockState = true;
|
|
349
|
+
this.applyInit(v.parse(initMessageSchema, message), false);
|
|
341
350
|
}
|
|
342
|
-
applyInit(message) {
|
|
351
|
+
applyInit(message, postResult) {
|
|
352
|
+
if (postResult) {
|
|
353
|
+
this.isMockState = false;
|
|
354
|
+
}
|
|
343
355
|
if (message.status === "error") {
|
|
344
356
|
// The host couldn't construct block location for this block (most commonly the parent record
|
|
345
357
|
// failed to resolve). Surface the failure through the `awaitInit` promise so callers see
|
|
@@ -349,7 +361,7 @@ export class SandboxBridge {
|
|
|
349
361
|
// back over the bridge since the host has already given up.
|
|
350
362
|
console.error(`[custom-blocks-sdk] host reported init error (${message.error.code}): ${message.error.message}`);
|
|
351
363
|
if (this.rejectInit) {
|
|
352
|
-
this.rejectInit(new
|
|
364
|
+
this.rejectInit(new CustomBlockInitializationError(message.error));
|
|
353
365
|
this.resolveInit = undefined;
|
|
354
366
|
this.rejectInit = undefined;
|
|
355
367
|
}
|
|
@@ -357,10 +369,28 @@ export class SandboxBridge {
|
|
|
357
369
|
}
|
|
358
370
|
const { blockId, parent, page } = message;
|
|
359
371
|
this.latestDataSourceBindings = message.dataSources.bindings;
|
|
360
|
-
const dataSources =
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
372
|
+
const dataSources = postResult
|
|
373
|
+
? resolveDataSources({
|
|
374
|
+
manifest: message.manifest,
|
|
375
|
+
dataSourceBindings: this.latestDataSourceBindings,
|
|
376
|
+
})
|
|
377
|
+
: resolveMockDataSources(this.latestDataSourceBindings);
|
|
378
|
+
const bindingError = getInitBindingError(message.manifest, dataSources);
|
|
379
|
+
if (postResult && bindingError !== undefined) {
|
|
380
|
+
const result = {
|
|
381
|
+
type: "initResult",
|
|
382
|
+
initializationId: message.initializationId,
|
|
383
|
+
status: "error",
|
|
384
|
+
error: bindingError,
|
|
385
|
+
};
|
|
386
|
+
this.postToHost(result);
|
|
387
|
+
if (this.rejectInit) {
|
|
388
|
+
this.rejectInit(new CustomBlockInitializationError(bindingError));
|
|
389
|
+
this.resolveInit = undefined;
|
|
390
|
+
this.rejectInit = undefined;
|
|
391
|
+
}
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
364
394
|
this.hostState = {
|
|
365
395
|
status: "initialized",
|
|
366
396
|
theme: message.theme,
|
|
@@ -368,15 +398,24 @@ export class SandboxBridge {
|
|
|
368
398
|
parent,
|
|
369
399
|
page,
|
|
370
400
|
currentUser: message.currentUser,
|
|
401
|
+
manifest: message.manifest,
|
|
371
402
|
dataSources,
|
|
372
403
|
dataSourceState: {},
|
|
373
404
|
};
|
|
374
405
|
this.notify();
|
|
406
|
+
if (postResult) {
|
|
407
|
+
const result = {
|
|
408
|
+
type: "initResult",
|
|
409
|
+
initializationId: message.initializationId,
|
|
410
|
+
status: "success",
|
|
411
|
+
};
|
|
412
|
+
this.postToHost(result);
|
|
413
|
+
}
|
|
375
414
|
// Resolve the awaitInit promise once. Subsequent `init` messages
|
|
376
415
|
// (the host shouldn't send these, but be tolerant) update state but
|
|
377
416
|
// don't re-resolve.
|
|
378
417
|
if (this.resolveInit) {
|
|
379
|
-
this.resolveInit(
|
|
418
|
+
this.resolveInit();
|
|
380
419
|
this.resolveInit = undefined;
|
|
381
420
|
this.rejectInit = undefined;
|
|
382
421
|
}
|
|
@@ -693,6 +732,38 @@ function makeDataSourceSubscriptionId(args) {
|
|
|
693
732
|
const { key } = args;
|
|
694
733
|
return `data-source:${encodeURIComponent(key)}`;
|
|
695
734
|
}
|
|
735
|
+
function getInitBindingError(manifest, dataSources) {
|
|
736
|
+
for (const [dataSourceKey, manifestDataSource] of Object.entries(manifest.dataSources)) {
|
|
737
|
+
const dataSource = dataSources.find(entry => entry.key === dataSourceKey);
|
|
738
|
+
if (dataSource?.collectionPointer === undefined ||
|
|
739
|
+
dataSource.collectionSchema === undefined) {
|
|
740
|
+
return {
|
|
741
|
+
code: "invalid_init_bindings",
|
|
742
|
+
message: `Host did not provide a complete binding for data source "${dataSourceKey}".`,
|
|
743
|
+
isRetryable: false,
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
for (const propertyKey of Object.keys(manifestDataSource.properties ?? {})) {
|
|
747
|
+
if (dataSource.propertyIdsByKey[propertyKey] === undefined) {
|
|
748
|
+
return {
|
|
749
|
+
code: "invalid_init_bindings",
|
|
750
|
+
message: `Host did not provide a valid binding for property "${dataSourceKey}.${propertyKey}".`,
|
|
751
|
+
isRetryable: false,
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return undefined;
|
|
757
|
+
}
|
|
758
|
+
function resolveMockDataSources(bindings) {
|
|
759
|
+
return Object.entries(bindings).map(([key, binding]) => ({
|
|
760
|
+
key,
|
|
761
|
+
collectionPointer: binding.collectionPointer,
|
|
762
|
+
collectionSchema: binding.collectionSchema,
|
|
763
|
+
propertyIdsByKey: { ...(binding.propertyIdsByKey ?? {}) },
|
|
764
|
+
propertySchemasById: binding.collectionSchema?.propertiesById ?? {},
|
|
765
|
+
}));
|
|
766
|
+
}
|
|
696
767
|
function formatInvalidHostReason(incomingType, issues) {
|
|
697
768
|
const labelled = incomingType
|
|
698
769
|
? `host message of type "${incomingType}"`
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import type { CustomBlockManifest } from "../manifest.js";
|
|
2
2
|
import type { NotionDataSource, NotionDataSourceBindings } from "./dataSource.js";
|
|
3
3
|
type ResolveDataSourcesArgs = {
|
|
4
|
-
manifest: CustomBlockManifest
|
|
4
|
+
manifest: CustomBlockManifest;
|
|
5
5
|
dataSourceBindings: NotionDataSourceBindings;
|
|
6
6
|
};
|
|
7
7
|
/**
|
|
8
8
|
* Builds the public {@link NotionDataSource} list the SDK exposes to consumers.
|
|
9
9
|
*
|
|
10
10
|
* Combines the host-supplied bindings (collection pointers + schemas) with the
|
|
11
|
-
* manifest's declared data-source keys.
|
|
12
|
-
*
|
|
13
|
-
* but does not infer a missing mapping.
|
|
11
|
+
* manifest's declared data-source keys. The host owns every mapping; the SDK
|
|
12
|
+
* validates supplied IDs but never infers omitted bindings.
|
|
14
13
|
*/
|
|
15
14
|
export declare function resolveDataSources(args: ResolveDataSourcesArgs): NotionDataSource[];
|
|
16
15
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/bridge/dataSources/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACzD,OAAO,KAAK,EACX,gBAAgB,EAChB,wBAAwB,EACxB,MAAM,iBAAiB,CAAA;AAExB,KAAK,sBAAsB,GAAG;IAC7B,QAAQ,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/bridge/dataSources/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACzD,OAAO,KAAK,EACX,gBAAgB,EAChB,wBAAwB,EACxB,MAAM,iBAAiB,CAAA;AAExB,KAAK,sBAAsB,GAAG;IAC7B,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,kBAAkB,EAAE,wBAAwB,CAAA;CAC5C,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,sBAAsB,GAC1B,gBAAgB,EAAE,CA+BpB"}
|
|
@@ -2,20 +2,10 @@
|
|
|
2
2
|
* Builds the public {@link NotionDataSource} list the SDK exposes to consumers.
|
|
3
3
|
*
|
|
4
4
|
* Combines the host-supplied bindings (collection pointers + schemas) with the
|
|
5
|
-
* manifest's declared data-source keys.
|
|
6
|
-
*
|
|
7
|
-
* but does not infer a missing mapping.
|
|
5
|
+
* manifest's declared data-source keys. The host owns every mapping; the SDK
|
|
6
|
+
* validates supplied IDs but never infers omitted bindings.
|
|
8
7
|
*/
|
|
9
8
|
export function resolveDataSources(args) {
|
|
10
|
-
if (args.manifest === null) {
|
|
11
|
-
return Object.entries(args.dataSourceBindings).map(([key, binding]) => ({
|
|
12
|
-
key,
|
|
13
|
-
collectionPointer: binding.collectionPointer,
|
|
14
|
-
collectionSchema: binding.collectionSchema,
|
|
15
|
-
propertyIdsByKey: { ...(binding.propertyIdsByKey ?? {}) },
|
|
16
|
-
propertySchemasById: binding.collectionSchema?.propertiesById ?? {},
|
|
17
|
-
}));
|
|
18
|
-
}
|
|
19
9
|
return Object.entries(args.manifest.dataSources).map(([key, manifestDataSource]) => {
|
|
20
10
|
const binding = args.dataSourceBindings[key];
|
|
21
11
|
const propertySchemasById = binding?.collectionSchema?.propertiesById ?? {};
|
|
@@ -2,6 +2,7 @@ import type { NotionDataSource } from "./dataSources/dataSource.js";
|
|
|
2
2
|
import type { NotionDataSourcePage, NotionDataSourcePageBridge, NotionDataSourcePageUpdateInput, NotionDataSourcePageUpdateResult } from "./dataSources/dataSourcePage.js";
|
|
3
3
|
import type { NotionPropertySchema } from "./dataSources/propertySchema.js";
|
|
4
4
|
import type { NotionBlockId } from "./ids.js";
|
|
5
|
+
import type { CustomBlockManifest } from "./manifest.js";
|
|
5
6
|
import type { CustomBlockQueryDataSourceErrorInfo } from "./messages/queryDataSourceResult.js";
|
|
6
7
|
import type { CustomBlockPage, NotionPageId } from "./pages/page.js";
|
|
7
8
|
import type { NotionParent } from "./parent.js";
|
|
@@ -19,6 +20,7 @@ export type InitializedHostState = {
|
|
|
19
20
|
parent: NotionParent;
|
|
20
21
|
page: CustomBlockPage;
|
|
21
22
|
currentUser: NotionUser;
|
|
23
|
+
manifest: CustomBlockManifest;
|
|
22
24
|
dataSources: NotionDataSource[];
|
|
23
25
|
dataSourceState: Record<string, DataSourceQueryState>;
|
|
24
26
|
};
|