fix(replay): preserve Date call and explicit constructor semantics (#512)

Signed-off-by: RudyCelekli <47457359+rudycelekli@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
This commit is contained in:
Rudy Mizrahi Celekli 2026-10-05 03:02:47 -04:00 committed by GitHub
parent 5b28a8b331
commit 9975bd23cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 48 additions and 3 deletions

View File

@ -276,8 +276,13 @@ const deterministicContext = (request: WorkerRequest, caseIndex: number) => {
};
const epoch = Date.parse(request.determinism.clockIso);
class ReplayDate extends Date {
constructor(...arguments_: [] | [string | number]) {
super(arguments_.length === 0 ? epoch : arguments_[0]);
constructor(...arguments_: unknown[]) {
super(epoch);
if (arguments_.length > 0) {
const date: unknown = Reflect.construct(Date, arguments_, new.target);
if (!types.isDate(date)) throw new TypeError("Invalid Date instance");
return date;
}
}
static override now(): number {
return epoch;
@ -286,7 +291,12 @@ const deterministicContext = (request: WorkerRequest, caseIndex: number) => {
const replayMath: Math = Object.create(Math);
Object.defineProperty(replayMath, "random", { value: random });
return createContext(
{ Date: ReplayDate, Math: replayMath },
{
Date: new Proxy(ReplayDate, {
apply: () => new ReplayDate().toString(),
}),
Math: replayMath,
},
{
name: "rea-controlled-replay",
codeGeneration: { strings: false, wasm: false },

View File

@ -226,6 +226,41 @@ describe("disposable JavaScript replay worker", () => {
});
});
describe("deterministic replay Date compatibility", () => {
it.each([
["new Date(2020, 0, 2).getFullYear()", 2020],
["new Date(2020, 0, 2, 3, 4, 5, 6).getMilliseconds()", 6],
["typeof Date()", "string"],
["Date() === new Date().toString()", true],
["new Date().toISOString()", "2000-01-01T00:00:00.000Z"],
["Date.now()", 946684800000],
["new Date(0).getTime()", 0],
[
"new Date('2020-01-02T00:00:00Z').toISOString()",
"2020-01-02T00:00:00.000Z",
],
["new Date() instanceof Date", true],
[
"(() => { class ChildDate extends Date {} const date = new ChildDate(2020, 0, 2); return [date instanceof ChildDate, date instanceof Date, date.getFullYear(), date.constructor === ChildDate]; })()",
[true, true, 2020, true],
],
["Date.UTC(2020, 0, 2)", 1577923200000],
])("preserves %s", async (expression, expected) => {
const workerRequest = await request("parser.mjs", "esm", "default", []);
workerRequest.left.modules[0] = {
alias: "entry",
format: "esm",
dependencies: {},
source: `export default function () { return ${expression}; }`,
};
const result = await runWorker(workerRequest);
expect(result.code).toBe(0);
expect(JSON.parse(result.stdout)).toMatchObject({
left: [{ outcome: "return", value: expected }],
});
});
});
describe("replay array projection", () => {
it.each([
["Array(3)", [null, null, null]],