lens-content-processor 0.79.0 → 0.81.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authoring-markup.d.ts +22 -0
- package/dist/authoring-markup.js +24 -0
- package/dist/authoring-markup.js.map +1 -1
- package/dist/cache.d.ts +10 -0
- package/dist/cache.js +6 -5
- package/dist/cache.js.map +1 -1
- package/dist/cli-steps.d.ts +37 -0
- package/dist/cli-steps.js +80 -0
- package/dist/cli-steps.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +52 -70
- package/dist/cli.js.map +1 -1
- package/dist/content-schema.js +4 -0
- package/dist/content-schema.js.map +1 -1
- package/dist/course-graph.d.ts +2 -2
- package/dist/course-graph.js +7 -2
- package/dist/course-graph.js.map +1 -1
- package/dist/criticmarkup-forms.d.ts +8 -6
- package/dist/criticmarkup-forms.js +22 -19
- package/dist/criticmarkup-forms.js.map +1 -1
- package/dist/dedupe-errors.d.ts +16 -0
- package/dist/dedupe-errors.js +23 -9
- package/dist/dedupe-errors.js.map +1 -1
- package/dist/dependency-cache.d.ts +15 -5
- package/dist/dependency-cache.js +33 -74
- package/dist/dependency-cache.js.map +1 -1
- package/dist/dropped.d.ts +114 -0
- package/dist/dropped.js +253 -0
- package/dist/dropped.js.map +1 -0
- package/dist/engine/assemble.d.ts +44 -0
- package/dist/engine/assemble.js +132 -0
- package/dist/engine/assemble.js.map +1 -0
- package/dist/engine/build.d.ts +8 -0
- package/dist/engine/build.js +558 -0
- package/dist/engine/build.js.map +1 -0
- package/dist/engine/file-facts.d.ts +26 -0
- package/dist/engine/file-facts.js +63 -0
- package/dist/engine/file-facts.js.map +1 -0
- package/dist/engine/git-source.d.ts +35 -0
- package/dist/engine/git-source.js +242 -0
- package/dist/engine/git-source.js.map +1 -0
- package/dist/engine/overlay.d.ts +43 -0
- package/dist/engine/overlay.js +167 -0
- package/dist/engine/overlay.js.map +1 -0
- package/dist/engine/protocol.d.ts +146 -0
- package/dist/engine/protocol.js +40 -0
- package/dist/engine/protocol.js.map +1 -0
- package/dist/engine/reader.d.ts +133 -0
- package/dist/engine/reader.js +298 -0
- package/dist/engine/reader.js.map +1 -0
- package/dist/engine/sources.d.ts +39 -0
- package/dist/engine/sources.js +137 -0
- package/dist/engine/sources.js.map +1 -0
- package/dist/engine/sqlite-store.d.ts +99 -0
- package/dist/engine/sqlite-store.js +452 -0
- package/dist/engine/sqlite-store.js.map +1 -0
- package/dist/engine/store.d.ts +85 -0
- package/dist/engine/store.js +31 -0
- package/dist/engine/store.js.map +1 -0
- package/dist/engine/tables.d.ts +15 -0
- package/dist/engine/tables.js +70 -0
- package/dist/engine/tables.js.map +1 -0
- package/dist/engine/units.d.ts +58 -0
- package/dist/engine/units.js +186 -0
- package/dist/engine/units.js.map +1 -0
- package/dist/engine/version.d.ts +6 -0
- package/dist/engine/version.js +21 -0
- package/dist/engine/version.js.map +1 -0
- package/dist/engine/worker.d.ts +1 -0
- package/dist/engine/worker.js +220 -0
- package/dist/engine/worker.js.map +1 -0
- package/dist/flattener/index.d.ts +34 -17
- package/dist/flattener/index.js +252 -115
- package/dist/flattener/index.js.map +1 -1
- package/dist/flattener/resolve-text-links.d.ts +15 -14
- package/dist/flattener/resolve-text-links.js +24 -25
- package/dist/flattener/resolve-text-links.js.map +1 -1
- package/dist/glossary.d.ts +2 -2
- package/dist/glossary.js.map +1 -1
- package/dist/index.d.ts +171 -2
- package/dist/index.js +883 -639
- package/dist/index.js.map +1 -1
- package/dist/markdown-parse.d.ts +6 -0
- package/dist/markdown-parse.js +14 -0
- package/dist/markdown-parse.js.map +1 -1
- package/dist/parser/learning-outcome.js +11 -2
- package/dist/parser/learning-outcome.js.map +1 -1
- package/dist/parser/lens.d.ts +7 -1
- package/dist/parser/lens.js +43 -9
- package/dist/parser/lens.js.map +1 -1
- package/dist/parser/meeting-doc.d.ts +2 -1
- package/dist/parser/meeting-doc.js +2 -1
- package/dist/parser/meeting-doc.js.map +1 -1
- package/dist/parser/module.d.ts +3 -1
- package/dist/parser/module.js +7 -5
- package/dist/parser/module.js.map +1 -1
- package/dist/parser/question-embeds.d.ts +4 -3
- package/dist/parser/question-embeds.js.map +1 -1
- package/dist/parser/response-segments.d.ts +4 -0
- package/dist/parser/response-segments.js +1 -1
- package/dist/parser/response-segments.js.map +1 -1
- package/dist/parser/sections.d.ts +10 -0
- package/dist/parser/sections.js +8 -3
- package/dist/parser/sections.js.map +1 -1
- package/dist/parser/widget.d.ts +2 -1
- package/dist/parser/widget.js.map +1 -1
- package/dist/parser/wikilink.d.ts +13 -3
- package/dist/parser/wikilink.js +55 -34
- package/dist/parser/wikilink.js.map +1 -1
- package/dist/reference-graph.d.ts +21 -3
- package/dist/reference-graph.js +35 -12
- package/dist/reference-graph.js.map +1 -1
- package/dist/same-page-links.d.ts +11 -1
- package/dist/same-page-links.js +9 -0
- package/dist/same-page-links.js.map +1 -1
- package/dist/skill-tree.d.ts +2 -1
- package/dist/skill-tree.js.map +1 -1
- package/dist/source-location.d.ts +0 -4
- package/dist/source-location.js +0 -43
- package/dist/source-location.js.map +1 -1
- package/dist/validator/card-module-membership.d.ts +17 -1
- package/dist/validator/card-module-membership.js +60 -56
- package/dist/validator/card-module-membership.js.map +1 -1
- package/dist/validator/criticmarkup.js +1 -1
- package/dist/validator/criticmarkup.js.map +1 -1
- package/dist/validator/field-typos.d.ts +1 -1
- package/dist/validator/field-typos.js +21 -21
- package/dist/validator/field-typos.js.map +1 -1
- package/dist/validator/output-integrity.d.ts +9 -5
- package/dist/validator/output-integrity.js +43 -45
- package/dist/validator/output-integrity.js.map +1 -1
- package/dist/validator/segment-fields.js +5 -8
- package/dist/validator/segment-fields.js.map +1 -1
- package/dist/validator/tier.d.ts +2 -2
- package/dist/validator/tier.js.map +1 -1
- package/dist/validator/uuid.d.ts +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// The whole-content step's tables as finish units read them. Every lookup is
|
|
2
|
+
// a read of one entry, recorded with a hash of that entry, so a page is
|
|
3
|
+
// finished again exactly when an entry it used changed: a module slug change
|
|
4
|
+
// re-finishes the pages that card one of that module's lenses, and a glossary
|
|
5
|
+
// entry change the pages that mark one of its terms.
|
|
6
|
+
import { entryVersion, sha1, tableView, } from "./reader.js";
|
|
7
|
+
/**
|
|
8
|
+
* The read-key prefix of each table. Glossary entries are keyed by their
|
|
9
|
+
* file (`glossary:<path>`), so "which pages use file X" finds them.
|
|
10
|
+
*/
|
|
11
|
+
const PREFIX = {
|
|
12
|
+
contentIdToModule: "table:contentIdToModule:",
|
|
13
|
+
glossary: "glossary:",
|
|
14
|
+
moduleContentIds: "table:moduleContentIds:",
|
|
15
|
+
slugToPath: "table:slugToPath:",
|
|
16
|
+
courseOutcomes: "table:courseOutcomes:",
|
|
17
|
+
topics: "table:topic:",
|
|
18
|
+
};
|
|
19
|
+
const NAMES = Object.keys(PREFIX);
|
|
20
|
+
/** `tables` as views whose lookups `log` records. */
|
|
21
|
+
export function recordingTables(tables, log) {
|
|
22
|
+
const views = {};
|
|
23
|
+
for (const name of NAMES) {
|
|
24
|
+
const table = tables[name];
|
|
25
|
+
views[name] = tableView(table, PREFIX[name], log);
|
|
26
|
+
}
|
|
27
|
+
return views;
|
|
28
|
+
}
|
|
29
|
+
/** The versions of table read keys in one job's tables. */
|
|
30
|
+
export class TableVersions {
|
|
31
|
+
tables;
|
|
32
|
+
memo = new Map();
|
|
33
|
+
constructor(tables) {
|
|
34
|
+
this.tables = tables;
|
|
35
|
+
}
|
|
36
|
+
/** True for a key that names a table entry. */
|
|
37
|
+
static owns(key) {
|
|
38
|
+
return NAMES.some((name) => key.startsWith(PREFIX[name]));
|
|
39
|
+
}
|
|
40
|
+
versionOf(key) {
|
|
41
|
+
let version = this.memo.get(key);
|
|
42
|
+
if (version !== undefined)
|
|
43
|
+
return version;
|
|
44
|
+
const name = NAMES.find((n) => key.startsWith(PREFIX[n]));
|
|
45
|
+
if (!name)
|
|
46
|
+
throw new Error(`Not a table read key: ${key}`);
|
|
47
|
+
const table = this.tables[name];
|
|
48
|
+
const entry = key.slice(PREFIX[name].length);
|
|
49
|
+
// A scan of the whole table (no finish step does one today).
|
|
50
|
+
version =
|
|
51
|
+
entry === "*"
|
|
52
|
+
? sha1(JSON.stringify([...table]))
|
|
53
|
+
: entryVersion(table.get(entry));
|
|
54
|
+
this.memo.set(key, version);
|
|
55
|
+
return version;
|
|
56
|
+
}
|
|
57
|
+
/** Every entry's version, by read key: what the next job compares with. */
|
|
58
|
+
all() {
|
|
59
|
+
const versions = new Map();
|
|
60
|
+
for (const name of NAMES) {
|
|
61
|
+
const table = this.tables[name];
|
|
62
|
+
for (const key of table.keys()) {
|
|
63
|
+
const readKey = PREFIX[name] + key;
|
|
64
|
+
versions.set(readKey, this.versionOf(readKey));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return versions;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=tables.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tables.js","sourceRoot":"","sources":["../../src/engine/tables.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { PageFinish, UnitSummary } from "../index.js";
|
|
2
|
+
/**
|
|
3
|
+
* A failure that is not about the content a unit reads: the content source
|
|
4
|
+
* or the build layer failed, or a unit made output the build layer cannot
|
|
5
|
+
* keep. It ends the job with status "error" instead of failing the unit, so
|
|
6
|
+
* no file is blamed for it and the next job does the work again
|
|
7
|
+
* (AGENTS.md: a showstopper, never degraded).
|
|
8
|
+
*/
|
|
9
|
+
export declare class Showstopper extends Error {
|
|
10
|
+
}
|
|
11
|
+
export declare const UNIT_KINDS: {
|
|
12
|
+
readonly path: "path:";
|
|
13
|
+
readonly flat: "flat:";
|
|
14
|
+
readonly page: "page:";
|
|
15
|
+
};
|
|
16
|
+
export declare function unitIdOf(path: string): string;
|
|
17
|
+
export declare function pathOfUnit(id: string): string;
|
|
18
|
+
export declare function flatIdOf(pageKey: string): string;
|
|
19
|
+
export declare function pageIdOf(pageKey: string): string;
|
|
20
|
+
/** The key of a page: its file, and its position among that file's pages. */
|
|
21
|
+
export declare function pageKey(sourcePath: string, index: number): string;
|
|
22
|
+
/** The page key of a `flat:` or `page:` record id. */
|
|
23
|
+
export declare function pageKeyOf(id: string): string;
|
|
24
|
+
/** The source path and index of a page key (`<path>#<index>`). */
|
|
25
|
+
export declare function splitPageKey(key: string): {
|
|
26
|
+
sourcePath: string;
|
|
27
|
+
index: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* What a path unit stores: its summary, and the version (a hash of the stored
|
|
31
|
+
* form) of each flattened page, which a finish unit's `flat:` read sees.
|
|
32
|
+
*/
|
|
33
|
+
export interface StoredSummary {
|
|
34
|
+
summary: UnitSummary;
|
|
35
|
+
flat: string[];
|
|
36
|
+
}
|
|
37
|
+
/** A deep copy of `value` that survives JSON.stringify and JSON.parse. */
|
|
38
|
+
export declare function encodeUnit(value: unknown): unknown;
|
|
39
|
+
/** The inverse of encodeUnit: a fresh copy, safe to mutate. */
|
|
40
|
+
export declare function decodeUnit<T>(stored: unknown): T;
|
|
41
|
+
/**
|
|
42
|
+
* What a path unit that the job did not run contributes: an error on its file
|
|
43
|
+
* first, then the unit's last good results when the build layer has them (its
|
|
44
|
+
* pages stop updating until the file is fixed), or nothing else. `reason` is
|
|
45
|
+
* the supervisor's "timeout", "memory" or "crash", or the message of an error
|
|
46
|
+
* the unit threw.
|
|
47
|
+
*/
|
|
48
|
+
export declare function skippedUnit(path: string, reason: string | undefined, lastGood: StoredSummary | undefined): StoredSummary;
|
|
49
|
+
/**
|
|
50
|
+
* What a finish unit that the job did not run contributes: an error on the
|
|
51
|
+
* page's file, in the first finish step's place, then its last good results.
|
|
52
|
+
* Without them the page is left out (`dropped`), as a path unit without
|
|
53
|
+
* results leaves its pages out.
|
|
54
|
+
*/
|
|
55
|
+
export declare function skippedPage(sourcePath: string, reason: string | undefined, lastGood: PageFinish | undefined): {
|
|
56
|
+
finish: PageFinish;
|
|
57
|
+
dropped: boolean;
|
|
58
|
+
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// Units as the build layer stores them.
|
|
2
|
+
//
|
|
3
|
+
// Three kinds of record share the units table (ids are `<kind>:<name>`; a page
|
|
4
|
+
// is named by its key, `<source path>#<index>`: pageKey below):
|
|
5
|
+
//
|
|
6
|
+
// path:<path> a path unit (runPathUnit): its summary for the whole-content
|
|
7
|
+
// step and the version of each flattened page it made
|
|
8
|
+
// flat:<page> one flattened page, as its path unit made it; no reads. It is
|
|
9
|
+
// written before its path unit's record, so a job killed in
|
|
10
|
+
// between reruns the path unit
|
|
11
|
+
// page:<page> a finish unit (finishPage): its errors per step; the finished
|
|
12
|
+
// page itself is in the pages table
|
|
13
|
+
//
|
|
14
|
+
// A stored output must come back exactly as it went in: finishing mutates a
|
|
15
|
+
// flattened page, the whole-content step mutates courses and question entries,
|
|
16
|
+
// and the result is serialized, so a value that comes back different changes
|
|
17
|
+
// the output. A JSON round trip loses three things, and the codec here keeps
|
|
18
|
+
// them:
|
|
19
|
+
//
|
|
20
|
+
// - line numbers, which ride on a Symbol (source-location.ts), as a reserved
|
|
21
|
+
// key;
|
|
22
|
+
// - keys whose value is undefined. JSON drops them, and a key that is assigned
|
|
23
|
+
// later would then move to the end of its object, changing the key order of
|
|
24
|
+
// the output;
|
|
25
|
+
// - numbers that are not finite (NaN, Infinity), which JSON writes as null.
|
|
26
|
+
//
|
|
27
|
+
// Anything that is not plain data (a Map, a class instance) is refused, so a
|
|
28
|
+
// rule that puts one into a unit's output fails a test instead of losing data
|
|
29
|
+
// in the build layer.
|
|
30
|
+
import { attachSourceLine, getSourceLine } from "../source-location.js";
|
|
31
|
+
/**
|
|
32
|
+
* A failure that is not about the content a unit reads: the content source
|
|
33
|
+
* or the build layer failed, or a unit made output the build layer cannot
|
|
34
|
+
* keep. It ends the job with status "error" instead of failing the unit, so
|
|
35
|
+
* no file is blamed for it and the next job does the work again
|
|
36
|
+
* (AGENTS.md: a showstopper, never degraded).
|
|
37
|
+
*/
|
|
38
|
+
export class Showstopper extends Error {
|
|
39
|
+
}
|
|
40
|
+
const PATH = "path:";
|
|
41
|
+
const FLAT = "flat:";
|
|
42
|
+
const PAGE = "page:";
|
|
43
|
+
export const UNIT_KINDS = { path: PATH, flat: FLAT, page: PAGE };
|
|
44
|
+
export function unitIdOf(path) {
|
|
45
|
+
return PATH + path;
|
|
46
|
+
}
|
|
47
|
+
export function pathOfUnit(id) {
|
|
48
|
+
if (!id.startsWith(PATH))
|
|
49
|
+
throw new Error(`Not a path unit: ${id}`);
|
|
50
|
+
return id.slice(PATH.length);
|
|
51
|
+
}
|
|
52
|
+
export function flatIdOf(pageKey) {
|
|
53
|
+
return FLAT + pageKey;
|
|
54
|
+
}
|
|
55
|
+
export function pageIdOf(pageKey) {
|
|
56
|
+
return PAGE + pageKey;
|
|
57
|
+
}
|
|
58
|
+
/** The key of a page: its file, and its position among that file's pages. */
|
|
59
|
+
export function pageKey(sourcePath, index) {
|
|
60
|
+
return `${sourcePath}#${index}`;
|
|
61
|
+
}
|
|
62
|
+
/** The page key of a `flat:` or `page:` record id. */
|
|
63
|
+
export function pageKeyOf(id) {
|
|
64
|
+
return id.slice(id.indexOf(":") + 1);
|
|
65
|
+
}
|
|
66
|
+
/** The source path and index of a page key (`<path>#<index>`). */
|
|
67
|
+
export function splitPageKey(key) {
|
|
68
|
+
const at = key.lastIndexOf("#");
|
|
69
|
+
return { sourcePath: key.slice(0, at), index: Number(key.slice(at + 1)) };
|
|
70
|
+
}
|
|
71
|
+
const LINE_KEY = "__lensSourceLine";
|
|
72
|
+
const UNDEFINED_KEY = "__lensUndefined";
|
|
73
|
+
const NUMBER_KEY = "__lensNumber";
|
|
74
|
+
/** A deep copy of `value` that survives JSON.stringify and JSON.parse. */
|
|
75
|
+
export function encodeUnit(value) {
|
|
76
|
+
if (value === undefined)
|
|
77
|
+
return { [UNDEFINED_KEY]: true };
|
|
78
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
79
|
+
return { [NUMBER_KEY]: String(value) };
|
|
80
|
+
}
|
|
81
|
+
if (value === null || typeof value !== "object")
|
|
82
|
+
return value;
|
|
83
|
+
if (Array.isArray(value))
|
|
84
|
+
return Array.from(value, encodeUnit);
|
|
85
|
+
const proto = Object.getPrototypeOf(value);
|
|
86
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
87
|
+
throw new Showstopper(`A unit's output must be plain data, not ${proto?.constructor?.name ?? "an object with a prototype"}`);
|
|
88
|
+
}
|
|
89
|
+
const out = {};
|
|
90
|
+
for (const [key, entry] of Object.entries(value))
|
|
91
|
+
out[key] = encodeUnit(entry);
|
|
92
|
+
const line = getSourceLine(value);
|
|
93
|
+
if (line !== undefined)
|
|
94
|
+
out[LINE_KEY] = line;
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
/** The inverse of encodeUnit: a fresh copy, safe to mutate. */
|
|
98
|
+
export function decodeUnit(stored) {
|
|
99
|
+
return decode(stored);
|
|
100
|
+
}
|
|
101
|
+
function decode(value) {
|
|
102
|
+
if (value === null || typeof value !== "object")
|
|
103
|
+
return value;
|
|
104
|
+
if (Array.isArray(value))
|
|
105
|
+
return value.map(decode);
|
|
106
|
+
const object = value;
|
|
107
|
+
if (UNDEFINED_KEY in object)
|
|
108
|
+
return undefined;
|
|
109
|
+
if (NUMBER_KEY in object)
|
|
110
|
+
return Number(object[NUMBER_KEY]);
|
|
111
|
+
const out = {};
|
|
112
|
+
let line;
|
|
113
|
+
for (const [key, entry] of Object.entries(object)) {
|
|
114
|
+
if (key === LINE_KEY)
|
|
115
|
+
line = entry;
|
|
116
|
+
else
|
|
117
|
+
out[key] = decode(entry);
|
|
118
|
+
}
|
|
119
|
+
return attachSourceLine(out, line);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* What a path unit that the job did not run contributes: an error on its file
|
|
123
|
+
* first, then the unit's last good results when the build layer has them (its
|
|
124
|
+
* pages stop updating until the file is fixed), or nothing else. `reason` is
|
|
125
|
+
* the supervisor's "timeout", "memory" or "crash", or the message of an error
|
|
126
|
+
* the unit threw.
|
|
127
|
+
*/
|
|
128
|
+
export function skippedUnit(path, reason, lastGood) {
|
|
129
|
+
const error = unitFailed(path, reason, lastGood !== undefined);
|
|
130
|
+
if (!lastGood) {
|
|
131
|
+
const summary = { path, errors: [error], urls: [], uuids: [], pages: [] };
|
|
132
|
+
return { summary, flat: [] };
|
|
133
|
+
}
|
|
134
|
+
const { summary } = lastGood;
|
|
135
|
+
return {
|
|
136
|
+
...lastGood,
|
|
137
|
+
summary: { ...summary, errors: [error, ...summary.errors] },
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* What a finish unit that the job did not run contributes: an error on the
|
|
142
|
+
* page's file, in the first finish step's place, then its last good results.
|
|
143
|
+
* Without them the page is left out (`dropped`), as a path unit without
|
|
144
|
+
* results leaves its pages out.
|
|
145
|
+
*/
|
|
146
|
+
export function skippedPage(sourcePath, reason, lastGood) {
|
|
147
|
+
const error = unitFailed(sourcePath, reason, lastGood !== undefined);
|
|
148
|
+
const finish = lastGood ?? {
|
|
149
|
+
glossary: [],
|
|
150
|
+
cardMembership: [],
|
|
151
|
+
optionalLensSets: [],
|
|
152
|
+
sharedQuestions: [],
|
|
153
|
+
integrity: [],
|
|
154
|
+
};
|
|
155
|
+
return {
|
|
156
|
+
finish: { ...finish, glossary: [error, ...finish.glossary] },
|
|
157
|
+
dropped: !lastGood,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function unitFailed(file, reason, hasLastGood) {
|
|
161
|
+
return {
|
|
162
|
+
file,
|
|
163
|
+
code: "validator.unit-failed",
|
|
164
|
+
message: `The validator ${failure(reason)}` +
|
|
165
|
+
(hasLastGood
|
|
166
|
+
? ", so the results shown for it are from its last good check"
|
|
167
|
+
: ", so it was not checked"),
|
|
168
|
+
suggestion: "Tell a developer. To check the file again, make it smaller or simpler and save it",
|
|
169
|
+
severity: "error",
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function failure(reason) {
|
|
173
|
+
switch (reason) {
|
|
174
|
+
case undefined:
|
|
175
|
+
return "ran out of time or memory on this file";
|
|
176
|
+
case "timeout":
|
|
177
|
+
return "ran out of time on this file";
|
|
178
|
+
case "memory":
|
|
179
|
+
return "ran out of memory on this file";
|
|
180
|
+
case "crash":
|
|
181
|
+
return "crashed on this file";
|
|
182
|
+
default:
|
|
183
|
+
return `stopped with an error on this file (${reason})`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=units.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"units.js","sourceRoot":"","sources":["../../src/engine/units.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,EAAE;AACF,+EAA+E;AAC/E,gEAAgE;AAChE,EAAE;AACF,8EAA8E;AAC9E,qEAAqE;AACrE,+EAA+E;AAC/E,2EAA2E;AAC3E,8CAA8C;AAC9C,+EAA+E;AAC/E,mDAAmD;AACnD,EAAE;AACF,4EAA4E;AAC5E,+EAA+E;AAC/E,6EAA6E;AAC7E,6EAA6E;AAC7E,QAAQ;AACR,EAAE;AACF,6EAA6E;AAC7E,SAAS;AACT,+EAA+E;AAC/E,8EAA8E;AAC9E,gBAAgB;AAChB,4EAA4E;AAC5E,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,sBAAsB;AAGtB,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAExE;;;;;;GAMG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAK;CAAG;AAEzC,MAAM,IAAI,GAAG,OAAO,CAAC;AACrB,MAAM,IAAI,GAAG,OAAO,CAAC;AACrB,MAAM,IAAI,GAAG,OAAO,CAAC;AACrB,MAAM,CAAC,MAAM,UAAU,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAW,CAAC;AAE1E,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO,IAAI,GAAG,IAAI,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EAAU;IACnC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;IACpE,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,OAAO,IAAI,GAAG,OAAO,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,OAAO,IAAI,GAAG,OAAO,CAAC;AACxB,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,OAAO,CAAC,UAAkB,EAAE,KAAa;IACvD,OAAO,GAAG,UAAU,IAAI,KAAK,EAAE,CAAC;AAClC,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,SAAS,CAAC,EAAU;IAClC,OAAO,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AACvC,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,YAAY,CAAC,GAAW;IAItC,MAAM,EAAE,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAChC,OAAO,EAAE,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AAC5E,CAAC;AAWD,MAAM,QAAQ,GAAG,kBAAkB,CAAC;AACpC,MAAM,aAAa,GAAG,iBAAiB,CAAC;AACxC,MAAM,UAAU,GAAG,cAAc,CAAC;AAElC,0EAA0E;AAC1E,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC;IAC1D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,OAAO,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IACzC,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,KAAK,KAAK,MAAM,CAAC,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACjD,MAAM,IAAI,WAAW,CACnB,2CAA2C,KAAK,EAAE,WAAW,EAAE,IAAI,IAAI,4BAA4B,EAAE,CACtG,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;QAC9C,GAAG,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,IAAI,KAAK,SAAS;QAAE,GAAG,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAC7C,OAAO,GAAG,CAAC;AACb,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,UAAU,CAAI,MAAe;IAC3C,OAAO,MAAM,CAAC,MAAM,CAAM,CAAC;AAC7B,CAAC;AAED,SAAS,MAAM,CAAC,KAAc;IAC5B,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,IAAI,aAAa,IAAI,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9C,IAAI,UAAU,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAC5D,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,IAAI,IAAwB,CAAC;IAC7B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,GAAG,KAAK,QAAQ;YAAE,IAAI,GAAG,KAAe,CAAC;;YACxC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CACzB,IAAY,EACZ,MAA0B,EAC1B,QAAmC;IAEnC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC;IAC/D,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;QAC1E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IAC/B,CAAC;IACD,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC;IAC7B,OAAO;QACL,GAAG,QAAQ;QACX,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE;KAC5D,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,UAAkB,EAClB,MAA0B,EAC1B,QAAgC;IAEhC,MAAM,KAAK,GAAG,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC;IACrE,MAAM,MAAM,GAAG,QAAQ,IAAI;QACzB,QAAQ,EAAE,EAAE;QACZ,cAAc,EAAE,EAAE;QAClB,gBAAgB,EAAE,EAAE;QACpB,eAAe,EAAE,EAAE;QACnB,SAAS,EAAE,EAAE;KACd,CAAC;IACF,OAAO;QACL,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE;QAC5D,OAAO,EAAE,CAAC,QAAQ;KACnB,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CACjB,IAAY,EACZ,MAA0B,EAC1B,WAAoB;IAEpB,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,uBAAuB;QAC7B,OAAO,EACL,iBAAiB,OAAO,CAAC,MAAM,CAAC,EAAE;YAClC,CAAC,WAAW;gBACV,CAAC,CAAC,4DAA4D;gBAC9D,CAAC,CAAC,yBAAyB,CAAC;QAChC,UAAU,EACR,mFAAmF;QACrF,QAAQ,EAAE,OAAO;KAClB,CAAC;AACJ,CAAC;AAED,SAAS,OAAO,CAAC,MAA0B;IACzC,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,wCAAwC,CAAC;QAClD,KAAK,SAAS;YACZ,OAAO,8BAA8B,CAAC;QACxC,KAAK,QAAQ;YACX,OAAO,gCAAgC,CAAC;QAC1C,KAAK,OAAO;YACV,OAAO,sBAAsB,CAAC;QAChC;YACE,OAAO,uCAAuC,MAAM,GAAG,CAAC;IAC5D,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<package version>-<16 hex digits of the code fingerprint>`, for example
|
|
3
|
+
* `0.75.0-3f9c0d1e2a4b5c6d`. The fingerprint (cache.ts) hashes this package's
|
|
4
|
+
* code, its manifest and lockfile, and the Node version. Computed once per process.
|
|
5
|
+
*/
|
|
6
|
+
export declare function validatorVersion(): string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// The validator version names the build layer: there is one store file per
|
|
2
|
+
// version (sqlite-store.ts storePathFor), so a store written by other code is
|
|
3
|
+
// never reused. It changes whenever anything that can change the output
|
|
4
|
+
// changes, because it contains the per-file cache's code fingerprint.
|
|
5
|
+
import { readFileSync } from "fs";
|
|
6
|
+
import { computeCodeFingerprint } from "../cache.js";
|
|
7
|
+
let version;
|
|
8
|
+
/**
|
|
9
|
+
* `<package version>-<16 hex digits of the code fingerprint>`, for example
|
|
10
|
+
* `0.75.0-3f9c0d1e2a4b5c6d`. The fingerprint (cache.ts) hashes this package's
|
|
11
|
+
* code, its manifest and lockfile, and the Node version. Computed once per process.
|
|
12
|
+
*/
|
|
13
|
+
export function validatorVersion() {
|
|
14
|
+
if (version === undefined) {
|
|
15
|
+
const manifest = new URL("../../package.json", import.meta.url);
|
|
16
|
+
const pkg = JSON.parse(readFileSync(manifest, "utf-8"));
|
|
17
|
+
version = `${pkg.version}-${computeCodeFingerprint().slice(0, 16)}`;
|
|
18
|
+
}
|
|
19
|
+
return version;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/engine/version.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,8EAA8E;AAC9E,wEAAwE;AACxE,sEAAsE;AAEtE,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD,IAAI,OAA2B,CAAC;AAEhC;;;;GAIG;AACH,MAAM,UAAU,gBAAgB;IAC9B,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAErD,CAAC;QACF,OAAO,GAAG,GAAG,GAAG,CAAC,OAAO,IAAI,sBAAsB,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACtE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// The content worker: one fresh process per job, run by a supervisor (the
|
|
2
|
+
// platform's is Python: core/content/engine.py, supervise).
|
|
3
|
+
//
|
|
4
|
+
// node --max-old-space-size=<MB> dist/engine/worker.js < job.json
|
|
5
|
+
// node dist/engine/worker.js --version prints the validator version and exits
|
|
6
|
+
//
|
|
7
|
+
// It reads one Job (protocol.ts) from stdin, opens the build layer and the content
|
|
8
|
+
// source, runs the job (build.ts runJob), and writes newline-delimited JSON events
|
|
9
|
+
// to stdout; everything else (warnings, console output of rules) goes to stderr:
|
|
10
|
+
//
|
|
11
|
+
// {"event":"unit","id":"path:Lenses/X.md"} before each unit
|
|
12
|
+
// {"event":"unit","id":null} after it
|
|
13
|
+
// {"event":"result", ...} the JobResult, always the last line
|
|
14
|
+
//
|
|
15
|
+
// Exit code 0 with status "ok", 1 with status "error".
|
|
16
|
+
//
|
|
17
|
+
// Blame and skip. When a worker hangs, runs out of memory or crashes, the
|
|
18
|
+
// supervisor blames the unit it was running, skips that unit, and runs the job
|
|
19
|
+
// again:
|
|
20
|
+
//
|
|
21
|
+
// 1. Start the worker in its own process group (setsid). Write the Job as JSON to
|
|
22
|
+
// its stdin, then close stdin.
|
|
23
|
+
// 2. Read stdout line by line. A unit event says which unit is running (null:
|
|
24
|
+
// none). The result line ends the job, whatever happens to the process then.
|
|
25
|
+
// 3. Watch the group's resident memory and the job's wall time, and kill the whole
|
|
26
|
+
// group when either passes its limit.
|
|
27
|
+
// 4. When the worker ends without a result, blame the unit that was running: for a
|
|
28
|
+
// SQLite store, the line in `<store>-running`; for ":memory:" and overlay jobs,
|
|
29
|
+
// the last unit event. The worker writes both synchronously before the unit
|
|
30
|
+
// starts, so they are out of the process even if the unit then hangs. The
|
|
31
|
+
// reason is "timeout" or "memory" when the supervisor killed the worker,
|
|
32
|
+
// "memory" when stderr says the JavaScript heap ran out, and "crash" otherwise.
|
|
33
|
+
// 5. For a SQLite store and a job that writes it, record the unit in failed_units
|
|
34
|
+
// with the reason (later jobs skip it until its inputs change, or until a job
|
|
35
|
+
// with retryFailed), and clear the running record. ":memory:" and overlay jobs
|
|
36
|
+
// keep nothing. Run the job again with the unit added to skipUnits, a cold job
|
|
37
|
+
// as a commit job, which keeps the units committed before the kill. Give up
|
|
38
|
+
// when no unit was running, or after a few restarts.
|
|
39
|
+
import { readSync, writeSync } from "fs";
|
|
40
|
+
import { runJob } from "./build.js";
|
|
41
|
+
import { GitSource } from "./git-source.js";
|
|
42
|
+
import { mapSource } from "./sources.js";
|
|
43
|
+
import { MemoryStore } from "./store.js";
|
|
44
|
+
import { validatorVersion } from "./version.js";
|
|
45
|
+
const pause = new Int32Array(new SharedArrayBuffer(4));
|
|
46
|
+
/** A synchronous sleep, for a pipe that is full or not ready. */
|
|
47
|
+
const sleep = (ms) => Atomics.wait(pause, 0, 0, ms);
|
|
48
|
+
/** Write one line to stdout synchronously, whole, even when the pipe is non-blocking. */
|
|
49
|
+
function emit(event) {
|
|
50
|
+
const bytes = Buffer.from(`${JSON.stringify(event)}\n`);
|
|
51
|
+
for (let at = 0; at < bytes.length;) {
|
|
52
|
+
try {
|
|
53
|
+
at += writeSync(1, bytes, at, bytes.length - at);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (error.code !== "EAGAIN")
|
|
57
|
+
throw error;
|
|
58
|
+
sleep(5);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function readStdin() {
|
|
63
|
+
const chunks = [];
|
|
64
|
+
const chunk = Buffer.alloc(1 << 20);
|
|
65
|
+
for (;;) {
|
|
66
|
+
let n;
|
|
67
|
+
try {
|
|
68
|
+
n = readSync(0, chunk, 0, chunk.length, null);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
const code = error.code;
|
|
72
|
+
if (code === "EAGAIN") {
|
|
73
|
+
sleep(5);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (code === "EOF")
|
|
77
|
+
break;
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
if (n === 0)
|
|
81
|
+
break;
|
|
82
|
+
chunks.push(Buffer.from(chunk.subarray(0, n)));
|
|
83
|
+
}
|
|
84
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
85
|
+
}
|
|
86
|
+
/** The store a job names; overlay jobs read the live store as one snapshot and write nothing. */
|
|
87
|
+
async function openStore(job, version) {
|
|
88
|
+
if (job.storePath === ":memory:") {
|
|
89
|
+
const store = new MemoryStore();
|
|
90
|
+
store.setMeta("validatorVersion", version);
|
|
91
|
+
return { store };
|
|
92
|
+
}
|
|
93
|
+
const sqlite = await import("./sqlite-store.js");
|
|
94
|
+
const readonly = job.kind === "overlay";
|
|
95
|
+
return {
|
|
96
|
+
store: sqlite.openSqliteStore(job.storePath, { version, readonly }),
|
|
97
|
+
sqlite,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function openSource(job, store) {
|
|
101
|
+
if (job.source.type === "files")
|
|
102
|
+
return mapSource(job.source.files);
|
|
103
|
+
const { repoDir, commit, directories } = job.source;
|
|
104
|
+
// Blob ids in the build layer were valid UTF-8 when stored: no need to read them again.
|
|
105
|
+
return new GitSource(repoDir, commit, directories, {
|
|
106
|
+
trusted: store.getFileVersions().values(),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function unitHooks(job, store) {
|
|
110
|
+
const record = job.kind !== "overlay";
|
|
111
|
+
const testHook = testHooks();
|
|
112
|
+
return {
|
|
113
|
+
beforeUnit(id) {
|
|
114
|
+
emit({ event: "unit", id });
|
|
115
|
+
if (record)
|
|
116
|
+
store.setRunning(id);
|
|
117
|
+
testHook?.(id);
|
|
118
|
+
},
|
|
119
|
+
afterUnit() {
|
|
120
|
+
emit({ event: "unit", id: null });
|
|
121
|
+
if (record)
|
|
122
|
+
store.setRunning(null);
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* For the blame-and-skip tests only (core/content/tests/test_engine_integration.py,
|
|
128
|
+
* test_worker_faults.py), with LENS_ENGINE_TEST_HOOKS=1: the unit named by
|
|
129
|
+
* LENS_ENGINE_TEST_HANG_UNIT loops forever, LENS_ENGINE_TEST_LEAK_UNIT keeps
|
|
130
|
+
* allocating memory until it is killed, and LENS_ENGINE_TEST_CRASH_UNIT aborts the process.
|
|
131
|
+
*/
|
|
132
|
+
function testHooks() {
|
|
133
|
+
const env = process.env;
|
|
134
|
+
if (env.LENS_ENGINE_TEST_HOOKS !== "1")
|
|
135
|
+
return undefined;
|
|
136
|
+
return (id) => {
|
|
137
|
+
if (id === env.LENS_ENGINE_TEST_HANG_UNIT)
|
|
138
|
+
for (;;)
|
|
139
|
+
;
|
|
140
|
+
if (id === env.LENS_ENGINE_TEST_LEAK_UNIT) {
|
|
141
|
+
const kept = [];
|
|
142
|
+
for (;;) {
|
|
143
|
+
kept.push(Buffer.alloc(1 << 20, 1)); // 1 MB a millisecond
|
|
144
|
+
sleep(1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (id === env.LENS_ENGINE_TEST_CRASH_UNIT)
|
|
148
|
+
process.abort();
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/** This version's store is complete, so older versions' stores go. Disk clean-up only: a failure is reported, not fatal. */
|
|
152
|
+
function removeOldStores(sqlite, storePath) {
|
|
153
|
+
try {
|
|
154
|
+
sqlite?.removeOtherStores(storePath);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
console.error(`Could not delete older build layers beside ${storePath}:`, error);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function errorResult(message, version) {
|
|
161
|
+
return {
|
|
162
|
+
event: "result",
|
|
163
|
+
status: "error",
|
|
164
|
+
message,
|
|
165
|
+
validatorVersion: version,
|
|
166
|
+
changedPages: [],
|
|
167
|
+
changedArtifacts: [],
|
|
168
|
+
unitsRun: 0,
|
|
169
|
+
skippedUnits: [],
|
|
170
|
+
timings: {},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
async function main() {
|
|
174
|
+
const version = validatorVersion();
|
|
175
|
+
if (process.argv.includes("--version")) {
|
|
176
|
+
emit({ validatorVersion: version });
|
|
177
|
+
return 0;
|
|
178
|
+
}
|
|
179
|
+
// stdout carries the events and nothing else.
|
|
180
|
+
console.log = console.info = console.debug = console.error;
|
|
181
|
+
let job;
|
|
182
|
+
let opened;
|
|
183
|
+
let source;
|
|
184
|
+
try {
|
|
185
|
+
job = JSON.parse(readStdin());
|
|
186
|
+
opened = await openStore(job, version);
|
|
187
|
+
source = openSource(job, opened.store);
|
|
188
|
+
const started = Math.round(performance.now());
|
|
189
|
+
const result = runJob(job, opened.store, source, unitHooks(job, opened.store));
|
|
190
|
+
result.timings.workerStart = started;
|
|
191
|
+
opened.store.close();
|
|
192
|
+
if (job.kind === "commit" || job.kind === "cold")
|
|
193
|
+
removeOldStores(opened.sqlite, job.storePath);
|
|
194
|
+
emit(result);
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
let message = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
|
199
|
+
try {
|
|
200
|
+
opened?.store.close();
|
|
201
|
+
}
|
|
202
|
+
catch (closeError) {
|
|
203
|
+
message += `\nClosing the build layer also failed: ${closeError}`;
|
|
204
|
+
}
|
|
205
|
+
if (opened?.sqlite?.isDamagedStoreError(error) && job?.kind !== "overlay") {
|
|
206
|
+
opened.sqlite.removeStoreFiles(job.storePath);
|
|
207
|
+
message = `The build layer was damaged and has been deleted; the next job builds everything again.\n${message}`;
|
|
208
|
+
}
|
|
209
|
+
emit(errorResult(message, version));
|
|
210
|
+
return 1;
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
source?.close?.();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
process.exitCode = await main();
|
|
217
|
+
// Exit by draining the event loop, so stderr written through a pipe is not cut
|
|
218
|
+
// short; if anything is still holding the loop after five seconds, leave anyway.
|
|
219
|
+
setTimeout(() => process.exit(), 5000).unref();
|
|
220
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/engine/worker.ts"],"names":[],"mappings":"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"}
|