Package Rules
Internal development standards — file naming, folder organization, code style, and type ownership.
File Naming Rules (Dot Notation)
All source files use dot notation to separate the domain prefix from the concern.
| Pattern | Example | Wrong |
|---|---|---|
| Single-word prefix | http.error.ts | http-error.ts |
| Multi-word prefix | externalService.error.ts | external-service-error.ts |
| Types/interfaces | configManager.type.ts | config-manager-type.ts |
| Utilities | cryptoRandom.helper.ts | crypto-random-helper.ts |
Folder Organization
Max 5 files per folder (excluding index.ts). Max 150 lines per file. Violations must be fixed before merge.
- Every src/ dir organized into related folders
- Every folder gets an index.ts barrel with JSDoc
- Folders may contain subfolders recursively
- camelCase for folder names
- File exceeds 150 lines → split by concern
- Folder exceeds 5 files → create subfolder
- Related files in same folder → group into subfolder
Code Style Rules
- Named exports only (no default exports)
- readonly on all interface properties
- Object.freeze() for immutable data
- JSDoc on all public API surfaces
- async/await exclusively
- No any — use unknown
- No var — use const/let
- No inline comments unless necessary
- No business logic in barrel index.ts
- No default exports
Import Order Rules
Testing Requirements
Vitest for all testing. Unit tests in packages/<name>/tests/. Integration tests in tests/integration/.
All new code MUST include unit tests. Run typecheck and tests before every commit.
Dependency Direction Rules
Dependencies flow inward. Foundation packages have no internal dependencies. Higher-level packages depend only on lower tiers.
Never create circular dependencies between packages. If you need functionality from a higher-layer package, move it to a lower-layer package.
Type Ownership Rules
Every type has exactly one owner package. All consuming packages import from the owner — never duplicate types.
| Type | Owner | Import From |
|---|---|---|
| EntityId, UserId, EventId | @zudojs/constants | import type { EventId } from "@zudojs/constants" |
| BaseError, ErrorCode | @zudojs/errors | import { ApplicationError } from "@zudojs/errors" |
| Logger, LogLevel | @zudojs/logger | import type { Logger } from "@zudojs/logger" |
| EventBus, EventHandler | @zudojs/events | import type { EventBus } from "@zudojs/events" |
| Container, Token | @zudojs/container | import type { Container } from "@zudojs/container" |
| Middleware, MiddlewareContext | @zudojs/middleware | import type { Middleware } from "@zudojs/middleware" |
| Maybe, DeepReadonly, isPlainObject | @zudojs/types | import { isPlainObject } from "@zudojs/types" |
Before defining ANY type in a new package, check if it already exists in a shared package. If it does, import from the owner — never redefine.