@beignet/core
Version:
Core framework primitives for Beignet
643 lines (635 loc) • 15.8 kB
text/typescript
/**
* @beignet/core/ports
*
* A tiny, framework-agnostic subpath that standardizes how apps define and
* type their "ports" (outbound dependencies like db, cache, event bus).
*
* This subpath provides:
* - `definePorts` – helper to define a typed ports object
* - `PortsContext` – a generic type that describes ctx objects that carry ports
* - `EventBusPort` – interface for event bus implementations
* - `JobDispatcherPort` – interface for job dispatch implementations
* - `UnitOfWorkPort` – interface for app-owned transaction boundaries
* - `OutboxPort` – interface for durable event/job delivery storage
* - `IdempotencyPort` – interface for retry-safe command/key storage
* - `AuthPort` – interface for request authentication implementations
* - `AuditLogPort` – interface for audit/activity log implementations
* - `ClockPort` – interface for deterministic time
* - `EntitlementsPort` – interface for product access checks
* - `IdGeneratorPort` – interface for deterministic id generation
* - `LoggerPort` – interface for structured application logging
* - `GatePort` – interface for application authorization policies
* - `RateLimitPort` – interface for rate limiting implementations
* - `CachePort` – interface for cache implementations
* - `StoragePort` – interface for object/file storage implementations
* - `FlagsPort` – interface for feature flag evaluation implementations
* - `ErrorReporterPort` – interface for production error reporting
* - `LocksPort` – interface for lease-backed distributed locks
*
* Dedicated framework areas own their capability-specific APIs:
* - `@beignet/core/entitlements` owns product access helpers and test adapters
* - `@beignet/core/error-reporting` owns error reporting helpers and test adapters
* - `@beignet/core/flags` owns feature flag helpers and test adapters
* - `@beignet/core/idempotency` owns idempotency helpers and test adapters
* - `@beignet/core/locks` owns lease-backed lock helpers and test adapters
* - `@beignet/core/mail` owns `MailerPort` and mail test adapters
* - `@beignet/core/notifications` owns notification helpers and test adapters
* - `@beignet/core/outbox` owns durable outbox helpers and test adapters
* - `@beignet/core/payments` owns `PaymentsPort` and payment test adapters
* - `@beignet/core/schedules` owns schedule definitions and runners
* - `@beignet/core/webhooks` owns inbound webhook definitions and verifiers
*/
import { createUnboundPort } from "./unbound.js";
/**
* A generic map of named "ports" (outbound dependencies) that your
* application depends on: db, mailer, cache, event bus, etc.
*
* This is intentionally very loose: users define the exact shape via
* `definePorts(...)`.
*/
export type AnyPorts = Record<string, unknown>;
/**
* Definition accepted by the curried `definePorts<P>()(...)` form.
*/
export type DeferredPortsDefinition<
P extends AnyPorts,
Deferred extends readonly (keyof P & string)[],
> = {
/**
* Ports the app binds directly. Optional keys of `P` may be omitted here
* and contributed by providers instead.
*/
bound: Omit<P, Deferred[number]>;
/**
* Port keys that providers contribute during server startup. Deferred keys
* boot as throwing placeholders; `createServer(...)` fails startup if any
* are still unbound after providers have started (see `onUnboundPorts`).
*/
deferred: Deferred;
};
/**
* Define the set of ports (outbound dependencies) for your application.
*
* The identity form captures the exact shape of the provided `ports` object
* so you can export `type AppPorts = typeof initialPorts`.
*
* The curried form, `definePorts<AppPorts>()({ bound, deferred })`, declares
* which port keys providers contribute at server startup. Deferred keys boot
* as throwing placeholders instead of hand-written stubs, and
* `createServer(...)` validates after provider startup that nothing is left
* unbound. Optional keys of `AppPorts` may go in either bucket.
*
* @example
* ```ts
* // Identity form: every port is bound directly.
* const initialPorts = definePorts({
* db: dbAdapter,
* mailer: mailerAdapter,
* });
* export type AppPorts = typeof initialPorts;
*
* // Deferred form: providers contribute the rest at startup.
* export const initialPorts = definePorts<AppPorts>()({
* bound: { gate },
* deferred: ["db", "mailer", "storage"],
* });
* ```
*/
export function definePorts<P extends AnyPorts>(ports: P): P;
export function definePorts<P extends AnyPorts>(): <
const Deferred extends readonly (keyof P & string)[],
>(
definition: DeferredPortsDefinition<P, Deferred>,
) => P;
export function definePorts<P extends AnyPorts>(
ports?: P,
):
| P
| ((
definition: DeferredPortsDefinition<P, readonly (keyof P & string)[]>,
) => P) {
if (ports !== undefined) {
return ports;
}
return (definition) => {
const result: AnyPorts = { ...(definition.bound as AnyPorts) };
for (const key of definition.deferred) {
result[key] = createUnboundPort(key);
}
return result as P;
};
}
/**
* A small helper type describing a context object that carries `ports`.
*
* You typically use this to define your application context:
*
* @example
* ```ts
* import type { PortsContext } from "@beignet/core/ports";
* import type { AppPorts } from "./core/ports";
*
* export interface AppCtx extends PortsContext<AppPorts> {
* user: { id: string } | null;
* now: () => Date;
* appError: AppErrorCreator<typeof errors>;
* }
* ```
*/
export interface PortsContext<P extends AnyPorts = AnyPorts> {
ports: P;
}
/**
* Entitlement port exports.
*/
export type {
CreateEntitlementsOptions,
CreateStaticEntitlementsOptions,
EntitlementAllowedDecision,
EntitlementCheckInput,
EntitlementDecision,
EntitlementDeniedDecision,
EntitlementKey,
EntitlementResolver,
EntitlementResolverResult,
EntitlementSubject,
EntitlementsContext,
EntitlementsPort,
RequireEntitlementOptions,
StaticEntitlementGrantMap,
} from "../entitlements/index.js";
/**
* Entitlement helper exports.
*/
export {
allowEntitlement,
createEntitlements,
createStaticEntitlements,
denyEntitlement,
EntitlementRequiredError,
requireEntitlement,
} from "../entitlements/index.js";
/**
* Error reporting port exports.
*/
export type {
ErrorReportContext,
ErrorReporterFlushOptions,
ErrorReporterPort,
ErrorReportJsonValue,
ErrorReportLevel,
ErrorReportOptions,
ErrorReportResult,
ErrorReportTags,
ErrorReportUser,
MemoryErrorReport,
MemoryErrorReporterPort,
MemoryReportedException,
MemoryReportedMessage,
} from "../error-reporting/index.js";
/**
* Error reporting helper exports.
*/
export {
createMemoryErrorReporter,
createNoopErrorReporter,
reportException,
reportMessage,
} from "../error-reporting/index.js";
/**
* Feature flag port exports.
*/
export type {
FlagDef,
FlagEvaluationContext,
FlagEvaluationDetails,
FlagEvaluationOptions,
FlagExposureOptions,
FlagsPort,
FlagTrackOptions,
FlagValue,
MemoryFlagsPort,
} from "../flags/index.js";
/**
* Feature flag helper exports.
*/
export {
createMemoryFlags,
createStaticFlags,
defineFlag,
defineFlags,
evaluateFlag,
getFlagDetails,
} from "../flags/index.js";
/**
* Idempotency port exports.
*/
export type {
IdempotencyCompleteInput,
IdempotencyFailInput,
IdempotencyPort,
IdempotencyReservation,
IdempotencyReserveInput,
} from "../idempotency/index.js";
/**
* Lease-backed lock port exports.
*/
export type {
CreateMemoryLocksOptions,
LeaseAcquireOptions,
LeaseAcquireResult,
LeaseHandle,
LeaseMetadata,
LeaseRenewOptions,
LocksPort,
MemoryLeaseRecord,
MemoryLocksPort,
MemoryLocksProviderOptions,
MemoryLocksProviderPorts,
} from "../locks/index.js";
/**
* Lease-backed lock helper exports.
*/
export {
acquireLease,
createMemoryLocks,
createMemoryLocksProvider,
LeaseOptionsError,
withLease,
} from "../locks/index.js";
/**
* Notification port exports.
*/
export type {
MemoryNotificationDelivery,
MemoryNotificationPort,
NotificationChannelResult,
NotificationPort,
SendNotificationOptions,
SendNotificationResult,
} from "../notifications/index.js";
/**
* Transactional outbox port exports.
*/
export type {
ClaimedOutboxMessage,
OutboxAdminPort,
OutboxClaimBatchOptions,
OutboxCountMessagesOptions,
OutboxDeleteResult,
OutboxEnqueueInput,
OutboxListMessagesOptions,
OutboxMarkDeliveredInput,
OutboxMarkFailedInput,
OutboxMessage,
OutboxMessageKind,
OutboxMessageQuery,
OutboxMessageStatus,
OutboxPort,
OutboxPruneDeliveredInput,
OutboxPurgeDeadLetteredInput,
OutboxRequeueMessageInput,
} from "../outbox/index.js";
/**
* Payments port exports.
*/
export type {
BillingPortalSession,
CheckoutSession,
CreateBillingPortalSessionInput,
CreateCheckoutSessionInput,
CreateRefundInput,
MemoryBillingPortalSession,
MemoryCheckoutSession,
MemoryPaymentsPort,
MemoryRefund,
PaymentCheckoutLineItem,
PaymentCheckoutMode,
PaymentMetadata,
PaymentRefundReason,
PaymentsPort,
PaymentWebhookEvent,
PaymentWebhookRawBody,
Refund,
VerifyPaymentWebhookInput,
} from "../payments/index.js";
/**
* Payments helper exports.
*/
export {
createMemoryPayments,
createMemoryPaymentsProvider,
PaymentProviderError,
} from "../payments/index.js";
/**
* Search port exports.
*
* Search has a dedicated subpath. Re-export the common app port types and
* helpers here so apps can import shared port surfaces from one place.
*/
export type {
DefineSearchIndexOptions,
MemorySearchIndexState,
MemorySearchPort,
MemorySearchProviderOptions,
MemorySearchProviderPorts,
SearchDeleteResult,
SearchDocument,
SearchDocumentBase,
SearchFilters,
SearchFilterValue,
SearchIndexDef,
SearchIndexResult,
SearchPort,
SearchPrimitive,
SearchQuery,
SearchResultPage,
SearchResults,
SearchSort,
SearchValue,
} from "../search/index.js";
/**
* Search helper exports.
*/
export {
createMemorySearch,
createMemorySearchProvider,
defineSearchIndex,
indexSearchDocuments,
SearchOptionsError,
searchDocuments,
} from "../search/index.js";
/**
* Tenant scope exports.
*/
export type { TenantScope } from "../tenancy/index.js";
export {
createTenantScope,
requireTenantScope,
tenantScopeId,
} from "../tenancy/index.js";
/**
* Webhook exports.
*
* Webhooks have a dedicated subpath. Re-export the common app-facing
* definitions and helpers here so production concerns can be imported from one
* shared port toolbox when useful.
*/
export type {
CreateHmacWebhookVerifierOptions,
CreateMemoryWebhookVerifierOptions,
DefineWebhookOptions,
InferSchemaOutput,
InferWebhookEvent,
MemoryWebhookVerifier,
VerifyWebhookInput,
VerifyWebhookOptions,
WebhookDef,
WebhookEvent,
WebhookEventSchemas,
WebhookHeaders,
WebhookRawBody,
WebhookVerifier,
} from "../webhooks/index.js";
/**
* Webhook helper exports.
*/
export {
createHmacWebhookVerifier,
createMemoryWebhookVerifier,
defineWebhook,
parseWebhookEvent,
verifyWebhook,
WebhookOptionsError,
WebhookValidationError,
WebhookVerificationError,
} from "../webhooks/index.js";
/**
* Audit log port exports.
*/
export type {
ActivityActor,
ActivityActorType,
ActivityMetadata,
ActivityMetadataValue,
ActivityResource,
ActivityTenant,
AuditLogEntry,
AuditLogEntryInput,
AuditLogOptions,
AuditLogPort,
AuditOutcome,
InstrumentedAuditLogOptions,
MemoryAuditLogPort,
} from "./audit.js";
/**
* Audit log helper exports.
*/
export {
createAnonymousActor,
createInstrumentedAuditLog,
createMemoryAuditLog,
createRedactedAuditLog,
createServiceActor,
createSystemActor,
createTenant,
createUserActor,
normalizeAuditLogEntry,
redactAuditLogEntry,
} from "./audit.js";
/**
* Auth port exports.
*/
export type {
AuthPort,
AuthRequestLike,
AuthSession,
RequireOptions,
StaticAuthSessionFactory,
} from "./auth.js";
/**
* Auth helper exports.
*/
export {
AuthUnauthorizedError,
createAnonymousAuth,
createStaticAuth,
requireSession,
requireTenant,
requireTenantId,
requireUser,
requireUserId,
TenantRequiredError,
} from "./auth.js";
/**
* Ports builder exports.
*/
export {
createPortsBuilder,
type PortsBuilder,
type PortsOf,
} from "./builder.js";
/**
* Cache port exports.
*/
export type { CachePort, CacheSetOptions } from "./cache.js";
/**
* Cache helper exports.
*/
export { createMemoryCache } from "./cache.js";
/**
* Clock port exports.
*/
export type { ClockPort, FrozenClockPort } from "./clock.js";
/**
* Clock helper exports.
*/
export { createFrozenClock, createSystemClock } from "./clock.js";
/**
* Event bus and job dispatcher port exports.
*/
export type {
DomainEventDef,
EventBusPort,
InferEventPayload,
InferJobPayload,
JobDef,
JobDispatcherPort,
} from "./events.js";
/**
* ID generator port exports.
*/
export type {
IdGeneratorPort,
SequenceIdGeneratorPort,
} from "./id-generator.js";
/**
* ID generator helper exports.
*/
export {
createSequenceIdGenerator,
createUuidIdGenerator,
} from "./id-generator.js";
/**
* Logger port exports.
*/
export type {
LoggerPort,
LogLevel,
MemoryLogEntry,
MemoryLoggerPort,
} from "./logger.js";
/**
* Logger helper exports.
*/
export { createMemoryLogger, createNoopLogger } from "./logger.js";
/**
* Policy and authorization gate type exports.
*/
export type {
BoundGate,
CreateGateOptions,
GateAllowedDecision,
GateContext,
GateDecision,
GateDecisionObservation,
GateDecisionObserver,
GateDecisionSource,
GateDeniedDecision,
GateDenyHandler,
GatePolicyResult,
GatePort,
PolicyBatch,
PolicyBatchBooleanMap,
PolicyBatchCheck,
PolicyBatchDecisionMap,
PolicyContextFromDefinitions,
PolicyDefinition,
PolicyMapFromDefinitions,
PolicyResolver,
PolicySubjectArgs,
} from "./policy.js";
/**
* Policy and authorization gate helper exports.
*/
export {
allow,
createGate,
definePolicy,
deny,
GateAuthorizationError,
} from "./policy.js";
/**
* Rate limit port exports.
*/
export type {
RateLimitHitOptions,
RateLimitPort,
RateLimitResult,
} from "./rate-limit.js";
/**
* Rate limit helper exports.
*/
export { createMemoryRateLimiter } from "./rate-limit.js";
/**
* Redaction helper exports.
*/
export {
createRedactor,
DEFAULT_CIRCULAR_VALUE,
DEFAULT_REDACTED_VALUE,
DEFAULT_SENSITIVE_KEY_TERMS,
DEFAULT_SENSITIVE_KEYS,
DEFAULT_TRUNCATED_VALUE,
isSensitiveKey,
type RedactableHeaders,
type RedactionDecisionContext,
type RedactionOptions,
type Redactor,
redactHeaders,
redactValue,
} from "./redaction.js";
/**
* Storage port exports.
*/
export type {
CreateStoragePublicUrlOptions,
MemoryStorageOptions,
PrefixStorageKeyOptions,
StorageBody,
StorageMetadata,
StorageObject,
StorageObjectBody,
StoragePort,
StoragePutOptions,
StorageVisibility,
} from "./storage.js";
/**
* Storage helper exports.
*/
export {
assertValidStorageKey,
createMemoryStorage,
createStoragePublicUrl,
normalizeStorageKeyPrefix,
prefixStorageKey,
} from "./storage.js";
/**
* Unbound (deferred) port helper exports.
*/
export { createUnboundPort, isUnboundPort } from "./unbound.js";
/**
* Unit of Work port exports.
*/
export {
type BufferedDomainEventRecorder,
createDomainEventRecorder,
createNoopUnitOfWork,
createObservedUnitOfWork,
type DomainEventRecorderPort,
type NoopUnitOfWorkOptions,
type ObservedUnitOfWorkOptions,
type RecordedDomainEvent,
type UnitOfWorkCallback,
type UnitOfWorkPort,
} from "./unit-of-work.js";