@notionhq/custom-blocks-host 0.3.4 → 0.3.6
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 +0 -5
- package/dist/browser.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/lifecycle/legacyPage.d.ts +70 -0
- package/dist/lifecycle/legacyPage.d.ts.map +1 -0
- package/dist/lifecycle/legacyPage.js +171 -0
- package/dist/lifecycle/normalizeHostToSandboxMessage.d.ts +2 -2
- package/dist/lifecycle/normalizeHostToSandboxMessage.d.ts.map +1 -1
- package/dist/lifecycle/normalizeHostToSandboxMessage.js +8 -30
- package/dist/lifecycle/normalizePage.d.ts +7 -0
- package/dist/lifecycle/normalizePage.d.ts.map +1 -0
- package/dist/lifecycle/normalizePage.js +22 -0
- package/dist/messages/types.d.ts +1 -2
- package/dist/messages/types.d.ts.map +1 -1
- package/dist/protocol/dataSources/dataSourcePage.d.ts +1 -162
- package/dist/protocol/dataSources/dataSourcePage.js +1 -9
- package/dist/protocol/messages/createPage.js +2 -2
- package/dist/protocol/messages/createPageResult.d.ts +468 -1
- package/dist/protocol/messages/createPageResult.js +17 -2
- package/dist/protocol/messages/getPage.d.ts +468 -1
- package/dist/protocol/messages/getPage.js +18 -3
- package/dist/protocol/messages/hostToSandbox.d.ts +2398 -159
- package/dist/protocol/messages/hostToSandbox.js +22 -12
- package/dist/protocol/messages/queryDataSourceResult.d.ts +312 -2
- package/dist/protocol/messages/queryDataSourceResult.js +18 -5
- package/dist/protocol/messages/sandboxToHost.d.ts +2 -0
- package/dist/protocol/messages/updatePage.d.ts +1 -0
- package/dist/protocol/messages/updatePage.js +4 -3
- package/dist/protocol/messages/updatePageResult.d.ts +467 -1
- package/dist/protocol/messages/updatePageResult.js +16 -2
- package/dist/protocol/pages/page.d.ts +386 -6
- package/dist/protocol/pages/page.js +23 -2
- package/dist/protocol/protocolVersion.js +1 -1
- package/package.json +1 -1
- package/src/index.ts +2 -4
- package/src/lifecycle/legacyPage.ts +208 -0
- package/src/lifecycle/normalizeHostToSandboxMessage.ts +18 -35
- package/src/lifecycle/normalizePage.ts +35 -0
- package/src/messages/types.ts +1 -1
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type { NotionDataSourceValue } from "@notionhq/custom-blocks-protocol/dataSources/dataSourceValue.js"
|
|
2
|
+
import type {
|
|
3
|
+
LegacyNotionPage,
|
|
4
|
+
NotionPagePropertyValue,
|
|
5
|
+
notionPageDataSchema,
|
|
6
|
+
} from "@notionhq/custom-blocks-protocol/pages/page.js"
|
|
7
|
+
import type { InferOutput } from "valibot"
|
|
8
|
+
import { unreachable } from "../utils.js"
|
|
9
|
+
|
|
10
|
+
export function toLegacyPage(
|
|
11
|
+
page: InferOutput<typeof notionPageDataSchema>,
|
|
12
|
+
): LegacyNotionPage {
|
|
13
|
+
return {
|
|
14
|
+
...page,
|
|
15
|
+
properties: Object.fromEntries(
|
|
16
|
+
Object.values(page.properties).flatMap(property =>
|
|
17
|
+
property ? [[property.id, property]] : [],
|
|
18
|
+
),
|
|
19
|
+
),
|
|
20
|
+
icon: page.icon ?? undefined,
|
|
21
|
+
cover: page.cover ?? undefined,
|
|
22
|
+
created_time: page.created_time ?? undefined,
|
|
23
|
+
last_edited_time: page.last_edited_time ?? undefined,
|
|
24
|
+
url: page.url ?? undefined,
|
|
25
|
+
public_url: page.public_url ?? undefined,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function toLegacyQueryPage(
|
|
30
|
+
page: InferOutput<typeof notionPageDataSchema>,
|
|
31
|
+
) {
|
|
32
|
+
const propertiesById: Record<string, NotionDataSourceValue | undefined> = {}
|
|
33
|
+
for (const property of Object.values(page.properties)) {
|
|
34
|
+
if (property) {
|
|
35
|
+
propertiesById[property.id] = toLegacyProperty(property)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
id: page.id,
|
|
40
|
+
is_archived: page.is_archived,
|
|
41
|
+
in_trash: page.in_trash,
|
|
42
|
+
propertiesById,
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function plainText(items: Record<string, unknown>[]): string {
|
|
47
|
+
return items
|
|
48
|
+
.map(item => {
|
|
49
|
+
if (typeof item.plain_text === "string") {
|
|
50
|
+
return item.plain_text
|
|
51
|
+
}
|
|
52
|
+
const text = item.text
|
|
53
|
+
return text &&
|
|
54
|
+
typeof text === "object" &&
|
|
55
|
+
"content" in text &&
|
|
56
|
+
typeof text.content === "string"
|
|
57
|
+
? text.content
|
|
58
|
+
: ""
|
|
59
|
+
})
|
|
60
|
+
.join("")
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function timestamp(
|
|
64
|
+
value: string | number | null | undefined,
|
|
65
|
+
): NotionDataSourceValue | undefined {
|
|
66
|
+
if (value === undefined || value === null || value === "") {
|
|
67
|
+
return undefined
|
|
68
|
+
}
|
|
69
|
+
const date = new Date(value)
|
|
70
|
+
if (!Number.isFinite(date.getTime())) {
|
|
71
|
+
return undefined
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
type: "datetime",
|
|
75
|
+
start_date: date.toISOString().slice(0, 10),
|
|
76
|
+
start_time: date.toISOString().slice(11, 16),
|
|
77
|
+
time_zone: "UTC",
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function legacyDate(value: {
|
|
82
|
+
start: string
|
|
83
|
+
end?: string | null
|
|
84
|
+
time_zone?: string | null
|
|
85
|
+
}): NotionDataSourceValue {
|
|
86
|
+
const timed = value.start.includes("T") || value.time_zone != null
|
|
87
|
+
const offset = value.start.match(/([+-])(\d{2}):(\d{2})$/)
|
|
88
|
+
const offsetMinutes = offset
|
|
89
|
+
? (Number(offset[2]) * 60 + Number(offset[3])) *
|
|
90
|
+
(offset[1] === "-" ? -1 : 1)
|
|
91
|
+
: 0
|
|
92
|
+
const parts = (input: string) => {
|
|
93
|
+
if (!input.includes("T")) {
|
|
94
|
+
return { date: input, time: "00:00" }
|
|
95
|
+
}
|
|
96
|
+
const wallClock = input.match(
|
|
97
|
+
/^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2})(?::\d{2}(?:\.\d+)?)?$/,
|
|
98
|
+
)
|
|
99
|
+
if (wallClock) {
|
|
100
|
+
return { date: wallClock[1], time: wallClock[2] }
|
|
101
|
+
}
|
|
102
|
+
if (!value.time_zone) {
|
|
103
|
+
const local = new Date(
|
|
104
|
+
new Date(input).getTime() + offsetMinutes * 60_000,
|
|
105
|
+
).toISOString()
|
|
106
|
+
return { date: local.slice(0, 10), time: local.slice(11, 16) }
|
|
107
|
+
}
|
|
108
|
+
const entries = new Intl.DateTimeFormat("en-CA", {
|
|
109
|
+
timeZone: value.time_zone,
|
|
110
|
+
year: "numeric",
|
|
111
|
+
month: "2-digit",
|
|
112
|
+
day: "2-digit",
|
|
113
|
+
hour: "2-digit",
|
|
114
|
+
minute: "2-digit",
|
|
115
|
+
hourCycle: "h23",
|
|
116
|
+
}).formatToParts(new Date(input))
|
|
117
|
+
const get = (type: string) =>
|
|
118
|
+
entries.find(part => part.type === type)?.value
|
|
119
|
+
return {
|
|
120
|
+
date: `${get("year")}-${get("month")}-${get("day")}`,
|
|
121
|
+
time: `${get("hour")}:${get("minute")}`,
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const start = parts(value.start)
|
|
125
|
+
const end = value.end ? parts(value.end) : undefined
|
|
126
|
+
if (!timed) {
|
|
127
|
+
return end
|
|
128
|
+
? { type: "daterange", start_date: start.date, end_date: end.date }
|
|
129
|
+
: { type: "date", start_date: start.date }
|
|
130
|
+
}
|
|
131
|
+
const zone =
|
|
132
|
+
value.time_zone ??
|
|
133
|
+
(offsetMinutes === 0
|
|
134
|
+
? "UTC"
|
|
135
|
+
: `UTC${offsetMinutes < 0 ? "-" : "+"}${Math.floor(Math.abs(offsetMinutes) / 60)}${Math.abs(offsetMinutes) % 60 === 0 ? "" : `:${String(Math.abs(offsetMinutes) % 60).padStart(2, "0")}`}`)
|
|
136
|
+
return end
|
|
137
|
+
? {
|
|
138
|
+
type: "datetimerange",
|
|
139
|
+
start_date: start.date,
|
|
140
|
+
start_time: start.time,
|
|
141
|
+
end_date: end.date,
|
|
142
|
+
end_time: end.time,
|
|
143
|
+
time_zone: zone,
|
|
144
|
+
}
|
|
145
|
+
: {
|
|
146
|
+
type: "datetime",
|
|
147
|
+
start_date: start.date,
|
|
148
|
+
start_time: start.time,
|
|
149
|
+
time_zone: zone,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function toLegacyProperty(
|
|
154
|
+
property: NotionPagePropertyValue,
|
|
155
|
+
): NotionDataSourceValue | undefined {
|
|
156
|
+
switch (property.type) {
|
|
157
|
+
case "title":
|
|
158
|
+
return plainText(property.title)
|
|
159
|
+
case "rich_text":
|
|
160
|
+
return plainText(property.rich_text)
|
|
161
|
+
case "number":
|
|
162
|
+
return property.number ?? undefined
|
|
163
|
+
case "checkbox":
|
|
164
|
+
return property.checkbox
|
|
165
|
+
case "url":
|
|
166
|
+
return property.url ?? ""
|
|
167
|
+
case "email":
|
|
168
|
+
return property.email ?? ""
|
|
169
|
+
case "phone_number":
|
|
170
|
+
return property.phone_number ?? ""
|
|
171
|
+
case "select":
|
|
172
|
+
return property.select?.name
|
|
173
|
+
case "status":
|
|
174
|
+
return property.status?.name
|
|
175
|
+
case "multi_select":
|
|
176
|
+
return property.multi_select.flatMap(option => option.name ?? [])
|
|
177
|
+
case "date":
|
|
178
|
+
return property.date ? legacyDate(property.date) : undefined
|
|
179
|
+
case "people":
|
|
180
|
+
return property.people.map(person =>
|
|
181
|
+
person.object === "group"
|
|
182
|
+
? {
|
|
183
|
+
id: person.id,
|
|
184
|
+
table: "space_permission_group",
|
|
185
|
+
spaceId: person.id.split("|")[1],
|
|
186
|
+
}
|
|
187
|
+
: { id: person.id, table: "notion_user" },
|
|
188
|
+
)
|
|
189
|
+
case "relation":
|
|
190
|
+
return property.relation.map(page => ({ id: page.id, table: "block" }))
|
|
191
|
+
case "files":
|
|
192
|
+
return property.files.map(file => file.name).join(",")
|
|
193
|
+
case "place":
|
|
194
|
+
return ""
|
|
195
|
+
case "created_time":
|
|
196
|
+
return timestamp(property.created_time)
|
|
197
|
+
case "last_edited_time":
|
|
198
|
+
return timestamp(property.last_edited_time)
|
|
199
|
+
case "last_visited_time":
|
|
200
|
+
return timestamp(property.last_visited_time)
|
|
201
|
+
case "created_by":
|
|
202
|
+
return property.created_by ? [property.created_by] : undefined
|
|
203
|
+
case "last_edited_by":
|
|
204
|
+
return property.last_edited_by ? [property.last_edited_by] : undefined
|
|
205
|
+
default:
|
|
206
|
+
return unreachable(property)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -1,46 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
HostToSandboxMessage,
|
|
3
|
+
HostToSandboxMessageLegacy,
|
|
4
|
+
} from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
|
|
5
|
+
import { normalizeToLegacyPage } from "./normalizePage.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
|
|
11
|
+
// reaches 6.
|
|
9
12
|
export function normalizeHostToSandboxMessage(
|
|
10
13
|
message: HostToSandboxMessage,
|
|
11
14
|
bridgeProtocolVersion: number,
|
|
12
|
-
):
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
): HostToSandboxMessageLegacy {
|
|
16
|
+
if (
|
|
17
|
+
message.type !== "queryDataSourceResult" &&
|
|
18
|
+
message.type !== "getPageResult" &&
|
|
19
|
+
message.type !== "createPageResult" &&
|
|
20
|
+
message.type !== "updatePageResult"
|
|
21
|
+
) {
|
|
15
22
|
return message
|
|
16
23
|
}
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
}
|
|
24
|
+
const normalizedMessage = normalizeToLegacyPage(
|
|
25
|
+
message,
|
|
26
|
+
bridgeProtocolVersion,
|
|
27
|
+
)
|
|
28
|
+
return normalizedMessage
|
|
46
29
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { CreatePageResultMessage } from "@notionhq/custom-blocks-protocol/messages/createPageResult.js"
|
|
2
|
+
import type { GetPageResultMessage } from "@notionhq/custom-blocks-protocol/messages/getPage.js"
|
|
3
|
+
import type { HostToSandboxMessageLegacy } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
|
|
4
|
+
import type { QueryDataSourceResultMessage } from "@notionhq/custom-blocks-protocol/messages/queryDataSourceResult.js"
|
|
5
|
+
import type { UpdatePageResultMessage } from "@notionhq/custom-blocks-protocol/messages/updatePageResult.js"
|
|
6
|
+
import { LEGACY_PROPERTY_SHAPE_PROTOCOL_VERSION } from "@notionhq/custom-blocks-protocol/protocolVersion.js"
|
|
7
|
+
import { unreachable } from "../utils.js"
|
|
8
|
+
import { toLegacyPage, toLegacyQueryPage } from "./legacyPage.js"
|
|
9
|
+
|
|
10
|
+
export function normalizeToLegacyPage(
|
|
11
|
+
message:
|
|
12
|
+
| CreatePageResultMessage
|
|
13
|
+
| GetPageResultMessage
|
|
14
|
+
| UpdatePageResultMessage
|
|
15
|
+
| QueryDataSourceResultMessage,
|
|
16
|
+
bridgeProtocolVersion: number,
|
|
17
|
+
): HostToSandboxMessageLegacy {
|
|
18
|
+
// Forward up to date messages
|
|
19
|
+
if (bridgeProtocolVersion >= LEGACY_PROPERTY_SHAPE_PROTOCOL_VERSION) {
|
|
20
|
+
return message
|
|
21
|
+
}
|
|
22
|
+
if (message.status === "error") {
|
|
23
|
+
return message
|
|
24
|
+
}
|
|
25
|
+
switch (message.type) {
|
|
26
|
+
case "queryDataSourceResult":
|
|
27
|
+
return { ...message, items: message.items.map(toLegacyQueryPage) }
|
|
28
|
+
case "getPageResult":
|
|
29
|
+
case "createPageResult":
|
|
30
|
+
case "updatePageResult":
|
|
31
|
+
return { ...message, page: toLegacyPage(message.page) }
|
|
32
|
+
default:
|
|
33
|
+
return unreachable(message)
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/messages/types.ts
CHANGED
|
@@ -57,7 +57,7 @@ import type {
|
|
|
57
57
|
import type { InferInput } from "valibot"
|
|
58
58
|
import type { CustomBlockHostQueryDataSourceResult } from "../queries/types.js"
|
|
59
59
|
|
|
60
|
-
type MaybePromise<T> = T | Promise<T>
|
|
60
|
+
export type MaybePromise<T> = T | Promise<T>
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Host results use InferInput so adapters can return plain string IDs.
|