flowli 0.2.1 → 0.3.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 +134 -168
- package/dist/core/types.d.ts +14 -1
- package/dist/index.d.ts +1 -1
- package/dist/runner/types.d.ts +1 -0
- package/jsr.json +11 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
**Typed jobs for modern TypeScript backends.**
|
|
4
4
|
|
|
5
|
+
[npm](https://www.npmjs.com/package/flowli) · [JSR](https://jsr.io/@alialnaghmoush/flowli) · [GitHub](https://github.com/alialnaghmoush/flowli)
|
|
6
|
+
|
|
5
7
|
Flowli is a jobs runtime with a code-first API, first-class execution strategies, runtime-scoped context injection, and pluggable Redis drivers.
|
|
6
8
|
|
|
7
9
|
Define jobs once. Run them anywhere.
|
|
@@ -9,6 +11,7 @@ Define jobs once. Run them anywhere.
|
|
|
9
11
|
## Navigate
|
|
10
12
|
|
|
11
13
|
- [Why Flowli](#why-flowli)
|
|
14
|
+
- [Compare](#compare)
|
|
12
15
|
- [What It Feels Like](#what-it-feels-like)
|
|
13
16
|
- [The Core Idea](#the-core-idea)
|
|
14
17
|
- [Primary Authoring Path](#primary-authoring-path)
|
|
@@ -46,6 +49,30 @@ Flowli is built around a different model:
|
|
|
46
49
|
|
|
47
50
|
It is a typed runtime for background and deferred execution with a code-first, framework-agnostic design.
|
|
48
51
|
|
|
52
|
+
## Compare
|
|
53
|
+
|
|
54
|
+
Choose Flowli when you want:
|
|
55
|
+
|
|
56
|
+
- application-first jobs authored in code, not in a dashboard
|
|
57
|
+
- a single runtime that supports both direct execution and persisted async work
|
|
58
|
+
- typed `context` injection without framework lock-in
|
|
59
|
+
- pluggable Redis clients behind a small API surface
|
|
60
|
+
|
|
61
|
+
Flowli vs BullMQ:
|
|
62
|
+
|
|
63
|
+
- Flowli centers the typed job-definition experience; BullMQ centers queue primitives and worker infrastructure
|
|
64
|
+
- Flowli makes `run()` a first-class in-process path; BullMQ is primarily queue-first
|
|
65
|
+
|
|
66
|
+
Flowli vs Trigger.dev:
|
|
67
|
+
|
|
68
|
+
- Flowli stays library-first and infrastructure-light
|
|
69
|
+
- Trigger.dev is stronger when you want a hosted platform, dashboard, and workflow operations out of the box
|
|
70
|
+
|
|
71
|
+
Flowli vs Inngest:
|
|
72
|
+
|
|
73
|
+
- Flowli is better suited when you want application-local jobs and direct runtime wiring
|
|
74
|
+
- Inngest is stronger when you want event-first workflows across services
|
|
75
|
+
|
|
49
76
|
```mermaid
|
|
50
77
|
flowchart LR
|
|
51
78
|
App["App Code<br/>Routes, Services, Scripts, Tests"] --> Runtime["defineJobs()<br/>Flowli Runtime"]
|
|
@@ -66,93 +93,9 @@ flowchart LR
|
|
|
66
93
|
## What It Feels Like
|
|
67
94
|
|
|
68
95
|
```ts
|
|
69
|
-
// src/flowli/jobs/create-audit-log.ts
|
|
70
|
-
import * as v from "valibot";
|
|
71
|
-
import { job } from "flowli";
|
|
72
|
-
import type { AppContext } from "..";
|
|
73
|
-
|
|
74
|
-
export const auditLogSchema = v.object({
|
|
75
|
-
entityType: v.string(),
|
|
76
|
-
entityId: v.string(),
|
|
77
|
-
action: v.string(),
|
|
78
|
-
message: v.string(),
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
export const auditLogMeta = v.object({
|
|
82
|
-
requestId: v.string(),
|
|
83
|
-
actorId: v.optional(v.string()),
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
export const createAuditLog = job.withContext<AppContext>()(
|
|
87
|
-
"create_audit_log",
|
|
88
|
-
{
|
|
89
|
-
input: auditLogSchema,
|
|
90
|
-
meta: auditLogMeta,
|
|
91
|
-
handler: async ({ input, ctx, meta }) => {
|
|
92
|
-
await ctx.db.insert(ctx.schema.auditLogs).values({
|
|
93
|
-
entityType: input.entityType,
|
|
94
|
-
entityId: input.entityId,
|
|
95
|
-
action: input.action,
|
|
96
|
-
message: input.message,
|
|
97
|
-
requestId: meta?.requestId,
|
|
98
|
-
actorId: meta?.actorId ?? null,
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
ctx.logger.info({
|
|
102
|
-
job: "create_audit_log",
|
|
103
|
-
requestId: meta?.requestId,
|
|
104
|
-
entityId: input.entityId,
|
|
105
|
-
});
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
);
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
```ts
|
|
112
|
-
// src/flowli/jobs/send-notification-email.ts
|
|
113
96
|
import * as v from "valibot";
|
|
114
|
-
import { job } from "flowli";
|
|
115
|
-
import type { AppContext } from "..";
|
|
116
|
-
|
|
117
|
-
export const notificationEmailSchema = v.object({
|
|
118
|
-
email: v.string(),
|
|
119
|
-
subject: v.string(),
|
|
120
|
-
message: v.string(),
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
export const sendNotificationEmail = job.withContext<AppContext>()(
|
|
124
|
-
"send_notification_email",
|
|
125
|
-
{
|
|
126
|
-
input: notificationEmailSchema,
|
|
127
|
-
handler: async ({ input, ctx }) => {
|
|
128
|
-
await ctx.mailer.send({
|
|
129
|
-
to: input.email,
|
|
130
|
-
subject: input.subject,
|
|
131
|
-
text: input.message,
|
|
132
|
-
});
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
);
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
```ts
|
|
139
|
-
// src/flowli/jobs/index.ts
|
|
140
|
-
export * from "./create-audit-log";
|
|
141
|
-
export * from "./send-notification-email";
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
```ts
|
|
145
|
-
// src/flowli/index.ts
|
|
146
97
|
import { defineJobs } from "flowli";
|
|
147
98
|
import { ioredisDriver } from "flowli/ioredis";
|
|
148
|
-
import * as jobs from "./jobs";
|
|
149
|
-
|
|
150
|
-
export type AppContext = {
|
|
151
|
-
db: typeof db;
|
|
152
|
-
schema: typeof schema;
|
|
153
|
-
logger: typeof logger;
|
|
154
|
-
mailer: typeof mailer;
|
|
155
|
-
};
|
|
156
99
|
|
|
157
100
|
export const flowli = defineJobs({
|
|
158
101
|
driver: ioredisDriver({
|
|
@@ -165,7 +108,59 @@ export const flowli = defineJobs({
|
|
|
165
108
|
logger,
|
|
166
109
|
mailer,
|
|
167
110
|
}),
|
|
168
|
-
jobs
|
|
111
|
+
jobs: ({ job }) => {
|
|
112
|
+
const auditLogSchema = v.object({
|
|
113
|
+
entityType: v.string(),
|
|
114
|
+
entityId: v.string(),
|
|
115
|
+
action: v.string(),
|
|
116
|
+
message: v.string(),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const auditLogMetaSchema = v.object({
|
|
120
|
+
requestId: v.string(),
|
|
121
|
+
actorId: v.optional(v.string()),
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const notificationEmailSchema = v.object({
|
|
125
|
+
email: v.string(),
|
|
126
|
+
subject: v.string(),
|
|
127
|
+
message: v.string(),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
createAuditLog: job("create_audit_log", {
|
|
132
|
+
input: auditLogSchema,
|
|
133
|
+
meta: auditLogMetaSchema,
|
|
134
|
+
handler: async ({ input, ctx, meta }) => {
|
|
135
|
+
await ctx.db.insert(ctx.schema.auditLogs).values({
|
|
136
|
+
entityType: input.entityType,
|
|
137
|
+
entityId: input.entityId,
|
|
138
|
+
action: input.action,
|
|
139
|
+
message: input.message,
|
|
140
|
+
requestId: meta?.requestId,
|
|
141
|
+
actorId: meta?.actorId ?? null,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
ctx.logger.info({
|
|
145
|
+
job: "create_audit_log",
|
|
146
|
+
requestId: meta?.requestId,
|
|
147
|
+
entityId: input.entityId,
|
|
148
|
+
});
|
|
149
|
+
},
|
|
150
|
+
}),
|
|
151
|
+
|
|
152
|
+
sendNotificationEmail: job("send_notification_email", {
|
|
153
|
+
input: notificationEmailSchema,
|
|
154
|
+
handler: async ({ input, ctx }) => {
|
|
155
|
+
await ctx.mailer.send({
|
|
156
|
+
to: input.email,
|
|
157
|
+
subject: input.subject,
|
|
158
|
+
text: input.message,
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
}),
|
|
162
|
+
};
|
|
163
|
+
},
|
|
169
164
|
});
|
|
170
165
|
```
|
|
171
166
|
|
|
@@ -236,63 +231,45 @@ flowchart TD
|
|
|
236
231
|
|
|
237
232
|
The canonical Flowli path is runtime-first:
|
|
238
233
|
|
|
239
|
-
```ts
|
|
240
|
-
// src/flowli/jobs/create-audit-log.ts
|
|
241
|
-
import * as v from "valibot";
|
|
242
|
-
import { job } from "flowli";
|
|
243
|
-
import type { AppContext } from "..";
|
|
244
|
-
|
|
245
|
-
export const auditLogSchema = v.object({
|
|
246
|
-
entityId: v.string(),
|
|
247
|
-
action: v.string(),
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
export const auditLogMeta = v.object({
|
|
251
|
-
requestId: v.string(),
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
export const createAuditLog = job.withContext<AppContext>()(
|
|
255
|
-
"create_audit_log",
|
|
256
|
-
{
|
|
257
|
-
input: auditLogSchema,
|
|
258
|
-
meta: auditLogMeta,
|
|
259
|
-
handler: async ({ input, ctx, meta }) => {
|
|
260
|
-
await ctx.db.insert("audit_logs").values({
|
|
261
|
-
entityId: input.entityId,
|
|
262
|
-
action: input.action,
|
|
263
|
-
requestId: meta?.requestId,
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
ctx.logger.info({
|
|
267
|
-
entityId: input.entityId,
|
|
268
|
-
action: input.action,
|
|
269
|
-
});
|
|
270
|
-
},
|
|
271
|
-
},
|
|
272
|
-
);
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
```ts
|
|
276
|
-
// src/flowli/jobs/index.ts
|
|
277
|
-
export * from "./create-audit-log";
|
|
278
|
-
```
|
|
279
|
-
|
|
280
234
|
```ts
|
|
281
235
|
// src/flowli/index.ts
|
|
236
|
+
import * as v from "valibot";
|
|
282
237
|
import { defineJobs } from "flowli";
|
|
283
|
-
import * as jobs from "./jobs";
|
|
284
|
-
|
|
285
|
-
export type AppContext = {
|
|
286
|
-
logger: typeof logger;
|
|
287
|
-
db: typeof db;
|
|
288
|
-
};
|
|
289
238
|
|
|
290
239
|
export const flowli = defineJobs({
|
|
291
240
|
context: {
|
|
292
241
|
logger,
|
|
293
242
|
db,
|
|
294
243
|
},
|
|
295
|
-
jobs
|
|
244
|
+
jobs: ({ job }) => {
|
|
245
|
+
const auditLogSchema = v.object({
|
|
246
|
+
entityId: v.string(),
|
|
247
|
+
action: v.string(),
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const auditLogMetaSchema = v.object({
|
|
251
|
+
requestId: v.string(),
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
createAuditLog: job("create_audit_log", {
|
|
256
|
+
input: auditLogSchema,
|
|
257
|
+
meta: auditLogMetaSchema,
|
|
258
|
+
handler: async ({ input, ctx, meta }) => {
|
|
259
|
+
await ctx.db.insert("audit_logs").values({
|
|
260
|
+
entityId: input.entityId,
|
|
261
|
+
action: input.action,
|
|
262
|
+
requestId: meta?.requestId,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
ctx.logger.info({
|
|
266
|
+
entityId: input.entityId,
|
|
267
|
+
action: input.action,
|
|
268
|
+
});
|
|
269
|
+
},
|
|
270
|
+
}),
|
|
271
|
+
};
|
|
272
|
+
},
|
|
296
273
|
});
|
|
297
274
|
```
|
|
298
275
|
|
|
@@ -407,43 +384,9 @@ In short:
|
|
|
407
384
|
To persist jobs, add a driver:
|
|
408
385
|
|
|
409
386
|
```ts
|
|
410
|
-
// src/flowli/jobs/send-email.ts
|
|
411
387
|
import * as v from "valibot";
|
|
412
|
-
import { job } from "flowli";
|
|
413
|
-
import type { AppContext } from "..";
|
|
414
|
-
|
|
415
|
-
export const emailInputSchema = v.object({
|
|
416
|
-
email: v.string(),
|
|
417
|
-
subject: v.string(),
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
export const sendEmail = job.withContext<AppContext>()("send_email", {
|
|
421
|
-
input: emailInputSchema,
|
|
422
|
-
handler: async ({ input, ctx }) => {
|
|
423
|
-
await ctx.mailer.send({
|
|
424
|
-
to: input.email,
|
|
425
|
-
subject: input.subject,
|
|
426
|
-
});
|
|
427
|
-
},
|
|
428
|
-
});
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
```ts
|
|
432
|
-
// src/flowli/jobs/index.ts
|
|
433
|
-
export * from "./send-email";
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
```ts
|
|
437
|
-
// src/flowli/index.ts
|
|
438
388
|
import { defineJobs } from "flowli";
|
|
439
389
|
import { ioredisDriver } from "flowli/ioredis";
|
|
440
|
-
import * as jobs from "./jobs";
|
|
441
|
-
|
|
442
|
-
export type AppContext = {
|
|
443
|
-
db: typeof db;
|
|
444
|
-
logger: typeof logger;
|
|
445
|
-
mailer: typeof mailer;
|
|
446
|
-
};
|
|
447
390
|
|
|
448
391
|
export const flowli = defineJobs({
|
|
449
392
|
driver: ioredisDriver({
|
|
@@ -455,7 +398,24 @@ export const flowli = defineJobs({
|
|
|
455
398
|
logger,
|
|
456
399
|
mailer,
|
|
457
400
|
}),
|
|
458
|
-
jobs
|
|
401
|
+
jobs: ({ job }) => {
|
|
402
|
+
const emailInputSchema = v.object({
|
|
403
|
+
email: v.string(),
|
|
404
|
+
subject: v.string(),
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
sendEmail: job("send_email", {
|
|
409
|
+
input: emailInputSchema,
|
|
410
|
+
handler: async ({ input, ctx }) => {
|
|
411
|
+
await ctx.mailer.send({
|
|
412
|
+
to: input.email,
|
|
413
|
+
subject: input.subject,
|
|
414
|
+
});
|
|
415
|
+
},
|
|
416
|
+
}),
|
|
417
|
+
};
|
|
418
|
+
},
|
|
459
419
|
});
|
|
460
420
|
```
|
|
461
421
|
|
|
@@ -467,6 +427,12 @@ Flowli supports:
|
|
|
467
427
|
|
|
468
428
|
The job definitions stay the same. Only the driver changes.
|
|
469
429
|
|
|
430
|
+
Retry defaults can be attached globally or per job, including:
|
|
431
|
+
|
|
432
|
+
- `fixed` and `exponential` backoff
|
|
433
|
+
- capped retries with `maxDelayMs`
|
|
434
|
+
- jitter to spread retry bursts
|
|
435
|
+
|
|
470
436
|
## Runner
|
|
471
437
|
|
|
472
438
|
The runner is explicit and secondary by design.
|
package/dist/core/types.d.ts
CHANGED
|
@@ -31,9 +31,15 @@ export interface JobDefaults {
|
|
|
31
31
|
readonly maxAttempts?: number;
|
|
32
32
|
readonly backoff?: BackoffOptions;
|
|
33
33
|
}
|
|
34
|
+
export interface BackoffJitterOptions {
|
|
35
|
+
readonly minRatio: number;
|
|
36
|
+
readonly maxRatio: number;
|
|
37
|
+
}
|
|
34
38
|
export interface BackoffOptions {
|
|
35
39
|
readonly type: "fixed" | "exponential";
|
|
36
40
|
readonly delayMs: number;
|
|
41
|
+
readonly maxDelayMs?: number;
|
|
42
|
+
readonly jitter?: boolean | BackoffJitterOptions;
|
|
37
43
|
}
|
|
38
44
|
export interface FlowliInvocationOptions<TMeta> {
|
|
39
45
|
readonly meta?: TMeta;
|
|
@@ -99,7 +105,7 @@ export interface FlowliDriver {
|
|
|
99
105
|
acquireNextReady(now: number, leaseMs: number): Promise<AcquiredJobRecord | null>;
|
|
100
106
|
renewLease(jobId: string, token: string, leaseMs: number): Promise<boolean>;
|
|
101
107
|
markCompleted(acquired: AcquiredJobRecord, finishedAt: number): Promise<void>;
|
|
102
|
-
markFailed(acquired: AcquiredJobRecord, finishedAt: number, error: PersistedJobError): Promise<
|
|
108
|
+
markFailed(acquired: AcquiredJobRecord, finishedAt: number, error: PersistedJobError): Promise<MarkFailedResult>;
|
|
103
109
|
materializeDueSchedules(now: number, leaseMs: number): Promise<number>;
|
|
104
110
|
}
|
|
105
111
|
export interface DefineJobsOptions<TJobs extends JobsRecord, TContext extends FlowliContextRecord> {
|
|
@@ -153,9 +159,16 @@ export interface PersistedJobRecord {
|
|
|
153
159
|
readonly updatedAt: number;
|
|
154
160
|
readonly scheduledFor: number;
|
|
155
161
|
readonly attemptsMade: number;
|
|
162
|
+
readonly failureCount: number;
|
|
156
163
|
readonly maxAttempts: number;
|
|
157
164
|
readonly backoff?: BackoffOptions;
|
|
158
165
|
readonly lastError?: PersistedJobError;
|
|
166
|
+
readonly lastFailedAt?: number;
|
|
167
|
+
readonly nextRetryAt?: number;
|
|
168
|
+
}
|
|
169
|
+
export interface MarkFailedResult {
|
|
170
|
+
readonly state: "failed" | "retrying";
|
|
171
|
+
readonly retryAt?: number;
|
|
159
172
|
}
|
|
160
173
|
export interface AcquiredJobRecord {
|
|
161
174
|
readonly token: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { defineJobs } from "./core/define-jobs.js";
|
|
2
2
|
export { FlowliDefinitionError, FlowliDriverError, FlowliError, FlowliSchedulingError, FlowliStrategyError, FlowliValidationError, } from "./core/errors.js";
|
|
3
3
|
export { createContextualJobFactory, job } from "./core/job.js";
|
|
4
|
-
export type { BackoffOptions, DefineJobsBuilder, DelayValue, FlowliContextRecord, FlowliContextResolver, FlowliDriver, FlowliInvocationOptions, FlowliJobSurface, FlowliRuntime, JobDefaults, JobDefinition, JobHandlerArgs, JobReceipt, ScheduleInvocation, ScheduleReceipt, StandardSchemaIssue, StandardSchemaV1, } from "./core/types.js";
|
|
4
|
+
export type { BackoffJitterOptions, BackoffOptions, DefineJobsBuilder, DelayValue, FlowliContextRecord, FlowliContextResolver, FlowliDriver, FlowliInvocationOptions, FlowliJobSurface, FlowliRuntime, JobDefaults, JobDefinition, JobHandlerArgs, JobReceipt, ScheduleInvocation, ScheduleReceipt, StandardSchemaIssue, StandardSchemaV1, } from "./core/types.js";
|
package/dist/runner/types.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export interface RunnerHooks {
|
|
|
3
3
|
readonly onJobStarted?: (jobId: string, jobName: string) => void | Promise<void>;
|
|
4
4
|
readonly onJobCompleted?: (jobId: string, jobName: string) => void | Promise<void>;
|
|
5
5
|
readonly onJobFailed?: (jobId: string, jobName: string, error: PersistedJobError) => void | Promise<void>;
|
|
6
|
+
readonly onJobRetryScheduled?: (jobId: string, jobName: string, retryAt: number, error: PersistedJobError) => void | Promise<void>;
|
|
6
7
|
}
|
|
7
8
|
export interface RunnerOptions<TJobs extends JobsRecord, TContext extends FlowliContextRecord> {
|
|
8
9
|
readonly flowli: FlowliRuntime<TJobs, TContext>;
|
package/jsr.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://jsr.io/schema/config-file.v1.json",
|
|
3
3
|
"name": "@alialnaghmoush/flowli",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/index.ts",
|
|
7
7
|
"./ioredis": "./src/ioredis.ts",
|
|
@@ -18,9 +18,19 @@
|
|
|
18
18
|
"package.json"
|
|
19
19
|
],
|
|
20
20
|
"exclude": [
|
|
21
|
+
".cursor/",
|
|
22
|
+
".git/",
|
|
23
|
+
".gitignore",
|
|
24
|
+
".npm-cache/",
|
|
25
|
+
"biome.json",
|
|
26
|
+
"bun.lock",
|
|
27
|
+
"docker-compose.yml",
|
|
21
28
|
"dist/",
|
|
22
29
|
"node_modules/",
|
|
23
30
|
"test/",
|
|
31
|
+
"tsconfig.build.json",
|
|
32
|
+
"tsconfig.json",
|
|
33
|
+
"tsconfig.type-tests.json",
|
|
24
34
|
"type-tests/"
|
|
25
35
|
]
|
|
26
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Flowli is a jobs runtime with a code-first API, first-class execution strategies, runtime-scoped context injection, and pluggable Redis drivers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jobs",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"type": "git",
|
|
21
21
|
"url": "git+https://github.com/alialnaghmoush/flowli.git"
|
|
22
22
|
},
|
|
23
|
+
"license": "MIT",
|
|
23
24
|
"main": "./dist/index.js",
|
|
24
25
|
"types": "./dist/index.d.ts",
|
|
25
26
|
"type": "module",
|
|
@@ -68,9 +69,9 @@
|
|
|
68
69
|
},
|
|
69
70
|
"peerDependencies": {
|
|
70
71
|
"@tanstack/react-start": "^1.0.0-rc || ^1",
|
|
71
|
-
"ioredis": "",
|
|
72
|
+
"ioredis": "^5",
|
|
72
73
|
"next": "^15 || ^16",
|
|
73
|
-
"redis": "",
|
|
74
|
+
"redis": "^5",
|
|
74
75
|
"typescript": "^5",
|
|
75
76
|
"valibot": "^1.3.0",
|
|
76
77
|
"zod": "^4.3.6"
|