@enclosurejs/core
v1.1.0
Published
> [!IMPORTANT] > This is the **zero-dependency kernel** of the Enclosure monorepo. Every other package — `platform-tauri`, `platform-electron`, `platform-web`, and all universal modules — depends on it. It defines the entire application shape without impo
Readme
@enclosurejs/core — Platform-agnostic application kernel
[!IMPORTANT] This is the zero-dependency kernel of the Enclosure monorepo. Every other package —
platform-tauri,platform-electron,platform-web, and all universal modules — depends on it. It defines the entire application shape without importing a single platform API.
The Problem
Desktop frameworks lock you into a single runtime: Tauri apps can't run on Electron, Electron apps can't run on Capacitor. Business logic, lifecycle management, and service wiring get entangled with platform-specific APIs, making migration painful and testing slow.
@enclosurejs/core solves this by providing a zero-dependency kernel that defines the entire application shape — DI, lifecycle, modules, plugins, error handling, eventing — without importing a single platform API.
Platform packages (platform-tauri, platform-electron, platform-web) implement the contracts; the kernel never knows which one is running.
Architecture
┌─────────────────────────────────────────────────────────┐
│ Your Application │
│ ┌───────────────────────────────────────────────────┐ │
│ │ UI (Svelte / React / VanillaJS) │ │
│ │ FrontendAdapter.mount(container, context) │ │
│ └───────────────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Modules (Logger, Config, DB, Auth, Renderer) │ │
│ │ ───── topo-sorted, guaranteed after init ───── │ │
│ │ Plugins (DevTools, Theme, Analytics) │ │
│ │ ───── lightweight, may come and go ────────── │ │
│ └───────────────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ @enclosurejs/core (this package — zero deps) │ │
│ │ DI · Lifecycle · EventBus · Result · Deferred │ │
│ │ Disposable · BackendAdapter · 26 Capabilities │ │
│ └───────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ BackendAdapter implementations (one per platform): │
│ ┌────────┐ ┌──────────┐ ┌─────────┐ │
│ │ Tauri │ │ Electron │ │ Web │ │
│ └────────┘ └──────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────┘Dependency rule: core imports nothing — every other package may import core.
Quick Start
import {
createApp,
type Module,
type Plugin,
type FrontendAdapter,
createToken,
WebBackend,
} from '@enclosurejs/core';
// 1. Define a service token
interface Logger {
log(msg: string): void;
}
const LoggerToken = createToken<Logger>('logger');
// 2. Create a module (installed in topo-sorted order)
const loggerModule: Module = {
id: 'logger',
install(ctx) {
ctx.provide(LoggerToken, { log: (msg) => console.warn(`[app] ${msg}`) });
},
};
// 3. Create a plugin (lightweight extension)
const devToolsPlugin: Plugin = {
id: 'devtools',
backendInstall(ctx) {
const logger = ctx.context.use(LoggerToken);
logger.log('DevTools activated');
},
};
// 4. Create a frontend adapter (framework-agnostic)
const frontend: FrontendAdapter = {
mount({ container, context }) {
const logger = context.use(LoggerToken);
container.textContent = 'Hello from Enclosure!';
logger.log('Frontend mounted');
},
};
// 5. Wire everything together
const app = createApp({
backend: new WebBackend(),
frontend,
container: document.getElementById('app')!,
modules: [loggerModule],
plugins: [devToolsPlugin],
});
await app.start();
// created → initializing (modules + backend plugins) → ready → running (frontend mount + frontend plugins)How It Works
Two Error Paths
@enclosurejs/core separates errors by intent:
throw CoreError— for programming errors and invariant violations (token not found, circular dependency, invalid phase transition). These should never happen in correct code.CoreErrorcarries structured context:{ domain, code, entity, message }.return Result<T, E>— for expected failures (no GPU adapter, invalid config, user cancellation). Callers must check.okbefore accessing.value, making error handling explicit and exhaustive.
DI Resolution Chain
Context forms a tree. When you call context.use(token):
child context → check local bindings → found? return value
↓ not found
→ walk to parent → check local bindings → found? return value
↓ not found
→ walk to grandparent → ...
↓ root reached, still not found
→ throw CoreError('context', 'TOKEN_NOT_FOUND', ...)tryUse() follows the same chain but returns undefined instead of throwing. Singleton factories are cached per resolving context (not per defining context), so a child and parent resolve to the same singleton when the factory lives in the parent.
Lifecycle State Machine
created ──→ initializing ──→ ready ──→ running ──→ stopping ──→ stopped
│
(terminal)Each transition runs registered hooks sequentially. start() advances three steps (created → running), stop() advances to stopped from any non-terminal phase (even partial startup). The finally block guarantees the terminal stopped state is reached regardless of hook errors. Invalid transitions throw immediately — you can't go backwards or skip phases.
Module Install Order
Modules declare dependencies via requires: string[]. topoSortModules uses Kahn's algorithm to produce a deterministic install order. Ties are broken alphabetically. The algorithm detects three error classes before any install runs:
- Duplicate IDs — two modules with the same
id - Missing dependencies — a module requires an ID that isn't registered
- Circular dependencies — A requires B, B requires A (directly or transitively)
Plugin Isolation
Plugins can fail without crashing the app. If onPluginError is provided to createApp(), plugin exceptions during backendInstall, frontendInstall, or deactivate are caught, forwarded to the callback, and emitted as plugin:error events. The plugin is rolled back (provided tokens removed, disposables cleaned up) and plugin:installed/plugin:reloaded events are suppressed. Without the callback, plugin errors propagate as CoreError — fail-fast by default, opt-in resilience.
Plugin ctx.events.on()/ctx.events.once() subscriptions and ctx.onPhase() lifecycle hooks are automatically tracked and disposed when the plugin is uninstalled — no manual cleanup needed by plugin authors.
API
Kernel primitives
| Export | Kind | Purpose |
| ------------------------ | ------- | ----------------------------------------------------------- |
| CoreError | class | Structured error with domain, code, entity, message |
| isCoreError(e) | guard | Narrows unknown to CoreError |
| Result<T, E> | type | { ok, value } or { ok, error } — error-as-value |
| ok(v) / err(e) | factory | Construct Result values |
| unwrap / unwrapOr | fn | Extract value or throw / fallback |
| mapResult / mapError | fn | Transform Result without unwrapping |
| isOk / isErr | guard | Type-narrowing for Result |
| BrandedId<Brand> | type | Compile-time safe numeric handle (number & { __brand }) |
| createBrandedId<B>(n) | factory | Cast a number to a branded handle |
Dependency injection
| Export | Kind | Purpose |
| ------------------------ | ------- | ------------------------------------------------------------ |
| Token<T> | type | Identity key for DI (frozen object reference) |
| createToken<T>(key) | factory | Create a typed token |
| Context | class | Hierarchical DI container with parent chain |
| Context.provide | method | Bind a value (throws on duplicate) |
| Context.override | method | Replace or create binding (no throw) |
| Context.provideFactory | method | Bind a lazy factory (singleton by default) |
| Context.use | method | Resolve token (throws if missing) |
| Context.tryUse | method | Resolve token (returns undefined if missing) |
| Context.createChild | method | Scoped child that inherits from parent |
| Context.useOrDefault | method | Resolve token or return a provided default |
| Context.has | method | True if token exists in this or any ancestor |
| Context.find | method | Returns nearest context that holds the token |
| Context.removeLocal | method | Delete a binding from this layer only |
| Context.clearLocal | method | Drop all local bindings (parent survives) |
| Context.getAllTokens | method | Union of tokens across the full hierarchy |
| Context.getTokensByTag | method | Discover bindings by tag across hierarchy |
| Context.capabilities | method | ReadonlySet<string> of all token keys — for conditional UI |
| Context.getStats | method | Depth, local/total binding counts |
Async & lifecycle
| Export | Kind | Purpose |
| -------------------------- | --------- | -------------------------------------------------------------------------------------------- |
| retry(fn, opts) | fn | Repeats fn with exponential backoff, jitter, and AbortSignal support |
| withTimeout(promise, ms) | fn | Races a promise against a timer — rejects with CoreError on timeout |
| Deferred<T> | class | Externally-controlled Promise with .resolve() / .reject() |
| Deferred.timeout(ms) | static | Create a deferred that auto-rejects after timeout |
| Deferred.resolve(value) | static | Ready-settled instance for bridging or tests |
| Deferred.reject(reason) | static | Pre-rejected instance (suppresses unhandled rejection) |
| deferred.timeout(ms) | method | Set auto-reject timer on existing instance |
| deferred.abort(signal) | method | Reject on AbortSignal |
| DisposableGroup | class | LIFO cleanup group (sync dispose + async disposeAsync) |
| DisposableGroup.add | method | Register Disposable |
| DisposableGroup.addAsync | method | Register AsyncDisposable |
| DisposableGroup.addFn | method | Register plain teardown function |
| DisposableGroup.addChild | method | Nest another group for async LIFO ordering |
| Pool<T> | interface | Generic resource pool (acquire / release lifecycle) |
| createPool(opts) | factory | Creates a pool with create, reset, destroy, maxIdle options |
| EventBus<E> | class | Typed pub/sub with on, once, emit, off, waitFor |
| EventBus.waitFor | method | One-shot promise, integrates with Deferred (timeout + signal) |
| EventBus.getStats | method | Topic and listener counts for metrics/debugging |
| EventBus.clear | method | Remove all topics and listeners (teardown / tests) |
| Lifecycle | class | Deterministic state machine: created → initializing → ready → running → stopping → stopped |
| Lifecycle.advance | method | One forward step in the phase graph |
| Lifecycle.start | method | Advance created → running (3 steps) |
| Lifecycle.stop | method | Advance any non-terminal phase → stopped |
| Lifecycle.onEnter | method | Register hook for a specific phase — returns Disposable that removes it |
Application layer
| Export | Kind | Purpose |
| ---------------------- | --------- | ------------------------------------------------------------------------------ |
| App | class | Convergence point: modules + backend + frontend + plugins |
| App.reloadPlugin(id) | method | Deactivate → dispose → remove tokens → re-install a single plugin at runtime |
| createApp(opts) | factory | Create an App instance |
| BackendAdapter | interface | Platform bridge (invoke + on + once) |
| WebBackend | class | Default in-browser adapter |
| TestBackend | class | Test double (records calls, push events) |
| FrontendAdapter | interface | VanillaJS mount(ctx) — core doesn't know your framework |
| Module | interface | Skeleton service (id, requires?, install, dispose?) |
| Plugin | interface | Extension (backendInstall?, frontendInstall?, deactivate?) |
| topoSortModules | fn | Kahn's algorithm — deterministic, detects cycles |
| WidgetRegistry | interface | Slot-based UI composition registry with ordering and events |
| createWidgetRegistry | factory | In-memory WidgetRegistry — lazy re-sort per slot |
| registerWidget | fn | Register widget + auto-schedule unregister in ctx.disposable |
| WidgetRegistryToken | token | Well-known: resolves WidgetRegistry (auto-provided when frontend is present) |
| BackendToken | token | Well-known: resolves BackendAdapter |
| LifecycleToken | token | Well-known: resolves Lifecycle |
| EventBusToken | token | Well-known: resolves EventBus |
| AppToken | token | Well-known: resolves App |
| capabilityTokenMap | const | Maps 26 capability short names to DI tokens (for plugin-loader) |
| ENCLOSURE_VERSION | const | Semver of @enclosurejs/core (for plugin compatibility checks) |
Capability contracts (26 services — interfaces + tokens)
Every capability lives in core/src/capabilities/ and exports a Token + a service interface.
Platform packages provide implementations; application code consumes tokens.
One exception: src/path.ts exports createPathService — a portable pure-string implementation of PathService shared by all browser-based platforms.
Full API reference with method signatures, types, and usage examples: capabilities/README.md.
| Tier | Tokens |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 — Core | DialogToken, WindowToken, IpcToken, FileSystemToken, ShellToken, ClipboardToken |
| 2 — Extended | NotificationToken, StorageToken, HttpClientToken, UpdaterToken, PathToken, SystemInfoToken, TrayToken, ShortcutToken, DisplayToken, PrintToken, SerialToken, FileWatcherToken, DeepLinkToken, DatabaseToken, AppLifecycleToken, ThemeToken, AutoLaunchToken, PowerMonitorToken, KeychainToken, UsbToken |
Configuration
Zero configuration.
@enclosurejs/core has no config files, no build-time flags, no environment variables.
It exports pure TypeScript — the consuming app decides what to wire.
Types Exported
Types other packages depend on:
| Type | Used by |
| ----------------------------------------------- | ---------------------------------- |
| BackendAdapter | all platform-* packages |
| FrontendAdapter | all frontend integrations |
| Module / InstallContext | any package providing a module |
| Plugin / PluginFrontendContext | any package providing a plugin |
| Token<T> / Context | everything that participates in DI |
| Result<T, E> / CoreError | all packages for error handling |
| Disposable / AsyncDisposable / TeardownFn | cleanup across the codebase |
| Pool<T> / PoolOptions<T> | resource pool consumers |
| RetryOptions | retry configuration |
| EventMap / Listener | custom event buses |
| Phase / LifecycleHook | lifecycle integration |
| BrandedId<B> | SoA/ECS numeric handles |
| AppOptions (incl. appId) | every app entry / example |
| WidgetRegistry / WidgetDescriptor | UI composition in shell layouts |
| All 26 capability interfaces + tokens | platform packages + consumer code |
Safety
Type Safety
Token<T>guarantees correct types atuse<T>()/provide<T>()call sites.BrandedId<Brand>prevents mixing up numeric handles at compile time.Result<T, E>forces callers to check.okbefore accessing.valueor.error.EventBus<E>constrains event names and payload types via theEtype parameter.- All capability interfaces are purely typed — no
any, nounknownin public surface.
Error Safety
- Two explicit error paths:
throw CoreErrorfor invariant violations,return Resultfor expected failures. CoreErrorcarries structured context (domain,code,entity) — no plainnew Error().DisposableGroupcontinues through errors in LIFO order, then throwsAggregateErrorif any failed.Deferredsuppresses unhandled rejection on timeout/abort cleanup and pre-rejected instances.
Runtime Safety
Lifecycleenforces a strict state machine — invalid transitions throw immediately.Context.provide()prevents silent token shadowing (throws on duplicate).topoSortModulesdetects missing dependencies and circular references before any install runs.- Plugin errors are isolated via
onPluginErrorcallback — a failing plugin doesn't crash the app.
Benchmarks
[!NOTE] Environment: Intel Core i7-10510U @ 1.80 GHz (4C/8T, 15W TDP), Windows 10 (22631), Node.js 22.22.0, Vitest 3.2.4. Results collected from clean PowerShell terminal, no IDE.
Context (DI)
| Operation | ops/sec | mean |
| ----------------------------------- | ------- | ------- |
| provide (first binding) | 2,367 | 0.42 ms |
| use — value hit | 2,694 | 0.37 ms |
| tryUse — value hit | 2,632 | 0.38 ms |
| tryUse — miss (no throw) | 7,142 | 0.14 ms |
| has — miss | 8,095 | 0.12 ms |
| singleton factory — warm cache | 1,677 | 0.60 ms |
| transient factory — every call | 1,899 | 0.53 ms |
| child depth=1 — resolve from parent | 2,255 | 0.44 ms |
| child depth=5 — resolve from root | 933 | 1.07 ms |
| child depth=10 — resolve from root | 508 | 1.97 ms |
| child shadows parent — local hit | 4,359 | 0.23 ms |
EventBus
| Operation | ops/sec | mean | | ----------------------------- | ------- | -------- | | construct | 9,733 | 0.10 ms | | emit → 1 listener | 1,263 | 0.79 ms | | emit → 10 listeners | 391 | 2.56 ms | | emit → 100 listeners | 53 | 18.84 ms | | emit → 1,000 listeners | 51 | 19.46 ms | | once (subscribe + first emit) | 1,452 | 0.69 ms | | subscribe + dispose cycle | 175 | 5.73 ms |
Lifecycle
| Operation | ops/sec | mean | | ------------------------------------- | ------- | ------- | | construct + single advance | 1,693 | 0.59 ms | | construct + start (3 advances) | 1,127 | 0.89 ms | | construct + start + stop (5 advances) | 1,341 | 0.75 ms | | advance with 10 hooks per phase | 341 | 2.93 ms | | phase getter (100k reads) | 13,809 | 0.07 ms |
App (end-to-end)
| Operation | ops/sec | mean |
| ----------------------------------------------- | ------- | ------- |
| createApp() — bare | 1,209 | 0.83 ms |
| createApp() — 5 modules, 3 plugins (no start) | 1,137 | 0.88 ms |
| bare start + stop | 1,117 | 0.90 ms |
| 5 modules (linear deps): start + stop | 865 | 1.16 ms |
| 5 modules + 3 plugins: start + stop | 619 | 1.62 ms |
Deferred
| Operation | ops/sec | mean |
| ------------------------------ | ------- | ------- |
| construct | 1,032 | 0.97 ms |
| construct + resolve | 939 | 1.07 ms |
| create → resolve → await cycle | 3,814 | 0.26 ms |
| static Deferred.resolve | 948 | 1.05 ms |
| construct + timeout setup | 862 | 1.16 ms |
DisposableGroup
| Operation | ops/sec | mean | | --------------------------------------------- | ------- | ------- | | construct | 5,126 | 0.20 ms | | 10 items LIFO dispose | 1,411 | 0.71 ms | | 100 items LIFO dispose | 319 | 3.13 ms | | 1,000 items LIFO dispose | 145 | 6.92 ms | | mixed add/addFn/addChild (realistic teardown) | 363 | 2.75 ms |
Result
| Operation | ops/sec | mean |
| ---------------------------- | ------- | ------- |
| ok() construction | 501 | 2.00 ms |
| err(string) construction | 500 | 2.00 ms |
| unwrapOr on Ok | 413 | 2.42 ms |
| unwrapOr on Err (fallback) | 422 | 2.37 ms |
| chained mapResult x 5 | 340 | 2.94 ms |
Analysis
DI resolution is the most performance-critical path — it runs on every context.use() call during module install and at runtime. The Map.has() + Map.get() chain resolves a value binding in ~0.37 ms (batch of operations), with linear degradation through the parent chain: depth=1 adds ~0.07 ms, depth=5 adds ~0.70 ms. For typical apps with 2-3 levels of DI hierarchy, resolution overhead is negligible. tryUse miss (the "token not found" path) is the fastest DI operation at 7.1K ops/sec because it exits immediately without creating an Error object.
EventBus emit scales linearly with listener count: 1 → 10 listeners costs ~3.2x, but 100 → 1,000 shows near-constant cost (53 vs 51 ops/sec) because the snapshot Array.from() dominates. For apps with typical event fan-out (1-10 listeners per topic), emit overhead is under 3 ms.
App lifecycle completes a full 5-module + 3-plugin start/stop cycle in 1.62 ms. This is a one-time cost at startup and shutdown — well within any interactive budget. The topoSortModules overhead for 85 modules (fanout=4, depth=3) is ~31 ms, which means even large applications can boot module ordering in a single frame.
DisposableGroup disposes 1,000 items in LIFO order in ~6.9 ms. Realistic app teardown (mixed add/addFn/addChild) completes in ~2.8 ms. The LIFO guarantee (most recently added is disposed first) ensures correct shutdown ordering.
Bundle Size
| Output | File | Size |
| ------------ | ------------ | --------- |
| Runtime (JS) | index.js | 50.70 KB |
| Types (DTS) | index.d.ts | 51.88 KB |
| Total | | 102.58 KB |
Zero external dependencies — the package is pure TypeScript with no runtime imports. The JS bundle contains the entire kernel: DI, Lifecycle, EventBus, Result, Deferred, DisposableGroup, App, BackendAdapter, topoSort. The DTS file includes all 26 capability interfaces + tokens.
Quality
| Metric | Value | | --------------------- | -------------------------------------------------------------------------------------- | | Unit tests | 345 (all pass) | | Benchmarks | 9 suites (DI, EventBus, Lifecycle, Deferred, Disposable, Result, Module, Backend, App) | | Source files | 19 kernel + 26 capabilities = 45 | | Test files | 16 test + 9 bench = 25 | | External dependencies | 0 (devDependencies only: tsup) | | Coverage thresholds | statements >= 90%, branches >= 85%, functions >= 95%, lines >= 90% |
Quality Layers
Layer 1: STATIC ANALYSIS (every commit)
tsc --noEmit strict mode, zero errors
eslint ESLint 9 flat config, zero warnings
prettier --check formatting
Layer 2: UNIT TESTS (every commit)
345 tests all kernel primitives, edge cases, path service
v8 coverage statements >= 90%, branches >= 85%, functions >= 95%, lines >= 90%
Layer 3: BENCHMARKS (package ready)
9 suites DI, EventBus, Lifecycle, Deferred, Disposable, Result, Module, Backend, App
Layer 4: PACKAGE HEALTH
0 external deps pure TypeScript, no runtime imports
tsup build ESM + DTS outputFile Structure
src/
├── errors.ts CoreError, isCoreError
├── result.ts Result<T,E>, ok, err, unwrap, mapResult, ...
├── branded.ts BrandedId<Brand>, createBrandedId
├── di.ts Token, Context (hierarchical DI container)
├── async.ts retry (backoff + jitter), withTimeout
├── deferred.ts Deferred<T> (externally-controlled Promise)
├── disposable.ts Disposable, AsyncDisposable, DisposableGroup
├── pool.ts Pool<T>, createPool (generic resource pool)
├── events.ts EventBus<E> (typed pub/sub + waitFor)
├── lifecycle.ts Lifecycle (phase state machine)
├── backend.ts BackendAdapter, WebBackend, TestBackend
├── frontend.ts FrontendAdapter, FrontendContext
├── module.ts Module, InstallContext, topoSortModules
├── plugin.ts Plugin, PluginFrontendContext
├── widgets.ts WidgetRegistry, WidgetDescriptor, createWidgetRegistry, registerWidget
├── app.ts App, createApp, well-known tokens, scoped EventBus proxy
├── capability-tokens.ts capabilityTokenMap (26 short names → Token)
├── version.ts ENCLOSURE_VERSION
├── path.ts createPathService (shared by all platforms)
├── index.ts Barrel (re-exports only)
└── capabilities/
├── app-lifecycle.ts AppLifecycleService, AppState
├── auto-launch.ts AutoLaunchService, AutoLaunchOptions
├── clipboard.ts ClipboardService
├── database.ts DatabaseService, Database, DbStatement
├── deep-link.ts DeepLinkService, DeepLinkEvent
├── dialogs.ts DialogService, FileDialogOptions, ...
├── display.ts DisplayService, Display, DisplayBounds
├── file-watcher.ts FileWatcherService, FileWatcher
├── fs.ts FileSystem, DirEntry, FileStat
├── http.ts HttpClient, HttpRequestOptions, HttpResponse
├── ipc.ts IpcService, IpcMessage
├── keychain.ts KeychainService
├── notifications.ts NotificationService, NotificationOptions
├── path.ts PathService, ParsedPath
├── power-monitor.ts PowerMonitorService, BatteryInfo
├── print.ts PrintService, PrintOptions
├── serial.ts SerialService, SerialConnection
├── shell.ts ShellService, ChildProcess, ExecResult
├── shortcuts.ts ShortcutService
├── storage.ts StorageService, StorageSize
├── system-info.ts SystemInfoService, CpuInfo, MemoryInfo
├── theme.ts ThemeService, Theme, ThemeSource
├── tray.ts TrayService, TrayMenuItem, TrayOptions
├── updater.ts UpdaterService, UpdateInfo, DownloadProgress
├── usb.ts UsbService, UsbDevice, UsbDeviceInfo
└── windows.ts WindowService, AppWindow, WindowOpenOptions
__tests__/
├── app.test.ts 62 tests — App lifecycle, module/plugin wiring, installPlugin, reloadPlugin, rollback, scoped EventBus, onPhase disposable
├── app.bench.ts startup/stop/module-install throughput
├── async.test.ts 20 tests — retry backoff/jitter/abort, withTimeout
├── backend.test.ts 12 tests — WebBackend, TestBackend
├── backend.bench.ts invoke/event throughput
├── branded.test.ts 7 tests — BrandedId type safety, validation
├── deferred.test.ts 27 tests — resolve/reject/timeout/abort
├── deferred.bench.ts creation/resolve/timeout throughput
├── di.test.ts 32 tests — Context provide/use/factory/child/tags/capabilities
├── di.bench.ts provide/use/factory/hierarchy throughput
├── disposable.test.ts 19 tests — sync/async/LIFO/error collection
├── disposable.bench.ts add/dispose throughput
├── events.test.ts 21 tests — on/off/once/emit/waitFor
├── events.bench.ts emit/subscribe/waitFor throughput
├── lifecycle.test.ts 14 tests — phase transitions, hooks, errors, removable hooks
├── lifecycle.bench.ts advance/start/stop throughput
├── module.test.ts 20 tests — topoSort, cycles, duplicates, missing deps
├── module.bench.ts sort throughput at scale
├── path.test.ts 30 tests — posix/windows separators, edge cases
├── pool.test.ts 19 tests — acquire/release/maxIdle/reset/dispose
├── result.test.ts 18 tests — ok/err/unwrap/map
├── result.bench.ts creation/unwrap/map throughput
├── capability-tokens.test.ts 3 tests — capabilityTokenMap completeness and token validity
├── version.test.ts 3 tests — ENCLOSURE_VERSION format and package.json sync
└── widgets.test.ts 38 tests — register/unregister/slots/groups/ordering/when/events, registerWidget helperLicense
MIT
