@wrongstack/plugins 0.295.0 → 0.295.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/dist/accessibility-auditor/index.d.ts.map +1 -1
- package/dist/accessibility-auditor.js +19 -2
- package/dist/agent-handoff/index.d.ts.map +1 -1
- package/dist/agent-handoff.js +30 -1
- package/dist/auto-doc/index.d.ts.map +1 -1
- package/dist/auto-doc.js +3 -1
- package/dist/auto-i18n-extractor.js +5 -0
- package/dist/branch-guard/index.d.ts.map +1 -1
- package/dist/branch-guard.js +103 -1
- package/dist/changelog-writer/index.d.ts.map +1 -1
- package/dist/changelog-writer.js +137 -3
- package/dist/checkpoint/index.d.ts +2 -0
- package/dist/checkpoint/index.d.ts.map +1 -1
- package/dist/checkpoint.js +34 -8
- package/dist/code-metrics.js +5 -0
- package/dist/commit-validator/index.d.ts.map +1 -1
- package/dist/commit-validator.js +17 -7
- package/dist/context-pins/index.d.ts.map +1 -1
- package/dist/context-pins.js +6 -5
- package/dist/cost-tracker/index.d.ts.map +1 -1
- package/dist/cost-tracker.js +101 -1
- package/dist/cron/index.d.ts.map +1 -1
- package/dist/cron.js +1 -0
- package/dist/dead-code-detector.js +5 -0
- package/dist/dependency-vulnerability-gate/index.d.ts.map +1 -1
- package/dist/dependency-vulnerability-gate.js +43 -8
- package/dist/diff-summary/index.d.ts.map +1 -1
- package/dist/diff-summary.js +100 -1
- package/dist/doc-sync-guard.js +5 -0
- package/dist/duplicate-code-detector/index.d.ts.map +1 -1
- package/dist/duplicate-code-detector.js +102 -2
- package/dist/feature-flag-tracker/index.d.ts.map +1 -1
- package/dist/feature-flag-tracker.js +30 -4
- package/dist/file-watcher/index.d.ts.map +1 -1
- package/dist/file-watcher.js +6 -7
- package/dist/format-on-save/index.d.ts +2 -0
- package/dist/format-on-save/index.d.ts.map +1 -1
- package/dist/format-on-save.js +288 -59
- package/dist/import-organizer/index.d.ts.map +1 -1
- package/dist/import-organizer.js +140 -20
- package/dist/index.js +1265 -544
- package/dist/interface-contract-guard/index.d.ts.map +1 -1
- package/dist/interface-contract-guard.js +60 -26
- package/dist/knowledge-graph/index.d.ts.map +1 -1
- package/dist/knowledge-graph.js +7 -6
- package/dist/lint-gate/index.d.ts.map +1 -1
- package/dist/lint-gate.js +103 -1
- package/dist/loop-breaker/index.d.ts.map +1 -1
- package/dist/loop-breaker.js +15 -5
- package/dist/migration-planner.js +3 -0
- package/dist/notify-hub/index.d.ts.map +1 -1
- package/dist/notify-hub.js +41 -4
- package/dist/prompt-firewall/index.d.ts.map +1 -1
- package/dist/prompt-firewall.js +140 -11
- package/dist/refactor-suggester/index.d.ts.map +1 -1
- package/dist/refactor-suggester.js +119 -4
- package/dist/runtime/bounded-map.d.ts +86 -0
- package/dist/runtime/bounded-map.d.ts.map +1 -0
- package/dist/runtime/credential-patterns.d.ts +42 -0
- package/dist/runtime/credential-patterns.d.ts.map +1 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/local-bin.d.ts +120 -0
- package/dist/runtime/local-bin.d.ts.map +1 -0
- package/dist/runtime/safe-json.d.ts +25 -0
- package/dist/runtime/safe-json.d.ts.map +1 -0
- package/dist/runtime.js +291 -22
- package/dist/schema-evolution-guard.js +5 -0
- package/dist/secret-scanner/index.d.ts.map +1 -1
- package/dist/secret-scanner.js +175 -48
- package/dist/security-hotspot-scanner/index.d.ts.map +1 -1
- package/dist/security-hotspot-scanner.js +132 -1
- package/dist/semantic-search-indexer/index.d.ts.map +1 -1
- package/dist/semantic-search-indexer.js +7 -1
- package/dist/semver-bump/index.d.ts.map +1 -1
- package/dist/semver-bump.js +21 -2
- package/dist/template-engine/index.d.ts.map +1 -1
- package/dist/template-engine.js +30 -2
- package/dist/test-coverage-gate.js +5 -0
- package/dist/test-runner-gate/index.d.ts.map +1 -1
- package/dist/test-runner-gate.js +130 -8
- package/dist/todo-listener/index.d.ts.map +1 -1
- package/dist/todo-listener.js +30 -1
- package/dist/todo-tracker/index.d.ts.map +1 -1
- package/dist/todo-tracker.js +3 -4
- package/dist/token-throttle/index.d.ts.map +1 -1
- package/dist/token-throttle.js +17 -2
- package/dist/type-gate/index.d.ts.map +1 -1
- package/dist/type-gate.js +96 -16
- package/package.json +3 -3
|
@@ -4,6 +4,105 @@ import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2 }
|
|
|
4
4
|
|
|
5
5
|
// src/runtime/index.ts
|
|
6
6
|
import { basename, extname, isAbsolute, relative, resolve } from "node:path";
|
|
7
|
+
|
|
8
|
+
// src/runtime/local-bin.ts
|
|
9
|
+
import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
|
|
10
|
+
|
|
11
|
+
// src/runtime/bounded-map.ts
|
|
12
|
+
var BoundedMap = class {
|
|
13
|
+
map = /* @__PURE__ */ new Map();
|
|
14
|
+
max;
|
|
15
|
+
ttlMs;
|
|
16
|
+
now;
|
|
17
|
+
/** Entries dropped to stay under `max`. Surfaced by plugin health(). */
|
|
18
|
+
evictions = 0;
|
|
19
|
+
constructor(options) {
|
|
20
|
+
const normalizedMax = Math.floor(options.max);
|
|
21
|
+
this.max = Number.isSafeInteger(normalizedMax) ? Math.max(1, normalizedMax) : 1;
|
|
22
|
+
this.ttlMs = options.ttlMs;
|
|
23
|
+
this.now = options.now ?? Date.now;
|
|
24
|
+
}
|
|
25
|
+
expired(entry) {
|
|
26
|
+
return this.ttlMs !== void 0 && this.now() - entry.storedAt > this.ttlMs;
|
|
27
|
+
}
|
|
28
|
+
get(key) {
|
|
29
|
+
const entry = this.map.get(key);
|
|
30
|
+
if (entry === void 0) return void 0;
|
|
31
|
+
if (this.expired(entry)) {
|
|
32
|
+
this.map.delete(key);
|
|
33
|
+
return void 0;
|
|
34
|
+
}
|
|
35
|
+
this.map.delete(key);
|
|
36
|
+
this.map.set(key, entry);
|
|
37
|
+
return entry.value;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Read without promoting the key to most-recently-used. Use for
|
|
41
|
+
* diagnostics that must not perturb the eviction order.
|
|
42
|
+
*/
|
|
43
|
+
peek(key) {
|
|
44
|
+
const entry = this.map.get(key);
|
|
45
|
+
if (entry === void 0 || this.expired(entry)) return void 0;
|
|
46
|
+
return entry.value;
|
|
47
|
+
}
|
|
48
|
+
has(key) {
|
|
49
|
+
const entry = this.map.get(key);
|
|
50
|
+
if (entry === void 0) return false;
|
|
51
|
+
if (this.expired(entry)) {
|
|
52
|
+
this.map.delete(key);
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
set(key, value) {
|
|
58
|
+
this.map.delete(key);
|
|
59
|
+
this.map.set(key, { value, storedAt: this.now() });
|
|
60
|
+
while (this.map.size > this.max) {
|
|
61
|
+
const coldest = this.map.keys().next().value;
|
|
62
|
+
if (coldest === void 0) break;
|
|
63
|
+
this.map.delete(coldest);
|
|
64
|
+
this.evictions += 1;
|
|
65
|
+
}
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
delete(key) {
|
|
69
|
+
return this.map.delete(key);
|
|
70
|
+
}
|
|
71
|
+
clear() {
|
|
72
|
+
this.map.clear();
|
|
73
|
+
this.evictions = 0;
|
|
74
|
+
}
|
|
75
|
+
get size() {
|
|
76
|
+
return this.map.size;
|
|
77
|
+
}
|
|
78
|
+
/** How many entries have been dropped to respect `max`, since the last clear. */
|
|
79
|
+
get evictionCount() {
|
|
80
|
+
return this.evictions;
|
|
81
|
+
}
|
|
82
|
+
/** Drop every expired entry. Cheap enough to call from a status tool. */
|
|
83
|
+
prune() {
|
|
84
|
+
if (this.ttlMs === void 0) return 0;
|
|
85
|
+
let removed = 0;
|
|
86
|
+
for (const [key, entry] of this.map) {
|
|
87
|
+
if (this.expired(entry)) {
|
|
88
|
+
this.map.delete(key);
|
|
89
|
+
removed += 1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return removed;
|
|
93
|
+
}
|
|
94
|
+
/** Live (non-expired) entries, coldest first. */
|
|
95
|
+
*entries() {
|
|
96
|
+
for (const [key, entry] of this.map) {
|
|
97
|
+
if (!this.expired(entry)) yield [key, entry.value];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
[Symbol.iterator]() {
|
|
101
|
+
return this.entries();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// src/runtime/index.ts
|
|
7
106
|
var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
|
|
8
107
|
function hasLeadingDash(arg) {
|
|
9
108
|
return arg.length > 0 && arg.startsWith("-");
|
|
@@ -63,6 +162,7 @@ function matchesExtension(p, exts) {
|
|
|
63
162
|
|
|
64
163
|
// src/refactor-suggester/index.ts
|
|
65
164
|
var API_VERSION = "^0.1.10";
|
|
165
|
+
var HOOK_WARNING_COOLDOWN_MS = 6e4;
|
|
66
166
|
var state = {
|
|
67
167
|
scanCount: 0,
|
|
68
168
|
suggestionCount: 0,
|
|
@@ -70,7 +170,7 @@ var state = {
|
|
|
70
170
|
warningCount: 0,
|
|
71
171
|
errorCount: 0,
|
|
72
172
|
hookUnregister: null,
|
|
73
|
-
lastHookWarning:
|
|
173
|
+
lastHookWarning: new BoundedMap({ max: 512, ttlMs: HOOK_WARNING_COOLDOWN_MS })
|
|
74
174
|
};
|
|
75
175
|
var DEFAULTS = {
|
|
76
176
|
enabled: false,
|
|
@@ -195,15 +295,26 @@ async function scanPath(rawPath, cfg) {
|
|
|
195
295
|
const exts = normalizeExtensions(cfg.extensions);
|
|
196
296
|
const files = await collectSourceFilesAsync(resolved, { extensions: exts });
|
|
197
297
|
const suggestions = [];
|
|
298
|
+
let scannedFiles = 0;
|
|
299
|
+
let truncated = false;
|
|
198
300
|
for (const p of files) {
|
|
199
301
|
try {
|
|
200
302
|
const content = await readFile(p, "utf-8");
|
|
303
|
+
scannedFiles += 1;
|
|
201
304
|
suggestions.push(...detectSmells(p, content, cfg.rules));
|
|
202
|
-
if (suggestions.length >= cfg.maxSuggestions)
|
|
305
|
+
if (suggestions.length >= cfg.maxSuggestions) {
|
|
306
|
+
truncated = scannedFiles < files.length;
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
203
309
|
} catch {
|
|
204
310
|
}
|
|
205
311
|
}
|
|
206
|
-
return {
|
|
312
|
+
return {
|
|
313
|
+
suggestions: suggestions.slice(0, cfg.maxSuggestions),
|
|
314
|
+
scannedFiles,
|
|
315
|
+
discoveredFiles: files.length,
|
|
316
|
+
truncated
|
|
317
|
+
};
|
|
207
318
|
}
|
|
208
319
|
var plugin = {
|
|
209
320
|
name: "refactor-suggester",
|
|
@@ -266,7 +377,7 @@ var plugin = {
|
|
|
266
377
|
state.hookInvocationCount += 1;
|
|
267
378
|
const now = Date.now();
|
|
268
379
|
const lastWarning = state.lastHookWarning.get(sourcePath);
|
|
269
|
-
if (lastWarning !== void 0 && now - lastWarning <
|
|
380
|
+
if (lastWarning !== void 0 && now - lastWarning < HOOK_WARNING_COOLDOWN_MS) return;
|
|
270
381
|
const resolved = resolve2(process.cwd(), sourcePath);
|
|
271
382
|
let content;
|
|
272
383
|
try {
|
|
@@ -316,6 +427,10 @@ var plugin = {
|
|
|
316
427
|
ok: true,
|
|
317
428
|
path: relativePath(resolve2(process.cwd(), rawPath)),
|
|
318
429
|
scannedFiles: result.scannedFiles,
|
|
430
|
+
discoveredFiles: result.discoveredFiles,
|
|
431
|
+
// Say so when the cap stopped the walk early: a partial scan
|
|
432
|
+
// that reports few findings must not read as a clean result.
|
|
433
|
+
truncated: result.truncated,
|
|
319
434
|
suggestions: result.suggestions,
|
|
320
435
|
rules: cfg.rules
|
|
321
436
|
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wrongstack/plugins — bounded key/value cache.
|
|
3
|
+
*
|
|
4
|
+
* Several plugins memoise per-path or per-model work in a module-scope
|
|
5
|
+
* `Map` that is only ever cleared at teardown. In a short test that is
|
|
6
|
+
* invisible; in a long session over a large repository it is a slow leak —
|
|
7
|
+
* one entry per file touched, per branch resolved, per model seen — held
|
|
8
|
+
* for the lifetime of the process.
|
|
9
|
+
*
|
|
10
|
+
* `BoundedMap` is a drop-in replacement for those maps: same `get`/`set`/
|
|
11
|
+
* `delete`/`clear`/`size` surface, plus a hard entry cap with
|
|
12
|
+
* least-recently-used eviction and an optional per-entry TTL. Nothing
|
|
13
|
+
* about the call sites has to change beyond the constructor.
|
|
14
|
+
*
|
|
15
|
+
* Why LRU rather than insertion-order eviction: the access pattern for
|
|
16
|
+
* these caches is "a few hot paths, a long tail of cold ones". Evicting by
|
|
17
|
+
* insertion order throws away the hot entries first, which is exactly
|
|
18
|
+
* backwards; re-reading a hot entry should keep it.
|
|
19
|
+
*/
|
|
20
|
+
export interface BoundedMapOptions {
|
|
21
|
+
/** Hard cap on retained entries. Must be a safe integer >= 1. */
|
|
22
|
+
max: number;
|
|
23
|
+
/**
|
|
24
|
+
* Optional per-entry lifetime in ms. An entry older than this is treated
|
|
25
|
+
* as absent by `get`/`has` and dropped on access. Omit for no expiry.
|
|
26
|
+
*/
|
|
27
|
+
ttlMs?: number | undefined;
|
|
28
|
+
/** Clock injection point — tests override this instead of faking timers. */
|
|
29
|
+
now?: (() => number) | undefined;
|
|
30
|
+
}
|
|
31
|
+
export declare class BoundedMap<K, V> {
|
|
32
|
+
private readonly map;
|
|
33
|
+
private readonly max;
|
|
34
|
+
private readonly ttlMs;
|
|
35
|
+
private readonly now;
|
|
36
|
+
/** Entries dropped to stay under `max`. Surfaced by plugin health(). */
|
|
37
|
+
private evictions;
|
|
38
|
+
constructor(options: BoundedMapOptions);
|
|
39
|
+
private expired;
|
|
40
|
+
get(key: K): V | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Read without promoting the key to most-recently-used. Use for
|
|
43
|
+
* diagnostics that must not perturb the eviction order.
|
|
44
|
+
*/
|
|
45
|
+
peek(key: K): V | undefined;
|
|
46
|
+
has(key: K): boolean;
|
|
47
|
+
set(key: K, value: V): this;
|
|
48
|
+
delete(key: K): boolean;
|
|
49
|
+
clear(): void;
|
|
50
|
+
get size(): number;
|
|
51
|
+
/** How many entries have been dropped to respect `max`, since the last clear. */
|
|
52
|
+
get evictionCount(): number;
|
|
53
|
+
/** Drop every expired entry. Cheap enough to call from a status tool. */
|
|
54
|
+
prune(): number;
|
|
55
|
+
/** Live (non-expired) entries, coldest first. */
|
|
56
|
+
entries(): IterableIterator<[K, V]>;
|
|
57
|
+
[Symbol.iterator](): IterableIterator<[K, V]>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A `Set` with the same hard cap and LRU eviction as {@link BoundedMap}.
|
|
61
|
+
*
|
|
62
|
+
* Plugins use module-scope sets to remember "already reported" keys so a
|
|
63
|
+
* finding is surfaced once rather than on every scan. That set has to
|
|
64
|
+
* outlive a single scan, which is why it lives at module scope — but
|
|
65
|
+
* without a bound it grows one entry per distinct finding for the life of
|
|
66
|
+
* the process.
|
|
67
|
+
*
|
|
68
|
+
* Eviction means a very old key may eventually be reported a second time.
|
|
69
|
+
* That is the right trade: re-reporting a finding the user last saw
|
|
70
|
+
* thousands of findings ago is reasonable behaviour, whereas unbounded
|
|
71
|
+
* growth is not.
|
|
72
|
+
*/
|
|
73
|
+
export declare class BoundedSet<T> {
|
|
74
|
+
private readonly inner;
|
|
75
|
+
constructor(options: BoundedMapOptions);
|
|
76
|
+
has(value: T): boolean;
|
|
77
|
+
add(value: T): this;
|
|
78
|
+
delete(value: T): boolean;
|
|
79
|
+
clear(): void;
|
|
80
|
+
get size(): number;
|
|
81
|
+
/** How many entries have been dropped to respect `max`, since the last clear. */
|
|
82
|
+
get evictionCount(): number;
|
|
83
|
+
values(): IterableIterator<T>;
|
|
84
|
+
[Symbol.iterator](): IterableIterator<T>;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=bounded-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bounded-map.d.ts","sourceRoot":"","sources":["../../src/runtime/bounded-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,iBAAiB;IAChC,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,4EAA4E;IAC5E,GAAG,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,SAAS,CAAC;CAClC;AAOD,qBAAa,UAAU,CAAC,CAAC,EAAE,CAAC;IAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA0B;IAC9C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,wEAAwE;IACxE,OAAO,CAAC,SAAS,CAAK;IAEtB,YAAY,OAAO,EAAE,iBAAiB,EAKrC;IAED,OAAO,CAAC,OAAO;IAIf,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAazB;IAED;;;OAGG;IACH,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAI1B;IAED,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAQnB;IAED,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAW1B;IAED,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAEtB;IAED,KAAK,IAAI,IAAI,CAGZ;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,iFAAiF;IACjF,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,yEAAyE;IACzE,KAAK,IAAI,MAAM,CAUd;IAED,iDAAiD;IAChD,OAAO,IAAI,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAInC;IAED,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAE5C;CACF;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,UAAU,CAAC,CAAC;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAE5C,YAAY,OAAO,EAAE,iBAAiB,EAErC;IAED,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAErB;IAED,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAGlB;IAED,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAExB;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,iFAAiF;IACjF,IAAI,aAAa,IAAI,MAAM,CAE1B;IAEA,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAE7B;IAED,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAEvC;CACF"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wrongstack/plugins — canonical credential-pattern table.
|
|
3
|
+
*
|
|
4
|
+
* Two plugins detect credentials, at different points in the pipeline:
|
|
5
|
+
*
|
|
6
|
+
* - `secret-scanner` gates tool *input* and inspects tool *output*.
|
|
7
|
+
* - `prompt-firewall` inspects the outgoing *provider request* (and the
|
|
8
|
+
* response) so a credential sitting in context is not shipped to a
|
|
9
|
+
* third party.
|
|
10
|
+
*
|
|
11
|
+
* They previously each carried their own pattern list. The lists drifted:
|
|
12
|
+
* one grew GitLab, npm, SendGrid and DigitalOcean coverage while the other
|
|
13
|
+
* did not, so which credentials were caught depended on which side of the
|
|
14
|
+
* pipeline they crossed. A credential is a credential — the two surfaces
|
|
15
|
+
* should never disagree about what one looks like.
|
|
16
|
+
*
|
|
17
|
+
* This module is the single source of truth. Adding a pattern here
|
|
18
|
+
* strengthens both surfaces at once.
|
|
19
|
+
*
|
|
20
|
+
* Style notes for new entries:
|
|
21
|
+
* - Prefer explicit lookaround boundaries — `(?<![A-Za-z0-9])` /
|
|
22
|
+
* `(?![A-Za-z0-9])` — over ``. `` is defined against word
|
|
23
|
+
* characters, so it silently fails next to the `+`, `/` and `=` that
|
|
24
|
+
* base64-shaped secrets routinely end with.
|
|
25
|
+
* - Keep every group non-capturing (`(?:…)`). The combined regex maps a
|
|
26
|
+
* capture-group index back to the pattern that fired; an inner group
|
|
27
|
+
* shifts that mapping. `secret-scanner` compensates for user-supplied
|
|
28
|
+
* patterns, but built-ins should not need it.
|
|
29
|
+
* - Avoid unbounded `.*` lookarounds: these run on every provider
|
|
30
|
+
* request, against the full context.
|
|
31
|
+
*/
|
|
32
|
+
/** One credential shape, with a stable machine-readable id. */
|
|
33
|
+
export interface CredentialPattern {
|
|
34
|
+
/** Stable id, e.g. `github_pat`. Reported to users and in metrics. */
|
|
35
|
+
type: string;
|
|
36
|
+
/** Global-flagged matcher. */
|
|
37
|
+
regex: RegExp;
|
|
38
|
+
}
|
|
39
|
+
export declare const CREDENTIAL_PATTERNS: readonly CredentialPattern[];
|
|
40
|
+
/** Fresh, independently-stateful copies (RegExp `lastIndex` is mutable). */
|
|
41
|
+
export declare function cloneCredentialPatterns(): CredentialPattern[];
|
|
42
|
+
//# sourceMappingURL=credential-patterns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-patterns.d.ts","sourceRoot":"","sources":["../../src/runtime/credential-patterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,+DAA+D;AAC/D,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,mBAAmB,EAAE,SAAS,iBAAiB,EAwG3D,CAAC;AAEF,4EAA4E;AAC5E,wBAAgB,uBAAuB,IAAI,iBAAiB,EAAE,CAK7D"}
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
* output parsing) stays in the plugin that owns that language.
|
|
30
30
|
*/
|
|
31
31
|
export { parseLlmJsonObject, runOptionalPluginLlm, stripOuterMarkdownFence, type OptionalLlmRequest, type OptionalLlmResult, } from './llm.js';
|
|
32
|
+
export { BoundedMap, BoundedSet, type BoundedMapOptions } from './bounded-map.js';
|
|
33
|
+
export { UNSERIALIZABLE, safeJsonStringify } from './safe-json.js';
|
|
34
|
+
export { clearLocalBinCache, findOnPath, resolveExecInvocation, resolveFirstNodeBin, resolveNodeBin, resolveWin32Command, type ExecInvocation, type ResolvedNodeBin, } from './local-bin.js';
|
|
32
35
|
export type LanguageId = 'typescript' | 'javascript' | 'python' | 'go' | 'rust' | 'shell' | 'ruby' | 'java' | 'kotlin' | 'dotnet' | 'generic';
|
|
33
36
|
export type PackageManagerId = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'pip' | 'poetry' | 'go' | 'cargo' | 'gem' | 'maven' | 'gradle' | 'dotnet' | 'none';
|
|
34
37
|
export interface LanguageRuntime {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAMH,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wrongstack/plugins — project-local binary resolution.
|
|
3
|
+
*
|
|
4
|
+
* Why this module exists
|
|
5
|
+
* ----------------------
|
|
6
|
+
* Several plugins need to run a Node CLI that ships with the project
|
|
7
|
+
* (`biome`, `eslint`, `tsc`, `vitest`, …). The obvious spelling —
|
|
8
|
+
* `execFile('npx', ['biome', …])` — is wrong in three separate ways:
|
|
9
|
+
*
|
|
10
|
+
* 1. **It does not run on Windows at all.** `npx`/`pnpm`/`npm` are
|
|
11
|
+
* `.cmd` shims there, and `execFile`/`spawn` without a shell do not
|
|
12
|
+
* consult `PATHEXT`. The call fails `ENOENT`, plugins swallow the
|
|
13
|
+
* error as "tool not installed", and the feature silently no-ops on
|
|
14
|
+
* every Windows machine.
|
|
15
|
+
* 2. **It is slow.** `npx` re-resolves the package on each invocation,
|
|
16
|
+
* and for a missing package it may try to *download* one — on a hook
|
|
17
|
+
* that fires after every write.
|
|
18
|
+
* 3. **It is a weaker sandbox.** The shim is a shell script; arguments
|
|
19
|
+
* cross a `cmd.exe` boundary on Windows (the BatBadBut class).
|
|
20
|
+
*
|
|
21
|
+
* `lint-gate` and `test-flake-detector` already solved this locally by
|
|
22
|
+
* resolving the package's `bin` entry through `createRequire` and running
|
|
23
|
+
* it as `process.execPath <entry>`. That is the correct pattern: no shell,
|
|
24
|
+
* no shim, no network, identical on every platform. This module promotes
|
|
25
|
+
* that proven approach to a shared helper so every plugin gets it.
|
|
26
|
+
*
|
|
27
|
+
* Fallback: when the package is genuinely absent from the project,
|
|
28
|
+
* `resolveNodeBin` returns `null` and the caller decides whether to
|
|
29
|
+
* degrade gracefully or fall back to a PATH lookup via
|
|
30
|
+
* `resolveWin32Command` (exported here for that purpose).
|
|
31
|
+
*/
|
|
32
|
+
import { resolveWin32Command } from '@wrongstack/tools/win32';
|
|
33
|
+
export { resolveWin32Command };
|
|
34
|
+
/** A spawn-ready invocation, already adjusted for the host platform. */
|
|
35
|
+
export interface ExecInvocation {
|
|
36
|
+
cmd: string;
|
|
37
|
+
args: string[];
|
|
38
|
+
/** Only ever true on the Windows `.cmd`/`.bat` shim path. */
|
|
39
|
+
windowsVerbatimArguments: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Turn a `(command, args)` pair into something `execFile`/`spawn` can
|
|
43
|
+
* actually launch on this platform.
|
|
44
|
+
*
|
|
45
|
+
* On Windows, `npx`/`npm`/`pnpm`/`biome`/`tsc`/… ship as `.cmd` wrappers.
|
|
46
|
+
* `execFile` and `spawn` without a shell ignore `PATHEXT`, so a bare
|
|
47
|
+
* `execFile('npx', …)` fails `ENOENT` — and because every plugin treats a
|
|
48
|
+
* spawn failure as "tool not installed", the feature silently no-ops on
|
|
49
|
+
* every Windows machine. This resolves the real path first and, for a
|
|
50
|
+
* `.cmd`/`.bat` shim, routes through `cmd.exe` with per-argument quoting
|
|
51
|
+
* and a metacharacter guard (the BatBadBut argument-injection class), so
|
|
52
|
+
* a dynamic path argument still cannot chain a second command.
|
|
53
|
+
*
|
|
54
|
+
* On non-Windows, and for real `.exe` binaries, this is a passthrough.
|
|
55
|
+
*
|
|
56
|
+
* Throws when an argument carries a `cmd.exe` metacharacter on the shim
|
|
57
|
+
* path — callers should treat that as "skip", never as "run anyway".
|
|
58
|
+
*
|
|
59
|
+
* Prefer {@link resolveNodeBin} when the target is a Node CLI that the
|
|
60
|
+
* project depends on: `node <bin-entry>` needs no shim at all.
|
|
61
|
+
*/
|
|
62
|
+
export declare function resolveExecInvocation(command: string, args?: readonly string[]): ExecInvocation;
|
|
63
|
+
/**
|
|
64
|
+
* Locate `cmd` on `PATH`, or return `null` if it is not there.
|
|
65
|
+
*
|
|
66
|
+
* This is the existence check `resolveWin32Command` deliberately does not
|
|
67
|
+
* provide: that function returns its input unchanged when nothing matches,
|
|
68
|
+
* so a caller cannot distinguish "found `biome`" from "gave up and handed
|
|
69
|
+
* back the string `biome`". Treating the passthrough as success makes a
|
|
70
|
+
* missing tool look installed — the caller then spawns it, gets `ENOENT`,
|
|
71
|
+
* and (because plugins read a spawn failure as "not installed") silently
|
|
72
|
+
* does nothing while reporting itself healthy.
|
|
73
|
+
*
|
|
74
|
+
* Returns the resolved path on success. Walks `PATH` directly, applying
|
|
75
|
+
* `PATHEXT` suffixes on Windows for a bare name and checking the execute
|
|
76
|
+
* bit on POSIX.
|
|
77
|
+
*/
|
|
78
|
+
export declare function findOnPath(cmd: string): string | null;
|
|
79
|
+
/** A resolved, directly-spawnable invocation. Never a shell or a shim. */
|
|
80
|
+
export interface ResolvedNodeBin {
|
|
81
|
+
/** Always `process.execPath` — the Node binary running this process. */
|
|
82
|
+
cmd: string;
|
|
83
|
+
/** `[binEntryPath, ...extraArgs]`. */
|
|
84
|
+
args: string[];
|
|
85
|
+
/** Absolute path of the package's bin entry, for diagnostics. */
|
|
86
|
+
entry: string;
|
|
87
|
+
}
|
|
88
|
+
/** Drop every cached resolution. Call from `teardown()`. */
|
|
89
|
+
export declare function clearLocalBinCache(): void;
|
|
90
|
+
/**
|
|
91
|
+
* Resolve a project-local Node CLI to a `process.execPath <entry>`
|
|
92
|
+
* invocation.
|
|
93
|
+
*
|
|
94
|
+
* @param packageName npm package to resolve, e.g. `@biomejs/biome`.
|
|
95
|
+
* @param binName which `bin` key to prefer when the package declares
|
|
96
|
+
* several. Falls back to the sole/first entry.
|
|
97
|
+
* @param cwd project root whose `package.json` anchors resolution.
|
|
98
|
+
* @param extraArgs arguments appended after the bin entry.
|
|
99
|
+
*
|
|
100
|
+
* Returns `null` when the package is not installed, declares no `bin`, or
|
|
101
|
+
* the declared entry escapes its own package directory (a tampered
|
|
102
|
+
* `package.json` must not become an arbitrary-file execution primitive).
|
|
103
|
+
*/
|
|
104
|
+
export declare function resolveNodeBin(packageName: string, binName: string, cwd: string, extraArgs?: readonly string[]): ResolvedNodeBin | null;
|
|
105
|
+
/**
|
|
106
|
+
* Resolve the first installed package from `candidates`.
|
|
107
|
+
*
|
|
108
|
+
* Used by plugins that accept several equivalent tools (biome *or*
|
|
109
|
+
* eslint, vitest *or* jest) and want the first one the project actually
|
|
110
|
+
* has, without probing each with a subprocess.
|
|
111
|
+
*/
|
|
112
|
+
export declare function resolveFirstNodeBin(candidates: readonly {
|
|
113
|
+
packageName: string;
|
|
114
|
+
binName: string;
|
|
115
|
+
args?: readonly string[];
|
|
116
|
+
}[], cwd: string): (ResolvedNodeBin & {
|
|
117
|
+
packageName: string;
|
|
118
|
+
binName: string;
|
|
119
|
+
}) | null;
|
|
120
|
+
//# sourceMappingURL=local-bin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-bin.d.ts","sourceRoot":"","sources":["../../src/runtime/local-bin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAKH,OAAO,EAA+B,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE3F,OAAO,EAAE,mBAAmB,EAAE,CAAC;AAE/B,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,6DAA6D;IAC7D,wBAAwB,EAAE,OAAO,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,SAAS,MAAM,EAAO,GAC3B,cAAc,CAWhB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoCrD;AAED,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,GAAG,EAAE,MAAM,CAAC;IACZ,sCAAsC;IACtC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;CACf;AAqCD,4DAA4D;AAC5D,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,SAAS,GAAE,SAAS,MAAM,EAAO,GAChC,eAAe,GAAG,IAAI,CAwCxB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,SAAS;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EAAE,EACzF,GAAG,EAAE,MAAM,GACV,CAAC,eAAe,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,CAMrE"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wrongstack/plugins — serialisation that cannot take the process down.
|
|
3
|
+
*
|
|
4
|
+
* Plugins routinely stringify values they did not construct: event-bus
|
|
5
|
+
* payloads, tool results, mailbox bodies. `JSON.stringify` throws on a
|
|
6
|
+
* circular reference and on `BigInt`, and several of these call sites sit
|
|
7
|
+
* inside detached (fire-and-forget) work where a throw becomes an
|
|
8
|
+
* unhandled rejection rather than a handled failure.
|
|
9
|
+
*
|
|
10
|
+
* `safeJsonStringify` never throws. A value it cannot represent is
|
|
11
|
+
* replaced with a marker, so the surrounding feature degrades to "this
|
|
12
|
+
* field is unreadable" instead of failing — or crashing — outright.
|
|
13
|
+
*/
|
|
14
|
+
/** Placeholder substituted for a value that cannot be serialised. */
|
|
15
|
+
export declare const UNSERIALIZABLE = "[unserializable]";
|
|
16
|
+
/**
|
|
17
|
+
* `JSON.stringify` with cycle handling and a total-failure fallback.
|
|
18
|
+
*
|
|
19
|
+
* @param value anything
|
|
20
|
+
* @param indent passed through to `JSON.stringify` (e.g. `2` to pretty-print)
|
|
21
|
+
* @returns a JSON string, or a marker string when the value resists
|
|
22
|
+
* serialisation entirely. Never throws.
|
|
23
|
+
*/
|
|
24
|
+
export declare function safeJsonStringify(value: unknown, indent?: number): string;
|
|
25
|
+
//# sourceMappingURL=safe-json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-json.d.ts","sourceRoot":"","sources":["../../src/runtime/safe-json.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,qEAAqE;AACrE,eAAO,MAAM,cAAc,qBAAqB,CAAC;AAEjD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAyBzE"}
|