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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +457 -0
  3. package/dist/Client.d.ts +167 -0
  4. package/dist/Client.js +144 -0
  5. package/dist/Events.d.ts +110 -0
  6. package/dist/Events.js +93 -0
  7. package/dist/Function.d.ts +384 -0
  8. package/dist/Function.js +104 -0
  9. package/dist/Group.d.ts +152 -0
  10. package/dist/Group.js +164 -0
  11. package/dist/HttpApi.d.ts +75 -0
  12. package/dist/HttpApi.js +47 -0
  13. package/dist/_virtual/rolldown_runtime.js +18 -0
  14. package/dist/index.d.ts +7 -0
  15. package/dist/index.js +8 -0
  16. package/dist/internal/constants.js +15 -0
  17. package/dist/internal/driver.d.ts +5 -0
  18. package/dist/internal/driver.js +117 -0
  19. package/dist/internal/errors.d.ts +56 -0
  20. package/dist/internal/errors.js +61 -0
  21. package/dist/internal/handler.d.ts +20 -0
  22. package/dist/internal/handler.js +145 -0
  23. package/dist/internal/helpers.js +44 -0
  24. package/dist/internal/interrupts.d.ts +2 -0
  25. package/dist/internal/interrupts.js +45 -0
  26. package/dist/internal/memo.js +56 -0
  27. package/dist/internal/protocol.d.ts +1 -0
  28. package/dist/internal/protocol.js +191 -0
  29. package/dist/internal/signature.d.ts +18 -0
  30. package/dist/internal/signature.js +97 -0
  31. package/dist/internal/step.d.ts +59 -0
  32. package/dist/internal/step.js +183 -0
  33. package/package.json +121 -0
  34. package/src/Client.ts +279 -0
  35. package/src/Events.ts +87 -0
  36. package/src/Function.ts +493 -0
  37. package/src/Group.ts +314 -0
  38. package/src/HttpApi.ts +82 -0
  39. package/src/index.ts +171 -0
  40. package/src/internal/constants.ts +11 -0
  41. package/src/internal/driver.ts +194 -0
  42. package/src/internal/errors.ts +130 -0
  43. package/src/internal/handler.ts +222 -0
  44. package/src/internal/helpers.ts +58 -0
  45. package/src/internal/interrupts.ts +62 -0
  46. package/src/internal/memo.ts +73 -0
  47. package/src/internal/protocol.ts +218 -0
  48. package/src/internal/signature.ts +158 -0
  49. package/src/internal/step.ts +377 -0
@@ -0,0 +1,493 @@
1
+ /**
2
+ * @since 0.1.0
3
+ */
4
+ import { Array as Arr, Duration, Predicate, Schema } from "effect";
5
+ import { timeStr } from "./internal/helpers.js";
6
+
7
+ /**
8
+ * @since 0.1.0
9
+ * @category type ids
10
+ */
11
+ export const TypeId: unique symbol = Symbol.for("effect-inngest/Function");
12
+
13
+ /**
14
+ * @since 0.1.0
15
+ * @category type ids
16
+ */
17
+ export type TypeId = typeof TypeId;
18
+
19
+ type EventSchema = Schema.Schema.Any & { readonly _tag: string };
20
+
21
+ /**
22
+ * An event-based trigger configuration.
23
+ *
24
+ * @since 0.1.0
25
+ * @category triggers
26
+ */
27
+ export interface EventTrigger<E extends EventSchema = EventSchema> {
28
+ readonly event: E;
29
+ readonly if?: string;
30
+ }
31
+
32
+ /**
33
+ * A cron-based trigger configuration.
34
+ *
35
+ * @since 0.1.0
36
+ * @category triggers
37
+ */
38
+ export interface CronTrigger {
39
+ readonly cron: string;
40
+ }
41
+
42
+ /**
43
+ * A trigger configuration for a function.
44
+ *
45
+ * @since 0.1.0
46
+ * @category triggers
47
+ */
48
+ export type Trigger<E extends EventSchema = EventSchema> = EventTrigger<E> | CronTrigger;
49
+
50
+ /**
51
+ * @since 0.1.0
52
+ * @category triggers
53
+ */
54
+ export type TriggerInput<E extends EventSchema = EventSchema> = Trigger<E> | ReadonlyArray<Trigger<E>>;
55
+
56
+ interface ConcurrencyOption {
57
+ /**
58
+ * The concurrency limit for this option, adding a limit on how many concurrent
59
+ * steps can execute at once.
60
+ */
61
+ readonly limit: number;
62
+
63
+ /**
64
+ * An optional concurrency key, as an expression using the common expression language
65
+ * (CEL). The result of this expression is used to create new concurrency groups, or
66
+ * sub-queues, for each function run.
67
+ *
68
+ * The event is passed into this expression as "event".
69
+ *
70
+ * Examples:
71
+ * - `event.data.user_id`: this evaluates to the user_id in the event.data object.
72
+ * - `event.data.user_id + "-" + event.data.account_id`: creates a new group per user/account
73
+ * - `"ai"`: references a custom string
74
+ */
75
+ readonly key?: string;
76
+
77
+ /**
78
+ * An optional scope for the concurrency group. By default, concurrency limits are
79
+ * scoped to functions - one function's concurrency limits do not impact other functions.
80
+ *
81
+ * Changing this "scope" allows concurrency limits to work across environments (eg. production
82
+ * vs branch environments) or across your account (global).
83
+ */
84
+ readonly scope?: "fn" | "env" | "account";
85
+ }
86
+
87
+ interface RateLimitOption {
88
+ /**
89
+ * An optional key to use for rate limiting, similar to idempotency.
90
+ */
91
+ readonly key?: string;
92
+
93
+ /**
94
+ * The number of times to allow the function to run per the given `period`.
95
+ */
96
+ readonly limit: number;
97
+
98
+ /**
99
+ * The period of time to allow the function to run `limit` times.
100
+ */
101
+ readonly period: Duration.DurationInput;
102
+ }
103
+
104
+ interface ThrottleOption {
105
+ /**
106
+ * An optional expression which returns a throttling key for controlling throttling.
107
+ * Every unique key is its own throttle limit. Event data may be used within this
108
+ * expression, eg "event.data.user_id".
109
+ */
110
+ readonly key?: string;
111
+
112
+ /**
113
+ * The total number of runs allowed to start within the given `period`. The limit is
114
+ * applied evenly over the period.
115
+ */
116
+ readonly limit: number;
117
+
118
+ /**
119
+ * The period of time for the rate limit. Run starts are evenly spaced through
120
+ * the given period. The minimum granularity is 1 second.
121
+ */
122
+ readonly period: Duration.DurationInput;
123
+
124
+ /**
125
+ * The number of runs allowed to start in the given window in a single burst.
126
+ * A burst > 1 bypasses smoothing for the burst and allows many runs to start
127
+ * at once, if desired. Defaults to 1, which disables bursting.
128
+ */
129
+ readonly burst?: number;
130
+ }
131
+
132
+ interface DebounceOption {
133
+ /**
134
+ * An optional key to use for debouncing.
135
+ */
136
+ readonly key?: string;
137
+
138
+ /**
139
+ * The period of time to delay after receiving the last trigger to run the function.
140
+ */
141
+ readonly period: Duration.DurationInput;
142
+
143
+ /**
144
+ * The maximum time that a debounce can be extended before running.
145
+ * If events are continually received within the given period, a function
146
+ * will always run after the given timeout period.
147
+ */
148
+ readonly timeout?: Duration.DurationInput;
149
+ }
150
+
151
+ interface BatchEventsOption {
152
+ /**
153
+ * The maximum number of events to be consumed in one batch.
154
+ */
155
+ readonly maxSize: number;
156
+
157
+ /**
158
+ * How long to wait before invoking the function with a list of events.
159
+ * If timeout is reached, the function will be invoked with a batch
160
+ * even if it's not filled up to `maxSize`.
161
+ */
162
+ readonly timeout: Duration.DurationInput;
163
+
164
+ /**
165
+ * An optional key to use for batching.
166
+ */
167
+ readonly key?: string;
168
+
169
+ /**
170
+ * An optional boolean expression to determine an event's eligibility for batching.
171
+ */
172
+ readonly if?: string;
173
+ }
174
+
175
+ interface PriorityOption {
176
+ /**
177
+ * An expression to use to determine the priority of a function run. The
178
+ * expression can return a number between `-600` and `600`, where `600`
179
+ * declares that this run should be executed before any others enqueued in
180
+ * the last 600 seconds (10 minutes), and `-600` declares that this run
181
+ * should be executed after any others enqueued in the last 600 seconds.
182
+ */
183
+ readonly run?: string;
184
+ }
185
+
186
+ interface TimeoutsOption {
187
+ /**
188
+ * Start represents the timeout for starting a function. If the time
189
+ * between scheduling and starting a function exceeds this value, the
190
+ * function will be cancelled.
191
+ *
192
+ * This is, essentially, the amount of time that a function sits in the
193
+ * queue before starting.
194
+ */
195
+ readonly start?: Duration.DurationInput;
196
+
197
+ /**
198
+ * Finish represents the time between a function starting and the function
199
+ * finishing. If a function takes longer than this time to finish, the
200
+ * function is marked as cancelled.
201
+ */
202
+ readonly finish?: Duration.DurationInput;
203
+ }
204
+
205
+ interface SingletonOption {
206
+ /**
207
+ * An optional key expression used to scope singleton execution.
208
+ * Each unique key has its own singleton lock. Event data can be referenced,
209
+ * e.g. "event.data.user_id".
210
+ */
211
+ readonly key?: string;
212
+
213
+ /**
214
+ * Determines how to handle new runs when one is already active for the same key.
215
+ * - `"skip"` skips the new run.
216
+ * - `"cancel"` cancels the existing run and starts the new one.
217
+ */
218
+ readonly mode: "skip" | "cancel";
219
+ }
220
+
221
+ interface CancellationOption {
222
+ /**
223
+ * The name of the event that should cancel the function run.
224
+ */
225
+ readonly event: string;
226
+
227
+ /**
228
+ * The expression that must evaluate to true in order to cancel the function run. There
229
+ * are two variables available in this expression:
230
+ * - event, referencing the original function's event trigger
231
+ * - async, referencing the new cancel event.
232
+ */
233
+ readonly if?: string;
234
+
235
+ /**
236
+ * An optional timeout that the cancel is valid for. If this isn't
237
+ * specified, cancellation triggers are valid for up to a year or until the
238
+ * function ends.
239
+ */
240
+ readonly timeout?: Duration.DurationInput;
241
+ }
242
+
243
+ type Retries = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20;
244
+
245
+ /**
246
+ * @since 0.1.0
247
+ * @category models
248
+ */
249
+ export interface FunctionOptions {
250
+ /**
251
+ * A name for the function as it will appear in the Inngest Cloud UI.
252
+ */
253
+ readonly name?: string;
254
+
255
+ /**
256
+ * A description of the function.
257
+ */
258
+ readonly description?: string;
259
+
260
+ /**
261
+ * Specifies the maximum number of retries for all steps across this function.
262
+ * Can be a number from `0` to `20`. Defaults to `3`.
263
+ */
264
+ readonly retries?: Retries;
265
+
266
+ /**
267
+ * Concurrency specifies a limit on the total number of concurrent steps that
268
+ * can occur across all runs of the function. A value of 0 (or undefined) means
269
+ * use the maximum available concurrency.
270
+ *
271
+ * Specifying just a number means specifying only the concurrency limit. A
272
+ * maximum of two concurrency options can be specified.
273
+ */
274
+ readonly concurrency?: number | ConcurrencyOption | readonly [ConcurrencyOption, ConcurrencyOption];
275
+
276
+ /**
277
+ * Rate limit function runs, only running them a given number of times (limit) per
278
+ * period. Note that rate limit is a lossy, hard limit. Once the limit is hit,
279
+ * new runs will be skipped.
280
+ */
281
+ readonly rateLimit?: RateLimitOption;
282
+
283
+ /**
284
+ * Throttles function runs, only running them a given number of times (limit) per
285
+ * period. Once the limit is hit, new runs will be enqueued and will start when there's
286
+ * capacity.
287
+ */
288
+ readonly throttle?: ThrottleOption;
289
+
290
+ /**
291
+ * Debounce delays functions for the `period` specified.
292
+ */
293
+ readonly debounce?: DebounceOption;
294
+
295
+ /**
296
+ * Allow the specification of an idempotency key using event data.
297
+ */
298
+ readonly idempotency?: string;
299
+
300
+ /**
301
+ * Configure how the priority of a function run is decided.
302
+ */
303
+ readonly priority?: PriorityOption;
304
+
305
+ /**
306
+ * Ensures that only one run of the function is active at a time for a given key.
307
+ */
308
+ readonly singleton?: SingletonOption;
309
+
310
+ /**
311
+ * Configuration for cancelling a function run based on incoming events.
312
+ */
313
+ readonly cancelOn?: ReadonlyArray<CancellationOption>;
314
+
315
+ /**
316
+ * Configure timeouts for the function.
317
+ */
318
+ readonly timeouts?: TimeoutsOption;
319
+
320
+ /**
321
+ * Batch events configuration.
322
+ */
323
+ readonly batchEvents?: BatchEventsOption;
324
+ }
325
+
326
+ interface RegistrationConfig {
327
+ readonly appId: string;
328
+ readonly url: string;
329
+ }
330
+
331
+ interface FunctionRegistration {
332
+ readonly id: string;
333
+ readonly name: string;
334
+ readonly triggers: ReadonlyArray<{ event?: string; cron?: string; expression?: string }>;
335
+ readonly steps: {
336
+ readonly step: {
337
+ readonly id: string;
338
+ readonly name: string;
339
+ readonly runtime: { readonly type: "http"; readonly url: string };
340
+ readonly retries?: { readonly attempts: number };
341
+ };
342
+ };
343
+ readonly cancel?: ReadonlyArray<{ event: string; if?: string; timeout?: string }>;
344
+ readonly timeouts?: { start?: string; finish?: string };
345
+ }
346
+
347
+ /**
348
+ * An Inngest function definition.
349
+ *
350
+ * @since 0.1.0
351
+ * @category models
352
+ */
353
+ export interface InngestFunction<
354
+ Tag extends string,
355
+ Triggers extends Trigger,
356
+ Success extends Schema.Schema.Any,
357
+ Options extends FunctionOptions = FunctionOptions,
358
+ > {
359
+ readonly [TypeId]: TypeId;
360
+ readonly _tag: Tag;
361
+ readonly key: string;
362
+ readonly triggers: ReadonlyArray<Triggers>;
363
+ readonly success: Success;
364
+ readonly options: Options;
365
+ readonly toRegistration: (config: RegistrationConfig) => FunctionRegistration;
366
+ }
367
+
368
+ /**
369
+ * @since 0.1.0
370
+ * @category models
371
+ */
372
+ export declare namespace InngestFunction {
373
+ export type Any = InngestFunction<string, Trigger, Schema.Schema.Any, FunctionOptions>;
374
+ export type Tag<F> = F extends InngestFunction<infer T, any, any, any> ? T : never;
375
+ export type Triggers<F> = F extends InngestFunction<any, infer T, any, any> ? T : never;
376
+ export type Events<F> =
377
+ F extends InngestFunction<any, infer T, any, any> ? (T extends EventTrigger<infer E> ? E : never) : never;
378
+ export type EventType<F> =
379
+ F extends InngestFunction<any, infer T, any, any>
380
+ ? T extends EventTrigger<infer E>
381
+ ? Schema.Schema.Type<E>
382
+ : never
383
+ : never;
384
+
385
+ export type Success<F> = F extends InngestFunction<any, any, infer S, any> ? Schema.Schema.Type<S> : never;
386
+ export type Options<F> = F extends InngestFunction<any, any, any, infer O> ? O : never;
387
+ }
388
+
389
+ const isEventTrigger = (t: Trigger): t is EventTrigger => Predicate.hasProperty(t, "event");
390
+
391
+ const Proto = {
392
+ [TypeId]: TypeId,
393
+
394
+ toRegistration(this: InngestFunction.Any, config: RegistrationConfig): FunctionRegistration {
395
+ const triggers: Array<{ event?: string; cron?: string; expression?: string }> = [];
396
+
397
+ for (const t of this.triggers) {
398
+ if (isEventTrigger(t)) {
399
+ triggers.push({ event: t.event._tag, expression: t.if });
400
+ } else {
401
+ triggers.push({ cron: t.cron });
402
+ }
403
+ }
404
+
405
+ const opts = this.options;
406
+ const cancel = opts.cancelOn?.map((c) => ({
407
+ event: c.event,
408
+ if: c.if,
409
+ timeout: c.timeout ? timeStr(c.timeout) : undefined,
410
+ }));
411
+ const timeouts =
412
+ opts.timeouts?.start || opts.timeouts?.finish
413
+ ? {
414
+ start: opts.timeouts.start ? timeStr(opts.timeouts.start) : undefined,
415
+ finish: opts.timeouts.finish ? timeStr(opts.timeouts.finish) : undefined,
416
+ }
417
+ : undefined;
418
+
419
+ const fnId = `${config.appId}-${this._tag}`;
420
+ const stepUrl = new URL(config.url);
421
+ stepUrl.searchParams.set("fnId", fnId);
422
+ stepUrl.searchParams.set("stepId", "step");
423
+
424
+ return {
425
+ id: fnId,
426
+ name: opts.name ?? this._tag,
427
+ triggers,
428
+ steps: {
429
+ step: {
430
+ id: "step",
431
+ name: "step",
432
+ runtime: { type: "http", url: stepUrl.href },
433
+ retries: { attempts: opts.retries ?? 3 },
434
+ },
435
+ },
436
+ cancel: cancel && cancel.length > 0 ? cancel : undefined,
437
+ timeouts,
438
+ };
439
+ },
440
+ };
441
+
442
+ type NormalizeTriggers<T extends TriggerInput> = T extends ReadonlyArray<Trigger> ? T[number] : T;
443
+
444
+ /**
445
+ * @since 0.1.0
446
+ * @category constructors
447
+ * @example
448
+ * ```ts
449
+ * // Event trigger
450
+ * const Fn1 = InngestFunction.make("Hello", {
451
+ * trigger: { event: HelloEvent },
452
+ * success: Schema.Void,
453
+ * })
454
+ *
455
+ * // Event trigger with CEL filter
456
+ * const Fn2 = InngestFunction.make("Hello", {
457
+ * trigger: { event: HelloEvent, if: "event.data.name != ''" },
458
+ * success: Schema.Void,
459
+ * })
460
+ *
461
+ * // Cron trigger
462
+ * const Fn3 = InngestFunction.make("Scheduled", {
463
+ * trigger: { cron: "0 * * * *" },
464
+ * success: Schema.Void,
465
+ * })
466
+ *
467
+ * // Multiple triggers
468
+ * const Fn4 = InngestFunction.make("Multi", {
469
+ * trigger: [
470
+ * { event: HelloEvent },
471
+ * { cron: "0 0 * * *" },
472
+ * ],
473
+ * success: Schema.Void,
474
+ * })
475
+ * ```
476
+ */
477
+ export function make<
478
+ const Tag extends string,
479
+ T extends TriggerInput,
480
+ S extends Schema.Schema.Any,
481
+ const O extends FunctionOptions = {},
482
+ >(
483
+ tag: Tag,
484
+ options: { readonly trigger: T; readonly success: S } & O,
485
+ ): InngestFunction<Tag, NormalizeTriggers<T>, S, O> {
486
+ const fn = Object.create(Proto);
487
+ fn._tag = tag;
488
+ fn.key = `effect-inngest/Function/${tag}`;
489
+ fn.triggers = Arr.ensure(options.trigger);
490
+ fn.success = options.success;
491
+ fn.options = options;
492
+ return fn;
493
+ }