You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(#14078): record the ruling in the isoFromValidDate docblocks + three patch changesets
The three copies of the sibling helper each promised "when #14078 rules, this
helper collapses into the shared spelling". #14078 ruled, and the collapse is
NOT mechanical: the two spellings differ across the whole non-Date domain and
one of the six call sites is a required z.string().datetime() field for which
neither ruled terminal value validates. Filed as #16422; every docblock and
neutrality pin that called #14078 "open" now says what was ruled and what was
not.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
Serve an Invalid `Date` from a driver instead of raising `RangeError` at two metadata read seams.
6
+
7
+
`canonicalIsoInstant` (`sys-metadata-repository.ts`) and the `occurredAt` arm inside `auditMetaItem` (`protocol.ts`) both reached `value.toISOString()` for any `Date`. That call raises `RangeError: Invalid time value` for the one `Date` whose time value is `NaN`, so a single bad row answered **500** on a read path — where the spelling these repairs replaced, `String(value)`, had served a visibly-wrong field the caller could see and report.
8
+
9
+
The shape is measured, not hypothetical: mysql2 3.23.1 returns a module constant literally named `INVALID_DATE` for a zero `DATETIME`, and postgres-date 1.0.7 builds `new Date(NaN)` for every year in 275760..294276 — a range Postgres itself stores. Legacy imports, hand migrations and a MySQL database shared with another application are all ordinary ways such a row arrives.
10
+
11
+
Both arms now guard on `Number.isNaN(value.getTime())`, and the terminal value is chosen per call site rather than uniformly:
12
+
13
+
-`canonicalIsoInstant` answers `undefined`, so each caller's existing `?? <default>` chain — the branch an absent column already takes — keeps its meaning. Its consumers are machines, and one forwards into a `z.string().datetime()` field that visible text would fail.
14
+
-`auditMetaItem`'s `occurredAt` falls into the `String(...)` arm already beside it, which renders exactly `"Invalid Date"`. `AuditMetaItemResponseSchema.events[].occurredAt` is a required plain `z.string()` read by an operator in Studio's audit tab, so the text satisfies the contract and one bad row no longer blanks the page.
15
+
16
+
Neither answer is a blank: a silent empty value is the shape that hides the producer's bug.
Serve an Invalid `Date` from a driver instead of raising `RangeError` in `DatabaseLoader.stat`.
6
+
7
+
`canonicalIsoInstant` reached `value.toISOString()` for any `Date`, and that call raises `RangeError: Invalid time value` for the one `Date` whose time value is `NaN`. `stat()` is a hot read path — REST `/meta/*`, ObjectQL plan resolution, runtime overlay merges — so one legacy `sys_metadata` row answered **500** where the spelling this repair replaced had served a visibly-wrong value.
8
+
9
+
The shape is measured: mysql2 3.23.1 hands back a constant literally named `INVALID_DATE` for a zero `DATETIME`, and postgres-date 1.0.7 builds `new Date(NaN)` for every year in 275760..294276, which Postgres itself stores.
10
+
11
+
The `Date` arm now guards on `Number.isNaN(value.getTime())` and answers `undefined`, so `stat()`'s own `?? new Date().toISOString()` — the branch an absent column already takes — publishes a parseable `MetadataStats.mtime`. `undefined` rather than visible text is deliberate here: `mtime` is declared `z.string().datetime()`, so the text `"Invalid Date"` would not produce a readable cell, it would produce a zod refusal at the consumer, moving the failure instead of removing it. A blank is excluded for the opposite reason — it hides the producer's bug.
Serve an Invalid `Date` from a driver as visible text instead of raising `RangeError` in the import-job DTO and the CSV export.
6
+
7
+
`canonicalIsoStamp` and `formatCsvCell` both reached `value.toISOString()` for any `Date`, and that call raises `RangeError: Invalid time value` for the one `Date` whose time value is `NaN` — so one bad timestamp column answered **500** on `GET /api/v1/data/import/jobs/:jobId` and aborted a CSV export mid-stream.
8
+
9
+
The shape is measured: mysql2 3.23.1 returns a module constant literally named `INVALID_DATE` for a zero `DATETIME`, and postgres-date 1.0.7 builds `new Date(NaN)` for every year in 275760..294276, a range Postgres itself stores.
10
+
11
+
Both arms now guard on `Number.isNaN(value.getTime())` and render the visible text `"Invalid Date"` — the rendering the spelling they replaced produced. Both are read by a human, and the declared contracts allow it: the four import-job stamps are plain `z.string()` (not `z.string().datetime()`), and a CSV cell has no schema at all. The operator sees a wrong-looking field they can report, rather than an error naming no row.
12
+
13
+
The CSV arm needs its own guard rather than a fall-through, because the branch below it is `JSON.stringify` and `Date.prototype.toJSON` answers `null` for an Invalid `Date` — the silent blank this change exists to avoid. Both CSV paths land on the guarded arm: with field metadata, `formatDate` rejects an Invalid `Date` and passes the value through unchanged.
0 commit comments