@catafal/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.
- package/LICENSE +21 -0
- package/README.md +552 -0
- package/bin/dev +17 -0
- package/bin/dev.cmd +3 -0
- package/bin/run +14 -0
- package/bin/run.cmd +3 -0
- package/dist/base-command.d.ts +73 -0
- package/dist/base-command.js +179 -0
- package/dist/base-flags.d.ts +14 -0
- package/dist/base-flags.js +59 -0
- package/dist/cache.d.ts +84 -0
- package/dist/cache.js +351 -0
- package/dist/commands/append.d.ts +37 -0
- package/dist/commands/append.js +120 -0
- package/dist/commands/batch/delete.d.ts +42 -0
- package/dist/commands/batch/delete.js +199 -0
- package/dist/commands/batch/retrieve.d.ts +43 -0
- package/dist/commands/batch/retrieve.js +272 -0
- package/dist/commands/block/append.d.ts +42 -0
- package/dist/commands/block/append.js +219 -0
- package/dist/commands/block/delete.d.ts +30 -0
- package/dist/commands/block/delete.js +97 -0
- package/dist/commands/block/retrieve/children.d.ts +31 -0
- package/dist/commands/block/retrieve/children.js +177 -0
- package/dist/commands/block/retrieve.d.ts +30 -0
- package/dist/commands/block/retrieve.js +101 -0
- package/dist/commands/block/update.d.ts +45 -0
- package/dist/commands/block/update.js +242 -0
- package/dist/commands/bookmark/list.d.ts +30 -0
- package/dist/commands/bookmark/list.js +60 -0
- package/dist/commands/bookmark/remove.d.ts +26 -0
- package/dist/commands/bookmark/remove.js +47 -0
- package/dist/commands/bookmark/set.d.ts +29 -0
- package/dist/commands/bookmark/set.js +96 -0
- package/dist/commands/browse.d.ts +13 -0
- package/dist/commands/browse.js +44 -0
- package/dist/commands/cache/info.d.ts +19 -0
- package/dist/commands/cache/info.js +145 -0
- package/dist/commands/config/set-token.d.ts +22 -0
- package/dist/commands/config/set-token.js +137 -0
- package/dist/commands/daily/index.d.ts +32 -0
- package/dist/commands/daily/index.js +135 -0
- package/dist/commands/daily/setup.d.ts +42 -0
- package/dist/commands/daily/setup.js +149 -0
- package/dist/commands/db/create.d.ts +31 -0
- package/dist/commands/db/create.js +124 -0
- package/dist/commands/db/query.d.ts +41 -0
- package/dist/commands/db/query.js +360 -0
- package/dist/commands/db/retrieve.d.ts +33 -0
- package/dist/commands/db/retrieve.js +134 -0
- package/dist/commands/db/schema.d.ts +32 -0
- package/dist/commands/db/schema.js +308 -0
- package/dist/commands/db/update.d.ts +31 -0
- package/dist/commands/db/update.js +117 -0
- package/dist/commands/doctor.d.ts +50 -0
- package/dist/commands/doctor.js +420 -0
- package/dist/commands/init.d.ts +65 -0
- package/dist/commands/init.js +479 -0
- package/dist/commands/list.d.ts +29 -0
- package/dist/commands/list.js +219 -0
- package/dist/commands/open.d.ts +29 -0
- package/dist/commands/open.js +100 -0
- package/dist/commands/page/create.d.ts +33 -0
- package/dist/commands/page/create.js +261 -0
- package/dist/commands/page/delete.d.ts +36 -0
- package/dist/commands/page/delete.js +107 -0
- package/dist/commands/page/export.d.ts +38 -0
- package/dist/commands/page/export.js +120 -0
- package/dist/commands/page/retrieve/property_item.d.ts +24 -0
- package/dist/commands/page/retrieve/property_item.js +75 -0
- package/dist/commands/page/retrieve.d.ts +36 -0
- package/dist/commands/page/retrieve.js +244 -0
- package/dist/commands/page/update.d.ts +34 -0
- package/dist/commands/page/update.js +184 -0
- package/dist/commands/quick.d.ts +35 -0
- package/dist/commands/quick.js +168 -0
- package/dist/commands/search.d.ts +43 -0
- package/dist/commands/search.js +361 -0
- package/dist/commands/stats.d.ts +35 -0
- package/dist/commands/stats.js +274 -0
- package/dist/commands/sync.d.ts +24 -0
- package/dist/commands/sync.js +183 -0
- package/dist/commands/template/get.d.ts +28 -0
- package/dist/commands/template/get.js +59 -0
- package/dist/commands/template/list.d.ts +32 -0
- package/dist/commands/template/list.js +62 -0
- package/dist/commands/template/remove.d.ts +27 -0
- package/dist/commands/template/remove.js +48 -0
- package/dist/commands/template/save.d.ts +32 -0
- package/dist/commands/template/save.js +92 -0
- package/dist/commands/template/use.d.ts +34 -0
- package/dist/commands/template/use.js +142 -0
- package/dist/commands/user/list.d.ts +27 -0
- package/dist/commands/user/list.js +99 -0
- package/dist/commands/user/retrieve/bot.d.ts +28 -0
- package/dist/commands/user/retrieve/bot.js +96 -0
- package/dist/commands/user/retrieve.d.ts +30 -0
- package/dist/commands/user/retrieve.js +103 -0
- package/dist/commands/whoami.d.ts +19 -0
- package/dist/commands/whoami.js +175 -0
- package/dist/deduplication.d.ts +41 -0
- package/dist/deduplication.js +71 -0
- package/dist/envelope.d.ts +169 -0
- package/dist/envelope.js +257 -0
- package/dist/errors/enhanced-errors.d.ts +168 -0
- package/dist/errors/enhanced-errors.js +567 -0
- package/dist/errors/index.d.ts +18 -0
- package/dist/errors/index.js +33 -0
- package/dist/examples/cache-retry-examples.d.ts +64 -0
- package/dist/examples/cache-retry-examples.js +375 -0
- package/dist/helper.d.ts +102 -0
- package/dist/helper.js +885 -0
- package/dist/http-agent.d.ts +38 -0
- package/dist/http-agent.js +60 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -0
- package/dist/interface.d.ts +4 -0
- package/dist/interface.js +2 -0
- package/dist/notion.d.ts +144 -0
- package/dist/notion.js +547 -0
- package/dist/retry.d.ts +72 -0
- package/dist/retry.js +381 -0
- package/dist/utils/bookmarks.d.ts +32 -0
- package/dist/utils/bookmarks.js +98 -0
- package/dist/utils/daily-config.d.ts +22 -0
- package/dist/utils/daily-config.js +60 -0
- package/dist/utils/disk-cache.d.ts +80 -0
- package/dist/utils/disk-cache.js +291 -0
- package/dist/utils/fuzzy.d.ts +36 -0
- package/dist/utils/fuzzy.js +69 -0
- package/dist/utils/interactive-navigator.d.ts +63 -0
- package/dist/utils/interactive-navigator.js +123 -0
- package/dist/utils/markdown-to-blocks.d.ts +21 -0
- package/dist/utils/markdown-to-blocks.js +333 -0
- package/dist/utils/notion-resolver.d.ts +49 -0
- package/dist/utils/notion-resolver.js +278 -0
- package/dist/utils/notion-url-parser.d.ts +48 -0
- package/dist/utils/notion-url-parser.js +121 -0
- package/dist/utils/property-expander.d.ts +45 -0
- package/dist/utils/property-expander.js +323 -0
- package/dist/utils/schema-examples.d.ts +40 -0
- package/dist/utils/schema-examples.js +359 -0
- package/dist/utils/schema-extractor.d.ts +65 -0
- package/dist/utils/schema-extractor.js +235 -0
- package/dist/utils/shell-config.d.ts +30 -0
- package/dist/utils/shell-config.js +84 -0
- package/dist/utils/table-formatter.d.ts +36 -0
- package/dist/utils/table-formatter.js +125 -0
- package/dist/utils/templates.d.ts +30 -0
- package/dist/utils/templates.js +82 -0
- package/dist/utils/terminal-banner.d.ts +24 -0
- package/dist/utils/terminal-banner.js +34 -0
- package/dist/utils/token-validator.d.ts +42 -0
- package/dist/utils/token-validator.js +66 -0
- package/dist/utils/update-notifier.d.ts +26 -0
- package/dist/utils/update-notifier.js +54 -0
- package/dist/utils/workspace-cache.d.ts +58 -0
- package/dist/utils/workspace-cache.js +185 -0
- package/oclif.manifest.json +6471 -0
- package/package.json +118 -0
- package/scripts/banner.js +38 -0
- package/scripts/postinstall.js +44 -0
package/dist/notion.js
ADDED
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mapPageStructure = exports.retrievePageRecursive = exports.CircuitBreaker = exports.enhancedFetchWithRetry = exports.cacheManager = exports.search = exports.searchDb = exports.botUser = exports.listUser = exports.retrieveUser = exports.deleteBlock = exports.appendBlockChildren = exports.retrieveBlockChildren = exports.updateBlock = exports.retrieveBlock = exports.updatePage = exports.updatePageProps = exports.createPage = exports.retrievePageProperty = exports.retrievePage = exports.updateDataSource = exports.retrieveDataSource = exports.retrieveDb = exports.updateDb = exports.createDb = exports.fetchAllPagesInDS = exports.fetchWithRetry = exports.BATCH_CONFIG = exports.client = void 0;
|
|
4
|
+
const client_1 = require("@notionhq/client");
|
|
5
|
+
const cache_1 = require("./cache");
|
|
6
|
+
const retry_1 = require("./retry");
|
|
7
|
+
const deduplication_1 = require("./deduplication");
|
|
8
|
+
const http_agent_1 = require("./http-agent");
|
|
9
|
+
/**
|
|
10
|
+
* Custom fetch function that uses our configured HTTPS agent and compression
|
|
11
|
+
*/
|
|
12
|
+
function createFetchWithAgent() {
|
|
13
|
+
return async (input, init) => {
|
|
14
|
+
// Merge headers with compression support
|
|
15
|
+
const headers = new Headers((init === null || init === void 0 ? void 0 : init.headers) || {});
|
|
16
|
+
// Add compression headers if not already present
|
|
17
|
+
if (!headers.has('Accept-Encoding')) {
|
|
18
|
+
// Request gzip, deflate, and brotli compression
|
|
19
|
+
headers.set('Accept-Encoding', 'gzip, deflate, br');
|
|
20
|
+
}
|
|
21
|
+
// Call native fetch with dispatcher (undici agent) and enhanced headers
|
|
22
|
+
return fetch(input, {
|
|
23
|
+
...init,
|
|
24
|
+
headers,
|
|
25
|
+
// @ts-expect-error - dispatcher is supported but not in @types/node yet
|
|
26
|
+
dispatcher: http_agent_1.httpsAgent,
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
exports.client = new client_1.Client({
|
|
31
|
+
auth: process.env.NOTION_TOKEN,
|
|
32
|
+
logLevel: process.env.DEBUG ? client_1.LogLevel.DEBUG : null,
|
|
33
|
+
// Note: The @notionhq/client library uses its own HTTP client
|
|
34
|
+
// We configure the agent globally for Node.js HTTP(S) requests
|
|
35
|
+
fetch: createFetchWithAgent(),
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Configuration for batch operations
|
|
39
|
+
*/
|
|
40
|
+
exports.BATCH_CONFIG = {
|
|
41
|
+
deleteConcurrency: parseInt(process.env.NOTION_CLI_DELETE_CONCURRENCY || '5', 10),
|
|
42
|
+
childrenConcurrency: parseInt(process.env.NOTION_CLI_CHILDREN_CONCURRENCY || '10', 10),
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Legacy fetchWithRetry for backward compatibility
|
|
46
|
+
* @deprecated Use the enhanced retry logic from retry.ts
|
|
47
|
+
*/
|
|
48
|
+
const fetchWithRetry = async (fn, retries = 3) => {
|
|
49
|
+
return (0, retry_1.fetchWithRetry)(fn, {
|
|
50
|
+
config: { maxRetries: retries },
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
exports.fetchWithRetry = fetchWithRetry;
|
|
54
|
+
/**
|
|
55
|
+
* Cached wrapper for API calls with retry logic and deduplication
|
|
56
|
+
*/
|
|
57
|
+
async function cachedFetch(cacheType, cacheKey, fetchFn, options = {}) {
|
|
58
|
+
const { cacheTtl, skipCache = false, skipDedup = false, retryConfig } = options;
|
|
59
|
+
// Check cache first (unless skipped or cache disabled)
|
|
60
|
+
if (!skipCache) {
|
|
61
|
+
const cached = await cache_1.cacheManager.get(cacheType, cacheKey);
|
|
62
|
+
if (cached !== null) {
|
|
63
|
+
if (process.env.DEBUG) {
|
|
64
|
+
console.log(`Cache HIT: ${cacheType}:${cacheKey}`);
|
|
65
|
+
}
|
|
66
|
+
return cached;
|
|
67
|
+
}
|
|
68
|
+
if (process.env.DEBUG) {
|
|
69
|
+
console.log(`Cache MISS: ${cacheType}:${cacheKey}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Generate deduplication key
|
|
73
|
+
const dedupKey = `${cacheType}:${JSON.stringify(cacheKey)}`;
|
|
74
|
+
// Wrap fetch function with deduplication (unless disabled)
|
|
75
|
+
const dedupEnabled = process.env.NOTION_CLI_DEDUP_ENABLED !== 'false' && !skipDedup;
|
|
76
|
+
const fetchWithDedup = dedupEnabled
|
|
77
|
+
? () => deduplication_1.deduplicationManager.execute(dedupKey, async () => {
|
|
78
|
+
if (process.env.DEBUG) {
|
|
79
|
+
console.log(`Dedup MISS: ${dedupKey}`);
|
|
80
|
+
}
|
|
81
|
+
return (0, retry_1.fetchWithRetry)(fetchFn, {
|
|
82
|
+
config: retryConfig,
|
|
83
|
+
context: `${cacheType}:${cacheKey}`,
|
|
84
|
+
});
|
|
85
|
+
})
|
|
86
|
+
: () => (0, retry_1.fetchWithRetry)(fetchFn, {
|
|
87
|
+
config: retryConfig,
|
|
88
|
+
context: `${cacheType}:${cacheKey}`,
|
|
89
|
+
});
|
|
90
|
+
// Execute fetch (with or without deduplication)
|
|
91
|
+
const data = await fetchWithDedup();
|
|
92
|
+
// Store in cache
|
|
93
|
+
if (!skipCache) {
|
|
94
|
+
cache_1.cacheManager.set(cacheType, data, cacheTtl, cacheKey);
|
|
95
|
+
}
|
|
96
|
+
return data;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Fetch all pages in a data source with pagination
|
|
100
|
+
*/
|
|
101
|
+
const fetchAllPagesInDS = async (databaseId, filter) => {
|
|
102
|
+
const f = filter;
|
|
103
|
+
const pages = [];
|
|
104
|
+
let cursor = undefined;
|
|
105
|
+
while (true) {
|
|
106
|
+
const { results, next_cursor } = await (0, retry_1.fetchWithRetry)(() => exports.client.dataSources.query({
|
|
107
|
+
data_source_id: databaseId,
|
|
108
|
+
filter: f,
|
|
109
|
+
start_cursor: cursor,
|
|
110
|
+
}), { context: `fetchAllPagesInDS:${databaseId}` });
|
|
111
|
+
pages.push(...results);
|
|
112
|
+
if (!next_cursor) {
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
cursor = next_cursor;
|
|
116
|
+
}
|
|
117
|
+
return pages;
|
|
118
|
+
};
|
|
119
|
+
exports.fetchAllPagesInDS = fetchAllPagesInDS;
|
|
120
|
+
/**
|
|
121
|
+
* Create a database
|
|
122
|
+
*/
|
|
123
|
+
const createDb = async (dbProps) => {
|
|
124
|
+
const result = await (0, retry_1.fetchWithRetry)(() => exports.client.databases.create(dbProps), { context: 'createDb' });
|
|
125
|
+
// Invalidate database list cache
|
|
126
|
+
cache_1.cacheManager.invalidate('search');
|
|
127
|
+
return result;
|
|
128
|
+
};
|
|
129
|
+
exports.createDb = createDb;
|
|
130
|
+
/**
|
|
131
|
+
* Update a database
|
|
132
|
+
*/
|
|
133
|
+
const updateDb = async (dbProps) => {
|
|
134
|
+
const result = await (0, retry_1.fetchWithRetry)(() => exports.client.databases.update(dbProps), { context: `updateDb:${dbProps.database_id}` });
|
|
135
|
+
// Invalidate this database's cache
|
|
136
|
+
cache_1.cacheManager.invalidate('database', dbProps.database_id);
|
|
137
|
+
cache_1.cacheManager.invalidate('dataSource', dbProps.database_id);
|
|
138
|
+
return result;
|
|
139
|
+
};
|
|
140
|
+
exports.updateDb = updateDb;
|
|
141
|
+
/**
|
|
142
|
+
* Retrieve a database (cached)
|
|
143
|
+
*/
|
|
144
|
+
const retrieveDb = async (databaseId) => {
|
|
145
|
+
return cachedFetch('database', databaseId, () => exports.client.databases.retrieve({ database_id: databaseId }));
|
|
146
|
+
};
|
|
147
|
+
exports.retrieveDb = retrieveDb;
|
|
148
|
+
/**
|
|
149
|
+
* Retrieve a data source (cached)
|
|
150
|
+
*/
|
|
151
|
+
const retrieveDataSource = async (dataSourceId) => {
|
|
152
|
+
return cachedFetch('dataSource', dataSourceId, () => exports.client.dataSources.retrieve({ data_source_id: dataSourceId }));
|
|
153
|
+
};
|
|
154
|
+
exports.retrieveDataSource = retrieveDataSource;
|
|
155
|
+
/**
|
|
156
|
+
* Update a data source
|
|
157
|
+
*/
|
|
158
|
+
const updateDataSource = async (dsProps) => {
|
|
159
|
+
const result = await (0, retry_1.fetchWithRetry)(() => exports.client.dataSources.update(dsProps), { context: `updateDataSource:${dsProps.data_source_id}` });
|
|
160
|
+
// Invalidate this data source's cache
|
|
161
|
+
cache_1.cacheManager.invalidate('dataSource', dsProps.data_source_id);
|
|
162
|
+
return result;
|
|
163
|
+
};
|
|
164
|
+
exports.updateDataSource = updateDataSource;
|
|
165
|
+
/**
|
|
166
|
+
* Retrieve a page (cached with short TTL)
|
|
167
|
+
*/
|
|
168
|
+
const retrievePage = async (pageProp) => {
|
|
169
|
+
return cachedFetch('page', pageProp.page_id, () => exports.client.pages.retrieve(pageProp));
|
|
170
|
+
};
|
|
171
|
+
exports.retrievePage = retrievePage;
|
|
172
|
+
/**
|
|
173
|
+
* Retrieve page property
|
|
174
|
+
*/
|
|
175
|
+
const retrievePageProperty = async (pageId, propId) => {
|
|
176
|
+
return (0, retry_1.fetchWithRetry)(() => exports.client.pages.properties.retrieve({
|
|
177
|
+
page_id: pageId,
|
|
178
|
+
property_id: propId,
|
|
179
|
+
}), { context: `retrievePageProperty:${pageId}:${propId}` });
|
|
180
|
+
};
|
|
181
|
+
exports.retrievePageProperty = retrievePageProperty;
|
|
182
|
+
/**
|
|
183
|
+
* Create a page
|
|
184
|
+
*/
|
|
185
|
+
const createPage = async (pageProps) => {
|
|
186
|
+
const result = await (0, retry_1.fetchWithRetry)(() => exports.client.pages.create(pageProps), { context: 'createPage' });
|
|
187
|
+
// Invalidate parent database/page cache
|
|
188
|
+
if ('parent' in pageProps && 'database_id' in pageProps.parent) {
|
|
189
|
+
cache_1.cacheManager.invalidate('dataSource', pageProps.parent.database_id);
|
|
190
|
+
}
|
|
191
|
+
return result;
|
|
192
|
+
};
|
|
193
|
+
exports.createPage = createPage;
|
|
194
|
+
/**
|
|
195
|
+
* Update page properties
|
|
196
|
+
*/
|
|
197
|
+
const updatePageProps = async (pageParams) => {
|
|
198
|
+
const result = await (0, retry_1.fetchWithRetry)(() => exports.client.pages.update(pageParams), { context: `updatePageProps:${pageParams.page_id}` });
|
|
199
|
+
// Invalidate this page's cache
|
|
200
|
+
cache_1.cacheManager.invalidate('page', pageParams.page_id);
|
|
201
|
+
return result;
|
|
202
|
+
};
|
|
203
|
+
exports.updatePageProps = updatePageProps;
|
|
204
|
+
/**
|
|
205
|
+
* Update page content by replacing all blocks
|
|
206
|
+
* To keep the same page URL, remove all blocks in the page and add new blocks
|
|
207
|
+
*/
|
|
208
|
+
const updatePage = async (pageId, blocks) => {
|
|
209
|
+
// Get all blocks
|
|
210
|
+
const blks = await (0, retry_1.fetchWithRetry)(() => exports.client.blocks.children.list({ block_id: pageId }), { context: `updatePage:list:${pageId}` });
|
|
211
|
+
// Delete all blocks in parallel
|
|
212
|
+
if (blks.results.length > 0) {
|
|
213
|
+
const deleteResults = await (0, retry_1.batchWithRetry)(blks.results.map(blk => () => exports.client.blocks.delete({ block_id: blk.id })), {
|
|
214
|
+
concurrency: exports.BATCH_CONFIG.deleteConcurrency,
|
|
215
|
+
config: { maxRetries: 3 },
|
|
216
|
+
});
|
|
217
|
+
// Check for errors
|
|
218
|
+
const failures = deleteResults.filter(r => !r.success);
|
|
219
|
+
if (failures.length > 0) {
|
|
220
|
+
throw new Error(`Failed to delete ${failures.length} of ${blks.results.length} blocks`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Append new blocks
|
|
224
|
+
const res = await (0, retry_1.fetchWithRetry)(() => exports.client.blocks.children.append({
|
|
225
|
+
block_id: pageId,
|
|
226
|
+
children: blocks,
|
|
227
|
+
}), { context: `updatePage:append:${pageId}` });
|
|
228
|
+
// Invalidate caches
|
|
229
|
+
cache_1.cacheManager.invalidate('page', pageId);
|
|
230
|
+
cache_1.cacheManager.invalidate('block', pageId);
|
|
231
|
+
return res;
|
|
232
|
+
};
|
|
233
|
+
exports.updatePage = updatePage;
|
|
234
|
+
/**
|
|
235
|
+
* Retrieve a block (cached with very short TTL)
|
|
236
|
+
*/
|
|
237
|
+
const retrieveBlock = async (blockId) => {
|
|
238
|
+
return cachedFetch('block', blockId, () => exports.client.blocks.retrieve({ block_id: blockId }));
|
|
239
|
+
};
|
|
240
|
+
exports.retrieveBlock = retrieveBlock;
|
|
241
|
+
/**
|
|
242
|
+
* Update a block
|
|
243
|
+
*/
|
|
244
|
+
const updateBlock = async (params) => {
|
|
245
|
+
const result = await (0, retry_1.fetchWithRetry)(() => exports.client.blocks.update(params), { context: `updateBlock:${params.block_id}` });
|
|
246
|
+
// Invalidate this block's cache
|
|
247
|
+
cache_1.cacheManager.invalidate('block', params.block_id);
|
|
248
|
+
return result;
|
|
249
|
+
};
|
|
250
|
+
exports.updateBlock = updateBlock;
|
|
251
|
+
/**
|
|
252
|
+
* Retrieve block children (cached with very short TTL)
|
|
253
|
+
*/
|
|
254
|
+
const retrieveBlockChildren = async (blockId) => {
|
|
255
|
+
return cachedFetch('block', `${blockId}:children`, () => exports.client.blocks.children.list({ block_id: blockId }));
|
|
256
|
+
};
|
|
257
|
+
exports.retrieveBlockChildren = retrieveBlockChildren;
|
|
258
|
+
/**
|
|
259
|
+
* Append block children
|
|
260
|
+
*/
|
|
261
|
+
const appendBlockChildren = async (params) => {
|
|
262
|
+
const result = await (0, retry_1.fetchWithRetry)(() => exports.client.blocks.children.append(params), { context: `appendBlockChildren:${params.block_id}` });
|
|
263
|
+
// Invalidate parent block's cache
|
|
264
|
+
cache_1.cacheManager.invalidate('block', params.block_id);
|
|
265
|
+
cache_1.cacheManager.invalidate('block', `${params.block_id}:children`);
|
|
266
|
+
return result;
|
|
267
|
+
};
|
|
268
|
+
exports.appendBlockChildren = appendBlockChildren;
|
|
269
|
+
/**
|
|
270
|
+
* Delete a block
|
|
271
|
+
*/
|
|
272
|
+
const deleteBlock = async (blockId) => {
|
|
273
|
+
const result = await (0, retry_1.fetchWithRetry)(() => exports.client.blocks.delete({ block_id: blockId }), { context: `deleteBlock:${blockId}` });
|
|
274
|
+
// Invalidate this block's cache
|
|
275
|
+
cache_1.cacheManager.invalidate('block', blockId);
|
|
276
|
+
return result;
|
|
277
|
+
};
|
|
278
|
+
exports.deleteBlock = deleteBlock;
|
|
279
|
+
/**
|
|
280
|
+
* Retrieve a user (cached with long TTL)
|
|
281
|
+
*/
|
|
282
|
+
const retrieveUser = async (userId) => {
|
|
283
|
+
return cachedFetch('user', userId, () => exports.client.users.retrieve({ user_id: userId }));
|
|
284
|
+
};
|
|
285
|
+
exports.retrieveUser = retrieveUser;
|
|
286
|
+
/**
|
|
287
|
+
* List all users (cached with long TTL)
|
|
288
|
+
*/
|
|
289
|
+
const listUser = async () => {
|
|
290
|
+
return cachedFetch('user', 'list', () => exports.client.users.list({}));
|
|
291
|
+
};
|
|
292
|
+
exports.listUser = listUser;
|
|
293
|
+
/**
|
|
294
|
+
* Get bot user info (cached with long TTL)
|
|
295
|
+
*/
|
|
296
|
+
const botUser = async () => {
|
|
297
|
+
return cachedFetch('user', 'me', () => exports.client.users.me({}));
|
|
298
|
+
};
|
|
299
|
+
exports.botUser = botUser;
|
|
300
|
+
/**
|
|
301
|
+
* Search for databases (cached with medium TTL)
|
|
302
|
+
*/
|
|
303
|
+
const searchDb = async () => {
|
|
304
|
+
const { results } = await cachedFetch('search', 'databases', async () => {
|
|
305
|
+
return await exports.client.search({
|
|
306
|
+
filter: {
|
|
307
|
+
value: 'data_source',
|
|
308
|
+
property: 'object',
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
return results;
|
|
313
|
+
};
|
|
314
|
+
exports.searchDb = searchDb;
|
|
315
|
+
/**
|
|
316
|
+
* General search (not cached due to variable parameters)
|
|
317
|
+
*/
|
|
318
|
+
const search = async (params) => {
|
|
319
|
+
return (0, retry_1.fetchWithRetry)(() => exports.client.search(params), { context: 'search' });
|
|
320
|
+
};
|
|
321
|
+
exports.search = search;
|
|
322
|
+
/**
|
|
323
|
+
* Export cache manager for external use
|
|
324
|
+
*/
|
|
325
|
+
var cache_2 = require("./cache");
|
|
326
|
+
Object.defineProperty(exports, "cacheManager", { enumerable: true, get: function () { return cache_2.cacheManager; } });
|
|
327
|
+
/**
|
|
328
|
+
* Export retry utilities for external use
|
|
329
|
+
*/
|
|
330
|
+
var retry_2 = require("./retry");
|
|
331
|
+
Object.defineProperty(exports, "enhancedFetchWithRetry", { enumerable: true, get: function () { return retry_2.fetchWithRetry; } });
|
|
332
|
+
Object.defineProperty(exports, "CircuitBreaker", { enumerable: true, get: function () { return retry_2.CircuitBreaker; } });
|
|
333
|
+
/**
|
|
334
|
+
* Recursively retrieve a page with all its blocks and nested content
|
|
335
|
+
* @param pageId - The ID of the page to retrieve
|
|
336
|
+
* @param depth - Current recursion depth (internal use)
|
|
337
|
+
* @param maxDepth - Maximum depth to recurse (default: 3)
|
|
338
|
+
* @returns Object containing page metadata, blocks, and optional warnings
|
|
339
|
+
*/
|
|
340
|
+
const retrievePageRecursive = async (pageId, depth = 0, maxDepth = 3) => {
|
|
341
|
+
var _a, _b;
|
|
342
|
+
// Prevent infinite recursion
|
|
343
|
+
if (depth >= maxDepth) {
|
|
344
|
+
return {
|
|
345
|
+
page: null,
|
|
346
|
+
blocks: [],
|
|
347
|
+
warnings: [
|
|
348
|
+
{
|
|
349
|
+
block_id: pageId,
|
|
350
|
+
type: 'max_depth_reached',
|
|
351
|
+
message: `Maximum recursion depth of ${maxDepth} reached`,
|
|
352
|
+
has_children: false,
|
|
353
|
+
},
|
|
354
|
+
],
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
// Retrieve the page
|
|
358
|
+
const page = await (0, exports.retrievePage)({ page_id: pageId });
|
|
359
|
+
// Retrieve all blocks (children)
|
|
360
|
+
const blocksResponse = await (0, exports.retrieveBlockChildren)(pageId);
|
|
361
|
+
const blocks = blocksResponse.results || [];
|
|
362
|
+
const warnings = [];
|
|
363
|
+
// Handle unsupported blocks (collect warnings)
|
|
364
|
+
for (const block of blocks) {
|
|
365
|
+
if ((0, client_1.isFullBlock)(block) && block.type === 'unsupported') {
|
|
366
|
+
warnings.push({
|
|
367
|
+
block_id: block.id,
|
|
368
|
+
type: 'unsupported',
|
|
369
|
+
notion_type: ((_a = block.unsupported) === null || _a === void 0 ? void 0 : _a.type) || 'unknown',
|
|
370
|
+
message: `Block type '${((_b = block.unsupported) === null || _b === void 0 ? void 0 : _b.type) || 'unknown'}' not supported by Notion API`,
|
|
371
|
+
has_children: block.has_children,
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// Collect blocks with children that need fetching
|
|
376
|
+
const blocksWithChildren = blocks.filter(block => (0, client_1.isFullBlock)(block) && block.has_children && block.type !== 'unsupported');
|
|
377
|
+
// Fetch children in parallel
|
|
378
|
+
if (blocksWithChildren.length > 0) {
|
|
379
|
+
const childFetchResults = await (0, retry_1.batchWithRetry)(blocksWithChildren.map(block => async () => {
|
|
380
|
+
// TypeScript guard - we already filtered for full blocks
|
|
381
|
+
if (!(0, client_1.isFullBlock)(block)) {
|
|
382
|
+
throw new Error('Block is not a full block');
|
|
383
|
+
}
|
|
384
|
+
try {
|
|
385
|
+
const childrenResponse = await (0, exports.retrieveBlockChildren)(block.id);
|
|
386
|
+
const children = childrenResponse.results || [];
|
|
387
|
+
// If this is a child_page block, recursively fetch that page too
|
|
388
|
+
let childPageDetails = null;
|
|
389
|
+
if (block.type === 'child_page' && depth + 1 < maxDepth) {
|
|
390
|
+
childPageDetails = await (0, exports.retrievePageRecursive)(block.id, depth + 1, maxDepth);
|
|
391
|
+
}
|
|
392
|
+
return {
|
|
393
|
+
success: true,
|
|
394
|
+
block,
|
|
395
|
+
children,
|
|
396
|
+
childPageDetails,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
return {
|
|
401
|
+
success: false,
|
|
402
|
+
block,
|
|
403
|
+
error,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
}), {
|
|
407
|
+
concurrency: exports.BATCH_CONFIG.childrenConcurrency,
|
|
408
|
+
});
|
|
409
|
+
// Process results
|
|
410
|
+
for (const result of childFetchResults) {
|
|
411
|
+
if (result.success && result.data && result.data.success) {
|
|
412
|
+
// Attach children to the block
|
|
413
|
+
;
|
|
414
|
+
result.data.block.children = result.data.children;
|
|
415
|
+
// Attach child page details if present
|
|
416
|
+
if (result.data.childPageDetails) {
|
|
417
|
+
;
|
|
418
|
+
result.data.block.child_page_details = result.data.childPageDetails;
|
|
419
|
+
// Merge warnings from recursive calls
|
|
420
|
+
if (result.data.childPageDetails.warnings) {
|
|
421
|
+
warnings.push(...result.data.childPageDetails.warnings);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
else if (result.success && result.data && !result.data.success) {
|
|
426
|
+
// Add warning for inner operation failure (wrapped in successful batch result)
|
|
427
|
+
warnings.push({
|
|
428
|
+
block_id: result.data.block.id,
|
|
429
|
+
type: 'fetch_error',
|
|
430
|
+
message: `Failed to fetch children for block: ${result.data.error instanceof Error ? result.data.error.message : 'Unknown error'}`,
|
|
431
|
+
has_children: true,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return {
|
|
437
|
+
page,
|
|
438
|
+
blocks,
|
|
439
|
+
...(warnings.length > 0 && { warnings }),
|
|
440
|
+
};
|
|
441
|
+
};
|
|
442
|
+
exports.retrievePageRecursive = retrievePageRecursive;
|
|
443
|
+
/**
|
|
444
|
+
* Map page structure (fast page discovery with parallel fetching)
|
|
445
|
+
* Returns minimal structure info (titles, types, IDs) instead of full content
|
|
446
|
+
* @param pageId - The ID of the page to map
|
|
447
|
+
* @returns Object containing page ID, title, icon, and structure overview
|
|
448
|
+
*/
|
|
449
|
+
const mapPageStructure = async (pageId) => {
|
|
450
|
+
// Parallel fetch: get page and blocks simultaneously
|
|
451
|
+
const [page, blocksResponse] = await Promise.all([
|
|
452
|
+
(0, exports.retrievePage)({ page_id: pageId }),
|
|
453
|
+
(0, exports.retrieveBlockChildren)(pageId),
|
|
454
|
+
]);
|
|
455
|
+
const blocks = blocksResponse.results || [];
|
|
456
|
+
// Extract page title
|
|
457
|
+
let pageTitle = 'Untitled';
|
|
458
|
+
if (page.object === 'page' && (0, client_1.isFullPage)(page)) {
|
|
459
|
+
Object.entries(page.properties).find(([, prop]) => {
|
|
460
|
+
if (prop.type === 'title' && prop.title.length > 0) {
|
|
461
|
+
pageTitle = prop.title[0].plain_text;
|
|
462
|
+
return true;
|
|
463
|
+
}
|
|
464
|
+
return false;
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
// Extract page icon
|
|
468
|
+
let pageIcon;
|
|
469
|
+
if ((0, client_1.isFullPage)(page) && page.icon) {
|
|
470
|
+
if (page.icon.type === 'emoji') {
|
|
471
|
+
pageIcon = page.icon.emoji;
|
|
472
|
+
}
|
|
473
|
+
else if (page.icon.type === 'external') {
|
|
474
|
+
pageIcon = page.icon.external.url;
|
|
475
|
+
}
|
|
476
|
+
else if (page.icon.type === 'file') {
|
|
477
|
+
pageIcon = page.icon.file.url;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
// Build minimal structure
|
|
481
|
+
const structure = blocks.map((block) => {
|
|
482
|
+
const structureItem = {
|
|
483
|
+
type: block.type,
|
|
484
|
+
id: block.id,
|
|
485
|
+
};
|
|
486
|
+
// Extract title/text based on block type
|
|
487
|
+
try {
|
|
488
|
+
switch (block.type) {
|
|
489
|
+
case 'child_page':
|
|
490
|
+
structureItem.title = block[block.type].title;
|
|
491
|
+
break;
|
|
492
|
+
case 'child_database':
|
|
493
|
+
structureItem.title = block[block.type].title;
|
|
494
|
+
break;
|
|
495
|
+
case 'heading_1':
|
|
496
|
+
case 'heading_2':
|
|
497
|
+
case 'heading_3':
|
|
498
|
+
case 'paragraph':
|
|
499
|
+
case 'bulleted_list_item':
|
|
500
|
+
case 'numbered_list_item':
|
|
501
|
+
case 'to_do':
|
|
502
|
+
case 'toggle':
|
|
503
|
+
case 'quote':
|
|
504
|
+
case 'callout':
|
|
505
|
+
case 'code':
|
|
506
|
+
if (block[block.type].rich_text && block[block.type].rich_text.length > 0) {
|
|
507
|
+
structureItem.text = block[block.type].rich_text[0].plain_text;
|
|
508
|
+
}
|
|
509
|
+
break;
|
|
510
|
+
case 'bookmark':
|
|
511
|
+
case 'embed':
|
|
512
|
+
case 'link_preview':
|
|
513
|
+
structureItem.text = block[block.type].url;
|
|
514
|
+
break;
|
|
515
|
+
case 'equation':
|
|
516
|
+
structureItem.text = block[block.type].expression;
|
|
517
|
+
break;
|
|
518
|
+
case 'image':
|
|
519
|
+
case 'file':
|
|
520
|
+
case 'video':
|
|
521
|
+
case 'pdf':
|
|
522
|
+
if (block[block.type].type === 'file') {
|
|
523
|
+
structureItem.text = block[block.type].file.url;
|
|
524
|
+
}
|
|
525
|
+
else if (block[block.type].type === 'external') {
|
|
526
|
+
structureItem.text = block[block.type].external.url;
|
|
527
|
+
}
|
|
528
|
+
break;
|
|
529
|
+
// For other types, just include type and id
|
|
530
|
+
default:
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
catch {
|
|
535
|
+
// If extraction fails, just include type and id
|
|
536
|
+
}
|
|
537
|
+
return structureItem;
|
|
538
|
+
});
|
|
539
|
+
return {
|
|
540
|
+
id: pageId,
|
|
541
|
+
title: pageTitle,
|
|
542
|
+
type: 'page',
|
|
543
|
+
...(pageIcon && { icon: pageIcon }),
|
|
544
|
+
structure,
|
|
545
|
+
};
|
|
546
|
+
};
|
|
547
|
+
exports.mapPageStructure = mapPageStructure;
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced retry logic with exponential backoff and jitter
|
|
3
|
+
* Handles rate limiting, network errors, and transient failures
|
|
4
|
+
*/
|
|
5
|
+
export interface RetryConfig {
|
|
6
|
+
maxRetries: number;
|
|
7
|
+
baseDelay: number;
|
|
8
|
+
maxDelay: number;
|
|
9
|
+
exponentialBase: number;
|
|
10
|
+
jitterFactor: number;
|
|
11
|
+
retryableStatusCodes: number[];
|
|
12
|
+
retryableErrorCodes: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface RetryContext {
|
|
15
|
+
attempt: number;
|
|
16
|
+
maxRetries: number;
|
|
17
|
+
lastError: any;
|
|
18
|
+
totalDelay: number;
|
|
19
|
+
}
|
|
20
|
+
export type RetryCallback = (context: RetryContext) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Categorize errors into retryable and non-retryable
|
|
23
|
+
*/
|
|
24
|
+
export declare function isRetryableError(error: any, config?: RetryConfig): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Calculate delay with exponential backoff and jitter
|
|
27
|
+
*/
|
|
28
|
+
export declare function calculateDelay(attempt: number, config?: RetryConfig, retryAfterHeader?: string): number;
|
|
29
|
+
/**
|
|
30
|
+
* Enhanced retry wrapper with exponential backoff and jitter
|
|
31
|
+
*/
|
|
32
|
+
export declare function fetchWithRetry<T>(fn: () => Promise<T>, options?: {
|
|
33
|
+
config?: Partial<RetryConfig>;
|
|
34
|
+
onRetry?: RetryCallback;
|
|
35
|
+
context?: string;
|
|
36
|
+
}): Promise<T>;
|
|
37
|
+
/**
|
|
38
|
+
* Batch retry wrapper for multiple operations
|
|
39
|
+
* Executes operations with retry logic and collects results
|
|
40
|
+
*/
|
|
41
|
+
export declare function batchWithRetry<T>(operations: Array<() => Promise<T>>, options?: {
|
|
42
|
+
config?: Partial<RetryConfig>;
|
|
43
|
+
onRetry?: RetryCallback;
|
|
44
|
+
concurrency?: number;
|
|
45
|
+
}): Promise<Array<{
|
|
46
|
+
success: boolean;
|
|
47
|
+
data?: T;
|
|
48
|
+
error?: any;
|
|
49
|
+
}>>;
|
|
50
|
+
/**
|
|
51
|
+
* Retry wrapper with circuit breaker pattern
|
|
52
|
+
* Prevents cascading failures by stopping retries after too many failures
|
|
53
|
+
*/
|
|
54
|
+
export declare class CircuitBreaker {
|
|
55
|
+
private readonly failureThreshold;
|
|
56
|
+
private readonly successThreshold;
|
|
57
|
+
private readonly timeout;
|
|
58
|
+
private failures;
|
|
59
|
+
private successes;
|
|
60
|
+
private state;
|
|
61
|
+
private nextAttempt;
|
|
62
|
+
constructor(failureThreshold?: number, successThreshold?: number, timeout?: number);
|
|
63
|
+
execute<T>(fn: () => Promise<T>, retryOptions?: Parameters<typeof fetchWithRetry>[1]): Promise<T>;
|
|
64
|
+
private onSuccess;
|
|
65
|
+
private onFailure;
|
|
66
|
+
getState(): {
|
|
67
|
+
state: string;
|
|
68
|
+
failures: number;
|
|
69
|
+
successes: number;
|
|
70
|
+
};
|
|
71
|
+
reset(): void;
|
|
72
|
+
}
|