@notionhq/custom-blocks 0.0.76 → 0.0.78
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 -37
- package/bin/src/bridge/manifest.js +3 -3
- package/dist/bridge/SandboxBridge.d.ts +6 -12
- package/dist/bridge/SandboxBridge.d.ts.map +1 -1
- package/dist/bridge/SandboxBridge.js +123 -71
- 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 +3 -2
- 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} +13 -11
- package/dist/bridge/messages/connect.d.ts.map +1 -0
- package/dist/bridge/messages/{ready.js → connect.js} +14 -13
- package/dist/bridge/messages/hostToSandbox.d.ts +24 -79
- package/dist/bridge/messages/hostToSandbox.d.ts.map +1 -1
- package/dist/bridge/messages/hostToSandbox.js +2 -3
- 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/queryDataSource.d.ts +3 -7
- package/dist/bridge/messages/queryDataSource.d.ts.map +1 -1
- package/dist/bridge/messages/queryDataSource.js +3 -7
- package/dist/bridge/messages/queryDataSourceResult.d.ts +4 -240
- package/dist/bridge/messages/queryDataSourceResult.d.ts.map +1 -1
- package/dist/bridge/messages/queryDataSourceResult.js +4 -22
- package/dist/bridge/messages/sandboxToHost.d.ts +21 -7
- 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 +6 -3
- package/dist/host/createCustomBlockHost.d.ts.map +1 -1
- package/dist/host/createCustomBlockHost.js +227 -58
- 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/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/queries/querySubscriptions.d.ts +10 -10
- package/dist/host/queries/querySubscriptions.d.ts.map +1 -1
- package/dist/host/queries/querySubscriptions.js +35 -19
- package/dist/host/queries/types.d.ts +5 -4
- package/dist/host/queries/types.d.ts.map +1 -1
- 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 +7 -5
- 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 +151 -77
- package/src/bridge/dataSources/resolve.ts +3 -14
- package/src/bridge/hostState.ts +3 -2
- package/src/bridge/loadManifest.ts +6 -6
- package/src/bridge/manifest.ts +3 -3
- package/src/bridge/messages/{ready.ts → connect.ts} +15 -14
- package/src/bridge/messages/hostToSandbox.ts +2 -6
- package/src/bridge/messages/init.ts +24 -10
- package/src/bridge/messages/initResult.ts +37 -0
- package/src/bridge/messages/queryDataSource.ts +3 -7
- package/src/bridge/messages/queryDataSourceResult.ts +4 -24
- 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 +269 -64
- 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/queries/querySubscriptions.ts +58 -39
- package/src/host/queries/types.ts +10 -11
- 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
|
@@ -1,52 +1,71 @@
|
|
|
1
1
|
import type { NotionDataSourceId } from "../../bridge/ids.js"
|
|
2
2
|
import type { QueryDataSourceMessage } from "../../bridge/messages/queryDataSource.js"
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
3
|
+
import type { CustomBlockHostQueryDataSourceResult } from "./types.js"
|
|
4
|
+
|
|
5
|
+
export type QueryRefresh = {
|
|
6
|
+
subscriptionId: string
|
|
7
|
+
response: CustomBlockHostQueryDataSourceResult
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type QuerySubscriptionToken = object
|
|
11
|
+
|
|
12
|
+
type QuerySubscriptionState = {
|
|
13
|
+
dataSourceId: NotionDataSourceId
|
|
14
|
+
limit: number
|
|
15
|
+
token: QuerySubscriptionToken
|
|
16
|
+
}
|
|
16
17
|
|
|
17
18
|
export class QuerySubscriptions {
|
|
18
|
-
private readonly
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
this.latestByDataSourceId.set(message.dataSourceId, {
|
|
25
|
-
requestId: message.requestId,
|
|
26
|
-
snapshotId: message.snapshotId,
|
|
19
|
+
private readonly bySubscriptionId = new Map<string, QuerySubscriptionState>()
|
|
20
|
+
|
|
21
|
+
track(message: QueryDataSourceMessage): QuerySubscriptionToken {
|
|
22
|
+
const token = {}
|
|
23
|
+
this.bySubscriptionId.set(message.subscriptionId, {
|
|
24
|
+
dataSourceId: message.dataSourceId,
|
|
27
25
|
limit: message.limit,
|
|
26
|
+
token,
|
|
28
27
|
})
|
|
28
|
+
return token
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
isCurrent(subscriptionId: string, token: QuerySubscriptionToken): boolean {
|
|
32
|
+
return this.bySubscriptionId.get(subscriptionId)?.token === token
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
resolveRefreshes(args: {
|
|
32
36
|
dataSourceId: NotionDataSourceId
|
|
33
37
|
response: CustomBlockHostQueryDataSourceResult
|
|
34
|
-
}):
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
items,
|
|
46
|
-
hasMore:
|
|
47
|
-
args.response.hasMore ?? args.response.items.length > items.length,
|
|
48
|
-
error: args.response.error,
|
|
49
|
-
},
|
|
38
|
+
}): QueryRefresh[] {
|
|
39
|
+
const refreshes: QueryRefresh[] = []
|
|
40
|
+
for (const [subscriptionId, queryState] of this.bySubscriptionId) {
|
|
41
|
+
if (queryState.dataSourceId !== args.dataSourceId) {
|
|
42
|
+
continue
|
|
43
|
+
}
|
|
44
|
+
queryState.token = {}
|
|
45
|
+
refreshes.push({
|
|
46
|
+
subscriptionId,
|
|
47
|
+
response: limitResponse(args.response, queryState.limit),
|
|
48
|
+
})
|
|
50
49
|
}
|
|
50
|
+
return refreshes
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
clear() {
|
|
54
|
+
this.bySubscriptionId.clear()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function limitResponse(
|
|
59
|
+
response: CustomBlockHostQueryDataSourceResult,
|
|
60
|
+
limit: number,
|
|
61
|
+
): CustomBlockHostQueryDataSourceResult {
|
|
62
|
+
if (response.status === "error") {
|
|
63
|
+
return response
|
|
64
|
+
}
|
|
65
|
+
const items = response.items.slice(0, limit)
|
|
66
|
+
return {
|
|
67
|
+
status: "success",
|
|
68
|
+
items,
|
|
69
|
+
hasMore: response.hasMore || response.items.length > items.length,
|
|
51
70
|
}
|
|
52
71
|
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import type { NotionDataSourcePageBridge } from "../../bridge/dataSources/dataSourcePage.js"
|
|
2
|
-
import type { QueryDataSourceMessage } from "../../bridge/messages/queryDataSource.js"
|
|
3
2
|
import type { CustomBlockQueryDataSourceErrorInfo } from "../../bridge/messages/queryDataSourceResult.js"
|
|
4
3
|
|
|
5
|
-
export type CustomBlockHostQueryDataSourceResult =
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
4
|
+
export type CustomBlockHostQueryDataSourceResult =
|
|
5
|
+
| {
|
|
6
|
+
status: "success"
|
|
7
|
+
items: NotionDataSourcePageBridge[]
|
|
8
|
+
hasMore: boolean
|
|
9
|
+
}
|
|
10
|
+
| {
|
|
11
|
+
status: "error"
|
|
12
|
+
error: CustomBlockQueryDataSourceErrorInfo
|
|
13
|
+
}
|
package/src/host.ts
CHANGED
|
@@ -38,6 +38,8 @@ export {
|
|
|
38
38
|
notionPageIdSchema,
|
|
39
39
|
} from "./bridge/ids.js"
|
|
40
40
|
export { readIncomingType } from "./bridge/incomingType.js"
|
|
41
|
+
export type { ConnectMessage } from "./bridge/messages/connect.js"
|
|
42
|
+
export { connectMessageSchema } from "./bridge/messages/connect.js"
|
|
41
43
|
export type { CreatePageMessage } from "./bridge/messages/createPage.js"
|
|
42
44
|
export { createPageMessageSchema } from "./bridge/messages/createPage.js"
|
|
43
45
|
export type {
|
|
@@ -90,6 +92,16 @@ export {
|
|
|
90
92
|
customBlockInitErrorInfoSchema,
|
|
91
93
|
initMessageSchema,
|
|
92
94
|
} from "./bridge/messages/init.js"
|
|
95
|
+
export type {
|
|
96
|
+
CustomBlockInitResultErrorCode,
|
|
97
|
+
CustomBlockInitResultErrorInfo,
|
|
98
|
+
InitResultMessage,
|
|
99
|
+
} from "./bridge/messages/initResult.js"
|
|
100
|
+
export {
|
|
101
|
+
customBlockInitResultErrorCodeSchema,
|
|
102
|
+
customBlockInitResultErrorInfoSchema,
|
|
103
|
+
initResultMessageSchema,
|
|
104
|
+
} from "./bridge/messages/initResult.js"
|
|
93
105
|
export type { InvalidHostMessage } from "./bridge/messages/invalidHostMessage.js"
|
|
94
106
|
export { invalidHostMessageSchema } from "./bridge/messages/invalidHostMessage.js"
|
|
95
107
|
export type { InvalidSandboxMessage } from "./bridge/messages/invalidSandboxMessage.js"
|
|
@@ -122,8 +134,6 @@ export {
|
|
|
122
134
|
customBlockQueryDataSourceErrorInfoSchema,
|
|
123
135
|
queryDataSourceResultMessageSchema,
|
|
124
136
|
} from "./bridge/messages/queryDataSourceResult.js"
|
|
125
|
-
export type { ReadyMessage } from "./bridge/messages/ready.js"
|
|
126
|
-
export { readyMessageSchema } from "./bridge/messages/ready.js"
|
|
127
137
|
export type { ResizeMessage } from "./bridge/messages/resize.js"
|
|
128
138
|
export { resizeMessageSchema } from "./bridge/messages/resize.js"
|
|
129
139
|
export type { SandboxToHostMessage } from "./bridge/messages/sandboxToHost.js"
|
|
@@ -151,6 +161,7 @@ export {
|
|
|
151
161
|
createCustomBlockHost,
|
|
152
162
|
} from "./host/createCustomBlockHost.js"
|
|
153
163
|
export type {
|
|
164
|
+
ConnectSuccessMessage,
|
|
154
165
|
CustomBlockHostDataSourcesPayload,
|
|
155
166
|
CustomBlockHostInitialDataSources,
|
|
156
167
|
CustomBlockHostInitialState,
|
package/src/index.ts
CHANGED
|
@@ -79,8 +79,10 @@ export type {
|
|
|
79
79
|
export type {
|
|
80
80
|
CustomBlockInitErrorCode,
|
|
81
81
|
CustomBlockInitErrorInfo,
|
|
82
|
+
CustomBlockInitializationErrorCode,
|
|
83
|
+
CustomBlockInitializationErrorInfo,
|
|
82
84
|
} from "./bridge/messages/init.js"
|
|
83
|
-
export {
|
|
85
|
+
export { CustomBlockInitializationError } from "./bridge/messages/init.js"
|
|
84
86
|
export type {
|
|
85
87
|
CustomBlockListUsersErrorCode,
|
|
86
88
|
CustomBlockListUsersErrorInfo,
|
package/src/init.ts
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import type { NotionDataSource } from "./bridge/dataSources/dataSource.js"
|
|
2
2
|
import type { NotionBlockId } from "./bridge/ids.js"
|
|
3
3
|
import { loadManifest } from "./bridge/loadManifest.js"
|
|
4
|
-
import {
|
|
5
|
-
CustomBlockInitError,
|
|
6
|
-
type CustomBlockInitErrorCode,
|
|
7
|
-
type InitMessage,
|
|
8
|
-
} from "./bridge/messages/init.js"
|
|
4
|
+
import { CustomBlockInitializationError } from "./bridge/messages/init.js"
|
|
9
5
|
import type { CustomBlockPage } from "./bridge/pages/page.js"
|
|
10
6
|
import type { NotionParent } from "./bridge/parent.js"
|
|
11
7
|
import { customBlockHost } from "./bridge/sandboxClient.js"
|
|
12
8
|
import type { NotionTheme } from "./bridge/theme.js"
|
|
13
9
|
import type { NotionUser } from "./bridge/users/user.js"
|
|
10
|
+
import { unreachable } from "./utils.js"
|
|
14
11
|
|
|
15
12
|
/**
|
|
16
|
-
* The payload sent by the host in the `init` message in response to the sandbox's `
|
|
13
|
+
* The payload sent by the host in the `init` message in response to the sandbox's `connect` message.
|
|
17
14
|
*/
|
|
18
15
|
export type CustomBlockInitPayload = {
|
|
19
16
|
theme: NotionTheme
|
|
@@ -30,16 +27,17 @@ export type CustomBlockInitPayload = {
|
|
|
30
27
|
* `<NotionCustomBlock>` catches this specifically and falls back to a standalone preview with a
|
|
31
28
|
* warning banner. Direct callers can `instanceof` it to apply their own policy.
|
|
32
29
|
*/
|
|
33
|
-
export class NotInIframeError extends
|
|
30
|
+
export class NotInIframeError extends CustomBlockInitializationError {
|
|
34
31
|
constructor(message: string = NOT_IN_IFRAME_MESSAGE) {
|
|
35
|
-
super(
|
|
32
|
+
super({
|
|
33
|
+
code: "not_in_iframe",
|
|
34
|
+
message,
|
|
35
|
+
isRetryable: false,
|
|
36
|
+
})
|
|
36
37
|
this.name = "NotInIframeError"
|
|
37
|
-
this.code = "not_in_iframe"
|
|
38
|
-
this.isRetryable = false
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
code:
|
|
42
|
-
isRetryable: boolean
|
|
40
|
+
declare code: "not_in_iframe"
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
/**
|
|
@@ -64,9 +62,10 @@ let initPromise: Promise<CustomBlockInitPayload> | undefined
|
|
|
64
62
|
|
|
65
63
|
/**
|
|
66
64
|
* Performs the SDK <-> host handshake: loads `custom_blocks.json`, posts
|
|
67
|
-
* `
|
|
65
|
+
* `connect`, then awaits the host's `init` message. Resolves after the sandbox
|
|
66
|
+
* applies the payload and returns `initResult.success`.
|
|
68
67
|
*
|
|
69
|
-
* Rejects with a `
|
|
68
|
+
* Rejects with a `CustomBlockInitializationError` if initialization fails.
|
|
70
69
|
*
|
|
71
70
|
* Idempotent: subsequent calls return the same promise as the first and ignore any new options.
|
|
72
71
|
* Mount your React tree (or call any SDK hook / `customBlock.subscribe`) only after the
|
|
@@ -76,61 +75,75 @@ export function initCustomBlock(
|
|
|
76
75
|
opts: InitCustomBlockOptions = {},
|
|
77
76
|
): Promise<CustomBlockInitPayload> {
|
|
78
77
|
if (initPromise === undefined) {
|
|
79
|
-
initPromise = (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (typeof window !== "undefined" && window.parent === window) {
|
|
84
|
-
throw new NotInIframeError()
|
|
85
|
-
}
|
|
78
|
+
initPromise = initializeCustomBlock(opts)
|
|
79
|
+
}
|
|
80
|
+
return initPromise
|
|
81
|
+
}
|
|
86
82
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
83
|
+
async function initializeCustomBlock(
|
|
84
|
+
opts: InitCustomBlockOptions,
|
|
85
|
+
): Promise<CustomBlockInitPayload> {
|
|
86
|
+
try {
|
|
87
|
+
// Fail fast with a typed error when rendered as a standalone tab and not in a parent frame.
|
|
88
|
+
// Otherwise, it would eventually hit the timeout, since `postMessage` to `window.parent`
|
|
89
|
+
// would just hit the same window and never arrive.
|
|
90
|
+
if (typeof window !== "undefined" && window.parent === window) {
|
|
91
|
+
throw new NotInIframeError()
|
|
92
|
+
}
|
|
90
93
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
message = await customBlockHost.awaitInit(
|
|
95
|
-
AbortSignal.timeout(timeoutMs),
|
|
96
|
-
)
|
|
97
|
-
} catch (error) {
|
|
98
|
-
if (isTimeoutError(error)) {
|
|
99
|
-
throw new CustomBlockInitError({
|
|
100
|
-
code: "init_timeout",
|
|
101
|
-
message: "Host did not respond to init before the timeout.",
|
|
102
|
-
isRetryable: true,
|
|
103
|
-
})
|
|
104
|
-
}
|
|
105
|
-
throw error
|
|
106
|
-
}
|
|
107
|
-
if (message.status === "error") {
|
|
108
|
-
console.error(
|
|
109
|
-
`[custom-blocks-sdk] host reported init error (${message.error.code}): ${message.error.message}`,
|
|
110
|
-
)
|
|
111
|
-
throw new CustomBlockInitError(message.error)
|
|
112
|
-
}
|
|
94
|
+
// Load the manifest and send it to the host.
|
|
95
|
+
const manifestResult = await loadManifest()
|
|
96
|
+
customBlockHost.sendConnect(manifestResult)
|
|
113
97
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
98
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_INIT_TIMEOUT_MS
|
|
99
|
+
await awaitHostInitWithTimeout(timeoutMs)
|
|
100
|
+
|
|
101
|
+
const hostState = customBlockHost.getState()
|
|
102
|
+
switch (hostState.status) {
|
|
103
|
+
case "initialized":
|
|
104
|
+
return {
|
|
105
|
+
theme: hostState.theme,
|
|
106
|
+
blockId: hostState.blockId,
|
|
107
|
+
parent: hostState.parent,
|
|
108
|
+
page: hostState.page,
|
|
109
|
+
currentUser: hostState.currentUser,
|
|
110
|
+
dataSources: hostState.dataSources,
|
|
111
|
+
}
|
|
112
|
+
case "uninitialized":
|
|
113
|
+
throw new CustomBlockInitializationError({
|
|
117
114
|
code: "context_unavailable",
|
|
118
115
|
message: "Host block payload is unavailable.",
|
|
119
116
|
isRetryable: true,
|
|
120
117
|
})
|
|
121
|
-
|
|
118
|
+
default:
|
|
119
|
+
return unreachable(hostState)
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (error instanceof CustomBlockInitializationError) {
|
|
123
|
+
throw error
|
|
124
|
+
}
|
|
125
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
126
|
+
throw new CustomBlockInitializationError({
|
|
127
|
+
code: "unknown_error",
|
|
128
|
+
message,
|
|
129
|
+
isRetryable: false,
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
}
|
|
122
133
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
134
|
+
async function awaitHostInitWithTimeout(timeoutMs: number): Promise<void> {
|
|
135
|
+
try {
|
|
136
|
+
await customBlockHost.awaitInit(AbortSignal.timeout(timeoutMs))
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if (isTimeoutError(error)) {
|
|
139
|
+
throw new CustomBlockInitializationError({
|
|
140
|
+
code: "init_timeout",
|
|
141
|
+
message: "Host did not respond to init before the timeout.",
|
|
142
|
+
isRetryable: true,
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
throw error
|
|
132
146
|
}
|
|
133
|
-
return initPromise
|
|
134
147
|
}
|
|
135
148
|
|
|
136
149
|
function isTimeoutError(error: unknown): boolean {
|
package/src/react/index.ts
CHANGED
|
@@ -8,11 +8,13 @@ const previewPageId = v.parse(notionPageIdSchema, "")
|
|
|
8
8
|
export function seedStandalonePreviewState() {
|
|
9
9
|
customBlockHost.setMockState({
|
|
10
10
|
type: "init",
|
|
11
|
+
initializationId: "standalone-preview",
|
|
11
12
|
status: "success",
|
|
12
13
|
theme: "light",
|
|
13
14
|
blockId: previewBlockId,
|
|
14
15
|
parent: { type: "page_id", page_id: previewPageId },
|
|
15
16
|
page: { id: previewPageId, parent: { type: "workspace", workspace: true } },
|
|
17
|
+
manifest: { version: 1, dataSources: {} },
|
|
16
18
|
dataSources: { bindings: {} },
|
|
17
19
|
currentUser: {
|
|
18
20
|
object: "user",
|
|
@@ -1,27 +1,24 @@
|
|
|
1
1
|
import { useEffect, useState } from "react"
|
|
2
|
-
import {
|
|
2
|
+
import type { CustomBlockInitializationError } from "../bridge/messages/init.js"
|
|
3
3
|
import {
|
|
4
4
|
type CustomBlockInitPayload,
|
|
5
5
|
type InitCustomBlockOptions,
|
|
6
6
|
initCustomBlock,
|
|
7
|
-
NotInIframeError,
|
|
8
7
|
} from "../init.js"
|
|
9
8
|
|
|
10
|
-
export type CustomBlockInitFailure = CustomBlockInitError | NotInIframeError
|
|
11
|
-
|
|
12
9
|
/**
|
|
13
10
|
* Discriminated state returned by {@link useCustomBlockInit}.
|
|
14
11
|
*
|
|
15
12
|
* Branch on `isLoaded`/`error`:
|
|
16
13
|
* - `{ isLoaded: false, error: undefined }` — handshake in progress.
|
|
17
|
-
* - `{ isLoaded: false, error:
|
|
18
|
-
* `
|
|
14
|
+
* - `{ isLoaded: false, error: CustomBlockInitializationError }` — handshake failed (most commonly a
|
|
15
|
+
* `CustomBlockInitializationError` with code `init_timeout` because the host never sent `init`).
|
|
19
16
|
* - `{ isLoaded: true, initial }` — handshake complete; safe to render
|
|
20
17
|
* children that call `useTheme`, `useBlockId`, etc.
|
|
21
18
|
*/
|
|
22
19
|
export type UseCustomBlockInitResult =
|
|
23
20
|
| { isLoaded: false; error: undefined }
|
|
24
|
-
| { isLoaded: false; error:
|
|
21
|
+
| { isLoaded: false; error: CustomBlockInitializationError }
|
|
25
22
|
| { isLoaded: true; error: undefined; initial: CustomBlockInitPayload }
|
|
26
23
|
|
|
27
24
|
/**
|
|
@@ -56,12 +53,9 @@ export function useCustomBlockInit(
|
|
|
56
53
|
setState({ isLoaded: true, error: undefined, initial })
|
|
57
54
|
}
|
|
58
55
|
},
|
|
59
|
-
|
|
56
|
+
error => {
|
|
60
57
|
if (!cancelled) {
|
|
61
|
-
setState({
|
|
62
|
-
isLoaded: false,
|
|
63
|
-
error: normalizeInitError(err),
|
|
64
|
-
})
|
|
58
|
+
setState({ isLoaded: false, error })
|
|
65
59
|
}
|
|
66
60
|
},
|
|
67
61
|
)
|
|
@@ -74,18 +68,3 @@ export function useCustomBlockInit(
|
|
|
74
68
|
}, [])
|
|
75
69
|
return state
|
|
76
70
|
}
|
|
77
|
-
|
|
78
|
-
function normalizeInitError(error: unknown): CustomBlockInitFailure {
|
|
79
|
-
if (
|
|
80
|
-
error instanceof CustomBlockInitError ||
|
|
81
|
-
error instanceof NotInIframeError
|
|
82
|
-
) {
|
|
83
|
-
return error
|
|
84
|
-
}
|
|
85
|
-
const message = error instanceof Error ? error.message : String(error)
|
|
86
|
-
return new CustomBlockInitError({
|
|
87
|
-
code: "unknown_error",
|
|
88
|
-
message,
|
|
89
|
-
isRetryable: false,
|
|
90
|
-
})
|
|
91
|
-
}
|
|
@@ -72,14 +72,15 @@ export function useCurrentUser(): NotionUser {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
* Returns the
|
|
75
|
+
* Returns the authoritative manifest received from the host — the semantic
|
|
76
76
|
* data-source keys plus their declared `name`, `description`, and property declarations.
|
|
77
|
-
*
|
|
77
|
+
*
|
|
78
|
+
* Throws if called before `initCustomBlock` has resolved.
|
|
78
79
|
*
|
|
79
80
|
* This is the configuration the block declared, not host-resolved bindings: use
|
|
80
81
|
* `useDataSource(key)` for rows and resolved schema. Handy for enumerating the declared
|
|
81
82
|
* data-source keys (e.g. picking a default key, or rendering a key switcher).
|
|
82
83
|
*/
|
|
83
|
-
export function useManifest(): CustomBlockManifest
|
|
84
|
+
export function useManifest(): CustomBlockManifest {
|
|
84
85
|
return useSyncExternalStore(customBlock.subscribe, customBlock.getManifest)
|
|
85
86
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ready.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/bridge/messages/ready.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AA0B5B;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;;IAG7B;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BASH,CAAA;AAEF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,kBAAkB,CAAC,CAAA"}
|