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

@@ -357,9 +357,9 @@ Game controllers, entity-component systems, and game-server logic.
| Package | Description |
|---|---|
| [`@suaveplan/controller`](./gaming/games/controller/README.md) | Universal input controller foundation. Ships the unified `ControllerInput` shape (37 normalised fields in `[0, 255]`), `BaseController` abstract base with inje… |
| [`@suaveplan/ecs`](./gaming/games/ecs/README.md) | Archetype-based Entity Component System with `Uint32`-backed bitset queries, phase-ordered system scheduling, entity-ID recycling, Zod-validated component sche… |
| [`@suaveplan/game-server`](./gaming/games/game-server/README.md) | Universal game-server framework. Migrated from `@suaveplan/game-server` on the reference branch `claude/review-game-engine-arch-Kaykr` (main repo `packages/uni… |
| [`@suaveplan/controller`](./gaming/gaming/controller/README.md) | Universal input controller foundation. Ships the unified `ControllerInput` shape (37 normalised fields in `[0, 255]`), `BaseController` abstract base with inje… |
| [`@suaveplan/ecs`](./gaming/gaming/ecs/README.md) | Archetype-based Entity Component System with `Uint32`-backed bitset queries, phase-ordered system scheduling, entity-ID recycling, Zod-validated component sche… |
| [`@suaveplan/game-server`](./gaming/gaming/game-server/README.md) | Universal game-server framework. Migrated from `@suaveplan/game-server` on the reference branch `claude/review-game-engine-arch-Kaykr` (main repo `packages/uni… |
### `geospatial`

View File

@@ -106,4 +106,3 @@ Every adapter implementing a writer participant MUST run
`tests/conformance/run-2pc-participant.test.ts` file. The harness
pins prepare / commit / rollback semantics and the idempotency
contract.

View File

@@ -82,4 +82,3 @@ await sink.dispose();
## Tier
Server-tier — depends on `node:fs/promises`.

View File

@@ -76,4 +76,3 @@ queue wait exceeds the bound the record is dropped, the
`api.audit.dropped_total{sink,reason}` counter increments, and
(subject to a once-per-`warnThrottleMs` throttle) a single warning is
logged. Drops are NEVER silent.

View File

@@ -7,7 +7,7 @@ _(no submodules)_
## Purpose
This module produces the HMAC-SHA-256 tag attached to every audit
line. Signing operates on a *canonical* byte representation of the
line. Signing operates on a _canonical_ byte representation of the
record (keys serialised in a fixed order, `cause` omitted when
absent) so the same logical record always produces the same MAC under
the same key, regardless of how the source object was constructed.

View File

@@ -11,7 +11,7 @@ writes. The shape is exactly what design doc §12.5 specifies for the
admin / audit pipeline: `ts`, `type`, `principal`, `tenant`,
`request_id`, `trace_id`, `subject`, `outcome`, and a conditional
`cause`. Every write goes through `parseAuditRecord(...)` so a
malformed record raises `AUDIT_STDOUT.INVALID_RECORD` *before* a line
malformed record raises `AUDIT_STDOUT.INVALID_RECORD` _before_ a line
is written — log pipelines downstream never see a half-finished
record.

View File

@@ -32,7 +32,7 @@ rejected synchronously without consuming the slot.
- Boundary Zod validation — `AUDIT_STDOUT.INVALID_RECORD` on a bad
record.
- Bounded queue — `AUDIT_STDOUT.QUEUE_FULL` on timeout, drop counter
+ throttled warning.
- throttled warning.
- HMAC-SHA-256 over canonical bytes appended as `_hmac` field.
- `dispose()` drains the in-flight write and rejects subsequent
writes as `closed` drops.

View File

@@ -57,4 +57,3 @@ The resolver passes `runAuthSessionConformance` (null-only mode) from
`AUTH_ANON.INVALID_OPTIONS` — thrown only when the factory is handed a
non-object or unknown-key options bag. No per-request error paths exist by
design.

View File

@@ -152,4 +152,3 @@ Both surfaces pass `runAuthSessionConformance` from
`@suaveplan/api-auth`. Spec-conformance tests live alongside the source
under `src/factory/*.spec-conformance.test.ts` and
`src/proxy/*.spec-conformance.test.ts`.

View File

@@ -169,4 +169,3 @@ createBetterAuthSession createBetterAuthProvider
▼ ▼
Principal auth.handler(rewrittenRequest)
```

View File

@@ -41,7 +41,7 @@ provider, not this package.
`JWT_BEARER_SCHEME` and `createJwtBearerSecurityScheme(name)` for
OpenAPI emission.
- [`./error-codes`](src/error-codes/error-codes.md) — `AUTH_JWT_*` codes
+ `AuthJwtError` class.
- `AuthJwtError` class.
- [`./options`](src/options/options.md) — Zod-validated options
boundary.
@@ -132,4 +132,3 @@ returns a non-Principal value.
## License
MIT — see the repository.

View File

@@ -65,4 +65,3 @@ package.
## Stage / status
Concrete providers ship in Stages 6 / 8.

View File

@@ -57,11 +57,11 @@ ownership.register(replacementProvider);
## API Reference
- `class PathOwnership` — runtime registry.
- `constructor(options?: PathOwnershipOptions)`
- `register(provider): PathOwnershipClaim`
- `release(): void`
- `current(): ProviderClaim | null`
- `hasProvider(): boolean`
- `constructor(options?: PathOwnershipOptions)`
- `register(provider): PathOwnershipClaim`
- `release(): void`
- `current(): ProviderClaim | null`
- `hasProvider(): boolean`
- `interface PathOwnershipOptions { clock? }`
- `type OwnershipClock = () => number`
- `interface PathOwnershipClaim { providerId; prefix; claimedAt }`

View File

@@ -4,6 +4,7 @@ Stage-8.12 rolled session-token resolver for `@suaveplan/api`. Issues
RS256-signed JWTs with rotating `kid` slots, verifies tokens through the
rotator's active + retired-non-revoked key set, projects DB rows onto
the `@suaveplan/api-types` `Principal` contract, and contributes cookie
+ bearer OpenAPI security schemes.
This milestone ships the **session-resolver half** of the eventual dual
@@ -14,20 +15,20 @@ a successor change `api-auth-rolled-provider`.
<!-- begin:auto-docs -->
## Documentation tree
- [composition](./src/composition/composition.md) — Top-level factory `createRolledAuthAdapter` that wires the four sub-modules (`token-rotator`, `session`, `principal-pro…
- [composition](./src/composition/composition.md) — Top-level factory `createRolledAuthAdapter` that wires the four sub-modules (`token-rotator`, `session`, `principal-pro…
- [error-codes](./src/error-codes/error-codes.md) — Stable error-code registry for `@suaveplan/api-auth-rolled`. Every code is namespaced under `AUTH_ROLLED.*` so cross-pa…
- [error-codes](./src/error-codes/error-codes.md) — Stable error-code registry for `@suaveplan/api-auth-rolled`. Every code is namespaced under `AUTH_ROLLED.*` so cross-pa…
- [options](./src/options/options.md) — Defines the configuration shape accepted by `createRolledAuthAdapter` and the Zod schema that validates it. Operators w…
- [options](./src/options/options.md) — Defines the configuration shape accepted by `createRolledAuthAdapter` and the Zod schema that validates it. Operators w…
- [principal-projection](./src/principal-projection/principal-projection.md) — Projects the package's internal `UserRow` storage shape onto the abstract `Principal` contract published by `@suaveplan…
- [principal-projection](./src/principal-projection/principal-projection.md) — Projects the package's internal `UserRow` storage shape onto the abstract `Principal` contract published by `@suaveplan…
- [security-schemes](./src/security-schemes/security-schemes.md) — Contributes OpenAPI 3.1 `securitySchemes` entries describing both transports the rolled session resolver accepts — `Aut…
- [security-schemes](./src/security-schemes/security-schemes.md) — Contributes OpenAPI 3.1 `securitySchemes` entries describing both transports the rolled session resolver accepts — `Aut…
- [session](./src/session/session.md) — The rolling-JWT session resolver — the keystone of the rolled auth adapter. Builds a `SessionResolver` (per `@suaveplan…
- [session](./src/session/session.md) — The rolling-JWT session resolver — the keystone of the rolled auth adapter. Builds a `SessionResolver` (per `@suaveplan…
- [token-rotator](./src/token-rotator/token-rotator.md) — Owns the rotating-keyset state for the rolled session-token resolver. The active key signs new JWTs; retired-but-non-re…
- [token-rotator](./src/token-rotator/token-rotator.md) — Owns the rotating-keyset state for the rolled session-token resolver. The active key signs new JWTs; retired-but-non-re…
+ [composition](./src/composition/composition.md) — Top-level factory `createRolledAuthAdapter` that wires the four sub-modules (`token-rotator`, `session`, `principal-pro…
+ [composition](./src/composition/composition.md) — Top-level factory `createRolledAuthAdapter` that wires the four sub-modules (`token-rotator`, `session`, `principal-pro…
+ [error-codes](./src/error-codes/error-codes.md) — Stable error-code registry for `@suaveplan/api-auth-rolled`. Every code is namespaced under `AUTH_ROLLED.*` so cross-pa…
+ [error-codes](./src/error-codes/error-codes.md) — Stable error-code registry for `@suaveplan/api-auth-rolled`. Every code is namespaced under `AUTH_ROLLED.*` so cross-pa…
+ [options](./src/options/options.md) — Defines the configuration shape accepted by `createRolledAuthAdapter` and the Zod schema that validates it. Operators w…
+ [options](./src/options/options.md) — Defines the configuration shape accepted by `createRolledAuthAdapter` and the Zod schema that validates it. Operators w…
+ [principal-projection](./src/principal-projection/principal-projection.md) — Projects the package's internal `UserRow` storage shape onto the abstract `Principal` contract published by `@suaveplan…
+ [principal-projection](./src/principal-projection/principal-projection.md) — Projects the package's internal `UserRow` storage shape onto the abstract `Principal` contract published by `@suaveplan…
+ [security-schemes](./src/security-schemes/security-schemes.md) — Contributes OpenAPI 3.1 `securitySchemes` entries describing both transports the rolled session resolver accepts — `Aut…
+ [security-schemes](./src/security-schemes/security-schemes.md) — Contributes OpenAPI 3.1 `securitySchemes` entries describing both transports the rolled session resolver accepts — `Aut…
+ [session](./src/session/session.md) — The rolling-JWT session resolver — the keystone of the rolled auth adapter. Builds a `SessionResolver` (per `@suaveplan…
+ [session](./src/session/session.md) — The rolling-JWT session resolver — the keystone of the rolled auth adapter. Builds a `SessionResolver` (per `@suaveplan…
+ [token-rotator](./src/token-rotator/token-rotator.md) — Owns the rotating-keyset state for the rolled session-token resolver. The active key signs new JWTs; retired-but-non-re…
+ [token-rotator](./src/token-rotator/token-rotator.md) — Owns the rotating-keyset state for the rolled session-token resolver. The active key signs new JWTs; retired-but-non-re…
<!-- end:auto-docs -->
## Why
@@ -80,4 +81,3 @@ setInterval(async () => {
## License
MIT

View File

@@ -122,4 +122,3 @@ const host = createTestHost({
Until then, callers wire the adapter directly via
`createStaticPrincipalAdapter()` as shown above.

View File

@@ -93,9 +93,9 @@ const principal = await chain.resolve(new Request("https://example.test"));
## Versioning
1.0`. Graduates to `1.0.0` when the Stage 1
contract bundle (`api-types`, `api-auth`, `api-tenant-resolver`,
`api-secrets`, `api-db-adapter`) all reach 1.0.
1.0`. Graduates to`1.0.0`when the Stage 1
contract bundle (`api-types`,`api-auth`,`api-tenant-resolver`,
`api-secrets`,`api-db-adapter`) all reach 1.0.
## Dependencies
@@ -106,4 +106,3 @@ contract bundle (`api-types`, `api-auth`, `api-tenant-resolver`,
| `@suaveplan/types` | Cross-package contracts (logger, clock, etc.). |
| `tslib` | TypeScript runtime helpers (catalog). |
| `zod` | Boundary validation for security-scheme contributions. |

View File

@@ -125,4 +125,3 @@ a no-op.
factory.
- `DB_ADAPTER.INVALID_OPTIONS` — Zod validation failed; issues attached
to `error.context.issues`.

View File

@@ -26,7 +26,7 @@ The result object exposes:
## Features
- Options are parsed once at factory time; the rest of the API is typed
against the *post-parse* `DbAdapterOptions`.
against the _post-parse_ `DbAdapterOptions`.
- Engine lookup is delegated to `buildEngineAdapter`, so any
declaration-merged engine works automatically.
- Tenant scoping config (`tenantColumn`, `autoInjectInserts`) flows from

View File

@@ -24,7 +24,7 @@ payloads.
- `parseDbAdapterOptions(input)` — convenience wrapper that converts every Zod
failure into a typed `DbAdapterError` with code `INVALID_OPTIONS`. Issues
are surfaced through `error.context.issues`.
- `engine` is `z.string().min(1)` — the *literal value* check happens against
- `engine` is `z.string().min(1)` — the _literal value_ check happens against
the open `DatabaseEngineRegistry` so declaration-merged engines pass.
- `pool` is an optional sub-schema with its own defaults; absent values are
forwarded as `undefined` to the engine layer rather than guessed.

View File

@@ -10,7 +10,7 @@ This module is the load-bearing safety boundary between a plugin route handler
and the underlying admin database surface. Every SELECT/UPDATE/DELETE that the
route handler issues is rewritten in flight to carry a `WHERE tenant_id = ?`
predicate; every INSERT either carries the tenant column or is rejected (or
auto-injected, configurable). The contract is enforced *at the wrapper*, so a
auto-injected, configurable). The contract is enforced _at the wrapper_, so a
route handler literally cannot read another tenant's rows even if the plugin
author is malicious.

View File

@@ -75,4 +75,3 @@ The final merged pragma map is `{ ...DEFAULT_PRAGMAS, ...options.pragmas }`, so
## License
MIT.

View File

@@ -109,4 +109,3 @@ parentPort?.once("message", (boot) => {
- [`src/tenant-scoped/tenant-scoped.md`](./src/tenant-scoped/tenant-scoped.md)
- [`src/error-codes/error-codes.md`](./src/error-codes/error-codes.md)
- [`src/testing/testing.md`](./src/testing/testing.md)

View File

@@ -102,4 +102,3 @@ underlying `z.ZodError` on `cause`.
## License
MIT — Suaveplan Team.

View File

@@ -87,4 +87,3 @@ console.log(result.delivered);
- [`payload-codec`](./src/payload-codec/payload-codec.md)
- [`error-codes`](./src/error-codes/error-codes.md)
- [`testing`](./src/testing/testing.md)

View File

@@ -66,4 +66,3 @@ slimmer dependency closure.
Original. Owned by the `api-stack` super-category.
Passes the abstract conformance harness with the same invariants Yoga
satisfies — see `src/engine/apollo-engine.spec-conformance.test.ts`.

View File

@@ -16,7 +16,7 @@ boundary.
The module also re-exports the unmodified `runGraphqlEngineConformance`
so downstream packages that want to run the harness against a
*different* factory (e.g. a federated subgraph composing Apollo + a
_different_ factory (e.g. a federated subgraph composing Apollo + a
custom transport) keep a single import surface.
## Features

View File

@@ -94,4 +94,3 @@ the `ui` label.
## License
MIT

View File

@@ -104,4 +104,3 @@ bunx --bun turbo lint typecheck test:coverage --filter=@suaveplan/api-graphql-ui
100% statements / branches / functions / lines required on touched
modules (`provider: istanbul`).

View File

@@ -61,4 +61,4 @@ if (cspAllowsInlineScripts(myCspValue)) {
Inline-style allowance is documented in the module header so future
maintainers don't tighten the policy without understanding why. The
predicate operates on the raw header string rather than parsing the CSP
grammar — sufficient for the conformance check.
grammar — sufficient for the conformance check.

View File

@@ -87,4 +87,3 @@ const result = await engine.executeQuery({ query: "{ ping }" }, ctx);
## License
MIT — Suaveplan Team.

View File

@@ -10,6 +10,7 @@ Bridges the `WsBroker` contract from `@suaveplan/api-ws` to GraphQL
`Subscription` resolvers via `AsyncIterable<Payload>`. The Yoga engine
wires `graphql-ws` over the host's already-mounted WebSocket transport;
this module is the per-subscription helper that converts a broker `topic`
+ `tenant` pair into an iterator the engine can consume.
Tenant filtering is enforced at the bridge: only `BrokerEnvelope` items
@@ -20,10 +21,10 @@ within one tick.
## Features
- Returns `AsyncIterable<Payload>` with an explicit `close()` accessor.
- Queues payloads when no consumer is awaiting; resolves immediately when
+ Returns `AsyncIterable<Payload>` with an explicit `close()` accessor.
+ Queues payloads when no consumer is awaiting; resolves immediately when
a consumer is parked.
- Calls `GraphqlYogaError("SUBSCRIPTION_TRANSPORT_UNAVAILABLE")` when the
+ Calls `GraphqlYogaError("SUBSCRIPTION_TRANSPORT_UNAVAILABLE")` when the
host hands a `null` broker, satisfying the design doc §10.4 contract.
## Usage: Basic
@@ -59,7 +60,7 @@ const subscription = {
## API Reference
- `subscribeBrokerTopic(broker, topic, tenant)` — returns
+ `subscribeBrokerTopic(broker, topic, tenant)` — returns
`AsyncIterable<Payload> & { close(): Promise<void> }`.
## Implementation Notes

View File

@@ -78,4 +78,3 @@ Altair packages land .
- `@suaveplan/error` — `defineCodes` + `SuaveplanError` base.
- `zod` — subscription protocol envelope validation.
- `tslib` — TypeScript helper functions.

View File

@@ -115,4 +115,3 @@ bun add @suaveplan/api-host
## License
MIT

View File

@@ -65,14 +65,14 @@ await registry.dispose();
## API Reference
- `class CapabilityRegistry`
- `register(reg)`: void
- `lookup<Id>(id): CapabilityRegistration<Id> | undefined`
- `require<Id>(id): CapabilityRegistration<Id>`
- `has(id): boolean`
- `ids(): readonly CapabilityId[]`
- `size(): number`
- `assertSatisfies(ranges: Record<string, string>): void`
- `dispose(): Promise<void>`
- `register(reg)`: void
- `lookup<Id>(id): CapabilityRegistration<Id> | undefined`
- `require<Id>(id): CapabilityRegistration<Id>`
- `has(id): boolean`
- `ids(): readonly CapabilityId[]`
- `size(): number`
- `assertSatisfies(ranges: Record<string, string>): void`
- `dispose(): Promise<void>`
- `defineCapability<const Id>(reg): CapabilityRegistration<Id>`
- `CapabilityScope`: `"host" | "plugin" | "request"`
- `CapabilityRegistration<Id>`: shape consumed by `register`.

View File

@@ -73,12 +73,12 @@ for (const name of HOST_COUNTER_NAMES) {
- `HOST_COUNTER_NAMES` — `readonly` tuple of every counter name.
- `HOST_HISTOGRAM_NAMES` — `readonly` tuple of every histogram name.
- `HostInstruments`:
- `meter: Meter`
- `tenantLabelOptIn: boolean`
- `counters: Readonly<Record<HostCounterName, Counter>>`
- `histograms: Readonly<Record<HostHistogramName, Histogram>>`
- `forPlugin(name): PluginInstruments`
- `dispose(): void`
- `meter: Meter`
- `tenantLabelOptIn: boolean`
- `counters: Readonly<Record<HostCounterName, Counter>>`
- `histograms: Readonly<Record<HostHistogramName, Histogram>>`
- `forPlugin(name): PluginInstruments`
- `dispose(): void`
- `PluginInstruments`: `{ meter: Meter; pluginName: string }`.
## Implementation Notes

View File

@@ -112,7 +112,7 @@ const PIPELINE_STEPS: readonly PipelineStepName[];
## Implementation Notes
The pipeline runs route-match *first* (technically step 2 in §6) so
The pipeline runs route-match _first_ (technically step 2 in §6) so
later step gates (`auth`, `csrf`, `rateLimit`, `idempotent`) can
consult per-route policy. The §6 ordering of "intake → match" is
preserved at the engine adapter; this driver assumes the intake has

View File

@@ -54,7 +54,7 @@ package adds only the jobs-specific verbs.
## Local conformance harness
The proposal originally referenced `runJobsConformance` from
`@suaveplan/job-scheduler/testing`. That harness does not yet exist;
`@suaveplan/job-scheduler/testing`. That harness does not yet exist;
this package therefore provides its own
`JOBS_CONFORMANCE_CASES` + `runJobsConformance` under `./testing`. The
surface matches what the upstream harness is intended to be — a
@@ -67,4 +67,3 @@ in-repo substitute can be swapped out without changing consumers.
bunx turbo lint typecheck test --filter=@suaveplan/api-jobs-worker-proxy
bunx turbo test:coverage --filter=@suaveplan/api-jobs-worker-proxy
```

View File

@@ -67,8 +67,8 @@ client.registerQueued("send-email", async (payload) => {
- `JobsProxyClient extends JobRegistrationHandle` — adds `close()` and a
`sizes` accessor reporting registration counts.
- `JobsProxyClientOptions`:
- `handlerTimeoutMs?: number` — per-dispatch timeout, `0` disables.
- `onHandlerError?: (err, ctx) => void` — observer hook.
- `handlerTimeoutMs?: number` — per-dispatch timeout, `0` disables.
- `onHandlerError?: (err, ctx) => void` — observer hook.
## Implementation Notes

View File

@@ -48,11 +48,11 @@ try {
## API Reference
- `JOBS_PROXY_CODES` — readonly registry of five string codes:
- `JOB_NOT_DECLARED` — name absent from manifest whitelist.
- `HANDLER_THREW` — worker-side handler threw or rejected.
- `PROXY_DISPOSED` — operation attempted after `close()`.
- `JOB_TIMEOUT` — dispatch exceeded `handlerTimeoutMs`.
- `DUPLICATE_REGISTRATION` — same name registered twice.
- `JOB_NOT_DECLARED` — name absent from manifest whitelist.
- `HANDLER_THREW` — worker-side handler threw or rejected.
- `PROXY_DISPOSED` — operation attempted after `close()`.
- `JOB_TIMEOUT` — dispatch exceeded `handlerTimeoutMs`.
- `DUPLICATE_REGISTRATION` — same name registered twice.
- `JobsProxyCode` — union of every literal value in the registry.
- `JobsProxyError(code, message, options?)` — typed error class.

View File

@@ -63,12 +63,12 @@ server.close("plugin-unload");
- `createJobsProxyServer(port, mainHandle, opts) -> JobsProxyServer`.
- `JobsProxyServer`:
- `close(reason?: string): void` — idempotent disposer.
- `registeredCount: number` — count of accepted registrations.
- `close(reason?: string): void` — idempotent disposer.
- `registeredCount: number` — count of accepted registrations.
- `JobsProxyServerOptions`:
- `declaredJobs: ReadonlyArray<{name, kind}>` — manifest whitelist.
- `runRpcTimeoutMs?: number` — optional per-tick RPC timeout.
- `onDispatchError?: (err, ctx) => void` — telemetry hook.
- `declaredJobs: ReadonlyArray<{name, kind}>` — manifest whitelist.
- `runRpcTimeoutMs?: number` — optional per-tick RPC timeout.
- `onDispatchError?: (err, ctx) => void` — telemetry hook.
## Implementation Notes

View File

@@ -79,4 +79,3 @@ await fsm.transition("startCompleted");
| `LIFECYCLE.MUTEX_HELD` | `assertAcquired` called on a null `tryAcquire` result |
| `LIFECYCLE.RETRY_BUDGET_EXHAUSTED` | start_failed × 3 OR crashed × 3 in window |
| `LIFECYCLE.REINSTALL_REQUIRED_VERSION_DOWNGRADE` | `next.version < prev.version` |

View File

@@ -73,4 +73,4 @@ sections 8 (Hot reload), 8.1a (RollbackJournal), 8.2 (cache busting).
- Version: `0.1.0`
- Wave: Stage 1 (Contracts)
- Consumers: `@suaveplan/api-host` (Stage 2) implements the contracts here.
- Consumers: `@suaveplan/api-host` (Stage 2) implements the contracts here.

View File

@@ -88,7 +88,7 @@ export type ModuleResolverFactory<M = unknown> = () => ModuleResolver<M>;
## Implementation Notes
`resolve` returns the *evaluated namespace*, not a thenable wrapping it —
`resolve` returns the _evaluated namespace_, not a thenable wrapping it —
the host's lifecycle FSM treats a successful return as a synchronous
"module loaded" signal even though the call itself is async. Concrete
implementations are free to memoise nothing (the cache-bust strategy in

View File

@@ -95,4 +95,3 @@ server.close();
## License
MIT

View File

@@ -33,7 +33,7 @@ proxy).
`WireLogRecord` with defaults.
- `runLoggerProxyConformance(plugin, options?)` — full paired
harness: client + server + captured-output array + control sender
+ `dispose()`.
- `dispose()`.
## Usage: Basic

View File

@@ -108,4 +108,3 @@ Every code is namespaced under `MANIFEST.*` and produced by `defineCodes`. The e
## License
MIT

View File

@@ -123,4 +123,3 @@ import { MIGRATION_CODES } from "@suaveplan/api-migrations/error-codes/error-cod
Server (Bun + Node). Uses `node:crypto` for SHA-256 and `node:fs/promises`
for directory scanning and file reads.

View File

@@ -80,4 +80,3 @@ const res = handler();
## License
MIT

View File

@@ -127,4 +127,3 @@ bunx --bun turbo lint typecheck test:coverage --filter=@suaveplan/api-openapi-ui
100% statements / branches / functions / lines required on touched
modules (`provider: istanbul`).

View File

@@ -78,4 +78,3 @@ for (const name of listBundledAssetNames()) {
- `OPENAPI_UI_SWAGGER.ASSETS_MISSING` — bundled mode requested without `assetBasePath`, or a bundled asset name is not registered.
Both surfaces raise at factory-call time (eager validation), so misconfiguration is caught at boot rather than at first request.

View File

@@ -220,4 +220,3 @@ Server-tier (Hono-typed). Runs in Bun + Node.
## License
MIT

View File

@@ -85,4 +85,3 @@ app.post("/users/:id/delete", async (c) => {
- `auditService` registered with `ctx.registerService("auditService", ...)` and exposed through the typed `ServiceRegistry` slot.
- Hono `ContextVariableMap` augmented with `audit: AuditContext` so handlers fire explicit events without import boilerplate.
- Four OpenTelemetry instruments: `audit.requests_recorded_total{outcome,sampled}`, `audit.requests_excluded_total`, `audit.sink_failures_total`, `audit.middleware_duration_ms`.

View File

@@ -192,4 +192,3 @@ createAuthPlugin({
- `better-auth ^1.4` (resolves to 1.6.x via the catalog).
- `hono`, `@hono/zod-openapi`, `zod` — supplied by the host
application; the plugin imports them directly.

View File

@@ -65,4 +65,3 @@ each ships green on its own:
`bunx turbo lint typecheck test --filter=@suaveplan/api-plugin-campaigns`
plus `bunx turbo test:coverage --filter=@suaveplan/api-plugin-campaigns`
at 100/100/100/100 are required for every chunk to land.

View File

@@ -94,4 +94,3 @@ app.onError((err, c) => respondProblemJson(c, err));
## License
MIT — © Suaveplan Team

View File

@@ -108,4 +108,3 @@ Migration metrics are emitted by `@suaveplan/api-migrations` under its own meter
## Errors
Every failure path raises `DatabaseError` carrying one of eight `DATABASE_*` codes: `CONNECTION_FAILED`, `POOL_EXHAUSTED`, `POOL_CLOSED`, `QUERY_FAILED`, `MIGRATION_FAILED`, `LOCK_CONTENDED`, `TENANT_INVALID`, `INVALID_CONFIG`. See [`error-codes/error-codes.md`](./src/error-codes/error-codes.md).

View File

@@ -193,4 +193,3 @@ table and `context.statusCode` mapping.
application.
- `@suaveplan/api-plugin-auth` — used by `requireAuth()` route gate
when `requireAuthForRoutes: true` (default).

View File

@@ -109,4 +109,3 @@ for the full list. Highlights:
`0.1.0` — feature parity with upstream `@suaveplan/api-plugin-events`
modulo the explicit scope-outs above. 100% line / branch / function /
statement coverage. Real SurrealDB testcontainer integration.

View File

@@ -102,4 +102,3 @@ bunx --bun turbo lint typecheck test:coverage --filter=@suaveplan/api-plugin-exa
100% Istanbul coverage is enforced; the manifest `errors.statusMap`
is verified end-to-end against the runtime by the integration suite.

View File

@@ -86,4 +86,3 @@ DELETE /v1/exports/{id} → { success: true }
## Scope notes
This package ships the upstream §2 functional surface (CSV / JSON / SpreadsheetML XML exporters + in-memory job store + four-route Hono group). The upstream §3–§4 enhancements (streaming exporters, S3-backed blob store, auth/permissions/rate-limit/idempotency middleware, OOXML/ZIP writer, scheduled exports, signed download URLs, Parquet/Markdown formats) are deferred to follow-up changes — they require downstream plugins that have not yet landed .

View File

@@ -55,4 +55,4 @@ const result = new XlsxExporter().export(rows, cols, { sheetName: "WideExport" }
## Implementation Notes
The output is *SpreadsheetML 2006* — a worksheet XML payload, not the ZIP package most consumers think of when they hear "xlsx". This is a deliberate carry-over from the upstream package which chose XML over the `xlsx` npm dependency. The MIME type is set to the OOXML sheet content-type so HTTP consumers can still dispatch by `Content-Type`; the filename suffix is `.xlsx.xml` to signal the format honestly. The sheet name is escaped into the `<worksheet sheetName="…">` attribute even though the bare-worksheet form is not strictly required to carry one — keeping the attribute lets downstream upgrades wrap this document in an OOXML/ZIP package without re-emitting the worksheet. The `columnIndexToLetter` loop uses `Math.floor(n / 26) - 1` so the multi-letter overflow is exactly Excel-compatible: `26 → "AA"`, `51 → "AZ"`, `52 → "BA"`, `676 → "ZA"`, `701 → "ZZ"`, `702 → "AAA"`. The boolean cell uses `1`/`0` (not `true`/`false`) because OOXML requires the integer form.
The output is _SpreadsheetML 2006_ — a worksheet XML payload, not the ZIP package most consumers think of when they hear "xlsx". This is a deliberate carry-over from the upstream package which chose XML over the `xlsx` npm dependency. The MIME type is set to the OOXML sheet content-type so HTTP consumers can still dispatch by `Content-Type`; the filename suffix is `.xlsx.xml` to signal the format honestly. The sheet name is escaped into the `<worksheet sheetName="…">` attribute even though the bare-worksheet form is not strictly required to carry one — keeping the attribute lets downstream upgrades wrap this document in an OOXML/ZIP package without re-emitting the worksheet. The `columnIndexToLetter` loop uses `Math.floor(n / 26) - 1` so the multi-letter overflow is exactly Excel-compatible: `26 → "AA"`, `51 → "AZ"`, `52 → "BA"`, `676 → "ZA"`, `701 → "ZZ"`, `702 → "AAA"`. The boolean cell uses `1`/`0` (not `true`/`false`) because OOXML requires the integer form.

View File

@@ -53,4 +53,4 @@ const inst = getExportInstruments(); // rebinds against the new provider
## Implementation Notes
The bundle is held in a module-scope `let cached: ExportInstruments | undefined` rather than in a class instance, because the function is the only documented entry point and there is no per-plugin state to keep — every plugin instance in a process shares the same meter. Counters use `add(value, attributes?)`; the `outcome` attribute is set by the call site rather than declared on the instrument because OTel attributes are runtime keys, not instrument metadata. The histogram unit is set to `"ms"` (lower-case) per OTel semantic conventions for latency. Resetting between tests is mandatory: meter handles cache internally inside `@opentelemetry/api`, so without `resetExportInstruments()` a test would emit to whichever provider was installed when the *first* `getExportInstruments()` call ran — usually the global default provider, which loses every metric.
The bundle is held in a module-scope `let cached: ExportInstruments | undefined` rather than in a class instance, because the function is the only documented entry point and there is no per-plugin state to keep — every plugin instance in a process shares the same meter. Counters use `add(value, attributes?)`; the `outcome` attribute is set by the call site rather than declared on the instrument because OTel attributes are runtime keys, not instrument metadata. The histogram unit is set to `"ms"` (lower-case) per OTel semantic conventions for latency. Resetting between tests is mandatory: meter handles cache internally inside `@opentelemetry/api`, so without `resetExportInstruments()` a test would emit to whichever provider was installed when the _first_ `getExportInstruments()` call ran — usually the global default provider, which loses every metric.

View File

@@ -56,4 +56,4 @@ const routes = createExportRoutes({
## Implementation Notes
The `runPostExport` helper in `post-handler.ts` owns the bulk of the work — the route definitions stay in this file so that the OpenAPI schema is one click away from the route name. Failure paths set `job.status = "failed"` and persist `job.error` *before* returning, so `GET /:id` immediately afterwards sees the failure record (mirroring upstream behaviour). The `downloadUrl` response is `/${jobId}/download` — the actual streaming endpoint is upstream §3 future work, deferred in this migration. The downstream consumer is expected to combine this `downloadUrl` with the `basePath` themselves (e.g. `${basePath}${downloadUrl}`) — the upstream package made the same choice and the route layer cannot know the full mount path. Every route is also registered against an explicit `createRoute(...)` descriptor so an OpenAPI document generated by `OpenAPIHono.getOpenAPIDocument()` includes the export routes with full Zod-derived schemas.
The `runPostExport` helper in `post-handler.ts` owns the bulk of the work — the route definitions stay in this file so that the OpenAPI schema is one click away from the route name. Failure paths set `job.status = "failed"` and persist `job.error` _before_ returning, so `GET /:id` immediately afterwards sees the failure record (mirroring upstream behaviour). The `downloadUrl` response is `/${jobId}/download` — the actual streaming endpoint is upstream §3 future work, deferred in this migration. The downstream consumer is expected to combine this `downloadUrl` with the `basePath` themselves (e.g. `${basePath}${downloadUrl}`) — the upstream package made the same choice and the route layer cannot know the full mount path. Every route is also registered against an explicit `createRoute(...)` descriptor so an OpenAPI document generated by `OpenAPIHono.getOpenAPIDocument()` includes the export routes with full Zod-derived schemas.

View File

@@ -77,4 +77,4 @@ try {
## Implementation Notes
The two guards run in a specific order: row count is checked *before* the exporter runs so a runaway provider doesn't burn CPU encoding fields the route layer is about to reject. File size is checked *after* encoding because cell-level transforms and XML/JSON overhead can push a "small" row count over the byte cap. The `default:` branch in `getExporter` is reachable only if a caller bypasses the Zod boundary (the schema enumerates `csv|json|xlsx`); the `format as string` cast in the error message is intentional — we want the actual value the caller passed to surface in logs, not the post-narrowing `never`. `provider.fetch` errors are wrapped rather than thrown raw because the route layer needs to distinguish a *bad provider* (502-class) from a *bad request* (400-class), and the `ExportError` code is the contract that drives that branching upstream.
The two guards run in a specific order: row count is checked _before_ the exporter runs so a runaway provider doesn't burn CPU encoding fields the route layer is about to reject. File size is checked _after_ encoding because cell-level transforms and XML/JSON overhead can push a "small" row count over the byte cap. The `default:` branch in `getExporter` is reachable only if a caller bypasses the Zod boundary (the schema enumerates `csv|json|xlsx`); the `format as string` cast in the error message is intentional — we want the actual value the caller passed to surface in logs, not the post-narrowing `never`. `provider.fetch` errors are wrapped rather than thrown raw because the route layer needs to distinguish a _bad provider_ (502-class) from a _bad request_ (400-class), and the `ExportError` code is the contract that drives that branching upstream.

View File

@@ -41,4 +41,4 @@ providers.set("orders", myOrderProvider);
## Implementation Notes
The augmentation pattern is identical to `@suaveplan/api-plugin-database`'s `registry-augmentation.ts` — both files exist purely to add slots to the shared registry interface defined by `@suaveplan/api-plugin/service-registry`. Without this file, every call site would have to cast the `getService("exportProviders")` return value, defeating the type safety that the `ServiceRegistry` interface was designed to provide. The two slots cover the two distinct concerns the plugin owns: the *registry* of data providers (writable by every consumer that wants to expose a resource for export) and the *store* of jobs (mutated by the route layer, observable by anyone tracking export progress). Exposing both is a deliberate departure from upstream, which exposed them via `ctx.registerService(...)` at runtime but never declared the corresponding types.
The augmentation pattern is identical to `@suaveplan/api-plugin-database`'s `registry-augmentation.ts` — both files exist purely to add slots to the shared registry interface defined by `@suaveplan/api-plugin/service-registry`. Without this file, every call site would have to cast the `getService("exportProviders")` return value, defeating the type safety that the `ServiceRegistry` interface was designed to provide. The two slots cover the two distinct concerns the plugin owns: the _registry_ of data providers (writable by every consumer that wants to expose a resource for export) and the _store_ of jobs (mutated by the route layer, observable by anyone tracking export progress). Exposing both is a deliberate departure from upstream, which exposed them via `ctx.registerService(...)` at runtime but never declared the corresponding types.

View File

@@ -97,4 +97,3 @@ await api.start();
- [`schemas/routes/initiate`](./src/schemas/routes/initiate.md) — chunked-init request/response.
- [`schemas/routes/status`](./src/schemas/routes/status.md) — chunk / complete / status / abort schemas.
- [`types/types`](./src/types/types.md) — public data model.

View File

@@ -105,4 +105,3 @@ This package is functional-parity with upstream `@suaveplan/api-plugin-graphql`
## License
MIT.

View File

@@ -138,4 +138,3 @@ upstream import success/failure).
## License
MIT

View File

@@ -106,4 +106,3 @@ const service = new ApnService({ config, transport });
See `src/transport/apn-transport/apn-transport.md` for the full
contract.

View File

@@ -157,4 +157,3 @@ const plugin = createPaymentPlugin({
- [`./src/otel/instruments.md`](./src/otel/instruments.md) — 5 counters + 1 histogram.
- [`./src/error-codes/error-codes.md`](./src/error-codes/error-codes.md) — 15 `PAYMENT_PLUGIN_*` codes.
- [`./src/types/types.md`](./src/types/types.md) — `PaymentPluginOptions` / `PaymentPluginConfig`.

View File

@@ -1,4 +1,4 @@
# @suaveplan/api-plugin-rate-limit - Rate-limit plugin for @suaveplan/api. Wraps @suaveplan/rate-limiter as a definePlugin descriptor: per-route + per-tenant configuration, parameterised path matcher, IP whitelist, tenant multiplier applied to numeric limit, Hono middleware factory, 429 + Retry-After + X-RateLimit-* / RateLimit-* headers, OTel counters, RFC 7807 Problem+JSON body. Registers `rateLimitService` in the host ServiceRegistry.
# @suaveplan/api-plugin-rate-limit - Rate-limit plugin for @suaveplan/api. Wraps @suaveplan/rate-limiter as a definePlugin descriptor: per-route + per-tenant configuration, parameterised path matcher, IP whitelist, tenant multiplier applied to numeric limit, Hono middleware factory, 429 + Retry-After + X-RateLimit-*/ RateLimit-* headers, OTel counters, RFC 7807 Problem+JSON body. Registers `rateLimitService` in the host ServiceRegistry.
Rate-limit plugin for `@suaveplan/api`. Wraps `@suaveplan/rate-limiter`
as a `definePlugin` descriptor exposing per-route + per-tenant
@@ -130,4 +130,3 @@ readiness).
## Status
`0.1.0` — initial ship. Surface is final for the 0.1.x line.

View File

@@ -107,10 +107,9 @@ await api.start();
## Status
`0.1.0` — first migration wave shipping` with full upstream
`0.1.0` — first migration wave shipping`with full upstream
parity. Persistent backends (`SearchAdapter` implementations against
SurrealDB FTS / Typesense / Meilisearch), tenant scoping, auth gates,
rate limiting, and the enterprise-audit §4 enhancements (vector search,
synonyms, query DSL, saved searches, learning-to-rank) are tracked as
follow-up changes.

View File

@@ -98,4 +98,3 @@ The site plugin ships **zero runtime dependencies** on `@suaveplan/api-plugin-ra
- See [`docs/middleware-integration.md`](./docs/middleware-integration.md) for the canonical end-to-end wiring example and the recommended layering order (audit OUTERMOST → rate-limit MIDDLE → site routes INNERMOST).
- See [`src/integration/middleware-recommendations.md`](./src/integration/middleware-recommendations.md) for the `MIDDLEWARE_RECOMMENDATIONS` constant exposing recommended rate-limit windows and audit categories per route group.
- The pattern is proven by [`src/integration/middleware-composition.integration.test.ts`](./src/integration/middleware-composition.integration.test.ts) — a runnable composition asserting site behaviour, rate-limit headers, and audit event emission.

View File

@@ -106,4 +106,3 @@ This package follows the canonical API-plugin pattern pioneered by `@suaveplan/a
## License
MIT

View File

@@ -118,4 +118,3 @@ const app = defineApp({
## License
MIT

View File

@@ -200,4 +200,3 @@ The plugin emits three counters:
Counters bind lazily to the global `MeterProvider`; tests
re-bind by calling `resetUserInstruments()` after swapping the
provider.

View File

@@ -163,4 +163,3 @@ Operators can:
- [`services/registry-augmentation`](./src/services/registry-augmentation.md)
— typed `ServiceRegistry` slot.
- [`types/types`](./src/types/types.md) — public TS surface.

View File

@@ -118,4 +118,3 @@ Status label values: `delivered`, `exhausted`, `not_found`, `inactive`, `invalid
## License
MIT

View File

@@ -108,7 +108,7 @@ ctx.getService("nope"); // type error — not augmented
`PLUGIN_CODES` defines ten codes (`PLUGIN.NOT_FOUND`, `PLUGIN.ALREADY_REGISTERED`, `PLUGIN.INIT_FAILED`, `PLUGIN.DEP_MISSING`, `PLUGIN.CIRCULAR_DEPENDENCY`, `PLUGIN.INIT_TIMEOUT`, `PLUGIN.SHUTDOWN_FAILED`, `PLUGIN.INVALID_CONFIG`, `PLUGIN.HOOK_FAILED`, `PLUGIN.ABORTED`). Every code is raised by at least one production call site — the spec-conformance test pins this invariant.
## design decisions
## design decisions
- **Unified lifecycle runtime** — `PluginRegistry` calls `onRegister`/`onInit`/`onReady`/`onClose` directly; the `HookExecutor` rejects registration of these four names. Upstream's dual-runtime hazard is eliminated by construction.
- **`initTimeout`/`shutdownTimeout` are enforced** — every hook runs with an `AbortController`-bound `ctx.signal` that fires when the budget is exceeded.
@@ -116,4 +116,3 @@ ctx.getService("nope"); // type error — not augmented
- **Topological sort is deterministic** — alphabetical tie-break for equal-in-degree nodes makes boot logs reproducible across runs.
- **No `applyMigrations`** — migrations live in `@suaveplan/api-migrations`. The plugin registry knows nothing about databases.
- **No ALS singletons** (`getCurrentApp`, etc.). Callers thread `ctx` explicitly.

View File

@@ -98,4 +98,3 @@ bunx --bun turbo lint typecheck test:coverage --filter=@suaveplan/api-rate-limit
```
100% statements/branches/functions/lines (Istanbul).

View File

@@ -82,4 +82,3 @@ Reason label values: `"missing-scope" | "rule-deny" | "no-principal"`.
## License
MIT

View File

@@ -59,4 +59,3 @@ await slot.dispose(draining.slotId, { timeoutMs: 1_000 });
## Version
`0.1.0` — initial Stage-4 ship. Graduates to `1.0.0` when the plugin system as a whole is declared production-ready.

View File

@@ -98,12 +98,12 @@ r.registerPipelineRoute({
## API Reference
- `class PipelineRouteTable` —
- `register(reg: HonoPipelineRegistration): void`
- `unregister(method: string, path: string): void`
- `unregisterPlugin(plugin: string): void`
- `list(): readonly HonoPipelineRoute[]`
- `match(method: string, url: string): HonoPipelineRoute | null`
- `size(): number`
- `register(reg: HonoPipelineRegistration): void`
- `unregister(method: string, path: string): void`
- `unregisterPlugin(plugin: string): void`
- `list(): readonly HonoPipelineRoute[]`
- `match(method: string, url: string): HonoPipelineRoute | null`
- `size(): number`
- `interface HonoPipelineRoute` — `{ plugin, method, path, options, handler, params, statusMap }`. Structurally compatible with `@suaveplan/api-types/pipeline-router`'s `PipelineRoute`.
- `interface HonoPipelineRegistration` — `{ plugin, method, path, handler, options?, statusMap? }`.
- `interface HonoPipelineRouteOptions` — narrow subset of the host's `PipelineRouteOptions`: `auth`, `csrf`, `streaming`, `idempotent`, `runtimeValidation`, `auditable`, `rateLimit`, `request`, `responses`.

View File

@@ -71,4 +71,3 @@ await slot.dispose(draining.slotId, { timeoutMs: 100 });
- Foundation tests that need a real `Router`/`PipelineRouter` without a Hono peer.
- Plugin tests that previously hand-rolled their own router shim (`api-router-hono` carries the Hono peer-dep cost).
- The default `router: "in-memory"` slot in the Stage-5 `@suaveplan/api-host/testing` adapter table.

View File

@@ -75,4 +75,3 @@ const { dispatch } = createMainBridge(channel.port1 as never);
const res = await dispatch(new Request("https://api.local/world"));
console.log(await res.text()); // "hi /world"
```

View File

@@ -95,4 +95,3 @@ import { runRouterConformance } from "@suaveplan/api-router/testing";
## License
MIT

View File

@@ -24,7 +24,7 @@ values via wrapping responses and inspecting the request shape.
list; order is preserved.
- `next()` enforcement: a middleware that calls `next()` twice in a row
has its dispatch rejected. (Multiple sequential `next()` calls across
*different* middlewares are correct; only same-middleware repeats fail.)
_different_ middlewares are correct; only same-middleware repeats fail.)
- Synchronous return values are accepted (wrapped in `Promise.resolve`).
## Usage: Basic

View File

@@ -72,7 +72,7 @@ isolated copies and never accidentally share state across branches. The
runtime cost is one object-spread per call; modern V8 inlines this away
for short builder chains.
The `body` setter is typed as a *re-parameterising* method — calling
The `body` setter is typed as a _re-parameterising_ method — calling
`builder.body<T>(value)` returns a `ResponseBuilder<T>`, picking up the
new body type. This lets adapters call `.body(zodValidatedShape)` and the
inferred `RouterResponse.body` lines up with the validation output. The

View File

@@ -104,7 +104,7 @@ keeps the steps-7-8 insertion contract honest: middleware only enters
through `use()`, and only at the post-validation boundary when supplied
by plugins.
Collision detection compares the *compiled* path key (`":id"` vs.
Collision detection compares the _compiled_ path key (`":id"` vs.
`":userId"` both normalise to `:`-form). Adapters that need parameter-name
preservation can introspect `RouteMatch.path` for the originally registered
form.

View File

@@ -90,8 +90,8 @@ runPipelineRouterConformance({
## API Reference
- `interface PipelineRouterConformanceFactory` —
- `label: string`
- `build(): { router: PipelineRouter; register: (reg: PipelineRouteRegistration) => void; reload?: () => void }`
- `label: string`
- `build(): { router: PipelineRouter; register: (reg: PipelineRouteRegistration) => void; reload?: () => void }`
- `interface PipelineRouteRegistration` — `{ plugin, method, path, handler, options?, statusMap? }`.
- `function runPipelineRouterConformance(factory): void` — declares one
`describe()` block with six required `it()` cases.

View File

@@ -84,4 +84,3 @@ participant whose `actions[name]` is missing fails at install with
`0.1.0` — bottom-up rebuild stage. Will graduate to `1.0.0` per the
foundation umbrella's Stage 6 acceptance gate (`a multi-step saga in
api-plugin-example runs end-to-end including a compensation path`).

View File

@@ -104,7 +104,7 @@ router.post("/webhook/saga", async (req) => {
## Implementation Notes
The emitter validates *outbound* payloads before pushing them onto the
The emitter validates _outbound_ payloads before pushing them onto the
bus. This is deliberate — `bus.emit` is fire-and-forget; if the
emitter trusted the caller and pushed a bad payload, every listener
would see corrupt data with no callback to recover. By failing at the

View File

@@ -96,4 +96,3 @@ if (result.outcome === "deny") {
Runtime: `@suaveplan/api-types`, `@suaveplan/error`, `@suaveplan/types`,
`@suaveplan/opentelemetry`, `@opentelemetry/api`, `tslib`, `zod`. No
`@suaveplan/api-*` runtime beyond `api-types`.

View File

@@ -92,4 +92,3 @@ lifecycle delegation (`LIFECYCLE_FAILED`).
the spec mandates: route registration, request round-trip, idempotent
lifecycle, error propagation with `cause` chain, and capability
passthrough including denied capability surfacing `SAND_PERM.DENIED`.

View File

@@ -89,4 +89,3 @@ import { runVmSandboxConformance } from "@suaveplan/api-sandbox-vm/testing";
runVmSandboxConformance("my-host", makeMyHandle);
```

View File

@@ -1,4 +1,4 @@
# @suaveplan/api-sandbox-wasm-host-bridge - Host bridge wrapping a @suaveplan/api-sandbox-wasm WasmSandboxHandle as a @suaveplan/api-host PluginModule. Validates the WASM ABI surface (__suaveplan_alloc / free / handle_request / routes / start / dispose + memory) at bridge-init, marshals SerializedRequest bytes into linear memory via __suaveplan_alloc, invokes __suaveplan_handle_request, reads the packed (responsePtr, responseLen) result, JSON-decodes the SerializedResponse, and unmarshals it back to a host Response. Traps surface as SAND_WASM_BRIDGE.HANDLER_TRAPPED with classified trapKind; denied capabilities surface as SAND_PERM.DENIED.
# @suaveplan/api-sandbox-wasm-host-bridge - Host bridge wrapping a @suaveplan/api-sandbox-wasm WasmSandboxHandle as a @suaveplan/api-host PluginModule. Validates the WASM ABI surface (__suaveplan_alloc / free / handle_request / routes / start / dispose + memory) at bridge-init, marshals SerializedRequest bytes into linear memory via__suaveplan_alloc, invokes __suaveplan_handle_request, reads the packed (responsePtr, responseLen) result, JSON-decodes the SerializedResponse, and unmarshals it back to a host Response. Traps surface as SAND_WASM_BRIDGE.HANDLER_TRAPPED with classified trapKind; denied capabilities surface as SAND_PERM.DENIED.
Host-integration layer that wraps a `@suaveplan/api-sandbox-wasm` `WasmSandboxHandle` as a `@suaveplan/api-host` `PluginModule`. Sandboxed-tier plugins compiled to WebAssembly export the `__suaveplan_*` ABI surface (`memory`, `__suaveplan_alloc`, `__suaveplan_free`, `__suaveplan_handle_request`, `__suaveplan_routes`, `__suaveplan_start`, `__suaveplan_dispose`); this bridge validates that surface, discovers the guest's routes once at bridge-init time, and emits per-route closures that marshal `SerializedRequest` records through the guest's linear memory.

View File

@@ -15,18 +15,19 @@ The split from `bridge.ts` is structural rather than mechanical: every
direct-variant import (`abi-direct/wasm-abi`, `abi-direct/memory`,
`abi-direct/marshal`) is referenced from this file only, so a future
removal of the direct variant collapses to deleting `bridge-direct.ts`
+ `abi-direct/` and trimming the dispatcher.
## Features
- `buildDirectInstall(options)` — accepts a `DirectVariantOptions`,
+ `buildDirectInstall(options)` — accepts a `DirectVariantOptions`,
returns the bridge's `SandboxedWasmPluginInstall`.
- Trap classification, capability-gate routing through
+ Trap classification, capability-gate routing through
`SAND_PERM.DENIED`, idempotent lifecycle — preserved exactly from
the shipped contract.
- Instrument registry plumbed with `abi_variant: "direct-i32-i64"` on
+ Instrument registry plumbed with `abi_variant: "direct-i32-i64"` on
every counter and histogram emission.
- Route closures are pure functions of the manifest + ABI exports;
+ Route closures are pure functions of the manifest + ABI exports;
re-instantiating the bridge against the same sandbox produces
identical `PipelineRoute[]` records.
@@ -81,7 +82,7 @@ try {
Internal entrypoint called by the dispatcher. Public consumers should
use `createSandboxedWasmPluginModule` from the package root.
- `options: DirectVariantOptions` — `sandbox`, `manifest`, optional
+ `options: DirectVariantOptions` — `sandbox`, `manifest`, optional
`instruments`.
Throws `SandboxWasmBridgeError` with code `INVALID_PLUGIN_SURFACE` if
@@ -89,14 +90,14 @@ ABI validation fails BEFORE any guest call.
## Implementation Notes
- The function never touches the sandbox's `instance` directly past
+ The function never touches the sandbox's `instance` directly past
the ABI validator — the typed `WasmAbiExports` view is the single
hand-off point.
- `freeBuffer` is called in a `finally` block so the request pointer
+ `freeBuffer` is called in a `finally` block so the request pointer
is released even when the response unmarshal throws. The response
pointer is released BEFORE unmarshal runs because Zod parsing may
allocate additional bytes that would shadow the original buffer.
- Capability denials surface through the canonical
+ Capability denials surface through the canonical
`SAND_PERM.DENIED` code, not the bridge-local `CAPABILITY_DENIED`.
Operators see one code for "permission denied" regardless of which
bridge raised it.

View File

@@ -98,4 +98,3 @@ bunx turbo lint typecheck test:coverage --filter=@suaveplan/api-sandbox-wasm
## License
MIT

View File

@@ -26,7 +26,9 @@ Shared Zod schemas describing Suaveplan API request/response shapes. The single
- [metadata](./src/events/metadata.md) — Defines the `EventMetadata` Zod schema for the distributed tracing and actor context attached to every domain event in…
- [helpers](./src/helpers/to-suaveplan-error.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [to-suaveplan-error](./src/helpers/to-suaveplan-error.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
<!-- end:auto-docs --> - [to-suaveplan-error](./src/helpers/to-suaveplan-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [to-suaveplan-error](./src/helpers/to-suaveplan-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [to-suaveplan-error](./src/helpers/to-suaveplan-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [to-suaveplan-error](./src/helpers/to-suaveplan-error.md) — <!-- begin:auto-docs --> _(no submodules)_
## Modules
| Module | Description |

View File

@@ -133,4 +133,3 @@ unchanged — see `src/adapter/adapter.spec-conformance.test.ts`.
## License
MIT

Some files were not shown because too many files have changed in this diff Show More