stapes-adsa 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/AGENTS.md +244 -0
  2. package/CHANGELOG.md +52 -0
  3. package/LICENSE +21 -0
  4. package/README.md +291 -0
  5. package/SECURITY.md +46 -0
  6. package/SKILL.md +198 -0
  7. package/dist/ast.d.ts +55 -0
  8. package/dist/ast.js +186 -0
  9. package/dist/ast.js.map +1 -0
  10. package/dist/checks/async-no-log.d.ts +19 -0
  11. package/dist/checks/async-no-log.js +77 -0
  12. package/dist/checks/async-no-log.js.map +1 -0
  13. package/dist/checks/decision-router.d.ts +16 -0
  14. package/dist/checks/decision-router.js +112 -0
  15. package/dist/checks/decision-router.js.map +1 -0
  16. package/dist/checks/fast-path-with-io.d.ts +19 -0
  17. package/dist/checks/fast-path-with-io.js +58 -0
  18. package/dist/checks/fast-path-with-io.js.map +1 -0
  19. package/dist/checks/index.d.ts +21 -0
  20. package/dist/checks/index.js +90 -0
  21. package/dist/checks/index.js.map +1 -0
  22. package/dist/checks/log-schema.d.ts +18 -0
  23. package/dist/checks/log-schema.js +124 -0
  24. package/dist/checks/log-schema.js.map +1 -0
  25. package/dist/checks/slow-path-no-log.d.ts +16 -0
  26. package/dist/checks/slow-path-no-log.js +71 -0
  27. package/dist/checks/slow-path-no-log.js.map +1 -0
  28. package/dist/cli.d.ts +25 -0
  29. package/dist/cli.js +186 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/column.d.ts +10 -0
  32. package/dist/column.js +30 -0
  33. package/dist/column.js.map +1 -0
  34. package/dist/fix.d.ts +34 -0
  35. package/dist/fix.js +110 -0
  36. package/dist/fix.js.map +1 -0
  37. package/dist/hook-install.d.ts +36 -0
  38. package/dist/hook-install.js +94 -0
  39. package/dist/hook-install.js.map +1 -0
  40. package/dist/lib/instrument.d.ts +49 -0
  41. package/dist/lib/instrument.js +53 -0
  42. package/dist/lib/instrument.js.map +1 -0
  43. package/dist/patterns.d.ts +36 -0
  44. package/dist/patterns.js +72 -0
  45. package/dist/patterns.js.map +1 -0
  46. package/dist/runner.d.ts +35 -0
  47. package/dist/runner.js +104 -0
  48. package/dist/runner.js.map +1 -0
  49. package/dist/types.d.ts +52 -0
  50. package/dist/types.js +9 -0
  51. package/dist/types.js.map +1 -0
  52. package/dist/util.d.ts +32 -0
  53. package/dist/util.js +105 -0
  54. package/dist/util.js.map +1 -0
  55. package/package.json +66 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Helpers for converting node positions into (line, column) coordinates.
3
+ *
4
+ * ts-morph's `getStart()` returns a numeric offset; `getStartLineNumber()`
5
+ * returns the 1-based line. We compute the 1-based column by subtracting
6
+ * the start of the line from the start offset.
7
+ */
8
+ import type { Node } from "ts-morph";
9
+ /** 1-based column number for the start of the given node. */
10
+ export declare function startColumn(node: Node): number;
package/dist/column.js ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Helpers for converting node positions into (line, column) coordinates.
3
+ *
4
+ * ts-morph's `getStart()` returns a numeric offset; `getStartLineNumber()`
5
+ * returns the 1-based line. We compute the 1-based column by subtracting
6
+ * the start of the line from the start offset.
7
+ */
8
+ /** ASCII code for newline. Extracted to avoid the magic-number heuristic. */
9
+ const NEWLINE_CODE = 10;
10
+ /** 1-based column number for the start of the given node. */
11
+ export function startColumn(node) {
12
+ const sf = node.getSourceFile();
13
+ const line = node.getStartLineNumber();
14
+ // ts-morph v22+ exposes the line-start offset via compiler internals.
15
+ // Walk the source text to find the start-of-line position.
16
+ const text = sf.getFullText();
17
+ let lineStart = 0;
18
+ let currentLine = 1;
19
+ for (let i = 0; i < text.length; i++) {
20
+ if (currentLine === line) {
21
+ lineStart = i;
22
+ break;
23
+ }
24
+ if (text.charCodeAt(i) === NEWLINE_CODE) {
25
+ currentLine++;
26
+ }
27
+ }
28
+ return node.getStart() - lineStart + 1;
29
+ }
30
+ //# sourceMappingURL=column.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column.js","sourceRoot":"","sources":["../src/column.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,6EAA6E;AAC7E,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB,6DAA6D;AAC7D,MAAM,UAAU,WAAW,CAAC,IAAU;IACpC,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;IAChC,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;IACvC,sEAAsE;IACtE,2DAA2D;IAC3D,MAAM,IAAI,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;IAC9B,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,SAAS,GAAG,CAAC,CAAC;YACd,MAAM;QACR,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,YAAY,EAAE,CAAC;YACxC,WAAW,EAAE,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,QAAQ,EAAE,GAAG,SAAS,GAAG,CAAC,CAAC;AACzC,CAAC"}
package/dist/fix.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ /**
2
+ * --fix — apply safe auto-fixes to findings produced by the runner.
3
+ *
4
+ * Conservative subset (SPEC.md invariant #7):
5
+ * - Insert slow-path log line at top of a function body
6
+ * (slow-path-no-log + async-no-log findings)
7
+ * - NEVER modify a file with fast-path-with-io / decision-router /
8
+ * log-schema findings — those require refactor / developer judgment.
9
+ *
10
+ * Dry-run by default. Pass `--fix-confirm` to actually write files.
11
+ *
12
+ * Output is a FixReport — list of per-file outcomes.
13
+ */
14
+ import type { Finding } from "./types.js";
15
+ export interface FixReport {
16
+ applied: number;
17
+ skipped: number;
18
+ failed: number;
19
+ files: FileFixReport[];
20
+ }
21
+ export interface FileFixReport {
22
+ file: string;
23
+ status: "applied" | "skipped" | "failed";
24
+ reason?: string;
25
+ findingsBefore: number;
26
+ findingsAfter: number;
27
+ }
28
+ export interface FixOptions {
29
+ /** If false (dry-run), no files are written. */
30
+ write: boolean;
31
+ }
32
+ export declare function applyFixes(findings: Finding[], opts: FixOptions): FixReport;
33
+ export declare const __FIXABLE_CHECKS: ReadonlySet<string>;
34
+ export declare const __ALL_CHECK_NAMES: string[];
package/dist/fix.js ADDED
@@ -0,0 +1,110 @@
1
+ /**
2
+ * --fix — apply safe auto-fixes to findings produced by the runner.
3
+ *
4
+ * Conservative subset (SPEC.md invariant #7):
5
+ * - Insert slow-path log line at top of a function body
6
+ * (slow-path-no-log + async-no-log findings)
7
+ * - NEVER modify a file with fast-path-with-io / decision-router /
8
+ * log-schema findings — those require refactor / developer judgment.
9
+ *
10
+ * Dry-run by default. Pass `--fix-confirm` to actually write files.
11
+ *
12
+ * Output is a FixReport — list of per-file outcomes.
13
+ */
14
+ import { readFileSync, writeFileSync } from "node:fs";
15
+ import { CHECKS } from "./checks/index.js";
16
+ /** Fixable check names — the conservative subset. */
17
+ const FIXABLE_CHECKS = new Set([
18
+ "slow-path-no-log",
19
+ "async-no-log",
20
+ ]);
21
+ export function applyFixes(findings, opts) {
22
+ const byFile = new Map();
23
+ for (const f of findings) {
24
+ if (!FIXABLE_CHECKS.has(f.check))
25
+ continue;
26
+ const arr = byFile.get(f.file) ?? [];
27
+ arr.push(f);
28
+ byFile.set(f.file, arr);
29
+ }
30
+ const report = {
31
+ applied: 0,
32
+ skipped: 0,
33
+ failed: 0,
34
+ files: [],
35
+ };
36
+ for (const [file, fileFindings] of byFile) {
37
+ const beforeCount = fileFindings.length;
38
+ const fixable = fileFindings.filter((f) => f.suggestedFix);
39
+ if (fixable.length === 0) {
40
+ report.files.push({
41
+ file,
42
+ status: "skipped",
43
+ reason: "no suggestedFix on findings",
44
+ findingsBefore: beforeCount,
45
+ findingsAfter: beforeCount,
46
+ });
47
+ report.skipped += beforeCount;
48
+ continue;
49
+ }
50
+ if (!opts.write) {
51
+ report.files.push({
52
+ file,
53
+ status: "skipped",
54
+ reason: "dry-run (--fix-confirm not set)",
55
+ findingsBefore: beforeCount,
56
+ findingsAfter: beforeCount,
57
+ });
58
+ report.skipped += fixable.length;
59
+ continue;
60
+ }
61
+ try {
62
+ const src = readFileSync(file, "utf8");
63
+ const lines = src.split("\n");
64
+ // Apply insertions in reverse line order so earlier insertions don't shift later line numbers.
65
+ const sortedFixes = [...fixable].sort((a, b) => (b.suggestedFix.line - a.suggestedFix.line));
66
+ for (const f of sortedFixes) {
67
+ const fix = f.suggestedFix;
68
+ // Insert the replacement text at `line`, `column`.
69
+ // Line is 1-based, column is 1-based.
70
+ const li = fix.line - 1;
71
+ if (li < 0 || li >= lines.length) {
72
+ report.failed++;
73
+ continue;
74
+ }
75
+ const lineText = lines[li] ?? "";
76
+ // Compute insertion offset: column - 1 (0-based), but clamp to line length.
77
+ const colIdx = Math.max(0, Math.min(lineText.length, fix.column - 1));
78
+ const before = lineText.slice(0, colIdx);
79
+ const after = lineText.slice(colIdx);
80
+ const replacement = fix.replacement.replace(/\n$/, "");
81
+ // If the replacement is multi-line, split and insert.
82
+ const newLines = replacement.split("\n");
83
+ lines.splice(li, 1, before + newLines[0], ...newLines.slice(1), after);
84
+ }
85
+ writeFileSync(file, lines.join("\n"));
86
+ report.applied += fixable.length;
87
+ report.files.push({
88
+ file,
89
+ status: "applied",
90
+ findingsBefore: beforeCount,
91
+ findingsAfter: beforeCount - fixable.length,
92
+ });
93
+ }
94
+ catch (err) {
95
+ report.failed += fixable.length;
96
+ report.files.push({
97
+ file,
98
+ status: "failed",
99
+ reason: err instanceof Error ? err.message : String(err),
100
+ findingsBefore: beforeCount,
101
+ findingsAfter: beforeCount,
102
+ });
103
+ }
104
+ }
105
+ return report;
106
+ }
107
+ // Re-export for unit tests to enumerate fixable checks.
108
+ export const __FIXABLE_CHECKS = FIXABLE_CHECKS;
109
+ export const __ALL_CHECK_NAMES = CHECKS.map((c) => c.name);
110
+ //# sourceMappingURL=fix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fix.js","sourceRoot":"","sources":["../src/fix.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * --init / --uninstall — install or remove a marker-block pre-commit hook.
3
+ *
4
+ * Behaviour (matches stapes-precommit):
5
+ * - Reads .git/hooks/pre-commit if it exists.
6
+ * - If our marker block is already present, --init is a no-op.
7
+ * - If a foreign hook (Husky, lefthook) is present, our block is
8
+ * appended inside our marker so the foreign hook still runs.
9
+ * - --uninstall removes only our marker block, leaves the rest.
10
+ */
11
+ export interface InitResult {
12
+ hookPath: string;
13
+ status: "created" | "appended" | "already-installed";
14
+ }
15
+ /**
16
+ * Install the pre-commit hook. Idempotent.
17
+ *
18
+ * Returns the path and a status string:
19
+ * - "created": hook file did not exist, created with our block
20
+ * - "appended": hook file existed with a foreign hook, appended our block
21
+ * - "already-installed": our marker was already present, no-op
22
+ */
23
+ export declare function installHook(repoRoot: string): InitResult;
24
+ export interface UninstallResult {
25
+ hookPath: string;
26
+ status: "removed" | "deleted" | "not-installed";
27
+ }
28
+ /**
29
+ * Remove our marker block from the pre-commit hook. If the file ends up
30
+ * empty after removal, delete it. Foreign hooks are left untouched.
31
+ */
32
+ export declare function uninstallHook(repoRoot: string): UninstallResult;
33
+ /** Test helper — also exported for the unit tests. */
34
+ export declare const __MARKER_START = "# >>> stapes-adsa hook >>>";
35
+ export declare const __MARKER_END = "# <<< stapes-adsa hook <<<";
36
+ export declare const __HOOK_BODY = "#!/usr/bin/env sh\n# >>> stapes-adsa hook >>>\n# This block is managed by 'stapes-adsa --init'. Do not edit.\n# To remove, run 'stapes-adsa --uninstall'.\nset -e\nnpx --no-install stapes-adsa --run --json\n# <<< stapes-adsa hook <<<\n";
@@ -0,0 +1,94 @@
1
+ /**
2
+ * --init / --uninstall — install or remove a marker-block pre-commit hook.
3
+ *
4
+ * Behaviour (matches stapes-precommit):
5
+ * - Reads .git/hooks/pre-commit if it exists.
6
+ * - If our marker block is already present, --init is a no-op.
7
+ * - If a foreign hook (Husky, lefthook) is present, our block is
8
+ * appended inside our marker so the foreign hook still runs.
9
+ * - --uninstall removes only our marker block, leaves the rest.
10
+ */
11
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from "node:fs";
12
+ import { join } from "node:path";
13
+ /** Marker block delimiters. Keep stable. */
14
+ const MARKER_START = "# >>> stapes-adsa hook >>>";
15
+ const MARKER_END = "# <<< stapes-adsa hook <<<";
16
+ /** Path to the hook script we install. Runs `stapes-adsa` via npx. */
17
+ const HOOK_BODY = `#!/usr/bin/env sh
18
+ ${MARKER_START}
19
+ # This block is managed by 'stapes-adsa --init'. Do not edit.
20
+ # To remove, run 'stapes-adsa --uninstall'.
21
+ set -e
22
+ npx --no-install stapes-adsa --run --json
23
+ ${MARKER_END}
24
+ `;
25
+ /**
26
+ * Install the pre-commit hook. Idempotent.
27
+ *
28
+ * Returns the path and a status string:
29
+ * - "created": hook file did not exist, created with our block
30
+ * - "appended": hook file existed with a foreign hook, appended our block
31
+ * - "already-installed": our marker was already present, no-op
32
+ */
33
+ export function installHook(repoRoot) {
34
+ const gitDir = join(repoRoot, ".git");
35
+ if (!existsSync(gitDir)) {
36
+ throw new Error(`not a git repository: ${repoRoot}`);
37
+ }
38
+ const hooksDir = join(gitDir, "hooks");
39
+ mkdirSync(hooksDir, { recursive: true });
40
+ const hookPath = join(hooksDir, "pre-commit");
41
+ if (!existsSync(hookPath)) {
42
+ writeFileSync(hookPath, HOOK_BODY, { mode: 0o755 });
43
+ return { hookPath, status: "created" };
44
+ }
45
+ const existing = readFileSync(hookPath, "utf8");
46
+ if (existing.includes(MARKER_START)) {
47
+ return { hookPath, status: "already-installed" };
48
+ }
49
+ // Append our block inside our marker. Use a newline separator
50
+ // so the foreign hook runs first.
51
+ const next = existing.trimEnd() + "\n\n" + HOOK_BODY;
52
+ writeFileSync(hookPath, next, { mode: 0o755 });
53
+ return { hookPath, status: "appended" };
54
+ }
55
+ /**
56
+ * Remove our marker block from the pre-commit hook. If the file ends up
57
+ * empty after removal, delete it. Foreign hooks are left untouched.
58
+ */
59
+ export function uninstallHook(repoRoot) {
60
+ const hookPath = join(repoRoot, ".git", "hooks", "pre-commit");
61
+ if (!existsSync(hookPath)) {
62
+ return { hookPath, status: "not-installed" };
63
+ }
64
+ const existing = readFileSync(hookPath, "utf8");
65
+ if (!existing.includes(MARKER_START)) {
66
+ return { hookPath, status: "not-installed" };
67
+ }
68
+ // Strip our marker block (inclusive of surrounding blank lines).
69
+ const blockStart = existing.indexOf(MARKER_START);
70
+ let blockEnd = existing.indexOf(MARKER_END, blockStart);
71
+ if (blockEnd >= 0) {
72
+ blockEnd += MARKER_END.length;
73
+ }
74
+ else {
75
+ blockEnd = existing.length;
76
+ }
77
+ let remaining = existing.slice(0, blockStart) + existing.slice(blockEnd);
78
+ // Tidy up trailing blank lines but keep one newline at EOF if anything remains.
79
+ remaining = remaining.replace(/\n+$/, "");
80
+ // If only the shebang line that we wrote remains, delete the file.
81
+ // (We wrote the shebang as part of HOOK_BODY, so "only shebang" means "only ours".)
82
+ const shebangOnly = /^#!.*\n?$/.test(remaining.trimEnd()) || /^#!.*$/.test(remaining);
83
+ if (remaining.length === 0 || shebangOnly) {
84
+ unlinkSync(hookPath);
85
+ return { hookPath, status: "deleted" };
86
+ }
87
+ writeFileSync(hookPath, remaining + "\n", { mode: 0o755 });
88
+ return { hookPath, status: "removed" };
89
+ }
90
+ /** Test helper — also exported for the unit tests. */
91
+ export const __MARKER_START = MARKER_START;
92
+ export const __MARKER_END = MARKER_END;
93
+ export const __HOOK_BODY = HOOK_BODY;
94
+ //# sourceMappingURL=hook-install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-install.js","sourceRoot":"","sources":["../src/hook-install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * `withAdsaLogging` — a reference runtime helper.
3
+ *
4
+ * Wrap any async operation to get automatic `{adsa_path: "slow"}`
5
+ * logging when it does I/O, and `{adsa_path: "fast"}` logging when
6
+ * the predicate returns true without performing the operation.
7
+ *
8
+ * Usage:
9
+ *
10
+ * import { withAdsaLogging } from "stapes-adsa/lib/instrument";
11
+ *
12
+ * const result = await withAdsaLogging(
13
+ * "permission",
14
+ * async () => isAdmin(userId), // fast predicate
15
+ * async () => loadPermissions(userId) // slow path
16
+ * );
17
+ *
18
+ * // Logs: { adsa_path: "fast", adsa_area: "permission", adsa_reason: "predicate_true" }
19
+ * // or { adsa_path: "slow", adsa_area: "permission", adsa_reason: "predicate_false" }
20
+ *
21
+ * The wrapper is intentionally minimal. It exists so engineers don't have
22
+ * to remember the four-field log shape. Real-world code may use pino,
23
+ * winston, bunyan, or a custom logger — pass yours via `log`.
24
+ */
25
+ export interface LoggerLike {
26
+ info: (obj: Record<string, unknown>, msg?: string) => void;
27
+ warn?: (obj: Record<string, unknown>, msg?: string) => void;
28
+ debug?: (obj: Record<string, unknown>, msg?: string) => void;
29
+ }
30
+ export interface WithAdsaLoggingOptions<TFastResult, TSlowResult> {
31
+ area: string;
32
+ predicate: () => Promise<TFastResult> | TFastResult;
33
+ slowPath: () => Promise<TSlowResult> | TSlowResult;
34
+ log?: LoggerLike;
35
+ /** Force the slow path even if the predicate succeeds. Useful for tests. */
36
+ forceSlow?: boolean;
37
+ }
38
+ export type WithAdsaLoggingResult<TFastResult, TSlowResult> = {
39
+ path: "fast";
40
+ value: TFastResult;
41
+ } | {
42
+ path: "slow";
43
+ value: TSlowResult;
44
+ };
45
+ /**
46
+ * Route between a fast predicate and a slow path, emitting the
47
+ * ADSA log line on each invocation.
48
+ */
49
+ export declare function withAdsaLogging<TFastResult, TSlowResult>(opts: WithAdsaLoggingOptions<TFastResult, TSlowResult>): Promise<WithAdsaLoggingResult<TFastResult, TSlowResult>>;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * `withAdsaLogging` — a reference runtime helper.
3
+ *
4
+ * Wrap any async operation to get automatic `{adsa_path: "slow"}`
5
+ * logging when it does I/O, and `{adsa_path: "fast"}` logging when
6
+ * the predicate returns true without performing the operation.
7
+ *
8
+ * Usage:
9
+ *
10
+ * import { withAdsaLogging } from "stapes-adsa/lib/instrument";
11
+ *
12
+ * const result = await withAdsaLogging(
13
+ * "permission",
14
+ * async () => isAdmin(userId), // fast predicate
15
+ * async () => loadPermissions(userId) // slow path
16
+ * );
17
+ *
18
+ * // Logs: { adsa_path: "fast", adsa_area: "permission", adsa_reason: "predicate_true" }
19
+ * // or { adsa_path: "slow", adsa_area: "permission", adsa_reason: "predicate_false" }
20
+ *
21
+ * The wrapper is intentionally minimal. It exists so engineers don't have
22
+ * to remember the four-field log shape. Real-world code may use pino,
23
+ * winston, bunyan, or a custom logger — pass yours via `log`.
24
+ */
25
+ /** Minimal console-based logger used when none is supplied. */
26
+ const defaultLogger = {
27
+ info: (obj) => {
28
+ process.stdout.write(JSON.stringify(obj) + "\n");
29
+ },
30
+ };
31
+ /**
32
+ * Route between a fast predicate and a slow path, emitting the
33
+ * ADSA log line on each invocation.
34
+ */
35
+ export async function withAdsaLogging(opts) {
36
+ const log = opts.log ?? defaultLogger;
37
+ if (!opts.forceSlow) {
38
+ try {
39
+ const value = await opts.predicate();
40
+ log.info({ adsa_path: "fast", adsa_area: opts.area, adsa_reason: "predicate_true" }, "adsa fast path");
41
+ return { path: "fast", value };
42
+ }
43
+ catch (err) {
44
+ log.info({ adsa_path: "slow", adsa_area: opts.area, adsa_reason: "predicate_threw" }, "adsa slow path (predicate threw)");
45
+ const value = await opts.slowPath();
46
+ return { path: "slow", value };
47
+ }
48
+ }
49
+ log.info({ adsa_path: "slow", adsa_area: opts.area, adsa_reason: "force_slow" }, "adsa slow path");
50
+ const value = await opts.slowPath();
51
+ return { path: "slow", value };
52
+ }
53
+ //# sourceMappingURL=instrument.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrument.js","sourceRoot":"","sources":["../../src/lib/instrument.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAQH,+DAA+D;AAC/D,MAAM,aAAa,GAAe;IAChC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE;QACZ,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IACnD,CAAC;CACF,CAAC;AAeF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAsD;IAEtD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,aAAa,CAAC;IACtC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;YACrC,GAAG,CAAC,IAAI,CACN,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,gBAAgB,EAAE,EAC1E,gBAAgB,CACjB,CAAC;YACF,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QACjC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,IAAI,CACN,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,iBAAiB,EAAE,EAC3E,kCAAkC,CACnC,CAAC;YACF,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QACjC,CAAC;IACH,CAAC;IACD,GAAG,CAAC,IAAI,CACN,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,YAAY,EAAE,EACtE,gBAAgB,CACjB,CAAC;IACF,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;IACpC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AACjC,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * ADSA pattern definitions.
3
+ *
4
+ * These constants are the contract between the linter and the framework
5
+ * described in SKILL.md. Changing them is a breaking change.
6
+ *
7
+ * - MARKER_PATTERNS: explicit declarations of fast/slow on a function.
8
+ * - IO_PATTERNS: call shapes that count as "I/O" inside a fast path.
9
+ * - LOG_FIELD_NAMES: the four-field {adsa_path, adsa_area, adsa_reason?, adsa_save_pct?}
10
+ * contract. Validated by the `log-schema` check.
11
+ */
12
+ /** Function-name suffix that explicitly marks a fast path. */
13
+ export declare const FAST_NAME_REGEX: RegExp;
14
+ /** Function-name suffix that explicitly marks a slow path. */
15
+ export declare const SLOW_NAME_REGEX: RegExp;
16
+ /** JSDoc tag that explicitly marks a fast path. */
17
+ export declare const ADSA_FAST_TAG = "@adsa:fast";
18
+ /** JSDoc tag that explicitly marks a slow path. */
19
+ export declare const ADSA_SLOW_TAG = "@adsa:slow";
20
+ /** Inline single-line comment that marks a path. */
21
+ export declare const ADSA_INLINE_FAST: RegExp;
22
+ export declare const ADSA_INLINE_SLOW: RegExp;
23
+ /**
24
+ * Call shapes that count as I/O. Used by `fast-path-with-io` and `decision-router`.
25
+ * Match is against the *full* callee text (e.g. `fetch(...)`, `db.query(...)`).
26
+ */
27
+ export declare const IO_CALLEE_PATTERNS: RegExp[];
28
+ /**
29
+ * Required fields on a valid {adsa_path, ...} log call.
30
+ * Validated by the `log-schema` check.
31
+ */
32
+ export declare const LOG_REQUIRED_FIELDS: readonly ["adsa_path", "adsa_area"];
33
+ /** Optional fields. Their presence is allowed; their absence is allowed. */
34
+ export declare const LOG_OPTIONAL_FIELDS: readonly ["adsa_reason", "adsa_save_pct"];
35
+ /** Valid values for adsa_path. Anything else is a schema violation. */
36
+ export declare const ADSA_PATH_VALUES: ReadonlyArray<"fast" | "slow">;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * ADSA pattern definitions.
3
+ *
4
+ * These constants are the contract between the linter and the framework
5
+ * described in SKILL.md. Changing them is a breaking change.
6
+ *
7
+ * - MARKER_PATTERNS: explicit declarations of fast/slow on a function.
8
+ * - IO_PATTERNS: call shapes that count as "I/O" inside a fast path.
9
+ * - LOG_FIELD_NAMES: the four-field {adsa_path, adsa_area, adsa_reason?, adsa_save_pct?}
10
+ * contract. Validated by the `log-schema` check.
11
+ */
12
+ /** Function-name suffix that explicitly marks a fast path. */
13
+ export const FAST_NAME_REGEX = /(?:^|[_$])(?:Fast|fast)(?:Path|path)?(?:$|[^a-zA-Z0-9_$])/;
14
+ /** Function-name suffix that explicitly marks a slow path. */
15
+ export const SLOW_NAME_REGEX = /(?:^|[_$])(?:Slow|slow)(?:Path|path)?(?:$|[^a-zA-Z0-9_$])/;
16
+ /** JSDoc tag that explicitly marks a fast path. */
17
+ export const ADSA_FAST_TAG = "@adsa:fast";
18
+ /** JSDoc tag that explicitly marks a slow path. */
19
+ export const ADSA_SLOW_TAG = "@adsa:slow";
20
+ /** Inline single-line comment that marks a path. */
21
+ export const ADSA_INLINE_FAST = /^\s*\/\/\s*adsa:\s*fast\b/i;
22
+ export const ADSA_INLINE_SLOW = /^\s*\/\/\s*adsa:\s*slow\b/i;
23
+ /**
24
+ * Call shapes that count as I/O. Used by `fast-path-with-io` and `decision-router`.
25
+ * Match is against the *full* callee text (e.g. `fetch(...)`, `db.query(...)`).
26
+ */
27
+ export const IO_CALLEE_PATTERNS = [
28
+ // Network
29
+ /\bfetch\s*\(/,
30
+ /\baxios\s*\(/,
31
+ /\bgot\s*\(/,
32
+ /\bnode-fetch\b/,
33
+ /\bhttp\.(get|post|put|delete|patch|request)\s*\(/,
34
+ /\bhttps\.(get|post|put|delete|patch|request)\s*\(/,
35
+ // DB clients
36
+ /\bdb\.(query|execute|find|findOne|findMany|insert|update|delete|raw|select)\s*\(/i,
37
+ // ORM call shapes — `db.<table>.<verb>(`, `prisma.<model>.<verb>(`, `sql.<table>(`
38
+ /\bdb\.\w+\.(query|execute|find|findOne|findMany|findUnique|findFirst|create|update|delete|upsert|insert|aggregate|count|groupBy|raw|select)\s*\(/i,
39
+ /\bknex\s*\(/,
40
+ /\bprisma\.\w+\.(find|findUnique|findMany|findFirst|create|update|delete|upsert|insert|aggregate|count|groupBy)\s*\(/i,
41
+ /\bsql\.\w+\s*\(/i,
42
+ /\bsql\.\w+\s*\(/i,
43
+ /\bpg\.(query|connect)\s*\(/,
44
+ /\bmysql\.(query|connect)\s*\(/,
45
+ // Cache / KV
46
+ /\bredis\.(get|set|del|hget|hset|lpush|rpush|sadd|srem|zadd|zrem|eval)\s*\(/i,
47
+ /\bmemcached\.(get|set|del)\s*\(/i,
48
+ // File system
49
+ /\bfs\.(readFile|writeFile|appendFile|unlink|mkdir|rm|readdir|stat|access)\s*\(/i,
50
+ /\bfs\.promises\.(readFile|writeFile|appendFile|unlink|mkdir|rm|readdir|stat|access)\s*\(/i,
51
+ /\bfsPromises\.(readFile|writeFile|appendFile|unlink|mkdir|rm|readdir|stat|access)\s*\(/i,
52
+ // Queue / message
53
+ /\bkafka\.(produce|publish|send|consume|subscribe)\s*\(/i,
54
+ /\bsqs\.(sendMessage|receiveMessage)\s*\(/i,
55
+ /\bsns\.(publish)\s*\(/i,
56
+ /\bnats\.(publish|subscribe|request)\s*\(/i,
57
+ /\bamqp\.(publish|consume)\s*\(/i,
58
+ // External RPC
59
+ /\bgrpc\.(unary|client)\b/i,
60
+ // Generic await of any identifier that *might* be I/O (heuristic).
61
+ /\bawait\s+\w+/,
62
+ ];
63
+ /**
64
+ * Required fields on a valid {adsa_path, ...} log call.
65
+ * Validated by the `log-schema` check.
66
+ */
67
+ export const LOG_REQUIRED_FIELDS = ["adsa_path", "adsa_area"];
68
+ /** Optional fields. Their presence is allowed; their absence is allowed. */
69
+ export const LOG_OPTIONAL_FIELDS = ["adsa_reason", "adsa_save_pct"];
70
+ /** Valid values for adsa_path. Anything else is a schema violation. */
71
+ export const ADSA_PATH_VALUES = ["fast", "slow"];
72
+ //# sourceMappingURL=patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patterns.js","sourceRoot":"","sources":["../src/patterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,8DAA8D;AAC9D,MAAM,CAAC,MAAM,eAAe,GAAG,2DAA2D,CAAC;AAE3F,8DAA8D;AAC9D,MAAM,CAAC,MAAM,eAAe,GAAG,2DAA2D,CAAC;AAE3F,mDAAmD;AACnD,MAAM,CAAC,MAAM,aAAa,GAAG,YAAY,CAAC;AAE1C,mDAAmD;AACnD,MAAM,CAAC,MAAM,aAAa,GAAG,YAAY,CAAC;AAE1C,oDAAoD;AACpD,MAAM,CAAC,MAAM,gBAAgB,GAAG,4BAA4B,CAAC;AAC7D,MAAM,CAAC,MAAM,gBAAgB,GAAG,4BAA4B,CAAC;AAE7D;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAa;IAC1C,UAAU;IACV,cAAc;IACd,cAAc;IACd,YAAY;IACZ,gBAAgB;IAChB,kDAAkD;IAClD,mDAAmD;IACnD,aAAa;IACb,mFAAmF;IACnF,mFAAmF;IACnF,mJAAmJ;IACnJ,aAAa;IACb,sHAAsH;IACtH,kBAAkB;IAClB,kBAAkB;IAClB,4BAA4B;IAC5B,+BAA+B;IAC/B,aAAa;IACb,6EAA6E;IAC7E,kCAAkC;IAClC,cAAc;IACd,iFAAiF;IACjF,2FAA2F;IAC3F,yFAAyF;IACzF,kBAAkB;IAClB,yDAAyD;IACzD,2CAA2C;IAC3C,wBAAwB;IACxB,2CAA2C;IAC3C,iCAAiC;IACjC,eAAe;IACf,2BAA2B;IAC3B,mEAAmE;IACnE,eAAe;CAChB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,WAAW,EAAE,WAAW,CAAU,CAAC;AAEvE,4EAA4E;AAC5E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,aAAa,EAAE,eAAe,CAAU,CAAC;AAE7E,uEAAuE;AACvE,MAAM,CAAC,MAAM,gBAAgB,GAAmC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Runner — orchestrates check execution and produces the v1 --json shape.
3
+ *
4
+ * Inputs:
5
+ * - root directory (or list of files)
6
+ * - list of check names to run (default = all enabled)
7
+ * - whether --strict is on (warnings become blocks)
8
+ *
9
+ * Output:
10
+ * RunReport — see SPEC.md invariant #5 for the JSON shape.
11
+ */
12
+ import { basename } from "node:path";
13
+ import type { Finding } from "./types.js";
14
+ /** Stable JSON shape per SPEC.md invariant #5. */
15
+ export interface RunReport {
16
+ startedAt: string;
17
+ tool: string;
18
+ version: string;
19
+ exitCode: 0 | 1 | 2;
20
+ checkCount: number;
21
+ failed: number;
22
+ warned: number;
23
+ findings: Finding[];
24
+ }
25
+ /** Runner options. */
26
+ export interface RunnerOptions {
27
+ root: string;
28
+ checks?: string[];
29
+ strict?: boolean;
30
+ files?: string[];
31
+ }
32
+ export declare function run(opts: RunnerOptions): RunReport;
33
+ /** Pretty-print the human-readable summary (no JSON). */
34
+ export declare function formatHuman(report: RunReport): string;
35
+ export { basename };