@moxn/kb-migrate 0.4.2 → 0.4.3
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.
|
@@ -429,12 +429,9 @@ export function richTextToMarkdown(richText) {
|
|
|
429
429
|
text = '*' + text + '*';
|
|
430
430
|
if (rt.annotations.strikethrough)
|
|
431
431
|
text = '~~' + text + '~~';
|
|
432
|
-
//
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
}
|
|
436
|
-
else if (rt.type === 'mention' && rt.mention) {
|
|
437
|
-
// Handle mention types
|
|
432
|
+
// Handle mentions BEFORE href — Notion sets href on mentions too
|
|
433
|
+
// (as a web URL), but we want notion:// protocol for resolution
|
|
434
|
+
if (rt.type === 'mention' && rt.mention) {
|
|
438
435
|
if (rt.mention.type === 'user' && rt.mention.user?.name) {
|
|
439
436
|
text = `@${rt.mention.user.name}`;
|
|
440
437
|
}
|
|
@@ -450,6 +447,9 @@ export function richTextToMarkdown(richText) {
|
|
|
450
447
|
text += ` → ${rt.mention.date.end}`;
|
|
451
448
|
}
|
|
452
449
|
}
|
|
450
|
+
else if (rt.href) {
|
|
451
|
+
text = `[${text}](${rt.href})`;
|
|
452
|
+
}
|
|
453
453
|
else if (rt.type === 'equation' && rt.equation) {
|
|
454
454
|
text = `$${rt.equation.expression}$`;
|
|
455
455
|
}
|
|
@@ -45,8 +45,8 @@ describe('richTextToMarkdown', () => {
|
|
|
45
45
|
const result = richTextToMarkdown([rt]);
|
|
46
46
|
expect(result).toBe('[My Database](notion://abc123de-f456-abc1-23de-f456abc123de)');
|
|
47
47
|
});
|
|
48
|
-
it('renders database mention with href using
|
|
49
|
-
//
|
|
48
|
+
it('renders database mention with href using notion:// (mention takes precedence over href)', () => {
|
|
49
|
+
// Mentions should always use notion:// protocol, even when href is set
|
|
50
50
|
const rt = {
|
|
51
51
|
type: 'mention',
|
|
52
52
|
plain_text: 'My Database',
|
|
@@ -65,8 +65,7 @@ describe('richTextToMarkdown', () => {
|
|
|
65
65
|
},
|
|
66
66
|
};
|
|
67
67
|
const result = richTextToMarkdown([rt]);
|
|
68
|
-
|
|
69
|
-
expect(result).toBe('[My Database](https://www.notion.so/workspace/abc123de-f456-abc1-23de-f456abc123de)');
|
|
68
|
+
expect(result).toBe('[My Database](notion://abc123de-f456-abc1-23de-f456abc123de)');
|
|
70
69
|
});
|
|
71
70
|
});
|
|
72
71
|
describe('page mentions', () => {
|
|
@@ -91,7 +90,7 @@ describe('richTextToMarkdown', () => {
|
|
|
91
90
|
const result = richTextToMarkdown([rt]);
|
|
92
91
|
expect(result).toBe('[My Page](notion://abc123de-f456-abc1-23de-f456abc123de)');
|
|
93
92
|
});
|
|
94
|
-
it('renders page mention with href using
|
|
93
|
+
it('renders page mention with href using notion:// (mention takes precedence over href)', () => {
|
|
95
94
|
const rt = {
|
|
96
95
|
type: 'mention',
|
|
97
96
|
plain_text: 'My Page',
|
|
@@ -110,7 +109,7 @@ describe('richTextToMarkdown', () => {
|
|
|
110
109
|
},
|
|
111
110
|
};
|
|
112
111
|
const result = richTextToMarkdown([rt]);
|
|
113
|
-
expect(result).toBe('[My Page](
|
|
112
|
+
expect(result).toBe('[My Page](notion://abc123de-f456-abc1-23de-f456abc123de)');
|
|
114
113
|
});
|
|
115
114
|
});
|
|
116
115
|
describe('other mention types preserved', () => {
|
|
@@ -18,6 +18,9 @@ const NOTION_PROTOCOL_RE = /\[([^\]]*)\]\(notion:\/\/([a-f0-9-]{32,36})\)/g;
|
|
|
18
18
|
const NOTION_WEB_URL_RE = /\[([^\]]*)\]\(https?:\/\/(?:www\.)?notion\.so\/(?:[a-zA-Z0-9_-]+\/)*([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})(?:[?#][^)]*)?(?:\))/g;
|
|
19
19
|
// Fallback for IDs without dashes embedded in slug URLs (e.g., ...Page-Title-abc123def456...)
|
|
20
20
|
const NOTION_WEB_URL_SLUG_RE = /\[([^\]]*)\]\(https?:\/\/(?:www\.)?notion\.so\/(?:[a-zA-Z0-9_-]+\/)*[a-zA-Z0-9-]+-([a-f0-9]{32})(?:[?#][^)]*)?\)/g;
|
|
21
|
+
// Bare 32-char hex on notion.so (e.g., https://www.notion.so/3004bf42cfeb818ebe8fe2f4befdad52)
|
|
22
|
+
// Catches mention hrefs that leak through as web URLs
|
|
23
|
+
const NOTION_WEB_URL_BARE_RE = /\[([^\]]*)\]\(https?:\/\/(?:www\.)?notion\.so\/(?:[a-zA-Z0-9_-]+\/)*([a-f0-9]{32})(?:[?#][^)]*)?\)/g;
|
|
21
24
|
// 3. Unresolved placeholders from link_to_page conversion
|
|
22
25
|
const LINK_PLACEHOLDER_RE = /\*\(Link to Notion page: ([a-f0-9-]{32,36})\)\*/g;
|
|
23
26
|
// 4. Relation property markers
|
|
@@ -87,6 +90,21 @@ export function resolveNotionReferences(sections, mapping) {
|
|
|
87
90
|
}
|
|
88
91
|
return `[${displayText}](notion://${nid})`;
|
|
89
92
|
});
|
|
93
|
+
// Pass 3b: Bare 32-char hex Notion URLs (safety net for mention hrefs)
|
|
94
|
+
text = text.replace(NOTION_WEB_URL_BARE_RE, (_match, displayText, rawId) => {
|
|
95
|
+
const nid = normalizeId(rawId);
|
|
96
|
+
const kbPath = mapping.notionIdToKbPath.get(nid);
|
|
97
|
+
if (kbPath) {
|
|
98
|
+
references.push({
|
|
99
|
+
sectionIndex,
|
|
100
|
+
targetNotionId: nid,
|
|
101
|
+
targetKbPath: kbPath,
|
|
102
|
+
displayText: displayText || kbPath,
|
|
103
|
+
});
|
|
104
|
+
return `[${displayText || kbPath}](${kbPath})`;
|
|
105
|
+
}
|
|
106
|
+
return `[${displayText}](notion://${nid})`;
|
|
107
|
+
});
|
|
90
108
|
// Pass 4: Unresolved link_to_page placeholders
|
|
91
109
|
text = text.replace(LINK_PLACEHOLDER_RE, (_match, rawId) => {
|
|
92
110
|
const nid = normalizeId(rawId);
|
|
@@ -120,6 +120,24 @@ describe('resolveNotionReferences', () => {
|
|
|
120
120
|
const { sections: resolved } = resolveNotionReferences(sections, m);
|
|
121
121
|
expect(getText(resolved[0])).toBe('See [link](/docs/my-page)');
|
|
122
122
|
});
|
|
123
|
+
it('resolves bare 32-char hex Notion URLs (no slug prefix)', () => {
|
|
124
|
+
const sections = [
|
|
125
|
+
textSection('Intro', 'See [Dev Setup](https://www.notion.so/3004bf42cfeb818ebe8fe2f4befdad52)'),
|
|
126
|
+
];
|
|
127
|
+
const m = mapping({ '3004bf42cfeb818ebe8fe2f4befdad52': '/docs/dev-setup' });
|
|
128
|
+
const { sections: resolved, references } = resolveNotionReferences(sections, m);
|
|
129
|
+
expect(getText(resolved[0])).toBe('See [Dev Setup](/docs/dev-setup)');
|
|
130
|
+
expect(references).toHaveLength(1);
|
|
131
|
+
expect(references[0].targetNotionId).toBe('3004bf42cfeb818ebe8fe2f4befdad52');
|
|
132
|
+
});
|
|
133
|
+
it('normalizes unresolved bare hex Notion URLs to notion://', () => {
|
|
134
|
+
const sections = [
|
|
135
|
+
textSection('Intro', 'See [page](https://www.notion.so/aabbccdd11223344aabbccdd11223344)'),
|
|
136
|
+
];
|
|
137
|
+
const m = mapping({});
|
|
138
|
+
const { sections: resolved } = resolveNotionReferences(sections, m);
|
|
139
|
+
expect(getText(resolved[0])).toBe('See [page](notion://aabbccdd11223344aabbccdd11223344)');
|
|
140
|
+
});
|
|
123
141
|
});
|
|
124
142
|
describe('link_to_page placeholders', () => {
|
|
125
143
|
it('resolves placeholders to KB path links', () => {
|
package/package.json
CHANGED