@kernlang/review 3.3.4 → 3.3.5

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 (35) hide show
  1. package/dist/cache.js +1 -1
  2. package/dist/concept-rules/contract-drift.d.ts +21 -0
  3. package/dist/concept-rules/contract-drift.js +66 -0
  4. package/dist/concept-rules/contract-drift.js.map +1 -0
  5. package/dist/concept-rules/cross-stack-utils.d.ts +50 -0
  6. package/dist/concept-rules/cross-stack-utils.js +98 -0
  7. package/dist/concept-rules/cross-stack-utils.js.map +1 -0
  8. package/dist/concept-rules/index.js +12 -1
  9. package/dist/concept-rules/index.js.map +1 -1
  10. package/dist/concept-rules/tainted-across-wire.d.ts +33 -0
  11. package/dist/concept-rules/tainted-across-wire.js +98 -0
  12. package/dist/concept-rules/tainted-across-wire.js.map +1 -0
  13. package/dist/concept-rules/untyped-api-response.d.ts +30 -0
  14. package/dist/concept-rules/untyped-api-response.js +71 -0
  15. package/dist/concept-rules/untyped-api-response.js.map +1 -0
  16. package/dist/external-tools.d.ts +17 -4
  17. package/dist/external-tools.js +12 -1
  18. package/dist/external-tools.js.map +1 -1
  19. package/dist/index.d.ts +2 -1
  20. package/dist/index.js +75 -6
  21. package/dist/index.js.map +1 -1
  22. package/dist/llm-bridge.d.ts +26 -1
  23. package/dist/llm-bridge.js +42 -6
  24. package/dist/llm-bridge.js.map +1 -1
  25. package/dist/llm-review.js +29 -11
  26. package/dist/llm-review.js.map +1 -1
  27. package/dist/mappers/ts-concepts.js +247 -1
  28. package/dist/mappers/ts-concepts.js.map +1 -1
  29. package/dist/rules/index.js +1 -1
  30. package/dist/rules/index.js.map +1 -1
  31. package/dist/rules/kern-source.js +35 -5
  32. package/dist/rules/kern-source.js.map +1 -1
  33. package/dist/types.d.ts +2 -0
  34. package/dist/types.js.map +1 -1
  35. package/package.json +2 -2
package/dist/cache.js CHANGED
@@ -3,7 +3,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync
3
3
  import { homedir } from 'os';
4
4
  import { dirname, join, resolve } from 'path';
5
5
  // Version stamp for cache invalidation — changes when rules/analyzers change
6
- const REVIEW_CACHE_VERSION = '3.2.3-review-cache-2';
6
+ const REVIEW_CACHE_VERSION = '3.2.3-review-cache-3';
7
7
  const IMPORT_SPECIFIER_RE = /(?:import|export)\s+(?:[^'"`]*?\s+from\s+)?['"]([^'"]+)['"]|import\(\s*['"]([^'"]+)['"]\s*\)/g;
8
8
  const EXTENSION_FALLBACK = {
9
9
  '.js': ['.ts', '.tsx', '.mts', '.cts'],
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Rule: contract-drift
3
+ *
4
+ * Cross-stack rule — fires when a frontend (TS) network call targets an API
5
+ * path that has no matching server-side route in the reviewed project.
6
+ *
7
+ * This is the moat rule for TS ↔ Python projects: Pydantic schemas drift,
8
+ * endpoints get renamed, frontend hits `/api/users/:id` but the FastAPI
9
+ * handler was moved to `/api/v2/users/:id`. ESLint and Bandit can't see
10
+ * this because they each only see one side of the wire.
11
+ *
12
+ * v1 scope: URL-path drift only (is there a server for this client?). Body
13
+ * shape / Pydantic field correlation is a follow-up once the mappers emit
14
+ * body concepts — see the TODO comments in ts-concepts.ts / review-python.
15
+ *
16
+ * Requires graph mode: `ctx.allConcepts` must be populated. Single-file
17
+ * review silently returns no findings (can't correlate from one file).
18
+ */
19
+ import type { ReviewFinding } from '../types.js';
20
+ import type { ConceptRuleContext } from './index.js';
21
+ export declare function contractDrift(ctx: ConceptRuleContext): ReviewFinding[];
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Rule: contract-drift
3
+ *
4
+ * Cross-stack rule — fires when a frontend (TS) network call targets an API
5
+ * path that has no matching server-side route in the reviewed project.
6
+ *
7
+ * This is the moat rule for TS ↔ Python projects: Pydantic schemas drift,
8
+ * endpoints get renamed, frontend hits `/api/users/:id` but the FastAPI
9
+ * handler was moved to `/api/v2/users/:id`. ESLint and Bandit can't see
10
+ * this because they each only see one side of the wire.
11
+ *
12
+ * v1 scope: URL-path drift only (is there a server for this client?). Body
13
+ * shape / Pydantic field correlation is a follow-up once the mappers emit
14
+ * body concepts — see the TODO comments in ts-concepts.ts / review-python.
15
+ *
16
+ * Requires graph mode: `ctx.allConcepts` must be populated. Single-file
17
+ * review silently returns no findings (can't correlate from one file).
18
+ */
19
+ import { createFingerprint } from '../types.js';
20
+ import { API_PATH_RE, CROSS_STACK_HEURISTIC_CONFIDENCE, collectRoutes, hasMatchingRoute, normalizeClientUrl, } from './cross-stack-utils.js';
21
+ export function contractDrift(ctx) {
22
+ // Graph mode only — URL correlation is useless within a single file.
23
+ if (!ctx.allConcepts || ctx.allConcepts.size === 0)
24
+ return [];
25
+ const serverRoutes = [];
26
+ const clientCalls = [];
27
+ for (const [, conceptMap] of ctx.allConcepts) {
28
+ collectRoutes(conceptMap, serverRoutes);
29
+ for (const node of conceptMap.nodes) {
30
+ if (node.kind !== 'effect' || node.payload.kind !== 'effect' || node.payload.subtype !== 'network')
31
+ continue;
32
+ const target = node.payload.target;
33
+ if (typeof target !== 'string')
34
+ continue;
35
+ const normalized = normalizeClientUrl(target);
36
+ if (!normalized || !API_PATH_RE.test(normalized))
37
+ continue;
38
+ clientCalls.push({ target, normalizedPath: normalized, node });
39
+ }
40
+ }
41
+ // Rule gate: need at least one route AND one client call, otherwise the
42
+ // project isn't a full-stack app and we'd fire on every external API hit.
43
+ if (serverRoutes.length === 0 || clientCalls.length === 0)
44
+ return [];
45
+ const findings = [];
46
+ for (const call of clientCalls) {
47
+ // Only report on calls that happen in files from the reviewed project —
48
+ // avoids firing on third-party SDK targets.
49
+ if (call.node.primarySpan.file !== ctx.filePath)
50
+ continue;
51
+ if (hasMatchingRoute(call.normalizedPath, serverRoutes))
52
+ continue;
53
+ findings.push({
54
+ source: 'kern',
55
+ ruleId: 'contract-drift',
56
+ severity: 'warning',
57
+ category: 'bug',
58
+ message: `Frontend calls \`${call.target}\` but no server-side route matches this path in the reviewed project. Either the endpoint was renamed/removed on the backend or the frontend is targeting the wrong URL.`,
59
+ primarySpan: call.node.primarySpan,
60
+ fingerprint: createFingerprint('contract-drift', call.node.primarySpan.startLine, call.node.primarySpan.startCol),
61
+ confidence: call.node.confidence * CROSS_STACK_HEURISTIC_CONFIDENCE,
62
+ });
63
+ }
64
+ return findings;
65
+ }
66
+ //# sourceMappingURL=contract-drift.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract-drift.js","sourceRoot":"","sources":["../../src/concept-rules/contract-drift.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,WAAW,EACX,gCAAgC,EAChC,aAAa,EACb,gBAAgB,EAChB,kBAAkB,GAEnB,MAAM,wBAAwB,CAAC;AAShC,MAAM,UAAU,aAAa,CAAC,GAAuB;IACnD,qEAAqE;IACrE,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAE9D,MAAM,YAAY,GAAkB,EAAE,CAAC;IACvC,MAAM,WAAW,GAAiB,EAAE,CAAC;IAErC,KAAK,MAAM,CAAC,EAAE,UAAU,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QAC7C,aAAa,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QACxC,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;YACpC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS;gBAAE,SAAS;YAC7G,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;YACnC,IAAI,OAAO,MAAM,KAAK,QAAQ;gBAAE,SAAS;YACzC,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;YAC9C,IAAI,CAAC,UAAU,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC;gBAAE,SAAS;YAC3D,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IAED,wEAAwE;IACxE,0EAA0E;IAC1E,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAErE,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,wEAAwE;QACxE,4CAA4C;QAC5C,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,GAAG,CAAC,QAAQ;YAAE,SAAS;QAC1D,IAAI,gBAAgB,CAAC,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC;YAAE,SAAS;QAElE,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,gBAAgB;YACxB,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,oBAAoB,IAAI,CAAC,MAAM,2KAA2K;YACnN,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW;YAClC,WAAW,EAAE,iBAAiB,CAAC,gBAAgB,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;YACjH,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,gCAAgC;SACpE,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Shared utilities for cross-stack concept rules.
3
+ *
4
+ * Every rule that correlates a frontend network call against a server-side
5
+ * route — contract-drift, untyped-api-response, and the upcoming
6
+ * tainted-across-wire — uses the same URL normalisation + route matching
7
+ * pipeline. Centralising it here means a bug fix (or new matching case like
8
+ * Next.js catch-all `[...slug]`) applies to every rule in one place.
9
+ */
10
+ import type { ConceptMap, ConceptNode } from '@kernlang/core';
11
+ /**
12
+ * Multiplier applied to a node's base confidence when firing a cross-stack
13
+ * finding. Each current rule matches only on URL-path shape — no HTTP-method
14
+ * correlation, no body-type correlation — so we intentionally cap confidence
15
+ * below 1.0 to reflect the heuristic nature. Upgrade per-rule once the
16
+ * matching is richer (e.g. once the Python mapper surfaces response_model=,
17
+ * untyped-api-response can bump its own multiplier).
18
+ */
19
+ export declare const CROSS_STACK_HEURISTIC_CONFIDENCE = 0.7;
20
+ /** Client URLs we consider "internal" to the reviewed project. */
21
+ export declare const API_PATH_RE: RegExp;
22
+ export interface ServerRoute {
23
+ path: string;
24
+ method: string | undefined;
25
+ /** Present when the caller needs to cite the server route in a finding. */
26
+ node?: ConceptNode;
27
+ }
28
+ /**
29
+ * Pull every server-side route out of a concept map. Callers typically fold
30
+ * this across `ctx.allConcepts` to collect routes for the whole project.
31
+ */
32
+ export declare function collectRoutes(map: ConceptMap, routes: ServerRoute[]): void;
33
+ /**
34
+ * Strip scheme/host, query string, and fragment from a client URL so it can
35
+ * match against a server route template. Returns undefined when the input
36
+ * isn't a recognisable path (e.g. a bare variable reference or an
37
+ * unresolved template expression).
38
+ */
39
+ export declare function normalizeClientUrl(raw: string): string | undefined;
40
+ /**
41
+ * Match a client-side concrete path against server-side route templates.
42
+ * Returns the first matching route (so callers can cite it in findings) or
43
+ * `undefined`. Server templates may contain params — Express/Koa `:id`,
44
+ * FastAPI `{id}` — which match any single segment. Trailing slashes are
45
+ * normalised on both sides. Case-sensitive (matches Express/FastAPI default
46
+ * behaviour).
47
+ */
48
+ export declare function findMatchingRoute(clientPath: string, routes: readonly ServerRoute[]): ServerRoute | undefined;
49
+ /** Boolean-returning thin wrapper preserved for callers that just need a yes/no. */
50
+ export declare function hasMatchingRoute(clientPath: string, routes: readonly ServerRoute[]): boolean;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Shared utilities for cross-stack concept rules.
3
+ *
4
+ * Every rule that correlates a frontend network call against a server-side
5
+ * route — contract-drift, untyped-api-response, and the upcoming
6
+ * tainted-across-wire — uses the same URL normalisation + route matching
7
+ * pipeline. Centralising it here means a bug fix (or new matching case like
8
+ * Next.js catch-all `[...slug]`) applies to every rule in one place.
9
+ */
10
+ /**
11
+ * Multiplier applied to a node's base confidence when firing a cross-stack
12
+ * finding. Each current rule matches only on URL-path shape — no HTTP-method
13
+ * correlation, no body-type correlation — so we intentionally cap confidence
14
+ * below 1.0 to reflect the heuristic nature. Upgrade per-rule once the
15
+ * matching is richer (e.g. once the Python mapper surfaces response_model=,
16
+ * untyped-api-response can bump its own multiplier).
17
+ */
18
+ export const CROSS_STACK_HEURISTIC_CONFIDENCE = 0.7;
19
+ /** Client URLs we consider "internal" to the reviewed project. */
20
+ export const API_PATH_RE = /^\/api\//;
21
+ /**
22
+ * Pull every server-side route out of a concept map. Callers typically fold
23
+ * this across `ctx.allConcepts` to collect routes for the whole project.
24
+ */
25
+ export function collectRoutes(map, routes) {
26
+ for (const node of map.nodes) {
27
+ if (node.kind !== 'entrypoint' || node.payload.kind !== 'entrypoint' || node.payload.subtype !== 'route')
28
+ continue;
29
+ const path = node.payload.name;
30
+ if (typeof path !== 'string' || !path.startsWith('/'))
31
+ continue;
32
+ routes.push({ path, method: node.payload.httpMethod, node });
33
+ }
34
+ }
35
+ /**
36
+ * Strip scheme/host, query string, and fragment from a client URL so it can
37
+ * match against a server route template. Returns undefined when the input
38
+ * isn't a recognisable path (e.g. a bare variable reference or an
39
+ * unresolved template expression).
40
+ */
41
+ export function normalizeClientUrl(raw) {
42
+ let url = raw.trim();
43
+ if (url.startsWith('`') && !url.startsWith('`/'))
44
+ return undefined;
45
+ url = url.replace(/^`|`$/g, '');
46
+ if (url.startsWith('http://') || url.startsWith('https://')) {
47
+ const pathStart = url.indexOf('/', url.indexOf('://') + 3);
48
+ url = pathStart === -1 ? '/' : url.slice(pathStart);
49
+ }
50
+ const q = url.indexOf('?');
51
+ if (q !== -1)
52
+ url = url.slice(0, q);
53
+ const h = url.indexOf('#');
54
+ if (h !== -1)
55
+ url = url.slice(0, h);
56
+ return url || undefined;
57
+ }
58
+ /**
59
+ * Match a client-side concrete path against server-side route templates.
60
+ * Returns the first matching route (so callers can cite it in findings) or
61
+ * `undefined`. Server templates may contain params — Express/Koa `:id`,
62
+ * FastAPI `{id}` — which match any single segment. Trailing slashes are
63
+ * normalised on both sides. Case-sensitive (matches Express/FastAPI default
64
+ * behaviour).
65
+ */
66
+ export function findMatchingRoute(clientPath, routes) {
67
+ const clientSegments = trimTrailing(clientPath).split('/');
68
+ for (const route of routes) {
69
+ const routeSegments = trimTrailing(route.path).split('/');
70
+ if (routeSegments.length !== clientSegments.length)
71
+ continue;
72
+ let matched = true;
73
+ for (let i = 0; i < routeSegments.length; i++) {
74
+ const rs = routeSegments[i];
75
+ const cs = clientSegments[i];
76
+ if (isParamSegment(rs))
77
+ continue;
78
+ if (rs !== cs) {
79
+ matched = false;
80
+ break;
81
+ }
82
+ }
83
+ if (matched)
84
+ return route;
85
+ }
86
+ return undefined;
87
+ }
88
+ /** Boolean-returning thin wrapper preserved for callers that just need a yes/no. */
89
+ export function hasMatchingRoute(clientPath, routes) {
90
+ return findMatchingRoute(clientPath, routes) !== undefined;
91
+ }
92
+ function trimTrailing(path) {
93
+ return path.length > 1 && path.endsWith('/') ? path.slice(0, -1) : path;
94
+ }
95
+ function isParamSegment(seg) {
96
+ return seg.startsWith(':') || (seg.startsWith('{') && seg.endsWith('}'));
97
+ }
98
+ //# sourceMappingURL=cross-stack-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cross-stack-utils.js","sourceRoot":"","sources":["../../src/concept-rules/cross-stack-utils.ts"],"names":[],"mappings":"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"}
@@ -5,10 +5,21 @@
5
5
  * Language-agnostic by design.
6
6
  */
7
7
  import { boundaryMutation } from './boundary-mutation.js';
8
+ import { contractDrift } from './contract-drift.js';
8
9
  import { ignoredError } from './ignored-error.js';
10
+ import { taintedAcrossWire } from './tainted-across-wire.js';
9
11
  import { unguardedEffect } from './unguarded-effect.js';
10
12
  import { unrecoveredEffect } from './unrecovered-effect.js';
11
- export const conceptRules = [boundaryMutation, ignoredError, unguardedEffect, unrecoveredEffect];
13
+ import { untypedApiResponse } from './untyped-api-response.js';
14
+ export const conceptRules = [
15
+ boundaryMutation,
16
+ contractDrift,
17
+ ignoredError,
18
+ taintedAcrossWire,
19
+ unguardedEffect,
20
+ unrecoveredEffect,
21
+ untypedApiResponse,
22
+ ];
12
23
  export function runConceptRules(concepts, filePath, allConcepts, graphImports) {
13
24
  const ctx = { concepts, filePath, allConcepts, graphImports };
14
25
  const findings = [];
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/concept-rules/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAa5D,MAAM,CAAC,MAAM,YAAY,GAAkB,CAAC,gBAAgB,EAAE,YAAY,EAAE,eAAe,EAAE,iBAAiB,CAAC,CAAC;AAEhH,MAAM,UAAU,eAAe,CAC7B,QAAoB,EACpB,QAAgB,EAChB,WAAqC,EACrC,YAAoC;IAEpC,MAAM,GAAG,GAAuB,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC;IAClF,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/concept-rules/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAa/D,MAAM,CAAC,MAAM,YAAY,GAAkB;IACzC,gBAAgB;IAChB,aAAa;IACb,YAAY;IACZ,iBAAiB;IACjB,eAAe;IACf,iBAAiB;IACjB,kBAAkB;CACnB,CAAC;AAEF,MAAM,UAAU,eAAe,CAC7B,QAAoB,EACpB,QAAgB,EAChB,WAAqC,EACrC,YAAoC;IAEpC,MAAM,GAAG,GAAuB,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC;IAClF,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Rule: tainted-across-wire
3
+ *
4
+ * Cross-stack rule — fires when a frontend (TS) network call sends a
5
+ * dynamic (user-controlled) body to a server-side route in the reviewed
6
+ * project AND that route's handler has no validation guard in scope.
7
+ *
8
+ * This is rule #3 of the fullstack wedge. Pure server-side taint analysis
9
+ * (already in @kernlang/review's taint-crossfile module) finds sinks
10
+ * reachable from `req.body`, but it can't see the *client-side call site*
11
+ * that's feeding the unvalidated input. And pure client-side analysis
12
+ * doesn't know whether the server validates — so it either fires on every
13
+ * dynamic POST (noise) or on none (misses the moat).
14
+ *
15
+ * By correlating both sides via the concept graph we can fire precisely:
16
+ * "here's the fetch that sends user input to an endpoint whose handler
17
+ * doesn't parse it with zod/yup/joi/pydantic". The finding lands on the
18
+ * client-side call so the fix is visible where the developer is working.
19
+ *
20
+ * Preconditions to fire:
21
+ * 1. Graph mode (`ctx.allConcepts` populated).
22
+ * 2. Client concept has `bodyKind === 'dynamic'` — we know real data is
23
+ * crossing the wire, not a ping/HEAD/etc.
24
+ * 3. Client target path matches a server route in the graph.
25
+ * 4. That server route's container has NO `guard` concept with
26
+ * `subtype === 'validation'` (schema.parse / zod.parse / …).
27
+ *
28
+ * Silent on `bodyKind === undefined` (mapper couldn't classify) and on
29
+ * missing server matches (contract-drift owns that class).
30
+ */
31
+ import type { ReviewFinding } from '../types.js';
32
+ import type { ConceptRuleContext } from './index.js';
33
+ export declare function taintedAcrossWire(ctx: ConceptRuleContext): ReviewFinding[];
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Rule: tainted-across-wire
3
+ *
4
+ * Cross-stack rule — fires when a frontend (TS) network call sends a
5
+ * dynamic (user-controlled) body to a server-side route in the reviewed
6
+ * project AND that route's handler has no validation guard in scope.
7
+ *
8
+ * This is rule #3 of the fullstack wedge. Pure server-side taint analysis
9
+ * (already in @kernlang/review's taint-crossfile module) finds sinks
10
+ * reachable from `req.body`, but it can't see the *client-side call site*
11
+ * that's feeding the unvalidated input. And pure client-side analysis
12
+ * doesn't know whether the server validates — so it either fires on every
13
+ * dynamic POST (noise) or on none (misses the moat).
14
+ *
15
+ * By correlating both sides via the concept graph we can fire precisely:
16
+ * "here's the fetch that sends user input to an endpoint whose handler
17
+ * doesn't parse it with zod/yup/joi/pydantic". The finding lands on the
18
+ * client-side call so the fix is visible where the developer is working.
19
+ *
20
+ * Preconditions to fire:
21
+ * 1. Graph mode (`ctx.allConcepts` populated).
22
+ * 2. Client concept has `bodyKind === 'dynamic'` — we know real data is
23
+ * crossing the wire, not a ping/HEAD/etc.
24
+ * 3. Client target path matches a server route in the graph.
25
+ * 4. That server route's container has NO `guard` concept with
26
+ * `subtype === 'validation'` (schema.parse / zod.parse / …).
27
+ *
28
+ * Silent on `bodyKind === undefined` (mapper couldn't classify) and on
29
+ * missing server matches (contract-drift owns that class).
30
+ */
31
+ import { createFingerprint } from '../types.js';
32
+ import { API_PATH_RE, CROSS_STACK_HEURISTIC_CONFIDENCE, collectRoutes, findMatchingRoute, normalizeClientUrl, } from './cross-stack-utils.js';
33
+ export function taintedAcrossWire(ctx) {
34
+ if (!ctx.allConcepts || ctx.allConcepts.size === 0)
35
+ return [];
36
+ const serverRoutes = [];
37
+ for (const [, conceptMap] of ctx.allConcepts) {
38
+ collectRoutes(conceptMap, serverRoutes);
39
+ }
40
+ if (serverRoutes.length === 0)
41
+ return [];
42
+ // Build the set of files that contain at least one validation guard.
43
+ // Container-level matching is too strict: zod/yup parsers typically live
44
+ // inside the route callback body (arrow function container) while the
45
+ // route entrypoint itself is emitted at the call-expression level (module
46
+ // container). File-level matching is coarser but safer — it silences the
47
+ // common case where a validator exists in the same server file as the
48
+ // route, which is a strong signal the handler is guarded. False negatives
49
+ // (a validation-less route in a file that validates *other* routes) cost
50
+ // us less than false positives on the pitch.
51
+ const validatedFiles = collectValidatedFiles(ctx.allConcepts);
52
+ const findings = [];
53
+ const localConcepts = ctx.allConcepts.get(ctx.filePath) ?? ctx.concepts;
54
+ for (const node of localConcepts.nodes) {
55
+ if (node.kind !== 'effect' || node.payload.kind !== 'effect' || node.payload.subtype !== 'network')
56
+ continue;
57
+ if (node.payload.bodyKind !== 'dynamic')
58
+ continue;
59
+ const target = node.payload.target;
60
+ if (typeof target !== 'string')
61
+ continue;
62
+ const normalized = normalizeClientUrl(target);
63
+ if (!normalized || !API_PATH_RE.test(normalized))
64
+ continue;
65
+ const matchedRoute = findMatchingRoute(normalized, serverRoutes);
66
+ if (!matchedRoute)
67
+ continue; // contract-drift owns the "wrong URL" class.
68
+ const routeFile = matchedRoute.node?.primarySpan.file;
69
+ if (routeFile && validatedFiles.has(routeFile))
70
+ continue;
71
+ findings.push({
72
+ source: 'kern',
73
+ ruleId: 'tainted-across-wire',
74
+ severity: 'warning',
75
+ category: 'pattern',
76
+ message: `Dynamic body sent to \`${target}\` but the matching server route has no validation guard (schema.parse / zod / yup / pydantic). Add a validator on the server before trusting the payload, or move validation to the client if this endpoint is internal-only.`,
77
+ primarySpan: node.primarySpan,
78
+ fingerprint: createFingerprint('tainted-across-wire', node.primarySpan.startLine, node.primarySpan.startCol),
79
+ confidence: node.confidence * CROSS_STACK_HEURISTIC_CONFIDENCE,
80
+ });
81
+ }
82
+ return findings;
83
+ }
84
+ function collectValidatedFiles(allConcepts) {
85
+ const set = new Set();
86
+ for (const [file, conceptMap] of allConcepts) {
87
+ for (const node of conceptMap.nodes) {
88
+ if (node.kind !== 'guard' || node.payload.kind !== 'guard')
89
+ continue;
90
+ if (node.payload.subtype !== 'validation')
91
+ continue;
92
+ set.add(file);
93
+ break;
94
+ }
95
+ }
96
+ return set;
97
+ }
98
+ //# sourceMappingURL=tainted-across-wire.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tainted-across-wire.js","sourceRoot":"","sources":["../../src/concept-rules/tainted-across-wire.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Rule: untyped-api-response
3
+ *
4
+ * Cross-stack rule — fires when a frontend (TS) network call targets a
5
+ * server-side route in the reviewed project AND consumes the JSON body
6
+ * without a type annotation, `as T` cast, or `satisfies T` clause.
7
+ *
8
+ * This is rule #2 of the fullstack wedge (TS ↔ Python/Express). The server
9
+ * has a declared response shape (Pydantic `response_model=`, Express
10
+ * `Response<T>`, …) but the client is treating the payload as `any`, which
11
+ * means any breaking change in the response shape will silently rot the
12
+ * frontend at runtime. ESLint can catch "no-explicit-any" at the TS level
13
+ * but it can't tell you *which* fetch() is actually talking to a project
14
+ * endpoint — only the concept graph knows that.
15
+ *
16
+ * Preconditions to fire:
17
+ * 1. Graph mode (`ctx.allConcepts` populated).
18
+ * 2. Client concept has `responseAsserted === false` (mapper proved the
19
+ * .json() consumption is untyped).
20
+ * 3. The call's target path matches a server-side route in the graph —
21
+ * otherwise this is just a generic untyped-fetch case, which Biome
22
+ * already covers and we don't want to duplicate.
23
+ *
24
+ * Kept conservative: when the mapper returns `undefined` for
25
+ * responseAsserted (patterns it couldn't analyze) the rule stays silent.
26
+ * False positives here would poison the pitch.
27
+ */
28
+ import type { ReviewFinding } from '../types.js';
29
+ import type { ConceptRuleContext } from './index.js';
30
+ export declare function untypedApiResponse(ctx: ConceptRuleContext): ReviewFinding[];
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Rule: untyped-api-response
3
+ *
4
+ * Cross-stack rule — fires when a frontend (TS) network call targets a
5
+ * server-side route in the reviewed project AND consumes the JSON body
6
+ * without a type annotation, `as T` cast, or `satisfies T` clause.
7
+ *
8
+ * This is rule #2 of the fullstack wedge (TS ↔ Python/Express). The server
9
+ * has a declared response shape (Pydantic `response_model=`, Express
10
+ * `Response<T>`, …) but the client is treating the payload as `any`, which
11
+ * means any breaking change in the response shape will silently rot the
12
+ * frontend at runtime. ESLint can catch "no-explicit-any" at the TS level
13
+ * but it can't tell you *which* fetch() is actually talking to a project
14
+ * endpoint — only the concept graph knows that.
15
+ *
16
+ * Preconditions to fire:
17
+ * 1. Graph mode (`ctx.allConcepts` populated).
18
+ * 2. Client concept has `responseAsserted === false` (mapper proved the
19
+ * .json() consumption is untyped).
20
+ * 3. The call's target path matches a server-side route in the graph —
21
+ * otherwise this is just a generic untyped-fetch case, which Biome
22
+ * already covers and we don't want to duplicate.
23
+ *
24
+ * Kept conservative: when the mapper returns `undefined` for
25
+ * responseAsserted (patterns it couldn't analyze) the rule stays silent.
26
+ * False positives here would poison the pitch.
27
+ */
28
+ import { createFingerprint } from '../types.js';
29
+ import { API_PATH_RE, CROSS_STACK_HEURISTIC_CONFIDENCE, collectRoutes, hasMatchingRoute, normalizeClientUrl, } from './cross-stack-utils.js';
30
+ export function untypedApiResponse(ctx) {
31
+ if (!ctx.allConcepts || ctx.allConcepts.size === 0)
32
+ return [];
33
+ const serverRoutes = [];
34
+ for (const [, conceptMap] of ctx.allConcepts) {
35
+ collectRoutes(conceptMap, serverRoutes);
36
+ }
37
+ if (serverRoutes.length === 0)
38
+ return [];
39
+ const findings = [];
40
+ // Only scan this file's concepts so we don't duplicate findings per call.
41
+ const localConcepts = ctx.allConcepts.get(ctx.filePath) ?? ctx.concepts;
42
+ for (const node of localConcepts.nodes) {
43
+ if (node.kind !== 'effect' || node.payload.kind !== 'effect' || node.payload.subtype !== 'network')
44
+ continue;
45
+ if (node.payload.responseAsserted !== false)
46
+ continue; // undefined stays silent
47
+ const target = node.payload.target;
48
+ if (typeof target !== 'string')
49
+ continue;
50
+ const normalized = normalizeClientUrl(target);
51
+ if (!normalized || !API_PATH_RE.test(normalized))
52
+ continue;
53
+ if (!hasMatchingRoute(normalized, serverRoutes))
54
+ continue;
55
+ findings.push({
56
+ source: 'kern',
57
+ ruleId: 'untyped-api-response',
58
+ severity: 'warning',
59
+ category: 'bug',
60
+ message: `Response from \`${target}\` is consumed without a type annotation. The server route defines a response shape — assign the awaited value to a typed variable or use \`as T\` / \`satisfies T\` so response-shape drift is caught at compile time instead of breaking at runtime.`,
61
+ primarySpan: node.primarySpan,
62
+ fingerprint: createFingerprint('untyped-api-response', node.primarySpan.startLine, node.primarySpan.startCol),
63
+ // Same tier as contract-drift. Upgrade once the Python mapper surfaces
64
+ // `response_model=` and we can also cite the specific server type the
65
+ // frontend should be asserting against.
66
+ confidence: node.confidence * CROSS_STACK_HEURISTIC_CONFIDENCE,
67
+ });
68
+ }
69
+ return findings;
70
+ }
71
+ //# sourceMappingURL=untyped-api-response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"untyped-api-response.js","sourceRoot":"","sources":["../../src/concept-rules/untyped-api-response.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,WAAW,EACX,gCAAgC,EAChC,aAAa,EACb,gBAAgB,EAChB,kBAAkB,GAEnB,MAAM,wBAAwB,CAAC;AAGhC,MAAM,UAAU,kBAAkB,CAAC,GAAuB;IACxD,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAE9D,MAAM,YAAY,GAAkB,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,EAAE,UAAU,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QAC7C,aAAa,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEzC,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,0EAA0E;IAC1E,MAAM,aAAa,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC;IACxE,KAAK,MAAM,IAAI,IAAI,aAAa,CAAC,KAAK,EAAE,CAAC;QACvC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS;YAAE,SAAS;QAC7G,IAAI,IAAI,CAAC,OAAO,CAAC,gBAAgB,KAAK,KAAK;YAAE,SAAS,CAAC,yBAAyB;QAChF,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QACnC,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,SAAS;QACzC,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,CAAC,UAAU,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC;YAAE,SAAS;QAC3D,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,YAAY,CAAC;YAAE,SAAS;QAE1D,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,sBAAsB;YAC9B,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,mBAAmB,MAAM,wPAAwP;YAC1R,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,iBAAiB,CAAC,sBAAsB,EAAE,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;YAC7G,uEAAuE;YACvE,sEAAsE;YACtE,wCAAwC;YACxC,UAAU,EAAE,IAAI,CAAC,UAAU,GAAG,gCAAgC;SAC/D,CAAC,CAAC;IACL,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -22,10 +22,23 @@ import type { InferResult, ReviewFinding } from './types.js';
22
22
  export declare function runESLint(filePaths: string[], cwd: string, health?: ReviewHealthBuilder): Promise<ReviewFinding[]>;
23
23
  export interface RunTSCDiagnosticsOptions {
24
24
  /**
25
- * When true, suppress TS6059/TS6307 project-loading diagnostics. Set this only for callers that
26
- * inject ad-hoc files into a Project that carries a host tsconfig those two codes then fire as
27
- * infrastructure noise, not user bugs. The --lint path must leave this false so a real tsconfig
28
- * misconfiguration still surfaces as an error.
25
+ * When true, suppress TS diagnostics that fire as kern-review infrastructure noise when we inject
26
+ * ad-hoc files into a Project that carries a host tsconfig. Suppressed codes fall into two classes:
27
+ *
28
+ * Project membership (in-memory Project vs host rootDir):
29
+ * - TS6059 — "File is not listed within the file list of project"
30
+ * - TS6307 — "File is not under 'rootDir'"
31
+ *
32
+ * Environmental (in-memory Project doesn't mirror host compilerOptions — moduleResolution, jsx, lib):
33
+ * - TS2792 — "Cannot find module X. Did you mean to set 'moduleResolution' to 'nodenext'?"
34
+ * - TS17004 — "Cannot use JSX unless the '--jsx' flag is provided"
35
+ * - TS2580 / TS2591 — "Cannot find name 'process'/'require'/'module'" (@types/node missing)
36
+ *
37
+ * The dev already sees the environmental class in their IDE / local `tsc --noEmit` when real.
38
+ * Set this only for the standard review path. The --lint path must leave it false so real
39
+ * tsconfig misconfigurations still surface as errors.
40
+ *
41
+ * The name is kept for backward compatibility; scope broadened deliberately.
29
42
  */
30
43
  downgradeProjectLoadingErrors?: boolean;
31
44
  }
@@ -151,8 +151,19 @@ export function runTSCDiagnostics(project, options = {}, health) {
151
151
  // them as info still pollutes every barrel/re-export report in composite monorepos.
152
152
  // ts6059 — "File is not listed within the file list of project"
153
153
  // ts6307 — "File is not under 'rootDir'"
154
+ // The following codes are environmental: they reflect ts-morph's in-memory Project not
155
+ // perfectly mirroring the host's compilerOptions (moduleResolution, jsx, lib). The dev
156
+ // already sees them in their IDE / local `tsc --noEmit` if real; the review's value-add
157
+ // is KERN-relevant findings, not duplicating compiler output. A sweep of the agon repo
158
+ // (451 files) emitted 1869 of these as errors — pure noise drowning real findings.
159
+ // ts2792 — "Cannot find module X. Did you mean to set 'moduleResolution' to 'nodenext'?"
160
+ // ts17004 — "Cannot use JSX unless the '--jsx' flag is provided"
161
+ // ts2580 / ts2591 — "Cannot find name 'process'/'require'/'module'. Install @types/node?"
162
+ // (TS emits 2580 when the name resolves via global lib shims, 2591 when it doesn't —
163
+ // both point at the same user-side remedy, both are environmental from review's POV.)
154
164
  const isLoadingNoise = code === 6059 || code === 6307;
155
- if (isLoadingNoise && options.downgradeProjectLoadingErrors) {
165
+ const isEnvironmentalNoise = code === 2792 || code === 17004 || code === 2580 || code === 2591;
166
+ if ((isLoadingNoise || isEnvironmentalNoise) && options.downgradeProjectLoadingErrors) {
156
167
  continue;
157
168
  }
158
169
  findings.push({
@@ -1 +1 @@
1
- {"version":3,"file":"external-tools.js","sourceRoot":"","sources":["../src/external-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,WAAW,EAA4B,MAAM,oBAAoB,CAAC;AAE3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,GAAY;IACpC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAClD,MAAM,IAAI,GAAI,GAA0B,CAAC,IAAI,CAAC;IAC9C,OAAO,IAAI,KAAK,kBAAkB,IAAI,IAAI,KAAK,sBAAsB,CAAC;AACxE,CAAC;AAED,4EAA4E;AAE5E;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,SAAmB,EACnB,GAAW,EACX,MAA4B;IAE5B,uFAAuF;IACvF,sFAAsF;IACtF,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IAClC,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,CAAC,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAQ,CAAC;QAC7D,MAAM,GAAG,YAAY,CAAC,MAAM,IAAI,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC;IAC/D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,EAAE,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE,gCAAgC,CAAC,CAAC;YACxE,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,uBAAuB,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/E,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU;YAAE,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;QACxF,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,EAAE,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE,4DAA4D,CAAC,CAAC;QACpG,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QACnC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAElD,MAAM,QAAQ,GAAoB,EAAE,CAAC;QAErC,KAAK,MAAM,MAAM,IAAI,OAAgB,EAAE,CAAC;YACtC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,QAAiB,EAAE,CAAC;gBAC3C,MAAM,QAAQ,GACZ,GAAG,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;gBAEzE,MAAM,WAAW,GAAe;oBAC9B,IAAI,EAAE,MAAM,CAAC,QAAQ;oBACrB,SAAS,EAAE,GAAG,CAAC,IAAI;oBACnB,QAAQ,EAAE,GAAG,CAAC,MAAM;oBACpB,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,IAAI;oBAChC,MAAM,EAAE,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,MAAM;iBACpC,CAAC;gBAEF,QAAQ,CAAC,IAAI,CAAC;oBACZ,MAAM,EAAE,QAAQ;oBAChB,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,gBAAgB;oBACtC,QAAQ;oBACR,QAAQ,EAAE,oBAAoB,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC;oBAChD,OAAO,EAAE,GAAG,CAAC,OAAO;oBACpB,WAAW;oBACX,UAAU,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS;oBAChD,WAAW,EAAE,iBAAiB,CAAC,GAAG,CAAC,MAAM,IAAI,QAAQ,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC;iBAC7E,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,wFAAwF;QACxF,sFAAsF;QACtF,4CAA4C;QAC5C,MAAM,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,+BAA+B,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;QACvF,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU;YAAE,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;QACxF,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,oBAAoB,CAAC,MAAc;IAC1C,IAAI,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,OAAO,CAAC;IAC/E,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAC1E,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9E,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,WAAW,CAAC;IAC/E,OAAO,SAAS,CAAC;AACnB,CAAC;AAcD;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAgB,EAChB,UAAoC,EAAE,EACtC,MAA4B;IAE5B,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC;QAEpD,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;YACxC,IAAI,CAAC,UAAU;gBAAE,SAAS;YAE1B,MAAM,QAAQ,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;YAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YAEhC,IAAI,SAAS,GAAG,CAAC,CAAC;YAClB,IAAI,QAAQ,GAAG,CAAC,CAAC;YACjB,IAAI,OAAO,GAAG,CAAC,CAAC;YAChB,IAAI,MAAM,GAAG,CAAC,CAAC;YAEf,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,MAAM,QAAQ,GAAG,UAAU,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;gBACzD,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC;gBAC1B,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC;gBAE3B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;oBACzB,MAAM,MAAM,GAAG,UAAU,CAAC,qBAAqB,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC;oBAChE,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC;oBACtB,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;gBACzB,CAAC;qBAAM,CAAC;oBACN,OAAO,GAAG,SAAS,CAAC;oBACpB,MAAM,GAAG,QAAQ,CAAC;gBACpB,CAAC;YACH,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;YACpC,MAAM,QAAQ,GACZ,QAAQ,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;YAE3F,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;YACtC,MAAM,UAAU,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC;YAEpF,sFAAsF;YACtF,6FAA6F;YAC7F,6FAA6F;YAC7F,oFAAoF;YACpF,kEAAkE;YAClE,2CAA2C;YAC3C,MAAM,cAAc,GAAG,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,CAAC;YACtD,IAAI,cAAc,IAAI,OAAO,CAAC,6BAA6B,EAAE,CAAC;gBAC5D,SAAS;YACX,CAAC;YAED,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,KAAK;gBACb,MAAM,EAAE,KAAK,IAAI,EAAE;gBACnB,QAAQ;gBACR,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,UAAU;gBACnB,WAAW,EAAE;oBACX,IAAI,EAAE,QAAQ;oBACd,SAAS;oBACT,QAAQ;oBACR,OAAO;oBACP,MAAM;iBACP;gBACD,WAAW,EAAE,iBAAiB,CAAC,KAAK,IAAI,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC;aACjE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,0EAA0E;QAC1E,MAAM,EAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,gCAAgC,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;QACrF,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU;YAAE,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;IAC9F,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,yEAAyE;AAEzE;;;;;;;;GAQG;AACH,MAAM,UAAU,0BAA0B,CAAC,SAAmB,EAAE,MAA4B;IAC1F,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEtC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,OAAO,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;YAClC,CAAC;YAAC,OAAO,EAAE,EAAE,CAAC;gBACZ,KAAK,EAAE,CAAC,CAAC,iDAAiD;YAC5D,CAAC;QACH,CAAC;QACD,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,EAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,oDAAoD,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;QACzG,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU;YAAE,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;QAC9F,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,4EAA4E;AAE5E;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,QAAyB,EAAE,QAAuB;IAC5E,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS,CAAC,iBAAiB;QAElE,MAAM,IAAI,GAAG,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC;QACrC,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,IAAI,IAAI,CAAC,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC;QAEpF,IAAI,YAAY,EAAE,CAAC;YACjB,CAAC,CAAC,OAAO,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"external-tools.js","sourceRoot":"","sources":["../src/external-tools.ts"],"names":[],"mappings":"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"}