timescale.info
Sign inSign up

How OpenChronology was built

Every merged pull request of openchronology/timeline-app, its first week of commits and its releases; and before them the 2020 PureScript editor, from snapshots of its deleted repositories in the Wayback Machine. Written to this server through the HTTP API.

327 moments · 2020-03-09T00:15:08Z CE – 2026-11-02T20:11:21Z CE · by @athan · @athan

#200 · Fixes from the #193 triage list

  • timeline-app
  • pull request

Closes #193. Every item was checked against the code first. Each item below says what was found and what changed; items that turned out fine are listed with the reason. ## Behaviour - **Errors mapped to 400 (http.mjs):** a 400 that comes from a TypeError, RangeError or SyntaxError rather than an `HttpError` is now logged. It stays a 400, since some clients rely on it. A malformed request path (`//`) is now an explicit 400. - **prepareNow / SKIP LOCKED: real.** A request refused with "still being prepared" now starts the preparation again. Before, a skipped copy waited for the next server start. - **Desktop generation race: real.** Opening a file returned a *predicted* generation. A save or restore finishing before `desktop_accept_open` left the new file refusing every request until it was reopened. `desktop_accept_open` now returns the real generation, and the editor uses it. - **och-convert instruction budget: real.** `.och` writes failed above about 9,000 moments with `rational_index … (9)`. The budget is now refilled for each statement. The overview index is filled with one statement per time instead of one for all of them; that statement alone ran out. 30,000 moments now convert. Beyond about 45–50k the converter hits its 20 s CPU limit instead: filed as #199. - **Auth throttle: real.** Past 10,000 keys it now forgets the oldest key instead of refusing everyone. The database rate limit still counts every key. - **Email change: partly.** Confirmation already gave a clean 400. A change to an address another account holds now sends no link, which could never be confirmed; the answer stays the generic one, so nothing leaks. - **Comparison cap: real.** The dashboard now uses the account's limit and says it ("2 selected for comparison (up to 4)"). The platform's "sign in to compare up to N" uses the signed-in limit, not 8. - **Avatar preview: real.** `crossOrigin="anonymous"` is removed. Nothing reads the pixels, and it hid images from hosts without CORS headers. `referrerPolicy="no-referrer"` stays. - **Profile tag links: not a bug.** They go to the dashboard's tag search (`/?tag=…`) on purpose. They no longer carry the profile's favorites page along. - **Re-enabling an account: real.** It now lifts the account's restricted moderation case, deciding a pending review as lifted, as lifting the case would. - **Labelled cases outside the unique index: not a bug.** That's by design: a labelled case is settled, and `lift()` expects several. - **oc_events: partly.** There is no UPDATE in it, but its node and moment lookups didn't check `FOUND`. It now raises "Broken timeline index", like `oc_overview_v2`. - **save()'s 403:** now "Write access is required to save this timeline." - **Custom time tokenizer: partly.** `x +1` was a documented syntax error, but a trap. After a value, `+1` now reads as `+ 1`. `x -1` says there is no subtraction operator and points to `api.sub`. A sign still starts a number where no value precedes it (`return -1`). - **calendar-picker: partly.** Nothing invalid was committed, but `1.5` gave a raw BigInt error and emptied the day grid. Each field now has to be a whole number in range, with a clear message. ## Performance - **Indexes added:** - `user_id` on oc_sessions, oc_members, oc_device_logins, oc_auth_challenges and oc_email_tokens; - expiry indexes on oc_mail_outbox, oc_mfa_setups and oc_age_refusals; - oc_registrations by username and by email; - the revision references history cleanup searches: forks' bases, pull requests' base, source and merged revisions, and revisions' snapshots. - **Timeline heads:** these change on every save, and an index would end the HOT updates `fillfactor=50` keeps, so they are read once (`r.id IN (SELECT head_revision_id …)`) in `STILL_READ`, `removeHistory` and retention. - **`event_text_stale`: left without an index.** It is read once at startup and flips on every save. - **workspace.rs `related()`: not a bug.** SQLite answers its ORs from both indexes (`MULTI-INDEX OR`,…

Sources

Open #200 · Fixes from the #193 triage list in the timeline