updated skills

This commit is contained in:
2026-06-09 21:06:27 +01:00
parent db1ce43ee1
commit 427a5bd919
1283 changed files with 24684 additions and 2313 deletions

View File

@@ -63,4 +63,3 @@ The coverage gate is 100/100/100/100 (istanbul). The package's
`tests/setup/polyfills.ts` registers an in-memory `MeterProvider` before
source modules import the OTel meter so the telemetry assertions resolve
against a real exporter, with no mocks.

View File

@@ -85,30 +85,30 @@ type error in strict-mode consumers.
## API Reference
- `CONTROLLER_CODES`
- `INVALID_OPTIONS` — Zod boundary rejected the constructor options
- `INVALID_OPTIONS` — Zod boundary rejected the constructor options
bundle; `context.issues` carries the Zod issue array.
- `INVALID_INPUT_KEY` — `setThreshold` (or future device adapters)
- `INVALID_INPUT_KEY` — `setThreshold` (or future device adapters)
received an input key not present in `CONTROLLER_INPUT_KEYS`.
- `DESTROYED_INSTANCE` — operation attempted after `destroy()` on an
- `DESTROYED_INSTANCE` — operation attempted after `destroy()` on an
instance that does not silently no-op (strict-mode opt-in).
- `KEYBOARD_TARGET_MISSING` — `KeyboardController.attach()` was
- `KEYBOARD_TARGET_MISSING` — `KeyboardController.attach()` was
called but neither an `EventTargetLike` was injected nor a global
`window` was discoverable; `context.target` reports the resolved
value.
- `INVALID_KEY_MAP` — `KeyboardController` received a `keyMap` whose
- `INVALID_KEY_MAP` — `KeyboardController` received a `keyMap` whose
value is not one of `CONTROLLER_INPUT_KEYS`; `context.entry`
carries the `[key, input]` tuple that failed validation.
- `GAMEPAD_API_UNAVAILABLE` — `GamepadController.update()` was
- `GAMEPAD_API_UNAVAILABLE` — `GamepadController.update()` was
called but neither a `GamepadSource` was injected nor a global
`navigator.getGamepads` was discoverable.
- `INVALID_GAMEPAD_MAP` — `GamepadController` received a
- `INVALID_GAMEPAD_MAP` — `GamepadController` received a
`buttonMap`/`axisMap` whose value targets an input key not present
in `CONTROLLER_INPUT_KEYS`; `context.entry` carries the failing
tuple.
- `DUPLICATE_CHILD` — `ControllerMerger.addController()` received a
- `DUPLICATE_CHILD` — `ControllerMerger.addController()` received a
child already attached, or a second child whose `id` collides with
an existing one; `context` carries the child id.
- `UNKNOWN_CHILD` — `ControllerMerger.removeController()` was called
- `UNKNOWN_CHILD` — `ControllerMerger.removeController()` was called
with a child that is not currently attached.
- `ControllerCode` — literal union of every code string.
- `class ControllerError extends SuaveplanError<ControllerCode>` —

View File

@@ -98,15 +98,15 @@ console.log(gp.getValue("right")); // 191
## API Reference
- `class GamepadController extends BaseController`
- `constructor(options?: GamepadControllerOptions)`
- `update(): void` — polls the source, diffs against last frame,
- `constructor(options?: GamepadControllerOptions)`
- `update(): void` — polls the source, diffs against last frame,
pushes updates through `setState()`. Throws
`ControllerError(GAMEPAD_API_UNAVAILABLE)` when no source is
available. No-op after `destroy()`.
- `getDeadzone(): number` — resolved deadzone.
- `getPadIndex(): number` — bound pad index.
- `getButtonMap(): GamepadButtonMap` — immutable button map.
- `getAxisMap(): GamepadAxisMap` — immutable axis map.
- `getDeadzone(): number` — resolved deadzone.
- `getPadIndex(): number` — bound pad index.
- `getButtonMap(): GamepadButtonMap` — immutable button map.
- `getAxisMap(): GamepadAxisMap` — immutable axis map.
- `DEFAULT_GAMEPAD_BUTTON_MAP: GamepadButtonMap` — W3C standard
mapping.
- `DEFAULT_GAMEPAD_AXIS_MAP: GamepadAxisMap` — W3C standard axis

View File

@@ -91,15 +91,15 @@ kb.destroy();
## API Reference
- `class KeyboardController extends BaseController`
- `constructor(options?: KeyboardControllerOptions)`
- `attach(): void` — bind `keydown`/`keyup` to the resolved
- `constructor(options?: KeyboardControllerOptions)`
- `attach(): void` — bind `keydown`/`keyup` to the resolved
target. Throws `ControllerError(KEYBOARD_TARGET_MISSING)` when
no target is available.
- `detach(): void` — idempotent removal of listeners.
- `getPressedKeys(): readonly string[]` — snapshot of physical
- `detach(): void` — idempotent removal of listeners.
- `getPressedKeys(): readonly string[]` — snapshot of physical
keys currently held down (by `code` or `key` identifier).
- `getKeyMap(): KeyboardKeyMap` — immutable key → input map.
- `destroy(): void` — detaches listeners and clears state.
- `getKeyMap(): KeyboardKeyMap` — immutable key → input map.
- `destroy(): void` — detaches listeners and clears state.
- `DEFAULT_KEYBOARD_KEY_MAP: KeyboardKeyMap` — frozen default map
used when `options.keyMap` is omitted.
- `interface KeyboardControllerOptions extends BaseControllerOptions`

View File

@@ -114,27 +114,27 @@ merger.destroy(); // destroys padB as well
## API Reference
- `class ControllerMerger extends BaseController`
- `constructor(options?: ControllerMergerOptions)` — accepts every
- `constructor(options?: ControllerMergerOptions)` — accepts every
`BaseControllerOptions` field plus `children?: readonly BaseController[]`
seeded eagerly via `addController()`.
- `addController(child: BaseController): void` — subscribes to
- `addController(child: BaseController): void` — subscribes to
`onAnyInput` + `onCombo`, captures the current snapshot, marks the
merger dirty. Throws `CONTROLLER.DUPLICATE_CHILD` on identity or
`id` collision.
- `removeController(child: BaseController): void` — disposes
- `removeController(child: BaseController): void` — disposes
subscriptions, drops the snapshot, marks dirty. Throws
`CONTROLLER.UNKNOWN_CHILD` when the child is not attached.
- `getChildren(): MergerChildren` — read-only snapshot of children
- `getChildren(): MergerChildren` — read-only snapshot of children
in insertion order.
- `get childCount(): number` — current attachment count.
- `update(): void` — polls every child, then runs the per-field max
- `get childCount(): number` — current attachment count.
- `update(): void` — polls every child, then runs the per-field max
reduction over `CONTROLLER_INPUT_KEYS` and routes diffs through
`updateInput` so threshold/deadzone gating + event emission stay
consistent.
- `destroy(): void` — disposes all subscriptions, destroys every
- `destroy(): void` — disposes all subscriptions, destroys every
child, clears local state, then defers to `BaseController.destroy`.
- `interface ControllerMergerOptions extends BaseControllerOptions`
- `children?: readonly BaseController[]` — initial seed.
- `children?: readonly BaseController[]` — initial seed.
- `type MergerChildren = readonly BaseController[]`
## Implementation Notes

View File

@@ -88,4 +88,3 @@ bun run docs:check --filter=@suaveplan/ecs
## License
MIT © Suaveplan Team

View File

@@ -58,7 +58,7 @@ const snapshots = await /* meterProvider */;
## Implementation Notes
- The meter is resolved at module load via `metrics.getMeter("@suaveplan/ecs")`. The OTel API guarantees a stable meter reference: when a real SDK is later registered, *previously created* instruments continue to emit through the new provider. This is the documented "no-op-then-bind" pattern.
- The meter is resolved at module load via `metrics.getMeter("@suaveplan/ecs")`. The OTel API guarantees a stable meter reference: when a real SDK is later registered, _previously created_ instruments continue to emit through the new provider. This is the documented "no-op-then-bind" pattern.
- The histogram unit is `"ms"`, matching `performance.now()`. The world records the wall-clock difference between the start and end of `execute(dt)`.
- Counter attributes are keyed by `op`, not by code-path, so dashboards can sum spawn + despawn as a single series or split per-op trivially.
- Tests should reset the in-memory exporter between cases (`testInMemoryMetricExporter.reset()`) to avoid cross-test bleed; the package's `world.spec-conformance.test.ts` does this in an `afterEach` hook.

View File

@@ -13,7 +13,7 @@ The optional Zod validator opts the descriptor into runtime validation: `World.a
## Features
- Deep-freeze on the descriptor and its `defaults` — strict-mode mutation throws.
- Defaults are *copied* on construction, so caller mutations after `defineComponent` don't leak into the descriptor.
- Defaults are _copied_ on construction, so caller mutations after `defineComponent` don't leak into the descriptor.
- Optional `schema?: z.ZodType<T>` validator threaded through to the world's validation step.
- specific addition over upstream's `defineComponent` — upstream lacked the Zod hook.
@@ -61,5 +61,5 @@ world.addComponent(entity, Health, { current: -1, max: 100 });
- The defaults clone is shallow (`{ ...defaults }`). For nested mutable references, callers should freeze deeply themselves before passing in. The component schema is identity-stable, but interior mutability of a passed reference is out of scope.
- The schema property is added conditionally (omitted from the object when not supplied) so JSON serialisation of a descriptor produces a clean two-key shape when no validator is supplied.
- Validation is *not* applied at registration — only at component-add / component-set boundaries. This keeps the registry decoupled from Zod and means a schema can be retrofitted onto an existing component without rewiring registration.
- Validation is _not_ applied at registration — only at component-add / component-set boundaries. This keeps the registry decoupled from Zod and means a schema can be retrofitted onto an existing component without rewiring registration.
- The factory does not generate IDs; the registry assigns those on first registration via `World.registerComponent` or implicitly during `addComponent`.

View File

@@ -6,7 +6,7 @@ _(no submodules)_
## Purpose
A *system* is the unit of behaviour in an ECS — a function that runs over every entity matching a query and mutates component state. `defineSystem` is the factory that fills in sensible defaults (`phase: "update"`, `priority: 0`, `enabled: true`) and produces a frozen `SystemDefinition`. `SystemScheduler` is the world's internal sorted registry: it orders systems by phase then priority, lazily resorting only when the set changes.
A _system_ is the unit of behaviour in an ECS — a function that runs over every entity matching a query and mutates component state. `defineSystem` is the factory that fills in sensible defaults (`phase: "update"`, `priority: 0`, `enabled: true`) and produces a frozen `SystemDefinition`. `SystemScheduler` is the world's internal sorted registry: it orders systems by phase then priority, lazily resorting only when the set changes.
Phase ordering is fixed at `preUpdate → update → postUpdate → render`, mirroring the canonical game-loop pipeline. Priority breaks ties within a phase, lowest first.

View File

@@ -163,4 +163,3 @@ full deferral table and rationale.
- [`src/engine/game-loop/game-loop.md`](src/engine/game-loop/game-loop.md)
- [`src/engine/plugin-manager/plugin-manager.md`](src/engine/plugin-manager/plugin-manager.md)
- [`src/observability/otel.md`](src/observability/otel.md)

View File

@@ -81,6 +81,7 @@ mgr.stopHeartbeatMonitor();
`new PlayerManager(deps, options?)` where `deps` is
`{ clock, timers, logger, events }`. Options:
- `timeoutThresholdMs` (default 30000) — ms without heartbeat before a
connected player is marked `timeout`.
- `reconnectionGraceMs` (default 10000) — ms after a disconnect during

View File

@@ -105,7 +105,7 @@ const match = engine.createMatchFromQueue(
The skill-based algorithm tries each candidate as an anchor and grows
the group greedily until either `maxPlayers` is reached or no further
peers fit inside the `skillRange`. The match returned is the *largest*
peers fit inside the `skillRange`. The match returned is the _largest_
viable group; on ties the first-found anchor wins, matching the
upstream behaviour. The engine deliberately does not own the
`QueueManager`'s "remove on match" semantics — `createMatchFromQueue`

View File

@@ -101,7 +101,7 @@ const stats = qm.getStats();
returns `[]` if not enough tickets are waiting; throws
`INVALID_MATCH_CRITERIA` on a non-positive size.
- `getQueueSize(queueKey): number` / `getQueuePosition(queueKey, playerId)`.
- `getStats(): readonly QueueStats[]` — one entry per *known* queue key
- `getStats(): readonly QueueStats[]` — one entry per _known_ queue key
(preserved even after the queue empties, so dashboards see history).
- `clear(): void` — drops every queue.

View File

@@ -13,9 +13,9 @@ seeker waits. It is the matchmaking primitive that powers
isolation when callers want fine-grained control over rating math, such
as during round-by-round tournament seeding or AI bot pairing.
Two pieces of math drive the matcher: the *effective range* (base
Two pieces of math drive the matcher: the _effective range_ (base
`ratingRange` plus `waitTimeBonus * waitMinutes`, capped at `maxRange`)
and the *score* (by default `Math.abs(seeker.rating - candidate.rating)`,
and the _score_ (by default `Math.abs(seeker.rating - candidate.rating)`,
overridable via the `score` dep so callers can plug in Glicko or
TrueSkill). Tie-breaks across candidates with identical scores can be
made deterministic by injecting an `Rng`.

View File

@@ -110,6 +110,6 @@ rather than a live `MapIterator` to keep callers from mutating the
internal map by accident. The `onSpectatorJoined` callback receives the
fully-constructed `Spectator` (id + `joinedAt` + permissions) so a
network fan-out caller does not need a second lookup to broadcast the
join. Duplicate-spectator detection runs *before* cap enforcement so an
join. Duplicate-spectator detection runs _before_ cap enforcement so an
already-attached viewer never accidentally fails with
`SPECTATOR_LIMIT_REACHED` when the session happens to be full.