@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
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { isInitHandshakeMessage } from "../protocol/messages/isInitHandshakeMessage.js";
|
|
2
|
+
import { sandboxToHostMessageSchema } from "../protocol/messages/sandboxToHost.js";
|
|
3
|
+
import * as v from "valibot";
|
|
4
|
+
import { initErrorForFailureReason } from "../lifecycle/initErrors.js";
|
|
5
|
+
import { unreachable } from "../utils.js";
|
|
6
|
+
import { getInvalidSandboxMessageResponse } from "./invalidSandboxMessage.js";
|
|
7
|
+
const initializationIdentitySchema = v.object({
|
|
8
|
+
initializationId: v.string(),
|
|
9
|
+
});
|
|
10
|
+
export function createSandboxMessageRouter(options) {
|
|
11
|
+
const { iframe, sandboxOrigin, getInitStatus, emit, post, onInvalidConnect, onInvalidInitResult, onConnect, onInitResult, onQueryDataSource, onUnsubscribeDataSourceQuery, onCreatePage, onGetPage, onGetUser, onListUsers, onUpdatePage, onResize, } = options;
|
|
12
|
+
function onInvalidSandboxMessage(data, issues) {
|
|
13
|
+
emit("warn", {
|
|
14
|
+
message: "Ignored unsupported sandbox message",
|
|
15
|
+
payload: data,
|
|
16
|
+
issues,
|
|
17
|
+
});
|
|
18
|
+
const response = getInvalidSandboxMessageResponse(data);
|
|
19
|
+
if (response.incomingType === "connect") {
|
|
20
|
+
const parsedIdentity = v.safeParse(initializationIdentitySchema, data);
|
|
21
|
+
if (parsedIdentity.success) {
|
|
22
|
+
const responseType = onInvalidConnect({
|
|
23
|
+
initializationId: parsedIdentity.output.initializationId,
|
|
24
|
+
error: initErrorForFailureReason("invalid_connect_payload"),
|
|
25
|
+
});
|
|
26
|
+
if (responseType === "initErrorSent") {
|
|
27
|
+
// The controller already sent `init.error`, so no need to also send a NACK.
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (response.incomingType === "initResult") {
|
|
33
|
+
const parsedIdentity = v.safeParse(initializationIdentitySchema, data);
|
|
34
|
+
onInvalidInitResult({
|
|
35
|
+
initializationId: parsedIdentity.success
|
|
36
|
+
? parsedIdentity.output.initializationId
|
|
37
|
+
: undefined,
|
|
38
|
+
error: initErrorForFailureReason("invalid_init_result_payload"),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// Other messages get NACKed indicating they were invalid and skipped
|
|
42
|
+
if (response.nack !== undefined) {
|
|
43
|
+
post(response.nack);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return function onMessage(event) {
|
|
47
|
+
// Verify the sender is this host's iframe, not another window with the same origin.
|
|
48
|
+
const isExpectedSource = event.source === iframe.contentWindow;
|
|
49
|
+
// Verify the sender's origin, since the iframe can navigate to another origin.
|
|
50
|
+
const isExpectedOrigin = event.origin === sandboxOrigin;
|
|
51
|
+
if (!isExpectedSource || !isExpectedOrigin) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Parse and validate the message from the sandbox.
|
|
55
|
+
const parsed = v.safeParse(sandboxToHostMessageSchema, event.data);
|
|
56
|
+
if (!parsed.success) {
|
|
57
|
+
onInvalidSandboxMessage(event.data, parsed.issues);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const message = parsed.output;
|
|
61
|
+
if (!isInitHandshakeMessage(message) && getInitStatus() !== "success") {
|
|
62
|
+
emit("warn", {
|
|
63
|
+
message: `Ignored ${message.type} before initialization completed`,
|
|
64
|
+
payload: message,
|
|
65
|
+
});
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
emit("in", message);
|
|
69
|
+
switch (message.type) {
|
|
70
|
+
case "connect":
|
|
71
|
+
onConnect(message);
|
|
72
|
+
return;
|
|
73
|
+
case "initResult":
|
|
74
|
+
onInitResult(message);
|
|
75
|
+
return;
|
|
76
|
+
case "queryDataSource":
|
|
77
|
+
void onQueryDataSource(message);
|
|
78
|
+
return;
|
|
79
|
+
case "unsubscribeDataSourceQuery":
|
|
80
|
+
onUnsubscribeDataSourceQuery(message);
|
|
81
|
+
return;
|
|
82
|
+
case "createPage":
|
|
83
|
+
void onCreatePage(message);
|
|
84
|
+
return;
|
|
85
|
+
case "getPage":
|
|
86
|
+
void onGetPage(message);
|
|
87
|
+
return;
|
|
88
|
+
case "getUser":
|
|
89
|
+
void onGetUser(message);
|
|
90
|
+
return;
|
|
91
|
+
case "listUsers":
|
|
92
|
+
void onListUsers(message);
|
|
93
|
+
return;
|
|
94
|
+
case "updatePage":
|
|
95
|
+
void onUpdatePage(message);
|
|
96
|
+
return;
|
|
97
|
+
case "resize":
|
|
98
|
+
onResize(message);
|
|
99
|
+
return;
|
|
100
|
+
case "invalidHostMessage":
|
|
101
|
+
// The inbound log records this rejection. Do not reply, to avoid an error-response loop.
|
|
102
|
+
return;
|
|
103
|
+
default:
|
|
104
|
+
unreachable(message);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
package/dist/messages/types.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { BridgeMessagePayload } from "../protocol/messagePayload.js";
|
|
2
1
|
import type { CreatePageMessage } from "../protocol/messages/createPage.js";
|
|
3
2
|
import type { CreatePageResultMessage, CustomBlockCreatePageErrorInfo } from "../protocol/messages/createPageResult.js";
|
|
4
3
|
import type { CustomBlockGetPageErrorInfo, GetPageMessage, GetPageResultMessage } from "../protocol/messages/getPage.js";
|
|
@@ -6,6 +5,7 @@ import type { CustomBlockGetUserErrorInfo, GetUserMessage, GetUserResultMessage
|
|
|
6
5
|
import type { CustomBlockInitErrorInfo } from "../protocol/messages/init.js";
|
|
7
6
|
import type { CustomBlockInitResultErrorInfo } from "../protocol/messages/initResult.js";
|
|
8
7
|
import type { CustomBlockListUsersErrorInfo, ListUsersMessage, ListUsersResultMessage } from "../protocol/messages/listUsers.js";
|
|
8
|
+
import type { BridgeMessagePayload } from "../protocol/messages/messagePayload.js";
|
|
9
9
|
import type { QueryDataSourceMessage } from "../protocol/messages/queryDataSource.js";
|
|
10
10
|
import type { ResizeMessage } from "../protocol/messages/resize.js";
|
|
11
11
|
import type { UpdatePageMessage } from "../protocol/messages/updatePage.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/messages/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/messages/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,OAAO,KAAK,EACX,uBAAuB,EACvB,8BAA8B,EAC9B,MAAM,+DAA+D,CAAA;AACtE,OAAO,KAAK,EACX,2BAA2B,EAC3B,cAAc,EACd,oBAAoB,EACpB,MAAM,sDAAsD,CAAA;AAC7D,OAAO,KAAK,EACX,2BAA2B,EAC3B,cAAc,EACd,oBAAoB,EACpB,MAAM,sDAAsD,CAAA;AAC7D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mDAAmD,CAAA;AACjG,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,yDAAyD,CAAA;AAC7G,OAAO,KAAK,EACX,6BAA6B,EAC7B,gBAAgB,EAChB,sBAAsB,EACtB,MAAM,wDAAwD,CAAA;AAC/D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6DAA6D,CAAA;AACvG,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8DAA8D,CAAA;AAC1G,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qDAAqD,CAAA;AACxF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAChG,OAAO,KAAK,EACX,8BAA8B,EAC9B,uBAAuB,EACvB,MAAM,+DAA+D,CAAA;AACtE,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,qBAAqB,CAAA;AAE/E,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;AAErC,MAAM,MAAM,+BAA+B,GAAG,oBAAoB,CACjE,uBAAuB,EACvB,8BAA8B,CAC9B,CAAA;AAED,MAAM,MAAM,4BAA4B,GAAG,oBAAoB,CAC9D,oBAAoB,EACpB,2BAA2B,CAC3B,CAAA;AAED,MAAM,MAAM,+BAA+B,GAAG,oBAAoB,CACjE,uBAAuB,EACvB,8BAA8B,CAC9B,CAAA;AAED,MAAM,MAAM,4BAA4B,GAAG,oBAAoB,CAC9D,oBAAoB,EACpB,2BAA2B,CAC3B,CAAA;AAED,MAAM,MAAM,8BAA8B,GAAG,oBAAoB,CAChE,sBAAsB,EACtB,6BAA6B,CAC7B,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACrC,eAAe,EAAE,CAChB,OAAO,EAAE,sBAAsB,KAC3B,YAAY,CAAC,oCAAoC,CAAC,CAAA;IACvD,UAAU,CAAC,EAAE,CACZ,OAAO,EAAE,iBAAiB,KACtB,YAAY,CAAC,+BAA+B,CAAC,CAAA;IAClD,OAAO,CAAC,EAAE,CACT,OAAO,EAAE,cAAc,KACnB,YAAY,CAAC,4BAA4B,CAAC,CAAA;IAC/C,OAAO,CAAC,EAAE,CACT,OAAO,EAAE,cAAc,KACnB,YAAY,CAAC,4BAA4B,CAAC,CAAA;IAC/C,SAAS,CAAC,EAAE,CACX,OAAO,EAAE,gBAAgB,KACrB,YAAY,CAAC,8BAA8B,CAAC,CAAA;IACjD,UAAU,CAAC,EAAE,CACZ,OAAO,EAAE,iBAAiB,KACtB,YAAY,CAAC,+BAA+B,CAAC,CAAA;IAClD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;CACzC,CAAA;AAED,MAAM,MAAM,oCAAoC,GAC7C,wBAAwB,GACxB,8BAA8B,CAAA;AAEjC,MAAM,MAAM,2BAA2B,GAAG,IAAI,GAAG,KAAK,GAAG,MAAM,CAAA"}
|
package/dist/protocol/index.d.ts
CHANGED
|
@@ -15,9 +15,7 @@ export * from "./dataSources/propertySchema.js";
|
|
|
15
15
|
export * from "./dataSources/recordPointer.js";
|
|
16
16
|
export * from "./errors.js";
|
|
17
17
|
export * from "./ids.js";
|
|
18
|
-
export * from "./incomingType.js";
|
|
19
18
|
export * from "./manifest.js";
|
|
20
|
-
export * from "./messagePayload.js";
|
|
21
19
|
export * from "./messages/connect.js";
|
|
22
20
|
export * from "./messages/contrastModeChanged.js";
|
|
23
21
|
export * from "./messages/createPage.js";
|
|
@@ -27,11 +25,14 @@ export * from "./messages/dataSourcesChanged.js";
|
|
|
27
25
|
export * from "./messages/getPage.js";
|
|
28
26
|
export * from "./messages/getUser.js";
|
|
29
27
|
export * from "./messages/hostToSandbox.js";
|
|
28
|
+
export * from "./messages/incomingType.js";
|
|
30
29
|
export * from "./messages/init.js";
|
|
31
30
|
export * from "./messages/initResult.js";
|
|
32
31
|
export * from "./messages/invalidHostMessage.js";
|
|
33
32
|
export * from "./messages/invalidSandboxMessage.js";
|
|
33
|
+
export * from "./messages/isInitHandshakeMessage.js";
|
|
34
34
|
export * from "./messages/listUsers.js";
|
|
35
|
+
export * from "./messages/messagePayload.js";
|
|
35
36
|
export * from "./messages/pageChanged.js";
|
|
36
37
|
export * from "./messages/parentChanged.js";
|
|
37
38
|
export * from "./messages/queryDataSource.js";
|
package/dist/protocol/index.js
CHANGED
|
@@ -15,9 +15,7 @@ export * from "./dataSources/propertySchema.js";
|
|
|
15
15
|
export * from "./dataSources/recordPointer.js";
|
|
16
16
|
export * from "./errors.js";
|
|
17
17
|
export * from "./ids.js";
|
|
18
|
-
export * from "./incomingType.js";
|
|
19
18
|
export * from "./manifest.js";
|
|
20
|
-
export * from "./messagePayload.js";
|
|
21
19
|
export * from "./messages/connect.js";
|
|
22
20
|
export * from "./messages/contrastModeChanged.js";
|
|
23
21
|
export * from "./messages/createPage.js";
|
|
@@ -27,11 +25,14 @@ export * from "./messages/dataSourcesChanged.js";
|
|
|
27
25
|
export * from "./messages/getPage.js";
|
|
28
26
|
export * from "./messages/getUser.js";
|
|
29
27
|
export * from "./messages/hostToSandbox.js";
|
|
28
|
+
export * from "./messages/incomingType.js";
|
|
30
29
|
export * from "./messages/init.js";
|
|
31
30
|
export * from "./messages/initResult.js";
|
|
32
31
|
export * from "./messages/invalidHostMessage.js";
|
|
33
32
|
export * from "./messages/invalidSandboxMessage.js";
|
|
33
|
+
export * from "./messages/isInitHandshakeMessage.js";
|
|
34
34
|
export * from "./messages/listUsers.js";
|
|
35
|
+
export * from "./messages/messagePayload.js";
|
|
35
36
|
export * from "./messages/pageChanged.js";
|
|
36
37
|
export * from "./messages/parentChanged.js";
|
|
37
38
|
export * from "./messages/queryDataSource.js";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { HostToSandboxMessage } from "./hostToSandbox.js";
|
|
2
|
+
import type { SandboxToHostMessage } from "./sandboxToHost.js";
|
|
3
|
+
/** Returns whether a message belongs to the initialization handshake. */
|
|
4
|
+
export declare function isInitHandshakeMessage(message: SandboxToHostMessage | HostToSandboxMessage): boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CustomBlockErrorInfo } from "
|
|
1
|
+
import type { CustomBlockErrorInfo } from "../errors.js";
|
|
2
2
|
type WithTypedErrorInfo<T, TErrorInfo extends CustomBlockErrorInfo | undefined> = TErrorInfo extends CustomBlockErrorInfo ? T extends {
|
|
3
3
|
error: CustomBlockErrorInfo;
|
|
4
4
|
} ? Omit<T, "error"> & {
|
package/dist/queries/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BridgeMessagePayload } from "../protocol/messagePayload.js";
|
|
1
|
+
import type { BridgeMessagePayload } from "../protocol/messages/messagePayload.js";
|
|
2
2
|
import type { CustomBlockQueryDataSourceErrorInfo, QueryDataSourceResultMessage } from "../protocol/messages/queryDataSourceResult.js";
|
|
3
3
|
export type CustomBlockHostQueryDataSourceResult = BridgeMessagePayload<QueryDataSourceResultMessage, CustomBlockQueryDataSourceErrorInfo>;
|
|
4
4
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/queries/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/queries/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6DAA6D,CAAA;AACvG,OAAO,KAAK,EACX,mCAAmC,EACnC,4BAA4B,EAC5B,MAAM,oEAAoE,CAAA;AAE3E,MAAM,MAAM,oCAAoC,GAAG,oBAAoB,CACtE,4BAA4B,EAC5B,mCAAmC,CACnC,CAAA"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
} from "@notionhq/custom-blocks-protocol/contrast.js"
|
|
5
5
|
import type { NotionDataSourceId } from "@notionhq/custom-blocks-protocol/ids.js"
|
|
6
6
|
import type { CustomBlockManifest } from "@notionhq/custom-blocks-protocol/manifest.js"
|
|
7
|
-
import type { ConnectMessage } from "@notionhq/custom-blocks-protocol/messages/connect.js"
|
|
8
7
|
import type { ContrastModeChangedMessage } from "@notionhq/custom-blocks-protocol/messages/contrastModeChanged.js"
|
|
9
8
|
import type { CreatePageMessage } from "@notionhq/custom-blocks-protocol/messages/createPage.js"
|
|
10
9
|
import type { CreatePageResultMessage } from "@notionhq/custom-blocks-protocol/messages/createPageResult.js"
|
|
@@ -19,7 +18,6 @@ import type {
|
|
|
19
18
|
} from "@notionhq/custom-blocks-protocol/messages/getUser.js"
|
|
20
19
|
import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
|
|
21
20
|
import type { CustomBlockInitErrorCode } from "@notionhq/custom-blocks-protocol/messages/init.js"
|
|
22
|
-
import type { InitResultMessage } from "@notionhq/custom-blocks-protocol/messages/initResult.js"
|
|
23
21
|
import type {
|
|
24
22
|
ListUsersMessage,
|
|
25
23
|
ListUsersResultMessage,
|
|
@@ -28,7 +26,6 @@ import type { PageChangedMessage } from "@notionhq/custom-blocks-protocol/messag
|
|
|
28
26
|
import type { ParentChangedMessage } from "@notionhq/custom-blocks-protocol/messages/parentChanged.js"
|
|
29
27
|
import type { QueryDataSourceMessage } from "@notionhq/custom-blocks-protocol/messages/queryDataSource.js"
|
|
30
28
|
import type { QueryDataSourceResultMessage } from "@notionhq/custom-blocks-protocol/messages/queryDataSourceResult.js"
|
|
31
|
-
import { sandboxToHostMessageSchema } from "@notionhq/custom-blocks-protocol/messages/sandboxToHost.js"
|
|
32
29
|
import type { ThemeChangedMessage } from "@notionhq/custom-blocks-protocol/messages/themeChanged.js"
|
|
33
30
|
import type { UpdatePageMessage } from "@notionhq/custom-blocks-protocol/messages/updatePage.js"
|
|
34
31
|
import type { UpdatePageResultMessage } from "@notionhq/custom-blocks-protocol/messages/updatePageResult.js"
|
|
@@ -37,15 +34,13 @@ import type { NotionParent } from "@notionhq/custom-blocks-protocol/parent.js"
|
|
|
37
34
|
import { CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION } from "@notionhq/custom-blocks-protocol/protocolVersion.js"
|
|
38
35
|
import type { NotionTheme } from "@notionhq/custom-blocks-protocol/theme.js"
|
|
39
36
|
import type { NotionUser } from "@notionhq/custom-blocks-protocol/users/user.js"
|
|
40
|
-
import * as v from "valibot"
|
|
41
37
|
import { initErrorForFailureReason } from "./lifecycle/initErrors.js"
|
|
42
38
|
import { createInitializationController } from "./lifecycle/initializationController.js"
|
|
43
39
|
import type {
|
|
44
|
-
ConnectSuccessMessage,
|
|
45
40
|
CustomBlockHostDataSourcesPayload,
|
|
46
41
|
CustomBlockHostInitialState,
|
|
47
42
|
} from "./lifecycle/types.js"
|
|
48
|
-
import {
|
|
43
|
+
import { createSandboxMessageRouter } from "./messages/sandboxMessageRouter.js"
|
|
49
44
|
import type {
|
|
50
45
|
CustomBlockHostHandlers,
|
|
51
46
|
CustomBlockHostInitializationFailure,
|
|
@@ -58,7 +53,6 @@ import {
|
|
|
58
53
|
import type { CustomBlockHostQueryDataSourceResult } from "./queries/types.js"
|
|
59
54
|
import { validateQueryDataSourceFilter } from "./queries/validateQueryDataSourceFilter.js"
|
|
60
55
|
import { validateQueryDataSourceSorts } from "./queries/validateQueryDataSourceSorts.js"
|
|
61
|
-
import { unreachable } from "./utils.js"
|
|
62
56
|
|
|
63
57
|
export type CustomBlockHostOptions = {
|
|
64
58
|
iframe: HTMLIFrameElement
|
|
@@ -68,19 +62,22 @@ export type CustomBlockHostOptions = {
|
|
|
68
62
|
noInitResultTimeoutMs?: number
|
|
69
63
|
initialState: CustomBlockHostInitialState
|
|
70
64
|
handlers: CustomBlockHostHandlers
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
/** Called as soon as the `manifest` is available after `connect`, before `onInitialized` and binding validation. */
|
|
66
|
+
onManifest?: (manifest: CustomBlockManifest) => void
|
|
67
|
+
/** Callback fired when the initialization handshake with the sandbox completed successfully. */
|
|
68
|
+
onInitialized?: (state: {
|
|
69
|
+
manifest: CustomBlockManifest
|
|
70
|
+
initialHeight?: number
|
|
71
|
+
}) => void
|
|
72
|
+
/** Callback fired when the initialization handshake with the sandbox failed or timed out. */
|
|
73
73
|
onInitializationFailure?: (
|
|
74
74
|
error: CustomBlockHostInitializationFailure,
|
|
75
75
|
) => void
|
|
76
76
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* stopped waiting. Either way the host cannot serve it: the embedder
|
|
80
|
-
* should recreate host and iframe together so the document can run a
|
|
81
|
-
* fresh handshake.
|
|
77
|
+
* Callback fired when the current host and iframe must restart.
|
|
78
|
+
* Recreate both to continue.
|
|
82
79
|
*/
|
|
83
|
-
|
|
80
|
+
onRestartRequired?: () => void
|
|
84
81
|
onLog?: (direction: CustomBlockHostLogDirection, payload: unknown) => void
|
|
85
82
|
}
|
|
86
83
|
|
|
@@ -96,10 +93,6 @@ export type CustomBlockHostHandle = {
|
|
|
96
93
|
refreshQuery: (args: { dataSourceId: NotionDataSourceId }) => void
|
|
97
94
|
}
|
|
98
95
|
|
|
99
|
-
const initializationIdentitySchema = v.object({
|
|
100
|
-
initializationId: v.string(),
|
|
101
|
-
})
|
|
102
|
-
|
|
103
96
|
export function createCustomBlockHost(
|
|
104
97
|
options: CustomBlockHostOptions,
|
|
105
98
|
): CustomBlockHostHandle {
|
|
@@ -107,10 +100,10 @@ export function createCustomBlockHost(
|
|
|
107
100
|
iframe,
|
|
108
101
|
initialState,
|
|
109
102
|
handlers,
|
|
110
|
-
|
|
111
|
-
|
|
103
|
+
onManifest,
|
|
104
|
+
onInitialized,
|
|
112
105
|
onInitializationFailure,
|
|
113
|
-
|
|
106
|
+
onRestartRequired,
|
|
114
107
|
onLog,
|
|
115
108
|
sandboxOrigin,
|
|
116
109
|
minBridgeProtocolVersion: configuredMinBridgeProtocolVersion,
|
|
@@ -322,6 +315,13 @@ export function createCustomBlockHost(
|
|
|
322
315
|
}),
|
|
323
316
|
prepareInit(connect) {
|
|
324
317
|
manifest = connect.manifest ?? initialState.manifest
|
|
318
|
+
if (manifest === undefined) {
|
|
319
|
+
return {
|
|
320
|
+
status: "error",
|
|
321
|
+
error: initErrorForFailureReason("manifest_unavailable"),
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
onManifest?.(manifest)
|
|
325
325
|
const resolvedDataSources =
|
|
326
326
|
typeof dataSources === "function" ? dataSources(connect) : dataSources
|
|
327
327
|
const bindingErrorCode = getBindingErrorCode(
|
|
@@ -347,121 +347,35 @@ export function createCustomBlockHost(
|
|
|
347
347
|
minBridgeProtocolVersion,
|
|
348
348
|
noConnectTimeoutMs,
|
|
349
349
|
noInitResultTimeoutMs,
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
350
|
+
onInitResult(message) {
|
|
351
|
+
if (message.status === "success" && manifest !== undefined) {
|
|
352
|
+
onInitialized?.({ manifest, initialHeight: message.initialHeight })
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
onRestartRequired,
|
|
353
356
|
onInitializationFailure,
|
|
354
357
|
})
|
|
355
358
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
emit
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
return
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
if (
|
|
380
|
-
response.incomingType === "initResult" &&
|
|
381
|
-
initializationController.handleInvalidInitResult()
|
|
382
|
-
) {
|
|
383
|
-
if (response.nack !== undefined) {
|
|
384
|
-
post(response.nack)
|
|
385
|
-
}
|
|
386
|
-
return
|
|
387
|
-
}
|
|
388
|
-
if (response.nack !== undefined) {
|
|
389
|
-
post(response.nack)
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
function onMessage(event: MessageEvent) {
|
|
394
|
-
// Ignore messages from windows other than this host's iframe.
|
|
395
|
-
if (event.source !== iframe.contentWindow) {
|
|
396
|
-
return
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// Ignore messages that do not come from the configured sandbox origin.
|
|
400
|
-
if (event.origin !== sandboxOrigin) {
|
|
401
|
-
return
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
const parsed = v.safeParse(sandboxToHostMessageSchema, event.data)
|
|
405
|
-
if (!parsed.success) {
|
|
406
|
-
onInvalidSandboxMessage(event.data, parsed.issues)
|
|
407
|
-
return
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
const message = parsed.output
|
|
411
|
-
emit("in", message)
|
|
412
|
-
|
|
413
|
-
if (
|
|
414
|
-
message.type !== "connect" &&
|
|
415
|
-
message.type !== "initResult" &&
|
|
416
|
-
initializationController.status !== "success"
|
|
417
|
-
) {
|
|
418
|
-
// Ignore all non-handshake messages until initialization succeeds.
|
|
419
|
-
if (message.type !== "resize") {
|
|
420
|
-
emit("warn", {
|
|
421
|
-
message: `Ignored ${message.type} before initialization completed`,
|
|
422
|
-
payload: message,
|
|
423
|
-
})
|
|
424
|
-
}
|
|
425
|
-
return
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
switch (message.type) {
|
|
429
|
-
case "connect":
|
|
430
|
-
initializationController.handleConnect(message)
|
|
431
|
-
return
|
|
432
|
-
case "initResult":
|
|
433
|
-
initializationController.handleInitResult(message)
|
|
434
|
-
return
|
|
435
|
-
case "queryDataSource":
|
|
436
|
-
void handleQuery(message)
|
|
437
|
-
return
|
|
438
|
-
case "unsubscribeDataSourceQuery":
|
|
439
|
-
querySubscriptions.unsubscribe(message.subscriptionId)
|
|
440
|
-
return
|
|
441
|
-
case "createPage":
|
|
442
|
-
void handleCreatePage(message)
|
|
443
|
-
return
|
|
444
|
-
case "getPage":
|
|
445
|
-
void handleGetPage(message)
|
|
446
|
-
return
|
|
447
|
-
case "getUser":
|
|
448
|
-
void handleGetUser(message)
|
|
449
|
-
return
|
|
450
|
-
case "listUsers":
|
|
451
|
-
void handleListUsers(message)
|
|
452
|
-
return
|
|
453
|
-
case "updatePage":
|
|
454
|
-
void handleUpdatePage(message)
|
|
455
|
-
return
|
|
456
|
-
case "resize":
|
|
457
|
-
handlers.resize?.(message)
|
|
458
|
-
return
|
|
459
|
-
case "invalidHostMessage":
|
|
460
|
-
return
|
|
461
|
-
default:
|
|
462
|
-
unreachable(message)
|
|
463
|
-
}
|
|
464
|
-
}
|
|
359
|
+
const onMessage = createSandboxMessageRouter({
|
|
360
|
+
iframe,
|
|
361
|
+
sandboxOrigin,
|
|
362
|
+
getInitStatus: () => initializationController.status,
|
|
363
|
+
emit,
|
|
364
|
+
post,
|
|
365
|
+
onInvalidConnect: initializationController.handleInvalidConnect,
|
|
366
|
+
onInvalidInitResult: initializationController.handleInvalidInitResult,
|
|
367
|
+
onConnect: initializationController.handleConnect,
|
|
368
|
+
onInitResult: initializationController.handleInitResult,
|
|
369
|
+
onQueryDataSource: handleQuery,
|
|
370
|
+
onUnsubscribeDataSourceQuery: message =>
|
|
371
|
+
querySubscriptions.unsubscribe(message.subscriptionId),
|
|
372
|
+
onCreatePage: handleCreatePage,
|
|
373
|
+
onGetPage: handleGetPage,
|
|
374
|
+
onGetUser: handleGetUser,
|
|
375
|
+
onListUsers: handleListUsers,
|
|
376
|
+
onUpdatePage: handleUpdatePage,
|
|
377
|
+
onResize: message => handlers.resize?.(message),
|
|
378
|
+
})
|
|
465
379
|
|
|
466
380
|
window.addEventListener("message", onMessage)
|
|
467
381
|
iframe.addEventListener("load", initializationController.onIframeLoad)
|
|
@@ -524,6 +438,7 @@ export function createCustomBlockHost(
|
|
|
524
438
|
setDataSources(nextDataSources) {
|
|
525
439
|
if (
|
|
526
440
|
initializationController.hasConnectedSandbox &&
|
|
441
|
+
manifest !== undefined &&
|
|
527
442
|
getBindingErrorCode(manifest, nextDataSources) !== undefined
|
|
528
443
|
) {
|
|
529
444
|
emit("warn", {
|
|
@@ -53,9 +53,8 @@ export type InitializationControllerOptions = {
|
|
|
53
53
|
minBridgeProtocolVersion: number
|
|
54
54
|
noConnectTimeoutMs?: number
|
|
55
55
|
noInitResultTimeoutMs?: number
|
|
56
|
-
onConnect?: (message: ConnectSuccessMessage) => void
|
|
57
56
|
onInitResult?: (message: InitResultMessage) => void
|
|
58
|
-
|
|
57
|
+
onRestartRequired?: () => void
|
|
59
58
|
onInitializationFailure?: (
|
|
60
59
|
error: CustomBlockHostInitializationFailure,
|
|
61
60
|
) => void
|
|
@@ -66,8 +65,14 @@ export type InitializationController = {
|
|
|
66
65
|
readonly hasConnectedSandbox: boolean
|
|
67
66
|
handleConnect: (message: ConnectMessage) => void
|
|
68
67
|
handleInitResult: (message: InitResultMessage) => void
|
|
69
|
-
handleInvalidConnect: (
|
|
70
|
-
|
|
68
|
+
handleInvalidConnect: (args: {
|
|
69
|
+
initializationId: string
|
|
70
|
+
error: CustomBlockInitErrorInfo
|
|
71
|
+
}) => "initErrorSent" | "nack"
|
|
72
|
+
handleInvalidInitResult: (args: {
|
|
73
|
+
initializationId: string | undefined
|
|
74
|
+
error: CustomBlockInitErrorInfo
|
|
75
|
+
}) => void
|
|
71
76
|
onIframeLoad: () => void
|
|
72
77
|
stop: () => void
|
|
73
78
|
}
|
|
@@ -85,9 +90,8 @@ export function createInitializationController(
|
|
|
85
90
|
minBridgeProtocolVersion,
|
|
86
91
|
noConnectTimeoutMs = DEFAULT_NO_CONNECT_TIMEOUT_MS,
|
|
87
92
|
noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS,
|
|
88
|
-
onConnect,
|
|
89
93
|
onInitResult,
|
|
90
|
-
|
|
94
|
+
onRestartRequired,
|
|
91
95
|
onInitializationFailure,
|
|
92
96
|
} = options
|
|
93
97
|
|
|
@@ -111,10 +115,11 @@ export function createInitializationController(
|
|
|
111
115
|
}
|
|
112
116
|
}
|
|
113
117
|
|
|
114
|
-
function sendInitError(
|
|
115
|
-
initializationId: string
|
|
116
|
-
error: CustomBlockInitErrorInfo
|
|
117
|
-
) {
|
|
118
|
+
function sendInitError(args: {
|
|
119
|
+
initializationId: string
|
|
120
|
+
error: CustomBlockInitErrorInfo
|
|
121
|
+
}) {
|
|
122
|
+
const { initializationId, error } = args
|
|
118
123
|
if (initStatus !== "waitingForConnect") {
|
|
119
124
|
return
|
|
120
125
|
}
|
|
@@ -134,10 +139,12 @@ export function createInitializationController(
|
|
|
134
139
|
if (initStatus !== "waitingForConnect") {
|
|
135
140
|
return false
|
|
136
141
|
}
|
|
137
|
-
onConnect?.(connect)
|
|
138
142
|
const preparation = prepareInit(connect)
|
|
139
143
|
if (preparation.status === "error") {
|
|
140
|
-
sendInitError(
|
|
144
|
+
sendInitError({
|
|
145
|
+
initializationId: connect.initializationId,
|
|
146
|
+
error: preparation.error,
|
|
147
|
+
})
|
|
141
148
|
return false
|
|
142
149
|
}
|
|
143
150
|
initStatus = "waitingForInitResult"
|
|
@@ -185,7 +192,7 @@ export function createInitializationController(
|
|
|
185
192
|
"Ignored connect after initialization settled; recreate the host and iframe to re-initialize",
|
|
186
193
|
initializationId: message.initializationId,
|
|
187
194
|
})
|
|
188
|
-
|
|
195
|
+
onRestartRequired?.()
|
|
189
196
|
return
|
|
190
197
|
}
|
|
191
198
|
|
|
@@ -194,7 +201,10 @@ export function createInitializationController(
|
|
|
194
201
|
const initError = initErrorForFailureReason("invalid_protocol_version", {
|
|
195
202
|
currentBridgeProtocolVersion: message.bridgeProtocolVersion,
|
|
196
203
|
})
|
|
197
|
-
sendInitError(
|
|
204
|
+
sendInitError({
|
|
205
|
+
initializationId: message.initializationId,
|
|
206
|
+
error: initError,
|
|
207
|
+
})
|
|
198
208
|
return
|
|
199
209
|
}
|
|
200
210
|
if (message.bridgeProtocolVersion < minBridgeProtocolVersion) {
|
|
@@ -205,13 +215,19 @@ export function createInitializationController(
|
|
|
205
215
|
minBridgeProtocolVersion,
|
|
206
216
|
},
|
|
207
217
|
)
|
|
208
|
-
sendInitError(
|
|
218
|
+
sendInitError({
|
|
219
|
+
initializationId: message.initializationId,
|
|
220
|
+
error: initError,
|
|
221
|
+
})
|
|
209
222
|
return
|
|
210
223
|
}
|
|
211
224
|
|
|
212
225
|
// If the sandbox reported an error during `connect`, return it as an `init` error.
|
|
213
226
|
if (message.status === "error") {
|
|
214
|
-
sendInitError(
|
|
227
|
+
sendInitError({
|
|
228
|
+
initializationId: message.initializationId,
|
|
229
|
+
error: message.error,
|
|
230
|
+
})
|
|
215
231
|
return
|
|
216
232
|
}
|
|
217
233
|
|
|
@@ -220,32 +236,37 @@ export function createInitializationController(
|
|
|
220
236
|
}
|
|
221
237
|
}
|
|
222
238
|
|
|
223
|
-
function handleInvalidConnect(
|
|
239
|
+
function handleInvalidConnect(args: {
|
|
240
|
+
initializationId: string
|
|
241
|
+
error: CustomBlockInitErrorInfo
|
|
242
|
+
}): "initErrorSent" | "nack" {
|
|
243
|
+
const { initializationId, error } = args
|
|
224
244
|
if (initStatus !== "waitingForConnect") {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const error = initErrorForFailureReason("invalid_connect_payload")
|
|
228
|
-
if (initializationId === undefined) {
|
|
229
|
-
initStatus = "error"
|
|
230
|
-
clearNoConnectTimeout()
|
|
231
|
-
warn(error)
|
|
232
|
-
onInitializationFailure?.(error)
|
|
233
|
-
} else {
|
|
234
|
-
sendInitError(initializationId, error)
|
|
245
|
+
// `connect` messages received when not waiting for one are NACKed like generic invalid messages.
|
|
246
|
+
return "nack"
|
|
235
247
|
}
|
|
236
|
-
|
|
248
|
+
// The sandbox awaits `init`. Reply with `init.error` using its initialization ID.
|
|
249
|
+
sendInitError({ initializationId, error })
|
|
250
|
+
return "initErrorSent"
|
|
237
251
|
}
|
|
238
252
|
|
|
239
|
-
function handleInvalidInitResult(
|
|
240
|
-
|
|
241
|
-
|
|
253
|
+
function handleInvalidInitResult(args: {
|
|
254
|
+
initializationId: string | undefined
|
|
255
|
+
error: CustomBlockInitErrorInfo
|
|
256
|
+
}): void {
|
|
257
|
+
const { initializationId, error } = args
|
|
258
|
+
if (
|
|
259
|
+
initStatus !== "waitingForInitResult" ||
|
|
260
|
+
initializationId === undefined ||
|
|
261
|
+
initializationId !== activeInitializationId
|
|
262
|
+
) {
|
|
263
|
+
return
|
|
242
264
|
}
|
|
265
|
+
clearNoConnectTimeout()
|
|
243
266
|
clearNoInitResultTimeout()
|
|
244
267
|
initStatus = "error"
|
|
245
|
-
const error = initErrorForFailureReason("invalid_init_result_payload")
|
|
246
268
|
warn(error)
|
|
247
269
|
onInitializationFailure?.(error)
|
|
248
|
-
return true
|
|
249
270
|
}
|
|
250
271
|
|
|
251
272
|
function onIframeLoad() {
|