11 KiB
Active conventions
This document captures the mandatory naming and structure conventions for the Active framework codebase. Every new module, type, constant, and file must comply. Existing code that drifts from these rules is treated as audit debt and migrated module by module.
The goal is single-rule homogeneity: a developer learns the convention once and applies it everywhere. No exceptions without an explicit decision recorded here.
1. Module identifier — full English word
Every artifact and lib that pairs with one is identified by a stable full English word that doubles as filesystem name, alias and canonical wire identifier:
| Folder | Alias | Wire / value |
|---|---|---|
arts/active-app/, libs/active-app/ |
$active-app |
'app' |
arts/auth/, libs/auth/, svrs/auth/ |
$auth |
'auth' |
arts/bus/, libs/bus/ |
$bus |
'bus' |
arts/cache/, libs/cache/, svrs/cache/ |
$cache |
'cache' |
arts/connection/ |
$connection |
'connection' |
libs/dom/ |
$libs/dom |
'dom' |
libs/errs/ |
$libs/errs |
'errs' |
arts/frontend/ |
$frontend |
'frontend' |
arts/formats/ (+ currency/, numbers/, units/, dates/) |
$formats |
'formats' |
arts/http/, libs/http/ |
$http |
'http' |
arts/lang/, libs/lang/ |
$lang |
'lang' |
arts/logger/, libs/logger/ |
$logger |
'logger' |
arts/permissions/, libs/permissions/, svrs/permissions/ |
$permissions |
'permissions' |
arts/session/ |
$session |
'session' |
arts/sium/ |
$sium |
'sium' (proper name of the validator) |
arts/storage/ |
$storage |
'storage' |
arts/timer/, libs/timer/ |
$timer |
'timer' |
Special cases
-
active-appuses a hyphen in the alias because$appis a reserved namespace in SvelteKit ($app/stores,$app/navigation). The constants and class names still useApp/APP_*; theactive-prefix only appears in filesystem and alias. -
langandsiumare kept as-is.langis the standard HTML/i18n term (<html lang="…">);siumis the proper name of the validator (likezod,valibot). They are not abbreviations. -
errsis the framework's own error system. Kept short intentionally because it sits at the foundation; lowering it to'errs'keeps the wire format compact for codes like'errs::format_invalid'.
What this replaces
Earlier the codebase used 4-letter abbreviations (buss, cach,
conn, sess, stor, timr, logr, aapp, fmts, fend,
perm, curr, unts, nums). That convention has been removed:
abbreviations created cognitive load with no real benefit.
2. Constant naming — <MOD>_<CATEGORY>_<NAME>
Every categorical module-level constant follows the pattern:
<MOD>_<CATEGORY>_<NAME>
-
<MOD>— the module's identifier in caps. For most modules this matches the filesystem (STORAGE_*,BUS_*,CACHE_*,LOGGER_*,TIMER_*,CONNECTION_*,SESSION_*,FORMATS_*,FRONTEND_*,HTTP_*,LANG_*,AUTH_*,SIUM_*,DOM_*,ERRS_*).Exception: the
permissionsmodule usesPERMISSION_*(singular) because the constants describe the concept ("a permission effect", "a permission decision"), not the module collection. TheSis reserved for the folder/alias/wire which group the system as a whole.Exception: the
active-appmodule usesAPP_*(without theactive-prefix) because theactive-prefix is purely a disambiguator forced by SvelteKit's reserved$appnamespace. -
<CATEGORY>— what kind of constant it is. Picked from the fixed vocabulary in section 3. -
<NAME>— the specific value's identifier inUPPER_SNAKE_CASE.
Examples
// good
BUS_DIAGNOSTIC_EVENTS
BUS_DEFAULT_MAX_LISTENERS_PER_EVENT
BUS_ERR_DISPOSED
SESSION_EVENT_LIFECYCLE_ADOPTED
PERMISSION_METHOD_CHECK
TIMER_STATE_PENDING
CONNECTION_REASON_AUTH_FAILED
CACHE_LIMIT_DEFAULT_MAX_ENTRIES
STORAGE_MODULE // value: 'storage'
APP_MODULE // value: 'app'
PERMISSION_MODULE // value: 'permissions'
// bad
BUSS_DIAGNOSTIC_EVENTS // 4-letter alias is dead (rule 1)
DEFAULT_TIMER_SCOPE_SEPARATOR // category before module (rule 2)
PERMISSIONS_EFFECT_ALLOW // wrong shape: concept is singular
AUTO_REAUTH_USER_IDENTITY_CHANGE // missing module prefix (rule 1)
Sub-categories
When a category needs internal hierarchy, append additional segments
in the <NAME> part, not in <CATEGORY>:
// good
BUS_LISTENER_ERROR_MODE_THROW
SESSION_EVENT_LIFECYCLE_ADOPTED
SESSION_EVENT_LIFECYCLE_REVOKED
PERMISSION_DEFAULT_REMOTE_FAILURE_BACKOFF_MS
// bad
BUS_LISTENER_ERR_MODE_THROW // category split across underscores
3. Category vocabulary — fixed list
Only these category tokens are allowed. Adding a new category is a deliberate decision recorded in this document, not an ad-hoc choice inside a module.
| Category | Use |
|---|---|
MODULE |
The module's identifier as a string constant. Single source of truth for the module's identity. Used wherever the module's identifier is needed: logger category, error message prefix, event scope, etc. Every module declares exactly one <MOD>_MODULE = '<value>'. |
ERR |
Error codes (ErrCode values from $libs/errs) |
EVENT |
Bus or lifecycle event names (string discriminators) |
DIAGNOSTIC_EVENTS |
Event values published through Logger (catalog object) |
METHOD |
Method labels for error messages (ensureLive(METHOD)) |
STATE |
Tagged-union state discriminators |
STATUS |
Tagged-union status discriminators |
KIND |
Tagged-union kind discriminators |
REASON |
Tagged-union reason discriminators |
TYPE |
Tagged-union type discriminators (when none of the above fits) |
MODE |
Mode discriminators (e.g. listener error mode) |
DEFAULT |
Default values for options |
LIMIT |
Maximum / minimum values |
ID_PREFIX |
Prefix used by ID factories |
LOG_MSG |
Log message strings |
ERROR_MSG |
Error message strings (technical, dev-facing). Must use a template referencing <MOD>_MODULE for the prefix, never a hardcoded literal: `[${MOD_MODULE}] ...`. |
ERROR_MESSAGES |
Catalogue object (<MOD>_ERROR_MESSAGES: ErrorMessages) indexed by ErrCode. Static strings or (...args) => string factories. |
CONTEXT_KEY |
Svelte context keys |
Categories that are not in this list (e.g. AUTO_REAUTH,
AUTO_INVALIDATE, BACKOFF, BUFFER_POLICY, CHANNEL_STATE) must
fold into one of the above. Examples:
CONNECTION_AUTO_REAUTH_*→CONNECTION_REASON_AUTO_REAUTH_*if the value drives a "why this happened" branch, orCONNECTION_MODE_AUTO_REAUTH_*if it configures behavior.CACHE_AUTO_INVALIDATE_*→CACHE_MODE_AUTO_INVALIDATE_*.CONNECTION_BUFFER_POLICY_*→CONNECTION_MODE_BUFFER_*.
When in doubt, propose the addition here before introducing it.
4. Error codes (ErrCode) — special case under rule 2
Error codes from $libs/errs follow rule 2 with category ERR. Each
module declares one <MOD>_ERR seed and builds the individual codes
from it via errCode(parent, segment) so the module string never
appears as a literal in error declarations:
import { errCode, moduleSeed, type ErrCode, type ModuleSeed } from '$libs/errs';
export const BUS_ERR: ModuleSeed = moduleSeed('bus'); // 'bus::'
export const BUS_ERR_DISPOSED: ErrCode = errCode(BUS_ERR, 'disposed'); // 'bus::disposed'
export const BUS_ERR_INVALID_PAYLOAD: ErrCode = errCode(BUS_ERR, 'invalid_payload');
export const BUS_ERR_LISTENER: ErrCode = errCode(BUS_ERR, 'listener'); // 'bus::listener'
export const BUS_ERR_LISTENER_FAILED: ErrCode = errCode(BUS_ERR_LISTENER, 'failed'); // 'bus::listener.failed'
The constant's <NAME> part mirrors the path segments inside the
runtime value: BUS_ERR_LISTENER_FAILED ↔ 'bus::listener.failed'.
The module name lives in exactly one place: moduleSeed('bus').
The :: separator distinguishes module from hierarchy. errCode
picks the right separator automatically — :: after a seed, .
between segments. A helper codeToLangPath(c) converts
'bus::listener.failed' → 'bus.listener.failed' for the i18n
path.
Family matching
A single predicate matches(err, family) from $libs/errs covers
both common cases. family can be:
- A module seed (
BUS_ERR) — matches any error from that module regardless of hierarchy. - An
ErrCode— matches the code itself or any hierarchical descendant within the same module.
matches(err, BUS_ERR_DISPOSED) // exact code
matches(err, BUS_ERR_LISTENER) // any descendant of bus::listener
matches(err, BUS_ERR) // any error declared by bus
Error class names — full English word
Error classes use the full English word of the concept, not the
module's <MOD>_* prefix. The class name is consumer-facing API; the
constant prefix is internal organization:
// good
StorageInvalidTypeError // class
STORAGE_ERR_INVALID_TYPE // constant
BusDisposedError // class
BUS_ERR_DISPOSED // constant
PermissionDeniedError // class — singular concept
PERMISSION_ERR_DENIED // constant — singular prefix
AppAlreadyCreatedError // class — no Active prefix
APP_ERR_ALREADY_CREATED // constant
5. String values inside diagnostic / event constants
Every string value emitted as an event identifier must start with the
module's identifier (rule 1) followed by . and the value-specific
path:
// good — value carries the module scope
export const CONNECTION_DIAGNOSTIC_EVENTS = {
AUTH_FAILED: 'connection.auth_failed',
RECONNECT_EXHAUSTED: 'connection.reconnect_exhausted'
} as const;
// bad — bare value collides across modules
export const CONNECTION_DIAGNOSTIC_EVENTS = {
AUTH_FAILED: 'auth_failed', // collides with auth.* events
LISTENER_THREW: 'listener_threw' // collides with session and timer
} as const;
6. File and folder structure
| Layer | Rule |
|---|---|
libs/<mod>/ |
Pure contracts: types, constants, error classes, helpers. No Svelte runes, no engine state. |
arts/<mod>/ |
Runtime engine + *.svelte.ts active wrappers. May import from $libs/<mod> and from its own files. |
arts/<mod>/index.ts |
Public barrel. Re-exports the artifact's surface. |
arts/<mod>/errors.ts |
Single home for the module's error infrastructure: <MOD>_ERR seed, <MOD>_ERR_* codes, <MOD>_ERROR_MESSAGES catalog, error classes and is*Error guards. Do not split these across consts.ts and errors.ts; everything error-related lives together. |
arts/<mod>/test/ |
Tests for engine and active wrappers. |
svrs/<mod>/ |
Server-side counterparts (cookies, db adapters, route handlers). |
arts/active-app/ |
Composition root. Allowed to import any $<mod>. |
Cross-artifact imports are forbidden (arts/X cannot import
$Y). Sole exception: arts/active-app/.
7. When this document is updated
- A new category in section 3 requires a record-of-decision: who, when, why.
- Module names are frozen: a new artifact picks a name not yet taken; renaming an existing module is a breaking change.
- Drift found in the codebase that violates these rules is filed as an audit finding and resolved in a dedicated commit.
Last updated: 2026-05-02 — full English word convention, dropping
the 4-letter alias rule.