pi-lens 2.2.9 → 3.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.
- package/CHANGELOG.md +198 -0
- package/README.md +709 -519
- package/clients/__tests__/file-time.test.js +216 -0
- package/clients/__tests__/file-time.test.ts +276 -0
- package/clients/__tests__/format-service.test.js +245 -0
- package/clients/__tests__/format-service.test.ts +339 -0
- package/clients/__tests__/formatters.test.js +271 -0
- package/clients/__tests__/formatters.test.ts +401 -0
- package/clients/amain-types.js +164 -0
- package/clients/amain-types.ts +165 -0
- package/clients/architect-client.js +56 -12
- package/clients/architect-client.ts +81 -16
- package/clients/ast-grep-client.js +2 -2
- package/clients/ast-grep-client.ts +14 -39
- package/clients/ast-grep-parser.ts +1 -1
- package/clients/ast-grep-rule-manager.js +8 -0
- package/clients/ast-grep-rule-manager.ts +10 -1
- package/clients/ast-grep-types.js +9 -0
- package/clients/ast-grep-types.ts +106 -0
- package/clients/auto-loop.js +10 -0
- package/clients/auto-loop.ts +14 -1
- package/clients/biome-client.js +81 -19
- package/clients/biome-client.ts +103 -22
- package/clients/bus/bus.js +191 -0
- package/clients/bus/bus.ts +251 -0
- package/clients/bus/events.js +214 -0
- package/clients/bus/events.ts +279 -0
- package/clients/bus/index.js +8 -0
- package/clients/bus/index.ts +9 -0
- package/clients/bus/integration.js +158 -0
- package/clients/bus/integration.ts +214 -0
- package/clients/complexity-client.js +13 -7
- package/clients/complexity-client.ts +13 -7
- package/clients/config-validator.js +465 -0
- package/clients/config-validator.ts +558 -0
- package/clients/dependency-checker.js +4 -10
- package/clients/dependency-checker.ts +4 -10
- package/clients/dispatch/__tests__/autofix-integration.test.js +245 -0
- package/clients/dispatch/__tests__/autofix-integration.test.ts +300 -0
- package/clients/dispatch/__tests__/runner-registration.test.js +236 -0
- package/clients/dispatch/__tests__/runner-registration.test.ts +282 -0
- package/clients/dispatch/bus-dispatcher.js +177 -0
- package/clients/dispatch/bus-dispatcher.ts +251 -0
- package/clients/dispatch/dispatcher.edge.test.js +82 -0
- package/clients/dispatch/dispatcher.edge.test.ts +100 -0
- package/clients/dispatch/dispatcher.format.test.js +46 -0
- package/clients/dispatch/dispatcher.format.test.ts +58 -0
- package/clients/dispatch/dispatcher.inline.test.js +74 -0
- package/clients/dispatch/dispatcher.inline.test.ts +93 -0
- package/clients/dispatch/dispatcher.js +19 -53
- package/clients/dispatch/dispatcher.ts +20 -67
- package/clients/dispatch/plan.js +9 -4
- package/clients/dispatch/plan.ts +9 -4
- package/clients/dispatch/runners/architect.js +21 -7
- package/clients/dispatch/runners/architect.test.js +138 -0
- package/clients/dispatch/runners/architect.test.ts +162 -0
- package/clients/dispatch/runners/architect.ts +22 -7
- package/clients/dispatch/runners/ast-grep-napi.js +462 -0
- package/clients/dispatch/runners/ast-grep-napi.test.js +111 -0
- package/clients/dispatch/runners/ast-grep-napi.test.ts +133 -0
- package/clients/dispatch/runners/ast-grep-napi.ts +506 -0
- package/clients/dispatch/runners/ast-grep.js +62 -19
- package/clients/dispatch/runners/ast-grep.ts +70 -18
- package/clients/dispatch/runners/biome.js +29 -53
- package/clients/dispatch/runners/biome.ts +29 -63
- package/clients/dispatch/runners/config-validation.js +67 -0
- package/clients/dispatch/runners/config-validation.ts +82 -0
- package/clients/dispatch/runners/go-vet.js +4 -28
- package/clients/dispatch/runners/go-vet.ts +4 -32
- package/clients/dispatch/runners/index.js +30 -10
- package/clients/dispatch/runners/index.ts +30 -10
- package/clients/dispatch/runners/oxlint.js +141 -0
- package/clients/dispatch/runners/oxlint.test.js +230 -0
- package/clients/dispatch/runners/oxlint.test.ts +303 -0
- package/clients/dispatch/runners/oxlint.ts +175 -0
- package/clients/dispatch/runners/pyright.js +40 -70
- package/clients/dispatch/runners/pyright.test.js +16 -2
- package/clients/dispatch/runners/pyright.test.ts +14 -2
- package/clients/dispatch/runners/pyright.ts +48 -91
- package/clients/dispatch/runners/python-slop.js +97 -0
- package/clients/dispatch/runners/python-slop.test.js +203 -0
- package/clients/dispatch/runners/python-slop.test.ts +298 -0
- package/clients/dispatch/runners/python-slop.ts +124 -0
- package/clients/dispatch/runners/ruff.js +18 -71
- package/clients/dispatch/runners/ruff.ts +19 -79
- package/clients/dispatch/runners/rust-clippy.js +28 -32
- package/clients/dispatch/runners/rust-clippy.ts +29 -31
- package/clients/dispatch/runners/scan_codebase.test.js +89 -0
- package/clients/dispatch/runners/scan_codebase.test.ts +105 -0
- package/clients/dispatch/runners/shellcheck.js +147 -0
- package/clients/dispatch/runners/shellcheck.test.js +98 -0
- package/clients/dispatch/runners/shellcheck.test.ts +129 -0
- package/clients/dispatch/runners/shellcheck.ts +188 -0
- package/clients/dispatch/runners/similarity.js +230 -0
- package/clients/dispatch/runners/similarity.ts +339 -0
- package/clients/dispatch/runners/spellcheck.js +106 -0
- package/clients/dispatch/runners/spellcheck.test.js +158 -0
- package/clients/dispatch/runners/spellcheck.test.ts +214 -0
- package/clients/dispatch/runners/spellcheck.ts +136 -0
- package/clients/dispatch/runners/tree-sitter.js +107 -0
- package/clients/dispatch/runners/tree-sitter.ts +135 -0
- package/clients/dispatch/runners/ts-lsp.js +104 -33
- package/clients/dispatch/runners/ts-lsp.ts +120 -38
- package/clients/dispatch/runners/ts-slop.js +113 -0
- package/clients/dispatch/runners/ts-slop.test.js +180 -0
- package/clients/dispatch/runners/ts-slop.test.ts +230 -0
- package/clients/dispatch/runners/ts-slop.ts +142 -0
- package/clients/dispatch/runners/utils/diagnostic-parsers.js +134 -0
- package/clients/dispatch/runners/utils/diagnostic-parsers.ts +186 -0
- package/clients/dispatch/runners/utils/runner-helpers.js +115 -0
- package/clients/dispatch/runners/utils/runner-helpers.ts +167 -0
- package/clients/dispatch/runners/utils.js +2 -4
- package/clients/dispatch/runners/utils.ts +2 -4
- package/clients/dispatch/types.ts +1 -1
- package/clients/dispatch/utils/format-utils.js +49 -0
- package/clients/dispatch/utils/format-utils.ts +60 -0
- package/clients/dogfood.test.js +201 -0
- package/clients/dogfood.test.ts +269 -0
- package/clients/file-time.js +152 -0
- package/clients/file-time.ts +208 -0
- package/clients/file-utils.js +40 -0
- package/clients/file-utils.ts +44 -0
- package/clients/fix-scanners.js +10 -20
- package/clients/fix-scanners.ts +10 -22
- package/clients/format-service.js +172 -0
- package/clients/format-service.ts +254 -0
- package/clients/formatters.js +435 -0
- package/clients/formatters.ts +508 -0
- package/clients/go-client.js +5 -14
- package/clients/go-client.ts +5 -13
- package/clients/installer/index.js +356 -0
- package/clients/installer/index.ts +426 -0
- package/clients/jscpd-client.js +11 -9
- package/clients/jscpd-client.ts +12 -8
- package/clients/knip-client.js +3 -7
- package/clients/knip-client.ts +3 -6
- package/clients/lsp/__tests__/client.test.js +325 -0
- package/clients/lsp/__tests__/client.test.ts +434 -0
- package/clients/lsp/__tests__/config.test.js +166 -0
- package/clients/lsp/__tests__/config.test.ts +209 -0
- package/clients/lsp/__tests__/error-recovery.test.js +213 -0
- package/clients/lsp/__tests__/error-recovery.test.ts +279 -0
- package/clients/lsp/__tests__/integration.test.js +127 -0
- package/clients/lsp/__tests__/integration.test.ts +160 -0
- package/clients/lsp/__tests__/launch.test.js +260 -0
- package/clients/lsp/__tests__/launch.test.ts +329 -0
- package/clients/lsp/__tests__/server.test.js +259 -0
- package/clients/lsp/__tests__/server.test.ts +332 -0
- package/clients/lsp/__tests__/service.test.js +417 -0
- package/clients/lsp/__tests__/service.test.ts +499 -0
- package/clients/lsp/client.js +235 -0
- package/clients/lsp/client.ts +328 -0
- package/clients/lsp/config.js +115 -0
- package/clients/lsp/config.ts +149 -0
- package/clients/lsp/index.js +222 -0
- package/clients/lsp/index.ts +280 -0
- package/clients/lsp/installer/index.js +391 -0
- package/clients/lsp/interactive-install.js +210 -0
- package/clients/lsp/interactive-install.ts +251 -0
- package/clients/lsp/language.js +170 -0
- package/clients/lsp/language.ts +216 -0
- package/clients/lsp/launch.js +174 -0
- package/clients/lsp/launch.ts +240 -0
- package/clients/lsp/lsp/launch.js +116 -0
- package/clients/lsp/lsp/server.js +532 -0
- package/clients/lsp/lsp-index.js +10 -0
- package/clients/lsp/lsp-index.ts +11 -0
- package/clients/lsp/path-utils.js +48 -0
- package/clients/lsp/path-utils.ts +52 -0
- package/clients/lsp/server.js +615 -0
- package/clients/lsp/server.ts +800 -0
- package/clients/lsp/test-py-spawn/requirements.txt +1 -0
- package/clients/lsp/test-py-spawn/test.py +3 -0
- package/clients/lsp/test-py-svc/requirements.txt +1 -0
- package/clients/lsp/test-py-svc/test.py +3 -0
- package/clients/lsp/test-python-project/requirements.txt +1 -0
- package/clients/lsp/test-python-project/test.py +5 -0
- package/clients/metrics-history.js +2 -2
- package/clients/metrics-history.ts +2 -2
- package/clients/production-readiness.js +522 -0
- package/clients/production-readiness.ts +556 -0
- package/clients/project-index.js +255 -0
- package/clients/project-index.ts +383 -0
- package/clients/project-metadata.js +531 -0
- package/clients/project-metadata.ts +624 -0
- package/clients/ruff-client.js +56 -16
- package/clients/ruff-client.ts +72 -15
- package/clients/runner-tracker.js +152 -0
- package/clients/runner-tracker.ts +213 -0
- package/clients/rust-client.js +4 -11
- package/clients/rust-client.ts +5 -11
- package/clients/safe-spawn.js +96 -0
- package/clients/safe-spawn.ts +128 -0
- package/clients/scan-architectural-debt.js +3 -6
- package/clients/scan-architectural-debt.ts +3 -6
- package/clients/scan-utils.js +5 -20
- package/clients/scan-utils.ts +5 -29
- package/clients/secrets-scanner.js +3 -17
- package/clients/secrets-scanner.ts +4 -20
- package/clients/services/__tests__/effect-integration.test.js +86 -0
- package/clients/services/__tests__/effect-integration.test.ts +111 -0
- package/clients/services/effect-integration.js +194 -0
- package/clients/services/effect-integration.ts +268 -0
- package/clients/services/index.js +7 -0
- package/clients/services/index.ts +8 -0
- package/clients/services/runner-service.js +105 -0
- package/clients/services/runner-service.ts +179 -0
- package/clients/sg-runner.js +87 -13
- package/clients/sg-runner.ts +97 -13
- package/clients/state-matrix.js +160 -0
- package/clients/state-matrix.ts +202 -0
- package/clients/subprocess-client.js +10 -9
- package/clients/subprocess-client.ts +10 -8
- package/clients/test-runner-client.js +3 -7
- package/clients/test-runner-client.ts +3 -6
- package/clients/tool-availability.js +4 -10
- package/clients/tool-availability.ts +4 -9
- package/clients/tree-sitter-client.js +564 -0
- package/clients/tree-sitter-client.ts +797 -0
- package/clients/tree-sitter-query-loader.js +355 -0
- package/clients/tree-sitter-query-loader.ts +425 -0
- package/clients/type-coverage-client.js +3 -7
- package/clients/type-coverage-client.ts +3 -6
- package/clients/typescript-client.codefix.test.js +157 -0
- package/clients/typescript-client.codefix.test.ts +186 -0
- package/clients/typescript-client.js +43 -0
- package/clients/typescript-client.ts +98 -0
- package/commands/booboo.js +799 -219
- package/commands/booboo.ts +1004 -225
- package/commands/clients/ast-grep-client.js +250 -0
- package/commands/clients/ast-grep-parser.js +86 -0
- package/commands/clients/ast-grep-rule-manager.js +91 -0
- package/commands/clients/ast-grep-types.js +9 -0
- package/commands/clients/biome-client.js +380 -0
- package/commands/clients/complexity-client.js +667 -0
- package/commands/clients/file-kinds.js +177 -0
- package/commands/clients/file-utils.js +40 -0
- package/commands/clients/jscpd-client.js +169 -0
- package/commands/clients/knip-client.js +211 -0
- package/commands/clients/ruff-client.js +297 -0
- package/commands/clients/safe-spawn.js +88 -0
- package/commands/clients/scan-utils.js +83 -0
- package/commands/clients/sg-runner.js +190 -0
- package/commands/clients/types.js +11 -0
- package/commands/clients/typescript-client.js +505 -0
- package/commands/fix-from-booboo.js +398 -0
- package/commands/fix-from-booboo.ts +485 -0
- package/commands/fix-simplified.js +618 -0
- package/commands/fix-simplified.ts +768 -0
- package/commands/rate.js +10 -14
- package/commands/rate.ts +9 -16
- package/default-architect.yaml +59 -15
- package/index.ts +342 -429
- package/package.json +16 -3
- package/rules/ast-grep-rules/rules/empty-catch.yml +38 -13
- package/rules/ast-grep-rules/rules/no-array-constructor.yml +1 -0
- package/rules/ast-grep-rules/rules/no-debugger.yml +2 -0
- package/rules/python-slop-rules/.sgconfig.yml +4 -0
- package/rules/python-slop-rules/rules/slop-rules.yml +647 -0
- package/rules/tree-sitter-queries/python/bare-except.yml +54 -0
- package/rules/tree-sitter-queries/python/eval-exec.yml +50 -0
- package/rules/tree-sitter-queries/python/is-vs-equals.yml +60 -0
- package/rules/tree-sitter-queries/python/mutable-default-arg.yml +57 -0
- package/rules/tree-sitter-queries/python/unreachable-except.yml +60 -0
- package/rules/tree-sitter-queries/python/wildcard-import.yml +46 -0
- package/rules/tree-sitter-queries/tsx/dangerously-set-inner-html.yml +63 -0
- package/rules/tree-sitter-queries/typescript/await-in-loop.yml +56 -0
- package/rules/tree-sitter-queries/typescript/console-statement.yml +47 -0
- package/rules/tree-sitter-queries/typescript/debugger.yml +47 -0
- package/rules/tree-sitter-queries/typescript/deep-nesting.yml +117 -0
- package/rules/tree-sitter-queries/typescript/deep-promise-chain.yml +73 -0
- package/rules/tree-sitter-queries/typescript/empty-catch.yml +64 -0
- package/rules/tree-sitter-queries/typescript/eval.yml +48 -0
- package/rules/tree-sitter-queries/typescript/hardcoded-secrets.yml +78 -0
- package/rules/tree-sitter-queries/typescript/long-parameter-list.yml +62 -0
- package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +49 -0
- package/rules/tree-sitter-queries/typescript/nested-ternary.yml +45 -0
- package/rules/ts-slop-rules/.sgconfig.yml +4 -0
- package/rules/ts-slop-rules/rules/in-correct-optional-input-type.yml +10 -0
- package/rules/ts-slop-rules/rules/jwt-no-verify.yml +13 -0
- package/rules/ts-slop-rules/rules/no-architecture-violation.yml +10 -0
- package/rules/ts-slop-rules/rules/no-case-declarations.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dangerously-set-inner-html.yml +10 -0
- package/rules/ts-slop-rules/rules/no-debugger.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dupe-args.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dupe-class-members.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dupe-keys.yml +10 -0
- package/rules/ts-slop-rules/rules/no-eval.yml +13 -0
- package/rules/ts-slop-rules/rules/no-hardcoded-secrets.yml +12 -0
- package/rules/ts-slop-rules/rules/no-implied-eval.yml +12 -0
- package/rules/ts-slop-rules/rules/no-inner-html.yml +13 -0
- package/rules/ts-slop-rules/rules/no-javascript-url.yml +10 -0
- package/rules/ts-slop-rules/rules/no-mutable-default.yml +10 -0
- package/rules/ts-slop-rules/rules/no-nested-links.yml +12 -0
- package/rules/ts-slop-rules/rules/no-new-symbol.yml +10 -0
- package/rules/ts-slop-rules/rules/no-new-wrappers.yml +13 -0
- package/rules/ts-slop-rules/rules/no-open-redirect.yml +16 -0
- package/rules/ts-slop-rules/rules/slop-rules.yml +455 -0
- package/rules/ts-slop-rules/rules/weak-rsa-key.yml +12 -0
- package/skills/ast-grep/SKILL.md +182 -0
- package/clients/dispatch/runners/secrets.js +0 -109
- package/commands/fix.js +0 -244
- package/commands/fix.ts +0 -373
- package/rules/ast-grep-rules/rules/no-lonely-if.yml +0 -13
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effect Integration Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for Effect-TS concurrent runner execution.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
8
|
+
import type {
|
|
9
|
+
DispatchContext,
|
|
10
|
+
RunnerDefinition,
|
|
11
|
+
RunnerGroup,
|
|
12
|
+
RunnerResult,
|
|
13
|
+
} from "../../dispatch/types.js";
|
|
14
|
+
import {
|
|
15
|
+
clearRunnerRegistry,
|
|
16
|
+
getRunner,
|
|
17
|
+
listRunners,
|
|
18
|
+
registerRunner,
|
|
19
|
+
} from "../../dispatch/dispatcher.js";
|
|
20
|
+
import {
|
|
21
|
+
dispatchLintWithEffect,
|
|
22
|
+
dispatchWithEffect,
|
|
23
|
+
type EffectDispatchResult,
|
|
24
|
+
} from "../effect-integration.js";
|
|
25
|
+
|
|
26
|
+
describe("Effect Integration", () => {
|
|
27
|
+
beforeEach(async () => {
|
|
28
|
+
clearRunnerRegistry();
|
|
29
|
+
// Register a simple test runner
|
|
30
|
+
const testRunner: RunnerDefinition = {
|
|
31
|
+
id: "test-runner",
|
|
32
|
+
appliesTo: ["jsts"],
|
|
33
|
+
priority: 10,
|
|
34
|
+
enabledByDefault: true,
|
|
35
|
+
async run(ctx) {
|
|
36
|
+
return {
|
|
37
|
+
status: "succeeded",
|
|
38
|
+
diagnostics: [{
|
|
39
|
+
id: "test:1",
|
|
40
|
+
message: "Test diagnostic",
|
|
41
|
+
filePath: ctx.filePath,
|
|
42
|
+
severity: "info",
|
|
43
|
+
semantic: "silent",
|
|
44
|
+
tool: "test-runner",
|
|
45
|
+
}],
|
|
46
|
+
semantic: "none",
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
registerRunner(testRunner);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should have runners registered", () => {
|
|
54
|
+
const runners = listRunners();
|
|
55
|
+
expect(runners.length).toBeGreaterThan(0);
|
|
56
|
+
expect(runners.some(r => r.id === "test-runner")).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should run effect integration with real runners", async () => {
|
|
60
|
+
const ctx: DispatchContext = {
|
|
61
|
+
filePath: "test.ts",
|
|
62
|
+
cwd: "/test",
|
|
63
|
+
kind: "jsts",
|
|
64
|
+
pi: {
|
|
65
|
+
getFlag: vi.fn(() => false),
|
|
66
|
+
},
|
|
67
|
+
autofix: true,
|
|
68
|
+
deltaMode: false,
|
|
69
|
+
baselines: new Map(),
|
|
70
|
+
hasTool: vi.fn(() => Promise.resolve(false)),
|
|
71
|
+
log: vi.fn(),
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Get actual runners for jsts
|
|
75
|
+
const { getRunnersForKind } = await import("../../dispatch/dispatcher.js");
|
|
76
|
+
const runners = getRunnersForKind("jsts");
|
|
77
|
+
|
|
78
|
+
const group: RunnerGroup = {
|
|
79
|
+
runnerIds: runners.slice(0, 2).map(r => r.id), // Use first 2 runners
|
|
80
|
+
mode: "all",
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const result = await dispatchWithEffect(ctx, [group]);
|
|
84
|
+
|
|
85
|
+
// Just verify it doesn't crash and returns valid result
|
|
86
|
+
expect(result).toBeDefined();
|
|
87
|
+
expect(typeof result.durationMs).toBe("number");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("should handle --lens-effect flag path", async () => {
|
|
91
|
+
const mockPi = {
|
|
92
|
+
getFlag: vi.fn((flag: string) => flag === "lens-effect"),
|
|
93
|
+
readFile: vi.fn(),
|
|
94
|
+
writeFile: vi.fn(),
|
|
95
|
+
editFile: vi.fn(),
|
|
96
|
+
bash: vi.fn(),
|
|
97
|
+
ui: {
|
|
98
|
+
notify: vi.fn(),
|
|
99
|
+
progress: vi.fn(),
|
|
100
|
+
prompt: vi.fn(),
|
|
101
|
+
},
|
|
102
|
+
llm: {
|
|
103
|
+
stream: vi.fn(),
|
|
104
|
+
createMessage: vi.fn(),
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const output = await dispatchLintWithEffect("test.ts", "/test", mockPi);
|
|
109
|
+
expect(typeof output).toBe("string");
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effect-TS Integration for pi-lens Dispatch
|
|
3
|
+
*
|
|
4
|
+
* Bridges the Effect service layer with the existing dispatch system.
|
|
5
|
+
*
|
|
6
|
+
* This provides:
|
|
7
|
+
* - Concurrent runner execution with Effect.all
|
|
8
|
+
* - Timeout handling for slow runners
|
|
9
|
+
* - Graceful error recovery
|
|
10
|
+
* - Bus event integration
|
|
11
|
+
*/
|
|
12
|
+
import { runRunnersConcurrent, executeEffect, } from "./runner-service.js";
|
|
13
|
+
import { DiagnosticFound, RunnerStarted, RunnerCompleted, FileModified, } from "../bus/events.js";
|
|
14
|
+
import { formatDiagnostics } from "../dispatch/utils/format-utils.js";
|
|
15
|
+
// Import runners to register them in the dispatcher
|
|
16
|
+
import "../dispatch/runners/index.js";
|
|
17
|
+
// --- Core Functions ---
|
|
18
|
+
/**
|
|
19
|
+
* Run all runners in a group concurrently using Effect
|
|
20
|
+
*/
|
|
21
|
+
async function runGroupConcurrent(ctx, group) {
|
|
22
|
+
const { getRunner, getRunnersForKind } = await import("../dispatch/dispatcher.js");
|
|
23
|
+
const startTime = Date.now();
|
|
24
|
+
// Get runner definitions
|
|
25
|
+
const runnerDefs = group.filterKinds
|
|
26
|
+
? group.runnerIds
|
|
27
|
+
.filter((id) => {
|
|
28
|
+
const runner = getRunner(id);
|
|
29
|
+
return runner && ctx.kind && group.filterKinds?.includes(ctx.kind);
|
|
30
|
+
})
|
|
31
|
+
: group.runnerIds;
|
|
32
|
+
const runners = runnerDefs
|
|
33
|
+
.map((id) => getRunner(id))
|
|
34
|
+
.filter((r) => r !== undefined)
|
|
35
|
+
.filter((r) => (r.when ? r.when(ctx) : true));
|
|
36
|
+
if (runners.length === 0) {
|
|
37
|
+
return { results: [], diagnostics: [] };
|
|
38
|
+
}
|
|
39
|
+
// Build the single runner execution function
|
|
40
|
+
const runSingle = async (filePath, runnerId) => {
|
|
41
|
+
const runner = getRunner(runnerId);
|
|
42
|
+
if (!runner) {
|
|
43
|
+
return { diagnostics: [], durationMs: 0 };
|
|
44
|
+
}
|
|
45
|
+
// Publish started event
|
|
46
|
+
RunnerStarted.publish({
|
|
47
|
+
runnerId,
|
|
48
|
+
filePath,
|
|
49
|
+
timestamp: Date.now(),
|
|
50
|
+
});
|
|
51
|
+
const runnerStart = Date.now();
|
|
52
|
+
let status = "completed";
|
|
53
|
+
let diagnostics = [];
|
|
54
|
+
try {
|
|
55
|
+
const result = await runner.run(ctx);
|
|
56
|
+
diagnostics = result.diagnostics.map((d) => ({
|
|
57
|
+
id: d.id,
|
|
58
|
+
message: d.message,
|
|
59
|
+
filePath: ctx.filePath,
|
|
60
|
+
line: d.line,
|
|
61
|
+
column: d.column,
|
|
62
|
+
severity: d.severity === "error" ? "error" : d.severity === "warning" ? "warning" : "info",
|
|
63
|
+
semantic: d.semantic ?? result.semantic ?? "warning",
|
|
64
|
+
tool: runnerId,
|
|
65
|
+
rule: d.rule,
|
|
66
|
+
fixable: d.fixable,
|
|
67
|
+
fixSuggestion: d.fixSuggestion,
|
|
68
|
+
}));
|
|
69
|
+
// Publish diagnostic found event
|
|
70
|
+
if (diagnostics.length > 0) {
|
|
71
|
+
DiagnosticFound.publish({
|
|
72
|
+
runnerId,
|
|
73
|
+
filePath: ctx.filePath,
|
|
74
|
+
diagnostics,
|
|
75
|
+
durationMs: Date.now() - runnerStart,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
diagnostics: diagnostics.map((d) => ({
|
|
80
|
+
id: d.id,
|
|
81
|
+
message: d.message,
|
|
82
|
+
severity: d.severity,
|
|
83
|
+
semantic: d.semantic,
|
|
84
|
+
})),
|
|
85
|
+
durationMs: Date.now() - runnerStart,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
status = "failed";
|
|
90
|
+
return {
|
|
91
|
+
diagnostics: [],
|
|
92
|
+
durationMs: Date.now() - runnerStart,
|
|
93
|
+
error: String(err),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
// Publish completed event
|
|
98
|
+
RunnerCompleted.publish({
|
|
99
|
+
runnerId,
|
|
100
|
+
filePath: ctx.filePath,
|
|
101
|
+
status,
|
|
102
|
+
durationMs: Date.now() - runnerStart,
|
|
103
|
+
diagnosticCount: diagnostics.length,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
// Run all runners concurrently using Effect
|
|
108
|
+
const runnerIds = runners.map((r) => r.id);
|
|
109
|
+
const concurrentResults = await executeEffect(runRunnersConcurrent(ctx.filePath, runnerIds, runSingle, 30000));
|
|
110
|
+
// Collect all diagnostics
|
|
111
|
+
const allDiagnostics = [];
|
|
112
|
+
for (const result of concurrentResults) {
|
|
113
|
+
if (result.status === "success") {
|
|
114
|
+
allDiagnostics.push(...result.diagnostics.map((d) => ({
|
|
115
|
+
id: d.id,
|
|
116
|
+
message: d.message,
|
|
117
|
+
filePath: ctx.filePath,
|
|
118
|
+
severity: d.severity,
|
|
119
|
+
semantic: d.semantic ?? group.semantic ?? "warning",
|
|
120
|
+
tool: result.runnerId,
|
|
121
|
+
})));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
results: concurrentResults,
|
|
126
|
+
diagnostics: allDiagnostics,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// --- Main Dispatch Function ---
|
|
130
|
+
export async function dispatchWithEffect(ctx, groups) {
|
|
131
|
+
const startTime = Date.now();
|
|
132
|
+
const allDiagnostics = [];
|
|
133
|
+
const allRunnerResults = [];
|
|
134
|
+
let stopped = false;
|
|
135
|
+
// Publish file modified event
|
|
136
|
+
FileModified.publish({
|
|
137
|
+
filePath: ctx.filePath,
|
|
138
|
+
changeType: "external",
|
|
139
|
+
});
|
|
140
|
+
for (const group of groups) {
|
|
141
|
+
if (stopped && ctx.pi.getFlag("stop-on-error")) {
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
const { results, diagnostics } = await runGroupConcurrent(ctx, group);
|
|
145
|
+
allDiagnostics.push(...diagnostics);
|
|
146
|
+
allRunnerResults.push(...results);
|
|
147
|
+
// Check for blockers
|
|
148
|
+
const semantic = group.semantic ?? "warning";
|
|
149
|
+
if (semantic === "blocking" && diagnostics.length > 0) {
|
|
150
|
+
stopped = true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Categorize results
|
|
154
|
+
const blockers = allDiagnostics.filter((d) => d.semantic === "blocking");
|
|
155
|
+
const warnings = allDiagnostics.filter((d) => d.semantic === "warning" || d.semantic === "none");
|
|
156
|
+
const fixedItems = allDiagnostics.filter((d) => d.semantic === "fixed");
|
|
157
|
+
const silentItems = allDiagnostics.filter((d) => d.semantic === "silent");
|
|
158
|
+
// Format output
|
|
159
|
+
let output = formatDiagnostics(blockers, "blocking");
|
|
160
|
+
output += formatDiagnostics(fixedItems, "fixed");
|
|
161
|
+
const durationMs = Date.now() - startTime;
|
|
162
|
+
// Log performance info in debug mode
|
|
163
|
+
if (ctx.pi.getFlag("lens-bus-debug")) {
|
|
164
|
+
console.error(`[effect] Total duration: ${durationMs}ms`);
|
|
165
|
+
for (const r of allRunnerResults) {
|
|
166
|
+
console.error(`[effect] ${r.runnerId}: ${r.status} (${r.durationMs}ms)`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
diagnostics: allDiagnostics,
|
|
171
|
+
blockers,
|
|
172
|
+
warnings,
|
|
173
|
+
fixed: fixedItems,
|
|
174
|
+
silent: silentItems,
|
|
175
|
+
output,
|
|
176
|
+
hasBlockers: blockers.length > 0,
|
|
177
|
+
durationMs,
|
|
178
|
+
runnerResults: allRunnerResults,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
// --- Simple Integration Helper ---
|
|
182
|
+
export async function dispatchLintWithEffect(filePath, cwd, pi) {
|
|
183
|
+
const { createDispatchContext } = await import("../dispatch/dispatcher.js");
|
|
184
|
+
const { TOOL_PLANS } = await import("../dispatch/plan.js");
|
|
185
|
+
const ctx = createDispatchContext(filePath, cwd, pi);
|
|
186
|
+
const kind = ctx.kind;
|
|
187
|
+
if (!kind)
|
|
188
|
+
return "";
|
|
189
|
+
const plan = TOOL_PLANS[kind];
|
|
190
|
+
if (!plan)
|
|
191
|
+
return "";
|
|
192
|
+
const result = await dispatchWithEffect(ctx, plan.groups);
|
|
193
|
+
return result.output;
|
|
194
|
+
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effect-TS Integration for pi-lens Dispatch
|
|
3
|
+
*
|
|
4
|
+
* Bridges the Effect service layer with the existing dispatch system.
|
|
5
|
+
*
|
|
6
|
+
* This provides:
|
|
7
|
+
* - Concurrent runner execution with Effect.all
|
|
8
|
+
* - Timeout handling for slow runners
|
|
9
|
+
* - Graceful error recovery
|
|
10
|
+
* - Bus event integration
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
runRunnersConcurrent,
|
|
15
|
+
executeEffect,
|
|
16
|
+
formatError,
|
|
17
|
+
type RunnerResult,
|
|
18
|
+
type ConcurrentRunnerResult,
|
|
19
|
+
} from "./runner-service.js";
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
DiagnosticFound,
|
|
23
|
+
RunnerStarted,
|
|
24
|
+
RunnerCompleted,
|
|
25
|
+
FileModified,
|
|
26
|
+
type Diagnostic,
|
|
27
|
+
} from "../bus/events.js";
|
|
28
|
+
import { formatDiagnostic, formatDiagnostics } from "../dispatch/utils/format-utils.js";
|
|
29
|
+
// Import runners to register them in the dispatcher
|
|
30
|
+
import "../dispatch/runners/index.js";
|
|
31
|
+
|
|
32
|
+
import type { DispatchContext, RunnerGroup } from "../dispatch/types.js";
|
|
33
|
+
|
|
34
|
+
// --- Enhanced Result Type ---
|
|
35
|
+
|
|
36
|
+
export interface EffectDispatchResult {
|
|
37
|
+
diagnostics: Diagnostic[];
|
|
38
|
+
blockers: Diagnostic[];
|
|
39
|
+
warnings: Diagnostic[];
|
|
40
|
+
fixed: Diagnostic[];
|
|
41
|
+
silent: Diagnostic[];
|
|
42
|
+
output: string;
|
|
43
|
+
hasBlockers: boolean;
|
|
44
|
+
durationMs: number;
|
|
45
|
+
runnerResults: ConcurrentRunnerResult[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// --- Core Functions ---
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Run all runners in a group concurrently using Effect
|
|
52
|
+
*/
|
|
53
|
+
async function runGroupConcurrent(
|
|
54
|
+
ctx: DispatchContext,
|
|
55
|
+
group: RunnerGroup,
|
|
56
|
+
): Promise<{ results: ConcurrentRunnerResult[]; diagnostics: Diagnostic[] }> {
|
|
57
|
+
const { getRunner, getRunnersForKind } = await import("../dispatch/dispatcher.js");
|
|
58
|
+
const startTime = Date.now();
|
|
59
|
+
|
|
60
|
+
// Get runner definitions
|
|
61
|
+
const runnerDefs = group.filterKinds
|
|
62
|
+
? group.runnerIds
|
|
63
|
+
.filter((id) => {
|
|
64
|
+
const runner = getRunner(id);
|
|
65
|
+
return runner && ctx.kind && group.filterKinds?.includes(ctx.kind);
|
|
66
|
+
})
|
|
67
|
+
: group.runnerIds;
|
|
68
|
+
|
|
69
|
+
const runners = runnerDefs
|
|
70
|
+
.map((id) => getRunner(id))
|
|
71
|
+
.filter((r): r is NonNullable<typeof r> => r !== undefined)
|
|
72
|
+
.filter((r) => (r.when ? r.when(ctx) : true));
|
|
73
|
+
|
|
74
|
+
if (runners.length === 0) {
|
|
75
|
+
return { results: [], diagnostics: [] };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Build the single runner execution function
|
|
79
|
+
const runSingle = async (filePath: string, runnerId: string): Promise<RunnerResult> => {
|
|
80
|
+
const runner = getRunner(runnerId);
|
|
81
|
+
if (!runner) {
|
|
82
|
+
return { diagnostics: [], durationMs: 0 };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Publish started event
|
|
86
|
+
RunnerStarted.publish({
|
|
87
|
+
runnerId,
|
|
88
|
+
filePath,
|
|
89
|
+
timestamp: Date.now(),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const runnerStart = Date.now();
|
|
93
|
+
let status: "completed" | "failed" = "completed";
|
|
94
|
+
let diagnostics: Diagnostic[] = [];
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const result = await runner.run(ctx);
|
|
98
|
+
diagnostics = result.diagnostics.map((d) => ({
|
|
99
|
+
id: d.id,
|
|
100
|
+
message: d.message,
|
|
101
|
+
filePath: ctx.filePath,
|
|
102
|
+
line: d.line,
|
|
103
|
+
column: d.column,
|
|
104
|
+
severity: d.severity === "error" ? "error" : d.severity === "warning" ? "warning" : "info",
|
|
105
|
+
semantic: d.semantic ?? result.semantic ?? "warning",
|
|
106
|
+
tool: runnerId,
|
|
107
|
+
rule: d.rule,
|
|
108
|
+
fixable: d.fixable,
|
|
109
|
+
fixSuggestion: d.fixSuggestion,
|
|
110
|
+
}));
|
|
111
|
+
|
|
112
|
+
// Publish diagnostic found event
|
|
113
|
+
if (diagnostics.length > 0) {
|
|
114
|
+
DiagnosticFound.publish({
|
|
115
|
+
runnerId,
|
|
116
|
+
filePath: ctx.filePath,
|
|
117
|
+
diagnostics,
|
|
118
|
+
durationMs: Date.now() - runnerStart,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
diagnostics: diagnostics.map((d) => ({
|
|
124
|
+
id: d.id,
|
|
125
|
+
message: d.message,
|
|
126
|
+
severity: d.severity,
|
|
127
|
+
semantic: d.semantic,
|
|
128
|
+
})),
|
|
129
|
+
durationMs: Date.now() - runnerStart,
|
|
130
|
+
};
|
|
131
|
+
} catch (err) {
|
|
132
|
+
status = "failed";
|
|
133
|
+
return {
|
|
134
|
+
diagnostics: [],
|
|
135
|
+
durationMs: Date.now() - runnerStart,
|
|
136
|
+
error: String(err),
|
|
137
|
+
};
|
|
138
|
+
} finally {
|
|
139
|
+
// Publish completed event
|
|
140
|
+
RunnerCompleted.publish({
|
|
141
|
+
runnerId,
|
|
142
|
+
filePath: ctx.filePath,
|
|
143
|
+
status,
|
|
144
|
+
durationMs: Date.now() - runnerStart,
|
|
145
|
+
diagnosticCount: diagnostics.length,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Run all runners concurrently using Effect
|
|
151
|
+
const runnerIds = runners.map((r) => r.id);
|
|
152
|
+
const concurrentResults = await executeEffect(
|
|
153
|
+
runRunnersConcurrent(ctx.filePath, runnerIds, runSingle, 30_000)
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
// Collect all diagnostics
|
|
157
|
+
const allDiagnostics: Diagnostic[] = [];
|
|
158
|
+
for (const result of concurrentResults) {
|
|
159
|
+
if (result.status === "success") {
|
|
160
|
+
allDiagnostics.push(
|
|
161
|
+
...result.diagnostics.map((d) => ({
|
|
162
|
+
id: d.id,
|
|
163
|
+
message: d.message,
|
|
164
|
+
filePath: ctx.filePath,
|
|
165
|
+
severity: d.severity,
|
|
166
|
+
semantic: d.semantic ?? group.semantic ?? "warning",
|
|
167
|
+
tool: result.runnerId,
|
|
168
|
+
}))
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
results: concurrentResults,
|
|
175
|
+
diagnostics: allDiagnostics,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// --- Main Dispatch Function ---
|
|
180
|
+
|
|
181
|
+
export async function dispatchWithEffect(
|
|
182
|
+
ctx: DispatchContext,
|
|
183
|
+
groups: RunnerGroup[],
|
|
184
|
+
): Promise<EffectDispatchResult> {
|
|
185
|
+
const startTime = Date.now();
|
|
186
|
+
const allDiagnostics: Diagnostic[] = [];
|
|
187
|
+
const allRunnerResults: ConcurrentRunnerResult[] = [];
|
|
188
|
+
let stopped = false;
|
|
189
|
+
|
|
190
|
+
// Publish file modified event
|
|
191
|
+
FileModified.publish({
|
|
192
|
+
filePath: ctx.filePath,
|
|
193
|
+
changeType: "external",
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
for (const group of groups) {
|
|
197
|
+
if (stopped && ctx.pi.getFlag("stop-on-error")) {
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const { results, diagnostics } = await runGroupConcurrent(ctx, group);
|
|
202
|
+
|
|
203
|
+
allDiagnostics.push(...diagnostics);
|
|
204
|
+
allRunnerResults.push(...results);
|
|
205
|
+
|
|
206
|
+
// Check for blockers
|
|
207
|
+
const semantic = group.semantic ?? "warning";
|
|
208
|
+
if (semantic === "blocking" && diagnostics.length > 0) {
|
|
209
|
+
stopped = true;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Categorize results
|
|
214
|
+
const blockers = allDiagnostics.filter((d) => d.semantic === "blocking");
|
|
215
|
+
const warnings = allDiagnostics.filter(
|
|
216
|
+
(d) => d.semantic === "warning" || d.semantic === "none",
|
|
217
|
+
);
|
|
218
|
+
const fixedItems = allDiagnostics.filter((d) => d.semantic === "fixed");
|
|
219
|
+
const silentItems = allDiagnostics.filter((d) => d.semantic === "silent");
|
|
220
|
+
|
|
221
|
+
// Format output
|
|
222
|
+
let output = formatDiagnostics(blockers, "blocking");
|
|
223
|
+
output += formatDiagnostics(fixedItems, "fixed");
|
|
224
|
+
|
|
225
|
+
const durationMs = Date.now() - startTime;
|
|
226
|
+
|
|
227
|
+
// Log performance info in debug mode
|
|
228
|
+
if (ctx.pi.getFlag("lens-bus-debug")) {
|
|
229
|
+
console.error(`[effect] Total duration: ${durationMs}ms`);
|
|
230
|
+
for (const r of allRunnerResults) {
|
|
231
|
+
console.error(`[effect] ${r.runnerId}: ${r.status} (${r.durationMs}ms)`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
diagnostics: allDiagnostics,
|
|
237
|
+
blockers,
|
|
238
|
+
warnings,
|
|
239
|
+
fixed: fixedItems,
|
|
240
|
+
silent: silentItems,
|
|
241
|
+
output,
|
|
242
|
+
hasBlockers: blockers.length > 0,
|
|
243
|
+
durationMs,
|
|
244
|
+
runnerResults: allRunnerResults,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// --- Simple Integration Helper ---
|
|
249
|
+
|
|
250
|
+
export async function dispatchLintWithEffect(
|
|
251
|
+
filePath: string,
|
|
252
|
+
cwd: string,
|
|
253
|
+
pi: { getFlag(flag: string): string | boolean | undefined },
|
|
254
|
+
): Promise<string> {
|
|
255
|
+
const { createDispatchContext } = await import("../dispatch/dispatcher.js");
|
|
256
|
+
const { TOOL_PLANS } = await import("../dispatch/plan.js");
|
|
257
|
+
|
|
258
|
+
const ctx = createDispatchContext(filePath, cwd, pi);
|
|
259
|
+
|
|
260
|
+
const kind = ctx.kind;
|
|
261
|
+
if (!kind) return "";
|
|
262
|
+
|
|
263
|
+
const plan = TOOL_PLANS[kind];
|
|
264
|
+
if (!plan) return "";
|
|
265
|
+
|
|
266
|
+
const result = await dispatchWithEffect(ctx, plan.groups);
|
|
267
|
+
return result.output;
|
|
268
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effect-TS Service Infrastructure for pi-lens
|
|
3
|
+
*
|
|
4
|
+
* Simplified implementation focusing on:
|
|
5
|
+
* - Concurrent runner execution
|
|
6
|
+
* - Timeout handling
|
|
7
|
+
* - Error recovery
|
|
8
|
+
*/
|
|
9
|
+
import { Effect } from "effect";
|
|
10
|
+
// --- Error Types ---
|
|
11
|
+
export class RunnerError {
|
|
12
|
+
constructor(runnerId, cause) {
|
|
13
|
+
this.runnerId = runnerId;
|
|
14
|
+
this.cause = cause;
|
|
15
|
+
this._tag = "RunnerError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class TimeoutError {
|
|
19
|
+
constructor(operation, timeoutMs) {
|
|
20
|
+
this.operation = operation;
|
|
21
|
+
this.timeoutMs = timeoutMs;
|
|
22
|
+
this._tag = "TimeoutError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// --- Concurrent Execution Helper ---
|
|
26
|
+
/**
|
|
27
|
+
* Run multiple runners concurrently with Effect
|
|
28
|
+
*
|
|
29
|
+
* Features:
|
|
30
|
+
* - Parallel execution with Effect.all
|
|
31
|
+
* - Per-runner timeout handling
|
|
32
|
+
* - Graceful error recovery (individual failures don't stop others)
|
|
33
|
+
* - Automatic resource cleanup
|
|
34
|
+
*/
|
|
35
|
+
export function runRunnersConcurrent(filePath, runnerIds, runSingle, timeoutMs = 30000) {
|
|
36
|
+
return Effect.gen(function* () {
|
|
37
|
+
const startTime = Date.now();
|
|
38
|
+
// Run all runners in parallel
|
|
39
|
+
const results = yield* Effect.all(runnerIds.map((runnerId) => Effect.gen(function* () {
|
|
40
|
+
const runnerStart = Date.now();
|
|
41
|
+
// Execute with timeout and error handling
|
|
42
|
+
const result = yield* Effect.tryPromise({
|
|
43
|
+
try: () => runSingle(filePath, runnerId),
|
|
44
|
+
catch: (err) => err,
|
|
45
|
+
}).pipe(Effect.timeout(timeoutMs), Effect.catchAll((err) => Effect.succeed({
|
|
46
|
+
diagnostics: [],
|
|
47
|
+
durationMs: Date.now() - runnerStart,
|
|
48
|
+
error: String(err),
|
|
49
|
+
})));
|
|
50
|
+
const isError = "error" in result;
|
|
51
|
+
return {
|
|
52
|
+
runnerId,
|
|
53
|
+
status: isError ? "failure" : "success",
|
|
54
|
+
diagnostics: isError ? [] : result.diagnostics,
|
|
55
|
+
durationMs: isError ? result.durationMs : result.durationMs,
|
|
56
|
+
error: isError ? result.error : undefined,
|
|
57
|
+
};
|
|
58
|
+
})), { concurrency: "unbounded" });
|
|
59
|
+
return results;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Run a single runner with timeout and error handling
|
|
64
|
+
*/
|
|
65
|
+
export function runRunnerWithTimeout(filePath, runnerId, runSingle, timeoutMs = 30000) {
|
|
66
|
+
return Effect.gen(function* () {
|
|
67
|
+
const startTime = Date.now();
|
|
68
|
+
const result = yield* Effect.tryPromise({
|
|
69
|
+
try: () => runSingle(filePath, runnerId),
|
|
70
|
+
catch: (err) => new RunnerError(runnerId, err),
|
|
71
|
+
}).pipe(Effect.timeout(timeoutMs), Effect.mapError((err) => {
|
|
72
|
+
if (err instanceof RunnerError)
|
|
73
|
+
return err;
|
|
74
|
+
return new TimeoutError(`runner:${runnerId}`, timeoutMs);
|
|
75
|
+
}));
|
|
76
|
+
return {
|
|
77
|
+
diagnostics: result.diagnostics,
|
|
78
|
+
durationMs: Date.now() - startTime,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// --- Execution Helpers ---
|
|
83
|
+
/**
|
|
84
|
+
* Execute Effect and get result
|
|
85
|
+
*/
|
|
86
|
+
export function executeEffect(effect) {
|
|
87
|
+
return Effect.runPromise(effect);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Execute Effect with error handling
|
|
91
|
+
*/
|
|
92
|
+
export function executeEffectWithError(effect, onError) {
|
|
93
|
+
return Effect.runPromise(Effect.catchAll(effect, (err) => Effect.succeed(onError(err))));
|
|
94
|
+
}
|
|
95
|
+
// --- Error Formatting ---
|
|
96
|
+
export function formatError(err) {
|
|
97
|
+
switch (err._tag) {
|
|
98
|
+
case "RunnerError":
|
|
99
|
+
return `Runner ${err.runnerId} failed: ${err.cause}`;
|
|
100
|
+
case "TimeoutError":
|
|
101
|
+
return `Operation ${err.operation} timed out after ${err.timeoutMs}ms`;
|
|
102
|
+
default:
|
|
103
|
+
return "Unknown error";
|
|
104
|
+
}
|
|
105
|
+
}
|