effect-inngest 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +457 -0
- package/dist/Client.d.ts +167 -0
- package/dist/Client.js +144 -0
- package/dist/Events.d.ts +110 -0
- package/dist/Events.js +93 -0
- package/dist/Function.d.ts +384 -0
- package/dist/Function.js +104 -0
- package/dist/Group.d.ts +152 -0
- package/dist/Group.js +164 -0
- package/dist/HttpApi.d.ts +75 -0
- package/dist/HttpApi.js +47 -0
- package/dist/_virtual/rolldown_runtime.js +18 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +8 -0
- package/dist/internal/constants.js +15 -0
- package/dist/internal/driver.d.ts +5 -0
- package/dist/internal/driver.js +117 -0
- package/dist/internal/errors.d.ts +56 -0
- package/dist/internal/errors.js +61 -0
- package/dist/internal/handler.d.ts +20 -0
- package/dist/internal/handler.js +145 -0
- package/dist/internal/helpers.js +44 -0
- package/dist/internal/interrupts.d.ts +2 -0
- package/dist/internal/interrupts.js +45 -0
- package/dist/internal/memo.js +56 -0
- package/dist/internal/protocol.d.ts +1 -0
- package/dist/internal/protocol.js +191 -0
- package/dist/internal/signature.d.ts +18 -0
- package/dist/internal/signature.js +97 -0
- package/dist/internal/step.d.ts +59 -0
- package/dist/internal/step.js +183 -0
- package/package.json +121 -0
- package/src/Client.ts +279 -0
- package/src/Events.ts +87 -0
- package/src/Function.ts +493 -0
- package/src/Group.ts +314 -0
- package/src/HttpApi.ts +82 -0
- package/src/index.ts +171 -0
- package/src/internal/constants.ts +11 -0
- package/src/internal/driver.ts +194 -0
- package/src/internal/errors.ts +130 -0
- package/src/internal/handler.ts +222 -0
- package/src/internal/helpers.ts +58 -0
- package/src/internal/interrupts.ts +62 -0
- package/src/internal/memo.ts +73 -0
- package/src/internal/protocol.ts +218 -0
- package/src/internal/signature.ts +158 -0
- package/src/internal/step.ts +377 -0
package/dist/Group.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/rolldown_runtime.js";
|
|
2
|
+
import { Headers } from "./internal/protocol.js";
|
|
3
|
+
import { SignatureLive } from "./internal/signature.js";
|
|
4
|
+
import { handleExecution, handleIntrospection, handleRegistration, verifyAndParseRequestBody } from "./internal/handler.js";
|
|
5
|
+
import * as HttpApp from "@effect/platform/HttpApp";
|
|
6
|
+
import "@effect/platform/HttpClient";
|
|
7
|
+
import * as HttpServerRequest from "@effect/platform/HttpServerRequest";
|
|
8
|
+
import * as HttpServerResponse from "@effect/platform/HttpServerResponse";
|
|
9
|
+
import * as UrlParams from "@effect/platform/UrlParams";
|
|
10
|
+
import * as Context from "effect/Context";
|
|
11
|
+
import * as Effect from "effect/Effect";
|
|
12
|
+
import * as Layer from "effect/Layer";
|
|
13
|
+
import * as Option from "effect/Option";
|
|
14
|
+
import * as Schema from "effect/Schema";
|
|
15
|
+
|
|
16
|
+
//#region src/Group.ts
|
|
17
|
+
/**
|
|
18
|
+
* @since 0.1.0
|
|
19
|
+
*/
|
|
20
|
+
var Group_exports = /* @__PURE__ */ __exportAll({
|
|
21
|
+
TypeId: () => TypeId,
|
|
22
|
+
make: () => make,
|
|
23
|
+
toHttpApp: () => toHttpApp,
|
|
24
|
+
toWebHandler: () => toWebHandler
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* @since 0.1.0
|
|
28
|
+
* @category type ids
|
|
29
|
+
*/
|
|
30
|
+
const TypeId = Symbol.for("effect-inngest/Group");
|
|
31
|
+
const Proto = {
|
|
32
|
+
[TypeId]: TypeId,
|
|
33
|
+
toLayer(handlers) {
|
|
34
|
+
const functions = this.functions;
|
|
35
|
+
return Layer.effectContext(Effect.gen(function* () {
|
|
36
|
+
const context = yield* Effect.context();
|
|
37
|
+
const contextMap = /* @__PURE__ */ new Map();
|
|
38
|
+
for (const [tag, handler] of Object.entries(handlers)) {
|
|
39
|
+
const fn = functions.get(tag);
|
|
40
|
+
contextMap.set(fn.key, {
|
|
41
|
+
handler,
|
|
42
|
+
context
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return Context.unsafeMake(contextMap);
|
|
46
|
+
}));
|
|
47
|
+
},
|
|
48
|
+
toLayerHandler(tag, handler) {
|
|
49
|
+
const fn = this.functions.get(tag);
|
|
50
|
+
return Layer.effectContext(Effect.gen(function* () {
|
|
51
|
+
const context = yield* Effect.context();
|
|
52
|
+
const contextMap = /* @__PURE__ */ new Map();
|
|
53
|
+
contextMap.set(fn.key, {
|
|
54
|
+
handler,
|
|
55
|
+
context
|
|
56
|
+
});
|
|
57
|
+
return Context.unsafeMake(contextMap);
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* @since 0.1.0
|
|
63
|
+
* @category constructors
|
|
64
|
+
*/
|
|
65
|
+
const make = (...fns) => {
|
|
66
|
+
const functions = new Map(fns.map((fn) => [fn._tag, fn]));
|
|
67
|
+
const group = Object.create(Proto);
|
|
68
|
+
group.functions = functions;
|
|
69
|
+
return group;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Build an HttpApp from an InngestGroup.
|
|
73
|
+
*
|
|
74
|
+
* @since 0.1.0
|
|
75
|
+
* @category http
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* import { Effect, Layer } from "effect"
|
|
79
|
+
* import { HttpServer } from "@effect/platform"
|
|
80
|
+
* import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"
|
|
81
|
+
* import { InngestGroup, InngestClient } from "effect-inngest"
|
|
82
|
+
*
|
|
83
|
+
* InngestGroup.toHttpApp(MyGroup).pipe(
|
|
84
|
+
* Effect.flatMap((app) => HttpServer.serve(app)),
|
|
85
|
+
* Effect.provide(InngestClient.layer({ id: "my-app" })),
|
|
86
|
+
* Effect.provide(NodeHttpServer.layer({ port: 3000 })),
|
|
87
|
+
* NodeRuntime.runMain,
|
|
88
|
+
* )
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
const toHttpApp = (group) => Effect.gen(function* () {
|
|
92
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
93
|
+
const method = request.method;
|
|
94
|
+
const requestUrl = Option.match(HttpServerRequest.toURL(request), {
|
|
95
|
+
onNone: () => request.url,
|
|
96
|
+
onSome: (url) => url.toString()
|
|
97
|
+
});
|
|
98
|
+
if (method === "GET") {
|
|
99
|
+
const result = yield* handleIntrospection(group, requestUrl);
|
|
100
|
+
return yield* HttpServerResponse.json(result.body, {
|
|
101
|
+
status: result.status,
|
|
102
|
+
headers: result.headers
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (method === "PUT") {
|
|
106
|
+
const result = yield* handleRegistration(group, requestUrl);
|
|
107
|
+
return yield* HttpServerResponse.json(result.body, {
|
|
108
|
+
status: result.status,
|
|
109
|
+
headers: result.headers
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (method === "POST") {
|
|
113
|
+
const url = Option.getOrThrow(HttpServerRequest.toURL(request));
|
|
114
|
+
const ExecuteParams = Schema.Struct({
|
|
115
|
+
fnId: Schema.String,
|
|
116
|
+
stepId: Schema.optional(Schema.String)
|
|
117
|
+
});
|
|
118
|
+
const params = yield* UrlParams.schemaStruct(ExecuteParams)(UrlParams.fromInput(url.searchParams)).pipe(Effect.catchAll(() => Effect.fail(HttpServerResponse.unsafeJson({ error: "Missing or invalid fnId query parameter" }, {
|
|
119
|
+
status: 400,
|
|
120
|
+
headers: { [Headers.NoRetry]: "true" }
|
|
121
|
+
}))));
|
|
122
|
+
const body = yield* verifyAndParseRequestBody(request).pipe(Effect.provide(SignatureLive), Effect.catchAll((error) => Effect.fail(HttpServerResponse.unsafeJson({ error: error.message }, {
|
|
123
|
+
status: error._tag === "SignatureError" ? 401 : 400,
|
|
124
|
+
headers: { [Headers.NoRetry]: "true" }
|
|
125
|
+
}))));
|
|
126
|
+
const result = yield* handleExecution(group, params.fnId, params.stepId, body);
|
|
127
|
+
return yield* HttpServerResponse.json(result.body, {
|
|
128
|
+
status: result.status,
|
|
129
|
+
headers: result.headers
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return yield* HttpServerResponse.json({ error: `Method ${method} not allowed` }, { status: 405 });
|
|
133
|
+
}).pipe(Effect.catchAllCause((cause) => HttpServerResponse.json({
|
|
134
|
+
error: "Internal server error",
|
|
135
|
+
cause: String(cause)
|
|
136
|
+
}, { status: 500 }).pipe(Effect.orDie)));
|
|
137
|
+
/**
|
|
138
|
+
* Create a standalone web handler from an InngestGroup.
|
|
139
|
+
*
|
|
140
|
+
* @since 0.1.0
|
|
141
|
+
* @category http
|
|
142
|
+
* @example
|
|
143
|
+
* ```ts
|
|
144
|
+
* import { InngestGroup, InngestClient } from "effect-inngest"
|
|
145
|
+
* import { HttpClient } from "@effect/platform"
|
|
146
|
+
* import { FetchHttpClient } from "@effect/platform"
|
|
147
|
+
*
|
|
148
|
+
* const { handler, dispose } = InngestGroup.toWebHandler(MyGroup, {
|
|
149
|
+
* layer: InngestClient.layer({ id: "my-app" }).pipe(
|
|
150
|
+
* Layer.provide(FetchHttpClient.layer),
|
|
151
|
+
* ),
|
|
152
|
+
* })
|
|
153
|
+
*
|
|
154
|
+
* // Use with any web framework
|
|
155
|
+
* Bun.serve({ fetch: handler, port: 3000 })
|
|
156
|
+
*
|
|
157
|
+
* // Call dispose() on shutdown
|
|
158
|
+
* process.on("SIGTERM", dispose)
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
const toWebHandler = (group, options) => HttpApp.toWebHandlerLayer(toHttpApp(group), Layer.mergeAll(options.layer, Layer.scope), { memoMap: options.memoMap });
|
|
162
|
+
|
|
163
|
+
//#endregion
|
|
164
|
+
export { Group_exports, TypeId, make, toHttpApp, toWebHandler };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { InngestClient } from "./Client.js";
|
|
2
|
+
import { InngestGroup } from "./Group.js";
|
|
3
|
+
import { SignatureError } from "./internal/signature.js";
|
|
4
|
+
import { InvalidRequestError } from "./internal/handler.js";
|
|
5
|
+
import * as HttpClient from "@effect/platform/HttpClient";
|
|
6
|
+
import * as Layer from "effect/Layer";
|
|
7
|
+
import * as Schema from "effect/Schema";
|
|
8
|
+
import * as HttpApi from "@effect/platform/HttpApi";
|
|
9
|
+
import * as HttpApiEndpoint from "@effect/platform/HttpApiEndpoint";
|
|
10
|
+
import * as HttpApiGroup from "@effect/platform/HttpApiGroup";
|
|
11
|
+
|
|
12
|
+
//#region src/HttpApi.d.ts
|
|
13
|
+
declare namespace HttpApi_d_exports {
|
|
14
|
+
export { InngestApiGroup, layerGroup };
|
|
15
|
+
}
|
|
16
|
+
declare const FunctionNotFoundError_base: Schema.TaggedErrorClass<FunctionNotFoundError, "FunctionNotFoundError", {
|
|
17
|
+
readonly _tag: Schema.tag<"FunctionNotFoundError">;
|
|
18
|
+
} & {
|
|
19
|
+
message: typeof Schema.String;
|
|
20
|
+
}>;
|
|
21
|
+
declare class FunctionNotFoundError extends FunctionNotFoundError_base {}
|
|
22
|
+
/**
|
|
23
|
+
* @since 0.1.0
|
|
24
|
+
* @category api
|
|
25
|
+
*/
|
|
26
|
+
declare const InngestApiGroup: HttpApiGroup.HttpApiGroup<"inngest", HttpApiEndpoint.HttpApiEndpoint<"introspect", "GET", never, never, never, never, ({
|
|
27
|
+
readonly mode: "cloud" | "dev";
|
|
28
|
+
readonly function_count: number;
|
|
29
|
+
readonly has_event_key: boolean;
|
|
30
|
+
readonly has_signing_key: boolean;
|
|
31
|
+
readonly has_signing_key_fallback: boolean;
|
|
32
|
+
readonly schema_version: "2024-05-24";
|
|
33
|
+
readonly extra?: {
|
|
34
|
+
readonly [x: string]: unknown;
|
|
35
|
+
} | undefined;
|
|
36
|
+
} & {
|
|
37
|
+
readonly env: string | null;
|
|
38
|
+
readonly authentication_succeeded: true;
|
|
39
|
+
readonly api_origin: string;
|
|
40
|
+
readonly app_id: string;
|
|
41
|
+
readonly event_api_origin: string;
|
|
42
|
+
readonly event_key_hash: string | null;
|
|
43
|
+
readonly framework: string;
|
|
44
|
+
readonly sdk_language: string;
|
|
45
|
+
readonly sdk_version: string;
|
|
46
|
+
readonly serve_origin: string | null;
|
|
47
|
+
readonly serve_path: string | null;
|
|
48
|
+
readonly signing_key_fallback_hash: string | null;
|
|
49
|
+
readonly signing_key_hash: string | null;
|
|
50
|
+
}) | ({
|
|
51
|
+
readonly mode: "cloud" | "dev";
|
|
52
|
+
readonly function_count: number;
|
|
53
|
+
readonly has_event_key: boolean;
|
|
54
|
+
readonly has_signing_key: boolean;
|
|
55
|
+
readonly has_signing_key_fallback: boolean;
|
|
56
|
+
readonly schema_version: "2024-05-24";
|
|
57
|
+
readonly extra?: {
|
|
58
|
+
readonly [x: string]: unknown;
|
|
59
|
+
} | undefined;
|
|
60
|
+
} & {
|
|
61
|
+
readonly authentication_succeeded: false | null;
|
|
62
|
+
readonly functions?: readonly unknown[] | undefined;
|
|
63
|
+
}), never, never, never> | HttpApiEndpoint.HttpApiEndpoint<"register", "PUT", never, never, never, never, {
|
|
64
|
+
readonly message?: string | undefined;
|
|
65
|
+
}, never, never, never> | HttpApiEndpoint.HttpApiEndpoint<"execute", "POST", never, {
|
|
66
|
+
readonly fnId: string;
|
|
67
|
+
readonly stepId?: string | undefined;
|
|
68
|
+
}, never, never, unknown, never, never, never>, FunctionNotFoundError | InvalidRequestError | SignatureError, never, false>;
|
|
69
|
+
/**
|
|
70
|
+
* @since 0.1.0
|
|
71
|
+
* @category layers
|
|
72
|
+
*/
|
|
73
|
+
declare const layerGroup: <ApiId extends string, Groups extends HttpApiGroup.HttpApiGroup.Any, ApiError, ApiR>(api: HttpApi.HttpApi<ApiId, Groups, ApiError, ApiR>, group: InngestGroup.Any) => Layer.Layer<HttpApiGroup.ApiGroup<ApiId, "inngest">, never, InngestClient | HttpClient.HttpClient>;
|
|
74
|
+
//#endregion
|
|
75
|
+
export { HttpApi_d_exports, InngestApiGroup, layerGroup };
|
package/dist/HttpApi.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/rolldown_runtime.js";
|
|
2
|
+
import { IntrospectionResponse, RegisterResponse } from "./internal/protocol.js";
|
|
3
|
+
import { SignatureError, SignatureLive } from "./internal/signature.js";
|
|
4
|
+
import { InvalidRequestError, handleExecution, handleIntrospection, handleRegistration, verifyAndParseRequestBody } from "./internal/handler.js";
|
|
5
|
+
import "@effect/platform/HttpClient";
|
|
6
|
+
import * as HttpServerRequest from "@effect/platform/HttpServerRequest";
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import "effect/Layer";
|
|
9
|
+
import * as Option from "effect/Option";
|
|
10
|
+
import * as Schema from "effect/Schema";
|
|
11
|
+
import "@effect/platform/HttpApi";
|
|
12
|
+
import * as HttpApiBuilder from "@effect/platform/HttpApiBuilder";
|
|
13
|
+
import * as HttpApiEndpoint from "@effect/platform/HttpApiEndpoint";
|
|
14
|
+
import * as HttpApiGroup from "@effect/platform/HttpApiGroup";
|
|
15
|
+
|
|
16
|
+
//#region src/HttpApi.ts
|
|
17
|
+
var HttpApi_exports = /* @__PURE__ */ __exportAll({
|
|
18
|
+
InngestApiGroup: () => InngestApiGroup,
|
|
19
|
+
layerGroup: () => layerGroup
|
|
20
|
+
});
|
|
21
|
+
const ExecuteParams = Schema.Struct({
|
|
22
|
+
fnId: Schema.String,
|
|
23
|
+
stepId: Schema.optional(Schema.String)
|
|
24
|
+
});
|
|
25
|
+
var FunctionNotFoundError = class extends Schema.TaggedError()("FunctionNotFoundError", { message: Schema.String }) {};
|
|
26
|
+
const IntrospectEndpoint = HttpApiEndpoint.get("introspect", "/").addSuccess(IntrospectionResponse);
|
|
27
|
+
const RegisterEndpoint = HttpApiEndpoint.put("register", "/").addSuccess(RegisterResponse);
|
|
28
|
+
const ExecuteEndpoint = HttpApiEndpoint.post("execute", "/").setUrlParams(ExecuteParams).addSuccess(Schema.Unknown);
|
|
29
|
+
/**
|
|
30
|
+
* @since 0.1.0
|
|
31
|
+
* @category api
|
|
32
|
+
*/
|
|
33
|
+
const InngestApiGroup = HttpApiGroup.make("inngest").add(IntrospectEndpoint).add(RegisterEndpoint).add(ExecuteEndpoint).addError(FunctionNotFoundError, { status: 404 }).addError(InvalidRequestError, { status: 400 }).addError(SignatureError, { status: 401 });
|
|
34
|
+
/**
|
|
35
|
+
* @since 0.1.0
|
|
36
|
+
* @category layers
|
|
37
|
+
*/
|
|
38
|
+
const layerGroup = (api, group) => {
|
|
39
|
+
const toUrl = (req) => Option.match(HttpServerRequest.toURL(req), {
|
|
40
|
+
onNone: () => req.url,
|
|
41
|
+
onSome: (url) => url.toString()
|
|
42
|
+
});
|
|
43
|
+
return HttpApiBuilder.group(api, "inngest", (handlers) => handlers.handle("introspect", ({ request }) => handleIntrospection(group, toUrl(request)).pipe(Effect.map((r) => r.body))).handle("register", ({ request }) => handleRegistration(group, toUrl(request)).pipe(Effect.map((r) => r.body))).handleRaw("execute", ({ urlParams, request }) => verifyAndParseRequestBody(request).pipe(Effect.provide(SignatureLive), Effect.flatMap((payload) => handleExecution(group, urlParams.fnId, urlParams.stepId, payload)), Effect.map((r) => r.body))));
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
//#endregion
|
|
47
|
+
export { HttpApi_exports, InngestApiGroup, layerGroup };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) {
|
|
6
|
+
__defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
if (symbols) {
|
|
12
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
13
|
+
}
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
export { __exportAll };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Client_d_exports } from "./Client.js";
|
|
2
|
+
import { Events_d_exports } from "./Events.js";
|
|
3
|
+
import { Function_d_exports } from "./Function.js";
|
|
4
|
+
import { NonRetriableError, RetryAfterError } from "./internal/errors.js";
|
|
5
|
+
import { Group_d_exports } from "./Group.js";
|
|
6
|
+
import { HttpApi_d_exports } from "./HttpApi.js";
|
|
7
|
+
export { Client_d_exports as InngestClient, Events_d_exports as InngestEvents, Function_d_exports as InngestFunction, Group_d_exports as InngestGroup, HttpApi_d_exports as InngestHttpApi, NonRetriableError, RetryAfterError };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Function_exports } from "./Function.js";
|
|
2
|
+
import { Client_exports } from "./Client.js";
|
|
3
|
+
import { NonRetriableError, RetryAfterError } from "./internal/errors.js";
|
|
4
|
+
import { Group_exports } from "./Group.js";
|
|
5
|
+
import { HttpApi_exports } from "./HttpApi.js";
|
|
6
|
+
import { Events_exports } from "./Events.js";
|
|
7
|
+
|
|
8
|
+
export { Client_exports as InngestClient, Events_exports as InngestEvents, Function_exports as InngestFunction, Group_exports as InngestGroup, HttpApi_exports as InngestHttpApi, NonRetriableError, RetryAfterError };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/internal/constants.ts
|
|
2
|
+
const OtelAttributes = {
|
|
3
|
+
RunId: "inngest.run_id",
|
|
4
|
+
FunctionId: "inngest.function_id",
|
|
5
|
+
AppId: "inngest.app_id",
|
|
6
|
+
Attempt: "inngest.attempt",
|
|
7
|
+
StepId: "inngest.step.id",
|
|
8
|
+
StepType: "inngest.step.type",
|
|
9
|
+
ExceptionType: "exception.type",
|
|
10
|
+
ExceptionMessage: "exception.message",
|
|
11
|
+
ExceptionStacktrace: "exception.stacktrace"
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
export { OtelAttributes };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { GeneratorOpcode, Headers as Headers$1, Opcode, UserError } from "./protocol.js";
|
|
2
|
+
import { isNonRetriableError, isRetryAfterError, isStepError } from "./errors.js";
|
|
3
|
+
import { StepInterrupt } from "./interrupts.js";
|
|
4
|
+
import { OtelAttributes } from "./constants.js";
|
|
5
|
+
import { Step, buildHandlerContext, createStepTools } from "./step.js";
|
|
6
|
+
import { Array as Array$1, Chunk, Duration, Effect, HashMap, Option, Predicate, Ref, Schema, pipe } from "effect";
|
|
7
|
+
import * as Headers from "@effect/platform/Headers";
|
|
8
|
+
import * as HttpTraceContext from "@effect/platform/HttpTraceContext";
|
|
9
|
+
|
|
10
|
+
//#region src/internal/driver.ts
|
|
11
|
+
/**
|
|
12
|
+
* Driver execution logic.
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
const SDK_VERSION = "2.0.0";
|
|
16
|
+
var ExecutionResult = class extends Schema.Class("ExecutionResult")({
|
|
17
|
+
status: Schema.Literal(200, 206, 500),
|
|
18
|
+
body: Schema.Unknown,
|
|
19
|
+
headers: Schema.Record({
|
|
20
|
+
key: Schema.String,
|
|
21
|
+
value: Schema.String
|
|
22
|
+
})
|
|
23
|
+
}) {};
|
|
24
|
+
const isStepInterrupt = Schema.is(StepInterrupt);
|
|
25
|
+
const isSomeWithValue = (v) => Predicate.isRecord(v) && Predicate.hasProperty(v, "_tag") && v._tag === "Some" && Predicate.hasProperty(v, "value");
|
|
26
|
+
const extractStepInterrupt = (value) => pipe(Option.liftPredicate(isStepInterrupt)(value), Option.orElse(() => pipe(Option.liftPredicate(isSomeWithValue)(value), Option.flatMap((v) => Option.liftPredicate(isStepInterrupt)(v.value)))));
|
|
27
|
+
const collectStepInterruptsFromError = (error) => pipe(extractStepInterrupt(error), Option.match({
|
|
28
|
+
onSome: Chunk.of,
|
|
29
|
+
onNone: () => Array.isArray(error) ? Chunk.fromIterable(Array$1.filterMap(error, extractStepInterrupt)) : Chunk.empty()
|
|
30
|
+
}));
|
|
31
|
+
const toUserError = (error) => UserError.make({
|
|
32
|
+
name: Predicate.hasProperty(error, "name") ? String(error.name) : "Error",
|
|
33
|
+
message: Predicate.hasProperty(error, "message") ? String(error.message) : String(error),
|
|
34
|
+
stack: Predicate.hasProperty(error, "stack") ? String(error.stack) : void 0
|
|
35
|
+
});
|
|
36
|
+
const baseHeaders = () => ({
|
|
37
|
+
"Content-Type": "application/json",
|
|
38
|
+
[Headers$1.SDK]: `effect-inngest:v${SDK_VERSION}`,
|
|
39
|
+
[Headers$1.RequestVersion]: "1"
|
|
40
|
+
});
|
|
41
|
+
const execute = (fn, handler, request, appName, traceHeaders = {}) => Effect.gen(function* () {
|
|
42
|
+
const step = createStepTools(request, appName, yield* Ref.make(HashMap.empty()));
|
|
43
|
+
const context = buildHandlerContext(fn, step, request);
|
|
44
|
+
const headers = baseHeaders();
|
|
45
|
+
return yield* handler(context).pipe(Effect.provideService(Step, step), Effect.map((value) => ExecutionResult.make({
|
|
46
|
+
status: 200,
|
|
47
|
+
body: value,
|
|
48
|
+
headers
|
|
49
|
+
})), Effect.catchAll((error) => {
|
|
50
|
+
const interrupts = collectStepInterruptsFromError(error);
|
|
51
|
+
if (!Chunk.isEmpty(interrupts)) {
|
|
52
|
+
const interruptArray = Chunk.toReadonlyArray(interrupts);
|
|
53
|
+
const opcodes = interruptArray.map((interrupt) => interrupt.opcode);
|
|
54
|
+
const hasNonRetriableError = opcodes.some((op) => op.op === Opcode.StepError && Predicate.isRecord(op.error) && Predicate.hasProperty(op.error, "noRetry") && op.error.noRetry === true);
|
|
55
|
+
const retryAfterMs = interruptArray.find((i) => i.retryAfterMs !== void 0)?.retryAfterMs;
|
|
56
|
+
const responseHeaders = hasNonRetriableError ? {
|
|
57
|
+
...headers,
|
|
58
|
+
[Headers$1.NoRetry]: "true"
|
|
59
|
+
} : headers;
|
|
60
|
+
if (retryAfterMs !== void 0) responseHeaders[Headers$1.RetryAfter] = String(Math.ceil(retryAfterMs / 1e3));
|
|
61
|
+
const encodedOpcodes = Schema.encodeSync(Schema.Array(GeneratorOpcode))(opcodes);
|
|
62
|
+
return Effect.succeed(ExecutionResult.make({
|
|
63
|
+
status: 206,
|
|
64
|
+
body: encodedOpcodes,
|
|
65
|
+
headers: responseHeaders
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
if (isRetryAfterError(error)) {
|
|
69
|
+
const retryAfterMs = Duration.toMillis(error.retryAfter);
|
|
70
|
+
const retryAfterSeconds = Math.ceil(retryAfterMs / 1e3);
|
|
71
|
+
return Effect.succeed(ExecutionResult.make({
|
|
72
|
+
status: 500,
|
|
73
|
+
body: { error: toUserError(error) },
|
|
74
|
+
headers: {
|
|
75
|
+
...headers,
|
|
76
|
+
[Headers$1.NoRetry]: "false",
|
|
77
|
+
[Headers$1.RetryAfter]: String(retryAfterSeconds)
|
|
78
|
+
}
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
const noRetry = isNonRetriableError(error) || isStepError(error) && error.noRetry === true ? "true" : "false";
|
|
82
|
+
return Effect.succeed(ExecutionResult.make({
|
|
83
|
+
status: 500,
|
|
84
|
+
body: { error: toUserError(error) },
|
|
85
|
+
headers: {
|
|
86
|
+
...headers,
|
|
87
|
+
[Headers$1.NoRetry]: noRetry
|
|
88
|
+
}
|
|
89
|
+
}));
|
|
90
|
+
}), Effect.catchAllDefect((defect) => Effect.succeed(ExecutionResult.make({
|
|
91
|
+
status: 500,
|
|
92
|
+
body: { error: toUserError(defect) },
|
|
93
|
+
headers: {
|
|
94
|
+
...headers,
|
|
95
|
+
[Headers$1.NoRetry]: "false"
|
|
96
|
+
}
|
|
97
|
+
}))));
|
|
98
|
+
}).pipe((base) => {
|
|
99
|
+
const headers = {};
|
|
100
|
+
if (traceHeaders.traceparent) headers["traceparent"] = traceHeaders.traceparent;
|
|
101
|
+
if (traceHeaders.tracestate) headers["tracestate"] = traceHeaders.tracestate;
|
|
102
|
+
return pipe(HttpTraceContext.fromHeaders(Headers.fromInput(headers)), Option.match({
|
|
103
|
+
onNone: () => base,
|
|
104
|
+
onSome: (externalSpan) => Effect.withParentSpan(base, externalSpan)
|
|
105
|
+
}));
|
|
106
|
+
}, Effect.withSpan(`inngest.function/${fn._tag}`, {
|
|
107
|
+
kind: "server",
|
|
108
|
+
attributes: {
|
|
109
|
+
[OtelAttributes.RunId]: request.ctx.run_id,
|
|
110
|
+
[OtelAttributes.FunctionId]: fn._tag,
|
|
111
|
+
[OtelAttributes.AppId]: appName,
|
|
112
|
+
[OtelAttributes.Attempt]: request.ctx.attempt
|
|
113
|
+
}
|
|
114
|
+
}));
|
|
115
|
+
|
|
116
|
+
//#endregion
|
|
117
|
+
export { execute };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
|
|
3
|
+
//#region src/internal/errors.d.ts
|
|
4
|
+
declare const SendEventError_base: Schema.TaggedErrorClass<SendEventError, "SendEventError", {
|
|
5
|
+
readonly _tag: Schema.tag<"SendEventError">;
|
|
6
|
+
} & {
|
|
7
|
+
message: typeof Schema.String;
|
|
8
|
+
events: Schema.Array$<typeof Schema.String>;
|
|
9
|
+
}>;
|
|
10
|
+
/**
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
declare class SendEventError extends SendEventError_base {}
|
|
14
|
+
declare const StepError_base: Schema.TaggedErrorClass<StepError, "StepError", {
|
|
15
|
+
readonly _tag: Schema.tag<"StepError">;
|
|
16
|
+
} & {
|
|
17
|
+
message: typeof Schema.String;
|
|
18
|
+
stepId: typeof Schema.String;
|
|
19
|
+
cause: Schema.optional<typeof Schema.Unknown>;
|
|
20
|
+
noRetry: Schema.optional<typeof Schema.Boolean>;
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
declare class StepError extends StepError_base {}
|
|
26
|
+
declare const NonRetriableError_base: Schema.TaggedErrorClass<NonRetriableError, "NonRetriableError", {
|
|
27
|
+
readonly _tag: Schema.tag<"NonRetriableError">;
|
|
28
|
+
} & {
|
|
29
|
+
message: typeof Schema.String;
|
|
30
|
+
cause: Schema.optional<typeof Schema.Unknown>;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Thrown to indicate that the error should not be retried.
|
|
34
|
+
* Use this when you know retrying won't help (e.g., validation errors, auth failures).
|
|
35
|
+
*
|
|
36
|
+
* @since 0.1.0
|
|
37
|
+
* @category errors
|
|
38
|
+
*/
|
|
39
|
+
declare class NonRetriableError extends NonRetriableError_base {}
|
|
40
|
+
declare const RetryAfterError_base: Schema.TaggedErrorClass<RetryAfterError, "RetryAfterError", {
|
|
41
|
+
readonly _tag: Schema.tag<"RetryAfterError">;
|
|
42
|
+
} & {
|
|
43
|
+
message: typeof Schema.String;
|
|
44
|
+
retryAfter: typeof Schema.DurationFromMillis;
|
|
45
|
+
cause: Schema.optional<typeof Schema.Unknown>;
|
|
46
|
+
}>;
|
|
47
|
+
/**
|
|
48
|
+
* Thrown to indicate that the operation should be retried after a specific delay.
|
|
49
|
+
* Use this for rate limiting or when you know when a resource will become available.
|
|
50
|
+
*
|
|
51
|
+
* @since 0.1.0
|
|
52
|
+
* @category errors
|
|
53
|
+
*/
|
|
54
|
+
declare class RetryAfterError extends RetryAfterError_base {}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { NonRetriableError, RetryAfterError, SendEventError, StepError };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
|
|
3
|
+
//#region src/internal/errors.ts
|
|
4
|
+
/**
|
|
5
|
+
* Internal error types for the Effect Inngest SDK.
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
var SendEventError = class extends Schema.TaggedError()("SendEventError", {
|
|
12
|
+
message: Schema.String,
|
|
13
|
+
events: Schema.Array(Schema.String)
|
|
14
|
+
}) {};
|
|
15
|
+
/**
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
var StepError = class extends Schema.TaggedError()("StepError", {
|
|
19
|
+
message: Schema.String,
|
|
20
|
+
stepId: Schema.String,
|
|
21
|
+
cause: Schema.optional(Schema.Unknown),
|
|
22
|
+
noRetry: Schema.optional(Schema.Boolean)
|
|
23
|
+
}) {};
|
|
24
|
+
/**
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
const isStepError = Schema.is(StepError);
|
|
28
|
+
/**
|
|
29
|
+
* Thrown to indicate that the error should not be retried.
|
|
30
|
+
* Use this when you know retrying won't help (e.g., validation errors, auth failures).
|
|
31
|
+
*
|
|
32
|
+
* @since 0.1.0
|
|
33
|
+
* @category errors
|
|
34
|
+
*/
|
|
35
|
+
var NonRetriableError = class extends Schema.TaggedError()("NonRetriableError", {
|
|
36
|
+
message: Schema.String,
|
|
37
|
+
cause: Schema.optional(Schema.Unknown)
|
|
38
|
+
}) {};
|
|
39
|
+
/**
|
|
40
|
+
* @internal
|
|
41
|
+
*/
|
|
42
|
+
const isNonRetriableError = Schema.is(NonRetriableError);
|
|
43
|
+
/**
|
|
44
|
+
* Thrown to indicate that the operation should be retried after a specific delay.
|
|
45
|
+
* Use this for rate limiting or when you know when a resource will become available.
|
|
46
|
+
*
|
|
47
|
+
* @since 0.1.0
|
|
48
|
+
* @category errors
|
|
49
|
+
*/
|
|
50
|
+
var RetryAfterError = class extends Schema.TaggedError()("RetryAfterError", {
|
|
51
|
+
message: Schema.String,
|
|
52
|
+
retryAfter: Schema.DurationFromMillis,
|
|
53
|
+
cause: Schema.optional(Schema.Unknown)
|
|
54
|
+
}) {};
|
|
55
|
+
/**
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
const isRetryAfterError = Schema.is(RetryAfterError);
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
export { NonRetriableError, RetryAfterError, SendEventError, StepError, isNonRetriableError, isRetryAfterError, isStepError };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import "../Client.js";
|
|
2
|
+
import "./protocol.js";
|
|
3
|
+
import "../Group.js";
|
|
4
|
+
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
|
+
import * as Schema from "effect/Schema";
|
|
11
|
+
|
|
12
|
+
//#region src/internal/handler.d.ts
|
|
13
|
+
declare const InvalidRequestError_base: Schema.TaggedErrorClass<InvalidRequestError, "InvalidRequestError", {
|
|
14
|
+
readonly _tag: Schema.tag<"InvalidRequestError">;
|
|
15
|
+
} & {
|
|
16
|
+
message: typeof Schema.String;
|
|
17
|
+
}>;
|
|
18
|
+
declare class InvalidRequestError extends InvalidRequestError_base {}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { InvalidRequestError };
|