effect-inngest 0.1.1 → 0.1.3

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/dist/HttpApi.d.ts CHANGED
@@ -24,20 +24,20 @@ declare class FunctionNotFoundError extends FunctionNotFoundError_base {}
24
24
  * @category api
25
25
  */
26
26
  declare const InngestApiGroup: HttpApiGroup.HttpApiGroup<"inngest", HttpApiEndpoint.HttpApiEndpoint<"introspect", "GET", never, never, never, never, ({
27
- readonly mode: "cloud" | "dev";
28
27
  readonly function_count: number;
29
28
  readonly has_event_key: boolean;
30
29
  readonly has_signing_key: boolean;
31
30
  readonly has_signing_key_fallback: boolean;
31
+ readonly mode: "cloud" | "dev";
32
32
  readonly schema_version: "2024-05-24";
33
33
  readonly extra?: {
34
34
  readonly [x: string]: unknown;
35
35
  } | undefined;
36
36
  } & {
37
- readonly env: string | null;
38
37
  readonly authentication_succeeded: true;
39
38
  readonly api_origin: string;
40
39
  readonly app_id: string;
40
+ readonly env: string | null;
41
41
  readonly event_api_origin: string;
42
42
  readonly event_key_hash: string | null;
43
43
  readonly framework: string;
@@ -48,11 +48,11 @@ declare const InngestApiGroup: HttpApiGroup.HttpApiGroup<"inngest", HttpApiEndpo
48
48
  readonly signing_key_fallback_hash: string | null;
49
49
  readonly signing_key_hash: string | null;
50
50
  }) | ({
51
- readonly mode: "cloud" | "dev";
52
51
  readonly function_count: number;
53
52
  readonly has_event_key: boolean;
54
53
  readonly has_signing_key: boolean;
55
54
  readonly has_signing_key_fallback: boolean;
55
+ readonly mode: "cloud" | "dev";
56
56
  readonly schema_version: "2024-05-24";
57
57
  readonly extra?: {
58
58
  readonly [x: string]: unknown;
@@ -6,18 +6,14 @@ import * as Schema$1 from "effect/Schema";
6
6
  * Wire protocol schemas and opcode factories for Inngest communication.
7
7
  * @internal
8
8
  */
9
- const stripTags = (value) => {
10
- if (Predicate.isRecord(value)) {
11
- const stripped = Struct.omit(value, "_tag");
12
- return Object.fromEntries(Object.entries(stripped).map(([k, v]) => [k, stripTags(v)]));
13
- }
14
- if (Array.isArray(value)) return value.map(stripTags);
9
+ const stripTopLevelTag = (value) => {
10
+ if (Predicate.isRecord(value)) return Struct.omit(value, "_tag");
15
11
  return value;
16
12
  };
17
13
  const WireUnknown = Schema$1.transform(Schema$1.Unknown, Schema$1.Unknown, {
18
14
  strict: true,
19
15
  decode: (value) => value,
20
- encode: (value) => stripTags(value)
16
+ encode: (value) => stripTopLevelTag(value)
21
17
  });
22
18
  const Opcode = {
23
19
  None: "None",
@@ -8,7 +8,7 @@ import * as Duration from "effect/Duration";
8
8
  import * as Context from "effect/Context";
9
9
  import * as Effect from "effect/Effect";
10
10
  import * as Option from "effect/Option";
11
- import "effect/Schema";
11
+ import * as Schema from "effect/Schema";
12
12
  import * as Predicate from "effect/Predicate";
13
13
  import * as Arr from "effect/Array";
14
14
  import * as HashMap from "effect/HashMap";
@@ -38,6 +38,11 @@ const errorOtelAttributes = (err) => {
38
38
  } else attrs[OtelAttributes.ExceptionMessage] = String(err);
39
39
  return attrs;
40
40
  };
41
+ const encodeTaggedEvent = (event) => {
42
+ const ctor = event.constructor;
43
+ if (Schema.isSchema(ctor)) return Schema.encode(ctor)(event).pipe(Effect.orElseSucceed(() => event));
44
+ return Effect.succeed(event);
45
+ };
41
46
  var Step = class extends Context.Tag("effect-inngest/Step")() {};
42
47
  const normalizeOpts = (opts) => typeof opts === "string" ? {
43
48
  id: opts,
@@ -89,16 +94,20 @@ const createStepTools = (request, appName, stepIdCounts) => {
89
94
  timeout: timeStr(options.timeout),
90
95
  if: options.if
91
96
  }))), Match.exhaustive));
92
- const invoke = (opts, options) => Effect.flatMap(getInfo(opts), (info) => pipe(getMemo(info.hash), Match.value, Match.tag("MemoData", ({ data }) => Effect.succeed(data)), Match.tag("MemoError", ({ error }) => stepError(info.id, Predicate.hasProperty(error, "message") ? String(error.message) : "Invoke failed", { cause: error })), Match.tag("MemoTimeout", () => stepError(info.id, "Invoke timed out", { noRetry: true })), Match.tag("MemoInput", () => Effect.succeed(void 0)), Match.tag("MemoNone", () => Effect.fail(invokeInterrupt({
93
- info,
94
- functionId: `${appName}-${options.function._tag}`,
95
- payload: {
96
- data: Predicate.hasProperty(options, "data") ? options.data : void 0,
97
- user: options.user ?? {},
98
- v: options.v ?? "1"
99
- },
100
- timeout: options.timeout ? timeStr(options.timeout) : "365d"
101
- }))), Match.exhaustive));
97
+ const invoke = (opts, options) => Effect.flatMap(getInfo(opts), (info) => pipe(getMemo(info.hash), Match.value, Match.tag("MemoData", ({ data }) => Effect.succeed(data)), Match.tag("MemoError", ({ error }) => stepError(info.id, Predicate.hasProperty(error, "message") ? String(error.message) : "Invoke failed", { cause: error })), Match.tag("MemoTimeout", () => stepError(info.id, "Invoke timed out", { noRetry: true })), Match.tag("MemoInput", () => Effect.succeed(void 0)), Match.tag("MemoNone", () => {
98
+ const rawData = Predicate.hasProperty(options, "data") ? options.data : void 0;
99
+ const encodeData = rawData ? encodeTaggedEvent(rawData) : Effect.succeed(void 0);
100
+ return Effect.flatMap(encodeData, (encodedData) => Effect.fail(invokeInterrupt({
101
+ info,
102
+ functionId: `${appName}-${options.function._tag}`,
103
+ payload: {
104
+ data: encodedData,
105
+ user: options.user ?? {},
106
+ v: options.v ?? "1"
107
+ },
108
+ timeout: options.timeout ? timeStr(options.timeout) : "365d"
109
+ })));
110
+ }), Match.exhaustive));
102
111
  const run = (opts, effect) => Effect.flatMap(getInfo(opts), (info) => pipe(getMemo(info.hash), Match.value, Match.tag("MemoData", ({ data }) => Effect.succeed(data)), Match.tag("MemoError", ({ error }) => stepError(info.id, Predicate.hasProperty(error, "message") ? String(error.message) : "Step failed", { noRetry: true })), Match.tag("MemoTimeout", () => stepError(info.id, "Step timed out", { noRetry: true })), Match.tag("MemoInput", () => stepError(info.id, "Unexpected step result type: input")), Match.tag("MemoNone", () => {
103
112
  if (isBlocked(info.hash) || !canExecute(info.hash)) return Effect.fail(plannedInterrupt({ info }));
104
113
  return effect.pipe(Effect.withSpan(`inngest.step/run/${info.id}`, { attributes: {
@@ -132,17 +141,17 @@ const createStepTools = (request, appName, stepIdCounts) => {
132
141
  events: []
133
142
  }))), Match.tag("MemoInput", () => Effect.succeed({ ids: [] })), Match.tag("MemoNone", () => {
134
143
  if (isBlocked(info.hash) || !canExecute(info.hash)) return Effect.fail(plannedInterrupt({ info }));
135
- const eventPayloads = Arr.map(Arr.ensure(payload), (e) => ({
144
+ const events = Arr.ensure(payload);
145
+ return Effect.flatMap(Effect.forEach(events, (e) => Effect.map(encodeTaggedEvent(e), (encoded) => ({
136
146
  name: e._tag,
137
- data: e
138
- }));
139
- return Effect.flatMap(InngestClient, (client) => client.sendEvent(eventPayloads).pipe(Effect.withSpan(`inngest.step/sendEvent/${info.id}`, { attributes: {
147
+ data: encoded
148
+ }))), (eventPayloads) => Effect.flatMap(InngestClient, (client) => client.sendEvent(eventPayloads).pipe(Effect.withSpan(`inngest.step/sendEvent/${info.id}`, { attributes: {
140
149
  [OtelAttributes.StepId]: info.id,
141
150
  [OtelAttributes.StepType]: "sendEvent"
142
151
  } }), Effect.flatMap((result) => Effect.fail(runInterrupt({
143
152
  info,
144
153
  data: result
145
- })))));
154
+ }))))));
146
155
  }), Match.exhaustive));
147
156
  return {
148
157
  run,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-inngest",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Native Effect client library for Inngest - build durable, type-safe workflows",
5
5
  "keywords": [
6
6
  "background-jobs",
@@ -79,7 +79,7 @@ export const execute = <F extends InngestFunction.Any, R>(
79
79
  const context = buildHandlerContext<F>(fn, step, request);
80
80
  const headers = baseHeaders();
81
81
 
82
- const result = yield* handler(context).pipe(
82
+ const result = yield* Effect.scoped(handler(context)).pipe(
83
83
  Effect.provideService(Step, step),
84
84
  Effect.map((value) => ExecutionResult.make({ status: 200, body: value, headers })),
85
85
  Effect.catchAll((error) => {
@@ -5,13 +5,9 @@
5
5
  import { Predicate, Struct } from "effect";
6
6
  import * as Schema from "effect/Schema";
7
7
 
8
- const stripTags = (value: unknown): unknown => {
8
+ const stripTopLevelTag = (value: unknown): unknown => {
9
9
  if (Predicate.isRecord(value)) {
10
- const stripped = Struct.omit(value as Record<string, unknown>, "_tag");
11
- return Object.fromEntries(Object.entries(stripped).map(([k, v]) => [k, stripTags(v)]));
12
- }
13
- if (Array.isArray(value)) {
14
- return value.map(stripTags);
10
+ return Struct.omit(value as Record<string, unknown>, "_tag");
15
11
  }
16
12
  return value;
17
13
  };
@@ -19,7 +15,7 @@ const stripTags = (value: unknown): unknown => {
19
15
  const WireUnknown = Schema.transform(Schema.Unknown, Schema.Unknown, {
20
16
  strict: true,
21
17
  decode: (value) => value,
22
- encode: (value) => stripTags(value),
18
+ encode: (value) => stripTopLevelTag(value),
23
19
  });
24
20
 
25
21
  export const Opcode = {
@@ -86,6 +86,14 @@ type InvokeOptions<F extends InngestFunction.Any> = [InngestFunction.EventType<F
86
86
  type EventSchema = Schema.Schema.Any & { readonly _tag: string };
87
87
  type TaggedEvent = { readonly _tag: string };
88
88
 
89
+ const encodeTaggedEvent = (event: TaggedEvent): Effect.Effect<unknown, never> => {
90
+ const ctor = event.constructor;
91
+ if (Schema.isSchema(ctor)) {
92
+ return Schema.encode(ctor as unknown as Schema.Schema.AnyNoContext)(event).pipe(Effect.orElseSucceed(() => event));
93
+ }
94
+ return Effect.succeed(event);
95
+ };
96
+
89
97
  interface StepTools {
90
98
  readonly run: <A, Err, R>(
91
99
  id: StepOptionsOrId,
@@ -226,20 +234,24 @@ export const createStepTools = (
226
234
  ),
227
235
  Match.tag("MemoTimeout", () => stepError(info.id, "Invoke timed out", { noRetry: true })),
228
236
  Match.tag("MemoInput", () => Effect.succeed(undefined as InngestFunction.Success<F>)),
229
- Match.tag("MemoNone", () =>
230
- Effect.fail(
231
- invokeInterrupt({
232
- info,
233
- functionId: `${appName}-${options.function._tag}`,
234
- payload: {
235
- data: Predicate.hasProperty(options, "data") ? options.data : undefined,
236
- user: options.user ?? {},
237
- v: options.v ?? "1",
238
- },
239
- timeout: options.timeout ? timeStr(options.timeout) : "365d",
240
- }),
241
- ),
242
- ),
237
+ Match.tag("MemoNone", () => {
238
+ const rawData = Predicate.hasProperty(options, "data") ? (options.data as TaggedEvent) : undefined;
239
+ const encodeData = rawData ? encodeTaggedEvent(rawData) : Effect.succeed(undefined);
240
+ return Effect.flatMap(encodeData, (encodedData) =>
241
+ Effect.fail(
242
+ invokeInterrupt({
243
+ info,
244
+ functionId: `${appName}-${options.function._tag}`,
245
+ payload: {
246
+ data: encodedData,
247
+ user: options.user ?? {},
248
+ v: options.v ?? "1",
249
+ },
250
+ timeout: options.timeout ? timeStr(options.timeout) : "365d",
251
+ }),
252
+ ),
253
+ );
254
+ }),
243
255
  Match.exhaustive,
244
256
  ),
245
257
  );
@@ -311,15 +323,20 @@ export const createStepTools = (
311
323
  return Effect.fail(plannedInterrupt({ info }));
312
324
  }
313
325
 
314
- const eventPayloads = Arr.map(Arr.ensure(payload), (e) => ({ name: e._tag, data: e }));
315
-
316
- return Effect.flatMap(InngestClient, (client) =>
317
- client.sendEvent(eventPayloads).pipe(
318
- Effect.withSpan(`inngest.step/sendEvent/${info.id}`, {
319
- attributes: { [OtelAttributes.StepId]: info.id, [OtelAttributes.StepType]: "sendEvent" },
320
- }),
321
- Effect.flatMap((result) => Effect.fail(runInterrupt({ info, data: result }))),
326
+ const events = Arr.ensure(payload);
327
+ return Effect.flatMap(
328
+ Effect.forEach(events, (e) =>
329
+ Effect.map(encodeTaggedEvent(e), (encoded) => ({ name: e._tag, data: encoded })),
322
330
  ),
331
+ (eventPayloads) =>
332
+ Effect.flatMap(InngestClient, (client) =>
333
+ client.sendEvent(eventPayloads).pipe(
334
+ Effect.withSpan(`inngest.step/sendEvent/${info.id}`, {
335
+ attributes: { [OtelAttributes.StepId]: info.id, [OtelAttributes.StepType]: "sendEvent" },
336
+ }),
337
+ Effect.flatMap((result) => Effect.fail(runInterrupt({ info, data: result }))),
338
+ ),
339
+ ),
323
340
  );
324
341
  }),
325
342
  Match.exhaustive,