effect-inngest 0.1.2 → 0.2.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.
- package/dist/Client.d.ts +55 -24
- package/dist/Client.js +84 -25
- package/dist/Events.d.ts +36 -61
- package/dist/Events.js +5 -13
- package/dist/Function.d.ts +35 -15
- package/dist/Function.js +14 -8
- package/dist/Group.d.ts +5 -4
- package/dist/Group.js +26 -24
- package/dist/HttpApi.d.ts +49 -54
- package/dist/HttpApi.js +32 -17
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/index.js +1 -2
- package/dist/internal/checkpoint.d.ts +32 -0
- package/dist/internal/checkpoint.js +112 -0
- package/dist/internal/constants.js +1 -2
- package/dist/internal/driver.d.ts +1 -5
- package/dist/internal/driver.js +164 -52
- package/dist/internal/errors.d.ts +20 -27
- package/dist/internal/errors.js +29 -15
- package/dist/internal/handler.d.ts +4 -13
- package/dist/internal/handler.js +70 -43
- package/dist/internal/helpers.js +12 -9
- package/dist/internal/interrupts.d.ts +1 -2
- package/dist/internal/interrupts.js +1 -3
- package/dist/internal/memo.js +22 -20
- package/dist/internal/protocol.d.ts +28 -1
- package/dist/internal/protocol.js +84 -52
- package/dist/internal/signature.d.ts +5 -9
- package/dist/internal/signature.js +34 -18
- package/dist/internal/step.d.ts +5 -11
- package/dist/internal/step.js +48 -32
- package/package.json +26 -21
- package/src/Client.ts +244 -68
- package/src/Events.ts +3 -3
- package/src/Function.ts +52 -15
- package/src/Group.ts +39 -26
- package/src/HttpApi.ts +38 -27
- package/src/internal/checkpoint.ts +218 -0
- package/src/internal/driver.ts +241 -93
- package/src/internal/errors.ts +21 -14
- package/src/internal/handler.ts +185 -142
- package/src/internal/helpers.ts +9 -9
- package/src/internal/memo.ts +48 -33
- package/src/internal/protocol.ts +89 -45
- package/src/internal/signature.ts +76 -58
- package/src/internal/step.ts +83 -32
- package/dist/_virtual/rolldown_runtime.js +0 -18
package/dist/internal/driver.js
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { make } from "./checkpoint.js";
|
|
2
|
+
import { GeneratorOpcode, Headers as Headers$1, Opcode, UserError, discoveryRequest, runComplete } from "./protocol.js";
|
|
3
|
+
import { InngestClient } from "../Client.js";
|
|
2
4
|
import { isNonRetriableError, isRetryAfterError, isStepError } from "./errors.js";
|
|
3
5
|
import { StepInterrupt } from "./interrupts.js";
|
|
4
6
|
import { OtelAttributes } from "./constants.js";
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import * as
|
|
8
|
-
import * as
|
|
9
|
-
|
|
7
|
+
import { buildHandlerContext, createStepTools } from "./step.js";
|
|
8
|
+
import * as Duration from "effect/Duration";
|
|
9
|
+
import * as Effect from "effect/Effect";
|
|
10
|
+
import * as Option from "effect/Option";
|
|
11
|
+
import * as Ref from "effect/Ref";
|
|
12
|
+
import * as Schema from "effect/Schema";
|
|
13
|
+
import * as Context from "effect/Context";
|
|
14
|
+
import "effect/Layer";
|
|
15
|
+
import * as Predicate from "effect/Predicate";
|
|
16
|
+
import * as Headers from "effect/unstable/http/Headers";
|
|
17
|
+
import * as Cause from "effect/Cause";
|
|
18
|
+
import * as HttpTraceContext from "effect/unstable/http/HttpTraceContext";
|
|
19
|
+
import * as Chunk from "effect/Chunk";
|
|
20
|
+
import * as HashMap from "effect/HashMap";
|
|
21
|
+
import { pipe } from "effect/Function";
|
|
10
22
|
//#region src/internal/driver.ts
|
|
11
23
|
/**
|
|
12
24
|
* Driver execution logic.
|
|
@@ -14,20 +26,21 @@ import * as HttpTraceContext from "@effect/platform/HttpTraceContext";
|
|
|
14
26
|
*/
|
|
15
27
|
const SDK_VERSION = "2.0.0";
|
|
16
28
|
var ExecutionResult = class extends Schema.Class("ExecutionResult")({
|
|
17
|
-
status: Schema.
|
|
29
|
+
status: Schema.Literals([
|
|
30
|
+
200,
|
|
31
|
+
206,
|
|
32
|
+
400,
|
|
33
|
+
500
|
|
34
|
+
]),
|
|
18
35
|
body: Schema.Unknown,
|
|
19
|
-
headers: Schema.Record(
|
|
20
|
-
key: Schema.String,
|
|
21
|
-
value: Schema.String
|
|
22
|
-
})
|
|
36
|
+
headers: Schema.Record(Schema.String, Schema.String)
|
|
23
37
|
}) {};
|
|
24
38
|
const isStepInterrupt = Schema.is(StepInterrupt);
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}));
|
|
39
|
+
const collectStepInterruptsFromCause = (cause) => {
|
|
40
|
+
const interrupts = [];
|
|
41
|
+
for (const reason of cause.reasons) if (Cause.isFailReason(reason) && isStepInterrupt(reason.error)) interrupts.push(reason.error);
|
|
42
|
+
return Chunk.fromIterable(interrupts);
|
|
43
|
+
};
|
|
31
44
|
const toUserError = (error) => UserError.make({
|
|
32
45
|
name: Predicate.hasProperty(error, "name") ? String(error.name) : "Error",
|
|
33
46
|
message: Predicate.hasProperty(error, "message") ? String(error.message) : String(error),
|
|
@@ -36,65 +49,164 @@ const toUserError = (error) => UserError.make({
|
|
|
36
49
|
const baseHeaders = () => ({
|
|
37
50
|
"Content-Type": "application/json",
|
|
38
51
|
[Headers$1.SDK]: `effect-inngest:v${SDK_VERSION}`,
|
|
39
|
-
[Headers$1.RequestVersion]: "
|
|
52
|
+
[Headers$1.RequestVersion]: "2"
|
|
40
53
|
});
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
const
|
|
54
|
+
const encodeOpcodes = (opcodes) => Schema.encodeSync(Schema.Array(GeneratorOpcode))(opcodes);
|
|
55
|
+
const execute = (fn, handler, request, appName, traceHeaders = {}, checkpointConfig = Option.none()) => Effect.gen(function* () {
|
|
56
|
+
const stepIdCounts = yield* Ref.make(HashMap.empty());
|
|
44
57
|
const headers = baseHeaders();
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
const checkpointState = yield* Option.match(checkpointConfig, {
|
|
59
|
+
onNone: () => Effect.succeed(Option.none()),
|
|
60
|
+
onSome: (config) => Effect.gen(function* () {
|
|
61
|
+
const client = yield* InngestClient;
|
|
62
|
+
const state = yield* make({
|
|
63
|
+
config,
|
|
64
|
+
runId: request.ctx.run_id,
|
|
65
|
+
fnId: request.ctx.fn_id,
|
|
66
|
+
qiId: request.ctx.qi_id,
|
|
67
|
+
checkpointAsync: (steps) => client.checkpointAsync({
|
|
68
|
+
runId: request.ctx.run_id,
|
|
69
|
+
fnId: request.ctx.fn_id,
|
|
70
|
+
qiId: request.ctx.qi_id,
|
|
71
|
+
steps
|
|
72
|
+
})
|
|
73
|
+
});
|
|
74
|
+
return Option.some(state);
|
|
75
|
+
})
|
|
76
|
+
});
|
|
77
|
+
const context = buildHandlerContext(fn, createStepTools(request, appName, stepIdCounts, checkpointState), request);
|
|
78
|
+
/**
|
|
79
|
+
* Drain any unflushed buffered opcodes (no-op outside checkpoint mode).
|
|
80
|
+
* Used at every terminal branch so step results are never lost.
|
|
81
|
+
*/
|
|
82
|
+
const drainBuffer = Option.match(checkpointState, {
|
|
83
|
+
onNone: () => Effect.succeed([]),
|
|
84
|
+
onSome: (state) => state.drain
|
|
85
|
+
});
|
|
86
|
+
const handlerWithRace = Option.match(checkpointState, {
|
|
87
|
+
onNone: () => Effect.map(handler(context), Option.some),
|
|
88
|
+
onSome: (state) => Effect.raceFirst(Effect.map(handler(context), Option.some), Effect.sleep(state.config.maxRuntime).pipe(Effect.tap(() => state.markRuntimeExceeded), Effect.as(Option.none())))
|
|
89
|
+
});
|
|
90
|
+
return yield* Effect.scoped(handlerWithRace).pipe(Effect.flatMap((raced) => Effect.gen(function* () {
|
|
91
|
+
const drained = yield* drainBuffer;
|
|
92
|
+
if (Option.isSome(checkpointState)) {
|
|
93
|
+
const terminal = (yield* checkpointState.value.isRuntimeExceeded) || Option.isNone(raced) ? discoveryRequest() : runComplete(raced.value);
|
|
94
|
+
const opcodes = [...drained, terminal];
|
|
95
|
+
return ExecutionResult.make({
|
|
96
|
+
status: 206,
|
|
97
|
+
body: encodeOpcodes(opcodes),
|
|
98
|
+
headers
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return ExecutionResult.make({
|
|
102
|
+
status: 200,
|
|
103
|
+
body: Option.getOrThrow(raced),
|
|
104
|
+
headers
|
|
105
|
+
});
|
|
106
|
+
})), Effect.catchCause((cause) => Effect.gen(function* () {
|
|
107
|
+
const drained = yield* drainBuffer;
|
|
108
|
+
const interrupts = collectStepInterruptsFromCause(cause);
|
|
51
109
|
if (!Chunk.isEmpty(interrupts)) {
|
|
52
110
|
const interruptArray = Chunk.toReadonlyArray(interrupts);
|
|
53
|
-
const opcodes = interruptArray.map((interrupt) => interrupt.opcode);
|
|
54
|
-
const hasNonRetriableError = opcodes.some((op) => op.op === Opcode.StepError && Predicate.
|
|
111
|
+
const opcodes = [...drained, ...interruptArray.map((interrupt) => interrupt.opcode)];
|
|
112
|
+
const hasNonRetriableError = opcodes.some((op) => op.op === Opcode.StepError && Predicate.isObject(op.error) && Predicate.hasProperty(op.error, "noRetry") && op.error.noRetry === true);
|
|
55
113
|
const retryAfterMs = interruptArray.find((i) => i.retryAfterMs !== void 0)?.retryAfterMs;
|
|
56
114
|
const responseHeaders = hasNonRetriableError ? {
|
|
57
115
|
...headers,
|
|
58
116
|
[Headers$1.NoRetry]: "true"
|
|
59
117
|
} : headers;
|
|
60
|
-
if (retryAfterMs !== void 0)
|
|
61
|
-
|
|
62
|
-
|
|
118
|
+
if (retryAfterMs !== void 0) {
|
|
119
|
+
responseHeaders[Headers$1.RetryAfter] = String(Math.ceil(retryAfterMs / 1e3));
|
|
120
|
+
responseHeaders[Headers$1.NoRetry] = "false";
|
|
121
|
+
}
|
|
122
|
+
return ExecutionResult.make({
|
|
63
123
|
status: 206,
|
|
64
|
-
body:
|
|
124
|
+
body: encodeOpcodes(opcodes),
|
|
65
125
|
headers: responseHeaders
|
|
66
|
-
})
|
|
126
|
+
});
|
|
67
127
|
}
|
|
128
|
+
const errorOpt = Option.orElse(Cause.findErrorOption(cause), () => {
|
|
129
|
+
const dieReason = cause.reasons.find(Cause.isDieReason);
|
|
130
|
+
return dieReason ? Option.some(dieReason.defect) : Option.none();
|
|
131
|
+
});
|
|
132
|
+
if (Option.isNone(errorOpt)) {
|
|
133
|
+
if (drained.length > 0) return ExecutionResult.make({
|
|
134
|
+
status: 206,
|
|
135
|
+
body: encodeOpcodes(drained),
|
|
136
|
+
headers
|
|
137
|
+
});
|
|
138
|
+
return ExecutionResult.make({
|
|
139
|
+
status: 500,
|
|
140
|
+
body: {
|
|
141
|
+
name: "Error",
|
|
142
|
+
message: "Unknown error"
|
|
143
|
+
},
|
|
144
|
+
headers: {
|
|
145
|
+
...headers,
|
|
146
|
+
[Headers$1.NoRetry]: "false"
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
const error = errorOpt.value;
|
|
68
151
|
if (isRetryAfterError(error)) {
|
|
69
152
|
const retryAfterMs = Duration.toMillis(error.retryAfter);
|
|
70
153
|
const retryAfterSeconds = Math.ceil(retryAfterMs / 1e3);
|
|
71
|
-
return
|
|
154
|
+
if (drained.length > 0) return ExecutionResult.make({
|
|
155
|
+
status: 206,
|
|
156
|
+
body: encodeOpcodes(drained),
|
|
157
|
+
headers: {
|
|
158
|
+
...headers,
|
|
159
|
+
[Headers$1.NoRetry]: "false",
|
|
160
|
+
[Headers$1.RetryAfter]: String(retryAfterSeconds)
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
return ExecutionResult.make({
|
|
72
164
|
status: 500,
|
|
73
|
-
body:
|
|
165
|
+
body: toUserError(error),
|
|
74
166
|
headers: {
|
|
75
167
|
...headers,
|
|
76
168
|
[Headers$1.NoRetry]: "false",
|
|
77
169
|
[Headers$1.RetryAfter]: String(retryAfterSeconds)
|
|
78
170
|
}
|
|
79
|
-
})
|
|
171
|
+
});
|
|
80
172
|
}
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
173
|
+
const noRetryValue = isNonRetriableError(error) || isStepError(error) && error.noRetry === true;
|
|
174
|
+
const noRetry = noRetryValue ? "true" : "false";
|
|
175
|
+
if (drained.length > 0) return ExecutionResult.make({
|
|
176
|
+
status: 206,
|
|
177
|
+
body: encodeOpcodes(drained),
|
|
85
178
|
headers: {
|
|
86
179
|
...headers,
|
|
87
180
|
[Headers$1.NoRetry]: noRetry
|
|
88
181
|
}
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
182
|
+
});
|
|
183
|
+
return ExecutionResult.make({
|
|
184
|
+
status: noRetryValue ? 400 : 500,
|
|
185
|
+
body: toUserError(error),
|
|
186
|
+
headers: {
|
|
187
|
+
...headers,
|
|
188
|
+
[Headers$1.NoRetry]: noRetry
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
})), Effect.catchDefect((defect) => Effect.gen(function* () {
|
|
192
|
+
const drained = yield* drainBuffer;
|
|
193
|
+
if (drained.length > 0) return ExecutionResult.make({
|
|
194
|
+
status: 206,
|
|
195
|
+
body: encodeOpcodes(drained),
|
|
196
|
+
headers: {
|
|
197
|
+
...headers,
|
|
198
|
+
[Headers$1.NoRetry]: "false"
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
return ExecutionResult.make({
|
|
202
|
+
status: 500,
|
|
203
|
+
body: toUserError(defect),
|
|
204
|
+
headers: {
|
|
205
|
+
...headers,
|
|
206
|
+
[Headers$1.NoRetry]: "false"
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
})));
|
|
98
210
|
}).pipe((base) => {
|
|
99
211
|
const headers = {};
|
|
100
212
|
if (traceHeaders.traceparent) headers["traceparent"] = traceHeaders.traceparent;
|
|
@@ -112,6 +224,6 @@ const execute = (fn, handler, request, appName, traceHeaders = {}) => Effect.gen
|
|
|
112
224
|
[OtelAttributes.Attempt]: request.ctx.attempt
|
|
113
225
|
}
|
|
114
226
|
}));
|
|
115
|
-
|
|
227
|
+
Context.Service()("effect-inngest/Driver");
|
|
116
228
|
//#endregion
|
|
117
|
-
export { execute };
|
|
229
|
+
export { execute };
|
|
@@ -1,34 +1,29 @@
|
|
|
1
1
|
import * as Schema from "effect/Schema";
|
|
2
|
+
import * as _$effect_Cause0 from "effect/Cause";
|
|
2
3
|
|
|
3
4
|
//#region src/internal/errors.d.ts
|
|
4
|
-
declare const SendEventError_base: Schema.
|
|
5
|
-
readonly
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
events: Schema.Array$<typeof Schema.String>;
|
|
9
|
-
}>;
|
|
5
|
+
declare const SendEventError_base: Schema.Class<SendEventError, Schema.TaggedStruct<"SendEventError", {
|
|
6
|
+
readonly message: Schema.String;
|
|
7
|
+
readonly events: Schema.$Array<Schema.String>;
|
|
8
|
+
}>, _$effect_Cause0.YieldableError>;
|
|
10
9
|
/**
|
|
11
10
|
* @internal
|
|
12
11
|
*/
|
|
13
12
|
declare class SendEventError extends SendEventError_base {}
|
|
14
|
-
declare const StepError_base: Schema.
|
|
15
|
-
readonly
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
noRetry: Schema.optional<typeof Schema.Boolean>;
|
|
21
|
-
}>;
|
|
13
|
+
declare const StepError_base: Schema.Class<StepError, Schema.TaggedStruct<"StepError", {
|
|
14
|
+
readonly message: Schema.String;
|
|
15
|
+
readonly stepId: Schema.String;
|
|
16
|
+
readonly cause: Schema.optional<Schema.Unknown>;
|
|
17
|
+
readonly noRetry: Schema.optional<Schema.Boolean>;
|
|
18
|
+
}>, _$effect_Cause0.YieldableError>;
|
|
22
19
|
/**
|
|
23
20
|
* @internal
|
|
24
21
|
*/
|
|
25
22
|
declare class StepError extends StepError_base {}
|
|
26
|
-
declare const NonRetriableError_base: Schema.
|
|
27
|
-
readonly
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
cause: Schema.optional<typeof Schema.Unknown>;
|
|
31
|
-
}>;
|
|
23
|
+
declare const NonRetriableError_base: Schema.Class<NonRetriableError, Schema.TaggedStruct<"NonRetriableError", {
|
|
24
|
+
readonly message: Schema.String;
|
|
25
|
+
readonly cause: Schema.optional<Schema.Unknown>;
|
|
26
|
+
}>, _$effect_Cause0.YieldableError>;
|
|
32
27
|
/**
|
|
33
28
|
* Thrown to indicate that the error should not be retried.
|
|
34
29
|
* Use this when you know retrying won't help (e.g., validation errors, auth failures).
|
|
@@ -37,13 +32,11 @@ declare const NonRetriableError_base: Schema.TaggedErrorClass<NonRetriableError,
|
|
|
37
32
|
* @category errors
|
|
38
33
|
*/
|
|
39
34
|
declare class NonRetriableError extends NonRetriableError_base {}
|
|
40
|
-
declare const RetryAfterError_base: Schema.
|
|
41
|
-
readonly
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
cause: Schema.optional<typeof Schema.Unknown>;
|
|
46
|
-
}>;
|
|
35
|
+
declare const RetryAfterError_base: Schema.Class<RetryAfterError, Schema.TaggedStruct<"RetryAfterError", {
|
|
36
|
+
readonly message: Schema.String;
|
|
37
|
+
readonly retryAfter: Schema.DurationFromMillis;
|
|
38
|
+
readonly cause: Schema.optional<Schema.Unknown>;
|
|
39
|
+
}>, _$effect_Cause0.YieldableError>;
|
|
47
40
|
/**
|
|
48
41
|
* Thrown to indicate that the operation should be retried after a specific delay.
|
|
49
42
|
* Use this for rate limiting or when you know when a resource will become available.
|
package/dist/internal/errors.js
CHANGED
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
import * as Schema from "effect/Schema";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
import * as Predicate from "effect/Predicate";
|
|
3
|
+
Schema.TaggedErrorClass()("SignatureError", { message: Schema.String });
|
|
4
|
+
Schema.TaggedErrorClass()("RegistrationError", {
|
|
5
|
+
message: Schema.String,
|
|
6
|
+
functions: Schema.Array(Schema.String)
|
|
7
|
+
});
|
|
8
|
+
Schema.TaggedErrorClass()("FunctionNotFoundError", {
|
|
9
|
+
message: Schema.String,
|
|
10
|
+
functionId: Schema.String
|
|
11
|
+
});
|
|
8
12
|
/**
|
|
9
13
|
* @internal
|
|
10
14
|
*/
|
|
11
|
-
var SendEventError = class extends Schema.
|
|
15
|
+
var SendEventError = class extends Schema.TaggedErrorClass()("SendEventError", {
|
|
12
16
|
message: Schema.String,
|
|
13
17
|
events: Schema.Array(Schema.String)
|
|
14
18
|
}) {};
|
|
19
|
+
Schema.TaggedErrorClass()("UseApiFetchError", {
|
|
20
|
+
message: Schema.String,
|
|
21
|
+
endpoint: Schema.Literals(["batch", "actions"]),
|
|
22
|
+
runId: Schema.String,
|
|
23
|
+
statusCode: Schema.optional(Schema.Number)
|
|
24
|
+
});
|
|
15
25
|
/**
|
|
16
26
|
* @internal
|
|
17
27
|
*/
|
|
18
|
-
var StepError = class extends Schema.
|
|
28
|
+
var StepError = class extends Schema.TaggedErrorClass()("StepError", {
|
|
19
29
|
message: Schema.String,
|
|
20
30
|
stepId: Schema.String,
|
|
21
31
|
cause: Schema.optional(Schema.Unknown),
|
|
@@ -24,7 +34,12 @@ var StepError = class extends Schema.TaggedError()("StepError", {
|
|
|
24
34
|
/**
|
|
25
35
|
* @internal
|
|
26
36
|
*/
|
|
27
|
-
const isStepError =
|
|
37
|
+
const isStepError = Predicate.isTagged("StepError");
|
|
38
|
+
Schema.TaggedErrorClass()("TimeoutError", {
|
|
39
|
+
message: Schema.String,
|
|
40
|
+
stepId: Schema.optional(Schema.String),
|
|
41
|
+
timeout: Schema.DurationFromMillis
|
|
42
|
+
});
|
|
28
43
|
/**
|
|
29
44
|
* Thrown to indicate that the error should not be retried.
|
|
30
45
|
* Use this when you know retrying won't help (e.g., validation errors, auth failures).
|
|
@@ -32,14 +47,14 @@ const isStepError = Schema.is(StepError);
|
|
|
32
47
|
* @since 0.1.0
|
|
33
48
|
* @category errors
|
|
34
49
|
*/
|
|
35
|
-
var NonRetriableError = class extends Schema.
|
|
50
|
+
var NonRetriableError = class extends Schema.TaggedErrorClass()("NonRetriableError", {
|
|
36
51
|
message: Schema.String,
|
|
37
52
|
cause: Schema.optional(Schema.Unknown)
|
|
38
53
|
}) {};
|
|
39
54
|
/**
|
|
40
55
|
* @internal
|
|
41
56
|
*/
|
|
42
|
-
const isNonRetriableError =
|
|
57
|
+
const isNonRetriableError = Predicate.isTagged("NonRetriableError");
|
|
43
58
|
/**
|
|
44
59
|
* Thrown to indicate that the operation should be retried after a specific delay.
|
|
45
60
|
* Use this for rate limiting or when you know when a resource will become available.
|
|
@@ -47,7 +62,7 @@ const isNonRetriableError = Schema.is(NonRetriableError);
|
|
|
47
62
|
* @since 0.1.0
|
|
48
63
|
* @category errors
|
|
49
64
|
*/
|
|
50
|
-
var RetryAfterError = class extends Schema.
|
|
65
|
+
var RetryAfterError = class extends Schema.TaggedErrorClass()("RetryAfterError", {
|
|
51
66
|
message: Schema.String,
|
|
52
67
|
retryAfter: Schema.DurationFromMillis,
|
|
53
68
|
cause: Schema.optional(Schema.Unknown)
|
|
@@ -55,7 +70,6 @@ var RetryAfterError = class extends Schema.TaggedError()("RetryAfterError", {
|
|
|
55
70
|
/**
|
|
56
71
|
* @internal
|
|
57
72
|
*/
|
|
58
|
-
const isRetryAfterError =
|
|
59
|
-
|
|
73
|
+
const isRetryAfterError = Predicate.isTagged("RetryAfterError");
|
|
60
74
|
//#endregion
|
|
61
|
-
export { NonRetriableError, RetryAfterError, SendEventError, StepError, isNonRetriableError, isRetryAfterError, isStepError };
|
|
75
|
+
export { NonRetriableError, RetryAfterError, SendEventError, StepError, isNonRetriableError, isRetryAfterError, isStepError };
|
|
@@ -1,20 +1,11 @@
|
|
|
1
|
-
import "../Client.js";
|
|
2
|
-
import "./protocol.js";
|
|
3
|
-
import "../Group.js";
|
|
4
1
|
import { SignatureError } from "./signature.js";
|
|
5
|
-
import "./driver.js";
|
|
6
|
-
import "@effect/platform/HttpClient";
|
|
7
|
-
import "@effect/platform/HttpServerRequest";
|
|
8
|
-
import "effect/Context";
|
|
9
|
-
import "effect/Effect";
|
|
10
2
|
import * as Schema from "effect/Schema";
|
|
3
|
+
import * as Cause from "effect/Cause";
|
|
11
4
|
|
|
12
5
|
//#region src/internal/handler.d.ts
|
|
13
|
-
declare const InvalidRequestError_base: Schema.
|
|
14
|
-
readonly
|
|
15
|
-
}
|
|
16
|
-
message: typeof Schema.String;
|
|
17
|
-
}>;
|
|
6
|
+
declare const InvalidRequestError_base: Schema.Class<InvalidRequestError, Schema.TaggedStruct<"InvalidRequestError", {
|
|
7
|
+
readonly message: Schema.String;
|
|
8
|
+
}>, Cause.YieldableError>;
|
|
18
9
|
declare class InvalidRequestError extends InvalidRequestError_base {}
|
|
19
10
|
//#endregion
|
|
20
11
|
export { InvalidRequestError };
|
package/dist/internal/handler.js
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveConfig } from "./checkpoint.js";
|
|
2
|
+
import { Headers as Headers$1, RegisterServerResponse, SDKRequestBody, SDKRequestContext, UserError } from "./protocol.js";
|
|
3
|
+
import { Signature } from "./signature.js";
|
|
2
4
|
import { InngestClient } from "../Client.js";
|
|
3
|
-
import { Signature, SignatureError } from "./signature.js";
|
|
4
5
|
import { execute } from "./driver.js";
|
|
5
|
-
import * as HttpClient from "@effect/platform/HttpClient";
|
|
6
|
-
import "@effect/platform/HttpServerRequest";
|
|
7
|
-
import "effect/Context";
|
|
8
6
|
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Option from "effect/Option";
|
|
9
8
|
import * as Schema from "effect/Schema";
|
|
10
|
-
import * as
|
|
11
|
-
import
|
|
9
|
+
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
10
|
+
import "effect/unstable/http/HttpServerRequest";
|
|
11
|
+
import "effect/Context";
|
|
12
|
+
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
|
|
13
|
+
import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse";
|
|
12
14
|
import * as Predicate from "effect/Predicate";
|
|
13
|
-
|
|
15
|
+
import * as Headers from "effect/unstable/http/Headers";
|
|
16
|
+
import * as Cause from "effect/Cause";
|
|
14
17
|
//#region src/internal/handler.ts
|
|
15
18
|
/**
|
|
16
19
|
* Internal handler implementation.
|
|
17
20
|
* @internal
|
|
18
21
|
*/
|
|
19
|
-
var InvalidRequestError = class extends Schema.
|
|
22
|
+
var InvalidRequestError = class extends Schema.TaggedErrorClass()("InvalidRequestError", { message: Schema.String }) {};
|
|
20
23
|
const SDK_VERSION = "2.0.0";
|
|
21
24
|
const baseHeaders = () => ({
|
|
22
25
|
"Content-Type": "application/json",
|
|
23
|
-
[Headers.SDK]: `effect-inngest:v${SDK_VERSION}`,
|
|
24
|
-
[Headers.RequestVersion]: "
|
|
26
|
+
[Headers$1.SDK]: `effect-inngest:v${SDK_VERSION}`,
|
|
27
|
+
[Headers$1.RequestVersion]: "2"
|
|
25
28
|
});
|
|
26
29
|
const buildServeUrl = (requestUrl, serveHost, servePath) => {
|
|
27
30
|
const url = new URL(requestUrl);
|
|
@@ -29,22 +32,24 @@ const buildServeUrl = (requestUrl, serveHost, servePath) => {
|
|
|
29
32
|
if (serveHost) return new URL(url.pathname + url.search, serveHost);
|
|
30
33
|
return url;
|
|
31
34
|
};
|
|
32
|
-
const verifyAndParseRequestBody =
|
|
35
|
+
const verifyAndParseRequestBody = Effect.fn("effect-inngest/handler/verifyAndParseRequestBody")(function* (request) {
|
|
33
36
|
const client = yield* InngestClient;
|
|
34
37
|
const sig = yield* Signature;
|
|
35
38
|
const config = client.config;
|
|
36
39
|
const isDev = client.mode === "dev";
|
|
37
|
-
const bodyText = yield* request.text.pipe(Effect.mapError((error) =>
|
|
40
|
+
const bodyText = yield* request.text.pipe(Effect.mapError((error) => {
|
|
41
|
+
return new InvalidRequestError({ message: `Failed to read request body: ${Predicate.hasProperty(error, "message") && typeof error.message === "string" ? error.message : "unknown"}` });
|
|
42
|
+
}));
|
|
38
43
|
yield* sig.verify({
|
|
39
44
|
body: new TextEncoder().encode(bodyText),
|
|
40
|
-
signatureHeader: request.headers
|
|
45
|
+
signatureHeader: Option.getOrUndefined(Headers.get(request.headers, Headers$1.Signature)),
|
|
41
46
|
signingKey: config.signingKey,
|
|
42
47
|
signingKeyFallback: config.signingKeyFallback,
|
|
43
48
|
isDev
|
|
44
49
|
});
|
|
45
|
-
return yield* Schema.
|
|
50
|
+
return yield* Schema.decodeUnknownEffect(Schema.fromJsonString(SDKRequestBody))(bodyText).pipe(Effect.mapError((error) => new InvalidRequestError({ message: `Invalid request body: ${String(error)}` })));
|
|
46
51
|
});
|
|
47
|
-
const handleIntrospection =
|
|
52
|
+
const handleIntrospection = Effect.fn("effect-inngest/handler/handleIntrospection")(function* (group, _requestUrl) {
|
|
48
53
|
const client = yield* InngestClient;
|
|
49
54
|
const config = client.config;
|
|
50
55
|
const body = {
|
|
@@ -62,16 +67,7 @@ const handleIntrospection = (group, _requestUrl) => Effect.gen(function* () {
|
|
|
62
67
|
body
|
|
63
68
|
};
|
|
64
69
|
});
|
|
65
|
-
const
|
|
66
|
-
url: Schema.String,
|
|
67
|
-
v: Schema.String,
|
|
68
|
-
deployType: Schema.Literal("ping"),
|
|
69
|
-
sdk: Schema.String,
|
|
70
|
-
appName: Schema.String,
|
|
71
|
-
framework: Schema.String,
|
|
72
|
-
functions: Schema.Array(Schema.Unknown)
|
|
73
|
-
});
|
|
74
|
-
const handleRegistration = (group, requestUrl) => Effect.gen(function* () {
|
|
70
|
+
const handleRegistration = Effect.fn("effect-inngest/handler/handleRegistration")(function* (group, requestUrl) {
|
|
75
71
|
const client = yield* InngestClient;
|
|
76
72
|
const httpClient = yield* HttpClient.HttpClient;
|
|
77
73
|
const config = client.config;
|
|
@@ -81,10 +77,10 @@ const handleRegistration = (group, requestUrl) => Effect.gen(function* () {
|
|
|
81
77
|
url: url.href
|
|
82
78
|
}));
|
|
83
79
|
const registerUrl = new URL("fn/register", client.apiBaseUrl).toString();
|
|
84
|
-
const
|
|
80
|
+
const request = HttpClientRequest.post(registerUrl).pipe(HttpClientRequest.setHeaders({
|
|
85
81
|
...baseHeaders(),
|
|
86
82
|
Authorization: `Bearer ${config.signingKey ?? ""}`
|
|
87
|
-
}), HttpClientRequest.
|
|
83
|
+
}), HttpClientRequest.bodyJsonUnsafe({
|
|
88
84
|
url: url.href,
|
|
89
85
|
v: "0.1",
|
|
90
86
|
deployType: "ping",
|
|
@@ -92,31 +88,58 @@ const handleRegistration = (group, requestUrl) => Effect.gen(function* () {
|
|
|
92
88
|
appName: config.id,
|
|
93
89
|
framework: "effect",
|
|
94
90
|
functions
|
|
95
|
-
}))
|
|
96
|
-
return {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
91
|
+
}));
|
|
92
|
+
return yield* Effect.gen(function* () {
|
|
93
|
+
const response = yield* httpClient.execute(request).pipe(Effect.scoped);
|
|
94
|
+
const responseBody = yield* HttpClientResponse.schemaBodyJson(RegisterServerResponse)(response).pipe(Effect.catch(() => Effect.succeed({ error: "Unknown registration response" })));
|
|
95
|
+
if (response.status !== 200 || !Predicate.hasProperty(responseBody, "ok")) return {
|
|
96
|
+
status: 500,
|
|
97
|
+
headers: baseHeaders(),
|
|
98
|
+
body: {
|
|
99
|
+
message: Predicate.hasProperty(responseBody, "error") && responseBody.error ? responseBody.error : `Registration failed with status ${response.status}`,
|
|
100
|
+
modified: false
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
return {
|
|
104
|
+
status: 200,
|
|
105
|
+
headers: baseHeaders(),
|
|
106
|
+
body: {
|
|
107
|
+
message: "Successfully synced.",
|
|
108
|
+
modified: responseBody.modified ?? false
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}).pipe(Effect.catchCause((cause) => {
|
|
112
|
+
const errorOpt = Cause.findErrorOption(cause);
|
|
113
|
+
const dieReason = cause.reasons.find(Cause.isDieReason);
|
|
114
|
+
const message = Option.isSome(errorOpt) ? Predicate.hasProperty(errorOpt.value, "message") && typeof errorOpt.value.message === "string" ? errorOpt.value.message : "Registration failed" : dieReason ? Predicate.hasProperty(dieReason.defect, "message") && typeof dieReason.defect.message === "string" ? dieReason.defect.message : "Registration failed" : "Registration failed";
|
|
115
|
+
return Effect.succeed({
|
|
116
|
+
status: 500,
|
|
117
|
+
headers: baseHeaders(),
|
|
118
|
+
body: {
|
|
119
|
+
message,
|
|
120
|
+
modified: false
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}));
|
|
101
124
|
});
|
|
102
|
-
const handleExecution = (group, fnId, urlStepId, body, traceHeaders = {})
|
|
125
|
+
const handleExecution = Effect.fn("effect-inngest/handler/handleExecution")(function* (group, fnId, urlStepId, body, traceHeaders = {}) {
|
|
103
126
|
const client = yield* InngestClient;
|
|
104
127
|
const context = yield* Effect.context();
|
|
105
128
|
const appId = client.config.id;
|
|
106
129
|
const prefix = `${appId}-`;
|
|
107
130
|
const fnTag = fnId.startsWith(prefix) ? fnId.slice(prefix.length) : fnId;
|
|
108
131
|
const fn = group.functions.get(fnTag);
|
|
109
|
-
const entry = fn ? context.
|
|
132
|
+
const entry = fn ? context.mapUnsafe.get(fn.key) : void 0;
|
|
110
133
|
if (!fn || !entry) return {
|
|
111
|
-
status:
|
|
134
|
+
status: 500,
|
|
112
135
|
headers: {
|
|
113
136
|
...baseHeaders(),
|
|
114
|
-
[Headers.NoRetry]: "
|
|
137
|
+
[Headers$1.NoRetry]: "false"
|
|
115
138
|
},
|
|
116
|
-
body:
|
|
139
|
+
body: UserError.make({
|
|
117
140
|
name: "FunctionNotFoundError",
|
|
118
141
|
message: `Unknown function: ${fnId}`
|
|
119
|
-
})
|
|
142
|
+
})
|
|
120
143
|
};
|
|
121
144
|
const effectiveBody = urlStepId && urlStepId !== body.ctx.step_id ? SDKRequestBody.make({
|
|
122
145
|
event: body.event,
|
|
@@ -125,21 +148,25 @@ const handleExecution = (group, fnId, urlStepId, body, traceHeaders = {}) => Eff
|
|
|
125
148
|
ctx: SDKRequestContext.make({
|
|
126
149
|
fn_id: body.ctx.fn_id,
|
|
127
150
|
run_id: body.ctx.run_id,
|
|
151
|
+
env: body.ctx.env,
|
|
128
152
|
step_id: urlStepId,
|
|
129
153
|
attempt: body.ctx.attempt,
|
|
154
|
+
max_attempts: body.ctx.max_attempts,
|
|
155
|
+
qi_id: body.ctx.qi_id,
|
|
130
156
|
disable_immediate_execution: body.ctx.disable_immediate_execution,
|
|
131
157
|
use_api: body.ctx.use_api,
|
|
132
158
|
stack: body.ctx.stack
|
|
133
159
|
}),
|
|
160
|
+
version: body.version,
|
|
134
161
|
use_api: body.use_api
|
|
135
162
|
}) : body;
|
|
136
|
-
const
|
|
163
|
+
const checkpointConfig = !urlStepId && effectiveBody.ctx.fn_id !== "" && !effectiveBody.ctx.disable_immediate_execution ? Option.fromNullishOr(resolveConfig(fn.options.checkpointing, client.config.checkpointing)) : Option.none();
|
|
164
|
+
const result = yield* Effect.provide(execute(fn, entry.handler, effectiveBody, appId, traceHeaders, checkpointConfig), entry.context);
|
|
137
165
|
return {
|
|
138
166
|
status: result.status,
|
|
139
167
|
headers: result.headers,
|
|
140
168
|
body: result.body
|
|
141
169
|
};
|
|
142
170
|
});
|
|
143
|
-
|
|
144
171
|
//#endregion
|
|
145
|
-
export { InvalidRequestError, handleExecution, handleIntrospection, handleRegistration, verifyAndParseRequestBody };
|
|
172
|
+
export { InvalidRequestError, handleExecution, handleIntrospection, handleRegistration, verifyAndParseRequestBody };
|