ravenflow 0.1.0 → 0.2.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,49 +1,313 @@
1
- # Ravenflow
1
+ # ravenflow
2
2
 
3
- Type-safe job queue management for TypeScript.
3
+ Type-safe job queue framework for TypeScript, built on [BullMQ](https://docs.bullmq.io/) and [Redis](https://redis.io/).
4
4
 
5
- ## Installation
5
+ Define jobs as plain functions, ravenflow handles scheduling, retries, rate limiting, distributed processing, and crash recovery.
6
+
7
+ ## Install
6
8
 
7
9
  ```bash
8
- npm install ravenflow ioredis
10
+ npm install ravenflow
11
+ # or
12
+ pnpm add ravenflow
9
13
  ```
10
14
 
15
+ **Requirements:** Redis 6+, Node.js 18+. MongoDB required for stateful-etl and token-bucket jobs.
16
+
11
17
  ## Quick Start
12
18
 
13
- ```typescript
14
- import { createRavenflowApp } from "ravenflow";
15
-
16
- const app = createRavenflowApp(
17
- {
18
- defaultQueue: "default",
19
- queues: ["default"],
20
- jobs: {
21
- sendEmail: {
22
- type: "job",
23
- queueName: "default",
24
- handler: async (job, params: { to: string; subject: string }) => {
25
- console.log(`Sending email to ${params.to}`);
26
- return { sent: true };
27
- },
19
+ ```ts
20
+ import {
21
+ createRavenflowApp,
22
+ defineConfig,
23
+ defineSimpleJob,
24
+ } from "ravenflow";
25
+
26
+ // 1. Define your jobs
27
+ const config = defineConfig({
28
+ defaultQueue: "default",
29
+ jobs: {
30
+ sendEmail: defineSimpleJob({
31
+ handler: async (job, params: { to: string; subject: string }) => {
32
+ await sendEmail(params.to, params.subject);
33
+ return { sent: true };
28
34
  },
35
+ }),
36
+ },
37
+ });
38
+
39
+ // 2. Create the app
40
+ const app = createRavenflowApp(config, {
41
+ redis: { host: "localhost", port: 6379 },
42
+ });
43
+
44
+ // 3. Start the worker
45
+ await app.startWorker("default", { concurrency: 10 });
46
+
47
+ // 4. Send jobs
48
+ await app.jobs.sendEmail.send({ to: "user@example.com", subject: "Hello" });
49
+
50
+ // Or send and wait for the result
51
+ const result = await app.jobs.sendEmail.sendAndWait({ to: "user@example.com", subject: "Hello" });
52
+ console.log(result); // { sent: true }
53
+ ```
54
+
55
+ ## Job Types
56
+
57
+ ### Simple Job
58
+
59
+ One-shot function execution. Ideal for notifications, cleanup tasks, webhooks.
60
+
61
+ ```ts
62
+ import { defineSimpleJob } from "ravenflow";
63
+
64
+ const sendEmail = defineSimpleJob({
65
+ cron: "*/5 * * * *", // optional: run every 5 minutes
66
+ handler: async (job, params: { to: string; text: string }) => {
67
+ const result = await emailService.send(params.to, params.text);
68
+ return { messageId: result.id };
69
+ },
70
+ });
71
+ ```
72
+
73
+ ### Memory ETL
74
+
75
+ Extract-Transform-Load pipeline that runs entirely in-memory. Cursor-based pagination, parallel transforms with rate limiting.
76
+
77
+ ```ts
78
+ import { defineMemoryEtlJob } from "ravenflow";
79
+
80
+ const syncUsers = defineMemoryEtlJob({
81
+ cron: "0 2 * * *", // daily at 2 AM
82
+
83
+ extract: async (cursor: number | null) => {
84
+ const offset = cursor ?? 0;
85
+ const users = await api.getUsers({ offset, limit: 100 });
86
+ return {
87
+ data: users,
88
+ cursor: offset + 100,
89
+ hasMore: users.length === 100,
90
+ };
91
+ },
92
+
93
+ transform: async (user) => ({
94
+ id: user.id,
95
+ email: user.email,
96
+ normalizedName: user.name.trim().toLowerCase(),
97
+ }),
98
+
99
+ load: async (users) => {
100
+ await db.users.insertMany(users);
101
+ },
102
+
103
+ steps: {
104
+ transform: {
105
+ rateLimit: { perSecond: 50 }, // even spacing: 1 every 20ms
29
106
  },
30
107
  },
31
- {
32
- host: "localhost",
33
- port: 6379,
34
- }
35
- );
108
+ });
109
+ ```
110
+
111
+ ### Distributed ETL
36
112
 
37
- // Enqueue job
38
- await app.jobs.sendEmail.send({
39
- to: "user@example.com",
40
- subject: "Hello",
113
+ Same extract/transform/load pattern, but transforms are dispatched as independent BullMQ sub-jobs for parallel processing across workers.
114
+
115
+ ```ts
116
+ import { defineDistributedEtlJob } from "ravenflow";
117
+
118
+ const processOrders = defineDistributedEtlJob({
119
+ cron: "*/10 * * * *",
120
+
121
+ extract: async (cursor: number | null) => {
122
+ const orders = await db.orders
123
+ .find({ status: "pending" })
124
+ .skip(cursor ?? 0)
125
+ .limit(50)
126
+ .toArray();
127
+ return {
128
+ data: orders,
129
+ cursor: (cursor ?? 0) + 50,
130
+ hasMore: orders.length === 50,
131
+ };
132
+ },
133
+
134
+ transform: async (order) => {
135
+ const enriched = await paymentGateway.verify(order.paymentId);
136
+ return { ...order, verified: enriched.status === "ok" };
137
+ },
138
+
139
+ load: async (orders) => {
140
+ await db.processedOrders.insertMany(orders);
141
+ },
142
+
143
+ steps: {
144
+ transform: {
145
+ rateLimit: { perSecond: 10 },
146
+ attempts: 3,
147
+ backoff: { type: "exponential", delay: 1000 },
148
+ },
149
+ },
41
150
  });
151
+ ```
152
+
153
+ ### Stateful ETL
154
+
155
+ Distributed ETL with MongoDB-backed checkpoints and automatic crash recovery. If a worker dies mid-processing, the Stall Watcher detects it and resumes from the last checkpoint.
156
+
157
+ ```ts
158
+ import { defineStatefulEtlJob } from "ravenflow";
159
+
160
+ const migrateLegacyData = defineStatefulEtlJob({
161
+ cron: "0 3 * * *",
162
+ singletonRun: true, // only one run at a time
163
+ stallTimeoutMs: 60_000, // detect stall after 60s without heartbeat
164
+ maxStallRetries: 3,
165
+
166
+ extract: async (cursor: string | null) => {
167
+ const page = await legacyApi.fetch({ after: cursor, limit: 100 });
168
+ return {
169
+ data: page.items,
170
+ cursor: page.nextCursor,
171
+ hasMore: page.hasMore,
172
+ };
173
+ },
42
174
 
43
- // Start worker
44
- app.startWorker("default");
175
+ transform: async (item) => {
176
+ return mapToNewSchema(item);
177
+ },
178
+
179
+ load: async (items) => {
180
+ await newDb.collection("records").insertMany(items);
181
+ },
182
+
183
+ steps: {
184
+ transform: {
185
+ rateLimit: { perSecond: 5 },
186
+ attempts: 3,
187
+ backoff: { type: "exponential", delay: 2000 },
188
+ },
189
+ },
190
+ });
45
191
  ```
46
192
 
193
+ **How crash recovery works:**
194
+ 1. During processing, the worker sends heartbeats to MongoDB
195
+ 2. A `__stall_check` cron job monitors all active runs
196
+ 3. If a heartbeat goes stale (exceeds `stallTimeoutMs`), the run is claimed as "stalled"
197
+ 4. A `__stall_resume` job re-enqueues the original job with the last checkpoint
198
+ 5. Processing resumes from where it left off (no duplicate work)
199
+
200
+ ## Configuration
201
+
202
+ ### App Options
203
+
204
+ ```ts
205
+ const app = createRavenflowApp(config, {
206
+ // Required: Redis connection
207
+ redis: { host: "localhost", port: 6379, password: "secret", db: 0 },
208
+
209
+ // Optional: MongoDB (required for stateful-etl and token-bucket)
210
+ // String form:
211
+ mongo: "mongodb://localhost:27017/myapp",
212
+ // Or object form:
213
+ mongo: { host: "localhost", port: 27017, username: "admin", password: "secret", database: "myapp" },
214
+
215
+ // Optional: job retention policy
216
+ retention: {
217
+ completed: 1000, // keep last 1000 completed jobs per queue
218
+ failed: 5000, // keep last 5000 failed jobs per queue
219
+ },
220
+
221
+ // Optional: logger config (pino)
222
+ logger: { level: "info", pretty: true },
223
+ });
224
+ ```
225
+
226
+ ### Rate Limiting
227
+
228
+ Even-spacing rate limiter. `perSecond: 10` means 1 request every 100ms (not a burst of 10 then wait).
229
+
230
+ ```ts
231
+ steps: {
232
+ extract: { rateLimit: { perMinute: 60 } }, // 1 per second
233
+ transform: { rateLimit: { perSecond: 20 } }, // 1 every 50ms
234
+ load: { rateLimit: { perSecond: 5 } }, // 1 every 200ms
235
+ }
236
+ ```
237
+
238
+ ### Step Config (Distributed & Stateful ETL)
239
+
240
+ Each step can have its own retry strategy, rate limit, and target queue:
241
+
242
+ ```ts
243
+ steps: {
244
+ transform: {
245
+ rateLimit: { perSecond: 10 },
246
+ attempts: 3,
247
+ backoff: { type: "exponential", delay: 1000 },
248
+ queue: "heavy-transforms", // route to a different queue
249
+ },
250
+ }
251
+ ```
252
+
253
+ ### Cron Scheduling
254
+
255
+ Jobs with a `cron` field are automatically scheduled when the worker starts:
256
+
257
+ ```ts
258
+ defineSimpleJob({
259
+ cron: "*/5 * * * *", // every 5 minutes
260
+ handler: async () => { /* ... */ },
261
+ });
262
+ ```
263
+
264
+ ## Sending Jobs
265
+
266
+ ```ts
267
+ // Fire and forget — returns BullMQ Job object
268
+ const job = await app.jobs.sendEmail.send({ to: "user@example.com", subject: "Hi" });
269
+
270
+ // Wait for the result
271
+ const result = await app.jobs.sendEmail.sendAndWait({ to: "user@example.com", subject: "Hi" });
272
+
273
+ // ETL jobs don't take parameters
274
+ await app.jobs.syncUsers.send();
275
+
276
+ // ETL sendAndWait returns counts
277
+ const counts = await app.jobs.syncUsers.sendAndWait();
278
+ // { extractedCount: 500, transformedCount: 500, loadedCount: 500 }
279
+ ```
280
+
281
+ ## Worker
282
+
283
+ ```ts
284
+ // Start processing jobs from a queue
285
+ await app.startWorker("default", { concurrency: 20 });
286
+
287
+ // Graceful shutdown
288
+ await app.close();
289
+ ```
290
+
291
+ When `startWorker` is called:
292
+ - Cron schedules are synced to Redis
293
+ - The Stall Watcher starts automatically if stateful-etl jobs are registered
294
+ - The worker begins processing jobs
295
+
296
+ ## Config Validation
297
+
298
+ Ravenflow validates your config at startup and throws clear errors:
299
+
300
+ ```
301
+ [ravenflow] Config validation failed:
302
+ - "migrateLegacy" (stateful-etl) requires a mongo connection.
303
+ - "rateLimiter" (token-bucket) requires a cron schedule.
304
+ ```
305
+
306
+ ## Companion Packages
307
+
308
+ - [`@ravenflow/mongo`](https://www.npmjs.com/package/@ravenflow/mongo) — Typed MongoDB collections, base repository, Zod schema integration
309
+ - [`@ravenflow/http`](https://www.npmjs.com/package/@ravenflow/http) — HTTP client utilities built on undici
310
+
47
311
  ## License
48
312
 
49
313
  MIT
package/dist/index.d.mts CHANGED
@@ -1,15 +1,9 @@
1
- import { Job, ConnectionOptions, Queue, QueueEvents } from 'bullmq';
2
- import * as pino from 'pino';
1
+ import { z } from 'zod';
2
+ import { Job, Queue, QueueEvents, ConnectionOptions, Worker } from 'bullmq';
3
+ import pino from 'pino';
3
4
 
4
- interface RavenflowConfig<TQueues extends string = string, TJobs extends string = string> {
5
- defaultQueue: TQueues;
6
- queues: TQueues[];
7
- jobs: Record<TJobs, JobConfig>;
8
- }
9
- type JobNames<T extends RavenflowConfig> = T extends RavenflowConfig<infer _, infer TJobs> ? TJobs : never;
10
- type QueueNames<T extends RavenflowConfig> = T extends RavenflowConfig<infer TQueues, infer _> ? TQueues : never;
11
5
  interface BaseJobConfig {
12
- queueName: string;
6
+ queue?: string;
13
7
  cron?: string;
14
8
  attempts?: number;
15
9
  backoff?: {
@@ -17,67 +11,207 @@ interface BaseJobConfig {
17
11
  delay: number;
18
12
  };
19
13
  }
14
+ declare const zRateLimitConfig: z.ZodUnion<readonly [z.ZodObject<{
15
+ perSecond: z.ZodNumber;
16
+ }, z.core.$strip>, z.ZodObject<{
17
+ perMinute: z.ZodNumber;
18
+ }, z.core.$strip>]>;
19
+ type RateLimitConfig = z.infer<typeof zRateLimitConfig>;
20
+ interface MemoryEtlStepConfig {
21
+ rateLimit?: RateLimitConfig;
22
+ }
23
+ interface StepConfig extends MemoryEtlStepConfig {
24
+ attempts?: number;
25
+ backoff?: {
26
+ type: "fixed" | "exponential";
27
+ delay: number;
28
+ };
29
+ queue?: string;
30
+ }
31
+ interface EtlExtractResult<TRaw, TCursor> {
32
+ data: TRaw[];
33
+ cursor: TCursor | null;
34
+ hasMore: boolean;
35
+ }
36
+ declare const zJobStatus: z.ZodEnum<{
37
+ pending: "pending";
38
+ in_progress: "in_progress";
39
+ completed: "completed";
40
+ failed: "failed";
41
+ stalled: "stalled";
42
+ }>;
43
+ type JobStatus = z.infer<typeof zJobStatus>;
44
+
20
45
  interface SimpleJobConfig<TData, TReturn> extends BaseJobConfig {
21
- type: "job";
46
+ type: "simple";
22
47
  handler: (job: Job, params: TData) => Promise<TReturn>;
23
48
  }
24
- interface EtlJobConfig<TRaw, TProcessed> extends BaseJobConfig {
25
- type: "etl";
26
- extract: (page: number) => Promise<{
27
- data: TRaw[];
28
- hasNextPage?: boolean;
29
- }>;
49
+ declare function defineSimpleJob<TData, TReturn>(config: Omit<SimpleJobConfig<TData, TReturn>, "type">): SimpleJobConfig<TData, TReturn>;
50
+
51
+ interface MemoryEtlJobConfig<TRaw, TProcessed, TCursor = number> extends BaseJobConfig {
52
+ type: "memory-etl";
53
+ extract: (cursor: TCursor | null) => Promise<EtlExtractResult<TRaw, TCursor>>;
54
+ transform: (data: TRaw) => Promise<TProcessed>;
55
+ load: (data: TProcessed[]) => Promise<void>;
56
+ steps?: {
57
+ extract?: MemoryEtlStepConfig;
58
+ transform?: MemoryEtlStepConfig;
59
+ load?: MemoryEtlStepConfig;
60
+ };
61
+ }
62
+ declare function defineMemoryEtlJob<TRaw, TProcessed, TCursor = number>(config: Omit<MemoryEtlJobConfig<TRaw, TProcessed, TCursor>, "type">): MemoryEtlJobConfig<TRaw, TProcessed, TCursor>;
63
+
64
+ interface DistributedEtlJobConfig<TRaw, TProcessed, TCursor = number> extends BaseJobConfig {
65
+ type: "distributed-etl";
66
+ extract: (cursor: TCursor | null) => Promise<EtlExtractResult<TRaw, TCursor>>;
67
+ transform: (data: TRaw) => Promise<TProcessed>;
68
+ load: (data: TProcessed[]) => Promise<void>;
69
+ steps?: {
70
+ extract?: StepConfig;
71
+ transform?: StepConfig;
72
+ load?: StepConfig;
73
+ };
74
+ }
75
+ declare function defineDistributedEtlJob<TRaw, TProcessed, TCursor = number>(config: Omit<DistributedEtlJobConfig<TRaw, TProcessed, TCursor>, "type">): DistributedEtlJobConfig<TRaw, TProcessed, TCursor>;
76
+
77
+ interface StatefulEtlJobConfig<TRaw, TProcessed, TCursor = string> extends BaseJobConfig {
78
+ type: "stateful-etl";
79
+ extract: (cursor: TCursor | null) => Promise<EtlExtractResult<TRaw, TCursor>>;
30
80
  transform: (data: TRaw) => Promise<TProcessed>;
31
81
  load: (data: TProcessed[]) => Promise<void>;
32
- transformQueue?: string;
82
+ /**
83
+ * When true, only one run of this job can be active at a time.
84
+ * If a run is already pending or in_progress (with a live heartbeat), the new trigger is skipped.
85
+ * Defaults to false — each trigger creates an independent run.
86
+ */
87
+ singletonRun?: boolean;
88
+ stallTimeoutMs?: number;
89
+ maxStallRetries?: number;
90
+ steps?: {
91
+ extract?: StepConfig;
92
+ transform?: StepConfig;
93
+ load?: StepConfig;
94
+ };
95
+ }
96
+ declare function defineStatefulEtlJob<TRaw, TProcessed, TCursor = string>(config: Omit<StatefulEtlJobConfig<TRaw, TProcessed, TCursor>, "type">): StatefulEtlJobConfig<TRaw, TProcessed, TCursor>;
97
+
98
+ interface TokenBucketJobConfig<TItem, TResult> extends BaseJobConfig {
99
+ type: "token-bucket";
100
+ cron: string;
101
+ rate: {
102
+ amount: number;
103
+ interval: "minute" | "hour" | "day";
104
+ };
105
+ activeHours?: {
106
+ start: number;
107
+ end: number;
108
+ timezone?: string;
109
+ };
110
+ maxPerBeat?: number;
111
+ getItems: (budget: number) => Promise<TItem[]>;
112
+ process: (item: TItem) => Promise<TResult>;
113
+ steps?: {
114
+ process?: StepConfig;
115
+ };
116
+ }
117
+ declare function defineTokenBucketJob<TItem, TResult>(config: Omit<TokenBucketJobConfig<TItem, TResult>, "type">): TokenBucketJobConfig<TItem, TResult>;
118
+
119
+ type JobConfig = SimpleJobConfig<any, any> | MemoryEtlJobConfig<any, any, any> | DistributedEtlJobConfig<any, any, any> | StatefulEtlJobConfig<any, any, any> | TokenBucketJobConfig<any, any>;
120
+
121
+ interface RetentionConfig {
122
+ /** Number of completed jobs to keep per queue. Set to 0 to remove immediately. */
123
+ completed?: number;
124
+ /** Number of failed jobs to keep per queue. Set to 0 to remove immediately. */
125
+ failed?: number;
126
+ }
127
+ interface RavenflowConfig<TJobs extends string = string> {
128
+ defaultQueue: string;
129
+ jobs: Record<TJobs, JobConfig>;
130
+ }
131
+ type JobNames<T extends RavenflowConfig> = T extends RavenflowConfig<infer TJobs> ? TJobs : never;
132
+ declare function defineConfig<const T extends RavenflowConfig>(config: T): T;
133
+
134
+ interface EtlResult {
135
+ extractedCount: number;
136
+ transformedCount: number;
137
+ loadedCount: number;
33
138
  }
34
- type JobConfig = SimpleJobConfig<any, any> | EtlJobConfig<any, any>;
35
139
  type JobProxy<T extends JobConfig> = T extends SimpleJobConfig<infer TData, infer TReturn> ? {
36
140
  send: (params: TData) => Promise<Job<TData, TReturn>>;
37
- } : T extends EtlJobConfig<any, any> ? {
38
- send: (params?: {
39
- startPage?: number;
40
- batchSize?: number;
41
- }) => Promise<void>;
141
+ sendAndWait: (params: TData) => Promise<TReturn>;
142
+ } : T extends MemoryEtlJobConfig<any, any, any> ? {
143
+ send: () => Promise<void>;
144
+ sendAndWait: () => Promise<EtlResult>;
145
+ } : T extends DistributedEtlJobConfig<any, any> ? {
146
+ send: () => Promise<void>;
147
+ sendAndWait: () => Promise<EtlResult>;
148
+ } : T extends StatefulEtlJobConfig<any, any, any> ? {
149
+ send: () => Promise<void>;
150
+ sendAndWait: () => Promise<void>;
151
+ } : T extends TokenBucketJobConfig<any, any> ? {
152
+ send: () => Promise<void>;
153
+ sendAndWait: () => Promise<void>;
42
154
  } : never;
43
155
  type JobsProxy<TJobs extends Record<string, JobConfig>> = {
44
156
  [K in keyof TJobs]: JobProxy<TJobs[K]>;
45
157
  };
46
158
 
159
+ type Logger = pino.Logger;
47
160
  interface LoggerOptions {
48
161
  level?: "trace" | "debug" | "info" | "warn" | "error" | "fatal";
49
162
  pretty?: boolean;
50
163
  }
51
- declare function createLogger(options?: LoggerOptions): pino.Logger<never>;
52
- type Logger = ReturnType<typeof createLogger>;
164
+ declare function initLogger(options?: LoggerOptions): Logger;
165
+ declare function getLogger(): Logger;
166
+
167
+ type QueueStore = Record<string, {
168
+ queue: Queue;
169
+ events: QueueEvents;
170
+ }>;
171
+
172
+ declare class StallWatcher {
173
+ private readonly workerQueue;
174
+ private readonly statefulEtlConfigs;
175
+ private readonly userQueues;
176
+ constructor(jobConfigs: Record<string, JobConfig>, userQueues: Record<string, {
177
+ queue: Queue;
178
+ }>, defaultQueue: string, workerQueue: Queue);
179
+ canProcess(jobName: string): boolean;
180
+ start(): Promise<void>;
181
+ stop(): Promise<void>;
182
+ process(job: Job): Promise<void>;
183
+ }
53
184
 
185
+ interface MongoConnectionObject {
186
+ host: string;
187
+ port?: number;
188
+ username?: string;
189
+ password?: string;
190
+ database?: string;
191
+ }
192
+ interface RavenflowAppOptions {
193
+ redis: ConnectionOptions;
194
+ mongo?: string | MongoConnectionObject;
195
+ retention?: RetentionConfig;
196
+ logger?: LoggerOptions;
197
+ }
54
198
  declare class RavenflowApp<TConfig extends RavenflowConfig = RavenflowConfig> {
55
- protected connection: ConnectionOptions;
199
+ protected redisConnection: ConnectionOptions;
56
200
  protected defaultConcurrency: number;
57
- protected logger: Logger;
58
201
  protected config: TConfig;
202
+ protected mongoUri: string | null;
59
203
  jobs: JobsProxy<TConfig["jobs"]>;
60
204
  protected jobConfigs: Record<JobNames<TConfig>, JobConfig>;
61
- protected queues: Record<QueueNames<TConfig>, {
62
- queue: Queue;
63
- events: QueueEvents;
64
- }>;
65
- constructor(connection: ConnectionOptions, config: TConfig, loggerOptions?: LoggerOptions);
66
- private initQueues;
67
- private initJobs;
68
- private sendJob;
69
- startWorker(queueName: QueueNames<TConfig>, options?: {
205
+ protected queues: QueueStore;
206
+ protected workers: Worker[];
207
+ protected stallWatcher: StallWatcher | null;
208
+ constructor(config: TConfig, options: RavenflowAppOptions);
209
+ startWorker(queueName: string, options?: {
70
210
  concurrency?: number;
71
- }): void;
72
- private printWorkerBanner;
73
- private setupWorkerEvents;
74
- private syncJobSchedulers;
75
- processJob(job: Job): Promise<any>;
211
+ }): Promise<void>;
212
+ close(): Promise<void>;
76
213
  }
77
214
 
78
- declare function createRavenflowApp<TConfig extends RavenflowConfig>(config: TConfig, connection: ConnectionOptions, loggerOptions?: {
79
- level?: "trace" | "debug" | "info" | "warn" | "error" | "fatal";
80
- pretty?: boolean;
81
- }): RavenflowApp<TConfig>;
215
+ declare function createRavenflowApp<TConfig extends RavenflowConfig>(config: TConfig, options: RavenflowAppOptions): RavenflowApp<TConfig>;
82
216
 
83
- export { type EtlJobConfig, type JobConfig, type Logger, type LoggerOptions, RavenflowApp, type RavenflowConfig, type SimpleJobConfig, createLogger, createRavenflowApp };
217
+ export { type DistributedEtlJobConfig, type EtlExtractResult, type EtlResult, type JobConfig, type JobStatus, type Logger, type LoggerOptions, type MemoryEtlJobConfig, type MemoryEtlStepConfig, type MongoConnectionObject, type RateLimitConfig, RavenflowApp, type RavenflowAppOptions, type RavenflowConfig, type RetentionConfig, type SimpleJobConfig, type StatefulEtlJobConfig, type StepConfig, type TokenBucketJobConfig, createRavenflowApp, defineConfig, defineDistributedEtlJob, defineMemoryEtlJob, defineSimpleJob, defineStatefulEtlJob, defineTokenBucketJob, getLogger, initLogger };