@infograb/notion-cli 5.9.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.
Files changed (114) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1386 -0
  3. package/bin/dev +17 -0
  4. package/bin/dev.cmd +3 -0
  5. package/bin/run +14 -0
  6. package/bin/run.cmd +3 -0
  7. package/dist/base-command.d.ts +73 -0
  8. package/dist/base-command.js +179 -0
  9. package/dist/base-flags.d.ts +14 -0
  10. package/dist/base-flags.js +59 -0
  11. package/dist/cache.d.ts +84 -0
  12. package/dist/cache.js +351 -0
  13. package/dist/commands/batch/retrieve.d.ts +43 -0
  14. package/dist/commands/batch/retrieve.js +265 -0
  15. package/dist/commands/block/append.d.ts +42 -0
  16. package/dist/commands/block/append.js +219 -0
  17. package/dist/commands/block/delete.d.ts +30 -0
  18. package/dist/commands/block/delete.js +94 -0
  19. package/dist/commands/block/retrieve/children.d.ts +31 -0
  20. package/dist/commands/block/retrieve/children.js +174 -0
  21. package/dist/commands/block/retrieve.d.ts +30 -0
  22. package/dist/commands/block/retrieve.js +98 -0
  23. package/dist/commands/block/update.d.ts +45 -0
  24. package/dist/commands/block/update.js +241 -0
  25. package/dist/commands/cache/info.d.ts +19 -0
  26. package/dist/commands/cache/info.js +145 -0
  27. package/dist/commands/config/set-token.d.ts +30 -0
  28. package/dist/commands/config/set-token.js +201 -0
  29. package/dist/commands/db/create.d.ts +31 -0
  30. package/dist/commands/db/create.js +124 -0
  31. package/dist/commands/db/query.d.ts +41 -0
  32. package/dist/commands/db/query.js +355 -0
  33. package/dist/commands/db/retrieve.d.ts +33 -0
  34. package/dist/commands/db/retrieve.js +134 -0
  35. package/dist/commands/db/schema.d.ts +32 -0
  36. package/dist/commands/db/schema.js +308 -0
  37. package/dist/commands/db/update.d.ts +31 -0
  38. package/dist/commands/db/update.js +117 -0
  39. package/dist/commands/doctor.d.ts +50 -0
  40. package/dist/commands/doctor.js +420 -0
  41. package/dist/commands/init.d.ts +57 -0
  42. package/dist/commands/init.js +471 -0
  43. package/dist/commands/list.d.ts +29 -0
  44. package/dist/commands/list.js +184 -0
  45. package/dist/commands/page/create.d.ts +33 -0
  46. package/dist/commands/page/create.js +240 -0
  47. package/dist/commands/page/retrieve/property_item.d.ts +24 -0
  48. package/dist/commands/page/retrieve/property_item.js +72 -0
  49. package/dist/commands/page/retrieve.d.ts +36 -0
  50. package/dist/commands/page/retrieve.js +244 -0
  51. package/dist/commands/page/update.d.ts +34 -0
  52. package/dist/commands/page/update.js +184 -0
  53. package/dist/commands/search.d.ts +40 -0
  54. package/dist/commands/search.js +348 -0
  55. package/dist/commands/sync.d.ts +24 -0
  56. package/dist/commands/sync.js +183 -0
  57. package/dist/commands/user/list.d.ts +27 -0
  58. package/dist/commands/user/list.js +99 -0
  59. package/dist/commands/user/retrieve/bot.d.ts +28 -0
  60. package/dist/commands/user/retrieve/bot.js +96 -0
  61. package/dist/commands/user/retrieve.d.ts +30 -0
  62. package/dist/commands/user/retrieve.js +103 -0
  63. package/dist/commands/whoami.d.ts +19 -0
  64. package/dist/commands/whoami.js +175 -0
  65. package/dist/deduplication.d.ts +41 -0
  66. package/dist/deduplication.js +71 -0
  67. package/dist/envelope.d.ts +169 -0
  68. package/dist/envelope.js +257 -0
  69. package/dist/errors/enhanced-errors.d.ts +168 -0
  70. package/dist/errors/enhanced-errors.js +570 -0
  71. package/dist/errors/index.d.ts +18 -0
  72. package/dist/errors/index.js +33 -0
  73. package/dist/examples/cache-retry-examples.d.ts +64 -0
  74. package/dist/examples/cache-retry-examples.js +375 -0
  75. package/dist/helper.d.ts +102 -0
  76. package/dist/helper.js +885 -0
  77. package/dist/http-agent.d.ts +38 -0
  78. package/dist/http-agent.js +60 -0
  79. package/dist/index.d.ts +1 -0
  80. package/dist/index.js +4 -0
  81. package/dist/interface.d.ts +4 -0
  82. package/dist/interface.js +2 -0
  83. package/dist/notion.d.ts +144 -0
  84. package/dist/notion.js +547 -0
  85. package/dist/retry.d.ts +72 -0
  86. package/dist/retry.js +381 -0
  87. package/dist/utils/disk-cache.d.ts +80 -0
  88. package/dist/utils/disk-cache.js +291 -0
  89. package/dist/utils/markdown-to-blocks.d.ts +19 -0
  90. package/dist/utils/markdown-to-blocks.js +259 -0
  91. package/dist/utils/notion-resolver.d.ts +48 -0
  92. package/dist/utils/notion-resolver.js +262 -0
  93. package/dist/utils/notion-url-parser.d.ts +46 -0
  94. package/dist/utils/notion-url-parser.js +111 -0
  95. package/dist/utils/property-expander.d.ts +45 -0
  96. package/dist/utils/property-expander.js +323 -0
  97. package/dist/utils/schema-examples.d.ts +40 -0
  98. package/dist/utils/schema-examples.js +359 -0
  99. package/dist/utils/schema-extractor.d.ts +65 -0
  100. package/dist/utils/schema-extractor.js +235 -0
  101. package/dist/utils/table-formatter.d.ts +36 -0
  102. package/dist/utils/table-formatter.js +122 -0
  103. package/dist/utils/terminal-banner.d.ts +24 -0
  104. package/dist/utils/terminal-banner.js +34 -0
  105. package/dist/utils/token-validator.d.ts +55 -0
  106. package/dist/utils/token-validator.js +85 -0
  107. package/dist/utils/update-notifier.d.ts +26 -0
  108. package/dist/utils/update-notifier.js +54 -0
  109. package/dist/utils/workspace-cache.d.ts +58 -0
  110. package/dist/utils/workspace-cache.js +185 -0
  111. package/oclif.manifest.json +4497 -0
  112. package/package.json +115 -0
  113. package/scripts/banner.js +38 -0
  114. package/scripts/postinstall.js +56 -0
@@ -0,0 +1,323 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.expandSimpleProperties = expandSimpleProperties;
4
+ exports.validateSimpleProperties = validateSimpleProperties;
5
+ /**
6
+ * Expand simple flat properties to Notion API format
7
+ *
8
+ * This function takes simplified property values and automatically expands them
9
+ * to the correct Notion API structure based on the database schema.
10
+ *
11
+ * @param simple - Flat key-value property object
12
+ * @param schema - Database properties schema from data source
13
+ * @returns Properly formatted Notion properties object
14
+ *
15
+ * @example
16
+ * // Input (simple):
17
+ * { "Name": "My Task", "Status": "In Progress", "Priority": 5 }
18
+ *
19
+ * // Output (Notion format):
20
+ * {
21
+ * "Name": { "title": [{ "text": { "content": "My Task" } }] },
22
+ * "Status": { "select": { "name": "In Progress" } },
23
+ * "Priority": { "number": 5 }
24
+ * }
25
+ */
26
+ async function expandSimpleProperties(simple, schema) {
27
+ const expanded = {};
28
+ for (const [propName, value] of Object.entries(simple)) {
29
+ // Find property in schema (case-insensitive)
30
+ const propDef = findProperty(schema, propName);
31
+ if (!propDef) {
32
+ throw new Error(`Property "${propName}" not found in database schema.\n` +
33
+ `Available properties: ${Object.keys(schema).join(', ')}`);
34
+ }
35
+ // Expand based on type
36
+ try {
37
+ expanded[propDef.actualName] = expandProperty(value, propDef.type, propDef);
38
+ }
39
+ catch (error) {
40
+ throw new Error(`Error expanding property "${propName}": ${error.message}`);
41
+ }
42
+ }
43
+ return expanded;
44
+ }
45
+ /**
46
+ * Find property in schema with case-insensitive matching
47
+ */
48
+ function findProperty(schema, name) {
49
+ const normalized = name.toLowerCase();
50
+ for (const [key, value] of Object.entries(schema)) {
51
+ if (key.toLowerCase() === normalized) {
52
+ // Cast value to object type to allow spreading
53
+ const propConfig = value;
54
+ return { actualName: key, ...propConfig };
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+ /**
60
+ * Expand a single property value to Notion format based on type
61
+ */
62
+ function expandProperty(value, type, propDef) {
63
+ // Handle null values
64
+ if (value === null) {
65
+ return null;
66
+ }
67
+ switch (type) {
68
+ case 'title':
69
+ return {
70
+ title: [{ text: { content: String(value) } }]
71
+ };
72
+ case 'rich_text':
73
+ return {
74
+ rich_text: [{ text: { content: String(value) } }]
75
+ };
76
+ case 'number': {
77
+ const num = Number(value);
78
+ if (isNaN(num)) {
79
+ throw new Error(`Invalid number value: "${value}"`);
80
+ }
81
+ return { number: num };
82
+ }
83
+ case 'checkbox': {
84
+ // Handle boolean or string representations
85
+ let boolValue;
86
+ if (typeof value === 'boolean') {
87
+ boolValue = value;
88
+ }
89
+ else if (typeof value === 'string') {
90
+ const lower = value.toLowerCase();
91
+ if (lower === 'true' || lower === 'yes' || lower === '1') {
92
+ boolValue = true;
93
+ }
94
+ else if (lower === 'false' || lower === 'no' || lower === '0') {
95
+ boolValue = false;
96
+ }
97
+ else {
98
+ throw new Error(`Invalid checkbox value: "${value}". Use true/false, yes/no, or 1/0`);
99
+ }
100
+ }
101
+ else {
102
+ boolValue = Boolean(value);
103
+ }
104
+ return { checkbox: boolValue };
105
+ }
106
+ case 'select':
107
+ return expandSelectProperty(value, propDef);
108
+ case 'multi_select':
109
+ return expandMultiSelectProperty(value, propDef);
110
+ case 'status':
111
+ return expandStatusProperty(value, propDef);
112
+ case 'date':
113
+ return expandDateProperty(value);
114
+ case 'url': {
115
+ const urlStr = String(value);
116
+ // Basic URL validation
117
+ if (!urlStr.match(/^https?:\/\/.+/)) {
118
+ throw new Error(`Invalid URL: "${value}". Must start with http:// or https://`);
119
+ }
120
+ return { url: urlStr };
121
+ }
122
+ case 'email': {
123
+ const emailStr = String(value);
124
+ // Basic email validation
125
+ if (!emailStr.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) {
126
+ throw new Error(`Invalid email: "${value}"`);
127
+ }
128
+ return { email: emailStr };
129
+ }
130
+ case 'phone_number':
131
+ return { phone_number: String(value) };
132
+ case 'people':
133
+ return expandPeopleProperty(value);
134
+ case 'files': {
135
+ // Files need external URLs
136
+ const files = Array.isArray(value) ? value : [value];
137
+ return {
138
+ files: files.map(f => {
139
+ if (typeof f === 'string') {
140
+ return { name: f, external: { url: f } };
141
+ }
142
+ return f;
143
+ })
144
+ };
145
+ }
146
+ case 'relation': {
147
+ // Relations need page IDs
148
+ const relations = Array.isArray(value) ? value : [value];
149
+ return {
150
+ relation: relations.map(id => ({ id: String(id) }))
151
+ };
152
+ }
153
+ default:
154
+ throw new Error(`Unsupported property type: ${type}. ` +
155
+ `Supported types: title, rich_text, number, checkbox, select, multi_select, ` +
156
+ `status, date, url, email, phone_number, people, files, relation`);
157
+ }
158
+ }
159
+ /**
160
+ * Expand select property with validation
161
+ */
162
+ function expandSelectProperty(value, propDef) {
163
+ var _a;
164
+ const selectOptions = ((_a = propDef.select) === null || _a === void 0 ? void 0 : _a.options) || [];
165
+ const strValue = String(value);
166
+ // Case-insensitive matching
167
+ const validOption = selectOptions.find((opt) => opt.name.toLowerCase() === strValue.toLowerCase());
168
+ if (!validOption && selectOptions.length > 0) {
169
+ const optionNames = selectOptions.map((o) => o.name).join(', ');
170
+ throw new Error(`Invalid select value: "${value}"\n` +
171
+ `Valid options: ${optionNames}\n` +
172
+ `Tip: Values are case-insensitive`);
173
+ }
174
+ // Use the exact option name from schema (preserving case)
175
+ const exactName = validOption ? validOption.name : strValue;
176
+ return { select: { name: exactName } };
177
+ }
178
+ /**
179
+ * Expand multi-select property with validation
180
+ */
181
+ function expandMultiSelectProperty(value, propDef) {
182
+ var _a;
183
+ const values = Array.isArray(value) ? value : [value];
184
+ const multiOptions = ((_a = propDef.multi_select) === null || _a === void 0 ? void 0 : _a.options) || [];
185
+ const validated = values.map(v => {
186
+ const strValue = String(v);
187
+ // Case-insensitive matching
188
+ const validOption = multiOptions.find((opt) => opt.name.toLowerCase() === strValue.toLowerCase());
189
+ if (!validOption && multiOptions.length > 0) {
190
+ const optionNames = multiOptions.map((o) => o.name).join(', ');
191
+ throw new Error(`Invalid multi-select value: "${v}"\n` +
192
+ `Valid options: ${optionNames}`);
193
+ }
194
+ // Use exact option name from schema
195
+ const exactName = validOption ? validOption.name : strValue;
196
+ return { name: exactName };
197
+ });
198
+ return { multi_select: validated };
199
+ }
200
+ /**
201
+ * Expand status property with validation
202
+ */
203
+ function expandStatusProperty(value, propDef) {
204
+ var _a;
205
+ const statusOptions = ((_a = propDef.status) === null || _a === void 0 ? void 0 : _a.options) || [];
206
+ const strValue = String(value);
207
+ // Case-insensitive matching
208
+ const validStatus = statusOptions.find((opt) => opt.name.toLowerCase() === strValue.toLowerCase());
209
+ if (!validStatus && statusOptions.length > 0) {
210
+ const optionNames = statusOptions.map((o) => o.name).join(', ');
211
+ throw new Error(`Invalid status value: "${value}"\n` +
212
+ `Valid options: ${optionNames}`);
213
+ }
214
+ // Use exact status name from schema
215
+ const exactName = validStatus ? validStatus.name : strValue;
216
+ return { status: { name: exactName } };
217
+ }
218
+ /**
219
+ * Expand date property with support for ISO dates and relative dates
220
+ */
221
+ function expandDateProperty(value) {
222
+ const dateStr = parseRelativeDate(String(value));
223
+ // Check if it includes time (ISO 8601 with time component)
224
+ if (dateStr.includes('T')) {
225
+ return { date: { start: dateStr } };
226
+ }
227
+ return { date: { start: dateStr } };
228
+ }
229
+ /**
230
+ * Parse relative date strings like "today", "tomorrow", "+7 days"
231
+ */
232
+ function parseRelativeDate(value) {
233
+ // Handle ISO dates (YYYY-MM-DD or full ISO 8601)
234
+ if (/^\d{4}-\d{2}-\d{2}/.test(value)) {
235
+ return value;
236
+ }
237
+ // Handle relative dates
238
+ const today = new Date();
239
+ today.setHours(0, 0, 0, 0); // Reset to start of day
240
+ if (value.toLowerCase() === 'today') {
241
+ return today.toISOString().split('T')[0];
242
+ }
243
+ if (value.toLowerCase() === 'tomorrow') {
244
+ today.setDate(today.getDate() + 1);
245
+ return today.toISOString().split('T')[0];
246
+ }
247
+ if (value.toLowerCase() === 'yesterday') {
248
+ today.setDate(today.getDate() - 1);
249
+ return today.toISOString().split('T')[0];
250
+ }
251
+ // Parse "+N days/weeks/months/years" format
252
+ const match = value.match(/^([+-]?\d+)\s*(day|week|month|year)s?$/i);
253
+ if (match) {
254
+ const amount = parseInt(match[1]);
255
+ const unit = match[2].toLowerCase();
256
+ switch (unit) {
257
+ case 'day':
258
+ today.setDate(today.getDate() + amount);
259
+ break;
260
+ case 'week':
261
+ today.setDate(today.getDate() + amount * 7);
262
+ break;
263
+ case 'month':
264
+ today.setMonth(today.getMonth() + amount);
265
+ break;
266
+ case 'year':
267
+ today.setFullYear(today.getFullYear() + amount);
268
+ break;
269
+ }
270
+ return today.toISOString().split('T')[0];
271
+ }
272
+ // If none of the above, assume it's already a valid date string
273
+ return value;
274
+ }
275
+ /**
276
+ * Expand people property
277
+ */
278
+ function expandPeopleProperty(value) {
279
+ const users = Array.isArray(value) ? value : [value];
280
+ return {
281
+ people: users.map(u => {
282
+ // Support user ID or email
283
+ if (typeof u === 'string') {
284
+ // Check if it's a UUID (user ID) or email
285
+ if (u.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)) {
286
+ return { id: u };
287
+ }
288
+ // For email, we can only use ID - throw helpful error
289
+ if (u.includes('@')) {
290
+ throw new Error(`Cannot use email addresses for people property. ` +
291
+ `Use Notion user IDs instead. You can get user IDs with: notion-cli user list`);
292
+ }
293
+ return { id: u };
294
+ }
295
+ return { id: String(u) };
296
+ })
297
+ };
298
+ }
299
+ /**
300
+ * Validate simple properties against schema before expansion
301
+ * This can be called optionally before expandSimpleProperties to get detailed errors
302
+ */
303
+ function validateSimpleProperties(simple, schema) {
304
+ const errors = [];
305
+ for (const [propName, value] of Object.entries(simple)) {
306
+ const propDef = findProperty(schema, propName);
307
+ if (!propDef) {
308
+ errors.push(`Property "${propName}" not found in schema`);
309
+ continue;
310
+ }
311
+ // Type-specific validation
312
+ try {
313
+ expandProperty(value, propDef.type, propDef);
314
+ }
315
+ catch (error) {
316
+ errors.push(`${propName}: ${error.message}`);
317
+ }
318
+ }
319
+ return {
320
+ valid: errors.length === 0,
321
+ errors
322
+ };
323
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Property Example Generator for Notion API
3
+ *
4
+ * Generates copy-pastable property payload examples based on database schema.
5
+ * Helps AI agents understand the correct format for create/update operations.
6
+ */
7
+ /**
8
+ * Property example with simple value and Notion API payload
9
+ */
10
+ export interface PropertyExample {
11
+ property_name: string;
12
+ property_type: string;
13
+ simple_value: string | number | boolean | string[] | null;
14
+ notion_payload: Record<string, any>;
15
+ description: string;
16
+ }
17
+ /**
18
+ * Generate property examples for all properties in a data source schema
19
+ *
20
+ * @param properties - Properties object from GetDataSourceResponse
21
+ * @returns Array of property examples
22
+ */
23
+ export declare function generatePropertyExamples(properties: Record<string, any>): PropertyExample[];
24
+ /**
25
+ * Format examples for human-readable console output
26
+ *
27
+ * @param examples - Array of property examples
28
+ * @returns Formatted string
29
+ */
30
+ export declare function formatExamplesForConsole(examples: PropertyExample[]): string;
31
+ /**
32
+ * Group examples by writability (writable vs read-only)
33
+ *
34
+ * @param examples - Array of property examples
35
+ * @returns Grouped examples
36
+ */
37
+ export declare function groupExamplesByWritability(examples: PropertyExample[]): {
38
+ writable: PropertyExample[];
39
+ readOnly: PropertyExample[];
40
+ };