@sillsdev/docu-notion 0.17.0-alpha.2 → 0.17.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -18,8 +18,13 @@ function notionColumnListToMarkdown(notionToMarkdown, getBlockChildren, block) {
|
|
|
18
18
|
if (!has_children)
|
|
19
19
|
return "";
|
|
20
20
|
const column_list_children = yield getBlockChildren(id);
|
|
21
|
-
const
|
|
22
|
-
const
|
|
21
|
+
const columns = [];
|
|
22
|
+
for (const column of column_list_children) {
|
|
23
|
+
// Keep column rendering sequential. A column block can trigger more Notion
|
|
24
|
+
// reads downstream, so Promise.all() here would turn one page into a burst
|
|
25
|
+
// of concurrent API requests during stage 2.
|
|
26
|
+
columns.push(yield notionToMarkdown.blockToMarkdown(column));
|
|
27
|
+
}
|
|
23
28
|
return `<div class='notion-row'>\n${columns.join("\n\n")}\n</div>`;
|
|
24
29
|
});
|
|
25
30
|
}
|
|
@@ -29,7 +29,13 @@ function notionColumnToMarkdown(notionToMarkdown, getBlockChildren, block) {
|
|
|
29
29
|
if (!has_children)
|
|
30
30
|
return "";
|
|
31
31
|
const columnChildren = yield getBlockChildren(id);
|
|
32
|
-
const childrenMdBlocksArray =
|
|
32
|
+
const childrenMdBlocksArray = [];
|
|
33
|
+
for (const child of columnChildren) {
|
|
34
|
+
// Intentionally serialize these subtree conversions. notion-to-md will fetch
|
|
35
|
+
// nested block children during blocksToMarkdown(), and parallelizing sibling
|
|
36
|
+
// columns creates bursts that can exceed Notion's per-integration rate limit.
|
|
37
|
+
childrenMdBlocksArray.push(yield notionToMarkdown.blocksToMarkdown([child]));
|
|
38
|
+
}
|
|
33
39
|
const childrenMarkdown = childrenMdBlocksArray.map(mdBlockArray => notionToMarkdown.toMarkdownString(mdBlockArray).parent);
|
|
34
40
|
const columnWidth = yield getColumnWidth(block);
|
|
35
41
|
return (`<div class='notion-column' style={{width: '${columnWidth}'}}>\n\n${childrenMarkdown.join("\n")}\n</div>` +
|
package/dist/pull.js
CHANGED
|
@@ -89,9 +89,7 @@ function notionPull(options) {
|
|
|
89
89
|
(0, log_1.info)("Connecting to Notion...");
|
|
90
90
|
// Do a quick test to see if we can connect to the root so that we can give a better error than just a generic "could not find page" one.
|
|
91
91
|
try {
|
|
92
|
-
yield
|
|
93
|
-
yield notionClient.pages.retrieve({ page_id: options.rootPage });
|
|
94
|
-
}));
|
|
92
|
+
yield notionClient.pages.retrieve({ page_id: options.rootPage });
|
|
95
93
|
}
|
|
96
94
|
catch (e) {
|
|
97
95
|
(0, log_1.error)(`docu-notion could not retrieve the root page from Notion. \r\na) Check that the root page id really is "${options.rootPage}".\r\nb) Check that your Notion API token (the "Integration Secret") is correct. It starts with "${optionsForLogging.notionToken}".\r\nc) Check that your root page includes your "integration" in its "connections".\r\nThis internal error message may help:\r\n ${e.message}`);
|
|
@@ -223,13 +221,31 @@ const notionLimiter = new limiter_1.RateLimiter({
|
|
|
223
221
|
let notionClient;
|
|
224
222
|
function getPageMetadata(id) {
|
|
225
223
|
return __awaiter(this, void 0, void 0, function* () {
|
|
226
|
-
return yield
|
|
227
|
-
|
|
228
|
-
page_id: id,
|
|
229
|
-
});
|
|
224
|
+
return yield notionClient.pages.retrieve({
|
|
225
|
+
page_id: id,
|
|
230
226
|
});
|
|
231
227
|
});
|
|
232
228
|
}
|
|
229
|
+
function isRetryableNotionError(error) {
|
|
230
|
+
const message = String((error === null || error === void 0 ? void 0 : error.message) || "");
|
|
231
|
+
return ((error === null || error === void 0 ? void 0 : error.code) === "notionhq_client_request_timeout" ||
|
|
232
|
+
(error === null || error === void 0 ? void 0 : error.code) === "notionhq_client_response_error" ||
|
|
233
|
+
(error === null || error === void 0 ? void 0 : error.code) === "service_unavailable" ||
|
|
234
|
+
(error === null || error === void 0 ? void 0 : error.code) === client_1.APIErrorCode.RateLimited ||
|
|
235
|
+
message.includes("timeout") ||
|
|
236
|
+
message.includes("Timeout") ||
|
|
237
|
+
message.includes("limit") ||
|
|
238
|
+
message.includes("Limit"));
|
|
239
|
+
}
|
|
240
|
+
function getRetryDelayMilliseconds(error, retryIndex) {
|
|
241
|
+
var _a, _b;
|
|
242
|
+
const retryAfterHeader = (_b = (_a = error === null || error === void 0 ? void 0 : error.headers) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(_a, "retry-after");
|
|
243
|
+
const retryAfterSeconds = Number.parseInt(retryAfterHeader || "", 10);
|
|
244
|
+
if (Number.isFinite(retryAfterSeconds) && retryAfterSeconds > 0) {
|
|
245
|
+
return retryAfterSeconds * 1000;
|
|
246
|
+
}
|
|
247
|
+
return (retryIndex + 1) * 1000;
|
|
248
|
+
}
|
|
233
249
|
// While everything works fine locally, on Github Actions we are getting a lot of timeouts, so
|
|
234
250
|
// we're trying this extra retry-able wrapper.
|
|
235
251
|
function executeWithRateLimitAndRetries(label, asyncFunction) {
|
|
@@ -243,16 +259,10 @@ function executeWithRateLimitAndRetries(label, asyncFunction) {
|
|
|
243
259
|
}
|
|
244
260
|
catch (e) {
|
|
245
261
|
lastException = e;
|
|
246
|
-
if ((e
|
|
247
|
-
e
|
|
248
|
-
e.message.
|
|
249
|
-
|
|
250
|
-
e.message.includes("Limit") ||
|
|
251
|
-
(e === null || e === void 0 ? void 0 : e.code) === "notionhq_client_response_error" ||
|
|
252
|
-
(e === null || e === void 0 ? void 0 : e.code) === "service_unavailable") {
|
|
253
|
-
const secondsToWait = i + 1;
|
|
254
|
-
(0, log_1.warning)(`While doing "${label}", got error "${e.message}". Will retry after ${secondsToWait}s...`);
|
|
255
|
-
yield new Promise(resolve => setTimeout(resolve, 1000 * secondsToWait));
|
|
262
|
+
if (isRetryableNotionError(e)) {
|
|
263
|
+
const millisecondsToWait = getRetryDelayMilliseconds(e, i);
|
|
264
|
+
(0, log_1.warning)(`While doing "${label}", got error "${e.message}". Will retry after ${millisecondsToWait / 1000}s...`);
|
|
265
|
+
yield new Promise(resolve => setTimeout(resolve, millisecondsToWait));
|
|
256
266
|
}
|
|
257
267
|
else {
|
|
258
268
|
throw e;
|
|
@@ -282,11 +292,9 @@ function getBlockChildren(id) {
|
|
|
282
292
|
// Note: there is a now a collectPaginatedAPI() in the notion client, so
|
|
283
293
|
// we could switch to using that (I don't know if it does rate limiting?)
|
|
284
294
|
do {
|
|
285
|
-
const response = yield
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
block_id: id,
|
|
289
|
-
});
|
|
295
|
+
const response = yield notionClient.blocks.children.list({
|
|
296
|
+
start_cursor: start_cursor,
|
|
297
|
+
block_id: id,
|
|
290
298
|
});
|
|
291
299
|
if (!overallResult) {
|
|
292
300
|
overallResult = response;
|
|
@@ -309,6 +317,10 @@ function initNotionClient(notionToken) {
|
|
|
309
317
|
notionClient = new client_1.Client({
|
|
310
318
|
auth: notionToken,
|
|
311
319
|
});
|
|
320
|
+
const originalRequest = notionClient.request.bind(notionClient);
|
|
321
|
+
notionClient.request = (args) => __awaiter(this, void 0, void 0, function* () {
|
|
322
|
+
return yield executeWithRateLimitAndRetries(`${args.method.toUpperCase()} ${args.path}`, () => originalRequest(args));
|
|
323
|
+
});
|
|
312
324
|
return notionClient;
|
|
313
325
|
}
|
|
314
326
|
function fromPageId(context, pageId, order, foundDirectlyInOutline) {
|
package/dist/transform.js
CHANGED
|
@@ -16,7 +16,6 @@ exports.getMarkdownForPage = getMarkdownForPage;
|
|
|
16
16
|
exports.getMarkdownFromNotionBlocks = getMarkdownFromNotionBlocks;
|
|
17
17
|
const chalk_1 = __importDefault(require("chalk"));
|
|
18
18
|
const log_1 = require("./log");
|
|
19
|
-
const pull_1 = require("./pull");
|
|
20
19
|
function getMarkdownForPage(config, context, page) {
|
|
21
20
|
return __awaiter(this, void 0, void 0, function* () {
|
|
22
21
|
(0, log_1.info)(`Reading & converting page ${page.layoutContext}/${page.nameOrTitle} (${chalk_1.default.blue(page.hasExplicitSlug
|
|
@@ -127,17 +126,10 @@ function doTransformsOnMarkdown(context, config, input) {
|
|
|
127
126
|
}
|
|
128
127
|
function doNotionToMarkdown(docunotionContext, blocks) {
|
|
129
128
|
return __awaiter(this, void 0, void 0, function* () {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// Calling blocksToMarkdown can modify the values in the blocks. If it does, and then
|
|
135
|
-
// we have to retry, we end up retrying with the modified values, which
|
|
136
|
-
// causes various issues (like using the transformed image url instead of the original one).
|
|
137
|
-
// Note, currently, we don't do anything else with blocks after this.
|
|
138
|
-
// If that changes, we'll need to figure out a more sophisticated approach.
|
|
139
|
-
JSON.parse(JSON.stringify(blocks)));
|
|
140
|
-
}));
|
|
129
|
+
const mdBlocks = yield docunotionContext.notionToMarkdown.blocksToMarkdown(
|
|
130
|
+
// We need to provide a copy of blocks.
|
|
131
|
+
// Calling blocksToMarkdown can modify the values in the blocks.
|
|
132
|
+
JSON.parse(JSON.stringify(blocks)));
|
|
141
133
|
const markdown = docunotionContext.notionToMarkdown.toMarkdownString(mdBlocks).parent || "";
|
|
142
134
|
return markdown;
|
|
143
135
|
});
|
package/package.json
CHANGED