@notionhq/custom-blocks-host 0.3.3 → 0.3.5

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.
Files changed (44) hide show
  1. package/README.md +11 -4
  2. package/dist/browser.js +1 -1
  3. package/dist/createCustomBlockHost.d.ts.map +1 -1
  4. package/dist/createCustomBlockHost.js +68 -0
  5. package/dist/index.d.ts +2 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/lifecycle/legacyPage.d.ts +68 -0
  8. package/dist/lifecycle/legacyPage.d.ts.map +1 -0
  9. package/dist/lifecycle/legacyPage.js +159 -0
  10. package/dist/lifecycle/normalizeHostToSandboxMessage.d.ts +2 -2
  11. package/dist/lifecycle/normalizeHostToSandboxMessage.d.ts.map +1 -1
  12. package/dist/lifecycle/normalizeHostToSandboxMessage.js +4 -32
  13. package/dist/lifecycle/normalizeQueryPage.d.ts +3 -0
  14. package/dist/lifecycle/normalizeQueryPage.d.ts.map +1 -0
  15. package/dist/lifecycle/normalizeQueryPage.js +13 -0
  16. package/dist/messages/parseExternalUrl.d.ts +3 -0
  17. package/dist/messages/parseExternalUrl.d.ts.map +1 -0
  18. package/dist/messages/parseExternalUrl.js +15 -0
  19. package/dist/messages/sandboxMessageRouter.d.ts +2 -0
  20. package/dist/messages/sandboxMessageRouter.d.ts.map +1 -1
  21. package/dist/messages/sandboxMessageRouter.js +19 -4
  22. package/dist/messages/types.d.ts +7 -0
  23. package/dist/messages/types.d.ts.map +1 -1
  24. package/dist/protocol/dataSources/dataSourcePage.d.ts +1 -2
  25. package/dist/protocol/dataSources/dataSourcePage.js +1 -2
  26. package/dist/protocol/index.d.ts +1 -0
  27. package/dist/protocol/index.js +1 -0
  28. package/dist/protocol/messages/hostToSandbox.d.ts +1649 -241
  29. package/dist/protocol/messages/hostToSandbox.js +15 -6
  30. package/dist/protocol/messages/openExternalUrl.d.ts +32 -0
  31. package/dist/protocol/messages/openExternalUrl.js +26 -0
  32. package/dist/protocol/messages/queryDataSourceResult.d.ts +174 -0
  33. package/dist/protocol/messages/queryDataSourceResult.js +15 -0
  34. package/dist/protocol/messages/sandboxToHost.d.ts +4 -0
  35. package/dist/protocol/messages/sandboxToHost.js +2 -0
  36. package/package.json +1 -1
  37. package/src/createCustomBlockHost.ts +79 -1
  38. package/src/index.ts +4 -0
  39. package/src/lifecycle/legacyPage.ts +183 -0
  40. package/src/lifecycle/normalizeHostToSandboxMessage.ts +9 -37
  41. package/src/lifecycle/normalizeQueryPage.ts +25 -0
  42. package/src/messages/parseExternalUrl.ts +18 -0
  43. package/src/messages/sandboxMessageRouter.ts +24 -3
  44. package/src/messages/types.ts +23 -0
@@ -8,16 +8,14 @@ import { getUserResultMessageSchema } from "./getUser.js";
8
8
  import { initMessageSchema } from "./init.js";
9
9
  import { invalidSandboxMessageSchema } from "./invalidSandboxMessage.js";
10
10
  import { listUsersResultMessageSchema } from "./listUsers.js";
11
+ import { openExternalUrlResultMessageSchema } from "./openExternalUrl.js";
11
12
  import { openPageResultMessageSchema } from "./openPage.js";
12
13
  import { pageChangedMessageSchema } from "./pageChanged.js";
13
14
  import { parentChangedMessageSchema } from "./parentChanged.js";
14
- import { queryDataSourceResultMessageSchema } from "./queryDataSourceResult.js";
15
+ import { queryDataSourceResultMessageLegacySchema, queryDataSourceResultMessageSchema, } from "./queryDataSourceResult.js";
15
16
  import { themeChangedMessageSchema } from "./themeChanged.js";
16
17
  import { updatePageResultMessageSchema } from "./updatePageResult.js";
17
- /**
18
- * Discriminated union of every message the host is allowed to send the sandbox.
19
- */
20
- export const hostToSandboxMessageSchema = v.variant("type", [
18
+ const baseHostToSandboxMessageSchema = v.variant("type", [
21
19
  initMessageSchema,
22
20
  themeChangedMessageSchema,
23
21
  contrastModeChangedMessageSchema,
@@ -25,12 +23,23 @@ export const hostToSandboxMessageSchema = v.variant("type", [
25
23
  pageChangedMessageSchema,
26
24
  currentUserChangedMessageSchema,
27
25
  dataSourcesChangedMessageSchema,
28
- queryDataSourceResultMessageSchema,
29
26
  createPageResultMessageSchema,
30
27
  getPageResultMessageSchema,
31
28
  getUserResultMessageSchema,
32
29
  listUsersResultMessageSchema,
33
30
  openPageResultMessageSchema,
31
+ openExternalUrlResultMessageSchema,
34
32
  updatePageResultMessageSchema,
35
33
  invalidSandboxMessageSchema,
36
34
  ]);
35
+ export const hostToSandboxMessageSchema = v.variant("type", [
36
+ ...baseHostToSandboxMessageSchema.options,
37
+ queryDataSourceResultMessageSchema,
38
+ ]);
39
+ /**
40
+ * Discriminated union of every message the host is allowed to send the sandbox.
41
+ */
42
+ export const hostToSandboxMessageLegacySchema = v.variant("type", [
43
+ ...baseHostToSandboxMessageSchema.options,
44
+ queryDataSourceResultMessageLegacySchema,
45
+ ]);
@@ -0,0 +1,32 @@
1
+ import * as v from "valibot";
2
+ import type { CustomBlockErrorCode, CustomBlockErrorInfo } from "../errors.js";
3
+ export declare const customBlockOpenExternalUrlErrorCodeSchema: v.StringSchema<undefined>;
4
+ export type CustomBlockOpenExternalUrlErrorCode = CustomBlockErrorCode<"user_decline" | "pending_navigation" | "invalid_url">;
5
+ export type CustomBlockOpenExternalUrlErrorInfo = CustomBlockErrorInfo<CustomBlockOpenExternalUrlErrorCode>;
6
+ export declare const customBlockOpenExternalUrlErrorInfoSchema: v.ObjectSchema<{
7
+ readonly code: v.StringSchema<undefined>;
8
+ readonly message: v.StringSchema<undefined>;
9
+ readonly isRetryable: v.BooleanSchema<undefined>;
10
+ }, undefined>;
11
+ /** Sandbox -> host: request approval to open an external URL. */
12
+ export declare const openExternalUrlMessageSchema: v.ObjectSchema<{
13
+ readonly type: v.LiteralSchema<"openExternalUrl", undefined>;
14
+ readonly requestId: v.StringSchema<undefined>;
15
+ readonly url: v.StringSchema<undefined>;
16
+ }, undefined>;
17
+ export type OpenExternalUrlMessage = v.InferOutput<typeof openExternalUrlMessageSchema>;
18
+ export declare const openExternalUrlResultMessageSchema: v.VariantSchema<"status", [v.ObjectSchema<{
19
+ readonly type: v.LiteralSchema<"openExternalUrlResult", undefined>;
20
+ readonly requestId: v.StringSchema<undefined>;
21
+ readonly status: v.LiteralSchema<"success", undefined>;
22
+ }, undefined>, v.ObjectSchema<{
23
+ readonly type: v.LiteralSchema<"openExternalUrlResult", undefined>;
24
+ readonly requestId: v.StringSchema<undefined>;
25
+ readonly status: v.LiteralSchema<"error", undefined>;
26
+ readonly error: v.ObjectSchema<{
27
+ readonly code: v.StringSchema<undefined>;
28
+ readonly message: v.StringSchema<undefined>;
29
+ readonly isRetryable: v.BooleanSchema<undefined>;
30
+ }, undefined>;
31
+ }, undefined>], undefined>;
32
+ export type OpenExternalUrlResultMessage = v.InferOutput<typeof openExternalUrlResultMessageSchema>;
@@ -0,0 +1,26 @@
1
+ import * as v from "valibot";
2
+ export const customBlockOpenExternalUrlErrorCodeSchema = v.string();
3
+ export const customBlockOpenExternalUrlErrorInfoSchema = v.object({
4
+ code: customBlockOpenExternalUrlErrorCodeSchema,
5
+ message: v.string(),
6
+ isRetryable: v.boolean(),
7
+ });
8
+ /** Sandbox -> host: request approval to open an external URL. */
9
+ export const openExternalUrlMessageSchema = v.object({
10
+ type: v.literal("openExternalUrl"),
11
+ requestId: v.string(),
12
+ url: v.string(),
13
+ });
14
+ export const openExternalUrlResultMessageSchema = v.variant("status", [
15
+ v.object({
16
+ type: v.literal("openExternalUrlResult"),
17
+ requestId: v.string(),
18
+ status: v.literal("success"),
19
+ }),
20
+ v.object({
21
+ type: v.literal("openExternalUrlResult"),
22
+ requestId: v.string(),
23
+ status: v.literal("error"),
24
+ error: customBlockOpenExternalUrlErrorInfoSchema,
25
+ }),
26
+ ]);
@@ -12,6 +12,179 @@ export declare const customBlockQueryDataSourceErrorInfoSchema: v.ObjectSchema<{
12
12
  * Message sent by the host to update a `queryDataSource` subscription.
13
13
  */
14
14
  export declare const queryDataSourceResultMessageSchema: v.VariantSchema<"status", [v.ObjectSchema<{
15
+ readonly type: v.LiteralSchema<"queryDataSourceResult", undefined>;
16
+ readonly subscriptionId: v.StringSchema<undefined>;
17
+ readonly status: v.LiteralSchema<"success", undefined>;
18
+ readonly items: v.ArraySchema<v.ObjectSchema<{
19
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionPageId">]>;
20
+ readonly is_archived: v.BooleanSchema<undefined>;
21
+ readonly in_trash: v.BooleanSchema<undefined>;
22
+ readonly properties: v.RecordSchema<v.StringSchema<undefined>, v.OptionalSchema<v.VariantSchema<"type", [v.ObjectSchema<{
23
+ readonly id: v.StringSchema<undefined>;
24
+ readonly type: v.LiteralSchema<"title", undefined>;
25
+ readonly title: v.ArraySchema<v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>, undefined>;
26
+ }, undefined>, v.ObjectSchema<{
27
+ readonly id: v.StringSchema<undefined>;
28
+ readonly type: v.LiteralSchema<"rich_text", undefined>;
29
+ readonly rich_text: v.ArraySchema<v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>, undefined>;
30
+ }, undefined>, v.ObjectSchema<{
31
+ readonly id: v.StringSchema<undefined>;
32
+ readonly type: v.LiteralSchema<"number", undefined>;
33
+ readonly number: v.NullableSchema<v.NumberSchema<undefined>, undefined>;
34
+ }, undefined>, v.ObjectSchema<{
35
+ readonly id: v.StringSchema<undefined>;
36
+ readonly type: v.LiteralSchema<"url", undefined>;
37
+ readonly url: v.NullableSchema<v.StringSchema<undefined>, undefined>;
38
+ }, undefined>, v.ObjectSchema<{
39
+ readonly id: v.StringSchema<undefined>;
40
+ readonly type: v.LiteralSchema<"email", undefined>;
41
+ readonly email: v.NullableSchema<v.StringSchema<undefined>, undefined>;
42
+ }, undefined>, v.ObjectSchema<{
43
+ readonly id: v.StringSchema<undefined>;
44
+ readonly type: v.LiteralSchema<"phone_number", undefined>;
45
+ readonly phone_number: v.NullableSchema<v.StringSchema<undefined>, undefined>;
46
+ }, undefined>, v.ObjectSchema<{
47
+ readonly id: v.StringSchema<undefined>;
48
+ readonly type: v.LiteralSchema<"checkbox", undefined>;
49
+ readonly checkbox: v.BooleanSchema<undefined>;
50
+ }, undefined>, v.ObjectSchema<{
51
+ readonly id: v.StringSchema<undefined>;
52
+ readonly type: v.LiteralSchema<"select", undefined>;
53
+ readonly select: v.NullableSchema<v.UnionSchema<[v.ObjectSchema<{
54
+ readonly id: v.StringSchema<undefined>;
55
+ readonly name: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
56
+ readonly color: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
57
+ readonly description: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
58
+ }, undefined>, v.ObjectSchema<{
59
+ readonly name: v.StringSchema<undefined>;
60
+ readonly id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
61
+ readonly color: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
62
+ readonly description: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
63
+ }, undefined>], undefined>, undefined>;
64
+ }, undefined>, v.ObjectSchema<{
65
+ readonly id: v.StringSchema<undefined>;
66
+ readonly type: v.LiteralSchema<"status", undefined>;
67
+ readonly status: v.NullableSchema<v.UnionSchema<[v.ObjectSchema<{
68
+ readonly id: v.StringSchema<undefined>;
69
+ readonly name: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
70
+ readonly color: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
71
+ readonly description: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
72
+ }, undefined>, v.ObjectSchema<{
73
+ readonly name: v.StringSchema<undefined>;
74
+ readonly id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
75
+ readonly color: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
76
+ readonly description: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
77
+ }, undefined>], undefined>, undefined>;
78
+ }, undefined>, v.ObjectSchema<{
79
+ readonly id: v.StringSchema<undefined>;
80
+ readonly type: v.LiteralSchema<"multi_select", undefined>;
81
+ readonly multi_select: v.ArraySchema<v.UnionSchema<[v.ObjectSchema<{
82
+ readonly id: v.StringSchema<undefined>;
83
+ readonly name: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
84
+ readonly color: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
85
+ readonly description: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
86
+ }, undefined>, v.ObjectSchema<{
87
+ readonly name: v.StringSchema<undefined>;
88
+ readonly id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
89
+ readonly color: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
90
+ readonly description: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
91
+ }, undefined>], undefined>, undefined>;
92
+ }, undefined>, v.ObjectSchema<{
93
+ readonly id: v.StringSchema<undefined>;
94
+ readonly type: v.LiteralSchema<"date", undefined>;
95
+ readonly date: v.NullableSchema<v.ObjectSchema<{
96
+ readonly start: v.StringSchema<undefined>;
97
+ readonly end: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
98
+ readonly time_zone: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
99
+ }, undefined>, undefined>;
100
+ }, undefined>, v.ObjectSchema<{
101
+ readonly id: v.StringSchema<undefined>;
102
+ readonly type: v.LiteralSchema<"people", undefined>;
103
+ readonly people: v.ArraySchema<v.UnionSchema<[v.ObjectSchema<{
104
+ readonly object: v.OptionalSchema<v.LiteralSchema<"user", undefined>, undefined>;
105
+ readonly id: v.StringSchema<undefined>;
106
+ }, undefined>, v.ObjectSchema<{
107
+ readonly object: v.LiteralSchema<"group", undefined>;
108
+ readonly id: v.StringSchema<undefined>;
109
+ readonly name: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
110
+ }, undefined>], undefined>, undefined>;
111
+ }, undefined>, v.ObjectSchema<{
112
+ readonly id: v.StringSchema<undefined>;
113
+ readonly type: v.LiteralSchema<"relation", undefined>;
114
+ readonly has_more: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
115
+ readonly relation: v.ArraySchema<v.ObjectSchema<{
116
+ readonly id: v.StringSchema<undefined>;
117
+ }, undefined>, undefined>;
118
+ }, undefined>, v.ObjectSchema<{
119
+ readonly id: v.StringSchema<undefined>;
120
+ readonly type: v.LiteralSchema<"files", undefined>;
121
+ readonly files: v.ArraySchema<v.UnionSchema<[v.ObjectSchema<{
122
+ readonly type: v.LiteralSchema<"external", undefined>;
123
+ readonly name: v.StringSchema<undefined>;
124
+ readonly external: v.ObjectSchema<{
125
+ readonly url: v.StringSchema<undefined>;
126
+ }, undefined>;
127
+ }, undefined>, v.ObjectSchema<{
128
+ readonly type: v.LiteralSchema<"file", undefined>;
129
+ readonly name: v.StringSchema<undefined>;
130
+ readonly file: v.ObjectSchema<{
131
+ readonly url: v.StringSchema<undefined>;
132
+ readonly expiry_time: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
133
+ }, undefined>;
134
+ }, undefined>], undefined>, undefined>;
135
+ }, undefined>, v.ObjectSchema<{
136
+ readonly id: v.StringSchema<undefined>;
137
+ readonly type: v.LiteralSchema<"place", undefined>;
138
+ readonly place: v.NullableSchema<v.ObjectSchema<{
139
+ readonly lat: v.NumberSchema<undefined>;
140
+ readonly lon: v.NumberSchema<undefined>;
141
+ readonly name: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
142
+ readonly address: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
143
+ readonly aws_place_id: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
144
+ readonly google_place_id: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
145
+ }, undefined>, undefined>;
146
+ }, undefined>, v.ObjectSchema<{
147
+ readonly id: v.StringSchema<undefined>;
148
+ readonly type: v.LiteralSchema<"created_time", undefined>;
149
+ readonly created_time: v.NullableSchema<v.StringSchema<undefined>, undefined>;
150
+ }, undefined>, v.ObjectSchema<{
151
+ readonly id: v.StringSchema<undefined>;
152
+ readonly type: v.LiteralSchema<"last_edited_time", undefined>;
153
+ readonly last_edited_time: v.NullableSchema<v.StringSchema<undefined>, undefined>;
154
+ }, undefined>, v.ObjectSchema<{
155
+ readonly id: v.StringSchema<undefined>;
156
+ readonly type: v.LiteralSchema<"created_by", undefined>;
157
+ readonly created_by: v.NullableSchema<v.ObjectSchema<{
158
+ readonly id: v.StringSchema<undefined>;
159
+ readonly table: v.PicklistSchema<["notion_user", "bot", "organization_bot"], undefined>;
160
+ readonly spaceId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
161
+ }, undefined>, undefined>;
162
+ }, undefined>, v.ObjectSchema<{
163
+ readonly id: v.StringSchema<undefined>;
164
+ readonly type: v.LiteralSchema<"last_edited_by", undefined>;
165
+ readonly last_edited_by: v.NullableSchema<v.ObjectSchema<{
166
+ readonly id: v.StringSchema<undefined>;
167
+ readonly table: v.PicklistSchema<["notion_user", "bot", "organization_bot"], undefined>;
168
+ readonly spaceId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
169
+ }, undefined>, undefined>;
170
+ }, undefined>, v.ObjectSchema<{
171
+ readonly id: v.StringSchema<undefined>;
172
+ readonly type: v.LiteralSchema<"last_visited_time", undefined>;
173
+ readonly last_visited_time: v.NullableSchema<v.StringSchema<undefined>, undefined>;
174
+ }, undefined>], undefined>, undefined>, undefined>;
175
+ }, undefined>, undefined>;
176
+ readonly hasMore: v.BooleanSchema<undefined>;
177
+ }, undefined>, v.ObjectSchema<{
178
+ readonly type: v.LiteralSchema<"queryDataSourceResult", undefined>;
179
+ readonly subscriptionId: v.StringSchema<undefined>;
180
+ readonly status: v.LiteralSchema<"error", undefined>;
181
+ readonly error: v.ObjectSchema<{
182
+ readonly code: v.StringSchema<undefined>;
183
+ readonly message: v.StringSchema<undefined>;
184
+ readonly isRetryable: v.BooleanSchema<undefined>;
185
+ }, undefined>;
186
+ }, undefined>], undefined>;
187
+ export declare const queryDataSourceResultMessageLegacySchema: v.VariantSchema<"status", [v.ObjectSchema<{
15
188
  readonly type: v.LiteralSchema<"queryDataSourceResult", undefined>;
16
189
  readonly subscriptionId: v.StringSchema<undefined>;
17
190
  readonly status: v.LiteralSchema<"success", undefined>;
@@ -250,3 +423,4 @@ export declare const queryDataSourceResultMessageSchema: v.VariantSchema<"status
250
423
  }, undefined>;
251
424
  }, undefined>], undefined>;
252
425
  export type QueryDataSourceResultMessage = v.InferOutput<typeof queryDataSourceResultMessageSchema>;
426
+ export type QueryDataSourceResultMessageLegacy = v.InferOutput<typeof queryDataSourceResultMessageLegacySchema>;
@@ -12,6 +12,21 @@ export const customBlockQueryDataSourceErrorInfoSchema = v.object({
12
12
  * Message sent by the host to update a `queryDataSource` subscription.
13
13
  */
14
14
  export const queryDataSourceResultMessageSchema = v.variant("status", [
15
+ v.object({
16
+ type: v.literal("queryDataSourceResult"),
17
+ subscriptionId: v.string(),
18
+ status: v.literal("success"),
19
+ items: v.array(notionDataSourcePageBridgeSchema),
20
+ hasMore: v.boolean(),
21
+ }),
22
+ v.object({
23
+ type: v.literal("queryDataSourceResult"),
24
+ subscriptionId: v.string(),
25
+ status: v.literal("error"),
26
+ error: customBlockQueryDataSourceErrorInfoSchema,
27
+ }),
28
+ ]);
29
+ export const queryDataSourceResultMessageLegacySchema = v.variant("status", [
15
30
  v.object({
16
31
  type: v.literal("queryDataSourceResult"),
17
32
  subscriptionId: v.string(),
@@ -759,6 +759,10 @@ export declare const sandboxToHostMessageSchema: v.VariantSchema<"type", [v.Vari
759
759
  readonly requestId: v.StringSchema<undefined>;
760
760
  readonly pageId: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionPageId">]>;
761
761
  readonly mode: v.PicklistSchema<["side_peek", "center_peek"], undefined>;
762
+ }, undefined>, v.ObjectSchema<{
763
+ readonly type: v.LiteralSchema<"openExternalUrl", undefined>;
764
+ readonly requestId: v.StringSchema<undefined>;
765
+ readonly url: v.StringSchema<undefined>;
762
766
  }, undefined>, v.ObjectSchema<{
763
767
  readonly type: v.LiteralSchema<"updatePage", undefined>;
764
768
  readonly requestId: v.StringSchema<undefined>;
@@ -6,6 +6,7 @@ import { getUserMessageSchema } from "./getUser.js";
6
6
  import { initResultMessageSchema } from "./initResult.js";
7
7
  import { invalidHostMessageSchema } from "./invalidHostMessage.js";
8
8
  import { listUsersMessageSchema } from "./listUsers.js";
9
+ import { openExternalUrlMessageSchema } from "./openExternalUrl.js";
9
10
  import { openPageMessageSchema } from "./openPage.js";
10
11
  import { queryDataSourceMessageSchema } from "./queryDataSource.js";
11
12
  import { resizeMessageSchema } from "./resize.js";
@@ -25,6 +26,7 @@ export const sandboxToHostMessageSchema = v.variant("type", [
25
26
  getUserMessageSchema,
26
27
  listUsersMessageSchema,
27
28
  openPageMessageSchema,
29
+ openExternalUrlMessageSchema,
28
30
  updatePageMessageSchema,
29
31
  resizeMessageSchema,
30
32
  invalidHostMessageSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notionhq/custom-blocks-host",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Reusable host runtime for Notion custom blocks.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -13,6 +13,10 @@ import {
13
13
  initMessageSchema,
14
14
  } from "@notionhq/custom-blocks-protocol/messages/init.js"
15
15
  import type { InvalidSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/invalidSandboxMessage.js"
16
+ import type {
17
+ OpenExternalUrlMessage,
18
+ OpenExternalUrlResultMessage,
19
+ } from "@notionhq/custom-blocks-protocol/messages/openExternalUrl.js"
16
20
  import type { QueryDataSourceMessage } from "@notionhq/custom-blocks-protocol/messages/queryDataSource.js"
17
21
  import type {
18
22
  CustomBlockQueryDataSourceErrorInfo,
@@ -45,6 +49,7 @@ import type {
45
49
  CustomBlockHostPrepareInitialState,
46
50
  } from "./lifecycle/types.js"
47
51
  import { customBlockHostInitialStateSchema } from "./lifecycle/types.js"
52
+ import { parseExternalUrl } from "./messages/parseExternalUrl.js"
48
53
  import { createSandboxMessageRouter } from "./messages/sandboxMessageRouter.js"
49
54
  import type {
50
55
  CustomBlockHostFailure,
@@ -143,6 +148,9 @@ export function createCustomBlockHost(
143
148
  MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION,
144
149
  )
145
150
 
151
+ const navigationAbortController = new AbortController()
152
+ let pendingOpenExternalUrlRequestId: string | undefined
153
+
146
154
  type HostStateUpdates = Partial<
147
155
  Omit<CustomBlockHostInitialState, "blockId" | "manifest">
148
156
  >
@@ -304,6 +312,75 @@ export function createCustomBlockHost(
304
312
  flushPendingHostStateChanges()
305
313
  }
306
314
 
315
+ async function handleOpenExternalUrl(message: OpenExternalUrlMessage) {
316
+ if (
317
+ handlers.openExternalUrl === undefined ||
318
+ navigationAbortController.signal.aborted
319
+ ) {
320
+ return
321
+ }
322
+ if (pendingOpenExternalUrlRequestId === message.requestId) {
323
+ return
324
+ }
325
+ if (pendingOpenExternalUrlRequestId !== undefined) {
326
+ const pendingResult: OpenExternalUrlResultMessage = {
327
+ type: "openExternalUrlResult",
328
+ requestId: message.requestId,
329
+ status: "error",
330
+ error: {
331
+ code: "pending_navigation",
332
+ message: "Another external navigation request is pending.",
333
+ isRetryable: true,
334
+ },
335
+ }
336
+ sendInitializedMessage(pendingResult)
337
+ return
338
+ }
339
+ const url = parseExternalUrl(message.url)
340
+ if (url === undefined) {
341
+ const invalidResult: OpenExternalUrlResultMessage = {
342
+ type: "openExternalUrlResult",
343
+ requestId: message.requestId,
344
+ status: "error",
345
+ error: {
346
+ code: "invalid_url",
347
+ message:
348
+ "Only valid HTTP or HTTPS URLs without credentials, up to 2,048 characters, can be opened.",
349
+ isRetryable: false,
350
+ },
351
+ }
352
+ sendInitializedMessage(invalidResult)
353
+ return
354
+ }
355
+
356
+ pendingOpenExternalUrlRequestId = message.requestId
357
+ try {
358
+ const response = await handlers.openExternalUrl(
359
+ { ...message, url },
360
+ {
361
+ signal: navigationAbortController.signal,
362
+ },
363
+ )
364
+ if (navigationAbortController.signal.aborted) {
365
+ return
366
+ }
367
+ const result: OpenExternalUrlResultMessage = {
368
+ type: "openExternalUrlResult",
369
+ requestId: message.requestId,
370
+ ...response,
371
+ }
372
+ sendInitializedMessage(result)
373
+ } catch (error) {
374
+ if (!navigationAbortController.signal.aborted) {
375
+ throw error
376
+ }
377
+ } finally {
378
+ if (pendingOpenExternalUrlRequestId === message.requestId) {
379
+ pendingOpenExternalUrlRequestId = undefined
380
+ }
381
+ }
382
+ }
383
+
307
384
  function flushPendingHostStateChanges() {
308
385
  // Keep updates queued until the sandbox acknowledges initialization.
309
386
  // Updates received before init is sent are included in its payload instead.
@@ -463,6 +540,7 @@ export function createCustomBlockHost(
463
540
  onQueryDataSource: handleQuery,
464
541
  onUnsubscribeDataSourceQuery: message =>
465
542
  querySubscriptions.unsubscribe(message.subscriptionId),
543
+ onOpenExternalUrl: handleOpenExternalUrl,
466
544
  onCreatePage: message => respondToRequest(message, handlers.createPage),
467
545
  onGetPage: message => respondToRequest(message, handlers.getPage),
468
546
  onOpenPage: message => respondToRequest(message, handlers.openPage),
@@ -474,8 +552,8 @@ export function createCustomBlockHost(
474
552
 
475
553
  window.addEventListener("message", onMessage)
476
554
  iframe.addEventListener("load", initializationController.onIframeLoad)
477
-
478
555
  function stop() {
556
+ navigationAbortController.abort()
479
557
  preparationController.abort()
480
558
  window.removeEventListener("message", onMessage)
481
559
  iframe.removeEventListener("load", initializationController.onIframeLoad)
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ export type { CreatePageMessage } from "@notionhq/custom-blocks-protocol/message
10
10
  export type { GetPageMessage } from "@notionhq/custom-blocks-protocol/messages/getPage.js"
11
11
  export type { GetUserMessage } from "@notionhq/custom-blocks-protocol/messages/getUser.js"
12
12
  export type { ListUsersMessage } from "@notionhq/custom-blocks-protocol/messages/listUsers.js"
13
+ export type { OpenExternalUrlMessage } from "@notionhq/custom-blocks-protocol/messages/openExternalUrl.js"
13
14
  export type { OpenPageMessage } from "@notionhq/custom-blocks-protocol/messages/openPage.js"
14
15
  export type {
15
16
  CustomBlockCheckboxFilterOperator,
@@ -76,11 +77,14 @@ export type {
76
77
  CustomBlockHostHandlers,
77
78
  CustomBlockHostListUsersResult,
78
79
  CustomBlockHostLogDirection,
80
+ CustomBlockHostNavigationContext,
81
+ CustomBlockHostOpenExternalUrlResult,
79
82
  CustomBlockHostOpenPageResult,
80
83
  CustomBlockHostUpdatePageResult,
81
84
  GetPageMessageInput,
82
85
  GetUserMessageInput,
83
86
  ListUsersMessageInput,
87
+ OpenExternalUrlMessageInput,
84
88
  OpenPageMessageInput,
85
89
  QueryDataSourceMessageInput,
86
90
  ResizeMessageInput,