mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 01:13:39 +00:00
fix: report provider admission failures as typed codes (#8066)
* fix: report provider admission failures as typed codes * fix(evm): keep limiter path on admission diagnostics * fix: assert WABT and CBOR admission codes * fix: keep admission details on returned failures * fix(evm): detect repeated CBOR keys in the decoder --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
1 parent
3b647132c6
commit
317bf3fe23
63 files changed
+1021
-432
No files matched your search
@@ -1,5 +1,15 @@
|
||||
import type { FileHandle } from "node:fs/promises";
|
||||
|
||||
/** Close admission for one owned file handle. `code` is the stable identity. */
|
||||
export class OwnedFileHandleError extends Error {
|
||||
readonly _tag = "OwnedFileHandleError";
|
||||
|
||||
constructor(readonly code: "handle_invalidated") {
|
||||
super(code);
|
||||
this.name = "OwnedFileHandleError";
|
||||
}
|
||||
}
|
||||
|
||||
type CloseState =
|
||||
| { readonly kind: "open" }
|
||||
| { readonly kind: "closing"; readonly attempt: Promise<void> }
|
||||
@@ -20,9 +30,7 @@ export class OwnedFileHandle<
|
||||
if (this.#state.kind === "closing") return this.#state.attempt;
|
||||
if (this.#state.kind === "failed") return Promise.reject(this.#state.cause);
|
||||
if (this.handle.fd < 0) {
|
||||
const cause = new Error(
|
||||
"File handle was invalidated before this owner confirmed its close",
|
||||
);
|
||||
const cause = new OwnedFileHandleError("handle_invalidated");
|
||||
this.#state = { kind: "failed", cause };
|
||||
return Promise.reject(cause);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { StagedOutputError } from "./StagedOutputError.js";
|
||||
import { lstat, readdir, rmdir, unlink } from "node:fs/promises";
|
||||
import type { Stats } from "node:fs";
|
||||
|
||||
@@ -48,7 +49,7 @@ export class OwnedStagedOutput {
|
||||
|
||||
setFile(file: OwnedFileHandle): void {
|
||||
if (this.#file !== undefined)
|
||||
throw new Error("Staged output already owns a file handle");
|
||||
throw new StagedOutputError("handle_already_owned");
|
||||
this.#file = file;
|
||||
}
|
||||
|
||||
@@ -58,7 +59,7 @@ export class OwnedStagedOutput {
|
||||
if (this.#file !== undefined && !this.#fileClosed)
|
||||
await this.#observeFileIdentity();
|
||||
if (this.#fileIdentity === undefined)
|
||||
throw new Error("Cannot verify staged file identity", {
|
||||
throw new StagedOutputError("identity_unverified", undefined, {
|
||||
cause: this.#identityFailure,
|
||||
});
|
||||
const stagedFile = await lstat(content);
|
||||
@@ -67,7 +68,7 @@ export class OwnedStagedOutput {
|
||||
stagedFile.isSymbolicLink() ||
|
||||
!sameIdentity(stagedFile, this.#fileIdentity)
|
||||
)
|
||||
throw new Error(`Staged file identity changed: ${content}`);
|
||||
throw new StagedOutputError("identity_changed", { path: content });
|
||||
}
|
||||
|
||||
/** Observe the descriptor's identity after its last write, then close it. */
|
||||
@@ -134,9 +135,11 @@ export class OwnedStagedOutput {
|
||||
) {
|
||||
stagedFile = undefined;
|
||||
} else if (errorCode(cause) === "ENOENT") {
|
||||
throw new Error(`Owned staged file disappeared: ${content}`, {
|
||||
cause,
|
||||
});
|
||||
throw new StagedOutputError(
|
||||
"file_disappeared",
|
||||
{ path: content },
|
||||
{ cause },
|
||||
);
|
||||
} else {
|
||||
throw cause;
|
||||
}
|
||||
@@ -148,7 +151,7 @@ export class OwnedStagedOutput {
|
||||
this.#fileIdentity === undefined ||
|
||||
!sameIdentity(stagedFile, this.#fileIdentity)
|
||||
)
|
||||
throw new Error(`Staged file identity changed: ${content}`);
|
||||
throw new StagedOutputError("identity_changed", { path: content });
|
||||
await unlink(content);
|
||||
this.#fileRemovedByOwner = true;
|
||||
}
|
||||
@@ -156,20 +159,20 @@ export class OwnedStagedOutput {
|
||||
|
||||
const remaining = await readdir(this.directory);
|
||||
if (remaining.length > 0)
|
||||
throw new Error(
|
||||
`Unexpected staging entries remain in ${this.directory}: ${remaining.join(", ")}`,
|
||||
);
|
||||
throw new StagedOutputError("unexpected_entries", {
|
||||
count: remaining.length,
|
||||
entries: remaining.join(","),
|
||||
});
|
||||
await this.#verifiedDirectory();
|
||||
await rmdir(this.directory);
|
||||
}
|
||||
|
||||
async #verifiedDirectory(): Promise<void> {
|
||||
if (this.directoryIdentity === undefined)
|
||||
throw new Error(
|
||||
`Cannot verify staging directory ownership: ${this.directory}`,
|
||||
{
|
||||
cause: this.#identityFailure,
|
||||
},
|
||||
throw new StagedOutputError(
|
||||
"directory_unverified",
|
||||
{ directory: this.directory },
|
||||
{ cause: this.#identityFailure },
|
||||
);
|
||||
const directory = await lstat(this.directory);
|
||||
if (
|
||||
@@ -177,7 +180,9 @@ export class OwnedStagedOutput {
|
||||
directory.isSymbolicLink() ||
|
||||
!sameIdentity(directory, this.directoryIdentity)
|
||||
)
|
||||
throw new Error(`Staging directory identity changed: ${this.directory}`);
|
||||
throw new StagedOutputError("directory_identity_changed", {
|
||||
directory: this.directory,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
const STAGED_OUTPUT_CODES = [
|
||||
"handle_already_owned",
|
||||
"identity_unverified",
|
||||
"identity_changed",
|
||||
"file_disappeared",
|
||||
"unexpected_entries",
|
||||
"directory_unverified",
|
||||
"directory_identity_changed",
|
||||
] as const;
|
||||
|
||||
export type StagedOutputCode = (typeof STAGED_OUTPUT_CODES)[number];
|
||||
|
||||
/** Owned staged-output admission failure. `code` is the stable identity. */
|
||||
export class StagedOutputError extends Error {
|
||||
readonly _tag = "StagedOutputError";
|
||||
|
||||
constructor(
|
||||
readonly code: StagedOutputCode,
|
||||
readonly detail?: Readonly<Record<string, string | number>>,
|
||||
options?: ErrorOptions,
|
||||
) {
|
||||
super(code, options);
|
||||
this.name = "StagedOutputError";
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user