@notionhq/custom-blocks-host 0.3.4 → 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.
@@ -12,13 +12,10 @@ import { openExternalUrlResultMessageSchema } from "./openExternalUrl.js";
12
12
  import { openPageResultMessageSchema } from "./openPage.js";
13
13
  import { pageChangedMessageSchema } from "./pageChanged.js";
14
14
  import { parentChangedMessageSchema } from "./parentChanged.js";
15
- import { queryDataSourceResultMessageSchema } from "./queryDataSourceResult.js";
15
+ import { queryDataSourceResultMessageLegacySchema, queryDataSourceResultMessageSchema, } from "./queryDataSourceResult.js";
16
16
  import { themeChangedMessageSchema } from "./themeChanged.js";
17
17
  import { updatePageResultMessageSchema } from "./updatePageResult.js";
18
- /**
19
- * Discriminated union of every message the host is allowed to send the sandbox.
20
- */
21
- export const hostToSandboxMessageSchema = v.variant("type", [
18
+ const baseHostToSandboxMessageSchema = v.variant("type", [
22
19
  initMessageSchema,
23
20
  themeChangedMessageSchema,
24
21
  contrastModeChangedMessageSchema,
@@ -26,7 +23,6 @@ export const hostToSandboxMessageSchema = v.variant("type", [
26
23
  pageChangedMessageSchema,
27
24
  currentUserChangedMessageSchema,
28
25
  dataSourcesChangedMessageSchema,
29
- queryDataSourceResultMessageSchema,
30
26
  createPageResultMessageSchema,
31
27
  getPageResultMessageSchema,
32
28
  getUserResultMessageSchema,
@@ -36,3 +32,14 @@ export const hostToSandboxMessageSchema = v.variant("type", [
36
32
  updatePageResultMessageSchema,
37
33
  invalidSandboxMessageSchema,
38
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
+ ]);
@@ -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(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notionhq/custom-blocks-host",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "Reusable host runtime for Notion custom blocks.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -0,0 +1,183 @@
1
+ import type { NotionDataSourcePageBridge } from "@notionhq/custom-blocks-protocol/dataSources/dataSourcePage.js"
2
+ import type { NotionDataSourceValue } from "@notionhq/custom-blocks-protocol/dataSources/dataSourceValue.js"
3
+ import type { NotionPagePropertyValue } from "@notionhq/custom-blocks-protocol/pages/page.js"
4
+ import { unreachable } from "../utils.js"
5
+
6
+ export function toLegacyQueryPage(page: NotionDataSourcePageBridge) {
7
+ const propertiesById: Record<string, NotionDataSourceValue | undefined> = {}
8
+ for (const property of Object.values(page.properties)) {
9
+ if (property) {
10
+ propertiesById[property.id] = toLegacyProperty(property)
11
+ }
12
+ }
13
+ return {
14
+ id: page.id,
15
+ is_archived: page.is_archived,
16
+ in_trash: page.in_trash,
17
+ propertiesById,
18
+ }
19
+ }
20
+
21
+ function plainText(items: Record<string, unknown>[]): string {
22
+ return items
23
+ .map(item => {
24
+ if (typeof item.plain_text === "string") {
25
+ return item.plain_text
26
+ }
27
+ const text = item.text
28
+ return text &&
29
+ typeof text === "object" &&
30
+ "content" in text &&
31
+ typeof text.content === "string"
32
+ ? text.content
33
+ : ""
34
+ })
35
+ .join("")
36
+ }
37
+
38
+ function timestamp(
39
+ value: string | number | null | undefined,
40
+ ): NotionDataSourceValue | undefined {
41
+ if (value === undefined || value === null || value === "") {
42
+ return undefined
43
+ }
44
+ const date = new Date(value)
45
+ if (!Number.isFinite(date.getTime())) {
46
+ return undefined
47
+ }
48
+ return {
49
+ type: "datetime",
50
+ start_date: date.toISOString().slice(0, 10),
51
+ start_time: date.toISOString().slice(11, 16),
52
+ time_zone: "UTC",
53
+ }
54
+ }
55
+
56
+ function legacyDate(value: {
57
+ start: string
58
+ end?: string | null
59
+ time_zone?: string | null
60
+ }): NotionDataSourceValue {
61
+ const timed = value.start.includes("T") || value.time_zone != null
62
+ const offset = value.start.match(/([+-])(\d{2}):(\d{2})$/)
63
+ const offsetMinutes = offset
64
+ ? (Number(offset[2]) * 60 + Number(offset[3])) *
65
+ (offset[1] === "-" ? -1 : 1)
66
+ : 0
67
+ const parts = (input: string) => {
68
+ if (!input.includes("T")) {
69
+ return { date: input, time: "00:00" }
70
+ }
71
+ const wallClock = input.match(
72
+ /^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2})(?::\d{2}(?:\.\d+)?)?$/,
73
+ )
74
+ if (wallClock) {
75
+ return { date: wallClock[1], time: wallClock[2] }
76
+ }
77
+ if (!value.time_zone) {
78
+ const local = new Date(
79
+ new Date(input).getTime() + offsetMinutes * 60_000,
80
+ ).toISOString()
81
+ return { date: local.slice(0, 10), time: local.slice(11, 16) }
82
+ }
83
+ const entries = new Intl.DateTimeFormat("en-CA", {
84
+ timeZone: value.time_zone,
85
+ year: "numeric",
86
+ month: "2-digit",
87
+ day: "2-digit",
88
+ hour: "2-digit",
89
+ minute: "2-digit",
90
+ hourCycle: "h23",
91
+ }).formatToParts(new Date(input))
92
+ const get = (type: string) =>
93
+ entries.find(part => part.type === type)?.value
94
+ return {
95
+ date: `${get("year")}-${get("month")}-${get("day")}`,
96
+ time: `${get("hour")}:${get("minute")}`,
97
+ }
98
+ }
99
+ const start = parts(value.start)
100
+ const end = value.end ? parts(value.end) : undefined
101
+ if (!timed) {
102
+ return end
103
+ ? { type: "daterange", start_date: start.date, end_date: end.date }
104
+ : { type: "date", start_date: start.date }
105
+ }
106
+ const zone =
107
+ value.time_zone ??
108
+ (offsetMinutes === 0
109
+ ? "UTC"
110
+ : `UTC${offsetMinutes < 0 ? "-" : "+"}${Math.floor(Math.abs(offsetMinutes) / 60)}${Math.abs(offsetMinutes) % 60 === 0 ? "" : `:${String(Math.abs(offsetMinutes) % 60).padStart(2, "0")}`}`)
111
+ return end
112
+ ? {
113
+ type: "datetimerange",
114
+ start_date: start.date,
115
+ start_time: start.time,
116
+ end_date: end.date,
117
+ end_time: end.time,
118
+ time_zone: zone,
119
+ }
120
+ : {
121
+ type: "datetime",
122
+ start_date: start.date,
123
+ start_time: start.time,
124
+ time_zone: zone,
125
+ }
126
+ }
127
+
128
+ function toLegacyProperty(
129
+ property: NotionPagePropertyValue,
130
+ ): NotionDataSourceValue | undefined {
131
+ switch (property.type) {
132
+ case "title":
133
+ return plainText(property.title)
134
+ case "rich_text":
135
+ return plainText(property.rich_text)
136
+ case "number":
137
+ return property.number ?? undefined
138
+ case "checkbox":
139
+ return property.checkbox
140
+ case "url":
141
+ return property.url ?? ""
142
+ case "email":
143
+ return property.email ?? ""
144
+ case "phone_number":
145
+ return property.phone_number ?? ""
146
+ case "select":
147
+ return property.select?.name
148
+ case "status":
149
+ return property.status?.name
150
+ case "multi_select":
151
+ return property.multi_select.flatMap(option => option.name ?? [])
152
+ case "date":
153
+ return property.date ? legacyDate(property.date) : undefined
154
+ case "people":
155
+ return property.people.map(person =>
156
+ person.object === "group"
157
+ ? {
158
+ id: person.id,
159
+ table: "space_permission_group",
160
+ spaceId: person.id.split("|")[1],
161
+ }
162
+ : { id: person.id, table: "notion_user" },
163
+ )
164
+ case "relation":
165
+ return property.relation.map(page => ({ id: page.id, table: "block" }))
166
+ case "files":
167
+ return property.files.map(file => file.name).join(",")
168
+ case "place":
169
+ return ""
170
+ case "created_time":
171
+ return timestamp(property.created_time)
172
+ case "last_edited_time":
173
+ return timestamp(property.last_edited_time)
174
+ case "last_visited_time":
175
+ return timestamp(property.last_visited_time)
176
+ case "created_by":
177
+ return property.created_by ? [property.created_by] : undefined
178
+ case "last_edited_by":
179
+ return property.last_edited_by ? [property.last_edited_by] : undefined
180
+ default:
181
+ return unreachable(property)
182
+ }
183
+ }
@@ -1,46 +1,18 @@
1
- import { NOTION_BUILTIN_PROPERTY_SCHEMAS } from "@notionhq/custom-blocks-protocol/dataSources/propertySchema.js"
2
- import type { HostToSandboxMessage } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
1
+ import type {
2
+ HostToSandboxMessage,
3
+ HostToSandboxMessageLegacy,
4
+ } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
5
+ import { normalizeQueryPage } from "./normalizeQueryPage.js"
3
6
 
4
7
  /**
5
8
  * Normalizes an outgoing message for the bridge protocol version requested by the sandbox.
6
9
  */
7
10
  // TODO(custom-blocks): Remove this function when MIN_HOST_SUPPORTED_BRIDGE_PROTOCOL_VERSION
8
- // reaches 5.
11
+ // reaches 6.
9
12
  export function normalizeHostToSandboxMessage(
10
13
  message: HostToSandboxMessage,
11
14
  bridgeProtocolVersion: number,
12
- ): HostToSandboxMessage {
13
- // The latest SDK version does not need any normalization.
14
- if (bridgeProtocolVersion >= 5) {
15
- return message
16
- }
17
- const containsDataSources =
18
- (message.type === "init" && message.status === "success") ||
19
- message.type === "dataSourcesChanged"
20
- if (!containsDataSources) {
21
- return message
22
- }
23
- // Protocol 5 excludes synthetic IDs such as created_time from collectionSchema.propertiesById.
24
- // Older SDKs derive read schemas from that map. Restore those entries in outgoing copies so
25
- // host query validation still uses the actual database schema.
26
- return {
27
- ...message,
28
- dataSources: {
29
- bindings: Object.fromEntries(
30
- Object.entries(message.dataSources.bindings).map(([key, binding]) => [
31
- key,
32
- {
33
- ...binding,
34
- collectionSchema: {
35
- ...binding.collectionSchema,
36
- propertiesById: {
37
- ...binding.collectionSchema?.propertiesById,
38
- ...NOTION_BUILTIN_PROPERTY_SCHEMAS,
39
- },
40
- },
41
- },
42
- ]),
43
- ),
44
- },
45
- }
15
+ ): HostToSandboxMessageLegacy {
16
+ const normalizedMessage = normalizeQueryPage(message, bridgeProtocolVersion)
17
+ return normalizedMessage
46
18
  }
@@ -0,0 +1,25 @@
1
+ import type {
2
+ HostToSandboxMessage,
3
+ HostToSandboxMessageLegacy,
4
+ } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
5
+ import { LEGACY_PROPERTY_SHAPE_PROTOCOL_VERSION } from "@notionhq/custom-blocks-protocol/protocolVersion.js"
6
+ import { toLegacyQueryPage } from "./legacyPage.js"
7
+
8
+ export function normalizeQueryPage(
9
+ message: HostToSandboxMessageLegacy | HostToSandboxMessage,
10
+ bridgeProtocolVersion: number,
11
+ ): HostToSandboxMessageLegacy {
12
+ if (
13
+ bridgeProtocolVersion < LEGACY_PROPERTY_SHAPE_PROTOCOL_VERSION &&
14
+ message.type === "queryDataSourceResult" &&
15
+ message.status === "success"
16
+ ) {
17
+ return {
18
+ ...message,
19
+ items: message.items.map(page =>
20
+ "properties" in page ? toLegacyQueryPage(page) : page,
21
+ ),
22
+ }
23
+ }
24
+ return message
25
+ }