fix(deps): upgrade zod without narrowing timestamp inputs

Upgrade Zod to 4.6.6 and refresh locked license inventories. Preserve REA's established timestamp spellings through one shared UTC/offset grammar, including completion-verifier consumers. Size the stdin resource regression from the child's actual heap capacity.

Fixes #7173
Includes the dependency and inventory changes proposed in #7184.

Co-authored-by: tijo95 <georgesarmede@gmail.com>
This commit is contained in:
gensokyo-devandtijo95 authored and GitHub committed 2026-10-26 20:13:12 +08:00
1 parent b675073b73
commit e3066c1705
22 files changed
+205 -87

No files matched your search

+2 -6
View File
@@ -691,12 +691,8 @@
"type": "object", "type": "object",
"properties": { "properties": {
"logical_path": { "type": "string" }, "logical_path": { "type": "string" },
"declared_sha256": { "declared_sha256": { "type": ["string", "null"] },
"anyOf": [{ "type": "string" }, { "type": "null" }] "calculated_sha256": { "type": ["string", "null"] },
},
"calculated_sha256": {
"anyOf": [{ "type": "string" }, { "type": "null" }]
},
"unpacked": { "type": "boolean" } "unpacked": { "type": "boolean" }
}, },
"required": [ "required": [
+4 -4
View File
@@ -4810,11 +4810,11 @@ yoctocolors@2.2.0 | development | MIT
Package: https://www.npmjs.com/package/yoctocolors/v/2.2.0 Package: https://www.npmjs.com/package/yoctocolors/v/2.2.0
Archive: https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.2.0.tgz Archive: https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.2.0.tgz
Integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg== Integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==
zod@4.4.3 | runtime | MIT zod@4.6.6 | runtime | MIT
Lockfile path: node_modules/zod Lockfile path: node_modules/zod
Package: https://www.npmjs.com/package/zod/v/4.4.3 Package: https://www.npmjs.com/package/zod/v/4.6.6
Archive: https://registry.npmjs.org/zod/-/zod-4.4.3.tgz Archive: https://registry.npmjs.org/zod/-/zod-4.6.6.tgz
Integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ== Integrity: sha512-4UmzQUF5fxvfcYHlbjBmejU+w+hbDnWFx4aS8kBx7nPBHcO2SqPbpAG8snbdg+Xse4pRefR98hgjFgIMCI/UhA==
zwitch@2.0.4 | development | MIT zwitch@2.0.4 | development | MIT
Lockfile path: node_modules/zwitch Lockfile path: node_modules/zwitch
Package: https://www.npmjs.com/package/zwitch/v/2.0.4 Package: https://www.npmjs.com/package/zwitch/v/2.0.4
+4 -4
View File
@@ -1,7 +1,7 @@
REA third-party npm runtime copyright, license and NOTICE texts REA third-party npm runtime copyright, license and NOTICE texts
Collected from integrity-verified locked npm archives. Release-specific supplements and upstream notice gaps are identified in their entries. Collected from integrity-verified locked npm archives. Release-specific supplements and upstream notice gaps are identified in their entries.
External engines are separately obtained; see THIRD_PARTY_NOTICES.md. External engines are separately obtained; see THIRD_PARTY_NOTICES.md.
Runtime dependency fingerprint (SHA-256): d0a0286bb8c158a670b019cdb56ee120542cb072bbecf3741c172b5f06b2301b Runtime dependency fingerprint (SHA-256): 8f4447e00ab3adb9d6b6ab2850634d23945552fafd06999eb207df4652912ece
===== @babel/helper-string-parser@8.0.6 ===== ===== @babel/helper-string-parser@8.0.6 =====
Declared license: MIT Declared license: MIT
@@ -7261,10 +7261,10 @@ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE. THIS SOFTWARE.
===== zod@4.4.3 ===== ===== zod@4.6.6 =====
Declared license: MIT Declared license: MIT
Archive: https://registry.npmjs.org/zod/-/zod-4.4.3.tgz Archive: https://registry.npmjs.org/zod/-/zod-4.6.6.tgz
Integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ== Integrity: sha512-4UmzQUF5fxvfcYHlbjBmejU+w+hbDnWFx4aS8kBx7nPBHcO2SqPbpAG8snbdg+Xse4pRefR98hgjFgIMCI/UhA==
Scope: npm runtime dependency, including optional platform packages. Scope: npm runtime dependency, including optional platform packages.
--- package/LICENSE --- --- package/LICENSE ---
+4 -4
View File
@@ -50,7 +50,7 @@
"write-file-atomic": "7.0.0", "write-file-atomic": "7.0.0",
"ws": "8.21.0", "ws": "8.21.0",
"yaml": "2.9.0", "yaml": "2.9.0",
"zod": "4.4.3" "zod": "4.6.6"
}, },
"bin": { "bin": {
"rea": "scripts/rea.mjs", "rea": "scripts/rea.mjs",
@@ -13916,9 +13916,9 @@
} }
}, },
"node_modules/zod": { "node_modules/zod": {
"version": "4.4.3", "version": "4.6.6",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.6.tgz",
"integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "integrity": "sha512-4UmzQUF5fxvfcYHlbjBmejU+w+hbDnWFx4aS8kBx7nPBHcO2SqPbpAG8snbdg+Xse4pRefR98hgjFgIMCI/UhA==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"
+1 -1
View File
@@ -379,7 +379,7 @@
"write-file-atomic": "7.0.0", "write-file-atomic": "7.0.0",
"ws": "8.21.0", "ws": "8.21.0",
"yaml": "2.9.0", "yaml": "2.9.0",
"zod": "4.4.3" "zod": "4.6.6"
}, },
"overrides": { "overrides": {
"typed-rest-client": { "typed-rest-client": {
+3 -2
View File
@@ -2,6 +2,7 @@ import { z } from "zod";
import { digestCanonicalValue } from "../../src/domain/canonicalDigest.js"; import { digestCanonicalValue } from "../../src/domain/canonicalDigest.js";
import { IDENTIFIER_PATTERN } from "../../src/domain/stringPatterns.js"; import { IDENTIFIER_PATTERN } from "../../src/domain/stringPatterns.js";
import { utcDateTimeSchema } from "../../src/domain/offsetDateTime.js";
import { import {
compareAsciiStrings, compareAsciiStrings,
isSortedUniqueAscii, isSortedUniqueAscii,
@@ -44,7 +45,7 @@ const verifierRunSchema = z
process_lineage: z.discriminatedUnion("status", [ process_lineage: z.discriminatedUnion("status", [
z.strictObject({ z.strictObject({
status: z.literal("verified"), status: z.literal("verified"),
observed_at: z.iso.datetime(), observed_at: utcDateTimeSchema,
launcher_pid: z.int().positive(), launcher_pid: z.int().positive(),
launcher_parent_pid: z.int().nonnegative(), launcher_parent_pid: z.int().nonnegative(),
process_group_id: z.int().positive(), process_group_id: z.int().positive(),
@@ -58,7 +59,7 @@ const verifierRunSchema = z
}), }),
z.strictObject({ z.strictObject({
status: z.literal("unavailable"), status: z.literal("unavailable"),
observed_at: z.iso.datetime(), observed_at: utcDateTimeSchema,
launcher_pid: z.int().positive(), launcher_pid: z.int().positive(),
launcher_parent_pid: z.int().nonnegative(), launcher_parent_pid: z.int().nonnegative(),
process_group_id: z.int().positive().nullable(), process_group_id: z.int().positive().nullable(),
+3 -2
View File
@@ -1,5 +1,6 @@
import { procedureInfoSchema } from "../domain/native/procedureInfo.js"; import { procedureInfoSchema } from "../domain/native/procedureInfo.js";
import { z } from "zod"; import { z } from "zod";
import { utcDateTimeSchema } from "../domain/offsetDateTime.js";
import { functionDossierSchema } from "../domain/hopperValues.js"; import { functionDossierSchema } from "../domain/hopperValues.js";
import { analysisProfileSchema } from "../domain/analysisProfile.js"; import { analysisProfileSchema } from "../domain/analysisProfile.js";
@@ -179,13 +180,13 @@ const processCoordinates = {
const processLineageObservation = z.discriminatedUnion("status", [ const processLineageObservation = z.discriminatedUnion("status", [
z.object({ z.object({
status: z.literal("unavailable"), status: z.literal("unavailable"),
observed_at: z.iso.datetime(), observed_at: utcDateTimeSchema,
...processCoordinates, ...processCoordinates,
reason: z.string().min(1), reason: z.string().min(1),
}), }),
z.object({ z.object({
status: z.literal("verified"), status: z.literal("verified"),
observed_at: z.iso.datetime(), observed_at: utcDateTimeSchema,
...processCoordinates, ...processCoordinates,
launcher_parent_pid: z.int().min(0), launcher_parent_pid: z.int().min(0),
descendants: z.array( descendants: z.array(
+5 -4
View File
@@ -1,4 +1,5 @@
import { z } from "zod"; import { z } from "zod";
import { utcDateTimeSchema } from "./offsetDateTime.js";
import { browserCompletenessSchema } from "./browserCompleteness.js"; import { browserCompletenessSchema } from "./browserCompleteness.js";
import { import {
@@ -266,8 +267,8 @@ export const webPageInspectionSchema = z.object({
browser: browserVersionSchema, browser: browserVersionSchema,
target: browserTargetSchema, target: browserTargetSchema,
capture_window: z.object({ capture_window: z.object({
started_at: z.iso.datetime(), started_at: utcDateTimeSchema,
ended_at: z.iso.datetime(), ended_at: utcDateTimeSchema,
observation_ms: z.int().min(0), observation_ms: z.int().min(0),
}), }),
completeness: browserCompletenessSchema, completeness: browserCompletenessSchema,
@@ -293,12 +294,12 @@ export const webPageInspectionSchema = z.object({
network: z.object({ network: z.object({
requests: z.array(browserNetworkRequestSchema), requests: z.array(browserNetworkRequestSchema),
websocket_connections: z.array(browserWebSocketConnectionSchema), websocket_connections: z.array(browserWebSocketConnectionSchema),
coverage_started_at: z.iso.datetime(), coverage_started_at: utcDateTimeSchema,
prior_activity_available: z.literal(false), prior_activity_available: z.literal(false),
}), }),
console: z.object({ console: z.object({
events: z.array(browserConsoleEventSchema), events: z.array(browserConsoleEventSchema),
coverage_started_at: z.iso.datetime(), coverage_started_at: utcDateTimeSchema,
prior_activity_available: z.literal(false), prior_activity_available: z.literal(false),
}), }),
workers: z.array(browserWorkerSchema), workers: z.array(browserWorkerSchema),
+3 -4
View File
@@ -1,6 +1,7 @@
import { createHash } from "node:crypto"; import { createHash } from "node:crypto";
import { z } from "zod"; import { z } from "zod";
import { utcDateTimeSchema } from "./offsetDateTime.js";
import { emptyArraySchema } from "./emptyArraySchema.js"; import { emptyArraySchema } from "./emptyArraySchema.js";
import { import {
@@ -148,8 +149,7 @@ const eventBase = {
const eventUrl = sanitizedBrowserUrlSchema.nullable(); const eventUrl = sanitizedBrowserUrlSchema.nullable();
/** The browser's wall-clock request start, reported once the request ends. */ /** The browser's wall-clock request start, reported once the request ends. */
const browserStartSchema = z.iso const browserStartSchema = utcDateTimeSchema
.datetime()
.optional() .optional()
.describe( .describe(
"Wall-clock time the browser started the request, as it reported at completion; absent when it reported none.", "Wall-clock time the browser started the request, as it reported at completion; absent when it reported none.",
@@ -201,8 +201,7 @@ export const browserScenarioEventSchema = z
kind: z.literal("request"), kind: z.literal("request"),
status: z.null(), status: z.null(),
failure: z.null(), failure: z.null(),
observed_at: z.iso observed_at: utcDateTimeSchema
.datetime()
.optional() .optional()
.describe( .describe(
"Wall-clock time the provider observed the request start; absent in captures made before it was recorded.", "Wall-clock time the provider observed the request start; absent in captures made before it was recorded.",
+3 -2
View File
@@ -1,4 +1,5 @@
import { z } from "zod"; import { z } from "zod";
import { utcDateTimeSchema } from "./offsetDateTime.js";
import { webRuntimeCommandTimeoutSchema } from "./browserDeadline.js"; import { webRuntimeCommandTimeoutSchema } from "./browserDeadline.js";
import { import {
@@ -60,8 +61,8 @@ export const webObservationSessionSchema = z.object({
final_url: z.string().nullable(), final_url: z.string().nullable(),
}), }),
window: z.object({ window: z.object({
armed_at: z.iso.datetime(), armed_at: utcDateTimeSchema,
ended_at: z.iso.datetime(), ended_at: utcDateTimeSchema,
requested_ms: z.int().min(1), requested_ms: z.int().min(1),
end_reason: z.enum([ end_reason: z.enum([
"window_elapsed", "window_elapsed",
+3 -2
View File
@@ -1,4 +1,5 @@
import { z } from "zod"; import { z } from "zod";
import { utcDateTimeSchema } from "../offsetDateTime.js";
import { webRuntimeCommandTimeoutSchema } from "../browserDeadline.js"; import { webRuntimeCommandTimeoutSchema } from "../browserDeadline.js";
import { browserCompletenessSchema } from "../browserCompleteness.js"; import { browserCompletenessSchema } from "../browserCompleteness.js";
@@ -83,8 +84,8 @@ export const electronPageInspectionSchema = z.object({
browser: browserVersionSchema, browser: browserVersionSchema,
target: electronTargetSchema, target: electronTargetSchema,
capture_window: z.object({ capture_window: z.object({
started_at: z.iso.datetime(), started_at: utcDateTimeSchema,
ended_at: z.iso.datetime(), ended_at: utcDateTimeSchema,
observation_ms: z.int().min(0), observation_ms: z.int().min(0),
}), }),
completeness: browserCompletenessSchema, completeness: browserCompletenessSchema,
+28 -30
View File
@@ -4,11 +4,9 @@ import { z } from "zod";
import { import {
offsetDateTimeSchema, offsetDateTimeSchema,
readRecordedDateTime, readRecordedDateTime,
utcDateTimeSchema,
} from "./offsetDateTime.js"; } from "./offsetDateTime.js";
/** The validator this schema replaces; it remains the oracle for the accepted set. */
const replaced = z.string().datetime({ offset: true });
const ACCEPTED: readonly (readonly [string, string])[] = [ const ACCEPTED: readonly (readonly [string, string])[] = [
["a UTC timestamp", "2026-10-09T04:00:00Z"], ["a UTC timestamp", "2026-10-09T04:00:00Z"],
["a positive offset", "2026-10-09T04:00:00+02:00"], ["a positive offset", "2026-10-09T04:00:00+02:00"],
@@ -45,8 +43,6 @@ const REJECTED: readonly (readonly [string, string])[] = [
["unrelated text", "not a timestamp"], ["unrelated text", "not a timestamp"],
]; ];
const CASES = [...ACCEPTED, ...REJECTED];
describe("offset datetime schema", () => { describe("offset datetime schema", () => {
it.each(ACCEPTED)("accepts %s", (_label, value) => { it.each(ACCEPTED)("accepts %s", (_label, value) => {
expect(offsetDateTimeSchema.safeParse(value).success).toBe(true); expect(offsetDateTimeSchema.safeParse(value).success).toBe(true);
@@ -55,29 +51,42 @@ describe("offset datetime schema", () => {
it.each(REJECTED)("rejects %s", (_label, value) => { it.each(REJECTED)("rejects %s", (_label, value) => {
expect(offsetDateTimeSchema.safeParse(value).success).toBe(false); expect(offsetDateTimeSchema.safeParse(value).success).toBe(false);
}); });
});
it("classifies every case exactly as the replaced Zod validator does", () => { describe("UTC datetime schema", () => {
const divergences = CASES.filter( it.each(ACCEPTED.filter(([, value]) => value.endsWith("Z")))(
([, value]) => "preserves %s without rewriting the source value",
offsetDateTimeSchema.safeParse(value).success !== (_label, value) => {
replaced.safeParse(value).success, expect(utcDateTimeSchema.parse(value)).toBe(value);
).map(([label]) => label); },
expect(divergences).toEqual([]); );
it.each([
...REJECTED,
...ACCEPTED.filter(([, value]) => !value.endsWith("Z")),
[
"omitted seconds with a numeric offset",
"2026-10-09T04:00+02:00",
] as const,
])("rejects %s", (_label, value) => {
expect(utcDateTimeSchema.safeParse(value).success).toBe(false);
}); });
});
describe.each([
["offset", offsetDateTimeSchema],
["UTC", utcDateTimeSchema],
] as const)("%s datetime advertisement", (_name, schema) => {
it("emits a pattern that compiles in every mode a client may use", () => { it("emits a pattern that compiles in every mode a client may use", () => {
const emitted = z.toJSONSchema( const emitted = z.toJSONSchema(z.object({ observed_at: schema }), {
z.object({ observed_at: offsetDateTimeSchema }), target: "draft-2020-12",
{ target: "draft-2020-12" }, });
);
const pattern = findPattern(emitted); const pattern = findPattern(emitted);
expect(pattern).toBeDefined(); expect(pattern).toBeDefined();
if (pattern === undefined) return; if (pattern === undefined) return;
// Annex B and `u` accept the pattern the replaced validator emitted; `v` does expect(() => new RegExp(pattern)).not.toThrow();
// not, which is the reason this schema exists.
expect(() => new RegExp(pattern, "u")).not.toThrow(); expect(() => new RegExp(pattern, "u")).not.toThrow();
expect(() => new RegExp(pattern, "v")).not.toThrow(); expect(() => new RegExp(pattern, "v")).not.toThrow();
expect(() => new RegExp(replacedSource(), "v")).toThrow();
}); });
}); });
@@ -222,14 +231,3 @@ const findPattern = (value: unknown): string | undefined => {
} }
return undefined; return undefined;
}; };
/** The pattern the replaced Zod validator projects, recovered through the same path. */
const replacedSource = (): string => {
const emitted = z.toJSONSchema(z.object({ observed_at: replaced }), {
target: "draft-2020-12",
});
const pattern = findPattern(emitted);
if (pattern === undefined)
throw new Error("The replaced validator must project a pattern");
return pattern;
};
+17 -12
View File
@@ -6,26 +6,31 @@ export interface OffsetDateTimeInstant {
readonly fraction: string; readonly fraction: string;
} }
const CALENDAR_DATE = String.raw`(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))`;
const CLOCK_TIME = String.raw`(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?`;
const UTC_OFFSET = String.raw`(?:Z|[+\x2d](?:[01]\d|2[0-3]):[0-5]\d)`;
/** /**
* ISO-8601 date-time that requires an explicit UTC offset. * REA's ISO-8601 timestamps preserve optional seconds and arbitrary fractional
* * precision, including inputs admitted before Zod 4.6.6 required seconds.
* `z.string().datetime({ offset: true })` emits Zod's offset class as `[+-]`. * Keep this grammar independent of the installed validator version. The
* Annex B and `u` mode accept that, but ECMA-262 `v` mode rejects the * escaped offset hyphen also compiles in ECMA-262 Unicode-set (`v`) mode.
* unescaped `-`, so any tool whose schema carries the pattern is unusable for a
* client that compiles advertised patterns — schema compilation happens before
* arguments are supplied, so the whole tools request fails rather than one call.
*
* The accepted set is identical to the Zod validator; only the offset class
* escapes `-`, as `\x2d`, because an escaped `-` inside a character class is
* removed by `oxlint --fix` as a useless escape.
*/ */
export const offsetDateTimeSchema = z export const offsetDateTimeSchema = z
.string() .string()
.regex( .regex(
/^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|(?:[+\x2d](?:[01]\d|2[0-3]):[0-5]\d)))$/u, new RegExp(`^${CALENDAR_DATE}T${CLOCK_TIME}${UTC_OFFSET}$`, "u"),
"Use an ISO-8601 date-time with an explicit UTC offset", "Use an ISO-8601 date-time with an explicit UTC offset",
); );
/** The same established timestamp grammar with the UTC `Z` designator only. */
export const utcDateTimeSchema = z
.string()
.regex(
new RegExp(`^${CALENDAR_DATE}T${CLOCK_TIME}Z$`, "u"),
"Use an ISO-8601 date-time in UTC",
);
/** Parse an already validated offset timestamp without losing sub-millisecond precision. */ /** Parse an already validated offset timestamp without losing sub-millisecond precision. */
export const parseOffsetDateTimeInstant = ( export const parseOffsetDateTimeInstant = (
value: string, value: string,
+3 -2
View File
@@ -1,4 +1,5 @@
import { z } from "zod"; import { z } from "zod";
import { utcDateTimeSchema } from "../offsetDateTime.js";
import { jsonValueSchema } from "../jsonValue.js"; import { jsonValueSchema } from "../jsonValue.js";
import { normalizationSchema } from "./processScenario.js"; import { normalizationSchema } from "./processScenario.js";
@@ -455,8 +456,8 @@ const processCaptureShapeSchema = z.strictObject({
platform: z.string().min(1), platform: z.string().min(1),
architecture: z.string().min(1), architecture: z.string().min(1),
pty_backend: z.literal("node-pty"), pty_backend: z.literal("node-pty"),
started_at: z.iso.datetime(), started_at: utcDateTimeSchema,
completed_at: z.iso.datetime(), completed_at: utcDateTimeSchema,
scenario: z.record(z.string(), jsonValueSchema), scenario: z.record(z.string(), jsonValueSchema),
comparison_contract: z.record(z.string(), jsonValueSchema), comparison_contract: z.record(z.string(), jsonValueSchema),
full_scenario_sha256: z.string().regex(/^[a-f0-9]{64}$/u), full_scenario_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
+2 -1
View File
@@ -1,4 +1,5 @@
import { z } from "zod"; import { z } from "zod";
import { utcDateTimeSchema } from "./offsetDateTime.js";
import { import {
webRuntimeLocationSchema, webRuntimeLocationSchema,
webRuntimeScopeSchema, webRuntimeScopeSchema,
@@ -21,7 +22,7 @@ export type InspectWebEventListenersInput = z.infer<
export const webEventListenersObservationSchema = z.object({ export const webEventListenersObservationSchema = z.object({
browser: browserVersionSchema, browser: browserVersionSchema,
target: webRuntimeTargetSchema, target: webRuntimeTargetSchema,
inspected_at: z.iso.datetime(), inspected_at: utcDateTimeSchema,
selected_node: z.object({ selected_node: z.object({
selector: z.string(), selector: z.string(),
match: z.enum(["found", "not_found"]), match: z.enum(["found", "not_found"]),
+3 -2
View File
@@ -1,4 +1,5 @@
import { z } from "zod"; import { z } from "zod";
import { utcDateTimeSchema } from "./offsetDateTime.js";
import { MAX_BROWSER_DEADLINE_MS } from "./browserDeadline.js"; import { MAX_BROWSER_DEADLINE_MS } from "./browserDeadline.js";
import { jsonObjectSchema } from "./jsonValue.js"; import { jsonObjectSchema } from "./jsonValue.js";
import { import {
@@ -34,8 +35,8 @@ export const webExecutionObservationSchema = z.object({
browser: browserVersionSchema, browser: browserVersionSchema,
target: webRuntimeTargetSchema, target: webRuntimeTargetSchema,
window: z.object({ window: z.object({
armed_at: z.iso.datetime(), armed_at: utcDateTimeSchema,
ended_at: z.iso.datetime(), ended_at: utcDateTimeSchema,
requested_ms: z.int().min(1), requested_ms: z.int().min(1),
end_reason: z.enum([ end_reason: z.enum([
"window_elapsed", "window_elapsed",
+2 -1
View File
@@ -1,6 +1,7 @@
import { createHash } from "node:crypto"; import { createHash } from "node:crypto";
import { z } from "zod"; import { z } from "zod";
import { utcDateTimeSchema } from "./offsetDateTime.js";
import { webRuntimeCommandTimeoutSchema } from "./browserDeadline.js"; import { webRuntimeCommandTimeoutSchema } from "./browserDeadline.js";
import { browserCompletenessSchema } from "./browserCompleteness.js"; import { browserCompletenessSchema } from "./browserCompleteness.js";
@@ -52,7 +53,7 @@ export const webScreenshotSchema = z.object({
url: z.string(), url: z.string(),
origin: z.string(), origin: z.string(),
}), }),
captured_at: z.iso.datetime(), captured_at: utcDateTimeSchema,
viewport: z.object({ viewport: z.object({
width: z.int().min(1), width: z.int().min(1),
height: z.int().min(1), height: z.int().min(1),
+2 -1
View File
@@ -20,6 +20,7 @@ import { join } from "node:path";
import canonicalize from "canonicalize"; import canonicalize from "canonicalize";
import writeFileAtomic from "write-file-atomic"; import writeFileAtomic from "write-file-atomic";
import { z } from "zod"; import { z } from "zod";
import { utcDateTimeSchema } from "../domain/offsetDateTime.js";
import { AnalysisCleanupLease } from "../artifacts/AnalysisCleanupLease.js"; import { AnalysisCleanupLease } from "../artifacts/AnalysisCleanupLease.js";
import { analysisErrorWithCleanupFailure } from "../domain/analysisErrorCleanup.js"; import { analysisErrorWithCleanupFailure } from "../domain/analysisErrorCleanup.js";
@@ -119,7 +120,7 @@ const manifestSchema = z.strictObject({
decompiled: z.int().nonnegative(), decompiled: z.int().nonnegative(),
failed: z.int().nonnegative(), failed: z.int().nonnegative(),
}), }),
created_at: z.string().datetime(), created_at: utcDateTimeSchema,
}); });
/** Immutable description of one published export, written once at publish. */ /** Immutable description of one published export, written once at publish. */
+27 -2
View File
@@ -49,7 +49,7 @@ cliTest.for([false, true])(
{ {
env: { env: {
...process.env, ...process.env,
NODE_OPTIONS: "--max-old-space-size=256", NODE_OPTIONS: `--max-old-space-size=256 --import=${probe}`,
REA_LOG_LEVEL: "silent", REA_LOG_LEVEL: "silent",
}, },
}, },
@@ -71,7 +71,32 @@ cliTest.for([false, true])(
child.once("error", reject); child.once("error", reject);
child.once("close", () => resolve()); child.once("close", () => resolve());
}); });
child.stdin.end(`[${"[],".repeat(1_400_000)}[]]`); await expect
.poll(() => /REA_TEST_STDIN_READ (\d+)/u.test(stderr), {
timeout: 10_000,
})
.toBe(true);
const heapSizeLimit = Number(
/REA_TEST_STDIN_READ (\d+)/u.exec(stderr)?.[1],
);
// Exceed the child's whole heap, independently of startup allocations or
// when V8 last collected. Reuse the production owner's expansion estimate.
let lower = 0,
upper = heapSizeLimit;
while (lower < upper) {
const middle = Math.floor((lower + upper) / 2);
if (
nativeJsonParseRequiredBytes(middle * 3 + 4, snapshot) > heapSizeLimit
)
upper = middle;
else lower = middle + 1;
}
const input = `[${"[],".repeat(lower)}[]]`;
expect(Buffer.byteLength(input)).toBeLessThanOrEqual(8 * 1024 * 1024);
expect(
nativeJsonParseRequiredBytes(Buffer.byteLength(input), snapshot),
).toBeGreaterThan(heapSizeLimit);
child.stdin.end(input);
expect(await waitForExit(child, 10_000), stderr).toBe(true); expect(await waitForExit(child, 10_000), stderr).toBe(true);
await closed; await closed;
if (writeFailure !== undefined) if (writeFailure !== undefined)
@@ -64,6 +64,43 @@ const firstClaim = (input: CompletionVerifierReport) => {
}; };
describe("completion ledger generation", () => { describe("completion ledger generation", () => {
it.each(["verified", "unavailable"] as const)(
"preserves minute-precision UTC in %s verifier lineage",
(status) => {
const input = report();
const observedAt = "2026-10-09T04:00Z";
const processLineage =
status === "verified"
? {
...input.verifier_run.process_lineage,
status: "verified" as const,
observed_at: observedAt,
descendants: [],
}
: {
status: "unavailable" as const,
observed_at: observedAt,
launcher_pid: input.verifier_run.verifier_pid,
launcher_parent_pid: input.verifier_run.parent_pid,
process_group_id: null,
reason: "Lineage observation unavailable",
};
const preserved = {
...input,
verifier_run: {
...input.verifier_run,
process_lineage: processLineage,
},
};
expect(
completionVerifierReportSchema.parse(preserved).verifier_run
.process_lineage.observed_at,
).toBe(observedAt);
expect(generate(preserved)).toEqual(generate(input));
},
);
it("separates verifier outcomes while preserving immutable commitments", () => { it("separates verifier outcomes while preserving immutable commitments", () => {
const generated = generate(); const generated = generate();
@@ -0,0 +1,44 @@
import { Ajv2020 } from "ajv/dist/2020.js";
import { describe, expect, it } from "vitest";
import { z } from "zod";
import { webPageInspectionSchema } from "../../../src/domain/browserObservationSchemas.js";
import { webObservationSessionSchema } from "../../../src/domain/browserSession.js";
import { webScreenshotSchema } from "../../../src/domain/webScreenshot.js";
const fields = [
["screenshot capture", webScreenshotSchema.shape.captured_at],
[
"page inspection start",
webPageInspectionSchema.shape.capture_window.shape.started_at,
],
[
"session observation end",
webObservationSessionSchema.shape.window.shape.ended_at,
],
] as const;
describe.each(fields)("%s timestamp compatibility", (_name, schema) => {
const advertised = z.toJSONSchema(schema, { target: "draft-2020-12" });
const validate = new Ajv2020().compile(advertised);
it.each([
"2026-10-09T04:00Z",
"2026-10-09T04:00:00Z",
"2026-10-09T04:00:00.1234567890123456789Z",
])("preserves the admitted source spelling %s", (value) => {
expect(schema.parse(value)).toBe(value);
expect(validate(value)).toBe(true);
});
it.each([
"2026-10-09T04:00",
"2026-10-09T04:00+02:00",
"2026-10-09T04:00:00-03:00",
"2026-04-31T04:00Z",
"2026-10-09T04:00.5Z",
])("rejects %s in both the canonical and advertised contracts", (value) => {
expect(schema.safeParse(value).success).toBe(false);
expect(validate(value)).toBe(false);
});
});
+5 -1
View File
@@ -1,6 +1,10 @@
import { getHeapStatistics } from "node:v8";
// Observe the production stdin reader without supplying data or closing its pipe. // Observe the production stdin reader without supplying data or closing its pipe.
const iterate = process.stdin[Symbol.asyncIterator]; const iterate = process.stdin[Symbol.asyncIterator];
process.stdin[Symbol.asyncIterator] = function (...args) { process.stdin[Symbol.asyncIterator] = function (...args) {
process.stderr.write("REA_TEST_STDIN_READ\n"); process.stderr.write(
`REA_TEST_STDIN_READ ${getHeapStatistics().heap_size_limit}\n`,
);
return iterate.apply(this, args); return iterate.apply(this, args);
}; };