@notionhq/custom-blocks-host 0.3.11 → 0.4.1
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/LICENSE +21 -0
- package/README.md +143 -236
- package/THIRD_PARTY_LICENSES.md +9 -0
- package/dist/autoBindProperties.d.ts +5 -2
- package/dist/autoBindProperties.d.ts.map +1 -1
- package/dist/autoBindProperties.js +5 -2
- package/dist/browser.js +1 -1
- package/dist/compatibility/addMetadataReadSchemasForProtocol3To4.d.ts +1215 -0
- package/dist/compatibility/addMetadataReadSchemasForProtocol3To4.d.ts.map +1 -0
- package/dist/compatibility/addMetadataReadSchemasForProtocol3To4.js +27 -0
- package/dist/compatibility/normalizeHostToSandboxMessage.d.ts +8 -0
- package/dist/compatibility/normalizeHostToSandboxMessage.d.ts.map +1 -0
- package/dist/compatibility/normalizeHostToSandboxMessage.js +39 -0
- package/dist/compatibility/normalizePageResultsForProtocol3To5.d.ts +11 -0
- package/dist/compatibility/normalizePageResultsForProtocol3To5.d.ts.map +1 -0
- package/dist/compatibility/normalizePageResultsForProtocol3To5.js +18 -0
- package/dist/compatibility/normalizePageResultsForProtocol6.d.ts +9 -0
- package/dist/compatibility/normalizePageResultsForProtocol6.d.ts.map +1 -0
- package/dist/compatibility/normalizePageResultsForProtocol6.js +18 -0
- package/dist/compatibility/normalizeSandboxToHostMessage.d.ts +1099 -0
- package/dist/compatibility/normalizeSandboxToHostMessage.d.ts.map +1 -0
- package/dist/compatibility/normalizeSandboxToHostMessage.js +9 -0
- package/dist/compatibility/pageForProtocol3To5.d.ts +159 -0
- package/dist/compatibility/pageForProtocol3To5.d.ts.map +1 -0
- package/dist/{lifecycle/legacyPage.js → compatibility/pageForProtocol3To5.js} +50 -9
- package/dist/compatibility/pageResult.d.ts +11 -0
- package/dist/compatibility/pageResult.d.ts.map +1 -0
- package/dist/compatibility/pageWithNullTextFallbackProperties.d.ts +9 -0
- package/dist/compatibility/pageWithNullTextFallbackProperties.d.ts.map +1 -0
- package/dist/compatibility/pageWithNullTextFallbackProperties.js +46 -0
- package/dist/createCustomBlockHost.d.ts +18 -19
- package/dist/createCustomBlockHost.d.ts.map +1 -1
- package/dist/createCustomBlockHost.js +103 -38
- package/dist/index.d.ts +2 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/{localBlockUrl.d.ts → isAllowedLocalPreviewUrl.d.ts} +2 -2
- package/dist/isAllowedLocalPreviewUrl.d.ts.map +1 -0
- package/dist/{localBlockUrl.js → isAllowedLocalPreviewUrl.js} +3 -7
- package/dist/lifecycle/initErrors.d.ts +1 -0
- package/dist/lifecycle/initErrors.d.ts.map +1 -1
- package/dist/lifecycle/initErrors.js +7 -0
- package/dist/lifecycle/initializationController.d.ts +6 -4
- package/dist/lifecycle/initializationController.d.ts.map +1 -1
- package/dist/lifecycle/initializationController.js +24 -15
- package/dist/lifecycle/resolveManifest.d.ts +13 -0
- package/dist/lifecycle/resolveManifest.d.ts.map +1 -0
- package/dist/lifecycle/resolveManifest.js +21 -0
- package/dist/lifecycle/types.d.ts +1637 -17
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/lifecycle/types.js +18 -0
- package/dist/messages/sandboxMessageRouter.d.ts.map +1 -1
- package/dist/messages/sandboxMessageRouter.js +2 -2
- package/dist/protocol/dataSources/dataSourcePage.d.ts +3 -4
- package/dist/protocol/dataSources/dataSourcePage.js +2 -3
- package/dist/protocol/index.d.ts +1 -0
- package/dist/protocol/index.js +1 -0
- package/dist/protocol/isLocalBlockServerUrl.d.ts +2 -0
- package/dist/protocol/isLocalBlockServerUrl.js +9 -0
- package/dist/protocol/messages/connect.d.ts +2 -2
- package/dist/protocol/messages/connect.js +2 -2
- package/dist/protocol/messages/createPageResult.d.ts +2 -2
- package/dist/protocol/messages/createPageResult.js +3 -3
- package/dist/protocol/messages/getPage.d.ts +2 -2
- package/dist/protocol/messages/getPage.js +3 -3
- package/dist/protocol/messages/hostToSandbox.d.ts +3 -3
- package/dist/protocol/messages/hostToSandbox.js +10 -10
- package/dist/protocol/messages/init.d.ts +1 -1
- package/dist/protocol/messages/queryDataSource.d.ts +24 -0
- package/dist/protocol/messages/queryDataSource.js +2 -0
- package/dist/protocol/messages/queryDataSourceResult.d.ts +3 -3
- package/dist/protocol/messages/queryDataSourceResult.js +6 -3
- package/dist/protocol/messages/sandboxToHost.d.ts +8 -0
- package/dist/protocol/messages/updatePageResult.d.ts +2 -2
- package/dist/protocol/messages/updatePageResult.js +3 -3
- package/dist/protocol/pages/page.d.ts +2 -2
- package/dist/protocol/pages/page.js +1 -1
- package/dist/protocol/protocolVersion.d.ts +2 -2
- package/dist/protocol/protocolVersion.js +2 -2
- package/dist/protocol.d.ts +9 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +8 -0
- package/docs/bridge/changelog.md +67 -0
- package/docs/bridge/protocol.md +189 -0
- package/docs/bridge/versioning.md +38 -0
- package/docs/compatibility.md +68 -0
- package/docs/handlers.md +199 -0
- package/docs/lifecycle.md +117 -0
- package/package.json +10 -2
- package/src/autoBindProperties.ts +5 -2
- package/src/compatibility/addMetadataReadSchemasForProtocol3To4.ts +36 -0
- package/src/compatibility/normalizeHostToSandboxMessage.ts +54 -0
- package/src/compatibility/normalizePageResultsForProtocol3To5.ts +28 -0
- package/src/compatibility/normalizePageResultsForProtocol6.ts +23 -0
- package/src/compatibility/normalizeSandboxToHostMessage.ts +10 -0
- package/src/{lifecycle/legacyPage.ts → compatibility/pageForProtocol3To5.ts} +52 -11
- package/src/compatibility/pageResult.ts +18 -0
- package/src/compatibility/pageWithNullTextFallbackProperties.ts +53 -0
- package/src/createCustomBlockHost.ts +152 -62
- package/src/index.ts +4 -51
- package/src/{localBlockUrl.ts → isAllowedLocalPreviewUrl.ts} +4 -7
- package/src/lifecycle/initErrors.ts +9 -0
- package/src/lifecycle/initializationController.ts +34 -19
- package/src/lifecycle/resolveManifest.ts +31 -0
- package/src/lifecycle/types.ts +84 -16
- package/src/messages/sandboxMessageRouter.ts +2 -2
- package/src/protocol.ts +8 -0
- package/dist/lifecycle/legacyPage.d.ts +0 -70
- package/dist/lifecycle/legacyPage.d.ts.map +0 -1
- package/dist/lifecycle/loadCustomBlockManifest.d.ts +0 -16
- package/dist/lifecycle/loadCustomBlockManifest.d.ts.map +0 -1
- package/dist/lifecycle/loadCustomBlockManifest.js +0 -65
- package/dist/lifecycle/manifestOptions.d.ts +0 -17
- package/dist/lifecycle/manifestOptions.d.ts.map +0 -1
- package/dist/lifecycle/normalizeHostToSandboxMessage.d.ts +0 -6
- package/dist/lifecycle/normalizeHostToSandboxMessage.d.ts.map +0 -1
- package/dist/lifecycle/normalizeHostToSandboxMessage.js +0 -14
- package/dist/lifecycle/normalizePage.d.ts +0 -7
- package/dist/lifecycle/normalizePage.d.ts.map +0 -1
- package/dist/lifecycle/normalizePage.js +0 -47
- package/dist/localBlockUrl.d.ts.map +0 -1
- package/src/lifecycle/loadCustomBlockManifest.ts +0 -89
- package/src/lifecycle/manifestOptions.ts +0 -19
- package/src/lifecycle/normalizeHostToSandboxMessage.ts +0 -27
- package/src/lifecycle/normalizePage.ts +0 -71
- /package/dist/{lifecycle/manifestOptions.js → compatibility/pageResult.js} +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Host initialization and lifecycle
|
|
2
|
+
|
|
3
|
+
The host prepares a block's manifest and context, completes initialization, and manages the
|
|
4
|
+
connection until the iframe is removed. This reference covers state preparation, timeouts,
|
|
5
|
+
callbacks, live state, and cleanup. Start with the [embed example](../README.md#create-a-host).
|
|
6
|
+
|
|
7
|
+
## Manifest and initial state
|
|
8
|
+
|
|
9
|
+
Pass the full block URL as `sandboxUrl`. The host derives the origin used to authenticate incoming
|
|
10
|
+
messages and target outgoing messages. `CustomBlockManifestSource` selects the manifest:
|
|
11
|
+
|
|
12
|
+
| `manifestSource` | Manifest source |
|
|
13
|
+
| ---------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
14
|
+
| `"host"` | The manifest returned by `getPersistedManifest({ signal })` |
|
|
15
|
+
| `"sandbox"` | The local declaration supplied in `connect.manifest`, subject to the [local URL policy](#local-block-urls) |
|
|
16
|
+
|
|
17
|
+
`getPersistedManifest` is required in both modes and starts when the host is created. It returns
|
|
18
|
+
`CustomBlockHostManifestResult`: either `{ status: "success", manifest }` or
|
|
19
|
+
`{ status: "error", error }`. It must not resolve to `null` or `undefined`. A definition that is
|
|
20
|
+
still loading keeps the promise pending; a failed load returns an error result. The host validates
|
|
21
|
+
the result and normalizes the manifest before calling `prepareInitialState`. Omitted `dataSources`
|
|
22
|
+
and each source's omitted `properties` default to `{}`.
|
|
23
|
+
|
|
24
|
+
The sandbox loads independently and sends `connect`. In sandbox mode, the SDK reads the manifest
|
|
25
|
+
from JSON metadata injected into the local block HTML by dev tooling. The host validates the
|
|
26
|
+
declaration in `connect`. The two sources do not wait for each other; only the selected result
|
|
27
|
+
determines initialization. Sandbox mode has no persisted fallback. The
|
|
28
|
+
[wire reference](bridge/protocol.md#initialization) describes the declaration and its
|
|
29
|
+
validation errors.
|
|
30
|
+
|
|
31
|
+
Once the selected manifest is ready, the host calls `prepareInitialState({ manifest, signal })`
|
|
32
|
+
once. Return `{ status: "success", initialState }` with context and data-source bindings, or
|
|
33
|
+
`{ status: "error", error }`. This callback also must not resolve to `null` or `undefined`. The host
|
|
34
|
+
validates its result. The prepared state omits the manifest; the host adds the selected manifest to
|
|
35
|
+
`init`. Host-mode preparation runs concurrently with connection setup. Sandbox-mode preparation
|
|
36
|
+
starts after `connect` supplies the declaration. The host sends `init` after preparation and a valid
|
|
37
|
+
connection succeed. A preparation failure is retained until the sandbox connects.
|
|
38
|
+
|
|
39
|
+
Errors from the selected manifest source retain their `code`, `message`, and `isRetryable`. A
|
|
40
|
+
missing sandbox declaration produces `manifest_unavailable`. A sandbox `manifest_invalid` error is
|
|
41
|
+
ignored in host mode because that mode uses the persisted manifest; other connect errors remain
|
|
42
|
+
fatal. Unexpected exceptions or invalid results from either callback produce `unknown_error`.
|
|
43
|
+
Diagnostics go to `onLog("warn", payload)`. Stopping aborts preparation and suppresses late results
|
|
44
|
+
without a cancellation warning.
|
|
45
|
+
|
|
46
|
+
## Timeouts
|
|
47
|
+
|
|
48
|
+
| Option | Waits for | Default | Failure code |
|
|
49
|
+
| ----------------------- | ------------------------------------- | --------- | ----------------------- |
|
|
50
|
+
| `noConnectTimeoutMs` | `connect` after the first iframe load | 5 seconds | `no_connect` |
|
|
51
|
+
| `initialStateTimeoutMs` | State preparation after `connect` | 5 seconds | `initial_state_timeout` |
|
|
52
|
+
| `noInitResultTimeoutMs` | `initResult` after `init` | 5 seconds | `no_init_result` |
|
|
53
|
+
|
|
54
|
+
The SDK waits for `init` without its own timeout. Handshake failures abort preparation and suppress
|
|
55
|
+
late results. Manifest and state preparation providers must respect their `signal`.
|
|
56
|
+
|
|
57
|
+
## Live state
|
|
58
|
+
|
|
59
|
+
`setTheme()`, `setContrastMode()`, `setParent()`, `setPage()`, `setDataSources()`, and
|
|
60
|
+
`setCurrentUser()` hold updates during initialization and send them after `initResult.success`. For
|
|
61
|
+
data-source query refreshes, see [Handlers and data](handlers.md#refresh-active-queries).
|
|
62
|
+
|
|
63
|
+
## Callbacks
|
|
64
|
+
|
|
65
|
+
The required `onStatusChange(state)` callback receives the `CustomBlockHostStatus` union:
|
|
66
|
+
|
|
67
|
+
| Status | Behavior | Payload |
|
|
68
|
+
| ---------------------- | -------------------------------------------------------------- | ------------------------------------------------------ |
|
|
69
|
+
| `waitingForConnect` | The host is listening; reported synchronously during creation | None |
|
|
70
|
+
| `preparingInit` | The host accepted a connection and is preparing initialization | `localManifest`, `sdkVersion`, `bridgeProtocolVersion` |
|
|
71
|
+
| `waitingForInitResult` | Successful `init` was sent; acknowledgment is pending | None |
|
|
72
|
+
| `initialized` | The sandbox accepted initialization | `resolvedManifest`, optional `initialHeight` |
|
|
73
|
+
| `failed` | Initialization or runtime failed | `error` |
|
|
74
|
+
| `stopped` | The application explicitly disposed of the host | None |
|
|
75
|
+
|
|
76
|
+
`localManifest` is optional and comes only from a permitted local sandbox. It is available before
|
|
77
|
+
initialization completes, so an adapter can use its requirements to prepare bindings.
|
|
78
|
+
`resolvedManifest` is the selected declaration sent in `init`. Rejected connections can skip
|
|
79
|
+
intermediate statuses. Both `failed` and `stopped` are terminal; stopping a failed host releases
|
|
80
|
+
resources without replacing its failure status.
|
|
81
|
+
|
|
82
|
+
Connection failures contain `{ phase, source, code, message, isRetryable }`. The phase is `connect`,
|
|
83
|
+
`init`, `initResult`, or `runtime`. The source identifies who reported the failure. Failures can
|
|
84
|
+
occur after initialization succeeds.
|
|
85
|
+
|
|
86
|
+
At `initialized`, apply a supplied `initialHeight` in CSS pixels before revealing the iframe. If the
|
|
87
|
+
height is absent, use your application's default size. The `resize` handler applies later height
|
|
88
|
+
changes. Reveal, sizing, and failure UI belong to the embedding application.
|
|
89
|
+
|
|
90
|
+
| Other callback | Behavior |
|
|
91
|
+
| ------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
92
|
+
| `onQueryUnsubscribed(subscriptionId)` | Runs once when a subscription ends through unsubscribe, replacement, terminal error, or stop |
|
|
93
|
+
| `onLog(direction, payload)` | Receives bridge messages and warnings |
|
|
94
|
+
|
|
95
|
+
## Iframe lifetime
|
|
96
|
+
|
|
97
|
+
Each host handles one handshake. Create a new host and iframe to retry or replace the block URL. The
|
|
98
|
+
[wire reference](bridge/protocol.md#initialization) defines handshake and
|
|
99
|
+
malformed-message behavior.
|
|
100
|
+
|
|
101
|
+
The first observed iframe `load` is allowed. A second load stops the host and reports retryable
|
|
102
|
+
`iframe_reloaded`. Load-event cleanup does not isolate documents before that event. A replacement
|
|
103
|
+
document at the same origin can receive messages and invoke handlers until the host stops.
|
|
104
|
+
|
|
105
|
+
`stop()` removes listeners, cancels preparation, clears timers, ends subscriptions, and suppresses
|
|
106
|
+
pending responses. Call it when removing the iframe.
|
|
107
|
+
|
|
108
|
+
## Local block URLs
|
|
109
|
+
|
|
110
|
+
`isAllowedLocalPreviewUrl({ url, hostOrigin })` accepts HTTP or HTTPS URLs on `localhost`,
|
|
111
|
+
`*.localhost`, or `127.0.0.1` without credentials. It rejects the embedding page's origin.
|
|
112
|
+
`hostOrigin` must be nonempty. Pass the embedding page's serialized origin.
|
|
113
|
+
|
|
114
|
+
The protocol helper `isLocalBlockServerUrl(url)` checks the server URL without requiring origin
|
|
115
|
+
isolation. The host adds the embedding-origin restriction. `manifestSource: "sandbox"` enforces this
|
|
116
|
+
policy before the host starts. An invalid sandbox-mode URL throws `CustomBlockInitializationError`
|
|
117
|
+
with code `invalid_local_preview_url`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@notionhq/custom-blocks-host",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Reusable host runtime for Notion custom blocks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
"import": "./dist/index.js",
|
|
15
15
|
"types": "./dist/index.d.ts",
|
|
16
16
|
"default": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./protocol": {
|
|
19
|
+
"types": "./dist/protocol.d.ts",
|
|
20
|
+
"import": "./dist/protocol.js",
|
|
21
|
+
"default": "./dist/protocol.js"
|
|
17
22
|
}
|
|
18
23
|
},
|
|
19
24
|
"dependencies": {
|
|
@@ -22,6 +27,9 @@
|
|
|
22
27
|
"files": [
|
|
23
28
|
"src",
|
|
24
29
|
"dist",
|
|
25
|
-
"
|
|
30
|
+
"docs",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE",
|
|
33
|
+
"THIRD_PARTY_LICENSES.md"
|
|
26
34
|
]
|
|
27
35
|
}
|
|
@@ -6,8 +6,8 @@ export type AutoBindableProperty = {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Returns a map from manifest property keys to database property IDs.
|
|
10
|
+
* Unmapped keys are omitted.
|
|
11
11
|
*
|
|
12
12
|
* For each manifest property:
|
|
13
13
|
* 1. Consider only schema properties of the same type.
|
|
@@ -15,6 +15,9 @@ export type AutoBindableProperty = {
|
|
|
15
15
|
* (exact string: no trim, no case folding) or its display name equals
|
|
16
16
|
* the manifest display name after `trim()` and lowercasing.
|
|
17
17
|
* 3. Bind when there is exactly one candidate. Otherwise leave it unmapped.
|
|
18
|
+
*
|
|
19
|
+
* An ID match has no priority over a display name match. If they identify two
|
|
20
|
+
* different properties of the same type, the manifest property stays unmapped.
|
|
18
21
|
*/
|
|
19
22
|
export function getAutoBoundPropertyIdsByKey(args: {
|
|
20
23
|
manifestProperties: Readonly<Record<string, AutoBindableProperty>>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { NOTION_BUILTIN_PROPERTY_SCHEMAS } from "@notionhq/custom-blocks-protocol/dataSources/propertySchema.js"
|
|
2
|
+
import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Protocols 3–4 derive metadata read schemas from host bindings. Restore those
|
|
6
|
+
* schemas only in outgoing copies so host query validation keeps the actual schema.
|
|
7
|
+
*
|
|
8
|
+
* Delete when MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION >= 5.
|
|
9
|
+
*/
|
|
10
|
+
export function addMetadataReadSchemasForProtocol3To4(
|
|
11
|
+
message: Extract<
|
|
12
|
+
HostToSandboxMessage,
|
|
13
|
+
{ type: "init"; status: "success" } | { type: "dataSourcesChanged" }
|
|
14
|
+
>,
|
|
15
|
+
) {
|
|
16
|
+
return {
|
|
17
|
+
...message,
|
|
18
|
+
dataSources: {
|
|
19
|
+
bindings: Object.fromEntries(
|
|
20
|
+
Object.entries(message.dataSources.bindings).map(([key, binding]) => [
|
|
21
|
+
key,
|
|
22
|
+
{
|
|
23
|
+
...binding,
|
|
24
|
+
collectionSchema: {
|
|
25
|
+
...binding.collectionSchema,
|
|
26
|
+
propertiesById: {
|
|
27
|
+
...binding.collectionSchema?.propertiesById,
|
|
28
|
+
...NOTION_BUILTIN_PROPERTY_SCHEMAS,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
]),
|
|
33
|
+
),
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
HostToSandboxMessage,
|
|
3
|
+
HostToSandboxMessageWithProtocol3To5Compatibility,
|
|
4
|
+
} from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
|
|
5
|
+
import {
|
|
6
|
+
TEXT_FALLBACK_PROPERTY_PROTOCOL_VERSION,
|
|
7
|
+
UNIFIED_PAGE_PROTOCOL_VERSION,
|
|
8
|
+
} from "@notionhq/custom-blocks-protocol/protocolVersion.js"
|
|
9
|
+
import { addMetadataReadSchemasForProtocol3To4 } from "./addMetadataReadSchemasForProtocol3To4.js"
|
|
10
|
+
import { normalizePageResultsForProtocol3To5 } from "./normalizePageResultsForProtocol3To5.js"
|
|
11
|
+
import { normalizePageResultsForProtocol6 } from "./normalizePageResultsForProtocol6.js"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Encodes current host messages for the sandbox's declared protocol. Handlers use
|
|
15
|
+
* current shapes. Each target adapter starts from that shape to avoid data loss.
|
|
16
|
+
* Unchanged messages and error results retain their original payloads.
|
|
17
|
+
*/
|
|
18
|
+
export function normalizeHostToSandboxMessage(
|
|
19
|
+
message: HostToSandboxMessage,
|
|
20
|
+
bridgeProtocolVersion: number,
|
|
21
|
+
): HostToSandboxMessageWithProtocol3To5Compatibility {
|
|
22
|
+
// Accepted compatibility gap (2026-10-31): leave unavailable containing-page
|
|
23
|
+
// context in init/pageChanged unchanged for early protocol 5 SDKs. Protocol 5
|
|
24
|
+
// shipped in SDK 0.2.16 on 2026-09-30. SDK 0.2.17 added unavailable context on
|
|
25
|
+
// 2026-10-02 without a protocol bump. SDK 0.2.16 cannot decode that context.
|
|
26
|
+
// Preserve normal contexts and later protocol 5 support. Affected blocks must
|
|
27
|
+
// upgrade their SDK rather than adding SDK-version-based translation here.
|
|
28
|
+
if (
|
|
29
|
+
(message.type === "init" && message.status === "success") ||
|
|
30
|
+
message.type === "dataSourcesChanged"
|
|
31
|
+
) {
|
|
32
|
+
return bridgeProtocolVersion < 5
|
|
33
|
+
? addMetadataReadSchemasForProtocol3To4(message)
|
|
34
|
+
: message
|
|
35
|
+
}
|
|
36
|
+
if (
|
|
37
|
+
message.type !== "queryDataSourceResult" &&
|
|
38
|
+
message.type !== "getPageResult" &&
|
|
39
|
+
message.type !== "createPageResult" &&
|
|
40
|
+
message.type !== "updatePageResult"
|
|
41
|
+
) {
|
|
42
|
+
return message
|
|
43
|
+
}
|
|
44
|
+
if (message.status === "error") {
|
|
45
|
+
return message
|
|
46
|
+
}
|
|
47
|
+
if (bridgeProtocolVersion < UNIFIED_PAGE_PROTOCOL_VERSION) {
|
|
48
|
+
return normalizePageResultsForProtocol3To5(message)
|
|
49
|
+
}
|
|
50
|
+
if (bridgeProtocolVersion < TEXT_FALLBACK_PROPERTY_PROTOCOL_VERSION) {
|
|
51
|
+
return normalizePageResultsForProtocol6(message)
|
|
52
|
+
}
|
|
53
|
+
return message
|
|
54
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { HostToSandboxMessageWithProtocol3To5Compatibility } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
|
|
2
|
+
import {
|
|
3
|
+
toPageForProtocol3To5,
|
|
4
|
+
toQueryPageForProtocol3To5,
|
|
5
|
+
} from "./pageForProtocol3To5.js"
|
|
6
|
+
import type { SuccessfulPageResult } from "./pageResult.js"
|
|
7
|
+
import { toPageWithNullTextFallbackProperties } from "./pageWithNullTextFallbackProperties.js"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Encodes current pages for protocols 3–5. Query values are flat and ID-keyed.
|
|
11
|
+
* Page operations omit unsupported property objects and use ID-keyed properties.
|
|
12
|
+
* Convert directly from current pages so query text fallbacks remain available.
|
|
13
|
+
*
|
|
14
|
+
* Delete when MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION >= 6.
|
|
15
|
+
*/
|
|
16
|
+
export function normalizePageResultsForProtocol3To5(
|
|
17
|
+
message: SuccessfulPageResult,
|
|
18
|
+
): HostToSandboxMessageWithProtocol3To5Compatibility {
|
|
19
|
+
if (message.type === "queryDataSourceResult") {
|
|
20
|
+
return { ...message, items: message.items.map(toQueryPageForProtocol3To5) }
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
...message,
|
|
24
|
+
page: toPageForProtocol3To5(
|
|
25
|
+
toPageWithNullTextFallbackProperties(message.page),
|
|
26
|
+
),
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
|
|
2
|
+
import type { SuccessfulPageResult } from "./pageResult.js"
|
|
3
|
+
import { toPageWithNullTextFallbackProperties } from "./pageWithNullTextFallbackProperties.js"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Encodes protocol 6 pages, whose receivers require null for text fallback properties.
|
|
7
|
+
*
|
|
8
|
+
* Delete when MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION >= 7.
|
|
9
|
+
*/
|
|
10
|
+
export function normalizePageResultsForProtocol6(
|
|
11
|
+
message: SuccessfulPageResult,
|
|
12
|
+
): HostToSandboxMessage {
|
|
13
|
+
if (message.type === "queryDataSourceResult") {
|
|
14
|
+
return {
|
|
15
|
+
...message,
|
|
16
|
+
items: message.items.map(toPageWithNullTextFallbackProperties),
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
...message,
|
|
21
|
+
page: toPageWithNullTextFallbackProperties(message.page),
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { sandboxToHostMessageSchema } from "@notionhq/custom-blocks-protocol/messages/sandboxToHost.js"
|
|
2
|
+
import * as v from "valibot"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Validates sandbox wire messages before handler dispatch. Supported requests
|
|
6
|
+
* currently share one schema, so parsing produces the current handler shape.
|
|
7
|
+
*/
|
|
8
|
+
export function normalizeSandboxToHostMessage(payload: unknown) {
|
|
9
|
+
return v.safeParse(sandboxToHostMessageSchema, payload)
|
|
10
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { NotionDataSourceValue } from "@notionhq/custom-blocks-protocol/dataSources/dataSourceValue.js"
|
|
2
2
|
import type {
|
|
3
|
-
|
|
3
|
+
NotionPageForProtocol3To5,
|
|
4
4
|
NotionPagePropertyValue,
|
|
5
5
|
notionPageDataSchema,
|
|
6
6
|
} from "@notionhq/custom-blocks-protocol/pages/page.js"
|
|
@@ -8,9 +8,17 @@ import type { InferOutput } from "valibot"
|
|
|
8
8
|
import { toNotionDateValue } from "../dateValue.js"
|
|
9
9
|
import { unreachable } from "../utils.js"
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Converts current page data to the page-operation format used by protocols 3–5.
|
|
13
|
+
* Keys property objects by raw ID and omits null properties. Optional icons,
|
|
14
|
+
* covers, timestamps, and URLs use undefined instead of null in this format.
|
|
15
|
+
* Call after clearing property objects unsupported by these protocols.
|
|
16
|
+
*
|
|
17
|
+
* Delete when MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION >= 6.
|
|
18
|
+
*/
|
|
19
|
+
export function toPageForProtocol3To5(
|
|
12
20
|
page: InferOutput<typeof notionPageDataSchema>,
|
|
13
|
-
):
|
|
21
|
+
): NotionPageForProtocol3To5 {
|
|
14
22
|
return {
|
|
15
23
|
...page,
|
|
16
24
|
properties: Object.fromEntries(
|
|
@@ -27,23 +35,44 @@ export function toLegacyPage(
|
|
|
27
35
|
}
|
|
28
36
|
}
|
|
29
37
|
|
|
30
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Converts a current page to a protocol 3–5 query row, keeping its ID and archive
|
|
40
|
+
* flags while flattening typed property objects into raw-ID-keyed values.
|
|
41
|
+
* Copies page creation and edit metadata into the reserved property IDs that
|
|
42
|
+
* these query readers expect.
|
|
43
|
+
*
|
|
44
|
+
* Delete when MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION >= 6.
|
|
45
|
+
*/
|
|
46
|
+
export function toQueryPageForProtocol3To5(
|
|
31
47
|
page: InferOutput<typeof notionPageDataSchema>,
|
|
32
48
|
) {
|
|
33
49
|
const propertiesById: Record<string, NotionDataSourceValue | undefined> = {}
|
|
34
50
|
for (const property of Object.values(page.properties)) {
|
|
35
51
|
if (property) {
|
|
36
|
-
propertiesById[property.id] =
|
|
52
|
+
propertiesById[property.id] = toPropertyValueForProtocol3To5(property)
|
|
37
53
|
}
|
|
38
54
|
}
|
|
39
55
|
return {
|
|
40
56
|
id: page.id,
|
|
41
57
|
is_archived: page.is_archived,
|
|
42
58
|
in_trash: page.in_trash,
|
|
43
|
-
|
|
59
|
+
// Reserved metadata IDs retain their historical meaning even if an actual
|
|
60
|
+
// property uses the same ID. Binding schemas use the same precedence.
|
|
61
|
+
propertiesById: {
|
|
62
|
+
...propertiesById,
|
|
63
|
+
created_time: toDateTimeValueForProtocol3To5(page.created_time),
|
|
64
|
+
last_edited_time: toDateTimeValueForProtocol3To5(page.last_edited_time),
|
|
65
|
+
created_by: page.created_by ? [page.created_by] : undefined,
|
|
66
|
+
last_edited_by: page.last_edited_by ? [page.last_edited_by] : undefined,
|
|
67
|
+
},
|
|
44
68
|
}
|
|
45
69
|
}
|
|
46
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Flattens rich-text items to the string used by protocol 3–5 query values.
|
|
73
|
+
*
|
|
74
|
+
* Delete when MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION >= 6.
|
|
75
|
+
*/
|
|
47
76
|
function plainText(items: Record<string, unknown>[]): string {
|
|
48
77
|
return items
|
|
49
78
|
.map(item => {
|
|
@@ -61,7 +90,13 @@ function plainText(items: Record<string, unknown>[]): string {
|
|
|
61
90
|
.join("")
|
|
62
91
|
}
|
|
63
92
|
|
|
64
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Converts a timestamp to the UTC date/time object used by protocol 3–5 queries.
|
|
95
|
+
* Missing or invalid timestamps produce undefined; time is stored to the minute.
|
|
96
|
+
*
|
|
97
|
+
* Delete when MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION >= 6.
|
|
98
|
+
*/
|
|
99
|
+
function toDateTimeValueForProtocol3To5(
|
|
65
100
|
value: string | number | null | undefined,
|
|
66
101
|
): NotionDataSourceValue | undefined {
|
|
67
102
|
if (value === undefined || value === null || value === "") {
|
|
@@ -79,7 +114,13 @@ function timestamp(
|
|
|
79
114
|
}
|
|
80
115
|
}
|
|
81
116
|
|
|
82
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Converts a typed page property to the scalar, list, record-reference, or
|
|
119
|
+
* date/time value used by protocol 3–5 query results.
|
|
120
|
+
*
|
|
121
|
+
* Delete when MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION >= 6.
|
|
122
|
+
*/
|
|
123
|
+
function toPropertyValueForProtocol3To5(
|
|
83
124
|
property: NotionPagePropertyValue,
|
|
84
125
|
): NotionDataSourceValue | undefined {
|
|
85
126
|
switch (property.type) {
|
|
@@ -134,11 +175,11 @@ function toLegacyProperty(
|
|
|
134
175
|
case "place":
|
|
135
176
|
return ""
|
|
136
177
|
case "created_time":
|
|
137
|
-
return
|
|
178
|
+
return toDateTimeValueForProtocol3To5(property.created_time)
|
|
138
179
|
case "last_edited_time":
|
|
139
|
-
return
|
|
180
|
+
return toDateTimeValueForProtocol3To5(property.last_edited_time)
|
|
140
181
|
case "last_visited_time":
|
|
141
|
-
return
|
|
182
|
+
return toDateTimeValueForProtocol3To5(property.last_visited_time)
|
|
142
183
|
case "created_by":
|
|
143
184
|
return property.created_by ? [property.created_by] : undefined
|
|
144
185
|
case "last_edited_by":
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Page-bearing responses accepted by the target-protocol adapters. The dispatcher
|
|
5
|
+
* passes errors and unrelated messages through unchanged before calling them.
|
|
6
|
+
* Sharing this subset keeps both adapters limited to successful page conversions.
|
|
7
|
+
*/
|
|
8
|
+
export type SuccessfulPageResult = Extract<
|
|
9
|
+
HostToSandboxMessage,
|
|
10
|
+
{
|
|
11
|
+
type:
|
|
12
|
+
| "queryDataSourceResult"
|
|
13
|
+
| "getPageResult"
|
|
14
|
+
| "createPageResult"
|
|
15
|
+
| "updatePageResult"
|
|
16
|
+
status: "success"
|
|
17
|
+
}
|
|
18
|
+
>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { notionPageDataSchema } from "@notionhq/custom-blocks-protocol/pages/page.js"
|
|
2
|
+
import type { InferOutput } from "valibot"
|
|
3
|
+
import { unreachable } from "../utils.js"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shared field conversion for receivers that cannot decode text fallback objects.
|
|
7
|
+
*
|
|
8
|
+
* Delete when MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION >= 7.
|
|
9
|
+
*/
|
|
10
|
+
export function toPageWithNullTextFallbackProperties(
|
|
11
|
+
page: InferOutput<typeof notionPageDataSchema>,
|
|
12
|
+
): InferOutput<typeof notionPageDataSchema> {
|
|
13
|
+
return {
|
|
14
|
+
...page,
|
|
15
|
+
properties: Object.fromEntries(
|
|
16
|
+
Object.entries(page.properties).map(([name, property]) => {
|
|
17
|
+
switch (property?.type) {
|
|
18
|
+
case "unique_id":
|
|
19
|
+
case "button":
|
|
20
|
+
case "formula":
|
|
21
|
+
case "location":
|
|
22
|
+
case "rollup":
|
|
23
|
+
case "verification":
|
|
24
|
+
return [name, null]
|
|
25
|
+
case "title":
|
|
26
|
+
case "rich_text":
|
|
27
|
+
case "number":
|
|
28
|
+
case "url":
|
|
29
|
+
case "email":
|
|
30
|
+
case "phone_number":
|
|
31
|
+
case "checkbox":
|
|
32
|
+
case "select":
|
|
33
|
+
case "status":
|
|
34
|
+
case "multi_select":
|
|
35
|
+
case "date":
|
|
36
|
+
case "people":
|
|
37
|
+
case "relation":
|
|
38
|
+
case "files":
|
|
39
|
+
case "place":
|
|
40
|
+
case "created_time":
|
|
41
|
+
case "last_edited_time":
|
|
42
|
+
case "created_by":
|
|
43
|
+
case "last_edited_by":
|
|
44
|
+
case "last_visited_time":
|
|
45
|
+
case undefined:
|
|
46
|
+
return [name, property]
|
|
47
|
+
default:
|
|
48
|
+
return unreachable(property)
|
|
49
|
+
}
|
|
50
|
+
}),
|
|
51
|
+
),
|
|
52
|
+
}
|
|
53
|
+
}
|