Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit af7c34c3c944660cd902009b395e9ab6d560b05c
parent baa444159ba09c567401f2df981ba35d1cd8f714
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Wed, 23 Sep 2026 19:23:17 -0400

settings: zod seams for workers, channelPriority and autoQueue, kept out of client bundles

one-core phase 3 slice 4a, commit 2. `workersSchema`, `channelPrioritySchema`
and `autoQueueSchema` wrap the existing sanitizers (`sanitizeWorkers`,
`sanitizeChannelPriority(value: unknown)`, `sanitizeAutoQueue`), which keep
their homes, names and signatures.

The seams live in lib/settingsFieldSchemas.ts rather than beside each
sanitizer: workers.ts, channelPriority.ts and (via jobs/autoQueuePolicy)
autoQueueSchema.ts are value-imported by "use client" forms, and a zod
import there would ship zod to the browser. Commit 1's seam in
autoQueueSchema.ts moves here for the same reason.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
Mcommon/jobs/autoQueuePolicy.ts | 1-
Mcommon/lib/autoQueueSchema.ts | 19++++++-------------
Acommon/lib/settingsFieldSchemas.ts | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 63 insertions(+), 14 deletions(-)

diff --git a/common/jobs/autoQueuePolicy.ts b/common/jobs/autoQueuePolicy.ts @@ -40,7 +40,6 @@ export { AUTO_QUEUE_MODES, AUTO_QUEUE_ORDERS, AUTO_QUEUE_MAX_WORKERS_MAX, - autoQueueSchema, defaultAutoQueue, defaultAutoQueuePolicy, sanitizeAutoQueue, diff --git a/common/lib/autoQueueSchema.ts b/common/lib/autoQueueSchema.ts @@ -16,8 +16,13 @@ // // It never throws. Every function is total over `unknown`, because its input is // a file an operator may have hand-edited and a settings read may not fail. +// +// NO ZOD HERE, deliberately. Four `"use client"` forms import constants from +// this module through `jobs/autoQueuePolicy` (LadderRung reads +// AUTO_QUEUE_MODES), so anything this file imports can land in a browser +// bundle. The zod field seam that wraps `sanitizeAutoQueue` lives in +// `lib/settingsFieldSchemas.ts`, which only server code imports. -import { z } from "zod"; import { type AutoQueueGroup, type AutoQueueKind, @@ -277,15 +282,3 @@ export function sanitizeAutoQueue(value: unknown): AutoQueueSettings { ) as AutoQueueSettings; } -// THE LANE POLICIES AS ONE SCHEMA FIELD. -// -// `sanitizeAutoQueue` above IS the parser — it is total, it never throws, and it -// is what fifteen hundred lines of tests pin. The schema is the seam that lets -// `lib/settingsSchema.ts` compose it with the other thirty fields without -// knowing any of that: zod supplies the plumbing (a key, a strip, a `.catch`), -// the sanitizer supplies the arithmetic. Same shape as `workersSchema` and -// `channelPrioritySchema`. -export const autoQueueSchema = z - .unknown() - .catch(undefined) - .transform((value): AutoQueueSettings => sanitizeAutoQueue(value)); diff --git a/common/lib/settingsFieldSchemas.ts b/common/lib/settingsFieldSchemas.ts @@ -0,0 +1,57 @@ +// THE ZOD SEAMS FOR THE THREE SANITIZERS THAT LIVE OUTSIDE settings.ts. +// +// `settings.json` has three blocks whose parsers have homes of their own and +// callers of their own: `workers` (lib/workers.ts — the pool and the settings +// form), `channelPriority` (lib/channelPriority.ts — storageWatch and the +// channels actions call `sanitizeChannelPriority(value: unknown)` directly) and +// `autoQueue` (lib/autoQueueSchema.ts — the picker's tests pin it). Each keeps +// its home and its signature. What this file adds is ONE schema per block, so +// `lib/settingsSchema.ts` can compose them with the other twenty-eight fields +// the same way it composes everything: zod supplies the plumbing (the key, the +// strip of unknown siblings, a `.catch` that makes a field total), the existing +// sanitizer supplies the arithmetic. Nothing was re-implemented, so nothing +// could drift. +// +// WHY A SEPARATE FILE, and not a `workersSchema` export beside each sanitizer: +// all three homes are imported AS VALUES by `"use client"` forms +// (WorkersConfigForm, ChannelTierSelect, LadderRung, …). A module-level +// `import { z } from "zod"` there would put zod in a browser bundle for no +// reason. Only server code imports this file — lib/settingsSchema.ts, which +// itself is only reached through lib/settings.ts (node:fs at module scope). + +import { z } from "zod"; +import { sanitizeWorkers, type Worker } from "./workers"; +import { + sanitizeChannelPriority, + type ChannelPriority, +} from "./channelPriority"; +import { sanitizeAutoQueue } from "./autoQueueSchema"; +import type { AutoQueueSettings } from "./autoQueueTypes"; + +// A settings FIELD: any JSON value in, a legal value out, never a throw. +// +// `.catch(undefined)` is today's try-and-continue: whatever zod could object +// to becomes `undefined`, which every coercion below treats as "absent → the +// default". `z.unknown()` accepts a missing key, and zod 4 still runs the +// transform for it and emits the key — so an absent field is DEFAULTED, not +// dropped, exactly as `defaults()` used to fill it. +// +// There is deliberately no `.default()` anywhere: a default applies only to +// `undefined`, and every coercion here already decides that case itself — the +// place "a zero limit is a hold" lives is inside the clamp, not in a fallback +// zod would apply around it. +export function settingsField<T>(coerce: (value: unknown) => T) { + return z.unknown().catch(undefined).transform((value): T => coerce(value)); +} + +export const workersSchema = settingsField( + (value): Worker[] => sanitizeWorkers(value), +); + +export const channelPrioritySchema = settingsField( + (value): ChannelPriority => sanitizeChannelPriority(value), +); + +export const autoQueueSchema = settingsField( + (value): AutoQueueSettings => sanitizeAutoQueue(value), +);