notion.mjs
71 lines 2.6 KB
Raw
sha256:94ec65bd2b200240ac785a97cf14c5db066832bd608a24d6a9c151f17b918b02 feat(calendar): hosted bridge/gateway route parity and time… Human minor ⚠ breaking 6 hours ago
1 /**
2 * Notion import. Fetches pages as markdown via Notion API (retrieve page as markdown).
3 * Requires NOTION_API_KEY. Input: comma-separated page IDs or a single page ID.
4 *
5 * One note per page; frontmatter: source: notion, source_id: page_id, date, title (if available).
6 */
7
8 import path from 'path';
9 import { writeNote } from '../write.mjs';
10 import { normalizeSlug } from '../vault.mjs';
11
12 const NOTION_API_BASE = 'https://api.notion.com/v1';
13 const NOTION_VERSION = '2022-06-28'; // Markdown endpoint may require 2026-03-11 on newer Notion API
14
15 /**
16 * @param {string} input - Comma-separated Notion page IDs (e.g. "uuid1,uuid2") or single ID
17 * @param {{ vaultPath: string, outputBase: string, project?: string, tags: string[], dryRun: boolean }} ctx
18 * @returns {Promise<{ imported: { path: string, source_id?: string }[], count: number }>}
19 */
20 export async function importNotion(input, ctx) {
21 const { vaultPath, outputBase, project, tags, dryRun } = ctx;
22 const apiKey = process.env.NOTION_API_KEY;
23 if (!apiKey || typeof apiKey !== 'string') {
24 throw new Error('NOTION_API_KEY is required for Notion import. Create an integration at notion.so/my-integrations.');
25 }
26
27 const pageIds = input.split(',').map((id) => id.trim()).filter(Boolean);
28 if (!pageIds.length) {
29 throw new Error('Provide at least one Notion page ID (or comma-separated list).');
30 }
31
32 const imported = [];
33 const now = new Date().toISOString().slice(0, 10);
34
35 for (let i = 0; i < pageIds.length; i++) {
36 const pageId = pageIds[i];
37 const url = `${NOTION_API_BASE}/pages/${encodeURIComponent(pageId)}/markdown`;
38 const res = await fetch(url, {
39 method: 'GET',
40 headers: {
41 Authorization: `Bearer ${apiKey}`,
42 'Notion-Version': NOTION_VERSION,
43 Accept: 'application/json',
44 },
45 });
46 if (!res.ok) {
47 const text = await res.text();
48 throw new Error(`Notion API error for page ${pageId}: ${res.status} ${text}`);
49 }
50 const data = await res.json();
51 const body = typeof data.markdown === 'string' ? data.markdown : '';
52 const safeId = pageId.replace(/[^a-zA-Z0-9-_]/g, '_').slice(0, 16);
53 const safeName = `notion-${safeId}-${i + 1}.md`;
54 const outputRel = path.join(outputBase, safeName).replace(/\\/g, '/');
55
56 const frontmatter = {
57 source: 'notion',
58 source_id: pageId,
59 date: now,
60 ...(project && { project: normalizeSlug(project) }),
61 ...(tags.length && { tags }),
62 };
63
64 if (!dryRun) {
65 writeNote(vaultPath, outputRel, { body, frontmatter });
66 }
67 imported.push({ path: outputRel, source_id: pageId });
68 }
69
70 return { imported, count: imported.length };
71 }
File History 1 commit
sha256:94ec65bd2b200240ac785a97cf14c5db066832bd608a24d6a9c151f17b918b02 feat(calendar): hosted bridge/gateway route parity and time… Human minor 6 hours ago