@mmnto/totem 1.15.2 → 1.15.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/compile-lesson.d.ts +35 -0
  2. package/dist/compile-lesson.d.ts.map +1 -1
  3. package/dist/compile-lesson.js +80 -39
  4. package/dist/compile-lesson.js.map +1 -1
  5. package/dist/compile-lesson.test.js +229 -0
  6. package/dist/compile-lesson.test.js.map +1 -1
  7. package/dist/compiler-schema.d.ts +58 -16
  8. package/dist/compiler-schema.d.ts.map +1 -1
  9. package/dist/compiler-schema.js +79 -0
  10. package/dist/compiler-schema.js.map +1 -1
  11. package/dist/compiler-schema.test.js +160 -1
  12. package/dist/compiler-schema.test.js.map +1 -1
  13. package/dist/compiler.d.ts +1 -1
  14. package/dist/compiler.d.ts.map +1 -1
  15. package/dist/compiler.js +1 -1
  16. package/dist/compiler.js.map +1 -1
  17. package/dist/index.d.ts +5 -2
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +6 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/lesson-pattern.d.ts +20 -0
  22. package/dist/lesson-pattern.d.ts.map +1 -1
  23. package/dist/lesson-pattern.js +38 -0
  24. package/dist/lesson-pattern.js.map +1 -1
  25. package/dist/lesson-pattern.test.js +94 -1
  26. package/dist/lesson-pattern.test.js.map +1 -1
  27. package/dist/regex-safety/apply-rules-bounded.d.ts +35 -0
  28. package/dist/regex-safety/apply-rules-bounded.d.ts.map +1 -0
  29. package/dist/regex-safety/apply-rules-bounded.js +114 -0
  30. package/dist/regex-safety/apply-rules-bounded.js.map +1 -0
  31. package/dist/regex-safety/apply-rules-bounded.test.d.ts +2 -0
  32. package/dist/regex-safety/apply-rules-bounded.test.d.ts.map +1 -0
  33. package/dist/regex-safety/apply-rules-bounded.test.js +136 -0
  34. package/dist/regex-safety/apply-rules-bounded.test.js.map +1 -0
  35. package/dist/regex-safety/evaluator.d.ts +95 -0
  36. package/dist/regex-safety/evaluator.d.ts.map +1 -0
  37. package/dist/regex-safety/evaluator.js +314 -0
  38. package/dist/regex-safety/evaluator.js.map +1 -0
  39. package/dist/regex-safety/evaluator.test.d.ts +2 -0
  40. package/dist/regex-safety/evaluator.test.d.ts.map +1 -0
  41. package/dist/regex-safety/evaluator.test.js +224 -0
  42. package/dist/regex-safety/evaluator.test.js.map +1 -0
  43. package/dist/regex-safety/telemetry.d.ts +50 -0
  44. package/dist/regex-safety/telemetry.d.ts.map +1 -0
  45. package/dist/regex-safety/telemetry.js +50 -0
  46. package/dist/regex-safety/telemetry.js.map +1 -0
  47. package/dist/regex-safety/telemetry.test.d.ts +2 -0
  48. package/dist/regex-safety/telemetry.test.d.ts.map +1 -0
  49. package/dist/regex-safety/telemetry.test.js +82 -0
  50. package/dist/regex-safety/telemetry.test.js.map +1 -0
  51. package/dist/regex-safety/worker.d.ts +31 -0
  52. package/dist/regex-safety/worker.d.ts.map +1 -0
  53. package/dist/regex-safety/worker.js +51 -0
  54. package/dist/regex-safety/worker.js.map +1 -0
  55. package/dist/rule-engine.d.ts +1 -0
  56. package/dist/rule-engine.d.ts.map +1 -1
  57. package/dist/rule-engine.js +1 -1
  58. package/dist/rule-engine.js.map +1 -1
  59. package/package.json +1 -1
@@ -0,0 +1,136 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { makeRuleEngineCtx } from '../test-utils.js';
3
+ import { applyRulesToAdditionsBounded } from './apply-rules-bounded.js';
4
+ import { RegexEvaluator } from './evaluator.js';
5
+ function addition(file, line, lineNumber) {
6
+ return { file, line, lineNumber, precedingLine: null };
7
+ }
8
+ function regexRule(lessonHash, pattern, engine = 'regex') {
9
+ return {
10
+ lessonHash,
11
+ lessonHeading: `rule ${lessonHash}`,
12
+ pattern,
13
+ message: `violation for ${lessonHash}`,
14
+ engine,
15
+ compiledAt: '2026-04-23T00:00:00Z',
16
+ };
17
+ }
18
+ describe('applyRulesToAdditionsBounded — happy path', () => {
19
+ it('flags matching additions under a sync evaluator', async () => {
20
+ const evaluator = new RegexEvaluator();
21
+ try {
22
+ const rules = [regexRule('h1', 'console\\.log')];
23
+ const additions = [
24
+ addition('foo.ts', 'console.log("a")', 10),
25
+ addition('foo.ts', 'logger.info("b")', 11),
26
+ ];
27
+ const result = await applyRulesToAdditionsBounded(makeRuleEngineCtx(), rules, additions, {
28
+ evaluator,
29
+ timeoutMode: 'strict',
30
+ repoRoot: '/tmp/repo',
31
+ });
32
+ expect(result.violations).toHaveLength(1);
33
+ expect(result.violations[0]?.rule.lessonHash).toBe('h1');
34
+ expect(result.violations[0]?.lineNumber).toBe(10);
35
+ expect(result.timeoutOutcomes).toEqual([]);
36
+ }
37
+ finally {
38
+ await evaluator.dispose();
39
+ }
40
+ });
41
+ it('respects fileGlobs when evaluating additions', async () => {
42
+ const evaluator = new RegexEvaluator();
43
+ try {
44
+ const rule = {
45
+ ...regexRule('scoped', 'foo'),
46
+ fileGlobs: ['**/*.md'],
47
+ };
48
+ const additions = [
49
+ addition('readme.md', 'foo', 1),
50
+ addition('app.ts', 'foo', 1),
51
+ ];
52
+ const result = await applyRulesToAdditionsBounded(makeRuleEngineCtx(), [rule], additions, {
53
+ evaluator,
54
+ timeoutMode: 'strict',
55
+ repoRoot: '/tmp/repo',
56
+ });
57
+ expect(result.violations).toHaveLength(1);
58
+ expect(result.violations[0]?.file).toBe('readme.md');
59
+ }
60
+ finally {
61
+ await evaluator.dispose();
62
+ }
63
+ });
64
+ });
65
+ describe('applyRulesToAdditionsBounded — timeout strict', () => {
66
+ it('returns a RuleTimeoutOutcome and excludes violations for the timing-out rule', async () => {
67
+ const evaluator = new RegexEvaluator({ timeoutMs: 150, softWarningMs: 50 });
68
+ try {
69
+ const rules = [regexRule('redos', '(a+)+b'), regexRule('healthy', 'foo')];
70
+ const additions = [
71
+ addition('a.ts', 'a'.repeat(50000) + 'c', 1),
72
+ addition('b.ts', 'foo', 2),
73
+ ];
74
+ const result = await applyRulesToAdditionsBounded(makeRuleEngineCtx(), rules, additions, {
75
+ evaluator,
76
+ timeoutMode: 'strict',
77
+ repoRoot: '/tmp/repo',
78
+ });
79
+ const timeoutHashes = result.timeoutOutcomes.map((o) => o.ruleHash);
80
+ expect(timeoutHashes).toContain('redos');
81
+ // Healthy rule should still produce its violation.
82
+ const healthyViolation = result.violations.find((v) => v.rule.lessonHash === 'healthy');
83
+ expect(healthyViolation).toBeDefined();
84
+ }
85
+ finally {
86
+ await evaluator.dispose();
87
+ }
88
+ });
89
+ });
90
+ describe('applyRulesToAdditionsBounded — timeout lenient', () => {
91
+ it('records timeout outcomes but does not differ from strict at the engine layer', async () => {
92
+ // The strict/lenient semantics are enforced at the CLI layer based on
93
+ // the timeoutOutcomes array this function returns. The engine itself
94
+ // is policy-free — it records the outcome and lets the caller decide
95
+ // the exit-code effect. Locking this in prevents future drift where
96
+ // the engine starts making policy decisions instead of surfacing them.
97
+ const evaluator = new RegexEvaluator({ timeoutMs: 150, softWarningMs: 50 });
98
+ try {
99
+ const rules = [regexRule('redos', '(a+)+b')];
100
+ const additions = [addition('a.ts', 'a'.repeat(50000) + 'c', 1)];
101
+ const strict = await applyRulesToAdditionsBounded(makeRuleEngineCtx(), rules, additions, {
102
+ evaluator,
103
+ timeoutMode: 'strict',
104
+ repoRoot: '/tmp/repo',
105
+ });
106
+ const lenient = await applyRulesToAdditionsBounded(makeRuleEngineCtx(), rules, additions, {
107
+ evaluator,
108
+ timeoutMode: 'lenient',
109
+ repoRoot: '/tmp/repo',
110
+ });
111
+ expect(strict.timeoutOutcomes).toHaveLength(1);
112
+ expect(lenient.timeoutOutcomes).toHaveLength(1);
113
+ }
114
+ finally {
115
+ await evaluator.dispose();
116
+ }
117
+ });
118
+ });
119
+ describe('applyRulesToAdditionsBounded — invalid pattern', () => {
120
+ it('fails loud on a compiled rule with an invalid regex (matches existing rule-engine contract)', async () => {
121
+ const evaluator = new RegexEvaluator();
122
+ try {
123
+ const rules = [regexRule('broken', '(unclosed')];
124
+ const additions = [addition('a.ts', 'foo', 1)];
125
+ await expect(applyRulesToAdditionsBounded(makeRuleEngineCtx(), rules, additions, {
126
+ evaluator,
127
+ timeoutMode: 'strict',
128
+ repoRoot: '/tmp/repo',
129
+ })).rejects.toThrow(/invalid regex|cannot be evaluated/i);
130
+ }
131
+ finally {
132
+ await evaluator.dispose();
133
+ }
134
+ });
135
+ });
136
+ //# sourceMappingURL=apply-rules-bounded.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-rules-bounded.test.js","sourceRoot":"","sources":["../../src/regex-safety/apply-rules-bounded.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Persistent-worker regex evaluator with per-batch timeout (mmnto-ai/totem#1641).
3
+ *
4
+ * Spawns one Node worker thread on construction, serializes batches onto
5
+ * it, and enforces a main-thread timeout. If a pattern catastrophic-
6
+ * backtracks inside the worker, the main-thread timer fires, calls
7
+ * `worker.terminate()`, and respawns a fresh worker for the next batch.
8
+ * Every evaluation resolves with one of three outcomes — `ok` (matched
9
+ * indices + elapsed + softWarning flag), `timeout` (the worker was
10
+ * terminated), or `error` (the pattern was syntactically invalid; the
11
+ * worker is still alive). The caller decides strict vs lenient handling.
12
+ *
13
+ * Invariants:
14
+ * - At most one `Worker` alive per evaluator instance.
15
+ * - `pending` never holds stale entries past a batch's terminal state.
16
+ * - Batches are serialized (one in-flight at a time) — no multiplexing.
17
+ * - Telemetry is emitted on every terminal outcome via the
18
+ * `onTelemetry` callback the caller supplies; if absent, telemetry
19
+ * is silently dropped (safe — the evaluator itself never fails on
20
+ * telemetry-sink failure).
21
+ */
22
+ import type { RegexTelemetry } from './telemetry.js';
23
+ export interface RegexEvaluatorConfig {
24
+ /** Hard timeout per batch (ms). Exceeded batches terminate the worker. */
25
+ timeoutMs: number;
26
+ /** Soft-warning threshold (ms). Sub-timeout but slow; sets the flag on telemetry. */
27
+ softWarningMs: number;
28
+ }
29
+ export interface EvaluateInput {
30
+ ruleHash: string;
31
+ pattern: string;
32
+ flags: string;
33
+ lines: readonly string[];
34
+ }
35
+ export type EvaluateResult = {
36
+ kind: 'ok';
37
+ matchedIndices: number[];
38
+ elapsedMs: number;
39
+ softWarningTriggered: boolean;
40
+ } | {
41
+ kind: 'timeout';
42
+ elapsedMs: number;
43
+ } | {
44
+ kind: 'error';
45
+ message: string;
46
+ elapsedMs: number;
47
+ };
48
+ export declare class RegexEvaluator {
49
+ private worker;
50
+ private readonly pending;
51
+ private readonly config;
52
+ private readonly onTelemetry;
53
+ private queue;
54
+ private disposed;
55
+ /**
56
+ * Coalesces concurrent respawn requests (mmnto-ai/totem#1641 Shield review
57
+ * round-1). Without this, a timeout event firing at roughly the same
58
+ * moment as a worker `error` event can call `spawnWorker()` twice,
59
+ * leaking a thread. `evaluate()` also awaits this promise before
60
+ * `postMessage` so a batch scheduled during a respawn waits for the
61
+ * new worker instead of silently dropping against a null handle.
62
+ */
63
+ private respawnPromise;
64
+ /**
65
+ * Worker-online gate (mmnto-ai/totem#1641, CI round-1 fix). The Node
66
+ * `Worker` constructor returns before the thread is actually running
67
+ * (thread-spawn takes ~30-50ms). If `evaluate()` starts its timeout
68
+ * timer before the worker is online, a slow CI box can trip a
69
+ * spurious timeout on the first batch. Gate postMessage on this
70
+ * promise so cold-start cost never counts against the budget.
71
+ */
72
+ private workerReady;
73
+ /**
74
+ * Consecutive-respawn counter (Shield review round-1). If the worker
75
+ * keeps dying at spawn time (missing worker.js, syntax error in the
76
+ * worker script, etc.), unbounded respawn becomes a CPU-pegging loop.
77
+ * The counter increments on each respawn, resets on every successful
78
+ * evaluation, and flips `permanentlyFailed` once the budget is spent.
79
+ */
80
+ private consecutiveRespawns;
81
+ private permanentlyFailed;
82
+ private static readonly MAX_CONSECUTIVE_RESPAWNS;
83
+ constructor(config?: Partial<RegexEvaluatorConfig>, onTelemetry?: (record: RegexTelemetry) => void);
84
+ evaluate(input: EvaluateInput & {
85
+ redactedPath?: string;
86
+ }): Promise<EvaluateResult>;
87
+ dispose(): Promise<void>;
88
+ private evaluateOnce;
89
+ private spawnWorker;
90
+ private respawnWorker;
91
+ private handleMessage;
92
+ private rejectAllPendingAsCrash;
93
+ private emitTelemetry;
94
+ }
95
+ //# sourceMappingURL=evaluator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluator.d.ts","sourceRoot":"","sources":["../../src/regex-safety/evaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AASH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,MAAM,WAAW,oBAAoB;IACnC,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,OAAO,CAAA;CAAE,GAC1F;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAkC1D,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuB;IAC9C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAiD;IAC7E,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,QAAQ,CAAS;IACzB;;;;;;;OAOG;IACH,OAAO,CAAC,cAAc,CAA8B;IACpD;;;;;;;OAOG;IACH,OAAO,CAAC,WAAW,CAAoC;IACvD;;;;;;OAMG;IACH,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,wBAAwB,CAAK;gBAGnD,MAAM,GAAE,OAAO,CAAC,oBAAoB,CAAM,EAC1C,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI;IAO1C,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG;QAAE,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,CAAC;IA6CnF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAa9B,OAAO,CAAC,YAAY;IAgDpB,OAAO,CAAC,WAAW;YAsCL,aAAa;IAkC3B,OAAO,CAAC,aAAa;IAkCrB,OAAO,CAAC,uBAAuB;IAiB/B,OAAO,CAAC,aAAa;CAQtB"}
@@ -0,0 +1,314 @@
1
+ /**
2
+ * Persistent-worker regex evaluator with per-batch timeout (mmnto-ai/totem#1641).
3
+ *
4
+ * Spawns one Node worker thread on construction, serializes batches onto
5
+ * it, and enforces a main-thread timeout. If a pattern catastrophic-
6
+ * backtracks inside the worker, the main-thread timer fires, calls
7
+ * `worker.terminate()`, and respawns a fresh worker for the next batch.
8
+ * Every evaluation resolves with one of three outcomes — `ok` (matched
9
+ * indices + elapsed + softWarning flag), `timeout` (the worker was
10
+ * terminated), or `error` (the pattern was syntactically invalid; the
11
+ * worker is still alive). The caller decides strict vs lenient handling.
12
+ *
13
+ * Invariants:
14
+ * - At most one `Worker` alive per evaluator instance.
15
+ * - `pending` never holds stale entries past a batch's terminal state.
16
+ * - Batches are serialized (one in-flight at a time) — no multiplexing.
17
+ * - Telemetry is emitted on every terminal outcome via the
18
+ * `onTelemetry` callback the caller supplies; if absent, telemetry
19
+ * is silently dropped (safe — the evaluator itself never fails on
20
+ * telemetry-sink failure).
21
+ */
22
+ import * as crypto from 'node:crypto';
23
+ import * as fs from 'node:fs';
24
+ import * as path from 'node:path';
25
+ import { fileURLToPath } from 'node:url';
26
+ import { Worker } from 'node:worker_threads';
27
+ import { TotemError } from '../errors.js';
28
+ const DEFAULT_CONFIG = {
29
+ timeoutMs: 100,
30
+ softWarningMs: 50,
31
+ };
32
+ function resolveWorkerPath() {
33
+ // In the built bundle, this module compiles to dist/regex-safety/evaluator.js
34
+ // sitting next to dist/regex-safety/worker.js — the relative lookup
35
+ // resolves directly. In vitest/dev the current module is the .ts source
36
+ // file under src/regex-safety/ with no sibling worker.js; fall back to
37
+ // the built dist path so tests can exercise the real worker without
38
+ // requiring a loader shim.
39
+ const here = fileURLToPath(import.meta.url);
40
+ const dir = path.dirname(here);
41
+ const siblingJs = path.join(dir, 'worker.js');
42
+ if (fs.existsSync(siblingJs))
43
+ return siblingJs;
44
+ const distFallback = path.resolve(dir, '..', '..', 'dist', 'regex-safety', 'worker.js');
45
+ if (fs.existsSync(distFallback))
46
+ return distFallback;
47
+ // Last resort: return the expected sibling path. Worker() will throw
48
+ // a descriptive MODULE_NOT_FOUND the caller can surface.
49
+ return siblingJs;
50
+ }
51
+ export class RegexEvaluator {
52
+ worker = null;
53
+ pending = new Map();
54
+ config;
55
+ onTelemetry;
56
+ queue = Promise.resolve();
57
+ disposed = false;
58
+ /**
59
+ * Coalesces concurrent respawn requests (mmnto-ai/totem#1641 Shield review
60
+ * round-1). Without this, a timeout event firing at roughly the same
61
+ * moment as a worker `error` event can call `spawnWorker()` twice,
62
+ * leaking a thread. `evaluate()` also awaits this promise before
63
+ * `postMessage` so a batch scheduled during a respawn waits for the
64
+ * new worker instead of silently dropping against a null handle.
65
+ */
66
+ respawnPromise = null;
67
+ /**
68
+ * Worker-online gate (mmnto-ai/totem#1641, CI round-1 fix). The Node
69
+ * `Worker` constructor returns before the thread is actually running
70
+ * (thread-spawn takes ~30-50ms). If `evaluate()` starts its timeout
71
+ * timer before the worker is online, a slow CI box can trip a
72
+ * spurious timeout on the first batch. Gate postMessage on this
73
+ * promise so cold-start cost never counts against the budget.
74
+ */
75
+ workerReady = Promise.resolve();
76
+ /**
77
+ * Consecutive-respawn counter (Shield review round-1). If the worker
78
+ * keeps dying at spawn time (missing worker.js, syntax error in the
79
+ * worker script, etc.), unbounded respawn becomes a CPU-pegging loop.
80
+ * The counter increments on each respawn, resets on every successful
81
+ * evaluation, and flips `permanentlyFailed` once the budget is spent.
82
+ */
83
+ consecutiveRespawns = 0;
84
+ permanentlyFailed = false;
85
+ static MAX_CONSECUTIVE_RESPAWNS = 3;
86
+ constructor(config = {}, onTelemetry) {
87
+ this.config = { ...DEFAULT_CONFIG, ...config };
88
+ this.onTelemetry = onTelemetry;
89
+ this.spawnWorker();
90
+ }
91
+ async evaluate(input) {
92
+ if (this.disposed) {
93
+ throw new TotemError('CHECK_FAILED', 'RegexEvaluator has been disposed', 'Construct a new RegexEvaluator before calling evaluate(). The dispose() method releases the worker and marks the instance unusable (mmnto-ai/totem#1641).');
94
+ }
95
+ if (this.permanentlyFailed) {
96
+ throw new TotemError('CHECK_FAILED', `RegexEvaluator exhausted ${RegexEvaluator.MAX_CONSECUTIVE_RESPAWNS} consecutive respawn attempts without a successful evaluation`, 'The regex worker script likely failed to initialize (missing worker.js, syntax error in the worker module, or a Node version that cannot load it). Inspect the worker script at packages/core/src/regex-safety/worker.ts and rebuild @mmnto/totem.');
97
+ }
98
+ // Serialize: the next batch waits for the current one to finish.
99
+ // Single-worker invariant — no batch multiplexing.
100
+ const previous = this.queue;
101
+ let release;
102
+ const gate = new Promise((resolve) => {
103
+ release = resolve;
104
+ });
105
+ this.queue = previous.then(() => gate);
106
+ await previous;
107
+ // Wait for any in-flight respawn so postMessage does not race a
108
+ // null `this.worker` handle (Shield review round-1 race fix).
109
+ if (this.respawnPromise) {
110
+ await this.respawnPromise;
111
+ }
112
+ // Wait for the worker thread to finish spawning before posting.
113
+ // Cold-start (thread spawn + module load) is ~30-50ms and must not
114
+ // count against the batch timeout budget, otherwise a slow CI box
115
+ // trips a spurious timeout on the first batch (CI round-1 fix).
116
+ await this.workerReady;
117
+ try {
118
+ return await this.evaluateOnce(input);
119
+ }
120
+ finally {
121
+ release();
122
+ }
123
+ }
124
+ async dispose() {
125
+ this.disposed = true;
126
+ await this.queue;
127
+ if (this.worker) {
128
+ await this.worker.terminate();
129
+ this.worker = null;
130
+ }
131
+ for (const entry of this.pending.values()) {
132
+ clearTimeout(entry.timer);
133
+ }
134
+ this.pending.clear();
135
+ }
136
+ evaluateOnce(input) {
137
+ return new Promise((resolve) => {
138
+ const id = crypto.randomBytes(8).toString('hex');
139
+ const startedAt = Date.now();
140
+ const inputSize = input.lines.reduce((acc, line) => acc + line.length, 0);
141
+ const timer = setTimeout(() => {
142
+ const entry = this.pending.get(id);
143
+ if (!entry)
144
+ return;
145
+ this.pending.delete(id);
146
+ const elapsedMs = Date.now() - startedAt;
147
+ this.emitTelemetry({
148
+ ruleHash: entry.ruleHash,
149
+ redactedPath: entry.redactedPath,
150
+ matchedInputSize: entry.inputSize,
151
+ elapsedTimeMs: elapsedMs,
152
+ timeoutTriggered: true,
153
+ softWarningTriggered: false,
154
+ });
155
+ // Terminate worker — the regex is hung on a line we can't interrupt.
156
+ // Await respawn before resolving so the next queued evaluate() does
157
+ // not race a not-yet-spawned worker and get a second timeout.
158
+ void this.respawnWorker().finally(() => {
159
+ entry.resolve({ kind: 'timeout', elapsedMs });
160
+ });
161
+ }, this.config.timeoutMs);
162
+ this.pending.set(id, {
163
+ resolve,
164
+ timer,
165
+ ruleHash: input.ruleHash,
166
+ startedAt,
167
+ inputSize,
168
+ redactedPath: input.redactedPath ?? '<unknown>',
169
+ });
170
+ const request = {
171
+ id,
172
+ pattern: input.pattern,
173
+ flags: input.flags,
174
+ lines: input.lines,
175
+ };
176
+ this.worker?.postMessage(request);
177
+ });
178
+ }
179
+ spawnWorker() {
180
+ const worker = new Worker(resolveWorkerPath());
181
+ this.worker = worker;
182
+ this.workerReady = new Promise((resolve) => {
183
+ worker.once('online', () => resolve());
184
+ });
185
+ worker.on('message', (response) => this.handleMessage(response));
186
+ worker.on('error', () => {
187
+ // Worker crashed outside of a normal message flow (e.g., an
188
+ // internal error). The queued-evaluate lock only releases when
189
+ // the in-flight promise resolves, so we must await respawn before
190
+ // resolving pending entries — otherwise the next evaluate() races
191
+ // a null `this.worker` and postMessage silently drops (spurious
192
+ // timeout on the next batch). Same invariant the timeout path
193
+ // already relies on (see evaluateOnce timer callback above).
194
+ void this.respawnWorker().finally(() => {
195
+ this.rejectAllPendingAsCrash();
196
+ });
197
+ });
198
+ worker.on('exit', (code) => {
199
+ // GCA PR #1644 round-1 — handle unexpected worker exits (OOM kill,
200
+ // internal Node crash) that do not surface through the `error`
201
+ // event. Skip respawn on graceful exit (code 0) and on explicit
202
+ // dispose (terminate() drives exit with non-zero, but we only
203
+ // spin up the respawn after checking the flag). Skip if this
204
+ // handle is no longer the active worker — `respawnWorker` calls
205
+ // `terminate()` on the prior worker before setting a new one, and
206
+ // the exit event for the old handle fires after the field has
207
+ // moved on; respawning again would leak a thread.
208
+ if (this.disposed)
209
+ return;
210
+ if (code === 0)
211
+ return;
212
+ if (this.worker !== worker)
213
+ return;
214
+ void this.respawnWorker().finally(() => {
215
+ this.rejectAllPendingAsCrash();
216
+ });
217
+ });
218
+ }
219
+ async respawnWorker() {
220
+ // Coalesce concurrent respawn calls (Shield review round-1). If two
221
+ // events (timeout + error) both request a respawn, they share the
222
+ // same in-flight promise instead of spawning two workers.
223
+ if (this.respawnPromise)
224
+ return this.respawnPromise;
225
+ if (this.disposed)
226
+ return;
227
+ this.respawnPromise = (async () => {
228
+ try {
229
+ this.consecutiveRespawns += 1;
230
+ if (this.consecutiveRespawns > RegexEvaluator.MAX_CONSECUTIVE_RESPAWNS) {
231
+ this.permanentlyFailed = true;
232
+ this.rejectAllPendingAsCrash();
233
+ return;
234
+ }
235
+ const old = this.worker;
236
+ this.worker = null;
237
+ if (old) {
238
+ try {
239
+ await old.terminate(); // totem-context: intentional best-effort cleanup — terminate() on an already-dead or still-initializing worker can throw, no recovery path at this layer; the new worker spawn is the load-bearing step.
240
+ }
241
+ catch {
242
+ // No-op (see totem-context on the terminate() call above).
243
+ }
244
+ }
245
+ if (this.disposed)
246
+ return;
247
+ this.spawnWorker();
248
+ }
249
+ finally {
250
+ this.respawnPromise = null;
251
+ }
252
+ })();
253
+ return this.respawnPromise;
254
+ }
255
+ handleMessage(response) {
256
+ const entry = this.pending.get(response.id);
257
+ if (!entry)
258
+ return;
259
+ this.pending.delete(response.id);
260
+ clearTimeout(entry.timer);
261
+ // Any successful round-trip resets the respawn-failure counter.
262
+ // Persistent spawn failures only matter when they fire back-to-back
263
+ // with no intervening successful evaluation (Shield review round-1).
264
+ this.consecutiveRespawns = 0;
265
+ const elapsedMs = Date.now() - entry.startedAt;
266
+ const softWarningTriggered = elapsedMs >= this.config.softWarningMs;
267
+ this.emitTelemetry({
268
+ ruleHash: entry.ruleHash,
269
+ redactedPath: entry.redactedPath,
270
+ matchedInputSize: entry.inputSize,
271
+ elapsedTimeMs: elapsedMs,
272
+ timeoutTriggered: false,
273
+ softWarningTriggered: response.kind === 'ok' ? softWarningTriggered : false,
274
+ });
275
+ if (response.kind === 'ok') {
276
+ entry.resolve({
277
+ kind: 'ok',
278
+ matchedIndices: response.matchedIndices,
279
+ elapsedMs,
280
+ softWarningTriggered,
281
+ });
282
+ }
283
+ else {
284
+ entry.resolve({ kind: 'error', message: response.message, elapsedMs });
285
+ }
286
+ }
287
+ rejectAllPendingAsCrash() {
288
+ for (const [id, entry] of this.pending.entries()) {
289
+ clearTimeout(entry.timer);
290
+ const elapsedMs = Date.now() - entry.startedAt;
291
+ this.emitTelemetry({
292
+ ruleHash: entry.ruleHash,
293
+ redactedPath: entry.redactedPath,
294
+ matchedInputSize: entry.inputSize,
295
+ elapsedTimeMs: elapsedMs,
296
+ timeoutTriggered: true,
297
+ softWarningTriggered: false,
298
+ });
299
+ entry.resolve({ kind: 'timeout', elapsedMs });
300
+ this.pending.delete(id);
301
+ }
302
+ }
303
+ emitTelemetry(record) {
304
+ if (!this.onTelemetry)
305
+ return;
306
+ try {
307
+ this.onTelemetry(record); // totem-context: intentional best-effort telemetry — the evaluator's correctness contract is regex matches, not telemetry delivery; sink failures (bad callback, disk full, permission error) must not interfere with match results.
308
+ }
309
+ catch {
310
+ // No-op (see totem-context on the onTelemetry call above).
311
+ }
312
+ }
313
+ }
314
+ //# sourceMappingURL=evaluator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluator.js","sourceRoot":"","sources":["../../src/regex-safety/evaluator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=evaluator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluator.test.d.ts","sourceRoot":"","sources":["../../src/regex-safety/evaluator.test.ts"],"names":[],"mappings":""}