@moxn/kb-migrate 0.4.19 → 0.4.20
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/dist/client.d.ts +17 -3
- package/dist/client.js +17 -0
- package/dist/index.js +50 -0
- package/dist/sources/notion-api.d.ts +1 -0
- package/dist/sources/notion-databases.d.ts +28 -3
- package/dist/sources/notion-databases.js +135 -8
- package/dist/sources/notion-databases.test.js +1 -0
- package/dist/targets/notion-database-export.js +130 -23
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -53,9 +53,10 @@ export declare class MoxnClient {
|
|
|
53
53
|
*/
|
|
54
54
|
addDatabaseColumn(databaseId: string, input: {
|
|
55
55
|
name: string;
|
|
56
|
-
type: 'select' | 'multi_select';
|
|
56
|
+
type: 'select' | 'multi_select' | 'status' | 'checkbox' | 'number' | 'date' | 'text' | 'url' | 'email';
|
|
57
57
|
optionTagIds?: string[];
|
|
58
58
|
newOptionParentPath?: string;
|
|
59
|
+
config?: Record<string, unknown>;
|
|
59
60
|
}): Promise<{
|
|
60
61
|
id: string;
|
|
61
62
|
name: string;
|
|
@@ -97,9 +98,10 @@ export declare class MoxnClient {
|
|
|
97
98
|
columns: Array<{
|
|
98
99
|
id: string;
|
|
99
100
|
name: string;
|
|
100
|
-
type:
|
|
101
|
+
type: string;
|
|
101
102
|
position: number;
|
|
102
103
|
newOptionParentPath: string | null;
|
|
104
|
+
config?: Record<string, unknown> | null;
|
|
103
105
|
options: Array<{
|
|
104
106
|
tagId: string;
|
|
105
107
|
tagName: string;
|
|
@@ -114,13 +116,21 @@ export declare class MoxnClient {
|
|
|
114
116
|
properties: Record<string, {
|
|
115
117
|
columnId: string;
|
|
116
118
|
columnName: string;
|
|
117
|
-
columnType:
|
|
119
|
+
columnType: string;
|
|
118
120
|
values: Array<{
|
|
119
121
|
tagId: string;
|
|
120
122
|
tagName: string;
|
|
121
123
|
tagPath: string;
|
|
122
124
|
tagColor: string | null;
|
|
123
125
|
}>;
|
|
126
|
+
} | {
|
|
127
|
+
columnId: string;
|
|
128
|
+
columnName: string;
|
|
129
|
+
columnType: string;
|
|
130
|
+
textValue: string | null;
|
|
131
|
+
numberValue: number | null;
|
|
132
|
+
dateValue: string | null;
|
|
133
|
+
booleanValue: boolean | null;
|
|
124
134
|
}>;
|
|
125
135
|
}>;
|
|
126
136
|
}>;
|
|
@@ -147,5 +157,9 @@ export declare class MoxnClient {
|
|
|
147
157
|
id: string;
|
|
148
158
|
};
|
|
149
159
|
}>;
|
|
160
|
+
/**
|
|
161
|
+
* Set a scalar property value on a document in a database.
|
|
162
|
+
*/
|
|
163
|
+
setPropertyValue(databaseId: string, documentId: string, columnName: string, value: string | number | boolean | null): Promise<void>;
|
|
150
164
|
private isConflictError;
|
|
151
165
|
}
|
package/dist/client.js
CHANGED
|
@@ -470,6 +470,23 @@ export class MoxnClient {
|
|
|
470
470
|
}
|
|
471
471
|
return response.json();
|
|
472
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* Set a scalar property value on a document in a database.
|
|
475
|
+
*/
|
|
476
|
+
async setPropertyValue(databaseId, documentId, columnName, value) {
|
|
477
|
+
const response = await fetch(`${this.apiUrl}/api/v1/kb/databases/${databaseId}/properties`, {
|
|
478
|
+
method: 'POST',
|
|
479
|
+
headers: {
|
|
480
|
+
'Content-Type': 'application/json',
|
|
481
|
+
'x-api-key': this.apiKey,
|
|
482
|
+
},
|
|
483
|
+
body: JSON.stringify({ documentId, columnName, value }),
|
|
484
|
+
});
|
|
485
|
+
if (!response.ok) {
|
|
486
|
+
const body = await response.json().catch(() => ({}));
|
|
487
|
+
throw new Error(body.error || `Failed to set property value: ${response.status}`);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
473
490
|
isConflictError(error) {
|
|
474
491
|
return (error instanceof Error &&
|
|
475
492
|
'documentId' in error &&
|
package/dist/index.js
CHANGED
|
@@ -370,6 +370,9 @@ program
|
|
|
370
370
|
for (const col of dbImport.schema.mappedColumns) {
|
|
371
371
|
allPropertyTypes.add(col.notionType);
|
|
372
372
|
}
|
|
373
|
+
for (const col of dbImport.schema.scalarColumns) {
|
|
374
|
+
allPropertyTypes.add(col.notionType);
|
|
375
|
+
}
|
|
373
376
|
for (const col of dbImport.schema.unmappedColumns) {
|
|
374
377
|
allPropertyTypes.add(col.notionType);
|
|
375
378
|
}
|
|
@@ -560,6 +563,24 @@ async function importNotionDatabase(client, dbImport, log, options, preCreatedDb
|
|
|
560
563
|
console.warn(` Warning: Failed to create column "${col.notionPropertyName}": ${error instanceof Error ? error.message : error}`);
|
|
561
564
|
}
|
|
562
565
|
}
|
|
566
|
+
// 2b. Create scalar columns (number, date, text, url, email)
|
|
567
|
+
const scalarColumnMap = new Map();
|
|
568
|
+
for (const col of schema.scalarColumns) {
|
|
569
|
+
try {
|
|
570
|
+
const column = await client.addDatabaseColumn(dbId, {
|
|
571
|
+
name: col.notionPropertyName,
|
|
572
|
+
type: col.moxnType,
|
|
573
|
+
});
|
|
574
|
+
scalarColumnMap.set(col.notionPropertyName, {
|
|
575
|
+
columnId: column.id,
|
|
576
|
+
moxnType: col.moxnType,
|
|
577
|
+
});
|
|
578
|
+
console.log(` Column "${col.notionPropertyName}" (${col.moxnType})`);
|
|
579
|
+
}
|
|
580
|
+
catch (error) {
|
|
581
|
+
console.warn(` Warning: Failed to create scalar column "${col.notionPropertyName}": ${error instanceof Error ? error.message : error}`);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
563
584
|
// 3. Link entries and assign tags
|
|
564
585
|
// Build a map of KB path → { documentId, branchId } from migration results
|
|
565
586
|
const docByPath = new Map();
|
|
@@ -605,6 +626,35 @@ async function importNotionDatabase(client, dbImport, log, options, preCreatedDb
|
|
|
605
626
|
}
|
|
606
627
|
}
|
|
607
628
|
}
|
|
629
|
+
// Set scalar property values
|
|
630
|
+
for (const [colName, scalarVal] of values.scalarValues) {
|
|
631
|
+
const scalarColInfo = scalarColumnMap.get(colName);
|
|
632
|
+
if (!scalarColInfo)
|
|
633
|
+
continue;
|
|
634
|
+
// Determine the value to send based on type
|
|
635
|
+
let value = null;
|
|
636
|
+
switch (scalarVal.moxnType) {
|
|
637
|
+
case 'number':
|
|
638
|
+
value = scalarVal.numberValue ?? null;
|
|
639
|
+
break;
|
|
640
|
+
case 'date':
|
|
641
|
+
value = scalarVal.dateValue ?? null;
|
|
642
|
+
break;
|
|
643
|
+
case 'text':
|
|
644
|
+
case 'url':
|
|
645
|
+
case 'email':
|
|
646
|
+
value = scalarVal.textValue ?? null;
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
if (value != null) {
|
|
650
|
+
try {
|
|
651
|
+
await client.setPropertyValue(dbId, docInfo.documentId, colName, value);
|
|
652
|
+
}
|
|
653
|
+
catch (error) {
|
|
654
|
+
console.warn(` Warning: Failed to set "${colName}" on ${docInfo.documentId}: ${error instanceof Error ? error.message : error}`);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
608
658
|
}
|
|
609
659
|
catch (error) {
|
|
610
660
|
console.warn(` Warning: Failed to link entry to database: ${error instanceof Error ? error.message : error}`);
|
|
@@ -8,17 +8,30 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { ContentBlock, SectionInput } from '../types.js';
|
|
10
10
|
import type { NotionDatabase, NotionPage } from './notion-api.js';
|
|
11
|
-
/** Parsed Notion column that maps to a Moxn DB column. */
|
|
11
|
+
/** Parsed Notion column that maps to a Moxn tag-based DB column. */
|
|
12
12
|
export interface MappedColumn {
|
|
13
13
|
notionPropertyId: string;
|
|
14
14
|
notionPropertyName: string;
|
|
15
|
-
notionType: 'select' | 'multi_select' | 'status';
|
|
16
|
-
moxnType: 'select' | 'multi_select';
|
|
15
|
+
notionType: 'select' | 'multi_select' | 'status' | 'checkbox';
|
|
16
|
+
moxnType: 'select' | 'multi_select' | 'status' | 'checkbox';
|
|
17
17
|
options: Array<{
|
|
18
18
|
notionId: string;
|
|
19
19
|
name: string;
|
|
20
20
|
color: string;
|
|
21
21
|
}>;
|
|
22
|
+
/** Status group metadata (only for status columns). */
|
|
23
|
+
groups?: Array<{
|
|
24
|
+
name: string;
|
|
25
|
+
color: string;
|
|
26
|
+
optionNames: string[];
|
|
27
|
+
}>;
|
|
28
|
+
}
|
|
29
|
+
/** Parsed Notion column that maps to a Moxn scalar DB column. */
|
|
30
|
+
export interface ScalarColumn {
|
|
31
|
+
notionPropertyId: string;
|
|
32
|
+
notionPropertyName: string;
|
|
33
|
+
notionType: 'number' | 'date' | 'url' | 'email' | 'rich_text' | 'checkbox';
|
|
34
|
+
moxnType: 'number' | 'date' | 'url' | 'email' | 'text';
|
|
22
35
|
}
|
|
23
36
|
/** Parsed Notion column that will be rendered as text in a Properties section. */
|
|
24
37
|
export interface UnmappedColumn {
|
|
@@ -31,14 +44,26 @@ export interface ParsedDatabaseSchema {
|
|
|
31
44
|
name: string;
|
|
32
45
|
description: string;
|
|
33
46
|
mappedColumns: MappedColumn[];
|
|
47
|
+
scalarColumns: ScalarColumn[];
|
|
34
48
|
unmappedColumns: UnmappedColumn[];
|
|
35
49
|
}
|
|
50
|
+
/** Scalar value for a single database entry property. */
|
|
51
|
+
export interface ScalarValue {
|
|
52
|
+
columnName: string;
|
|
53
|
+
moxnType: 'number' | 'date' | 'url' | 'email' | 'text';
|
|
54
|
+
textValue?: string | null;
|
|
55
|
+
numberValue?: number | null;
|
|
56
|
+
dateValue?: string | null;
|
|
57
|
+
booleanValue?: boolean | null;
|
|
58
|
+
}
|
|
36
59
|
/** Parsed property values for a single database entry. */
|
|
37
60
|
export interface ParsedEntryValues {
|
|
38
61
|
/** Document name derived from title property. */
|
|
39
62
|
title: string;
|
|
40
63
|
/** Mapped column values: column name → selected option names. */
|
|
41
64
|
tagValues: Map<string, string[]>;
|
|
65
|
+
/** Scalar property values: column name → scalar value. */
|
|
66
|
+
scalarValues: Map<string, ScalarValue>;
|
|
42
67
|
/** Unmapped property values for markdown table: property name → rendered value. */
|
|
43
68
|
textValues: Map<string, string>;
|
|
44
69
|
/** File attachments to include as content blocks. */
|
|
@@ -17,6 +17,7 @@ export function parseDatabaseSchema(db) {
|
|
|
17
17
|
const name = richTextToPlain(db.title) || 'Untitled Database';
|
|
18
18
|
const description = richTextToPlain(db.description);
|
|
19
19
|
const mappedColumns = [];
|
|
20
|
+
const scalarColumns = [];
|
|
20
21
|
const unmappedColumns = [];
|
|
21
22
|
for (const [propName, prop] of Object.entries(db.properties)) {
|
|
22
23
|
// Skip title — it becomes the document name
|
|
@@ -49,17 +50,77 @@ export function parseDatabaseSchema(db) {
|
|
|
49
50
|
});
|
|
50
51
|
}
|
|
51
52
|
else if (prop.type === 'status' && prop.status) {
|
|
52
|
-
// Status
|
|
53
|
+
// Status → Moxn status column with group metadata
|
|
53
54
|
mappedColumns.push({
|
|
54
55
|
notionPropertyId: prop.id,
|
|
55
56
|
notionPropertyName: propName,
|
|
56
57
|
notionType: 'status',
|
|
57
|
-
moxnType: '
|
|
58
|
+
moxnType: 'status',
|
|
58
59
|
options: prop.status.options.map((o) => ({
|
|
59
60
|
notionId: o.id,
|
|
60
61
|
name: o.name,
|
|
61
62
|
color: o.color,
|
|
62
63
|
})),
|
|
64
|
+
groups: prop.status.groups?.map((g) => ({
|
|
65
|
+
name: g.name,
|
|
66
|
+
color: g.color ?? 'default',
|
|
67
|
+
optionNames: g.option_ids
|
|
68
|
+
? g.option_ids.map((id) => prop.status.options.find((o) => o.id === id)?.name ?? id)
|
|
69
|
+
: [],
|
|
70
|
+
})),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
else if (prop.type === 'checkbox') {
|
|
74
|
+
// Checkbox → Moxn checkbox column (tag-based 2-option select)
|
|
75
|
+
mappedColumns.push({
|
|
76
|
+
notionPropertyId: prop.id,
|
|
77
|
+
notionPropertyName: propName,
|
|
78
|
+
notionType: 'checkbox',
|
|
79
|
+
moxnType: 'checkbox',
|
|
80
|
+
options: [
|
|
81
|
+
{ notionId: 'checked', name: 'Checked', color: 'green' },
|
|
82
|
+
{ notionId: 'unchecked', name: 'Unchecked', color: 'gray' },
|
|
83
|
+
],
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
else if (prop.type === 'number') {
|
|
87
|
+
scalarColumns.push({
|
|
88
|
+
notionPropertyId: prop.id,
|
|
89
|
+
notionPropertyName: propName,
|
|
90
|
+
notionType: 'number',
|
|
91
|
+
moxnType: 'number',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
else if (prop.type === 'date') {
|
|
95
|
+
scalarColumns.push({
|
|
96
|
+
notionPropertyId: prop.id,
|
|
97
|
+
notionPropertyName: propName,
|
|
98
|
+
notionType: 'date',
|
|
99
|
+
moxnType: 'date',
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
else if (prop.type === 'url') {
|
|
103
|
+
scalarColumns.push({
|
|
104
|
+
notionPropertyId: prop.id,
|
|
105
|
+
notionPropertyName: propName,
|
|
106
|
+
notionType: 'url',
|
|
107
|
+
moxnType: 'url',
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
else if (prop.type === 'email') {
|
|
111
|
+
scalarColumns.push({
|
|
112
|
+
notionPropertyId: prop.id,
|
|
113
|
+
notionPropertyName: propName,
|
|
114
|
+
notionType: 'email',
|
|
115
|
+
moxnType: 'email',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
else if (prop.type === 'rich_text') {
|
|
119
|
+
scalarColumns.push({
|
|
120
|
+
notionPropertyId: prop.id,
|
|
121
|
+
notionPropertyName: propName,
|
|
122
|
+
notionType: 'rich_text',
|
|
123
|
+
moxnType: 'text',
|
|
63
124
|
});
|
|
64
125
|
}
|
|
65
126
|
else {
|
|
@@ -74,7 +135,7 @@ export function parseDatabaseSchema(db) {
|
|
|
74
135
|
});
|
|
75
136
|
}
|
|
76
137
|
}
|
|
77
|
-
return { name, description, mappedColumns, unmappedColumns };
|
|
138
|
+
return { name, description, mappedColumns, scalarColumns, unmappedColumns };
|
|
78
139
|
}
|
|
79
140
|
// ============================================
|
|
80
141
|
// Entry Parsing
|
|
@@ -85,20 +146,38 @@ export function parseDatabaseSchema(db) {
|
|
|
85
146
|
export function parseEntryValues(page, schema) {
|
|
86
147
|
const title = getPageTitle(page);
|
|
87
148
|
const tagValues = new Map();
|
|
149
|
+
const scalarValues = new Map();
|
|
88
150
|
const textValues = new Map();
|
|
89
151
|
const fileBlocks = [];
|
|
90
152
|
// Build lookup by property name
|
|
91
153
|
const mappedByName = new Map(schema.mappedColumns.map((c) => [c.notionPropertyName, c]));
|
|
154
|
+
const scalarByName = new Map(schema.scalarColumns.map((c) => [c.notionPropertyName, c]));
|
|
92
155
|
const unmappedByName = new Set(schema.unmappedColumns.map((c) => c.notionPropertyName));
|
|
93
156
|
for (const [propName, propValue] of Object.entries(page.properties)) {
|
|
94
157
|
if (propValue.type === 'title')
|
|
95
158
|
continue; // Already extracted
|
|
96
|
-
// Check if this is a mapped column
|
|
159
|
+
// Check if this is a tag-based mapped column
|
|
97
160
|
const mapped = mappedByName.get(propName);
|
|
98
161
|
if (mapped) {
|
|
99
|
-
|
|
100
|
-
if (
|
|
101
|
-
|
|
162
|
+
// Checkbox uses tag-based approach: extract "Checked"/"Unchecked" option name
|
|
163
|
+
if (mapped.moxnType === 'checkbox') {
|
|
164
|
+
const isChecked = propValue.checkbox === true;
|
|
165
|
+
tagValues.set(propName, [isChecked ? 'Checked' : 'Unchecked']);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
const values = extractSelectValues(propValue, mapped.notionType);
|
|
169
|
+
if (values.length > 0) {
|
|
170
|
+
tagValues.set(propName, values);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
// Check if this is a scalar column
|
|
176
|
+
const scalar = scalarByName.get(propName);
|
|
177
|
+
if (scalar) {
|
|
178
|
+
const extracted = extractScalarValue(propValue, scalar);
|
|
179
|
+
if (extracted) {
|
|
180
|
+
scalarValues.set(propName, extracted);
|
|
102
181
|
}
|
|
103
182
|
continue;
|
|
104
183
|
}
|
|
@@ -147,7 +226,7 @@ export function parseEntryValues(page, schema) {
|
|
|
147
226
|
}
|
|
148
227
|
}
|
|
149
228
|
}
|
|
150
|
-
return { title, tagValues, textValues, fileBlocks };
|
|
229
|
+
return { title, tagValues, scalarValues, textValues, fileBlocks };
|
|
151
230
|
}
|
|
152
231
|
/**
|
|
153
232
|
* Render unmapped properties as a markdown table section.
|
|
@@ -187,6 +266,54 @@ function extractSelectValues(propValue, notionType) {
|
|
|
187
266
|
return [];
|
|
188
267
|
}
|
|
189
268
|
}
|
|
269
|
+
function extractScalarValue(propValue, scalarCol) {
|
|
270
|
+
switch (scalarCol.moxnType) {
|
|
271
|
+
case 'number':
|
|
272
|
+
if (propValue.number == null)
|
|
273
|
+
return null;
|
|
274
|
+
return {
|
|
275
|
+
columnName: scalarCol.notionPropertyName,
|
|
276
|
+
moxnType: 'number',
|
|
277
|
+
numberValue: propValue.number,
|
|
278
|
+
};
|
|
279
|
+
case 'date':
|
|
280
|
+
if (!propValue.date?.start)
|
|
281
|
+
return null;
|
|
282
|
+
return {
|
|
283
|
+
columnName: scalarCol.notionPropertyName,
|
|
284
|
+
moxnType: 'date',
|
|
285
|
+
dateValue: propValue.date.start,
|
|
286
|
+
};
|
|
287
|
+
case 'url':
|
|
288
|
+
if (!propValue.url)
|
|
289
|
+
return null;
|
|
290
|
+
return {
|
|
291
|
+
columnName: scalarCol.notionPropertyName,
|
|
292
|
+
moxnType: 'url',
|
|
293
|
+
textValue: propValue.url,
|
|
294
|
+
};
|
|
295
|
+
case 'email':
|
|
296
|
+
if (!propValue.email)
|
|
297
|
+
return null;
|
|
298
|
+
return {
|
|
299
|
+
columnName: scalarCol.notionPropertyName,
|
|
300
|
+
moxnType: 'email',
|
|
301
|
+
textValue: propValue.email,
|
|
302
|
+
};
|
|
303
|
+
case 'text': {
|
|
304
|
+
const text = richTextToPlain(propValue.rich_text ?? []);
|
|
305
|
+
if (!text)
|
|
306
|
+
return null;
|
|
307
|
+
return {
|
|
308
|
+
columnName: scalarCol.notionPropertyName,
|
|
309
|
+
moxnType: 'text',
|
|
310
|
+
textValue: text,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
default:
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
190
317
|
function renderPropertyValue(prop) {
|
|
191
318
|
switch (prop.type) {
|
|
192
319
|
case 'rich_text':
|
|
@@ -130,17 +130,62 @@ async function createNotionDatabase(ctx, parentPageId, resolved) {
|
|
|
130
130
|
name: opt.tagName,
|
|
131
131
|
color: hexToNotionColor(opt.tagColor),
|
|
132
132
|
}));
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
133
|
+
switch (col.type) {
|
|
134
|
+
case 'select':
|
|
135
|
+
properties[col.name] = {
|
|
136
|
+
type: 'select',
|
|
137
|
+
select: { options },
|
|
138
|
+
};
|
|
139
|
+
break;
|
|
140
|
+
case 'multi_select':
|
|
141
|
+
properties[col.name] = {
|
|
142
|
+
type: 'multi_select',
|
|
143
|
+
multi_select: { options },
|
|
144
|
+
};
|
|
145
|
+
break;
|
|
146
|
+
case 'status':
|
|
147
|
+
// Status exports as select in Notion (status requires specific API access)
|
|
148
|
+
properties[col.name] = {
|
|
149
|
+
type: 'select',
|
|
150
|
+
select: { options },
|
|
151
|
+
};
|
|
152
|
+
break;
|
|
153
|
+
case 'checkbox':
|
|
154
|
+
properties[col.name] = {
|
|
155
|
+
type: 'checkbox',
|
|
156
|
+
checkbox: {},
|
|
157
|
+
};
|
|
158
|
+
break;
|
|
159
|
+
case 'number':
|
|
160
|
+
properties[col.name] = {
|
|
161
|
+
type: 'number',
|
|
162
|
+
number: { format: 'number' },
|
|
163
|
+
};
|
|
164
|
+
break;
|
|
165
|
+
case 'date':
|
|
166
|
+
properties[col.name] = {
|
|
167
|
+
type: 'date',
|
|
168
|
+
date: {},
|
|
169
|
+
};
|
|
170
|
+
break;
|
|
171
|
+
case 'url':
|
|
172
|
+
properties[col.name] = {
|
|
173
|
+
type: 'url',
|
|
174
|
+
url: {},
|
|
175
|
+
};
|
|
176
|
+
break;
|
|
177
|
+
case 'email':
|
|
178
|
+
properties[col.name] = {
|
|
179
|
+
type: 'email',
|
|
180
|
+
email: {},
|
|
181
|
+
};
|
|
182
|
+
break;
|
|
183
|
+
case 'text':
|
|
184
|
+
properties[col.name] = {
|
|
185
|
+
type: 'rich_text',
|
|
186
|
+
rich_text: {},
|
|
187
|
+
};
|
|
188
|
+
break;
|
|
144
189
|
}
|
|
145
190
|
}
|
|
146
191
|
await sleep(RATE_LIMIT_MS);
|
|
@@ -216,19 +261,81 @@ async function createDatabaseEntries(ctx, dataSourceId, resolved) {
|
|
|
216
261
|
};
|
|
217
262
|
// Add column values
|
|
218
263
|
for (const [colName, propValue] of Object.entries(doc.properties)) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
select:
|
|
225
|
-
|
|
264
|
+
// Tag-based columns (select, multi_select, status, checkbox)
|
|
265
|
+
if ('values' in propValue) {
|
|
266
|
+
if (propValue.values.length === 0)
|
|
267
|
+
continue;
|
|
268
|
+
switch (propValue.columnType) {
|
|
269
|
+
case 'select':
|
|
270
|
+
case 'status':
|
|
271
|
+
properties[colName] = {
|
|
272
|
+
type: 'select',
|
|
273
|
+
select: { name: propValue.values[0].tagName },
|
|
274
|
+
};
|
|
275
|
+
break;
|
|
276
|
+
case 'multi_select':
|
|
277
|
+
properties[colName] = {
|
|
278
|
+
type: 'multi_select',
|
|
279
|
+
multi_select: propValue.values.map((v) => ({ name: v.tagName })),
|
|
280
|
+
};
|
|
281
|
+
break;
|
|
282
|
+
case 'checkbox': {
|
|
283
|
+
// The first tag is the "checked" option
|
|
284
|
+
const isChecked = propValue.values[0]?.tagName === 'Checked';
|
|
285
|
+
properties[colName] = {
|
|
286
|
+
type: 'checkbox',
|
|
287
|
+
checkbox: isChecked,
|
|
288
|
+
};
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
226
292
|
}
|
|
227
|
-
else
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
293
|
+
else {
|
|
294
|
+
// Scalar columns (number, date, text, url, email)
|
|
295
|
+
if (propValue.textValue == null && propValue.numberValue == null && propValue.dateValue == null && propValue.booleanValue == null)
|
|
296
|
+
continue;
|
|
297
|
+
switch (propValue.columnType) {
|
|
298
|
+
case 'number':
|
|
299
|
+
if (propValue.numberValue != null) {
|
|
300
|
+
properties[colName] = {
|
|
301
|
+
type: 'number',
|
|
302
|
+
number: propValue.numberValue,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
break;
|
|
306
|
+
case 'date':
|
|
307
|
+
if (propValue.dateValue) {
|
|
308
|
+
properties[colName] = {
|
|
309
|
+
type: 'date',
|
|
310
|
+
date: { start: propValue.dateValue },
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
break;
|
|
314
|
+
case 'url':
|
|
315
|
+
if (propValue.textValue) {
|
|
316
|
+
properties[colName] = {
|
|
317
|
+
type: 'url',
|
|
318
|
+
url: propValue.textValue,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
break;
|
|
322
|
+
case 'email':
|
|
323
|
+
if (propValue.textValue) {
|
|
324
|
+
properties[colName] = {
|
|
325
|
+
type: 'email',
|
|
326
|
+
email: propValue.textValue,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
break;
|
|
330
|
+
case 'text':
|
|
331
|
+
if (propValue.textValue) {
|
|
332
|
+
properties[colName] = {
|
|
333
|
+
type: 'rich_text',
|
|
334
|
+
rich_text: [{ type: 'text', text: { content: propValue.textValue } }],
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
232
339
|
}
|
|
233
340
|
}
|
|
234
341
|
await sleep(RATE_LIMIT_MS);
|
package/package.json
CHANGED