### Install Duron and Database Dependencies Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/getting-started.mdx Commands to install the Duron core library along with required database adapters like Postgres or PGlite using various package managers. ```bash # Install Duron npm install duron drizzle-orm@beta pnpm add duron drizzle-orm@beta yarn add duron drizzle-orm@beta bun add duron drizzle-orm@beta # Install Postgres npm install postgres pnpm add postgres yarn add postgres bun add postgres # Install PGlite npm install @electric-sql/pglite pnpm add @electric-sql/pglite yarn add @electric-sql/pglite bun add @electric-sql/pglite ``` -------------------------------- ### Full Server Setup (TypeScript) Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/server-api.mdx An example of setting up a Duron server using Elysia. It demonstrates client creation with a PostgreSQL adapter, defining actions, configuring the API server with authentication, and starting the main application. ```TypeScript import { Elysia } from 'elysia' import { createServer } from 'duron/server' import { duron } from 'duron' import { postgresAdapter } from 'duron/adapters/postgres' // Create Duron client const client = duron({ database: postgresAdapter({ connection: process.env.DATABASE_URL!, }), actions: { sendEmail, processOrder, }, }) // Create API server const api = createServer({ client, prefix: '/api', login: { onLogin: async ({ email, password }) => { // Your authentication logic return await authenticateUser(email, password) }, jwtSecret: process.env.JWT_SECRET!, expirationTime: '1h', // Optional, defaults to '1h' (access token) refreshTokenExpirationTime: '7d', // Optional, defaults to '7d' (refresh token) }, }) // Create main app const app = new Elysia() .use(api) .listen(3000) console.log('Server running on http://localhost:3000') ``` -------------------------------- ### Implement Duron Action and Client Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/getting-started.mdx A TypeScript example showing how to define a background action with Zod validation, configure the Duron client with a PGlite adapter, and execute a job. ```typescript import { duron } from 'duron' import { defineAction } from 'duron/action' import { pgliteAdapter } from 'duron/adapters/pglite' import { z } from 'zod' const sendEmail = defineAction()({ name: 'sendEmail', input: z.object({ to: z.string().email(), subject: z.string(), body: z.string(), }), output: z.object({ success: z.boolean(), }), handler: async (ctx) => { const { to, subject, body } = ctx.input const result = await ctx.step('sendEmail', async ({ signal }) => { console.log(`Sending email to ${to}`) const response = await fetch('https://api.email.com/send', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ to, subject, body }), signal, }) if (!response.ok) { throw new Error('Failed to send email') } return await response.json() }) return { success: result.success ?? true, } }, }) const client = duron({ database: pgliteAdapter({ connection: ':memory:', }), actions: { sendEmail, }, logger: 'info', }) await client.start() const jobId = await client.runAction('sendEmail', { to: 'user@example.com', subject: 'Hello', body: 'This is a test email', }) console.log(`Job created with ID: ${jobId}`) ``` -------------------------------- ### Complete Action Definition Example Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/actions.mdx An example demonstrating the integration of all configuration options for defining a Duron action. ```APIDOC ## POST /actions ### Description Demonstrates a comprehensive action definition including input/output schemas, lifecycle settings, concurrency, grouping, steps, and handler logic. ### Method POST ### Endpoint /actions ### Parameters #### Request Body - **name** (string) - Required - The name of the action. - **version** (string) - Required - The version of the action. - **input** (object) - Required - Zod schema for action input. - **output** (object) - Required - Zod schema for action output. - **expire** (number) - Optional - Action-level timeout. - **concurrency** (number) - Optional - Maximum concurrent jobs for the action. - **groups** (object) - Optional - Grouping and group concurrency configuration. - **description** (function) - Optional - Dynamic description generator. - **steps** (object) - Optional - Configuration for steps within the action. - **handler** (function) - Required - The main execution logic. ### Request Example ```typescript import { defineAction } from 'duron/action' import { z } from 'zod' const processOrder = defineAction<{ db: Database }>()({ name: 'processOrder', version: '1.0.0', input: z.object({ orderId: z.string(), userId: z.string(), }), output: z.object({ success: z.boolean(), orderNumber: z.string(), }), expire: 30 * 60 * 1000, // 30 minutes concurrency: 50, groups: { groupKey: async (ctx) => `user-${ctx.input.userId}`, concurrency: async (ctx) => 5, }, description: async (ctx) => `Process order ${ctx.input.orderId} for user ${ctx.input.userId}`, steps: { concurrency: 10, retry: { limit: 3, factor: 2, minTimeout: 1000, maxTimeout: 10000, }, expire: 5 * 60 * 1000, }, handler: async (ctx) => { ctx.logger.info({ orderId: ctx.input.orderId }, 'Processing order') const db = ctx.var.db await ctx.step('fetchUser', async ({ signal }) => { return await db.users.findById(ctx.input.userId, { signal }) }) const order = await ctx.step('createOrder', async ({ signal }) => { return await db.orders.create({ orderId: ctx.input.orderId, userId: ctx.input.userId }, { signal }) }) return { success: true, orderNumber: order.number } }, }) ``` ### Response #### Success Response (200) - **success** (boolean) - Indicates if the action was defined successfully. #### Response Example ```json { "success": true } ``` ``` -------------------------------- ### Configure Telemetry Storage and Export Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/telemetry.mdx Examples for configuring local storage options, external OTLP trace exporters, and combined telemetry setups. ```typescript // Local storage with custom flush delay telemetry: { local: { flushDelayMs: 10000 } } // External OTLP export import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' telemetry: { traceExporter: new OTLPTraceExporter({ url: 'http://localhost:4318/v1/traces', }), } // Combined storage and export telemetry: { local: true, traceExporter: new OTLPTraceExporter({ url: 'http://localhost:4318/v1/traces', }), } ``` -------------------------------- ### Duron Dashboard Development Commands Source: https://github.com/geut/duron/blob/main/packages/duron-dashboard/README.md Common commands for developing the Duron Dashboard project, including installing dependencies, starting the development server, building the project, and type checking. ```bash # Install dependencies bun install # Start development server bun dev # Build bun run build # Type check bun run typecheck ``` -------------------------------- ### Install Duron Dependencies Source: https://github.com/geut/duron/blob/main/packages/duron/README.md Commands to install the Duron package and its required database dependencies using various package managers. ```bash bun add duron postgres drizzle-orm@beta npm install duron postgres drizzle-orm@beta pnpm add duron postgres drizzle-orm@beta yarn add duron postgres drizzle-orm@beta ``` -------------------------------- ### Install Duron with npm, bun, pnpm, or yarn Source: https://github.com/geut/duron/blob/main/README.md Installs the Duron package along with necessary dependencies like 'postgres' and 'drizzle-orm'. This command is essential for setting up Duron in your project. ```bash bun add duron postgres drizzle-orm@beta # or npm install duron postgres drizzle-orm@beta # or pnpm add duron postgres drizzle-orm@beta # or yarn add duron postgres drizzle-orm@beta ``` -------------------------------- ### Initialize Duron Worker with PostgreSQL Adapter Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/multi-worker.mdx This snippet demonstrates how to initialize a Duron worker client configured for multi-process mode with a PostgreSQL database adapter. It sets up worker identification, synchronization patterns, database connection, action handlers, logging, and job recovery settings. The worker starts processing jobs and includes graceful shutdown handling for SIGINT signals. ```typescript import { duron } from 'duron' import { postgresAdapter } from 'duron/adapters/postgres' const workerId = process.env.WORKER_ID || `worker-${process.pid}` const client = duron({ id: workerId, syncPattern: 'hybrid', database: postgresAdapter({ connection: process.env.DATABASE_URL!, }), actions: { sendEmail, processOrder, }, logger: 'debug', recoverJobsOnStart: true, multiProcessMode: true, processTimeout: 5 * 1000, // 5 seconds }) await client.start() client.logger.info(`✅ Worker ${workerId} started and ready to process jobs`) // Keep the process alive process.on('SIGINT', async () => { client.logger.info(`Shutting down worker ${workerId}...`) await client.stop() process.exit(0) }) ``` -------------------------------- ### Install Telemetry Dependencies Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/telemetry.mdx Commands to install necessary OpenTelemetry packages for external exporting to OTLP or Jaeger. ```bash bun add @opentelemetry/exporter-trace-otlp-http bun add @opentelemetry/exporter-jaeger ``` -------------------------------- ### Implement Multi-Worker Orchestration Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/multi-worker.mdx Demonstrates a parent process using Bun to spawn multiple worker instances and manage graceful shutdown of the entire system. ```typescript import { serve } from 'bun' import { duron } from 'duron' import { postgresAdapter } from 'duron/adapters/postgres' import { createServer } from 'duron/server' import { getHTML } from 'duron-dashboard/get-html' const client = duron({ id: 'dashboard-server', syncPattern: false, database: postgresAdapter({ connection: process.env.DATABASE_URL!, }), actions: { sendEmail, processOrder }, }) await client.start() const app = createServer({ client }) const workerCount = parseInt(process.env.WORKER_COUNT || '2', 10) const workers: Bun.Subprocess[] = [] for (let i = 1; i <= workerCount; i++) { const worker = Bun.spawn(['bun', 'worker.ts'], { cwd: import.meta.dir, env: { ...process.env, WORKER_ID: `worker-${i}` }, stdout: 'inherit', stderr: 'inherit', }) workers.push(worker) } process.on('SIGINT', async () => { for (const worker of workers) worker.kill() await Promise.all(workers.map((w) => w.exited)) await client.stop() process.exit(0) }) ``` -------------------------------- ### GET /actions/metadata Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/server-api.mdx Retrieves metadata for actions, including their input schemas and example mock data. ```APIDOC ## GET /actions/metadata ### Description Get action metadata including input schemas and mock data. ### Method GET ### Endpoint `/actions/metadata` ### Response #### Success Response (200) - **name** (string) - The name of the action. - **mockInput** (object) - Example input data for the action. #### Response Example ```json [ { "name": "sendEmail", "mockInput": { "to": "string", "subject": "string", "body": "string" } } ] ``` ``` -------------------------------- ### Complete Duron Action Definition Example in TypeScript Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/actions.mdx This example demonstrates a comprehensive Duron action definition, incorporating all available configuration options including input/output schemas, expiration, concurrency, group settings, description, step configurations, and the main handler logic. ```typescript import { defineAction } from 'duron/action' import { z } from 'zod' const processOrder = defineAction<{ db: Database }>()({ name: 'processOrder', version: '1.0.0', input: z.object({ orderId: z.string(), userId: z.string(), }), output: z.object({ success: z.boolean(), orderNumber: z.string(), }), expire: 30 * 60 * 1000, // 30 minutes concurrency: 50, groups: { groupKey: async (ctx) => `user-${ctx.input.userId}`, concurrency: async (ctx) => 5, }, description: async (ctx) => `Process order ${ctx.input.orderId} for user ${ctx.input.userId}`, steps: { concurrency: 10, retry: { limit: 3, factor: 2, minTimeout: 1000, maxTimeout: 10000, }, expire: 5 * 60 * 1000, }, handler: async (ctx) => { ctx.logger.info({ orderId: ctx.input.orderId }, 'Processing order') // Use variables const db = ctx.var.db // Execute steps const user = await ctx.step('fetchUser', async ({ signal }) => { return await db.users.findById(ctx.input.userId, { signal }) }) const order = await ctx.step('createOrder', async ({ signal }) => { return await db.orders.create({ orderId: ctx.input.orderId, userId: ctx.input.userId, }, { signal }) }) return { success: true, orderNumber: order.number, } }, }) ``` -------------------------------- ### Initialize Duron Client with Database Adapter Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/adapters.mdx Demonstrates how to initialize the Duron client by passing a configured database adapter. This example shows integrating the PostgreSQL adapter with the Duron client, along with defining actions. ```typescript import { duron } from 'duron' import { postgresAdapter } from 'duron/adapters/postgres' const client = duron({ database: postgresAdapter({ connection: process.env.DATABASE_URL!, }), actions: { sendEmail, }, }) ``` -------------------------------- ### Install Duron dependencies Source: https://context7.com/geut/duron/llms.txt Commands to install the Duron package along with required database drivers using various package managers. ```bash bun add duron postgres drizzle-orm@beta # or npm install duron postgres drizzle-orm@beta # or pnpm add duron postgres drizzle-orm@beta ``` -------------------------------- ### Install Duron Dashboard using package managers Source: https://github.com/geut/duron/blob/main/packages/duron-dashboard/README.md Instructions for installing the duron-dashboard package using popular package managers like bun, npm, pnpm, and yarn. ```bash # Using bun bun add duron-dashboard # Using npm npm install duron-dashboard # Using pnpm pnpm add duron-dashboard # Using yarn yarn add duron-dashboard ``` -------------------------------- ### REST API Usage Examples Source: https://context7.com/geut/duron/llms.txt Examples of how to interact with the Duron server via HTTP requests using cURL. ```APIDOC ## REST API Usage Examples Interact with the Duron server via HTTP requests. ### Login and Get Tokens ```bash curl -X POST http://localhost:3000/api/login \ -H "Content-Type: application/json" \ -d '{"email": "admin@example.com", "password": "secret"}' # Response: {"accessToken": "eyJ...", "refreshToken": "eyJ..."} ``` ### List Jobs with Filters ```bash curl "http://localhost:3000/api/jobs?page=1&pageSize=20&fStatus=active,completed&sort=createdAt:desc" \ -H "Authorization: Bearer eyJ..." ``` ### Get Job Details ```bash curl http://localhost:3000/api/jobs/uuid-here \ -H "Authorization: Bearer eyJ..." ``` ### Run an Action ```bash curl -X POST http://localhost:3000/api/actions/send-email/run \ -H "Authorization: Bearer eyJ..." \ -H "Content-Type: application/json" \ -d '{"to": "user@example.com", "subject": "Hello", "body": "Test"}' # Response: {"success": true, "jobId": "uuid"} ``` ### Cancel a Job ```bash curl -X POST http://localhost:3000/api/jobs/uuid/cancel \ -H "Authorization: Bearer eyJ..." ``` ### Retry a Failed Job ```bash curl -X POST http://localhost:3000/api/jobs/uuid/retry \ -H "Authorization: Bearer eyJ..." ``` ### Time Travel to a Step ```bash curl -X POST http://localhost:3000/api/jobs/uuid/time-travel \ -H "Authorization: Bearer eyJ..." \ -H "Content-Type: application/json" \ -d '{"stepId": "step-uuid"}' ``` ### Refresh Access Token ```bash curl -X POST http://localhost:3000/api/refresh \ -H "Content-Type: application/json" \ -d '{"refreshToken": "eyJ..."}' ``` ``` -------------------------------- ### Configure Database Adapters Source: https://context7.com/geut/duron/llms.txt Shows how to configure PostgreSQL and PGLite adapters for different environments. Includes examples for production connection pooling and local development with in-memory or file-based storage. ```typescript import { postgresAdapter } from 'duron/adapters/postgres' import { pgliteAdapter } from 'duron/adapters/pglite' import postgres from 'postgres' // PostgreSQL for production const prodAdapter = postgresAdapter({ connection: process.env.DATABASE_URL, schema: 'duron', // Database schema migrateOnStart: true, }) // PostgreSQL with connection pooling const sql = postgres(process.env.DATABASE_URL!, { max: 10 }) const pooledAdapter = postgresAdapter({ connection: sql, schema: 'duron', }) // PGLite for development/testing const devAdapter = pgliteAdapter({ connection: ':memory:', // In-memory database schema: 'duron', migrateOnStart: true, }) // PGLite with file persistence const persistentAdapter = pgliteAdapter({ connection: './data/duron.db', schema: 'duron', }) // Use in client const client = duron({ database: process.env.NODE_ENV === 'production' ? prodAdapter : devAdapter, actions: { sendEmail }, }) ``` -------------------------------- ### Configure Duron Client Options Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/client-api.mdx Provides examples for various configuration options including database adapters, custom variables, synchronization patterns, and telemetry settings. ```typescript { database: pgliteAdapter({ connection: ':memory:' }), actions: { sendEmail, processOrder }, variables: { apiKey: process.env.API_KEY, db: myDatabase }, id: 'worker-1', syncPattern: 'hybrid', pullInterval: 10_000, batchSize: 20, actionConcurrencyLimit: 50, groupConcurrencyLimit: 5, migrateOnStart: true, recoverJobsOnStart: true, multiProcessMode: true, processTimeout: 10 * 1000, logger: 'info', telemetry: { local: true, serviceName: 'my-service', } } ``` -------------------------------- ### Configure Duron Telemetry Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/telemetry.mdx Initialize the Duron client with telemetry settings. This example demonstrates enabling local database storage for spans. ```typescript import { duron } from 'duron' import { postgresAdapter } from 'duron/adapters/postgres' const client = duron({ database: postgresAdapter({ connection: process.env.DATABASE_URL, }), actions: { /* your actions */ }, telemetry: { local: true, // Store spans in the database }, }) ``` -------------------------------- ### Configure Step Retry and Expiration Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/jobs-and-steps.mdx Provides examples for setting timeout (expire) and retry logic (limit, factor, timeouts) for individual steps. ```typescript await ctx.step( 'fetchData', async ({ signal }) => { return await fetchData({ signal }) }, { expire: 10 * 60 * 1000, retry: { limit: 3, factor: 2, minTimeout: 1000, maxTimeout: 10000, }, } ) ``` -------------------------------- ### GET /jobs/:id/steps Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/server-api.mdx Retrieves the steps associated with a specific job with pagination and search. ```APIDOC ## GET /jobs/:id/steps ### Description Get steps for a job with pagination and search. ### Method GET ### Endpoint /jobs/:id/steps ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the job #### Query Parameters - **page** (integer) - Optional - Page number - **search** (string) - Optional - Fuzzy search in step names ### Response #### Success Response (200) - **data** (array) - List of step objects - **pagination** (object) - Pagination metadata ``` -------------------------------- ### Duron Server Authentication Setup Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/dashboard.mdx Demonstrates how to configure authentication for the Duron server. This involves providing an `onLogin` function for credential verification and a `jwtSecret` for token generation. The dashboard can then be accessed with login enabled. ```typescript // Server with authentication const api = createServer({ client, login: { onLogin: async ({ email, password }) => { return email === 'admin@example.com' && password === 'secret' }, jwtSecret: process.env.JWT_SECRET!, }, }) // Dashboard with login enabled const html = await getHTML({ url: 'http://localhost:3000/api', enableLogin: true, showLogo: true, }) ``` -------------------------------- ### Complete Duron Action Implementation Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/error-handling.mdx A comprehensive example showing the integration of multiple non-retriable steps within a single Duron action definition. ```typescript import { defineAction } from 'duron/action' import { NonRetriableError } from 'duron/errors' import { z } from 'zod' const processOrder = defineAction()({ name: 'processOrder', input: z.object({ orderId: z.string(), userId: z.string(), items: z.array(z.any()) }), handler: async (ctx) => { await ctx.step('validateOrder', async () => { if (ctx.input.items.length === 0) throw new NonRetriableError('Empty order'); }, { retry: { limit: 0 } }); await ctx.step('processPayment', async () => { const payment = await processPayment(ctx.input); if (!payment.success) throw new NonRetriableError('Payment failed', { cause: payment }); return payment; }); } }) ``` -------------------------------- ### Initialize and Start Duron Client Source: https://context7.com/geut/duron/llms.txt Configures the main Duron client instance with database adapters, action definitions, and operational settings like concurrency limits and telemetry. ```typescript import { duron } from 'duron' import { postgresAdapter } from 'duron/adapters/postgres' const variables = { apiKey: process.env.EMAIL_API_KEY, db: myDatabase, } const client = duron({ id: 'my-worker', database: postgresAdapter({ connection: process.env.DATABASE_URL, schema: 'duron', migrateOnStart: true, }), actions: { sendEmail, processOrder, }, variables, syncPattern: 'hybrid', pullInterval: 5000, batchSize: 10, actionConcurrencyLimit: 100, groupConcurrencyLimit: 10, migrateOnStart: true, recoverJobsOnStart: true, multiProcessMode: false, processTimeout: 5000, logger: 'info', telemetry: { local: true, serviceName: 'my-service', }, }) await client.start() await client.stop() ``` -------------------------------- ### Configure Multi-Process Worker in TypeScript Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/examples.mdx Initializes a Duron client instance with multiProcessMode enabled. Each worker requires a unique ID and a shared database adapter to coordinate tasks across processes. ```typescript // worker-1.ts const client = duron({ id: 'worker-1', database: postgresAdapter({ connection: process.env.DATABASE_URL!, }), actions: { sendEmail, processOrder, }, multiProcessMode: true, processTimeout: 5 * 1000, // 5 seconds }) await client.start() // worker-2.ts (same setup, different ID) const client = duron({ id: 'worker-2', // ... same configuration }) ``` -------------------------------- ### Accessing Action Context Properties Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/actions.mdx Examples showing how to access various properties within the action handler context, including input, job metadata, shared variables, logging, and telemetry. ```typescript // Input access handler: async (ctx) => { const orderId = ctx.input.orderId const userId = ctx.input.userId } // Job ID and Group Key handler: async (ctx) => { console.log(`Processing job ${ctx.jobId}`) console.log(`Job belongs to group: ${ctx.groupKey}`) } // Variables handler: async (ctx) => { const apiKey = ctx.var.apiKey const db = ctx.var.db } // Logging handler: async (ctx) => { ctx.logger.info({ orderId: ctx.input.orderId }, 'Processing order') } // Telemetry handler: async (ctx) => { ctx.telemetry.recordMetric('custom.counter', 1) const span = ctx.telemetry.getActiveSpan() span.setAttribute('customKey', 'value') } ``` -------------------------------- ### Implement Cancellation with AbortSignal in TypeScript Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/jobs-and-steps.mdx Shows the importance of using the `signal` parameter provided by `ctx.step` for cancellation support. The example demonstrates passing the signal to an asynchronous operation like `fetch` to ensure it can be aborted. ```typescript await ctx.step('fetchData', async ({ signal }) => { const response = await fetch(url, { signal }) return response.json() }) ``` -------------------------------- ### Initialize Duron Client and Run Jobs Source: https://github.com/geut/duron/blob/main/CLAUDE.md Configures a Duron client with a Postgres adapter and demonstrates how to trigger an action and wait for its completion. ```typescript import { duron } from 'duron' import { postgresAdapter } from 'duron/adapters/postgres' const client = duron({ id: 'my-worker', syncPattern: 'hybrid', database: postgresAdapter({ connection: process.env.DATABASE_URL, }), actions: { sendEmail }, variables: { }, logger: 'info', }) await client.start() const jobId = await client.runAction('send-email', { email: 'user@example.com', subject: 'Hello', body: 'World', }) const job = await client.waitForJob(jobId) ``` -------------------------------- ### Setting Process Timeout in Duron Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/multi-worker.mdx Provides an example of configuring the `processTimeout` option in Duron. This setting determines how long a worker waits before considering another process unresponsive and recovering its jobs. The example shows setting it to 5000 milliseconds (5 seconds). ```typescript processTimeout: 5 * 1000, // 5 seconds (default) ``` -------------------------------- ### Initialize and Run Duron Client Source: https://github.com/geut/duron/blob/main/packages/duron/README.md Configures the Duron client with a PostgreSQL adapter and demonstrates how to trigger an action and wait for its completion. ```typescript import { duron } from 'duron' import { postgresAdapter } from 'duron/adapters/postgres' const client = duron({ database: postgresAdapter({ connection: process.env.DATABASE_URL, }), actions: { sendEmail, }, logger: 'info', }) await client.start() const jobId = await client.runAction('send-email', { to: 'user@example.com', subject: 'Hello', body: 'Welcome!', }) const job = await client.waitForJob(jobId) console.log('Job completed:', job?.output) ``` -------------------------------- ### Get Action Metadata (TypeScript) Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/client-api.mdx Fetches metadata for actions, such as their input schemas and example mock data. This function returns a promise that resolves to an array of action metadata objects. ```typescript const metadata = await client.getActionsMetadata() ``` -------------------------------- ### Linting and Formatting Configuration Source: https://github.com/geut/duron/blob/main/CLAUDE.md Commands to maintain code quality using Biome with the standard configuration. ```bash bun run lint bun run lint:fix ``` -------------------------------- ### Initialize Duron Server with Dashboard Source: https://github.com/geut/duron/blob/main/CLAUDE.md Configures a Duron server instance with authentication and serves the dashboard UI. Requires a JWT secret and client configuration. ```typescript import { createServer } from 'duron/server' import { getHTML } from 'duron-dashboard/get-html' const app = createServer({ client, prefix: '/api', login: { onLogin: async ({ email, password }) => { return email === 'admin@example.com' && password === 'secret' }, jwtSecret: process.env.JWT_SECRET, }, }) app.get('/', async () => { const html = await getHTML({ url: 'http://localhost:3000/api' }) return new Response(html, { headers: { 'Content-Type': 'text/html' }, }) }) app.listen(3000) ``` -------------------------------- ### Get Action Metadata (API) Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/server-api.mdx Fetches metadata for actions, such as input schemas and example mock data. This information is useful for understanding how to interact with specific actions. Returns an array of action metadata objects. ```HTTP GET /actions/metadata ``` -------------------------------- ### Initialize Duron Client Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/client-api.mdx Demonstrates how to instantiate the Duron client using the duron function, requiring a database adapter and optional action definitions. ```typescript import { duron } from 'duron' import { pgliteAdapter } from 'duron/adapters/pglite' const client = duron({ database: pgliteAdapter({ connection: ':memory:', }), actions: { sendEmail, processOrder, }, }) ``` -------------------------------- ### Execute Tests with bun:test Source: https://github.com/geut/duron/blob/main/CLAUDE.md Demonstrates the standard testing pattern using the Bun test runner and provides common CLI commands for execution. ```typescript import { describe, test, expect } from 'bun:test' describe('Feature', () => { test('should work', () => { expect(true).toBe(true) }) }) ``` ```bash bun test bun test --watch bun test specific.test.ts ``` -------------------------------- ### Serve Duron Dashboard as HTML Application (Bun) Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/dashboard.mdx Demonstrates how to serve the Duron Dashboard as a standalone HTML application using Bun. It sets up a Duron client, an API server, and serves the inlined HTML dashboard, connecting it to the API. ```typescript import { serve } from 'bun' import { duron } from 'duron' import { postgresAdapter } from 'duron/adapters/postgres' import { createServer } from 'duron/server' import { getHTML } from 'duron-dashboard/get-html' // Create Duron client const client = duron({ database: postgresAdapter({ connection: process.env.DATABASE_URL!, }), actions: { sendEmail }, }) await client.start() // Create API server const api = createServer({ client, prefix: '/api', }) // Serve dashboard and API serve({ port: 3000, routes: { '/': async () => { const html = await getHTML({ url: 'http://localhost:3000/api', enableLogin: false, showLogo: true, theme: 'system', }) return new Response(html, { headers: { 'Content-Type': 'text/html' }, }) }, '/api/*': api.fetch, }, }) console.log('Dashboard running at http://localhost:3000') ``` -------------------------------- ### StepAlreadyExecutedError Example (TypeScript) Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/error-handling.mdx Demonstrates the `StepAlreadyExecutedError` in Duron, which occurs when attempting to execute a step with the same name more than once within the same job. The example shows the scenario that triggers this error. ```typescript // This error is thrown automatically when you try to run the same step twice await ctx.step('fetchUser', async () => { /* ... */ }); await ctx.step('fetchUser', async () => { /* ... */ }); // Throws StepAlreadyExecutedError ``` -------------------------------- ### Development Commands Source: https://github.com/geut/duron/blob/main/packages/duron/README.md Standard development workflow commands for the Duron project using Bun. ```bash bun install bun test bun run build bun run typecheck ``` -------------------------------- ### StepTimeoutError Example (TypeScript) Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/error-handling.mdx Illustrates `StepTimeoutError`, which is thrown when a specific step exceeds its configured timeout. Unlike `ActionTimeoutError`, this error is retriable by default. The example sets a 5-second timeout for a `fetchData` step. ```typescript // Assume fetch is a function that might take time // const fetch = async (url) => { ... }; await ctx.step('fetchData', async () => { // If this takes too long, StepTimeoutError is thrown await fetch(url); }, { expire: 5000, // 5 second timeout for this step }); ``` -------------------------------- ### Generate and Run Migrations with Drizzle Kit (Bash) Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/adapters.mdx Commands to generate new database migrations based on your Drizzle Kit configuration and then apply those migrations to your database. This is necessary when `migrateOnStart` is set to `false` in the Duron adapter. ```bash drizzle-kit generate drizzle-kit migrate ``` -------------------------------- ### Initialize Duron Server and Dashboard Source: https://github.com/geut/duron/blob/main/README.md Configures the Duron client with a database adapter and actions, sets up an Elysia server with JWT-based authentication, and serves the dashboard HTML. ```typescript import { createServer } from "duron/server"; import { getHTML } from "duron-dashboard/get-html"; import { Elysia } from "elysia"; const variables = { apiKey: process.env.EMAIL_API_KEY, }; const client = duron({ database: postgresAdapter({ connection: process.env.DATABASE_URL, }), actions: { sendEmail, }, variables, }); const app = createServer({ client, prefix: "/api", login: { onLogin: async ({ email, password }) => { return email === "admin@example.com" && password === "secure-password"; }, jwtSecret: process.env.JWT_SECRET || "your-secret-key", expirationTime: "24h", refreshTokenExpirationTime: "7d", }, }); app.get("/", async () => { const html = await getHTML({ url: "http://localhost:3000/api" }); return new Response(html, { headers: { "Content-Type": "text/html" }, }); }); app.listen(3000); ``` -------------------------------- ### UnhandledChildStepsError Example (TypeScript) Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/error-handling.mdx Illustrates the `UnhandledChildStepsError` in Duron, which is thrown when a parent step completes without awaiting all its child steps. The example shows both the incorrect usage leading to the error and the correct way to await all child steps. ```typescript import { UnhandledChildStepsError } from 'duron'; // Incorrect usage (child steps not awaited): await ctx.step('parent', async ({ step }) => { step('child1', async () => { /* ... */ }); // Not awaited! step('child2', async () => { /* ... */ }); // Not awaited! return { done: true }; // Throws UnhandledChildStepsError }); // Correct usage (all child steps awaited): await ctx.step('parent', async ({ step }) => { await step('child1', async () => { /* ... */ }); await step('child2', async () => { /* ... */ }); return { done: true }; }); ``` -------------------------------- ### ActionTimeoutError Example (TypeScript) Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/error-handling.mdx Shows how `ActionTimeoutError` is thrown when an action exceeds its configured timeout. The example defines an action with a 5-second timeout (`expire: 5000`) and includes a step that intentionally takes longer (`sleep(10000)`) to trigger the timeout. ```typescript import { defineAction } from 'duron'; const myAction = defineAction()({ name: 'myAction', expire: 5000, // 5 second timeout handler: async (ctx) => { // If this takes longer than 5 seconds, ActionTimeoutError is thrown await ctx.step('slowOperation', async () => { await sleep(10000); // Will timeout }); }, }); ``` -------------------------------- ### Create Duron Server with Authentication Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/server-api.mdx This snippet demonstrates how to create a Duron server instance with authentication configured. It initializes the Duron client with a PGlite adapter and defines an `onLogin` function for authentication. The server is then created with a prefix and login options, including JWT secret and expiration times. Finally, it starts the server listening on port 3000. ```typescript import { createServer } from 'duron/server' import { duron } from 'duron' import { pgliteAdapter } from 'duron/adapters/pglite' // Create your Duron client const client = duron({ database: pgliteAdapter({ connection: ':memory:' }), actions: { sendEmail, }, }) // Create the server const app = createServer({ client, prefix: '/api', // Optional, defaults to '/api' login: { onLogin: async ({ email, password }) => { // Implement your authentication logic return email === 'admin@example.com' && password === 'password' }, jwtSecret: process.env.JWT_SECRET || 'your-secret-key', expirationTime: '1h', // Optional, defaults to '1h' }, }) // Start your server (using Elysia/Bun) app.listen(3000) ``` -------------------------------- ### GET /jobs Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/server-api.mdx Retrieves a list of jobs with support for pagination, complex filtering, and sorting. ```APIDOC ## GET /jobs ### Description Retrieve a paginated list of jobs with various filtering options and sorting capabilities. ### Method GET ### Endpoint /jobs ### Parameters #### Query Parameters - **page** (integer) - Optional - Page number (default: 1) - **pageSize** (integer) - Optional - Items per page (default: 20, max: 1000) - **fStatus** (string) - Optional - Filter by status - **fCreatedAt** (string) - Optional - Filter by created date (ISO string or JSON array) - **sort** (string) - Optional - Sort string format: "field:asc,field:desc" ### Response #### Success Response (200) - **data** (array) - List of job objects - **pagination** (object) - Pagination metadata ### Response Example { "data": [{ "id": "uuid", "actionName": "sendEmail", "status": "completed" }], "pagination": { "page": 1, "pageSize": 20, "total": 100 } } ``` -------------------------------- ### Create a Duron Client with PostgreSQL Adapter in TypeScript Source: https://github.com/geut/duron/blob/main/README.md Initializes a Duron client for a Node.js or Bun application using the PostgreSQL adapter. It configures the client with an ID, database connection, defined actions, shared variables, and logging level, then starts the client and runs an action. ```typescript import { duron } from "duron"; import { postgresAdapter } from "duron/adapters/postgres"; // Define variables (same type used in defineAction) const variables = { apiKey: process.env.EMAIL_API_KEY, }; const client = duron({ id: "my-app", database: postgresAdapter({ connection: process.env.DATABASE_URL, // PostgreSQL connection string }), actions: { sendEmail, }, variables, // Pass the variables object logger: "info", }); await client.start(); // Run an action const jobId = await client.runAction("sendEmail", { email: "user@example.com", subject: "Hello", body: "Welcome!", }); ``` -------------------------------- ### Access Step Context and Metadata Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/jobs-and-steps.mdx Illustrates how to utilize the step context to access metadata, handle cancellation signals, and execute reusable step definitions. ```typescript await ctx.step('fetchData', async ({ signal, stepId, parentStepId, run }) => { console.log(`Executing step ${stepId}, parent: ${parentStepId}`); // Use signal for cancellation const response = await fetch(url, { signal }); // Call a step definition from within an inline step await run(sendEmailStep, { to: 'user@example.com', body: 'Done!' }); return response.json(); }); ``` -------------------------------- ### GET /config Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/server-api.mdx Retrieves the server configuration settings. This endpoint is publicly accessible and does not require authentication. ```APIDOC ## GET /config ### Description Get server configuration. This endpoint does not require authentication. ### Method GET ### Endpoint `/config` ### Response #### Success Response (200) - **spansEnabled** (boolean) - Indicates if spans are enabled on the server. - **authEnabled** (boolean) - Indicates if authentication is enabled on the server. #### Response Example ```json { "spansEnabled": true, "authEnabled": true } ``` ``` -------------------------------- ### GET /jobs/:id/spans Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/server-api.mdx Retrieves detailed span information for a specific job, with options for filtering and sorting. ```APIDOC ## GET /jobs/:id/spans ### Description Get spans for a job. ### Method GET ### Endpoint `/jobs/:id/spans` ### Parameters #### Query Parameters - **fName** (string) - Optional - Filter by span name (string or comma-separated) - **fKind** (integer) - Optional - Filter by span kind (0=INTERNAL, 1=SERVER, 2=CLIENT, 3=PRODUCER, 4=CONSUMER) - **fTraceId** (string) - Optional - Filter by trace ID - **fAttributesFilter** (string) - Optional - JSONB filter on attributes (JSON string) - **sort** (string) - Optional - Sort string format: `"field:asc"` or `"field:desc"` ### Response #### Success Response (200) - **data** (array) - An array of span objects. - **traceId** (string) - The ID of the trace. - **spanId** (string) - The ID of the span. - **parentSpanId** (string) - The ID of the parent span, or null if it's a root span. - **jobId** (string) - The ID of the job associated with the span. - **stepId** (string) - The ID of the step associated with the span. - **name** (string) - The name of the span. - **kind** (integer) - The kind of the span (e.g., INTERNAL, SERVER). - **startTimeUnixNano** (string) - The start time of the span in nanoseconds. - **endTimeUnixNano** (string) - The end time of the span in nanoseconds. - **statusCode** (integer) - The status code of the span. - **statusMessage** (string) - The status message of the span, or null. - **attributes** (object) - Key-value pairs of attributes associated with the span. - **events** (array) - An array of events associated with the span. - **total** (integer) - The total number of spans returned. #### Response Example ```json { "data": [ { "traceId": "abc123...", "spanId": "def456...", "parentSpanId": null, "jobId": "job-uuid", "stepId": "step-uuid", "name": "step:fetchUser", "kind": 0, "startTimeUnixNano": "1704067200000000000", "endTimeUnixNano": "1704067201000000000", "statusCode": 1, "statusMessage": null, "attributes": { "duron.job.id": "job-uuid" }, "events": [] } ], "total": 1 } ``` ``` -------------------------------- ### Create a Custom Duron Adapter (TypeScript) Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/adapters.mdx Demonstrates how to create a custom Duron adapter by extending the base `Adapter` class. Requires implementing methods for starting/stopping the adapter and handling job creation and fetching. Other methods like `_fetch` also need implementation. ```typescript import { Adapter } from 'duron/adapter' import type { CreateJobOptions, Job, // ... other types } from 'duron/adapter' export class MyCustomAdapter extends Adapter { async _start() { // Initialize your database connection } async _stop() { // Close your database connection } protected async _createJob(options: CreateJobOptions): Promise { // Implement job creation } protected async _fetch(options: FetchOptions): Promise { // Implement job fetching } // ... implement other required methods } ``` -------------------------------- ### ActionCancelError Example (TypeScript) Source: https://github.com/geut/duron/blob/main/packages/docs/content/docs/error-handling.mdx Describes the `ActionCancelError` in Duron, which is thrown when an action is cancelled, for instance, by calling `client.cancelJob()`. This is a non-retriable error. ```typescript // When you cancel a job, the action receives this error // Assume client is an instance of Duron client // await client.cancelJob(jobId); ```