@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.
Files changed (162) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +552 -0
  3. package/bin/dev +17 -0
  4. package/bin/dev.cmd +3 -0
  5. package/bin/run +14 -0
  6. package/bin/run.cmd +3 -0
  7. package/dist/base-command.d.ts +73 -0
  8. package/dist/base-command.js +179 -0
  9. package/dist/base-flags.d.ts +14 -0
  10. package/dist/base-flags.js +59 -0
  11. package/dist/cache.d.ts +84 -0
  12. package/dist/cache.js +351 -0
  13. package/dist/commands/append.d.ts +37 -0
  14. package/dist/commands/append.js +120 -0
  15. package/dist/commands/batch/delete.d.ts +42 -0
  16. package/dist/commands/batch/delete.js +199 -0
  17. package/dist/commands/batch/retrieve.d.ts +43 -0
  18. package/dist/commands/batch/retrieve.js +272 -0
  19. package/dist/commands/block/append.d.ts +42 -0
  20. package/dist/commands/block/append.js +219 -0
  21. package/dist/commands/block/delete.d.ts +30 -0
  22. package/dist/commands/block/delete.js +97 -0
  23. package/dist/commands/block/retrieve/children.d.ts +31 -0
  24. package/dist/commands/block/retrieve/children.js +177 -0
  25. package/dist/commands/block/retrieve.d.ts +30 -0
  26. package/dist/commands/block/retrieve.js +101 -0
  27. package/dist/commands/block/update.d.ts +45 -0
  28. package/dist/commands/block/update.js +242 -0
  29. package/dist/commands/bookmark/list.d.ts +30 -0
  30. package/dist/commands/bookmark/list.js +60 -0
  31. package/dist/commands/bookmark/remove.d.ts +26 -0
  32. package/dist/commands/bookmark/remove.js +47 -0
  33. package/dist/commands/bookmark/set.d.ts +29 -0
  34. package/dist/commands/bookmark/set.js +96 -0
  35. package/dist/commands/browse.d.ts +13 -0
  36. package/dist/commands/browse.js +44 -0
  37. package/dist/commands/cache/info.d.ts +19 -0
  38. package/dist/commands/cache/info.js +145 -0
  39. package/dist/commands/config/set-token.d.ts +22 -0
  40. package/dist/commands/config/set-token.js +137 -0
  41. package/dist/commands/daily/index.d.ts +32 -0
  42. package/dist/commands/daily/index.js +135 -0
  43. package/dist/commands/daily/setup.d.ts +42 -0
  44. package/dist/commands/daily/setup.js +149 -0
  45. package/dist/commands/db/create.d.ts +31 -0
  46. package/dist/commands/db/create.js +124 -0
  47. package/dist/commands/db/query.d.ts +41 -0
  48. package/dist/commands/db/query.js +360 -0
  49. package/dist/commands/db/retrieve.d.ts +33 -0
  50. package/dist/commands/db/retrieve.js +134 -0
  51. package/dist/commands/db/schema.d.ts +32 -0
  52. package/dist/commands/db/schema.js +308 -0
  53. package/dist/commands/db/update.d.ts +31 -0
  54. package/dist/commands/db/update.js +117 -0
  55. package/dist/commands/doctor.d.ts +50 -0
  56. package/dist/commands/doctor.js +420 -0
  57. package/dist/commands/init.d.ts +65 -0
  58. package/dist/commands/init.js +479 -0
  59. package/dist/commands/list.d.ts +29 -0
  60. package/dist/commands/list.js +219 -0
  61. package/dist/commands/open.d.ts +29 -0
  62. package/dist/commands/open.js +100 -0
  63. package/dist/commands/page/create.d.ts +33 -0
  64. package/dist/commands/page/create.js +261 -0
  65. package/dist/commands/page/delete.d.ts +36 -0
  66. package/dist/commands/page/delete.js +107 -0
  67. package/dist/commands/page/export.d.ts +38 -0
  68. package/dist/commands/page/export.js +120 -0
  69. package/dist/commands/page/retrieve/property_item.d.ts +24 -0
  70. package/dist/commands/page/retrieve/property_item.js +75 -0
  71. package/dist/commands/page/retrieve.d.ts +36 -0
  72. package/dist/commands/page/retrieve.js +244 -0
  73. package/dist/commands/page/update.d.ts +34 -0
  74. package/dist/commands/page/update.js +184 -0
  75. package/dist/commands/quick.d.ts +35 -0
  76. package/dist/commands/quick.js +168 -0
  77. package/dist/commands/search.d.ts +43 -0
  78. package/dist/commands/search.js +361 -0
  79. package/dist/commands/stats.d.ts +35 -0
  80. package/dist/commands/stats.js +274 -0
  81. package/dist/commands/sync.d.ts +24 -0
  82. package/dist/commands/sync.js +183 -0
  83. package/dist/commands/template/get.d.ts +28 -0
  84. package/dist/commands/template/get.js +59 -0
  85. package/dist/commands/template/list.d.ts +32 -0
  86. package/dist/commands/template/list.js +62 -0
  87. package/dist/commands/template/remove.d.ts +27 -0
  88. package/dist/commands/template/remove.js +48 -0
  89. package/dist/commands/template/save.d.ts +32 -0
  90. package/dist/commands/template/save.js +92 -0
  91. package/dist/commands/template/use.d.ts +34 -0
  92. package/dist/commands/template/use.js +142 -0
  93. package/dist/commands/user/list.d.ts +27 -0
  94. package/dist/commands/user/list.js +99 -0
  95. package/dist/commands/user/retrieve/bot.d.ts +28 -0
  96. package/dist/commands/user/retrieve/bot.js +96 -0
  97. package/dist/commands/user/retrieve.d.ts +30 -0
  98. package/dist/commands/user/retrieve.js +103 -0
  99. package/dist/commands/whoami.d.ts +19 -0
  100. package/dist/commands/whoami.js +175 -0
  101. package/dist/deduplication.d.ts +41 -0
  102. package/dist/deduplication.js +71 -0
  103. package/dist/envelope.d.ts +169 -0
  104. package/dist/envelope.js +257 -0
  105. package/dist/errors/enhanced-errors.d.ts +168 -0
  106. package/dist/errors/enhanced-errors.js +567 -0
  107. package/dist/errors/index.d.ts +18 -0
  108. package/dist/errors/index.js +33 -0
  109. package/dist/examples/cache-retry-examples.d.ts +64 -0
  110. package/dist/examples/cache-retry-examples.js +375 -0
  111. package/dist/helper.d.ts +102 -0
  112. package/dist/helper.js +885 -0
  113. package/dist/http-agent.d.ts +38 -0
  114. package/dist/http-agent.js +60 -0
  115. package/dist/index.d.ts +1 -0
  116. package/dist/index.js +4 -0
  117. package/dist/interface.d.ts +4 -0
  118. package/dist/interface.js +2 -0
  119. package/dist/notion.d.ts +144 -0
  120. package/dist/notion.js +547 -0
  121. package/dist/retry.d.ts +72 -0
  122. package/dist/retry.js +381 -0
  123. package/dist/utils/bookmarks.d.ts +32 -0
  124. package/dist/utils/bookmarks.js +98 -0
  125. package/dist/utils/daily-config.d.ts +22 -0
  126. package/dist/utils/daily-config.js +60 -0
  127. package/dist/utils/disk-cache.d.ts +80 -0
  128. package/dist/utils/disk-cache.js +291 -0
  129. package/dist/utils/fuzzy.d.ts +36 -0
  130. package/dist/utils/fuzzy.js +69 -0
  131. package/dist/utils/interactive-navigator.d.ts +63 -0
  132. package/dist/utils/interactive-navigator.js +123 -0
  133. package/dist/utils/markdown-to-blocks.d.ts +21 -0
  134. package/dist/utils/markdown-to-blocks.js +333 -0
  135. package/dist/utils/notion-resolver.d.ts +49 -0
  136. package/dist/utils/notion-resolver.js +278 -0
  137. package/dist/utils/notion-url-parser.d.ts +48 -0
  138. package/dist/utils/notion-url-parser.js +121 -0
  139. package/dist/utils/property-expander.d.ts +45 -0
  140. package/dist/utils/property-expander.js +323 -0
  141. package/dist/utils/schema-examples.d.ts +40 -0
  142. package/dist/utils/schema-examples.js +359 -0
  143. package/dist/utils/schema-extractor.d.ts +65 -0
  144. package/dist/utils/schema-extractor.js +235 -0
  145. package/dist/utils/shell-config.d.ts +30 -0
  146. package/dist/utils/shell-config.js +84 -0
  147. package/dist/utils/table-formatter.d.ts +36 -0
  148. package/dist/utils/table-formatter.js +125 -0
  149. package/dist/utils/templates.d.ts +30 -0
  150. package/dist/utils/templates.js +82 -0
  151. package/dist/utils/terminal-banner.d.ts +24 -0
  152. package/dist/utils/terminal-banner.js +34 -0
  153. package/dist/utils/token-validator.d.ts +42 -0
  154. package/dist/utils/token-validator.js +66 -0
  155. package/dist/utils/update-notifier.d.ts +26 -0
  156. package/dist/utils/update-notifier.js +54 -0
  157. package/dist/utils/workspace-cache.d.ts +58 -0
  158. package/dist/utils/workspace-cache.js +185 -0
  159. package/oclif.manifest.json +6471 -0
  160. package/package.json +118 -0
  161. package/scripts/banner.js +38 -0
  162. 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;
@@ -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
+ }