@forzalabs/remora 2.0.0 → 2.0.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/CHANGELOG.md +12 -0
- package/index.js +455 -300
- package/package.json +1 -1
- package/workers/ExecutorWorker.js +193 -34
package/package.json
CHANGED
|
@@ -11057,9 +11057,20 @@ var init_FileLogService = __esm({
|
|
|
11057
11057
|
FileLogServiceClass = class {
|
|
11058
11058
|
constructor() {
|
|
11059
11059
|
this._enabled = false;
|
|
11060
|
+
/**
|
|
11061
|
+
* Points the sink at a file, once per target.
|
|
11062
|
+
*
|
|
11063
|
+
* Idempotent on purpose: the executor worker re-applies the parent's logger config at the START
|
|
11064
|
+
* OF EVERY TASK, and a thread under the run-long pool handles many tasks. Building a transport
|
|
11065
|
+
* per call left one open append stream per task on the same file.
|
|
11066
|
+
*/
|
|
11060
11067
|
this.enable = (folder = "./remora/.temp/logs", file = "remora.log") => {
|
|
11068
|
+
if (this._enabled && this._folder === folder && this._file === file)
|
|
11069
|
+
return;
|
|
11061
11070
|
if (!(0, import_fs.existsSync)(folder))
|
|
11062
11071
|
(0, import_fs.mkdirSync)(folder, { recursive: true });
|
|
11072
|
+
if (this._logger)
|
|
11073
|
+
this._logger.end();
|
|
11063
11074
|
this._folder = folder;
|
|
11064
11075
|
this._file = file;
|
|
11065
11076
|
this._logger = import_winston.default.createLogger({
|
|
@@ -11073,6 +11084,9 @@ ${stack}` : base;
|
|
|
11073
11084
|
})
|
|
11074
11085
|
),
|
|
11075
11086
|
transports: [
|
|
11087
|
+
// Rotation is the transport's job. A run writes this file from the main process and
|
|
11088
|
+
// from every executor thread, so a rotation renames a file others hold open — the
|
|
11089
|
+
// 'error' handler below is what keeps that a logged line rather than a dead thread.
|
|
11076
11090
|
new import_winston.default.transports.File({
|
|
11077
11091
|
filename: import_path.default.join(folder, file),
|
|
11078
11092
|
maxsize: 10 * 1024 * 1024,
|
|
@@ -11082,6 +11096,7 @@ ${stack}` : base;
|
|
|
11082
11096
|
})
|
|
11083
11097
|
]
|
|
11084
11098
|
});
|
|
11099
|
+
this._logger.on("error", (error) => console.error(`File logger error: ${error?.message ?? String(error)}`));
|
|
11085
11100
|
this._enabled = true;
|
|
11086
11101
|
};
|
|
11087
11102
|
/**
|
|
@@ -11123,11 +11138,22 @@ ${stack}` : base;
|
|
|
11123
11138
|
}
|
|
11124
11139
|
});
|
|
11125
11140
|
};
|
|
11141
|
+
/**
|
|
11142
|
+
* Ends the sink. The state goes with it, so the next write re-enables a fresh one.
|
|
11143
|
+
*
|
|
11144
|
+
* That reset is the point: a closed winston logger still accepts `log()` calls and writes them
|
|
11145
|
+
* into an ended stream, which throws ERR_STREAM_WRITE_AFTER_END from a stream nothing is
|
|
11146
|
+
* listening to. Leaving `_enabled` true after a close is what turned "someone logged after
|
|
11147
|
+
* close" into "the process died". Use `flush()` when the sink is still needed afterwards.
|
|
11148
|
+
*/
|
|
11126
11149
|
this.close = () => {
|
|
11127
11150
|
if (!this._enabled || !this._logger) return Promise.resolve();
|
|
11151
|
+
const closing = this._logger;
|
|
11152
|
+
this._enabled = false;
|
|
11153
|
+
this._logger = null;
|
|
11128
11154
|
return new Promise((resolve) => {
|
|
11129
|
-
|
|
11130
|
-
|
|
11155
|
+
closing.on("finish", resolve);
|
|
11156
|
+
closing.end();
|
|
11131
11157
|
});
|
|
11132
11158
|
};
|
|
11133
11159
|
}
|
|
@@ -11549,7 +11575,7 @@ var CONSTANTS, Constants_default;
|
|
|
11549
11575
|
var init_Constants = __esm({
|
|
11550
11576
|
"../../packages/constants/src/Constants.ts"() {
|
|
11551
11577
|
CONSTANTS = {
|
|
11552
|
-
cliVersion: "2.0.
|
|
11578
|
+
cliVersion: "2.0.1",
|
|
11553
11579
|
backendVersion: 1,
|
|
11554
11580
|
backendPort: 5088,
|
|
11555
11581
|
workerVersion: 2,
|
|
@@ -11834,6 +11860,132 @@ var init_SecretManager = __esm({
|
|
|
11834
11860
|
}
|
|
11835
11861
|
});
|
|
11836
11862
|
|
|
11863
|
+
// ../../packages/common/src/schema/SchemaErrorReporter.ts
|
|
11864
|
+
var MAX_REPORTED_ERRORS, MAX_LISTED_VALUES, MAX_QUOTED_LENGTH, VALUE_KEYWORDS, ITEM_NAME_KEYS, SchemaErrorReporterClass, SchemaErrorReporter, SchemaErrorReporter_default;
|
|
11865
|
+
var init_SchemaErrorReporter = __esm({
|
|
11866
|
+
"../../packages/common/src/schema/SchemaErrorReporter.ts"() {
|
|
11867
|
+
MAX_REPORTED_ERRORS = 3;
|
|
11868
|
+
MAX_LISTED_VALUES = 30;
|
|
11869
|
+
MAX_QUOTED_LENGTH = 40;
|
|
11870
|
+
VALUE_KEYWORDS = ["type", "pattern", "format", "maxLength", "minLength", "minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf", "const"];
|
|
11871
|
+
ITEM_NAME_KEYS = ["name", "alias", "key", "title"];
|
|
11872
|
+
SchemaErrorReporterClass = class {
|
|
11873
|
+
constructor() {
|
|
11874
|
+
/**
|
|
11875
|
+
* One human-readable line for a failed validation. `data` is the value that was validated, and is
|
|
11876
|
+
* read to quote the offending value back and to name the resource's own parts.
|
|
11877
|
+
*/
|
|
11878
|
+
this.describe = (errors, data) => {
|
|
11879
|
+
const relevant = this._prune(errors ?? []);
|
|
11880
|
+
const shown = relevant.slice(0, MAX_REPORTED_ERRORS).map((x2) => this._explain(x2, data));
|
|
11881
|
+
const hidden = relevant.length - shown.length;
|
|
11882
|
+
return [shown.join("; "), hidden > 0 ? `+${hidden} more` : null].filter(Boolean).join(", ");
|
|
11883
|
+
};
|
|
11884
|
+
/**
|
|
11885
|
+
* Drops the alternatives' complaints from every failed `oneOf`/`anyOf`, because they are one
|
|
11886
|
+
* rejection per alternative rather than one problem. What survives is whatever an alternative
|
|
11887
|
+
* said about a value BELOW the branch point — the alternative that got far enough in to find the
|
|
11888
|
+
* real mistake — and when no alternative got that far, the branch point itself, which then reads
|
|
11889
|
+
* as the list of what was allowed there.
|
|
11890
|
+
*/
|
|
11891
|
+
this._prune = (errors) => {
|
|
11892
|
+
const branchPoints = errors.filter((x2) => x2.keyword === "oneOf" || x2.keyword === "anyOf");
|
|
11893
|
+
const kept = branchPoints.reduce((remaining, branch) => this._resolveBranch(remaining, branch), errors);
|
|
11894
|
+
return this._dedupe(kept);
|
|
11895
|
+
};
|
|
11896
|
+
/** Keeps, of everything reported at one branch point, only what says where the mistake actually is. */
|
|
11897
|
+
this._resolveBranch = (kept, branch) => {
|
|
11898
|
+
if (!kept.includes(branch)) return kept;
|
|
11899
|
+
const isAtBranch = (x2) => x2.instancePath === branch.instancePath;
|
|
11900
|
+
const hasDeeper = kept.some((x2) => x2.instancePath.startsWith(branch.instancePath + "/"));
|
|
11901
|
+
return hasDeeper ? kept.filter((x2) => !isAtBranch(x2)) : kept.filter((x2) => !isAtBranch(x2) || x2 === branch);
|
|
11902
|
+
};
|
|
11903
|
+
/** The same complaint from several alternatives is still one complaint. */
|
|
11904
|
+
this._dedupe = (errors) => {
|
|
11905
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11906
|
+
return errors.filter((x2) => {
|
|
11907
|
+
const signature = `${x2.instancePath}|${x2.keyword}|${x2.message}|${JSON.stringify(x2.params ?? {})}`;
|
|
11908
|
+
if (seen.has(signature)) return false;
|
|
11909
|
+
seen.add(signature);
|
|
11910
|
+
return true;
|
|
11911
|
+
});
|
|
11912
|
+
};
|
|
11913
|
+
this._explain = (error, data) => `${this._locate(error.instancePath, data)} ${this._reason(error, data)}`;
|
|
11914
|
+
/**
|
|
11915
|
+
* The JSON pointer to the offending value, followed by the name of the array item it sits in.
|
|
11916
|
+
* "/fields/2" is a position; "(field "created_at")" is what the author called it.
|
|
11917
|
+
*/
|
|
11918
|
+
this._locate = (instancePath, data) => {
|
|
11919
|
+
const named = this._nameItem(instancePath, data);
|
|
11920
|
+
return [instancePath || "/", named].filter(Boolean).join(" ");
|
|
11921
|
+
};
|
|
11922
|
+
/** `/fields/2/transform/cast` in a consumer -> `(field "created_at")`, when the item carries a name. */
|
|
11923
|
+
this._nameItem = (instancePath, data) => {
|
|
11924
|
+
const segments = this._segments(instancePath);
|
|
11925
|
+
for (let index = segments.length - 1; index > 0; index--) {
|
|
11926
|
+
if (!/^\d+$/.test(segments[index])) continue;
|
|
11927
|
+
const item = this._valueAt(data, segments.slice(0, index + 1));
|
|
11928
|
+
const nameKey = ITEM_NAME_KEYS.find((key) => typeof item?.[key] === "string");
|
|
11929
|
+
if (!nameKey) continue;
|
|
11930
|
+
return `(${this._singular(segments[index - 1])} "${item[nameKey]}")`;
|
|
11931
|
+
}
|
|
11932
|
+
return null;
|
|
11933
|
+
};
|
|
11934
|
+
/** Reads the way a config author speaks about the collection: `fields` -> `field`. */
|
|
11935
|
+
this._singular = (collection) => collection.endsWith("s") ? collection.slice(0, -1) : collection;
|
|
11936
|
+
/**
|
|
11937
|
+
* What was wrong, in the terms the author wrote. Ajv's own message says what the schema wanted;
|
|
11938
|
+
* these add what was actually there, which is the half that names the typo.
|
|
11939
|
+
*/
|
|
11940
|
+
this._reason = (error, data) => {
|
|
11941
|
+
const value = "data" in error ? error.data : this._valueAt(data, this._segments(error.instancePath));
|
|
11942
|
+
if (error.keyword === "enum")
|
|
11943
|
+
return `must be one of: ${this._list(error.params.allowedValues)} (got ${this._quote(value)})`;
|
|
11944
|
+
if (error.keyword === "additionalProperties")
|
|
11945
|
+
return `has unknown property "${error.params.additionalProperty}"`;
|
|
11946
|
+
if (error.keyword === "oneOf" || error.keyword === "anyOf")
|
|
11947
|
+
return this._describeAlternatives(error, value);
|
|
11948
|
+
if (VALUE_KEYWORDS.includes(error.keyword))
|
|
11949
|
+
return `${error.message} (got ${this._quote(value)})`;
|
|
11950
|
+
return error.message;
|
|
11951
|
+
};
|
|
11952
|
+
/**
|
|
11953
|
+
* A branch point no alternative matched, named by what the alternatives ARE — read off the schema
|
|
11954
|
+
* rather than off their rejections, because an alternative says nothing about the property it
|
|
11955
|
+
* requires when that property is present and the rest of it is what failed.
|
|
11956
|
+
*/
|
|
11957
|
+
this._describeAlternatives = (error, value) => {
|
|
11958
|
+
const shapes = (error.schema ?? []).map((x2) => this._shape(x2)).filter(Boolean);
|
|
11959
|
+
if (shapes.length === 0) return error.message;
|
|
11960
|
+
if (value && typeof value === "object" && !Array.isArray(value))
|
|
11961
|
+
return `must have exactly one of: ${this._list(shapes)} (has: ${this._list(Object.keys(value))})`;
|
|
11962
|
+
return `must be one of: ${this._list(shapes)} (got ${this._quote(value)})`;
|
|
11963
|
+
};
|
|
11964
|
+
/** How one alternative would be described to whoever has to write it: a property, or a type. */
|
|
11965
|
+
this._shape = (alternative) => {
|
|
11966
|
+
if (alternative?.required?.length > 0) return alternative.required.join(" + ");
|
|
11967
|
+
if (alternative?.pattern) return `${alternative.type ?? "string"} matching "${alternative.pattern}"`;
|
|
11968
|
+
return alternative?.type ?? null;
|
|
11969
|
+
};
|
|
11970
|
+
/** `/fields/2/transform` -> ['fields', '2', 'transform'], undoing the pointer's escapes. */
|
|
11971
|
+
this._segments = (instancePath) => (instancePath || "").split("/").slice(1).map((x2) => x2.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
11972
|
+
this._valueAt = (data, segments) => segments.reduce((current, segment) => current?.[segment], data);
|
|
11973
|
+
this._list = (values) => {
|
|
11974
|
+
const shown = (values ?? []).slice(0, MAX_LISTED_VALUES).join(", ");
|
|
11975
|
+
const hidden = (values ?? []).length - MAX_LISTED_VALUES;
|
|
11976
|
+
return hidden > 0 ? `${shown}, +${hidden} more` : shown;
|
|
11977
|
+
};
|
|
11978
|
+
this._quote = (value) => {
|
|
11979
|
+
const text = value === void 0 ? "nothing" : JSON.stringify(value);
|
|
11980
|
+
return text.length > MAX_QUOTED_LENGTH ? `${text.slice(0, MAX_QUOTED_LENGTH)}\u2026` : text;
|
|
11981
|
+
};
|
|
11982
|
+
}
|
|
11983
|
+
};
|
|
11984
|
+
SchemaErrorReporter = new SchemaErrorReporterClass();
|
|
11985
|
+
SchemaErrorReporter_default = SchemaErrorReporter;
|
|
11986
|
+
}
|
|
11987
|
+
});
|
|
11988
|
+
|
|
11837
11989
|
// ../../packages/common/src/schema/SchemaValidator.ts
|
|
11838
11990
|
var import_ajv, import_ajv_formats, import_fs3, import_path3, SchemaValidatorClass, SchemaValidator, SchemaValidator_default;
|
|
11839
11991
|
var init_SchemaValidator = __esm({
|
|
@@ -11843,11 +11995,12 @@ var init_SchemaValidator = __esm({
|
|
|
11843
11995
|
import_fs3 = __toESM(require("fs"), 1);
|
|
11844
11996
|
import_path3 = __toESM(require("path"), 1);
|
|
11845
11997
|
init_src();
|
|
11998
|
+
init_SchemaErrorReporter();
|
|
11846
11999
|
SchemaValidatorClass = class {
|
|
11847
12000
|
constructor() {
|
|
11848
12001
|
this.addSchema = (schema, schemaKey) => {
|
|
11849
12002
|
const isValid = this.ajv.validateSchema(schema);
|
|
11850
|
-
Affirm_default(isValid, `Invalid JSON Schema: ${
|
|
12003
|
+
Affirm_default(isValid, `Invalid JSON Schema: ${SchemaErrorReporter_default.describe(this.ajv.errors, schema)}`);
|
|
11851
12004
|
this.ajv.addSchema(schema, schemaKey);
|
|
11852
12005
|
};
|
|
11853
12006
|
this.getSchema = (schemaKey) => {
|
|
@@ -11901,7 +12054,10 @@ var init_SchemaValidator = __esm({
|
|
|
11901
12054
|
strictSchema: false,
|
|
11902
12055
|
// Code values may be string | number | boolean, so schemas legitimately use union types.
|
|
11903
12056
|
allowUnionTypes: true,
|
|
11904
|
-
validateFormats: true
|
|
12057
|
+
validateFormats: true,
|
|
12058
|
+
// Carries the failing schema and value on each error, which is what lets
|
|
12059
|
+
// SchemaErrorReporter say what was allowed there and what was found instead.
|
|
12060
|
+
verbose: true
|
|
11905
12061
|
});
|
|
11906
12062
|
(0, import_ajv_formats.default)(this.ajv);
|
|
11907
12063
|
this.loadInternalSchema();
|
|
@@ -13231,7 +13387,7 @@ ${errors.map((x2) => ` - ${x2}`).join("\n")}`);
|
|
|
13231
13387
|
});
|
|
13232
13388
|
|
|
13233
13389
|
// ../../packages/common/src/Environment.ts
|
|
13234
|
-
var import_fs4, import_crypto, import_adm_zip, import_path4, import_client_s3, RESOURCE_SCHEMA_KEYS, INVALID_RESOURCE_MESSAGES,
|
|
13390
|
+
var import_fs4, import_crypto, import_adm_zip, import_path4, import_client_s3, RESOURCE_SCHEMA_KEYS, INVALID_RESOURCE_MESSAGES, EnvironmentClass, Environment, Environment_default;
|
|
13235
13391
|
var init_Environment = __esm({
|
|
13236
13392
|
"../../packages/common/src/Environment.ts"() {
|
|
13237
13393
|
import_fs4 = __toESM(require("fs"), 1);
|
|
@@ -13240,6 +13396,7 @@ var init_Environment = __esm({
|
|
|
13240
13396
|
init_src();
|
|
13241
13397
|
import_path4 = __toESM(require("path"), 1);
|
|
13242
13398
|
init_SchemaValidator();
|
|
13399
|
+
init_SchemaErrorReporter();
|
|
13243
13400
|
init_Validator();
|
|
13244
13401
|
init_ProducerExpander();
|
|
13245
13402
|
init_src3();
|
|
@@ -13259,7 +13416,6 @@ var init_Environment = __esm({
|
|
|
13259
13416
|
codeSet: "Invalid code set configuration",
|
|
13260
13417
|
schema: "Invalid schema configuration"
|
|
13261
13418
|
};
|
|
13262
|
-
MAX_REPORTED_SCHEMA_ERRORS = 3;
|
|
13263
13419
|
EnvironmentClass = class {
|
|
13264
13420
|
constructor() {
|
|
13265
13421
|
this._env = null;
|
|
@@ -13432,21 +13588,21 @@ var init_Environment = __esm({
|
|
|
13432
13588
|
schemas.forEach((schema) => this._registerSchema(schema, problems));
|
|
13433
13589
|
const sourceFiles = read(projectConfig.sources);
|
|
13434
13590
|
const sources = sourceFiles.map((x2) => x2.config);
|
|
13435
|
-
|
|
13591
|
+
sourceFiles.forEach((source) => this._recordSchemaProblem(source, "source", problems));
|
|
13436
13592
|
this._checkApiQueueSource(projectConfig, sources, problems);
|
|
13437
13593
|
const producerFiles = read(projectConfig.producers);
|
|
13438
13594
|
const producers = producerFiles.map((x2) => x2.config);
|
|
13439
|
-
|
|
13440
|
-
const schemaProblem = this._schemaProblem(producer, "producer");
|
|
13595
|
+
producerFiles.forEach(({ config: producer, file }) => {
|
|
13596
|
+
const schemaProblem = this._schemaProblem(producer, "producer", file);
|
|
13441
13597
|
this._expandProducer(producer, problems);
|
|
13442
13598
|
if (schemaProblem) problems.push(schemaProblem);
|
|
13443
13599
|
});
|
|
13444
13600
|
const consumerFiles = read(projectConfig.consumers);
|
|
13445
13601
|
const consumers = consumerFiles.map((x2) => x2.config);
|
|
13446
|
-
|
|
13602
|
+
consumerFiles.forEach((consumer) => this._recordSchemaProblem(consumer, "consumer", problems));
|
|
13447
13603
|
const codeSetFiles = read(projectConfig.codeSets ?? []);
|
|
13448
13604
|
const codeSets = codeSetFiles.map((x2) => x2.config);
|
|
13449
|
-
|
|
13605
|
+
codeSetFiles.forEach((codeSet) => this._recordSchemaProblem(codeSet, "codeSet", problems));
|
|
13450
13606
|
this._checkCodeSetNames(codeSets, problems);
|
|
13451
13607
|
return {
|
|
13452
13608
|
env: { settings: this._readSettings(projectConfig), sources, producers, consumers, schemas, codeSets },
|
|
@@ -13526,7 +13682,7 @@ var init_Environment = __esm({
|
|
|
13526
13682
|
severity: "error",
|
|
13527
13683
|
scope: "project",
|
|
13528
13684
|
file: projectPath,
|
|
13529
|
-
message: `Invalid project configuration
|
|
13685
|
+
message: `Invalid project configuration - ${SchemaErrorReporter_default.describe(validation.errors, projectConfig)}`
|
|
13530
13686
|
});
|
|
13531
13687
|
}
|
|
13532
13688
|
return projectConfig;
|
|
@@ -13582,8 +13738,8 @@ var init_Environment = __esm({
|
|
|
13582
13738
|
}
|
|
13583
13739
|
return results;
|
|
13584
13740
|
};
|
|
13585
|
-
this._recordSchemaProblem = (
|
|
13586
|
-
const problem = this._schemaProblem(
|
|
13741
|
+
this._recordSchemaProblem = (read, kind, problems) => {
|
|
13742
|
+
const problem = this._schemaProblem(read.config, kind, read.file);
|
|
13587
13743
|
if (problem) problems.push(problem);
|
|
13588
13744
|
};
|
|
13589
13745
|
/**
|
|
@@ -13594,15 +13750,17 @@ var init_Environment = __esm({
|
|
|
13594
13750
|
* Returns the problem instead of recording it, because a producer's is reported after its
|
|
13595
13751
|
* expansion has had its say (see `inspect`).
|
|
13596
13752
|
*/
|
|
13597
|
-
this._schemaProblem = (resource, kind) => {
|
|
13753
|
+
this._schemaProblem = (resource, kind, file) => {
|
|
13598
13754
|
const name = resource?.name;
|
|
13599
13755
|
const validation = SchemaValidator_default.validate(RESOURCE_SCHEMA_KEYS[kind], resource);
|
|
13600
13756
|
if (validation.isValid) return null;
|
|
13757
|
+
const where = [name, file ? `(${file})` : null].filter(Boolean).join(" ");
|
|
13601
13758
|
return {
|
|
13602
13759
|
severity: "error",
|
|
13603
13760
|
scope: "resource",
|
|
13761
|
+
file,
|
|
13604
13762
|
resource: { kind, name },
|
|
13605
|
-
message: `${INVALID_RESOURCE_MESSAGES[kind]}: ${
|
|
13763
|
+
message: `${INVALID_RESOURCE_MESSAGES[kind]}: ${where} - ${SchemaErrorReporter_default.describe(validation.errors, resource)}`
|
|
13606
13764
|
};
|
|
13607
13765
|
};
|
|
13608
13766
|
this._registerSchema = (schema, problems) => {
|
|
@@ -13679,12 +13837,6 @@ var init_Environment = __esm({
|
|
|
13679
13837
|
/** Kept verbatim from the batch loader this replaced, so `load()` still throws the same string. */
|
|
13680
13838
|
this._loadFailureMessage = (remoraPath, configPath, error) => `Error loading from ${import_path4.default.resolve(remoraPath)} configuration from ${configPath}: ${this._messageOf(error)}`;
|
|
13681
13839
|
this._messageOf = (error) => error instanceof Error ? error.message : String(error);
|
|
13682
|
-
/** The first few Ajv complaints, so "Invalid producer configuration: p_x" says what is wrong with it. */
|
|
13683
|
-
this._describeSchemaErrors = (errors) => {
|
|
13684
|
-
const shown = (errors ?? []).slice(0, MAX_REPORTED_SCHEMA_ERRORS).map((item) => `${item.instancePath || "/"} ${item.message}`);
|
|
13685
|
-
const hidden = (errors ?? []).length - shown.length;
|
|
13686
|
-
return [shown.join("; "), hidden > 0 ? `+${hidden} more` : null].filter(Boolean).join(", ");
|
|
13687
|
-
};
|
|
13688
13840
|
/**
|
|
13689
13841
|
* The loaded environment, flattened for transport to a worker thread.
|
|
13690
13842
|
*
|
|
@@ -52262,8 +52414,9 @@ var init_ExecutorOrchestrator = __esm({
|
|
|
52262
52414
|
*
|
|
52263
52415
|
* `maxWorkers` must come from getParallelWorkerCount(), never from the amount of work: that count
|
|
52264
52416
|
* is memory-aware and is what stops a low-end machine oversubscribing itself. Surplus work queues
|
|
52265
|
-
* instead of spawning threads. `census` counts
|
|
52266
|
-
*
|
|
52417
|
+
* instead of spawning threads. `census.created` counts every thread workerpool spawns — they
|
|
52418
|
+
* spawn lazily, on first exec, and a thread that dies mid-run is replaced by another, so this is
|
|
52419
|
+
* a count of spawns and NOT a concurrency figure (see IWorkerCensus).
|
|
52267
52420
|
*
|
|
52268
52421
|
* `workerData` carries the loaded environment, so it is cloned once per THREAD at spawn rather
|
|
52269
52422
|
* than read and validated from disk once per chunk. Only run-constant data belongs there; per-task
|
|
@@ -52280,12 +52433,8 @@ var init_ExecutorOrchestrator = __esm({
|
|
|
52280
52433
|
}
|
|
52281
52434
|
},
|
|
52282
52435
|
onCreateWorker: () => {
|
|
52283
|
-
census.
|
|
52284
|
-
census.peak = Math.max(census.peak, census.alive);
|
|
52436
|
+
census.created++;
|
|
52285
52437
|
return void 0;
|
|
52286
|
-
},
|
|
52287
|
-
onTerminateWorker: () => {
|
|
52288
|
-
census.alive--;
|
|
52289
52438
|
}
|
|
52290
52439
|
};
|
|
52291
52440
|
const workerPath = OrchestratorHelper_default.getPhysicalWorkerPath();
|
|
@@ -52304,7 +52453,7 @@ var init_ExecutorOrchestrator = __esm({
|
|
|
52304
52453
|
const { usageId } = UsageManager_default.startUsage(consumer, details);
|
|
52305
52454
|
const scope = { id: usageId, folder: `${consumer.name}_${usageId}`, workersId: [], limitFileSize: consumer.maximumFileSize };
|
|
52306
52455
|
const expandedFields = ConsumerManager_default.getExpandedFields(consumer);
|
|
52307
|
-
const census = {
|
|
52456
|
+
const census = { created: 0, peak: 0 };
|
|
52308
52457
|
try {
|
|
52309
52458
|
const start = performance.now();
|
|
52310
52459
|
const executorResults = [];
|
|
@@ -52543,6 +52692,7 @@ var init_ExecutorOrchestrator = __esm({
|
|
|
52543
52692
|
const context = {
|
|
52544
52693
|
consumer: request.consumer,
|
|
52545
52694
|
scope,
|
|
52695
|
+
census,
|
|
52546
52696
|
options: request.options,
|
|
52547
52697
|
lookups: request.lookups,
|
|
52548
52698
|
progress: request.progress,
|
|
@@ -52557,8 +52707,12 @@ var init_ExecutorOrchestrator = __esm({
|
|
|
52557
52707
|
phase = performance.now();
|
|
52558
52708
|
const resultsByFile = await Promise.all(pendingByFile.map((file) => Promise.all(file)));
|
|
52559
52709
|
tracker.measure("worker-wait", performance.now() - phase);
|
|
52710
|
+
census.peak = Math.max(census.peak, pool.stats().totalWorkers);
|
|
52560
52711
|
Affirm_default(census.peak <= poolSize, `The worker pool ran ${census.peak} thread(s) at once but was sized ${poolSize}`);
|
|
52561
52712
|
const results = resultsByFile.flat();
|
|
52713
|
+
const failures = results.filter((x2) => !Algo_default.hasVal(x2)).length;
|
|
52714
|
+
if (failures > 0 || census.created > poolSize)
|
|
52715
|
+
Logger_default.warn(`[${scope.id}] ${failures} of ${chunkCount} chunk(s) failed and ${census.created} thread(s) were spawned for a pool of ${poolSize} \u2014 a thread that dies is replaced, so the chunk error(s) logged above are the cause`);
|
|
52562
52716
|
this.recordWorkerBoot(results, context.requestedAtByWorker, tracker);
|
|
52563
52717
|
for (const [index, fileResults] of resultsByFile.entries())
|
|
52564
52718
|
this._assignChunkRowMetadata(workFiles[index].chunks, fileResults);
|
|
@@ -52589,7 +52743,12 @@ var init_ExecutorOrchestrator = __esm({
|
|
|
52589
52743
|
context.requestedAtByWorker.set(workerId, performance.timeOrigin + performance.now());
|
|
52590
52744
|
Logger_default.log(`[${scope.id}] Queued worker ${workerId} for ${file.label} \u2014 chunk ${chunk.start}-${chunk.end} (${Math.round((chunk.end - chunk.start) / 1024)}KB)`);
|
|
52591
52745
|
return pool.exec("executor", [workerData], {
|
|
52592
|
-
on
|
|
52746
|
+
// Progress packets are the run's only regular tick on the main thread, which makes them
|
|
52747
|
+
// the cheapest place to sample how many threads are actually alive at once.
|
|
52748
|
+
on: (payload) => {
|
|
52749
|
+
context.census.peak = Math.max(context.census.peak, pool.stats().totalWorkers);
|
|
52750
|
+
this.onWorkAdvanced(payload, workerId, context);
|
|
52751
|
+
}
|
|
52593
52752
|
}).catch((error) => {
|
|
52594
52753
|
Logger_default.error(error);
|
|
52595
52754
|
return null;
|
|
@@ -53393,11 +53552,11 @@ var runExecutorTask = async (workerData) => {
|
|
|
53393
53552
|
rssMB: Math.round(memory.rss / (1024 * 1024)),
|
|
53394
53553
|
heapUsedMB: Math.round(memory.heapUsed / (1024 * 1024))
|
|
53395
53554
|
};
|
|
53396
|
-
await Logger_default.
|
|
53555
|
+
await Logger_default.flush();
|
|
53397
53556
|
return res;
|
|
53398
53557
|
} catch (error) {
|
|
53399
53558
|
Logger_default.error(error);
|
|
53400
|
-
await Logger_default.
|
|
53559
|
+
await Logger_default.flush();
|
|
53401
53560
|
throw error;
|
|
53402
53561
|
}
|
|
53403
53562
|
};
|