mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
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:
1 parent
b675073b73
commit
e3066c1705
22 files changed
+205
-87
No files matched your search
@@ -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": [
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 ---
|
||||||
|
|||||||
Generated
+4
-4
@@ -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
@@ -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": {
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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.",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
|
||||||
};
|
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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"]),
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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. */
|
||||||
|
|||||||
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
Vendored
+5
-1
@@ -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);
|
||||||
};
|
};
|
||||||
Reference in new issue
Block a user