| export type Requirements = { |
| bins: string[]; |
| anyBins: string[]; |
| env: string[]; |
| config: string[]; |
| os: string[]; |
| }; |
|
|
| export type RequirementConfigCheck = { |
| path: string; |
| satisfied: boolean; |
| }; |
|
|
| export type RequirementsMetadata = { |
| requires?: Partial<Pick<Requirements, "bins" | "anyBins" | "env" | "config">>; |
| os?: string[]; |
| }; |
|
|
| export type RequirementRemote = { |
| hasBin?: (bin: string) => boolean; |
| hasAnyBin?: (bins: string[]) => boolean; |
| platforms?: string[]; |
| }; |
|
|
| type RequirementsEvaluationContext = { |
| always: boolean; |
| hasLocalBin: (bin: string) => boolean; |
| localPlatform: string; |
| isEnvSatisfied: (envName: string) => boolean; |
| isConfigSatisfied: (pathStr: string) => boolean; |
| }; |
|
|
| type RequirementsEvaluationRemoteContext = { |
| hasRemoteBin?: (bin: string) => boolean; |
| hasRemoteAnyBin?: (bins: string[]) => boolean; |
| remotePlatforms?: string[]; |
| }; |
|
|
| export function resolveMissingBins(params: { |
| required: string[]; |
| hasLocalBin: (bin: string) => boolean; |
| hasRemoteBin?: (bin: string) => boolean; |
| }): string[] { |
| const remote = params.hasRemoteBin; |
| return params.required.filter((bin) => { |
| if (params.hasLocalBin(bin)) { |
| return false; |
| } |
| if (remote?.(bin)) { |
| return false; |
| } |
| return true; |
| }); |
| } |
|
|
| export function resolveMissingAnyBins(params: { |
| required: string[]; |
| hasLocalBin: (bin: string) => boolean; |
| hasRemoteAnyBin?: (bins: string[]) => boolean; |
| }): string[] { |
| if (params.required.length === 0) { |
| return []; |
| } |
| if (params.required.some((bin) => params.hasLocalBin(bin))) { |
| return []; |
| } |
| if (params.hasRemoteAnyBin?.(params.required)) { |
| return []; |
| } |
| return params.required; |
| } |
|
|
| export function resolveMissingOs(params: { |
| required: string[]; |
| localPlatform: string; |
| remotePlatforms?: string[]; |
| }): string[] { |
| if (params.required.length === 0) { |
| return []; |
| } |
| if (params.required.includes(params.localPlatform)) { |
| return []; |
| } |
| if (params.remotePlatforms?.some((platform) => params.required.includes(platform))) { |
| return []; |
| } |
| return params.required; |
| } |
|
|
| export function resolveMissingEnv(params: { |
| required: string[]; |
| isSatisfied: (envName: string) => boolean; |
| }): string[] { |
| const missing: string[] = []; |
| for (const envName of params.required) { |
| if (params.isSatisfied(envName)) { |
| continue; |
| } |
| missing.push(envName); |
| } |
| return missing; |
| } |
|
|
| export function buildConfigChecks(params: { |
| required: string[]; |
| isSatisfied: (pathStr: string) => boolean; |
| }): RequirementConfigCheck[] { |
| return params.required.map((pathStr) => { |
| const satisfied = params.isSatisfied(pathStr); |
| return { path: pathStr, satisfied }; |
| }); |
| } |
|
|
| export function evaluateRequirements( |
| params: RequirementsEvaluationContext & |
| RequirementsEvaluationRemoteContext & { |
| required: Requirements; |
| }, |
| ): { missing: Requirements; eligible: boolean; configChecks: RequirementConfigCheck[] } { |
| const missingBins = resolveMissingBins({ |
| required: params.required.bins, |
| hasLocalBin: params.hasLocalBin, |
| hasRemoteBin: params.hasRemoteBin, |
| }); |
| const missingAnyBins = resolveMissingAnyBins({ |
| required: params.required.anyBins, |
| hasLocalBin: params.hasLocalBin, |
| hasRemoteAnyBin: params.hasRemoteAnyBin, |
| }); |
| const missingOs = resolveMissingOs({ |
| required: params.required.os, |
| localPlatform: params.localPlatform, |
| remotePlatforms: params.remotePlatforms, |
| }); |
| const missingEnv = resolveMissingEnv({ |
| required: params.required.env, |
| isSatisfied: params.isEnvSatisfied, |
| }); |
| const configChecks = buildConfigChecks({ |
| required: params.required.config, |
| isSatisfied: params.isConfigSatisfied, |
| }); |
| const missingConfig = configChecks.filter((check) => !check.satisfied).map((check) => check.path); |
|
|
| const missing = params.always |
| ? { bins: [], anyBins: [], env: [], config: [], os: [] } |
| : { |
| bins: missingBins, |
| anyBins: missingAnyBins, |
| env: missingEnv, |
| config: missingConfig, |
| os: missingOs, |
| }; |
|
|
| const eligible = |
| params.always || |
| (missing.bins.length === 0 && |
| missing.anyBins.length === 0 && |
| missing.env.length === 0 && |
| missing.config.length === 0 && |
| missing.os.length === 0); |
|
|
| return { missing, eligible, configChecks }; |
| } |
|
|
| export function evaluateRequirementsFromMetadata( |
| params: RequirementsEvaluationContext & |
| RequirementsEvaluationRemoteContext & { |
| metadata?: RequirementsMetadata; |
| }, |
| ): { |
| required: Requirements; |
| missing: Requirements; |
| eligible: boolean; |
| configChecks: RequirementConfigCheck[]; |
| } { |
| const required: Requirements = { |
| bins: params.metadata?.requires?.bins ?? [], |
| anyBins: params.metadata?.requires?.anyBins ?? [], |
| env: params.metadata?.requires?.env ?? [], |
| config: params.metadata?.requires?.config ?? [], |
| os: params.metadata?.os ?? [], |
| }; |
|
|
| const result = evaluateRequirements({ |
| always: params.always, |
| required, |
| hasLocalBin: params.hasLocalBin, |
| hasRemoteBin: params.hasRemoteBin, |
| hasRemoteAnyBin: params.hasRemoteAnyBin, |
| localPlatform: params.localPlatform, |
| remotePlatforms: params.remotePlatforms, |
| isEnvSatisfied: params.isEnvSatisfied, |
| isConfigSatisfied: params.isConfigSatisfied, |
| }); |
| return { required, ...result }; |
| } |
|
|
| export function evaluateRequirementsFromMetadataWithRemote( |
| params: RequirementsEvaluationContext & { |
| metadata?: RequirementsMetadata; |
| remote?: RequirementRemote; |
| }, |
| ): { |
| required: Requirements; |
| missing: Requirements; |
| eligible: boolean; |
| configChecks: RequirementConfigCheck[]; |
| } { |
| return evaluateRequirementsFromMetadata({ |
| always: params.always, |
| metadata: params.metadata, |
| hasLocalBin: params.hasLocalBin, |
| hasRemoteBin: params.remote?.hasBin, |
| hasRemoteAnyBin: params.remote?.hasAnyBin, |
| localPlatform: params.localPlatform, |
| remotePlatforms: params.remote?.platforms, |
| isEnvSatisfied: params.isEnvSatisfied, |
| isConfigSatisfied: params.isConfigSatisfied, |
| }); |
| } |
|
|