The largest file, 43 query sites, now with none of its reads untyped. Typed sites across the backend go from 22 to 43.
CustomerRecord extends the existing CustomerRow rather than restating it, because that is the relationship that actually holds. CustomerRow was already there and is not a table row — it is the subset safe to return to the customer, written that way so adding a column could not silently start being echoed back by a ...c downstream. The full row read by SELECT * is that subset plus seven fields that are deliberately not on it, password_hash among them. Extending keeps the two connected: adding a column to the table means adding it to CustomerRecord and deciding at that moment whether it belongs in CustomerRow, which is exactly the decision the older comment is about.
The column list came from the live schema rather than from reading migrations, since the migrations are additive and reconstructing the current shape from six files invites getting a nullability wrong.
Typing the data export surfaced something worth a decision, and it is recorded in the code rather than quietly changed. GET /me/export runs SELECT * FROM orders and sends every column verbatim, including raw_event — the processor's entire capture payload. That is defensible for a GDPR export, since it is the customer's own transaction, but it is a decision rather than an accident, and it is now visible in a type instead of hidden behind any. The order-history route two functions above deliberately selects six named columns instead, which is the contrast that makes the export's behaviour worth confirming. No behaviour changed here; #159 is about types.
Nullability follows the schema rather than optimism: orders.amount_cents, status, item_id, customer_id and checkout_id are all nullable in Postgres, and customers.first_name and last_name are nullable despite registration requiring them, because customers who registered while the field was optional genuinely have none.
Verified: tsc clean, and the full integration suite passes 238/238 across 17 suites.
The largest file, 43 query sites, now with none of its reads untyped. Typed sites across the backend go from 22 to 43.
CustomerRecord extends the existing CustomerRow rather than restating it, because that is the relationship that actually holds. CustomerRow was already there and is not a table row — it is the subset safe to return to the customer, written that way so adding a column could not silently start being echoed back by a `...c` downstream. The full row read by `SELECT *` is that subset plus seven fields that are deliberately not on it, password_hash among them. Extending keeps the two connected: adding a column to the table means adding it to CustomerRecord and deciding at that moment whether it belongs in CustomerRow, which is exactly the decision the older comment is about.
The column list came from the live schema rather than from reading migrations, since the migrations are additive and reconstructing the current shape from six files invites getting a nullability wrong.
Typing the data export surfaced something worth a decision, and it is recorded in the code rather than quietly changed. `GET /me/export` runs `SELECT * FROM orders` and sends every column verbatim, including raw_event — the processor's entire capture payload. That is defensible for a GDPR export, since it is the customer's own transaction, but it is a decision rather than an accident, and it is now visible in a type instead of hidden behind `any`. The order-history route two functions above deliberately selects six named columns instead, which is the contrast that makes the export's behaviour worth confirming. No behaviour changed here; #159 is about types.
Nullability follows the schema rather than optimism: orders.amount_cents, status, item_id, customer_id and checkout_id are all nullable in Postgres, and customers.first_name and last_name are nullable despite registration requiring them, because customers who registered while the field was optional genuinely have none.
Verified: tsc clean, and the full integration suite passes 238/238 across 17 suites.
Refs #159
bermudalamb
self-assigned this 2026-08-24 13:23:02 -05:00
bermudalamb
added this to the Code Quality and Hardening 2 project 2026-08-24 13:23:02 -05:00
The largest file, 43 query sites, now with none of its reads untyped. Typed sites across the backend go from 22 to 43.
CustomerRecord extends the existing CustomerRow rather than restating it, because that is the relationship that actually holds. CustomerRow was already there and is not a table row — it is the subset safe to return to the customer, written that way so adding a column could not silently start being echoed back by a `...c` downstream. The full row read by `SELECT *` is that subset plus seven fields that are deliberately not on it, password_hash among them. Extending keeps the two connected: adding a column to the table means adding it to CustomerRecord and deciding at that moment whether it belongs in CustomerRow, which is exactly the decision the older comment is about.
The column list came from the live schema rather than from reading migrations, since the migrations are additive and reconstructing the current shape from six files invites getting a nullability wrong.
Typing the data export surfaced something worth a decision, and it is recorded in the code rather than quietly changed. `GET /me/export` runs `SELECT * FROM orders` and sends every column verbatim, including raw_event — the processor's entire capture payload. That is defensible for a GDPR export, since it is the customer's own transaction, but it is a decision rather than an accident, and it is now visible in a type instead of hidden behind `any`. The order-history route two functions above deliberately selects six named columns instead, which is the contrast that makes the export's behaviour worth confirming. No behaviour changed here; #159 is about types.
Nullability follows the schema rather than optimism: orders.amount_cents, status, item_id, customer_id and checkout_id are all nullable in Postgres, and customers.first_name and last_name are nullable despite registration requiring them, because customers who registered while the field was optional genuinely have none.
Verified: tsc clean, and the full integration suite passes 238/238 across 17 suites.
Refs #159
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The largest file, 43 query sites, now with none of its reads untyped. Typed sites across the backend go from 22 to 43.
CustomerRecord extends the existing CustomerRow rather than restating it, because that is the relationship that actually holds. CustomerRow was already there and is not a table row — it is the subset safe to return to the customer, written that way so adding a column could not silently start being echoed back by a
...cdownstream. The full row read bySELECT *is that subset plus seven fields that are deliberately not on it, password_hash among them. Extending keeps the two connected: adding a column to the table means adding it to CustomerRecord and deciding at that moment whether it belongs in CustomerRow, which is exactly the decision the older comment is about.The column list came from the live schema rather than from reading migrations, since the migrations are additive and reconstructing the current shape from six files invites getting a nullability wrong.
Typing the data export surfaced something worth a decision, and it is recorded in the code rather than quietly changed.
GET /me/exportrunsSELECT * FROM ordersand sends every column verbatim, including raw_event — the processor's entire capture payload. That is defensible for a GDPR export, since it is the customer's own transaction, but it is a decision rather than an accident, and it is now visible in a type instead of hidden behindany. The order-history route two functions above deliberately selects six named columns instead, which is the contrast that makes the export's behaviour worth confirming. No behaviour changed here; #159 is about types.Nullability follows the schema rather than optimism: orders.amount_cents, status, item_id, customer_id and checkout_id are all nullable in Postgres, and customers.first_name and last_name are nullable despite registration requiring them, because customers who registered while the field was optional genuinely have none.
Verified: tsc clean, and the full integration suite passes 238/238 across 17 suites.
Refs #159