@abshahin/workflows-sdk
v0.1.0
Published
TypeScript SDK for dispatching typed workflow events to a Cloudflare Worker runtime.
Maintainers
Readme
@abshahin/workflows-sdk
TypeScript SDK for dispatching typed background workflow events to a Cloudflare Worker runtime.
The package is transport-oriented and framework-agnostic. It provides a client, an HTTP transport, strongly typed event contracts, retry utilities, and job facades for email, notification, and payment workflows.
Related worker runtime: https://github.com/aashahin/cloudflare-workflows-worker
Why this package exists
@abshahin/workflows-sdk separates event production from workflow execution.
- Producers only need to know how to send typed events
- The transport layer decides how those events reach the workflow runtime
- Event names and payloads stay typed across package boundaries
- Existing application code can migrate away from Inngest-style producers with minimal surface change
- The default companion runtime for this SDK is the Workflows Worker:
https://github.com/aashahin/cloudflare-workflows-worker
Features
- Typed contracts for email, notification, and payment events
- HTTP transport for dispatching event batches to a worker endpoint
- Producer-friendly job facades:
EmailJobs,NotificationJobs,PaymentJobs - Transport retry support with exponential backoff
- Optional dual-run mode for phased migrations
- Optional
onSendExhaustedhook for persisting failed transport attempts - Sub-path exports for contracts and helpers
Installation
Inside this monorepo:
"@abshahin/workflows-sdk": "workspace:*"Once published publicly, install it with your package manager of choice:
bun add @abshahin/workflows-sdknpm install @abshahin/workflows-sdkQuick start
import {
EmailJobs,
HttpTransport,
NotificationJobs,
PaymentJobs,
createWorkflowsClient,
} from "@abshahin/workflows-sdk";
const client = createWorkflowsClient({
transport: new HttpTransport({
baseUrl: "https://workflows.example.com",
authToken: process.env.WORKFLOWS_AUTH_TOKEN!,
}),
});
const emailJobs = new EmailJobs(client, () => true);
const notificationJobs = new NotificationJobs(client);
const paymentJobs = new PaymentJobs(client);
await emailJobs.sendResetPasswordEmail({
email: "[email protected]",
userName: "John",
otpCode: "123456",
tenantId: "tenant_123",
});
await notificationJobs.addNotification(
"tenant_123",
"user_456",
{
title: "New course available",
message: "Check the dashboard for details.",
type: "info",
},
{ delay: 5_000 },
);
await paymentJobs.processPayout({
tenantId: "tenant_123",
transactionId: "txn_001",
walletId: "wallet_001",
amount: 250,
currency: "USD",
});How it fits together
flowchart LR
A[Application code] --> B[Job facade]
B --> C[WorkflowsClient]
C --> D[Transport]
D --> E[Worker /dispatch endpoint]
E --> F[Workflow runtime]Core API
createWorkflowsClient
Creates a WorkflowsClient from a transport configuration.
Use it when you want a minimal factory instead of constructing the client class directly.
HttpTransport
Sends one or more workflow events to the worker's /dispatch endpoint.
Config:
baseUrl: worker base URLauthToken: shared bearer tokentimeoutMs: request timeout, default10000retry: retry policy, orfalseto disable transport retries
Transport behavior:
- Trims trailing slashes from
baseUrl - Wraps network/timeouts as
WorkflowSendError - Treats
429and all5xxresponses as retryable - Treats other
4xxresponses as non-retryable transport failures - Logs partial batch failures without throwing away successful IDs
EmailJobs
Facade for email-related workflow events.
Notable methods:
sendResetPasswordEmailsendVerificationEmailsendChangeEmailVerificationsendNewAccountCredentialssendInvitationEmailsendEnrollmentConfirmationEmailsendCartRecoveryEmailsendTrialEndingRemindersendPaymentReceiptEmailsendWithdrawalStatusEmailsendFailedPaymentAlertEmailsendRefundConfirmationEmail
The constructor accepts an optional isEnabled callback to short-circuit email dispatch when email delivery is disabled.
NotificationJobs
Facade for notification-related workflow events.
Notable methods:
addNotificationaddNotificationForCustomeraddBulkNotificationaddMultipleNotifications
addMultipleNotifications preserves per-item delays by sending individual events only when needed; otherwise it batches them.
PaymentJobs
Facade for payment-related workflow events.
Current method:
processPayout
This dispatches the payout orchestration workflow handled by the worker runtime.
Usage examples
Email workflow dispatch
import {
EmailJobs,
HttpTransport,
createWorkflowsClient,
} from "@abshahin/workflows-sdk";
const client = createWorkflowsClient({
transport: new HttpTransport({
baseUrl: "https://workflows.example.com",
authToken: "secret",
}),
});
const emailJobs = new EmailJobs(client, () => true);
await emailJobs.sendVerificationEmail(
{
email: "[email protected]",
otpCode: "123456",
tenantId: "tenant_123",
},
{ delay: 5_000 },
);Notification workflow dispatch
import {
HttpTransport,
NotificationJobs,
createWorkflowsClient,
} from "@abshahin/workflows-sdk";
const client = createWorkflowsClient({
transport: new HttpTransport({
baseUrl: "https://workflows.example.com",
authToken: "secret",
}),
});
const notificationJobs = new NotificationJobs(client);
await notificationJobs.addBulkNotification(
"tenant_123",
{
title: "Billing update",
message: "Your subscription has been renewed.",
type: "billing",
},
["user_1", "user_2"],
);Payment workflow dispatch
import {
HttpTransport,
PaymentJobs,
createWorkflowsClient,
} from "@abshahin/workflows-sdk";
const client = createWorkflowsClient({
transport: new HttpTransport({
baseUrl: "https://workflows.example.com",
authToken: "secret",
}),
});
const paymentJobs = new PaymentJobs(client);
await paymentJobs.processPayout({
tenantId: "tenant_123",
transactionId: "txn_001",
walletId: "wallet_001",
amount: 250,
currency: "USD",
});Advanced patterns
Dual-run mode
Use dual-run mode when migrating between workflow backends or validating a new transport.
import { HttpTransport, createWorkflowsClient } from "@abshahin/workflows-sdk";
const primaryTransport = new HttpTransport({
baseUrl: "https://primary.example.com",
authToken: "primary-secret",
});
const shadowTransport = new HttpTransport({
baseUrl: "https://shadow.example.com",
authToken: "shadow-secret",
});
const client = createWorkflowsClient({
transport: primaryTransport,
shadowTransport,
dualRun: true,
});The primary transport result is returned. Shadow transport failures are logged and suppressed.
Persist exhausted transport failures
Use onSendExhausted when you want to store failed dispatches for later replay instead of letting producer-side business logic fail immediately.
import { HttpTransport, createWorkflowsClient } from "@abshahin/workflows-sdk";
const client = createWorkflowsClient({
transport: new HttpTransport({
baseUrl: "https://workflows.example.com",
authToken: "secret",
}),
onSendExhausted: async ({ events, options, error, attempts }) => {
console.error("Persist failed workflow dispatch", {
attempts,
error: error.message,
eventCount: events.length,
traceId: options?.traceId,
});
},
});When persistence succeeds, the client returns { ids: [] } instead of throwing.
Event domains
Email events
email/reset-passwordemail/new-account-credentialsemail/change-email-verificationemail/verificationemail/cart-recoveryemail/invitationemail/enrollment-confirmationemail/trial-reminderemail/payment-receiptemail/withdrawal-statusemail/failed-payment-alertemail/refund-confirmation
Notification events
notification/createnotification/create-for-customernotification/bulk-create
Payment events
payment/process-payout
Exports
| Export | Description |
| ------------------------------------------------------------------------------------------------- | ----------------------------- |
| createWorkflowsClient | Factory for WorkflowsClient |
| WorkflowsClient | Core dispatch client |
| HttpTransport | HTTP transport adapter |
| EmailJobs | Email job facade |
| NotificationJobs | Notification job facade |
| PaymentJobs | Payment job facade |
| EMAIL_EVENTS / NOTIFICATION_EVENTS / PAYMENT_EVENTS | Event name constants |
| withRetry / getBackoffDelay / DEFAULT_RETRY_POLICY | Retry helpers |
| deriveIdempotencyKey / generateEventId | Idempotency helpers |
| WorkflowError / WorkflowSendError / WorkflowValidationError / WorkflowRetryExhaustedError | Typed errors |
Sub-path exports
The SDK exposes sub-path imports for tree-shaking or isolated usage:
import {
EMAIL_EVENTS,
type ResetPasswordEmailData,
} from "@abshahin/workflows-sdk/contracts";
import {
deriveIdempotencyKey,
withRetry,
} from "@abshahin/workflows-sdk/helpers";Migration from Inngest
The job facades are designed to make migration straightforward, but there is one important API difference.
Previous Inngest usage relied on static methods:
- EmailJobs.sendResetPasswordEmail(data);
+ emailJobs.sendResetPasswordEmail(data);The SDK uses instance-based facades so transports, feature flags, and fallback behavior can be injected once at application bootstrap.
License
MIT. Add the LICENSE file in the published workflows-sdk repository.
