mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 23:16:46 +00:00
feat(server): gate writes outside a transaction with lint:tx
The rule that a multi-statement write runs in uow.transactional was written down but nothing held it. scripts/tx-writes.mjs reads every class under src/ with the TypeScript parser and counts, per method, the writes it issues outside a transactional callback: repository calls that build an INSERT, UPDATE or DELETE, calls to methods that write (on the class itself or on a constructor-injected one), and a transaction holding writes as one. A write in a loop counts twice, of two branches only the larger counts, local helpers count where they are called, and a helper whose every call sits inside a transaction is not reported. --explain lists the line of every call that counted. Today's 102 reported methods sit in scripts/tx-writes-baseline.json and may only shrink; most are syncs with network I/O between their writes, plugin lifecycle steps and adapters calling two service writes. A new method fails CI, and an entry above what its method holds fails until --update lowers it, the same ratchet as lint:size and lint:boundaries. Two JSDoc tags mark the deliberate cases with their reason in the tag: @txStandalone for a write that stands on its own whatever the caller does (the audit row, notification sends, the booking price mirror, the assignment journey refresh, the quota retention purge), @txIndependent for writes complete one by one on purpose (the OAuth token retention sweep, the notification claim and its release). Runs in CI after lint:boundaries; documented in server/CLAUDE.md, the root CLAUDE.md and the wiki's dev setup page.
This commit is contained in:
@@ -352,6 +352,9 @@ export class OauthTokensRepository extends TrekRepository<OauthTokens> {
|
||||
* live chain is what replay detection walks. Each pass removes the current
|
||||
* leaves, so a whole expired chain goes over a few passes; `maxPasses`
|
||||
* bounds a chain that is still growing. Returns the number deleted.
|
||||
*
|
||||
* @txIndependent a retention sweep: every pass leaves a consistent table, and the
|
||||
* next sweep picks up what a failed one left.
|
||||
*/
|
||||
async deleteExpiredBefore(cutoffIso: string, maxPasses = 50): Promise<number> {
|
||||
let total = 0;
|
||||
|
||||
Reference in New Issue
Block a user