@notionhq/custom-blocks-host 0.1.8 → 0.1.10
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/README.md +43 -24
- package/dist/createCustomBlockHost.d.ts +13 -11
- package/dist/createCustomBlockHost.d.ts.map +1 -1
- package/dist/createCustomBlockHost.js +35 -106
- package/dist/lifecycle/initializationController.d.ts +9 -4
- package/dist/lifecycle/initializationController.d.ts.map +1 -1
- package/dist/lifecycle/initializationController.js +34 -26
- package/dist/lifecycle/types.d.ts +5 -1
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/messages/invalidSandboxMessage.js +1 -1
- package/dist/messages/sandboxMessageRouter.d.ts +35 -0
- package/dist/messages/sandboxMessageRouter.d.ts.map +1 -0
- package/dist/messages/sandboxMessageRouter.js +107 -0
- package/dist/messages/types.d.ts +1 -1
- package/dist/messages/types.d.ts.map +1 -1
- package/dist/protocol/index.d.ts +3 -2
- package/dist/protocol/index.js +3 -2
- package/dist/protocol/messages/isInitHandshakeMessage.d.ts +4 -0
- package/dist/protocol/messages/isInitHandshakeMessage.js +11 -0
- package/dist/protocol/{messagePayload.d.ts → messages/messagePayload.d.ts} +1 -1
- package/dist/queries/types.d.ts +1 -1
- package/dist/queries/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/createCustomBlockHost.ts +49 -134
- package/src/lifecycle/initializationController.ts +54 -33
- package/src/lifecycle/types.ts +5 -1
- package/src/messages/invalidSandboxMessage.ts +1 -1
- package/src/messages/sandboxMessageRouter.ts +176 -0
- package/src/messages/types.ts +1 -1
- package/src/queries/types.ts +1 -1
- /package/dist/protocol/{incomingType.d.ts → messages/incomingType.d.ts} +0 -0
- /package/dist/protocol/{incomingType.js → messages/incomingType.js} +0 -0
- /package/dist/protocol/{messagePayload.js → messages/messagePayload.js} +0 -0
package/src/lifecycle/types.ts
CHANGED
|
@@ -34,7 +34,11 @@ export type CustomBlockHostInitialState = {
|
|
|
34
34
|
blockId: NotionBlockId
|
|
35
35
|
parent: NotionParent
|
|
36
36
|
page: CustomBlockPage
|
|
37
|
-
|
|
37
|
+
/**
|
|
38
|
+
* The host's persisted manifest, when one exists. If omitted, the host
|
|
39
|
+
* requires the sandbox to provide a manifest in `connect`.
|
|
40
|
+
*/
|
|
41
|
+
manifest?: CustomBlockManifest
|
|
38
42
|
dataSources: CustomBlockHostInitialDataSources
|
|
39
43
|
currentUser: NotionUser
|
|
40
44
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readIncomingType } from "@notionhq/custom-blocks-protocol/incomingType.js"
|
|
1
|
+
import { readIncomingType } from "@notionhq/custom-blocks-protocol/messages/incomingType.js"
|
|
2
2
|
import type { InvalidSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/invalidSandboxMessage.js"
|
|
3
3
|
|
|
4
4
|
export type InvalidSandboxMessageResponse = {
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import type { ConnectMessage } from "@notionhq/custom-blocks-protocol/messages/connect.js"
|
|
2
|
+
import type { CreatePageMessage } from "@notionhq/custom-blocks-protocol/messages/createPage.js"
|
|
3
|
+
import type { GetPageMessage } from "@notionhq/custom-blocks-protocol/messages/getPage.js"
|
|
4
|
+
import type { GetUserMessage } from "@notionhq/custom-blocks-protocol/messages/getUser.js"
|
|
5
|
+
import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
|
|
6
|
+
import type { InitResultMessage } from "@notionhq/custom-blocks-protocol/messages/initResult.js"
|
|
7
|
+
import { isInitHandshakeMessage } from "@notionhq/custom-blocks-protocol/messages/isInitHandshakeMessage.js"
|
|
8
|
+
import type { ListUsersMessage } from "@notionhq/custom-blocks-protocol/messages/listUsers.js"
|
|
9
|
+
import type { QueryDataSourceMessage } from "@notionhq/custom-blocks-protocol/messages/queryDataSource.js"
|
|
10
|
+
import type { ResizeMessage } from "@notionhq/custom-blocks-protocol/messages/resize.js"
|
|
11
|
+
import { sandboxToHostMessageSchema } from "@notionhq/custom-blocks-protocol/messages/sandboxToHost.js"
|
|
12
|
+
import type { UnsubscribeDataSourceQueryMessage } from "@notionhq/custom-blocks-protocol/messages/unsubscribeDataSourceQuery.js"
|
|
13
|
+
import type { UpdatePageMessage } from "@notionhq/custom-blocks-protocol/messages/updatePage.js"
|
|
14
|
+
import * as v from "valibot"
|
|
15
|
+
import { initErrorForFailureReason } from "../lifecycle/initErrors.js"
|
|
16
|
+
import type { InitializationController } from "../lifecycle/initializationController.js"
|
|
17
|
+
import type { InitStatus } from "../lifecycle/types.js"
|
|
18
|
+
import { unreachable } from "../utils.js"
|
|
19
|
+
import { getInvalidSandboxMessageResponse } from "./invalidSandboxMessage.js"
|
|
20
|
+
import type { CustomBlockHostLogDirection } from "./types.js"
|
|
21
|
+
|
|
22
|
+
const initializationIdentitySchema = v.object({
|
|
23
|
+
initializationId: v.string(),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export type SandboxMessageRouterOptions = {
|
|
27
|
+
iframe: HTMLIFrameElement
|
|
28
|
+
sandboxOrigin: string
|
|
29
|
+
getInitStatus: () => InitStatus
|
|
30
|
+
emit: (direction: CustomBlockHostLogDirection, payload: unknown) => void
|
|
31
|
+
post: (message: HostToSandboxMessage) => void
|
|
32
|
+
onInvalidConnect: InitializationController["handleInvalidConnect"]
|
|
33
|
+
onInvalidInitResult: InitializationController["handleInvalidInitResult"]
|
|
34
|
+
onConnect: (message: ConnectMessage) => void
|
|
35
|
+
onInitResult: (message: InitResultMessage) => void
|
|
36
|
+
onQueryDataSource: (message: QueryDataSourceMessage) => void | Promise<void>
|
|
37
|
+
onUnsubscribeDataSourceQuery: (
|
|
38
|
+
message: UnsubscribeDataSourceQueryMessage,
|
|
39
|
+
) => void
|
|
40
|
+
onCreatePage: (message: CreatePageMessage) => void | Promise<void>
|
|
41
|
+
onGetPage: (message: GetPageMessage) => void | Promise<void>
|
|
42
|
+
onGetUser: (message: GetUserMessage) => void | Promise<void>
|
|
43
|
+
onListUsers: (message: ListUsersMessage) => void | Promise<void>
|
|
44
|
+
onUpdatePage: (message: UpdatePageMessage) => void | Promise<void>
|
|
45
|
+
onResize: (message: ResizeMessage) => void
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function createSandboxMessageRouter(
|
|
49
|
+
options: SandboxMessageRouterOptions,
|
|
50
|
+
): (event: MessageEvent) => void {
|
|
51
|
+
const {
|
|
52
|
+
iframe,
|
|
53
|
+
sandboxOrigin,
|
|
54
|
+
getInitStatus,
|
|
55
|
+
emit,
|
|
56
|
+
post,
|
|
57
|
+
onInvalidConnect,
|
|
58
|
+
onInvalidInitResult,
|
|
59
|
+
onConnect,
|
|
60
|
+
onInitResult,
|
|
61
|
+
onQueryDataSource,
|
|
62
|
+
onUnsubscribeDataSourceQuery,
|
|
63
|
+
onCreatePage,
|
|
64
|
+
onGetPage,
|
|
65
|
+
onGetUser,
|
|
66
|
+
onListUsers,
|
|
67
|
+
onUpdatePage,
|
|
68
|
+
onResize,
|
|
69
|
+
} = options
|
|
70
|
+
|
|
71
|
+
function onInvalidSandboxMessage(
|
|
72
|
+
data: unknown,
|
|
73
|
+
issues: readonly v.BaseIssue<unknown>[],
|
|
74
|
+
) {
|
|
75
|
+
emit("warn", {
|
|
76
|
+
message: "Ignored unsupported sandbox message",
|
|
77
|
+
payload: data,
|
|
78
|
+
issues,
|
|
79
|
+
})
|
|
80
|
+
const response = getInvalidSandboxMessageResponse(data)
|
|
81
|
+
|
|
82
|
+
if (response.incomingType === "connect") {
|
|
83
|
+
const parsedIdentity = v.safeParse(initializationIdentitySchema, data)
|
|
84
|
+
if (parsedIdentity.success) {
|
|
85
|
+
const responseType = onInvalidConnect({
|
|
86
|
+
initializationId: parsedIdentity.output.initializationId,
|
|
87
|
+
error: initErrorForFailureReason("invalid_connect_payload"),
|
|
88
|
+
})
|
|
89
|
+
if (responseType === "initErrorSent") {
|
|
90
|
+
// The controller already sent `init.error`, so no need to also send a NACK.
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (response.incomingType === "initResult") {
|
|
97
|
+
const parsedIdentity = v.safeParse(initializationIdentitySchema, data)
|
|
98
|
+
onInvalidInitResult({
|
|
99
|
+
initializationId: parsedIdentity.success
|
|
100
|
+
? parsedIdentity.output.initializationId
|
|
101
|
+
: undefined,
|
|
102
|
+
error: initErrorForFailureReason("invalid_init_result_payload"),
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Other messages get NACKed indicating they were invalid and skipped
|
|
107
|
+
if (response.nack !== undefined) {
|
|
108
|
+
post(response.nack)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return function onMessage(event: MessageEvent) {
|
|
113
|
+
// Verify the sender is this host's iframe, not another window with the same origin.
|
|
114
|
+
const isExpectedSource = event.source === iframe.contentWindow
|
|
115
|
+
// Verify the sender's origin, since the iframe can navigate to another origin.
|
|
116
|
+
const isExpectedOrigin = event.origin === sandboxOrigin
|
|
117
|
+
if (!isExpectedSource || !isExpectedOrigin) {
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Parse and validate the message from the sandbox.
|
|
122
|
+
const parsed = v.safeParse(sandboxToHostMessageSchema, event.data)
|
|
123
|
+
if (!parsed.success) {
|
|
124
|
+
onInvalidSandboxMessage(event.data, parsed.issues)
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const message = parsed.output
|
|
129
|
+
if (!isInitHandshakeMessage(message) && getInitStatus() !== "success") {
|
|
130
|
+
emit("warn", {
|
|
131
|
+
message: `Ignored ${message.type} before initialization completed`,
|
|
132
|
+
payload: message,
|
|
133
|
+
})
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
emit("in", message)
|
|
138
|
+
switch (message.type) {
|
|
139
|
+
case "connect":
|
|
140
|
+
onConnect(message)
|
|
141
|
+
return
|
|
142
|
+
case "initResult":
|
|
143
|
+
onInitResult(message)
|
|
144
|
+
return
|
|
145
|
+
case "queryDataSource":
|
|
146
|
+
void onQueryDataSource(message)
|
|
147
|
+
return
|
|
148
|
+
case "unsubscribeDataSourceQuery":
|
|
149
|
+
onUnsubscribeDataSourceQuery(message)
|
|
150
|
+
return
|
|
151
|
+
case "createPage":
|
|
152
|
+
void onCreatePage(message)
|
|
153
|
+
return
|
|
154
|
+
case "getPage":
|
|
155
|
+
void onGetPage(message)
|
|
156
|
+
return
|
|
157
|
+
case "getUser":
|
|
158
|
+
void onGetUser(message)
|
|
159
|
+
return
|
|
160
|
+
case "listUsers":
|
|
161
|
+
void onListUsers(message)
|
|
162
|
+
return
|
|
163
|
+
case "updatePage":
|
|
164
|
+
void onUpdatePage(message)
|
|
165
|
+
return
|
|
166
|
+
case "resize":
|
|
167
|
+
onResize(message)
|
|
168
|
+
return
|
|
169
|
+
case "invalidHostMessage":
|
|
170
|
+
// The inbound log records this rejection. Do not reply, to avoid an error-response loop.
|
|
171
|
+
return
|
|
172
|
+
default:
|
|
173
|
+
unreachable(message)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
package/src/messages/types.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { BridgeMessagePayload } from "@notionhq/custom-blocks-protocol/messagePayload.js"
|
|
2
1
|
import type { CreatePageMessage } from "@notionhq/custom-blocks-protocol/messages/createPage.js"
|
|
3
2
|
import type {
|
|
4
3
|
CreatePageResultMessage,
|
|
@@ -21,6 +20,7 @@ import type {
|
|
|
21
20
|
ListUsersMessage,
|
|
22
21
|
ListUsersResultMessage,
|
|
23
22
|
} from "@notionhq/custom-blocks-protocol/messages/listUsers.js"
|
|
23
|
+
import type { BridgeMessagePayload } from "@notionhq/custom-blocks-protocol/messages/messagePayload.js"
|
|
24
24
|
import type { QueryDataSourceMessage } from "@notionhq/custom-blocks-protocol/messages/queryDataSource.js"
|
|
25
25
|
import type { ResizeMessage } from "@notionhq/custom-blocks-protocol/messages/resize.js"
|
|
26
26
|
import type { UpdatePageMessage } from "@notionhq/custom-blocks-protocol/messages/updatePage.js"
|
package/src/queries/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BridgeMessagePayload } from "@notionhq/custom-blocks-protocol/messagePayload.js"
|
|
1
|
+
import type { BridgeMessagePayload } from "@notionhq/custom-blocks-protocol/messages/messagePayload.js"
|
|
2
2
|
import type {
|
|
3
3
|
CustomBlockQueryDataSourceErrorInfo,
|
|
4
4
|
QueryDataSourceResultMessage,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|