rotion 3.6.3 → 4.0.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/README.ja.md +20 -12
- package/README.md +26 -17
- package/dist/exporter/api.d.ts +11 -1
- package/dist/exporter/api.js +35 -30
- package/dist/exporter/api.js.map +1 -1
- package/dist/exporter/bin.d.ts +2 -0
- package/dist/exporter/bin.js +4 -0
- package/dist/exporter/bin.js.map +1 -0
- package/dist/exporter/blocks.js +355 -322
- package/dist/exporter/blocks.js.map +1 -1
- package/dist/exporter/breadcrumbs.js +3 -3
- package/dist/exporter/breadcrumbs.js.map +1 -1
- package/dist/exporter/cli.d.ts +16 -0
- package/dist/exporter/cli.js +126 -0
- package/dist/exporter/cli.js.map +1 -0
- package/dist/exporter/database.js +82 -75
- package/dist/exporter/database.js.map +1 -1
- package/dist/exporter/dependencies.d.ts +38 -0
- package/dist/exporter/dependencies.js +102 -0
- package/dist/exporter/dependencies.js.map +1 -0
- package/dist/exporter/failures.d.ts +52 -0
- package/dist/exporter/failures.js +105 -0
- package/dist/exporter/failures.js.map +1 -0
- package/dist/exporter/files.d.ts +21 -0
- package/dist/exporter/files.js +164 -94
- package/dist/exporter/files.js.map +1 -1
- package/dist/exporter/github.d.ts +8 -0
- package/dist/exporter/github.js +22 -5
- package/dist/exporter/github.js.map +1 -1
- package/dist/exporter/index.d.ts +4 -0
- package/dist/exporter/index.js +2 -0
- package/dist/exporter/index.js.map +1 -1
- package/dist/exporter/log.d.ts +6 -0
- package/dist/exporter/log.js +15 -0
- package/dist/exporter/log.js.map +1 -0
- package/dist/exporter/mutex.js +55 -16
- package/dist/exporter/mutex.js.map +1 -1
- package/dist/exporter/page.js +51 -35
- package/dist/exporter/page.js.map +1 -1
- package/dist/exporter/prune.d.ts +27 -0
- package/dist/exporter/prune.js +95 -0
- package/dist/exporter/prune.js.map +1 -0
- package/dist/exporter/types.d.ts +13 -0
- package/dist/exporter/variables.d.ts +61 -17
- package/dist/exporter/variables.js +86 -23
- package/dist/exporter/variables.js.map +1 -1
- package/dist/ui/components/Gallery/GalleryCard/options.d.ts +11 -0
- package/dist/ui/esm/components/Gallery/GalleryCard/GalleryCard.js +2 -17
- package/dist/ui/esm/components/Gallery/GalleryCard/GalleryCard.js.map +1 -1
- package/dist/ui/esm/components/Gallery/GalleryCard/options.js +27 -0
- package/dist/ui/esm/components/Gallery/GalleryCard/options.js.map +1 -0
- package/dist/ui/esm/components/Page/CodeBlock/Code/Code.js +5 -0
- package/dist/ui/esm/components/Page/CodeBlock/Code/Code.js.map +1 -1
- package/dist/ui/index-without-dark.css +1 -1
- package/dist/ui/index.css +1 -1
- package/package.json +30 -24
package/dist/exporter/github.js
CHANGED
|
@@ -1,15 +1,32 @@
|
|
|
1
1
|
import { fetchWithTimeout } from './api.js';
|
|
2
2
|
import { saveImage } from './files.js';
|
|
3
|
-
import {
|
|
3
|
+
import { config } from './variables.js';
|
|
4
|
+
import { HTTPStatusError } from './failures.js';
|
|
4
5
|
const reqInit = {
|
|
5
6
|
headers: {
|
|
6
7
|
'Accept': 'application/vnd.github+json',
|
|
7
8
|
'X-GitHub-Api-Version': '2022-11-28',
|
|
8
9
|
}
|
|
9
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* fetchGitHub returns the JSON body of a successful response from the GitHub
|
|
13
|
+
* API. Any other status throws an HTTPStatusError, instead of handing an
|
|
14
|
+
* error message to a caller that expects a repository or an issue. A request
|
|
15
|
+
* over the rate limit (403 or 429 with no requests remaining) is reported as
|
|
16
|
+
* 429, because it succeeds once the limit resets.
|
|
17
|
+
*/
|
|
18
|
+
export async function fetchGitHub(url) {
|
|
19
|
+
const res = await fetchWithTimeout(url, reqInit);
|
|
20
|
+
if (!res.ok) {
|
|
21
|
+
await res.body?.cancel();
|
|
22
|
+
const limited = (res.status === 403 || res.status === 429) && res.headers.get('x-ratelimit-remaining') === '0';
|
|
23
|
+
throw new HTTPStatusError(limited ? 429 : res.status, url);
|
|
24
|
+
}
|
|
25
|
+
return await res.json();
|
|
26
|
+
}
|
|
10
27
|
export async function getRepo({ owner, repo }, func) {
|
|
11
28
|
const url = `https://api.github.com/repos/${owner}/${repo}`;
|
|
12
|
-
return (func) ? func(url, reqInit) : (
|
|
29
|
+
return (func) ? func(url, reqInit) : fetchGitHub(url);
|
|
13
30
|
}
|
|
14
31
|
export async function getRepoForLinkPreview({ owner, repo }, func) {
|
|
15
32
|
const res = await getRepo({ owner, repo }, func);
|
|
@@ -20,7 +37,7 @@ export async function getRepoForLinkPreview({ owner, repo }, func) {
|
|
|
20
37
|
avatar_src = ipws.path;
|
|
21
38
|
}
|
|
22
39
|
catch (e) {
|
|
23
|
-
if (debug) {
|
|
40
|
+
if (config().debug) {
|
|
24
41
|
console.log(`Failed to save github avatar: ${e}`);
|
|
25
42
|
}
|
|
26
43
|
}
|
|
@@ -34,7 +51,7 @@ export async function getRepoForLinkPreview({ owner, repo }, func) {
|
|
|
34
51
|
}
|
|
35
52
|
export async function getIssue({ owner, repo, number }, func) {
|
|
36
53
|
const url = `https://api.github.com/repos/${owner}/${repo}/issues/${number}`;
|
|
37
|
-
return func ? func(url, reqInit) : (
|
|
54
|
+
return func ? func(url, reqInit) : fetchGitHub(url);
|
|
38
55
|
}
|
|
39
56
|
export async function getIssueForLinkPreview({ owner, repo, number }, func) {
|
|
40
57
|
const res = await getIssue({ owner, repo, number }, func);
|
|
@@ -46,7 +63,7 @@ export async function getIssueForLinkPreview({ owner, repo, number }, func) {
|
|
|
46
63
|
avatar_src = ipws.path;
|
|
47
64
|
}
|
|
48
65
|
catch (e) {
|
|
49
|
-
if (debug) {
|
|
66
|
+
if (config().debug) {
|
|
50
67
|
console.log(`Failed to save github avatar: ${e}`);
|
|
51
68
|
}
|
|
52
69
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/exporter/github.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/exporter/github.ts"],"names":[],"mappings":"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"}
|
package/dist/exporter/index.d.ts
CHANGED
|
@@ -4,3 +4,7 @@ export * from './page.js';
|
|
|
4
4
|
export * from './types.js';
|
|
5
5
|
export * from './breadcrumbs.js';
|
|
6
6
|
export * from './validation.js';
|
|
7
|
+
export { configure } from './variables.js';
|
|
8
|
+
export type { RotionConfig } from './variables.js';
|
|
9
|
+
export { pruneCache } from './prune.js';
|
|
10
|
+
export type { PruneCacheArgs, PruneCacheRes } from './prune.js';
|
package/dist/exporter/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/exporter/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA;AAChC,cAAc,iBAAiB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/exporter/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA;AAChC,cAAc,iBAAiB,CAAA;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* warn prints one line to stderr for something that makes the output
|
|
3
|
+
* incomplete or slower, whether or not ROTION_DEBUG is set. Only the first
|
|
4
|
+
* line of the error message is printed; ROTION_DEBUG adds the whole error.
|
|
5
|
+
*/
|
|
6
|
+
export declare function warn(message: string, error?: unknown): void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { config } from './variables.js';
|
|
2
|
+
const messageOf = (error) => error instanceof Error ? error.message : String(error);
|
|
3
|
+
/**
|
|
4
|
+
* warn prints one line to stderr for something that makes the output
|
|
5
|
+
* incomplete or slower, whether or not ROTION_DEBUG is set. Only the first
|
|
6
|
+
* line of the error message is printed; ROTION_DEBUG adds the whole error.
|
|
7
|
+
*/
|
|
8
|
+
export function warn(message, error) {
|
|
9
|
+
const reason = error === undefined ? '' : `: ${messageOf(error).split('\n')[0]}`;
|
|
10
|
+
console.warn(`[rotion] ${message}${reason}`);
|
|
11
|
+
if (config().debug && error !== undefined) {
|
|
12
|
+
console.warn(error);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/exporter/log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAEvC,MAAM,SAAS,GAAG,CAAC,KAAc,EAAU,EAAE,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAEpG;;;;GAIG;AACH,MAAM,UAAU,IAAI,CAAE,OAAe,EAAE,KAAe;IACpD,MAAM,MAAM,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAChF,OAAO,CAAC,IAAI,CAAC,YAAY,OAAO,GAAG,MAAM,EAAE,CAAC,CAAA;IAC5C,IAAI,MAAM,EAAE,CAAC,KAAK,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACrB,CAAC;AACH,CAAC"}
|
package/dist/exporter/mutex.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { config } from './variables.js';
|
|
5
|
+
import { warn } from './log.js';
|
|
4
6
|
const DEFAULT_OPTIONS = {
|
|
5
7
|
timeout: 600000, // 10 minutes — must accommodate long operations like
|
|
6
8
|
// FetchDatabase that wrap many sequential image downloads
|
|
@@ -16,7 +18,7 @@ const DEFAULT_OPTIONS = {
|
|
|
16
18
|
*/
|
|
17
19
|
export async function withFileLock(key, operation, options = {}) {
|
|
18
20
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
19
|
-
const lockDir = path.join(cacheDir, 'locks');
|
|
21
|
+
const lockDir = path.join(config().cacheDir, 'locks');
|
|
20
22
|
const lockFile = path.join(lockDir, `${key}.lock`);
|
|
21
23
|
await fs.mkdir(lockDir, { recursive: true });
|
|
22
24
|
const startTime = Date.now();
|
|
@@ -24,17 +26,18 @@ export async function withFileLock(key, operation, options = {}) {
|
|
|
24
26
|
while (Date.now() - startTime < opts.timeout) {
|
|
25
27
|
try {
|
|
26
28
|
// Cleanup stale lock files
|
|
27
|
-
await cleanupStaleLock(lockFile, opts.maxAge);
|
|
29
|
+
await cleanupStaleLock(lockFile, opts.maxAge, opts.operationTimeout);
|
|
28
30
|
// Create lock file exclusively
|
|
29
31
|
fd = await fs.open(lockFile, 'wx');
|
|
30
|
-
// Write process ID and timestamp
|
|
32
|
+
// Write process ID, host and timestamp
|
|
31
33
|
const lockData = JSON.stringify({
|
|
32
34
|
pid: process.pid,
|
|
35
|
+
hostname: os.hostname(),
|
|
33
36
|
timestamp: Date.now(),
|
|
34
37
|
key
|
|
35
38
|
});
|
|
36
39
|
await fd.writeFile(lockData);
|
|
37
|
-
if (debug) {
|
|
40
|
+
if (config().debug) {
|
|
38
41
|
console.log(`Lock acquired: ${key} (pid: ${process.pid})`);
|
|
39
42
|
}
|
|
40
43
|
// Execute operation, bounded by operationTimeout so a hanging
|
|
@@ -62,18 +65,37 @@ export async function withFileLock(key, operation, options = {}) {
|
|
|
62
65
|
/**
|
|
63
66
|
* Cleanup stale lock files
|
|
64
67
|
*/
|
|
65
|
-
async function cleanupStaleLock(lockFile, maxAge) {
|
|
68
|
+
async function cleanupStaleLock(lockFile, maxAge, operationTimeout) {
|
|
66
69
|
try {
|
|
67
70
|
const stats = await fs.stat(lockFile);
|
|
68
71
|
const age = Date.now() - stats.mtime.getTime();
|
|
69
72
|
if (age > maxAge) {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
//
|
|
73
|
-
|
|
73
|
+
const { pid, hostname } = await readLockHolder(lockFile);
|
|
74
|
+
// A lock holder writes its pid right after creating the file, so a lock
|
|
75
|
+
// older than maxAge without a readable pid was left by a process that
|
|
76
|
+
// died in between. Nothing will ever remove it, and without this it
|
|
77
|
+
// would hold every waiter until the timeout.
|
|
78
|
+
let reason;
|
|
79
|
+
if (pid === undefined) {
|
|
80
|
+
reason = 'no readable pid';
|
|
81
|
+
}
|
|
82
|
+
else if (hostname !== undefined && hostname !== os.hostname()) {
|
|
83
|
+
// A pid means nothing on another host that shares the cache directory
|
|
84
|
+
// (a volume shared by containers, for example): it may belong to an
|
|
85
|
+
// unrelated process here, or to none. The holder releases the lock
|
|
86
|
+
// within operationTimeout, so only a lock older than that is left
|
|
87
|
+
// behind.
|
|
88
|
+
if (age > operationTimeout) {
|
|
89
|
+
reason = `held by ${hostname} for longer than ${operationTimeout}ms`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (!isProcessAlive(pid)) {
|
|
93
|
+
reason = `dead pid: ${pid}`;
|
|
94
|
+
}
|
|
95
|
+
if (reason !== undefined) {
|
|
74
96
|
await fs.unlink(lockFile);
|
|
75
|
-
if (debug) {
|
|
76
|
-
console.log(`Cleaned up stale lock: ${lockFile} (
|
|
97
|
+
if (config().debug) {
|
|
98
|
+
console.log(`Cleaned up stale lock: ${lockFile} (${reason})`);
|
|
77
99
|
}
|
|
78
100
|
}
|
|
79
101
|
}
|
|
@@ -82,6 +104,24 @@ async function cleanupStaleLock(lockFile, maxAge) {
|
|
|
82
104
|
// Ignore if file doesn't exist or can't be read
|
|
83
105
|
}
|
|
84
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Read the pid and host a lock file was written with. Either is undefined
|
|
109
|
+
* when the file is empty or does not hold it; a lock written before the host
|
|
110
|
+
* was recorded is taken as one from this host.
|
|
111
|
+
*/
|
|
112
|
+
async function readLockHolder(lockFile) {
|
|
113
|
+
const lockData = await fs.readFile(lockFile, 'utf-8');
|
|
114
|
+
try {
|
|
115
|
+
const { pid, hostname } = JSON.parse(lockData);
|
|
116
|
+
return {
|
|
117
|
+
pid: Number.isInteger(pid) ? pid : undefined,
|
|
118
|
+
hostname: typeof hostname === 'string' ? hostname : undefined,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
85
125
|
/**
|
|
86
126
|
* Check if process is alive
|
|
87
127
|
*/
|
|
@@ -126,14 +166,13 @@ async function releaseLock(fd, lockFile, key) {
|
|
|
126
166
|
try {
|
|
127
167
|
await fd.close();
|
|
128
168
|
await fs.unlink(lockFile);
|
|
129
|
-
if (debug) {
|
|
169
|
+
if (config().debug) {
|
|
130
170
|
console.log(`Lock released: ${key} (pid: ${process.pid})`);
|
|
131
171
|
}
|
|
132
172
|
}
|
|
133
173
|
catch (error) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
174
|
+
// A lock file left behind makes the next caller wait until it is stale
|
|
175
|
+
warn(`failed to release the lock ${lockFile}`, error);
|
|
137
176
|
}
|
|
138
177
|
}
|
|
139
178
|
//# sourceMappingURL=mutex.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutex.js","sourceRoot":"","sources":["../../src/exporter/mutex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAA;AACnC,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"mutex.js","sourceRoot":"","sources":["../../src/exporter/mutex.ts"],"names":[],"mappings":"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"}
|
package/dist/exporter/page.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { reqAPIWithBackoff, reqAPIWithBackoffAndCache, notion, } from './api.js';
|
|
2
|
-
import {
|
|
2
|
+
import { config } from './variables.js';
|
|
3
3
|
import { createDirWhenNotfound, saveImage, readCache, writeCache, isEmpty, } from './files.js';
|
|
4
4
|
import { withFileLock } from './mutex.js';
|
|
5
|
+
import { collectFailures, reportFailure } from './failures.js';
|
|
6
|
+
import { warn } from './log.js';
|
|
7
|
+
// Only these property types answer pages.properties.retrieve with a list,
|
|
8
|
+
// which is what page.meta collects. Any other type answers with a single
|
|
9
|
+
// item, which page.meta leaves out, so asking for it would be a wasted request.
|
|
10
|
+
const listPropertyTypes = new Set(['title', 'rich_text', 'people', 'relation', 'rollup']);
|
|
5
11
|
/**
|
|
6
12
|
* FetchPage retrieves page properties and download images in from properties.
|
|
7
13
|
* And create cache that includes filepath of downloaded images.
|
|
8
14
|
* The last_edited_time of 2nd args is for ROTION_INCREMENTAL_CACHE.
|
|
9
15
|
*/
|
|
10
16
|
export const FetchPage = async ({ page_id, last_edited_time }) => {
|
|
17
|
+
const { cacheDir, incrementalCache, debug } = config();
|
|
11
18
|
await createDirWhenNotfound(cacheDir);
|
|
12
19
|
const cacheFile = `${cacheDir}/notion.pages.retrieve-${page_id}`;
|
|
13
20
|
const lockKey = `page-${page_id}`;
|
|
@@ -35,37 +42,50 @@ export const FetchPage = async ({ page_id, last_edited_time }) => {
|
|
|
35
42
|
catch (_) {
|
|
36
43
|
/* not fatal */
|
|
37
44
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
45
|
+
// A result that misses something because of a transient failure is not
|
|
46
|
+
// cached; see FetchBlocks.
|
|
47
|
+
const { value, complete } = await collectFailures(async () => {
|
|
48
|
+
const page = await reqAPIWithBackoff({
|
|
49
|
+
func: notion().pages.retrieve,
|
|
50
|
+
args: { page_id },
|
|
51
|
+
count: 3
|
|
52
|
+
});
|
|
53
|
+
if ('properties' in page) {
|
|
54
|
+
let list;
|
|
55
|
+
for (const [, v] of Object.entries(page.properties)) {
|
|
56
|
+
if (!listPropertyTypes.has(v.type)) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const property_id = v.id;
|
|
60
|
+
const res = await reqAPIWithBackoffAndCache({
|
|
61
|
+
name: 'notion.pages.properties.retrieve',
|
|
62
|
+
func: notion().pages.properties.retrieve,
|
|
63
|
+
args: { page_id, property_id },
|
|
64
|
+
count: 3,
|
|
65
|
+
});
|
|
66
|
+
if (res.object !== 'list') {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (list === undefined) {
|
|
70
|
+
list = res;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
list.results.push(...res.results);
|
|
74
|
+
}
|
|
61
75
|
}
|
|
76
|
+
page.meta = list;
|
|
62
77
|
}
|
|
63
|
-
page
|
|
78
|
+
await savePageCover(page);
|
|
79
|
+
await savePageIcon(page);
|
|
80
|
+
return page;
|
|
81
|
+
});
|
|
82
|
+
if (complete) {
|
|
83
|
+
await writeCache(cacheFile, value);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
warn(`not caching page ${page_id} because of a transient failure; it is fetched again on the next call`);
|
|
64
87
|
}
|
|
65
|
-
|
|
66
|
-
await savePageIcon(page);
|
|
67
|
-
await writeCache(cacheFile, page);
|
|
68
|
-
return page;
|
|
88
|
+
return value;
|
|
69
89
|
});
|
|
70
90
|
};
|
|
71
91
|
export async function savePageCover(page) {
|
|
@@ -83,9 +103,7 @@ export async function savePageCover(page) {
|
|
|
83
103
|
}
|
|
84
104
|
}
|
|
85
105
|
catch (e) {
|
|
86
|
-
|
|
87
|
-
console.log(`Failed to save page cover: ${e}`);
|
|
88
|
-
}
|
|
106
|
+
reportFailure(`cover of page ${page.id}`, e);
|
|
89
107
|
}
|
|
90
108
|
}
|
|
91
109
|
export function getNotionIconUrl(icon) {
|
|
@@ -111,9 +129,7 @@ export async function savePageIcon(page) {
|
|
|
111
129
|
}
|
|
112
130
|
}
|
|
113
131
|
catch (e) {
|
|
114
|
-
|
|
115
|
-
console.log(`Failed to save page icon: ${e}`);
|
|
116
|
-
}
|
|
132
|
+
reportFailure(`icon of page ${page.id}`, e);
|
|
117
133
|
}
|
|
118
134
|
}
|
|
119
135
|
//# sourceMappingURL=page.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page.js","sourceRoot":"","sources":["../../src/exporter/page.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,MAAM,GACP,MAAM,UAAU,CAAA;AACjB,OAAO,
|
|
1
|
+
{"version":3,"file":"page.js","sourceRoot":"","sources":["../../src/exporter/page.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface PruneCacheArgs {
|
|
2
|
+
/** Remove what has not been used since this time */
|
|
3
|
+
before: Date;
|
|
4
|
+
/** List what would be removed without removing it */
|
|
5
|
+
dryRun?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface PruneCacheRes {
|
|
8
|
+
/** Paths of the files removed, or that would be removed with dryRun */
|
|
9
|
+
removed: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* pruneCache removes cache files, downloaded images and downloaded files that
|
|
13
|
+
* Rotion has not used since `before`. Rotion marks what it uses by setting
|
|
14
|
+
* the access time: a cache file when it is read or written, a download when
|
|
15
|
+
* it is saved or found, and the downloads a cached result refers to when that
|
|
16
|
+
* result is read.
|
|
17
|
+
*
|
|
18
|
+
* Only files named the way Rotion names them are removed: cache files that
|
|
19
|
+
* start with "notion.", and downloads whose name ends with a hash. Other
|
|
20
|
+
* files in the same directories, such as images of the site itself, are
|
|
21
|
+
* kept. A download and its conversions (the WebP of an image, the PNG of a
|
|
22
|
+
* HEIC) are removed together, and only when none of them was used.
|
|
23
|
+
*
|
|
24
|
+
* Run it after a full build with the time the build started, or on a server
|
|
25
|
+
* with a period longer than any page is kept without asking Rotion again.
|
|
26
|
+
*/
|
|
27
|
+
export declare function pruneCache({ before, dryRun }: PruneCacheArgs): Promise<PruneCacheRes>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { readdir, stat, unlink } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { config } from './variables.js';
|
|
4
|
+
// A downloaded file is named <prefix>-<sha1 of the name in its URL><ext>,
|
|
5
|
+
// and its conversions (.webp, .png) share the name without the extension.
|
|
6
|
+
const downloadedName = /^(.+-[0-9a-f]{40})(\.[^.]+)?$/i;
|
|
7
|
+
// Files a write left behind when it failed: <path>.<pid>.<time>.<random>.tmp
|
|
8
|
+
const temporaryName = /\.\d+\.\d+\.[0-9a-z]+\.tmp$/;
|
|
9
|
+
async function listFiles(dir) {
|
|
10
|
+
let names;
|
|
11
|
+
try {
|
|
12
|
+
names = await readdir(dir);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
const files = [];
|
|
18
|
+
for (const name of names) {
|
|
19
|
+
const file = path.join(dir, name);
|
|
20
|
+
try {
|
|
21
|
+
const s = await stat(file);
|
|
22
|
+
if (s.isFile()) {
|
|
23
|
+
files.push({ name, file, atime: s.atimeMs, mtime: s.mtimeMs });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Removed in the meantime
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return files;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* pruneCache removes cache files, downloaded images and downloaded files that
|
|
34
|
+
* Rotion has not used since `before`. Rotion marks what it uses by setting
|
|
35
|
+
* the access time: a cache file when it is read or written, a download when
|
|
36
|
+
* it is saved or found, and the downloads a cached result refers to when that
|
|
37
|
+
* result is read.
|
|
38
|
+
*
|
|
39
|
+
* Only files named the way Rotion names them are removed: cache files that
|
|
40
|
+
* start with "notion.", and downloads whose name ends with a hash. Other
|
|
41
|
+
* files in the same directories, such as images of the site itself, are
|
|
42
|
+
* kept. A download and its conversions (the WebP of an image, the PNG of a
|
|
43
|
+
* HEIC) are removed together, and only when none of them was used.
|
|
44
|
+
*
|
|
45
|
+
* Run it after a full build with the time the build started, or on a server
|
|
46
|
+
* with a period longer than any page is kept without asking Rotion again.
|
|
47
|
+
*/
|
|
48
|
+
export async function pruneCache({ before, dryRun = false }) {
|
|
49
|
+
const { cacheDir, docRoot, imageDir, fileDir } = config();
|
|
50
|
+
const limit = before.getTime();
|
|
51
|
+
const removed = [];
|
|
52
|
+
for (const f of await listFiles(cacheDir)) {
|
|
53
|
+
const unused = f.name.startsWith('notion.') && !temporaryName.test(f.name) && f.atime < limit;
|
|
54
|
+
const leftover = temporaryName.test(f.name) && f.mtime < limit;
|
|
55
|
+
if (unused || leftover) {
|
|
56
|
+
removed.push(f.file);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
for (const dir of [path.join(docRoot, imageDir), path.join(docRoot, fileDir)]) {
|
|
60
|
+
const groups = new Map();
|
|
61
|
+
for (const f of await listFiles(dir)) {
|
|
62
|
+
if (temporaryName.test(f.name)) {
|
|
63
|
+
if (f.mtime < limit) {
|
|
64
|
+
removed.push(f.file);
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const m = f.name.match(downloadedName);
|
|
69
|
+
if (m === null) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const group = groups.get(m[1]) ?? { files: [], used: 0 };
|
|
73
|
+
group.files.push(f.file);
|
|
74
|
+
group.used = Math.max(group.used, f.atime);
|
|
75
|
+
groups.set(m[1], group);
|
|
76
|
+
}
|
|
77
|
+
for (const group of groups.values()) {
|
|
78
|
+
if (group.used < limit) {
|
|
79
|
+
removed.push(...group.files);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!dryRun) {
|
|
84
|
+
for (const file of removed) {
|
|
85
|
+
try {
|
|
86
|
+
await unlink(file);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Removed in the meantime
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { removed };
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=prune.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prune.js","sourceRoot":"","sources":["../../src/exporter/prune.ts"],"names":[],"mappings":"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"}
|
package/dist/exporter/types.d.ts
CHANGED
|
@@ -278,10 +278,23 @@ export type SyncedBlockBlockObjectResponseEx = SyncedBlockBlockObjectResponse &
|
|
|
278
278
|
children?: ListBlockChildrenResponseEx;
|
|
279
279
|
};
|
|
280
280
|
export type BlockObjectResponse = ParagraphBlockObjectResponseEx | Heading1BlockObjectResponse | Heading2BlockObjectResponse | Heading3BlockObjectResponse | BulletedListItemBlockObjectResponseEx | NumberedListItemBlockObjectResponseEx | QuoteBlockObjectResponse | ToDoBlockObjectResponse | ToggleBlockObjectResponseEx | TemplateBlockObjectResponse | SyncedBlockBlockObjectResponseEx | ChildPageBlockObjectResponseEx | ChildDatabaseBlockObjectResponseEx | EquationBlockObjectResponse | CodeBlockObjectResponse | CalloutBlockObjectResponseEx | DividerBlockObjectResponse | BreadcrumbBlockObjectResponseEx | TableOfContentsBlockObjectResponse | ColumnListBlockObjectResponseEx | ColumnBlockObjectResponse | LinkToPageBlockObjectResponse | TableBlockObjectResponseEx | TableRowBlockObjectResponse | EmbedBlockObjectResponseEx | BookmarkBlockObjectResponseEx | ImageBlockObjectResponseEx | VideoBlockObjectResponseEx | PdfBlockObjectResponseEx | FileBlockObjectResponseEx | AudioBlockObjectResponse | LinkPreviewBlockObjectResponseEx | UnsupportedBlockObjectResponse;
|
|
281
|
+
/**
|
|
282
|
+
* Dependency is a page or database whose content is copied into a result of
|
|
283
|
+
* FetchBlocks: the source of a synced block, a mentioned page or database, a
|
|
284
|
+
* child page or a child database. Editing it does not change the
|
|
285
|
+
* last_edited_time of the page that shows it, so the incremental cache checks
|
|
286
|
+
* the dependency itself before it reuses the result.
|
|
287
|
+
*/
|
|
288
|
+
export interface Dependency {
|
|
289
|
+
type: 'page' | 'database';
|
|
290
|
+
id: string;
|
|
291
|
+
last_edited_time: string;
|
|
292
|
+
}
|
|
281
293
|
export type ListBlockChildrenResponseEx = ListBlockChildrenResponse & {
|
|
282
294
|
results: Array<BlockObjectResponse>;
|
|
283
295
|
children?: ListBlockChildrenResponse;
|
|
284
296
|
last_edited_time?: string;
|
|
297
|
+
dependencies?: Dependency[];
|
|
285
298
|
};
|
|
286
299
|
export type LinkPreviewBlockObjectResponseEx = LinkPreviewBlockObjectResponse & {
|
|
287
300
|
link_preview: {
|