effect-inngest 0.1.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/LICENSE +21 -0
- package/README.md +457 -0
- package/dist/Client.d.ts +167 -0
- package/dist/Client.js +144 -0
- package/dist/Events.d.ts +110 -0
- package/dist/Events.js +93 -0
- package/dist/Function.d.ts +384 -0
- package/dist/Function.js +104 -0
- package/dist/Group.d.ts +152 -0
- package/dist/Group.js +164 -0
- package/dist/HttpApi.d.ts +75 -0
- package/dist/HttpApi.js +47 -0
- package/dist/_virtual/rolldown_runtime.js +18 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +8 -0
- package/dist/internal/constants.js +15 -0
- package/dist/internal/driver.d.ts +5 -0
- package/dist/internal/driver.js +117 -0
- package/dist/internal/errors.d.ts +56 -0
- package/dist/internal/errors.js +61 -0
- package/dist/internal/handler.d.ts +20 -0
- package/dist/internal/handler.js +145 -0
- package/dist/internal/helpers.js +44 -0
- package/dist/internal/interrupts.d.ts +2 -0
- package/dist/internal/interrupts.js +45 -0
- package/dist/internal/memo.js +56 -0
- package/dist/internal/protocol.d.ts +1 -0
- package/dist/internal/protocol.js +191 -0
- package/dist/internal/signature.d.ts +18 -0
- package/dist/internal/signature.js +97 -0
- package/dist/internal/step.d.ts +59 -0
- package/dist/internal/step.js +183 -0
- package/package.json +121 -0
- package/src/Client.ts +279 -0
- package/src/Events.ts +87 -0
- package/src/Function.ts +493 -0
- package/src/Group.ts +314 -0
- package/src/HttpApi.ts +82 -0
- package/src/index.ts +171 -0
- package/src/internal/constants.ts +11 -0
- package/src/internal/driver.ts +194 -0
- package/src/internal/errors.ts +130 -0
- package/src/internal/handler.ts +222 -0
- package/src/internal/helpers.ts +58 -0
- package/src/internal/interrupts.ts +62 -0
- package/src/internal/memo.ts +73 -0
- package/src/internal/protocol.ts +218 -0
- package/src/internal/signature.ts +158 -0
- package/src/internal/step.ts +377 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Erik Shestopal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<br />
|
|
3
|
+
<img src="./logo.png" alt="Effect Inngest" width="180" />
|
|
4
|
+
<h1>Effect Inngest</h1>
|
|
5
|
+
<p><strong>Build durable, type-safe workflows with Effect and Inngest</strong></p>
|
|
6
|
+
<p>The native Effect SDK for <a href="https://inngest.com">Inngest</a> — full type inference, composable steps, and dependency injection.</p>
|
|
7
|
+
<br />
|
|
8
|
+
|
|
9
|
+
<a href="#getting-started">Getting Started</a>
|
|
10
|
+
<span> • </span>
|
|
11
|
+
<a href="#features">Features</a>
|
|
12
|
+
<span> • </span>
|
|
13
|
+
<a href="#api-reference">API Reference</a>
|
|
14
|
+
<span> • </span>
|
|
15
|
+
<a href="./examples">Examples</a>
|
|
16
|
+
<br />
|
|
17
|
+
<br />
|
|
18
|
+
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## What is Effect Inngest?
|
|
24
|
+
|
|
25
|
+
Effect Inngest brings the power of [Effect](https://effect.website) to [Inngest's](https://inngest.com) durable execution platform. Define event schemas once, and types flow automatically through triggers, handlers, and step operations — no manual annotations needed.
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { InngestFunction, InngestGroup, InngestClient } from "effect-inngest";
|
|
29
|
+
import { Effect, Schema, Layer } from "effect";
|
|
30
|
+
|
|
31
|
+
// Define events as TaggedClass
|
|
32
|
+
class UserSignup extends Schema.TaggedClass<UserSignup>()("user/signup", {
|
|
33
|
+
userId: Schema.String,
|
|
34
|
+
email: Schema.String,
|
|
35
|
+
}) {}
|
|
36
|
+
|
|
37
|
+
// Create a function — event type is inferred from the trigger
|
|
38
|
+
const ProcessSignup = InngestFunction.make("process-signup", {
|
|
39
|
+
trigger: { event: UserSignup },
|
|
40
|
+
success: Schema.Void,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Create group and implement handler
|
|
44
|
+
const Group = InngestGroup.make(ProcessSignup);
|
|
45
|
+
|
|
46
|
+
class UserOnboarded extends Schema.TaggedClass<UserOnboarded>()("user/onboarded", {
|
|
47
|
+
userId: Schema.String,
|
|
48
|
+
}) {}
|
|
49
|
+
|
|
50
|
+
const Handlers = Group.toLayer({
|
|
51
|
+
"process-signup": ({ event, step }) =>
|
|
52
|
+
Effect.gen(function* () {
|
|
53
|
+
// event is typed as UserSignup
|
|
54
|
+
yield* step.run("send-welcome", sendWelcomeEmail(event.email));
|
|
55
|
+
yield* step.sleep("delay", "1 hour");
|
|
56
|
+
yield* step.sendEvent("notify", new UserOnboarded({ userId: event.userId }));
|
|
57
|
+
}),
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
<h3><a href="#getting-started">Get started →</a></h3>
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Features
|
|
66
|
+
|
|
67
|
+
- 🧙♂️ **Full type inference** — Payload types flow from schemas through triggers to handlers
|
|
68
|
+
- ⚡ **Effect-native steps** — `step.run`, `step.sleep`, `step.waitForEvent` return proper Effects
|
|
69
|
+
- 🔌 **Dependency injection** — Use Effect's Layer system for services in handlers
|
|
70
|
+
- 🛡️ **Schema validation** — Define events once with Effect Schema, validated everywhere
|
|
71
|
+
- 🚀 **Zero boilerplate** — No code generation, no manual type annotations
|
|
72
|
+
- 🔄 **Parallel execution** — Run steps concurrently with `Effect.all`
|
|
73
|
+
- 🌐 **Multi-runtime** — Works with Bun, Node.js, and Cloudflare Workers
|
|
74
|
+
- 🪶 **Lightweight** — Minimal dependencies, tree-shakeable
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm install effect-inngest effect @effect/platform
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
<details>
|
|
85
|
+
<summary>Other package managers</summary>
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# pnpm
|
|
89
|
+
pnpm add effect-inngest effect @effect/platform
|
|
90
|
+
|
|
91
|
+
# yarn
|
|
92
|
+
yarn add effect-inngest effect @effect/platform
|
|
93
|
+
|
|
94
|
+
# bun
|
|
95
|
+
bun add effect-inngest effect @effect/platform
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
</details>
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Quick Start
|
|
103
|
+
|
|
104
|
+
Copy this into a file called `inngest-demo.ts` and run it:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { FetchHttpClient } from "@effect/platform";
|
|
108
|
+
import { BunHttpServer, BunRuntime } from "@effect/platform-bun";
|
|
109
|
+
import * as HttpMiddleware from "@effect/platform/HttpMiddleware";
|
|
110
|
+
import * as HttpServer from "@effect/platform/HttpServer";
|
|
111
|
+
import { Duration, Effect, Layer, Schema } from "effect";
|
|
112
|
+
import { InngestFunction, InngestGroup, InngestClient } from "effect-inngest";
|
|
113
|
+
|
|
114
|
+
// 1. Define your events as TaggedClass
|
|
115
|
+
class UserSignup extends Schema.TaggedClass<UserSignup>()("user/signup", {
|
|
116
|
+
userId: Schema.String,
|
|
117
|
+
email: Schema.String,
|
|
118
|
+
}) {}
|
|
119
|
+
|
|
120
|
+
class UserWelcomeSent extends Schema.TaggedClass<UserWelcomeSent>()("user/welcome-sent", {
|
|
121
|
+
userId: Schema.String,
|
|
122
|
+
}) {}
|
|
123
|
+
|
|
124
|
+
// 2. Define your functions
|
|
125
|
+
const ProcessSignup = InngestFunction.make("process-signup", {
|
|
126
|
+
trigger: { event: UserSignup },
|
|
127
|
+
success: { welcomed: Schema.Boolean },
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const DailyDigest = InngestFunction.make("daily-digest", {
|
|
131
|
+
trigger: { cron: "0 9 * * *" },
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// 3. Create function group and implement handlers
|
|
135
|
+
const App = InngestGroup.make(ProcessSignup, DailyDigest);
|
|
136
|
+
|
|
137
|
+
const Handlers = App.toLayer({
|
|
138
|
+
"process-signup": ({ event, step }) =>
|
|
139
|
+
Effect.gen(function* () {
|
|
140
|
+
// event is typed as UserSignup
|
|
141
|
+
yield* Effect.log(`Processing signup for ${event.email}`);
|
|
142
|
+
|
|
143
|
+
// Durable step - memoized across retries
|
|
144
|
+
const user = yield* step.run("create-user", Effect.succeed({ id: event.userId, email: event.email }));
|
|
145
|
+
|
|
146
|
+
// Sleep durably
|
|
147
|
+
yield* step.sleep("welcome-delay", Duration.seconds(5));
|
|
148
|
+
|
|
149
|
+
// Send follow-up event
|
|
150
|
+
yield* step.sendEvent("notify", new UserWelcomeSent({ userId: user.id }));
|
|
151
|
+
|
|
152
|
+
return { welcomed: true };
|
|
153
|
+
}),
|
|
154
|
+
|
|
155
|
+
"daily-digest": ({ step }) => step.run("send-digest", Effect.log("Sending daily digest...")),
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// 4. Create client and start server
|
|
159
|
+
const Client = InngestClient.layer({
|
|
160
|
+
id: "my-app",
|
|
161
|
+
mode: "dev",
|
|
162
|
+
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
163
|
+
|
|
164
|
+
const Server = Layer.unwrapEffect(
|
|
165
|
+
Effect.gen(function* () {
|
|
166
|
+
const app = yield* InngestGroup.toHttpApp(App);
|
|
167
|
+
return HttpServer.serve(app, HttpMiddleware.logger).pipe(
|
|
168
|
+
HttpServer.withLogAddress,
|
|
169
|
+
Layer.provide(BunHttpServer.layer({ port: 3000 })),
|
|
170
|
+
);
|
|
171
|
+
}),
|
|
172
|
+
).pipe(Layer.provide(Handlers), Layer.provide(Client), Layer.provide(FetchHttpClient.layer));
|
|
173
|
+
|
|
174
|
+
// Run it
|
|
175
|
+
BunRuntime.runMain(Layer.launch(Server));
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Then in two terminals:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# Terminal 1: Start your app
|
|
182
|
+
bun inngest-demo.ts
|
|
183
|
+
|
|
184
|
+
# Terminal 2: Start Inngest dev server
|
|
185
|
+
bunx inngest-cli@latest dev -u http://localhost:3000
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Open http://localhost:8288 to trigger events and watch your functions run.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Getting Started
|
|
193
|
+
|
|
194
|
+
### 1. Define your events as TaggedClass
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
import { Schema } from "effect";
|
|
198
|
+
|
|
199
|
+
class UserSignup extends Schema.TaggedClass<UserSignup>()("user/signup", {
|
|
200
|
+
userId: Schema.String,
|
|
201
|
+
email: Schema.String,
|
|
202
|
+
plan: Schema.Literal("free", "pro", "enterprise"),
|
|
203
|
+
}) {}
|
|
204
|
+
|
|
205
|
+
class OrderPlaced extends Schema.TaggedClass<OrderPlaced>()("order/placed", {
|
|
206
|
+
orderId: Schema.String,
|
|
207
|
+
items: Schema.Array(Schema.Struct({ sku: Schema.String, qty: Schema.Number })),
|
|
208
|
+
total: Schema.Number,
|
|
209
|
+
}) {}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### 2. Define your functions
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import { InngestFunction } from "effect-inngest";
|
|
216
|
+
|
|
217
|
+
// Event-triggered function
|
|
218
|
+
const ProcessSignup = InngestFunction.make("process-signup", {
|
|
219
|
+
trigger: [{ event: UserSignup }], // pass multiple triggers as Array.
|
|
220
|
+
success: { welcomeEmailSent: Schema.Boolean },
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Cron-triggered function
|
|
224
|
+
const DailyReport = InngestFunction.make("daily-report", {
|
|
225
|
+
trigger: { cron: "0 9 * * *" },
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### 3. Create a function group and implement handlers
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { InngestGroup } from "effect-inngest";
|
|
233
|
+
import { Effect, Duration } from "effect";
|
|
234
|
+
|
|
235
|
+
const AppFunctions = InngestGroup.make(ProcessSignup, DailyReport);
|
|
236
|
+
|
|
237
|
+
const HandlersLive = AppFunctions.toLayer({
|
|
238
|
+
"process-signup": ({ event, step }) =>
|
|
239
|
+
Effect.gen(function* () {
|
|
240
|
+
yield* step.run("create-user", createUser(event));
|
|
241
|
+
yield* step.sleep("delay", Duration.minutes(5));
|
|
242
|
+
yield* step.sendEvent("welcome", new UserWelcomeSent({ userId: event.userId }));
|
|
243
|
+
return { welcomeEmailSent: true };
|
|
244
|
+
}),
|
|
245
|
+
|
|
246
|
+
"daily-report": ({ step }) => step.run("generate", Effect.log("Generating report...")),
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### 4. Create a web handler
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import { InngestClient, InngestGroup } from "effect-inngest";
|
|
254
|
+
import { FetchHttpClient } from "@effect/platform";
|
|
255
|
+
import { Layer } from "effect";
|
|
256
|
+
|
|
257
|
+
const ClientLive = InngestClient.layer({
|
|
258
|
+
id: "my-app",
|
|
259
|
+
mode: "cloud",
|
|
260
|
+
signingKey: process.env.INNGEST_SIGNING_KEY,
|
|
261
|
+
eventKey: process.env.INNGEST_EVENT_KEY,
|
|
262
|
+
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
263
|
+
|
|
264
|
+
const { handler, dispose } = InngestGroup.toWebHandler(AppFunctions, {
|
|
265
|
+
layer: Layer.mergeAll(HandlersLive, ClientLive),
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// Use with Bun
|
|
269
|
+
Bun.serve({ port: 3000, fetch: handler });
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Step Operations
|
|
275
|
+
|
|
276
|
+
All step operations are durable — they're memoized and survive retries:
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
({ step }) =>
|
|
280
|
+
Effect.gen(function* () {
|
|
281
|
+
// Run any Effect with memoization
|
|
282
|
+
const user = yield* step.run("fetch-user", fetchUser(userId));
|
|
283
|
+
|
|
284
|
+
// Sleep for a duration
|
|
285
|
+
yield* step.sleep("wait", Duration.hours(24));
|
|
286
|
+
|
|
287
|
+
// Sleep until a timestamp
|
|
288
|
+
yield* step.sleepUntil("deadline", new Date("2024-12-31"));
|
|
289
|
+
|
|
290
|
+
// Wait for an event (returns Option)
|
|
291
|
+
const payment = yield* step.waitForEvent("await-payment", PaymentReceived, {
|
|
292
|
+
timeout: Duration.days(7),
|
|
293
|
+
if: `async.data.orderId == "${orderId}"`,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Invoke another function
|
|
297
|
+
const result = yield* step.invoke("process", {
|
|
298
|
+
function: ProcessOrder,
|
|
299
|
+
data: { orderId: "123" },
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Send events
|
|
303
|
+
yield* step.sendEvent("notify", new OrderShipped({ orderId }));
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Parallel Execution
|
|
308
|
+
|
|
309
|
+
Run steps in parallel with Effect's concurrency:
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
const [user, orders, prefs] =
|
|
313
|
+
yield *
|
|
314
|
+
Effect.all(
|
|
315
|
+
[step.run("user", fetchUser(id)), step.run("orders", fetchOrders(id)), step.run("prefs", fetchPreferences(id))],
|
|
316
|
+
{ concurrency: "unbounded" },
|
|
317
|
+
);
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Dependency Injection
|
|
323
|
+
|
|
324
|
+
Use Effect's Layer system for clean dependency injection:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
import { Context, Layer } from "effect";
|
|
328
|
+
|
|
329
|
+
// Define a service
|
|
330
|
+
class EmailService extends Context.Tag("EmailService")<
|
|
331
|
+
EmailService,
|
|
332
|
+
{ readonly send: (to: string, body: string) => Effect.Effect<void> }
|
|
333
|
+
>() {}
|
|
334
|
+
|
|
335
|
+
// Use in handler
|
|
336
|
+
const HandlersLive = AppFunctions.toLayer({
|
|
337
|
+
"process-signup": ({ event, step }) =>
|
|
338
|
+
Effect.gen(function* () {
|
|
339
|
+
const email = yield* EmailService;
|
|
340
|
+
yield* step.run("send", email.send(event.email, "Welcome!"));
|
|
341
|
+
return { welcomeEmailSent: true };
|
|
342
|
+
}),
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Provide implementation
|
|
346
|
+
const EmailServiceLive = Layer.succeed(EmailService, {
|
|
347
|
+
send: (to, body) => Effect.log(`Sending to ${to}: ${body}`),
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
const { handler } = InngestGroup.toWebHandler(AppFunctions, {
|
|
351
|
+
layer: Layer.mergeAll(HandlersLive.pipe(Layer.provide(EmailServiceLive)), ClientLive),
|
|
352
|
+
});
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Function Options
|
|
358
|
+
|
|
359
|
+
Configure retries, concurrency, rate limiting, and more:
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
const ProcessOrder = InngestFunction.make("process-order", {
|
|
363
|
+
trigger: { event: OrderPlaced },
|
|
364
|
+
retries: 5,
|
|
365
|
+
concurrency: { limit: 10, key: "event.data.userId" },
|
|
366
|
+
rateLimit: { limit: 100, period: Duration.minutes(1) },
|
|
367
|
+
throttle: { limit: 50, period: Duration.seconds(10) },
|
|
368
|
+
debounce: { period: Duration.seconds(5) },
|
|
369
|
+
idempotency: "event.data.orderId",
|
|
370
|
+
timeouts: { finish: Duration.hours(1) },
|
|
371
|
+
cancelOn: [{ event: "order/cancelled", if: "event.data.orderId == async.data.orderId" }],
|
|
372
|
+
priority: { run: "event.data.isPremium ? 100 : 0" },
|
|
373
|
+
});
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Error Handling
|
|
379
|
+
|
|
380
|
+
Control retry behavior with typed errors:
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
import { NonRetriableError, RetryAfterError } from "effect-inngest";
|
|
384
|
+
|
|
385
|
+
// Don't retry this error
|
|
386
|
+
yield * new NonRetriableError({ message: "Card permanently declined" });
|
|
387
|
+
|
|
388
|
+
// Retry after a specific duration (retryAfter in milliseconds)
|
|
389
|
+
yield * new RetryAfterError({ message: "Rate limited", retryAfter: 60000 });
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## API Reference
|
|
395
|
+
|
|
396
|
+
### Core Modules
|
|
397
|
+
|
|
398
|
+
| Module | Description |
|
|
399
|
+
| ------------------- | ------------------------------------------------------ |
|
|
400
|
+
| `InngestFunction` | Function definition with trigger-based event inference |
|
|
401
|
+
| `InngestGroup` | Group functions, create handlers, and serve HTTP |
|
|
402
|
+
| `InngestClient` | Client configuration and event operations |
|
|
403
|
+
| `NonRetriableError` | Error to skip retries |
|
|
404
|
+
| `RetryAfterError` | Error to retry after delay |
|
|
405
|
+
|
|
406
|
+
### Step Methods
|
|
407
|
+
|
|
408
|
+
| Method | Description |
|
|
409
|
+
| ------------------------------------------ | ---------------------------------- |
|
|
410
|
+
| `step.run(id, effect)` | Execute an effect with memoization |
|
|
411
|
+
| `step.sleep(id, duration)` | Sleep for a duration |
|
|
412
|
+
| `step.sleepUntil(id, timestamp)` | Sleep until a timestamp |
|
|
413
|
+
| `step.waitForEvent(id, EventSchema, opts)` | Wait for an event with timeout |
|
|
414
|
+
| `step.invoke(id, opts)` | Invoke another function |
|
|
415
|
+
| `step.sendEvent(id, TaggedEvent)` | Send events to Inngest |
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## Examples
|
|
420
|
+
|
|
421
|
+
See the [`examples/`](./examples) directory:
|
|
422
|
+
|
|
423
|
+
| Example | Description |
|
|
424
|
+
| ------------------------------------------- | ---------------------------------------------- |
|
|
425
|
+
| [simple.ts](./examples/simple.ts) | Minimal hello world with parallel steps |
|
|
426
|
+
| [KitchenSink.ts](./examples/KitchenSink.ts) | Complete e-commerce workflow with all features |
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
# Run an example
|
|
430
|
+
bun examples/simple.ts
|
|
431
|
+
|
|
432
|
+
# Start Inngest dev server
|
|
433
|
+
bunx inngest-cli@latest dev -u http://localhost:9999
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## Current Limitations
|
|
439
|
+
|
|
440
|
+
This library is under active development. The following Inngest features are **not yet supported**:
|
|
441
|
+
|
|
442
|
+
| Feature | Status |
|
|
443
|
+
| ---------------------- | -------------------- |
|
|
444
|
+
| Middleware | 🚧 Not yet supported |
|
|
445
|
+
| AI Steps (`step.ai.*`) | 🚧 Not yet supported |
|
|
446
|
+
| Encryption | 🚧 Not yet supported |
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
## Requirements
|
|
451
|
+
|
|
452
|
+
- **TypeScript** 5.4+ with `strict: true`
|
|
453
|
+
- **Effect** 3.x
|
|
454
|
+
|
|
455
|
+
## License
|
|
456
|
+
|
|
457
|
+
MIT
|
package/dist/Client.d.ts
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import * as HttpClient from "@effect/platform/HttpClient";
|
|
2
|
+
import * as Context from "effect/Context";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Layer from "effect/Layer";
|
|
5
|
+
import * as Schema from "effect/Schema";
|
|
6
|
+
import * as Config from "effect/Config";
|
|
7
|
+
import * as ConfigError from "effect/ConfigError";
|
|
8
|
+
|
|
9
|
+
//#region src/Client.d.ts
|
|
10
|
+
declare namespace Client_d_exports {
|
|
11
|
+
export { EventPayload, InngestClient, layer, layerConfig, layerFromEnv };
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* @since 0.1.0
|
|
15
|
+
* @category type ids
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
declare const TypeId: unique symbol;
|
|
19
|
+
/**
|
|
20
|
+
* @since 0.1.0
|
|
21
|
+
* @category type ids
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
type TypeId = typeof TypeId;
|
|
25
|
+
type ClientMode = "dev" | "cloud";
|
|
26
|
+
interface ClientConfig {
|
|
27
|
+
/**
|
|
28
|
+
* The ID of this instance, most commonly a reference to the application it
|
|
29
|
+
* resides in.
|
|
30
|
+
*
|
|
31
|
+
* The ID of your client should remain the same for its lifetime; if you'd
|
|
32
|
+
* like to change the name of your client as it appears in the Inngest UI,
|
|
33
|
+
* change the `name` property instead.
|
|
34
|
+
*/
|
|
35
|
+
readonly id: string;
|
|
36
|
+
/**
|
|
37
|
+
* Inngest event key, used to send events to Inngest Cloud. If not provided,
|
|
38
|
+
* will search for the `INNGEST_EVENT_KEY` environment variable. If neither
|
|
39
|
+
* can be found, a warning will be shown and any attempts to send events in
|
|
40
|
+
* cloud mode will throw an error.
|
|
41
|
+
*/
|
|
42
|
+
readonly eventKey?: string | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Inngest signing key, used for request signature verification.
|
|
45
|
+
* If not provided, will search for the `INNGEST_SIGNING_KEY` environment variable.
|
|
46
|
+
*/
|
|
47
|
+
readonly signingKey?: string | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Fallback signing key for key rotation scenarios.
|
|
50
|
+
* If not provided, will search for the `INNGEST_SIGNING_KEY_FALLBACK` environment variable.
|
|
51
|
+
*/
|
|
52
|
+
readonly signingKeyFallback?: string | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* The base URL for the Inngest API (registration, run fetching, etc.)
|
|
55
|
+
* Defaults to https://api.inngest.com/ in cloud mode.
|
|
56
|
+
*/
|
|
57
|
+
readonly apiBaseUrl?: string | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* The base URL for sending events.
|
|
60
|
+
* Defaults to https://inn.gs/ in cloud mode.
|
|
61
|
+
*/
|
|
62
|
+
readonly eventBaseUrl?: string | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Can be used to explicitly set the client to Development Mode or Cloud Mode.
|
|
65
|
+
* If not set, mode is inferred from environment variables (INNGEST_DEV, NODE_ENV, etc.)
|
|
66
|
+
*
|
|
67
|
+
* Development mode will turn off signature verification and default to using
|
|
68
|
+
* a local URL to access a local Dev Server.
|
|
69
|
+
*/
|
|
70
|
+
readonly mode?: ClientMode | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* The Inngest environment to send events to. Defaults to whichever
|
|
73
|
+
* environment this client's event key is associated with.
|
|
74
|
+
*
|
|
75
|
+
* It's likely you never need to change this unless you're trying to sync
|
|
76
|
+
* multiple systems together using branch names.
|
|
77
|
+
*/
|
|
78
|
+
readonly env?: string | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* The application-specific version identifier. This can be an arbitrary value
|
|
81
|
+
* such as a version string, a Git commit SHA, or any other unique identifier.
|
|
82
|
+
*/
|
|
83
|
+
readonly appVersion?: string | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* The host where this app is served. Used for registration.
|
|
86
|
+
*/
|
|
87
|
+
readonly serveHost?: string | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* The path where the Inngest serve handler is mounted. Used for registration.
|
|
90
|
+
*/
|
|
91
|
+
readonly servePath?: string | undefined;
|
|
92
|
+
}
|
|
93
|
+
declare const EventPayload: Schema.Struct<{
|
|
94
|
+
name: typeof Schema.String;
|
|
95
|
+
data: typeof Schema.Unknown;
|
|
96
|
+
ts: Schema.optional<typeof Schema.Number>;
|
|
97
|
+
id: Schema.optional<typeof Schema.String>;
|
|
98
|
+
v: Schema.optional<typeof Schema.String>;
|
|
99
|
+
}>;
|
|
100
|
+
declare const SendEventResponse: Schema.Struct<{
|
|
101
|
+
ids: Schema.optionalWith<Schema.Array$<typeof Schema.String>, {
|
|
102
|
+
default: () => never[];
|
|
103
|
+
}>;
|
|
104
|
+
status: Schema.optional<typeof Schema.Number>;
|
|
105
|
+
}>;
|
|
106
|
+
type EventPayload = typeof EventPayload.Type;
|
|
107
|
+
type SendEventResponse = typeof SendEventResponse.Type;
|
|
108
|
+
declare const SendEventError_base: Schema.TaggedErrorClass<SendEventError, "SendEventError", {
|
|
109
|
+
readonly _tag: Schema.tag<"SendEventError">;
|
|
110
|
+
} & {
|
|
111
|
+
message: typeof Schema.String;
|
|
112
|
+
events: Schema.Array$<typeof Schema.String>;
|
|
113
|
+
}>;
|
|
114
|
+
declare class SendEventError extends SendEventError_base {}
|
|
115
|
+
interface InngestClientService {
|
|
116
|
+
readonly [TypeId]: TypeId;
|
|
117
|
+
readonly config: ClientConfig;
|
|
118
|
+
readonly mode: ClientMode;
|
|
119
|
+
readonly eventBaseUrl: string;
|
|
120
|
+
readonly apiBaseUrl: string;
|
|
121
|
+
readonly sendEvent: (events: ReadonlyArray<EventPayload>) => Effect.Effect<SendEventResponse, SendEventError>;
|
|
122
|
+
}
|
|
123
|
+
declare const InngestClient_base: Context.TagClass<InngestClient, "effect-inngest/InngestClient", InngestClientService>;
|
|
124
|
+
/**
|
|
125
|
+
* InngestClient service for communicating with Inngest.
|
|
126
|
+
*
|
|
127
|
+
* @since 0.1.0
|
|
128
|
+
* @category context
|
|
129
|
+
*/
|
|
130
|
+
declare class InngestClient extends InngestClient_base {}
|
|
131
|
+
/**
|
|
132
|
+
* Create an InngestClient layer from a config.
|
|
133
|
+
*
|
|
134
|
+
* @since 0.1.0
|
|
135
|
+
* @category layers
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* const ClientLive = InngestClient.layer({
|
|
139
|
+
* id: "my-app",
|
|
140
|
+
* eventKey: "my-event-key",
|
|
141
|
+
* })
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
declare const layer: (config: ClientConfig) => Layer.Layer<InngestClient, never, HttpClient.HttpClient>;
|
|
145
|
+
/**
|
|
146
|
+
* Create an InngestClient layer from Effect Config.
|
|
147
|
+
*
|
|
148
|
+
* @since 0.1.0
|
|
149
|
+
* @category layers
|
|
150
|
+
*/
|
|
151
|
+
declare const layerConfig: (config: Config.Config.Wrap<ClientConfig>) => Layer.Layer<InngestClient, ConfigError.ConfigError, HttpClient.HttpClient>;
|
|
152
|
+
/**
|
|
153
|
+
* Create an InngestClient layer from environment variables.
|
|
154
|
+
*
|
|
155
|
+
* Uses:
|
|
156
|
+
* - INNGEST_APP_ID or "app" as id
|
|
157
|
+
* - INNGEST_EVENT_KEY for event key
|
|
158
|
+
* - INNGEST_SIGNING_KEY for signing key
|
|
159
|
+
*
|
|
160
|
+
* Mode is inferred from environment (INNGEST_DEV, NODE_ENV, etc.)
|
|
161
|
+
*
|
|
162
|
+
* @since 0.1.0
|
|
163
|
+
* @category layers
|
|
164
|
+
*/
|
|
165
|
+
declare const layerFromEnv: Layer.Layer<InngestClient, ConfigError.ConfigError, HttpClient.HttpClient>;
|
|
166
|
+
//#endregion
|
|
167
|
+
export { Client_d_exports, EventPayload, InngestClient, layer, layerConfig, layerFromEnv };
|