@gfdelarue/testkit 0.6.6 → 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/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 +27 -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
package/dist/evidence/reading.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isWorkObserved as
|
|
1
|
+
import{isWorkObserved as g,recordWork as p}from"../kernel/work-metrics.js";import u from"fs";import{posixPath as v}from"../process/files/paths.js";import c from"path";import{normalizeEngineJsonValue as R}from"../engines/api/index.js";import{readJsonFile as h,requireContainedPath as y}from"./storage.js";import{loadRunBundleArtifact as A}from"./bundles.js";import{readRunDocument as x}from"./read-run.js";import{runModel as w}from"./run-model.js";import{requireObjectRecord as P,requireResultsReferencePath as E,optionalStringValue as k,optionalNullableStringValue as m}from"./read-fields.js";export function loadLatestRunArtifact(e){const t=c.join(e,".testkit","results","latest.json");if(!u.existsSync(t))throw new Error(`No run artifact found at ${c.relative(e,t)}`);return x(h(t),t)}export function loadLatestRun(e){return w(loadLatestRunArtifact(e))}export function loadCurrentRunArtifact(e){const t=c.join(e,".testkit","results","live.json");return u.existsSync(t)?x(h(t),t):loadLatestRunArtifact(e)}export function loadRunArtifact(e,t=null){return t?A(e,t):loadLatestRunArtifact(e)}export function loadRun(e,t=null){return w(loadRunArtifact(e,t))}export function resolveFileSubject(e,t=null,n=null){const r=S(e,n);if(r.length===0)throw new Error("No file results were found in the latest run artifact.");if(!t)return r.find(s=>s.file.status==="failed")||r[0];const o=v(t),i=r.filter(s=>s.file.path===o);if(i.length===1)return i[0];if(i.length>1)throw new Error(`Multiple files matched "${t}". Re-run with --service to disambiguate.`);const a=r.filter(s=>s.file.path.endsWith(`/${o}`));if(a.length===1)return a[0];throw a.length>1?new Error(`Multiple files matched "${t}". Re-run with --service to disambiguate.`):new Error(`No file matched "${t}".`)}export function collectArtifactEntries(e,t,n=null,r=null,o,i=!0){const a=n?[resolveFileSubject(t,n,r)]:S(t,r),s=[];for(const f of a)for(const l of f.file.artifacts||[]){if(o&&l.kind!==o)continue;const d=M(e,l.path,`artifact "${l.name}" path`),$=i&&u.existsSync(d)?j(d,l):null;s.push({...f,artifactRef:l,absolutePath:d,payload:$})}return s}function j(e,t){let n;if(g()){const o=u.readFileSync(e);p("artifacts.payloadFilesRead",1),p("artifacts.payloadBytesRead",o.byteLength),n=o.toString("utf8")}else n=u.readFileSync(e,"utf8");const r=t.contentType||"";if(r==="text/markdown"||r==="text/plain")return{contentType:r,kind:t.kind||null,name:t.name||null,summary:t.summary||null,text:n};try{const o=JSON.parse(n);return T(o,e)}catch{return{contentType:r||"text/plain",kind:t.kind||null,name:t.name||null,summary:t.summary||null,text:n}}}function S(e,t=null){const n=[];for(const r of e.groups)if(!(t&&r.name!==t))for(const o of r.suites)for(const i of o.files)n.push({group:r,suite:o,file:i});return n}function T(e,t){const n=P(e,t),r=k(n.emittedAt,`${t}.emittedAt`);return{...n,contentType:m(n.contentType,`${t}.contentType`),...n.data===void 0?{}:{data:R(n.data,`${t}.data`)},...r===void 0?{}:{emittedAt:r},kind:m(n.kind,`${t}.kind`),name:m(n.name,`${t}.name`),summary:m(n.summary,`${t}.summary`),text:k(n.text,`${t}.text`)}}function M(e,t,n){const r=E(t,n),o=c.resolve(e,".testkit","results"),i=c.resolve(e,r),a=`${n} must resolve within .testkit/results`;if(y(o,i,a),u.existsSync(i)){const s=u.realpathSync(o),f=u.realpathSync(i);y(s,f,a)}return i}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import J from"path";import{publishEnvironmentRecord as K}from"./environment-record.js";import{DEFAULT_REPORTED_ARCHIVE_CONFIG as U}from"./reporting-types.js";export function buildRunArtifact({productDir:n,runId:e=null,results:i,startedAt:
|
|
1
|
+
import J from"path";import{publishEnvironmentRecord as K}from"./environment-record.js";import{DEFAULT_REPORTED_ARCHIVE_CONFIG as U}from"./reporting-types.js";export function buildRunArtifact({productDir:n,runId:e=null,results:i,startedAt:u,finishedAt:s,execution:o,workerCount:a,typeValues:d,suiteSelectors:R,fileNames:y,groupFilter:b,planning:f=null,archiveConfig:D=null,declaredEnvironmentKeys:M=[],metadata:r,environment:A,operationTimings:w=[],runInterruptReason:m=null,runStatus:B=null}){const l=i.filter(t=>!t.skipped),G=l.filter(S),g=l.filter(t=>t.failed),F=l.filter(t=>t.infraFailed),I=l.filter(t=>t.blockedByRuntime),O=l.filter(t=>Q(t)),C=Z(l),v=W(l),E=c(l,"totalFileCount"),T=c(l,"passedFileCount"),q=c(l,"failedFileCount"),x=c(l,"skippedFileCount"),N=c(l,"notRunFileCount"),k=p(l,t=>t.status==="cancelled"),h=p(l,t=>t.status==="interrupted"),V=Math.max(0,N-k-h),z=p(l,t=>t.infraFailed===!0),H=p(l,t=>t.blockedByRuntime===!0),_=new Date(s??Date.now()).toISOString(),L=s===null?null:new Date(s).toISOString(),P=s===null?null:s-u,j=D??U;return{schemaVersion:20,source:"testkit",generatedAt:_,product:{name:J.basename(n),directory:n},git:{branch:r.git.branch??null,commitSha:r.git.commitSha??null,dirty:r.git.dirty===!0,pullRequest:r.git.pullRequest??null,pullRequestHeadSha:r.git.pullRequestHeadSha??null,repository:r.git.repository??null},host:r.host,...r.inputs===void 0?{}:{inputs:r.inputs},run:{id:e,status:B||(g.length>0||F.length>0?"failed":"passed"),startedAt:new Date(u).toISOString(),finishedAt:L,durationMs:P,workers:o.taskConcurrency,fileTimeoutSeconds:o.fileTimeoutSeconds,workerCount:a,types:[...d],suiteSelectors:R.map(t=>t.raw),fileNames:[...y],groupFilter:b,testkitVersion:r.testkitVersion,...m===null?{}:{interruptReason:m}},archive:{request:j,declaredEnvironmentKeys:[...new Set(M)].sort()},...f?{planning:f}:{},summary:{groups:{total:l.length,passed:O.length,failed:g.length,infraFailed:F.length,blockedByRuntime:I.length,skipped:G.length},suites:v,files:{total:E,passed:T,failed:q,skipped:x,notRun:V,cancelled:k,interrupted:h,infraFailed:z,blockedByRuntime:H},...C.length>0?{diagnostics:C}:{}},environment:K(A),timings:{operations:$(w)},groups:i.map(t=>({name:t.name,failed:t.failed,infraFailed:t.infraFailed===!0,blockedByRuntime:t.blockedByRuntime===!0,skipped:t.skipped,suiteCount:t.suiteCount,completedSuiteCount:t.completedSuiteCount,skippedSuiteCount:t.skippedSuiteCount,failedSuiteCount:t.failedSuiteCount,totalFileCount:t.totalFileCount,completedFileCount:t.completedFileCount,passedFileCount:t.passedFileCount,failedFileCount:t.failedFileCount,skippedFileCount:t.skippedFileCount,notRunFileCount:t.notRunFileCount,durationMs:t.durationMs,totalTaskDurationMs:t.totalTaskDurationMs,suites:t.suites,errors:t.errors,...t.diagnostics&&t.diagnostics.length>0?{diagnostics:t.diagnostics}:{},...t.errorDetails&&t.errorDetails.length>0?{errorDetails:t.errorDetails}:{}}))}}export function buildLiveRunArtifact({updatedAt:n,...e}){return{...buildRunArtifact({...e,finishedAt:null,runStatus:"running"}),generatedAt:new Date(n).toISOString()}}function S(n){return!n.skipped&&!n.failed&&n.skippedSuiteCount>0&&n.skippedSuiteCount===n.suiteCount&&n.notRunFileCount===0}function Q(n){return!n.failed&&!n.infraFailed&&!S(n)&&n.notRunFileCount===0}function p(n,e){let i=0;for(const u of n)for(const s of u.suites)for(const o of s.files??[])e(o)&&(i+=1);return i}function W(n){const e={total:0,completed:0,passed:0,failed:0,skipped:0};for(const i of n){const u=i.suites,s=i.suiteCount;if(u.length===0){const o=X(i,s);e.total+=o.total,e.completed+=o.completed,e.passed+=o.passed,e.failed+=o.failed,e.skipped+=o.skipped;continue}e.total+=s;for(const o of u){const a=Y(o);a==="passed"?(e.completed+=1,e.passed+=1):a==="failed"?(e.completed+=1,e.failed+=1):a==="skipped"&&(e.completed+=1,e.skipped+=1)}}return e}function X(n,e){const i=n.failedSuiteCount,u=n.skippedSuiteCount,s=n.completedSuiteCount;return{total:e,completed:s,passed:Math.max(0,s-i-u),failed:i,skipped:u}}function Y(n){const e=n.files??[],i=e.length,u=Math.max(n.fileCount??0,i),s=e.length>0?e.filter(d=>d.status==="failed"||d.failed===!0).length:n.failedFileCount??0,o=e.length>0?e.filter(d=>d.status==="skipped").length:n.skippedFileCount??0,a=e.length>0?e.filter(d=>d.status==="passed").length:n.passedFileCount??0;return s>0||n.failed===!0?"failed":u>0&&o>=u?"skipped":u>0&&a+o>=u?"passed":"pending"}function c(n,e){return n.reduce((i,u)=>i+u[e],0)}function Z(n){const e=[],i=new Set,u=s=>{const o=JSON.stringify(s);i.has(o)||(i.add(o),e.push(s))};for(const s of n){const o=s.diagnostics??[];for(const a of o)u({...a,group:a.group||s.name});if(!(o.length>0))for(const a of s.suites)for(const d of a.diagnostics??[])u({...d,group:d.group||s.name,suite:d.suite||a.name})}return e}function $(n){return n.map(e=>({id:e.id||null,source:e.source||"testkit",name:e.name||"operation",kind:e.kind||"operation",summary:e.summary||null,scope:e.scope?{...e.scope}:{},parentId:e.parentId||null,status:e.status||"unknown",startedAt:e.startedAt||null,updatedAt:e.updatedAt||null,finishedAt:e.finishedAt||null,durationMs:e.durationMs!==null&&e.durationMs!==void 0&&Number.isFinite(e.durationMs)?Math.max(0,Math.round(e.durationMs)):null,error:e.error||null,message:e.message||null,logRefs:(e.logRefs??[]).map(i=>ee(i)?{...i}:i),metadata:e.metadata?{...e.metadata}:{}}))}function ee(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import{finalizeLiveRunArtifact as u,resetResultArtifacts as d}from"./artifacts.js";import{isUnknownRecord as a}from"../kernel/unknown-values.js";import{readLiveRunArtifact as o}from"./live-run-summary.js";export function createRunEvidencePublisher(t={}){return{controllerExited(e,r){u(e,{runId:r.runId,reason:r.reason,redactKeys:t.redactKeys,redactionTargets:t.redactionTargets?.(),status:r.status,finishedAt:r.finishedAt})},runStopped(e,r){u(e,{runId:r.runId,reason:r.reason,status:"interrupted"})},activeRunsListed(e,r){const n=o(e),s=a(n)&&a(n.run)?n.run:null,i=typeof s?.id=="string"?s.id:"";!i||s?.status!=="running"||r.has(i)||u(e,{runId:i,reason:"stale live artifact has no active run manifest",status:"interrupted"})},runStarting(e){t.resetResults===!0&&d(e)}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isUnknownArray as p,isUnknownRecord as m}from"../kernel/unknown-values.js";export const RUN_INPUTS_VERSION=1;const a=/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/u,l=/^[0-7]{6}$/u,E=/^[0-9a-f]{64}$/u;export function readRunInputs(t){if(t===void 0)return{kind:"missing"};try{return{inputs:w(t),kind:"recorded"}}catch(n){return{kind:"unreadable",reason:n instanceof Error?n.message:String(n)}}}function w(t){const n=i(t,"inputs");if(n.version!==RUN_INPUTS_VERSION)throw new Error(`inputs.version ${JSON.stringify(n.version)} isn't ${String(RUN_INPUTS_VERSION)}`);return{content:v(n.content),duringRun:k(n.duringRun),environment:$(n.environment),options:y(n.options),version:RUN_INPUTS_VERSION}}function v(t){const n=i(t,"inputs.content");if(n.kind==="unavailable")return{kind:"unavailable",reason:r(n.reason,"reason")};if(n.kind!=="snapshot")throw new Error("inputs.content.kind must be snapshot or unavailable");const e=i(n.snapshot,"inputs.content.snapshot"),o=r(e.base,"inputs.content.snapshot.base");if(!a.test(o))throw new Error("inputs.content.snapshot.base must be a Git object ID");const u=i(e.changes,"inputs.content.snapshot.changes");return{kind:"snapshot",snapshot:{base:o,changes:Object.fromEntries(Object.entries(u).map(([s,g])=>[h(s),b(g,s)])),product:R(e.product,"inputs.content.snapshot.product"),unidentified:d(e.unidentified,"inputs.content.snapshot.unidentified").map(h)}}}function b(t,n){if(t===null)return null;const e=i(t,`inputs.content.snapshot.changes[${n}]`);if(typeof e.mode!="string"||!l.test(e.mode))throw new Error(`${n} has an invalid mode`);if(typeof e.object!="string"||!a.test(e.object))throw new Error(`${n} has an invalid object ID`);return{mode:e.mode,object:e.object}}function k(t){const n=i(t,"inputs.duringRun");if(n.kind==="unchanged")return{kind:"unchanged"};if(n.kind==="changed")return{kind:"changed",paths:d(n.paths,"inputs.duringRun.paths").map(e=>r(e,"inputs.duringRun.paths[]"))};if(n.kind==="unchecked")return{kind:"unchecked",reason:r(n.reason,"reason")};throw new Error("inputs.duringRun.kind must be unchanged, changed, or unchecked")}function $(t){const n=i(t,"inputs.environment"),e=i(n.lineEndings,"inputs.environment.lineEndings");return{arch:r(n.arch,"inputs.environment.arch"),dependencies:f(n.dependencies,"inputs.environment.dependencies"),envFiles:f(n.envFiles,"inputs.environment.envFiles"),lineEndings:{autocrlf:c(e.autocrlf,"inputs.environment.lineEndings.autocrlf"),eol:c(e.eol,"inputs.environment.lineEndings.eol")},node:r(n.node,"inputs.environment.node"),platform:r(n.platform,"inputs.environment.platform"),testkit:r(n.testkit,"inputs.environment.testkit"),timeZone:r(n.timeZone,"inputs.environment.timeZone")}}function y(t){const n=i(t,"inputs.options");if(typeof n.suppliedEnvironment!="boolean")throw new Error("inputs.options.suppliedEnvironment must be a boolean");return{overrides:d(n.overrides,"inputs.options.overrides"),suppliedEnvironment:n.suppliedEnvironment,vitestConfig:c(n.vitestConfig,"inputs.options.vitestConfig"),vitestProject:c(n.vitestProject,"inputs.options.vitestProject"),vitestRunner:r(n.vitestRunner,"inputs.options.vitestRunner")}}function f(t,n){if(!p(t))throw new Error(`${n} must be an array`);return t.map((e,o)=>{const u=i(e,`${n}[${String(o)}]`),s=u.sha256===null?null:r(u.sha256,`${n}[${String(o)}].sha256`);if(s!==null&&!E.test(s))throw new Error(`${n}[${String(o)}].sha256 must be a SHA-256`);return{path:r(u.path,`${n}[${String(o)}].path`),sha256:s}})}function i(t,n){if(!m(t))throw new Error(`${n} must be an object`);return t}function R(t,n){if(typeof t!="string")throw new Error(`${n} must be a string`);return t}function r(t,n){if(typeof t!="string"||t.length===0)throw new Error(`${n} must be a non-empty string`);return t}function c(t,n){return t===null?null:r(t,n)}function d(t,n){const e=p(t)?t.filter(o=>typeof o=="string"):null;if(e===null||!p(t)||e.length!==t.length)throw new Error(`${n} must be an array of strings`);return e}function h(t){if(t.length===0||t.startsWith("/")||t.split("/").some(n=>n===".."||n===""))throw new Error(`${JSON.stringify(t)} isn't a relative repository path`);return t}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isUnknownRecord as e}from"../kernel/unknown-values.js";export function runModel(r){return r.schemaVersion===19?
|
|
1
|
+
import{isUnknownRecord as e}from"../kernel/unknown-values.js";import{readRunInputs as l}from"./run-inputs.js";export function runModel(r){return r.schemaVersion===19?f(r):p(r)}function f(r){return{...n(r),groups:r.services.map(t),summary:r.summary===void 0?null:{files:r.summary.files,groups:r.summary.services,suites:r.summary.suites}}}function p(r){return{...n(r),groups:r.groups.map(t),summary:r.summary===void 0?null:{files:r.summary.files,groups:r.summary.groups,suites:r.summary.suites}}}function n(r){return{archive:y(r.archive),finalizationFailure:r.finalizationFailure??null,generatedAt:r.generatedAt??null,git:{commitSha:e(r.git)?a(r.git.commitSha):null},inputs:r.schemaVersion===20?l(r.inputs):{kind:"missing"},run:{durationMs:r.run.durationMs??null,finishedAt:r.run.finishedAt,id:r.run.id,selection:{fileNames:u(r.run.fileNames),groupFilter:a(r.run.groupFilter),suiteSelectors:u(r.run.suiteSelectors)},startedAt:r.run.startedAt,status:r.run.status,types:r.run.types??[]},schemaVersion:r.schemaVersion}}function t(r){return{errors:Array.isArray(r.errors)?r.errors.filter(s=>typeof s=="string"):[],name:r.name,suites:r.suites.map(o)}}function o(r){return{engineId:r.engineId,files:r.files??[],name:r.name,profileId:r.profileId,...r.testType===void 0?{}:{testType:r.testType}}}function y(r){if(!e(r))return{cleanup:null,publication:null};const{cleanup:s,publication:i}=r;return{cleanup:e(s)&&typeof s.status=="string"?{status:s.status}:null,publication:e(i)&&typeof i.status=="string"?{required:i.required===!0,status:i.status}:null}}function u(r){return Array.isArray(r)?r.filter(s=>typeof s=="string"):[]}function a(r){return typeof r=="string"?r:null}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{recordWork as
|
|
1
|
+
import{recordWork as M}from"../kernel/work-metrics.js";const y=String;export function createSetupOperationRegistry({logRegistry:t=null,onChange:m=null}={}){const l=[],o=new Map;let c=1;function f(){m?.()}function h({config:e,stage:n,kind:u="setup",summary:a=null,parentId:s=null},r,i){const d={id:`setup-${c.toString()}`,serviceName:e.name,runtimeLabel:e.runtimeLabel||e.name,stage:n,kind:u,summary:a,parentId:s,status:r.status,startedAt:r.startedAt,finishedAt:r.finishedAt,durationMs:r.durationMs,error:null,logRef:i?{path:i.path,stage:i.stage}:null,_logRecord:i};return c+=1,l.push(d),o.set(d.id,d),f(),d}function A({recordLog:e=!0,...n}){const u=new Date().toISOString(),a=e&&t?.ensureSetupLogRecord(n.config,n.stage)||null;return h(n,{status:"running",startedAt:u,finishedAt:null,durationMs:null},a)}function g(e,{status:n="passed",summary:u,error:a=null}={}){const s=typeof e=="string"?o.get(e):e;if(!s)return null;const r=new Date().toISOString();return s.finishedAt=r,s.durationMs=Math.max(0,Date.parse(r)-Date.parse(s.startedAt||r)),s.status=n,u!==void 0&&(s.summary=u),a!==null&&(s.error=y(a)),f(),s}function p(e){const n=new Date().toISOString();return h(e,{status:"cached",startedAt:n,finishedAt:n,durationMs:0},null)}function S(){return M("setup.historyClones"),l.map(I)}return{start:A,finish:g,recordCached:p,listOperations:S}}function I(t){return{id:t.id,serviceName:t.serviceName,runtimeLabel:t.runtimeLabel,stage:t.stage,kind:t.kind,summary:t.summary,parentId:t.parentId,status:t.status,startedAt:t.startedAt,finishedAt:t.finishedAt,durationMs:t.durationMs,error:t.error,logRef:t.logRef}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import u from"fs";import a from"path";import{recordWork as F}from"../kernel/work-metrics.js";import{requireContainedPath as P}from"./storage.js";const W=20,w=new Set([".testkit/results/latest.json",".testkit/results/live.json"]);export function collectTestkitStorageInventory(e,t={}){const n=a.resolve(e),{rootDir:s,rootRelativePath:i}=E(n,t.rootRelativePath||".testkit"),f=t.largestFileLimit??W;if(!u.existsSync(s))return A(i,f);const c=[],h=[],b=new Set;let k=0,g=0;for(const o of S(s)){const r=m(a.relative(n,o));if(w.has(r))continue;const l=u.statSync(o,{bigint:!0,throwIfNoEntry:!1});if(l===void 0)continue;const d=classifyTestkitStoragePath(r),v=l.ino?`${String(l.dev)}:${String(l.ino)}`:r,_=!b.has(v);b.add(v);const p=Number(l.size);k+=p,_&&(g+=p);const y={path:r,bytes:p,classification:d.classification,reason:d.reason,linkCount:Number(l.nlink)||1,unique:_};d.duplicateOf&&(y.duplicateOf=d.duplicateOf,h.push(y)),c.push(y)}return c.sort((o,r)=>r.bytes-o.bytes||o.path.localeCompare(r.path)),h.sort((o,r)=>r.bytes-o.bytes||o.path.localeCompare(r.path)),{root:i,largestFileLimit:f,totalFiles:c.length,totalLogicalBytes:k,totalUniqueBytes:g,largestFiles:c.slice(0,f),duplicateRepresentations:h}}export function classifyTestkitStoragePath(e){const t=m(e);return t.endsWith(".b64")?{classification:"duplicate_representation",reason:"base64 representation of a sibling artifact",duplicateOf:t.slice(0,-4)}:t.startsWith(".testkit/results/runs/")?{classification:"retained_run_evidence",reason:"immutable run-addressed Testkit evidence"}:t.startsWith(".testkit/results/objects/")?{classification:"content_addressed_evidence",reason:"deduplicated immutable payload content referenced by run bundles"}:t.startsWith(".testkit/results/publications/")||t.startsWith(".testkit/results/retention/")?{classification:"evidence_lifecycle_metadata",reason:"publication receipt or observable retention decision"}:t.startsWith(".testkit/tmp/")||t.startsWith(".testkit/results/")||t.startsWith(".testkit/run-logs/")||t.startsWith(".testkit/logs/")?{classification:"per_run_scratch",reason:"run-scoped Testkit output or scratch state"}:t.startsWith(".testkit/_config/")||t.startsWith(".testkit/_bundles/")?{classification:"per_run_scratch",reason:"generated Testkit runtime state"}:{classification:"testkit_state",reason:"Testkit-owned state outside a more specific storage class"}}function A(e,t){return{root:e,largestFileLimit:t,totalFiles:0,totalLogicalBytes:0,totalUniqueBytes:0,largestFiles:[],duplicateRepresentations:[]}}function*S(e,t="root"){F(`storage.inventoryDirectoriesRead.${t}`);const n=R(e).sort((s,i)=>s.name.localeCompare(i.name));for(const s of n){const i=a.join(e,s.name);s.isDirectory()?yield*S(i,t==="root"?s.name:t):s.isFile()&&(yield i)}}function R(e){try{return u.readdirSync(e,{withFileTypes:!0})}catch(t){if(t instanceof Error&&"code"in t&&t.code==="ENOENT")return[];throw t}}function E(e,t){if(I(t))throw new Error(T(t));const n=m(t),s=a.resolve(e,n),i=T(t);if(P(e,s,i,!0),u.existsSync(s)){const f=u.realpathSync.native(e),c=u.realpathSync.native(s);P(f,c,i,!0)}return{rootDir:s,rootRelativePath:n}}function I(e){return a.isAbsolute(e)||a.posix.isAbsolute(e)||a.win32.isAbsolute(e)}function T(e){return`Storage inventory root must resolve within the product directory: ${e}`}function m(e){return L(e||"").replace(/\\/g,"/").replace(/^\/+/,"")}const L=String;
|
package/dist/evidence/storage.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{isWorkObserved as f,recordWork as
|
|
2
|
-
`)}export function hashFile(r){const
|
|
1
|
+
import{isWorkObserved as f,recordWork as a}from"../kernel/work-metrics.js";import{isPathWithin as p}from"../process/files/paths.js";import{isUnknownRecord as b}from"../kernel/unknown-values.js";import x from"crypto";import n from"fs";import u from"path";import{RUNS_DIRNAME as h}from"./contracts.js";import{requireManifestRelativePath as v}from"./read-fields.js";import{posixPath as m}from"../process/files/paths.js";import{errorMessage as g}from"../kernel/error-serialization.js";import{recordEvidenceWrite as w}from"../process/files/evidence-writes.js";export function readJsonFile(r){try{let e;if(f()){const i=n.readFileSync(r);a("json.filesRead",1),a("json.bytesRead",i.byteLength),e=i.toString("utf8")}else e=n.readFileSync(r,"utf8");return JSON.parse(e)}catch(e){throw new Error(`Invalid JSON in ${r}: ${g(e)}`,{cause:e})}}export function runIncludesFile(r,e){const t=m(e);return r.groups.some(i=>i.suites.some(o=>o.files.some(s=>s.path===t||s.path.endsWith(t))))}export function requireRunId(r){const e=r.run.id==null?null:normalizeRunId(r.run.id);if(e===null)throw new Error("Cannot seal a run bundle without a run id");return e}export function normalizeRunId(r){const e=typeof r=="string"?r:"";if(!e||e==="."||e===".."||e.includes("\0")||e.includes("/")||e.includes("\\"))throw new Error(`Invalid Testkit run id: ${e||"<empty>"}`);return e}export function walkFiles(r){if(!n.existsSync(r))return[];const e=[];for(const t of n.readdirSync(r,{withFileTypes:!0})){const i=u.join(r,t.name);t.isDirectory()?e.push(...walkFiles(i)):t.isFile()&&e.push(i)}return e}export function treeBytes(r){return walkFiles(r).reduce((e,t)=>e+n.statSync(t).size,0)}export function writeBufferAtomic(r,e){w(r),n.mkdirSync(u.dirname(r),{recursive:!0});const t=`${r}.${String(process.pid)}.${String(Date.now())}.tmp`;n.writeFileSync(t,e);try{n.renameSync(t,r)}catch(i){if(n.rmSync(t,{force:!0}),!n.existsSync(r))throw i}}export function writeJson(r,e){w(r),n.mkdirSync(u.dirname(r),{recursive:!0});const t=JSON.stringify(e,null,2);a("json.serializations"),f()&&a("json.bytesSerialized",Buffer.byteLength(t)),n.writeFileSync(r,`${t}
|
|
2
|
+
`)}export function hashFile(r){const e=n.readFileSync(r);return a("evidence.hashFilesRead",1),a("evidence.hashBytesRead",e.byteLength),sha256(e)}export function sha256(r){return x.createHash("sha256").update(r).digest("hex")}export function stableJson(r){const e=JSON.stringify(d(r));return a("json.canonicalSerializations"),f()&&a("json.canonicalBytesSerialized",Buffer.byteLength(e)),e}function d(r){return Array.isArray(r)?r.map(d):b(r)?Object.fromEntries(Object.keys(r).sort().map(e=>[e,d(r[e])])):r}export function resolveRunBundleDirectory(r,e){const t=resolveRunsRoot(r,!1),i=u.join(t,e),o=n.lstatSync(i,{throwIfNoEntry:!1});if(o===void 0)return i;if(o.isSymbolicLink())throw new Error(`Immutable run bundle directory must not be a symbolic link: ${e}`);if(!o.isDirectory())throw new Error(`Immutable run bundle path must be a directory: ${e}`);const s=n.realpathSync(t),c=n.realpathSync(i);return requireContainedPath(s,c,`Immutable run bundle directory must resolve within the runs root: ${e}`),i}export function resolveRunsRoot(r,e){const t=u.resolve(r),i=resultsRoot(t),o=u.join(i,h);if(e)E(t,i,"Testkit results root"),n.mkdirSync(i,{recursive:!0});else if(!n.existsSync(i))return o;const s=S(i,"Testkit results root");if(requireContainedPath(n.realpathSync(t),s,"Testkit results root must resolve within the product directory"),e)n.mkdirSync(o,{recursive:!0});else if(!n.existsSync(o))return o;const c=S(o,"Immutable run bundles root");return requireContainedPath(s,c,"Immutable run bundles root must resolve within .testkit/results"),o}function E(r,e,t){const i=u.resolve(r),o=n.realpathSync(i);let s=u.resolve(e),c=n.lstatSync(s,{throwIfNoEntry:!1});for(;c===void 0;){const l=u.dirname(s);if(l===s)throw new Error(`${t} has no existing parent directory`);s=l,c=n.lstatSync(s,{throwIfNoEntry:!1})}if(s!==i&&c.isSymbolicLink())throw new Error(`${t} must not traverse a symbolic link`);if(!c.isDirectory())throw new Error(`${t} parent must be a directory`);requireContainedPath(o,n.realpathSync(s),`${t} must resolve within the product directory`,!0)}function S(r,e){const t=n.lstatSync(r,{throwIfNoEntry:!1});if(t===void 0)throw new Error(`${e} does not exist`);if(t.isSymbolicLink())throw new Error(`${e} must not be a symbolic link`);if(!t.isDirectory())throw new Error(`${e} must be a directory`);return n.realpathSync(r)}export function requireContainedPath(r,e,t,i=!1){const o=u.relative(r,e);if(!i&&o.length===0||o===".."||o.startsWith(`..${u.sep}`)||u.isAbsolute(o))throw new Error(t)}export function requirePublicationPathOutsideResults(r,e){const t=u.resolve(resultsRoot(r)),i=u.resolve(e);if(p(t,i))throw new Error("Publication directory must be outside .testkit/results");const o=n.realpathSync(t);let s=i,c=n.lstatSync(s,{throwIfNoEntry:!1});for(;c===void 0;){const y=u.dirname(s);if(y===s)throw new Error("Publication directory has no existing parent");s=y,c=n.lstatSync(s,{throwIfNoEntry:!1})}const l=n.realpathSync(s);if(p(o,l))throw new Error("Publication directory must be outside .testkit/results")}export function resolveManifestEntryPath(r,e){const t=u.resolve(r),i=u.resolve(t,v(e,"manifest entry path")),o=`manifest path escapes bundle directory: ${e}`;return requireContainedPath(t,i,o),n.existsSync(i)&&requireContainedPath(n.realpathSync(t),n.realpathSync(i),o),i}export function optionalStringArray(r){return Array.isArray(r)?r.filter(e=>typeof e=="string"):[]}export function optionalUnknownArray(r){return Array.isArray(r)?r:[]}export function bundleRelativePath(r,e){return m(u.join(".testkit","results",h,r,e))}export function resultsRoot(r){return u.join(r,".testkit","results")}export function sanitizeSegment(r,e){return r.toLowerCase().replace(/[^a-z0-9._-]+/gu,"-").replace(/^-+|-+$/gu,"")||e}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export function captureOutput(n,o={}){if(!n)return Promise.resolve();const u=n;if(u.destroyed===!0||u.readableEnded===!0)return Promise.resolve();const f=o.onLine??null,t=o.liveWriter??null,r=o.livePrefix??"";let e="";return new Promise(d=>{let s=!1;const l=()=>{s||(s=!0,d())};n.on("data",v=>{e+=String(v);const c=e.split(/\r?\n/);e=c.pop()||"";for(const i of c)i.length!==0&&(f?.(i),t&&t(r?`${r} ${i}`:i))}),n.on("end",()=>{e.length>0&&(f?.(e),t&&t(r?`${r} ${e}`:e)),l()}),n.on("close",l),n.on("error",l)})}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"entrypoint": "dist/process/process.js",
|
|
4
|
-
"sourceSha256": "
|
|
5
|
-
"bundleSha256": "
|
|
6
|
-
"packageLockSha256": "
|
|
4
|
+
"sourceSha256": "4882923ad24b8a63021548b0e96407eeb9275ca665447a35001c49e7a1f91e2c",
|
|
5
|
+
"bundleSha256": "7c85510dd1181f64545c9ebed4a5b2045ebdf528dd2f73f5794102bb3d7d3955",
|
|
6
|
+
"packageLockSha256": "c8dc7f96e4663560a72b07a8b72ee4ba1035eccb1a1a969fe037bcadb605040f",
|
|
7
7
|
"esbuildVersion": "0.28.1",
|
|
8
8
|
"options": {
|
|
9
9
|
"format": "esm",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import m from"node:fs";import i from"node:path";import{isUnknownArray as x,isUnknownRecord as u}from"../../kernel/unknown-values.js";export const SUPPORTED_PLATFORMS=["darwin","linux","win32"],SUPPORTED_ARCHITECTURES=["arm64","x64"],SUPPORTED_PLATFORM_TARGETS=Object.freeze([Object.freeze({arch:"arm64",platform:"darwin"}),Object.freeze({arch:"x64",platform:"darwin"}),Object.freeze({arch:"arm64",platform:"linux"}),Object.freeze({arch:"x64",platform:"linux"}),Object.freeze({arch:"x64",platform:"win32"})]);export class UnsupportedPlatformTargetError extends Error{arch;code="TESTKIT_PLATFORM_TARGET_UNSUPPORTED";platform;constructor(e,t,n="platform target"){super(`${n} does not support ${e}-${t}`),this.name="UnsupportedPlatformTargetError",this.platform=e,this.arch=t}}export function platformTargetKey(r){return`${r.platform}-${r.arch}`}export function currentPlatformTarget(r=process.platform,e=process.arch){if(!isSupportedPlatform(r)||!isSupportedArchitecture(e))throw new UnsupportedPlatformTargetError(r,e);const t={platform:r,arch:e};return assertSupportedPlatformTarget(t,"current platform target"),t}export function currentLinuxLibc(){if(process.platform!=="linux")return null;const r=process.report.getReport(),e=u(r)?r.header:null,t=u(e)?e.glibcVersionRuntime:null;return typeof t=="string"&&t.trim().length>0?"glibc":"musl"}export function assertSupportedPlatformTarget(r,e="platform target"){if(r.platform==="win32"&&r.arch!=="x64")throw new UnsupportedPlatformTargetError(r.platform,r.arch,e)}export function isSupportedPlatform(r){return SUPPORTED_PLATFORMS.includes(r)}export function isSupportedArchitecture(r){return SUPPORTED_ARCHITECTURES.includes(r)}export function isWindows(r=process.platform){return r==="win32"}export function commandName(r,e=process.platform){const t=stringifyUnknown(r||"").trim();if(!t)throw new Error("commandName requires a non-empty command name");return!isWindows(e)||/\.(?:cmd|bat|exe)$/i.test(t)?t:`${t}.cmd`}export function resolveCommandForSpawn(r,e={}){const t=e.platform||process.platform,n=e.env||process.env,o=stringifyUnknown(r||"").trim();if(!o)throw new Error("resolveCommandForSpawn requires a non-empty command name");if(!isWindows(t))return o;if(/[\\/]/u.test(o)||i.win32.isAbsolute(o))return A(o);const s=h(o,n),c=[...stringifyUnknown(n.PATH||n.Path||"").split(f(t)).map(a=>a.trim()).filter(Boolean),...T(o,n)];for(const a of c)for(const d of s){const l=i.join(a,`${o}${d}`);if(m.existsSync(l))return l}return commandName(o,t)}export function prepareCommandForSpawn(r,e=[],t={}){const n=t.platform||process.platform,o=t.env||process.env,s=resolveCommandForSpawn(r,{...t,platform:n,env:o}),c=x(e)?e.map(a=>stringifyUnknown(a)):[];return!isWindows(n)||!/\.(?:cmd|bat)$/i.test(s)?{command:s,args:c,windowsVerbatimArguments:!1}:{command:o.ComSpec||o.COMSPEC||"cmd.exe",args:["/d","/s","/c",P([s,...c])],windowsVerbatimArguments:!0}}export function packageBinPath(r,e,t=process.platform){const n=i.resolve(r||process.cwd()),o=stringifyUnknown(e||"").trim();if(!/^[A-Za-z0-9@._/-]+$/.test(o))throw new Error(`Invalid package bin name: ${stringifyUnknown(e)}`);const s=i.basename(o);return i.join(n,"node_modules",".bin",commandName(s,t))}export function shellQuote(r,e=process.platform){const t=stringifyUnknown(r);return isWindows(e)?w(t):/^[A-Za-z0-9_/:.,@%+=-]*$/.test(t)?t:`'${t.replaceAll("'","'\\''")}'`}function w(r){return/^[A-Za-z0-9_/:.,@%+=\\-]*$/.test(r)?r:`"${r.replaceAll('"','\\"')}"`}function P(r){const[e,...t]=r;return`"${[p(e,{force:!0}),...t.map(n=>p(n))].join(" ")}"`}function p(r,e={}){const t=stringifyUnknown(r);return!e.force&&t.length>0&&!/[\s&()<>^|"]/u.test(t)?t:`"${t.replace(/(\\*)"/gu,'$1$1\\"').replace(/\\+$/u,"$&$&")}"`}export function joinPathList(r,e=process.platform){return r.filter(Boolean).map(t=>stringifyUnknown(t)).join(f(e))}function f(r){return isWindows(r)?";":":"}function h(r,e){if(/\.(?:cmd|bat|exe|com)$/i.test(r))return[""];const t=stringifyUnknown(e.PATHEXT||".COM;.EXE;.BAT;.CMD").split(";").map(n=>n.trim().toLowerCase()).filter(Boolean);return[...new Set([".cmd",".exe",".bat",".com",...t])]}function A(r){if(m.existsSync(r)||/\.(?:cmd|bat|exe|com)$/i.test(r))return r;for(const e of[".cmd",".exe",".bat",".com"]){const t=`${r}${e}`;if(m.existsSync(t))return t}return commandName(r)}function T(r,e){return r.toLowerCase()!=="volta"?[]:[e.VOLTA_HOME,e.ProgramFiles?i.join(e.ProgramFiles,"Volta"):null,e["ProgramFiles(x86)"]?i.join(e["ProgramFiles(x86)"],"Volta"):null,e.LOCALAPPDATA?i.join(e.LOCALAPPDATA,"Volta","bin"):null,e.USERPROFILE?i.join(e.USERPROFILE,"AppData","Local","Volta","bin"):null].filter(t=>!!t)}export function dockerHostAddress(r,e=process.platform){const t=stringifyUnknown(r||"").trim().toLowerCase();return["win32","darwin"].includes(e)&&(t==="127.0.0.1"||t==="localhost"||t==="::1")?"host.docker.internal":r}export function shouldUseDockerHostNetwork(r=process.platform){return r==="linux"}export const stringifyUnknown=String;
|
package/dist/process/locks.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{AsyncLocalStorage as Ie}from"node:async_hooks";import{randomUUID as
|
|
2
|
-
`,{encoding:"utf8",flag:"wx",mode:384}),i.writeFileSync(a.join(e,
|
|
3
|
-
`,{encoding:"utf8",flag:"wx",mode:384})}catch(r){throw i.rmSync(e,{force:!0,recursive:!0}),r}}function
|
|
4
|
-
`,{encoding:"utf8",flag:"wx",mode:384}),i.rmSync(a.join(e,
|
|
1
|
+
import{AsyncLocalStorage as Ie}from"node:async_hooks";import{randomUUID as W}from"node:crypto";import i from"node:fs";import Ne from"node:os";import a from"node:path";import{setTimeout as Q}from"node:timers/promises";import{captureProcessCreationIdentity as Z,inspectProcessLiveness as Re,sameProcessCreationIdentity as z}from"./process-identity.js";import{retireWindowsClaim as Le}from"./windows-native.js";import{channel as Te}from"node:diagnostics_channel";const L=Te("testkit.work");import{isUnknownRecord as ee}from"../kernel/unknown-values.js";import{describeProcessLockProgress as te,formatSeconds as Ae,noteProcessLockWait as _e,runWithHeldProcessLockClaim as Me}from"./lock-progress.js";export{observeProcessLockWaits,withProcessLockProgress}from"./lock-progress.js";import{hasErrorCode as u}from"../kernel/error-serialization.js";function ge(e){try{i.mkdirSync(e,{mode:448})}catch(t){if(!u(t,"EEXIST"))throw t}return ne(e)}function Ce(e){try{const t=i.lstatSync(e);return t.isDirectory()&&!t.isSymbolicLink()?"real":"unsafe"}catch(t){if(u(t,"ENOENT"))return"absent";throw t}}function re(e){const t=i.lstatSync(e,{bigint:!0});if(!t.isDirectory()||t.isSymbolicLink())throw new Error(`Process lock path is not a real directory: ${e}`);return q(t)}function ne(e){try{return re(e)}catch(t){if(u(t,"ENOENT")||Oe(t))return null;throw t}}function y(e,t){return oe(ne(e),t)}function oe(e,t){return e!==null&&e.birthtimeNs===t.birthtimeNs&&e.dev===t.dev&&e.ino===t.ino}export function processLockClaimPublicationState(e,t,r){if(y(e,t))try{return X(r)?"unsafe":"retry"}catch(o){if(C(o))return"retry";throw o}return y(r,t)?"published":"unsafe"}function Oe(e){return e instanceof Error&&e.message.startsWith("Process lock path is not a real directory:")}const se="owner.json",x=1,U="claim-",j="choosing",ie="ticket",ce=100,ae=65e3,Pe=100,$e=new Set(["EACCES","EBUSY","EPERM"]),ve=1e4,De=3,We=20,xe=5,Ue=20,je=5e3,ue=Ne.hostname(),Fe=process.platform==="win32"?je:0,F=new Ie,K=new Set;class le extends Error{constructor(t,r){super(`Timed out waiting for process lock ${a.basename(t)}${ut(r?.ownerRecord??null)}`+(r===null?"":te(r.claimDirectory))),this.name="ProcessLockTimeoutError"}}class Ke extends Error{constructor(t,r){super(`Process lock ${a.basename(t)} has a claim from this process, made at ${String(r.ownerRecord?.createdAt)}, that no operation in it holds. A release of it failed; it clears when this process exits: ${r.claimDirectory}`),this.name="ProcessLockLeakedClaimError"}}export async function withProcessLock(e,t,r={}){const n=Se(e);it(n);const{claim:o,release:s}=await Ve(n,r),c=new Set(F.getStore()??[]);c.add(n);try{return await F.run(c,()=>Me(o,t))}finally{await s()}}export async function tryWithProcessLock(e,t,r={}){let n;try{n=await withProcessLock(e,async()=>{try{return{ok:!0,value:await t()}}catch(o){return{error:o,ok:!1}}},{...r,timeoutMs:0})}catch(o){if(o instanceof le)return{acquired:!1};throw o}if(!n.ok)throw n.error;return{acquired:!0,value:n.value}}export function probeProcessLock(e,t={}){const r=Se(e),n=ke(r);if(!X(n))return"absent";const o=t.identityObservations??new Map,s=me(n,de(n),o,fe(),t);return s.rootAvailable&&s.claims.length===0?(Y(n),"absent"):"active"}async function Ve(e,t){const r=t.timeoutMs??null,n=t.retryIntervalMs??ce;let o=t.retryIntervalMs===void 0?10:n;async function s(){const m=o;o=Math.min(n,o*2),await ct(m,t.signal,e)}const c=t.identityObservations??new Map,k=fe(),f=async m=>me(d,await A(()=>de(d,m),t.signal,e),c,k,t),p=Z(process.pid);if(p===null)throw new Error(`Cannot acquire process lock ${e}: current process creation identity is unavailable`);const S=Date.now(),I={createdAt:new Date(S).toISOString(),hostname:ue,pid:process.pid,processIdentity:p,schemaVersion:x,token:W()},d=ke(e),l=a.join(d,`${U}${I.token}`),N=`${d}.preparing-${I.token}`;He(N,I);let E=null,B=!1,R=null,M=!1,G=0,J=null,$;try{for(E=re(N);;){_(t.signal,e);const b=await A(()=>ge(d),t.signal,e);if(b!==null){try{i.renameSync(N,l),M=!0,R=b}catch(h){if(!u(h,"ENOENT"))if(C(h)){const w=processLockClaimPublicationState(N,E,l);if(w==="published")M=!0,R=b;else if(w==="retry"&&G<Pe)G+=1;else throw h}else throw h}if(M){if(!y(d,b))throw new Error(`Process lock directory changed during claim publication: ${d}`);if(!y(l,E))throw new Error(`Process lock claim changed during publication: ${l}`);break}}else await f(null);P(e,null,S,r),await s()}if(R===null)throw new Error(`Process lock claim was published without its directory identity: ${l}`);const m=R;let T=nextProcessLockTicket((await f(m)).claims);for(;T===null;)P(e,null,S,r),await s(),T=nextProcessLockTicket((await f(m)).claims);const g=T;Xe(l,g);const v=performance.now();for(;;){_(t.signal,e);const b=await f(m),h=b.claims.find(D=>D.claimDirectory===l);if(h?.state==="changing"){P(e,null,S,r),await s();continue}if(h?.state!=="ready"||h.ticket!==g||h.ownerRecord===null||!Ee(h.ownerRecord,I))throw new Error(`Process lock claim disappeared or changed before acquisition: ${l}`);const w=firstElectionBlocker(b.claims,h);if(w===null){B=!0;const D=E;return{claim:{claimDirectory:l,intact:()=>y(d,m)&&y(l,D)},release:async()=>{try{await tt(l,d,m,I,g)}catch(be){throw K.add(l),be}}}}if(qe(w,I))throw new Ke(e,w);w.claimDirectory!==J&&(J=w.claimDirectory,$?.(),$=_e(()=>Ye(e,w,performance.now()-v))),P(e,w,S,r),await s()}}finally{if($?.(),!M)E!==null&&y(N,E)&&i.rmSync(N,{force:!0,recursive:!0});else if(!B&&R!==null&&E!==null){const m=R,T=E;await we(l,d,()=>y(d,m)&&y(l,T)).catch(v=>{throw K.add(l),v})&&c.delete(l)}}}function qe(e,t){const r=e.ownerRecord;return r!==null&&r.hostname===t.hostname&&r.pid===t.pid&&z(r.processIdentity,t.processIdentity)&&K.has(e.claimDirectory)}function Ye(e,t,r){const n=t.ownerRecord;return`Waiting ${Ae(r)} for process lock ${a.basename(e)}, `+(n===null?"held by a claim it can't read":`held by pid ${n.pid.toString()} on ${n.hostname} since ${n.createdAt}`)+te(t.claimDirectory)}function C(e,t=process.platform){return t==="win32"&&ee(e)&&typeof e.code=="string"&&$e.has(e.code)}export function tolerateTransientClaimFailure(e,t,r,n=Date.now(),o=process.platform){if(!C(r,o))throw r;const s=e.get(t),c={count:(s?.count??0)+1,firstAtMs:s?.firstAtMs??n};if(c.count>=De&&n-c.firstAtMs>=ve)throw r;e.set(t,c)}export function transientWindowsLockRetryDelayMs(e,t,r=process.platform){return!C(e,r)||t>=We?null:Math.min(ce,10*t)}async function A(e,t,r=""){for(let n=1;;n+=1)try{return e()}catch(o){const s=transientWindowsLockRetryDelayMs(o,n);if(s===null)throw o;L.hasSubscribers&&L.publish({metric:"locks.transientWindowsRetries",amount:1});try{await Q(s,void 0,{signal:t})}catch(c){throw _(t,r),c}}}function fe(){return{entryReads:new Map,retirements:new Map}}function He(e,t){i.mkdirSync(e,{mode:448});try{i.writeFileSync(a.join(e,se),`${JSON.stringify(t)}
|
|
2
|
+
`,{encoding:"utf8",flag:"wx",mode:384}),i.writeFileSync(a.join(e,j),`${t.token}
|
|
3
|
+
`,{encoding:"utf8",flag:"wx",mode:384})}catch(r){throw i.rmSync(e,{force:!0,recursive:!0}),r}}function Xe(e,t){i.writeFileSync(a.join(e,ie),`${t.toString()}
|
|
4
|
+
`,{encoding:"utf8",flag:"wx",mode:384}),i.rmSync(a.join(e,j))}export function nextProcessLockTicket(e){if(e.some(r=>r.state==="changing"))return null;const t=e.reduce((r,n)=>n.ticket===null?r:Math.max(r,n.ticket),0);if(t>=Number.MAX_SAFE_INTEGER)throw new Error("Process lock ticket space is exhausted");return t+1}export function firstElectionBlocker(e,t){if(t.ticket===null||t.ownerRecord===null)return t;const r=t.ownerRecord.token;for(const n of e){if(n.claimDirectory===t.claimDirectory)continue;if(n.state!=="ready"||n.ticket===null||n.ownerRecord===null)return n;const o=n.ownerRecord.token;if(Be(n.ticket,o,t.ticket,r)<0)return n}return null}function Be(e,t,r,n){return e===r?t.localeCompare(n):e-r}function de(e,t=null){const r=Ce(e);if(r!=="real")return{state:r};if(t!==null&&!y(e,t))return{state:"changed"};try{return{entries:i.readdirSync(e,{withFileTypes:!0}),state:"listed"}}catch(n){if(u(n,"ENOENT"))return{state:"absent"};throw n}}function me(e,t,r,n,o){if(t.state!=="listed")return t.state==="absent"?{claims:[],rootAvailable:!0}:t.state==="unsafe"&&et(e,r,o)?{claims:[],rootAvailable:!0}:{claims:[O(e,"malformed")],rootAvailable:!1};const{entries:s}=t,c=[];for(const k of s){const f=a.join(e,k.name);let p;try{p=Ge(k,f)}catch(S){tolerateTransientClaimFailure(n.entryReads,f,S),c.push(O(f,"changing"));continue}n.entryReads.delete(f),p===null?n.retirements.delete(f):Ze(p,r,o)?ze(e,f,p.identity,r,n.retirements)==="held"&&c.push(O(f,"changing")):(n.retirements.delete(f),c.push(p.claim))}return{claims:c,rootAvailable:!0}}function Ge(e,t){const r=V(t);if(r===null)return null;const n=!e.name.startsWith(U)||!e.isDirectory()?O(t,"malformed"):he(t),o=V(t);return o===null?null:{claim:n,identity:q(r),modifiedAtMs:Number(o.mtimeMs)}}function Je(e){const t=V(e);return t===null?null:q(t)}function V(e){try{return i.lstatSync(e,{bigint:!0})}catch(t){if(u(t,"ENOENT"))return null;throw t}}function q(e){return{birthtimeNs:e.birthtimeNs,dev:e.dev,ino:e.ino}}function O(e,t){return{claimDirectory:e,ownerRecord:null,state:t,ticket:null}}function he(e){const t=ot(e),r=X(a.join(e,j)),n=Qe(e),o=a.basename(e).slice(U.length),s=t===null||t.token===o;return{claimDirectory:e,ownerRecord:t,state:t===null||!s?"malformed":r?"choosing":n===null?"malformed":"ready",ticket:n}}function Qe(e){const t=ye(a.join(e,ie));if(t===null)return null;const r=Number(t.trim());return Number.isSafeInteger(r)&&r>0?r:null}function ye(e){try{return i.readFileSync(e,"utf8")}catch(t){if(u(t,"ENOENT")||u(t,"EISDIR"))return null;throw t}}function Ze({claim:e,modifiedAtMs:t},r,n){const o=e.ownerRecord;if(o===null||o.hostname!==ue)return n.hostScope==="current-host"&&Date.now()-t>=ae;if(Re(o.pid)==="absent")return!0;const s=H(o),c=r.get(e.claimDirectory);if(c?.ownerKey===s&&Date.now()-c.checkedAtMs<Fe)return!1;const k=Z(o.pid);return r.set(e.claimDirectory,{checkedAtMs:Date.now(),ownerKey:s}),k!==null&&!z(k,o.processIdentity)}function ze(e,t,r,n,o){n.delete(t);const s=`${e}.reclaiming-${W()}`;try{if(process.platform==="win32"){if(Le(t,s,r)==="absent")return o.delete(t),"removed"}else i.renameSync(t,s)}catch(c){return process.platform!=="win32"&&u(c,"ENOENT")?(o.delete(t),"removed"):(tolerateTransientClaimFailure(o,t,c),"held")}if(o.delete(t),!oe(Je(s),r))throw new Error(`Process lock claim ${t} changed before reclamation and is now at ${s}`);return i.rmSync(s,{maxRetries:xe,recursive:!0,retryDelay:Ue}),L.hasSubscribers&&L.publish({metric:"locks.reclaimedClaims",amount:1}),"removed"}function et(e,t,r){if(r.hostScope!=="current-host")return!1;const n=pe(e);if(n==="absent")return!0;if(n==="real-directory"||nt(n)<ae)return!1;const o=pe(e);if(o==="absent")return!0;if(o==="real-directory"||!rt(o,n))return!1;try{return i.unlinkSync(e),t.delete(e),!0}catch(s){if(u(s,"ENOENT"))return!0;if(u(s,"EISDIR")||u(s,"EPERM"))return!1;throw s}}async function tt(e,t,r,n,o){await we(e,t,()=>{if(!y(t,r))throw new Error(`Process lock directory changed before release: ${t}`);const s=he(e);if(s.state!=="ready"||s.ticket!==o||s.ownerRecord===null||!Ee(s.ownerRecord,n))throw new Error(`Process lock ownership changed before release: ${e}`);return!0})}async function we(e,t,r){if(process.platform!=="win32")return r()?(i.rmSync(e,{force:!0,recursive:!0}),Y(t),!0):!1;const n=`${t}.releasing-${W()}`;return await A(()=>r()?(i.renameSync(e,n),!0):!1)?(await A(()=>{i.rmSync(n,{force:!0,recursive:!0})}),await A(()=>{Y(t)}),!0):!1}function pe(e){try{const t=i.lstatSync(e,{bigint:!0});return t.isDirectory()&&!t.isSymbolicLink()?"real-directory":{birthtimeNs:t.birthtimeNs,ctimeNs:t.ctimeNs,dev:t.dev,ino:t.ino,mode:t.mode,mtimeNs:t.mtimeNs,size:t.size}}catch(t){if(u(t,"ENOENT"))return"absent";throw t}}function rt(e,t){return e.birthtimeNs===t.birthtimeNs&&e.ctimeNs===t.ctimeNs&&e.dev===t.dev&&e.ino===t.ino&&e.mode===t.mode&&e.mtimeNs===t.mtimeNs&&e.size===t.size}function nt(e){return Math.max(0,Date.now()-Number(e.mtimeNs)/1e6)}function Y(e){try{i.rmdirSync(e)}catch(t){if(!u(t,"ENOENT")&&!u(t,"ENOTEMPTY")&&!u(t,"EEXIST"))throw t}}function ot(e){const t=ye(a.join(e,se));if(t===null)return null;let r;try{r=JSON.parse(t)}catch{return null}return ee(r)?st(r):null}function st(e){const t=Object.keys(e).sort(),r=["createdAt","hostname","pid","processIdentity","schemaVersion","token"];return t.length!==r.length||t.some((n,o)=>n!==r[o])||e.schemaVersion!==x||!lt(e.createdAt)||typeof e.hostname!="string"||e.hostname.length===0||!ft(e.pid)||typeof e.processIdentity!="string"||e.processIdentity.length===0||typeof e.token!="string"||e.token.length===0?null:{createdAt:e.createdAt,hostname:e.hostname,pid:e.pid,processIdentity:e.processIdentity,schemaVersion:x,token:e.token}}function Ee(e,t){return H(e)===H(t)}function H(e){return[e.hostname,e.pid.toString(),e.processIdentity,e.token,e.createdAt].join("\0")}function ke(e){return`${e}.dir`}function Se(e){const t=a.resolve(e),r=i.realpathSync.native(a.dirname(t));return a.join(r,a.basename(t))}function it(e){if(F.getStore()?.has(e))throw new Error(`Reentrant process lock acquisition is not allowed: ${e}`)}function P(e,t,r,n){if(n!==null&&Date.now()-r>=n)throw new le(e,t)}async function ct(e,t,r){_(t,r),L.hasSubscribers&&L.publish({metric:"locks.contentionRetries",amount:1});try{await Q(e,void 0,{signal:t})}catch(n){throw _(t,r),n}}function _(e,t){if(e?.aborted)throw at(e,t)}function at(e,t){return e.reason instanceof Error?e.reason:new Error(`Waiting for process lock was aborted: ${t}`)}function ut(e){return e===null?"":` (host=${e.hostname} pid=${e.pid.toString()} identity=${e.processIdentity} createdAt=${e.createdAt})`}function lt(e){return typeof e=="string"&&e.length>0&&Number.isFinite(Date.parse(e))}function ft(e){return Number.isSafeInteger(e)&&Number(e)>0}function X(e){try{return i.lstatSync(e),!0}catch(t){if(u(t,"ENOENT"))return!1;throw t}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DEFAULT_SHUTDOWN_TIMEOUT_MS as
|
|
1
|
+
import{DEFAULT_SHUTDOWN_TIMEOUT_MS as u,FORCE_TERMINATION_WAIT_MS as c}from"./shutdown.js";import{hasErrorCode as a,toError as l}from"../kernel/error-serialization.js";export async function cleanupExitedProcessGroup(r,n={}){if(process.platform==="win32")return null;const t=n.groupExists??processGroupExists,o=n.sendSignal??f,s=n.graceMs??u;try{return!t(r)||(n.initialSignalSent||o(r,"SIGTERM"),await i(r,s,t,n.remainingMs))?null:(o(r,"SIGKILL"),await i(r,c,t)?null:new Error(`Managed process group ${r.toString()} did not stop`))}catch(e){return a(e,"ESRCH")?null:l(e)}}async function i(r,n,t,o){const s=performance.now(),e=o??(()=>n-(performance.now()-s));for(;e()>0;){if(!t(r))return!0;await delay(Math.min(25,Math.max(1,e())))}return!t(r)}function f(r,n){process.kill(-r,n)}export function delay(r){return new Promise(n=>setTimeout(n,r))}export function processGroupExists(r){try{return process.kill(-r,0),!0}catch(n){if(a(n,"ESRCH"))return!1;if(a(n,"EPERM"))return!0;throw n}}
|