Initial Commit
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# `"tenant.provider"` — capability id
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`@suaveplan/api-types/capabilities` exports `CapabilityId`, the open
|
||||
union derived from the `CapabilityIdRegistry` interface. As of the
|
||||
Stage-1 coherence fixes the foundation registry includes
|
||||
`"tenant.provider"` directly, so this package no longer maintains a
|
||||
widening alias — the runtime constant lives here because the
|
||||
`TenantProvider` interface lives here, and host code needs a stable
|
||||
sentinel without re-typing the literal everywhere.
|
||||
|
||||
The module re-exports the canonical `CapabilityId` from api-types for
|
||||
source compatibility with consumers that historically imported it from
|
||||
this package.
|
||||
|
||||
## Features
|
||||
|
||||
- Runtime constant `TENANT_PROVIDER_CAPABILITY_ID = "tenant.provider"`.
|
||||
- Type alias `TenantProviderCapabilityId = typeof TENANT_PROVIDER_CAPABILITY_ID`.
|
||||
- Re-export of `CapabilityId` from `@suaveplan/api-types/capabilities` — source-compatible with callers that imported the widened alias previously.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { TENANT_PROVIDER_CAPABILITY_ID } from "@suaveplan/api-tenant-provider/capability-id";
|
||||
|
||||
if (manifest.requires?.includes(TENANT_PROVIDER_CAPABILITY_ID)) {
|
||||
// The plugin opted into per-tenant fan-out.
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
type CapabilityId,
|
||||
TENANT_PROVIDER_CAPABILITY_ID,
|
||||
} from "@suaveplan/api-tenant-provider/capability-id";
|
||||
|
||||
// A capability-checker that accepts both foundation and tenant-provider ids.
|
||||
function describeCapability(id: CapabilityId): string {
|
||||
switch (id) {
|
||||
case TENANT_PROVIDER_CAPABILITY_ID:
|
||||
return "tenant enumeration (per-tenant cron fan-out)";
|
||||
case "db":
|
||||
return "tenant-scoped database adapter";
|
||||
default:
|
||||
return id;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `const TENANT_PROVIDER_CAPABILITY_ID = "tenant.provider" as const`.
|
||||
- `type TenantProviderCapabilityId = "tenant.provider"`.
|
||||
- `type CapabilityId` — re-export of the canonical union from `@suaveplan/api-types/capabilities`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Foundation `CapabilityId` is `keyof CapabilityIdRegistry`, and the
|
||||
foundation registry already contributes `"tenant.provider"` directly,
|
||||
so a separate widening alias here would be redundant. Any third-party
|
||||
package adding its own capability id should extend
|
||||
`CapabilityIdRegistry` from `@suaveplan/api-types/capabilities` via
|
||||
`declare module` rather than re-introducing a widened type union — the
|
||||
registry pattern is the documented extension point.
|
||||
|
||||
The constant is `as const` so the value type narrows to the exact
|
||||
string literal `"tenant.provider"`, which is what makes
|
||||
`expectTypeOf<typeof TENANT_PROVIDER_CAPABILITY_ID>().toEqualTypeOf<TenantProviderCapabilityId>()`
|
||||
hold at the type level.
|
||||
@@ -0,0 +1,31 @@
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import {
|
||||
type CapabilityId,
|
||||
TENANT_PROVIDER_CAPABILITY_ID,
|
||||
type TenantProviderCapabilityId,
|
||||
} from "./capability-id.js";
|
||||
|
||||
describe("TENANT_PROVIDER_CAPABILITY_ID", () => {
|
||||
it('is the literal "tenant.provider"', { timeout: 5000 }, () => {
|
||||
expect(TENANT_PROVIDER_CAPABILITY_ID).toBe("tenant.provider");
|
||||
});
|
||||
|
||||
it("narrows as a string literal at the type level", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const id: TenantProviderCapabilityId = TENANT_PROVIDER_CAPABILITY_ID;
|
||||
const widened: CapabilityId = id;
|
||||
expect(widened).toBe("tenant.provider");
|
||||
});
|
||||
|
||||
it("widened CapabilityId accepts both base and tenant.provider", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
// `db` is part of api-types' base CapabilityId union, so the
|
||||
// widened alias must still accept it.
|
||||
const fromBase: CapabilityId = "db";
|
||||
const fromExtension: CapabilityId = TENANT_PROVIDER_CAPABILITY_ID;
|
||||
expect(fromBase).toBe("db");
|
||||
expect(fromExtension).toBe("tenant.provider");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Runtime sentinel identifying this capability — string-keyed so it
|
||||
* survives type erasure and is safe to log / pass through wire formats.
|
||||
*
|
||||
* Plugins that declare `perTenant: true` jobs in their manifest
|
||||
* implicitly require this capability — the host resolves it
|
||||
* automatically — but the literal MUST exist as a value so
|
||||
* manifest-shape checks and host-side lookups can compare against it.
|
||||
*/
|
||||
export const TENANT_PROVIDER_CAPABILITY_ID = "tenant.provider" as const;
|
||||
|
||||
export type TenantProviderCapabilityId = typeof TENANT_PROVIDER_CAPABILITY_ID;
|
||||
|
||||
/**
|
||||
* Re-export of the canonical {@link
|
||||
* import("@suaveplan/api-types/capabilities").CapabilityId} union.
|
||||
*
|
||||
* As of the Stage-1 coherence fixes, `"tenant.provider"` is part of the
|
||||
* `CapabilityIdRegistry` shipped by `@suaveplan/api-types/capabilities`
|
||||
* directly, so this package no longer needs a widening alias. The
|
||||
* re-export remains for source compatibility with consumers that
|
||||
* imported `CapabilityId` from `@suaveplan/api-tenant-provider`.
|
||||
*/
|
||||
export type { CapabilityId } from "@suaveplan/api-types/capabilities";
|
||||
@@ -0,0 +1,5 @@
|
||||
export {
|
||||
type CapabilityId,
|
||||
TENANT_PROVIDER_CAPABILITY_ID,
|
||||
type TenantProviderCapabilityId,
|
||||
} from "./capability-id.js";
|
||||
Reference in New Issue
Block a user