Extracted from alkhub_ts packages/core/operations/ and packages/core/mcp/. - Runtime-agnostic (injected fs/env deps, no Deno globals) - Direct @logtape/logtape import instead of logger wrapper - PendingRequestMap with pubsub-wired call protocol - Peer-dep isolation for MCP adapter (sub-path export) - Schema const naming convention (XSchema + X type alias) - 68 tests passing, build + lint + test all green
2.8 KiB
ADR-004: Schema Const Naming Convention
Status: Accepted Date: 2026-04-30
Context
TypeBox schemas and their inferred types need different names but represent the same concept. For example, the AccessControl schema defines the runtime shape and the AccessControl type provides the TypeScript interface. Using the same name for both creates a naming collision.
Two naming conventions are common in TypeBox codebases:
- Same name —
AccessControlfor both the schema constant and the inferred type (relies on TypeScript's value/type namespace separation) - Different names —
AccessControlSchemafor the schema constant,AccessControlfor the inferred type
Decision
Use the Schema suffix for schema constants. The inferred type uses the bare name.
export const AccessControlSchema = Type.Object({ ... })
export type AccessControl = Static<typeof AccessControlSchema>
export const OperationSpecSchema = Type.Object({ ... })
export type OperationSpec = Static<typeof OperationSpecSchema>
export const ErrorDefinitionSchema = Type.Object({ ... })
export type ErrorDefinition = Static<typeof ErrorDefinitionSchema>
export const OperationDefinitionSchema = Type.Object({ ... })
export interface IOperationDefinition<...> extends OperationSpec<...> { ... }
Rationale
-
No ambiguity —
AccessControlalways refers to the type,AccessControlSchemaalways refers to the runtime schema. No mental overhead to distinguish them. -
TypeScript namespace separation is fragile — while TypeScript technically allows the same name for a value and a type (they occupy different namespaces), this creates confusion when reading code.
const ac: AccessControl = ...— whichAccessControl? The schema or the type? With theSchemasuffix, it's immediately clear. -
Consistent with TypeBox conventions — TypeBox's own
Type.*factory functions produce schema objects. Naming the const with aSchemasuffix aligns with the mental model that these are schema definitions, not data instances. -
Export clarity — in
index.ts, both are exported. Having distinct names meansimport { AccessControlSchema, AccessControl }is immediately clear: one is a runtime value, one is a type. -
Existing pattern — this convention is already used in
OperationDefinitionSchemavsIOperationDefinitionandOperationSpecSchemavsOperationSpecin the codebase.
Consequences
- All schema const names end in
Schema - All type names are the bare concept name (or prefixed with
Ifor interfaces) - This applies to
AccessControlSchema/AccessControl,ErrorDefinitionSchema/ErrorDefinition,OperationContextSchema/OperationContext,OperationSpecSchema/OperationSpec,OperationDefinitionSchema/IOperationDefinition - When adding new schemas, follow this convention:
FooSchemafor the const,Foofor the type