dsh-ab-wechat-scrape 0.4.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 (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +330 -0
  3. package/cordis.patch.yml +8 -0
  4. package/lib/clean.d.ts +32 -0
  5. package/lib/clean.d.ts.map +1 -0
  6. package/lib/clean.js +193 -0
  7. package/lib/clean.js.map +1 -0
  8. package/lib/document.d.ts +27 -0
  9. package/lib/document.d.ts.map +1 -0
  10. package/lib/document.js +79 -0
  11. package/lib/document.js.map +1 -0
  12. package/lib/fetch.d.ts +84 -0
  13. package/lib/fetch.d.ts.map +1 -0
  14. package/lib/fetch.js +238 -0
  15. package/lib/fetch.js.map +1 -0
  16. package/lib/filename.d.ts +36 -0
  17. package/lib/filename.d.ts.map +1 -0
  18. package/lib/filename.js +92 -0
  19. package/lib/filename.js.map +1 -0
  20. package/lib/html.d.ts +114 -0
  21. package/lib/html.d.ts.map +1 -0
  22. package/lib/html.js +402 -0
  23. package/lib/html.js.map +1 -0
  24. package/lib/index.d.ts +219 -0
  25. package/lib/index.d.ts.map +1 -0
  26. package/lib/index.js +2523 -0
  27. package/lib/index.js.map +1 -0
  28. package/lib/list.d.ts +55 -0
  29. package/lib/list.d.ts.map +1 -0
  30. package/lib/list.js +191 -0
  31. package/lib/list.js.map +1 -0
  32. package/lib/markdown.d.ts +20 -0
  33. package/lib/markdown.d.ts.map +1 -0
  34. package/lib/markdown.js +250 -0
  35. package/lib/markdown.js.map +1 -0
  36. package/lib/render.d.ts +103 -0
  37. package/lib/render.d.ts.map +1 -0
  38. package/lib/render.js +209 -0
  39. package/lib/render.js.map +1 -0
  40. package/lib/store.d.ts +50 -0
  41. package/lib/store.d.ts.map +1 -0
  42. package/lib/store.js +173 -0
  43. package/lib/store.js.map +1 -0
  44. package/lib/types.d.ts +133 -0
  45. package/lib/types.d.ts.map +1 -0
  46. package/lib/types.js +8 -0
  47. package/lib/types.js.map +1 -0
  48. package/package.json +91 -0
  49. package/tsconfig.json +30 -0
  50. package/tsdown.config.ts +18 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Browser rendering for pages whose article body is built by client script, and
3
+ * for pages the plain HTTP path is refused. Playwright is imported lazily
4
+ * through a variable specifier, so the plugin loads, and every statically
5
+ * served article still works, when the optional driver is absent. One browser
6
+ * instance is reused and closed on an idle timer or when the owning fiber
7
+ * disposes. A render waits for the configured body selector to fill, bounded by
8
+ * its own window, instead of trusting a fixed settle time.
9
+ * @module @deepseek-ai/dsh-ab-wechat-scrape/render
10
+ */
11
+ /** Deployment values the renderer reads. */
12
+ export interface RenderPolicy {
13
+ /** Driver that produces rendered HTML. */
14
+ driver: 'playwright' | 'http';
15
+ /** Endpoint the http driver posts to. */
16
+ url: string;
17
+ /** Playwright browser channel, used when no executable path is set. */
18
+ channel: string;
19
+ /** Explicit browser executable, overriding the channel. */
20
+ executablePath: string;
21
+ /** Whether the browser runs headless. */
22
+ headless: boolean;
23
+ /** Milliseconds an idle browser is kept before it is closed. */
24
+ idleMs: number;
25
+ /** Extra milliseconds to wait after navigation for lazy content and images. */
26
+ settleMs: number;
27
+ /** Selector the scripted article body fills; empty skips the readiness wait. */
28
+ readySelector: string;
29
+ /** Longest milliseconds the readiness wait may take before the page is read as it stands. */
30
+ waitMs: number;
31
+ /** Navigation timeout in milliseconds. */
32
+ timeoutMs: number;
33
+ /** User agent the browser context sends. */
34
+ userAgent: string;
35
+ /** Referer the browser context sends; omitted when empty. */
36
+ referer: string;
37
+ /** Cookie header copied into the browser context. */
38
+ cookie: string;
39
+ /** Shared request pacing, so a render respects the deployment's interval. */
40
+ pace: (signal?: AbortSignal) => Promise<void>;
41
+ }
42
+ /** One browser renderer the plugin instance owns. */
43
+ export interface Renderer {
44
+ /**
45
+ * Render one page and return its final HTML.
46
+ * @param url - the page to render.
47
+ * @param signal - caller cancellation; an aborted render closes its page.
48
+ * @returns the rendered HTML.
49
+ */
50
+ render(url: string, signal?: AbortSignal): Promise<string>;
51
+ /**
52
+ * Close the browser and cancel the idle timer.
53
+ * @returns a promise that settles once the browser is closed.
54
+ */
55
+ dispose(): Promise<void>;
56
+ }
57
+ /** The subset of Playwright's chromium launcher this plugin uses. */
58
+ interface ChromiumLauncher {
59
+ /** Start one browser instance. */
60
+ launch(options: Record<string, unknown>): Promise<BrowserLike>;
61
+ }
62
+ /** The subset of a Playwright browser this plugin uses. */
63
+ interface BrowserLike {
64
+ /** Open an isolated context. */
65
+ newContext(options: Record<string, unknown>): Promise<ContextLike>;
66
+ /** Whether the browser process is still alive. */
67
+ isConnected(): boolean;
68
+ /** Terminate the browser process. */
69
+ close(): Promise<void>;
70
+ }
71
+ /** The subset of a Playwright context this plugin uses. */
72
+ interface ContextLike {
73
+ /** Open a page inside the context. */
74
+ newPage(): Promise<PageLike>;
75
+ /** Seed the context with cookies parsed from the deployment's Cookie header. */
76
+ addCookies(cookies: readonly Record<string, string>[]): Promise<void>;
77
+ /** Close the context and every page it owns. */
78
+ close(): Promise<void>;
79
+ }
80
+ /** The subset of a Playwright page this plugin uses. */
81
+ interface PageLike {
82
+ /** Navigate, resolving once the requested lifecycle event fires. */
83
+ goto(url: string, options: Record<string, unknown>): Promise<unknown>;
84
+ /** Poll one expression until it is true, or reject once the timeout elapses. */
85
+ waitForFunction(expression: string, argument: unknown, options: Record<string, unknown>): Promise<unknown>;
86
+ /** Wait a fixed number of milliseconds. */
87
+ waitForTimeout(ms: number): Promise<void>;
88
+ /** Evaluate one expression in the page. */
89
+ evaluate(expression: string): Promise<unknown>;
90
+ /** The page's current HTML. */
91
+ content(): Promise<string>;
92
+ /** Close the page. */
93
+ close(): Promise<void>;
94
+ }
95
+ /**
96
+ * Build the plugin instance's renderer.
97
+ * @param policy - the deployment's driver, browser, and timing values.
98
+ * @param load - launcher factory, replaced by tests with a structural fake.
99
+ * @returns the renderer whose dispose() the plugin fiber owns.
100
+ */
101
+ export declare function createRenderer(policy: RenderPolicy, load?: () => Promise<ChromiumLauncher>): Renderer;
102
+ export {};
103
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,4CAA4C;AAC5C,MAAM,WAAW,YAAY;IAC3B,0CAA0C;IAC1C,MAAM,EAAE,YAAY,GAAG,MAAM,CAAA;IAC7B,yCAAyC;IACzC,GAAG,EAAE,MAAM,CAAA;IACX,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAA;IACf,2DAA2D;IAC3D,cAAc,EAAE,MAAM,CAAA;IACtB,yCAAyC;IACzC,QAAQ,EAAE,OAAO,CAAA;IACjB,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAA;IACd,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAA;IAChB,gFAAgF;IAChF,aAAa,EAAE,MAAM,CAAA;IACrB,6FAA6F;IAC7F,MAAM,EAAE,MAAM,CAAA;IACd,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAA;IACjB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAA;IACjB,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAA;IACf,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAA;IACd,6EAA6E;IAC7E,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9C;AAED,qDAAqD;AACrD,MAAM,WAAW,QAAQ;IACvB;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC1D;;;OAGG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAED,qEAAqE;AACrE,UAAU,gBAAgB;IACxB,kCAAkC;IAClC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;CAC/D;AAED,2DAA2D;AAC3D,UAAU,WAAW;IACnB,gCAAgC;IAChC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IAClE,kDAAkD;IAClD,WAAW,IAAI,OAAO,CAAA;IACtB,qCAAqC;IACrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED,2DAA2D;AAC3D,UAAU,WAAW;IACnB,sCAAsC;IACtC,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC5B,gFAAgF;IAChF,UAAU,CAAC,OAAO,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrE,gDAAgD;IAChD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED,wDAAwD;AACxD,UAAU,QAAQ;IAChB,oEAAoE;IACpE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACrE,gFAAgF;IAChF,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC1G,2CAA2C;IAC3C,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzC,2CAA2C;IAC3C,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9C,+BAA+B;IAC/B,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IAC1B,sBAAsB;IACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAmED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,GAAE,MAAM,OAAO,CAAC,gBAAgB,CAAgB,GAAG,QAAQ,CAiHnH"}
package/lib/render.js ADDED
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Browser rendering for pages whose article body is built by client script, and
3
+ * for pages the plain HTTP path is refused. Playwright is imported lazily
4
+ * through a variable specifier, so the plugin loads, and every statically
5
+ * served article still works, when the optional driver is absent. One browser
6
+ * instance is reused and closed on an idle timer or when the owning fiber
7
+ * disposes. A render waits for the configured body selector to fill, bounded by
8
+ * its own window, instead of trusting a fixed settle time.
9
+ * @module @deepseek-ai/dsh-ab-wechat-scrape/render
10
+ */
11
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
12
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
13
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
14
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
15
+ });
16
+ }
17
+ return path;
18
+ };
19
+ /** Extra milliseconds lazy images need after the page is scrolled to its end. */
20
+ const SCROLL_SETTLE_MS = 400;
21
+ /**
22
+ * The expression the readiness wait polls: the article body element exists and
23
+ * carries text or an image. A body that stayed empty is what a page with no
24
+ * article behind the link looks like, so the caller reads the page as it stands
25
+ * rather than waiting the whole window out for nothing.
26
+ * @param selector - the selector the scripted body fills.
27
+ * @returns a self-contained expression whose value is a boolean.
28
+ */
29
+ function readinessExpression(selector) {
30
+ return '(() => { const body = document.querySelector(' + JSON.stringify(selector) + '); '
31
+ + 'if (body === null) return false; '
32
+ + 'return (body.textContent || "").trim().length > 0 || body.querySelector("img") !== null; })()';
33
+ }
34
+ /**
35
+ * Load the optional Playwright package through a variable specifier, so the
36
+ * emitted bundle never inlines it and a deployment without it still loads.
37
+ * @returns the chromium launcher.
38
+ */
39
+ async function loadChromium() {
40
+ const specifier = 'playwright';
41
+ let loaded;
42
+ try {
43
+ loaded = await import(__rewriteRelativeImportExtension(specifier));
44
+ }
45
+ catch {
46
+ // The package is optional: a deployment without it gets an actionable error
47
+ // only when a page actually needs rendering.
48
+ throw new Error('wechat: JS rendering needs the optional playwright package installed for this plugin');
49
+ }
50
+ const chromium = loaded.chromium;
51
+ if (chromium === undefined)
52
+ throw new Error('wechat: the playwright package exposes no chromium launcher');
53
+ return chromium;
54
+ }
55
+ /**
56
+ * Split one Cookie header into Playwright cookie objects for the target host.
57
+ * @param header - the deployment's Cookie header.
58
+ * @param target - the page being rendered.
59
+ * @returns one cookie object per header entry.
60
+ */
61
+ function cookiesFor(header, target) {
62
+ let host = 'mp.weixin.qq.com';
63
+ try {
64
+ host = new URL(target).hostname;
65
+ }
66
+ catch {
67
+ // An unparseable target keeps the default host; the request itself will fail visibly.
68
+ }
69
+ return header
70
+ .split(';')
71
+ .map(part => part.trim())
72
+ .filter(part => part !== '')
73
+ .map((part) => {
74
+ const equals = part.indexOf('=');
75
+ return {
76
+ name: equals === -1 ? part : part.slice(0, equals),
77
+ value: equals === -1 ? '' : part.slice(equals + 1),
78
+ domain: host,
79
+ path: '/',
80
+ };
81
+ });
82
+ }
83
+ /**
84
+ * Build the plugin instance's renderer.
85
+ * @param policy - the deployment's driver, browser, and timing values.
86
+ * @param load - launcher factory, replaced by tests with a structural fake.
87
+ * @returns the renderer whose dispose() the plugin fiber owns.
88
+ */
89
+ export function createRenderer(policy, load = loadChromium) {
90
+ let browser;
91
+ let launching;
92
+ let idleTimer;
93
+ const closeBrowser = async () => {
94
+ if (idleTimer !== undefined) {
95
+ clearTimeout(idleTimer);
96
+ idleTimer = undefined;
97
+ }
98
+ const current = browser;
99
+ browser = undefined;
100
+ launching = undefined;
101
+ if (current !== undefined) {
102
+ // A browser that already died rejects close(); there is nothing left to report.
103
+ await current.close().catch(() => undefined);
104
+ }
105
+ };
106
+ const scheduleIdleClose = () => {
107
+ if (idleTimer !== undefined)
108
+ clearTimeout(idleTimer);
109
+ idleTimer = setTimeout(() => { void closeBrowser(); }, policy.idleMs);
110
+ };
111
+ const acquire = async () => {
112
+ if (idleTimer !== undefined) {
113
+ clearTimeout(idleTimer);
114
+ idleTimer = undefined;
115
+ }
116
+ if (browser?.isConnected() === true)
117
+ return browser;
118
+ if (launching === undefined) {
119
+ launching = (async () => {
120
+ const chromium = await load();
121
+ const options = { headless: policy.headless };
122
+ if (policy.executablePath !== '')
123
+ options.executablePath = policy.executablePath;
124
+ else if (policy.channel !== '')
125
+ options.channel = policy.channel;
126
+ return chromium.launch(options);
127
+ })();
128
+ // A failed launch must not poison the cache; the awaiting caller still sees the error.
129
+ void launching.catch(() => { launching = undefined; });
130
+ }
131
+ const started = await launching;
132
+ browser = started;
133
+ return started;
134
+ };
135
+ const renderWithBrowser = async (url, signal) => {
136
+ const active = await acquire();
137
+ const contextOptions = {
138
+ userAgent: policy.userAgent,
139
+ locale: 'zh-CN',
140
+ viewport: { width: 1280, height: 900 },
141
+ };
142
+ if (policy.referer !== '')
143
+ contextOptions.extraHTTPHeaders = { referer: policy.referer };
144
+ const context = await active.newContext(contextOptions);
145
+ try {
146
+ if (policy.cookie !== '')
147
+ await context.addCookies(cookiesFor(policy.cookie, url));
148
+ const page = await context.newPage();
149
+ const onAbort = () => { void page.close().catch(() => undefined); };
150
+ signal?.addEventListener('abort', onAbort, { once: true });
151
+ try {
152
+ await page.goto(url, { waitUntil: 'domcontentloaded', timeout: policy.timeoutMs });
153
+ // The body element is what tells a scripted page apart from one that has
154
+ // nothing behind the link, so the render waits for it to fill rather than
155
+ // guessing a fixed settle time. A body that never fills falls through to
156
+ // the same settle window, and the caller refuses the empty body.
157
+ if (policy.readySelector !== '') {
158
+ try {
159
+ await page.waitForFunction(readinessExpression(policy.readySelector), undefined, { timeout: policy.waitMs });
160
+ }
161
+ catch {
162
+ // Only the readiness window's expiry is swallowed: every other failure
163
+ // reaches the caller through content() below or the surrounding finally.
164
+ }
165
+ }
166
+ await page.waitForTimeout(policy.settleMs);
167
+ // Scrolling to the end triggers the lazy images WeChat defers until visible.
168
+ await page.evaluate('window.scrollTo(0, document.body.scrollHeight)');
169
+ await page.waitForTimeout(SCROLL_SETTLE_MS);
170
+ return await page.content();
171
+ }
172
+ finally {
173
+ signal?.removeEventListener('abort', onAbort);
174
+ }
175
+ }
176
+ finally {
177
+ await context.close().catch(() => undefined);
178
+ scheduleIdleClose();
179
+ }
180
+ };
181
+ const renderWithHttp = async (url, signal) => {
182
+ if (policy.url === '')
183
+ throw new Error('wechat: the http renderer driver needs a rendererUrl');
184
+ const timeout = AbortSignal.timeout(policy.timeoutMs);
185
+ const combined = signal === undefined ? timeout : AbortSignal.any([signal, timeout]);
186
+ const response = await fetch(policy.url, {
187
+ method: 'POST',
188
+ headers: { 'content-type': 'application/json', accept: 'text/html, application/json' },
189
+ body: JSON.stringify({ url }),
190
+ signal: combined,
191
+ });
192
+ if (!response.ok)
193
+ throw new Error('wechat: the renderer endpoint returned HTTP ' + response.status);
194
+ const text = await response.text();
195
+ if (!(response.headers.get('content-type') ?? '').includes('json'))
196
+ return text;
197
+ const parsed = JSON.parse(text);
198
+ const html = typeof parsed === 'object' && parsed !== null ? parsed.html : undefined;
199
+ if (typeof html !== 'string' || html === '')
200
+ throw new Error('wechat: the renderer response carried no html string');
201
+ return html;
202
+ };
203
+ const render = async (url, signal) => {
204
+ await policy.pace(signal);
205
+ return policy.driver === 'http' ? renderWithHttp(url, signal) : renderWithBrowser(url, signal);
206
+ };
207
+ return { render, dispose: closeBrowser };
208
+ }
209
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
package/lib/store.d.ts ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * File persistence for scraped articles: the article's file name, the
3
+ * same-title collision rule, and the write itself. Every operation goes through
4
+ * the mounted `ctx.fs` capability rather than the host filesystem directly, so
5
+ * a deployment's file policy sees the write and its own backend decides how the
6
+ * bytes are published. The calling session's per-call sandbox policy travels
7
+ * with the write, which is what lets a confining backend fence it against the
8
+ * workspace the session actually runs in.
9
+ * @module @deepseek-ai/dsh-ab-wechat-scrape/store
10
+ */
11
+ import type { SandboxExecutionPolicy } from '@deepseek-ai/dsh-sandbox';
12
+ import type FileSystem from '@deepseek-ai/dsh-fs';
13
+ import type { CollectedArticle, DraftedArticle } from './types.ts';
14
+ /** Deployment values one save reads. */
15
+ export interface StorePolicy {
16
+ /** Absolute directory the article files are written to. */
17
+ dir: string;
18
+ /** Longest base name, before the extension, that normalization may produce. */
19
+ fileNameMaxChars: number;
20
+ /** Per-call policy the confining filesystem fences the write by; absent on a bare backend. */
21
+ sandboxPolicy?: SandboxExecutionPolicy | undefined;
22
+ }
23
+ /**
24
+ * Drop the body, which now lives in the file, from the value the call records.
25
+ *
26
+ * Written out field by field rather than spread-and-delete: adding a field to the
27
+ * article then stops this compiling, instead of silently leaving the new field —
28
+ * and any body-sized thing it might carry — in the value and the log.
29
+ * @param drafted - the article as it was built, body included.
30
+ * @returns the article as the call records it.
31
+ */
32
+ export declare function recordedArticle(drafted: DraftedArticle): CollectedArticle;
33
+ /**
34
+ * Write one article's Markdown file, and return the value the call records.
35
+ *
36
+ * The body stops here: it is written to the file, and what the caller gets back is
37
+ * the article without it. That is the whole point of the split — a call that
38
+ * returned the body would pay the article's full length in context and in the log,
39
+ * then have the reader look at a window of it anyway.
40
+ * @param article - the article as it was built, body included.
41
+ * @param policy - the deployment's output directory, name ceiling, and per-call sandbox policy.
42
+ * @param fs - the mounted filesystem capability the write goes through.
43
+ * @param signal - caller cancellation, forwarded to the write.
44
+ * @returns the saved path, in the backend's execution world, and the recorded value.
45
+ */
46
+ export declare function saveArticle(article: DraftedArticle, policy: StorePolicy, fs: FileSystem, signal?: AbortSignal): Promise<{
47
+ file: string;
48
+ article: CollectedArticle;
49
+ }>;
50
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AACtE,OAAO,KAAK,UAAU,MAAM,qBAAqB,CAAA;AAIjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAElE,wCAAwC;AACxC,MAAM,WAAW,WAAW;IAC1B,2DAA2D;IAC3D,GAAG,EAAE,MAAM,CAAA;IACX,+EAA+E;IAC/E,gBAAgB,EAAE,MAAM,CAAA;IACxB,8FAA8F;IAC9F,aAAa,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAA;CACnD;AAwBD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,gBAAgB,CAgBzE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,WAAW,EACnB,EAAE,EAAE,UAAU,EACd,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,CAAC,CAKtD"}
package/lib/store.js ADDED
@@ -0,0 +1,173 @@
1
+ /**
2
+ * File persistence for scraped articles: the article's file name, the
3
+ * same-title collision rule, and the write itself. Every operation goes through
4
+ * the mounted `ctx.fs` capability rather than the host filesystem directly, so
5
+ * a deployment's file policy sees the write and its own backend decides how the
6
+ * bytes are published. The calling session's per-call sandbox policy travels
7
+ * with the write, which is what lets a confining backend fence it against the
8
+ * workspace the session actually runs in.
9
+ * @module @deepseek-ai/dsh-ab-wechat-scrape/store
10
+ */
11
+ import { join } from 'node:path';
12
+ import { articleDocument } from "./document.js";
13
+ import { articleFileName, FILE_EXTENSION } from "./filename.js";
14
+ /** Number-suffixed names tried before a colliding name falls back to a timestamp. */
15
+ const MAX_COLLISION_SUFFIX = 50;
16
+ /** Opening line of the YAML front matter every article file starts with. */
17
+ const FRONT_MATTER_FENCE = '---';
18
+ /** The structured code a sandboxing filesystem reports a fenced write with. */
19
+ const SANDBOX_DENIED = 'FS_SANDBOX_DENIED';
20
+ /** Front-matter key carrying the article's resolved link. */
21
+ const SOURCE_KEY = 'source: ';
22
+ /**
23
+ * Whether one failure is the file sandbox refusing the write, judged by the
24
+ * stable code the capability publishes rather than by its message text.
25
+ * @param error - the error the write threw.
26
+ * @returns true when a confining filesystem denied the write.
27
+ */
28
+ function isSandboxDenial(error) {
29
+ return typeof error === 'object' && error !== null && error.code === SANDBOX_DENIED;
30
+ }
31
+ /**
32
+ * Drop the body, which now lives in the file, from the value the call records.
33
+ *
34
+ * Written out field by field rather than spread-and-delete: adding a field to the
35
+ * article then stops this compiling, instead of silently leaving the new field —
36
+ * and any body-sized thing it might carry — in the value and the log.
37
+ * @param drafted - the article as it was built, body included.
38
+ * @returns the article as the call records it.
39
+ */
40
+ export function recordedArticle(drafted) {
41
+ return {
42
+ url: drafted.url,
43
+ resolvedUrl: drafted.resolvedUrl,
44
+ title: drafted.title,
45
+ account: drafted.account,
46
+ author: drafted.author,
47
+ publishTime: drafted.publishTime,
48
+ digest: drafted.digest,
49
+ cover: drafted.cover,
50
+ chars: drafted.chars,
51
+ excerpt: drafted.excerpt,
52
+ excerptClipped: drafted.excerptClipped,
53
+ images: drafted.images,
54
+ rendered: drafted.rendered,
55
+ };
56
+ }
57
+ /**
58
+ * Write one article's Markdown file, and return the value the call records.
59
+ *
60
+ * The body stops here: it is written to the file, and what the caller gets back is
61
+ * the article without it. That is the whole point of the split — a call that
62
+ * returned the body would pay the article's full length in context and in the log,
63
+ * then have the reader look at a window of it anyway.
64
+ * @param article - the article as it was built, body included.
65
+ * @param policy - the deployment's output directory, name ceiling, and per-call sandbox policy.
66
+ * @param fs - the mounted filesystem capability the write goes through.
67
+ * @param signal - caller cancellation, forwarded to the write.
68
+ * @returns the saved path, in the backend's execution world, and the recorded value.
69
+ */
70
+ export async function saveArticle(article, policy, fs, signal) {
71
+ const name = articleFileName(article.title, policy.fileNameMaxChars, article.resolvedUrl);
72
+ const target = await resolveTarget(fs, policy.dir, name, article.resolvedUrl);
73
+ await write(fs, target, articleDocument(article) + '\n', policy, signal);
74
+ return { file: fs.processPath(target), article: recordedArticle(article) };
75
+ }
76
+ /**
77
+ * Publish one article file, turning a sandbox refusal into the one instruction
78
+ * that resolves it.
79
+ *
80
+ * A refusal under a stamped policy is a statement about the output directory
81
+ * rather than about the article: the confining backend fences the write by the
82
+ * workspace root the calling session runs in, so an outputDir outside that root
83
+ * has to move. Reporting the bare provider text would leave that unsaid. A
84
+ * call that stamped no policy gets the provider's own message, which already
85
+ * names the mode it was fenced by.
86
+ * @param fs - the mounted filesystem capability.
87
+ * @param target - the resolved file target.
88
+ * @param document - the complete file content.
89
+ * @param policy - the deployment's output directory and per-call sandbox policy.
90
+ * @param signal - caller cancellation.
91
+ * @throws Error the provider raised, or the same failure restated with the remedy.
92
+ */
93
+ async function write(fs, target, document, policy, signal) {
94
+ try {
95
+ await fs.writeText(target, document, undefined, signal, policy.sandboxPolicy);
96
+ }
97
+ catch (error) {
98
+ const stamped = policy.sandboxPolicy;
99
+ if (stamped === undefined || !isSandboxDenial(error))
100
+ throw error;
101
+ throw new Error('wechat: the file sandbox refused to write ' + fs.processPath(target)
102
+ + ' under mode ' + stamped.mode + '; this session\'s workspace is ' + stamped.workspaceRoot
103
+ + ', so set the plugin row\'s outputDir inside it', { cause: error });
104
+ }
105
+ }
106
+ /**
107
+ * Choose the file one article is written to.
108
+ * @param fs - the mounted filesystem capability.
109
+ * @param dir - the absolute output directory.
110
+ * @param name - the normalized file name, extension included.
111
+ * @param source - the article's resolved link, the identity a collision is judged by.
112
+ * @returns a free target, or the target already holding this same article.
113
+ */
114
+ async function resolveTarget(fs, dir, name, source) {
115
+ const base = name.slice(0, -FILE_EXTENSION.length);
116
+ const first = await fs.resolve(join(dir, name));
117
+ if (await matchesOrIsFree(fs, first, source))
118
+ return first;
119
+ for (let suffix = 2; suffix <= MAX_COLLISION_SUFFIX; suffix += 1) {
120
+ const candidate = await fs.resolve(join(dir, base + '-' + suffix + FILE_EXTENSION));
121
+ if (await matchesOrIsFree(fs, candidate, source))
122
+ return candidate;
123
+ }
124
+ return fs.resolve(join(dir, base + '-' + Date.now() + FILE_EXTENSION));
125
+ }
126
+ /**
127
+ * Whether one target is free, or already holds the article with this resolved link.
128
+ * @param fs - the mounted filesystem capability.
129
+ * @param target - the candidate file target.
130
+ * @param source - the article's resolved link.
131
+ * @returns true when the caller may write the article to this target.
132
+ */
133
+ async function matchesOrIsFree(fs, target, source) {
134
+ if (await fs.stat(target) === undefined)
135
+ return true;
136
+ return (await recordedSource(fs, target)) === source;
137
+ }
138
+ /**
139
+ * The resolved link recorded in one file's front matter.
140
+ * @param fs - the mounted filesystem capability.
141
+ * @param target - the existing file target.
142
+ * @returns the recorded link, or an empty string when the file is unreadable or carries none.
143
+ */
144
+ async function recordedSource(fs, target) {
145
+ let text;
146
+ try {
147
+ text = await fs.readText(target);
148
+ }
149
+ catch {
150
+ // A file the backend refuses to read cannot be identified as this article,
151
+ // so the caller picks another name rather than overwriting it.
152
+ return '';
153
+ }
154
+ const lines = text.split('\n');
155
+ if (lines[0] !== FRONT_MATTER_FENCE)
156
+ return '';
157
+ for (const line of lines.slice(1)) {
158
+ if (line === FRONT_MATTER_FENCE)
159
+ break;
160
+ if (!line.startsWith(SOURCE_KEY))
161
+ continue;
162
+ try {
163
+ const parsed = JSON.parse(line.slice(SOURCE_KEY.length));
164
+ return typeof parsed === 'string' ? parsed : '';
165
+ }
166
+ catch {
167
+ // A front-matter value this plugin did not write carries no usable identity.
168
+ return '';
169
+ }
170
+ }
171
+ return '';
172
+ }
173
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAIhC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAa/D,qFAAqF;AACrF,MAAM,oBAAoB,GAAG,EAAE,CAAA;AAE/B,4EAA4E;AAC5E,MAAM,kBAAkB,GAAG,KAAK,CAAA;AAEhC,+EAA+E;AAC/E,MAAM,cAAc,GAAG,mBAAmB,CAAA;AAE1C,6DAA6D;AAC7D,MAAM,UAAU,GAAG,UAAU,CAAA;AAE7B;;;;;GAKG;AACH,SAAS,eAAe,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAK,KAA4B,CAAC,IAAI,KAAK,cAAc,CAAA;AAC7G,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,OAAuB;IACrD,OAAO;QACL,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,OAAuB,EACvB,MAAmB,EACnB,EAAc,EACd,MAAoB;IAEpB,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,gBAAgB,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IACzF,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,EAAE,EAAE,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAC7E,MAAM,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACxE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,CAAA;AAC5E,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,KAAK,UAAU,KAAK,CAClB,EAAc,EACd,MAAgB,EAChB,QAAgB,EAChB,MAAmB,EACnB,MAAoB;IAEpB,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC,CAAA;IAC/E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,CAAA;QACpC,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAA;QACjE,MAAM,IAAI,KAAK,CAAC,4CAA4C,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC;cACjF,cAAc,GAAG,OAAO,CAAC,IAAI,GAAG,iCAAiC,GAAG,OAAO,CAAC,aAAa;cACzF,gDAAgD,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;IACzE,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,aAAa,CAAC,EAAc,EAAE,GAAW,EAAE,IAAY,EAAE,MAAc;IACpF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;IAClD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAA;IAC/C,IAAI,MAAM,eAAe,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC;QAAE,OAAO,KAAK,CAAA;IAC1D,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,IAAI,oBAAoB,EAAE,MAAM,IAAI,CAAC,EAAE,CAAC;QACjE,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,GAAG,GAAG,GAAG,MAAM,GAAG,cAAc,CAAC,CAAC,CAAA;QACnF,IAAI,MAAM,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC;YAAE,OAAO,SAAS,CAAA;IACpE,CAAC;IACD,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc,CAAC,CAAC,CAAA;AACxE,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,eAAe,CAAC,EAAc,EAAE,MAAgB,EAAE,MAAc;IAC7E,IAAI,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,SAAS;QAAE,OAAO,IAAI,CAAA;IACpD,OAAO,CAAC,MAAM,cAAc,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,KAAK,MAAM,CAAA;AACtD,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,cAAc,CAAC,EAAc,EAAE,MAAgB;IAC5D,IAAI,IAAY,CAAA;IAChB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,2EAA2E;QAC3E,+DAA+D;QAC/D,OAAO,EAAE,CAAA;IACX,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC9B,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,kBAAkB;QAAE,OAAO,EAAE,CAAA;IAC9C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAClC,IAAI,IAAI,KAAK,kBAAkB;YAAE,MAAK;QACtC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;YAAE,SAAQ;QAC1C,IAAI,CAAC;YACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAA;YACjE,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,6EAA6E;YAC7E,OAAO,EAAE,CAAA;QACX,CAAC;IACH,CAAC;IACD,OAAO,EAAE,CAAA;AACX,CAAC"}