@mcp-monorepo/notion-query 1.1.0 → 1.3.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/dist/index.js +56 -7
- package/dist/index.js.map +1 -1
- package/dist/lib/client.d.ts +9 -0
- package/dist/lib/client.d.ts.map +1 -0
- package/dist/lib/client.js +19 -0
- package/dist/lib/client.js.map +1 -0
- package/dist/lib/config.d.ts +44 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +49 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/id-utils.d.ts +8 -0
- package/dist/lib/id-utils.d.ts.map +1 -0
- package/dist/lib/id-utils.js +20 -0
- package/dist/lib/id-utils.js.map +1 -0
- package/dist/lib/markdown-converter.d.ts +8 -0
- package/dist/lib/markdown-converter.d.ts.map +1 -0
- package/dist/lib/markdown-converter.js +95 -0
- package/dist/lib/markdown-converter.js.map +1 -0
- package/dist/lib/notion-syncer.d.ts +27 -0
- package/dist/lib/notion-syncer.d.ts.map +1 -0
- package/dist/lib/notion-syncer.js +212 -0
- package/dist/lib/notion-syncer.js.map +1 -0
- package/dist/lib/parser.d.ts +13 -0
- package/dist/lib/parser.d.ts.map +1 -0
- package/dist/lib/parser.js +88 -0
- package/dist/lib/parser.js.map +1 -0
- package/dist/lib/property-parser.d.ts +23 -0
- package/dist/lib/property-parser.d.ts.map +1 -0
- package/dist/lib/property-parser.js +200 -0
- package/dist/lib/property-parser.js.map +1 -0
- package/dist/lib/response-formatter.d.ts +16 -0
- package/dist/lib/response-formatter.d.ts.map +1 -0
- package/dist/lib/response-formatter.js +173 -0
- package/dist/lib/response-formatter.js.map +1 -0
- package/dist/lib/sync-state-manager.d.ts +29 -0
- package/dist/lib/sync-state-manager.d.ts.map +1 -0
- package/dist/lib/sync-state-manager.js +45 -0
- package/dist/lib/sync-state-manager.js.map +1 -0
- package/dist/local-rag/DEMO.d.ts +22 -0
- package/dist/local-rag/DEMO.d.ts.map +1 -0
- package/dist/local-rag/DEMO.js +142 -0
- package/dist/local-rag/DEMO.js.map +1 -0
- package/dist/local-rag/chunker.d.ts +24 -0
- package/dist/local-rag/chunker.d.ts.map +1 -0
- package/dist/local-rag/chunker.js +58 -0
- package/dist/local-rag/chunker.js.map +1 -0
- package/dist/local-rag/embedder.d.ts +43 -0
- package/dist/local-rag/embedder.d.ts.map +1 -0
- package/dist/local-rag/embedder.js +74 -0
- package/dist/local-rag/embedder.js.map +1 -0
- package/dist/local-rag/embedder.service.d.ts +15 -0
- package/dist/local-rag/embedder.service.d.ts.map +1 -0
- package/dist/local-rag/embedder.service.js +84 -0
- package/dist/local-rag/embedder.service.js.map +1 -0
- package/dist/local-rag/embedder.worker.d.ts +2 -0
- package/dist/local-rag/embedder.worker.d.ts.map +1 -0
- package/dist/local-rag/embedder.worker.js +34 -0
- package/dist/local-rag/embedder.worker.js.map +1 -0
- package/dist/local-rag/errors.d.ts +31 -0
- package/dist/local-rag/errors.d.ts.map +1 -0
- package/dist/local-rag/errors.js +47 -0
- package/dist/local-rag/errors.js.map +1 -0
- package/dist/local-rag/html-parser.d.ts +2 -0
- package/dist/local-rag/html-parser.d.ts.map +1 -0
- package/dist/local-rag/html-parser.js +32 -0
- package/dist/local-rag/html-parser.js.map +1 -0
- package/dist/local-rag/index.d.ts +67 -0
- package/dist/local-rag/index.d.ts.map +1 -0
- package/dist/local-rag/index.js +410 -0
- package/dist/local-rag/index.js.map +1 -0
- package/dist/local-rag/parser.d.ts +59 -0
- package/dist/local-rag/parser.d.ts.map +1 -0
- package/dist/local-rag/parser.js +206 -0
- package/dist/local-rag/parser.js.map +1 -0
- package/dist/local-rag/types.d.ts +209 -0
- package/dist/local-rag/types.d.ts.map +1 -0
- package/dist/local-rag/types.js +5 -0
- package/dist/local-rag/types.js.map +1 -0
- package/dist/local-rag/utils/pool.d.ts +60 -0
- package/dist/local-rag/utils/pool.d.ts.map +1 -0
- package/dist/local-rag/utils/pool.js +140 -0
- package/dist/local-rag/utils/pool.js.map +1 -0
- package/dist/local-rag/utils/typed-emitter.d.ts +28 -0
- package/dist/local-rag/utils/typed-emitter.d.ts.map +1 -0
- package/dist/local-rag/utils/typed-emitter.js +44 -0
- package/dist/local-rag/utils/typed-emitter.js.map +1 -0
- package/dist/local-rag/vectordb/index.d.ts +91 -0
- package/dist/local-rag/vectordb/index.d.ts.map +1 -0
- package/dist/local-rag/vectordb/index.js +278 -0
- package/dist/local-rag/vectordb/index.js.map +1 -0
- package/dist/local-rag/vectordb/manager.d.ts +28 -0
- package/dist/local-rag/vectordb/manager.d.ts.map +1 -0
- package/dist/local-rag/vectordb/manager.js +91 -0
- package/dist/local-rag/vectordb/manager.js.map +1 -0
- package/dist/local-rag/vectordb/migration.d.ts +27 -0
- package/dist/local-rag/vectordb/migration.d.ts.map +1 -0
- package/dist/local-rag/vectordb/migration.js +121 -0
- package/dist/local-rag/vectordb/migration.js.map +1 -0
- package/dist/local-rag/vectordb/retriever.d.ts +51 -0
- package/dist/local-rag/vectordb/retriever.d.ts.map +1 -0
- package/dist/local-rag/vectordb/retriever.js +157 -0
- package/dist/local-rag/vectordb/retriever.js.map +1 -0
- package/dist/local-rag/vectordb/schema.d.ts +33 -0
- package/dist/local-rag/vectordb/schema.d.ts.map +1 -0
- package/dist/local-rag/vectordb/schema.js +102 -0
- package/dist/local-rag/vectordb/schema.js.map +1 -0
- package/dist/local-rag/watcher.d.ts +48 -0
- package/dist/local-rag/watcher.d.ts.map +1 -0
- package/dist/local-rag/watcher.js +102 -0
- package/dist/local-rag/watcher.js.map +1 -0
- package/dist/tools/create-pages.d.ts +4 -0
- package/dist/tools/create-pages.d.ts.map +1 -0
- package/dist/tools/create-pages.js +184 -0
- package/dist/tools/create-pages.js.map +1 -0
- package/dist/tools/fetch.d.ts +4 -0
- package/dist/tools/fetch.d.ts.map +1 -0
- package/dist/tools/fetch.js +90 -0
- package/dist/tools/fetch.js.map +1 -0
- package/dist/tools/query-datasource.d.ts +4 -0
- package/dist/tools/query-datasource.d.ts.map +1 -0
- package/dist/tools/{notion-query.js → query-datasource.js} +31 -38
- package/dist/tools/query-datasource.js.map +1 -0
- package/dist/tools/search.d.ts +12 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +75 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/update-page.d.ts +4 -0
- package/dist/tools/update-page.d.ts.map +1 -0
- package/dist/tools/update-page.js +135 -0
- package/dist/tools/update-page.js.map +1 -0
- package/package.json +23 -8
- package/dist/tools/notion-query.d.ts +0 -3
- package/dist/tools/notion-query.d.ts.map +0 -1
- package/dist/tools/notion-query.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,59 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
import { mkdir } from 'node:fs/promises';
|
|
3
|
+
import { createMcpServer, logger } from '@mcp-monorepo/shared';
|
|
4
|
+
import { NOTION_CONTENT_DIR, NOTION_RAG_DIR } from './lib/config.js';
|
|
5
|
+
import { NotionSyncer } from './lib/notion-syncer.js';
|
|
6
|
+
import { LocalRAG } from './local-rag/index.js';
|
|
7
|
+
import { registerCreatePagesTool } from './tools/create-pages.js';
|
|
8
|
+
import { registerFetchTool } from './tools/fetch.js';
|
|
9
|
+
import { registerQueryDatasourceTool } from './tools/query-datasource.js';
|
|
10
|
+
import { registerSearchTool } from './tools/search.js'; // Import the new tool
|
|
11
|
+
import { registerUpdatePageTool } from './tools/update-page.js';
|
|
12
|
+
/**
|
|
13
|
+
* Asynchronous main function to initialize services and start the MCP server.
|
|
14
|
+
*/
|
|
15
|
+
async function main() {
|
|
16
|
+
// 1. Initialize LocalRAG to watch the dedicated Notion content directory.
|
|
17
|
+
logger.info(`Initializing LocalRAG to watch: ${NOTION_CONTENT_DIR}`);
|
|
18
|
+
const localRag = await LocalRAG.create({
|
|
19
|
+
dbPath: NOTION_RAG_DIR,
|
|
20
|
+
baseDir: NOTION_RAG_DIR,
|
|
21
|
+
});
|
|
22
|
+
// Watch the content folder for changes
|
|
23
|
+
await mkdir(NOTION_CONTENT_DIR, { recursive: true });
|
|
24
|
+
logger.log(`Watching Notion content directory: ${NOTION_CONTENT_DIR}`);
|
|
25
|
+
await localRag.ingestFolder({ folderPath: NOTION_CONTENT_DIR, watch: true });
|
|
26
|
+
// 2. Initialize and start the Notion Syncer in the background.
|
|
27
|
+
const notionSyncer = new NotionSyncer();
|
|
28
|
+
await notionSyncer.start();
|
|
29
|
+
// 3. Create the MCP server and register all tools.
|
|
30
|
+
// The search tool gets access to the RAG and Syncer instances.
|
|
31
|
+
createMcpServer({
|
|
32
|
+
name: 'notion-query',
|
|
33
|
+
importMetaPath: import.meta.filename,
|
|
34
|
+
title: 'Notion Query MCP Server',
|
|
35
|
+
tools: [
|
|
36
|
+
(server) => registerQueryDatasourceTool(server, notionSyncer),
|
|
37
|
+
(server) => registerFetchTool(server, notionSyncer),
|
|
38
|
+
(server) => registerCreatePagesTool(server, notionSyncer),
|
|
39
|
+
(server) => registerUpdatePageTool(server, notionSyncer),
|
|
40
|
+
(server) => registerSearchTool(server, localRag, notionSyncer),
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
// 4. Graceful shutdown handling
|
|
44
|
+
const shutdown = async () => {
|
|
45
|
+
logger.info('Shutting down services gracefully...');
|
|
46
|
+
notionSyncer.stop();
|
|
47
|
+
await localRag.shutdown();
|
|
48
|
+
logger.close();
|
|
49
|
+
setTimeout(() => process.exit(0), 1000);
|
|
50
|
+
};
|
|
51
|
+
process.on('SIGINT', shutdown);
|
|
52
|
+
process.on('SIGTERM', shutdown);
|
|
53
|
+
}
|
|
54
|
+
// Execute the main function
|
|
55
|
+
main().catch((error) => {
|
|
56
|
+
logger.error('Failed to start the Notion Query MCP Server.', error);
|
|
57
|
+
setTimeout(() => process.exit(1), 1000);
|
|
9
58
|
});
|
|
10
59
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Client } from '@notionhq/client';
|
|
2
|
+
/**
|
|
3
|
+
* Initializes and returns a singleton instance of the Notion SDK Client.
|
|
4
|
+
* It retrieves the NOTION_API_KEY from the environment variables.
|
|
5
|
+
* @throws {Error} If the NOTION_API_KEY environment variable is not set.
|
|
6
|
+
* @returns {Client} The initialized Notion client.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getNotionClient(): Client;
|
|
9
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAIzC;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,MAAM,CASxC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Client } from '@notionhq/client';
|
|
2
|
+
let notionClient;
|
|
3
|
+
/**
|
|
4
|
+
* Initializes and returns a singleton instance of the Notion SDK Client.
|
|
5
|
+
* It retrieves the NOTION_API_KEY from the environment variables.
|
|
6
|
+
* @throws {Error} If the NOTION_API_KEY environment variable is not set.
|
|
7
|
+
* @returns {Client} The initialized Notion client.
|
|
8
|
+
*/
|
|
9
|
+
export function getNotionClient() {
|
|
10
|
+
if (!notionClient) {
|
|
11
|
+
const NOTION_API_KEY = process.env.NOTION_API_KEY;
|
|
12
|
+
if (!NOTION_API_KEY) {
|
|
13
|
+
throw new Error('NOTION_API_KEY environment variable is not set.');
|
|
14
|
+
}
|
|
15
|
+
notionClient = new Client({ auth: NOTION_API_KEY });
|
|
16
|
+
}
|
|
17
|
+
return notionClient;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAEzC,IAAI,YAAgC,CAAA;AAEpC;;;;;GAKG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAA;QACjD,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAA;QACpE,CAAC;QACD,YAAY,GAAG,IAAI,MAAM,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAA;IACrD,CAAC;IACD,OAAO,YAAY,CAAA;AACrB,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The root directory for all application-specific data, located in the user's home directory.
|
|
3
|
+
*/
|
|
4
|
+
export declare const APP_DATA_DIR: string;
|
|
5
|
+
/**
|
|
6
|
+
* The directory where all data related to the Notion RAG index is stored.
|
|
7
|
+
*/
|
|
8
|
+
export declare const NOTION_RAG_DIR: string;
|
|
9
|
+
/**
|
|
10
|
+
* The directory where downloaded and converted Notion pages (as Markdown) are stored.
|
|
11
|
+
* This is the folder that the LocalRAG instance will watch.
|
|
12
|
+
*/
|
|
13
|
+
export declare const NOTION_CONTENT_DIR: string;
|
|
14
|
+
/**
|
|
15
|
+
* The full path to the JSON file that stores the synchronization state.
|
|
16
|
+
*/
|
|
17
|
+
export declare const SYNC_STATE_PATH: string;
|
|
18
|
+
/**
|
|
19
|
+
* The delay in milliseconds between API calls enforced by the NotionApiThrottler.
|
|
20
|
+
* The Notion API has a limit of approximately 3 requests per second. 350ms provides a safe buffer.
|
|
21
|
+
*/
|
|
22
|
+
export declare const NOTION_API_RATE_LIMIT_DELAY_MS = 350;
|
|
23
|
+
/**
|
|
24
|
+
* The number of pages to fetch in a single paginated API request.
|
|
25
|
+
* The maximum allowed by the Notion API is 100.
|
|
26
|
+
*/
|
|
27
|
+
export declare const NOTION_PAGINATION_LIMIT = 100;
|
|
28
|
+
/**
|
|
29
|
+
* The total number of pages to add to the processing queue during each sync cycle.
|
|
30
|
+
* This acts as a cap to ensure each sync loop is reasonably short.
|
|
31
|
+
* @default 30
|
|
32
|
+
*/
|
|
33
|
+
export declare const SYNC_BATCH_SIZE: number;
|
|
34
|
+
/**
|
|
35
|
+
* The interval in milliseconds that the main synchronization loop waits after completing.
|
|
36
|
+
* @default 15 minutes
|
|
37
|
+
*/
|
|
38
|
+
export declare const SYNC_INTERVAL_MS: number;
|
|
39
|
+
/**
|
|
40
|
+
* The interval for the local-only garbage collection task, which removes orphaned files.
|
|
41
|
+
* @default 12 hours
|
|
42
|
+
*/
|
|
43
|
+
export declare const GC_INTERVAL_MS: number;
|
|
44
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,eAAO,MAAM,YAAY,QAEW,CAAA;AAEpC;;GAEG;AACH,eAAO,MAAM,cAAc,QAAe,CAAA;AAE1C;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAAmC,CAAA;AAElE;;GAEG;AACH,eAAO,MAAM,eAAe,QAA2C,CAAA;AAEvE;;;GAGG;AACH,eAAO,MAAM,8BAA8B,MAAM,CAAA;AAEjD;;;GAGG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAA;AAE1C;;;;GAIG;AACH,eAAO,MAAM,eAAe,QAA2D,CAAA;AAEvF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,QAA2E,CAAA;AAExG;;;GAGG;AACH,eAAO,MAAM,cAAc,QAA6E,CAAA"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { findProjectRoot } from '@mcp-monorepo/shared';
|
|
3
|
+
const projectRoot = await findProjectRoot(import.meta.dirname);
|
|
4
|
+
/**
|
|
5
|
+
* The root directory for all application-specific data, located in the user's home directory.
|
|
6
|
+
*/
|
|
7
|
+
export const APP_DATA_DIR = process.env.NOTION_QUERY_APP_DATA_DIR
|
|
8
|
+
? resolve(process.env.NOTION_QUERY_APP_DATA_DIR)
|
|
9
|
+
: resolve(projectRoot, 'rag-data');
|
|
10
|
+
/**
|
|
11
|
+
* The directory where all data related to the Notion RAG index is stored.
|
|
12
|
+
*/
|
|
13
|
+
export const NOTION_RAG_DIR = APP_DATA_DIR;
|
|
14
|
+
/**
|
|
15
|
+
* The directory where downloaded and converted Notion pages (as Markdown) are stored.
|
|
16
|
+
* This is the folder that the LocalRAG instance will watch.
|
|
17
|
+
*/
|
|
18
|
+
export const NOTION_CONTENT_DIR = resolve(APP_DATA_DIR, 'content');
|
|
19
|
+
/**
|
|
20
|
+
* The full path to the JSON file that stores the synchronization state.
|
|
21
|
+
*/
|
|
22
|
+
export const SYNC_STATE_PATH = resolve(APP_DATA_DIR, 'sync-state.json');
|
|
23
|
+
/**
|
|
24
|
+
* The delay in milliseconds between API calls enforced by the NotionApiThrottler.
|
|
25
|
+
* The Notion API has a limit of approximately 3 requests per second. 350ms provides a safe buffer.
|
|
26
|
+
*/
|
|
27
|
+
export const NOTION_API_RATE_LIMIT_DELAY_MS = 350;
|
|
28
|
+
/**
|
|
29
|
+
* The number of pages to fetch in a single paginated API request.
|
|
30
|
+
* The maximum allowed by the Notion API is 100.
|
|
31
|
+
*/
|
|
32
|
+
export const NOTION_PAGINATION_LIMIT = 100;
|
|
33
|
+
/**
|
|
34
|
+
* The total number of pages to add to the processing queue during each sync cycle.
|
|
35
|
+
* This acts as a cap to ensure each sync loop is reasonably short.
|
|
36
|
+
* @default 30
|
|
37
|
+
*/
|
|
38
|
+
export const SYNC_BATCH_SIZE = parseInt(process.env.NOTION_SYNC_BATCH_SIZE ?? '30', 10);
|
|
39
|
+
/**
|
|
40
|
+
* The interval in milliseconds that the main synchronization loop waits after completing.
|
|
41
|
+
* @default 15 minutes
|
|
42
|
+
*/
|
|
43
|
+
export const SYNC_INTERVAL_MS = parseInt(process.env.NOTION_SYNC_INTERVAL_MS ?? `${15 * 60 * 1000}`, 10);
|
|
44
|
+
/**
|
|
45
|
+
* The interval for the local-only garbage collection task, which removes orphaned files.
|
|
46
|
+
* @default 12 hours
|
|
47
|
+
*/
|
|
48
|
+
export const GC_INTERVAL_MS = parseInt(process.env.NOTION_GC_INTERVAL_MS ?? `${6 * 60 * 60 * 1000}`, 10);
|
|
49
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;AAE9D;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,yBAAyB;IAC/D,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC;IAChD,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAA;AAEpC;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,YAAY,CAAA;AAE1C;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;AAElE;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAA;AAEvE;;;GAGG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAA;AAEjD;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,CAAA;AAE1C;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,IAAI,EAAE,EAAE,CAAC,CAAA;AAEvF;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,CAAA;AAExG;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts a Notion page, database, or block ID from a URL or a raw ID string.
|
|
3
|
+
* Normalizes the ID by removing dashes.
|
|
4
|
+
* @param idOrUrl - The Notion URL or ID string.
|
|
5
|
+
* @returns The normalized 32-character ID, or undefined if no valid ID is found.
|
|
6
|
+
*/
|
|
7
|
+
export declare function normalizeId(idOrUrl: string): string | undefined;
|
|
8
|
+
//# sourceMappingURL=id-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id-utils.d.ts","sourceRoot":"","sources":["../../src/lib/id-utils.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO/D"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A regex to extract a UUID v4 from a string.
|
|
3
|
+
* It matches UUIDs with or without dashes.
|
|
4
|
+
*/
|
|
5
|
+
const UUID_REGEX = /([0-9a-f]{8})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{12})/;
|
|
6
|
+
/**
|
|
7
|
+
* Extracts a Notion page, database, or block ID from a URL or a raw ID string.
|
|
8
|
+
* Normalizes the ID by removing dashes.
|
|
9
|
+
* @param idOrUrl - The Notion URL or ID string.
|
|
10
|
+
* @returns The normalized 32-character ID, or undefined if no valid ID is found.
|
|
11
|
+
*/
|
|
12
|
+
export function normalizeId(idOrUrl) {
|
|
13
|
+
const match = idOrUrl.match(UUID_REGEX);
|
|
14
|
+
if (!match) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
// Reconstruct the ID without dashes
|
|
18
|
+
return match.slice(1).join('');
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=id-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id-utils.js","sourceRoot":"","sources":["../../src/lib/id-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,GAAG,4EAA4E,CAAA;AAE/F;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IACvC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,oCAAoC;IACpC,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAChC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type BlockObjectResponse } from '@notionhq/client/build/src/api-endpoints.js';
|
|
2
|
+
/**
|
|
3
|
+
* Converts a list of Notion blocks to a Notion-flavored Markdown string [9].
|
|
4
|
+
* @param blocks - The array of block objects.
|
|
5
|
+
* @returns A promise that resolves to the full Markdown string.
|
|
6
|
+
*/
|
|
7
|
+
export declare function notionToMarkdown(blocks: BlockObjectResponse[]): Promise<string>;
|
|
8
|
+
//# sourceMappingURL=markdown-converter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-converter.d.ts","sourceRoot":"","sources":["../../src/lib/markdown-converter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,mBAAmB,EAGzB,MAAM,6CAA6C,CAAA;AAqGpD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAOrF"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { NotionToMarkdown } from 'notion-to-md';
|
|
2
|
+
import { getNotionClient } from './client.js';
|
|
3
|
+
let n2m;
|
|
4
|
+
/**
|
|
5
|
+
* Renders an array of rich text items into a Notion-flavored Markdown string.
|
|
6
|
+
* Supports bold, italic, strikethrough, underline, code, and links [9].
|
|
7
|
+
* @param richTextItems - The array of rich text items.
|
|
8
|
+
* @returns A formatted markdown string.
|
|
9
|
+
*/
|
|
10
|
+
function renderRichText(richTextItems) {
|
|
11
|
+
return richTextItems
|
|
12
|
+
.map((item) => {
|
|
13
|
+
let text = item.plain_text;
|
|
14
|
+
if (item.href) {
|
|
15
|
+
// Handle citations [^URL] and regular links [text](URL)
|
|
16
|
+
if (text === item.href) {
|
|
17
|
+
return `[^${item.href}]`;
|
|
18
|
+
}
|
|
19
|
+
return `[${text}](${item.href})`;
|
|
20
|
+
}
|
|
21
|
+
// Handle annotations [6]
|
|
22
|
+
if (item.annotations.code)
|
|
23
|
+
text = `\`${text}\``;
|
|
24
|
+
if (item.annotations.bold)
|
|
25
|
+
text = `**${text}**`;
|
|
26
|
+
if (item.annotations.italic)
|
|
27
|
+
text = `*${text}*`;
|
|
28
|
+
if (item.annotations.strikethrough)
|
|
29
|
+
text = `~~${text}~~`;
|
|
30
|
+
if (item.annotations.underline)
|
|
31
|
+
text = `<span underline="true">${text}</span>`;
|
|
32
|
+
return text;
|
|
33
|
+
})
|
|
34
|
+
.join('');
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Gets a pre-configured singleton instance of NotionToMarkdown.
|
|
38
|
+
* Sets up custom transformers for Notion-flavored Markdown [9].
|
|
39
|
+
* @returns An instance of NotionToMarkdown.
|
|
40
|
+
*/
|
|
41
|
+
function getMarkdownConverter() {
|
|
42
|
+
if (n2m) {
|
|
43
|
+
return n2m;
|
|
44
|
+
}
|
|
45
|
+
const notionClient = getNotionClient();
|
|
46
|
+
n2m = new NotionToMarkdown({ notionClient });
|
|
47
|
+
// A generic transformer for simple blocks with rich_text and color properties [1].
|
|
48
|
+
const createColorBlockTransformer = (prefix = '') => async (block) => {
|
|
49
|
+
const blockContent = 'type' in block &&
|
|
50
|
+
block[block.type];
|
|
51
|
+
if (!blockContent) {
|
|
52
|
+
return '';
|
|
53
|
+
}
|
|
54
|
+
const markdownContent = renderRichText(blockContent.rich_text);
|
|
55
|
+
let finalMarkdown = `${prefix}${markdownContent}`;
|
|
56
|
+
if (blockContent.color && blockContent.color !== 'default') {
|
|
57
|
+
finalMarkdown += ` {color="${blockContent.color}"}`;
|
|
58
|
+
}
|
|
59
|
+
return finalMarkdown;
|
|
60
|
+
};
|
|
61
|
+
// Register transformers for block types that support color [1, 9].
|
|
62
|
+
n2m.setCustomTransformer('paragraph', createColorBlockTransformer());
|
|
63
|
+
n2m.setCustomTransformer('heading_1', createColorBlockTransformer('# '));
|
|
64
|
+
n2m.setCustomTransformer('heading_2', createColorBlockTransformer('## '));
|
|
65
|
+
n2m.setCustomTransformer('heading_3', createColorBlockTransformer('### '));
|
|
66
|
+
n2m.setCustomTransformer('bulleted_list_item', createColorBlockTransformer('- '));
|
|
67
|
+
n2m.setCustomTransformer('numbered_list_item', createColorBlockTransformer('1. '));
|
|
68
|
+
n2m.setCustomTransformer('quote', createColorBlockTransformer('> '));
|
|
69
|
+
// Custom transformer for to-do blocks to handle the checked state [9].
|
|
70
|
+
n2m.setCustomTransformer('to_do', async (block) => {
|
|
71
|
+
const todoBlock = block;
|
|
72
|
+
const prefix = todoBlock.to_do.checked ? '- [x] ' : '- [ ] ';
|
|
73
|
+
const markdownContent = renderRichText(todoBlock.to_do.rich_text);
|
|
74
|
+
let finalMarkdown = `${prefix}${markdownContent}`;
|
|
75
|
+
if (todoBlock.to_do.color && todoBlock.to_do.color !== 'default') {
|
|
76
|
+
finalMarkdown += ` {color="${todoBlock.to_do.color}"}`;
|
|
77
|
+
}
|
|
78
|
+
return finalMarkdown;
|
|
79
|
+
});
|
|
80
|
+
return n2m;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Converts a list of Notion blocks to a Notion-flavored Markdown string [9].
|
|
84
|
+
* @param blocks - The array of block objects.
|
|
85
|
+
* @returns A promise that resolves to the full Markdown string.
|
|
86
|
+
*/
|
|
87
|
+
export async function notionToMarkdown(blocks) {
|
|
88
|
+
const converter = getMarkdownConverter();
|
|
89
|
+
// The 'blocks' parameter expects the 'results' from a 'listBlockChildren' call, which matches BlockObjectResponse[]
|
|
90
|
+
const mdBlocks = await converter.blocksToMarkdown(blocks);
|
|
91
|
+
const mdString = converter.toMarkdownString(mdBlocks);
|
|
92
|
+
// `toMarkdownString` returns an object where `parent` holds the main content [7]
|
|
93
|
+
return mdString.parent ?? '';
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=markdown-converter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-converter.js","sourceRoot":"","sources":["../../src/lib/markdown-converter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type SyncState } from './sync-state-manager.js';
|
|
2
|
+
/**
|
|
3
|
+
* Manages the continuous, queue-based synchronization of a Notion workspace
|
|
4
|
+
* to a local RAG-compatible directory using a throttled, robust process.
|
|
5
|
+
*/
|
|
6
|
+
export declare class NotionSyncer {
|
|
7
|
+
private readonly notion;
|
|
8
|
+
private readonly throttler;
|
|
9
|
+
private state;
|
|
10
|
+
private syncTimeout;
|
|
11
|
+
private gcTimeout;
|
|
12
|
+
private isSyncing;
|
|
13
|
+
constructor();
|
|
14
|
+
start(): Promise<void>;
|
|
15
|
+
stop(): void;
|
|
16
|
+
getSyncState(): SyncState | undefined;
|
|
17
|
+
triggerImmediateSync(pageId: string): Promise<void>;
|
|
18
|
+
private scheduleNextSync;
|
|
19
|
+
private scheduleNextGc;
|
|
20
|
+
private _runSyncLoop;
|
|
21
|
+
private _buildProcessingQueue;
|
|
22
|
+
private _processQueue;
|
|
23
|
+
private _processPage;
|
|
24
|
+
private _garbageCollect;
|
|
25
|
+
private _deletePageData;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=notion-syncer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notion-syncer.d.ts","sourceRoot":"","sources":["../../src/lib/notion-syncer.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAItF;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,SAAS,CAAQ;;IAOZ,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B,IAAI,IAAI,IAAI;IAMZ,YAAY,IAAI,SAAS,GAAG,SAAS;IAI/B,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAehE,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,cAAc;YAUR,YAAY;YAeZ,qBAAqB;YAgDrB,aAAa;YAoBb,YAAY;YA8BZ,eAAe;YAuBf,eAAe;CAM9B"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { rm, readdir, mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { logger } from '@mcp-monorepo/shared';
|
|
4
|
+
import { ThrottledExecutor } from '@mcp-monorepo/shared';
|
|
5
|
+
import { isFullPage, APIErrorCode, isFullBlock } from '@notionhq/client';
|
|
6
|
+
import { getNotionClient } from './client.js';
|
|
7
|
+
import { GC_INTERVAL_MS, NOTION_API_RATE_LIMIT_DELAY_MS, NOTION_CONTENT_DIR, NOTION_PAGINATION_LIMIT, SYNC_BATCH_SIZE, SYNC_INTERVAL_MS, } from './config.js';
|
|
8
|
+
import { normalizeId } from './id-utils.js';
|
|
9
|
+
import { notionToMarkdown } from './markdown-converter.js';
|
|
10
|
+
import { loadSyncState, saveSyncState } from './sync-state-manager.js';
|
|
11
|
+
const NOTION_URL_REGEX = /https:\/\/www.notion.so\/[a-zA-Z0-9-]+-([a-zA-Z0-9]{32})/g;
|
|
12
|
+
/**
|
|
13
|
+
* Manages the continuous, queue-based synchronization of a Notion workspace
|
|
14
|
+
* to a local RAG-compatible directory using a throttled, robust process.
|
|
15
|
+
*/
|
|
16
|
+
export class NotionSyncer {
|
|
17
|
+
notion;
|
|
18
|
+
throttler;
|
|
19
|
+
state;
|
|
20
|
+
syncTimeout;
|
|
21
|
+
gcTimeout;
|
|
22
|
+
isSyncing = false;
|
|
23
|
+
constructor() {
|
|
24
|
+
this.notion = getNotionClient();
|
|
25
|
+
this.throttler = new ThrottledExecutor(NOTION_API_RATE_LIMIT_DELAY_MS);
|
|
26
|
+
}
|
|
27
|
+
async start() {
|
|
28
|
+
await mkdir(NOTION_CONTENT_DIR, { recursive: true });
|
|
29
|
+
this.state = await loadSyncState();
|
|
30
|
+
logger.info('NotionSyncer started. Scheduling initial sync and GC.');
|
|
31
|
+
this.scheduleNextSync(1000); // Start first sync almost immediately
|
|
32
|
+
this.scheduleNextGc(GC_INTERVAL_MS);
|
|
33
|
+
}
|
|
34
|
+
stop() {
|
|
35
|
+
if (this.syncTimeout)
|
|
36
|
+
clearTimeout(this.syncTimeout);
|
|
37
|
+
if (this.gcTimeout)
|
|
38
|
+
clearTimeout(this.gcTimeout);
|
|
39
|
+
logger.info('NotionSyncer stopped.');
|
|
40
|
+
}
|
|
41
|
+
getSyncState() {
|
|
42
|
+
return this.state;
|
|
43
|
+
}
|
|
44
|
+
async triggerImmediateSync(pageId) {
|
|
45
|
+
const normalizedId = normalizeId(pageId);
|
|
46
|
+
if (!normalizedId || !this.state)
|
|
47
|
+
return;
|
|
48
|
+
logger.info(`Received immediate sync trigger for page: ${normalizedId}`);
|
|
49
|
+
const existingPage = this.state.pages[normalizedId];
|
|
50
|
+
this.state.pages[normalizedId] = {
|
|
51
|
+
...(existingPage ?? { title: undefined, url: undefined }),
|
|
52
|
+
lastEdited: undefined, // Mark as needing processing
|
|
53
|
+
};
|
|
54
|
+
await saveSyncState(this.state);
|
|
55
|
+
}
|
|
56
|
+
scheduleNextSync(delay) {
|
|
57
|
+
if (this.syncTimeout)
|
|
58
|
+
clearTimeout(this.syncTimeout);
|
|
59
|
+
this.syncTimeout = setTimeout(() => {
|
|
60
|
+
this._runSyncLoop().catch((error) => {
|
|
61
|
+
logger.error('Fatal error in sync loop. Rescheduling.', error);
|
|
62
|
+
this.isSyncing = false;
|
|
63
|
+
this.scheduleNextSync(SYNC_INTERVAL_MS);
|
|
64
|
+
});
|
|
65
|
+
}, delay);
|
|
66
|
+
}
|
|
67
|
+
scheduleNextGc(delay) {
|
|
68
|
+
if (this.gcTimeout)
|
|
69
|
+
clearTimeout(this.gcTimeout);
|
|
70
|
+
this.gcTimeout = setTimeout(() => {
|
|
71
|
+
this._garbageCollect().catch((error) => {
|
|
72
|
+
logger.error('Error during garbage collection. Rescheduling.', error);
|
|
73
|
+
this.scheduleNextGc(GC_INTERVAL_MS);
|
|
74
|
+
});
|
|
75
|
+
}, delay);
|
|
76
|
+
}
|
|
77
|
+
async _runSyncLoop() {
|
|
78
|
+
if (this.isSyncing)
|
|
79
|
+
return;
|
|
80
|
+
this.isSyncing = true;
|
|
81
|
+
logger.info('Starting sync loop...');
|
|
82
|
+
try {
|
|
83
|
+
const processingQueue = await this._buildProcessingQueue();
|
|
84
|
+
await this._processQueue(processingQueue);
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
this.isSyncing = false;
|
|
88
|
+
logger.info('Sync loop finished.');
|
|
89
|
+
this.scheduleNextSync(SYNC_INTERVAL_MS);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async _buildProcessingQueue() {
|
|
93
|
+
if (!this.state)
|
|
94
|
+
return [];
|
|
95
|
+
const queue = new Set();
|
|
96
|
+
// Priority 1: Pages discovered but never processed
|
|
97
|
+
for (const [pageId, pageInfo] of Object.entries(this.state.pages)) {
|
|
98
|
+
if (pageInfo.lastEdited === undefined) {
|
|
99
|
+
queue.add(pageId);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
logger.info(`Found ${queue.size} unprocessed (discovered) pages.`);
|
|
103
|
+
if (queue.size >= SYNC_BATCH_SIZE) {
|
|
104
|
+
return Array.from(queue).slice(0, SYNC_BATCH_SIZE);
|
|
105
|
+
}
|
|
106
|
+
// Priority 2: Find recently changed pages via search API
|
|
107
|
+
let hasMore = true;
|
|
108
|
+
let nextCursor;
|
|
109
|
+
while (queue.size < SYNC_BATCH_SIZE && hasMore) {
|
|
110
|
+
const response = await this.throttler.execute(() => this.notion.search({
|
|
111
|
+
sort: { direction: 'descending', timestamp: 'last_edited_time' },
|
|
112
|
+
page_size: NOTION_PAGINATION_LIMIT,
|
|
113
|
+
start_cursor: nextCursor,
|
|
114
|
+
}));
|
|
115
|
+
for (const page of response.results) {
|
|
116
|
+
if (queue.size >= SYNC_BATCH_SIZE)
|
|
117
|
+
break;
|
|
118
|
+
if (isFullPage(page)) {
|
|
119
|
+
const existingPage = this.state.pages[page.id];
|
|
120
|
+
if (!existingPage) {
|
|
121
|
+
this.state.pages[page.id] = { lastEdited: undefined, title: undefined, url: undefined };
|
|
122
|
+
queue.add(page.id);
|
|
123
|
+
}
|
|
124
|
+
else if (existingPage.lastEdited !== page.last_edited_time) {
|
|
125
|
+
queue.add(page.id);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
hasMore = response.has_more;
|
|
130
|
+
nextCursor = response.next_cursor ?? undefined;
|
|
131
|
+
}
|
|
132
|
+
await saveSyncState(this.state);
|
|
133
|
+
return Array.from(queue);
|
|
134
|
+
}
|
|
135
|
+
async _processQueue(queue) {
|
|
136
|
+
if (queue.length === 0)
|
|
137
|
+
return;
|
|
138
|
+
if (!this.state)
|
|
139
|
+
throw new Error('Sync state not initialized.');
|
|
140
|
+
logger.info(`Sync state contains ${Object.keys(this.state.pages).length} pages. ${queue.length} will be processed.`);
|
|
141
|
+
for (const pageId of queue) {
|
|
142
|
+
try {
|
|
143
|
+
await this._processPage(pageId);
|
|
144
|
+
await saveSyncState(this.state);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (typeof error === 'object' && error && 'code' in error && error.code === APIErrorCode.ObjectNotFound) {
|
|
148
|
+
logger.info(`Page ${pageId} not found on Notion during processing. Deleting.`);
|
|
149
|
+
await this._deletePageData(pageId);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
logger.error(`Failed to process page ${pageId}. It will be retried in a future cycle.`, error);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async _processPage(pageId) {
|
|
158
|
+
if (!this.state)
|
|
159
|
+
throw new Error('Sync state not initialized.');
|
|
160
|
+
const page = await this.throttler.execute(() => this.notion.pages.retrieve({ page_id: pageId }));
|
|
161
|
+
if (!isFullPage(page))
|
|
162
|
+
return;
|
|
163
|
+
const blocks = await this.throttler.execute(() => this.notion.blocks.children.list({ block_id: page.id }));
|
|
164
|
+
const markdownContent = await notionToMarkdown(blocks.results.filter(isFullBlock));
|
|
165
|
+
const titleProperty = Object.values(page.properties).find((prop) => prop.type === 'title');
|
|
166
|
+
const title = titleProperty?.type === 'title' && titleProperty.title[0]?.plain_text
|
|
167
|
+
? titleProperty.title[0].plain_text
|
|
168
|
+
: 'Untitled';
|
|
169
|
+
await writeFile(resolve(NOTION_CONTENT_DIR, `${page.id}.md`), `# ${title}\n\n${markdownContent}`, 'utf-8');
|
|
170
|
+
this.state.pages[page.id] = { lastEdited: page.last_edited_time, title, url: page.url };
|
|
171
|
+
const blockString = JSON.stringify(blocks.results);
|
|
172
|
+
const matches = blockString.matchAll(NOTION_URL_REGEX);
|
|
173
|
+
for (const match of matches) {
|
|
174
|
+
const childId = normalizeId(match[1]);
|
|
175
|
+
if (childId && !this.state.pages[childId]) {
|
|
176
|
+
logger.info(`Discovered new child page ${childId}. It will be processed in a future cycle.`);
|
|
177
|
+
this.state.pages[childId] = { lastEdited: undefined, title: undefined, url: undefined };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async _garbageCollect() {
|
|
182
|
+
if (!this.state)
|
|
183
|
+
return;
|
|
184
|
+
logger.info('Running local file garbage collection...');
|
|
185
|
+
let stateWasChanged = false;
|
|
186
|
+
try {
|
|
187
|
+
const localFileIds = (await readdir(NOTION_CONTENT_DIR)).map((name) => name.replace('.md', ''));
|
|
188
|
+
for (const fileId of localFileIds) {
|
|
189
|
+
if (!this.state.pages[fileId]) {
|
|
190
|
+
logger.info(`GC: Deleting orphaned local file: ${fileId}.md`);
|
|
191
|
+
await rm(resolve(NOTION_CONTENT_DIR, `${fileId}.md`));
|
|
192
|
+
stateWasChanged = true;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
logger.error('Error during local file garbage collection.', error);
|
|
198
|
+
}
|
|
199
|
+
if (stateWasChanged) {
|
|
200
|
+
await saveSyncState(this.state);
|
|
201
|
+
}
|
|
202
|
+
this.scheduleNextGc(GC_INTERVAL_MS);
|
|
203
|
+
}
|
|
204
|
+
async _deletePageData(pageId) {
|
|
205
|
+
if (!this.state)
|
|
206
|
+
return;
|
|
207
|
+
delete this.state.pages[pageId];
|
|
208
|
+
await rm(resolve(NOTION_CONTENT_DIR, `${pageId}.md`), { force: true });
|
|
209
|
+
await saveSyncState(this.state);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=notion-syncer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notion-syncer.js","sourceRoot":"","sources":["../../src/lib/notion-syncer.ts"],"names":[],"mappings":"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"}
|