You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
291 lines
11 KiB
291 lines
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-app`** uses a hyphen in the alias because `$app` is a
|
|
reserved namespace in SvelteKit (`$app/stores`, `$app/navigation`).
|
|
The constants and class names still use `App` / `APP_*`; the
|
|
`active-` prefix only appears in filesystem and alias.
|
|
|
|
- **`lang`** and **`sium`** are kept as-is. `lang` is the standard
|
|
HTML/i18n term (`<html lang="…">`); `sium` is the proper name of
|
|
the validator (like `zod`, `valibot`). They are not abbreviations.
|
|
|
|
- **`errs`** is 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 `permissions` module uses `PERMISSION_*` (singular)
|
|
because the constants describe the concept ("a permission effect",
|
|
"a permission decision"), not the module collection. The `S` is
|
|
reserved for the folder/alias/wire which group the system as a
|
|
whole.
|
|
|
|
**Exception**: the `active-app` module uses `APP_*` (without the
|
|
`active-` prefix) because the `active-` prefix is purely a
|
|
disambiguator forced by SvelteKit's reserved `$app` namespace.
|
|
|
|
- **`<CATEGORY>`** — what kind of constant it is. Picked from the
|
|
fixed vocabulary in section 3.
|
|
- **`<NAME>`** — the specific value's identifier in
|
|
`UPPER_SNAKE_CASE`.
|
|
|
|
### Examples
|
|
|
|
```ts
|
|
// 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>`:
|
|
|
|
```ts
|
|
// 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, or
|
|
`CONNECTION_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:
|
|
|
|
```ts
|
|
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.
|
|
|
|
```ts
|
|
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:
|
|
|
|
```ts
|
|
// 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:
|
|
|
|
```ts
|
|
// 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.
|