flowfn 0.0.1

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.
Files changed (59) hide show
  1. package/dist/index.d.mts +1305 -0
  2. package/dist/index.d.ts +1305 -0
  3. package/dist/index.js +3180 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.mjs +3088 -0
  6. package/dist/index.mjs.map +1 -0
  7. package/docs/API.md +801 -0
  8. package/docs/USAGE.md +619 -0
  9. package/package.json +75 -0
  10. package/src/adapters/base.ts +46 -0
  11. package/src/adapters/memory.ts +183 -0
  12. package/src/adapters/postgres/index.ts +383 -0
  13. package/src/adapters/postgres/postgres.test.ts +100 -0
  14. package/src/adapters/postgres/schema.ts +110 -0
  15. package/src/adapters/redis.test.ts +124 -0
  16. package/src/adapters/redis.ts +331 -0
  17. package/src/core/flow-fn.test.ts +70 -0
  18. package/src/core/flow-fn.ts +198 -0
  19. package/src/core/metrics.ts +198 -0
  20. package/src/core/scheduler.test.ts +80 -0
  21. package/src/core/scheduler.ts +154 -0
  22. package/src/index.ts +57 -0
  23. package/src/monitoring/health.ts +261 -0
  24. package/src/patterns/backoff.ts +30 -0
  25. package/src/patterns/batching.ts +248 -0
  26. package/src/patterns/circuit-breaker.test.ts +52 -0
  27. package/src/patterns/circuit-breaker.ts +52 -0
  28. package/src/patterns/priority.ts +146 -0
  29. package/src/patterns/rate-limit.ts +290 -0
  30. package/src/patterns/retry.test.ts +62 -0
  31. package/src/queue/batch.test.ts +35 -0
  32. package/src/queue/dependencies.test.ts +33 -0
  33. package/src/queue/dlq.ts +222 -0
  34. package/src/queue/job.ts +67 -0
  35. package/src/queue/queue.ts +243 -0
  36. package/src/queue/types.ts +153 -0
  37. package/src/queue/worker.ts +66 -0
  38. package/src/storage/event-log.ts +205 -0
  39. package/src/storage/job-storage.ts +206 -0
  40. package/src/storage/workflow-storage.ts +182 -0
  41. package/src/stream/stream.ts +194 -0
  42. package/src/stream/types.ts +81 -0
  43. package/src/utils/hashing.ts +29 -0
  44. package/src/utils/id-generator.ts +109 -0
  45. package/src/utils/serialization.ts +142 -0
  46. package/src/utils/time.ts +167 -0
  47. package/src/workflow/advanced.test.ts +43 -0
  48. package/src/workflow/events.test.ts +39 -0
  49. package/src/workflow/types.ts +132 -0
  50. package/src/workflow/workflow.test.ts +55 -0
  51. package/src/workflow/workflow.ts +422 -0
  52. package/tests/dlq.test.ts +205 -0
  53. package/tests/health.test.ts +228 -0
  54. package/tests/integration.test.ts +253 -0
  55. package/tests/stream.test.ts +233 -0
  56. package/tests/workflow.test.ts +286 -0
  57. package/tsconfig.json +17 -0
  58. package/tsup.config.ts +10 -0
  59. package/vitest.config.ts +15 -0
@@ -0,0 +1,198 @@
1
+ import { FlowAdapter } from "../adapters/base.js";
2
+ import { MemoryAdapter } from "../adapters/memory.js";
3
+ import { Queue, QueueOptions } from "../queue/types.js";
4
+ import { QueueImpl } from "../queue/queue.js";
5
+ import { Stream, StreamOptions } from "../stream/types.js";
6
+ import { StreamImpl } from "../stream/stream.js";
7
+ import { Workflow, WorkflowBuilder } from "../workflow/types.js";
8
+ import { WorkflowBuilderImpl } from "../workflow/workflow.js";
9
+ import { Scheduler } from "./scheduler.js";
10
+ import { MetricsManager } from "./metrics.js";
11
+ import {
12
+ HealthCheckerImpl,
13
+ HealthStatus,
14
+ MemoryEventTracker,
15
+ EventTracker,
16
+ } from "../monitoring/health.js";
17
+ import EventEmitter from "eventemitter3";
18
+
19
+ export interface FlowFnConfig {
20
+ adapter:
21
+ | FlowAdapter
22
+ | "memory"
23
+ | "redis"
24
+ | "postgres"
25
+ | "d1"
26
+ | "sqs"
27
+ | "kafka";
28
+ namespace?: string;
29
+ defaultJobOptions?: any;
30
+ defaultQueueOptions?: QueueOptions;
31
+ defaultStreamOptions?: StreamOptions;
32
+ telemetry?: {
33
+ enabled: boolean;
34
+ provider?: "opentelemetry" | "custom";
35
+ };
36
+ onError?: (error: Error, context: any) => void;
37
+ }
38
+
39
+ export interface FlowFn {
40
+ queue<T = any>(name: string, options?: QueueOptions): Queue<T>;
41
+ listQueues(): Promise<string[]>;
42
+
43
+ stream<T = any>(name: string, options?: StreamOptions): Stream<T>;
44
+ listStreams(): Promise<string[]>;
45
+
46
+ workflow<T = any>(name: string): WorkflowBuilder<T>;
47
+ listWorkflows(): Promise<Workflow[]>;
48
+
49
+ scheduler(): Scheduler;
50
+
51
+ metrics: MetricsManager;
52
+ healthCheck(): Promise<HealthStatus>;
53
+ getEventTracker(): EventTracker;
54
+
55
+ on(event: string, handler: (...args: any[]) => void): void;
56
+ off(event: string, handler: (...args: any[]) => void): void;
57
+
58
+ close(): Promise<void>;
59
+ }
60
+
61
+ export class FlowFnImpl extends EventEmitter implements FlowFn {
62
+ private adapter: FlowAdapter;
63
+ private queues: Map<string, Queue> = new Map();
64
+ private streams: Map<string, Stream> = new Map();
65
+ private _metrics: MetricsManager;
66
+ private _scheduler: Scheduler;
67
+ private healthChecker: HealthCheckerImpl;
68
+ private eventTracker: EventTracker;
69
+ private startTime: number;
70
+
71
+ constructor(config: FlowFnConfig) {
72
+ super();
73
+ this.startTime = Date.now();
74
+
75
+ if (config.adapter === "memory") {
76
+ this.adapter = new MemoryAdapter();
77
+ } else if (typeof config.adapter === "string") {
78
+ // Placeholder for other string-based adapter inits
79
+ throw new Error(
80
+ `Adapter ${config.adapter} not automatically initialized yet. Pass an instance.`
81
+ );
82
+ } else {
83
+ this.adapter = config.adapter;
84
+ }
85
+
86
+ this._metrics = new MetricsManager(this.adapter);
87
+ this._scheduler = new Scheduler();
88
+ this.healthChecker = new HealthCheckerImpl();
89
+ this.eventTracker = new MemoryEventTracker();
90
+
91
+ // Add custom health checks
92
+ this.setupHealthChecks();
93
+ }
94
+
95
+ private setupHealthChecks(): void {
96
+ // Queue health check
97
+ this.healthChecker.addCheck("queues", async () => {
98
+ const queueCount = this.queues.size;
99
+ return {
100
+ name: "queues",
101
+ status: "pass",
102
+ details: {
103
+ count: queueCount,
104
+ active: Array.from(this.queues.keys()),
105
+ },
106
+ };
107
+ });
108
+
109
+ // Adapter health check
110
+ this.healthChecker.addCheck("adapter", async () => {
111
+ try {
112
+ // Simple ping check
113
+ await this.adapter.getQueueStats("health-check");
114
+ return {
115
+ name: "adapter",
116
+ status: "pass",
117
+ message: "Adapter responding",
118
+ };
119
+ } catch (error) {
120
+ return {
121
+ name: "adapter",
122
+ status: "fail",
123
+ message: (error as Error).message,
124
+ };
125
+ }
126
+ });
127
+ }
128
+
129
+ queue<T = any>(name: string, options?: QueueOptions): Queue<T> {
130
+ if (!this.queues.has(name)) {
131
+ this.queues.set(name, new QueueImpl<T>(name, this.adapter, options));
132
+ }
133
+ return this.queues.get(name) as Queue<T>;
134
+ }
135
+
136
+ async listQueues(): Promise<string[]> {
137
+ return Array.from(this.queues.keys());
138
+ }
139
+
140
+ stream<T = any>(name: string, options?: StreamOptions): Stream<T> {
141
+ if (!this.streams.has(name)) {
142
+ this.streams.set(name, new StreamImpl<T>(name, this.adapter, options));
143
+ }
144
+ return this.streams.get(name) as Stream<T>;
145
+ }
146
+
147
+ async listStreams(): Promise<string[]> {
148
+ return Array.from(this.streams.keys());
149
+ }
150
+
151
+ workflow<T = any>(name: string): WorkflowBuilder<T> {
152
+ return new WorkflowBuilderImpl<T>(name, this.adapter);
153
+ }
154
+
155
+ async listWorkflows(): Promise<Workflow[]> {
156
+ return [];
157
+ }
158
+
159
+ scheduler(): Scheduler {
160
+ return this._scheduler;
161
+ }
162
+
163
+ get metrics(): MetricsManager {
164
+ return this._metrics;
165
+ }
166
+
167
+ async healthCheck(): Promise<HealthStatus> {
168
+ const health = await this.healthChecker.check();
169
+
170
+ // Track health check event
171
+ this.eventTracker.track({
172
+ type: "health.check",
173
+ category: "system",
174
+ severity: health.healthy ? "info" : "warn",
175
+ message: health.healthy ? "System healthy" : "System unhealthy",
176
+ metadata: { checksCount: health.checks.length },
177
+ });
178
+
179
+ return health;
180
+ }
181
+
182
+ /**
183
+ * Get event tracker
184
+ */
185
+ getEventTracker(): EventTracker {
186
+ return this.eventTracker;
187
+ }
188
+
189
+ async close(): Promise<void> {
190
+ await this.adapter.cleanup();
191
+ for (const q of this.queues.values()) await q.close();
192
+ for (const s of this.streams.values()) await s.close();
193
+ }
194
+ }
195
+
196
+ export function createFlow(config: FlowFnConfig): FlowFn {
197
+ return new FlowFnImpl(config);
198
+ }
@@ -0,0 +1,198 @@
1
+ import { FlowAdapter } from "../adapters/base.js";
2
+
3
+ export interface MetricDataPoint {
4
+ timestamp: number;
5
+ value: number;
6
+ tags?: Record<string, string>;
7
+ }
8
+
9
+ export interface TimeSeriesMetrics {
10
+ dataPoints: MetricDataPoint[];
11
+
12
+ // Aggregations
13
+ min: number;
14
+ max: number;
15
+ avg: number;
16
+ sum: number;
17
+ count: number;
18
+ p50?: number;
19
+ p95?: number;
20
+ p99?: number;
21
+ }
22
+
23
+ export class MetricsManager {
24
+ private adapter: FlowAdapter;
25
+ private metrics: Map<string, MetricDataPoint[]> = new Map();
26
+ private maxDataPoints = 1000;
27
+
28
+ constructor(adapter: FlowAdapter) {
29
+ this.adapter = adapter;
30
+ }
31
+
32
+ /**
33
+ * Record a metric data point
34
+ */
35
+ record(name: string, value: number, tags?: Record<string, string>): void {
36
+ const key = this.getMetricKey(name, tags);
37
+ if (!this.metrics.has(key)) {
38
+ this.metrics.set(key, []);
39
+ }
40
+
41
+ const dataPoints = this.metrics.get(key)!;
42
+ dataPoints.push({
43
+ timestamp: Date.now(),
44
+ value,
45
+ tags,
46
+ });
47
+
48
+ // Trim if exceeds max
49
+ if (dataPoints.length > this.maxDataPoints) {
50
+ this.metrics.set(key, dataPoints.slice(-this.maxDataPoints));
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Get time series for a metric
56
+ */
57
+ getTimeSeries(
58
+ name: string,
59
+ options?: {
60
+ tags?: Record<string, string>;
61
+ since?: number;
62
+ limit?: number;
63
+ }
64
+ ): TimeSeriesMetrics | null {
65
+ const key = this.getMetricKey(name, options?.tags);
66
+ let dataPoints = this.metrics.get(key) || [];
67
+
68
+ if (options?.since) {
69
+ dataPoints = dataPoints.filter((dp) => dp.timestamp >= options.since!);
70
+ }
71
+
72
+ if (options?.limit) {
73
+ dataPoints = dataPoints.slice(-options.limit);
74
+ }
75
+
76
+ if (dataPoints.length === 0) {
77
+ return null;
78
+ }
79
+
80
+ return this.calculateAggregations(dataPoints);
81
+ }
82
+
83
+ private calculateAggregations(
84
+ dataPoints: MetricDataPoint[]
85
+ ): TimeSeriesMetrics {
86
+ const values = dataPoints.map((dp) => dp.value).sort((a, b) => a - b);
87
+ const sum = values.reduce((a, b) => a + b, 0);
88
+ const count = values.length;
89
+
90
+ return {
91
+ dataPoints,
92
+ min: values[0],
93
+ max: values[count - 1],
94
+ avg: sum / count,
95
+ sum,
96
+ count,
97
+ p50: this.percentile(values, 50),
98
+ p95: this.percentile(values, 95),
99
+ p99: this.percentile(values, 99),
100
+ };
101
+ }
102
+
103
+ private percentile(sorted: number[], p: number): number {
104
+ const index = Math.ceil((sorted.length * p) / 100) - 1;
105
+ return sorted[Math.max(0, index)];
106
+ }
107
+
108
+ private getMetricKey(name: string, tags?: Record<string, string>): string {
109
+ if (!tags) return name;
110
+ const tagStr = Object.entries(tags)
111
+ .sort(([a], [b]) => a.localeCompare(b))
112
+ .map(([k, v]) => `${k}:${v}`)
113
+ .join(",");
114
+ return `${name}{${tagStr}}`;
115
+ }
116
+
117
+ async getQueueMetrics(name: string): Promise<any> {
118
+ const stats = await this.adapter.getQueueStats(name);
119
+
120
+ // Get throughput from recorded metrics
121
+ const throughputMetrics = this.getTimeSeries("queue.throughput", {
122
+ tags: { queue: name },
123
+ since: Date.now() - 60000, // Last minute
124
+ });
125
+
126
+ const durationMetrics = this.getTimeSeries("queue.job.duration", {
127
+ tags: { queue: name },
128
+ });
129
+
130
+ return {
131
+ ...stats,
132
+ throughput: throughputMetrics?.avg || 0,
133
+ avgDuration: durationMetrics?.avg || 0,
134
+ p95Duration: durationMetrics?.p95 || 0,
135
+ };
136
+ }
137
+
138
+ async getStreamMetrics(name: string): Promise<any> {
139
+ const info = await this.adapter.getStreamInfo(name);
140
+
141
+ const throughputMetrics = this.getTimeSeries("stream.throughput", {
142
+ tags: { stream: name },
143
+ since: Date.now() - 1000, // Last second
144
+ });
145
+
146
+ return {
147
+ ...info,
148
+ lag: 0,
149
+ throughput: throughputMetrics?.avg || 0,
150
+ avgLatency: 0,
151
+ };
152
+ }
153
+
154
+ async getWorkflowMetrics(name: string): Promise<any> {
155
+ const metricsData = this.getTimeSeries("workflow.executions", {
156
+ tags: { workflow: name },
157
+ });
158
+
159
+ return {
160
+ totalExecutions: metricsData?.count || 0,
161
+ running: 0,
162
+ completed: 0,
163
+ failed: 0,
164
+ successRate: 0,
165
+ avgDuration: metricsData?.avg || 0,
166
+ };
167
+ }
168
+
169
+ async getSystemMetrics(): Promise<any> {
170
+ const memUsage =
171
+ typeof process !== "undefined" && process.memoryUsage
172
+ ? process.memoryUsage().heapUsed
173
+ : 0;
174
+
175
+ return {
176
+ queues: 0,
177
+ streams: 0,
178
+ workflows: 0,
179
+ workers: 0,
180
+ memoryUsage: memUsage,
181
+ };
182
+ }
183
+
184
+ /**
185
+ * Clear old metrics
186
+ */
187
+ cleanup(maxAge: number): void {
188
+ const now = Date.now();
189
+ for (const [key, dataPoints] of this.metrics.entries()) {
190
+ const filtered = dataPoints.filter((dp) => now - dp.timestamp <= maxAge);
191
+ if (filtered.length === 0) {
192
+ this.metrics.delete(key);
193
+ } else {
194
+ this.metrics.set(key, filtered);
195
+ }
196
+ }
197
+ }
198
+ }
@@ -0,0 +1,80 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { Scheduler } from './scheduler.js';
3
+
4
+ describe('Scheduler', () => {
5
+ let scheduler: Scheduler;
6
+
7
+ beforeEach(() => {
8
+ scheduler = new Scheduler();
9
+ vi.useFakeTimers();
10
+ });
11
+
12
+ afterEach(async () => {
13
+ await scheduler.close();
14
+ vi.useRealTimers();
15
+ });
16
+
17
+ it('should schedule a task using cron pattern', async () => {
18
+ let count = 0;
19
+ // Every second
20
+ await scheduler.schedule('test', '* * * * * *', async () => {
21
+ count++;
22
+ });
23
+
24
+ const list = await scheduler.list();
25
+ expect(list).toHaveLength(1);
26
+ expect(list[0].name).toBe('test');
27
+
28
+ await vi.advanceTimersByTimeAsync(1000);
29
+ expect(count).toBe(1);
30
+
31
+ await vi.advanceTimersByTimeAsync(1000);
32
+ expect(count).toBe(2);
33
+ });
34
+
35
+ it('should schedule a task using "every" interval', async () => {
36
+ let count = 0;
37
+ await scheduler.schedule('repeat', { every: 100 }, async () => {
38
+ count++;
39
+ });
40
+
41
+ await vi.advanceTimersByTimeAsync(100);
42
+ expect(count).toBe(1);
43
+
44
+ await vi.advanceTimersByTimeAsync(100);
45
+ expect(count).toBe(2);
46
+ });
47
+
48
+ it('should respect limits', async () => {
49
+ let count = 0;
50
+ await scheduler.schedule('limited', { every: 100, limit: 2 }, async () => {
51
+ count++;
52
+ });
53
+
54
+ await vi.advanceTimersByTimeAsync(100);
55
+ await vi.advanceTimersByTimeAsync(100);
56
+ await vi.advanceTimersByTimeAsync(100);
57
+
58
+ expect(count).toBe(2);
59
+ const list = await scheduler.list();
60
+ expect(list).toHaveLength(0);
61
+ });
62
+
63
+ it('should pause and resume', async () => {
64
+ let count = 0;
65
+ await scheduler.schedule('pause-test', { every: 100 }, async () => {
66
+ count++;
67
+ });
68
+
69
+ await vi.advanceTimersByTimeAsync(100);
70
+ expect(count).toBe(1);
71
+
72
+ await scheduler.pause('pause-test');
73
+ await vi.advanceTimersByTimeAsync(100);
74
+ expect(count).toBe(1);
75
+
76
+ await scheduler.resume('pause-test');
77
+ await vi.advanceTimersByTimeAsync(100);
78
+ expect(count).toBe(2);
79
+ });
80
+ });
@@ -0,0 +1,154 @@
1
+ import parser from 'cron-parser';
2
+
3
+ export interface ScheduleOptions {
4
+ pattern?: string;
5
+ timezone?: string;
6
+ data?: any;
7
+ startDate?: Date;
8
+ endDate?: Date;
9
+ limit?: number;
10
+ every?: number; // ms
11
+ }
12
+
13
+ export class Scheduler {
14
+ private tasks: Map<string, {
15
+ pattern: string | ScheduleOptions;
16
+ handler: Function;
17
+ nextRun: number;
18
+ timer?: NodeJS.Timeout;
19
+ count: number;
20
+ paused: boolean;
21
+ }> = new Map();
22
+
23
+ async schedule(name: string, pattern: string | ScheduleOptions, handler: Function): Promise<void> {
24
+ await this.cancel(name);
25
+
26
+ const options: ScheduleOptions = typeof pattern === 'string' ? { pattern } : pattern;
27
+ const now = Date.now();
28
+ let nextRun = 0;
29
+
30
+ if (options.pattern) {
31
+ const interval = parser.parseExpression(options.pattern, {
32
+ currentDate: options.startDate || new Date(),
33
+ tz: options.timezone
34
+ });
35
+ nextRun = interval.next().getTime();
36
+ } else if (options.every) {
37
+ nextRun = (options.startDate?.getTime() || now) + options.every;
38
+ }
39
+
40
+ this.tasks.set(name, {
41
+ pattern,
42
+ handler,
43
+ nextRun,
44
+ count: 0,
45
+ paused: false
46
+ });
47
+
48
+ this.plan(name);
49
+ }
50
+
51
+ async repeat(name: string, options: any, handler: Function): Promise<void> {
52
+ return this.schedule(name, options, handler);
53
+ }
54
+
55
+ private plan(name: string) {
56
+ const task = this.tasks.get(name);
57
+ if (!task || task.paused) return;
58
+
59
+ const now = Date.now();
60
+ const delay = Math.max(0, task.nextRun - now);
61
+
62
+ if (task.timer) clearTimeout(task.timer);
63
+
64
+ task.timer = setTimeout(async () => {
65
+ if (task.paused) return;
66
+
67
+ try {
68
+ const options: ScheduleOptions = typeof task.pattern === 'string' ? { pattern: task.pattern } : task.pattern;
69
+ await task.handler(options.data);
70
+ task.count++;
71
+
72
+ // Check limits
73
+ if (options.limit && task.count >= options.limit) {
74
+ this.tasks.delete(name);
75
+ return;
76
+ }
77
+
78
+ // Plan next run
79
+ if (options.pattern) {
80
+ const interval = parser.parseExpression(options.pattern, {
81
+ currentDate: new Date(),
82
+ tz: options.timezone
83
+ });
84
+ task.nextRun = interval.next().getTime();
85
+ } else if (options.every) {
86
+ task.nextRun = Date.now() + options.every;
87
+ }
88
+
89
+ if (options.endDate && task.nextRun > options.endDate.getTime()) {
90
+ this.tasks.delete(name);
91
+ return;
92
+ }
93
+
94
+ this.plan(name);
95
+ } catch (err) {
96
+ console.error(`Scheduler task "${name}" failed:`, err);
97
+ // Still plan next run? Usually yes.
98
+ this.plan(name);
99
+ }
100
+ }, delay);
101
+ }
102
+
103
+ async list(): Promise<any[]> {
104
+ return Array.from(this.tasks.entries()).map(([name, task]) => ({
105
+ name,
106
+ nextRun: new Date(task.nextRun),
107
+ count: task.count,
108
+ paused: task.paused
109
+ }));
110
+ }
111
+
112
+ async cancel(name: string): Promise<void> {
113
+ const task = this.tasks.get(name);
114
+ if (task) {
115
+ if (task.timer) clearTimeout(task.timer);
116
+ this.tasks.delete(name);
117
+ }
118
+ }
119
+
120
+ async pause(name: string): Promise<void> {
121
+ const task = this.tasks.get(name);
122
+ if (task) {
123
+ task.paused = true;
124
+ if (task.timer) clearTimeout(task.timer);
125
+ }
126
+ }
127
+
128
+ async resume(name: string): Promise<void> {
129
+ const task = this.tasks.get(name);
130
+ if (task && task.paused) {
131
+ task.paused = false;
132
+ // Recalculate next run if it passed while paused?
133
+ // For cron, we just find the next occurrence from now.
134
+ const options: ScheduleOptions = typeof task.pattern === 'string' ? { pattern: task.pattern } : task.pattern;
135
+ if (options.pattern) {
136
+ const interval = parser.parseExpression(options.pattern, {
137
+ currentDate: new Date(),
138
+ tz: options.timezone
139
+ });
140
+ task.nextRun = interval.next().getTime();
141
+ } else if (options.every) {
142
+ task.nextRun = Date.now() + options.every;
143
+ }
144
+ this.plan(name);
145
+ }
146
+ }
147
+
148
+ async close(): Promise<void> {
149
+ for (const task of this.tasks.values()) {
150
+ if (task.timer) clearTimeout(task.timer);
151
+ }
152
+ this.tasks.clear();
153
+ }
154
+ }
package/src/index.ts ADDED
@@ -0,0 +1,57 @@
1
+ export * from "./core/flow-fn.js";
2
+ export * from "./core/scheduler.js";
3
+ export * from "./core/metrics.js";
4
+
5
+ export * from "./queue/types.js";
6
+ export * from "./queue/worker.js";
7
+ export * from "./queue/dlq.js";
8
+ export * from "./stream/types.js";
9
+ export * from "./workflow/types.js";
10
+
11
+ export * from "./adapters/base.js";
12
+ export * from "./adapters/memory.js";
13
+ export * from "./adapters/redis.js";
14
+ export * from "./adapters/postgres/index.js";
15
+
16
+ export * from "./patterns/backoff.js";
17
+ export * from "./patterns/circuit-breaker.js";
18
+ export * from "./patterns/rate-limit.js";
19
+ export {
20
+ batch,
21
+ chunk,
22
+ processBatches,
23
+ BatchWriter,
24
+ batchByKey,
25
+ BatchAccumulator,
26
+ type BatchOptions as BatchingOptions,
27
+ } from "./patterns/batching.js";
28
+ export * from "./patterns/priority.js";
29
+
30
+ export * from "./monitoring/health.js";
31
+
32
+ export * from "./utils/hashing.js";
33
+ export * from "./utils/id-generator.js";
34
+ export {
35
+ toMilliseconds,
36
+ fromMilliseconds,
37
+ formatDuration,
38
+ sleep,
39
+ sleepDuration,
40
+ timeout,
41
+ now,
42
+ delayUntil,
43
+ isPast,
44
+ isFuture,
45
+ addDuration,
46
+ parseDuration,
47
+ type Duration as TimeDuration,
48
+ } from "./utils/time.js";
49
+ export * from "./utils/serialization.js";
50
+
51
+ export * from "./storage/job-storage.js";
52
+ export {
53
+ MemoryWorkflowStorage,
54
+ WorkflowStorage,
55
+ type ListOptions as StorageListOptions,
56
+ } from "./storage/workflow-storage.js";
57
+ export * from "./storage/event-log.js";