Bridge Training

Ports

Cross-cutting ports for infrastructure the core depends on, who implements each one and which fake stands in for it in tests.

Import from @workspace/training/application/ports. These are the interfaces the application layer depends on for infrastructure that is not a module's own repository: queueing, events, email, the auth provider, object storage, session tokens and webhook verification. Every export here is a type except DEFAULT_SESSION_TOKEN_TTL_MS. Implementations live in capability-named adapter packages and are injected at the composition root of each app, see Adapter packages. A module's own repository ports live under that module's ports/ folder and are documented on its page.

PortImplemented byFake
QueuecreatePgBossQueue in @workspace/queue/drivers/pg-bosscreateFakeQueue in @workspace/training/testing
DomainEventBuscreateEventBus in @workspace/shared/event-bus (mitt)none; an object with a recorded emit
EmailSendercreateResendEmailSender in @workspace/emailsnone; an object with a recorded send
AdBannersStoragecreateSupabaseAdBannersStorage in @workspace/ads/adminnone
BrandAssetsStoragecreateSupabaseBrandAssetsStorage in @workspace/ads/brand-assets/supabase-storagenone
SessionTokenServicecreateHmacSessionTokenService in @workspace/auth/servernone
WebhookVerifiercreateClerkWebhookVerifier in @workspace/auth/server, createStripeWebhookVerifier in @workspace/payment/serversigning helpers in @workspace/auth/server/testing and @workspace/payment/server/testing drive the real verifiers

Queue

Durable message queueing for background work such as webhook delivery. The EventIngestionService of Events is its consumer inside the core: it publishes { id } jobs on its process topic. The API worker registers the handlers and the sweep cron.

MethodSignatureDescription
send<T>(topic: string, data: T) => Promise<void>Publish one message.
work<T>(topic: string, handler: (msg: QueueMessage<T>) => Promise<void>, opts?: QueueRetryOptions) => Promise<void>Register a handler for a topic, with optional retry options.
schedule(topic: string, cron: string) => Promise<void>Register a recurring job on topic (cron syntax, UTC). Consume it via work.
stop() => Promise<void>Stop the driver.

QueueMessage

Prop

Type

QueueRetryOptions

Prop

Type

Topic names must satisfy the driver: pg-boss accepts alphanumerics, _, -, . and /, never a colon. The events module publishes its defaults as DEFAULT_INGESTION_TOPICS.

ports.ts
type ProcessJob = { id: string };

const PROCESS_TOPIC = "webhook.process";

const RETRY: QueueRetryOptions = {
  maxAttempts: 8,
  initialDelayMs: 60_000,
  backoff: "exponential",
};

export function enqueueProcessing(queue: Queue, eventId: string): Promise<void> {
  return queue.send<ProcessJob>(PROCESS_TOPIC, { id: eventId });
}

export function startProcessing(
  queue: Queue,
  process: (eventId: string) => Promise<void>
): Promise<void> {
  return queue.work<ProcessJob>(
    PROCESS_TOPIC,
    async (msg: QueueMessage<ProcessJob>) => {
      // msg.id is the queue's job id; msg.retryCount is 0 on the first delivery
      await process(msg.data.id);
    },
    RETRY
  );
}

export async function scheduleSweep(queue: Queue): Promise<void> {
  await queue.schedule("webhook.sweep-pending", "*/2 * * * *"); // UTC cron
  await queue.work("webhook.sweep-pending", async () => {
    // consumed like any other topic
  });
}

DomainEventBus

Emits domain events; subscribers react asynchronously. createAccountsService takes one and emits account:created when ensureAccount creates a row, see Accounts. The API builds its bus with createEventBus from @workspace/shared/event-bus over a ServerEvents map whose entries match DomainEvent.

MethodSignatureDescription
emit(event: DomainEvent) => voidFire-and-forget.

DomainEvent is the closed union of the events the core emits:

typepayload
"account:created"{ userId: string; email: string }
"account:updated"{ userId: string }
ports.ts
export function announceAccount(
  eventBus: DomainEventBus,
  userId: string,
  email: string
): void {
  eventBus.emit({ type: "account:created", payload: { userId, email } });
}

EmailSender

Renders an email message and hands it to the provider (Resend today). Its only consumer is the EmailsService of Emails, on the worker side; nothing else sends an email. The adapter is null when no API key is configured, and the service then marks every email skipped.

MethodSignatureDescription
send(input: SendEmailInput) => Promise<SendEmailResult>Render and send one message to its one recipient. The idempotency key goes to the provider, which refuses a second email under the same key.

SendEmailInput

Prop

Type

SendEmailResult

Prop

Type

AdBannersStorage

The object store holding sponsor banner assets, consumed by the ad banners service of Ads. The Supabase Storage implementation lives in @workspace/ads.

MethodSignatureDescription
listForSponsor(sponsorSlug: string) => Promise<AdBanner[]>Banners uploaded for a sponsor slug. Returns an empty array when the sponsor has no folder yet; never throws for "not found".

BrandAssetsStorage

The object store holding a deal owner's visuals, consumed by the brand assets service of Ads. The API mints a signed upload URL, the browser writes the object, and on confirm the API reads the stored bytes back through this port.

MethodSignatureDescription
createUploadUrl(storageKey: string) => Promise<BrandAssetUploadTicket>Mint a signed upload ticket for a key.
readImage(storageKey: string) => Promise<StoredBrandAssetImage | null>The stored image's size, decoded content type and dimensions; null when no object is stored at the key.
publicUrl(storageKey: string) => stringPublic URL of a stored key.

BrandAssetUploadTicket

Prop

Type

SessionTokenService

Minting and verification of signed anonymous client session tokens, used by the API's telemetry session guard and its session token handlers. Verification failures throw SessionTokenVerificationError from @workspace/training, see Errors.

MethodSignatureDescription
mint() => MintedSessionTokenMint a fresh signed session token.
verify(token: string) => VerifiedSessionTokenVerify signature and expiry; returns the session id or throws a SessionTokenVerificationError.
renew(previousToken: string) => MintedSessionTokenRe-sign the sid of an authentic previous token with a fresh issuedAt, ignoring expiry; throws a SessionTokenVerificationError when the token is malformed or its signature is invalid.
ExportDefinition
DEFAULT_SESSION_TOKEN_TTL_MSdays(1) from @workspace/shared/time-units: the 24 hour default lifetime of a client session token.

MintedSessionToken

Prop

Type

VerifiedSessionToken

Prop

Type

WebhookVerifier

Webhook signature verification, the first step of EventIngestionService.ingest in Events. WebhookVerifier<TProvider extends string = string> lets an adapter narrow the provider tag to its own literal ("clerk", "stripe") so the composition root can require an exact match at the wiring site. The admin app, which only publishes replays, wires a verifier whose verify throws.

MethodSignatureDescription
verify(request: Request) => Promise<IngestedEvent<TProvider>>Verify the webhook signature and return the parsed event.

IngestedEvent

A verified webhook event, before persistence. The domain treats provider as opaque; the default string suits readers of a persisted event, whose column has no CHECK, so an unknown value stays lookup-safe. The persisted shape, PersistedIngestedEvent, belongs to the events module. The makeIngestedEvent factory builds one with neutral provider and event type defaults, see Factories.

Prop

Type