Follow-up from the review of #329, which added named connections.
Model.on(name: string) and DB.connection(name: string) take any string. An unknown one raises UnknownConnectionError and lists what is configured — but only when that query runs, which for the nightly sweep the feature exists to isolate means 3am rather than the edit.
That is the same gap the PR argues about elsewhere and closes everywhere it can: on("analitycs") silently running on the hot path is the incident the second pool was configured to prevent, and the runtime refusal is the last line of defence rather than the first.
A declaration-merged interface, in the style the framework already uses for generated surfaces, would make it a type error at the call site:
declare module "gemi/database" {
interface ConnectionNames {
analytics: true
}
}
Subscription.on("analitycs") // ✗ not assignable to "default" | "analytics"
Open questions worth settling before building it:
- Where the names come from.
app/config/database.ts is application code, not a generated artifact, so nothing emits a type from it today. Either the app declares the interface by hand (cheap, and drifts from the config), or gemi check / the generator learns to read the config (accurate, and adds a build step for what is a handful of strings).
- The fallback has to stay open. A name computed at runtime — a tenant router, a test harness — must still be expressible, so the parameter type wants to be
keyof ConnectionNames | (string & {}) or an explicit escape hatch rather than a closed union.
- Empty by default. With no declarations the interface is empty, and
keyof on it must degrade to string rather than to never, or every existing call site becomes an error.
Not a blocker for #329: the runtime refusal is correct, loud and tested. This is about moving the failure from 3am to the keyboard.
Follow-up from the review of #329, which added named connections.
Model.on(name: string)andDB.connection(name: string)take any string. An unknown one raisesUnknownConnectionErrorand lists what is configured — but only when that query runs, which for the nightly sweep the feature exists to isolate means 3am rather than the edit.That is the same gap the PR argues about elsewhere and closes everywhere it can:
on("analitycs")silently running on the hot path is the incident the second pool was configured to prevent, and the runtime refusal is the last line of defence rather than the first.A declaration-merged interface, in the style the framework already uses for generated surfaces, would make it a type error at the call site:
Open questions worth settling before building it:
app/config/database.tsis application code, not a generated artifact, so nothing emits a type from it today. Either the app declares the interface by hand (cheap, and drifts from the config), orgemi check/ the generator learns to read the config (accurate, and adds a build step for what is a handful of strings).keyof ConnectionNames | (string & {})or an explicit escape hatch rather than a closed union.keyofon it must degrade tostringrather than tonever, or every existing call site becomes an error.Not a blocker for #329: the runtime refusal is correct, loud and tested. This is about moving the failure from 3am to the keyboard.