first commit
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
# `capability-id` — `"auth.provider"` capability contribution
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
The `capability-id` module pins the runtime literal for the
|
||||
`"auth.provider"` capability this package contributes to the host's
|
||||
capability registry. The closed `CapabilityId` union in
|
||||
`@suaveplan/api-types` already includes `"auth.provider"` (alongside
|
||||
`"auth.session"`); this module simply exposes a typed constant and predicate
|
||||
so concrete providers, the host's capability resolver, and downstream
|
||||
plugin manifests can author against a single value rather than a free-form
|
||||
string. Centralising the literal gives us one place to grep if the id ever
|
||||
needs to evolve via an OpenSpec change.
|
||||
|
||||
## Features
|
||||
|
||||
- `AUTH_PROVIDER_CAPABILITY_ID` constant pinned to `"auth.provider"` via a
|
||||
`satisfies` clause that fails the build if the literal drifts out of the
|
||||
`CapabilityId` union.
|
||||
- `isAuthProviderCapabilityId(value)` runtime type-guard for capability
|
||||
descriptors that flow through `unknown`.
|
||||
- `AuthProviderCapabilityId` type alias — `Extract<CapabilityId, "auth.provider">` —
|
||||
so consumers can name the type without re-spelling the literal.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { AUTH_PROVIDER_CAPABILITY_ID } from "@suaveplan/api-auth-provider/capability-id";
|
||||
|
||||
definePlugin({
|
||||
requires: [AUTH_PROVIDER_CAPABILITY_ID] as const,
|
||||
start: async (ctx) => {
|
||||
// ctx.capabilities["auth.provider"] is now in scope.
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
The host's capability resolver inspects untyped descriptors and narrows them
|
||||
to the typed slot via the predicate:
|
||||
|
||||
```ts
|
||||
import {
|
||||
AUTH_PROVIDER_CAPABILITY_ID,
|
||||
isAuthProviderCapabilityId,
|
||||
} from "@suaveplan/api-auth-provider/capability-id";
|
||||
|
||||
function bindCapability(descriptor: { id: string }) {
|
||||
if (isAuthProviderCapabilityId(descriptor.id)) {
|
||||
// descriptor.id is now typed as "auth.provider".
|
||||
registerSlot(AUTH_PROVIDER_CAPABILITY_ID, descriptor);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `const AUTH_PROVIDER_CAPABILITY_ID: AuthProviderCapabilityId` — frozen
|
||||
literal.
|
||||
- `function isAuthProviderCapabilityId(value: string): value is AuthProviderCapabilityId`
|
||||
- `type AuthProviderCapabilityId = Extract<CapabilityId, "auth.provider">`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The `satisfies` clause is the load-bearing line of the module — if the
|
||||
`CapabilityId` union ever loses `"auth.provider"`, the build fails here
|
||||
rather than downstream. The predicate uses an equality test against the
|
||||
constant rather than the literal to keep the two in lockstep — if a future
|
||||
change renames the id, the constant and predicate move together. There is
|
||||
intentionally no companion type-augmentation block here; the union in
|
||||
`@suaveplan/api-types` is closed and pre-includes every capability the host
|
||||
knows about, so adding a capability is a co-located OpenSpec change to that
|
||||
package and not a downstream module-augmentation pattern.
|
||||
Reference in New Issue
Block a user