@beignet/core
Version:
Core framework primitives for Beignet
56 lines (50 loc) • 1.65 kB
text/typescript
import type { ActivityTenant } from "../ports/audit.js";
import { type RequireOptions, requireTenant } from "../ports/auth.js";
const tenantScopeBrand = Symbol("TenantScope");
/**
* Branded tenant scope token for repository boundaries.
*
* Apps still own tenant resolution and tenant data modeling. `TenantScope`
* carries the already-resolved activity tenant through app-facing repository
* ports so adapters can apply tenant predicates without accepting arbitrary
* caller-provided tenant IDs.
*/
export type TenantScope = Readonly<{
id: string;
tenant: ActivityTenant;
readonly [tenantScopeBrand]: true;
}>;
/**
* Create a tenant scope from a resolved activity tenant.
*
* This does not load, create, or validate a tenant record. It only brands the
* already-resolved tenant for repository and adapter boundaries.
*/
export function createTenantScope(tenant: ActivityTenant): TenantScope {
return {
id: tenant.id,
tenant,
[tenantScopeBrand]: true,
};
}
/**
* Return a branded tenant scope from `ctx.tenant` or throw.
*
* Throws `TenantRequiredError` by default through `requireTenant`. Pass
* `options.error` to throw an app-owned error instead.
*/
export function requireTenantScope(
ctx: { tenant?: ActivityTenant | null },
options?: RequireOptions,
): TenantScope {
return createTenantScope(requireTenant(ctx, options));
}
/**
* Extract the stable tenant ID from a tenant scope.
*
* Repository adapters should use this helper at the persistence boundary
* instead of accepting raw tenant IDs from use cases.
*/
export function tenantScopeId(scope: TenantScope): string {
return scope.id;
}