@fireonce/sdk
v0.2.0
Published
Official TypeScript SDK for the FireOnce guaranteed-execution job scheduling platform
Downloads
52
Maintainers
Readme
@fireonce/sdk
Official TypeScript SDK for FireOnce -- the guaranteed-execution job scheduling platform.
Zero dependencies. Supports ESM and CommonJS. TypeScript strict mode.
Installation
pnpm add @fireonce/sdk
# or
npm install @fireonce/sdkQuick Start
import { FireOnce, defineJob } from '@fireonce/sdk';
// 1. Define a job type
const sendEmail = defineJob<{ to: string; subject: string; body: string }>({
name: 'send-email',
handler: 'https://api.myapp.com/webhooks/fireonce/email',
retry: { strategy: 'exponential', maxAttempts: 3, delayMs: 1000 },
timeout: '30s',
});
// 2. Create a client
const fireonce = new FireOnce({
apiKey: process.env.FIREONCE_API_KEY!,
});
// 3. Schedule a job — fires in 2 days
const handle = await fireonce.schedule(sendEmail, {
id: 'welcome-email-user-123',
payload: { to: '[email protected]', subject: 'Welcome!', body: 'Hello...' },
}).inDays(2);
// 4. Use the handle
const job = await handle.status();
console.log(job.status); // 'scheduled'Concepts
Job Definitions
Job definitions capture the what and how of a job without scheduling anything. They are reusable templates that describe the handler, retry strategy, and timeout.
import { defineJob } from '@fireonce/sdk';
// HTTP callback mode -- FireOnce sends a POST to your URL when the job executes
const sendEmail = defineJob<{ to: string; subject: string; body: string }>({
name: 'send-email',
handler: 'https://api.myapp.com/webhooks/fireonce/email',
retry: { strategy: 'exponential', maxAttempts: 3, delayMs: 1000 },
timeout: '30s',
});
// Local handler mode -- define the handler as a function (requires FireOnce Worker)
const processPayment = defineJob<{ orderId: string; amount: number }, { transactionId: string }>({
name: 'process-payment',
handler: async (payload) => {
const result = await stripe.charges.create({ amount: payload.amount });
return { transactionId: result.id };
},
retry: { strategy: 'exponential', maxAttempts: 5, delayMs: 2000 },
timeout: '60s',
});The first generic parameter (TPayload) types the payload. The second (TResult) types the return value and defaults to void.
Scheduling Jobs
Use fireonce.schedule() to create a job instance. Chain fluent time methods to set when it fires:
// Execute immediately
const handle = await fireonce.schedule(sendEmail, {
payload: { to: '[email protected]', subject: 'Welcome!', body: 'Hello...' },
});
// Execute in 2 days, 3 hours, and 30 minutes
const handle = await fireonce.schedule(sendEmail, {
id: 'welcome-email-user-123',
payload: { to: '[email protected]', subject: 'Welcome!', body: 'Hello...' },
}).inDays(2).inHours(3).inMinutes(30);
// Execute at an absolute time
const handle = await fireonce.schedule(sendEmail, {
payload: { to: '[email protected]', subject: 'Welcome!', body: 'Hello...' },
}).at(new Date('2024-12-25T00:00:00Z'));
// All options
const handle = await fireonce.schedule(sendEmail, {
id: 'welcome-email-user-123',
payload: { to: '[email protected]', subject: 'Welcome!', body: 'Hello...' },
idempotencyKey: 'welcome-user-123',
webhookUrl: 'https://myapp.com/job-status',
}).inDays(1);Available time methods (all additive):
| Method | Description |
|---|---|
| .inWeeks(n) | Add n weeks |
| .inDays(n) | Add n days |
| .inHours(n) | Add n hours |
| .inMinutes(n) | Add n minutes |
| .inSeconds(n) | Add n seconds |
| .at(date) | Set absolute time (overrides relative offsets) |
Job Handles
A JobHandle is your interface to a running (or completed) job:
// Get current status
const job = await handle.status();
console.log(job.status); // 'queued' | 'scheduled' | 'processing' | 'completed' | ...
// Cancel a pending job
await handle.cancel();
// Replay a failed job
const replayed = await handle.replay();
// Poll until the job reaches a terminal state
const result = await handle.result({ timeout: 30_000, pollInterval: 2_000 });
console.log(result.status); // 'completed' | 'failed' | 'dead_lettered' | 'cancelled'You can also get a handle for an existing job without scheduling:
const handle = fireonce.getHandle('job-id-123');
const status = await handle.status();Bulk Scheduling
Schedule many jobs in a single API call:
const handles = await fireonce.scheduleBulk([
{
job: sendEmail,
options: {
id: 'email-1',
payload: { to: '[email protected]', subject: 'Hi', body: 'Hello Alice' },
},
},
{
job: sendEmail,
options: {
id: 'email-2',
payload: { to: '[email protected]', subject: 'Hi', body: 'Hello Bob' },
},
},
]);
// Each item in the array is a JobHandle
for (const handle of handles) {
console.log(handle.id);
}Management APIs
API Keys
// Create a new API key
const key = await fireonce.apiKeys.create({ label: 'Production' });
console.log(key.key); // Only returned once -- store securely
// List all keys
const keys = await fireonce.apiKeys.list();
// Revoke a key
await fireonce.apiKeys.revoke('key-id');Usage
// Current billing period
const usage = await fireonce.usage.current();
console.log(usage.jobsCreated, usage.jobsCompleted);
// Historical usage
const history = await fireonce.usage.history();Billing
// Create a checkout session
const { url } = await fireonce.billing.checkout({ plan: 'pro' });
// Upgrade plan
const { url: upgradeUrl } = await fireonce.billing.upgrade({ plan: 'enterprise' });Authentication
// Register
const { token, tenant } = await fireonce.auth.register({
name: 'Acme Corp',
email: '[email protected]',
password: 'secure-password',
});
// Login
const { token: loginToken } = await fireonce.auth.login({
email: '[email protected]',
password: 'secure-password',
});Error Handling
All API errors throw FireOnceError with structured fields:
import { FireOnceError } from '@fireonce/sdk';
try {
await handle.cancel();
} catch (err) {
if (err instanceof FireOnceError) {
console.error(err.message); // Human-readable message
console.error(err.status); // HTTP status (0 for network/timeout)
console.error(err.code); // Machine-readable code (e.g. 'NOT_FOUND')
}
}Error Codes
| Code | Description |
|------|-------------|
| CONFIG_ERROR | Invalid client configuration |
| TIMEOUT | Request timed out |
| NETWORK_ERROR | Network connectivity issue |
| LOCAL_HANDLER_NO_CALLBACK | Local handler used without a Worker or callbackUrl fallback |
| RESULT_TIMEOUT | handle.result() timed out waiting for terminal state |
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
| apiKey | string | required | Bearer token for API authentication |
| baseUrl | string | https://api.fireonce.com | Base URL of the FireOnce API |
| timeout | number | 30000 | Request timeout in milliseconds |
| maxRetries | number | 3 | Max retries on 429/5xx responses |
| retryDelayMs | number | 500 | Initial retry delay (doubles each attempt) |
Local Handler Mode
When you define a job with a function handler, the function captures your execution intent but requires a FireOnce Worker to actually run. If you schedule a local-handler job without a Worker, provide a callbackUrl fallback:
const processPayment = defineJob<{ orderId: string }, { txId: string }>({
name: 'process-payment',
handler: async (payload) => {
// This runs in the Worker, not when scheduling
return { txId: await charge(payload.orderId) };
},
});
// Without a Worker, provide a callbackUrl fallback
const handle = await fireonce.schedule(processPayment, {
payload: { orderId: 'order-123' },
callbackUrl: 'https://api.myapp.com/webhooks/fireonce/payments',
});Requirements
- Node.js >= 18.0.0 (uses native
fetch) - TypeScript >= 5.0 (for best type inference)
- Zero runtime dependencies
