@gfdelarue/testkit 0.3.4 → 0.3.5
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 +4 -0
- package/dist/app/doctor/index.js +1 -1
- package/dist/app/inspect/artifacts.d.ts +1 -0
- package/dist/app/inspect/artifacts.js +1 -1
- package/dist/app/inspect/index.js +1 -1
- package/dist/app/lint/index.js +1 -1
- package/dist/app/lint/native.js +1 -1
- package/dist/app/lint/policy.js +1 -1
- package/dist/app/lint/rules/data.js +1 -1
- package/dist/app/lint/rules/engine-imports.js +1 -1
- package/dist/app/lint/rules/syntax.js +1 -1
- package/dist/app/lint/rules/unit.js +1 -1
- package/dist/app/lint/shared.d.ts +1 -1
- package/dist/app/lint/shared.js +1 -1
- package/dist/app/run/orchestrator.js +1 -1
- package/dist/app/run/request.js +1 -1
- package/dist/app/setup/playwright.js +1 -1
- package/dist/app/status/index.d.ts +2 -0
- package/dist/app/status/index.js +1 -1
- package/dist/app/status/runs.d.ts +2 -2
- package/dist/app/status/runs.js +1 -1
- package/dist/app/stop/index.js +1 -1
- package/dist/app/typecheck/index.d.ts +2 -0
- package/dist/app/typecheck/index.js +4 -4
- package/dist/cli/commands/artifacts.js +2 -2
- package/dist/cli/commands/db/schema/refresh.js +1 -1
- package/dist/cli/commands/db/schema/verify.js +1 -1
- package/dist/cli/commands/doctor.js +1 -1
- package/dist/cli/operations/db/schema/refresh/operation.d.ts +1 -0
- package/dist/cli/operations/db/schema/refresh/operation.js +1 -1
- package/dist/cli/operations/db/schema/verify/operation.d.ts +1 -0
- package/dist/cli/operations/db/schema/verify/operation.js +1 -1
- package/dist/cli/operations/run/operation.js +1 -1
- package/dist/config/loader/discover-tests.d.ts +5 -0
- package/dist/config/loader/discover-tests.js +1 -1
- package/dist/config/loader/discovery.js +2 -2
- package/dist/config/loader/file-metadata.js +1 -1
- package/dist/config/loader/index.d.ts +2 -1
- package/dist/config/loader/index.js +1 -1
- package/dist/config/loader/load.d.ts +2 -0
- package/dist/config/loader/load.js +1 -1
- package/dist/config/loader/managed-configs.d.ts +2 -0
- package/dist/config/loader/managed-configs.js +1 -1
- package/dist/config/loader/paths.d.ts +1 -1
- package/dist/config/loader/paths.js +1 -1
- package/dist/config/loader/skip-config.d.ts +1 -1
- package/dist/config/loader/test-selection.d.ts +15 -0
- package/dist/config/loader/test-selection.js +1 -0
- package/dist/database/index.d.ts +1 -3
- package/dist/database/index.js +1 -1
- package/dist/database/lifecycle-cleanup.d.ts +7 -0
- package/dist/database/lifecycle-cleanup.js +1 -0
- package/dist/database/local-postgres.js +1 -1
- package/dist/database/schema-source.js +3 -3
- package/dist/engines/adapters/evidence-files.js +1 -1
- package/dist/engines/catalog.d.ts +3 -8
- package/dist/engines/catalog.js +1 -1
- package/dist/engines/definitions.d.ts +8 -1
- package/dist/engines/definitions.js +1 -1
- package/dist/engines/generated-performance.d.ts +1 -0
- package/dist/engines/generated-performance.js +12 -0
- package/dist/engines/installations/node-vitest.d.ts +8 -22
- package/dist/engines/installations/node-vitest.js +1 -1
- package/dist/engines/installations/package-requirements.d.ts +28 -0
- package/dist/engines/installations/package-requirements.js +1 -0
- package/dist/engines/installations/playwright.d.ts +1 -11
- package/dist/engines/installations/playwright.js +1 -1
- package/dist/engines/k6/adapter/repository-configuration.d.ts +1 -1
- package/dist/engines/k6/adapter/repository-configuration.js +1 -1
- package/dist/engines/managed-module-resolution.js +111 -21
- package/dist/engines/manager.d.ts +1 -0
- package/dist/engines/manager.js +1 -1
- package/dist/engines/node-vitest/adapter/adapter.d.ts +1 -1
- package/dist/engines/node-vitest/adapter/adapter.js +1 -1
- package/dist/engines/node-vitest/adapter/consumer-typescript-paths.js +1 -1
- package/dist/engines/node-vitest/adapter/resolution.d.ts +1 -1
- package/dist/engines/node-vitest/adapter/resolution.js +1 -1
- package/dist/engines/node-vitest/adapter/workspace.js +33 -9
- package/dist/engines/node-vitest/runtime/runtime-input.d.ts +1 -0
- package/dist/engines/node-vitest/runtime/runtime-input.js +1 -1
- package/dist/engines/runtime-input-file.d.ts +3 -1
- package/dist/engines/runtime-input-file.js +1 -1
- package/dist/environment/lifecycle-controller.d.ts +1 -2
- package/dist/environment/lifecycle-controller.js +1 -1
- package/dist/environment/lifecycle.js +1 -1
- package/dist/environment/maintenance.d.ts +1 -1
- package/dist/environment/maintenance.js +1 -1
- package/dist/environment/ready-environment.d.ts +1 -1
- package/dist/environment/ready-environment.js +1 -1
- package/dist/environment/runtime-contexts.d.ts +1 -1
- package/dist/environment/runtime-contexts.js +1 -1
- package/dist/environment/runtime-preparation.js +1 -1
- package/dist/environment/services.js +1 -1
- package/dist/environment/template.d.ts +1 -1
- package/dist/environment/template.js +1 -1
- package/dist/evidence/bundles.js +1 -1
- package/dist/evidence/read-run.js +1 -1
- package/dist/evidence/reading.d.ts +1 -1
- package/dist/evidence/reading.js +1 -1
- package/dist/evidence/storage.js +2 -2
- package/dist/kernel/error-serialization.js +1 -1
- package/dist/kernel/work-metrics.d.ts +6 -0
- package/dist/kernel/work-metrics.js +1 -0
- package/dist/process/capture-output.js +1 -1
- package/dist/process/execa-bundle.json +1 -1
- package/dist/process/files/product-dir.d.ts +1 -0
- package/dist/process/files/product-dir.js +1 -0
- package/dist/process/locks.js +4 -4
- package/dist/process/readiness.js +1 -1
- package/dist/process/runs/inspection.d.ts +5 -0
- package/dist/process/runs/inspection.js +1 -0
- package/dist/process/runs/manifest.d.ts +1 -0
- package/dist/process/runs/manifest.js +1 -1
- package/dist/process/service-processes.d.ts +1 -1
- package/dist/process/service-processes.js +1 -1
- package/dist/toolchains/index.js +2 -2
- package/docs/engine-architecture.md +4 -1
- package/docs/test-types.md +9 -0
- package/package.json +2 -1
- package/dist/cli/renderers/artifact-preview.d.ts +0 -4
- package/dist/cli/renderers/artifact-preview.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{requireNativeEvidenceJson as
|
|
1
|
+
import{requireNativeEvidenceJson as N}from"./native-report.js";import E from"node:crypto";import{constants as O}from"node:fs";import d from"node:fs/promises";import F from"node:path";import{readOnlyNoFollowOpenFlags as R,sameFileSnapshot as U,samePathAndHandleFileSnapshot as C}from"../../process/files/file-snapshot.js";const z=64*1024;export const evidenceFileAccess={lstat:async t=>d.lstat(t,{bigint:!0}),openReadOnly:async t=>d.open(t,R(process.platform,O)),platform:process.platform,readChunk:async(t,n)=>(await t.read(n,0,n.byteLength,null)).bytesRead,statHandle:async t=>t.stat({bigint:!0})};export function stableEvidenceFromBuffer(t){return{contents:t,sha256:E.createHash("sha256").update(t).digest("hex"),sizeBytes:t.byteLength}}export async function readStableJsonFile(t,n,e,r){let i;try{i=await readStableEvidenceFile(t,n,e,r)}catch(o){if(L(o))return{file:null,value:null};throw o}let a;try{a=JSON.parse(i.contents.toString("utf8"))}catch(o){throw new Error(`${e} is not valid JSON`,{cause:o})}return N(a,e),{file:i,value:a}}export async function readStableEvidenceFile(t,n,e,r,i){const a=i??await r.lstat(t);u(a,n,e);const o=await r.openReadOnly(t);try{const w=await r.statHandle(o);u(w,n,e),m(a,w,e,r.platform);const p=[],l=E.createHash("sha256");let h=0;const s=Buffer.allocUnsafe(z);for(;;){const f=await r.readChunk(o,s);if(I(f,s.byteLength,e),f===0)break;if(h+=f,h>n)throw new Error(`${e} exceeds its ${n.toString()} byte limit`);const g=Buffer.from(s.subarray(0,f));p.push(g),l.update(g)}const c=await r.statHandle(o),y=await r.lstat(t);if(u(c,n,e),u(y,n,e),S(w,c,e),S(a,y,e),m(y,c,e,r.platform),BigInt(h)!==w.size)throw new Error(`${e} changed while it was read`);return{contents:Buffer.concat(p,h),sha256:l.digest("hex"),sizeBytes:h}}finally{await o.close()}}export async function writeOwnedEvidenceFile(t,n,e){const r=F.join(t,n),i=F.join(t,`.${n}.${E.randomUUID()}.tmp`);let a=null;try{return a=await d.open(i,"wx",384),await a.writeFile(e),await a.sync(),await a.close(),a=null,await d.rename(i,r),process.platform!=="win32"&&await d.chmod(r,384),r}catch(o){throw a!==null&&await a.close().catch(()=>{}),await d.rm(i,{force:!0}).catch(()=>{}),o}}export async function snapshotStableEvidenceFile(t,n,e,r,i,a,o){const w=await r.lstat(t);u(w,n,e),S(i,w,e);const p=await r.openReadOnly(t),l=F.join(a,o),h=F.join(a,`.${o}.${E.randomUUID()}.tmp`);let s=null;try{const c=await r.statHandle(p);u(c,n,e),m(w,c,e,r.platform),s=await d.open(h,"wx",384);const y=E.createHash("sha256");let f=0;const g=Buffer.allocUnsafe(z);for(;;){const $=await r.readChunk(p,g);if(I($,g.byteLength,e),$===0)break;if(f+=$,f>n)throw new Error(`${e} exceeds its ${n.toString()} byte limit`);const k=g.subarray(0,$);await s.write(k),y.update(k)}const v=await r.statHandle(p),H=await r.lstat(t);if(u(v,n,e),u(H,n,e),S(c,v,e),S(w,H,e),m(H,v,e,r.platform),BigInt(f)!==c.size)throw new Error(`${e} changed while it was read`);return await s.sync(),await s.close(),s=null,await d.rename(h,l),process.platform!=="win32"&&await d.chmod(l,384),{filePath:l,identity:{sha256:y.digest("hex"),sizeBytes:f}}}catch(c){throw s!==null&&await s.close().catch(()=>{}),await d.rm(h,{force:!0}).catch(()=>{}),c}finally{await p.close()}}export function evidenceArtifact(t,n,e,r,i,a){return{contentType:e,kind:r,metadata:{sha256:n.sha256,sizeBytes:n.sizeBytes},name:i,path:t,summary:a}}function I(t,n,e){if(!Number.isSafeInteger(t)||t<0||t>n)throw new Error(`${e} returned an invalid read length`)}function u(t,n,e){if(!t.isFile()||t.isSymbolicLink())throw new Error(`${e} must be a regular non-symbolic-link file`);if(t.size<0n||t.size>BigInt(n))throw new Error(`${e} exceeds its ${n.toString()} byte limit`)}function S(t,n,e){if(!U(t,n))throw new Error(`${e} changed while it was read`)}function m(t,n,e,r){if(!C(t,n,r))throw new Error(`${e} changed while it was read`)}function L(t){return t instanceof Error&&"code"in t&&t.code==="ENOENT"}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import type { EngineJsonObject, RunnerProfileDescriptor } from "./api/index.js";
|
|
2
2
|
import { BUILT_IN_RUNNER_PROFILES } from "./definitions.js";
|
|
3
|
-
import {
|
|
4
|
-
export
|
|
5
|
-
readonly engineId: EngineId;
|
|
6
|
-
readonly profileId: RunnerProfileId;
|
|
7
|
-
readonly testType: PublicTestType;
|
|
8
|
-
}
|
|
3
|
+
import type { EngineId, PublicTestType, RunnerProfileId } from "../domain/identifiers.js";
|
|
4
|
+
export { isEngineProfileIdentity, type EngineProfileIdentity } from "./definitions.js";
|
|
9
5
|
export interface EngineCatalog {
|
|
10
6
|
readonly profiles: readonly RunnerProfileDescriptor[];
|
|
11
7
|
profilesForTestType(testType: PublicTestType): readonly RunnerProfileDescriptor[];
|
|
@@ -14,7 +10,6 @@ export interface EngineCatalog {
|
|
|
14
10
|
export declare const BUILT_IN_ENGINE_CATALOG: EngineCatalog;
|
|
15
11
|
export { BUILT_IN_RUNNER_PROFILES };
|
|
16
12
|
export declare function resolveEngineProfile<Id extends EngineId>(testType: PublicTestType, engineId: Id): RunnerProfileDescriptor<Id>;
|
|
17
|
-
export declare function isEngineProfileIdentity(value: unknown): value is EngineProfileIdentity;
|
|
18
13
|
type EngineAuthoringModule = "@gfdelarue/testkit/k6" | "@gfdelarue/testkit/node-vitest/data" | "@gfdelarue/testkit/node-vitest/contract" | "@gfdelarue/testkit/playwright";
|
|
19
14
|
export interface EngineAuthoringProfile {
|
|
20
15
|
readonly engineId: EngineId;
|
|
@@ -25,7 +20,7 @@ export interface ManagedPublicPackageOwner {
|
|
|
25
20
|
name: string;
|
|
26
21
|
}
|
|
27
22
|
export type EngineConfigurations = Readonly<Partial<Record<EngineId, EngineJsonObject>>>;
|
|
28
|
-
export declare function buildEngineConfigurations(config: object, engineIds: ReadonlySet<EngineId>): EngineConfigurations | undefined
|
|
23
|
+
export declare function buildEngineConfigurations(config: object, engineIds: ReadonlySet<EngineId>): Promise<EngineConfigurations | undefined>;
|
|
29
24
|
export declare function expectedEngineAuthoringModule(profileId: RunnerProfileId): EngineAuthoringModule;
|
|
30
25
|
export declare function invalidEngineAuthoringImport(filePath: string, metadata: Readonly<EngineAuthoringProfile>, specifier: string, expectedModule?: EngineAuthoringModule): Error;
|
|
31
26
|
export declare function requireMatchingHostImport(filePath: string, metadata: Readonly<EngineAuthoringProfile>, specifier: string): void;
|
package/dist/engines/catalog.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{builtinModules as
|
|
1
|
+
import{builtinModules as u}from"node:module";import{NODE_VITEST_PACKAGE_REQUIREMENTS as s,NODE_VITEST_PRIVATE_PACKAGE_PREFIXES as d}from"./installations/package-requirements.js";import{PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS as a}from"./installations/package-requirements.js";import{BUILT_IN_RUNNER_PROFILES as i}from"./definitions.js";export{isEngineProfileIdentity}from"./definitions.js";export const BUILT_IN_ENGINE_CATALOG=Object.freeze({profiles:i,profilesForTestType(e){return Object.freeze(i.filter(n=>n.supportedTestTypes.includes(e)))},resolve(e,n){const t=i.find(r=>r.engineId===n&&r.supportedTestTypes.includes(e));if(!t)throw new Error(`Unsupported engine/test-type pair: ${n}/${e}`);return t}});export{i as BUILT_IN_RUNNER_PROFILES};export function resolveEngineProfile(e,n){return BUILT_IN_ENGINE_CATALOG.resolve(e,n)}const p=new Set(u.map(e=>e.replace(/^node:/u,""))),l=Object.freeze([...s.map(({packageName:e})=>Object.freeze({engineId:"node-vitest",name:e})),...a.map(({packageName:e})=>Object.freeze({engineId:"playwright",name:e}))]);export async function buildEngineConfigurations(e,n){if(!n.has("k6"))return;const{buildK6RepositoryConfiguration:t}=await import("./k6/adapter/repository-configuration.js"),r=t(e);return r===void 0?void 0:{k6:r}}export function expectedEngineAuthoringModule(e){switch(e){case"k6-http":case"k6-dal":return"@gfdelarue/testkit/k6";case"node-vitest-data":return"@gfdelarue/testkit/node-vitest/data";case"node-vitest-contract":return"@gfdelarue/testkit/node-vitest/contract";case"playwright-ui":return"@gfdelarue/testkit/playwright"}}export function invalidEngineAuthoringImport(e,n,t,r=expectedEngineAuthoringModule(n.profileId)){return new Error(`Invalid Testkit authoring in "${e}": ${n.engineId}/${n.profileId} may not import "${t}"; use "${r}" for this suite`)}export function requireMatchingHostImport(e,n,t){if(n.engineId==="k6"&&g(t))throw new Error(`Invalid Testkit authoring in "${e}": k6 suites may not import Node built-in module "${t}"`);if(n.engineId!=="k6"&&I(t))throw new Error(`Invalid Testkit authoring in "${e}": ${n.engineId} suites may not import k6 module "${t}"`);if(d.some(o=>t.startsWith(o)))throw new Error(`Invalid Testkit authoring in "${e}": ${n.engineId} suites may not import private Vitest implementation package "${t}"`);const r=managedPublicPackageOwner(t);if(r!==null&&r.engineId!==n.engineId)throw new Error(`Invalid Testkit authoring in "${e}": ${n.engineId} suites may not import ${r.engineId} package "${t}"`)}export function managedPublicPackageOwner(e){for(const n of l)if(e===n.name||e.startsWith(`${n.name}/`))return n;return null}function g(e){const n=e.replace(/^node:/u,"");return p.has(n)}function I(e){return e==="k6"||e.startsWith("k6/")}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RunnerProfileDescriptor } from "./api/index.js";
|
|
2
|
-
import type { EngineId } from "../domain/identifiers.js";
|
|
2
|
+
import type { EngineId, PublicTestType, RunnerProfileId } from "../domain/identifiers.js";
|
|
3
3
|
import type { InternalEngineRegistrationById } from "./installation-catalog.js";
|
|
4
4
|
export type EndpointReadinessOwner = "adapter" | "orchestrator-http";
|
|
5
5
|
export interface BuiltInRunnerProfile<Id extends EngineId = EngineId> extends RunnerProfileDescriptor<Id> {
|
|
@@ -12,4 +12,11 @@ type BuiltInDefinitions = {
|
|
|
12
12
|
};
|
|
13
13
|
export declare const BUILT_IN_ENGINE_DEFINITIONS: BuiltInDefinitions;
|
|
14
14
|
export declare const BUILT_IN_RUNNER_PROFILES: readonly BuiltInRunnerProfile[];
|
|
15
|
+
export interface EngineProfileIdentity {
|
|
16
|
+
readonly engineId: EngineId;
|
|
17
|
+
readonly profileId: RunnerProfileId;
|
|
18
|
+
readonly testType: PublicTestType;
|
|
19
|
+
}
|
|
20
|
+
/** Validate persisted identities without loading engine configuration builders. */
|
|
21
|
+
export declare function isEngineProfileIdentity(value: unknown): value is EngineProfileIdentity;
|
|
15
22
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export const BUILT_IN_ENGINE_DEFINITIONS=Object.freeze({k6:Object.freeze({id:"k6",profiles:Object.freeze([t({capabilities:{applicationRuntime:"required",databaseRuntime:"optional",endpointAccess:"published-http",executionHost:"orchestrator",runtimeIsolation:"orchestrated",taskBatching:"isolated"},engineId:"k6",id:"k6-http",supportedTestTypes:["int","e2e","load"],endpointReadinessOwner:"orchestrator-http"}),t({capabilities:{applicationRuntime:"none",databaseRuntime:"required",endpointAccess:"none",executionHost:"orchestrator",runtimeIsolation:"orchestrated",taskBatching:"isolated"},engineId:"k6",id:"k6-dal",supportedTestTypes:["dal"],endpointReadinessOwner:"adapter"})]),async resolveInstallation(e){return(await import("./installations/k6.js")).resolveK6Installation(e)},async loadExecutor(){return(await import("./k6/adapter/index.js")).executeK6Engine}}),"node-vitest":Object.freeze({id:"node-vitest",profiles:Object.freeze([t({capabilities:{applicationRuntime:"required",databaseRuntime:"optional",endpointAccess:"published-http",executionHost:"orchestrator",runtimeIsolation:"orchestrated",taskBatching:"isolated"},engineId:"node-vitest",id:"node-vitest-data",supportedTestTypes:["data"],endpointReadinessOwner:"adapter"}),t({capabilities:{applicationRuntime:"required",databaseRuntime:"optional",endpointAccess:"published-http",executionHost:"orchestrator",runtimeIsolation:"orchestrated",taskBatching:"isolated"},engineId:"node-vitest",id:"node-vitest-contract",supportedTestTypes:["contract"],endpointReadinessOwner:"adapter"})]),async resolveInstallation(e){return(await import("./installations/node-vitest.js")).resolveNodeVitestInstallation(e)},async loadExecutor(){return(await import("./node-vitest/adapter/index.js")).executeNodeVitestEngine}}),playwright:Object.freeze({id:"playwright",profiles:Object.freeze([t({capabilities:{applicationRuntime:"required",databaseRuntime:"optional",endpointAccess:"published-http",executionHost:"orchestrator",runtimeIsolation:"orchestrated",taskBatching:"compatible"},engineId:"playwright",id:"playwright-ui",supportedTestTypes:["ui"],endpointReadinessOwner:"orchestrator-http"})]),async resolveInstallation(e){return(await import("./installations/playwright.js")).resolvePlaywrightInstallation(e)},async loadExecutor(){return(await import("./playwright/adapter/index.js")).executePlaywrightEngine}})}),BUILT_IN_RUNNER_PROFILES=Object.freeze(Object.values(BUILT_IN_ENGINE_DEFINITIONS).flatMap(e=>e.profiles));export function isEngineProfileIdentity(e){if(typeof e!="object"||e===null||Array.isArray(e))return!1;const i=e,n=BUILT_IN_RUNNER_PROFILES.find(a=>a.engineId===i.engineId&&a.supportedTestTypes.some(o=>o===i.testType));return n!==void 0&&n.id===i.profileId}function t(e){return Object.freeze(e.capabilities),Object.freeze(e.supportedTestTypes),Object.freeze(e)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function ownedGeneratedScript(source: string, label: string): string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const t=`
|
|
2
|
+
function __testkitWithOwner(owner, label, callback) {
|
|
3
|
+
const observer = globalThis[Symbol.for("testkit.performance.observer")];
|
|
4
|
+
const finish = observer?.beginPerformanceOwner?.(owner, label);
|
|
5
|
+
try { return callback(); }
|
|
6
|
+
finally { finish?.(); }
|
|
7
|
+
}
|
|
8
|
+
`;export function ownedGeneratedScript(e,n){return`${t}
|
|
9
|
+
__testkitWithOwner("testkit", ${JSON.stringify(n)}, () => {
|
|
10
|
+
${e}
|
|
11
|
+
});
|
|
12
|
+
`}
|
|
@@ -1,23 +1,6 @@
|
|
|
1
|
+
export { NODE_VITEST_PACKAGE_REQUIREMENTS, NODE_VITEST_PRIVATE_PACKAGE_PREFIXES } from "./package-requirements.js";
|
|
1
2
|
import { RootPackageResolver, type ResolvedPackageEntrypoint } from "./package-resolution.js";
|
|
2
3
|
import type { DirectPackageInstallation, EngineResolutionContext } from "./types.js";
|
|
3
|
-
export declare const NODE_VITEST_PACKAGE_REQUIREMENTS: readonly [Readonly<{
|
|
4
|
-
authoringResolution: "package-exports";
|
|
5
|
-
packageName: "vitest";
|
|
6
|
-
publicEntrypoints: readonly ["vitest"];
|
|
7
|
-
}>, Readonly<{
|
|
8
|
-
authoringResolution: "package-exports";
|
|
9
|
-
packageName: "vite";
|
|
10
|
-
publicEntrypoints: readonly ["vite"];
|
|
11
|
-
}>, Readonly<{
|
|
12
|
-
authoringResolution: "exact-entrypoint";
|
|
13
|
-
packageName: "typescript";
|
|
14
|
-
publicEntrypoints: readonly ["typescript"];
|
|
15
|
-
}>, Readonly<{
|
|
16
|
-
authoringResolution: "exact-entrypoint";
|
|
17
|
-
packageName: "esbuild";
|
|
18
|
-
publicEntrypoints: readonly ["esbuild"];
|
|
19
|
-
}>];
|
|
20
|
-
export declare const NODE_VITEST_PRIVATE_PACKAGE_PREFIXES: readonly ["@vitest/"];
|
|
21
4
|
interface VitestPackageInstallation extends DirectPackageInstallation<"vitest"> {
|
|
22
5
|
readonly cli: string;
|
|
23
6
|
}
|
|
@@ -39,9 +22,12 @@ export interface NodeVitestInstallation {
|
|
|
39
22
|
};
|
|
40
23
|
}
|
|
41
24
|
interface NodeVitestResolutionDependencies {
|
|
42
|
-
readonly importEntrypoint: (selectedPackage: Readonly<ResolvedPackageEntrypoint>) => Promise<unknown>;
|
|
43
25
|
readonly packageResolver: RootPackageResolver;
|
|
44
26
|
}
|
|
45
|
-
export declare function resolveNodeVitestInstallation(context: Readonly<EngineResolutionContext>, dependencies?: Readonly<NodeVitestResolutionDependencies>):
|
|
46
|
-
export declare function preflightNodeVitestInstallation(installation: Readonly<NodeVitestInstallation>, context: Readonly<
|
|
47
|
-
|
|
27
|
+
export declare function resolveNodeVitestInstallation(context: Readonly<EngineResolutionContext>, dependencies?: Readonly<NodeVitestResolutionDependencies>): NodeVitestInstallation;
|
|
28
|
+
export declare function preflightNodeVitestInstallation(installation: Readonly<NodeVitestInstallation>, context: Readonly<{
|
|
29
|
+
signal: {
|
|
30
|
+
readonly aborted: boolean;
|
|
31
|
+
readonly reason?: unknown;
|
|
32
|
+
};
|
|
33
|
+
}>, importEntrypoint?: (selectedPackage: Readonly<ResolvedPackageEntrypoint>) => Promise<unknown>): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import{NODE_VITEST_PACKAGE_REQUIREMENTS as u}from"./package-requirements.js";export{NODE_VITEST_PACKAGE_REQUIREMENTS,NODE_VITEST_PRIVATE_PACKAGE_PREFIXES}from"./package-requirements.js";import l from"node:fs";import p from"node:path";import{isPathWithin as d}from"../../process/files/paths.js";import{BrokenPackageInstallationError as f,RootPackageResolver as y,importResolvedPackageEntrypoint as g}from"./package-resolution.js";const v=Object.freeze({packageResolver:new y});export function resolveNodeVitestInstallation(t,e=v){t.signal.throwIfAborted();const o=new Map;for(const n of u)o.set(n.packageName,e.packageResolver.resolveImportFromRoot({dependencyGroup:"dependencies",packageName:n.packageName,publicEntrypoint:n.publicEntrypoints[0]}));const r=s(o,"vitest");return Object.freeze({authoringEntrypoints:m(e.packageResolver),engineId:"node-vitest",packages:Object.freeze({esbuild:c(s(o,"esbuild")),typescript:c(s(o,"typescript")),vite:c(s(o,"vite")),vitest:Object.freeze({...c(r),cli:h(r,e.packageResolver)})})})}function m(t){return Object.freeze({config:t.resolveRootPublicEntrypoint("@gfdelarue/testkit/config"),contract:t.resolveRootPublicEntrypoint("@gfdelarue/testkit/node-vitest/contract"),data:t.resolveRootPublicEntrypoint("@gfdelarue/testkit/node-vitest/data"),root:t.resolveRootPublicEntrypoint("@gfdelarue/testkit"),rootPackageJson:t.packageJsonPath})}export async function preflightNodeVitestInstallation(t,e,o=g){for(const r of u){b(e.signal);const i=t.packages[r.packageName];try{const n=await o(i);await k(r.packageName,n)}catch(n){throw b(e.signal),new f(i.name,i.version,`its public ${r.packageName} capability failed; reinstall without omitting optional dependencies`,{cause:T(n)})}}}function s(t,e){const o=t.get(e);if(o===void 0||o.name!==e)throw new TypeError(`Node/Vitest package resolution did not select ${e}`);return o}function c(t){return Object.freeze({entrypoint:t.entrypoint,name:t.name,root:t.root,version:t.version})}function h(t,e){try{const r=w(t.manifestPath).bin,i=typeof r=="object"&&r!==null&&!Array.isArray(r)?r.vitest:r;if(typeof i!="string"||p.isAbsolute(i))throw new TypeError("package bin.vitest must be a relative path");const n=l.realpathSync(p.resolve(t.root,i));if(!d(t.root,n)||!l.statSync(n).isFile())throw new TypeError("package bin.vitest must resolve to a contained regular file");return n}catch(o){throw e.brokenInstallation(t,"its public Vitest CLI is invalid",o)}}function w(t){const e=JSON.parse(l.readFileSync(t,"utf8"));if(typeof e!="object"||e===null||Array.isArray(e))throw new TypeError("package manifest must contain a JSON object");return e}async function k(t,e){const o=E(e,`${t} public module`);switch(t){case"vitest":a(o,"expect",t),a(o,"test",t);return;case"vite":return;case"typescript":{const r=E(o.ScriptTarget,"typescript ScriptTarget");a(o,"createSourceFile",t)("preflight.ts","export {};",r.Latest);return}case"esbuild":a(o,"stop",t),await a(o,"build",t)({stdin:{contents:"export {};",loader:"js"},write:!1})}}function E(t,e){if(typeof t!="object"||t===null)throw new TypeError(`${e} must be an object`);return t}function a(t,e,o){const r=t[e];if(typeof r!="function")throw new TypeError(`${o} public module must export function ${e}`);return r}function T(t){return t instanceof f&&t.cause!==void 0?t.cause:t}function b(t){if(t.aborted)throw t.reason instanceof Error?t.reason:new Error("Node/Vitest installation check cancelled",{cause:t.reason})}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare const NODE_VITEST_PACKAGE_REQUIREMENTS: readonly [Readonly<{
|
|
2
|
+
authoringResolution: "package-exports";
|
|
3
|
+
packageName: "vitest";
|
|
4
|
+
publicEntrypoints: readonly ["vitest"];
|
|
5
|
+
}>, Readonly<{
|
|
6
|
+
authoringResolution: "package-exports";
|
|
7
|
+
packageName: "vite";
|
|
8
|
+
publicEntrypoints: readonly ["vite"];
|
|
9
|
+
}>, Readonly<{
|
|
10
|
+
authoringResolution: "exact-entrypoint";
|
|
11
|
+
packageName: "typescript";
|
|
12
|
+
publicEntrypoints: readonly ["typescript"];
|
|
13
|
+
}>, Readonly<{
|
|
14
|
+
authoringResolution: "exact-entrypoint";
|
|
15
|
+
packageName: "esbuild";
|
|
16
|
+
publicEntrypoints: readonly ["esbuild"];
|
|
17
|
+
}>];
|
|
18
|
+
export declare const NODE_VITEST_PRIVATE_PACKAGE_PREFIXES: readonly ["@vitest/"];
|
|
19
|
+
export declare const PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS: readonly [Readonly<{
|
|
20
|
+
packageName: "@playwright/test";
|
|
21
|
+
publicEntrypoints: readonly ["@playwright/test", "@playwright/test/cli"];
|
|
22
|
+
}>, Readonly<{
|
|
23
|
+
packageName: "playwright";
|
|
24
|
+
publicEntrypoints: readonly ["playwright"];
|
|
25
|
+
}>, Readonly<{
|
|
26
|
+
packageName: "playwright-core";
|
|
27
|
+
publicEntrypoints: readonly ["playwright-core"];
|
|
28
|
+
}>];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const NODE_VITEST_PACKAGE_REQUIREMENTS=Object.freeze([Object.freeze({authoringResolution:"package-exports",packageName:"vitest",publicEntrypoints:Object.freeze(["vitest"])}),Object.freeze({authoringResolution:"package-exports",packageName:"vite",publicEntrypoints:Object.freeze(["vite"])}),Object.freeze({authoringResolution:"exact-entrypoint",packageName:"typescript",publicEntrypoints:Object.freeze(["typescript"])}),Object.freeze({authoringResolution:"exact-entrypoint",packageName:"esbuild",publicEntrypoints:Object.freeze(["esbuild"])})]),NODE_VITEST_PRIVATE_PACKAGE_PREFIXES=Object.freeze(["@vitest/"]),PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS=Object.freeze([Object.freeze({packageName:"@playwright/test",publicEntrypoints:Object.freeze(["@playwright/test","@playwright/test/cli"])}),Object.freeze({packageName:"playwright",publicEntrypoints:Object.freeze(["playwright"])}),Object.freeze({packageName:"playwright-core",publicEntrypoints:Object.freeze(["playwright-core"])})]);
|
|
@@ -1,17 +1,8 @@
|
|
|
1
|
+
export { PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS } from "./package-requirements.js";
|
|
1
2
|
import { type ManagedProcessResult } from "../../process/index.js";
|
|
2
3
|
import { type LinuxLibc, type PlatformTarget } from "../../process/host/platform.js";
|
|
3
4
|
import { RootPackageResolver } from "./package-resolution.js";
|
|
4
5
|
import type { DirectPackageInstallation, EngineResolutionContext } from "./types.js";
|
|
5
|
-
export declare const PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS: readonly [Readonly<{
|
|
6
|
-
packageName: "@playwright/test";
|
|
7
|
-
publicEntrypoints: readonly ["@playwright/test", "@playwright/test/cli"];
|
|
8
|
-
}>, Readonly<{
|
|
9
|
-
packageName: "playwright";
|
|
10
|
-
publicEntrypoints: readonly ["playwright"];
|
|
11
|
-
}>, Readonly<{
|
|
12
|
-
packageName: "playwright-core";
|
|
13
|
-
publicEntrypoints: readonly ["playwright-core"];
|
|
14
|
-
}>];
|
|
15
6
|
export interface PlaywrightPackageFamily {
|
|
16
7
|
readonly cli: string;
|
|
17
8
|
readonly playwright: DirectPackageInstallation<"playwright">;
|
|
@@ -82,4 +73,3 @@ export declare function parsePlaywrightChildOutput(stdout: string): ParsedChildO
|
|
|
82
73
|
export declare function requireMatchingPlaywrightChildRequest(output: Readonly<PlaywrightChildSuccess>, request: Readonly<PlaywrightChildRequest>): void;
|
|
83
74
|
export declare function requireBrowserExecutable(value: string, target: Readonly<PlatformTarget>): string;
|
|
84
75
|
export declare function requireSupportedPlaywrightHost(host: Readonly<PlaywrightHost>): void;
|
|
85
|
-
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import c from"node:fs";import w from"node:os";import f from"node:path";import{fileURLToPath as h}from"node:url";import{runManagedProcess as
|
|
1
|
+
import{PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS as R}from"./package-requirements.js";export{PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS}from"./package-requirements.js";import c from"node:fs";import w from"node:os";import f from"node:path";import{fileURLToPath as h}from"node:url";import{runManagedProcess as x}from"../../process/index.js";import{currentLinuxLibc as T,currentPlatformTarget as _,platformTargetKey as d,SUPPORTED_PLATFORM_TARGETS as I}from"../../process/host/platform.js";import{RootPackageResolver as m}from"./package-resolution.js";const A=64*1024,O=6e4,b="PLAYWRIGHT_BROWSERS_PATH",S=new Set(["npm_config_playwright_browsers_path","npm_package_config_playwright_browsers_path"]);export class PlaywrightInstallationError extends Error{code="TESTKIT_PLAYWRIGHT_INSTALLATION_INVALID";constructor(r,t){super(`Playwright installation is invalid: ${r}. Run testkit setup playwright --force.`,t),this.name="PlaywrightInstallationError"}}export class UnsupportedPlaywrightPlatformError extends Error{code="TESTKIT_PLAYWRIGHT_PLATFORM_UNSUPPORTED";constructor(r,t){super(`Playwright does not support ${d(r)}${r.platform==="linux"?` with ${t??"unknown"} libc`:""}`),this.name="UnsupportedPlaywrightPlatformError"}}export function resolvePlaywrightPackageFamily(e=new m){const[r,t,o]=R,i=e.resolveRequireFromRoot(u(r)),n=e.resolveImportFromRoot(u(r));L(e,i,n);const a=e.resolveRequireFromPackage(i,u(t)),s=e.resolveRequireFromPackage(a,u(o)),P=e.resolveRequirePublicEntrypoint(i,r.publicEntrypoints[1]);return Object.freeze({cli:P,playwright:p(a,"playwright"),playwrightCore:p(s,"playwright-core"),playwrightTest:p(n,"@playwright/test")})}function L(e,r,t){if(!(r.name===t.name&&r.version===t.version&&r.root===t.root))throw e.brokenInstallation(r,"the import and require public entrypoints select different physical package roots",void 0)}export function resolvePlaywrightRegistry(e){const r=e.platform==="win32"?f.win32:f.posix;if(!r.isAbsolute(e.productRoot))throw new TypeError("Playwright product root must be absolute");const t=e.configuredValue;if(t==="0")return Object.freeze({canonicalValue:"0",mode:"hermetic"});if(t!==void 0&&t.length>0){const o=r.isAbsolute(t);return Object.freeze({canonicalValue:o?r.normalize(t):r.resolve(e.productRoot,t),mode:o?"configured-absolute":"configured-relative"})}return Object.freeze({canonicalValue:r.resolve(e.productRoot,r.join(C(e.platform,e.ambientEnvironment,e.homeDirectory??w.homedir(),r),"ms-playwright")),mode:"default"})}export function playwrightRegistryEnvironment(e,r){const t={};for(const[o,i]of Object.entries(e))typeof i=="string"&&!S.has(o.toLowerCase())&&o.toUpperCase()!==b&&(t[o]=i);return t[b]=r.canonicalValue,Object.freeze(t)}export async function resolvePlaywrightInstallation(e,r={}){e.signal.throwIfAborted();const t={linuxLibc:T(),target:_()};requireSupportedPlaywrightHost(t);const o=new m,i=r.ambientEnvironment??process.env,n=resolvePlaywrightPackageFamily(o),a=resolvePlaywrightRegistry({ambientEnvironment:i,configuredValue:r.configuredRegistry??i.PLAYWRIGHT_BROWSERS_PATH,homeDirectory:w.homedir(),platform:t.target.platform,productRoot:e.productRoot}),s=await k(e,{family:n,registry:a},i);return Object.freeze({authoringEntrypoints:Object.freeze({authoring:o.resolveRootPublicEntrypoint("@gfdelarue/testkit/playwright"),root:o.resolveRootPublicEntrypoint("@gfdelarue/testkit"),rootPackageJson:o.resolveRootPublicEntrypoint("@gfdelarue/testkit/package.json")}),browserExecutable:requireBrowserExecutable(s.browserExecutable,t.target),engineId:"playwright",family:n,registry:a})}async function k(e,r,t){const o=Buffer.from(JSON.stringify(r),"utf8").toString("base64url"),i=await x({args:[N(),o],cwd:e.productRoot,displayLabel:"Playwright installation resolver",env:playwrightRegistryEnvironment(t,r.registry),executable:process.execPath,signal:e.signal,timeoutMs:O}),n=j(i);throwIfPlaywrightProcessAborted(e.signal,i,n),requireBoundedPlaywrightChildOutput(i.stdout);let a;try{a=parsePlaywrightChildOutput(i.stdout)}catch(s){throw n!==null?new PlaywrightInstallationError(`isolated resolver failed: ${n.message}`,{cause:n}):s}if(n!==null)throw new PlaywrightInstallationError(`isolated resolver failed: ${n.message}`,{cause:n});if(!a.ok){if(i.exitCode!==1||i.exitSignal!==null){const s=E(i);throw new PlaywrightInstallationError(`isolated resolver failed: ${s.message}`,{cause:s})}throw new PlaywrightInstallationError(`isolated resolver failed: ${a.error.message}`,{cause:M(a.error)})}if(i.exitCode!==0||i.exitSignal!==null){const s=E(i);throw new PlaywrightInstallationError(`isolated resolver failed: ${s.message}`,{cause:s})}return requireMatchingPlaywrightChildRequest(a,r),a}export function throwIfPlaywrightProcessAborted(e,r,t){if(e.aborted){if(!v(r)||t===null){e.throwIfAborted();return}throw t===e.reason?t:new AggregateError([e.reason,t],"Playwright process was cancelled and process cleanup also failed",{cause:e.reason})}}function v(e){return e.terminationError!==null||e.error!==null||e.timedOut||e.outputLimitExceeded}export function requireBoundedPlaywrightChildOutput(e){if(Buffer.byteLength(e,"utf8")>A)throw new PlaywrightInstallationError("isolated resolver returned oversized output")}export function parsePlaywrightChildOutput(e){let r;try{r=JSON.parse(e)}catch(t){throw new PlaywrightInstallationError("isolated resolver did not return bounded JSON",{cause:t})}if(!l(r)||typeof r.ok!="boolean")throw new PlaywrightInstallationError("isolated resolver returned an invalid response");if(!r.ok){if(!l(r.error)||typeof r.error.name!="string"||typeof r.error.message!="string"||r.error.code!==void 0&&typeof r.error.code!="string")throw new PlaywrightInstallationError("isolated resolver returned an invalid error");return{error:{...r.error.code===void 0?{}:{code:r.error.code},message:r.error.message,name:r.error.name},ok:!1}}if(typeof r.browserExecutable!="string"||!$(r.family)||!H(r.registry))throw new PlaywrightInstallationError("isolated resolver returned an invalid installation");return{browserExecutable:r.browserExecutable,family:r.family,ok:!0,registry:r.registry}}export function requireMatchingPlaywrightChildRequest(e,r){if(JSON.stringify(e.family)!==JSON.stringify(r.family))throw new PlaywrightInstallationError("isolated resolver selected a different physical package family");if(e.registry.canonicalValue!==r.registry.canonicalValue||e.registry.mode!==r.registry.mode)throw new PlaywrightInstallationError("isolated resolver selected a different browser registry")}export function requireBrowserExecutable(e,r){if(!f.isAbsolute(e))throw new PlaywrightInstallationError(`public chromium.executablePath() returned a non-absolute path ${JSON.stringify(e)}`);let t;try{t=c.statSync(e)}catch(o){throw new PlaywrightInstallationError(`public chromium.executablePath() returned missing path ${JSON.stringify(e)}`,{cause:o})}if(!t.isFile())throw new PlaywrightInstallationError(`public chromium.executablePath() must identify a regular file; received ${JSON.stringify(e)}`);if(r.platform!=="win32")try{c.accessSync(e,c.constants.X_OK)}catch(o){throw new PlaywrightInstallationError(`public chromium.executablePath() is not executable: ${JSON.stringify(e)}`,{cause:o})}return e}export function requireSupportedPlaywrightHost(e){const r=d(e.target);if(!I.some(t=>d(t)===r)||e.target.platform==="linux"&&e.linuxLibc!=="glibc")throw new UnsupportedPlaywrightPlatformError(e.target,e.linuxLibc)}function C(e,r,t,o){return e==="linux"?r.XDG_CACHE_HOME||o.join(g(t),".cache"):e==="darwin"?o.join(g(t),"Library","Caches"):r.LOCALAPPDATA||o.join(g(t),"AppData","Local")}function g(e){if(e.length===0)throw new TypeError("Playwright default registry requires a home directory");return e}function u(e){return{dependencyGroup:"dependencies",packageName:e.packageName,publicEntrypoint:e.publicEntrypoints[0]}}function p(e,r){if(e.name!==r)throw new TypeError(`Playwright package family expected ${r}, received ${e.name}`);return Object.freeze({entrypoint:e.entrypoint,name:r,root:e.root,version:e.version})}function N(){const e=h(new URL("./playwright-resolution-child.js",import.meta.url));return c.existsSync(e)?e:h(new URL("../../../dist/engines/installations/playwright-resolution-child.js",import.meta.url))}function j(e){return e.terminationError!==null?e.terminationError:e.error!==null?e.error:e.timedOut?new Error("isolated resolver timed out"):e.cancelled?new Error("isolated resolver was cancelled"):e.outputLimitExceeded?new Error("isolated resolver output exceeded the bound"):null}function E(e){return new Error(`isolated resolver exited with ${e.exitCode===null?String(e.exitSignal):e.exitCode.toString()}`)}function M(e){const r=new Error(e.message);return r.name=e.name,e.code!==void 0&&Object.defineProperty(r,"code",{enumerable:!0,value:e.code}),r}function $(e){return l(e)&&typeof e.cli=="string"&&y(e.playwrightTest,"@playwright/test")&&y(e.playwright,"playwright")&&y(e.playwrightCore,"playwright-core")}function y(e,r){return l(e)&&e.name===r&&typeof e.version=="string"&&typeof e.root=="string"&&typeof e.entrypoint=="string"}function H(e){return l(e)&&typeof e.canonicalValue=="string"&&(e.mode==="configured-absolute"||e.mode==="configured-relative"||e.mode==="default"||e.mode==="hermetic")}function l(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { EngineJsonObject } from "../../api/evidence.js";
|
|
2
2
|
export declare function buildK6RepositoryConfiguration(config: object): EngineJsonObject | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{normalizeEngineJsonValue as c}from"../../api/
|
|
1
|
+
import{normalizeEngineJsonValue as c}from"../../api/runtime-artifact.js";import{K6_REPOSITORY_CONFIGURATION_VERSION as u}from"../../contracts/k6-repository-configuration.js";export function buildK6RepositoryConfiguration(e){const t=i(e,"profiles","testkit config profiles");if(t===void 0)return;const o=i(t,"http","testkit config profiles.http");if(o===void 0)return;const r=[];for(const[n,s]of Object.entries(o)){if(n.trim().length===0)throw new TypeError("testkit config profiles.http keys must be non-empty");r.push([n,p(s,`testkit config profiles.http.${n}`)])}const f=Object.fromEntries(r);return Object.freeze({httpProfiles:Object.freeze(f),kind:"testkit-k6-repository-configuration",version:u})}function p(e,t){try{return Object.freeze({kind:"json",value:l(c(e,t),t)})}catch(o){throw new TypeError(`${t} cannot cross the Node/k6 runtime boundary. Repository profiles must be JSON-only. Define setup and header functions in the k6 suite module instead.`,{cause:o})}}function l(e,t){if(e===null||Array.isArray(e)||typeof e!="object")throw new TypeError(`${t} must be a JSON object`);return e}function i(e,t,o){const r=Reflect.get(e,t);if(r!=null){if(!d(r))throw new TypeError(`${o} must be an object`);return r}}function d(e){if(typeof e!="object"||e===null||Array.isArray(e))return!1;const t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{ownedGeneratedScript as o}from"./generated-performance.js";import{inspectManagedNodeModuleResolutionPolicy as s}from"./managed-module-resolution-policy.js";export function createManagedNodeModuleResolution(e){const t=s(e),n={...t.direct};for(const[a,i]of Object.entries(t.packageExports)){const r=i.import??i.default??i.require;r!==null&&(n[a]=r)}return Object.freeze({aliases:Object.freeze(n),bootstrapSource:c(t),dependencyRoots:t.managedImporterRoots})}export function managedNodeModuleResolutionBootstrapSource(e){return createManagedNodeModuleResolution(e).bootstrapSource}function c(e){return o(`
|
|
2
2
|
const fs = require("node:fs");
|
|
3
3
|
const path = require("node:path");
|
|
4
|
-
const { registerHooks } = require("node:module");
|
|
4
|
+
const { isBuiltin, registerHooks } = require("node:module");
|
|
5
5
|
const { fileURLToPath, pathToFileURL } = require("node:url");
|
|
6
6
|
|
|
7
|
+
let canonicalPaths = null;
|
|
7
8
|
const POLICY = Object.freeze(${JSON.stringify(e)});
|
|
8
9
|
const CONSUMER_ROOTS = Object.freeze(
|
|
9
10
|
POLICY.consumerRoots.map((root) => verifiedDirectory(root, "consumer root")),
|
|
@@ -15,6 +16,9 @@ const DIRECT = new Map(
|
|
|
15
16
|
]),
|
|
16
17
|
);
|
|
17
18
|
const PACKAGE_ROOTS = verifyPackageRoots(POLICY.packageRoots, "native");
|
|
19
|
+
const PRIVATE_NATIVE_SCOPES = Object.freeze(
|
|
20
|
+
POLICY.managedImporterRoots.filter((root) => !Object.values(PACKAGE_ROOTS).includes(root)),
|
|
21
|
+
);
|
|
18
22
|
const DIRECT_BRIDGE_ROOTS = Object.freeze(
|
|
19
23
|
POLICY.directBridgeRoots.map((root) =>
|
|
20
24
|
verifiedDirectory(root, "direct bridge root"),
|
|
@@ -68,7 +72,30 @@ if (existing !== undefined) {
|
|
|
68
72
|
// tools that register asynchronous loaders. Resolution is the complete
|
|
69
73
|
// policy here, so do not install a load hook that has no policy to enforce.
|
|
70
74
|
const hooks = registerHooks({
|
|
71
|
-
resolve(specifier, context,
|
|
75
|
+
resolve(specifier, context, nativeResolve) {
|
|
76
|
+
const nextResolve = (specifier, context) => {
|
|
77
|
+
canonicalPaths = new Map();
|
|
78
|
+
try { return __testkitWithOwner("external", "Native module resolution", () => nativeResolve(specifier, context)); }
|
|
79
|
+
finally { canonicalPaths = new Map(); }
|
|
80
|
+
};
|
|
81
|
+
return __testkitWithOwner("testkit", "Testkit managed resolution policy", () => {
|
|
82
|
+
const previousPaths = canonicalPaths;
|
|
83
|
+
canonicalPaths = new Map();
|
|
84
|
+
try {
|
|
85
|
+
// Builtins and physically native-internal file edges need no package graph scan.
|
|
86
|
+
// A downstream resolver may redirect either one, so inspect its result.
|
|
87
|
+
let nativeResult;
|
|
88
|
+
if (isBuiltin(specifier) && !DIRECT.has(specifier)) {
|
|
89
|
+
nativeResult = nextResolve(specifier, context);
|
|
90
|
+
if (isBuiltin(nativeResult.url)) return nativeResult;
|
|
91
|
+
}
|
|
92
|
+
const nativeOrigin = specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("file:") || path.isAbsolute(specifier)
|
|
93
|
+
? nativeFileOrigin(specifier, context.parentURL) : null;
|
|
94
|
+
if (nativeOrigin !== null) {
|
|
95
|
+
nativeResult = nextResolve(specifier, context);
|
|
96
|
+
if (sameNativePackageEdge(nativeOrigin, context.parentURL, nativeResult.url))
|
|
97
|
+
return nativeResult;
|
|
98
|
+
}
|
|
72
99
|
const conditions = normalizedConditions(context.conditions);
|
|
73
100
|
const classified = classifyResolvedSpecifier(specifier, context.parentURL);
|
|
74
101
|
requireAllowedResolvedSpecifier(classified);
|
|
@@ -106,7 +133,7 @@ if (existing !== undefined) {
|
|
|
106
133
|
forbiddenPackage,
|
|
107
134
|
);
|
|
108
135
|
}
|
|
109
|
-
const resolved = nextResolve(specifier, context);
|
|
136
|
+
const resolved = nativeResult ?? nextResolve(specifier, context);
|
|
110
137
|
const resolvedClassification = classifyResolvedSpecifier(
|
|
111
138
|
resolved.url,
|
|
112
139
|
context.parentURL,
|
|
@@ -121,6 +148,8 @@ if (existing !== undefined) {
|
|
|
121
148
|
if (redirected !== null) return redirected;
|
|
122
149
|
}
|
|
123
150
|
return resolved;
|
|
151
|
+
} finally { canonicalPaths = previousPaths; }
|
|
152
|
+
});
|
|
124
153
|
},
|
|
125
154
|
});
|
|
126
155
|
Object.defineProperty(globalThis, STATE, {
|
|
@@ -131,6 +160,66 @@ if (existing !== undefined) {
|
|
|
131
160
|
});
|
|
132
161
|
}
|
|
133
162
|
|
|
163
|
+
function recordResolutionWork(metric) {
|
|
164
|
+
globalThis[Symbol.for("testkit.work.observer")]?.observer?.(metric, 1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function observedRealpath(file) {
|
|
168
|
+
recordResolutionWork("resolver.realpath");
|
|
169
|
+
// Match policy inspection on Windows: native realpath expands 8.3 names and
|
|
170
|
+
// changes their spelling, mixing physical paths with the inspected roots.
|
|
171
|
+
return process.platform === "win32" ? fs.realpathSync(file) : fs.realpathSync.native(file);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function observedRead(file, encoding) {
|
|
175
|
+
recordResolutionWork("resolver.read");
|
|
176
|
+
return fs.readFileSync(file, encoding);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function observedExists(file) {
|
|
180
|
+
recordResolutionWork("resolver.exists");
|
|
181
|
+
return fs.existsSync(file);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function nativeFileOrigin(specifier, parentURL) {
|
|
185
|
+
const requestedPath = specifier.startsWith("file:") ? fileURLToPath(specifier) : path.isAbsolute(specifier) ? specifier : null;
|
|
186
|
+
// A lexical miss only skips this optimization; all trust uses fresh physical paths.
|
|
187
|
+
if (requestedPath !== null && ![...Object.values(PACKAGE_ROOTS), ...PRIVATE_NATIVE_SCOPES].some(root => isPathWithin(root, requestedPath))) return null;
|
|
188
|
+
if (typeof parentURL !== "string" || !parentURL.startsWith("file:")) return null;
|
|
189
|
+
try {
|
|
190
|
+
const file = observedRealpath(fileURLToPath(parentURL));
|
|
191
|
+
const root = nativePackageRoot(file);
|
|
192
|
+
if (root === undefined) return null;
|
|
193
|
+
if (requestedPath !== null) {
|
|
194
|
+
const requested = observedRealpath(requestedPath);
|
|
195
|
+
if (!isPathWithin(root, requested)) return null;
|
|
196
|
+
}
|
|
197
|
+
return { file, root };
|
|
198
|
+
} catch { return null; }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function nativePackageRoot(file) {
|
|
202
|
+
const selected = Object.values(PACKAGE_ROOTS).find((root) => isPathWithin(root, file));
|
|
203
|
+
if (selected !== undefined) return selected;
|
|
204
|
+
for (const scope of PRIVATE_NATIVE_SCOPES) {
|
|
205
|
+
if (!isPathWithin(scope, file)) continue;
|
|
206
|
+
const relative = path.relative(scope, file);
|
|
207
|
+
const separator = relative.indexOf(path.sep);
|
|
208
|
+
if (separator > 0) return path.join(scope, relative.slice(0, separator));
|
|
209
|
+
}
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function sameNativePackageEdge(origin, parentURL, targetURL) {
|
|
214
|
+
// Trust never survives another resolver changing the importer or its root.
|
|
215
|
+
if (observedRealpath(fileURLToPath(parentURL)) !== origin.file ||
|
|
216
|
+
observedRealpath(origin.root) !== origin.root)
|
|
217
|
+
throw new Error("Managed " + POLICY.engineLabel + " native importer changed during resolution");
|
|
218
|
+
if (typeof targetURL !== "string" || !targetURL.startsWith("file:")) return false;
|
|
219
|
+
try { return isPathWithin(origin.root, observedRealpath(fileURLToPath(targetURL))); }
|
|
220
|
+
catch { return false; }
|
|
221
|
+
}
|
|
222
|
+
|
|
134
223
|
function resolveManagedSpecifier(specifier, conditions, parentURL) {
|
|
135
224
|
const direct = DIRECT.get(specifier);
|
|
136
225
|
if (direct !== undefined)
|
|
@@ -185,9 +274,9 @@ function nearestPackageManifest(importerFile) {
|
|
|
185
274
|
let directory = path.dirname(importerFile);
|
|
186
275
|
while (true) {
|
|
187
276
|
const manifestFile = path.join(directory, "package.json");
|
|
188
|
-
if (
|
|
277
|
+
if (observedExists(manifestFile)) {
|
|
189
278
|
try {
|
|
190
|
-
const manifest = JSON.parse(
|
|
279
|
+
const manifest = JSON.parse(observedRead(manifestFile, "utf8"));
|
|
191
280
|
if (isObject(manifest)) return manifest;
|
|
192
281
|
} catch {}
|
|
193
282
|
return null;
|
|
@@ -376,10 +465,7 @@ function unclassified() {
|
|
|
376
465
|
function resolvedFileCandidates(file) {
|
|
377
466
|
const resolved = path.resolve(file);
|
|
378
467
|
const candidates = [];
|
|
379
|
-
|
|
380
|
-
const canonical = fs.realpathSync(file);
|
|
381
|
-
candidates.push(canonical);
|
|
382
|
-
} catch {}
|
|
468
|
+
candidates.push(canonicalPath(file));
|
|
383
469
|
if (!candidates.includes(resolved)) candidates.push(resolved);
|
|
384
470
|
return candidates;
|
|
385
471
|
}
|
|
@@ -391,8 +477,8 @@ function classifyPackageBoundaries(file, parentURL) {
|
|
|
391
477
|
if (scanBoundary !== null && canonicalPath(directory) === scanBoundary)
|
|
392
478
|
return null;
|
|
393
479
|
const manifestFile = path.join(directory, "package.json");
|
|
394
|
-
if (
|
|
395
|
-
const manifest = JSON.parse(
|
|
480
|
+
if (observedExists(manifestFile)) {
|
|
481
|
+
const manifest = JSON.parse(observedRead(manifestFile, "utf8"));
|
|
396
482
|
const packageName =
|
|
397
483
|
typeof manifest.name === "string" ? manifest.name : null;
|
|
398
484
|
if (packageName !== null && Object.hasOwn(PACKAGE_ROOTS, packageName))
|
|
@@ -637,7 +723,7 @@ function verifyPackageRoots(roots, kind) {
|
|
|
637
723
|
Object.entries(roots).map(([packageName, root]) => {
|
|
638
724
|
const canonical = canonicalPath(root);
|
|
639
725
|
const manifest = JSON.parse(
|
|
640
|
-
|
|
726
|
+
observedRead(path.join(canonical, "package.json"), "utf8"),
|
|
641
727
|
);
|
|
642
728
|
if (manifest.name !== packageName)
|
|
643
729
|
throw new Error(
|
|
@@ -658,7 +744,7 @@ function verifiedOptionalFile(file, label) {
|
|
|
658
744
|
}
|
|
659
745
|
|
|
660
746
|
function verifiedDirectory(directory, label) {
|
|
661
|
-
const resolved =
|
|
747
|
+
const resolved = observedRealpath(directory);
|
|
662
748
|
if (!fs.statSync(resolved).isDirectory())
|
|
663
749
|
throw new Error(
|
|
664
750
|
"Managed " + POLICY.engineLabel + " " + label + " is not a directory",
|
|
@@ -667,7 +753,7 @@ function verifiedDirectory(directory, label) {
|
|
|
667
753
|
}
|
|
668
754
|
|
|
669
755
|
function verifiedFile(file, label) {
|
|
670
|
-
const resolved =
|
|
756
|
+
const resolved = observedRealpath(file);
|
|
671
757
|
if (!fs.statSync(resolved).isFile())
|
|
672
758
|
throw new Error(
|
|
673
759
|
"Managed " + POLICY.engineLabel + " " + label + " is not a regular file",
|
|
@@ -676,11 +762,15 @@ function verifiedFile(file, label) {
|
|
|
676
762
|
}
|
|
677
763
|
|
|
678
764
|
function canonicalPath(file) {
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
765
|
+
// Share physical checks only within this policy decision. A later import or
|
|
766
|
+
// another resolver can change the filesystem and must see fresh identities.
|
|
767
|
+
const cached = canonicalPaths?.get(file);
|
|
768
|
+
if (cached !== undefined) return cached;
|
|
769
|
+
let canonical;
|
|
770
|
+
try { canonical = observedRealpath(file); }
|
|
771
|
+
catch { canonical = path.resolve(file); }
|
|
772
|
+
canonicalPaths?.set(file, canonical);
|
|
773
|
+
return canonical;
|
|
684
774
|
}
|
|
685
775
|
|
|
686
776
|
function sameFile(left, right) {
|
|
@@ -696,4 +786,4 @@ function isPathWithin(root, candidate) {
|
|
|
696
786
|
!path.isAbsolute(relative))
|
|
697
787
|
);
|
|
698
788
|
}
|
|
699
|
-
|
|
789
|
+
`,"Testkit managed resolution bootstrap")}
|
|
@@ -18,6 +18,7 @@ export declare class EngineExecutorLoadError extends Error {
|
|
|
18
18
|
export declare class EngineManager {
|
|
19
19
|
private readonly catalog;
|
|
20
20
|
private readonly productRoot;
|
|
21
|
+
private readonly installations;
|
|
21
22
|
constructor(options: Readonly<EngineManagerOptions>);
|
|
22
23
|
resolve<Id extends EngineId>(engineId: Id): InternalEngineRegistration<Id>;
|
|
23
24
|
withEngine<Id extends EngineId, Result>(engineId: Id, signal: AbortSignal, operation: (session: ResolvedEngineExecutionSession<Id>) => Result | PromiseLike<Result>): Promise<Result>;
|
package/dist/engines/manager.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ENGINE_PROTOCOL_LIMITS as d,beginEngineExecution as E,createEngineInvocationScope as
|
|
1
|
+
import{ENGINE_PROTOCOL_LIMITS as d,beginEngineExecution as E,createEngineInvocationScope as h}from"./api/index.js";import"../domain/identifiers.js";export class EngineExecutorLoadError extends Error{code="TESTKIT_ENGINE_EXECUTOR_LOAD_FAILED";engineId;constructor(t,o){super(`Resolved ${t} executor could not be loaded`,o),this.name="EngineExecutorLoadError",this.engineId=t}}export class EngineManager{catalog;productRoot;installations=new WeakMap;constructor(t){this.catalog=t.catalog,this.productRoot=t.productRoot}resolve(t){return this.catalog.resolve(t)}async withEngine(t,o,i){const c=this.resolve(t);o.throwIfAborted();let n=this.installations.get(o);n||(n=new Map,this.installations.set(o,n));let r=n.get(t);if(!r){const a=n;r=c.resolveInstallation({productRoot:this.productRoot,signal:o}).catch(l=>{throw a.delete(t),l}),a.set(t,r)}const s=await r;return o.throwIfAborted(),i(w(c,s))}}function w(e,t){return Object.freeze({engineId:e.id,async execute(o){u(o.signal);const i=E(o),c=h({timeoutMs:d.cleanupTimeoutMs});let n=null,r,s=!1;try{const l=await g(e);u(o.signal),n=i.acceptResult(await l(i.request,t,c.scope))}catch(l){s=!0,r=l}finally{i.close()}const a=await c.drain();if(s)throw a.length===0?r:new AggregateError([r,...a],`Engine invocation failed and ${a.length.toString()} cleanup handler(s) also failed`,{cause:r});if(a.length>0)throw new AggregateError(a,`${a.length.toString()} engine invocation cleanup handler(s) failed`);if(n===null)throw new Error("Engine invocation completed without a result");return i.commit(),n}})}function u(e){if(e.aborted)throw e.reason instanceof Error?e.reason:new Error("Resolved engine invocation was cancelled",{cause:e.reason})}async function g(e){try{return await e.loadExecutor()}catch(t){throw new EngineExecutorLoadError(e.id,{cause:t})}}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ProcessClassification } from "../../adapters/execution.js";
|
|
2
2
|
import { type EngineExecutionRequest, type EngineExecutionResult, type EngineFailureEvidence, type EngineInvocationScope } from "../../api/index.js";
|
|
3
3
|
import { type ManagedProcessResult } from "../../../process/index.js";
|
|
4
|
-
import type
|
|
4
|
+
import { type NodeVitestInstallation } from "../../installations/node-vitest.js";
|
|
5
5
|
import { type CollectedNodeVitestEvidence } from "./evidence.js";
|
|
6
6
|
import { writeOwnedNodeVitestRuntimeInput, type NodeVitestRuntimeInputDocument } from "../runtime/runtime-input.js";
|
|
7
7
|
import { writeNodeVitestWorkspace } from "./workspace.js";
|