@gfdelarue/testkit 0.6.5 → 0.6.7
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/README.md +134 -0
- package/dist/app/bugs/ci-report.js +2 -2
- package/dist/app/campaigns/brief.js +1 -1
- package/dist/app/cleanup/index.js +1 -1
- package/dist/app/doctor/index.js +1 -1
- package/dist/app/environments/static-values.js +1 -1
- package/dist/app/environments/supplied.js +1 -1
- package/dist/app/environments/task-databases.js +2 -2
- package/dist/app/evidence/ci-status.js +1 -0
- package/dist/app/evidence/ci.js +1 -0
- package/dist/app/evidence/index.js +1 -0
- package/dist/app/evidence/inputs.js +1 -0
- package/dist/app/evidence/report.js +1 -0
- package/dist/app/lint/index.js +1 -1
- package/dist/app/run/engine-evidence-persistence.js +1 -1
- package/dist/app/run/engine-execution-failures.js +1 -1
- package/dist/app/run/engine-execution.js +1 -1
- package/dist/app/run/operation-progress.js +1 -1
- package/dist/app/run/orchestrator.js +1 -1
- package/dist/app/run/results.js +1 -1
- package/dist/app/run/run-events.js +1 -1
- package/dist/app/run/run-finalization.js +1 -1
- package/dist/app/run/run-guards.js +3 -3
- package/dist/app/run/scheduler/estimates.js +1 -1
- package/dist/app/run/scheduler/index.js +1 -1
- package/dist/app/run/task-batches.js +1 -1
- package/dist/app/run/worker-loop.js +1 -1
- package/dist/app/status/runs.js +1 -1
- package/dist/app/typecheck/index.js +4 -4
- package/dist/cli/command-catalog.js +1 -1
- package/dist/cli/command-registry.js +1 -1
- package/dist/cli/commands/evidence.js +2 -0
- package/dist/cli/renderers/destroy/text.js +1 -1
- package/dist/cli/renderers/evidence/text.js +1 -0
- package/dist/cli/renderers/run/failure.js +1 -1
- package/dist/cli/renderers/run/status.js +1 -1
- package/dist/cli/renderers/run/text-reporter.js +23 -23
- package/dist/config/loader/decode-discovery.js +1 -1
- package/dist/config/loader/discover-tests.js +1 -1
- package/dist/config/loader/discovery-result.js +1 -1
- package/dist/config/loader/discovery.js +2 -2
- package/dist/config/loader/env.js +1 -1
- package/dist/config/loader/file-metadata.js +1 -1
- package/dist/config/loader/groups.js +2 -2
- package/dist/config/loader/index.js +1 -1
- package/dist/config/loader/load.js +1 -1
- package/dist/config/loader/path-policy.js +1 -1
- package/dist/config/loader/runtime-isolation.js +1 -1
- package/dist/config/loader/runtime.js +1 -1
- package/dist/config/loader/test-selection.js +1 -1
- package/dist/config/loader/validation.js +1 -1
- package/dist/database/admin.js +4 -4
- package/dist/database/cleanup.js +1 -1
- package/dist/database/configured-steps.js +1 -1
- package/dist/database/docker.js +3 -5
- package/dist/database/index.js +2 -2
- package/dist/database/local-postgres.js +3 -4
- package/dist/database/postgres/index.js +2 -2
- package/dist/database/progress.js +1 -1
- package/dist/database/roles.js +2 -2
- package/dist/database/runtime-config.js +1 -1
- package/dist/database/schema-source.js +3 -3
- package/dist/database/source-url.js +1 -1
- package/dist/database/template-steps.js +3 -3
- package/dist/domain/operation-reason.js +1 -1
- package/dist/domain/suite-selection.js +1 -1
- package/dist/engines/adapters/evidence-files.js +1 -1
- package/dist/engines/adapters/evidence-identity.js +1 -1
- package/dist/engines/adapters/execution.js +1 -1
- package/dist/engines/adapters/node-startup-cache.js +1 -1
- package/dist/engines/adapters/workspace.js +1 -1
- package/dist/engines/contracts/fixture-manifest.js +1 -1
- package/dist/engines/installations/package-resolution.js +1 -1
- package/dist/engines/managed-module-resolution.js +2 -2
- package/dist/engines/node-vitest/adapter/adapter.js +1 -1
- package/dist/engines/node-vitest/adapter/evidence.js +2 -2
- package/dist/engines/node-vitest/runtime/data/report.js +3 -3
- package/dist/engines/node-vitest/runtime/data/scenario.js +2 -2
- package/dist/engines/node-vitest/selection.js +1 -1
- package/dist/engines/playwright/adapter/adapter.js +1 -1
- package/dist/engines/playwright/adapter/config.js +3 -3
- package/dist/engines/playwright/adapter/index.js +1 -1
- package/dist/environment/lifecycle-controller.js +1 -1
- package/dist/environment/template.js +1 -1
- package/dist/evidence/artifacts.js +3 -3
- package/dist/evidence/bundles.js +2 -2
- package/dist/evidence/environment-record.js +1 -1
- package/dist/evidence/index.js +1 -1
- package/dist/evidence/logs.js +2 -2
- package/dist/evidence/operation-timings.js +1 -1
- package/dist/evidence/reading.js +1 -1
- package/dist/evidence/reporting.js +1 -1
- package/dist/evidence/run-evidence.js +1 -1
- package/dist/evidence/run-inputs.js +1 -0
- package/dist/evidence/run-model.js +1 -1
- package/dist/evidence/setup-progress.js +1 -1
- package/dist/evidence/storage-inventory.js +1 -1
- package/dist/evidence/storage.js +2 -2
- package/dist/process/capture-output.js +1 -1
- package/dist/process/execa-bundle.json +3 -3
- package/dist/process/host/platform.js +1 -1
- package/dist/process/locks.js +4 -4
- package/dist/process/process-group.js +1 -1
- package/dist/process/process.js +1 -1
- package/dist/process/ready-deadline.js +1 -1
- package/dist/process/runs/manifest.js +1 -1
- package/dist/process/runs/stop.js +1 -1
- package/dist/process/windows-native.js +1 -1
- package/dist/repo/content.js +4 -0
- package/dist/repo/fingerprint-policy.js +1 -1
- package/dist/repo/managed-git.js +1 -1
- package/dist/repo/metadata.js +1 -1
- package/dist/repo/state.js +4 -4
- package/dist/steps/configured-step-testkit-modules.js +1 -1
- package/dist/steps/configured-steps.js +1 -1
- package/dist/steps/environment.js +1 -1
- package/dist/steps/index.js +1 -1
- package/dist/steps/run.js +2 -2
- package/dist/toolchains/esbuild.js +1 -1
- package/dist/toolchains/index.js +3 -3
- package/dist/toolchains/node-integrity.js +1 -1
- package/dist/toolchains/node-provisioning.js +2 -2
- package/dist/toolchains/semver.js +1 -1
- package/dist/tracker/gitea.js +1 -1
- package/dist/tracker/github.js +1 -1
- package/dist/tracker/requests.js +1 -1
- package/dist/tracker/responses.js +1 -1
- package/docs/bug-records.md +18 -2
- package/docs/release-notes.md +36 -0
- package/package.json +1 -1
- package/dist/app/run/planning-types.js +0 -1
- package/dist/app/run/selection.js +0 -1
- package/dist/config/authoring/http-methods.js +0 -1
- package/dist/evidence/artifact-validation.js +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import w from"node:path";import{randomUUID as
|
|
2
|
-
`),{cause:p});throw Object.defineProperty(l,"result",{enumerable:!1,value:Object.freeze({...
|
|
1
|
+
import w from"node:path";import{randomUUID as D}from"node:crypto";import{setTimeout as O}from"node:timers/promises";import{errorMessage as _}from"../../kernel/error-serialization.js";import{dockerHostAddress as v,shouldUseDockerHostNetwork as L}from"../../process/host/platform.js";import{managedHostProcessInfrastructureError as k,runManagedHostProcess as b,startManagedHostProcess as G}from"../../process/index.js";import{dockerCliEnvironment as N}from"../docker-endpoint.js";import{ownedClientStartFailure as R,ownedPostgresExec as U,reclaimOwnedPostgresExecs as $}from"./owned-exec.js";export const POSTGRES_CLIENT_CONTAINER_LABEL="com.elench.testkit.postgres-client.container";const F="postgres:16",h="postgres";export const POSTGRES_CLIENT_TIMEOUT_MS=30*6e4;const q=["-h","--host","-p","--port","-U","--username","-d","--dbname"],j=/^(?:--host|--port|--username|--dbname)=/,m=String;export function postgresClientImage(e=process.env){return e.TESTKIT_POSTGRES_CLIENT_IMAGE||F}export async function runDockerPsql(e){try{return await startDockerPostgresClient({...e,clientCommand:"psql"})}catch(t){throw A(t)}}export function startDockerPgDump(e){return startDockerPostgresClient({...e,clientCommand:"pg_dump"})}export function startDockerPostgresClient({clientCommand:e,args:t=[],consumerSql:o=!1,cwd:n,env:i=process.env,connection:c=null,databaseUrl:s=null,image:a=null,ownedContainer:r=null,purpose:u,signal:d,timeoutMs:f=POSTGRES_CLIENT_TIMEOUT_MS}){const p=u?` for ${u}`:"",l=K({args:t,connection:c,databaseUrl:s});if(!V(l))throw new Error(`Dockerized ${e||"Postgres client"} requires a Postgres connection URL or connection object`);const E=y(t,l);if(r&&!o&&rewritePostgresFileArgsForDocker(E,n).mountArgs.length===0){const P=U(r,l,i,[e,"-h","127.0.0.1","-p",m(r.containerPort||5432),"-U",l.user,"-d",l.database||h,...E]);return C("docker",P.args,{cwd:n,displayLabel:`Owned Postgres client${p}`,env:P.env,reclaim:()=>$(r,l,P.id,i),signal:d,startFailure:I=>R(r,e,I),timeoutMs:f})}const g=buildDockerPostgresClientArgs({clientCommand:e,args:t,connection:l,cwd:n,env:i,image:a||postgresClientImage(i),ownedContainer:r}),S=`testkit-postgres-client-${D()}`;g.splice(1,0,"--name",S);const x=T(i,l);return C("docker",g,{cwd:n,displayLabel:`Dockerized Postgres client${p}`,env:x,reclaim:()=>H(S,n,x),signal:d,timeoutMs:f})}function A(e){return z(e)&&e.code==="ENOENT"||e instanceof Error&&e.message.includes('managed host executable "docker"')?new Error("Docker is required for Testkit Postgres client operations, but the docker command was not found."):e}function z(e){return e instanceof Error&&"code"in e}function C(e,t,o){let n;try{n=G({recoveryIdentity:"none",args:t,cwd:o.cwd||process.cwd(),displayLabel:o.displayLabel,env:o.env,executable:e,outputMode:"bounded-capture",signal:o.signal,timeoutMs:o.timeoutMs})}catch(r){throw A(r)}let i=!1,c=!1;const s=n.completion.then(async r=>{const u=c&&B(r)?null:k(o.displayLabel,r);if(r.cancelled||r.timedOut||r.outputLimitExceeded||c)try{await o.reclaim()}catch(f){const p=u||(r.exitCode!==0?new Error(`${o.displayLabel} failed with exit code ${String(r.exitCode)}`):null);if(!p)throw f;const l=new AggregateError([p,f],[p.message,r.stderr.trim(),r.stdout.trim(),_(f)].filter(Boolean).join(`
|
|
2
|
+
`),{cause:p});throw Object.defineProperty(l,"result",{enumerable:!1,value:Object.freeze({...r})}),l}if(u)throw A(u);const d=o.startFailure?.(r);if(d)throw d;return{command:e,escapedCommand:[e,...t].join(" "),exitCode:r.exitCode,failed:r.exitCode!==0||c,signal:r.exitSignal,stderr:r.stderr,stdout:r.stdout}});s.then(()=>{i=!0},()=>{i=!0});const a=s;return Object.defineProperties(a,{pid:{enumerable:!0,get:()=>n.pid},stdout:{enumerable:!0,value:n.stdout},stderr:{enumerable:!0,value:n.stderr},stop:{enumerable:!0,value:()=>i?!1:(c=!0,n.stop(),!0)}}),a}async function H(e,t,o){const n={executable:"docker",cwd:t||process.cwd(),env:o,displayLabel:"Postgres client container cleanup"},i=async()=>{const c=performance.now()+3e4;let s;for(;;){const a=Math.floor(c-performance.now());if(a<=0)throw s??new Error("Postgres client container cleanup timed out");const r=await b({...n,args:["rm","--force",e],timeoutMs:a}),u=k("Postgres client container cleanup",r);if(u)throw u;if(r.exitCode===0)return;const d=r.stderr.trim();if(d===`Error response from daemon: No such container: ${e}`)return;if(s=new Error(`Postgres client container cleanup failed: ${r.stderr}`),d!==`Error response from daemon: removal of container ${e} is already in progress`)throw s;await O(Math.min(100,Math.max(0,c-performance.now())))}};try{(await b({...n,args:["kill","--signal=SIGINT",e],timeoutMs:5e3})).exitCode===0&&await b({...n,args:["wait",e],timeoutMs:2e3})}finally{await i()}}function B(e){return e.error===null&&e.terminationError===null&&!e.timedOut&&!e.cancelled&&!e.outputLimitExceeded}export function buildDockerPostgresClientArgs({clientCommand:e,args:t=[],connection:o,cwd:n,env:i=process.env,image:c=postgresClientImage(i),ownedContainer:s=null}){const a=y(t,o),{args:r,mountArgs:u}=rewritePostgresFileArgsForDocker(a,n),[d,f,p]=W(o,s),l=T(i,o),E=["PGPASSWORD","PGSSLMODE","PGAPPNAME"].filter(g=>l[g]!=null).flatMap(g=>["-e",g]);return["run","--rm",...d,...s?["--label",`${POSTGRES_CLIENT_CONTAINER_LABEL}=${s.containerId}`]:[],...E,...u,c,e,"-h",f,"-p",p,"-U",o.user,"-d",o.database||h,...r]}function W(e,t){if(!t)return[L()?["--network","host"]:[],m(v(e.host)),m(e.port||5432)];if(!t.containerId)throw new TypeError("ownedContainer.containerId must be a container id");return[["--network",`container:${t.containerId}`],"127.0.0.1",m(t.containerPort||5432)]}export function rewritePostgresFileArgsForDocker(e,t){const o=[],n=[];let i=0;const c=s=>{const a=w.resolve(t||process.cwd(),s),r=`/testkit-postgres-files-${String(i)}`;return i+=1,n.push("--mount",`type=bind,source=${w.dirname(a)},target=${r},readonly`),`${r}/${w.basename(a)}`};for(let s=0;s<e.length;s+=1){const a=e[s],r=e[s+1];if((a==="-f"||a==="--file")&&r){o.push(a,c(r)),s+=1;continue}if(a.startsWith("--file=")){o.push(`--file=${c(a.slice(7))}`);continue}o.push(a)}return{args:o,mountArgs:n}}function K({args:e,connection:t,databaseUrl:o}){return t?{...t,port:t.port||5432,database:t.database||h}:connectionFromDatabaseUrl(o||J(e))}function J(e){const t=e[0];return M(t)?t:null}function y(e,t){const o=t?.rawUrl,n=o&&e[0]===o||M(e[0])?e.slice(1):e;return Q(n).map(i=>m(i))}function Q(e){const t=[];for(let o=0;o<e.length;o+=1){const n=e[o];if(typeof n=="string"&&q.includes(n)){o+=1;continue}j.test(m(n))||t.push(n)}return t}function T(e,t){return N({...e,PGPASSWORD:t.password||"",...t.sslMode?{PGSSLMODE:t.sslMode}:{},...e.PGAPPNAME?{PGAPPNAME:e.PGAPPNAME}:{}})}function M(e){return/^postgres(?:ql)?:\/\//i.test(m(e||""))}function V(e){return!!(e?.host&&e.user)}export function connectionFromDatabaseUrl(e){if(!e)return null;let t;try{t=new URL(m(e))}catch{return null}return["postgres:","postgresql:"].includes(t.protocol)?{rawUrl:e,host:t.hostname,port:t.port||"5432",database:decodeURIComponent(t.pathname.replace(/^\//,""))||h,user:decodeURIComponent(t.username||""),password:decodeURIComponent(t.password||""),sslMode:t.searchParams.get("sslmode")||void 0}:null}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{}
|
|
1
|
+
export function finishSetupOperation(t,i,n){const e=t&&i.setupRegistry?i.setupRegistry.finish(t,n):null;e&&i.reporter?.setupOperationFinished?.(e)}
|
package/dist/database/roles.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{errorMessage as
|
|
1
|
+
import{errorMessage as N}from"../kernel/error-serialization.js";import{awaitCapturedResult as c,writeCapturedLine as d}from"../evidence/step-output.js";import{LOCAL_ADMIN_DB as R}from"./constants.js";import{escapeSqlLiteral as A}from"./naming.js";import{runDockerPsql as S}from"./postgres/index.js";import{callCap as T}from"./call-caps.js";import{finishSetupOperation as l}from"./progress.js";export const DATABASE_ROLES_LOCK=[1952805748,1919904869];export async function ensureDatabaseRoles(r,t,e={}){const i=r.testkit.database.roles;if(i.length===0)return;const o=e.setupRegistry?.start({config:r,kind:"database-prerequisites",stage:"database-prerequisites",summary:"ensure declared PostgreSQL roles"}),a=o?._logRecord||null;try{e.signal?.throwIfAborted();const E=e.timeoutMs??T("roleSetup");if(i.some(O=>O.name===t.user))throw new Error("A declared PostgreSQL role cannot be the owned bootstrap user");const s=await S({args:["-X","-v","ON_ERROR_STOP=1","-c",L(i,E)],connection:{...t,database:R},ownedContainer:{containerId:t.containerId,containerPort:t.containerPort,image:t.image},cwd:r.productDir,env:{...process.env,PGAPPNAME:"testkit:database-prerequisites"},signal:e.signal,timeoutMs:E});c(s,{logRecord:a}),l(o,e,{status:"passed",summary:"declared PostgreSQL roles ready"})}catch(E){const s=e.signal?.aborted?e.signal.reason:E;throw s!==E&&d(a,"stderr",N(E)),d(a,"stderr",N(s)),l(o,e,{status:e.signal?.aborted?"cancelled":"failed",error:N(s)}),s}}function L(r,t){const e=r.map(o=>n(o.name)).join(", "),i=`DECLARE
|
|
2
2
|
desired text;
|
|
3
3
|
existing pg_authid%ROWTYPE;
|
|
4
4
|
conflicts text[];
|
|
@@ -38,4 +38,4 @@ BEGIN
|
|
|
38
38
|
FOREACH desired IN ARRAY reused LOOP
|
|
39
39
|
RAISE NOTICE 'Reused declared PostgreSQL role "%"', desired;
|
|
40
40
|
END LOOP;
|
|
41
|
-
END`;return`BEGIN; SET LOCAL statement_timeout = ${
|
|
41
|
+
END`;return`BEGIN; SET LOCAL statement_timeout = ${n(String(t))}; DO ${n(i)}; COMMIT;`}function n(r){return`E'${A(r).replaceAll("\\","\\\\")}'`}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{materializeDatabaseConfig as u}from"../config/loader/index.js";import{finalizeConfiguredInputs as t,finalizeConfiguredSteps as f}from"../steps/configured-steps.js";import{finalizeString as r}from"../steps/index.js";export function finalizeRuntimeDatabaseConfig(i,n){return i?u({...i,sourceSchema:a(i.sourceSchema,n),template:o(i.template,n)}):void 0}function o(i,n){return
|
|
1
|
+
import{materializeDatabaseConfig as u}from"../config/loader/index.js";import{finalizeConfiguredInputs as t,finalizeConfiguredSteps as f}from"../steps/configured-steps.js";import{finalizeString as r}from"../steps/index.js";export function finalizeRuntimeDatabaseConfig(i,n){return i?u({...i,sourceSchema:a(i.sourceSchema,n),template:o(i.template,n)}):void 0}function o(i,n){return{inputs:t(i.inputs,e=>r(e,n)),migrate:f(i.migrate,e=>r(e,n)),seed:f(i.seed,e=>r(e,n)),verify:f(i.verify,e=>r(e,n))}}function a(i,n){return i?{...i,..."env"in i&&typeof i.env=="string"?{env:r(i.env,n)}:{}}:null}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{recordWork as l}from"../kernel/work-metrics.js";import
|
|
2
|
-
`))}export async function appendSourceSchemaCacheToHash(e,r,t=null){if(!(r.testkit.database?.sourceSchema||null))return;const a=t?.cachePath||await getSourceSchemaCachePath(r),n=t?.active&&t.cacheKey?t.cacheKey:s.relative(getSourceSchemaRootDir(r),s.dirname(a));if(e.update(`source-schema-key:${n}`),e.update(`source-schema-cache:${s.relative(r.productDir,a)}`),!
|
|
3
|
-
`}function
|
|
1
|
+
import{recordWork as l}from"../kernel/work-metrics.js";import h from"fs";import s from"path";import{createRequire as z}from"node:module";import{collectRepoState as J}from"../repo/state.js";import{findDatabasePlaceholders as V,resolveExecutionEnvValues as G}from"../steps/index.js";import{sourceSchemaEnvNames as K}from"./source-schema-env.js";import{callCap as X}from"./call-caps.js";import{finishSetupOperation as _}from"./progress.js";import{dumpPostgresSchemaToFile as Z,captureTemplateSnapshotText as Q,PG_DUMP_ARGS as Y,resolvePgDumpClientImage as ee,runTemplateStep as re,sanitizeSnapshotText as te}from"./template-steps.js";import{getSourceSchemaRefreshLockPath as ae,withSourceSchemaRefreshLock as ce}from"./source-refresh-lock.js";import{resolveSourceSchemaDumpUrl as ne}from"./source-url.js";import{makeTempDir as se,removeTempDir as ie}from"../process/files/temp-dir.js";import{withProcessLockProgress as oe}from"../process/locks.js";import{isNullableString as he,isUnknownRecord as f,readDynamicProperty as $}from"../kernel/unknown-values.js";import{requireContainedTestkitStateDirectory as U,resolveContainedTestkitStateDirectory as v}from"../process/runs/product-state.js";const F=z(import.meta.url);let P;function ue(){if(P)return P;const e=F.resolve("diff/package.json"),t=JSON.parse(h.readFileSync(e,"utf8"))?.exports?.["."]?.import?.default;if(typeof t!="string"||!t.startsWith("./"))throw new Error("The diff package must declare a local ESM import entry");const c=s.dirname(e),a=s.resolve(c,t),n=s.relative(c,a);if(!n||n===".."||n.startsWith(`..${s.sep}`)||s.isAbsolute(n))throw new Error("The diff package ESM import entry must stay within its package directory");return P=F(a)}const M="db",N="source-schemas",D="source-schema.sql",w="source-schema.meta.json",me="index.json",L=20,p=String;export function getSourceSchemaRootDir(e){return v(e.productDir,M,e.name,N)}export async function getSourceSchemaCachePath(e,r={}){return(await resolveSourceSchemaCacheLocation(e,r)).cachePath}export async function resolveSourceSchemaCacheLocation(e,r={}){const t=r.repoState||await J(e.productDir,{fingerprints:e.testkit?.fingerprints}),c=getSourceSchemaRootDir(e),a=v(e.productDir,M,e.name,N,...t.cacheKey.split("/").map(y));return{rootDir:c,cacheDir:a,cachePath:s.join(a,D),metadataPath:s.join(a,w),indexPath:s.join(c,me),cacheKey:t.cacheKey,cacheKind:t.kind,repoState:t}}export async function resolveSourceSchemaState(e,r={}){const t=e.testkit.database?.sourceSchema||null;if(!t)return{active:!1};const c=await resolveSourceSchemaCacheLocation(e,r),a=c.cachePath,n=h.existsSync(a);n&&g(a);const i=r.env||process.env,o=K(e);Ce(e,o);const u=G(e,[...o,"CI"],i),m=o.find(B=>{const A=u[B];return typeof A=="string"&&A.trim().length>0})||null,C=m?u[m]:void 0,R=typeof C=="string"?C.trim():null;return t.kind==="env"||!!R||n?{active:!0,cachePath:a,metadataPath:c.metadataPath,indexPath:c.indexPath,rootDir:c.rootDir,cacheDir:c.cacheDir,cacheKey:c.cacheKey,cacheKind:c.cacheKind,repoState:c.repoState,envName:m,sourceUrl:R,sourceSchema:t,cacheExists:n,refreshed:!1,ci:u.CI==="true",unavailableMode:ke(t.unavailable,i)}:{active:!1,cachePath:a}}export async function prepareSourceSchemaCache(e,r={}){r.signal?.throwIfAborted();const t=await resolveSourceSchemaState(e,r);if(!t.active)return t;if(H(t)){if(ve(t))try{const a=await I(e,t,r),n={...t,refreshed:!0,cacheExists:!0,refreshInfo:a};return S(e,n,{refreshed:!0}),n}catch(a){if(r.signal?.aborted)throw a;const n=le(e,t,a,r);if(n)return n;throw a}return S(e,t),r.setupRegistry?.recordCached({config:e,stage:"source-schema",kind:"database-source-schema",summary:"source schema cache hit"}),t}if(t.cacheExists&&t.unavailableMode==="warn-cache")return S(e,t),r.setupRegistry?.recordCached({config:e,stage:"source-schema",kind:"database-source-schema",summary:"source unavailable; using cached source schema"}),t;const c=K(e).join(" or ");throw new Error(`Source schema database URL is unavailable for service "${e.name}". Set ${c}.`)}function le(e,r,t,c={}){if(r.unavailableMode!=="warn-cache")return null;const a=de(e,r);if(!a)return null;const n={...r,...a,cacheExists:!0,refreshed:!1,refreshError:$(t,"message")||p(t)};return c.setupRegistry?.recordCached({config:e,stage:"source-schema",kind:"database-source-schema",summary:`source unavailable; using cached source schema: ${s.relative(e.productDir,a.cachePath)}`}),n}function de(e,r){const t=j(e,r,{cachePath:r.cachePath,metadataPath:r.metadataPath,cacheKey:r.cacheKey,cacheKind:r.cacheKind});if(t)return t;const c=T(r.indexPath);c.sort((a,n)=>Date.parse(n.lastUsedAt||n.refreshedAt||"0")-Date.parse(a.lastUsedAt||a.refreshedAt||"0"));for(const a of c){if(!a.cachePath)continue;const n=s.resolve(e.productDir,a.cachePath),i=j(e,r,{cachePath:n,metadataPath:s.join(s.dirname(n),w),cacheKey:a.cacheKey,cacheKind:a.kind||"unknown"});if(i)return i}return null}function j(e,r,t){const c=s.resolve(t.cachePath);if(s.basename(c)!==D)return null;const a=U(e.productDir,s.dirname(c),`Service "${e.name}" source schema cache directory`),n=s.join(a,D),i=s.resolve(r.rootDir);if(n!==i&&!n.startsWith(`${i}${s.sep}`)||!h.existsSync(n))return null;const o=h.lstatSync(n);if(o.isSymbolicLink()||!o.isFile())throw new Error(`Refusing unsafe source schema cache file at ${n}`);return{cachePath:n,metadataPath:s.join(a,w),cacheDir:a,cacheKey:t.cacheKey,cacheKind:t.cacheKind,indexPath:r.indexPath,rootDir:r.rootDir}}export async function forceRefreshSourceSchemaCache(e,r,t={}){const c=await resolveSourceSchemaState(e,t);if(!c.active||!H(c)){const i=r?.active?r.envName:null,o=r?.active&&r.sourceSchema.kind==="env"?r.sourceSchema.env:null,u=i||o||"source schema env";throw new Error(`Cannot refresh source schema for service "${e.name}" because ${u} is unavailable.`)}const a=await I(e,c,t),n={...c,refreshed:!0,cacheExists:!0,refreshInfo:a};return S(e,n,{refreshed:!0}),n}export async function applySourceSchemaCache(e,r,t,c={}){if(!t?.active)return!1;if(!h.existsSync(t.cachePath))throw new Error(`Missing source schema cache for service "${e.name}": ${t.cachePath}`);const a={kind:"sql-file",path:s.relative(e.productDir,t.cachePath).split(s.sep).join("/"),cwd:null,inputs:[]};return l("schema.sourceReplays"),await re(e,"source-schema",a,0,r,{...c,stepTimeoutMs:X("sourceSchemaReplay")}),!0}export async function verifyLocalSchemaMatchesSource(e,r,t,c={}){if(!t?.active)return{status:"disabled"};if(!t.sourceSchema.verify||c.skipSchemaSourceVerify)return c.setupRegistry?.recordCached({config:e,stage:"source-schema:verify",kind:"database-source-schema",summary:"source schema verification skipped"}),{status:"skipped"};be(e);const a=ye(t.cachePath);l("schema.verificationDumps");const n=await Q(e,r,{ownedContainer:c.ownedContainer||null,reporter:c.reporter||null,signal:c.signal});return l("schema.comparisons"),l("schema.bytesCompared",Buffer.byteLength(a)+Buffer.byteLength(n)),n===a?{status:"matched"}:{status:"mismatch",diagnostics:Ee(e,t,a,n)}}export function createSourceSchemaMismatchError(e,r,t){const c=[`Local schema replay does not match source schema for service "${e.name}".`,`Source cache: ${s.relative(e.productDir,r.cachePath)}`];return t?.status==="mismatch"&&c.push(`Diff: ${s.relative(e.productDir,t.diagnostics.diffPath)}`),new Error(c.join(`
|
|
2
|
+
`))}export async function appendSourceSchemaCacheToHash(e,r,t=null){if(!(r.testkit.database?.sourceSchema||null))return;const a=t?.cachePath||await getSourceSchemaCachePath(r),n=t?.active&&t.cacheKey?t.cacheKey:s.relative(getSourceSchemaRootDir(r),s.dirname(a));if(e.update(`source-schema-key:${n}`),e.update(`source-schema-cache:${s.relative(r.productDir,a)}`),!h.existsSync(a)){e.update(":missing");return}const i=h.statSync(a);e.update(`:${i.size.toString()}:${i.mtimeMs.toString()}`);const o=h.readFileSync(a);l("schema.cacheHashFilesRead"),l("schema.cacheHashBytesRead",o.length),e.update(o)}async function I(e,r,t={}){const c=t.setupRegistry?.start({config:e,stage:"source-schema:refresh",kind:"database-source-schema",summary:`source schema refresh: ${s.relative(e.productDir,r.cachePath)}`});try{const a=await ce(ae(r.cachePath),({waited:n,requestedAt:i})=>oe(`refreshing the source schema for ${e.name}`,async()=>{const o=Se(r,i);if(o)return{reusedExistingRefresh:!0,waitedForLock:n,metadata:o};const u=await fe(e,r.sourceUrl,r.cachePath,{reporter:t.reporter||null,logRecord:c?._logRecord||null,env:t.env||process.env,signal:t.signal}),m=pe(e,r,u);return De(r.metadataPath,m),{reusedExistingRefresh:!1,waitedForLock:n,metadata:m}}),{signal:t.signal});return _(c,t,{status:"passed",summary:`source schema refresh: ${s.relative(e.productDir,r.cachePath)}`}),a}catch(a){throw _(c,t,{status:"failed",summary:`source schema refresh: ${s.relative(e.productDir,r.cachePath)}`,error:$(a,"message")||a}),a}}async function fe(e,r,t,c={}){const a=ne(r);h.mkdirSync(s.dirname(t),{recursive:!0});const n=se(".source-schema-",{parentDir:s.dirname(t)}),i=s.join(n,s.basename(t));try{const o=await ee(e,a.dumpUrl,{env:c.env||process.env,pgApplicationName:b(e.name),signal:c.signal});return l("schema.sourceDumps"),await Z(e,i,a.dumpUrl,{...c,image:o,pgApplicationName:b(e.name)}),h.renameSync(i,t),{resolution:a,pgDump:{args:[...Y],applicationName:b(e.name),image:o}}}finally{ie(n)}}function pe(e,r,t){return{refreshedAt:new Date().toISOString(),serviceName:e.name,envName:r.envName,cacheKey:r.cacheKey,cacheKind:r.cacheKind,cachePath:s.relative(e.productDir,r.cachePath),repo:r.repoState,sourceUrl:t.resolution.metadata,pgDump:t.pgDump}}function Se(e,r){if(!h.existsSync(e.cachePath))return null;const t=E(e.metadataPath);if(!_e(t))return null;const c=Date.parse(t.refreshedAt);return!Number.isFinite(c)||c<r?null:t}function b(e){return`testkit:schema-source:${e.replace(/[^a-zA-Z0-9._:-]+/g,"-").slice(0,24)||"service"}`}function ye(e){const r=h.readFileSync(e);return l("schema.cacheFilesRead"),l("schema.cacheBytesRead",r.length),te(r.toString("utf8"))}function ve(e){if(!e.cacheExists)return!0;if(e.sourceSchema.refresh.mode==="auto")return e.ci;if(e.sourceSchema.refresh.mode==="ttl"){const r=k(e.metadataPath),t=r?Date.parse(r):0;return!Number.isFinite(t)||t<=0?!0:Date.now()-t>=e.sourceSchema.refresh.ttlSeconds*1e3}return!0}function S(e,r,t={}){if(!r?.active)return;const c=new Date().toISOString(),a=T(r.indexPath).filter(o=>o.cacheKey!==r.cacheKey),n=k(r.metadataPath);a.push({cacheKey:r.cacheKey,kind:r.cacheKind,commitSha:r.repoState.commitSha||null,branch:r.repoState.branch||null,detached:r.repoState.detached,dirty:r.repoState.dirty,dirtyFingerprint:r.repoState.dirtyFingerprint||null,contentFingerprint:r.repoState.contentFingerprint||null,remoteUrl:r.repoState.remoteUrl||null,repository:r.repoState.repository,cachePath:s.relative(e.productDir,r.cachePath),refreshedAt:t.refreshed?c:n,lastUsedAt:c});const i=Pe(e,r,a);x(r.indexPath,{version:1,entries:i})}function Pe(e,r,t){if(t.length<=L)return t;const c=[...t].sort((n,i)=>Date.parse(i.lastUsedAt||"0")-Date.parse(n.lastUsedAt||"0")),a=new Set(c.slice(0,L).map(n=>n.cacheKey));a.add(r.cacheKey);for(const n of t){if(a.has(n.cacheKey))continue;const i=n.cachePath?s.join(e.productDir,n.cachePath):null,o=i?s.resolve(i):null,u=s.resolve(r.rootDir);if(!i||!o||o!==u&&!o.startsWith(`${u}${s.sep}`))continue;const m=U(e.productDir,s.dirname(i),`Service "${e.name}" pruned source schema cache directory`);h.rmSync(m,{recursive:!0,force:!0})}return t.filter(n=>a.has(n.cacheKey))}function De(e,r){x(e,r)}function x(e,r){h.mkdirSync(s.dirname(e),{recursive:!0}),g(e),h.writeFileSync(e,Re(r))}function E(e){if(!e||!h.existsSync(e))return null;g(e);try{return JSON.parse(h.readFileSync(e,"utf8"))}catch{return null}}function O(e){return v(e.productDir,"results","schema")}function g(e){if(!h.existsSync(e))return;const r=h.lstatSync(e);if(r.isSymbolicLink()||!r.isFile())throw new Error(`Refusing unsafe source schema state file at ${e}`)}function we(e){return`${y(e.runtimeLabel||e.name)}__${y(e.name)}`}function be(e){const r=O(e);if(!h.existsSync(r))return;const t=y(e.name);for(const c of h.readdirSync(r))xe(c,t)&&h.rmSync(s.join(r,c),{force:!0})}function xe(e,r){for(const t of["__source-schema.sql","__local-replay-schema.sql","__schema.diff","__schema.meta.json"])if(e.endsWith(t))return e.slice(0,-t.length).endsWith(`__${r}`);return!1}function Ee(e,r,t,c){const a=O(e);h.mkdirSync(a,{recursive:!0});const n=we(e),i=s.join(a,`${n}__source-schema.sql`),o=s.join(a,`${n}__local-replay-schema.sql`),u=s.join(a,`${n}__schema.diff`),m=s.join(a,`${n}__schema.meta.json`);return h.writeFileSync(i,t),h.writeFileSync(o,c),h.writeFileSync(u,buildUnifiedDiffText(t,c,i,o)),x(m,ge(e,r)),{sourceCachePath:r.cachePath,sourcePath:i,localPath:o,diffPath:u,metaPath:m}}function ge(e,r){const t=k(r.metadataPath);return{version:1,createdAt:new Date().toISOString(),serviceName:e.name,runtimeLabel:e.runtimeLabel||e.name,sourceSchema:{envName:r.envName||null,cacheKey:r.cacheKey||null,cacheKind:r.cacheKind||null,cachePath:s.relative(e.productDir,r.cachePath),refreshedAt:t},repo:r.repoState}}export function buildUnifiedDiffText(e,r,t="source",c="target"){if(e===r)return"";const{createTwoFilesPatch:a}=ue();return a(t,c,p(e),p(r),"","",{context:3})}function ke(e,r){return e==="fail"||e==="warn-cache"?e:r.CI==="true"?"fail":"warn-cache"}function Ce(e,r){const t={...e.testkit.serviceEnv,...e.testkit.local?.env};for(const c of r){const a=t[c];if(typeof a!="string")continue;const n=V(a).find(i=>(i.serviceName??e.name)===e.name);if(n)throw new Error(`Service "${e.name}" env ${c} uses ${n.placeholder}, which is the database the source schema verifies. Point it at another service's database or an external database URL.`)}}function y(e){return e.trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||"schema"}function T(e){const r=E(e);return!f(r)||!Array.isArray(r.entries)?[]:r.entries.filter(Ae)}function k(e){const r=E(e);return f(r)&&typeof r.refreshedAt=="string"?r.refreshedAt:null}function Re(e){return`${p(JSON.stringify(e,null,2))}
|
|
3
|
+
`}function H(e){return!!e.sourceUrl}function Ae(e){return!f(e)||typeof e.cacheKey!="string"||e.cacheKey.length===0?!1:d(e.branch)&&d(e.cachePath)&&d(e.commitSha)&&d(e.contentFingerprint)&&W(e.detached)&&W(e.dirty)&&d(e.dirtyFingerprint)&&d(e.kind)&&d(e.lastUsedAt)&&d(e.refreshedAt)&&d(e.remoteUrl)&&Ke(e.repository)}function Ke(e){return e==null||f(e)&&typeof e.host=="string"&&typeof e.owner=="string"&&typeof e.name=="string"}function _e(e){return f(e)&&typeof e.refreshedAt=="string"&&(e.sourceUrl===void 0||$e(e.sourceUrl))}function $e(e){return f(e)&&typeof e.originalHost=="string"&&q(e.originalClassification)&&typeof e.resolvedHost=="string"&&q(e.resolvedClassification)&&typeof e.rewritten=="boolean"}function q(e){return e==="neon-pooler"||e==="neon-direct"||e==="unsupported-pooler"||e==="unknown-direct"}function d(e){return e===void 0||he(e)}function W(e){return e===void 0||typeof e=="boolean"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import{errorMessage as l}from"../kernel/error-serialization.js";const s=".neon.tech",i="-pooler.",c=/(^|[-.])(pgbouncer|pooler)([-.]|$)/i;export function resolveSourceSchemaDumpUrl(o){const e=d(o),r=classifyDatabaseHost(e.hostname);if(r.kind==="neon-pooler"){const n=new URL(e.url.href);return n.hostname=e.hostname.replace(i,"."),a({original:e,resolved:n,originalClassification:r,resolvedClassification:classifyDatabaseHost(n.hostname),rewritten:!0,notice:"Source schema URL uses Neon pooler; Testkit is using the direct endpoint for pg_dump."})}if(r.kind==="unsupported-pooler")throw new Error(["Refusing to run pg_dump through a pooled source database URL.",`Host: ${e.hostname}`,"Configure a direct source database URL."].join(`
|
|
2
|
-
`));return a({original:e,resolved:e.url,originalClassification:r,resolvedClassification:r,rewritten:!1,notice:null})}export function classifyDatabaseHost(o){const e=o.toLowerCase();return e.endsWith(s)&&e.includes(i)?{kind:"neon-pooler",host:e}:e.endsWith(s)?{kind:"neon-direct",host:e}:c.test(e)?{kind:"unsupported-pooler",host:e}:{kind:"unknown-direct",host:e}}
|
|
2
|
+
`));return a({original:e,resolved:e.url,originalClassification:r,resolvedClassification:r,rewritten:!1,notice:null})}export function classifyDatabaseHost(o){const e=o.toLowerCase();return e.endsWith(s)&&e.includes(i)?{kind:"neon-pooler",host:e}:e.endsWith(s)?{kind:"neon-direct",host:e}:c.test(e)?{kind:"unsupported-pooler",host:e}:{kind:"unknown-direct",host:e}}function d(o){try{const e=new URL(o);if(!e.hostname)throw new Error("missing host");return{url:e,hostname:e.hostname.toLowerCase()}}catch(e){throw new Error(`Invalid source database URL: ${l(e)}`,{cause:e})}}function a({original:o,resolved:e,originalClassification:r,resolvedClassification:n,rewritten:t,notice:u}){return{originalUrl:o.url.href,dumpUrl:e.href,rewritten:t,notice:u,metadata:{originalHost:o.hostname,originalClassification:r.kind,resolvedHost:e.hostname.toLowerCase(),resolvedClassification:n.kind,rewritten:t}}}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
`)}})],
|
|
1
|
+
import w from"fs";import{buildTemplateExecutionEnv as p,collectConfiguredInputs as E}from"../steps/index.js";import{runConfiguredSteps as T}from"./configured-steps.js";import"../evidence/setup-progress.js";import{captureOutput as A}from"../process/index.js";import{runDockerPsql as v,startDockerPgDump as b}from"./postgres/index.js";import{callCap as m,describeCap as l}from"./call-caps.js";const d=String,D=["migrate","seed","verify"];export function runTemplateStage(e,r,t,n={}){const o=e.testkit.database?.template?.[r]||[];return f(e,r,o,0,t,n)}export function runTemplateStep(e,r,t,n,o,s={}){return f(e,r,[t],n,o,s)}async function f(e,r,t,n,o,s){await T({...s,config:e,steps:t,env:{...p(e,{},process.env),DATABASE_URL:o},labelPrefix:`template:${r}`,startIndex:n})}export function collectTemplateInputs(e,r={}){const t=D.flatMap(n=>r[n]||[]);return E(e,{inputs:r.inputs||[],steps:t})}export async function captureTemplateSnapshotText(e,r,t={}){return sanitizeSnapshotText(await x(e,r,t))}export async function dumpPostgresSchemaToFile(e,r,t,n={}){w.writeFileSync(r,await captureTemplateSnapshotText(e,t,n))}async function x(e,r,t){const n={...p(e,{},t.env||process.env),...S(r,{applicationName:t.pgApplicationName})},o=await C(e,PG_DUMP_ARGS,n,t);if(o.exitCode!==0)throw new Error(o.shortMessage||o.stderr||o.stdout||"pg_dump failed");return o.stdout}export const PG_DUMP_ARGS=Object.freeze(["--schema-only","--no-owner","--no-privileges"]);function g(e){return{host:e.PGHOST,port:e.PGPORT||5432,database:e.PGDATABASE,user:e.PGUSER,password:e.PGPASSWORD,sslMode:e.PGSSLMODE}}async function C(e,r,t,n={}){const o=m("schemaDump"),s=b({args:r,cwd:e.productDir,env:t,image:n.image||null,ownedContainer:n.ownedContainer||null,purpose:l("the schema-only pg_dump",o),signal:n.signal,timeoutMs:o,connection:g(t)}),a=n.reporter,i=a?.outputMode==="debug"?c=>a.writeDebugLine?.(c):null,u=n.logRecord||null,P=[A(s.stderr,{livePrefix:`[${e.runtimeLabel||e.name}:${e.name}]`,liveWriter:i,onLine(c){u&&u.write(`${new Date().toISOString()} [stderr] ${c}
|
|
2
|
+
`)}})],h=await s;return await Promise.all(P),h}export async function resolvePgDumpClientImage(e,r,t={}){const n=t.env||process.env,o=G(n);if(o)return o;const s={...n,...S(r,{applicationName:t.pgApplicationName})},a=m("serverVersionProbe"),i=await v({args:["-tAc","SHOW server_version_num"],cwd:e.productDir,env:s,purpose:l("the server version probe",a),signal:t.signal,timeoutMs:a,connection:g(s)});if(i.exitCode!==0)throw new Error(i.shortMessage||i.stderr||i.stdout||"Failed to detect source Postgres version");return postgresClientImageForServerVersion(i.stdout)}export function postgresClientImageForServerVersion(e){const r=Number(d(O(e,"")).trim());if(!Number.isFinite(r)||r<1e5)throw new Error(`Unable to determine Postgres major version from server_version_num: ${JSON.stringify(e)}`);return`postgres:${String(Math.floor(r/1e4))}`}function G(e){return e.TESTKIT_POSTGRES_CLIENT_IMAGE||null}function S(e,r={}){const t=new URL(e);return _({PGHOST:t.hostname,PGPORT:t.port||"5432",PGDATABASE:decodeURIComponent(t.pathname.replace(/^\//,"")),PGUSER:decodeURIComponent(t.username||""),PGPASSWORD:decodeURIComponent(t.password||""),PGSSLMODE:t.searchParams.get("sslmode")||void 0,PGAPPNAME:r.applicationName||void 0})}function _(e){const r={};for(const[t,n]of Object.entries(e))n!=null&&n!==""&&(r[t]=n);return r}export function sanitizeSnapshotText(e){return y(d(e).split(`
|
|
3
3
|
`)).filter(r=>{const t=r.trim();return!t.startsWith("-- Dumped from database version ")&&!t.startsWith("-- Dumped by pg_dump version ")&&t!=="SET transaction_timeout = 0;"&&!t.startsWith("\\restrict ")&&!t.startsWith("\\unrestrict ")}).join(`
|
|
4
|
-
`)}function
|
|
4
|
+
`)}function y(e){const r=[];for(let t=0;t<e.length;t+=1){if(e[t]?.trim()==="--"&&e[t+1]?.startsWith("-- Name: public; Type: SCHEMA;")){let n=t+1;for(;n<e.length&&!e[n]?.includes("*not* creating schema");)n+=1;if(n<e.length){for(;n+1<e.length&&e[n+1]?.trim()==="";)n+=1;t=n;continue}}r.push(e[t])}return r}function O(e,r){return!!e?e:r}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{redactSensitiveText as
|
|
1
|
+
import{redactSensitiveText as n}from"../kernel/redaction.js";export const MAX_OPERATION_REASON_LENGTH=240;export function normalizeOperationReason(t,e="requested"){return r(t)||r(e)||"requested"}function r(t){const e=n(t).replace(/\s+/g," ").trim();return e.length<=MAX_OPERATION_REASON_LENGTH?e:`${e.slice(0,MAX_OPERATION_REASON_LENGTH-3)}...`}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{RUN_TYPE_ORDER as f,
|
|
1
|
+
import{RUN_TYPE_ORDER as f,isPublicTestType as m,isRunType as T,publicTestTypeListText as l}from"./test-types.js";import"./identifiers.js";import{RETIRED_TEST_TYPES as a,rejectRetiredTestType as y}from"./retired-test-types.js";const d=String;export function normalizeTypeValues(e=[],r=a){const t=[];for(const s of e)if(s!=null)for(const n of d(s).split(",")){const i=n.trim();if(!i)continue;const p=i;if(y(p,`Type "${i}"`,r),!T(p))throw new Error(`Unknown type "${i}". Expected one of: ${l({includeAll:!0})}.`);t.push(p)}if(t.length===0)return["all"];const o=[...new Set(t)];if(o.includes("all")&&o.length>1)throw new Error('"--type all" cannot be combined with other types.');return o.sort((s,n)=>f.indexOf(s)-f.indexOf(n))}export function parseSuiteSelectors(e=[],r=a){const t=[];for(const o of e)if(o!=null)for(const s of d(o).split(",")){const n=s.trim();if(!n)continue;const i=n.indexOf(":");if(i<0){t.push({kind:"plain",name:n,raw:n});continue}const p=n.slice(0,i),u=n.slice(i+1).trim(),c=p.trim();if(y(c,`Suite selector "${n}"`,r),!m(c))throw new Error(`Unknown suite selector type "${p}". Expected one of: ${l()}.`);if(!u)throw new Error(`Invalid suite selector "${n}". Expected "<type>:<suite-name>".`);t.push({kind:"typed",testType:c,name:u,raw:`${c}:${u}`})}return t}export function suiteKey(e){return`${e.testType??""}:${e.name}`}export function parseTypeOption(e,r=null){const t=[];if(r&&t.push(r),Array.isArray(e)){const o=e;t.push(...o)}else e&&t.push(e);return normalizeTypeValues(t)}export function parseSuiteOption(e){const r=Array.isArray(e)?e:e?[e]:[];return parseSuiteSelectors(r)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{requireNativeEvidenceJson as
|
|
1
|
+
import{requireNativeEvidenceJson as H}from"./native-report.js";import g from"node:crypto";import{constants as N}from"node:fs";import d from"node:fs/promises";import E from"node:path";import{recordEvidenceWrite as S}from"../../process/files/evidence-writes.js";import{readOnlyNoFollowOpenFlags as O,sameFileSnapshot as z,samePathAndHandleFileSnapshot as I}from"../../process/files/file-snapshot.js";const R=64*1024;export const evidenceFileAccess={lstat:async t=>d.lstat(t,{bigint:!0}),openReadOnly:async t=>d.open(t,O(process.platform,N)),platform:process.platform,readChunk:async(t,e)=>(await t.read(e,0,e.byteLength,null)).bytesRead,statHandle:async t=>t.stat({bigint:!0})};export function stableEvidenceFromBuffer(t){return{contents:t,sha256:g.createHash("sha256").update(t).digest("hex"),sizeBytes:t.byteLength}}export async function readStableJsonFile(t,e,n,a){let o;try{o=await readStableEvidenceFile(t,e,n,a)}catch(i){if(isMissingFileError(i))return{file:null,value:null};throw i}let r;try{r=JSON.parse(o.contents.toString("utf8"))}catch(i){throw new Error(`${n} is not valid JSON`,{cause:i})}return H(r,n),{file:o,value:r}}export async function readStableEvidenceFile(t,e,n,a,o){const r=o??await a.lstat(t);l(r,e,n);const i=await a.openReadOnly(t);try{const c=await a.statHandle(i);l(c,e,n),F(r,c,n,a.platform);const w=[],s=await $(t,i,r,c,e,n,a,u=>w.push(Buffer.from(u)));return{contents:Buffer.concat(w,s.sizeBytes),...s}}finally{await i.close()}}export async function writeOwnedEvidenceFile(t,e,n){const a=E.join(t,e),o=E.join(t,`.${e}.${g.randomUUID()}.tmp`);let r=null;try{return r=await d.open(o,"wx",384),await r.writeFile(n),await r.sync(),await r.close(),r=null,S(a),await d.rename(o,a),process.platform!=="win32"&&await d.chmod(a,384),a}catch(i){throw r!==null&&await r.close().catch(()=>{}),await d.rm(o,{force:!0}).catch(()=>{}),i}}export async function snapshotStableEvidenceFile(t,e,n,a,o,r,i){const c=await a.lstat(t);l(c,e,n),m(o,c,n);const w=await a.openReadOnly(t),s=E.join(r,i),u=E.join(r,`.${i}.${g.randomUUID()}.tmp`);let p=null;try{const f=await a.statHandle(w);l(f,e,n),F(c,f,n,a.platform);const h=await d.open(u,"wx",384);p=h;const y=await $(t,w,c,f,e,n,a,v=>h.write(v));return await h.sync(),await h.close(),p=null,S(s),await d.rename(u,s),process.platform!=="win32"&&await d.chmod(s,384),{filePath:s,identity:y}}catch(f){throw p!==null&&await p.close().catch(()=>{}),await d.rm(u,{force:!0}).catch(()=>{}),f}finally{await w.close()}}export function evidenceArtifact(t,e,n,a,o,r){return{contentType:n,kind:a,metadata:{sha256:e.sha256,sizeBytes:e.sizeBytes},name:o,path:t,summary:r}}async function $(t,e,n,a,o,r,i,c){const w=g.createHash("sha256");let s=0;const u=Buffer.allocUnsafe(R);for(;;){const h=await i.readChunk(e,u);if(k(h,u.byteLength,r),h===0)break;if(s+=h,s>o)throw new Error(`${r} exceeds its ${o.toString()} byte limit`);const y=u.subarray(0,h);await c(y),w.update(y)}const p=await i.statHandle(e),f=await i.lstat(t);if(l(p,o,r),l(f,o,r),m(a,p,r),m(n,f,r),F(f,p,r,i.platform),BigInt(s)!==a.size)throw new Error(`${r} changed while it was read`);return{sha256:w.digest("hex"),sizeBytes:s}}function k(t,e,n){if(!Number.isSafeInteger(t)||t<0||t>e)throw new Error(`${n} returned an invalid read length`)}function l(t,e,n){if(!t.isFile()||t.isSymbolicLink())throw new Error(`${n} must be a regular non-symbolic-link file`);if(t.size<0n||t.size>BigInt(e))throw new Error(`${n} exceeds its ${e.toString()} byte limit`)}function m(t,e,n){if(!z(t,e))throw new Error(`${n} changed while it was read`)}function F(t,e,n,a){if(!I(t,e,a))throw new Error(`${n} changed while it was read`)}export function isMissingFileError(t){return t instanceof Error&&"code"in t&&t.code==="ENOENT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import n from"node:fs";import{fileURLToPath as o}from"node:url";import{currentPlatformTarget as i}from"../../process/host/platform.js";import{isUnknownRecord as a}from"../../kernel/unknown-values.js";const s=f();export function engineEvidenceIdentity(t){const e=i();return Object.freeze({engineId:t.engineId,installationKind:t.installationKind,nodeVersion:process.versions.node,profileId:t.profile.id,target:Object.freeze({arch:e.arch,platform:e.platform}),testkitVersion:s})}function f(){const t=o(new URL("../../../package.json",import.meta.url));let e;try{e=JSON.parse(n.readFileSync(t,"utf8"))}catch(r){throw new TypeError(`Root Testkit package manifest could not be read from ${t}`,{cause:r})}if(!a(e)||e.name!=="@gfdelarue/testkit")throw new TypeError(`Root Testkit package manifest at ${t} has an invalid package identity`);if(typeof e.version!="string"||!/^(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u.test(e.version))throw new TypeError(`Root Testkit package manifest at ${t} has an invalid version`);return e.version}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isEngineTaskDeadlineExceededReason as
|
|
1
|
+
import{isEngineTaskDeadlineExceededReason as l}from"../api/index.js";import"../../domain/test-outcomes.js";import{toError as c}from"../../kernel/error-serialization.js";import{redactedPrefix as u}from"../../kernel/redaction.js";export async function executeEngineTasks(e,t,n,r,i){const a=[];for(const o of t){e.observer?.taskStarted(o.task);const s=Date.now(),d=taskBeforeStartOutcome(o.task,e.signal,s,n);d!==null?a.push(d):o.preparationFailure!==null?a.push(terminalOutcome(o.task.id,"failed",s,emptyEvidence(),o.preparationFailure)):a.push(await r(o,s))}return engineExecutionResult(e,a,i)}export function engineExecutionResult(e,t,n,r=[]){for(const i of t)e.observer?.evidenceRecorded(i.taskId,i.evidence),e.observer?.taskFinished(i);return{evidence:{artifacts:t.flatMap(i=>i.evidence.artifacts),logs:t.flatMap(i=>i.evidence.logs),metrics:t.flatMap(i=>i.evidence.metrics),native:n},invocationId:e.invocationId,outcomes:t,...r.length===0?{}:{runErrors:r}}}export function taskBeforeStartOutcome(e,t,n,r){return e.deadlineMs<=n||l(t.reason)?terminalOutcome(e.id,"failed",n,emptyEvidence(),taskTimeoutFailure(e.timeoutMs,r)):t.aborted?terminalOutcome(e.id,"cancelled",n,emptyEvidence(),{code:`${r.codePrefix}_CANCELLED`,message:`${r.label} task was cancelled before its process started`}):null}export function taskTimeoutFailure(e,t){return{code:`${t.codePrefix}_TASK_TIMEOUT`,details:{timeoutMs:e},message:`${t.label} task exceeded its ${e.toString()}ms timeout`}}export function managedProcessFailure(e,t,n=null){return e.terminationError===null?null:{classification:"infra",code:`${t.codePrefix}_PROCESS_TERMINATION_INCOMPLETE`,details:{cancelled:e.cancelled,...n===null?{}:{evidenceCollectionFailure:errorDetails(n)},outputLimitExceeded:e.outputLimitExceeded,timedOut:e.timedOut},message:`${t.label} managed-process termination was incomplete: ${e.terminationError.message}`,...e.terminationError.stack===void 0?{}:{stack:e.terminationError.stack}}}export function evidenceCollectionFailure(e,t,n){const r=c(n);return managedProcessFailure(e,t,r)??{...failureFromError(r,`${t.codePrefix}_EVIDENCE_FAILED`),classification:"infra"}}export function taskOrchestrationEnvironment(e,{orchestration:t}){return{TESTKIT_INVOCATION_ID:e,TESTKIT_LEASE_DIR:t.lease.directory,TESTKIT_LEASE_ID:t.lease.id,TESTKIT_MODE:t.mode,...t.runtimeId===void 0?{}:{TESTKIT_RUNTIME_ID:t.runtimeId}}}export class ConsumerPreparationError extends Error{constructor(t,n){const r=c(n);super(r.message,{cause:r}),this.name=t}}export async function consumerPreparationBoundary(e,t){try{return await t()}catch(n){throw new ConsumerPreparationError(e,n)}}export function classifyManagedProcessResult(e,t,n,r){const i=o=>({failure:o,status:"failed"}),a=managedProcessFailure(e,r);return a!==null?i(a):e.timedOut||n?i(taskTimeoutFailure(t,r)):e.cancelled?{failure:{code:`${r.codePrefix}_CANCELLED`,message:`${r.label} task was cancelled`},status:"cancelled"}:e.outputLimitExceeded?i({code:`${r.codePrefix}_OUTPUT_LIMIT`,message:`${r.label} task exceeded the bounded stdout/stderr capture limit`}):e.error!==null?i({...failureFromError(e.error,`${r.codePrefix}_PROCESS_START_FAILED`),classification:"infra"}):null}export function processExitDetails(e){return{...e.exitCode===null?{}:{exitCode:e.exitCode},...e.exitSignal===null?{}:{exitSignal:e.exitSignal}}}export function processExitFailure(e,t,n=null,r=t.label){return{failure:{code:`${t.codePrefix}_PROCESS_FAILED`,details:processExitDetails(e),message:n??(e.exitCode===null?`${r} process terminated by ${e.exitSignal??"an unknown signal"}`:`${r} process exited with code ${e.exitCode.toString()}`)},status:"failed"}}const f=/^\(node:\d+\) (?:\[\w+\] )?\w*Warning: /u,m=/^\(Use `node --trace-\w+ \.\.\.` /u;export function firstOutputLine(e){const t=e.split(/\r?\n/u).map(n=>n.trim()).find(n=>n!==""&&!f.test(n)&&!m.test(n));return t===void 0?null:u(t,4e3)}export function terminalOutcome(e,t,n,r,i){const a=Date.now();return{durationMs:Math.max(0,a-n),evidence:r,failure:i,finishedAtMs:a,startedAtMs:n,status:t,taskId:e}}export function processOutputEvidence(e){return{artifacts:[],logs:[...e.stdout.length===0?[]:[{stream:"stdout",text:e.stdout}],...e.stderr.length===0?[]:[{stream:"stderr",text:e.stderr}]],metrics:[]}}export function emptyEvidence(){return{artifacts:[],logs:[],metrics:[]}}export function failureFromError(e,t){const n=c(e);return{code:t,message:n.message,...n.stack===void 0?{}:{stack:n.stack}}}export function errorDetails(e){return{message:e.message,name:e.name,...e.stack===void 0?{}:{stack:e.stack}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import o from"node:fs/promises";import d from"node:path";import{makeTempDirAsync as m}from"../../process/files/temp-dir.js";const n=new Map,C=128,l=new Set(["NODE_COMPILE_CACHE","NODE_DISABLE_COMPILE_CACHE","NODE_V8_COVERAGE"]);export async function getNodeStartupCacheEnvironment(t,a){if(t===void 0||!d.isAbsolute(t)||Object.keys(a).some(e=>l.has(e.toUpperCase())))return{};try{await o.mkdir(t,{mode:448,recursive:!0});const e=await o.lstat(t);if(!e.isDirectory()||e.isSymbolicLink())return{};const E=await o.realpath(t),s=`${E}\0${u(e)}`;let r=n.get(s);if(r!==void 0){const i=await r,c=await o.lstat(i.directory).catch(()=>null);if(c!==null&&p(c)&&u(c)===i.identity&&await o.realpath(i.directory)===i.directory)return{NODE_COMPILE_CACHE:d.toNamespacedPath(i.directory)};n.delete(s)}if(r=n.get(s),r===void 0&&(r=y(E),n.set(s,r),n.size>C)){const i=n.keys().next().value;i!==void 0&&n.delete(i)}const f=await r;return{NODE_COMPILE_CACHE:d.toNamespacedPath(f.directory)}}catch{return{}}}async function y(t){const a=await m("node-compile-",{parentDir:t}),e=await o.lstat(a);if(!p(e))throw new Error("Node startup cache directory is not private");return{directory:a,identity:u(e)}}function u(t){return`${t.dev.toString()}:${t.ino.toString()}:${t.birthtimeMs.toString()}`}function p(t){return t.isDirectory()&&!t.isSymbolicLink()&&(process.platform==="win32"||(t.mode&63)===0&&t.uid===process.getuid?.())}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import m from"node:crypto";import o from"node:fs/promises";import s from"node:path";import{isPathWithin as w}from"../../process/files/paths.js";import{makeTempDirAsync as y}from"../../process/files/temp-dir.js";export async function createInvocationWorkspace(r,t,e,i){const a=await requireRootDirectory(r.workspace.rootDirectory);requireAbsoluteWorkspacePath(r.workspace.temporaryDirectory,"temporary directory"),requireAbsoluteWorkspacePath(r.workspace.artifactDirectory,"artifact directory"),await o.mkdir(r.workspace.temporaryDirectory,{recursive:!0}),await o.mkdir(r.workspace.artifactDirectory,{recursive:!0});const c=await o.realpath(r.workspace.temporaryDirectory),p=await o.realpath(r.workspace.artifactDirectory),n=await y(`${t}-${safeSegment(r.invocationId)}-`,{parentDir:c});return e.defer(()=>i(n)),{artifactRoot:p,invocationDirectory:n,rootDirectory:a}}export async function requireRootDirectory(r){requireAbsoluteWorkspacePath(r,"root directory");const t=await o.realpath(r);if(!(await o.stat(t)).isDirectory())throw new Error("Engine execution root directory must be a directory");return t}export function requireAbsoluteWorkspacePath(r,t){if(!s.isAbsolute(r))throw new Error(`Engine execution ${t} must be absolute`)}export async function requireConsumerFile(r,t,e){const i=s.resolve(r,t),a=await o.realpath(i);if(!w(r,a))throw new Error(`${e} must stay within the consumer root`);if(!(await o.stat(a)).isFile())throw new Error(`${e} must be a regular file`);return a}export function safeSegment(r){const t=r.trim().toLowerCase().replace(/[^a-z0-9._-]+/gu,"-").replace(/^-+|-+$/gu,"").slice(0,80);return t.length>0?t:m.createHash("sha256").update(r).digest("hex").slice(0,16)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export{};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import p from"node:fs";import{createRequire as y}from"node:module";import l from"node:path";import{fileURLToPath as $,pathToFileURL as b}from"node:url";import{isPathWithin as m}from"../../process/files/paths.js";export const TESTKIT_PACKAGE_NAME="@gfdelarue/testkit";const E=$(new URL("../../../package.json",import.meta.url)),k=/^(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/u;export class RootPackageDeclarationError extends Error{code="TESTKIT_ROOT_PACKAGE_DECLARATION_INVALID";constructor(t,e){super(t,e),this.name="RootPackageDeclarationError"}}export class MissingDirectPackageError extends Error{code="TESTKIT_DIRECT_PACKAGE_MISSING";packageName;version;constructor(t,e,n,i,r){super(`Testkit direct dependency ${t}@${e} could not resolve its public entrypoint ${JSON.stringify(n)} from ${TESTKIT_PACKAGE_NAME}. Run a clean npm install from the consumer lockfile${i?" without omitting optional dependencies":""}.`,r),this.name="MissingDirectPackageError",this.packageName=t,this.version=e}}export class BrokenPackageInstallationError extends Error{code="TESTKIT_PACKAGE_INSTALLATION_BROKEN";packageName;version;constructor(t,e,n,i){super(`Testkit dependency ${t}@${e} is installed but broken: ${n}. Run a clean npm install from the consumer lockfile.`,i),this.name="BrokenPackageInstallationError",this.packageName=t,this.version=e}}export class RootPackageResolver{packageJsonPath;packageRoot;manifest;requireFromRoot;constructor(t={}){const e=l.resolve(t.packageJsonPath??E);this.packageJsonPath=u(e,`${TESTKIT_PACKAGE_NAME} package manifest`,(n,i)=>new RootPackageDeclarationError(n,d(i))),this.packageRoot=l.dirname(this.packageJsonPath),this.manifest=N(this.packageJsonPath),this.requireFromRoot=y(this.packageJsonPath)}resolveImportFromRoot(t){const e=this.declaredVersion(t);return g({label:TESTKIT_PACKAGE_NAME,manifestRequire:this.requireFromRoot,resolveEntrypoint:n=>A(import.meta.resolve(n)),rootSelection:!0},t,e)}resolveRequireFromRoot(t){const e=this.declaredVersion(t);return g({label:TESTKIT_PACKAGE_NAME,manifestRequire:this.requireFromRoot,resolveEntrypoint:this.requireFromRoot.resolve,rootSelection:!0},t,e)}resolveRequireFromPackage(t,e){const n=this.declaredVersion(e),i=y(t.manifestPath);return g({label:`${t.name}@${t.version}`,manifestRequire:i,resolveEntrypoint:i.resolve,rootSelection:!1},e,n)}resolveRequirePublicEntrypoint(t,e){v(t.name,e);let n;try{n=y(t.manifestPath).resolve(e)}catch(i){throw a(t.name,t.version,`public entrypoint ${JSON.stringify(e)} could not be resolved`,i)}return J(n,t.root,t.name,t.version,e)}resolveRootPublicEntrypoint(t){v(TESTKIT_PACKAGE_NAME,t);let e;try{e=this.requireFromRoot.resolve(t)}catch(i){throw new RootPackageDeclarationError(`${TESTKIT_PACKAGE_NAME} public entrypoint ${JSON.stringify(t)} could not be resolved from its package anchor`,{cause:i})}const n=u(e,`${TESTKIT_PACKAGE_NAME} public entrypoint ${JSON.stringify(t)}`,(i,r)=>new RootPackageDeclarationError(i,d(r)));if(!m(this.packageRoot,n))throw new RootPackageDeclarationError(`${TESTKIT_PACKAGE_NAME} public entrypoint ${JSON.stringify(t)} resolves outside its package root`);return n}brokenInstallation(t,e,n){return a(t.name,t.version,e,n)}declaredVersion(t){v(t.packageName,t.publicEntrypoint);const e=this.manifest[t.dependencyGroup]?.[t.packageName];if(typeof e!="string"||!k.test(e))throw new RootPackageDeclarationError(`${TESTKIT_PACKAGE_NAME} ${t.dependencyGroup}.${t.packageName} must be an exact version; received ${JSON.stringify(e)}`);return e}}export async function importResolvedPackageEntrypoint(o){try{return await import(b(o.entrypoint).href)}catch(t){throw a(o.name,o.version,`importing its resolved public entrypoint ${JSON.stringify(o.entrypoint)} failed`,t)}}function g(o,t,e){let n;try{n=o.resolveEntrypoint(t.publicEntrypoint)}catch(s){throw o.rootSelection&&_(s,t.packageName,t.publicEntrypoint)?new MissingDirectPackageError(t.packageName,e,t.publicEntrypoint,t.dependencyGroup==="optionalDependencies",{cause:s}):a(t.packageName,e,o.rootSelection?`its public entrypoint ${JSON.stringify(t.publicEntrypoint)} could not be resolved from the root package anchor`:`its public entrypoint ${JSON.stringify(t.publicEntrypoint)} could not be resolved from the selected ${o.label} package chain`,s)}let i;try{i=u(n,`${t.packageName} public entrypoint ${JSON.stringify(t.publicEntrypoint)}`,(s,h)=>a(t.packageName,e,s,h))}catch(s){throw s instanceof BrokenPackageInstallationError?s:a(t.packageName,e,`its public entrypoint ${JSON.stringify(t.publicEntrypoint)} is invalid`,s)}const r=S(o.manifestRequire,i,t.packageName,e),c=l.dirname(r);if(!m(c,i))throw a(t.packageName,e,`public entrypoint ${JSON.stringify(t.publicEntrypoint)} resolves outside its owning package root`);const f=P(r,t.packageName,e);return Object.freeze({entrypoint:i,manifestPath:r,name:f.name,root:c,version:f.version})}function S(o,t,e,n){try{const r=u(o.resolve(`${e}/package.json`),`${e} package manifest`,(f,s)=>a(e,n,f,s)),c=l.dirname(r);if(!m(c,t))throw a(e,n,"the selected public entrypoint resolves outside the exported package manifest root");return r}catch(r){if(r instanceof BrokenPackageInstallationError)throw r;if(!F(r,e))throw a(e,n,"exported package manifest resolution failed",r)}let i=l.dirname(t);for(;i!==l.parse(i).root;){const r=l.join(i,"package.json");if(T(r,e,n)){const c=u(r,`${e} package manifest`,(s,h)=>a(e,n,s,h)),f=R(c,e,n);if(f.name!==e)throw a(e,n,`manifest discovery reached incompatible package boundary ${JSON.stringify(f.name)} before ${JSON.stringify(e)}`);return c}i=l.dirname(i)}throw a(e,n,"no owning package manifest was found for the selected public entrypoint")}function N(o){const t=O(o,`${TESTKIT_PACKAGE_NAME} package manifest`,(e,n)=>new RootPackageDeclarationError(e,d(n)));if(t.name!==TESTKIT_PACKAGE_NAME)throw new RootPackageDeclarationError(`Root package manifest must name ${JSON.stringify(TESTKIT_PACKAGE_NAME)}; received ${JSON.stringify(t.name)}`);if(typeof t.version!="string")throw new RootPackageDeclarationError(`Root package manifest version must be a string; received ${JSON.stringify(t.version)}`);return w(t,"dependencies"),w(t,"optionalDependencies"),t}function P(o,t,e){const n=R(o,t,e);if(n.name!==t||n.version!==e)throw a(t,e,`manifest must be exactly ${t}@${e}; received ${n.name}@${n.version}`);return n}function R(o,t,e){const n=O(o,`${t} package manifest`,(i,r)=>a(t,e,i,r));if(typeof n.name!="string"||typeof n.version!="string")throw a(t,e,`manifest name and version must be strings; received ${String(n.name)}@${String(n.version)}`);return{name:n.name,version:n.version}}function O(o,t,e){let n;try{n=JSON.parse(p.readFileSync(o,"utf8"))}catch(i){throw e(`${t} could not be read as JSON`,i)}if(typeof n!="object"||n===null||Array.isArray(n))throw e(`${t} must contain a JSON object`);return n}function w(o,t){const e=o[t];if(e!==void 0&&(typeof e!="object"||e===null||Array.isArray(e)))throw new RootPackageDeclarationError(`Root package manifest ${t} must be a JSON object`)}function v(o,t){if(o.length===0||t!==o&&!t.startsWith(`${o}/`))throw new RootPackageDeclarationError(`Public entrypoint ${JSON.stringify(t)} must belong to package ${JSON.stringify(o)}`)}function J(o,t,e,n,i){const r=u(o,`${e} public entrypoint ${JSON.stringify(i)}`,(c,f)=>a(e,n,c,f));if(!m(t,r))throw a(e,n,`public entrypoint ${JSON.stringify(i)} resolves outside its owning package root`);return r}function u(o,t,e){let n,i;try{n=p.realpathSync(o),i=p.statSync(n)}catch(r){throw e(`${t} could not be resolved as a real file`,r)}if(!i.isFile())throw e(`${t} must be a regular file`);return n}function T(o,t,e){try{return p.lstatSync(o),!0}catch(n){if(D(n))return!1;throw a(t,e,"owning package manifest discovery failed",n)}}function _(o,t,e){if(typeof o!="object"||o===null)return!1;const n=o;return(n.code==="MODULE_NOT_FOUND"||n.code==="ERR_MODULE_NOT_FOUND")&&typeof n.message=="string"&&(n.message.startsWith(`Cannot find module '${e}'`)||n.message.startsWith(`Cannot find package '${t}'`))}function A(o){let t;try{t=new URL(o)}catch(e){throw new TypeError(`Root import resolution must return an absolute URL; received ${JSON.stringify(o)}`,{cause:e})}if(t.protocol!=="file:")throw new TypeError(`Root package entrypoint must resolve to a file URL; received ${JSON.stringify(o)}`);return $(t)}function D(o){if(typeof o!="object"||o===null)return!1;const t=o.code;return t==="ENOENT"||t==="ENOTDIR"}function F(o,t){if(typeof o!="object"||o===null)return!1;const e=o;return e.code==="ERR_PACKAGE_PATH_NOT_EXPORTED"?!0:(e.code==="MODULE_NOT_FOUND"||e.code==="ERR_MODULE_NOT_FOUND")&&typeof e.message=="string"&&(e.message.startsWith(`Cannot find module '${t}/package.json'`)||e.message.startsWith(`Cannot find package '${t}'`))}function a(o,t,e,n){return new BrokenPackageInstallationError(o,t,e,d(n))}function d(o){return o===void 0?void 0:{cause:o}}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import{ownedGeneratedScript as
|
|
1
|
+
import{ownedGeneratedScript as t}from"./generated-performance.js";import{inspectManagedNodeModuleResolutionPolicy as i}from"./managed-module-resolution-policy.js";export function createManagedNodeModuleResolution(e){return Object.freeze({bootstrapSource:n(i(e))})}function n(e){return t(`
|
|
2
2
|
const fs = require("node:fs");
|
|
3
3
|
const path = require("node:path");
|
|
4
4
|
const { isBuiltin, registerHooks } = require("node:module");
|
|
5
5
|
const { fileURLToPath, pathToFileURL } = require("node:url");
|
|
6
6
|
|
|
7
7
|
let canonicalPaths = null;
|
|
8
|
-
const POLICY = Object.freeze(${JSON.stringify(
|
|
8
|
+
const POLICY = Object.freeze(${JSON.stringify(e)});
|
|
9
9
|
const CONSUMER_ROOTS = Object.freeze(
|
|
10
10
|
POLICY.consumerRoots.map((root) => verifiedDirectory(root, "consumer root")),
|
|
11
11
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{makeTempDirAsync as U}from"../../../process/files/temp-dir.js";import{toError as B}from"../../../kernel/error-serialization.js";import{isDeepStrictEqual as te}from"node:util";import{classifyManagedProcessResult as re,engineExecutionResult as ne,terminalOutcome as P,emptyEvidence as L,failureFromError as j,firstOutputLine as K,processExitDetails as ie,processExitFailure as W,taskBeforeStartOutcome as oe,managedProcessFailure as z,taskTimeoutFailure as se}from"../../adapters/execution.js";import{requireRootDirectory as ae,requireAbsoluteWorkspacePath as H,requireConsumerFile as ce,safeSegment as J}from"../../adapters/workspace.js";import w from"node:fs/promises";import I from"node:path";import{pathToFileURL as de}from"node:url";import{isEngineTaskDeadlineExceededReason as le,resolveEngineTaskInputs as ue}from"../../api/index.js";import{createInvocationProcessEnvironment as pe,runManagedProcess as fe}from"../../../process/index.js";import{managedVitestAliases as me}from"../../installations/node-vitest.js";import{decodeVitestSelection as Ee,resolveRepositoryVitest as ve}from"../selection.js";import{collectNodeVitestEvidence as Te,readNodeVitestRunReport as Ie}from"./evidence.js";import{assertNodeVitestSpawnEnvironmentBudget as X,NODE_VITEST_RUNTIME_INPUT_FILE_ENV as ye,NODE_VITEST_RUNTIME_INPUT_SHA256_ENV as De,selectNodeVitestForwardedEnvironment as he,splitManagedNodeVitestEnvironment as we,verifyOwnedNodeVitestRuntimeInput as Ne,writeOwnedNodeVitestRuntimeInput as ge}from"../runtime/runtime-input.js";import{NODE_VITEST_DRIVER_FILE as Y,writeNodeVitestWorkspace as _e}from"./workspace.js";import{nodeVitestReportOptions as Ve}from"./reporter.js";import{engineEvidenceIdentity as Oe}from"../../adapters/evidence-identity.js";import{getNodeStartupCacheEnvironment as Re}from"../../adapters/node-startup-cache.js";const g={codePrefix:"NODE_VITEST",label:"Node/Vitest"},G="Unhandled Reporter Error";class $ extends Error{constructor(t){const r=B(t);super(r.message,{cause:r}),this.name="NodeVitestConsumerPreparationError"}}export const nodeVitestExecutorDependencies={removeOwnedDirectory:e=>w.rm(e,{force:!0,recursive:!0}),writeRuntimeInput:ge,writeWorkspace:_e};export async function executeNodeVitestEngine(e,t,r,f=nodeVitestExecutorDependencies){const u=await Se(e,t,f,r),o=Date.now(),s=u.tasks.map(c=>{e.observer?.taskStarted(c.task);const m=c.preparationFailure;return oe(c.task,e.signal,o,g)??(m===null?null:P(c.task.id,"failed",o,L(),m))}),p=u.tasks.filter((c,m)=>s[m]===null),i=[],v=p.length===0?[]:await Fe(e,u,p,o,i);let E=0;return ne(e,s.map(c=>c??v[E++]),Pe(e,u),i)}async function Se(e,t,r,f){const u=e.profile.id,o=await ae(e.workspace.rootDirectory);H(e.workspace.temporaryDirectory,"temporary directory"),H(e.workspace.artifactDirectory,"artifact directory"),await w.mkdir(e.workspace.temporaryDirectory,{recursive:!0}),await w.mkdir(e.workspace.artifactDirectory,{recursive:!0});const s=await w.realpath(e.workspace.temporaryDirectory),p=await w.realpath(e.workspace.artifactDirectory),i=await U(`node-vitest-${J(e.invocationId)}-`,{parentDir:s});f.defer(()=>r.removeOwnedDirectory(i));const v=I.join(i,"reports");await w.mkdir(v,{mode:448});const E=await pe(i);let c=[];for(const[n,l]of e.tasks.entries()){const a=ue(e,l),N=`${n.toString().padStart(3,"0")}-${J(l.id)}`,y=await U(`${N}-`,{parentDir:p});try{const x=await Z(()=>ce(o,l.file,"Node/Vitest task file")),C=I.join(v,`${N}.json`),M={file:x,reportFile:C,sourceFile:l.file,taskId:l.id},A=I.join(y,"data");c.push({artifactDirectory:y,inputs:a,task:l,preparationFailure:null,prepared:{dataDirectory:A,module:M}})}catch(x){const C=Q(x);c.push({artifactDirectory:y,inputs:a,task:l,preparationFailure:C,prepared:null})}}const m=c.filter(n=>n.prepared!==null),d=I.join(i,"setup.mjs"),T=I.join(i,"input-bootstrap.mjs"),h=I.join(i,"driver-request.json"),R=I.join(v,"run.json"),_=I.join(i,"startup-failure.txt");let V=null,S=null,D=t.vitest;const F=`--import=${JSON.stringify(de(T).href)}`;let O={...E.environment,FORCE_COLOR:"0",NODE_OPTIONS:F,NO_COLOR:"1"};if(m.length>0)try{Ce(m);const{configuration:n}=m[0].inputs,l=n===void 0?null:Ee(n),a=l!==null&&l.runner!=="managed"?l:null;a!==null&&(D=ve(o));const N=Object.keys(m[0].inputs.supportModules);if(N.length>0)throw new $(new Error(`Node/Vitest engine does not support support module(s): ${N.join(", ")}`));let y=await Z(()=>ke(e,u,m,a?.host??null));if(a===null){const k=we(y.environment);y={...y,environment:k.overlay},O={...O,...k.process}}else{const k=b=>process.platform==="win32"?b.toUpperCase():b,ee=new Set(Object.keys(y.environment).map(k));O={...Object.fromEntries(Object.entries(a.host).filter(([b])=>!ee.has(k(b)))),...y.environment,NODE_OPTIONS:[F,a.host.NODE_OPTIONS].join(" ").trim()}}V=await r.writeRuntimeInput(i,y);const{file:x,sha256:C,sizeBytes:M}=V;await r.writeWorkspace({inputBootstrapFile:T,runtimeInput:{expectedFile:x,expectedInvocationId:e.invocationId,expectedSha256:C,expectedSizeBytes:M,startupFailure:{driverFile:Y,file:_,vitestWorkersDirectory:I.join(D.root,"dist","workers")}},setupFile:d,vitestModule:D.index});const A={cacheDirectory:I.join(i,"cache"),invocationId:e.invocationId,runReportFile:R,setupFile:d,startupFailureFile:_,vitest:{entrypoint:D.entrypoint,name:"vitest",version:D.version}};S=a===null?{...A,aliases:me(t),config:!1,managed:{externalModules:[t.authoringEntrypoints.data.replaceAll(I.sep,"/")]},project:null,test:{disableConsoleIntercept:!0,environment:"node",fileParallelism:!1,isolate:!0,maxWorkers:1,pool:"forks",...q(l)}}:{...A,aliases:{},config:a.config,managed:null,project:a.project,test:q(l)}}catch(n){const l=Q(n);c=c.map(a=>a.prepared===null?a:{...a,preparationFailure:l,prepared:null})}return Object.freeze({driverRequest:S,driverRequestFile:h,installation:t,processEnvironment:O,profileId:u,rootDirectory:o,runReportFile:R,runtimeInput:V,startupFailureFile:_,tasks:Object.freeze(c),vitest:D})}async function Fe(e,t,r,f,u){const o=n=>r.map(({task:l})=>P(l.id,"failed",f,L(),n(l.timeoutMs)));try{if(t.runtimeInput===null||t.driverRequest===null)throw new Error("Node/Vitest run was not prepared");await Ne(t.runtimeInput)}catch(n){return o(()=>({...j(n,"NODE_VITEST_INPUT_INVALID"),classification:"infra"}))}const s=await xe(e,t,t.runtimeInput,r[0]),p=Math.min(...r.map(({task:n})=>n.deadlineMs))-Date.now();if(p<=0)return o(n=>se(n,g));const i=t.driverRequest.managed===null?{}:{hookTimeout:p,teardownTimeout:p,testTimeout:p},v=r.map(({prepared:n})=>n.module),E={...t.driverRequest,modules:v,test:{...t.driverRequest.test,...i}};await w.writeFile(t.driverRequestFile,JSON.stringify(E),{flag:"wx",mode:384});const c=await fe({args:[Y,t.driverRequestFile],cwd:t.rootDirectory,displayLabel:"Node/Vitest",env:s,executable:process.execPath,signal:e.signal,timeoutMs:p}),m=await w.readFile(t.startupFailureFile,"utf8").catch(()=>null),d=await Ie(e.invocationId,t.runReportFile),T=d instanceof Error?[]:d.unhandledErrors.filter(({type:n})=>n!==G),h=[];for(const{artifactDirectory:n,prepared:l}of r)h.push(await Te({identity:Ve(e.invocationId,l.module).identity,artifactDirectory:n,dataDirectory:l.dataDirectory,reportFile:l.module.reportFile,rootDirectory:t.rootDirectory,stderr:c.stderr,stdout:c.stdout,unhandledErrors:T.filter(({testPath:a})=>a===void 0||I.resolve(a)===I.resolve(l.module.file))}).catch(a=>nodeVitestEvidenceCollectionFailure(c,a)));const R=h.map(n=>"reportSummary"in n?n.reportSummary:null),_=le(e.signal.reason),V=c.cancelled||c.timedOut||_,S=V&&d instanceof Error&&R.includes(null),D=T.length>0||R.some(n=>n?.success===!1||n?.numTotalTests===0),F=d instanceof Error?0:c.exitCode??d.exitCode,O=d instanceof Error?S?{exitCode:0,unhandledErrors:[]}:d:{...d,exitCode:F===1&&(D||d.afterTests!==void 0)?0:F};return!(d instanceof Error)&&d.afterTests!==void 0&&u.push(`Vitest failed the run after its tests ${D?"finished":"passed"}: ${d.afterTests.join("; ")}`),r.map(({task:n},l)=>{const a=h[l];if(m!==null&&!V)return P(n.id,"failed",f,"reportSummary"in a?a.evidence:L(),{classification:"infra",code:"NODE_VITEST_START_FAILED",message:`Vitest could not start: ${m}`});if(!("reportSummary"in a))return P(n.id,"failed",f,L(),a);const{failure:N,status:y}=classifyNodeVitestTaskResult(c,O,a,n.timeoutMs,_,S);return P(n.id,y,f,a.evidence,N)})}export function classifyNodeVitestTaskResult(e,t,r,f,u,o=!1){const s=d=>({failure:d,status:"failed"}),p=z(e,g);if(p!==null)return s(p);const i=r.reportSummary,v=i===null||!o?re(e,f,u,g):null;if(v!==null)return v;const E=t instanceof Error?void 0:t.unhandledErrors.find(({type:d})=>d===G),c=t instanceof Error?void 0:t.failure??(E&&{...E,phase:"reporter"});if(c!==void 0){const{message:d,phase:T,stack:h}=c;return{failure:{classification:"infra",code:"NODE_VITEST_RUN_FAILED",details:{phase:T},message:`Vitest ${T} failed: ${d}`,...h===void 0?{}:{stack:h}},status:T==="config"?"not_run":"failed"}}if(r.environmentFailure!==null)return s(r.environmentFailure);if(i===null||t instanceof Error){const d=K(e.stderr)??K(e.stdout),T=e.exitCode===0?null:W(e,g,d,"Vitest").failure;return s({...j(r.reportError??t,"NODE_VITEST_REPORT_INVALID"),...T===null?{}:{details:T.details,message:T.message},classification:"infra"})}const m=o?null:e.exitSignal;if(t.exitCode!==0||m!==null){const d=m===null?`exited with code ${t.exitCode.toString()}`:`was terminated by ${m}`;return s({classification:"infra",code:"NODE_VITEST_EXIT_CODE",details:ie(e),message:`Vitest ${d}, which its test results do not explain`})}return i.numTotalTests===0&&r.reportErrorCount===0?s({code:"NODE_VITEST_NO_TESTS",message:"Vitest did not discover any tests for the task file"}):r.dataEvidenceError!==null?s(j(r.dataEvidenceError,"NODE_VITEST_DATA_EVIDENCE_INVALID")):!i.success||i.numFailedTests>0||r.reportErrorCount>0?W(e,g,r.reportFailureMessage,"Vitest"):i.numPassedTests===0&&(i.numPendingTests>0||i.numTodoTests>0)?{failure:null,status:"skipped"}:i.numPassedTests===0?s({code:"NODE_VITEST_REPORT_FAILED",message:"Vitest report did not contain a completed passing test"}):{failure:null,status:"passed"}}export function nodeVitestEvidenceCollectionFailure(e,t){const r=B(t);return z(e,g,r)??{...j(r,"NODE_VITEST_EVIDENCE_FAILED"),classification:"infra"}}async function xe(e,t,r,{inputs:f,task:u}){const o=e.target.baseUrl,s={...t.processEnvironment,...o===null?{}:{BASE_URL:o},TESTKIT_ACTIVE:"1",[ye]:r.file,[De]:r.sha256,TESTKIT_INVOCATION_ID:e.invocationId,TESTKIT_LEASE_DIR:u.orchestration.lease.directory,TESTKIT_LEASE_ID:u.orchestration.lease.id,TESTKIT_MODE:u.orchestration.mode,...u.orchestration.runtimeId===void 0?{}:{TESTKIT_RUNTIME_ID:u.orchestration.runtimeId}},p=t.driverRequest?.managed!==null;p&&X(s);const i={...t.processEnvironment,...f.environment};Object.assign(s,await Re(e.workspace.cacheDirectory,i));try{p&&X(s)}catch{delete s.NODE_COMPILE_CACHE}return s}function Ce(e){const t=({prepared:o})=>o.module.file,r=e.find((o,s)=>e.findIndex(p=>t(p)===t(o))!==s);if(r!==void 0)throw new Error(`Node/Vitest task ${r.task.id} selects a file that another task in the request selects`);const f=({inputs:o,task:s})=>[o.environment,o.supportModules,s.orchestration,s.timeoutMs],u=e.find(o=>!te(f(o),f(e[0])));if(u!==void 0)throw new Error(`Node/Vitest task ${u.task.id} needs a different environment, support modules, orchestration or timeout than ${e[0].task.id}`)}function ke(e,t,r,f){const{baseUrl:u,databases:o,endpoints:s,handles:p}=e.target;return{...f===null?{}:{host:f},baseUrl:u,databases:o,endpoints:s,engineId:"node-vitest",environment:he(r[0].inputs.environment),handles:p,invocationId:e.invocationId,profileId:t,tasks:r.map(({inputs:i,prepared:v,task:E})=>({context:{baseUrl:u,engineId:"node-vitest",invocationId:e.invocationId,profileId:t,serviceName:E.serviceName,task:{file:E.file,id:E.id,suiteName:E.suiteName,...E.testType===void 0?{}:{testType:E.testType}}},dataDirectory:v.dataDirectory,file:v.module.file,fixtures:i.fixtures,...i.setupData===void 0?{}:{setupData:i.setupData},taskId:E.id})),version:1}}function Pe(e,t){return{...Oe({engineId:"node-vitest",installationKind:t.driverRequest?.managed===null?"repository-package":"root-direct-packages",profile:e.profile}),directPackages:{vitest:t.vitest.version}}}function Q(e){return{...j(e,"NODE_VITEST_PREPARE_FAILED"),classification:e instanceof $?"product":"infra"}}async function Z(e){try{return await e()}catch(t){throw new $(t)}}function q(e){return e===null?{}:{...e.shuffle===null?{}:{sequence:{seed:e.shuffle,shuffle:{files:!0,tests:!0}}},...e.retry===void 0?{}:{retry:e.retry}}}
|
|
1
|
+
import{makeTempDirAsync as Y}from"../../../process/files/temp-dir.js";import{isDeepStrictEqual as G}from"node:util";import{classifyManagedProcessResult as Q,engineExecutionResult as Z,terminalOutcome as x,emptyEvidence as j,failureFromError as k,firstOutputLine as L,processExitDetails as q,processExitFailure as $,taskBeforeStartOutcome as ee,managedProcessFailure as te,taskTimeoutFailure as ne,ConsumerPreparationError as U,consumerPreparationBoundary as B,evidenceCollectionFailure as re,taskOrchestrationEnvironment as ie}from"../../adapters/execution.js";import{createInvocationWorkspace as oe,requireConsumerFile as se,safeSegment as ae}from"../../adapters/workspace.js";import b from"node:fs/promises";import g from"node:path";import{pathToFileURL as le}from"node:url";import{isEngineTaskDeadlineExceededReason as ce,resolveEngineTaskInputs as de}from"../../api/index.js";import{createInvocationProcessEnvironment as ue,runManagedProcess as fe}from"../../../process/index.js";import{managedVitestAliases as pe}from"../../installations/node-vitest.js";import{resolveRepositoryVitest as me}from"../selection.js";import{collectNodeVitestEvidence as Ee,readNodeVitestRunReport as ve}from"./evidence.js";import{assertNodeVitestSpawnEnvironmentBudget as z,NODE_VITEST_RUNTIME_INPUT_FILE_ENV as Te,NODE_VITEST_RUNTIME_INPUT_SHA256_ENV as Ie,selectNodeVitestForwardedEnvironment as ge,splitManagedNodeVitestEnvironment as he,verifyOwnedNodeVitestRuntimeInput as Ne,writeOwnedNodeVitestRuntimeInput as ye}from"../runtime/runtime-input.js";import{NODE_VITEST_DRIVER_FILE as W,writeNodeVitestWorkspace as Re}from"./workspace.js";import{nodeVitestReportOptions as De}from"./reporter.js";import{engineEvidenceIdentity as Oe}from"../../adapters/evidence-identity.js";import{getNodeStartupCacheEnvironment as _e}from"../../adapters/node-startup-cache.js";const _={codePrefix:"NODE_VITEST",label:"Node/Vitest"},H="Unhandled Reporter Error",M="NodeVitestConsumerPreparationError";export async function executeNodeVitestEngine(e,t,i){const l=await Ve(e,t,i),p=Date.now(),r=l.tasks.map(s=>{e.observer?.taskStarted(s.task);const f=s.preparationFailure;return ee(s.task,e.signal,p,_)??(f===null?null:x(s.task.id,"failed",p,j(),f))}),o=l.tasks.filter((s,f)=>r[f]===null),c=[],u=o.length===0?[]:await Fe(e,l,o,p,c);let E=0;return Z(e,r.map(s=>s??u[E++]),Ce(e,l),c)}async function Ve(e,t,i){const l=e.profile.id,{artifactRoot:p,invocationDirectory:r,rootDirectory:o}=await oe(e,"node-vitest",i,I=>b.rm(I,{force:!0,recursive:!0})),c=g.join(r,"reports");await b.mkdir(c,{mode:448});const u=await ue(r);let E=[];for(const[I,m]of e.tasks.entries()){const n=de(e,m),v=`${I.toString().padStart(3,"0")}-${ae(m.id)}`,d=await Y(`${v}-`,{parentDir:p});try{const D=await B(M,()=>se(o,m.file,"Node/Vitest task file")),O=g.join(c,`${v}.json`),A={file:D,reportFile:O,sourceFile:m.file,taskId:m.id},C=g.join(d,"data");E.push({artifactDirectory:d,inputs:n,task:m,preparationFailure:null,prepared:{dataDirectory:C,module:A}})}catch(D){const O=J(D);E.push({artifactDirectory:d,inputs:n,task:m,preparationFailure:O,prepared:null})}}const s=E.filter(I=>I.prepared!==null),f=g.join(r,"setup.mjs"),N=g.join(r,"input-bootstrap.mjs"),a=g.join(r,"driver-request.json"),T=g.join(c,"run.json"),h=g.join(r,"startup-failure.txt");let V=null,F=null,y=t.vitest;const w=`--import=${JSON.stringify(le(N).href)}`;let R={...u.environment,FORCE_COLOR:"0",NODE_OPTIONS:w,NO_COLOR:"1"};if(s.length>0)try{Se(s);const{configuration:I}=s[0].inputs,m=I===void 0?null:I,n=m!==null&&m.runner!=="managed"?m:null;n!==null&&(y=me(o));const v=Object.keys(s[0].inputs.supportModules);if(v.length>0)throw new U(M,new Error(`Node/Vitest engine does not support support module(s): ${v.join(", ")}`));let d=await B(M,()=>xe(e,l,s,n?.host??null));if(n===null){const S=he(d.environment);d={...d,environment:S.overlay},R={...R,...S.process}}else{const S=P=>process.platform==="win32"?P.toUpperCase():P,X=new Set(Object.keys(d.environment).map(S));R={...Object.fromEntries(Object.entries(n.host).filter(([P])=>!X.has(S(P)))),...d.environment,NODE_OPTIONS:[w,n.host.NODE_OPTIONS].join(" ").trim()}}V=await ye(r,d);const{file:D,sha256:O,sizeBytes:A}=V;await Re({inputBootstrapFile:N,runtimeInput:{expectedFile:D,expectedInvocationId:e.invocationId,expectedSha256:O,expectedSizeBytes:A,startupFailure:{driverFile:W,file:h,vitestWorkersDirectory:g.join(y.root,"dist","workers")}},setupFile:f,vitestModule:y.index});const C={cacheDirectory:g.join(r,"cache"),invocationId:e.invocationId,runReportFile:T,setupFile:f,startupFailureFile:h,vitest:{entrypoint:y.entrypoint,name:"vitest",version:y.version}};F=n===null?{...C,aliases:pe(t),config:!1,managed:{externalModules:[t.authoringEntrypoints.data.replaceAll(g.sep,"/")]},project:null,test:{disableConsoleIntercept:!0,environment:"node",fileParallelism:!1,isolate:!0,maxWorkers:1,pool:"forks",...K(m)}}:{...C,aliases:{},config:n.config,managed:null,project:n.project,test:K(m)}}catch(I){const m=J(I);E=E.map(n=>n.prepared===null?n:{...n,preparationFailure:m,prepared:null})}return Object.freeze({driverRequest:F,driverRequestFile:a,installation:t,processEnvironment:R,profileId:l,rootDirectory:o,runReportFile:T,runtimeInput:V,startupFailureFile:h,tasks:Object.freeze(E),vitest:y})}async function Fe(e,t,i,l,p){const r=n=>i.map(({task:v})=>x(v.id,"failed",l,j(),n(v.timeoutMs)));try{if(t.runtimeInput===null||t.driverRequest===null)throw new Error("Node/Vitest run was not prepared");await Ne(t.runtimeInput)}catch(n){return r(()=>({...k(n,"NODE_VITEST_INPUT_INVALID"),classification:"infra"}))}const o=await we(e,t,t.runtimeInput,i[0]),c=Math.min(...i.map(({task:n})=>n.deadlineMs))-Date.now();if(c<=0)return r(n=>ne(n,_));const u=t.driverRequest.managed===null?{}:{hookTimeout:c,teardownTimeout:c,testTimeout:c},E=i.map(({prepared:n})=>n.module),s={...t.driverRequest,modules:E,test:{...t.driverRequest.test,...u}};await b.writeFile(t.driverRequestFile,JSON.stringify(s),{flag:"wx",mode:384});const f=await fe({args:[W,t.driverRequestFile],cwd:t.rootDirectory,displayLabel:"Node/Vitest",env:o,executable:process.execPath,signal:e.signal,timeoutMs:c}),N=await b.readFile(t.startupFailureFile,"utf8").catch(()=>null),a=await ve(e.invocationId,t.runReportFile),T=a instanceof Error?[]:a.unhandledErrors.filter(({type:n})=>n!==H),h=[];for(const{artifactDirectory:n,prepared:v}of i)h.push(await Ee({identity:De(e.invocationId,v.module).identity,artifactDirectory:n,dataDirectory:v.dataDirectory,reportFile:v.module.reportFile,rootDirectory:t.rootDirectory,stderr:f.stderr,stdout:f.stdout,unhandledErrors:T.filter(({testPath:d})=>d===void 0||g.resolve(d)===g.resolve(v.module.file))}).catch(d=>nodeVitestEvidenceCollectionFailure(f,d)));const V=h.map(n=>"reportSummary"in n?n.reportSummary:null),F=ce(e.signal.reason),y=f.cancelled||f.timedOut||F,w=y&&a instanceof Error&&V.includes(null),R=T.length>0||V.some(n=>n?.success===!1||n?.numTotalTests===0),I=a instanceof Error?0:f.exitCode??a.exitCode,m=a instanceof Error?w?{exitCode:0,unhandledErrors:[]}:a:{...a,exitCode:I===1&&(R||a.afterTests!==void 0)?0:I};return!(a instanceof Error)&&a.afterTests!==void 0&&p.push(`Vitest failed the run after its tests ${R?"finished":"passed"}: ${a.afterTests.join("; ")}`),i.map(({task:n},v)=>{const d=h[v];if(N!==null&&!y)return x(n.id,"failed",l,"reportSummary"in d?d.evidence:j(),{classification:"infra",code:"NODE_VITEST_START_FAILED",message:`Vitest could not start: ${N}`});if(!("reportSummary"in d))return x(n.id,"failed",l,j(),d);const{failure:D,status:O}=classifyNodeVitestTaskResult(f,m,d,n.timeoutMs,F,w);return x(n.id,O,l,d.evidence,D)})}export function classifyNodeVitestTaskResult(e,t,i,l,p,r=!1){const o=a=>({failure:a,status:"failed"}),c=te(e,_);if(c!==null)return o(c);const u=i.reportSummary,E=u===null||!r?Q(e,l,p,_):null;if(E!==null)return E;const s=t instanceof Error?void 0:t.unhandledErrors.find(({type:a})=>a===H),f=t instanceof Error?void 0:t.failure??(s&&{...s,phase:"reporter"});if(f!==void 0){const{message:a,phase:T,stack:h}=f;return{failure:{classification:"infra",code:"NODE_VITEST_RUN_FAILED",details:{phase:T},message:`Vitest ${T} failed: ${a}`,...h===void 0?{}:{stack:h}},status:T==="config"?"not_run":"failed"}}if(i.environmentFailure!==null)return o(i.environmentFailure);if(u===null||t instanceof Error){const a=L(e.stderr)??L(e.stdout),T=e.exitCode===0?null:$(e,_,a,"Vitest").failure;return o({...k(i.reportError??t,"NODE_VITEST_REPORT_INVALID"),...T===null?{}:{details:T.details,message:T.message},classification:"infra"})}const N=r?null:e.exitSignal;if(t.exitCode!==0||N!==null){const a=N===null?`exited with code ${t.exitCode.toString()}`:`was terminated by ${N}`;return o({classification:"infra",code:"NODE_VITEST_EXIT_CODE",details:q(e),message:`Vitest ${a}, which its test results do not explain`})}return u.numTotalTests===0&&i.reportErrorCount===0?o({code:"NODE_VITEST_NO_TESTS",message:"Vitest did not discover any tests for the task file"}):i.dataEvidenceError!==null?o(k(i.dataEvidenceError,"NODE_VITEST_DATA_EVIDENCE_INVALID")):!u.success||u.numFailedTests>0||i.reportErrorCount>0?$(e,_,i.reportFailureMessage,"Vitest"):u.numPassedTests===0&&(u.numPendingTests>0||u.numTodoTests>0)?{failure:null,status:"skipped"}:u.numPassedTests===0?o({code:"NODE_VITEST_REPORT_FAILED",message:"Vitest report did not contain a completed passing test"}):{failure:null,status:"passed"}}export function nodeVitestEvidenceCollectionFailure(e,t){return re(e,_,t)}async function we(e,t,i,{inputs:l,task:p}){const r=e.target.baseUrl,o={...t.processEnvironment,...r===null?{}:{BASE_URL:r},TESTKIT_ACTIVE:"1",[Te]:i.file,[Ie]:i.sha256,...ie(e.invocationId,p)},c=t.driverRequest?.managed!==null;c&&z(o);const u={...t.processEnvironment,...l.environment};Object.assign(o,await _e(e.workspace.cacheDirectory,u));try{c&&z(o)}catch{delete o.NODE_COMPILE_CACHE}return o}function Se(e){const t=({prepared:r})=>r.module.file,i=e.find((r,o)=>e.findIndex(c=>t(c)===t(r))!==o);if(i!==void 0)throw new Error(`Node/Vitest task ${i.task.id} selects a file that another task in the request selects`);const l=({inputs:r,task:o})=>[r.environment,r.supportModules,o.orchestration,o.timeoutMs],p=e.find(r=>!G(l(r),l(e[0])));if(p!==void 0)throw new Error(`Node/Vitest task ${p.task.id} needs a different environment, support modules, orchestration or timeout than ${e[0].task.id}`)}function xe(e,t,i,l){const{baseUrl:p,databases:r,endpoints:o,handles:c}=e.target;return{...l===null?{}:{host:l},baseUrl:p,databases:r,endpoints:o,engineId:"node-vitest",environment:ge(i[0].inputs.environment),handles:c,invocationId:e.invocationId,profileId:t,tasks:i.map(({inputs:u,prepared:E,task:s})=>({context:{baseUrl:p,engineId:"node-vitest",invocationId:e.invocationId,profileId:t,serviceName:s.serviceName,task:{file:s.file,id:s.id,suiteName:s.suiteName,...s.testType===void 0?{}:{testType:s.testType}}},dataDirectory:E.dataDirectory,file:E.module.file,fixtures:u.fixtures,...u.setupData===void 0?{}:{setupData:u.setupData},taskId:s.id})),version:1}}function Ce(e,t){return{...Oe({engineId:"node-vitest",installationKind:t.driverRequest?.managed===null?"repository-package":"root-direct-packages",profile:e.profile}),directPackages:{vitest:t.vitest.version}}}function J(e){return{...k(e,"NODE_VITEST_PREPARE_FAILED"),classification:e instanceof U?"product":"infra"}}function K(e){return e===null?{}:{...e.shuffle===null?{}:{sequence:{seed:e.shuffle,shuffle:{files:!0,tests:!0}}},...e.retry===void 0?{}:{retry:e.retry}}}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import y from"node:fs/promises";import w from"node:path";import{evidenceFileAccess as O,readStableEvidenceFile as x,writeOwnedEvidenceFile as $}from"../../adapters/evidence-files.js";import{NODE_VITEST_DATA_FAILURE_DETAILS_ARTIFACT_KIND as X,nodeVitestDataScenarioFile as Y}from"../runtime/runtime-input.js";import{dataFlowArtifacts as z}from"../runtime/data/report.js";import{readVitestReportCases as U,sharedVitestTitles as K,vitestErrorDetails as q,vitestReportErrors as D,vitestTestFailureDetails as G}from"./failure-details.js";import{parseEnvironmentValueMissingFailure as H}from"../../contracts/environment-values.js";import{readNativeReport as
|
|
2
|
-
`,"utf8"));return{...await A(n,"application/json","testkit.failure-details","vitest-failure-details",[...e.length===0?[]:[I(e.length,"failed Vitest test")],...r.length===0?[]:[`${I(r.length,"Vitest error")} outside a test`]].join(" and ")),data:s}}function I(t,e){return`${t.toString()} ${e}${t===1?"":"s"}`}async function at({dataDirectory:t,identity:e},r,s,n,a){const o=w.dirname(Y(t,"")),u=[];try{const m=(await y.readdir(o).catch(i=>{if(J(i)&&i.code==="ENOENT")return[];throw i})).filter(i=>i.endsWith(".json")).sort();if(m.length>
|
|
1
|
+
import y from"node:fs/promises";import w from"node:path";import{evidenceFileAccess as O,readStableEvidenceFile as x,writeOwnedEvidenceFile as $}from"../../adapters/evidence-files.js";import{NODE_VITEST_DATA_FAILURE_DETAILS_ARTIFACT_KIND as X,nodeVitestDataScenarioFile as Y}from"../runtime/runtime-input.js";import{dataFlowArtifacts as z}from"../runtime/data/report.js";import{readVitestReportCases as U,sharedVitestTitles as K,vitestErrorDetails as q,vitestReportErrors as D,vitestTestFailureDetails as G}from"./failure-details.js";import{parseEnvironmentValueMissingFailure as H}from"../../contracts/environment-values.js";import{readNativeReport as j,requireNativeEvidenceJson as Q}from"../../adapters/native-report.js";import{isUnknownRecord as g}from"../../../kernel/unknown-values.js";import{toError as N}from"../../../kernel/error-serialization.js";import{isPathWithin as Z}from"../../../process/files/paths.js";import{firstOutputLine as tt}from"../../adapters/execution.js";import{nodeVitestReportOptions as et}from"./reporter.js";const rt=16*1024*1024,k=1024;export const NODE_VITEST_DATA_ARTIFACT_MAX_BYTES=16*1024*1024,NODE_VITEST_DATA_EVIDENCE_MAX_BYTES=64*1024*1024;const nt=".testkit-owned-evidence";export async function collectNodeVitestEvidence(t,e=O){await y.mkdir(t.artifactDirectory,{recursive:!0});const r=await y.realpath(t.artifactDirectory),s=w.join(r,nt);await y.mkdir(s,{mode:448,recursive:!1});const n=[],a=[];t.stdout.length>0&&(a.push({stream:"stdout",text:t.stdout}),n.push(await R(r,"stdout.log","node-vitest-stdout",t.stdout))),t.stderr.length>0&&(a.push({stream:"stderr",text:t.stderr}),n.push(await R(r,"stderr.log","node-vitest-stderr",t.stderr)));const o=await st(t,s,e);n.push(...o.artifacts);const u=await at(t,r,s,o.cases,e);return n.push(...u.artifacts),{dataEvidenceError:u.error,environmentFailure:o.environmentFailure,evidence:{artifacts:n,logs:a,metrics:[],...o.summary===null?{}:{native:{vitest:o.summary}}},reportError:o.error,reportErrorCount:o.errorCount,reportFailureMessage:o.failureMessage,reportSummary:o.summary}}async function st({identity:t,reportFile:e,rootDirectory:r,unhandledErrors:s=[]},n,a){try{const o=await b(e,a);if(!o.exists||o.contents===null)throw new Error("Vitest did not produce its Testkit native report");const u=j(o.value,t).result;if(!g(u)||!g(u.summary))throw new Error("Vitest native report must contain result and summary objects");const f=ct(u.summary),m=U(u.cases,f),l=G(u.failures,r,K(m));if(f.numFailedTests!==l.length)throw new Error("Vitest native report failure count does not match its summary");const h=D(u.errors);if(f.success&&h.length>0)throw new Error("Vitest native report success conflicts with runtime errors");const V=[...h,...s],E=H(u.environmentFailure,"Vitest native report environment failure"),i=await $(n,"testkit-vitest-report.json",E===null?o.contents:Buffer.from(it(o.value))),c=await A(i,"application/json","engine.testkit-vitest-report","testkit-vitest-report","Testkit native Vitest report"),T=await ot(n,l,q(V)),p=[c,...T===null?[]:[T]];if(u.pending!==void 0){const v=new Error("Vitest did not finish the file in every project that selects it");return{artifacts:p,cases:m,environmentFailure:null,error:v,errorCount:0,failureMessage:null,summary:null}}return{artifacts:p,cases:m,environmentFailure:E,error:null,errorCount:V.length,failureMessage:typeof l[0]?.message=="string"?l[0].message:V.find(v=>v.message.trim().length>0)?.message??null,summary:f}}catch(o){return{artifacts:[],cases:[],environmentFailure:null,error:N(o),errorCount:0,failureMessage:null,summary:null}}}function it(t){if(!g(t)||!g(t.result))throw new Error("Vitest native report must contain result and summary objects");const e=Object.fromEntries(Object.entries(t.result).filter(([r])=>r!=="environmentFailure"));return JSON.stringify({...t,result:e})}async function ot(t,e,r){if(e.length+r.length===0)return null;const s={failures:[...e,...r],phase:"exec"},n=await $(t,"vitest-failure-details.json",Buffer.from(`${JSON.stringify(s,null,2)}
|
|
2
|
+
`,"utf8"));return{...await A(n,"application/json","testkit.failure-details","vitest-failure-details",[...e.length===0?[]:[I(e.length,"failed Vitest test")],...r.length===0?[]:[`${I(r.length,"Vitest error")} outside a test`]].join(" and ")),data:s}}function I(t,e){return`${t.toString()} ${e}${t===1?"":"s"}`}async function at({dataDirectory:t,identity:e},r,s,n,a){const o=w.dirname(Y(t,"")),u=[];try{const m=(await y.readdir(o).catch(i=>{if(J(i)&&i.code==="ENOENT")return[];throw i})).filter(i=>i.endsWith(".json")).sort();if(m.length>k)throw new Error(`Data evidence exceeds its ${k.toString()} test limit`);let l=0;const h=[];for(const i of m){const c=`Data scenario ${i}`,T=await mt(r,w.join(o,i),c),{contents:p}=await x(T,NODE_VITEST_DATA_ARTIFACT_MAX_BYTES,c,a);if(l+=p.byteLength,l>NODE_VITEST_DATA_EVIDENCE_MAX_BYTES)throw new Error(`Data evidence exceeds its ${NODE_VITEST_DATA_EVIDENCE_MAX_BYTES.toString()} byte aggregate limit`);h.push({contents:p,label:c,name:i})}const E=h.flatMap(({contents:i,label:c,name:T})=>{const p=F(C(i,c),c),v=d(p.attempt,`${c}.attempt`),_=d(p.repeat,`${c}.repeat`),B=d(p.repeats,`${c}.repeats`),L=ut(p.scenario,`${c}.scenario`),S=n.find(({id:W})=>W===decodeURIComponent(T.slice(0,-5)));return S!==void 0&&S.state!=="skipped"&&S.attempts===v&&S.repeat===_&&_===B?[{index:n.indexOf(S),scenario:L}]:[]}).sort((i,c)=>i.index-c.index).map(({scenario:i})=>i);for(const i of E.length===0?[]:z(E,e.sourceFile,e.taskId)){const c=i.contentType==="text/markdown"?".md":".json",T=await $(s,`${i.name}${c}`,Buffer.from(i.contents,"utf8"));u.push({...await A(T,i.contentType,i.kind,i.name,i.summary),...i.kind===X?{data:F(JSON.parse(i.contents),i.name)}:{}})}}catch(f){return{artifacts:u,error:N(f)}}return{artifacts:u,error:null}}function ut(t,e){const r=F(t,e);if(M(r.name,`${e}.name`),M(F(r.location,`${e}.location`).path,`${e}.location.path`),r.status!=="failed"&&r.status!=="passed")throw new Error(`${e}.status must be failed or passed`);const n=["budgetFailures","budgets","events","evidence","findings","metrics","sequences"].find(a=>!Array.isArray(r[a]));if(n!==void 0)throw new Error(`${e}.${n} must be an array`);return r}async function R(t,e,r,s){const n=w.join(t,e);return await y.writeFile(n,s,"utf8"),A(n,"text/plain; charset=utf-8","engine.output",r,tt(s)??"captured output")}async function A(t,e,r,s,n){const a=await y.stat(t);if(!a.isFile())throw new Error(`${s} artifact is not a regular file`);return{contentType:e,kind:r,metadata:{sizeBytes:a.size},name:s,path:t,summary:n}}async function b(t,e){let r;try{({contents:r}=await x(t,rt,t,e))}catch(n){if(J(n)&&n.code==="ENOENT")return{contents:null,exists:!1,value:null};throw n}const s=C(r,t);return{contents:r,exists:!0,value:s}}function C(t,e){const r=JSON.parse(t.toString("utf8"));return Q(r,e),r}function ct(t){const e={numFailedTests:d(t.numFailedTests,"Vitest native report.summary.numFailedTests"),numPassedTests:d(t.numPassedTests,"Vitest native report.summary.numPassedTests"),numPendingTests:d(t.numPendingTests,"Vitest native report.summary.numPendingTests"),numTodoTests:d(t.numTodoTests,"Vitest native report.summary.numTodoTests"),numTotalTests:d(t.numTotalTests,"Vitest native report.summary.numTotalTests"),numFailedTestSuites:d(t.numFailedTestSuites,"Vitest native report.summary.numFailedTestSuites"),numPassedTestSuites:d(t.numPassedTestSuites,"Vitest native report.summary.numPassedTestSuites"),numPendingTestSuites:d(t.numPendingTestSuites,"Vitest native report.summary.numPendingTestSuites"),numTotalTestSuites:d(t.numTotalTestSuites,"Vitest native report.summary.numTotalTestSuites"),success:dt(t.success,"Vitest native report.summary.success")};if(e.numTotalTests!==e.numFailedTests+e.numPassedTests+e.numPendingTests+e.numTodoTests||e.numTotalTestSuites!==e.numFailedTestSuites+e.numPassedTestSuites+e.numPendingTestSuites||e.success&&(e.numFailedTests>0||e.numFailedTestSuites>0||e.numTotalTestSuites===0))throw new Error("Vitest native report summary contains inconsistent counts or success");return e}async function mt(t,e,r){const s=w.isAbsolute(e)?e:w.resolve(t,e),n=await y.lstat(s);if(!n.isFile()||n.isSymbolicLink())throw new Error(`${r} must be a regular non-symbolic-link file`);const a=await y.realpath(s);if(!Z(t,a))throw new Error(`${r} must stay within the task artifact directory`);return a}function F(t,e){if(!g(t))throw new Error(`${e} must contain an object`);const r=Object.create(null);for(const[s,n]of Object.entries(t))r[s]=P(n,`${e}.${s}`);return r}function P(t,e){if(t===null||typeof t=="boolean"||typeof t=="string")return t;if(typeof t=="number"){if(!Number.isFinite(t))throw new Error(`${e} must be finite`);return t}return Array.isArray(t)?t.map((r,s)=>P(r,`${e}[${s.toString()}]`)):F(t,e)}function M(t,e){if(typeof t!="string"||t.trim().length===0)throw new Error(`${e} must be a non-empty string`);return t}function d(t,e){if(typeof t!="number"||!Number.isSafeInteger(t)||t<0)throw new Error(`${e} must be a non-negative safe integer`);return t}function dt(t,e){if(typeof t!="boolean")throw new Error(`${e} must be boolean`);return t}function J(t){return t instanceof Error}export async function readNodeVitestRunReport(t,e,r=O){try{const{identity:s}=et(t,{reportFile:e,sourceFile:".",taskId:"run"}),{result:n}=j((await b(e,r)).value,s);if(!g(n)||!Number.isSafeInteger(n.exitCode)||!Array.isArray(n.unhandledErrors))throw new Error("Vitest run report must contain an exit code and unhandled errors");const a=n.unhandledErrors,o=n.failure===void 0?[]:[n.failure];D([...a,...o],"Vitest run report errors");const u=m=>g(m)&&[m.testPath,m.type].every(l=>l===void 0||typeof l=="string"),f=n.afterTests===void 0?[]:n.afterTests;if(!Array.isArray(f)||!f.every(m=>typeof m=="string")||!a.every(u)||!o.every(m=>g(m)&&["close","config","run"].includes(m.phase)))throw new Error("Vitest run report errors are malformed");return n}catch(s){return N(s)}}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{stringFromUnknown as
|
|
1
|
+
import{stringFromUnknown as f}from"./primitives.js";import i from"node:fs";import h from"node:path";import{NODE_VITEST_DATA_BUDGET_FAILURE_DETAIL_KIND as g,NODE_VITEST_DATA_FAILURE_DETAILS_ARTIFACT_KIND as l,NODE_VITEST_DATA_FAILURE_DETAILS_ARTIFACT_NAME as $,nodeVitestDataScenarioFile as S}from"../runtime-input.js";import{DATA_ARTIFACT_KIND as p,DATA_REPORT_KIND as T,DATA_SCHEMA_VERSION as y}from"./types.js";export function writeDataFlowScenario(t,n,e){const r=process.env.TESTKIT_DATA_ARTIFACT_DIR;if(!r)return;const u=S(r,t);i.mkdirSync(h.dirname(u),{recursive:!0}),i.writeFileSync(`${u}.tmp`,JSON.stringify({...n,scenario:e})),i.renameSync(`${u}.tmp`,u)}export function dataFlowArtifacts(t,n,e){const r=D(t,n,e),u=A(r),o=u?.failures.length??0;return[{contentType:"application/json",contents:`${JSON.stringify(r,null,2)}
|
|
2
2
|
`,kind:p,name:"data-flow",summary:r.summaryText},{contentType:"text/markdown",contents:`${renderDataFlowMarkdown(r)}
|
|
3
3
|
`,kind:T,name:"data-flow-report",summary:r.summaryText},...u===null?[]:[{contentType:"application/json",contents:`${JSON.stringify(u,null,2)}
|
|
4
|
-
`,kind:l,name:$,summary:`${String(
|
|
5
|
-
`).trimEnd()}function
|
|
4
|
+
`,kind:l,name:$,summary:`${String(o)} failed data-flow budget${o===1?"":"s"}`}]]}function A(t){const n=t.scenarios.flatMap(e=>e.budgetFailures.map(r=>{const u=`${r.target} budget "${r.name}" (${r.operator})`;return{actual:c(r.actual,`${e.name} ${r.name} actual value`),checkName:r.name,count:1,expected:c(r.expected,`${e.name} ${r.name} expected value`),groupPath:[e.name],key:`${e.name} > ${u}`,kind:g,location:e.location,message:r.message,title:u}}));return n.length===0?null:{failures:n,phase:"exec"}}function c(t,n){let e;try{e=JSON.stringify(t)}catch(r){throw new TypeError(`${n} must be JSON-serializable`,{cause:r})}return JSON.parse(e)}function D(t,n,e){const r=t.filter(a=>a.status==="failed").length,u=t.reduce((a,m)=>a+m.budgetFailures.length,0),o=O(t.flatMap(a=>a.findings)),d=`${String(t.length-r)}/${String(t.length)} data scenario(s) passed, ${String(u)} budget failure(s)`;return{schemaVersion:y,kind:p,generatedAt:new Date().toISOString(),file:n,taskId:e,summary:{budgetFailures:u,failed:r,findingCounts:o,passed:t.length-r,scenarios:t.length,status:r>0?"failed":"passed"},summaryText:d,scenarios:t}}export function renderDataFlowMarkdown(t){const n=[];n.push("# Data Flow Report"),n.push(""),n.push(`Status: ${t.summary.status}`),n.push(`Scenarios: ${String(t.summary.passed)}/${String(t.summary.scenarios)} passed`),n.push(`Budget failures: ${String(t.summary.budgetFailures)}`),n.push("");for(const e of t.scenarios)n.push(`## ${e.name}`),n.push(""),n.push(`Status: ${e.status}`),n.push(`Duration: ${String(e.durationMs)}ms`),e.error?.message&&n.push(`Error: ${e.error.message}`),n.push(""),E(n,e.metrics),N(n,e.sequences),_(n,e.evidence),F(n,e.budgets),b(n,e.findings);return n.join(`
|
|
5
|
+
`).trimEnd()}function _(t,n){if(t.push("### Evidence"),n.length===0){t.push(""),t.push("No structured data-flow evidence recorded."),t.push("");return}t.push(""),t.push("| Kind | Name | Status | Details |"),t.push("| --- | --- | --- | --- |");for(const e of n)t.push(`| ${s(e.kind)} | ${s(e.name)} | ${s(e.status||"")} | ${s(I(e.details))} |`);t.push("")}function E(t,n){if(t.push("### Metrics"),n.length===0){t.push(""),t.push("No metrics recorded."),t.push("");return}t.push(""),t.push("| Metric | Value | Unit | Kind |"),t.push("| --- | ---: | --- | --- |");for(const e of n)t.push(`| ${s(e.name)} | ${k(e.value)} | ${s(e.unit||"")} | ${s(e.kind||"")} |`);t.push("")}function N(t,n){if(t.push("### Sequences"),n.length===0){t.push(""),t.push("No sequences recorded."),t.push("");return}t.push(""),t.push("| Sequence | Values |"),t.push("| --- | --- |");for(const e of n)t.push(`| ${s(e.name)} | ${s(e.values.join(" -> "))} |`);t.push("")}function F(t,n){if(t.push("### Budgets"),n.length===0){t.push(""),t.push("No budgets declared."),t.push("");return}t.push("");for(const e of n)t.push(`- ${e.status.toUpperCase()} ${e.name}: ${e.message}`);t.push("")}function b(t,n){if(t.push("### Findings"),n.length===0){t.push(""),t.push("No findings recorded."),t.push("");return}t.push("");for(const e of n)t.push(`- ${e.severity.toUpperCase()}: ${e.message}`);t.push("")}function I(t){const n=Object.entries(t||{}).filter(([e])=>e!=="status").slice(0,6);return n.length===0?"":n.map(([e,r])=>`${e}: ${w(r)}`).join(", ")}function w(t){return t==null?"":typeof t=="string"||typeof t=="number"||typeof t=="boolean"?String(t):JSON.stringify(t)}function O(t){const n={info:0,watch:0,waste:0,error:0};for(const e of t)n[e.severity]+=1;return n}function s(t){return f(t??"").replaceAll("|","\\|").replace(/\s+/gu," ").trim()}function k(t){return Number.isInteger(t)?String(t):String(Number(t.toFixed(4)))}
|