ntion 0.1.0
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 +204 -0
- package/dist/audit/log.d.ts +10 -0
- package/dist/audit/log.js +18 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +939 -0
- package/dist/commands/context.d.ts +17 -0
- package/dist/commands/context.js +35 -0
- package/dist/commands/mutation.d.ts +8 -0
- package/dist/commands/mutation.js +102 -0
- package/dist/commands/output.d.ts +7 -0
- package/dist/commands/output.js +22 -0
- package/dist/concurrency/versioning.d.ts +1 -0
- package/dist/concurrency/versioning.js +5 -0
- package/dist/config/paths.d.ts +5 -0
- package/dist/config/paths.js +26 -0
- package/dist/config/store.d.ts +5 -0
- package/dist/config/store.js +72 -0
- package/dist/config/types.d.ts +141 -0
- package/dist/config/types.js +26 -0
- package/dist/contracts/envelope.d.ts +32 -0
- package/dist/contracts/envelope.js +35 -0
- package/dist/errors/cli-error.d.ts +11 -0
- package/dist/errors/cli-error.js +59 -0
- package/dist/errors/codes.d.ts +2 -0
- package/dist/errors/codes.js +9 -0
- package/dist/idempotency/store.d.ts +34 -0
- package/dist/idempotency/store.js +120 -0
- package/dist/notion/client.d.ts +17 -0
- package/dist/notion/client.js +140 -0
- package/dist/notion/mappers.d.ts +5 -0
- package/dist/notion/mappers.js +224 -0
- package/dist/notion/markdown.d.ts +1 -0
- package/dist/notion/markdown.js +237 -0
- package/dist/notion/properties.d.ts +7 -0
- package/dist/notion/properties.js +267 -0
- package/dist/notion/repository.d.ts +142 -0
- package/dist/notion/repository.js +998 -0
- package/dist/notion/types.d.ts +4 -0
- package/dist/notion/types.js +1 -0
- package/dist/utils/json.d.ts +3 -0
- package/dist/utils/json.js +32 -0
- package/package.json +34 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
const MAX_RICH_TEXT_CHARS = 1800;
|
|
2
|
+
function chunkText(content) {
|
|
3
|
+
if (content.length <= MAX_RICH_TEXT_CHARS) {
|
|
4
|
+
return [content];
|
|
5
|
+
}
|
|
6
|
+
const chunks = [];
|
|
7
|
+
for (let index = 0; index < content.length; index += MAX_RICH_TEXT_CHARS) {
|
|
8
|
+
chunks.push(content.slice(index, index + MAX_RICH_TEXT_CHARS));
|
|
9
|
+
}
|
|
10
|
+
return chunks;
|
|
11
|
+
}
|
|
12
|
+
function toRichText(content) {
|
|
13
|
+
const normalized = content.length > 0 ? content : " ";
|
|
14
|
+
return chunkText(normalized).map((chunk) => ({
|
|
15
|
+
type: "text",
|
|
16
|
+
text: {
|
|
17
|
+
content: chunk,
|
|
18
|
+
},
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
function paragraphBlock(text) {
|
|
22
|
+
return {
|
|
23
|
+
object: "block",
|
|
24
|
+
type: "paragraph",
|
|
25
|
+
paragraph: {
|
|
26
|
+
rich_text: toRichText(text),
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function headingBlock(level, text) {
|
|
31
|
+
const type = `heading_${level}`;
|
|
32
|
+
return {
|
|
33
|
+
object: "block",
|
|
34
|
+
type,
|
|
35
|
+
[type]: {
|
|
36
|
+
rich_text: toRichText(text),
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function bulletedItemBlock(text) {
|
|
41
|
+
return {
|
|
42
|
+
object: "block",
|
|
43
|
+
type: "bulleted_list_item",
|
|
44
|
+
bulleted_list_item: {
|
|
45
|
+
rich_text: toRichText(text),
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function numberedItemBlock(text) {
|
|
50
|
+
return {
|
|
51
|
+
object: "block",
|
|
52
|
+
type: "numbered_list_item",
|
|
53
|
+
numbered_list_item: {
|
|
54
|
+
rich_text: toRichText(text),
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function todoBlock(text, checked) {
|
|
59
|
+
return {
|
|
60
|
+
object: "block",
|
|
61
|
+
type: "to_do",
|
|
62
|
+
to_do: {
|
|
63
|
+
rich_text: toRichText(text),
|
|
64
|
+
checked,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function quoteBlock(text) {
|
|
69
|
+
return {
|
|
70
|
+
object: "block",
|
|
71
|
+
type: "quote",
|
|
72
|
+
quote: {
|
|
73
|
+
rich_text: toRichText(text),
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function dividerBlock() {
|
|
78
|
+
return {
|
|
79
|
+
object: "block",
|
|
80
|
+
type: "divider",
|
|
81
|
+
divider: {},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function normalizeCodeLanguage(raw) {
|
|
85
|
+
const language = raw.trim().toLowerCase();
|
|
86
|
+
if (!language) {
|
|
87
|
+
return "plain text";
|
|
88
|
+
}
|
|
89
|
+
switch (language) {
|
|
90
|
+
case "ts":
|
|
91
|
+
case "typescript":
|
|
92
|
+
return "typescript";
|
|
93
|
+
case "js":
|
|
94
|
+
case "javascript":
|
|
95
|
+
return "javascript";
|
|
96
|
+
case "py":
|
|
97
|
+
case "python":
|
|
98
|
+
return "python";
|
|
99
|
+
case "sh":
|
|
100
|
+
case "bash":
|
|
101
|
+
case "shell":
|
|
102
|
+
return "shell";
|
|
103
|
+
case "json":
|
|
104
|
+
return "json";
|
|
105
|
+
case "sql":
|
|
106
|
+
return "sql";
|
|
107
|
+
case "yaml":
|
|
108
|
+
case "yml":
|
|
109
|
+
return "yaml";
|
|
110
|
+
case "md":
|
|
111
|
+
case "markdown":
|
|
112
|
+
return "markdown";
|
|
113
|
+
case "html":
|
|
114
|
+
return "html";
|
|
115
|
+
case "css":
|
|
116
|
+
return "css";
|
|
117
|
+
case "go":
|
|
118
|
+
return "go";
|
|
119
|
+
case "java":
|
|
120
|
+
return "java";
|
|
121
|
+
case "ruby":
|
|
122
|
+
return "ruby";
|
|
123
|
+
case "rust":
|
|
124
|
+
return "rust";
|
|
125
|
+
default:
|
|
126
|
+
return "plain text";
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function codeBlock(text, language) {
|
|
130
|
+
return {
|
|
131
|
+
object: "block",
|
|
132
|
+
type: "code",
|
|
133
|
+
code: {
|
|
134
|
+
rich_text: toRichText(text),
|
|
135
|
+
language,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function isDivider(trimmed) {
|
|
140
|
+
return trimmed === "---" || trimmed === "***" || trimmed === "___";
|
|
141
|
+
}
|
|
142
|
+
export function markdownToBlocks(markdown) {
|
|
143
|
+
const normalized = markdown.replace(/\r\n/g, "\n");
|
|
144
|
+
const lines = normalized.split("\n");
|
|
145
|
+
const blocks = [];
|
|
146
|
+
let paragraphLines = [];
|
|
147
|
+
const flushParagraph = () => {
|
|
148
|
+
if (paragraphLines.length === 0) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const text = paragraphLines.join("\n").trim();
|
|
152
|
+
paragraphLines = [];
|
|
153
|
+
if (text.length > 0) {
|
|
154
|
+
blocks.push(paragraphBlock(text));
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
for (let index = 0; index < lines.length;) {
|
|
158
|
+
const line = lines[index];
|
|
159
|
+
const trimmed = line.trim();
|
|
160
|
+
if (trimmed.startsWith("```")) {
|
|
161
|
+
flushParagraph();
|
|
162
|
+
const language = normalizeCodeLanguage(trimmed.slice(3));
|
|
163
|
+
index += 1;
|
|
164
|
+
const codeLines = [];
|
|
165
|
+
while (index < lines.length && !lines[index].trim().startsWith("```")) {
|
|
166
|
+
codeLines.push(lines[index]);
|
|
167
|
+
index += 1;
|
|
168
|
+
}
|
|
169
|
+
if (index < lines.length && lines[index].trim().startsWith("```")) {
|
|
170
|
+
index += 1;
|
|
171
|
+
}
|
|
172
|
+
blocks.push(codeBlock(codeLines.join("\n"), language));
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (trimmed.length === 0) {
|
|
176
|
+
flushParagraph();
|
|
177
|
+
index += 1;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (isDivider(trimmed)) {
|
|
181
|
+
flushParagraph();
|
|
182
|
+
blocks.push(dividerBlock());
|
|
183
|
+
index += 1;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const headingMatch = trimmed.match(/^(#{1,3})\s+(.+)$/);
|
|
187
|
+
if (headingMatch) {
|
|
188
|
+
flushParagraph();
|
|
189
|
+
const level = headingMatch[1].length;
|
|
190
|
+
blocks.push(headingBlock(level, headingMatch[2]));
|
|
191
|
+
index += 1;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const todoMatch = trimmed.match(/^[-*]\s+\[( |x|X)\]\s+(.+)$/);
|
|
195
|
+
if (todoMatch) {
|
|
196
|
+
flushParagraph();
|
|
197
|
+
blocks.push(todoBlock(todoMatch[2], todoMatch[1].toLowerCase() === "x"));
|
|
198
|
+
index += 1;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const quoteMatch = trimmed.match(/^>\s?(.*)$/);
|
|
202
|
+
if (quoteMatch) {
|
|
203
|
+
flushParagraph();
|
|
204
|
+
const quoteLines = [quoteMatch[1]];
|
|
205
|
+
index += 1;
|
|
206
|
+
while (index < lines.length) {
|
|
207
|
+
const next = lines[index].trim();
|
|
208
|
+
const nextMatch = next.match(/^>\s?(.*)$/);
|
|
209
|
+
if (!nextMatch) {
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
quoteLines.push(nextMatch[1]);
|
|
213
|
+
index += 1;
|
|
214
|
+
}
|
|
215
|
+
blocks.push(quoteBlock(quoteLines.join("\n")));
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
const bulletMatch = trimmed.match(/^[-*+]\s+(.+)$/);
|
|
219
|
+
if (bulletMatch) {
|
|
220
|
+
flushParagraph();
|
|
221
|
+
blocks.push(bulletedItemBlock(bulletMatch[1]));
|
|
222
|
+
index += 1;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const numberedMatch = trimmed.match(/^\d+\.\s+(.+)$/);
|
|
226
|
+
if (numberedMatch) {
|
|
227
|
+
flushParagraph();
|
|
228
|
+
blocks.push(numberedItemBlock(numberedMatch[1]));
|
|
229
|
+
index += 1;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
paragraphLines.push(line);
|
|
233
|
+
index += 1;
|
|
234
|
+
}
|
|
235
|
+
flushParagraph();
|
|
236
|
+
return blocks;
|
|
237
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function normalizePropertyValue(property: Record<string, unknown>): unknown;
|
|
2
|
+
export declare function normalizeProperties(properties: Record<string, unknown>): Record<string, unknown>;
|
|
3
|
+
export declare function buildPropertyValueByType(type: string, value: unknown): Record<string, unknown>;
|
|
4
|
+
export declare function buildPropertiesPayloadGeneric(patch: Record<string, unknown>, schemaProperties: Record<string, {
|
|
5
|
+
id: string;
|
|
6
|
+
type: string;
|
|
7
|
+
}>): Record<string, unknown>;
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { CliError } from "../errors/cli-error.js";
|
|
2
|
+
function readPlainText(rich) {
|
|
3
|
+
if (!rich || rich.length === 0) {
|
|
4
|
+
return "";
|
|
5
|
+
}
|
|
6
|
+
return rich
|
|
7
|
+
.map((fragment) => {
|
|
8
|
+
const plainText = fragment.plain_text;
|
|
9
|
+
if (typeof plainText === "string") {
|
|
10
|
+
return plainText;
|
|
11
|
+
}
|
|
12
|
+
const text = fragment.text;
|
|
13
|
+
return text?.content ?? "";
|
|
14
|
+
})
|
|
15
|
+
.join("");
|
|
16
|
+
}
|
|
17
|
+
function normalizeFormula(formula) {
|
|
18
|
+
const type = formula.type;
|
|
19
|
+
if (typeof type !== "string") {
|
|
20
|
+
return formula;
|
|
21
|
+
}
|
|
22
|
+
return formula[type];
|
|
23
|
+
}
|
|
24
|
+
function normalizeRollup(rollup) {
|
|
25
|
+
const type = rollup.type;
|
|
26
|
+
if (typeof type !== "string") {
|
|
27
|
+
return rollup;
|
|
28
|
+
}
|
|
29
|
+
if (type === "array") {
|
|
30
|
+
const arrayValue = rollup.array;
|
|
31
|
+
if (!Array.isArray(arrayValue)) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
return arrayValue.map((entry) => normalizePropertyValue(entry));
|
|
35
|
+
}
|
|
36
|
+
return rollup[type];
|
|
37
|
+
}
|
|
38
|
+
export function normalizePropertyValue(property) {
|
|
39
|
+
const type = property.type;
|
|
40
|
+
if (typeof type !== "string") {
|
|
41
|
+
return property;
|
|
42
|
+
}
|
|
43
|
+
switch (type) {
|
|
44
|
+
case "title": {
|
|
45
|
+
const title = property.title;
|
|
46
|
+
return readPlainText(title);
|
|
47
|
+
}
|
|
48
|
+
case "rich_text": {
|
|
49
|
+
const richText = property.rich_text;
|
|
50
|
+
return readPlainText(richText);
|
|
51
|
+
}
|
|
52
|
+
case "status": {
|
|
53
|
+
const status = property.status;
|
|
54
|
+
return status?.name ?? null;
|
|
55
|
+
}
|
|
56
|
+
case "select": {
|
|
57
|
+
const select = property.select;
|
|
58
|
+
return select?.name ?? null;
|
|
59
|
+
}
|
|
60
|
+
case "multi_select": {
|
|
61
|
+
const selections = property.multi_select;
|
|
62
|
+
return selections?.map((item) => item.name).filter((value) => Boolean(value)) ?? [];
|
|
63
|
+
}
|
|
64
|
+
case "date": {
|
|
65
|
+
const date = property.date;
|
|
66
|
+
return date;
|
|
67
|
+
}
|
|
68
|
+
case "relation": {
|
|
69
|
+
const relation = property.relation;
|
|
70
|
+
return relation?.map((entry) => entry.id).filter((value) => Boolean(value)) ?? [];
|
|
71
|
+
}
|
|
72
|
+
case "people": {
|
|
73
|
+
const people = property.people;
|
|
74
|
+
return people?.map((person) => person.id).filter((value) => Boolean(value)) ?? [];
|
|
75
|
+
}
|
|
76
|
+
case "checkbox":
|
|
77
|
+
case "number":
|
|
78
|
+
case "url":
|
|
79
|
+
case "email":
|
|
80
|
+
case "phone_number":
|
|
81
|
+
return property[type] ?? null;
|
|
82
|
+
case "files": {
|
|
83
|
+
const files = property.files;
|
|
84
|
+
return (files ?? []).map((file) => ({
|
|
85
|
+
name: file.name ?? null,
|
|
86
|
+
type: file.type ?? null,
|
|
87
|
+
url: file.type === "external" ? file.external?.url ?? null : file.file?.url ?? null,
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
case "formula": {
|
|
91
|
+
const formula = property.formula;
|
|
92
|
+
return normalizeFormula(formula);
|
|
93
|
+
}
|
|
94
|
+
case "rollup": {
|
|
95
|
+
const rollup = property.rollup;
|
|
96
|
+
return normalizeRollup(rollup);
|
|
97
|
+
}
|
|
98
|
+
case "created_time":
|
|
99
|
+
case "last_edited_time":
|
|
100
|
+
return property[type] ?? null;
|
|
101
|
+
default:
|
|
102
|
+
return property[type] ?? null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export function normalizeProperties(properties) {
|
|
106
|
+
const normalized = {};
|
|
107
|
+
for (const [name, property] of Object.entries(properties)) {
|
|
108
|
+
if (property && typeof property === "object") {
|
|
109
|
+
normalized[name] = normalizePropertyValue(property);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return normalized;
|
|
113
|
+
}
|
|
114
|
+
function normalizeDateInput(value) {
|
|
115
|
+
if (value === null) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
if (typeof value === "string") {
|
|
119
|
+
return { start: value };
|
|
120
|
+
}
|
|
121
|
+
if (value && typeof value === "object") {
|
|
122
|
+
const candidate = value;
|
|
123
|
+
if (typeof candidate.start !== "string") {
|
|
124
|
+
throw new CliError("invalid_input", "Date value must include a string \"start\" field.");
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
start: candidate.start,
|
|
128
|
+
end: typeof candidate.end === "string" || candidate.end === null ? candidate.end : undefined,
|
|
129
|
+
time_zone: typeof candidate.time_zone === "string" || candidate.time_zone === null
|
|
130
|
+
? candidate.time_zone
|
|
131
|
+
: undefined,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
throw new CliError("invalid_input", "Invalid date value.");
|
|
135
|
+
}
|
|
136
|
+
function asStringArray(value, label) {
|
|
137
|
+
if (!Array.isArray(value)) {
|
|
138
|
+
throw new CliError("invalid_input", `${label} expects an array of strings.`);
|
|
139
|
+
}
|
|
140
|
+
const items = value.filter((item) => typeof item === "string" && item.length > 0);
|
|
141
|
+
if (items.length !== value.length) {
|
|
142
|
+
throw new CliError("invalid_input", `${label} expects an array of strings.`);
|
|
143
|
+
}
|
|
144
|
+
return items;
|
|
145
|
+
}
|
|
146
|
+
function toRichText(content) {
|
|
147
|
+
return [{ type: "text", text: { content } }];
|
|
148
|
+
}
|
|
149
|
+
function isRawPropertyPayload(value) {
|
|
150
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
const keys = Object.keys(value);
|
|
154
|
+
const known = new Set([
|
|
155
|
+
"title",
|
|
156
|
+
"rich_text",
|
|
157
|
+
"status",
|
|
158
|
+
"select",
|
|
159
|
+
"multi_select",
|
|
160
|
+
"date",
|
|
161
|
+
"relation",
|
|
162
|
+
"people",
|
|
163
|
+
"checkbox",
|
|
164
|
+
"number",
|
|
165
|
+
"url",
|
|
166
|
+
"email",
|
|
167
|
+
"phone_number",
|
|
168
|
+
"files",
|
|
169
|
+
"formula",
|
|
170
|
+
]);
|
|
171
|
+
return keys.some((key) => known.has(key));
|
|
172
|
+
}
|
|
173
|
+
export function buildPropertyValueByType(type, value) {
|
|
174
|
+
switch (type) {
|
|
175
|
+
case "title": {
|
|
176
|
+
if (typeof value !== "string") {
|
|
177
|
+
throw new CliError("invalid_input", "Title properties require string values.");
|
|
178
|
+
}
|
|
179
|
+
return { title: toRichText(value) };
|
|
180
|
+
}
|
|
181
|
+
case "rich_text": {
|
|
182
|
+
if (typeof value !== "string") {
|
|
183
|
+
throw new CliError("invalid_input", "Rich text properties require string values.");
|
|
184
|
+
}
|
|
185
|
+
return { rich_text: toRichText(value) };
|
|
186
|
+
}
|
|
187
|
+
case "status": {
|
|
188
|
+
if (typeof value !== "string") {
|
|
189
|
+
throw new CliError("invalid_input", "Status properties require a string status name.");
|
|
190
|
+
}
|
|
191
|
+
return { status: { name: value } };
|
|
192
|
+
}
|
|
193
|
+
case "select": {
|
|
194
|
+
if (typeof value !== "string") {
|
|
195
|
+
throw new CliError("invalid_input", "Select properties require a string option name.");
|
|
196
|
+
}
|
|
197
|
+
return { select: { name: value } };
|
|
198
|
+
}
|
|
199
|
+
case "multi_select": {
|
|
200
|
+
const names = asStringArray(value, "multi_select");
|
|
201
|
+
return { multi_select: names.map((name) => ({ name })) };
|
|
202
|
+
}
|
|
203
|
+
case "date": {
|
|
204
|
+
const normalized = normalizeDateInput(value);
|
|
205
|
+
return { date: normalized };
|
|
206
|
+
}
|
|
207
|
+
case "relation": {
|
|
208
|
+
const ids = asStringArray(value, "relation");
|
|
209
|
+
return { relation: ids.map((id) => ({ id })) };
|
|
210
|
+
}
|
|
211
|
+
case "people": {
|
|
212
|
+
const ids = asStringArray(value, "people");
|
|
213
|
+
return { people: ids.map((id) => ({ id })) };
|
|
214
|
+
}
|
|
215
|
+
case "checkbox": {
|
|
216
|
+
if (typeof value !== "boolean") {
|
|
217
|
+
throw new CliError("invalid_input", "Checkbox properties require boolean values.");
|
|
218
|
+
}
|
|
219
|
+
return { checkbox: value };
|
|
220
|
+
}
|
|
221
|
+
case "number": {
|
|
222
|
+
if (typeof value !== "number") {
|
|
223
|
+
throw new CliError("invalid_input", "Number properties require numeric values.");
|
|
224
|
+
}
|
|
225
|
+
return { number: value };
|
|
226
|
+
}
|
|
227
|
+
case "url": {
|
|
228
|
+
if (typeof value !== "string" && value !== null) {
|
|
229
|
+
throw new CliError("invalid_input", "URL properties require string or null values.");
|
|
230
|
+
}
|
|
231
|
+
return { url: value };
|
|
232
|
+
}
|
|
233
|
+
case "email": {
|
|
234
|
+
if (typeof value !== "string" && value !== null) {
|
|
235
|
+
throw new CliError("invalid_input", "Email properties require string or null values.");
|
|
236
|
+
}
|
|
237
|
+
return { email: value };
|
|
238
|
+
}
|
|
239
|
+
case "phone_number": {
|
|
240
|
+
if (typeof value !== "string" && value !== null) {
|
|
241
|
+
throw new CliError("invalid_input", "Phone number properties require string or null values.");
|
|
242
|
+
}
|
|
243
|
+
return { phone_number: value };
|
|
244
|
+
}
|
|
245
|
+
default: {
|
|
246
|
+
if (typeof value === "string") {
|
|
247
|
+
return { rich_text: toRichText(value) };
|
|
248
|
+
}
|
|
249
|
+
return { rich_text: toRichText(JSON.stringify(value)) };
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
export function buildPropertiesPayloadGeneric(patch, schemaProperties) {
|
|
254
|
+
const payload = {};
|
|
255
|
+
for (const [propertyName, value] of Object.entries(patch)) {
|
|
256
|
+
const schemaEntry = schemaProperties[propertyName];
|
|
257
|
+
if (!schemaEntry) {
|
|
258
|
+
throw new CliError("invalid_input", `Unknown property \"${propertyName}\" for this data source. Use data-sources get to inspect available properties.`);
|
|
259
|
+
}
|
|
260
|
+
if (isRawPropertyPayload(value)) {
|
|
261
|
+
payload[propertyName] = value;
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
payload[propertyName] = buildPropertyValueByType(schemaEntry.type, value);
|
|
265
|
+
}
|
|
266
|
+
return payload;
|
|
267
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { AppConfig } from "../config/types.js";
|
|
2
|
+
import { NotionClientAdapter } from "./client.js";
|
|
3
|
+
export interface PaginationResult {
|
|
4
|
+
has_more: boolean;
|
|
5
|
+
next_cursor: string | null;
|
|
6
|
+
returned: number;
|
|
7
|
+
}
|
|
8
|
+
export interface QueryPagesInput {
|
|
9
|
+
dataSourceId: string;
|
|
10
|
+
limit: number;
|
|
11
|
+
cursor?: string;
|
|
12
|
+
filter?: Record<string, unknown>;
|
|
13
|
+
sorts?: Array<Record<string, unknown>>;
|
|
14
|
+
view: "compact" | "full";
|
|
15
|
+
fields?: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface RepositoryContext {
|
|
18
|
+
notion: NotionClientAdapter;
|
|
19
|
+
config: AppConfig;
|
|
20
|
+
saveConfig: (config: AppConfig) => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export interface SearchWorkspaceInput {
|
|
23
|
+
query: string;
|
|
24
|
+
limit: number;
|
|
25
|
+
cursor?: string;
|
|
26
|
+
scope?: string;
|
|
27
|
+
createdAfter?: string;
|
|
28
|
+
createdBefore?: string;
|
|
29
|
+
editedAfter?: string;
|
|
30
|
+
editedBefore?: string;
|
|
31
|
+
createdBy?: string;
|
|
32
|
+
object?: "page" | "data_source";
|
|
33
|
+
scanLimit: number;
|
|
34
|
+
}
|
|
35
|
+
export interface SearchWorkspaceResult {
|
|
36
|
+
results: Record<string, unknown>[];
|
|
37
|
+
pagination: PaginationResult;
|
|
38
|
+
scan_count: number;
|
|
39
|
+
}
|
|
40
|
+
export interface CreateBulkInput {
|
|
41
|
+
parentDataSourceId: string;
|
|
42
|
+
items: Array<{
|
|
43
|
+
propertiesPatch: Record<string, unknown>;
|
|
44
|
+
}>;
|
|
45
|
+
view: "compact" | "full";
|
|
46
|
+
fields?: string[];
|
|
47
|
+
concurrency: number;
|
|
48
|
+
}
|
|
49
|
+
export declare function searchWorkspace(notion: NotionClientAdapter, input: SearchWorkspaceInput): Promise<SearchWorkspaceResult>;
|
|
50
|
+
export declare function listDataSources(notion: NotionClientAdapter, input: {
|
|
51
|
+
query?: string;
|
|
52
|
+
limit: number;
|
|
53
|
+
cursor?: string;
|
|
54
|
+
}): Promise<{
|
|
55
|
+
data_sources: Record<string, unknown>[];
|
|
56
|
+
pagination: PaginationResult;
|
|
57
|
+
}>;
|
|
58
|
+
export declare function getDataSource(notion: NotionClientAdapter, dataSourceId: string, view: "compact" | "full"): Promise<Record<string, unknown>>;
|
|
59
|
+
export declare function getDataSourceSchema(notion: NotionClientAdapter, dataSourceId: string): Promise<Record<string, unknown>>;
|
|
60
|
+
export declare function queryDataSourcePages(ctx: RepositoryContext, input: QueryPagesInput): Promise<{
|
|
61
|
+
records: Record<string, unknown>[];
|
|
62
|
+
pagination: PaginationResult;
|
|
63
|
+
}>;
|
|
64
|
+
export declare function getPage(notion: NotionClientAdapter, pageId: string, view: "compact" | "full", fields?: string[]): Promise<Record<string, unknown>>;
|
|
65
|
+
export declare function createPage(ctx: RepositoryContext, input: {
|
|
66
|
+
parentDataSourceId: string;
|
|
67
|
+
propertiesPatch: Record<string, unknown>;
|
|
68
|
+
view: "compact" | "full";
|
|
69
|
+
fields?: string[];
|
|
70
|
+
}): Promise<Record<string, unknown>>;
|
|
71
|
+
export declare function createPagesBulk(ctx: RepositoryContext, input: CreateBulkInput): Promise<Record<string, unknown>>;
|
|
72
|
+
export declare function updatePage(ctx: RepositoryContext, input: {
|
|
73
|
+
pageId: string;
|
|
74
|
+
patch: Record<string, unknown>;
|
|
75
|
+
view: "compact" | "full";
|
|
76
|
+
fields?: string[];
|
|
77
|
+
}): Promise<Record<string, unknown>>;
|
|
78
|
+
export declare function archivePage(ctx: RepositoryContext, input: {
|
|
79
|
+
pageId: string;
|
|
80
|
+
view: "compact" | "full";
|
|
81
|
+
fields?: string[];
|
|
82
|
+
}): Promise<Record<string, unknown>>;
|
|
83
|
+
export declare function unarchivePage(ctx: RepositoryContext, input: {
|
|
84
|
+
pageId: string;
|
|
85
|
+
view: "compact" | "full";
|
|
86
|
+
fields?: string[];
|
|
87
|
+
}): Promise<Record<string, unknown>>;
|
|
88
|
+
export declare function setRelation(ctx: RepositoryContext, args: {
|
|
89
|
+
fromId: string;
|
|
90
|
+
toId: string;
|
|
91
|
+
property: string;
|
|
92
|
+
mode: "add" | "remove";
|
|
93
|
+
view: "compact" | "full";
|
|
94
|
+
fields?: string[];
|
|
95
|
+
}): Promise<Record<string, unknown>>;
|
|
96
|
+
export interface BlockSelector {
|
|
97
|
+
where?: {
|
|
98
|
+
type?: string;
|
|
99
|
+
text_contains?: string;
|
|
100
|
+
parent_id?: string;
|
|
101
|
+
};
|
|
102
|
+
nth?: number;
|
|
103
|
+
from?: "start" | "end";
|
|
104
|
+
}
|
|
105
|
+
export type BlockInsertPosition = {
|
|
106
|
+
type: "start";
|
|
107
|
+
} | {
|
|
108
|
+
type: "end";
|
|
109
|
+
} | {
|
|
110
|
+
type: "after_block";
|
|
111
|
+
after_block: {
|
|
112
|
+
id: string;
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
export type BlockReadFormat = "compact" | "full" | "markdown";
|
|
116
|
+
export declare function getBlocks(notion: NotionClientAdapter, pageOrBlockId: string, maxBlocks: number, depth: number, view: BlockReadFormat): Promise<Record<string, unknown>>;
|
|
117
|
+
export declare function insertBlocks(notion: NotionClientAdapter, args: {
|
|
118
|
+
parentId: string;
|
|
119
|
+
blocks: Array<Record<string, unknown>>;
|
|
120
|
+
position: BlockInsertPosition;
|
|
121
|
+
dryRun: boolean;
|
|
122
|
+
}): Promise<Record<string, unknown>>;
|
|
123
|
+
export declare function appendBlocks(notion: NotionClientAdapter, args: {
|
|
124
|
+
parentId: string;
|
|
125
|
+
blocks: Array<Record<string, unknown>>;
|
|
126
|
+
dryRun: boolean;
|
|
127
|
+
}): Promise<Record<string, unknown>>;
|
|
128
|
+
export declare function selectBlocks(notion: NotionClientAdapter, args: {
|
|
129
|
+
scopeId: string;
|
|
130
|
+
selector: BlockSelector;
|
|
131
|
+
maxBlocks: number;
|
|
132
|
+
}): Promise<Record<string, unknown>>;
|
|
133
|
+
export declare function replaceBlockRange(notion: NotionClientAdapter, args: {
|
|
134
|
+
scopeId: string;
|
|
135
|
+
startSelector: BlockSelector;
|
|
136
|
+
endSelector: BlockSelector;
|
|
137
|
+
blocks: Array<Record<string, unknown>>;
|
|
138
|
+
inclusiveStart: boolean;
|
|
139
|
+
inclusiveEnd: boolean;
|
|
140
|
+
dryRun: boolean;
|
|
141
|
+
maxBlocks: number;
|
|
142
|
+
}): Promise<Record<string, unknown>>;
|