mirror of
https://github.com/morluto/rea.git
synced 2026-10-27 09:55:46 +00:00
Share parser-owned package and runtime facts across dependency consumers. Preserve uncertain loader requests and explicit wrapper bindings without inventing unbound require authority in known modules. Cover selected source grammar through the public MCP boundary and retain ambient/type dependencies. Open each selected directory before loading its scoped policy, retaining partial directory failures and cleanup ownership. Preserve explicit caller negation precedence and source diagnostics across inventory projections. Clarify how version comparison retains container provenance without claiming semantic drift. Use independent unknown source inputs to keep candidate-limit fixtures meaningful as conditional branch evaluation becomes more precise. Fixes #6891 Fixes #6882 Fixes #6883 Fixes #6859
372 lines
12 KiB
TypeScript
372 lines
12 KiB
TypeScript
import { lstat, realpath, stat } from "node:fs/promises";
|
|
import fs from "node:fs";
|
|
import { join } from "node:path";
|
|
import { locateReferenceSourceGitDirectory } from "./ReferenceSourceGitDirectory.js";
|
|
import type { ReferenceSourceReaderError } from "../reference/ReferenceSourceReaderTypes.js";
|
|
|
|
import ignore from "ignore";
|
|
import {
|
|
readRegularFileText,
|
|
RegularFileReadFailure,
|
|
retryRegularFileCleanup,
|
|
} from "./RegularFileRead.js";
|
|
|
|
import { err, ok, type Result } from "../domain/result.js";
|
|
import type { ArtifactResourceScope } from "../artifacts/ArtifactResourceScope.js";
|
|
import { ArtifactReaderFailure } from "../artifacts/ArtifactReader.js";
|
|
import {
|
|
DEFAULT_REFERENCE_SOURCE_IGNORE_PATTERNS,
|
|
type ReferenceSourceImportError,
|
|
type ReferenceSourceImportOptions,
|
|
} from "./ReferenceSourceImportTypes.js";
|
|
import { rootFilesystemFailure } from "../reference/ReferenceSourceReaderErrors.js";
|
|
import {
|
|
RegularFileCleanupFailure,
|
|
regularFileCancellation,
|
|
} from "../filesystem/OwnedRegularFile.js";
|
|
|
|
/** Validated inputs ready for filesystem traversal. */
|
|
export interface PreparedReferenceSourceImport {
|
|
readonly root: string;
|
|
readonly ignored: Pick<ReturnType<typeof ignore>, "test">;
|
|
readonly beforeDirectory: (
|
|
path: string,
|
|
) => Promise<Result<undefined, ReferenceSourceReaderError>>;
|
|
readonly secrets: ReturnType<typeof ignore>;
|
|
}
|
|
|
|
const failure = (
|
|
code: ReferenceSourceImportError["code"],
|
|
message: string,
|
|
): ReferenceSourceImportError => ({
|
|
tag: "reference-source-import",
|
|
code,
|
|
message,
|
|
});
|
|
|
|
class IgnorePolicyReadCancelled extends Error {
|
|
constructor(readonly reason: unknown) {
|
|
super("Reference source ignore policy read was cancelled", {
|
|
cause: reason,
|
|
});
|
|
this.name = "IgnorePolicyReadCancelled";
|
|
}
|
|
}
|
|
|
|
const isIgnorePolicyCancellation = (cause: unknown): boolean =>
|
|
cause instanceof IgnorePolicyReadCancelled;
|
|
|
|
const ignorePolicyCause = (cause: unknown): unknown =>
|
|
cause instanceof IgnorePolicyReadCancelled
|
|
? cause.reason
|
|
: cause instanceof RegularFileReadFailure
|
|
? cause.operationCause
|
|
: cause;
|
|
|
|
const isAborted = (signal?: AbortSignal): boolean => signal?.aborted === true;
|
|
|
|
const addMarkedPatterns = (
|
|
matcher: ReturnType<typeof ignore>,
|
|
patterns: readonly string[],
|
|
mark: "project-ignored" | "default-ignored" | "caller-excluded",
|
|
): void => {
|
|
for (const pattern of patterns)
|
|
for (const line of pattern.split(/\r?\n/u))
|
|
matcher.add({ pattern: line, mark });
|
|
};
|
|
|
|
const resolveRoot = async (
|
|
requestedRoot: string,
|
|
signal?: AbortSignal,
|
|
): Promise<Result<string, ReferenceSourceImportError>> => {
|
|
if (isAborted(signal))
|
|
return err(failure("cancelled", "Reference source import cancelled"));
|
|
try {
|
|
const metadata = await stat(requestedRoot);
|
|
if (isAborted(signal))
|
|
return err(failure("cancelled", "Reference source import cancelled"));
|
|
if (!metadata.isDirectory())
|
|
return err(
|
|
failure(
|
|
"invalid-root",
|
|
`Reference source root is not a directory: ${requestedRoot}`,
|
|
),
|
|
);
|
|
const canonicalRoot = await realpath(requestedRoot);
|
|
if (isAborted(signal))
|
|
return err(failure("cancelled", "Reference source import cancelled"));
|
|
return ok(canonicalRoot);
|
|
} catch (cause: unknown) {
|
|
if (isAborted(signal))
|
|
return err(failure("cancelled", "Reference source import cancelled"));
|
|
const rootFailure = rootFilesystemFailure(
|
|
cause,
|
|
"Reference source root could not be resolved",
|
|
);
|
|
if (rootFailure === undefined) throw cause;
|
|
return err({ ...failure(rootFailure.code, rootFailure.message), cause });
|
|
}
|
|
};
|
|
|
|
/** Read one policy/configuration file through the existing descriptor owner. */
|
|
const readPolicyFile = async (
|
|
policyPath: string,
|
|
resources: ArtifactResourceScope,
|
|
signal?: AbortSignal,
|
|
): Promise<Result<string | undefined, ReferenceSourceImportError>> => {
|
|
try {
|
|
let present = true;
|
|
try {
|
|
await lstat(policyPath);
|
|
} catch (cause: unknown) {
|
|
if (
|
|
!(
|
|
typeof cause === "object" &&
|
|
cause !== null &&
|
|
"code" in cause &&
|
|
cause.code === "ENOENT"
|
|
)
|
|
)
|
|
throw cause;
|
|
present = false;
|
|
}
|
|
if (signal?.aborted === true)
|
|
throw new IgnorePolicyReadCancelled(signal.reason);
|
|
if (present) {
|
|
let text: string;
|
|
try {
|
|
text = await readRegularFileText(policyPath, {
|
|
signal,
|
|
cancellationFailure: (reason) =>
|
|
new IgnorePolicyReadCancelled(reason),
|
|
});
|
|
} catch (cause: unknown) {
|
|
if (cause instanceof IgnorePolicyReadCancelled) throw cause;
|
|
if (!(cause instanceof RegularFileCleanupFailure)) throw cause;
|
|
const retry = await retryRegularFileCleanup(cause, resources, signal);
|
|
const operationCancellation =
|
|
cause.outcome.kind === "failed"
|
|
? regularFileCancellation(cause)
|
|
: undefined;
|
|
if (retry.cleanup !== undefined) {
|
|
const primary =
|
|
retry.outcome.kind === "failed"
|
|
? retry.outcome.cause
|
|
: cause.cleanupCause;
|
|
const primaryCause = ignorePolicyCause(primary);
|
|
return err({
|
|
...failure(
|
|
isIgnorePolicyCancellation(primary) ||
|
|
operationCancellation !== undefined
|
|
? "cancelled"
|
|
: "io",
|
|
`Reference source ignore policy could not be read at ${policyPath}: ${errorMessage(primaryCause)}`,
|
|
),
|
|
cleanup: retry.cleanup,
|
|
cause: primaryCause,
|
|
});
|
|
}
|
|
if (retry.outcome.kind === "failed") {
|
|
if (operationCancellation !== undefined)
|
|
throw new IgnorePolicyReadCancelled(operationCancellation.reason);
|
|
throw retry.outcome.cause;
|
|
}
|
|
text = retry.outcome.value;
|
|
}
|
|
return ok(text);
|
|
}
|
|
return ok(undefined);
|
|
} catch (cause: unknown) {
|
|
const causeValue = ignorePolicyCause(cause);
|
|
return err({
|
|
...failure(
|
|
isIgnorePolicyCancellation(cause) ? "cancelled" : "io",
|
|
`Reference source policy could not be read at ${policyPath}: ${errorMessage(causeValue)}`,
|
|
),
|
|
cause: causeValue,
|
|
});
|
|
}
|
|
};
|
|
|
|
const readIgnoreCase = async (
|
|
root: string,
|
|
resources: ArtifactResourceScope,
|
|
signal?: AbortSignal,
|
|
): Promise<Result<boolean, ReferenceSourceImportError>> => {
|
|
let readFailure: ReferenceSourceImportError | undefined;
|
|
const readText = async (path: string): Promise<string> => {
|
|
// isomorphic-git probes readFile() without a path to detect promise APIs.
|
|
if (path === undefined)
|
|
throw new TypeError("Git metadata path is required");
|
|
const read = await readPolicyFile(path, resources, signal);
|
|
if (!read.ok) {
|
|
readFailure = read.error;
|
|
throw read.error;
|
|
}
|
|
if (read.value === undefined) {
|
|
const absent = new Error(`Git metadata file is absent: ${path}`);
|
|
Object.assign(absent, { code: "ENOENT" });
|
|
throw absent;
|
|
}
|
|
return read.value;
|
|
};
|
|
try {
|
|
const location = await locateReferenceSourceGitDirectory(
|
|
root,
|
|
readText,
|
|
signal,
|
|
);
|
|
if (location === undefined) return ok(false);
|
|
const { getConfig } = await import("isomorphic-git");
|
|
const value = await getConfig({
|
|
fs: { ...fs, promises: { ...fs.promises, readFile: readText } },
|
|
gitdir: location.commonDirectory,
|
|
path: "core.ignorecase",
|
|
});
|
|
if (readFailure !== undefined) return err(readFailure);
|
|
if (value === undefined) return ok(false);
|
|
// The installed Git parser applies the core.ignorecase boolean schema.
|
|
if (typeof value === "boolean") return ok(value);
|
|
return err(
|
|
failure(
|
|
"io",
|
|
`Invalid core.ignorecase in ${location.commonDirectory}/config`,
|
|
),
|
|
);
|
|
} catch (cause: unknown) {
|
|
if (readFailure !== undefined) return err(readFailure);
|
|
if (isAborted(signal) || isIgnorePolicyCancellation(cause))
|
|
return err(failure("cancelled", "Reference source import cancelled"));
|
|
return err({
|
|
...failure(
|
|
"io",
|
|
`Reference source Git policy could not be read: ${errorMessage(cause)}`,
|
|
),
|
|
cause,
|
|
});
|
|
}
|
|
};
|
|
|
|
const buildIgnored = async (
|
|
root: string,
|
|
excludePaths: readonly string[],
|
|
resources: ArtifactResourceScope,
|
|
signal?: AbortSignal,
|
|
): Promise<
|
|
Result<
|
|
Pick<PreparedReferenceSourceImport, "ignored" | "beforeDirectory">,
|
|
ReferenceSourceImportError
|
|
>
|
|
> => {
|
|
const ignoreCase = await readIgnoreCase(root, resources, signal);
|
|
if (!ignoreCase.ok) return ignoreCase;
|
|
const mandatory = ignore();
|
|
addMarkedPatterns(
|
|
mandatory,
|
|
DEFAULT_REFERENCE_SOURCE_IGNORE_PATTERNS,
|
|
"default-ignored",
|
|
);
|
|
for (const path of excludePaths)
|
|
addMarkedPatterns(mandatory, [path, `${path}/`], "caller-excluded");
|
|
const scopes = new Map<string, ReturnType<typeof ignore>>();
|
|
const loadDirectory = async (
|
|
path: string,
|
|
): Promise<Result<undefined, ReferenceSourceImportError>> => {
|
|
if (scopes.has(path)) return ok(undefined);
|
|
const policyPath =
|
|
path === "." ? join(root, ".gitignore") : join(root, path, ".gitignore");
|
|
const read = await readPolicyFile(policyPath, resources, signal);
|
|
if (!read.ok) return read;
|
|
const matcher = ignore({ ignoreCase: ignoreCase.value });
|
|
if (read.value !== undefined)
|
|
addMarkedPatterns(matcher, [read.value], "project-ignored");
|
|
scopes.set(path, matcher);
|
|
return ok(undefined);
|
|
};
|
|
const rootPolicy = await loadDirectory(".");
|
|
if (!rootPolicy.ok) return rootPolicy;
|
|
return ok({
|
|
ignored: {
|
|
test: (path) => {
|
|
const mandatoryMatch = mandatory.test(path);
|
|
// Caller negations override project rules just as caller exclusions do.
|
|
if (mandatoryMatch.ignored || mandatoryMatch.unignored)
|
|
return mandatoryMatch;
|
|
let result = mandatoryMatch;
|
|
const pieces = path.replace(/\/$/u, "").split("/");
|
|
for (let depth = 0; depth < pieces.length; depth += 1) {
|
|
const scope = depth === 0 ? "." : pieces.slice(0, depth).join("/");
|
|
const matcher = scopes.get(scope);
|
|
if (matcher === undefined) continue;
|
|
const scopedPath =
|
|
pieces.slice(depth).join("/") + (path.endsWith("/") ? "/" : "");
|
|
const match = matcher.test(scopedPath);
|
|
if (match.ignored || match.unignored) result = match;
|
|
}
|
|
return result;
|
|
},
|
|
},
|
|
beforeDirectory: async (path) => {
|
|
const loaded = await loadDirectory(path);
|
|
return loaded.ok
|
|
? loaded
|
|
: err({
|
|
...loaded.error,
|
|
tag: "reference-source-reader",
|
|
code: loaded.error.code === "cancelled" ? "cancelled" : "io",
|
|
});
|
|
},
|
|
});
|
|
};
|
|
|
|
/** Resolve the caller-selected directory and build path filters. */
|
|
export const prepareReferenceSourceImport = async (
|
|
options: ReferenceSourceImportOptions,
|
|
resources: ArtifactResourceScope,
|
|
): Promise<
|
|
Result<PreparedReferenceSourceImport, ReferenceSourceImportError>
|
|
> => {
|
|
try {
|
|
return await resources.run(async () => {
|
|
const root = await resolveRoot(options.root, options.signal);
|
|
if (!root.ok) return root;
|
|
try {
|
|
const ignored = await buildIgnored(
|
|
root.value,
|
|
options.excludePaths ?? [],
|
|
resources,
|
|
options.signal,
|
|
);
|
|
if (!ignored.ok) return ignored;
|
|
return ok({
|
|
root: root.value,
|
|
...ignored.value,
|
|
secrets: ignore().add([...options.policy.secretPatterns]),
|
|
});
|
|
} catch (cause: unknown) {
|
|
const causeValue = ignorePolicyCause(cause);
|
|
return err({
|
|
...failure(
|
|
isIgnorePolicyCancellation(cause) ? "cancelled" : "io",
|
|
`Reference source ignore policy could not be read at ${join(root.value, ".gitignore")}: ${errorMessage(causeValue)}`,
|
|
),
|
|
cause: causeValue,
|
|
});
|
|
}
|
|
});
|
|
} catch (cause: unknown) {
|
|
if (!(cause instanceof ArtifactReaderFailure)) throw cause;
|
|
return err({
|
|
...failure(
|
|
cause.reason === "cancelled" ? "cancelled" : "io",
|
|
cause.message,
|
|
),
|
|
...(cause.cleanup === undefined ? {} : { cleanup: cause.cleanup }),
|
|
cause,
|
|
});
|
|
}
|
|
};
|
|
|
|
const errorMessage = (cause: unknown): string =>
|
|
cause instanceof Error ? cause.message : String(cause);
|