@brandboostinggmbh/observable-workflows 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,15 +1,396 @@
1
- # observable-workflows [![npm](https://img.shields.io/npm/v/ts-starter.svg)](https://npmjs.com/package/ts-starter)
1
+ # Observable Workflows [![npm](https://img.shields.io/npm/v/@brandboostinggmbh/observable-workflows.svg)](https://npmjs.com/package/@brandboostinggmbh/observable-workflows)
2
2
 
3
- [![Unit Test](https://github.com/sxzz/ts-starter/actions/workflows/unit-test.yml/badge.svg)](https://github.com/sxzz/ts-starter/actions/workflows/unit-test.yml)
3
+ [![Build Status](https://github.com/Brand-Boosting-GmbH/observable-workflows/actions/workflows/ci.yml/badge.svg)](https://github.com/Brand-Boosting-GmbH/observable-workflows/actions/workflows/ci.yml)
4
4
 
5
- TODO
5
+ A powerful TypeScript library for creating observable, durable workflows with step-by-step tracking, comprehensive logging, and database persistence. Built specifically for Cloudflare Workers and D1 database environments.
6
6
 
7
- ## Install
7
+ ## Features
8
+
9
+ - 🔍 **Observable Execution** - Track every step of your workflow with detailed logging
10
+ - 💾 **Durable Persistence** - Automatic state persistence using Cloudflare D1 database
11
+ - 🔄 **Retry Mechanisms** - Built-in workflow retry with step result reuse
12
+ - 📊 **Multi-tenancy Support** - Isolate workflows by tenant for SaaS applications
13
+ - ⚡ **Queue Integration** - Support for queue-based workflow processing
14
+ - 🏷️ **Property Management** - Attach and query custom properties on workflows
15
+ - 🔎 **Advanced Filtering** - Filter workflows by status, properties, date ranges
16
+ - 📝 **Comprehensive Logging** - Structured logging with multiple levels (info, warn, error)
17
+ - 🔧 **TypeScript First** - Full type safety and excellent developer experience
18
+
19
+ ## Installation
8
20
 
9
21
  ```bash
10
- npm i @brandboostinggmbh/observable-workflows
22
+ npm install @brandboostinggmbh/observable-workflows
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ### 1. Define a Workflow
28
+
29
+ ```typescript
30
+ import { defineWorkflow } from '@brandboostinggmbh/observable-workflows'
31
+
32
+ interface EmailInput {
33
+ to: string
34
+ subject: string
35
+ body: string
36
+ }
37
+
38
+ const sendEmailWorkflow = defineWorkflow<EmailInput>(
39
+ 'send-email',
40
+ async (input, { step, console }) => {
41
+ // Step 1: Validate email
42
+ await step('validate-email', async () => {
43
+ console.log('Validating email address:', input.to)
44
+ if (!input.to.includes('@')) {
45
+ throw new Error('Invalid email address')
46
+ }
47
+ return { valid: true }
48
+ })
49
+
50
+ // Step 2: Send email
51
+ const result = await step('send-email', async () => {
52
+ console.log('Sending email to:', input.to)
53
+ // Your email sending logic here - simulate async operation
54
+ await new Promise((resolve) => setTimeout(resolve, 100))
55
+ return { messageId: 'msg_123', status: 'sent' }
56
+ })
57
+
58
+ // Step 3: Log result
59
+ await step('log-result', async () => {
60
+ console.log('Email sent successfully:', result.messageId)
61
+ return { logged: true }
62
+ })
63
+
64
+ return result
65
+ },
66
+ )
67
+ ```
68
+
69
+ ### 2. Create Workflow Context and Execute
70
+
71
+ ```typescript
72
+ import { createWorkflowContext } from '@brandboostinggmbh/observable-workflows'
73
+
74
+ // Create workflow context with D1 database
75
+ const workflowContext = createWorkflowContext({
76
+ D1: env.D1, // Cloudflare D1 database binding
77
+ })
78
+
79
+ // Execute the workflow
80
+ const result = await workflowContext.call({
81
+ workflow: sendEmailWorkflow,
82
+ input: {
83
+ to: 'user@example.com',
84
+ subject: 'Welcome!',
85
+ body: 'Welcome to our service!',
86
+ },
87
+ workflowName: 'Welcome Email',
88
+ tenantId: 'tenant-123',
89
+ })
90
+ ```
91
+
92
+ ### 3. Access Workflow Logs and Data
93
+
94
+ ```typescript
95
+ import { createLogAccessor } from '@brandboostinggmbh/observable-workflows'
96
+
97
+ const logAccessor = createLogAccessor({
98
+ D1: env.D1,
99
+ tenantId: 'tenant-123',
100
+ })
101
+
102
+ // List recent workflows
103
+ const workflows = await logAccessor.listWorkflows(10, 0)
104
+
105
+ // Get specific workflow with steps and logs
106
+ const workflow = await logAccessor.getWorkflow(instanceId)
107
+ console.log('Workflow status:', workflow.workflowStatus)
108
+ console.log('Steps:', workflow.steps)
109
+ console.log('Logs:', workflow.logs)
110
+ ```
111
+
112
+ ## Core Concepts
113
+
114
+ ### Workflows
115
+
116
+ A workflow is a series of steps that execute in sequence. Each workflow has:
117
+
118
+ - **Type**: A unique identifier for the workflow type
119
+ - **Instance**: A specific execution of a workflow
120
+ - **Steps**: Individual units of work within the workflow
121
+ - **Properties**: Custom metadata that can be attached and queried
122
+
123
+ ### Steps
124
+
125
+ Steps are the individual units of work within a workflow. They provide:
126
+
127
+ - **Automatic retry** - Failed steps can be retried
128
+ - **Result caching** - Successful step results are cached for retry scenarios
129
+ - **Isolated logging** - Each step has its own log context
130
+ - **Error handling** - Failed steps capture error details
131
+
132
+ ### Tenancy
133
+
134
+ All workflows are isolated by tenant ID, enabling multi-tenant applications:
135
+
136
+ - Each workflow execution requires a `tenantId`
137
+ - Queries are automatically scoped to the tenant
138
+ - Complete data isolation between tenants
139
+
140
+ ## Advanced Usage
141
+
142
+ ### Workflow with Metadata and Properties
143
+
144
+ ```typescript
145
+ const processOrderWorkflow = defineWorkflow(
146
+ {
147
+ workflowType: 'process-order',
148
+ metadata: { version: '1.0', category: 'ecommerce' },
149
+ },
150
+ async (input, { step, console, setWorkflowProperty, setWorkflowName }) => {
151
+ // Set dynamic workflow name
152
+ await setWorkflowName(`Order ${input.orderId}`)
153
+
154
+ // Set searchable properties
155
+ await setWorkflowProperty('orderId', input.orderId)
156
+ await setWorkflowProperty('customerId', input.customerId)
157
+ await setWorkflowProperty('amount', input.amount)
158
+
159
+ const result = await step('process-payment', async () => {
160
+ // Payment processing logic - simulate async operation
161
+ await new Promise((resolve) => setTimeout(resolve, 100))
162
+ return { transactionId: 'txn_123' }
163
+ })
164
+
165
+ await step('update-inventory', async () => {
166
+ // Inventory update logic - simulate async operation
167
+ await new Promise((resolve) => setTimeout(resolve, 100))
168
+ return { updated: true }
169
+ })
170
+
171
+ return result
172
+ },
173
+ )
174
+ ```
175
+
176
+ ### Retry Failed Workflows
177
+
178
+ ```typescript
179
+ // Retry a failed workflow, reusing successful step results
180
+ await workflowContext.retry(processOrderWorkflow, failedWorkflowId, {
181
+ reuseSuccessfulSteps: true, // Default: true
182
+ })
183
+ ```
184
+
185
+ ### Queue-based Workflow Processing
186
+
187
+ ```typescript
188
+ import { createQueueWorkflowContext } from '@brandboostinggmbh/observable-workflows'
189
+
190
+ const queueContext = createQueueWorkflowContext({
191
+ workflowContext,
192
+ queue: env.WORKFLOW_QUEUE, // Cloudflare Queue binding
193
+ })
194
+
195
+ // Queue a workflow for processing
196
+ await queueContext.queueWorkflow({
197
+ workflow: sendEmailWorkflow,
198
+ input: emailData,
199
+ workflowName: 'Queued Email',
200
+ tenantId: 'tenant-123',
201
+ })
202
+ ```
203
+
204
+ ### Advanced Filtering and Querying
205
+
206
+ ```typescript
207
+ // Filter workflows by properties and status
208
+ const filteredWorkflows = await logAccessor.listWorkflows(50, 0, {
209
+ workflowStatus: ['completed', 'failed'],
210
+ properties: {
211
+ amount: { gt: 100 }, // Amount greater than 100
212
+ customerId: { equals: 'customer-456' },
213
+ },
214
+ startTime: {
215
+ gte: Date.now() - 24 * 60 * 60 * 1000, // Last 24 hours
216
+ },
217
+ workflowType: { contains: 'order' },
218
+ })
219
+
220
+ // Get workflow properties
221
+ const properties = await logAccessor.getWorkflowProperties(instanceId)
11
222
  ```
12
223
 
224
+ ## API Reference
225
+
226
+ ### Core Functions
227
+
228
+ #### `defineWorkflow<T>(type, callback)`
229
+
230
+ Defines a new workflow type.
231
+
232
+ **Parameters:**
233
+
234
+ - `type` (string | object): Workflow type or configuration object
235
+ - `callback` (function): Workflow implementation function
236
+
237
+ **Returns:** `WorkflowFunction<T>`
238
+
239
+ #### `createWorkflowContext(options)`
240
+
241
+ Creates a workflow execution context.
242
+
243
+ **Parameters:**
244
+
245
+ - `options.D1` (D1Database): Cloudflare D1 database instance
246
+ - `options.serializer?` (Serializer): Custom serialization (optional)
247
+ - `options.idFactory?` (function): Custom ID generation (optional)
248
+
249
+ **Returns:** `WorkflowContextInstance`
250
+
251
+ #### `createLogAccessor(options)`
252
+
253
+ Creates an accessor for querying workflow data.
254
+
255
+ **Parameters:**
256
+
257
+ - `options.D1` (D1Database): Cloudflare D1 database instance
258
+ - `options.tenantId` (string): Tenant identifier
259
+ - `options.serializer?` (Serializer): Custom serialization (optional)
260
+
261
+ **Returns:** Log accessor with query methods
262
+
263
+ ### Workflow Context Methods
264
+
265
+ #### `call(params)`
266
+
267
+ Executes a workflow.
268
+
269
+ **Parameters (object):**
270
+
271
+ - `workflow` (WorkflowFunction): The workflow to execute
272
+ - `input` (T): Input data for the workflow
273
+ - `workflowName` (string): Human-readable workflow name
274
+ - `tenantId` (string): Tenant identifier
275
+ - `parentInstanceId?` (string): Parent workflow ID (for retries)
276
+ - `reuseSuccessfulSteps?` (boolean): Whether to reuse successful steps in retries
277
+
278
+ #### `retry(workflow, retryInstanceId, retryOptions?)`
279
+
280
+ Retries a failed workflow.
281
+
282
+ **Parameters:**
283
+
284
+ - `workflow` (WorkflowFunction): The workflow definition
285
+ - `retryInstanceId` (string): ID of the workflow to retry
286
+ - `retryOptions?` (RetryWorkflowOptions): Retry configuration
287
+
288
+ ### Step Context
289
+
290
+ Within a workflow, the step function provides:
291
+
292
+ ```typescript
293
+ await step('step-name', async ({ console }) => {
294
+ console.log('Step is executing')
295
+ console.error('Something went wrong')
296
+ console.warn('Warning message')
297
+ // Simulate async operation
298
+ await new Promise((resolve) => setTimeout(resolve, 100))
299
+ // Return step result
300
+ return { success: true }
301
+ })
302
+ ```
303
+
304
+ ### Workflow Context
305
+
306
+ The workflow callback receives a context with:
307
+
308
+ - `step` (function): Execute a workflow step
309
+ - `console` (ConsoleWrapper): Logging interface
310
+ - `setWorkflowName` (function): Update workflow name
311
+ - `setWorkflowProperty` (function): Set workflow property
312
+
313
+ ## Configuration
314
+
315
+ ### Custom Serialization
316
+
317
+ ```typescript
318
+ import { createWorkflowContext } from '@brandboostinggmbh/observable-workflows'
319
+
320
+ const customSerializer = {
321
+ serialize: (obj: any) => JSON.stringify(obj),
322
+ deserialize: (str: string) => JSON.parse(str),
323
+ }
324
+
325
+ const context = createWorkflowContext({
326
+ D1: env.D1,
327
+ serializer: customSerializer,
328
+ })
329
+ ```
330
+
331
+ ### Custom ID Generation
332
+
333
+ ```typescript
334
+ const customIdFactory = () => `custom_${Date.now()}_${Math.random()}`
335
+
336
+ const context = createWorkflowContext({
337
+ D1: env.D1,
338
+ idFactory: customIdFactory,
339
+ })
340
+ ```
341
+
342
+ ## Database Schema
343
+
344
+ The library automatically creates the following tables in your D1 database:
345
+
346
+ - **WorkflowTable**: Stores workflow instances
347
+ - **StepTable**: Stores individual step executions
348
+ - **LogTable**: Stores all log entries
349
+ - **WorkflowProperties**: Stores custom workflow properties
350
+
351
+ Tables are created automatically when first accessing the database.
352
+
353
+ ## Error Handling
354
+
355
+ The library provides comprehensive error handling:
356
+
357
+ - **Step Failures**: Captured with full error details and stack traces
358
+ - **Workflow Failures**: Marked with appropriate status and error information
359
+ - **Retry Logic**: Failed workflows can be retried with step result reuse
360
+ - **Validation**: Input validation and type checking
361
+
362
+ ## Best Practices
363
+
364
+ 1. **Use Descriptive Step Names**: Make step names descriptive for better observability
365
+ 2. **Keep Steps Atomic**: Each step should represent a single unit of work
366
+ 3. **Handle Errors Gracefully**: Use try-catch within steps for custom error handling
367
+ 4. **Use Properties for Searching**: Add relevant properties to make workflows searchable
368
+ 5. **Monitor Performance**: Use the logging data to monitor workflow performance
369
+ 6. **Tenant Isolation**: Always use consistent tenant IDs for proper data isolation
370
+
371
+ ## TypeScript Support
372
+
373
+ This library is built with TypeScript and provides full type safety:
374
+
375
+ ```typescript
376
+ interface MyInput {
377
+ userId: string
378
+ data: Record<string, any>
379
+ }
380
+
381
+ const typedWorkflow = defineWorkflow<MyInput>(
382
+ 'my-workflow',
383
+ async (input, ctx) => {
384
+ // input is fully typed as MyInput
385
+ // ctx provides typed workflow context
386
+ },
387
+ )
388
+ ```
389
+
390
+ ## Contributing
391
+
392
+ We welcome contributions! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details.
393
+
13
394
  ## License
14
395
 
15
396
  [MIT](./LICENSE) License © 2025 [Tim Stepanov](https://github.com/TimStepanovAtBrandBoosting)
package/dist/index.d.ts CHANGED
@@ -49,7 +49,9 @@ declare function insertStepRecordFull(context: StepContextOptions, {
49
49
  startTime,
50
50
  endTime,
51
51
  result,
52
- error
52
+ error,
53
+ resultRef,
54
+ errorRef
53
55
  }: {
54
56
  instanceId: string;
55
57
  name: string;
@@ -59,6 +61,8 @@ declare function insertStepRecordFull(context: StepContextOptions, {
59
61
  endTime: number | null;
60
62
  result: string | null;
61
63
  error: string | null;
64
+ resultRef?: string | null;
65
+ errorRef?: string | null;
62
66
  }): Promise<D1Result<Record<string, unknown>>>;
63
67
  declare function pushLogToDB(options: InternalWorkflowContextOptions, {
64
68
  instanceId,
@@ -78,6 +82,18 @@ declare function pushLogToDB(options: InternalWorkflowContextOptions, {
78
82
  tenantId: string;
79
83
  }): Promise<D1Result<Record<string, unknown>>>;
80
84
  declare function tryDeserializeObj(obj: string, serializer: Serializer): any;
85
+ /**
86
+ * Serialize data and store externally if it exceeds the threshold
87
+ * Returns tuple of [data, externalRef] where one will be non-null
88
+ */
89
+ declare function serializeWithExternalStorage(obj: any, serializer: Serializer, externalBlobStorage?: ExternalBlobStorage): Promise<{
90
+ data: string | null;
91
+ externalRef: string | null;
92
+ }>;
93
+ /**
94
+ * Deserialize data from direct storage or external storage
95
+ */
96
+ declare function deserializeWithExternalStorage(data: string | null, externalRef: string | null, serializer: Serializer, externalBlobStorage?: ExternalBlobStorage): Promise<any>;
81
97
  /**
82
98
  * We want to save steps to a databse, we are using D1, so SQLite is the database.
83
99
  * Step Table:
@@ -98,7 +114,8 @@ declare function tryDeserializeObj(obj: string, serializer: Serializer): any;
98
114
  * - type: string (info, error, warning)
99
115
  */
100
116
  /**
101
- * Ensure that the required tables exist in D1 (SQLite).
117
+ * Ensure that the required tables exist in D1 (SQLite) and migrate schema if needed.
118
+ * This function is idempotent and ensures the schema matches the current requirements.
102
119
  */
103
120
  declare function ensureTables(db: D1Database): Promise<void>;
104
121
  declare function workflowTableRowToWorkflowRun(row: {
@@ -106,12 +123,13 @@ declare function workflowTableRowToWorkflowRun(row: {
106
123
  workflowType: string;
107
124
  workflowName: string;
108
125
  input: string;
126
+ inputRef: string | null;
109
127
  tenantId: string;
110
128
  workflowStatus: StepWorkflowStatus;
111
129
  startTime: number;
112
130
  endTime: number | null;
113
131
  parentInstanceId: string | null;
114
- }, serializer: Serializer): WorkflowRun;
132
+ }, serializer: Serializer, externalBlobStorage?: ExternalBlobStorage): Promise<WorkflowRun>;
115
133
  declare function updateWorkflowName(context: {
116
134
  D1: D1Database;
117
135
  }, instanceId: string, newWorkflowName: string): Promise<D1Result<Record<string, unknown>>>;
@@ -149,6 +167,8 @@ type StepContextOptions = {
149
167
  idFactory: () => string;
150
168
  /** If true this step context will attempt to reuse results from parent instance steps where possible. Defaults to True */
151
169
  reuseSuccessfulSteps?: boolean;
170
+ /** Optional external blob storage for large data that exceeds D1 size limits */
171
+ externalBlobStorage?: ExternalBlobStorage;
152
172
  };
153
173
  type ConsoleWrapper = {
154
174
  log: (message?: any, ...optionalParams: any[]) => void;
@@ -205,15 +225,120 @@ type WorkflowPropertyDefinition = {
205
225
  key: string;
206
226
  valueType: 'string' | 'number' | 'boolean' | 'object';
207
227
  };
228
+ /**
229
+ * Date range filter for filtering workflows by time periods
230
+ */
231
+ type DateRangeFilter = {
232
+ /** Greater than or equal to (inclusive) */
233
+ gte?: number;
234
+ /** Less than or equal to (inclusive) */
235
+ lte?: number;
236
+ /** Greater than (exclusive) */
237
+ gt?: number;
238
+ /** Less than (exclusive) */
239
+ lt?: number;
240
+ };
241
+ /**
242
+ * String filter supporting exact match or substring matching
243
+ */
244
+ type StringFilter = string | {
245
+ equals: string;
246
+ } | {
247
+ contains: string;
248
+ };
249
+ /**
250
+ * Property filter supporting various comparison operators for custom workflow properties
251
+ */
252
+ type PropertyFilter = {
253
+ /** Exact match */
254
+ equals?: any;
255
+ /** Substring match (for string values) */
256
+ contains?: string;
257
+ /** Greater than (for numeric values) */
258
+ gt?: number;
259
+ /** Greater than or equal to (for numeric values) */
260
+ gte?: number;
261
+ /** Less than (for numeric values) */
262
+ lt?: number;
263
+ /** Less than or equal to (for numeric values) */
264
+ lte?: number;
265
+ /** Match any value in the array */
266
+ in?: any[];
267
+ };
268
+ /**
269
+ * Comprehensive filter object for workflow listing with support for:
270
+ * - Text search across workflow names and types
271
+ * - Direct field filtering with various operators
272
+ * - Date range filtering
273
+ * - Custom property filtering with joins
274
+ *
275
+ * @example
276
+ * ```typescript
277
+ * // Text search
278
+ * const workflows = await listWorkflows(10, 0, { search: "payment" })
279
+ *
280
+ * // Status and type filtering
281
+ * const workflows = await listWorkflows(10, 0, {
282
+ * workflowStatus: ["completed", "failed"],
283
+ * workflowType: "PaymentWorkflow"
284
+ * })
285
+ *
286
+ * // Date range filtering
287
+ * const workflows = await listWorkflows(10, 0, {
288
+ * startTime: { gte: Date.now() - 86400000 } // Last 24 hours
289
+ * })
290
+ *
291
+ * // Property filtering
292
+ * const workflows = await listWorkflows(10, 0, {
293
+ * properties: {
294
+ * amount: { gt: 100, lte: 1000 },
295
+ * currency: { equals: "USD" },
296
+ * customer: { contains: "acme" }
297
+ * }
298
+ * })
299
+ *
300
+ * // Combined filtering
301
+ * const workflows = await listWorkflows(10, 0, {
302
+ * search: "payment",
303
+ * workflowStatus: "completed",
304
+ * startTime: { gte: Date.now() - 86400000 },
305
+ * properties: { amount: { gt: 100 } }
306
+ * })
307
+ * ```
308
+ */
309
+ type WorkflowFilter = {
310
+ /** Text search across workflow name and type */
311
+ search?: string;
312
+ /** Filter by workflow type (exact match) */
313
+ workflowType?: string | string[];
314
+ /** Filter by workflow status */
315
+ workflowStatus?: StepWorkflowStatus | StepWorkflowStatus[];
316
+ /** Filter by workflow name with string matching */
317
+ workflowName?: StringFilter;
318
+ /** Filter by workflow start time */
319
+ startTime?: DateRangeFilter;
320
+ /** Filter by workflow end time */
321
+ endTime?: DateRangeFilter;
322
+ /** Filter by custom workflow properties */
323
+ properties?: {
324
+ [key: string]: PropertyFilter;
325
+ };
326
+ };
208
327
  type StepContext = Awaited<ReturnType<typeof createStepContext>>;
209
328
  type Serializer = {
210
329
  serialize: (data: any) => string;
211
330
  deserialize: (data: string) => any;
212
331
  };
332
+ type ExternalBlobStorage = {
333
+ threshold: number;
334
+ set: (data: string) => Promise<string>;
335
+ get: (id: string) => Promise<string>;
336
+ };
213
337
  type WorkflowContextOptions = {
214
338
  D1: D1Database;
215
339
  idFactory?: () => string;
216
340
  serializer?: Serializer;
341
+ externalBlobStorage?: ExternalBlobStorage;
217
342
  };
218
343
  type InternalWorkflowContextOptions = WorkflowContextOptions & Required<Pick<WorkflowContextOptions, 'serializer' | 'idFactory'>>;
219
344
  type WorkflowContextInstance = {
@@ -263,43 +388,36 @@ type RetryWorkflowOptions = {
263
388
  reuseSuccessfulSteps?: boolean;
264
389
  };
265
390
 
266
- //#endregion
267
- //#region src/observableWorkflows/defineWorkflow.d.ts
268
- declare function defineWorkflow<I extends {} | null>(workflow: {
269
- workflowType: string;
270
- metadata?: Record<string, any>;
271
- }, callback: (input: I, ctx: WorkflowContext) => Promise<any>): WorkflowFunction<I>;
272
- declare function defineWorkflow<I extends {} | null>(workflowType: string, callback: (input: I, ctx: WorkflowContext) => Promise<any>): WorkflowFunction<I>;
273
-
274
- //#endregion
275
- //#region src/observableWorkflows/createWorkflowContext.d.ts
276
- declare function createWorkflowContext(options: WorkflowContextOptions): WorkflowContextInstance;
277
-
278
- //#endregion
279
- //#region src/observableWorkflows/createQueueWorkflowContext.d.ts
280
- declare function createQueueWorkflowContext(options: QueueWorkflowContextOptions): {
281
- enqueueWorkflow: <I>(workflow: WorkflowFunction<I>, tenantId: string, input: I, initialName: string) => Promise<void>;
282
- enqueueRetryWorkflow: <I>(workflow: WorkflowFunction<I>, tenantId: string, oldInstanceId: string) => Promise<void>;
283
- handleWorkflowQueueMessage: (message: WorkflowQueueMessage, env: {
284
- LOG_DB: D1Database;
285
- }, workflowResolver: (workflowType: string) => WorkflowFunction<any> | undefined) => Promise<void>;
286
- };
287
-
288
391
  //#endregion
289
392
  //#region src/observableWorkflows/createLogAccessor.d.ts
290
393
  declare const createLogAccessor: (context: {
291
394
  D1: D1Database;
292
395
  tenantId: string;
293
396
  serializer?: Serializer;
397
+ externalBlobStorage?: ExternalBlobStorage;
294
398
  }) => {
295
399
  listSteps: (limit: number, offset: number, instanceId?: string | undefined) => Promise<Step[]>;
296
400
  getStep: (instanceId: string, stepName: string) => Promise<Step | null>;
297
- listWorkflows: (limit: number, offset: number) => Promise<WorkflowRun[]>;
401
+ listWorkflows: (limit: number, offset: number, filter?: WorkflowFilter) => Promise<WorkflowRun[]>;
298
402
  getWorkflow: (instanceId: string) => Promise<WorkflowRun | null>;
299
403
  getWorkflowTypesByTenantId: (tenantId: string) => Promise<string[]>;
300
404
  getPropertiesKeys: (instanceId?: string) => Promise<WorkflowPropertyDefinition[]>;
301
405
  };
302
406
 
407
+ //#endregion
408
+ //#region src/observableWorkflows/createQueueWorkflowContext.d.ts
409
+ declare function createQueueWorkflowContext(options: QueueWorkflowContextOptions): {
410
+ enqueueWorkflow: <I>(workflow: WorkflowFunction<I>, tenantId: string, input: I, initialName: string) => Promise<void>;
411
+ enqueueRetryWorkflow: <I>(workflow: WorkflowFunction<I>, tenantId: string, oldInstanceId: string) => Promise<void>;
412
+ handleWorkflowQueueMessage: (message: WorkflowQueueMessage, env: {
413
+ LOG_DB: D1Database;
414
+ }, workflowResolver: (workflowType: string) => WorkflowFunction<any> | undefined) => Promise<void>;
415
+ };
416
+
417
+ //#endregion
418
+ //#region src/observableWorkflows/createWorkflowContext.d.ts
419
+ declare function createWorkflowContext(options: WorkflowContextOptions): WorkflowContextInstance;
420
+
303
421
  //#endregion
304
422
  //#region src/observableWorkflows/defaultImplementations.d.ts
305
423
  declare const defaultSerializer: {
@@ -312,4 +430,48 @@ declare const defaultSerializer: {
312
430
  declare const defaultIdFactory: () => string;
313
431
 
314
432
  //#endregion
315
- export { ConsoleWrapper, InternalWorkflowContextOptions, Log, PossibleValueTypeNames, PossibleValueTypes, QueueWorkflowContextOptions, Serializer, Step, StepContextOptions, StepCtx, StepWorkflowStatus, ValueTypeMap, WorkflowContext, WorkflowContextInstance, WorkflowContextOptions, WorkflowFunction, WorkflowProperty, WorkflowPropertyDefinition, WorkflowQueueMessage, WorkflowRun, createLogAccessor, createQueueWorkflowContext, createStepContext, createWorkflowContext, defaultIdFactory, defaultSerializer, defineWorkflow, ensureTables, finalizeWorkflowRecord, insertStepRecordFull, insertWorkflowRecord, pushLogToDB, tryDeserializeObj, updateWorkflowName, upsertWorkflowProperty, workflowTableRowToWorkflowRun };
433
+ //#region src/observableWorkflows/defineWorkflow.d.ts
434
+ declare function defineWorkflow<I extends {} | null>(workflow: {
435
+ workflowType: string;
436
+ metadata?: Record<string, any>;
437
+ }, callback: (input: I, ctx: WorkflowContext) => Promise<any>): WorkflowFunction<I>;
438
+ declare function defineWorkflow<I extends {} | null>(workflowType: string, callback: (input: I, ctx: WorkflowContext) => Promise<any>): WorkflowFunction<I>;
439
+
440
+ //#endregion
441
+ //#region src/observableWorkflows/r2ExternalBlobStorage.d.ts
442
+ /**
443
+ * Configuration options for Cloudflare R2 external blob storage
444
+ */
445
+ type R2ExternalBlobStorageOptions = {
446
+ /** The R2 bucket instance to use for storage */
447
+ bucket: R2Bucket;
448
+ /** Size threshold in bytes for when to use external storage */
449
+ threshold: number;
450
+ /** Optional prefix for all keys stored in R2 */
451
+ keyPrefix?: string;
452
+ /** Optional custom ID factory for generating unique keys */
453
+ idFactory?: () => string;
454
+ };
455
+ /**
456
+ * Cloudflare R2 implementation of ExternalBlobStorage interface
457
+ *
458
+ * @example
459
+ * ```typescript
460
+ * import { createWorkflowContext, createR2ExternalBlobStorage } from '@brandboostinggmbh/observable-workflows'
461
+ *
462
+ * const externalBlobStorage = createR2ExternalBlobStorage({
463
+ * bucket: env.MY_R2_BUCKET,
464
+ * threshold: 1024 * 1024, // 1MB threshold
465
+ * keyPrefix: 'workflows/', // Optional prefix
466
+ * })
467
+ *
468
+ * const workflowContext = createWorkflowContext({
469
+ * D1: env.D1,
470
+ * externalBlobStorage,
471
+ * })
472
+ * ```
473
+ */
474
+ declare function createR2ExternalBlobStorage(options: R2ExternalBlobStorageOptions): ExternalBlobStorage;
475
+
476
+ //#endregion
477
+ export { ConsoleWrapper, DateRangeFilter, ExternalBlobStorage, InternalWorkflowContextOptions, Log, PossibleValueTypeNames, PossibleValueTypes, PropertyFilter, QueueWorkflowContextOptions, R2ExternalBlobStorageOptions, Serializer, Step, StepContextOptions, StepCtx, StepWorkflowStatus, StringFilter, ValueTypeMap, WorkflowContext, WorkflowContextInstance, WorkflowContextOptions, WorkflowFilter, WorkflowFunction, WorkflowProperty, WorkflowPropertyDefinition, WorkflowQueueMessage, WorkflowRun, createLogAccessor, createQueueWorkflowContext, createR2ExternalBlobStorage, createStepContext, createWorkflowContext, defaultIdFactory, defaultSerializer, defineWorkflow, deserializeWithExternalStorage, ensureTables, finalizeWorkflowRecord, insertStepRecordFull, insertWorkflowRecord, pushLogToDB, serializeWithExternalStorage, tryDeserializeObj, updateWorkflowName, upsertWorkflowProperty, workflowTableRowToWorkflowRun };