@vitest-agent/sdk 3.1.1 → 4.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 (66) hide show
  1. package/README.md +23 -19
  2. package/dispatch.d.ts +25 -6
  3. package/formatters/markdown.js +1 -0
  4. package/formatters/terminal.js +4 -3
  5. package/index.d.ts +448 -2341
  6. package/index.js +10 -68
  7. package/internal-inject-env.js +5 -7
  8. package/package.json +3 -19
  9. package/schemas/run-report-file-1.0.0.json +18 -18
  10. package/sidecar-dispatch.js +7 -5
  11. package/utils/format-console.js +9 -10
  12. package/utils/format-terminal.js +7 -7
  13. package/utils/posix-path.js +52 -0
  14. package/utils/test-location.js +11 -5
  15. package/version.js +17 -0
  16. package/layers/ConfigLive.js +0 -46
  17. package/layers/DataReaderLive.js +0 -1923
  18. package/layers/DataStoreLive.js +0 -1287
  19. package/layers/DetailResolverLive.js +0 -9
  20. package/layers/DiscoveryRegistryLive.js +0 -70
  21. package/layers/EnvironmentDetectorLive.js +0 -21
  22. package/layers/EnvironmentDetectorTest.js +0 -13
  23. package/layers/ExecutorResolverLive.js +0 -9
  24. package/layers/FormatSelectorLive.js +0 -20
  25. package/layers/HistoryTrackerLive.js +0 -49
  26. package/layers/HistoryTrackerTest.js +0 -25
  27. package/layers/LoggerLive.js +0 -64
  28. package/layers/OutputPipelineLive.js +0 -13
  29. package/layers/OutputRendererLive.js +0 -28
  30. package/layers/PathResolutionLive.js +0 -40
  31. package/layers/PerClientSessionMapLive.js +0 -145
  32. package/layers/ProjectDiscoveryLive.js +0 -68
  33. package/layers/ProjectDiscoveryTest.js +0 -12
  34. package/layers/ProjectIdentityLive.js +0 -96
  35. package/layers/RunContextLive.js +0 -81
  36. package/lib/format-triage.js +0 -103
  37. package/lib/format-wrapup.js +0 -67
  38. package/migrations/0001_initial.js +0 -824
  39. package/migrations/0002_test_artifacts.js +0 -30
  40. package/migrations/index.js +0 -26
  41. package/migrations/registry_0001_initial.js +0 -39
  42. package/migrations/session_map_0001_initial.js +0 -38
  43. package/services/Config.js +0 -15
  44. package/services/DataReader.js +0 -8
  45. package/services/DataStore.js +0 -15
  46. package/services/DetailResolver.js +0 -8
  47. package/services/DiscoveryRegistry.js +0 -8
  48. package/services/EnvironmentDetector.js +0 -8
  49. package/services/ExecutorResolver.js +0 -8
  50. package/services/FormatSelector.js +0 -8
  51. package/services/HistoryTracker.js +0 -20
  52. package/services/OutputRenderer.js +0 -8
  53. package/services/PerClientSessionMap.js +0 -22
  54. package/services/ProjectDiscovery.js +0 -8
  55. package/services/ProjectIdentity.js +0 -63
  56. package/services/RunContext.js +0 -53
  57. package/services/idempotency.js +0 -58
  58. package/sql/assemblers.js +0 -69
  59. package/testing/layers.js +0 -21
  60. package/testing.d.ts +0 -1862
  61. package/testing.js +0 -254
  62. package/utils/ensure-migrated.js +0 -52
  63. package/utils/failure-signature.js +0 -34
  64. package/utils/resolve-data-path.js +0 -65
  65. package/utils/resolve-project-key-from-cwd.js +0 -53
  66. package/utils/resolve-workspace-key.js +0 -41
@@ -1,30 +0,0 @@
1
- import { Effect } from "effect";
2
- import { SqlClient } from "effect/unstable/sql/SqlClient";
3
-
4
- //#region src/migrations/0002_test_artifacts.ts
5
- /** @internal */
6
- const migration = Effect.gen(function* () {
7
- const sql = yield* SqlClient;
8
- yield* sql`DROP INDEX IF EXISTS idx_test_annotations_case`;
9
- yield* sql`DROP INDEX IF EXISTS idx_test_annotations_type`;
10
- yield* sql`DROP TABLE IF EXISTS test_annotations`;
11
- yield* sql`
12
- CREATE TABLE test_annotations (
13
- id INTEGER PRIMARY KEY AUTOINCREMENT,
14
- test_case_id INTEGER NOT NULL REFERENCES test_cases(id) ON DELETE CASCADE,
15
- type TEXT NOT NULL,
16
- message TEXT NOT NULL,
17
- location_file_id INTEGER REFERENCES files(id),
18
- location_line INTEGER,
19
- location_column INTEGER
20
- )
21
- `;
22
- yield* sql`CREATE INDEX idx_test_annotations_case ON test_annotations(test_case_id)`;
23
- yield* sql`CREATE INDEX idx_test_annotations_type ON test_annotations(type)`;
24
- yield* sql`ALTER TABLE test_artifacts ADD COLUMN data TEXT`;
25
- yield* sql`ALTER TABLE attachments ADD COLUMN byte_size INTEGER`;
26
- yield* sql`ALTER TABLE attachments ADD COLUMN body_encoding TEXT`;
27
- });
28
-
29
- //#endregion
30
- export { migration as default };
@@ -1,26 +0,0 @@
1
- import migration from "./0001_initial.js";
2
- import migration$1 from "./0002_test_artifacts.js";
3
-
4
- //#region src/migrations/index.ts
5
- /**
6
- * Every migration that must run against a per-project `data.db`, keyed by
7
- * migration id in application order.
8
- *
9
- * This is the single source of truth for the project database's migration
10
- * set: `ensureMigrated`, the sdk testing layer, `ReporterLive`, `McpLive`,
11
- * `CliLive` and `SidecarLive` all pass it straight to
12
- * `SqliteMigrator.fromRecord`, so adding a migration file and registering it
13
- * here is enough to reach every process that opens a project database.
14
- *
15
- * The session-map and discovery-registry databases are separate schemas with
16
- * their own migrations and are deliberately not listed here.
17
- *
18
- * @public
19
- */
20
- const PROJECT_MIGRATIONS = {
21
- "0001_initial": migration,
22
- "0002_test_artifacts": migration$1
23
- };
24
-
25
- //#endregion
26
- export { PROJECT_MIGRATIONS };
@@ -1,39 +0,0 @@
1
- import { Effect } from "effect";
2
- import { SqlClient } from "effect/unstable/sql/SqlClient";
3
-
4
- //#region src/migrations/registry_0001_initial.ts
5
- /**
6
- * Migration for the global discovery registry SQLite at
7
- * `$XDG_DATA_HOME/vitest-agent/registry.db`.
8
- *
9
- * Single table `known_projects` indexed by `project_key` (the
10
- * filesystem-safe form from {@link ProjectIdentity}). Tooling like
11
- * `mcp-app` queries this table to enumerate every vitest-agent
12
- * project the user has ever run on this machine.
13
- *
14
- * The registry never blocks anything — its writes are best-effort.
15
- * Concurrent writers from multiple Claude Code windows converge via
16
- * the `ON CONFLICT(project_key) DO UPDATE` upsert.
17
- * @public
18
- */
19
- const migration = Effect.gen(function* () {
20
- const sql = yield* SqlClient;
21
- yield* sql`PRAGMA journal_mode=WAL`;
22
- yield* sql`PRAGMA foreign_keys=ON`;
23
- yield* sql`PRAGMA busy_timeout=5000`;
24
- yield* sql`
25
- CREATE TABLE known_projects (
26
- project_key TEXT PRIMARY KEY,
27
- canonical_form TEXT NOT NULL,
28
- data_db_path TEXT NOT NULL,
29
- git_remote_origin TEXT,
30
- workspace_root TEXT NOT NULL,
31
- first_seen_at INTEGER NOT NULL,
32
- last_seen_at INTEGER NOT NULL
33
- ) STRICT
34
- `;
35
- yield* sql`CREATE INDEX idx_known_projects_workspace_root ON known_projects(workspace_root)`;
36
- });
37
-
38
- //#endregion
39
- export { migration as default };
@@ -1,38 +0,0 @@
1
- import { Effect } from "effect";
2
- import { SqlClient } from "effect/unstable/sql/SqlClient";
3
-
4
- //#region src/migrations/session_map_0001_initial.ts
5
- /** @internal */
6
- const migration = Effect.gen(function* () {
7
- const sql = yield* SqlClient;
8
- yield* sql`PRAGMA journal_mode=WAL`;
9
- yield* sql`PRAGMA foreign_keys=ON`;
10
- yield* sql`PRAGMA busy_timeout=5000`;
11
- yield* sql`
12
- CREATE TABLE conversation_map (
13
- transcript_uuid TEXT PRIMARY KEY,
14
- transcript_path TEXT NOT NULL,
15
- conversation_id TEXT NOT NULL UNIQUE,
16
- first_seen_at INTEGER NOT NULL,
17
- last_seen_at INTEGER NOT NULL
18
- ) STRICT
19
- `;
20
- yield* sql`
21
- CREATE TABLE session_map (
22
- host_session_id TEXT PRIMARY KEY,
23
- conversation_id TEXT NOT NULL REFERENCES conversation_map(conversation_id) ON DELETE RESTRICT,
24
- project_key TEXT NOT NULL,
25
- project_dir TEXT NOT NULL,
26
- main_agent_id TEXT NOT NULL,
27
- started_at INTEGER NOT NULL,
28
- ended_at INTEGER
29
- ) STRICT
30
- `;
31
- yield* sql`CREATE INDEX idx_session_map_project_key ON session_map(project_key)`;
32
- yield* sql`CREATE INDEX idx_session_map_project_dir ON session_map(project_dir)`;
33
- yield* sql`CREATE INDEX idx_session_map_active ON session_map(project_dir) WHERE ended_at IS NULL`;
34
- yield* sql`CREATE INDEX idx_session_map_conversation_id ON session_map(conversation_id)`;
35
- });
36
-
37
- //#endregion
38
- export { migration as default };
@@ -1,15 +0,0 @@
1
- import { ConfigFile } from "@effected/config-file";
2
-
3
- //#region src/services/Config.ts
4
- /**
5
- * Typed service class (Context tag) for the vitest-agent config file service.
6
- *
7
- * Both runtime packages (reporter, MCP) yield this tag to access the loaded
8
- * `VitestAgentConfig`. The live layer is built per `projectDir` via
9
- * `ConfigLive(projectDir)`.
10
- * @public
11
- */
12
- var VitestAgentConfigFile = class extends ConfigFile.Service()("vitest-agent/Config") {};
13
-
14
- //#endregion
15
- export { VitestAgentConfigFile };
@@ -1,8 +0,0 @@
1
- import { Context } from "effect";
2
-
3
- //#region src/services/DataReader.ts
4
- /** @public */
5
- var DataReader = class extends Context.Service()("vitest-agent/DataReader") {};
6
-
7
- //#endregion
8
- export { DataReader };
@@ -1,15 +0,0 @@
1
- import { Context } from "effect";
2
-
3
- //#region src/services/DataStore.ts
4
- /**
5
- * Ceiling on an inline attachment body. Vitest has already copied file
6
- * attachments into `.vitest/attachments/`, so a large body is referenced
7
- * by `path` rather than duplicated into `data.db`.
8
- * @public
9
- */
10
- const INLINE_ATTACHMENT_BODY_CAP_BYTES = 65536;
11
- /** @public */
12
- var DataStore = class extends Context.Service()("vitest-agent/DataStore") {};
13
-
14
- //#endregion
15
- export { DataStore, INLINE_ATTACHMENT_BODY_CAP_BYTES };
@@ -1,8 +0,0 @@
1
- import { Context } from "effect";
2
-
3
- //#region src/services/DetailResolver.ts
4
- /** @public */
5
- var DetailResolver = class extends Context.Service()("vitest-agent/DetailResolver") {};
6
-
7
- //#endregion
8
- export { DetailResolver };
@@ -1,8 +0,0 @@
1
- import { Context } from "effect";
2
-
3
- //#region src/services/DiscoveryRegistry.ts
4
- /** @public */
5
- var DiscoveryRegistry = class extends Context.Service()("vitest-agent/DiscoveryRegistry") {};
6
-
7
- //#endregion
8
- export { DiscoveryRegistry };
@@ -1,8 +0,0 @@
1
- import { Context } from "effect";
2
-
3
- //#region src/services/EnvironmentDetector.ts
4
- /** @public */
5
- var EnvironmentDetector = class extends Context.Service()("vitest-agent/EnvironmentDetector") {};
6
-
7
- //#endregion
8
- export { EnvironmentDetector };
@@ -1,8 +0,0 @@
1
- import { Context } from "effect";
2
-
3
- //#region src/services/ExecutorResolver.ts
4
- /** @public */
5
- var ExecutorResolver = class extends Context.Service()("vitest-agent/ExecutorResolver") {};
6
-
7
- //#endregion
8
- export { ExecutorResolver };
@@ -1,8 +0,0 @@
1
- import { Context } from "effect";
2
-
3
- //#region src/services/FormatSelector.ts
4
- /** @public */
5
- var FormatSelector = class extends Context.Service()("vitest-agent/FormatSelector") {};
6
-
7
- //#endregion
8
- export { FormatSelector };
@@ -1,20 +0,0 @@
1
- import { Context } from "effect";
2
-
3
- //#region src/services/HistoryTracker.ts
4
- /**
5
- * Builds the composite (modulePath, fullName) key used to key the internal
6
- * testMap and the returned classifications Map, so identically-named tests
7
- * in different files classify independently instead of colliding.
8
- *
9
- * Uses `JSON.stringify` for an injective encoding: a plain delimiter such as
10
- * a space is not collision-proof (e.g. `("a b", "c")` and `("a", "b c")` would
11
- * both yield `"a b c"`), which would reintroduce the very collision class this
12
- * key exists to prevent.
13
- * @public
14
- */
15
- const historyKey = (modulePath, fullName) => JSON.stringify([modulePath, fullName]);
16
- /** @public */
17
- var HistoryTracker = class extends Context.Service()("vitest-agent/HistoryTracker") {};
18
-
19
- //#endregion
20
- export { HistoryTracker, historyKey };
@@ -1,8 +0,0 @@
1
- import { Context } from "effect";
2
-
3
- //#region src/services/OutputRenderer.ts
4
- /** @public */
5
- var OutputRenderer = class extends Context.Service()("vitest-agent/OutputRenderer") {};
6
-
7
- //#endregion
8
- export { OutputRenderer };
@@ -1,22 +0,0 @@
1
- import { Context } from "effect";
2
-
3
- //#region src/services/PerClientSessionMap.ts
4
- /**
5
- * Read-only side of the session map.
6
- *
7
- * Provided by `PerClientSessionMapReaderLive` (opens SQLite with
8
- * `?mode=ro`). MCP server depends on this tag — calls to write
9
- * methods would be a type error.
10
- * @public
11
- */
12
- var PerClientSessionMapReader = class extends Context.Service()("vitest-agent/PerClientSessionMapReader") {};
13
- /**
14
- * Read-write side of the session map. Sidecar provides this; the
15
- * Writer layer also satisfies the Reader tag so consumers of either
16
- * one resolve.
17
- * @public
18
- */
19
- var PerClientSessionMapWriter = class extends Context.Service()("vitest-agent/PerClientSessionMapWriter") {};
20
-
21
- //#endregion
22
- export { PerClientSessionMapReader, PerClientSessionMapWriter };
@@ -1,8 +0,0 @@
1
- import { Context } from "effect";
2
-
3
- //#region src/services/ProjectDiscovery.ts
4
- /** @public */
5
- var ProjectDiscovery = class extends Context.Service()("vitest-agent/ProjectDiscovery") {};
6
-
7
- //#endregion
8
- export { ProjectDiscovery };
@@ -1,63 +0,0 @@
1
- import { canonicalizeGitUrl, gitUrlToProjectKey } from "../utils/canonicalize-git-url.js";
2
- import { normalizeWorkspaceKey } from "../utils/normalize-workspace-key.js";
3
- import { Context } from "effect";
4
-
5
- //#region src/services/ProjectIdentity.ts
6
- const isUseful = (value) => value !== void 0 && value.trim().length > 0;
7
- /**
8
- * Pure priority resolver. Returns `null` when no candidate produces a
9
- * usable value — the caller wraps `null` into a tagged error.
10
- *
11
- * For non-git sources the `canonicalForm` equals the original
12
- * (un-normalized) input so the discovery registry can display
13
- * `@spencerbeggs/vitest-agent` rather than the underscored form. For
14
- * git sources, `canonicalForm` is the slash-preserving `host/path`
15
- * shape from `canonicalizeGitUrl`.
16
- * @public
17
- */
18
- const resolveProjectIdentityFromCandidates = (candidates) => {
19
- if (isUseful(candidates.explicit)) return {
20
- projectKey: normalizeWorkspaceKey(candidates.explicit),
21
- canonicalForm: candidates.explicit,
22
- source: "explicit"
23
- };
24
- if (isUseful(candidates.toml)) return {
25
- projectKey: normalizeWorkspaceKey(candidates.toml),
26
- canonicalForm: candidates.toml,
27
- source: "toml"
28
- };
29
- if (isUseful(candidates.gitRemote)) {
30
- const canonical = canonicalizeGitUrl(candidates.gitRemote);
31
- const key = gitUrlToProjectKey(candidates.gitRemote);
32
- if (canonical !== null && key !== null) return {
33
- projectKey: key,
34
- canonicalForm: canonical,
35
- source: "git-remote"
36
- };
37
- }
38
- if (isUseful(candidates.packageJsonRepoUrl)) {
39
- const canonical = canonicalizeGitUrl(candidates.packageJsonRepoUrl);
40
- const key = gitUrlToProjectKey(candidates.packageJsonRepoUrl);
41
- if (canonical !== null && key !== null) return {
42
- projectKey: key,
43
- canonicalForm: canonical,
44
- source: "package-repository"
45
- };
46
- }
47
- if (isUseful(candidates.packageJsonName)) return {
48
- projectKey: normalizeWorkspaceKey(candidates.packageJsonName),
49
- canonicalForm: candidates.packageJsonName,
50
- source: "package-name"
51
- };
52
- return null;
53
- };
54
- /**
55
- * Service tag. The Live layer (in `layers/ProjectIdentityLive.ts`)
56
- * wires up the I/O sources; tests provide a stub that returns a
57
- * pre-built `ResolvedIdentity`.
58
- * @public
59
- */
60
- var ProjectIdentity = class extends Context.Service()("vitest-agent/ProjectIdentity") {};
61
-
62
- //#endregion
63
- export { ProjectIdentity, resolveProjectIdentityFromCandidates };
@@ -1,53 +0,0 @@
1
- import { Context, Schema } from "effect";
2
-
3
- //#region src/services/RunContext.ts
4
- /**
5
- * Per-run snapshot of the workspace's git state and host context.
6
- * Every field is nullable: a non-git workspace has all-NULL git
7
- * fields, a CI run with no terminal probe match has all-NULL host
8
- * fields, and a worker that can't read upstream info has a NULL
9
- * `gitUpstream` while everything else is set.
10
- *
11
- * Declared as a `Schema.Class` so callers can `new RunContext({...})`
12
- * and `Schema.encode(...)` for the SQLite row mapping.
13
- * @public
14
- */
15
- var RunContext = class extends Schema.Class("vitest-agent/RunContext")({
16
- gitBranch: Schema.NullOr(Schema.String),
17
- gitCommitSha: Schema.NullOr(Schema.String),
18
- gitDirty: Schema.NullOr(Schema.Boolean),
19
- gitUpstream: Schema.NullOr(Schema.String),
20
- gitWorktreeDir: Schema.NullOr(Schema.String),
21
- hostSource: Schema.NullOr(Schema.String),
22
- hostValue: Schema.NullOr(Schema.String),
23
- hostMetadata: Schema.NullOr(Schema.Unknown)
24
- }) {};
25
- /**
26
- * Subset of {@link RunContext} captured at agent registration. Only
27
- * the inheritable git context — branch, sha, worktree path. Mid-run
28
- * dirty-tree state and upstream tracking are per-run concerns.
29
- * @public
30
- */
31
- var AgentContext = class extends Schema.Class("vitest-agent/AgentContext")({
32
- startGitBranch: Schema.NullOr(Schema.String),
33
- startGitCommitSha: Schema.NullOr(Schema.String),
34
- startWorktreeDir: Schema.NullOr(Schema.String)
35
- }) {};
36
- /**
37
- * Service tag. The Live layer captures real I/O; the Test layer
38
- * returns whatever the test fixture passes.
39
- * @public
40
- */
41
- var RunContext$ = class extends Context.Service()("vitest-agent/RunContextService") {};
42
- /**
43
- * Re-export the service tag under a less-collision-prone name. The
44
- * `RunContext` Schema.Class above shares its symbol-name with the
45
- * service tag concept; the `$` suffix on the Context.Tag class name
46
- * keeps both addressable from the same module.
47
- *
48
- * @public
49
- */
50
- const RunContextService = RunContext$;
51
-
52
- //#endregion
53
- export { AgentContext, RunContext, RunContext$, RunContextService };
@@ -1,58 +0,0 @@
1
- import { createHash } from "node:crypto";
2
-
3
- //#region src/services/idempotency.ts
4
- /**
5
- * Sentinel substituted for `parentAgentId` when the value is `null`.
6
- * Avoids ambiguity around hashing the literal string "null" or empty
7
- * string, both of which could collide with caller-supplied values.
8
- * @public
9
- */
10
- const IDEMPOTENCY_ROOT_SENTINEL = "__ROOT__";
11
- /**
12
- * Derive the canonical idempotency key for an `agents` row.
13
- *
14
- * Algorithm:
15
- * sha256(agentType + "|" + (parentAgentId ?? "__ROOT__") + "|" + clientNonce)
16
- * then base32 (RFC 4648 alphabet, lowercased) truncated to 26 chars.
17
- *
18
- * 26 base32 chars = 130 bits, far beyond the birthday-bound for the
19
- * scoping (UNIQUE per session). The base32 alphabet is a-z2-7 — all
20
- * lowercase to keep the column lexicographically friendly and
21
- * case-insensitive on filesystems that we never hit but might in
22
- * future debugging.
23
- * @public
24
- */
25
- const deriveIdempotencyKey = (input) => {
26
- const parent = input.parentAgentId ?? "__ROOT__";
27
- const message = `${input.agentType}|${parent}|${input.clientNonce}`;
28
- const digest = createHash("sha256").update(message).digest();
29
- return base32Encode(digest).slice(0, 26);
30
- };
31
- const BASE32_ALPHABET = "abcdefghijklmnopqrstuvwxyz234567";
32
- /**
33
- * RFC 4648 base32 encoder, lowercase, no padding. Produces ceil(8n/5)
34
- * characters from an `n`-byte input. SHA-256 output (32 bytes) yields
35
- * 52 base32 chars; the caller truncates to 26.
36
- */
37
- const base32Encode = (bytes) => {
38
- let output = "";
39
- let buffer = 0;
40
- let bitsInBuffer = 0;
41
- for (const byte of bytes) {
42
- buffer = buffer << 8 | byte;
43
- bitsInBuffer += 8;
44
- while (bitsInBuffer >= 5) {
45
- bitsInBuffer -= 5;
46
- const index = buffer >> bitsInBuffer & 31;
47
- output += BASE32_ALPHABET[index];
48
- }
49
- }
50
- if (bitsInBuffer > 0) {
51
- const index = buffer << 5 - bitsInBuffer & 31;
52
- output += BASE32_ALPHABET[index];
53
- }
54
- return output;
55
- };
56
-
57
- //#endregion
58
- export { IDEMPOTENCY_ROOT_SENTINEL, deriveIdempotencyKey };
package/sql/assemblers.js DELETED
@@ -1,69 +0,0 @@
1
- import { historyKey } from "../services/HistoryTracker.js";
2
-
3
- //#region src/sql/assemblers.ts
4
- /**
5
- * Builds a `CacheManifest` from an array of test_runs aggregation rows.
6
- *
7
- * Each row represents the latest run for a project/sub_project pair.
8
- * The `dbPath` is stored as `cacheDir` so CLI tools can locate the source.
9
- * @public
10
- */
11
- function assembleManifest(rows, dbPath) {
12
- return {
13
- updatedAt: rows.reduce((latest, row) => {
14
- return row.timestamp > latest ? row.timestamp : latest;
15
- }, (/* @__PURE__ */ new Date(0)).toISOString()),
16
- cacheDir: dbPath,
17
- projects: rows.map((row) => {
18
- const projectName = row.sub_project ? `${row.project}:${row.sub_project}` : row.project;
19
- const safeProjectName = projectName.replace(/[^a-z0-9@._-]/gi, "_");
20
- const reportFile = `sql:reports/${safeProjectName}.json`;
21
- const historyFile = `sql:history/${safeProjectName}.history.json`;
22
- const lastResult = (() => {
23
- if (row.reason === "passed" || row.reason === "failed" || row.reason === "interrupted") return row.reason;
24
- return null;
25
- })();
26
- return {
27
- project: projectName,
28
- reportFile,
29
- historyFile,
30
- lastRun: row.timestamp,
31
- lastResult
32
- };
33
- })
34
- };
35
- }
36
- /**
37
- * Groups test_history rows by the composite (module_path, full_name) key
38
- * into a keyed record.
39
- *
40
- * Returns `Record<compositeKey, { modulePath, fullName, runs: TestRun[] }>`
41
- * where `runs` are the accumulated test runs for that test, in the order
42
- * they appear in `rows`. Two rows sharing `full_name` but differing
43
- * `module_path` are tracked as distinct entries rather than merged.
44
- *
45
- * Only rows with state `"passed"` or `"failed"` are included in runs
46
- * (matching the `TestRun` schema constraint).
47
- * @public
48
- */
49
- function assembleHistoryRecord(rows) {
50
- const record = {};
51
- for (const row of rows) {
52
- const state = row.state === "passed" || row.state === "failed" ? row.state : null;
53
- if (state === null) continue;
54
- const key = historyKey(row.module_path, row.full_name);
55
- if (!record[key]) record[key] = {
56
- modulePath: row.module_path,
57
- fullName: row.full_name,
58
- runs: []
59
- };
60
- record[key].runs.push({
61
- timestamp: row.timestamp,
62
- state
63
- });
64
- }
65
- return record;
66
- }
67
-
68
- //#endregion
69
- export { assembleHistoryRecord, assembleManifest };
package/testing/layers.js DELETED
@@ -1,21 +0,0 @@
1
- import { DataReaderLive } from "../layers/DataReaderLive.js";
2
- import { DataStoreLive } from "../layers/DataStoreLive.js";
3
- import { PROJECT_MIGRATIONS } from "../migrations/index.js";
4
- import { Layer } from "effect";
5
- import * as NodeServices from "@effect/platform-node/NodeServices";
6
- import { layer } from "@effect/sql-sqlite-node/SqliteClient";
7
- import * as SqliteMigrator from "@effect/sql-sqlite-node/SqliteMigrator";
8
-
9
- //#region src/testing/layers.ts
10
- /** @public */
11
- function makeTestLayer(filename) {
12
- const SqliteLayer = layer({ filename });
13
- const PlatformLayer = NodeServices.layer;
14
- const MigratorLayer = SqliteMigrator.layer({ loader: SqliteMigrator.fromRecord(PROJECT_MIGRATIONS) }).pipe(Layer.provide(Layer.merge(SqliteLayer, PlatformLayer)));
15
- return Layer.mergeAll(DataStoreLive.pipe(Layer.provide(SqliteLayer)), DataReaderLive.pipe(Layer.provide(SqliteLayer)), MigratorLayer, SqliteLayer, PlatformLayer);
16
- }
17
- /** @public */
18
- const DataStoreTestLayer = makeTestLayer(":memory:");
19
-
20
- //#endregion
21
- export { DataStoreTestLayer, makeTestLayer };