notion-mcp-server 1.0.0 → 2.4.2
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 +383 -192
- package/build/config/index.js +3 -1
- package/build/dispatch/concurrency.js +15 -0
- package/build/dispatch/idempotency.js +38 -0
- package/build/dispatch/index.js +175 -0
- package/build/dispatch/rate-limit.js +56 -0
- package/build/dispatch/retry.js +97 -0
- package/build/index.js +1 -1
- package/build/markdown/parse.js +265 -0
- package/build/operations/blocks.js +331 -0
- package/build/operations/comments.js +191 -0
- package/build/operations/data-sources.js +85 -0
- package/build/operations/databases.js +345 -0
- package/build/operations/files.js +239 -0
- package/build/operations/index.js +19 -0
- package/build/operations/pages.js +486 -0
- package/build/operations/registry.js +16 -0
- package/build/operations/users.js +101 -0
- package/build/prompts/index.js +105 -0
- package/build/schema/blocks.js +19 -77
- package/build/schema/database.js +27 -86
- package/build/schema/emit.js +68 -0
- package/build/schema/file.js +1 -1
- package/build/schema/filter-dsl.js +333 -0
- package/build/schema/icon.js +1 -1
- package/build/schema/page-properties.js +17 -3
- package/build/schema/page.js +12 -88
- package/build/schema/refs.js +16 -0
- package/build/schema/rich-text.js +1 -1
- package/build/server/index.js +15 -2
- package/build/services/auth.js +19 -0
- package/build/services/notion.js +14 -17
- package/build/tools/index.js +119 -51
- package/build/utils/error.js +125 -86
- package/build/utils/handler.js +11 -0
- package/build/utils/learning-error.js +40 -0
- package/build/utils/notion-types.js +16 -0
- package/build/utils/paginate.js +35 -0
- package/build/utils/schema-slice.js +156 -0
- package/build/utils/slim.js +269 -0
- package/package.json +13 -7
- package/build/resources/imageList.js +0 -62
- package/build/resources/index.js +0 -1
- package/build/resources/predictionList.js +0 -43
- package/build/resources/svgList.js +0 -69
- package/build/schema/comments.js +0 -34
- package/build/schema/notion.js +0 -57
- package/build/schema/richText.js +0 -757
- package/build/schema/tools.js +0 -17
- package/build/schema/users.js +0 -13
- package/build/services/replicate.js +0 -23
- package/build/tools/appendBlockChildren.js +0 -25
- package/build/tools/batchAppendBlockChildren.js +0 -33
- package/build/tools/batchDeleteBlocks.js +0 -32
- package/build/tools/batchMixedOperations.js +0 -58
- package/build/tools/batchUpdateBlocks.js +0 -33
- package/build/tools/comments.js +0 -62
- package/build/tools/createDatabase.js +0 -18
- package/build/tools/createPage.js +0 -18
- package/build/tools/createPrediction.js +0 -28
- package/build/tools/deleteBlock.js +0 -24
- package/build/tools/formatRichText.js +0 -83
- package/build/tools/generateImage.js +0 -48
- package/build/tools/generateImageVariants.js +0 -105
- package/build/tools/generateMultipleImages.js +0 -60
- package/build/tools/generateSVG.js +0 -43
- package/build/tools/getPrediction.js +0 -22
- package/build/tools/predictionList.js +0 -30
- package/build/tools/queryDatabase.js +0 -22
- package/build/tools/retrieveBlock.js +0 -24
- package/build/tools/retrieveBlockChildren.js +0 -32
- package/build/tools/searchPage.js +0 -24
- package/build/tools/updateBlock.js +0 -25
- package/build/tools/updateDatabase.js +0 -18
- package/build/tools/updatePage.js +0 -40
- package/build/tools/updatePageProperties.js +0 -21
- package/build/tools/users.js +0 -62
- package/build/types/blocks.js +0 -11
- package/build/types/comments.js +0 -6
- package/build/types/database.js +0 -5
- package/build/types/notion.js +0 -1
- package/build/types/page.js +0 -7
- package/build/types/richText.js +0 -1
- package/build/types/tools.js +0 -1
- package/build/types/users.js +0 -5
- package/build/utils/blob.js +0 -5
- package/build/utils/image.js +0 -34
- package/build/utils/index.js +0 -1
- package/build/utils/richText.js +0 -174
- package/build/validation/blocks.js +0 -568
- package/build/validation/notion.js +0 -51
- package/build/validation/page.js +0 -262
- package/build/validation/richText.js +0 -744
- package/build/validation/tools.js +0 -16
- /package/build/{types/index.js → operations/types.js} +0 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { server } from "../server/index.js";
|
|
3
|
+
function userMessage(text) {
|
|
4
|
+
return {
|
|
5
|
+
messages: [
|
|
6
|
+
{
|
|
7
|
+
role: "user",
|
|
8
|
+
content: { type: "text", text },
|
|
9
|
+
},
|
|
10
|
+
],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function registerAllPrompts() {
|
|
14
|
+
server.registerPrompt("create_task", {
|
|
15
|
+
title: "Create Notion task",
|
|
16
|
+
description: "Create a new task page in Notion with optional status and due date.",
|
|
17
|
+
argsSchema: {
|
|
18
|
+
title: z.string().describe("Task title."),
|
|
19
|
+
status: z
|
|
20
|
+
.string()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Status select value, e.g. Todo / In Progress / Done."),
|
|
23
|
+
due: z
|
|
24
|
+
.string()
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("Due date as ISO 8601 (YYYY-MM-DD)."),
|
|
27
|
+
},
|
|
28
|
+
}, ({ title, status, due }) => {
|
|
29
|
+
const propLines = [`- title: ${JSON.stringify(title)}`];
|
|
30
|
+
if (status)
|
|
31
|
+
propLines.push(`- status: ${JSON.stringify(status)}`);
|
|
32
|
+
if (due)
|
|
33
|
+
propLines.push(`- due: ${JSON.stringify(due)}`);
|
|
34
|
+
return userMessage([
|
|
35
|
+
`Create a new Notion task page with these fields:`,
|
|
36
|
+
...propLines,
|
|
37
|
+
``,
|
|
38
|
+
`Steps:`,
|
|
39
|
+
`1. If you don't already know which database holds tasks, call notion_execute with operation "search_pages" (or query_database against a known tasks DB) to locate it.`,
|
|
40
|
+
`2. Call notion_execute with operation "create_page" and a payload that sets parent.database_id (or data_source_id) plus a properties object containing Title=${JSON.stringify(title)}${status ? `, Status=${JSON.stringify(status)}` : ""}${due ? `, Due=${JSON.stringify(due)}` : ""}.`,
|
|
41
|
+
`3. Return the new page url to the user.`,
|
|
42
|
+
].join("\n"));
|
|
43
|
+
});
|
|
44
|
+
server.registerPrompt("weekly_review", {
|
|
45
|
+
title: "Weekly review of completed work",
|
|
46
|
+
description: "Summarize tasks marked Done in the last 7 days from a Notion database.",
|
|
47
|
+
argsSchema: {},
|
|
48
|
+
}, () => userMessage([
|
|
49
|
+
`Generate a weekly review of completed Notion tasks.`,
|
|
50
|
+
``,
|
|
51
|
+
`Steps:`,
|
|
52
|
+
`1. Identify the tasks database (ask the user if you don't know its id).`,
|
|
53
|
+
`2. Call notion_execute with operation "query_database" using a filter for Status=Done AND Last edited time (or Created time) on_or_after the date 7 days ago. Sort by last_edited_time descending.`,
|
|
54
|
+
`3. Summarize the results grouped by theme or project, with bullet points and links to each page.`,
|
|
55
|
+
].join("\n")));
|
|
56
|
+
server.registerPrompt("find_pages", {
|
|
57
|
+
title: "Find Notion pages by query",
|
|
58
|
+
description: "Search Notion and show the top 5 matching pages.",
|
|
59
|
+
argsSchema: {
|
|
60
|
+
query: z.string().describe("Text to search for across page titles."),
|
|
61
|
+
},
|
|
62
|
+
}, ({ query }) => userMessage([
|
|
63
|
+
`Find Notion pages matching ${JSON.stringify(query)}.`,
|
|
64
|
+
``,
|
|
65
|
+
`Steps:`,
|
|
66
|
+
`1. Call notion_execute with operation "search_pages" and payload { "query": ${JSON.stringify(query)} }.`,
|
|
67
|
+
`2. Take the top 5 results and present them as a numbered list with each page's title and url.`,
|
|
68
|
+
`3. If there are no results, say so plainly.`,
|
|
69
|
+
].join("\n")));
|
|
70
|
+
server.registerPrompt("daily_log", {
|
|
71
|
+
title: "Append to daily log",
|
|
72
|
+
description: "Append a timestamped paragraph to a daily-log page in Notion.",
|
|
73
|
+
argsSchema: {
|
|
74
|
+
date: z
|
|
75
|
+
.string()
|
|
76
|
+
.optional()
|
|
77
|
+
.describe("Date for the entry as ISO 8601 (defaults to today)."),
|
|
78
|
+
content: z
|
|
79
|
+
.string()
|
|
80
|
+
.optional()
|
|
81
|
+
.describe("Text to append; prompt the user if omitted."),
|
|
82
|
+
},
|
|
83
|
+
}, ({ date, content }) => {
|
|
84
|
+
const pageId = process.env.NOTION_DAILY_LOG_PAGE_ID;
|
|
85
|
+
const pageLine = pageId
|
|
86
|
+
? `The daily-log page id is ${pageId} (from NOTION_DAILY_LOG_PAGE_ID).`
|
|
87
|
+
: `NOTION_DAILY_LOG_PAGE_ID is not set — ask the user for the daily-log page id or search for it.`;
|
|
88
|
+
const dateLine = date ? `Use the date ${date}.` : `Use today's date.`;
|
|
89
|
+
const contentLine = content
|
|
90
|
+
? `Append this content: ${JSON.stringify(content)}.`
|
|
91
|
+
: `Ask the user what to log if the content is not yet known.`;
|
|
92
|
+
return userMessage([
|
|
93
|
+
`Append a timestamped paragraph to the user's daily-log page.`,
|
|
94
|
+
``,
|
|
95
|
+
pageLine,
|
|
96
|
+
dateLine,
|
|
97
|
+
contentLine,
|
|
98
|
+
``,
|
|
99
|
+
`Steps:`,
|
|
100
|
+
`1. Compose a paragraph block prefixed with the timestamp (date + current time).`,
|
|
101
|
+
`2. Call notion_execute with operation "append_blocks" and a payload of { "block_id": "<daily-log page id>", "children": [{ "type": "paragraph", "paragraph": { "rich_text": [{ "type": "text", "text": { "content": "<timestamp> — <content>" } }] } }] }.`,
|
|
102
|
+
`3. Report back the appended block's id.`,
|
|
103
|
+
].join("\n"));
|
|
104
|
+
});
|
|
105
|
+
}
|
package/build/schema/blocks.js
CHANGED
|
@@ -59,6 +59,23 @@ export const HEADING3_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
|
|
|
59
59
|
.describe("Whether heading can be toggled"),
|
|
60
60
|
}).describe("Heading 3 block content"),
|
|
61
61
|
});
|
|
62
|
+
export const HEADING4_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
|
|
63
|
+
type: z.literal("heading_4").describe("Heading 4 block type"),
|
|
64
|
+
heading_4: TEXT_BLOCK_BASE_REQUEST_SCHEMA.extend({
|
|
65
|
+
is_toggleable: z
|
|
66
|
+
.boolean()
|
|
67
|
+
.optional()
|
|
68
|
+
.describe("Whether heading can be toggled"),
|
|
69
|
+
}).describe("Heading 4 block content"),
|
|
70
|
+
});
|
|
71
|
+
export const TAB_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
|
|
72
|
+
type: z.literal("tab").describe("Tab block type"),
|
|
73
|
+
tab: z
|
|
74
|
+
.object({
|
|
75
|
+
icon: ICON_SCHEMA.optional(),
|
|
76
|
+
})
|
|
77
|
+
.describe("Tab block content"),
|
|
78
|
+
});
|
|
62
79
|
export const QUOTE_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
|
|
63
80
|
type: z.literal("quote").describe("Quote block type"),
|
|
64
81
|
quote: TEXT_BLOCK_BASE_REQUEST_SCHEMA.describe("Quote block content"),
|
|
@@ -119,6 +136,7 @@ export const TEXT_BLOCK_REQUEST_SCHEMA = z.preprocess(preprocessJson, z
|
|
|
119
136
|
HEADING1_BLOCK_REQUEST_SCHEMA,
|
|
120
137
|
HEADING2_BLOCK_REQUEST_SCHEMA,
|
|
121
138
|
HEADING3_BLOCK_REQUEST_SCHEMA,
|
|
139
|
+
HEADING4_BLOCK_REQUEST_SCHEMA,
|
|
122
140
|
QUOTE_BLOCK_REQUEST_SCHEMA,
|
|
123
141
|
CALLOUT_BLOCK_REQUEST_SCHEMA,
|
|
124
142
|
TOGGLE_BLOCK_REQUEST_SCHEMA,
|
|
@@ -128,82 +146,6 @@ export const TEXT_BLOCK_REQUEST_SCHEMA = z.preprocess(preprocessJson, z
|
|
|
128
146
|
CODE_BLOCK_REQUEST_SCHEMA,
|
|
129
147
|
DIVIDER_BLOCK_REQUEST_SCHEMA,
|
|
130
148
|
IMAGE_BLOCK_REQUEST_SCHEMA,
|
|
149
|
+
TAB_BLOCK_REQUEST_SCHEMA,
|
|
131
150
|
])
|
|
132
151
|
.describe("Union of all possible text block request types"));
|
|
133
|
-
export const APPEND_BLOCK_CHILDREN_SCHEMA = {
|
|
134
|
-
blockId: z.string().describe("The ID of the block to append children to"),
|
|
135
|
-
children: z
|
|
136
|
-
.array(TEXT_BLOCK_REQUEST_SCHEMA)
|
|
137
|
-
.describe("Array of blocks to append as children"),
|
|
138
|
-
};
|
|
139
|
-
export const RETRIEVE_BLOCK_SCHEMA = {
|
|
140
|
-
blockId: z.string().describe("The ID of the block to retrieve"),
|
|
141
|
-
};
|
|
142
|
-
export const RETRIEVE_BLOCK_CHILDREN_SCHEMA = {
|
|
143
|
-
blockId: z.string().describe("The ID of the block to retrieve children for"),
|
|
144
|
-
start_cursor: z.string().optional().describe("Cursor for pagination"),
|
|
145
|
-
page_size: z
|
|
146
|
-
.number()
|
|
147
|
-
.min(1)
|
|
148
|
-
.max(100)
|
|
149
|
-
.optional()
|
|
150
|
-
.describe("Number of results to return (1-100)"),
|
|
151
|
-
};
|
|
152
|
-
export const UPDATE_BLOCK_SCHEMA = {
|
|
153
|
-
blockId: z.string().describe("The ID of the block to update"),
|
|
154
|
-
data: TEXT_BLOCK_REQUEST_SCHEMA.describe("The block data to update"),
|
|
155
|
-
};
|
|
156
|
-
export const DELETE_BLOCK_SCHEMA = {
|
|
157
|
-
blockId: z.string().describe("The ID of the block to delete/archive"),
|
|
158
|
-
};
|
|
159
|
-
// Batch operation schemas for multiple blocks
|
|
160
|
-
export const BATCH_APPEND_BLOCK_CHILDREN_SCHEMA = {
|
|
161
|
-
operations: z
|
|
162
|
-
.array(z.object({
|
|
163
|
-
blockId: z
|
|
164
|
-
.string()
|
|
165
|
-
.describe("The ID of the block to append children to"),
|
|
166
|
-
children: z
|
|
167
|
-
.array(TEXT_BLOCK_REQUEST_SCHEMA)
|
|
168
|
-
.describe("Array of blocks to append as children"),
|
|
169
|
-
}))
|
|
170
|
-
.describe("Array of append operations to perform in a single batch"),
|
|
171
|
-
};
|
|
172
|
-
export const BATCH_UPDATE_BLOCKS_SCHEMA = {
|
|
173
|
-
operations: z
|
|
174
|
-
.array(z.object({
|
|
175
|
-
blockId: z.string().describe("The ID of the block to update"),
|
|
176
|
-
data: TEXT_BLOCK_REQUEST_SCHEMA.describe("The block data to update"),
|
|
177
|
-
}))
|
|
178
|
-
.describe("Array of update operations to perform in a single batch"),
|
|
179
|
-
};
|
|
180
|
-
export const BATCH_DELETE_BLOCKS_SCHEMA = {
|
|
181
|
-
blockIds: z
|
|
182
|
-
.array(z.string().describe("The ID of a block to delete/archive"))
|
|
183
|
-
.describe("Array of block IDs to delete in a single batch"),
|
|
184
|
-
};
|
|
185
|
-
// Schema for multi-operation batches (mixed operations)
|
|
186
|
-
export const BATCH_MIXED_OPERATIONS_SCHEMA = {
|
|
187
|
-
operations: z
|
|
188
|
-
.array(z.discriminatedUnion("operation", [
|
|
189
|
-
z.object({
|
|
190
|
-
operation: z.literal("append"),
|
|
191
|
-
blockId: z
|
|
192
|
-
.string()
|
|
193
|
-
.describe("The ID of the block to append children to"),
|
|
194
|
-
children: z
|
|
195
|
-
.array(TEXT_BLOCK_REQUEST_SCHEMA)
|
|
196
|
-
.describe("Array of blocks to append as children"),
|
|
197
|
-
}),
|
|
198
|
-
z.object({
|
|
199
|
-
operation: z.literal("update"),
|
|
200
|
-
blockId: z.string().describe("The ID of the block to update"),
|
|
201
|
-
data: TEXT_BLOCK_REQUEST_SCHEMA.describe("The block data to update"),
|
|
202
|
-
}),
|
|
203
|
-
z.object({
|
|
204
|
-
operation: z.literal("delete"),
|
|
205
|
-
blockId: z.string().describe("The ID of the block to delete/archive"),
|
|
206
|
-
}),
|
|
207
|
-
]))
|
|
208
|
-
.describe("Array of mixed operations to perform in a single batch"),
|
|
209
|
-
};
|
package/build/schema/database.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { ICON_SCHEMA } from "./icon.js";
|
|
3
|
-
import { FILE_SCHEMA } from "./file.js";
|
|
4
|
-
import { PARENT_SCHEMA } from "./page.js";
|
|
5
|
-
import { RICH_TEXT_ITEM_REQUEST_SCHEMA, TEXT_CONTENT_REQUEST_SCHEMA, TEXT_RICH_TEXT_ITEM_REQUEST_SCHEMA, } from "./rich-text.js";
|
|
6
2
|
import { preprocessJson } from "./preprocess.js";
|
|
7
3
|
import { NUMBER_FORMAT } from "./number.js";
|
|
8
|
-
import { getRootPageId } from "../services/notion.js";
|
|
9
4
|
export const EMPTY_OBJECT_SCHEMA = z.record(z.string(), z.never()).default({});
|
|
10
5
|
export const SELECT_COLOR_SCHEMA = z.enum([
|
|
11
6
|
"default",
|
|
@@ -19,14 +14,6 @@ export const SELECT_COLOR_SCHEMA = z.enum([
|
|
|
19
14
|
"pink",
|
|
20
15
|
"red",
|
|
21
16
|
]);
|
|
22
|
-
// Title property for database creation
|
|
23
|
-
export const TITLE_PROPERTY_SCHEMA = z.object({
|
|
24
|
-
title: z
|
|
25
|
-
.array(z.object({
|
|
26
|
-
text: TEXT_CONTENT_REQUEST_SCHEMA.describe("Text content for title segment"),
|
|
27
|
-
}))
|
|
28
|
-
.describe("Array of text segments that make up the title"),
|
|
29
|
-
});
|
|
30
17
|
// Database property schemas
|
|
31
18
|
// 1. Title property
|
|
32
19
|
export const TITLE_DB_PROPERTY_SCHEMA = z.object({
|
|
@@ -242,6 +229,30 @@ export const STATUS_DB_PROPERTY_SCHEMA = z.object({
|
|
|
242
229
|
.describe("Status property configuration"),
|
|
243
230
|
description: z.string().optional().describe("Property description"),
|
|
244
231
|
});
|
|
232
|
+
export const BUTTON_DB_PROPERTY_SCHEMA = z.object({
|
|
233
|
+
type: z.literal("button"),
|
|
234
|
+
button: EMPTY_OBJECT_SCHEMA,
|
|
235
|
+
description: z.string().optional(),
|
|
236
|
+
});
|
|
237
|
+
export const UNIQUE_ID_DB_PROPERTY_SCHEMA = z.object({
|
|
238
|
+
type: z.literal("unique_id"),
|
|
239
|
+
unique_id: z
|
|
240
|
+
.object({
|
|
241
|
+
prefix: z
|
|
242
|
+
.string()
|
|
243
|
+
.regex(/^[A-Za-z][A-Za-z0-9-]{1,9}$/, "prefix must start with a letter, then 1–9 more letters/digits/hyphens (total length 2–10)")
|
|
244
|
+
.nullable()
|
|
245
|
+
.optional()
|
|
246
|
+
.describe("Prefix shown before the auto-incrementing number. 2–10 chars total, must start with a letter, alphanumeric + hyphen only. Single-letter prefixes are rejected by Notion."),
|
|
247
|
+
})
|
|
248
|
+
.describe("Unique ID property configuration"),
|
|
249
|
+
description: z.string().optional(),
|
|
250
|
+
});
|
|
251
|
+
export const VERIFICATION_DB_PROPERTY_SCHEMA = z.object({
|
|
252
|
+
type: z.literal("verification"),
|
|
253
|
+
verification: EMPTY_OBJECT_SCHEMA,
|
|
254
|
+
description: z.string().optional(),
|
|
255
|
+
});
|
|
245
256
|
// Combined database property schema
|
|
246
257
|
export const DATABASE_PROPERTY_SCHEMA = z.preprocess(preprocessJson, z
|
|
247
258
|
.discriminatedUnion("type", [
|
|
@@ -264,78 +275,8 @@ export const DATABASE_PROPERTY_SCHEMA = z.preprocess(preprocessJson, z
|
|
|
264
275
|
CREATED_BY_DB_PROPERTY_SCHEMA,
|
|
265
276
|
LAST_EDITED_TIME_DB_PROPERTY_SCHEMA,
|
|
266
277
|
LAST_EDITED_BY_DB_PROPERTY_SCHEMA,
|
|
278
|
+
BUTTON_DB_PROPERTY_SCHEMA,
|
|
279
|
+
UNIQUE_ID_DB_PROPERTY_SCHEMA,
|
|
280
|
+
VERIFICATION_DB_PROPERTY_SCHEMA,
|
|
267
281
|
])
|
|
268
282
|
.describe("Union of all possible database property types"));
|
|
269
|
-
// Create database schema
|
|
270
|
-
export const CREATE_DATABASE_SCHEMA = {
|
|
271
|
-
parent: PARENT_SCHEMA.optional()
|
|
272
|
-
.default({
|
|
273
|
-
type: "page_id",
|
|
274
|
-
page_id: getRootPageId(),
|
|
275
|
-
})
|
|
276
|
-
.describe("Optional parent - if not provided, will use NOTION_PAGE_ID as parent page"),
|
|
277
|
-
title: z.array(TEXT_RICH_TEXT_ITEM_REQUEST_SCHEMA).describe("Database title"),
|
|
278
|
-
description: z
|
|
279
|
-
.array(TEXT_RICH_TEXT_ITEM_REQUEST_SCHEMA)
|
|
280
|
-
.optional()
|
|
281
|
-
.describe("Database description"),
|
|
282
|
-
properties: z
|
|
283
|
-
.record(z.string().describe("Property name"), DATABASE_PROPERTY_SCHEMA.describe("Property schema"))
|
|
284
|
-
.describe("Database properties"),
|
|
285
|
-
is_inline: z
|
|
286
|
-
.boolean()
|
|
287
|
-
.optional()
|
|
288
|
-
.default(false)
|
|
289
|
-
.describe("Whether database is inline"),
|
|
290
|
-
icon: z.preprocess(preprocessJson, ICON_SCHEMA.nullable().optional().describe("Optional icon for the database")),
|
|
291
|
-
cover: z.preprocess(preprocessJson, FILE_SCHEMA.nullable()
|
|
292
|
-
.optional()
|
|
293
|
-
.describe("Optional cover image for the database")),
|
|
294
|
-
};
|
|
295
|
-
// Query database schema
|
|
296
|
-
export const QUERY_DATABASE_SCHEMA = {
|
|
297
|
-
database_id: z.string().describe("The ID of the database to query"),
|
|
298
|
-
filter: z
|
|
299
|
-
.preprocess(preprocessJson, z.any())
|
|
300
|
-
.optional()
|
|
301
|
-
.describe("Filter criteria for the query"),
|
|
302
|
-
sorts: z
|
|
303
|
-
.array(z.object({
|
|
304
|
-
property: z.string().optional().describe("Property to sort by"),
|
|
305
|
-
timestamp: z
|
|
306
|
-
.enum(["created_time", "last_edited_time"])
|
|
307
|
-
.describe("Timestamp to sort by"),
|
|
308
|
-
direction: z
|
|
309
|
-
.enum(["ascending", "descending"])
|
|
310
|
-
.describe("Sort direction"),
|
|
311
|
-
}))
|
|
312
|
-
.optional()
|
|
313
|
-
.describe("Sort criteria for the query"),
|
|
314
|
-
start_cursor: z.string().optional().describe("Cursor for pagination"),
|
|
315
|
-
page_size: z
|
|
316
|
-
.number()
|
|
317
|
-
.min(1)
|
|
318
|
-
.max(100)
|
|
319
|
-
.optional()
|
|
320
|
-
.describe("Number of results to return (1-100)"),
|
|
321
|
-
};
|
|
322
|
-
// Update database schema
|
|
323
|
-
export const UPDATE_DATABASE_SCHEMA = {
|
|
324
|
-
database_id: z.string().describe("The ID of the database to update"),
|
|
325
|
-
title: z
|
|
326
|
-
.array(RICH_TEXT_ITEM_REQUEST_SCHEMA)
|
|
327
|
-
.optional()
|
|
328
|
-
.describe("Updated database title"),
|
|
329
|
-
description: z
|
|
330
|
-
.array(RICH_TEXT_ITEM_REQUEST_SCHEMA)
|
|
331
|
-
.optional()
|
|
332
|
-
.describe("Updated database description"),
|
|
333
|
-
properties: z
|
|
334
|
-
.record(z.string().describe("Property name"), DATABASE_PROPERTY_SCHEMA.describe("Property schema"))
|
|
335
|
-
.describe("Properties of the page"),
|
|
336
|
-
is_inline: z.boolean().optional().describe("Whether database is inline"),
|
|
337
|
-
icon: z.preprocess(preprocessJson, ICON_SCHEMA.nullable().optional().describe("Updated icon for the database")),
|
|
338
|
-
cover: z.preprocess(preprocessJson, FILE_SCHEMA.nullable()
|
|
339
|
-
.optional()
|
|
340
|
-
.describe("Updated cover image for the database")),
|
|
341
|
-
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const SHARED_REFS = {};
|
|
3
|
+
export function registerSharedRef(name, schema) {
|
|
4
|
+
SHARED_REFS[name] = schema;
|
|
5
|
+
}
|
|
6
|
+
export function emitJsonSchema(schema) {
|
|
7
|
+
const raw = z.toJSONSchema(schema, {
|
|
8
|
+
target: "draft-7",
|
|
9
|
+
unrepresentable: "any",
|
|
10
|
+
});
|
|
11
|
+
return hoistSharedRefs(raw);
|
|
12
|
+
}
|
|
13
|
+
function hoistSharedRefs(root) {
|
|
14
|
+
const sharedSchemas = {};
|
|
15
|
+
for (const [name, zodSchema] of Object.entries(SHARED_REFS)) {
|
|
16
|
+
const raw = z.toJSONSchema(zodSchema, {
|
|
17
|
+
target: "draft-7",
|
|
18
|
+
unrepresentable: "any",
|
|
19
|
+
});
|
|
20
|
+
// Root-level emission adds `$schema`, but nested inline usages don't carry it.
|
|
21
|
+
// Strip it so the equality key matches inline sites.
|
|
22
|
+
const { $schema: _drop, ...rest } = raw;
|
|
23
|
+
void _drop;
|
|
24
|
+
sharedSchemas[name] = rest;
|
|
25
|
+
}
|
|
26
|
+
const refByJson = new Map();
|
|
27
|
+
for (const [name, json] of Object.entries(sharedSchemas)) {
|
|
28
|
+
refByJson.set(stableStringify(json), name);
|
|
29
|
+
}
|
|
30
|
+
let usedNames = new Set();
|
|
31
|
+
function walk(node) {
|
|
32
|
+
if (Array.isArray(node))
|
|
33
|
+
return node.map(walk);
|
|
34
|
+
if (node === null || typeof node !== "object")
|
|
35
|
+
return node;
|
|
36
|
+
const obj = node;
|
|
37
|
+
const key = stableStringify(obj);
|
|
38
|
+
const match = refByJson.get(key);
|
|
39
|
+
if (match) {
|
|
40
|
+
usedNames.add(match);
|
|
41
|
+
return { $ref: `#/$defs/${match}` };
|
|
42
|
+
}
|
|
43
|
+
const out = {};
|
|
44
|
+
for (const [k, v] of Object.entries(obj))
|
|
45
|
+
out[k] = walk(v);
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
const walked = walk(root);
|
|
49
|
+
if (usedNames.size === 0)
|
|
50
|
+
return walked;
|
|
51
|
+
const defs = walked.$defs ?? {};
|
|
52
|
+
for (const name of usedNames) {
|
|
53
|
+
if (!(name in defs))
|
|
54
|
+
defs[name] = walk(sharedSchemas[name]);
|
|
55
|
+
}
|
|
56
|
+
return { ...walked, $defs: defs };
|
|
57
|
+
}
|
|
58
|
+
function stableStringify(value) {
|
|
59
|
+
if (value === null || typeof value !== "object")
|
|
60
|
+
return JSON.stringify(value);
|
|
61
|
+
if (Array.isArray(value))
|
|
62
|
+
return "[" + value.map(stableStringify).join(",") + "]";
|
|
63
|
+
const obj = value;
|
|
64
|
+
const keys = Object.keys(obj).sort();
|
|
65
|
+
return ("{" +
|
|
66
|
+
keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k])).join(",") +
|
|
67
|
+
"}");
|
|
68
|
+
}
|
package/build/schema/file.js
CHANGED
|
@@ -3,7 +3,7 @@ export const FILE_SCHEMA = z
|
|
|
3
3
|
.object({
|
|
4
4
|
external: z
|
|
5
5
|
.object({
|
|
6
|
-
url: z.
|
|
6
|
+
url: z.url({ protocol: /^https?$/ }).describe("URL of the external file"),
|
|
7
7
|
})
|
|
8
8
|
.describe("External file source"),
|
|
9
9
|
type: z.literal("external").describe("Type of file source"),
|