amenbo 0.1.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +11 -1
- package/README.md +11 -1
- package/dist/cache.d.ts +48 -2
- package/dist/cache.js +164 -53
- package/dist/cache.js.map +1 -1
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +59 -4
- package/dist/cli.js.map +1 -1
- package/dist/core.d.ts +6 -0
- package/dist/core.js +134 -44
- package/dist/core.js.map +1 -1
- package/dist/diff.d.ts +1 -1
- package/dist/diff.js +29 -9
- package/dist/diff.js.map +1 -1
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -1
- package/dist/extract/dataSources.js +17 -4
- package/dist/extract/dataSources.js.map +1 -1
- package/dist/extract/markdown.d.ts +44 -0
- package/dist/extract/markdown.js +181 -2
- package/dist/extract/markdown.js.map +1 -1
- package/dist/extract/outline.d.ts +18 -0
- package/dist/extract/outline.js +37 -4
- package/dist/extract/outline.js.map +1 -1
- package/dist/extract/pdf.js +2 -2
- package/dist/extract/pruning.d.ts +4 -2
- package/dist/extract/pruning.js +36 -7
- package/dist/extract/pruning.js.map +1 -1
- package/dist/extract/qualityScore.d.ts +3 -2
- package/dist/extract/qualityScore.js +8 -3
- package/dist/extract/qualityScore.js.map +1 -1
- package/dist/fetcher/browser.js +15 -1
- package/dist/fetcher/browser.js.map +1 -1
- package/dist/fetcher/http.d.ts +11 -1
- package/dist/fetcher/http.js +6 -1
- package/dist/fetcher/http.js.map +1 -1
- package/dist/fetcher/index.d.ts +6 -0
- package/dist/fetcher/index.js +18 -13
- package/dist/fetcher/index.js.map +1 -1
- package/dist/installBrowser.d.ts +11 -0
- package/dist/installBrowser.js +30 -0
- package/dist/installBrowser.js.map +1 -0
- package/dist/links.d.ts +12 -0
- package/dist/links.js +32 -17
- package/dist/links.js.map +1 -1
- package/dist/politeness.d.ts +23 -3
- package/dist/politeness.js +27 -6
- package/dist/politeness.js.map +1 -1
- package/dist/screenshot.d.ts +2 -0
- package/dist/screenshot.js +14 -4
- package/dist/screenshot.js.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.js +55 -26
- package/dist/server.js.map +1 -1
- package/package.json +3 -6
package/README.en.md
CHANGED
|
@@ -37,7 +37,12 @@ For a measured comparison against similar tools (official fetch MCP / Jina Reade
|
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
39
|
npm install -g amenbo
|
|
40
|
-
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
That's enough for plain Markdown fetching (`fetch` / `links`). If you also want browser-based fetching — SPA escalation or screenshots — run this once (a ~170MB download the first time):
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npx -y amenbo install-browser
|
|
41
46
|
```
|
|
42
47
|
|
|
43
48
|
Or for development:
|
|
@@ -165,6 +170,11 @@ Discovery order: sitemap → RSS/Atom → in-page links.
|
|
|
165
170
|
- **SSRF protection**: rejects non-http/https schemes (`file:`, `ftp:`, etc.). Rejects DNS-resolved destinations that are private / loopback / link-local / reserved addresses. As a DNS rebinding (TOCTOU) countermeasure, actual connections are pinned to the validated IP
|
|
166
171
|
- **Body size cap**: prevents OOM from oversized responses
|
|
167
172
|
|
|
173
|
+
## Known limitations
|
|
174
|
+
|
|
175
|
+
- **No HTTP proxy support**: `HTTP_PROXY` / `HTTPS_PROXY` environment variables are not honored. As an SSRF countermeasure, connections are pinned to a validated IP (DNS rebinding protection), which is incompatible with delegating name resolution to an upstream proxy. Networks that require an upstream proxy are currently unsupported
|
|
176
|
+
- **No anti-bot circumvention**: fetch failures due to robots.txt denial or bot countermeasures are by design and reported as-is (see [Low impact on target sites](#low-impact-on-target-sites))
|
|
177
|
+
|
|
168
178
|
## Development
|
|
169
179
|
|
|
170
180
|
```bash
|
package/README.md
CHANGED
|
@@ -37,7 +37,12 @@ amenbo(アメンボ / water strider)は、Claude Code や Codex のようなコ
|
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
39
|
npm install -g amenbo
|
|
40
|
-
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Markdown 取得(通常の `fetch` / `links`)はこれだけで動きます。JS 描画が必要な SPA への昇格やスクリーンショットなど、ブラウザ(Chromium)経由の取得を使う場合のみ、初回に一度だけ実行してください(約 170MB のダウンロード):
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npx -y amenbo install-browser
|
|
41
46
|
```
|
|
42
47
|
|
|
43
48
|
または開発用途:
|
|
@@ -160,6 +165,11 @@ sitemap → RSS/Atom → ページ内リンクの順で探索します。
|
|
|
160
165
|
- **SSRF 対策**:http/https 以外のスキーム(`file:`, `ftp:` 等)を拒否。DNS 解決した接続先が private / loopback / link-local / 予約アドレスなら拒否。DNS rebinding(TOCTOU)対策として実接続を検証済み IP に固定します
|
|
161
166
|
- **ボディサイズ上限**:巨大レスポンスによる OOM を防止
|
|
162
167
|
|
|
168
|
+
## 既知の制限
|
|
169
|
+
|
|
170
|
+
- **HTTP プロキシ非対応**:`HTTP_PROXY` / `HTTPS_PROXY` 等の環境変数は尊重しません。SSRF 対策として接続先を検証済み IP に固定する設計(DNS rebinding 対策)と、プロキシへ名前解決を委ねる方式が両立しないためです。上流プロキシ必須のネットワークでは現状ご利用いただけません
|
|
171
|
+
- **anti-bot 回避は実装しません**:robots.txt 拒否やボット対策による取得失敗は仕様です。失敗はそのまま報告します([収集先への低負荷](#収集先への低負荷)参照)
|
|
172
|
+
|
|
163
173
|
## 開発
|
|
164
174
|
|
|
165
175
|
```bash
|
package/dist/cache.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export interface CacheWriteInput {
|
|
|
17
17
|
markdown: string;
|
|
18
18
|
metadata: Record<string, unknown>;
|
|
19
19
|
}
|
|
20
|
+
export declare function resolveCacheDir(): string;
|
|
20
21
|
export declare function hashContent(content: string): string;
|
|
21
22
|
/** スクリーンショットのキャッシュキー(url+撮影パラメータの組)を計算する。 */
|
|
22
23
|
export declare function computeScreenshotCacheKey(url: string, width: number, scale: number, fullPage: boolean): string;
|
|
@@ -38,14 +39,31 @@ export declare class PageCache {
|
|
|
38
39
|
private readonly cacheDir;
|
|
39
40
|
private readonly ttlMs;
|
|
40
41
|
private readonly now;
|
|
42
|
+
private readonly statements;
|
|
43
|
+
private closed;
|
|
41
44
|
constructor(options?: {
|
|
42
45
|
dbPath?: string;
|
|
43
46
|
cacheDir?: string;
|
|
44
47
|
ttlMs?: number;
|
|
45
48
|
now?: () => number;
|
|
46
49
|
});
|
|
50
|
+
/** 全SQL文を一度だけprepareする(DDL適用後・prune前に呼ぶ)。 */
|
|
51
|
+
private prepareStatements;
|
|
47
52
|
/**
|
|
48
|
-
*
|
|
53
|
+
* 前回pruneからTTL未満なら掃除を間引く。
|
|
54
|
+
*
|
|
55
|
+
* 起動のたびにpruneExpired(複数テーブルのDELETE+screenshots走査)を実行すると、CLIの
|
|
56
|
+
* 1コマンド=1プロセスでのバルク収集ループで毎回のプロセス起動に掃除コストが乗る。
|
|
57
|
+
* prune直後のTTL窓ではほとんど新規失効が無いため、前回pruneからの経過がTTL未満なら
|
|
58
|
+
* skipする(掃除間隔がTTL単位まで粗くなるだけで、ディスク無制限増加の防止という
|
|
59
|
+
* M3の目的は維持される。失効エントリの残留は最大でも約2×TTLに収まる)。
|
|
60
|
+
* meta未記録(新規DB/初回起動)では必ず実行するため、起動時掃除のテストは従来通り成立する。
|
|
61
|
+
*/
|
|
62
|
+
private maybePruneExpired;
|
|
63
|
+
/** meta テーブルの単一整数値を返す(未記録ならnull)。 */
|
|
64
|
+
private getMetaValue;
|
|
65
|
+
/**
|
|
66
|
+
* M3: TTL超過エントリを削除する(ディスク無制限増加の防止)。maybePruneExpired経由で呼ばれる。
|
|
49
67
|
* screenshotsはSQLite行に加え、対応するPNGファイル(タイル)もあわせて削除する。
|
|
50
68
|
*/
|
|
51
69
|
private pruneExpired;
|
|
@@ -57,7 +75,16 @@ export declare class PageCache {
|
|
|
57
75
|
}): boolean;
|
|
58
76
|
/** 保存済みスクリーンショットエントリを取得する(TTL判定は isFresh を使う)。 */
|
|
59
77
|
getScreenshot(cacheKey: string): ScreenshotCacheEntry | undefined;
|
|
60
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* スクリーンショットのタイル群をファイル保存し、パスをSQLiteへ記録する。
|
|
80
|
+
*
|
|
81
|
+
* レビュー指摘対応(Medium): 同一cacheKeyへの再撮影でタイル数が減った場合(旧5枚→新3枚)、
|
|
82
|
+
* 新しいtile_pathsで上書きされるDB行からは旧tile-3.png/tile-4.pngを参照できなくなり、
|
|
83
|
+
* pruneExpiredもそれらを認識できず孤立ファイルとしてディスクに残り続けていた。
|
|
84
|
+
* tileDirはcacheKey単位のディレクトリ(他のcacheKeyのタイルと混在しない)のため、
|
|
85
|
+
* 書き込み前にディレクトリごと削除してから作り直すことで、枚数が変化しても
|
|
86
|
+
* 旧タイルを確実に一掃する(初回撮影時はディレクトリが存在しないためforce:trueで無視する)。
|
|
87
|
+
*/
|
|
61
88
|
setScreenshot(input: ScreenshotWriteInput): ScreenshotCacheEntry;
|
|
62
89
|
/** 新規取得結果を保存(既存があれば上書き)する。 */
|
|
63
90
|
set(input: CacheWriteInput): CacheEntry;
|
|
@@ -86,5 +113,24 @@ export declare class PageCache {
|
|
|
86
113
|
* store JSDoc参照)。せめて時刻が巻き戻らないよう、SQL側でMAXを取って単調増加を保証する。
|
|
87
114
|
*/
|
|
88
115
|
setHostLastRequestAt(host: string, at: number): void;
|
|
116
|
+
/**
|
|
117
|
+
* レビュー指摘対応: オリジン(例 "https://example.com")のrobots.txtキャッシュを返す
|
|
118
|
+
* (未記録ならnull)。PolitenessManagerのrobotsStoreオプションとして注入し、プロセス間で
|
|
119
|
+
* robots.txt取得結果を共有するために使う。TTL判定(robotsTtlMs)は呼び出し元(PolitenessManager)
|
|
120
|
+
* がfetchedAtを見て行う(page cacheのttlMsとは別の時間軸のため、ここでは判定しない)。
|
|
121
|
+
* 空bodyのrobots.txt(=制限なし)も正しい取得結果として保存されるため、
|
|
122
|
+
* 「未記録(null)」と「空bodyの記録あり」は区別される。
|
|
123
|
+
*/
|
|
124
|
+
getRobotsCache(origin: string): {
|
|
125
|
+
body: string;
|
|
126
|
+
fetchedAt: number;
|
|
127
|
+
} | null;
|
|
128
|
+
/** オリジンのrobots.txt取得結果(本文+取得時刻)を記録する。既存があれば上書きする。 */
|
|
129
|
+
setRobotsCache(origin: string, body: string, fetchedAt: number): void;
|
|
130
|
+
/**
|
|
131
|
+
* 冪等なclose。better-sqlite3のclose()は閉鎖済みでも安全だったが、node:sqliteは
|
|
132
|
+
* "database is not open" を投げるため、CLIのfinallyとprocess 'exit'ハンドラの両方から
|
|
133
|
+
* 呼ばれる二重close経路(core.ts参照)を自前のフラグで吸収する。
|
|
134
|
+
*/
|
|
89
135
|
close(): void;
|
|
90
136
|
}
|
package/dist/cache.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* cache.ts —
|
|
2
|
+
* cache.ts — node:sqlite(DatabaseSync)によるURL単位のローカルキャッシュ。
|
|
3
3
|
*
|
|
4
4
|
* 保存場所: $AMENBO_CACHE_DIR または ~/.cache/amenbo/cache.sqlite
|
|
5
5
|
* URL毎にETag/Last-Modified/本文ハッシュ/変換済みMarkdown/取得時刻を保存する。
|
|
@@ -21,17 +21,32 @@
|
|
|
21
21
|
* 短時間に連続実行すると同一ドメインへの最小間隔が守られない。host_requests テーブルへ
|
|
22
22
|
* 最終リクエスト時刻を永続化し、PolitenessManagerのstoreオプションとして注入することで
|
|
23
23
|
* MCPサーバー/CLIの複数プロセス間で共有する(再起動直後の連続アクセスも守れる副次効果あり)。
|
|
24
|
-
*
|
|
24
|
+
* 同じ理由でrobots.txtの取得結果もrobots_cache テーブルへ永続化し、PolitenessManagerの
|
|
25
|
+
* robotsStoreオプションとして注入する(CLIでの複数ページ一括収集時にコマンド毎の
|
|
26
|
+
* robots.txt再取得を防ぐ)。journal_mode=WAL は複数プロセスからの同時アクセスを想定して設定している。
|
|
27
|
+
*
|
|
28
|
+
* 性能: SQL文は全てコンストラクタで一度だけprepareし(this.statements)、以降は再利用する。
|
|
29
|
+
* node:sqliteのprepare()は呼び出しの度にSQLを再コンパイルするため、MCPサーバー常駐時の
|
|
30
|
+
* 反復実行やCLIバルク収集ループでの再コンパイルコストを避ける。
|
|
31
|
+
*
|
|
32
|
+
* node:sqliteの制約: better-sqlite3はNumber.MAX_SAFE_INTEGERを超えるINTEGER列を読むと
|
|
33
|
+
* 例外を投げたが、node:sqliteは黙ってbigintを返す(上流の挙動差)。本ファイルの整数列
|
|
34
|
+
* (fetched_at/last_request_at)は全てDate.now()由来でMAX_SAFE_INTEGERを超えないため、
|
|
35
|
+
* number型へのキャストのみで安全に扱える。
|
|
25
36
|
*/
|
|
26
37
|
import { createHash } from "node:crypto";
|
|
27
38
|
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
28
39
|
import { homedir } from "node:os";
|
|
29
40
|
import { join } from "node:path";
|
|
30
|
-
import
|
|
41
|
+
import { DatabaseSync } from "node:sqlite";
|
|
31
42
|
const DEFAULT_TTL_MS = 15 * 60 * 1000; // 15分
|
|
32
43
|
/** Phase 4テンプレート学習: 定型ブロック判定に使う「直近ページ数」の既定値。 */
|
|
33
44
|
const DEFAULT_TEMPLATE_RECENT_PAGES = 3;
|
|
34
|
-
|
|
45
|
+
/** meta テーブルに前回prune時刻を記録するキー。 */
|
|
46
|
+
const META_LAST_PRUNED_AT = "last_pruned_at";
|
|
47
|
+
// レビュー指摘対応: cli.ts(fetch画像の保存先)がキャッシュディレクトリ解決ロジックを
|
|
48
|
+
// 再利用できるようexportする(AMENBO_CACHE_DIR未指定時の既定値~/.cache/amenboとのずれを防ぐ)。
|
|
49
|
+
export function resolveCacheDir() {
|
|
35
50
|
const dir = process.env.AMENBO_CACHE_DIR ?? join(homedir(), ".cache", "amenbo");
|
|
36
51
|
if (!existsSync(dir)) {
|
|
37
52
|
mkdirSync(dir, { recursive: true });
|
|
@@ -50,15 +65,17 @@ export class PageCache {
|
|
|
50
65
|
cacheDir;
|
|
51
66
|
ttlMs;
|
|
52
67
|
now;
|
|
68
|
+
statements;
|
|
69
|
+
closed = false;
|
|
53
70
|
constructor(options = {}) {
|
|
54
71
|
this.cacheDir = options.cacheDir ?? resolveCacheDir();
|
|
55
72
|
if (!existsSync(this.cacheDir)) {
|
|
56
73
|
mkdirSync(this.cacheDir, { recursive: true });
|
|
57
74
|
}
|
|
58
|
-
this.db = new
|
|
75
|
+
this.db = new DatabaseSync(options.dbPath ?? join(this.cacheDir, "cache.sqlite"));
|
|
59
76
|
this.ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
60
77
|
this.now = options.now ?? Date.now;
|
|
61
|
-
this.db.
|
|
78
|
+
this.db.exec("PRAGMA journal_mode = WAL");
|
|
62
79
|
this.db.exec(`
|
|
63
80
|
CREATE TABLE IF NOT EXISTS pages (
|
|
64
81
|
url TEXT PRIMARY KEY,
|
|
@@ -95,22 +112,112 @@ export class PageCache {
|
|
|
95
112
|
last_request_at INTEGER NOT NULL
|
|
96
113
|
)
|
|
97
114
|
`);
|
|
115
|
+
// レビュー指摘対応: robots.txtの取得結果をhost_requestsと同じ考え方でプロセス間共有する。
|
|
116
|
+
// CLIバルク収集(1コマンド=1プロセス)で、コマンド毎にrobots.txtを再取得してしまう
|
|
117
|
+
// 低負荷原則違反への対応(PolitenessManagerのrobotsStoreオプションとして注入する)。
|
|
118
|
+
this.db.exec(`
|
|
119
|
+
CREATE TABLE IF NOT EXISTS robots_cache (
|
|
120
|
+
origin TEXT PRIMARY KEY,
|
|
121
|
+
body TEXT NOT NULL,
|
|
122
|
+
fetched_at INTEGER NOT NULL
|
|
123
|
+
)
|
|
124
|
+
`);
|
|
125
|
+
// 前回prune時刻等の単一値メタデータ。maybePruneExpired の間引き判定に使う。
|
|
126
|
+
this.db.exec(`
|
|
127
|
+
CREATE TABLE IF NOT EXISTS meta (
|
|
128
|
+
key TEXT PRIMARY KEY,
|
|
129
|
+
value INTEGER NOT NULL
|
|
130
|
+
)
|
|
131
|
+
`);
|
|
132
|
+
this.statements = this.prepareStatements();
|
|
98
133
|
// M3: TTL超過削除・サイズ上限が無くディスクが無制限に増加する問題への対応。
|
|
99
|
-
//
|
|
134
|
+
// 起動のたびに掃除するとCLIバルク収集(1コマンド=1プロセス)で毎回DELETEが走るため、
|
|
135
|
+
// 前回pruneからTTL未満なら間引く(maybePruneExpired参照)。
|
|
136
|
+
this.maybePruneExpired();
|
|
137
|
+
}
|
|
138
|
+
/** 全SQL文を一度だけprepareする(DDL適用後・prune前に呼ぶ)。 */
|
|
139
|
+
prepareStatements() {
|
|
140
|
+
return {
|
|
141
|
+
deletePagesExpired: this.db.prepare("DELETE FROM pages WHERE fetched_at < ?"),
|
|
142
|
+
deleteDomainPagesExpired: this.db.prepare("DELETE FROM domain_pages WHERE fetched_at < ?"),
|
|
143
|
+
deleteHostRequestsExpired: this.db.prepare("DELETE FROM host_requests WHERE last_request_at < ?"),
|
|
144
|
+
selectScreenshotsExpired: this.db.prepare("SELECT * FROM screenshots WHERE fetched_at < ?"),
|
|
145
|
+
deleteScreenshotsExpired: this.db.prepare("DELETE FROM screenshots WHERE fetched_at < ?"),
|
|
146
|
+
getPage: this.db.prepare("SELECT * FROM pages WHERE url = ?"),
|
|
147
|
+
setPage: this.db.prepare(`INSERT INTO pages (url, etag, last_modified, content_hash, markdown, metadata, fetched_at)
|
|
148
|
+
VALUES (@url, @etag, @lastModified, @contentHash, @markdown, @metadata, @fetchedAt)
|
|
149
|
+
ON CONFLICT(url) DO UPDATE SET
|
|
150
|
+
etag = excluded.etag,
|
|
151
|
+
last_modified = excluded.last_modified,
|
|
152
|
+
content_hash = excluded.content_hash,
|
|
153
|
+
markdown = excluded.markdown,
|
|
154
|
+
metadata = excluded.metadata,
|
|
155
|
+
fetched_at = excluded.fetched_at`),
|
|
156
|
+
touchPage: this.db.prepare("UPDATE pages SET fetched_at = ? WHERE url = ?"),
|
|
157
|
+
getScreenshot: this.db.prepare("SELECT * FROM screenshots WHERE cache_key = ?"),
|
|
158
|
+
setScreenshot: this.db.prepare(`INSERT INTO screenshots (cache_key, url, tile_paths, metadata, fetched_at)
|
|
159
|
+
VALUES (@cacheKey, @url, @tilePaths, @metadata, @fetchedAt)
|
|
160
|
+
ON CONFLICT(cache_key) DO UPDATE SET
|
|
161
|
+
url = excluded.url,
|
|
162
|
+
tile_paths = excluded.tile_paths,
|
|
163
|
+
metadata = excluded.metadata,
|
|
164
|
+
fetched_at = excluded.fetched_at`),
|
|
165
|
+
recordDomainPage: this.db.prepare(`INSERT INTO domain_pages (domain, url, fetched_at, block_hashes)
|
|
166
|
+
VALUES (@domain, @url, @fetchedAt, @blockHashes)
|
|
167
|
+
ON CONFLICT(domain, url) DO UPDATE SET
|
|
168
|
+
fetched_at = excluded.fetched_at,
|
|
169
|
+
block_hashes = excluded.block_hashes`),
|
|
170
|
+
getDomainPages: this.db.prepare("SELECT * FROM domain_pages WHERE domain = ? ORDER BY fetched_at DESC LIMIT ?"),
|
|
171
|
+
getHostRequest: this.db.prepare("SELECT * FROM host_requests WHERE host = ?"),
|
|
172
|
+
setHostRequest: this.db.prepare(`INSERT INTO host_requests (host, last_request_at)
|
|
173
|
+
VALUES (@host, @lastRequestAt)
|
|
174
|
+
ON CONFLICT(host) DO UPDATE SET last_request_at = MAX(last_request_at, excluded.last_request_at)`),
|
|
175
|
+
getRobots: this.db.prepare("SELECT * FROM robots_cache WHERE origin = ?"),
|
|
176
|
+
setRobots: this.db.prepare(`INSERT INTO robots_cache (origin, body, fetched_at)
|
|
177
|
+
VALUES (@origin, @body, @fetchedAt)
|
|
178
|
+
ON CONFLICT(origin) DO UPDATE SET
|
|
179
|
+
body = excluded.body,
|
|
180
|
+
fetched_at = excluded.fetched_at`),
|
|
181
|
+
getMeta: this.db.prepare("SELECT value FROM meta WHERE key = ?"),
|
|
182
|
+
setMeta: this.db.prepare(`INSERT INTO meta (key, value) VALUES (@key, @value)
|
|
183
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value`),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* 前回pruneからTTL未満なら掃除を間引く。
|
|
188
|
+
*
|
|
189
|
+
* 起動のたびにpruneExpired(複数テーブルのDELETE+screenshots走査)を実行すると、CLIの
|
|
190
|
+
* 1コマンド=1プロセスでのバルク収集ループで毎回のプロセス起動に掃除コストが乗る。
|
|
191
|
+
* prune直後のTTL窓ではほとんど新規失効が無いため、前回pruneからの経過がTTL未満なら
|
|
192
|
+
* skipする(掃除間隔がTTL単位まで粗くなるだけで、ディスク無制限増加の防止という
|
|
193
|
+
* M3の目的は維持される。失効エントリの残留は最大でも約2×TTLに収まる)。
|
|
194
|
+
* meta未記録(新規DB/初回起動)では必ず実行するため、起動時掃除のテストは従来通り成立する。
|
|
195
|
+
*/
|
|
196
|
+
maybePruneExpired() {
|
|
197
|
+
const lastPrunedAt = this.getMetaValue(META_LAST_PRUNED_AT);
|
|
198
|
+
const at = this.now();
|
|
199
|
+
if (lastPrunedAt !== null && at - lastPrunedAt < this.ttlMs)
|
|
200
|
+
return;
|
|
100
201
|
this.pruneExpired();
|
|
202
|
+
this.statements.setMeta.run({ key: META_LAST_PRUNED_AT, value: at });
|
|
203
|
+
}
|
|
204
|
+
/** meta テーブルの単一整数値を返す(未記録ならnull)。 */
|
|
205
|
+
getMetaValue(key) {
|
|
206
|
+
const row = this.statements.getMeta.get(key);
|
|
207
|
+
return row ? Number(row.value) : null;
|
|
101
208
|
}
|
|
102
209
|
/**
|
|
103
|
-
* M3: TTL超過エントリを削除する(ディスク無制限増加の防止)
|
|
210
|
+
* M3: TTL超過エントリを削除する(ディスク無制限増加の防止)。maybePruneExpired経由で呼ばれる。
|
|
104
211
|
* screenshotsはSQLite行に加え、対応するPNGファイル(タイル)もあわせて削除する。
|
|
105
212
|
*/
|
|
106
213
|
pruneExpired() {
|
|
107
214
|
const cutoff = this.now() - this.ttlMs;
|
|
108
|
-
this.
|
|
109
|
-
this.
|
|
215
|
+
this.statements.deletePagesExpired.run(cutoff);
|
|
216
|
+
this.statements.deleteDomainPagesExpired.run(cutoff);
|
|
110
217
|
// code-reviewer指摘: host_requestsもM3の対象から漏れていた(訪問ホスト毎に1行増える
|
|
111
218
|
// 無制限増加テーブルという点でdomain_pages等と同じ性質を持つため、他テーブルと一貫させる)。
|
|
112
|
-
this.
|
|
113
|
-
const expiredScreenshots = this.
|
|
219
|
+
this.statements.deleteHostRequestsExpired.run(cutoff);
|
|
220
|
+
const expiredScreenshots = this.statements.selectScreenshotsExpired.all(cutoff);
|
|
114
221
|
for (const row of expiredScreenshots) {
|
|
115
222
|
const tilePaths = JSON.parse(row.tile_paths);
|
|
116
223
|
for (const filePath of tilePaths) {
|
|
@@ -122,11 +229,11 @@ export class PageCache {
|
|
|
122
229
|
}
|
|
123
230
|
}
|
|
124
231
|
}
|
|
125
|
-
this.
|
|
232
|
+
this.statements.deleteScreenshotsExpired.run(cutoff);
|
|
126
233
|
}
|
|
127
234
|
/** 保存済みエントリを取得する(TTL判定は行わない。判定は isFresh を使う)。 */
|
|
128
235
|
get(url) {
|
|
129
|
-
const row = this.
|
|
236
|
+
const row = this.statements.getPage.get(url);
|
|
130
237
|
if (!row)
|
|
131
238
|
return undefined;
|
|
132
239
|
return {
|
|
@@ -145,7 +252,7 @@ export class PageCache {
|
|
|
145
252
|
}
|
|
146
253
|
/** 保存済みスクリーンショットエントリを取得する(TTL判定は isFresh を使う)。 */
|
|
147
254
|
getScreenshot(cacheKey) {
|
|
148
|
-
const row = this.
|
|
255
|
+
const row = this.statements.getScreenshot.get(cacheKey);
|
|
149
256
|
if (!row)
|
|
150
257
|
return undefined;
|
|
151
258
|
return {
|
|
@@ -156,9 +263,19 @@ export class PageCache {
|
|
|
156
263
|
fetchedAt: row.fetched_at,
|
|
157
264
|
};
|
|
158
265
|
}
|
|
159
|
-
/**
|
|
266
|
+
/**
|
|
267
|
+
* スクリーンショットのタイル群をファイル保存し、パスをSQLiteへ記録する。
|
|
268
|
+
*
|
|
269
|
+
* レビュー指摘対応(Medium): 同一cacheKeyへの再撮影でタイル数が減った場合(旧5枚→新3枚)、
|
|
270
|
+
* 新しいtile_pathsで上書きされるDB行からは旧tile-3.png/tile-4.pngを参照できなくなり、
|
|
271
|
+
* pruneExpiredもそれらを認識できず孤立ファイルとしてディスクに残り続けていた。
|
|
272
|
+
* tileDirはcacheKey単位のディレクトリ(他のcacheKeyのタイルと混在しない)のため、
|
|
273
|
+
* 書き込み前にディレクトリごと削除してから作り直すことで、枚数が変化しても
|
|
274
|
+
* 旧タイルを確実に一掃する(初回撮影時はディレクトリが存在しないためforce:trueで無視する)。
|
|
275
|
+
*/
|
|
160
276
|
setScreenshot(input) {
|
|
161
277
|
const tileDir = join(this.cacheDir, "screenshots", input.cacheKey);
|
|
278
|
+
rmSync(tileDir, { recursive: true, force: true });
|
|
162
279
|
mkdirSync(tileDir, { recursive: true });
|
|
163
280
|
const tilePaths = input.tiles.map((buffer, index) => {
|
|
164
281
|
const filePath = join(tileDir, `tile-${index}.png`);
|
|
@@ -166,15 +283,7 @@ export class PageCache {
|
|
|
166
283
|
return filePath;
|
|
167
284
|
});
|
|
168
285
|
const fetchedAt = this.now();
|
|
169
|
-
this.
|
|
170
|
-
.prepare(`INSERT INTO screenshots (cache_key, url, tile_paths, metadata, fetched_at)
|
|
171
|
-
VALUES (@cacheKey, @url, @tilePaths, @metadata, @fetchedAt)
|
|
172
|
-
ON CONFLICT(cache_key) DO UPDATE SET
|
|
173
|
-
url = excluded.url,
|
|
174
|
-
tile_paths = excluded.tile_paths,
|
|
175
|
-
metadata = excluded.metadata,
|
|
176
|
-
fetched_at = excluded.fetched_at`)
|
|
177
|
-
.run({
|
|
286
|
+
this.statements.setScreenshot.run({
|
|
178
287
|
cacheKey: input.cacheKey,
|
|
179
288
|
url: input.url,
|
|
180
289
|
tilePaths: JSON.stringify(tilePaths),
|
|
@@ -187,17 +296,7 @@ export class PageCache {
|
|
|
187
296
|
set(input) {
|
|
188
297
|
const contentHash = hashContent(input.markdown);
|
|
189
298
|
const fetchedAt = this.now();
|
|
190
|
-
this.
|
|
191
|
-
.prepare(`INSERT INTO pages (url, etag, last_modified, content_hash, markdown, metadata, fetched_at)
|
|
192
|
-
VALUES (@url, @etag, @lastModified, @contentHash, @markdown, @metadata, @fetchedAt)
|
|
193
|
-
ON CONFLICT(url) DO UPDATE SET
|
|
194
|
-
etag = excluded.etag,
|
|
195
|
-
last_modified = excluded.last_modified,
|
|
196
|
-
content_hash = excluded.content_hash,
|
|
197
|
-
markdown = excluded.markdown,
|
|
198
|
-
metadata = excluded.metadata,
|
|
199
|
-
fetched_at = excluded.fetched_at`)
|
|
200
|
-
.run({
|
|
299
|
+
this.statements.setPage.run({
|
|
201
300
|
url: input.url,
|
|
202
301
|
etag: input.etag,
|
|
203
302
|
lastModified: input.lastModified,
|
|
@@ -218,17 +317,11 @@ export class PageCache {
|
|
|
218
317
|
}
|
|
219
318
|
/** 304再検証成功時: 本文は変えず取得時刻のみ更新する(再変換しない)。 */
|
|
220
319
|
touch(url) {
|
|
221
|
-
this.
|
|
320
|
+
this.statements.touchPage.run(this.now(), url);
|
|
222
321
|
}
|
|
223
322
|
/** Phase 4テンプレート学習: このページの(除去前・全)ブロックハッシュ集合をドメイン別に記録する。 */
|
|
224
323
|
recordDomainPageBlocks(domain, url, blockHashes) {
|
|
225
|
-
this.
|
|
226
|
-
.prepare(`INSERT INTO domain_pages (domain, url, fetched_at, block_hashes)
|
|
227
|
-
VALUES (@domain, @url, @fetchedAt, @blockHashes)
|
|
228
|
-
ON CONFLICT(domain, url) DO UPDATE SET
|
|
229
|
-
fetched_at = excluded.fetched_at,
|
|
230
|
-
block_hashes = excluded.block_hashes`)
|
|
231
|
-
.run({ domain, url, fetchedAt: this.now(), blockHashes: JSON.stringify(blockHashes) });
|
|
324
|
+
this.statements.recordDomainPage.run({ domain, url, fetchedAt: this.now(), blockHashes: JSON.stringify(blockHashes) });
|
|
232
325
|
}
|
|
233
326
|
/**
|
|
234
327
|
* 直近recentPages件のページ全てに共通して出現したブロックハッシュを定型ブロック
|
|
@@ -236,9 +329,7 @@ export class PageCache {
|
|
|
236
329
|
* 無い場合(コールドスタート)は誤除去を避けるため空集合を返す。
|
|
237
330
|
*/
|
|
238
331
|
getTemplateBlockHashes(domain, recentPages = DEFAULT_TEMPLATE_RECENT_PAGES) {
|
|
239
|
-
const rows = this.
|
|
240
|
-
.prepare("SELECT * FROM domain_pages WHERE domain = ? ORDER BY fetched_at DESC LIMIT ?")
|
|
241
|
-
.all(domain, recentPages);
|
|
332
|
+
const rows = this.statements.getDomainPages.all(domain, recentPages);
|
|
242
333
|
if (rows.length < recentPages)
|
|
243
334
|
return new Set();
|
|
244
335
|
const occurrenceCount = new Map();
|
|
@@ -260,7 +351,7 @@ export class PageCache {
|
|
|
260
351
|
* PolitenessManagerのstoreオプションとして注入し、プロセス間でレート制御状態を共有するために使う。
|
|
261
352
|
*/
|
|
262
353
|
getHostLastRequestAt(host) {
|
|
263
|
-
const row = this.
|
|
354
|
+
const row = this.statements.getHostRequest.get(host);
|
|
264
355
|
return row ? row.last_request_at : null;
|
|
265
356
|
}
|
|
266
357
|
/**
|
|
@@ -273,13 +364,33 @@ export class PageCache {
|
|
|
273
364
|
* store JSDoc参照)。せめて時刻が巻き戻らないよう、SQL側でMAXを取って単調増加を保証する。
|
|
274
365
|
*/
|
|
275
366
|
setHostLastRequestAt(host, at) {
|
|
276
|
-
this.
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
367
|
+
this.statements.setHostRequest.run({ host, lastRequestAt: at });
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* レビュー指摘対応: オリジン(例 "https://example.com")のrobots.txtキャッシュを返す
|
|
371
|
+
* (未記録ならnull)。PolitenessManagerのrobotsStoreオプションとして注入し、プロセス間で
|
|
372
|
+
* robots.txt取得結果を共有するために使う。TTL判定(robotsTtlMs)は呼び出し元(PolitenessManager)
|
|
373
|
+
* がfetchedAtを見て行う(page cacheのttlMsとは別の時間軸のため、ここでは判定しない)。
|
|
374
|
+
* 空bodyのrobots.txt(=制限なし)も正しい取得結果として保存されるため、
|
|
375
|
+
* 「未記録(null)」と「空bodyの記録あり」は区別される。
|
|
376
|
+
*/
|
|
377
|
+
getRobotsCache(origin) {
|
|
378
|
+
const row = this.statements.getRobots.get(origin);
|
|
379
|
+
return row ? { body: row.body, fetchedAt: row.fetched_at } : null;
|
|
281
380
|
}
|
|
381
|
+
/** オリジンのrobots.txt取得結果(本文+取得時刻)を記録する。既存があれば上書きする。 */
|
|
382
|
+
setRobotsCache(origin, body, fetchedAt) {
|
|
383
|
+
this.statements.setRobots.run({ origin, body, fetchedAt });
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* 冪等なclose。better-sqlite3のclose()は閉鎖済みでも安全だったが、node:sqliteは
|
|
387
|
+
* "database is not open" を投げるため、CLIのfinallyとprocess 'exit'ハンドラの両方から
|
|
388
|
+
* 呼ばれる二重close経路(core.ts参照)を自前のフラグで吸収する。
|
|
389
|
+
*/
|
|
282
390
|
close() {
|
|
391
|
+
if (this.closed)
|
|
392
|
+
return;
|
|
393
|
+
this.closed = true;
|
|
283
394
|
this.db.close();
|
|
284
395
|
}
|
|
285
396
|
}
|
package/dist/cache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
CHANGED
|
@@ -11,7 +11,9 @@ export type ParsedCommand = {
|
|
|
11
11
|
kind: "version";
|
|
12
12
|
} | {
|
|
13
13
|
kind: "help";
|
|
14
|
-
topic?: "fetch" | "links" | "screenshot";
|
|
14
|
+
topic?: "fetch" | "links" | "screenshot" | "install-browser";
|
|
15
|
+
} | {
|
|
16
|
+
kind: "install-browser";
|
|
15
17
|
} | ParsedFetchCommand | ParsedLinksCommand | ParsedScreenshotCommand;
|
|
16
18
|
export interface ParsedFetchCommand {
|
|
17
19
|
kind: "fetch";
|