@nitpicker/core 0.24.0 → 0.25.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 (83) hide show
  1. package/README.md +165 -35
  2. package/lib/assert-u32-id.d.ts +15 -0
  3. package/lib/assert-u32-id.js +22 -0
  4. package/lib/check-native-executor-config.d.ts +21 -0
  5. package/lib/check-native-executor-config.js +24 -0
  6. package/lib/compute-alias-keys.d.ts +28 -0
  7. package/lib/compute-alias-keys.js +30 -0
  8. package/lib/compute-body-hash.d.ts +22 -0
  9. package/lib/compute-body-hash.js +25 -0
  10. package/lib/compute-url-sort-ranks.d.ts +28 -0
  11. package/lib/compute-url-sort-ranks.js +49 -0
  12. package/lib/create-isolated-cluster-run.d.ts +35 -0
  13. package/lib/create-isolated-cluster-run.js +52 -0
  14. package/lib/create-template-classification-run.d.ts +49 -0
  15. package/lib/create-template-classification-run.js +386 -0
  16. package/lib/group-alias-candidates.d.ts +40 -0
  17. package/lib/group-alias-candidates.js +104 -0
  18. package/lib/load-native-binding.d.ts +26 -0
  19. package/lib/load-native-binding.js +152 -0
  20. package/lib/match-js-license-comment-signal.d.ts +19 -0
  21. package/lib/match-js-license-comment-signal.js +21 -0
  22. package/lib/match-selector-html.d.ts +41 -0
  23. package/lib/match-selector-html.js +42 -0
  24. package/lib/match-selector-snapshots.d.ts +40 -0
  25. package/lib/match-selector-snapshots.js +57 -0
  26. package/lib/match-structural-technology-signals.d.ts +23 -0
  27. package/lib/match-structural-technology-signals.js +25 -0
  28. package/lib/max-decoded-snapshot-bytes.d.ts +22 -0
  29. package/lib/max-decoded-snapshot-bytes.js +35 -0
  30. package/lib/native-binding.d.ts +9 -0
  31. package/lib/native-binding.js +9 -0
  32. package/lib/pack-stored-snapshots.d.ts +23 -0
  33. package/lib/pack-stored-snapshots.js +53 -0
  34. package/lib/run-with-signal.d.ts +27 -0
  35. package/lib/run-with-signal.js +54 -0
  36. package/lib/search-html-document.d.ts +32 -0
  37. package/lib/search-html-document.js +31 -0
  38. package/lib/search-html-snapshots.d.ts +46 -0
  39. package/lib/search-html-snapshots.js +54 -0
  40. package/lib/template-block-sample-size.d.ts +13 -0
  41. package/lib/template-block-sample-size.js +14 -0
  42. package/lib/template-exact-page-limit.d.ts +18 -0
  43. package/lib/template-exact-page-limit.js +19 -0
  44. package/lib/template-memory-budget.d.ts +16 -0
  45. package/lib/template-memory-budget.js +17 -0
  46. package/lib/to-native-selector-plan.d.ts +16 -0
  47. package/lib/to-native-selector-plan.js +40 -0
  48. package/lib/to-u32-ids.d.ts +14 -0
  49. package/lib/to-u32-ids.js +23 -0
  50. package/lib/types.d.ts +736 -248
  51. package/lib/validate-search-pattern.d.ts +25 -0
  52. package/lib/validate-search-pattern.js +27 -0
  53. package/package.json +83 -16
  54. package/lib/core.d.ts +0 -4
  55. package/lib/core.js +0 -4
  56. package/lib/discover-analyze-plugins.d.ts +0 -14
  57. package/lib/discover-analyze-plugins.js +0 -32
  58. package/lib/get-table-cache-root.d.ts +0 -18
  59. package/lib/get-table-cache-root.js +0 -22
  60. package/lib/hooks/define-plugin.d.ts +0 -68
  61. package/lib/hooks/define-plugin.js +0 -69
  62. package/lib/import-modules.d.ts +0 -24
  63. package/lib/import-modules.js +0 -38
  64. package/lib/load-plugin-settings.d.ts +0 -44
  65. package/lib/load-plugin-settings.js +0 -125
  66. package/lib/nitpicker.d.ts +0 -150
  67. package/lib/nitpicker.js +0 -437
  68. package/lib/page-analysis-worker.d.ts +0 -58
  69. package/lib/page-analysis-worker.js +0 -68
  70. package/lib/read-plugin-labels.d.ts +0 -15
  71. package/lib/read-plugin-labels.js +0 -30
  72. package/lib/table.d.ts +0 -75
  73. package/lib/table.js +0 -132
  74. package/lib/url-event-bus.d.ts +0 -33
  75. package/lib/url-event-bus.js +0 -21
  76. package/lib/worker/runner.d.ts +0 -25
  77. package/lib/worker/runner.js +0 -31
  78. package/lib/worker/types.d.ts +0 -23
  79. package/lib/worker/types.js +0 -1
  80. package/lib/worker/worker-pool.d.ts +0 -82
  81. package/lib/worker/worker-pool.js +0 -278
  82. package/lib/worker/worker.d.ts +0 -34
  83. package/lib/worker/worker.js +0 -108
@@ -1,68 +0,0 @@
1
- /**
2
- * Worker thread module for per-page single-plugin execution.
3
- *
4
- * This is the default export loaded by {@link ./worker/runner.ts!runner}
5
- * for each task dispatched by the {@link ./worker/worker-pool.ts!WorkerPool}. It:
6
- *
7
- * 1. Dynamically imports the configured plugin via {@link importModules}
8
- * (cached after the first task on the same worker)
9
- * 2. If the plugin implements `eachPage`:
10
- * - Creates a JSDOM instance from the raw HTML, with `runScripts:
11
- * 'outside-only'` so that plugins can use `window.eval` to run
12
- * DOM-scoped scripts (e.g. injecting a browser-oriented library that
13
- * needs to bind to that specific window — see
14
- * `@nitpicker/analyze-axe`'s `injectAxe`)
15
- * - Calls the plugin's `eachPage` hook with the DOM window
16
- * - Closes the JSDOM window to free memory
17
- * 3. Returns the plugin result as {@link ReportPage} or `null`
18
- *
19
- * One invocation processes exactly one plugin × one page, even though
20
- * the worker hosting this module handles many tasks over its lifetime.
21
- * `eachPage` plugins must not rely on `dom.window` being reachable from
22
- * `globalThis` — nothing exposes it there — and must not cache state that
23
- * outlives a single call, since the window backing it is closed once the
24
- * call returns.
25
- * @module
26
- */
27
- import { JSDOM } from 'jsdom';
28
- import { importModules } from './import-modules.js';
29
- /**
30
- * Executes a single plugin's `eachPage` hook against a single page.
31
- * @template T - Column key union from the plugin's headers.
32
- * @param data - Contains the single plugin and page data (HTML + URL).
33
- * @param urlEventBus - Event bus for URL discovery events (forwarded to the main thread).
34
- * @param num - Zero-based index of the current page in the batch.
35
- * @param total - Total number of pages being processed.
36
- * @returns Report data from the plugin, or `null` if skipped.
37
- * @see {@link ./worker/runner.ts!runner} for how this function is called
38
- * @see {@link ./nitpicker.ts!Nitpicker.analyze} for the orchestration context
39
- */
40
- export default async function (data, urlEventBus, num, total) {
41
- const { plugin, pages: { html, url }, } = data;
42
- const [analyzeMod] = await importModules([plugin]);
43
- if (!analyzeMod?.eachPage) {
44
- return null;
45
- }
46
- await urlEventBus.emit('url', url.href);
47
- const dom = new JSDOM(html, {
48
- url: url.href,
49
- runScripts: 'outside-only',
50
- });
51
- try {
52
- const report = await analyzeMod.eachPage({
53
- url,
54
- html,
55
- window: dom.window,
56
- num,
57
- total,
58
- });
59
- return report ?? null;
60
- }
61
- catch (error) {
62
- const message = error instanceof Error ? error.message : String(error);
63
- throw new Error(`[${plugin.name}] ${message}`);
64
- }
65
- finally {
66
- dom.window.close();
67
- }
68
- }
@@ -1,15 +0,0 @@
1
- import type { Plugin } from './types.js';
2
- /**
3
- * Reads the `label` property from each plugin by importing and
4
- * initializing the plugin module.
5
- *
6
- * Each plugin module's default export (a `PluginFactory`) is called
7
- * with the plugin's configured settings. The resulting `AnalyzePlugin`
8
- * object's `label` field is collected into a Map keyed by plugin name.
9
- *
10
- * Plugins that fail to import or initialize, or that lack a `label`,
11
- * are silently skipped.
12
- * @param plugins - Array of plugin definitions from the resolved config.
13
- * @returns Map from plugin name to its human-readable label.
14
- */
15
- export declare function readPluginLabels(plugins: readonly Plugin[]): Promise<Map<string, string>>;
@@ -1,30 +0,0 @@
1
- /**
2
- * Reads the `label` property from each plugin by importing and
3
- * initializing the plugin module.
4
- *
5
- * Each plugin module's default export (a `PluginFactory`) is called
6
- * with the plugin's configured settings. The resulting `AnalyzePlugin`
7
- * object's `label` field is collected into a Map keyed by plugin name.
8
- *
9
- * Plugins that fail to import or initialize, or that lack a `label`,
10
- * are silently skipped.
11
- * @param plugins - Array of plugin definitions from the resolved config.
12
- * @returns Map from plugin name to its human-readable label.
13
- */
14
- export async function readPluginLabels(plugins) {
15
- const labels = new Map();
16
- await Promise.all(plugins.map(async (plugin) => {
17
- try {
18
- const mod = await import(plugin.module);
19
- const factory = mod.default;
20
- const instance = await factory(plugin.settings ?? {}, plugin.configFilePath);
21
- if (instance && typeof instance.label === 'string') {
22
- labels.set(plugin.name, instance.label);
23
- }
24
- }
25
- catch {
26
- // Module not importable or factory failed — skip
27
- }
28
- }));
29
- return labels;
30
- }
package/lib/table.d.ts DELETED
@@ -1,75 +0,0 @@
1
- import type { TableData, TableHeaders, TablePages } from './types.js';
2
- import type { ExURL as URL } from '@d-zero/shared/parse-url';
3
- /**
4
- * In-memory accumulator for tabular report data.
5
- *
6
- * Table collects column headers from plugins and per-URL row data from
7
- * analysis results. It uses `Map` internally for efficient merge operations
8
- * (multiple plugins contribute columns to the same URL row), then serializes
9
- * to plain objects for JSON storage in the archive.
10
- *
11
- * ## Merge semantics
12
- *
13
- * When data is added for a URL that already has entries, the new columns are
14
- * shallow-merged with existing ones (later values overwrite earlier ones for
15
- * the same key). This allows multiple plugins to contribute different columns
16
- * to the same row without conflicts, as long as they use distinct column keys.
17
- * @template T - String literal union of all column keys across plugins.
18
- * @example
19
- * ```ts
20
- * const table = new Table<'title' | 'score'>();
21
- * table.addHeaders({ title: 'Page Title', score: 'Score' });
22
- * table.addDataToUrl(url, {
23
- * title: { value: 'Home' },
24
- * score: { value: 95 },
25
- * });
26
- * const json = table.toJSON();
27
- * // { headers: { title: 'Page Title', score: 'Score' }, data: { 'https://...': { ... } } }
28
- * ```
29
- * @see {@link ./types.ts} for the underlying type aliases
30
- */
31
- export declare class Table<T extends string> {
32
- #private;
33
- /**
34
- * Merges a batch of URL-keyed page data into the table.
35
- * Typically called with deserialized data from a Worker thread or cache.
36
- * @param data - Object where keys are URL strings and values are column data.
37
- */
38
- addData(data: TablePages<T>): void;
39
- /**
40
- * Adds or merges column data for a single URL.
41
- * @param url - The page URL to associate the data with.
42
- * @param data - Column values to store for this URL.
43
- */
44
- addDataToUrl(url: URL, data: TableData<T>): void;
45
- /**
46
- * Registers column headers from a plugin.
47
- *
48
- * Multiple plugins can call this independently; headers are merged by key.
49
- * If two plugins declare the same key with different labels, the later
50
- * registration wins.
51
- * @param headers - Map of column keys to display labels.
52
- */
53
- addHeaders(headers: TableHeaders<T>): void;
54
- /**
55
- * Returns all row data as a plain object (serializable to JSON).
56
- * @returns URL-keyed record of column data.
57
- */
58
- getData(): TablePages<T>;
59
- /**
60
- * Retrieves column data for a specific URL, or `undefined` if not present.
61
- * @param url - The page URL to look up.
62
- * @returns Column data for the URL, or `undefined`.
63
- */
64
- getDataByUrl(url: URL): TableData<T> | undefined;
65
- /**
66
- * Serializes the entire table (headers + data) to a JSON-compatible object.
67
- *
68
- * Used when storing the table in the archive via `archive.setData('analysis/table', ...)`.
69
- * @returns Plain object with `headers` and `data` properties.
70
- */
71
- toJSON(): {
72
- headers: Record<T, string>;
73
- data: Record<string, TableData<T>>;
74
- };
75
- }
package/lib/table.js DELETED
@@ -1,132 +0,0 @@
1
- /**
2
- * In-memory accumulator for tabular report data.
3
- *
4
- * Table collects column headers from plugins and per-URL row data from
5
- * analysis results. It uses `Map` internally for efficient merge operations
6
- * (multiple plugins contribute columns to the same URL row), then serializes
7
- * to plain objects for JSON storage in the archive.
8
- *
9
- * ## Merge semantics
10
- *
11
- * When data is added for a URL that already has entries, the new columns are
12
- * shallow-merged with existing ones (later values overwrite earlier ones for
13
- * the same key). This allows multiple plugins to contribute different columns
14
- * to the same row without conflicts, as long as they use distinct column keys.
15
- * @template T - String literal union of all column keys across plugins.
16
- * @example
17
- * ```ts
18
- * const table = new Table<'title' | 'score'>();
19
- * table.addHeaders({ title: 'Page Title', score: 'Score' });
20
- * table.addDataToUrl(url, {
21
- * title: { value: 'Home' },
22
- * score: { value: 95 },
23
- * });
24
- * const json = table.toJSON();
25
- * // { headers: { title: 'Page Title', score: 'Score' }, data: { 'https://...': { ... } } }
26
- * ```
27
- * @see {@link ./types.ts} for the underlying type aliases
28
- */
29
- export class Table {
30
- /** Per-URL row data. Key is the URL href string. */
31
- #data = new Map();
32
- /** Column header definitions accumulated from all plugins. */
33
- #headers = new Map();
34
- /**
35
- * Merges a batch of URL-keyed page data into the table.
36
- * Typically called with deserialized data from a Worker thread or cache.
37
- * @param data - Object where keys are URL strings and values are column data.
38
- */
39
- addData(data) {
40
- const entries = Object.entries(data);
41
- for (const [k, v] of entries) {
42
- this.#add(k, v);
43
- }
44
- }
45
- /**
46
- * Adds or merges column data for a single URL.
47
- * @param url - The page URL to associate the data with.
48
- * @param data - Column values to store for this URL.
49
- */
50
- addDataToUrl(url, data) {
51
- this.#add(url.href, data);
52
- }
53
- /**
54
- * Registers column headers from a plugin.
55
- *
56
- * Multiple plugins can call this independently; headers are merged by key.
57
- * If two plugins declare the same key with different labels, the later
58
- * registration wins.
59
- * @param headers - Map of column keys to display labels.
60
- */
61
- addHeaders(headers) {
62
- const entries = Object.entries(headers);
63
- for (const entry of entries) {
64
- const [key, name] = entry;
65
- this.#headers.set(key, name);
66
- }
67
- }
68
- /**
69
- * Returns all row data as a plain object (serializable to JSON).
70
- * @returns URL-keyed record of column data.
71
- */
72
- getData() {
73
- return mapToObject(this.#data);
74
- }
75
- /**
76
- * Retrieves column data for a specific URL, or `undefined` if not present.
77
- * @param url - The page URL to look up.
78
- * @returns Column data for the URL, or `undefined`.
79
- */
80
- getDataByUrl(url) {
81
- return this.#data.get(url.href);
82
- }
83
- /**
84
- * Serializes the entire table (headers + data) to a JSON-compatible object.
85
- *
86
- * Used when storing the table in the archive via `archive.setData('analysis/table', ...)`.
87
- * @returns Plain object with `headers` and `data` properties.
88
- */
89
- toJSON() {
90
- return {
91
- headers: mapToObject(this.#headers),
92
- data: mapToObject(this.#data),
93
- };
94
- }
95
- /**
96
- * Internal merge-or-insert for a single URL row.
97
- * If the URL already has data, the new values are shallow-merged.
98
- * @param k - URL href string used as the row key.
99
- * @param v - Column data to add or merge.
100
- */
101
- #add(k, v) {
102
- const data = this.#data.get(k);
103
- if (data) {
104
- this.#data.set(k, {
105
- ...data,
106
- ...v,
107
- });
108
- }
109
- else {
110
- this.#data.set(k, v);
111
- }
112
- }
113
- }
114
- /**
115
- * Converts a `Map<K, V>` to a plain `Record<K, V>` object.
116
- *
117
- * Used internally to serialize Map-based storage into JSON-compatible
118
- * objects for archive storage and Worker message passing.
119
- * @template K - String key type.
120
- * @template V - Value type.
121
- * @param map - The Map to convert.
122
- * @returns A plain object with the same key-value pairs.
123
- */
124
- function mapToObject(map) {
125
- const entries = map.entries();
126
- const object = {};
127
- for (const entry of entries) {
128
- const [k, v] = entry;
129
- object[k] = v;
130
- }
131
- return object;
132
- }
@@ -1,33 +0,0 @@
1
- import { TypedAwaitEventEmitter as EventEmitter } from '@d-zero/shared/typed-await-event-emitter';
2
- /**
3
- * Event map for {@link UrlEventBus}.
4
- *
5
- * Currently supports a single event type for URL discovery notifications.
6
- */
7
- export interface UrlEventBusEvent {
8
- /**
9
- * Emitted when a URL is discovered or being processed.
10
- * The payload is the URL href string.
11
- */
12
- url: string;
13
- }
14
- /**
15
- * Typed event bus for URL discovery notifications.
16
- *
17
- * Used as a communication channel between Worker threads and the main thread:
18
- *
19
- * - **Inside Workers**: The each-page worker emits `'url'` events on a local
20
- * UrlEventBus. The Worker thread entry point ({@link ./worker/worker.ts})
21
- * listens for these and forwards them to the main thread via `parentPort.postMessage`.
22
- *
23
- * - **In the main thread**: {@link ./worker/worker-pool.ts!WorkerPool} routes
24
- * `'url'` messages received from workers to the per-task UrlEventBus
25
- * supplied by the caller.
26
- *
27
- * This indirection lets plugin code work the same way regardless of which
28
- * worker in the pool is currently processing a given page.
29
- * @see {@link ./worker/worker.ts} for worker-side forwarding
30
- * @see {@link ./worker/worker-pool.ts!WorkerPool} for main-thread routing
31
- */
32
- export declare class UrlEventBus extends EventEmitter<UrlEventBusEvent> {
33
- }
@@ -1,21 +0,0 @@
1
- import { TypedAwaitEventEmitter as EventEmitter } from '@d-zero/shared/typed-await-event-emitter';
2
- /**
3
- * Typed event bus for URL discovery notifications.
4
- *
5
- * Used as a communication channel between Worker threads and the main thread:
6
- *
7
- * - **Inside Workers**: The each-page worker emits `'url'` events on a local
8
- * UrlEventBus. The Worker thread entry point ({@link ./worker/worker.ts})
9
- * listens for these and forwards them to the main thread via `parentPort.postMessage`.
10
- *
11
- * - **In the main thread**: {@link ./worker/worker-pool.ts!WorkerPool} routes
12
- * `'url'` messages received from workers to the per-task UrlEventBus
13
- * supplied by the caller.
14
- *
15
- * This indirection lets plugin code work the same way regardless of which
16
- * worker in the pool is currently processing a given page.
17
- * @see {@link ./worker/worker.ts} for worker-side forwarding
18
- * @see {@link ./worker/worker-pool.ts!WorkerPool} for main-thread routing
19
- */
20
- export class UrlEventBus extends EventEmitter {
21
- }
@@ -1,25 +0,0 @@
1
- import type { WorkerData } from './types.js';
2
- import type { UrlEventBus } from '../url-event-bus.js';
3
- /**
4
- * Dynamically imports and executes a plugin worker module.
5
- *
6
- * This function is the final execution step in the worker pipeline:
7
- * 1. It `import()`s the module specified by `data.filePath`
8
- * 2. Calls the module's default export with the remaining data, emitter,
9
- * and progress counters
10
- * 3. Returns the result
11
- *
12
- * The `filePath` field is deleted from `data` before passing it to the
13
- * module function, so the module only receives its own domain-specific data.
14
- *
15
- * This function is called from the Worker thread ({@link ./worker.ts}) for
16
- * every task delivered by the {@link ./worker-pool.ts!WorkerPool}.
17
- * @template I - Shape of the caller's initial data (minus the worker infrastructure fields).
18
- * @template R - Return type of the plugin module's default export.
19
- * @param data - Combined worker data containing the module path and plugin-specific payload.
20
- * @param emitter - Event emitter for URL discovery notifications (forwarded to the plugin).
21
- * @returns The result produced by the dynamically imported module.
22
- * @see {@link ./types.ts!WorkerData} for the data shape
23
- * @see {@link ../page-analysis-worker.ts} for a typical module loaded by this runner
24
- */
25
- export declare function runner<I extends Record<string, unknown>, R>(data: WorkerData<I>, emitter: UrlEventBus): Promise<R>;
@@ -1,31 +0,0 @@
1
- /**
2
- * Dynamically imports and executes a plugin worker module.
3
- *
4
- * This function is the final execution step in the worker pipeline:
5
- * 1. It `import()`s the module specified by `data.filePath`
6
- * 2. Calls the module's default export with the remaining data, emitter,
7
- * and progress counters
8
- * 3. Returns the result
9
- *
10
- * The `filePath` field is deleted from `data` before passing it to the
11
- * module function, so the module only receives its own domain-specific data.
12
- *
13
- * This function is called from the Worker thread ({@link ./worker.ts}) for
14
- * every task delivered by the {@link ./worker-pool.ts!WorkerPool}.
15
- * @template I - Shape of the caller's initial data (minus the worker infrastructure fields).
16
- * @template R - Return type of the plugin module's default export.
17
- * @param data - Combined worker data containing the module path and plugin-specific payload.
18
- * @param emitter - Event emitter for URL discovery notifications (forwarded to the plugin).
19
- * @returns The result produced by the dynamically imported module.
20
- * @see {@link ./types.ts!WorkerData} for the data shape
21
- * @see {@link ../page-analysis-worker.ts} for a typical module loaded by this runner
22
- */
23
- export async function runner(data, emitter) {
24
- const { filePath, num, total } = data;
25
- const mod = await import(filePath);
26
- const fn = mod.default;
27
- // @ts-expect-error
28
- delete data.filePath;
29
- const result = await fn(data, emitter, num, total);
30
- return result;
31
- }
@@ -1,23 +0,0 @@
1
- /**
2
- * Data payload sent inside a `task` message from {@link ./worker-pool.ts!WorkerPool} to a worker.
3
- *
4
- * This type merges the worker infrastructure fields (`filePath`, `num`, `total`)
5
- * with the caller-supplied initial data (`I`). The `filePath` points to the
6
- * module whose default export will be invoked by the {@link ../runner.ts!runner}.
7
- * @template I - Shape of the additional data the caller provides
8
- * (e.g. {@link ../../page-analysis-worker.ts!PageAnalysisWorkerData}).
9
- * @see {@link ./worker-pool.ts!WorkerPool.run} for where this payload is constructed
10
- * @see {@link ./runner.ts!runner} for where it is consumed
11
- */
12
- export type WorkerData<I extends Record<string, unknown>> = {
13
- /**
14
- * Absolute path to the compiled JS module to `import()` and execute.
15
- * The module must export a default function matching the
16
- * `(data, emitter, num, total) => Promise<R>` signature.
17
- */
18
- filePath: string;
19
- /** Zero-based index of the current item in the batch (for progress tracking). */
20
- num: number;
21
- /** Total number of items in the batch (for progress tracking). */
22
- total: number;
23
- } & I;
@@ -1 +0,0 @@
1
- export {};
@@ -1,82 +0,0 @@
1
- import type { UrlEventBus } from '../url-event-bus.js';
2
- /**
3
- * Per-task payload sent into the pool's `run()` method.
4
- * @template I - Shape of the per-task data merged into the worker message.
5
- */
6
- export interface PoolTaskParams<I extends Record<string, unknown>> {
7
- /** Absolute path to the module to dynamically import inside the worker. */
8
- readonly filePath: string;
9
- /** Zero-based index of the current item (for progress display). */
10
- readonly num: number;
11
- /** Total number of items in the batch (for progress display). */
12
- readonly total: number;
13
- /** Event bus to receive `'url'` events forwarded from the worker. */
14
- readonly emitter: UrlEventBus;
15
- /** Plugin-specific data delivered to the dynamically imported module. */
16
- readonly initialData: I;
17
- }
18
- /** Constructor options for {@link WorkerPool}. */
19
- export interface WorkerPoolOptions {
20
- /** Number of long-lived worker threads to maintain. Must be >= 1. */
21
- readonly size: number;
22
- /** Absolute path to the worker entry script (compiled `worker.js`). */
23
- readonly workerPath: string;
24
- }
25
- /**
26
- * Long-lived pool of Worker threads that share execution across many tasks.
27
- *
28
- * Replaces the previous "one worker per page" model where every analyzed page
29
- * paid the cost of spawning a new V8 isolate, importing JSDOM, importing the
30
- * plugin module, and tearing it all down. With the pool, each worker is
31
- * created once at the start of an analyze run and processes tasks via
32
- * message passing until the pool is terminated.
33
- *
34
- * ## Why this matters
35
- *
36
- * On a 750-page archive with `CONCURRENCY_LIMIT = 50`, the old model spawned
37
- * 50 workers nearly simultaneously and repeated this every time a worker
38
- * finished, producing recurring "boot waves" that drove peak memory above
39
- * 20GB. The pool model performs N initial boots and then reuses those V8
40
- * isolates for the rest of the run, eliminating the recurring cost entirely.
41
- *
42
- * ## Message protocol (Pool ↔ Worker)
43
- *
44
- * | Direction | `type` | Payload |
45
- * |------------------|---------------|----------------------------------|
46
- * | Pool → Worker | `'task'` | `{ taskId, data }` |
47
- * | Pool → Worker | `'shutdown'` | (none) |
48
- * | Worker → Pool | `'url'` | `{ taskId, url }` (forwarded) |
49
- * | Worker → Pool | `'result'` | `{ taskId, result, error? }` |
50
- *
51
- * Each task carries a unique numeric `taskId` so that responses and URL
52
- * notifications from a worker can be routed back to the correct caller.
53
- */
54
- export declare class WorkerPool {
55
- #private;
56
- /**
57
- * Constructs a pool with the given size and worker entry script.
58
- * @param options - Pool size and worker entry script path.
59
- */
60
- constructor(options: WorkerPoolOptions);
61
- /**
62
- * Submits a task to the pool. Resolves with the worker's `result` payload
63
- * once a worker has finished processing it. The task may be queued if all
64
- * workers are currently busy.
65
- * @template I - Shape of the per-task `initialData` payload.
66
- * @template R - Expected result type returned by the worker module.
67
- * @param params - Task payload (file path, batch indices, emitter, data).
68
- * @returns The worker's result.
69
- */
70
- run<I extends Record<string, unknown>, R>(params: PoolTaskParams<I>): Promise<R>;
71
- /**
72
- * Rejects every queued or in-flight task, sends each worker a `shutdown`
73
- * message, and awaits its exit. After this call returns, no further
74
- * submissions are accepted.
75
- *
76
- * Pending tasks are rejected synchronously so callers waiting on
77
- * `pool.run()` Promises do not hang. Workers handle the shutdown message
78
- * by exiting cleanly; if that fails, {@link #shutdownSlot} forces
79
- * termination after a 5 second timeout.
80
- */
81
- terminate(): Promise<void>;
82
- }