effect-inngest 0.2.0-beta.0 → 0.3.0-beta.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.
Files changed (139) hide show
  1. package/README.md +89 -67
  2. package/dist/Client.d.ts +29 -12
  3. package/dist/Client.js +35 -32
  4. package/dist/Event.d.ts +43 -0
  5. package/dist/Event.js +46 -0
  6. package/dist/Events.d.ts +32 -35
  7. package/dist/Events.js +15 -12
  8. package/dist/Function.d.ts +17 -10
  9. package/dist/Function.js +22 -18
  10. package/dist/Group.d.ts +18 -8
  11. package/dist/Group.js +24 -68
  12. package/dist/HttpApi.d.ts +7 -7
  13. package/dist/HttpApi.js +8 -6
  14. package/dist/index.d.ts +2 -1
  15. package/dist/index.js +3 -2
  16. package/dist/internal/checkpoint/Config.d.ts +10 -0
  17. package/dist/internal/checkpoint/Config.js +29 -0
  18. package/dist/internal/checkpoint/Error.d.ts +11 -0
  19. package/dist/internal/checkpoint/Error.js +8 -0
  20. package/dist/internal/checkpoint/State.d.ts +1 -0
  21. package/dist/internal/checkpoint/State.js +54 -0
  22. package/dist/internal/checkpoint.d.ts +2 -32
  23. package/dist/internal/codec/EventPayload.d.ts +6 -0
  24. package/dist/internal/codec/EventPayload.js +60 -0
  25. package/dist/internal/codec/StepResult.js +55 -0
  26. package/dist/internal/domain/ExecutionInput.d.ts +40 -0
  27. package/dist/internal/domain/ExecutionInput.js +57 -0
  28. package/dist/internal/domain/ExecutionSuspension.d.ts +34 -0
  29. package/dist/internal/domain/ExecutionSuspension.js +64 -0
  30. package/dist/internal/domain/FunctionDefinition.js +13 -0
  31. package/dist/internal/domain/Memo.d.ts +22 -0
  32. package/dist/internal/domain/Memo.js +18 -0
  33. package/dist/internal/domain/StepCommand.d.ts +79 -0
  34. package/dist/internal/domain/StepCommand.js +124 -0
  35. package/dist/internal/domain/StepInfo.d.ts +12 -0
  36. package/dist/internal/domain/StepInfo.js +10 -0
  37. package/dist/internal/domain/StepInput.d.ts +10 -0
  38. package/dist/internal/driver.js +14 -225
  39. package/dist/internal/errors.d.ts +1 -21
  40. package/dist/internal/errors.js +2 -51
  41. package/dist/internal/execution/CheckpointRun.js +25 -0
  42. package/dist/internal/execution/ExecutionFailure.js +19 -0
  43. package/dist/internal/execution/ExecutionHeaders.js +58 -0
  44. package/dist/internal/execution/ExecutionResponse.js +68 -0
  45. package/dist/internal/execution/ExecutionResult.js +56 -0
  46. package/dist/internal/execution/ExecutionScope.js +30 -0
  47. package/dist/internal/execution/HandlerRun.js +16 -0
  48. package/dist/internal/handler.d.ts +2 -4
  49. package/dist/internal/handler.js +87 -82
  50. package/dist/internal/protocol.d.ts +120 -5
  51. package/dist/internal/protocol.js +313 -150
  52. package/dist/internal/runtime/CheckpointContext.js +5 -0
  53. package/dist/internal/runtime/HandlerContext.d.ts +13 -0
  54. package/dist/internal/runtime/HandlerContext.js +19 -0
  55. package/dist/internal/runtime/HandlerFiberScope.d.ts +10 -0
  56. package/dist/internal/runtime/HandlerFiberScope.js +5 -0
  57. package/dist/internal/runtime/StepCommandBus.d.ts +27 -0
  58. package/dist/internal/runtime/StepCommandBus.js +76 -0
  59. package/dist/internal/runtime/StepIdentity.d.ts +27 -0
  60. package/dist/internal/runtime/StepIdentity.js +46 -0
  61. package/dist/internal/runtime/StepTools.d.ts +83 -0
  62. package/dist/internal/runtime/StepTools.js +76 -0
  63. package/dist/internal/runtime/steps/InvokeStep.js +43 -0
  64. package/dist/internal/runtime/steps/SendEventStep.js +46 -0
  65. package/dist/internal/runtime/steps/SleepStep.js +22 -0
  66. package/dist/internal/runtime/steps/SleepUntilStep.js +22 -0
  67. package/dist/internal/runtime/steps/StepRun.js +48 -0
  68. package/dist/internal/runtime/steps/WaitForEventStep.js +27 -0
  69. package/dist/internal/serve/HttpApp.js +71 -0
  70. package/dist/internal/serve/Request.js +23 -0
  71. package/dist/internal/{signature.d.ts → serve/Signature.d.ts} +2 -5
  72. package/dist/internal/serve/Signature.js +123 -0
  73. package/dist/internal/wire/Duration.js +19 -0
  74. package/dist/internal/wire/Timestamp.js +14 -0
  75. package/package.json +17 -10
  76. package/src/Client.ts +69 -67
  77. package/src/Event.ts +107 -0
  78. package/src/Events.ts +50 -30
  79. package/src/Function.ts +58 -39
  80. package/src/Group.ts +54 -119
  81. package/src/HttpApi.ts +5 -6
  82. package/src/index.ts +21 -11
  83. package/src/internal/checkpoint/Config.ts +74 -0
  84. package/src/internal/checkpoint/Error.ts +6 -0
  85. package/src/internal/checkpoint/State.ts +107 -0
  86. package/src/internal/checkpoint.ts +3 -218
  87. package/src/internal/codec/EventPayload.ts +98 -0
  88. package/src/internal/codec/StepResult.ts +95 -0
  89. package/src/internal/domain/ExecutionInput.ts +66 -0
  90. package/src/internal/domain/ExecutionSuspension.ts +79 -0
  91. package/src/internal/domain/FunctionDefinition.ts +30 -0
  92. package/src/internal/domain/Memo.ts +28 -0
  93. package/src/internal/domain/StepCommand.ts +166 -0
  94. package/src/internal/domain/StepInfo.ts +8 -0
  95. package/src/internal/domain/StepInput.ts +10 -0
  96. package/src/internal/driver.ts +26 -332
  97. package/src/internal/errors.ts +1 -102
  98. package/src/internal/execution/CheckpointRun.ts +33 -0
  99. package/src/internal/execution/ExecutionFailure.ts +19 -0
  100. package/src/internal/execution/ExecutionHeaders.ts +86 -0
  101. package/src/internal/execution/ExecutionResponse.ts +79 -0
  102. package/src/internal/execution/ExecutionResult.ts +57 -0
  103. package/src/internal/execution/ExecutionScope.ts +41 -0
  104. package/src/internal/execution/HandlerRun.ts +38 -0
  105. package/src/internal/handler.ts +94 -87
  106. package/src/internal/protocol.ts +239 -72
  107. package/src/internal/runtime/CheckpointContext.ts +7 -0
  108. package/src/internal/runtime/HandlerContext.ts +21 -0
  109. package/src/internal/runtime/HandlerFiberScope.ts +9 -0
  110. package/src/internal/runtime/StepCommandBus.ts +129 -0
  111. package/src/internal/runtime/StepIdentity.ts +67 -0
  112. package/src/internal/runtime/StepTools.ts +161 -0
  113. package/src/internal/runtime/steps/InvokeStep.ts +71 -0
  114. package/src/internal/runtime/steps/SendEventStep.ts +67 -0
  115. package/src/internal/runtime/steps/SleepStep.ts +34 -0
  116. package/src/internal/runtime/steps/SleepUntilStep.ts +34 -0
  117. package/src/internal/runtime/steps/StepRun.ts +95 -0
  118. package/src/internal/runtime/steps/WaitForEventStep.ts +55 -0
  119. package/src/internal/serve/HttpApp.ts +123 -0
  120. package/src/internal/serve/Request.ts +27 -0
  121. package/src/internal/serve/Signature.ts +170 -0
  122. package/src/internal/wire/Duration.ts +31 -0
  123. package/src/internal/wire/Timestamp.ts +11 -0
  124. package/dist/internal/checkpoint.js +0 -112
  125. package/dist/internal/constants.js +0 -14
  126. package/dist/internal/driver.d.ts +0 -1
  127. package/dist/internal/helpers.js +0 -47
  128. package/dist/internal/interrupts.d.ts +0 -1
  129. package/dist/internal/interrupts.js +0 -43
  130. package/dist/internal/memo.js +0 -58
  131. package/dist/internal/signature.js +0 -113
  132. package/dist/internal/step.d.ts +0 -53
  133. package/dist/internal/step.js +0 -208
  134. package/src/internal/constants.ts +0 -11
  135. package/src/internal/helpers.ts +0 -58
  136. package/src/internal/interrupts.ts +0 -62
  137. package/src/internal/memo.ts +0 -88
  138. package/src/internal/signature.ts +0 -176
  139. package/src/internal/step.ts +0 -447
package/README.md CHANGED
@@ -25,14 +25,16 @@
25
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
26
 
27
27
  ```typescript
28
- import { InngestFunction, InngestGroup, InngestClient } from "effect-inngest";
28
+ import { InngestClient, InngestEvent, InngestFunction, InngestGroup } from "effect-inngest";
29
29
  import { Effect, Schema, Layer } from "effect";
30
30
 
31
- // Define events as TaggedClass
32
- class UserSignup extends Schema.TaggedClass<UserSignup>()("user/signup", {
33
- userId: Schema.String,
34
- email: Schema.String,
35
- }) {}
31
+ const UserSignup = InngestEvent.make(
32
+ "user/signup",
33
+ Schema.Struct({
34
+ userId: Schema.String,
35
+ email: Schema.String,
36
+ }),
37
+ );
36
38
 
37
39
  // Create a function — event type is inferred from the trigger
38
40
  const ProcessSignup = InngestFunction.make("process-signup", {
@@ -43,17 +45,15 @@ const ProcessSignup = InngestFunction.make("process-signup", {
43
45
  // Create group and implement handler
44
46
  const Group = InngestGroup.make(ProcessSignup);
45
47
 
46
- class UserOnboarded extends Schema.TaggedClass<UserOnboarded>()("user/onboarded", {
47
- userId: Schema.String,
48
- }) {}
48
+ const UserOnboarded = InngestEvent.make("user/onboarded", Schema.Struct({ userId: Schema.String }));
49
49
 
50
50
  const Handlers = Group.toLayer({
51
51
  "process-signup": ({ event, step }) =>
52
52
  Effect.gen(function* () {
53
- // event is typed as UserSignup
54
- yield* step.run("send-welcome", sendWelcomeEmail(event.email));
53
+ // event is typed as { name: "user/signup", data: { userId, email } }
54
+ yield* step.run("send-welcome", sendWelcomeEmail(event.data.email));
55
55
  yield* step.sleep("delay", "1 hour");
56
- yield* step.sendEvent("notify", new UserOnboarded({ userId: event.userId }));
56
+ yield* step.sendEvent("notify", UserOnboarded.make({ userId: event.data.userId }));
57
57
  }),
58
58
  });
59
59
  ```
@@ -109,17 +109,18 @@ import { BunHttpServer, BunRuntime } from "@effect/platform-bun";
109
109
  import * as HttpMiddleware from "@effect/platform/HttpMiddleware";
110
110
  import * as HttpServer from "@effect/platform/HttpServer";
111
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
- }) {}
112
+ import { InngestClient, InngestEvent, InngestFunction, InngestGroup } from "effect-inngest";
113
+
114
+ // 1. Define your Inngest event definitions
115
+ const UserSignup = InngestEvent.make(
116
+ "user/signup",
117
+ Schema.Struct({
118
+ userId: Schema.String,
119
+ email: Schema.String,
120
+ }),
121
+ );
119
122
 
120
- class UserWelcomeSent extends Schema.TaggedClass<UserWelcomeSent>()("user/welcome-sent", {
121
- userId: Schema.String,
122
- }) {}
123
+ const UserWelcomeSent = InngestEvent.make("user/welcome-sent", Schema.Struct({ userId: Schema.String }));
123
124
 
124
125
  // 2. Define your functions
125
126
  const ProcessSignup = InngestFunction.make("process-signup", {
@@ -138,17 +139,19 @@ const App = InngestGroup.make(ProcessSignup, DailyDigest);
138
139
  const Handlers = App.toLayer({
139
140
  "process-signup": ({ event, step }) =>
140
141
  Effect.gen(function* () {
141
- // event is typed as UserSignup
142
- yield* Effect.log(`Processing signup for ${event.email}`);
142
+ // event is typed as { name: "user/signup", data: { userId, email } }
143
+ yield* Effect.log(`Processing signup for ${event.data.email}`);
143
144
 
144
- // Durable step - memoized across retries
145
- const user = yield* step.run("create-user", Effect.succeed({ id: event.userId, email: event.email }));
145
+ // Durable value-returning steps need a replay schema
146
+ const user = yield* step.run("create-user", Effect.succeed({ id: event.data.userId, email: event.data.email }), {
147
+ schema: Schema.Struct({ id: Schema.String, email: Schema.String }),
148
+ });
146
149
 
147
150
  // Sleep durably
148
151
  yield* step.sleep("welcome-delay", Duration.seconds(5));
149
152
 
150
153
  // Send follow-up event
151
- yield* step.sendEvent("notify", new UserWelcomeSent({ userId: user.id }));
154
+ yield* step.sendEvent("notify", UserWelcomeSent.make({ userId: user.id }));
152
155
 
153
156
  return { welcomed: true };
154
157
  }),
@@ -197,17 +200,18 @@ import { FetchHttpClient } from "@effect/platform";
197
200
  import { BunHttpServer, BunRuntime } from "@effect/platform-bun";
198
201
  import { Duration, Effect, Layer, Schema } from "effect";
199
202
  import { InngestApiGroup, layerGroup } from "effect-inngest/HttpApi";
200
- import { InngestClient, InngestFunction, InngestGroup } from "effect-inngest";
203
+ import { InngestClient, InngestEvent, InngestFunction, InngestGroup } from "effect-inngest";
201
204
 
202
205
  // 1. Define your events
203
- class UserSignup extends Schema.TaggedClass<UserSignup>()("user/signup", {
204
- userId: Schema.String,
205
- email: Schema.String,
206
- }) {}
206
+ const UserSignup = InngestEvent.make(
207
+ "user/signup",
208
+ Schema.Struct({
209
+ userId: Schema.String,
210
+ email: Schema.String,
211
+ }),
212
+ );
207
213
 
208
- class UserWelcomeSent extends Schema.TaggedClass<UserWelcomeSent>()("user/welcome-sent", {
209
- userId: Schema.String,
210
- }) {}
214
+ const UserWelcomeSent = InngestEvent.make("user/welcome-sent", Schema.Struct({ userId: Schema.String }));
211
215
 
212
216
  // 2. Define functions
213
217
  const ProcessSignup = InngestFunction.make("process-signup", {
@@ -226,10 +230,12 @@ const App = InngestGroup.make(ProcessSignup, DailyDigest);
226
230
  const Handlers = App.toLayer({
227
231
  "process-signup": ({ event, step }) =>
228
232
  Effect.gen(function* () {
229
- yield* Effect.log(`Processing signup for ${event.email}`);
230
- const user = yield* step.run("create-user", Effect.succeed({ id: event.userId, email: event.email }));
233
+ yield* Effect.log(`Processing signup for ${event.data.email}`);
234
+ const user = yield* step.run("create-user", Effect.succeed({ id: event.data.userId, email: event.data.email }), {
235
+ schema: Schema.Struct({ id: Schema.String, email: Schema.String }),
236
+ });
231
237
  yield* step.sleep("welcome-delay", Duration.seconds(5));
232
- yield* step.sendEvent("notify", new UserWelcomeSent({ userId: user.id }));
238
+ yield* step.sendEvent("notify", UserWelcomeSent.make({ userId: user.id }));
233
239
  return { welcomed: true };
234
240
  }),
235
241
  "daily-digest": ({ step }) => step.run("send-digest", Effect.log("Sending daily digest...")),
@@ -262,22 +268,31 @@ BunRuntime.runMain(Layer.launch(Server));
262
268
 
263
269
  ## Getting Started
264
270
 
265
- ### 1. Define your events as TaggedClass
271
+ ### 1. Define your events
266
272
 
267
273
  ```typescript
268
274
  import { Schema } from "effect";
275
+ import { InngestEvent } from "effect-inngest";
276
+
277
+ const UserSignup = InngestEvent.make(
278
+ "user/signup",
279
+ Schema.Struct({
280
+ userId: Schema.String,
281
+ email: Schema.String,
282
+ plan: Schema.Literal("free", "pro", "enterprise"),
283
+ }),
284
+ );
285
+
286
+ const OrderPlaced = InngestEvent.make(
287
+ "order/placed",
288
+ Schema.Struct({
289
+ orderId: Schema.String,
290
+ items: Schema.Array(Schema.Struct({ sku: Schema.String, qty: Schema.Number })),
291
+ total: Schema.Number,
292
+ }),
293
+ );
269
294
 
270
- class UserSignup extends Schema.TaggedClass<UserSignup>()("user/signup", {
271
- userId: Schema.String,
272
- email: Schema.String,
273
- plan: Schema.Literal("free", "pro", "enterprise"),
274
- }) {}
275
-
276
- class OrderPlaced extends Schema.TaggedClass<OrderPlaced>()("order/placed", {
277
- orderId: Schema.String,
278
- items: Schema.Array(Schema.Struct({ sku: Schema.String, qty: Schema.Number })),
279
- total: Schema.Number,
280
- }) {}
295
+ const UserWelcomeSent = InngestEvent.make("user/welcome-sent", Schema.Struct({ userId: Schema.String }));
281
296
  ```
282
297
 
283
298
  ### 2. Define your functions
@@ -311,7 +326,7 @@ const HandlersLive = AppFunctions.toLayer({
311
326
  Effect.gen(function* () {
312
327
  yield* step.run("create-user", createUser(event));
313
328
  yield* step.sleep("delay", Duration.minutes(5));
314
- yield* step.sendEvent("welcome", new UserWelcomeSent({ userId: event.userId }));
329
+ yield* step.sendEvent("welcome", UserWelcomeSent.make({ userId: event.data.userId }));
315
330
  return { welcomeEmailSent: true };
316
331
  }),
317
332
 
@@ -350,8 +365,8 @@ All step operations are durable — they're memoized and survive retries:
350
365
  ```typescript
351
366
  ({ step }) =>
352
367
  Effect.gen(function* () {
353
- // Run any Effect with memoization
354
- const user = yield* step.run("fetch-user", fetchUser(userId));
368
+ // Run an Effect with memoization. Value-returning steps require a replay schema.
369
+ const user = yield* step.run("fetch-user", fetchUser(userId), { schema: User });
355
370
 
356
371
  // Sleep for a duration
357
372
  yield* step.sleep("wait", Duration.hours(24));
@@ -372,7 +387,7 @@ All step operations are durable — they're memoized and survive retries:
372
387
  });
373
388
 
374
389
  // Send events
375
- yield* step.sendEvent("notify", new OrderShipped({ orderId }));
390
+ yield* step.sendEvent("notify", OrderShipped.make({ orderId }));
376
391
  });
377
392
  ```
378
393
 
@@ -384,7 +399,11 @@ Run steps in parallel with Effect's concurrency:
384
399
  const [user, orders, prefs] =
385
400
  yield *
386
401
  Effect.all(
387
- [step.run("user", fetchUser(id)), step.run("orders", fetchOrders(id)), step.run("prefs", fetchPreferences(id))],
402
+ [
403
+ step.run("user", fetchUser(id), { schema: User }),
404
+ step.run("orders", fetchOrders(id), { schema: Schema.Array(Order) }),
405
+ step.run("prefs", fetchPreferences(id), { schema: Preferences }),
406
+ ],
388
407
  { concurrency: "unbounded" },
389
408
  );
390
409
  ```
@@ -409,7 +428,7 @@ const HandlersLive = AppFunctions.toLayer({
409
428
  "process-signup": ({ event, step }) =>
410
429
  Effect.gen(function* () {
411
430
  const email = yield* EmailService;
412
- yield* step.run("send", email.send(event.email, "Welcome!"));
431
+ yield* step.run("send", email.send(event.data.email, "Welcome!"));
413
432
  return { welcomeEmailSent: true };
414
433
  }),
415
434
  });
@@ -452,13 +471,14 @@ const ProcessOrder = InngestFunction.make("process-order", {
452
471
  Control retry behavior with typed errors:
453
472
 
454
473
  ```typescript
474
+ import { Duration, Effect } from "effect";
455
475
  import { NonRetriableError, RetryAfterError } from "effect-inngest";
456
476
 
457
477
  // Don't retry this error
458
- yield * new NonRetriableError({ message: "Card permanently declined" });
478
+ yield * Effect.fail(NonRetriableError.make({ message: "Card permanently declined" }));
459
479
 
460
- // Retry after a specific duration (retryAfter in milliseconds)
461
- yield * new RetryAfterError({ message: "Rate limited", retryAfter: 60000 });
480
+ // Retry after a specific duration
481
+ yield * Effect.fail(RetryAfterError.make({ message: "Rate limited", retryAfter: Duration.seconds(30) }));
462
482
  ```
463
483
 
464
484
  ---
@@ -472,19 +492,21 @@ yield * new RetryAfterError({ message: "Rate limited", retryAfter: 60000 });
472
492
  | `InngestFunction` | Function definition with trigger-based event inference |
473
493
  | `InngestGroup` | Group functions, create handlers, and serve HTTP |
474
494
  | `InngestClient` | Client configuration and event operations |
495
+ | `InngestEvent` | Event schema constructor with typed `.make` envelopes |
475
496
  | `NonRetriableError` | Error to skip retries |
476
497
  | `RetryAfterError` | Error to retry after delay |
477
498
 
478
499
  ### Step Methods
479
500
 
480
- | Method | Description |
481
- | ------------------------------------------ | ---------------------------------- |
482
- | `step.run(id, effect)` | Execute an effect with memoization |
483
- | `step.sleep(id, duration)` | Sleep for a duration |
484
- | `step.sleepUntil(id, timestamp)` | Sleep until a timestamp |
485
- | `step.waitForEvent(id, EventSchema, opts)` | Wait for an event with timeout |
486
- | `step.invoke(id, opts)` | Invoke another function |
487
- | `step.sendEvent(id, TaggedEvent)` | Send events to Inngest |
501
+ | Method | Description |
502
+ | ------------------------------------------------ | ----------------------------------------------- |
503
+ | `step.run(id, effect)` | Execute a void effect with memoization |
504
+ | `step.run(id, effect, { schema })` | Execute a value-returning effect with replay IO |
505
+ | `step.sleep(id, duration)` | Sleep for a duration |
506
+ | `step.sleepUntil(id, timestamp)` | Sleep until a timestamp |
507
+ | `step.waitForEvent(id, InngestEvent, opts)` | Wait for an event with timeout |
508
+ | `step.invoke(id, opts)` | Invoke another function |
509
+ | `step.sendEvent(id, InngestEvent.make(payload))` | Send events to Inngest |
488
510
 
489
511
  ---
490
512
 
package/dist/Client.d.ts CHANGED
@@ -1,16 +1,13 @@
1
+ import { CheckpointingOption } from "./internal/checkpoint/Config.js";
2
+ import { CheckpointApiError } from "./internal/checkpoint/Error.js";
1
3
  import { GeneratorOpcode } from "./internal/protocol.js";
2
- import { CheckpointApiError, CheckpointingOption } from "./internal/checkpoint.js";
3
- import * as Effect from "effect/Effect";
4
- import * as Schema from "effect/Schema";
4
+ import { Config, Context, Effect, Layer, Schedule, Schema } from "effect";
5
5
  import * as HttpClient from "effect/unstable/http/HttpClient";
6
- import * as Context from "effect/Context";
7
- import * as Layer from "effect/Layer";
8
- import * as Config from "effect/Config";
9
6
  import * as _$effect_Cause0 from "effect/Cause";
10
7
 
11
8
  //#region src/Client.d.ts
12
9
  declare namespace Client_d_exports {
13
- export { CheckpointApiError, CheckpointingOption, EventPayload, InngestClient, layer, layerConfig, layerFromEnv };
10
+ export { CheckpointApiError, CheckpointRetrySchedule, CheckpointingOption, ClientConfig, EventPayload, InngestClient, InngestConfig, layer, layerConfig, layerFromEnv };
14
11
  }
15
12
  /**
16
13
  * @since 0.1.0
@@ -91,6 +88,11 @@ interface ClientConfig {
91
88
  * The path where the Inngest serve handler is mounted. Used for registration.
92
89
  */
93
90
  readonly servePath?: string | undefined;
91
+ /**
92
+ * The framework adapter serving this app, e.g. "bun" or "nodejs". Used for
93
+ * protocol headers and registration metadata.
94
+ */
95
+ readonly framework?: string | undefined;
94
96
  /**
95
97
  * Whether to use checkpointing by default for executions of functions
96
98
  * created using this client.
@@ -116,7 +118,7 @@ declare const EventPayload: Schema.Struct<{
116
118
  readonly v: Schema.optional<Schema.String>;
117
119
  }>;
118
120
  declare const SendEventResponse: Schema.Struct<{
119
- readonly ids: Schema.withDecodingDefault<Schema.$Array<Schema.String>>;
121
+ readonly ids: Schema.withDecodingDefault<Schema.$Array<Schema.String>, never>;
120
122
  readonly status: Schema.optional<Schema.Number>;
121
123
  }>;
122
124
  type EventPayload = typeof EventPayload.Type;
@@ -148,6 +150,9 @@ interface InngestClientService {
148
150
  readonly runId: string;
149
151
  readonly fnId: string;
150
152
  readonly qiId: string;
153
+ readonly requestId?: string;
154
+ readonly generationId?: number;
155
+ readonly requestStartedAt?: number;
151
156
  readonly steps: ReadonlyArray<typeof GeneratorOpcode.Type>;
152
157
  }) => Effect.Effect<void, CheckpointApiError>;
153
158
  }
@@ -159,6 +164,18 @@ declare const InngestClient_base: Context.ServiceClass<InngestClient, "effect-in
159
164
  * @category context
160
165
  */
161
166
  declare class InngestClient extends InngestClient_base {}
167
+ declare const InngestConfig_base: Context.ServiceClass<InngestConfig, "effect-inngest/InngestConfig", ClientConfig>;
168
+ /**
169
+ * Static Inngest application configuration.
170
+ *
171
+ * This is the narrow dependency for internals that need app settings but not
172
+ * client behavior such as event sending or checkpoint API calls.
173
+ *
174
+ * @since 0.1.0
175
+ * @category context
176
+ */
177
+ declare class InngestConfig extends InngestConfig_base {}
178
+ declare const CheckpointRetrySchedule: Context.Reference<Schedule.Schedule<unknown, CheckpointApiError, never, never>>;
162
179
  /**
163
180
  * Create an InngestClient layer from a config.
164
181
  *
@@ -172,14 +189,14 @@ declare class InngestClient extends InngestClient_base {}
172
189
  * })
173
190
  * ```
174
191
  */
175
- declare const layer: (config: ClientConfig) => Layer.Layer<InngestClient, never, HttpClient.HttpClient>;
192
+ declare const layer: (config: ClientConfig) => Layer.Layer<InngestClient | InngestConfig, never, HttpClient.HttpClient>;
176
193
  /**
177
194
  * Create an InngestClient layer from Effect Config.
178
195
  *
179
196
  * @since 0.1.0
180
197
  * @category layers
181
198
  */
182
- declare const layerConfig: (config: Config.Wrap<ClientConfig>) => Layer.Layer<InngestClient, Config.ConfigError, HttpClient.HttpClient>;
199
+ declare const layerConfig: (config: Config.Wrap<ClientConfig>) => Layer.Layer<InngestClient | InngestConfig, Config.ConfigError, HttpClient.HttpClient>;
183
200
  /**
184
201
  * Create an InngestClient layer from environment variables.
185
202
  *
@@ -193,6 +210,6 @@ declare const layerConfig: (config: Config.Wrap<ClientConfig>) => Layer.Layer<In
193
210
  * @since 0.1.0
194
211
  * @category layers
195
212
  */
196
- declare const layerFromEnv: Layer.Layer<InngestClient, Config.ConfigError, HttpClient.HttpClient>;
213
+ declare const layerFromEnv: Layer.Layer<InngestClient | InngestConfig, Config.ConfigError, HttpClient.HttpClient>;
197
214
  //#endregion
198
- export { CheckpointApiError, type CheckpointingOption, Client_d_exports, EventPayload, InngestClient, layer, layerConfig, layerFromEnv };
215
+ export { CheckpointApiError, CheckpointRetrySchedule, type CheckpointingOption, ClientConfig, Client_d_exports, EventPayload, InngestClient, InngestConfig, layer, layerConfig, layerFromEnv };
package/dist/Client.js CHANGED
@@ -1,28 +1,21 @@
1
1
  import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
- import { CheckpointApiError } from "./internal/checkpoint.js";
3
- import { GeneratorOpcode, Headers } from "./internal/protocol.js";
4
- import { hashSigningKey } from "./internal/signature.js";
5
- import * as Clock from "effect/Clock";
6
- import * as Effect from "effect/Effect";
7
- import * as Option from "effect/Option";
8
- import * as Ref from "effect/Ref";
9
- import * as Schema from "effect/Schema";
2
+ import { CheckpointApiError } from "./internal/checkpoint/Error.js";
3
+ import { Headers } from "./internal/protocol.js";
4
+ import { hashSigningKey } from "./internal/serve/Signature.js";
5
+ import { Clock, Config, Context, Effect, Layer, Option, Predicate, Ref, Schedule, Schema } from "effect";
10
6
  import * as HttpClient from "effect/unstable/http/HttpClient";
11
- import * as Context from "effect/Context";
12
- import * as Layer from "effect/Layer";
13
7
  import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
14
8
  import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse";
15
- import * as Config from "effect/Config";
16
- import * as Predicate from "effect/Predicate";
17
- import * as Schedule from "effect/Schedule";
18
9
  //#region src/Client.ts
19
10
  /**
20
11
  * @since 0.1.0
21
12
  */
22
13
  var Client_exports = /* @__PURE__ */ __exportAll({
23
14
  CheckpointApiError: () => CheckpointApiError,
15
+ CheckpointRetrySchedule: () => CheckpointRetrySchedule,
24
16
  EventPayload: () => EventPayload,
25
17
  InngestClient: () => InngestClient,
18
+ InngestConfig: () => InngestConfig,
26
19
  layer: () => layer,
27
20
  layerConfig: () => layerConfig,
28
21
  layerFromEnv: () => layerFromEnv
@@ -59,15 +52,27 @@ var SendEventError = class extends Schema.TaggedErrorClass()("SendEventError", {
59
52
  * @category context
60
53
  */
61
54
  var InngestClient = class extends Context.Service()("effect-inngest/InngestClient") {};
55
+ /**
56
+ * Static Inngest application configuration.
57
+ *
58
+ * This is the narrow dependency for internals that need app settings but not
59
+ * client behavior such as event sending or checkpoint API calls.
60
+ *
61
+ * @since 0.1.0
62
+ * @category context
63
+ */
64
+ var InngestConfig = class extends Context.Service()("effect-inngest/InngestConfig") {};
62
65
  const resolveMode = (config) => config.mode ?? "dev";
63
66
  const resolveEventBaseUrl = (config, mode) => config.eventBaseUrl ?? (mode === "dev" ? DEFAULT_DEV_SERVER_URL : DEFAULT_EVENT_BASE_URL);
64
67
  const resolveApiBaseUrl = (config, mode) => config.apiBaseUrl ?? (mode === "dev" ? DEFAULT_DEV_SERVER_URL : DEFAULT_API_BASE_URL);
65
- const isRetriable = (error) => error.status === void 0 || error.status >= 500;
68
+ const isRetriable = (error) => Predicate.isUndefined(error.status) || error.status >= 500;
66
69
  const defaultCheckpointRetrySchedule = Schedule.exponential("100 millis", 2).pipe(Schedule.jittered, Schedule.both(Schedule.recurs(5)), Schedule.while((m) => isRetriable(m.input)));
67
- const makeClient = (config, httpClient, retrySchedule = defaultCheckpointRetrySchedule) => Effect.gen(function* () {
70
+ const CheckpointRetrySchedule = Context.Reference("effect-inngest/internal/CheckpointRetrySchedule", { defaultValue: () => defaultCheckpointRetrySchedule });
71
+ const makeClient = (config, httpClient) => Effect.gen(function* () {
68
72
  const mode = resolveMode(config);
69
73
  const eventBaseUrl = resolveEventBaseUrl(config, mode);
70
74
  const apiBaseUrl = resolveApiBaseUrl(config, mode);
75
+ const retrySchedule = yield* CheckpointRetrySchedule;
71
76
  /**
72
77
  * One-way switch: once the fallback signing key succeeds for any
73
78
  * checkpoint API call, all subsequent calls use it first. Per spec
@@ -79,7 +84,7 @@ const makeClient = (config, httpClient, retrySchedule = defaultCheckpointRetrySc
79
84
  message: "Event key is required to send events in cloud mode",
80
85
  events: events.map((e) => e.name)
81
86
  }));
82
- const key = config.eventKey ?? "local";
87
+ const key = config.eventKey ?? "NO_EVENT_KEY_SET";
83
88
  const url = new URL(`e/${key}`, eventBaseUrl).toString();
84
89
  const eventNames = events.map((e) => e.name);
85
90
  const now = yield* Clock.currentTimeMillis;
@@ -108,14 +113,12 @@ const makeClient = (config, httpClient, retrySchedule = defaultCheckpointRetrySc
108
113
  * error on non-2xx so the caller can decide whether to retry, swap keys,
109
114
  * or fall back.
110
115
  */
111
- const checkpointRequest = (runId, body, signingKey) => {
116
+ const checkpointRequest = (runId, body, hashedSigningKey) => {
112
117
  const url = new URL(`v1/checkpoint/${runId}/async`, apiBaseUrl).toString();
113
118
  const request = HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders({
114
119
  "Content-Type": "application/json",
115
- [Headers.SDK]: `effect-inngest:v${SDK_VERSION}`,
116
- [Headers.RequestVersion]: "2",
117
120
  ...config.env ? { [Headers.Env]: config.env } : {}
118
- }), HttpClientRequest.bearerToken(hashSigningKey(signingKey)), HttpClientRequest.bodyText(body, "application/json"));
121
+ }), HttpClientRequest.bearerToken(hashedSigningKey), HttpClientRequest.bodyText(body, "application/json"));
119
122
  return httpClient.execute(request).pipe(Effect.scoped, Effect.catch((error) => Effect.fail(new CheckpointApiError({ message: `Network error issuing checkpoint: ${Predicate.hasProperty(error, "message") ? error.message : String(error)}` }))), Effect.flatMap((response) => {
120
123
  if (response.status >= 200 && response.status < 300) return Effect.void;
121
124
  return Effect.fail(new CheckpointApiError({
@@ -126,19 +129,23 @@ const makeClient = (config, httpClient, retrySchedule = defaultCheckpointRetrySc
126
129
  };
127
130
  const checkpointAsync = Effect.fn("InngestClient.checkpointAsync")(function* (args) {
128
131
  const signingKey = config.signingKey;
129
- if (!signingKey) return yield* Effect.fail(new CheckpointApiError({ message: "No signing key configured for checkpoint API" }));
132
+ if (!signingKey && mode !== "dev") return yield* Effect.fail(new CheckpointApiError({ message: "No signing key configured for checkpoint API" }));
130
133
  const now = yield* Clock.currentTimeMillis;
131
134
  const body = JSON.stringify({
132
135
  run_id: args.runId,
133
136
  fn_id: args.fnId,
134
137
  qi_id: args.qiId,
135
- steps: Schema.encodeSync(Schema.Array(GeneratorOpcode))(args.steps),
138
+ request_id: args.requestId,
139
+ generation_id: args.generationId,
140
+ request_started_at: args.requestStartedAt,
141
+ steps: args.steps,
136
142
  ts: now
137
143
  });
138
144
  const fallbackKey = config.signingKeyFallback;
139
145
  return yield* Ref.get(useFallbackKey).pipe(Effect.flatMap((usingFallback) => {
140
146
  const primaryKey = usingFallback && fallbackKey ? fallbackKey : signingKey;
141
- return checkpointRequest(args.runId, body, primaryKey).pipe(Effect.catch((error) => error.status === 401 && !usingFallback && fallbackKey ? checkpointRequest(args.runId, body, fallbackKey).pipe(Effect.andThen(Ref.set(useFallbackKey, true))) : Effect.fail(error)));
147
+ const primaryToken = primaryKey ? hashSigningKey(primaryKey) : "";
148
+ return checkpointRequest(args.runId, body, primaryToken).pipe(Effect.catch((error) => error.status === 401 && !usingFallback && fallbackKey ? checkpointRequest(args.runId, body, hashSigningKey(fallbackKey)).pipe(Effect.andThen(Ref.set(useFallbackKey, true))) : Effect.fail(error)));
142
149
  })).pipe(Effect.retry(retrySchedule));
143
150
  });
144
151
  return {
@@ -164,21 +171,17 @@ const makeClient = (config, httpClient, retrySchedule = defaultCheckpointRetrySc
164
171
  * })
165
172
  * ```
166
173
  */
167
- const layer = (config) => Layer.effect(InngestClient, Effect.gen(function* () {
174
+ const layer = (config) => Layer.mergeAll(Layer.succeed(InngestConfig, config), Layer.effect(InngestClient, Effect.gen(function* () {
168
175
  return yield* makeClient(config, yield* HttpClient.HttpClient);
169
- }));
170
- const TEST_FACTORY_KEY = Symbol.for("effect-inngest/internal/test-retry-schedule-factory");
171
- InngestClient[TEST_FACTORY_KEY] = (config, retrySchedule) => Layer.effect(InngestClient, Effect.gen(function* () {
172
- return yield* makeClient(config, yield* HttpClient.HttpClient, retrySchedule);
173
- }));
176
+ })));
174
177
  /**
175
178
  * Create an InngestClient layer from Effect Config.
176
179
  *
177
180
  * @since 0.1.0
178
181
  * @category layers
179
182
  */
180
- const layerConfig = (config) => Layer.effect(InngestClient, Effect.gen(function* () {
181
- return yield* makeClient(yield* Config.unwrap(config), yield* HttpClient.HttpClient);
183
+ const layerConfig = (config) => Layer.unwrap(Effect.gen(function* () {
184
+ return layer(yield* Config.unwrap(config));
182
185
  }));
183
186
  /**
184
187
  * Create an InngestClient layer from environment variables.
@@ -200,4 +203,4 @@ const layerFromEnv = layerConfig(Config.all({
200
203
  signingKeyFallback: Config.string("INNGEST_SIGNING_KEY_FALLBACK").pipe(Config.option, Config.map(Option.getOrUndefined))
201
204
  }));
202
205
  //#endregion
203
- export { CheckpointApiError, Client_exports, EventPayload, InngestClient, layer, layerConfig, layerFromEnv };
206
+ export { CheckpointApiError, CheckpointRetrySchedule, Client_exports, EventPayload, InngestClient, InngestConfig, layer, layerConfig, layerFromEnv };
@@ -0,0 +1,43 @@
1
+ import { Schema } from "effect";
2
+ import { MakeOptions } from "effect/Schema";
3
+
4
+ //#region src/Event.d.ts
5
+ declare namespace Event_d_exports {
6
+ export { EventData, EventDefinition, EventEnvelope, EventType, isEventSchema, make };
7
+ }
8
+ declare const TypeId: unique symbol;
9
+ interface EventOptions extends MakeOptions {
10
+ readonly id?: string;
11
+ readonly ts?: number;
12
+ readonly v?: string;
13
+ }
14
+ interface EventEnvelope<Name extends string, Data> extends EventOptions {
15
+ readonly name: Name;
16
+ readonly data: Data;
17
+ }
18
+ type EventFields<Name extends string, DataSchema extends Schema.Top> = {
19
+ readonly name: Schema.tag<Name>;
20
+ readonly data: DataSchema;
21
+ readonly id: Schema.optional<Schema.String>;
22
+ readonly ts: Schema.optional<Schema.Number>;
23
+ readonly v: Schema.optional<Schema.String>;
24
+ };
25
+ type EventStruct<Name extends string, DataSchema extends Schema.Top> = Schema.Struct<EventFields<Name, DataSchema>>;
26
+ type EventConstructor<Name extends string, DataSchema extends Schema.Top> = abstract new (_: never) => EventEnvelope<Name, Schema.Schema.Type<DataSchema>>;
27
+ type PayloadConstructor<Name extends string, DataSchema extends Schema.Top> = {
28
+ bivariance(data: Schema.Schema.Type<DataSchema>, options?: EventOptions): EventEnvelope<Name, Schema.Schema.Type<DataSchema>>;
29
+ }["bivariance"];
30
+ type EventDefinition<Name extends string = string, DataSchema extends Schema.Top = Schema.Top> = EventConstructor<Name, DataSchema> & Omit<Schema.Opaque<EventEnvelope<Name, Schema.Schema.Type<DataSchema>>, EventStruct<Name, DataSchema>, {}>, "make" | "~type.make"> & {
31
+ readonly [TypeId]: typeof TypeId;
32
+ readonly identifier: Name;
33
+ readonly schema: DataSchema;
34
+ readonly make: unknown extends Schema.Schema.Type<DataSchema> ? (data: never, options?: EventOptions) => EventEnvelope<Name, unknown> : PayloadConstructor<Name, DataSchema>;
35
+ readonly "~type.make": EventEnvelope<Name, Schema.Schema.Type<DataSchema>>;
36
+ };
37
+ type EventData<Event extends EventDefinition> = Event extends EventDefinition<any, infer S> ? Schema.Schema.Type<S> : never;
38
+ type EventType<Event extends EventDefinition> = Event extends EventDefinition<infer Name, infer S> ? EventEnvelope<Name, Schema.Schema.Type<S>> : never;
39
+ declare function make<const Name extends string>(name: Name): EventDefinition<Name, Schema.Struct<{}>>;
40
+ declare function make<const Name extends string, const DataSchema extends Schema.Top>(name: Name, schema: DataSchema): EventDefinition<Name, DataSchema>;
41
+ declare const isEventSchema: (value: unknown) => value is EventDefinition;
42
+ //#endregion
43
+ export { EventDefinition, EventType, Event_d_exports };
package/dist/Event.js ADDED
@@ -0,0 +1,46 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
+ import { Predicate, Schema } from "effect";
3
+ //#region src/Event.ts
4
+ /**
5
+ * Public event definitions.
6
+ *
7
+ * Inngest events are protocol-shaped: `{ name, data, id?, ts?, v? }`.
8
+ * The event name is the discriminator; user payload lives under `data`.
9
+ *
10
+ * @since 0.1.0
11
+ */
12
+ var Event_exports = /* @__PURE__ */ __exportAll({
13
+ isEventSchema: () => isEventSchema,
14
+ make: () => make
15
+ });
16
+ const TypeId = Symbol.for("effect-inngest/Event");
17
+ function make(name, schema = Schema.Struct({})) {
18
+ const fields = {
19
+ name: Schema.tag(name),
20
+ data: schema,
21
+ id: Schema.optional(Schema.String),
22
+ ts: Schema.optional(Schema.Number),
23
+ v: Schema.optional(Schema.String)
24
+ };
25
+ const Event = Schema.Opaque()(Schema.Struct(fields));
26
+ class InngestEvent {
27
+ static [TypeId] = TypeId;
28
+ static identifier = name;
29
+ static schema = schema;
30
+ static make(data, options) {
31
+ const { id, ts, v } = options ?? {};
32
+ return {
33
+ name,
34
+ data,
35
+ id,
36
+ ts,
37
+ v
38
+ };
39
+ }
40
+ }
41
+ Object.setPrototypeOf(InngestEvent, Event);
42
+ return InngestEvent;
43
+ }
44
+ const isEventSchema = (value) => Schema.isSchema(value) && Predicate.hasProperty(value, TypeId);
45
+ //#endregion
46
+ export { Event_exports, make };