@nitpicker/core 0.24.0 → 0.25.1
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.md +165 -35
- package/lib/assert-u32-id.d.ts +15 -0
- package/lib/assert-u32-id.js +22 -0
- package/lib/check-native-executor-config.d.ts +21 -0
- package/lib/check-native-executor-config.js +24 -0
- package/lib/compute-alias-keys.d.ts +28 -0
- package/lib/compute-alias-keys.js +30 -0
- package/lib/compute-body-hash.d.ts +22 -0
- package/lib/compute-body-hash.js +25 -0
- package/lib/compute-url-sort-ranks.d.ts +28 -0
- package/lib/compute-url-sort-ranks.js +49 -0
- package/lib/create-isolated-cluster-run.d.ts +35 -0
- package/lib/create-isolated-cluster-run.js +52 -0
- package/lib/create-template-classification-run.d.ts +49 -0
- package/lib/create-template-classification-run.js +386 -0
- package/lib/group-alias-candidates.d.ts +40 -0
- package/lib/group-alias-candidates.js +104 -0
- package/lib/load-native-binding.d.ts +26 -0
- package/lib/load-native-binding.js +152 -0
- package/lib/match-js-license-comment-signal.d.ts +19 -0
- package/lib/match-js-license-comment-signal.js +21 -0
- package/lib/match-selector-html.d.ts +41 -0
- package/lib/match-selector-html.js +42 -0
- package/lib/match-selector-snapshots.d.ts +40 -0
- package/lib/match-selector-snapshots.js +57 -0
- package/lib/match-structural-technology-signals.d.ts +23 -0
- package/lib/match-structural-technology-signals.js +25 -0
- package/lib/max-decoded-snapshot-bytes.d.ts +22 -0
- package/lib/max-decoded-snapshot-bytes.js +35 -0
- package/lib/native-binding.d.ts +9 -0
- package/lib/native-binding.js +9 -0
- package/lib/pack-stored-snapshots.d.ts +23 -0
- package/lib/pack-stored-snapshots.js +53 -0
- package/lib/run-with-signal.d.ts +27 -0
- package/lib/run-with-signal.js +54 -0
- package/lib/search-html-document.d.ts +32 -0
- package/lib/search-html-document.js +31 -0
- package/lib/search-html-snapshots.d.ts +46 -0
- package/lib/search-html-snapshots.js +54 -0
- package/lib/template-block-sample-size.d.ts +13 -0
- package/lib/template-block-sample-size.js +14 -0
- package/lib/template-exact-page-limit.d.ts +18 -0
- package/lib/template-exact-page-limit.js +19 -0
- package/lib/template-memory-budget.d.ts +16 -0
- package/lib/template-memory-budget.js +17 -0
- package/lib/to-native-selector-plan.d.ts +16 -0
- package/lib/to-native-selector-plan.js +40 -0
- package/lib/to-u32-ids.d.ts +14 -0
- package/lib/to-u32-ids.js +22 -0
- package/lib/types.d.ts +736 -248
- package/lib/validate-search-pattern.d.ts +25 -0
- package/lib/validate-search-pattern.js +27 -0
- package/package.json +83 -16
- package/lib/core.d.ts +0 -4
- package/lib/core.js +0 -4
- package/lib/discover-analyze-plugins.d.ts +0 -14
- package/lib/discover-analyze-plugins.js +0 -32
- package/lib/get-table-cache-root.d.ts +0 -18
- package/lib/get-table-cache-root.js +0 -22
- package/lib/hooks/define-plugin.d.ts +0 -68
- package/lib/hooks/define-plugin.js +0 -69
- package/lib/import-modules.d.ts +0 -24
- package/lib/import-modules.js +0 -38
- package/lib/load-plugin-settings.d.ts +0 -44
- package/lib/load-plugin-settings.js +0 -125
- package/lib/nitpicker.d.ts +0 -150
- package/lib/nitpicker.js +0 -437
- package/lib/page-analysis-worker.d.ts +0 -58
- package/lib/page-analysis-worker.js +0 -68
- package/lib/read-plugin-labels.d.ts +0 -15
- package/lib/read-plugin-labels.js +0 -30
- package/lib/table.d.ts +0 -75
- package/lib/table.js +0 -132
- package/lib/url-event-bus.d.ts +0 -33
- package/lib/url-event-bus.js +0 -21
- package/lib/worker/runner.d.ts +0 -25
- package/lib/worker/runner.js +0 -31
- package/lib/worker/types.d.ts +0 -23
- package/lib/worker/types.js +0 -1
- package/lib/worker/worker-pool.d.ts +0 -82
- package/lib/worker/worker-pool.js +0 -278
- package/lib/worker/worker.d.ts +0 -34
- 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
|
-
}
|
package/lib/url-event-bus.d.ts
DELETED
|
@@ -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
|
-
}
|
package/lib/url-event-bus.js
DELETED
|
@@ -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
|
-
}
|
package/lib/worker/runner.d.ts
DELETED
|
@@ -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>;
|
package/lib/worker/runner.js
DELETED
|
@@ -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
|
-
}
|
package/lib/worker/types.d.ts
DELETED
|
@@ -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;
|
package/lib/worker/types.js
DELETED
|
@@ -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
|
-
}
|