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/helpers.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import * as Duration from "effect/Duration";
|
|
2
|
-
|
|
2
|
+
import * as DateTime from "effect/DateTime";
|
|
3
3
|
//#region src/internal/helpers.ts
|
|
4
4
|
/**
|
|
5
5
|
* Internal helper utilities.
|
|
6
6
|
* @internal
|
|
7
7
|
*/
|
|
8
|
-
const
|
|
8
|
+
const millisecond = 1;
|
|
9
|
+
const second = millisecond * 1e3;
|
|
9
10
|
const minute = second * 60;
|
|
10
11
|
const hour = minute * 60;
|
|
11
12
|
const day = hour * 24;
|
|
@@ -14,7 +15,8 @@ const periods = [
|
|
|
14
15
|
["d", day],
|
|
15
16
|
["h", hour],
|
|
16
17
|
["m", minute],
|
|
17
|
-
["s", second]
|
|
18
|
+
["s", second],
|
|
19
|
+
["ms", millisecond]
|
|
18
20
|
];
|
|
19
21
|
/**
|
|
20
22
|
* Convert a Duration to an Inngest-compatible time string (e.g. `"1d"` or `"2h30m"`).
|
|
@@ -23,7 +25,7 @@ const periods = [
|
|
|
23
25
|
* to their equivalent in weeks/days.
|
|
24
26
|
*/
|
|
25
27
|
const timeStr = (input) => {
|
|
26
|
-
let ms = Duration.toMillis(Duration.
|
|
28
|
+
let ms = Duration.toMillis(Duration.fromInputUnsafe(input));
|
|
27
29
|
const [, result] = periods.reduce(([num, str], [suffix, period]) => {
|
|
28
30
|
const numPeriods = Math.floor(num / period);
|
|
29
31
|
if (numPeriods > 0) return [num % period, `${str}${numPeriods}${suffix}`];
|
|
@@ -33,12 +35,13 @@ const timeStr = (input) => {
|
|
|
33
35
|
};
|
|
34
36
|
/**
|
|
35
37
|
* Format a timestamp as an ISO string for Inngest's sleepUntil.
|
|
38
|
+
*
|
|
39
|
+
* String inputs are passed through unchanged (assumed ISO-formatted), matching
|
|
40
|
+
* the prior contract. Date/number inputs are normalized via DateTime.
|
|
36
41
|
*/
|
|
37
42
|
const formatTimestamp = (timestamp) => {
|
|
38
|
-
if (timestamp
|
|
39
|
-
|
|
40
|
-
return timestamp;
|
|
43
|
+
if (typeof timestamp === "string") return timestamp;
|
|
44
|
+
return DateTime.formatIso(DateTime.makeUnsafe(timestamp));
|
|
41
45
|
};
|
|
42
|
-
|
|
43
46
|
//#endregion
|
|
44
|
-
export { formatTimestamp, timeStr };
|
|
47
|
+
export { formatTimestamp, timeStr };
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import "effect/Schema";
|
|
1
|
+
export { };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { GeneratorOpcode, UserError, invokeFunction, sleep, stepError, stepPlanned, stepRun, waitForEvent } from "./protocol.js";
|
|
2
2
|
import * as Schema from "effect/Schema";
|
|
3
3
|
import * as Predicate from "effect/Predicate";
|
|
4
|
-
|
|
5
4
|
//#region src/internal/interrupts.ts
|
|
6
5
|
/**
|
|
7
6
|
* StepInterrupt schema and factory functions.
|
|
@@ -40,6 +39,5 @@ const errorInterrupt = (opts) => StepInterrupt.make({
|
|
|
40
39
|
opcode: stepError(opts.info, toUserError(opts.error), opts.noRetry),
|
|
41
40
|
retryAfterMs: opts.retryAfterMs
|
|
42
41
|
});
|
|
43
|
-
|
|
44
42
|
//#endregion
|
|
45
|
-
export { StepInterrupt, errorInterrupt, invokeInterrupt, plannedInterrupt, runInterrupt, sleepInterrupt, waitForEventInterrupt };
|
|
43
|
+
export { StepInterrupt, errorInterrupt, invokeInterrupt, plannedInterrupt, runInterrupt, sleepInterrupt, waitForEventInterrupt };
|
package/dist/internal/memo.js
CHANGED
|
@@ -1,56 +1,58 @@
|
|
|
1
|
+
import * as Option from "effect/Option";
|
|
1
2
|
import * as Schema from "effect/Schema";
|
|
2
3
|
import * as Predicate from "effect/Predicate";
|
|
3
|
-
|
|
4
|
+
import * as SchemaTransformation from "effect/SchemaTransformation";
|
|
4
5
|
//#region src/internal/memo.ts
|
|
5
6
|
/**
|
|
6
7
|
* Step memoization schemas for decoding cached step results.
|
|
7
8
|
* @internal
|
|
8
9
|
*/
|
|
9
|
-
const hasKey = (key) => (u) => Predicate.
|
|
10
|
+
const hasKey = (key) => (u) => Predicate.isObject(u) && Predicate.hasProperty(u, key);
|
|
10
11
|
const MemoDataSchema = Schema.TaggedStruct("MemoData", { data: Schema.Unknown });
|
|
11
12
|
const MemoErrorSchema = Schema.TaggedStruct("MemoError", { error: Schema.Unknown });
|
|
12
13
|
const MemoInputSchema = Schema.TaggedStruct("MemoInput", { input: Schema.Unknown });
|
|
13
14
|
const MemoTimeoutSchema = Schema.TaggedStruct("MemoTimeout", {});
|
|
14
15
|
const MemoNoneSchema = Schema.TaggedStruct("MemoNone", {});
|
|
15
|
-
const DataWire = Schema.Unknown.pipe(Schema.
|
|
16
|
+
const DataWire = Schema.Unknown.pipe(Schema.check(Schema.makeFilter(hasKey("data"))), Schema.decodeTo(MemoDataSchema, SchemaTransformation.transform({
|
|
16
17
|
decode: (v) => ({
|
|
17
18
|
_tag: "MemoData",
|
|
18
19
|
data: v.data
|
|
19
20
|
}),
|
|
20
21
|
encode: ({ data }) => ({ data })
|
|
21
|
-
}));
|
|
22
|
-
const ErrorWire = Schema.Unknown.pipe(Schema.
|
|
22
|
+
})));
|
|
23
|
+
const ErrorWire = Schema.Unknown.pipe(Schema.check(Schema.makeFilter(hasKey("error"))), Schema.decodeTo(MemoErrorSchema, SchemaTransformation.transform({
|
|
23
24
|
decode: (v) => ({
|
|
24
25
|
_tag: "MemoError",
|
|
25
26
|
error: v.error
|
|
26
27
|
}),
|
|
27
28
|
encode: ({ error }) => ({ error })
|
|
28
|
-
}));
|
|
29
|
-
const InputWire = Schema.Unknown.pipe(Schema.
|
|
29
|
+
})));
|
|
30
|
+
const InputWire = Schema.Unknown.pipe(Schema.check(Schema.makeFilter(hasKey("input"))), Schema.decodeTo(MemoInputSchema, SchemaTransformation.transform({
|
|
30
31
|
decode: (v) => ({
|
|
31
32
|
_tag: "MemoInput",
|
|
32
33
|
input: v.input
|
|
33
34
|
}),
|
|
34
35
|
encode: ({ input }) => ({ input })
|
|
35
|
-
}));
|
|
36
|
-
const TimeoutWire = Schema.
|
|
36
|
+
})));
|
|
37
|
+
const TimeoutWire = Schema.Null.pipe(Schema.decodeTo(MemoTimeoutSchema, SchemaTransformation.transform({
|
|
37
38
|
decode: () => ({ _tag: "MemoTimeout" }),
|
|
38
39
|
encode: () => null
|
|
39
|
-
});
|
|
40
|
-
const NoneWire = Schema.
|
|
40
|
+
})));
|
|
41
|
+
const NoneWire = Schema.Undefined.pipe(Schema.decodeTo(MemoNoneSchema, SchemaTransformation.transform({
|
|
41
42
|
decode: () => ({ _tag: "MemoNone" }),
|
|
42
43
|
encode: () => void 0
|
|
43
|
-
});
|
|
44
|
-
const MemoSchema = Schema.Union(
|
|
44
|
+
})));
|
|
45
|
+
const MemoSchema = Schema.Union([
|
|
46
|
+
ErrorWire,
|
|
47
|
+
InputWire,
|
|
48
|
+
DataWire,
|
|
49
|
+
TimeoutWire,
|
|
50
|
+
NoneWire
|
|
51
|
+
]);
|
|
45
52
|
/**
|
|
46
53
|
* Decode a step result into a Memo type.
|
|
47
54
|
* Order matters: error > input > data (more specific properties first).
|
|
48
55
|
*/
|
|
49
|
-
const decodeMemo = (value) => {
|
|
50
|
-
const result = Schema.decodeUnknownOption(MemoSchema)(value);
|
|
51
|
-
if (result._tag === "Some") return result.value;
|
|
52
|
-
return MemoNoneSchema.make();
|
|
53
|
-
};
|
|
54
|
-
|
|
56
|
+
const decodeMemo = (value) => Option.getOrElse(Schema.decodeUnknownOption(MemoSchema)(value), () => MemoNoneSchema.make({}));
|
|
55
57
|
//#endregion
|
|
56
|
-
export { decodeMemo };
|
|
58
|
+
export { decodeMemo };
|
|
@@ -1 +1,28 @@
|
|
|
1
|
-
import "effect/Schema";
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
|
|
3
|
+
//#region src/internal/protocol.d.ts
|
|
4
|
+
declare const UserError_base: Schema.Class<UserError, Schema.Struct<{
|
|
5
|
+
readonly name: Schema.String;
|
|
6
|
+
readonly message: Schema.String;
|
|
7
|
+
readonly stack: Schema.optional<Schema.String>;
|
|
8
|
+
readonly data: Schema.optional<Schema.Unknown>;
|
|
9
|
+
readonly noRetry: Schema.optional<Schema.Boolean>;
|
|
10
|
+
readonly cause: Schema.optional<Schema.Unknown>;
|
|
11
|
+
}>, {}>;
|
|
12
|
+
declare class UserError extends UserError_base {}
|
|
13
|
+
declare const GeneratorOpcode_base: Schema.Class<GeneratorOpcode, Schema.Struct<{
|
|
14
|
+
readonly op: Schema.Literals<readonly ["None", "Step", "StepRun", "StepError", "StepPlanned", "Sleep", "WaitForEvent", "InvokeFunction", "AIGateway", "Gateway", "WaitForSignal", "RunComplete", "StepFailed", "SyncRunComplete", "DiscoveryRequest"]>;
|
|
15
|
+
readonly id: Schema.String;
|
|
16
|
+
readonly name: Schema.String;
|
|
17
|
+
readonly mode: Schema.optional<Schema.Literals<readonly ["sync", "async"]>>;
|
|
18
|
+
readonly opts: Schema.optional<Schema.decodeTo<Schema.Unknown, Schema.Unknown, never, never>>;
|
|
19
|
+
readonly data: Schema.optional<Schema.decodeTo<Schema.Unknown, Schema.Unknown, never, never>>;
|
|
20
|
+
readonly error: Schema.optional<typeof UserError>;
|
|
21
|
+
readonly displayName: Schema.optional<Schema.String>;
|
|
22
|
+
readonly userland: Schema.optional<Schema.Struct<{
|
|
23
|
+
readonly id: Schema.String;
|
|
24
|
+
}>>;
|
|
25
|
+
}>, {}>;
|
|
26
|
+
declare class GeneratorOpcode extends GeneratorOpcode_base {}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { GeneratorOpcode };
|
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
import { Predicate, Struct } from "effect";
|
|
1
|
+
import { Effect, Predicate, SchemaTransformation, Struct } from "effect";
|
|
2
2
|
import * as Schema$1 from "effect/Schema";
|
|
3
|
-
|
|
4
3
|
//#region src/internal/protocol.ts
|
|
5
4
|
/**
|
|
6
5
|
* Wire protocol schemas and opcode factories for Inngest communication.
|
|
7
6
|
* @internal
|
|
8
7
|
*/
|
|
9
8
|
const stripTopLevelTag = (value) => {
|
|
10
|
-
if (Predicate.
|
|
9
|
+
if (Predicate.isObject(value)) return Struct.omit(value, ["_tag"]);
|
|
11
10
|
return value;
|
|
12
11
|
};
|
|
13
|
-
const WireUnknown = Schema$1.
|
|
14
|
-
strict: true,
|
|
12
|
+
const WireUnknown = Schema$1.Unknown.pipe(Schema$1.decodeTo(Schema$1.Unknown, SchemaTransformation.transform({
|
|
15
13
|
decode: (value) => value,
|
|
16
14
|
encode: (value) => stripTopLevelTag(value)
|
|
17
|
-
});
|
|
15
|
+
})));
|
|
18
16
|
const Opcode = {
|
|
19
17
|
None: "None",
|
|
20
18
|
Step: "Step",
|
|
@@ -40,10 +38,7 @@ var UserError = class extends Schema$1.Class("UserError")({
|
|
|
40
38
|
noRetry: Schema$1.optional(Schema$1.Boolean),
|
|
41
39
|
cause: Schema$1.optional(Schema$1.Unknown)
|
|
42
40
|
}) {};
|
|
43
|
-
const StepResult = Schema$1.NullOr(Schema$1.Record({
|
|
44
|
-
key: Schema$1.String,
|
|
45
|
-
value: Schema$1.Unknown
|
|
46
|
-
}).pipe(Schema$1.annotations({ identifier: "StepResultObject" })));
|
|
41
|
+
const StepResult = Schema$1.NullOr(Schema$1.Record(Schema$1.String, Schema$1.Unknown).pipe(Schema$1.annotate({ identifier: "StepResultObject" })));
|
|
47
42
|
var FunctionStack = class extends Schema$1.Class("FunctionStack")({
|
|
48
43
|
stack: Schema$1.Array(Schema$1.String),
|
|
49
44
|
current: Schema$1.Number
|
|
@@ -51,45 +46,33 @@ var FunctionStack = class extends Schema$1.Class("FunctionStack")({
|
|
|
51
46
|
var InngestEvent = class extends Schema$1.Class("InngestEvent")({
|
|
52
47
|
id: Schema$1.optional(Schema$1.String),
|
|
53
48
|
name: Schema$1.String,
|
|
54
|
-
data: Schema$1.
|
|
55
|
-
key: Schema$1.String,
|
|
56
|
-
value: Schema$1.Unknown
|
|
57
|
-
}), {
|
|
58
|
-
default: () => ({}),
|
|
59
|
-
nullable: true
|
|
60
|
-
}),
|
|
49
|
+
data: Schema$1.NullOr(Schema$1.Record(Schema$1.String, Schema$1.Unknown)).pipe(Schema$1.withDecodingDefaultType(Effect.succeed({}))),
|
|
61
50
|
ts: Schema$1.optional(Schema$1.Number),
|
|
62
|
-
user: Schema$1.optional(Schema$1.Record(
|
|
63
|
-
key: Schema$1.String,
|
|
64
|
-
value: Schema$1.Unknown
|
|
65
|
-
})),
|
|
51
|
+
user: Schema$1.optional(Schema$1.Record(Schema$1.String, Schema$1.Unknown)),
|
|
66
52
|
v: Schema$1.optional(Schema$1.String)
|
|
67
53
|
}) {};
|
|
68
54
|
var SDKRequestContext = class extends Schema$1.Class("SDKRequestContext")({
|
|
69
55
|
fn_id: Schema$1.String,
|
|
70
56
|
run_id: Schema$1.String,
|
|
71
|
-
env: Schema$1.
|
|
72
|
-
step_id: Schema$1.
|
|
73
|
-
attempt: Schema$1.
|
|
74
|
-
max_attempts: Schema$1.
|
|
75
|
-
stack: Schema$1.
|
|
57
|
+
env: Schema$1.String.pipe(Schema$1.withDecodingDefault(Effect.succeed("dev"))),
|
|
58
|
+
step_id: Schema$1.String.pipe(Schema$1.withDecodingDefault(Effect.succeed("step"))),
|
|
59
|
+
attempt: Schema$1.Number.pipe(Schema$1.withDecodingDefault(Effect.succeed(0))),
|
|
60
|
+
max_attempts: Schema$1.Number.pipe(Schema$1.withDecodingDefault(Effect.succeed(4))),
|
|
61
|
+
stack: FunctionStack.pipe(Schema$1.withDecodingDefaultType(Effect.succeed(FunctionStack.make({
|
|
76
62
|
stack: [],
|
|
77
63
|
current: 0
|
|
78
|
-
})
|
|
79
|
-
qi_id: Schema$1.
|
|
80
|
-
disable_immediate_execution: Schema$1.
|
|
81
|
-
use_api: Schema$1.
|
|
64
|
+
})))),
|
|
65
|
+
qi_id: Schema$1.String.pipe(Schema$1.withDecodingDefault(Effect.succeed(""))),
|
|
66
|
+
disable_immediate_execution: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false))),
|
|
67
|
+
use_api: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false)))
|
|
82
68
|
}) {};
|
|
83
69
|
var SDKRequestBody = class extends Schema$1.Class("SDKRequestBody")({
|
|
84
70
|
event: InngestEvent,
|
|
85
71
|
events: Schema$1.Array(InngestEvent),
|
|
86
|
-
steps: Schema$1.
|
|
87
|
-
key: Schema$1.String,
|
|
88
|
-
value: StepResult
|
|
89
|
-
}), { default: () => ({}) }),
|
|
72
|
+
steps: Schema$1.Record(Schema$1.String, StepResult).pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
|
|
90
73
|
ctx: SDKRequestContext,
|
|
91
|
-
version: Schema$1.
|
|
92
|
-
use_api: Schema$1.
|
|
74
|
+
version: Schema$1.Number.pipe(Schema$1.withDecodingDefault(Effect.succeed(1))),
|
|
75
|
+
use_api: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false)))
|
|
93
76
|
}) {};
|
|
94
77
|
const Headers = {
|
|
95
78
|
SDK: "X-Inngest-SDK",
|
|
@@ -105,10 +88,26 @@ const Headers = {
|
|
|
105
88
|
Env: "X-Inngest-Env"
|
|
106
89
|
};
|
|
107
90
|
var GeneratorOpcode = class extends Schema$1.Class("GeneratorOpcode")({
|
|
108
|
-
op: Schema$1.
|
|
91
|
+
op: Schema$1.Literals([
|
|
92
|
+
Opcode.None,
|
|
93
|
+
Opcode.Step,
|
|
94
|
+
Opcode.StepRun,
|
|
95
|
+
Opcode.StepError,
|
|
96
|
+
Opcode.StepPlanned,
|
|
97
|
+
Opcode.Sleep,
|
|
98
|
+
Opcode.WaitForEvent,
|
|
99
|
+
Opcode.InvokeFunction,
|
|
100
|
+
Opcode.AIGateway,
|
|
101
|
+
Opcode.Gateway,
|
|
102
|
+
Opcode.WaitForSignal,
|
|
103
|
+
Opcode.RunComplete,
|
|
104
|
+
Opcode.StepFailed,
|
|
105
|
+
Opcode.SyncRunComplete,
|
|
106
|
+
Opcode.DiscoveryRequest
|
|
107
|
+
]),
|
|
109
108
|
id: Schema$1.String,
|
|
110
109
|
name: Schema$1.String,
|
|
111
|
-
mode: Schema$1.optional(Schema$1.
|
|
110
|
+
mode: Schema$1.optional(Schema$1.Literals(["sync", "async"])),
|
|
112
111
|
opts: Schema$1.optional(WireUnknown),
|
|
113
112
|
data: Schema$1.optional(WireUnknown),
|
|
114
113
|
error: Schema$1.optional(UserError),
|
|
@@ -138,7 +137,8 @@ const sleep = (info, duration) => GeneratorOpcode.make({
|
|
|
138
137
|
id: info.hash,
|
|
139
138
|
name: duration,
|
|
140
139
|
displayName: info.name,
|
|
141
|
-
mode: "async"
|
|
140
|
+
mode: "async",
|
|
141
|
+
opts: { duration }
|
|
142
142
|
});
|
|
143
143
|
const waitForEvent = (info, opts) => mkOpcode(info, Opcode.WaitForEvent, {
|
|
144
144
|
mode: "async",
|
|
@@ -149,23 +149,41 @@ const invokeFunction = (info, opts) => mkOpcode(info, Opcode.InvokeFunction, {
|
|
|
149
149
|
opts,
|
|
150
150
|
userland: { id: info.id }
|
|
151
151
|
});
|
|
152
|
+
/**
|
|
153
|
+
* Terminal opcode emitted by the SDK in checkpoint mode when the function
|
|
154
|
+
* completes successfully. The executor uses this to correlate run completion
|
|
155
|
+
* (spec §10.4.1).
|
|
156
|
+
*/
|
|
157
|
+
const runComplete = (data) => GeneratorOpcode.make({
|
|
158
|
+
op: Opcode.RunComplete,
|
|
159
|
+
id: "step",
|
|
160
|
+
name: "step",
|
|
161
|
+
data
|
|
162
|
+
});
|
|
163
|
+
/**
|
|
164
|
+
* Yield opcode emitted by the SDK in checkpoint mode when `maxRuntime` is
|
|
165
|
+
* exceeded. Tells the executor to schedule a new Call Request to continue
|
|
166
|
+
* execution (spec §10.4.1).
|
|
167
|
+
*/
|
|
168
|
+
const discoveryRequest = () => GeneratorOpcode.make({
|
|
169
|
+
op: Opcode.DiscoveryRequest,
|
|
170
|
+
id: "step",
|
|
171
|
+
name: "step"
|
|
172
|
+
});
|
|
152
173
|
const IntrospectionBase = Schema$1.Struct({
|
|
153
174
|
function_count: Schema$1.Number,
|
|
154
175
|
has_event_key: Schema$1.Boolean,
|
|
155
176
|
has_signing_key: Schema$1.Boolean,
|
|
156
177
|
has_signing_key_fallback: Schema$1.Boolean,
|
|
157
|
-
mode: Schema$1.
|
|
178
|
+
mode: Schema$1.Literals(["cloud", "dev"]),
|
|
158
179
|
schema_version: Schema$1.Literal("2024-05-24"),
|
|
159
|
-
extra: Schema$1.optional(Schema$1.Record(
|
|
160
|
-
key: Schema$1.String,
|
|
161
|
-
value: Schema$1.Unknown
|
|
162
|
-
}))
|
|
180
|
+
extra: Schema$1.optional(Schema$1.Record(Schema$1.String, Schema$1.Unknown))
|
|
163
181
|
});
|
|
164
|
-
const IntrospectionUnauthenticated =
|
|
165
|
-
authentication_succeeded: Schema$1.Union(Schema$1.Literal(false), Schema$1.Null),
|
|
166
|
-
functions: Schema$1.
|
|
182
|
+
const IntrospectionUnauthenticated = IntrospectionBase.pipe(Schema$1.fieldsAssign({
|
|
183
|
+
authentication_succeeded: Schema$1.Union([Schema$1.Literal(false), Schema$1.Null]),
|
|
184
|
+
functions: Schema$1.optionalKey(Schema$1.Array(Schema$1.Unknown))
|
|
167
185
|
}));
|
|
168
|
-
const IntrospectionAuthenticated =
|
|
186
|
+
const IntrospectionAuthenticated = IntrospectionBase.pipe(Schema$1.fieldsAssign({
|
|
169
187
|
authentication_succeeded: Schema$1.Literal(true),
|
|
170
188
|
api_origin: Schema$1.String,
|
|
171
189
|
app_id: Schema$1.String,
|
|
@@ -180,8 +198,22 @@ const IntrospectionAuthenticated = Schema$1.extend(IntrospectionBase, Schema$1.S
|
|
|
180
198
|
signing_key_fallback_hash: Schema$1.NullOr(Schema$1.String),
|
|
181
199
|
signing_key_hash: Schema$1.NullOr(Schema$1.String)
|
|
182
200
|
}));
|
|
183
|
-
const IntrospectionResponse = Schema$1.Union(IntrospectionAuthenticated, IntrospectionUnauthenticated);
|
|
184
|
-
|
|
185
|
-
|
|
201
|
+
const IntrospectionResponse = Schema$1.Union([IntrospectionAuthenticated, IntrospectionUnauthenticated]);
|
|
202
|
+
/**
|
|
203
|
+
* SDK → executor response shape for PUT sync requests per spec §4.3.1.
|
|
204
|
+
*/
|
|
205
|
+
const RegisterResponse = Schema$1.Struct({
|
|
206
|
+
message: Schema$1.String,
|
|
207
|
+
modified: Schema$1.Boolean
|
|
208
|
+
});
|
|
209
|
+
/**
|
|
210
|
+
* Executor → SDK response shape from `POST /fn/register` per spec §4.3.4.
|
|
211
|
+
* On success the body has `{ ok: true, modified?: boolean }`; on failure
|
|
212
|
+
* the body may carry an `error` string.
|
|
213
|
+
*/
|
|
214
|
+
const RegisterServerResponse = Schema$1.Union([Schema$1.Struct({
|
|
215
|
+
ok: Schema$1.Literal(true),
|
|
216
|
+
modified: Schema$1.optional(Schema$1.Boolean)
|
|
217
|
+
}), Schema$1.Struct({ error: Schema$1.optional(Schema$1.String) })]);
|
|
186
218
|
//#endregion
|
|
187
|
-
export { GeneratorOpcode, Headers, IntrospectionResponse, Opcode, RegisterResponse, SDKRequestBody, SDKRequestContext, UserError, invokeFunction, sleep, stepError, stepPlanned, stepRun, waitForEvent };
|
|
219
|
+
export { GeneratorOpcode, Headers, IntrospectionResponse, Opcode, RegisterResponse, RegisterServerResponse, SDKRequestBody, SDKRequestContext, UserError, discoveryRequest, invokeFunction, runComplete, sleep, stepError, stepPlanned, stepRun, waitForEvent };
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import "effect/Context";
|
|
2
|
-
import "effect/Effect";
|
|
3
|
-
import "effect/Layer";
|
|
4
1
|
import * as Schema from "effect/Schema";
|
|
2
|
+
import * as _$effect_Cause0 from "effect/Cause";
|
|
5
3
|
|
|
6
4
|
//#region src/internal/signature.d.ts
|
|
7
|
-
declare const SignatureError_base: Schema.
|
|
8
|
-
readonly
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
message: typeof Schema.String;
|
|
12
|
-
}>;
|
|
5
|
+
declare const SignatureError_base: Schema.Class<SignatureError, Schema.TaggedStruct<"SignatureError", {
|
|
6
|
+
readonly reason: Schema.Literals<readonly ["missing_header", "invalid_format", "expired", "invalid_signature", "missing_signing_key"]>;
|
|
7
|
+
readonly message: Schema.String;
|
|
8
|
+
}>, _$effect_Cause0.YieldableError>;
|
|
13
9
|
/**
|
|
14
10
|
* @internal
|
|
15
11
|
*/
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Headers } from "./protocol.js";
|
|
2
|
-
import * as Context from "effect/Context";
|
|
3
2
|
import * as Effect from "effect/Effect";
|
|
4
|
-
import * as Layer from "effect/Layer";
|
|
5
3
|
import * as Schema from "effect/Schema";
|
|
6
|
-
import * as Crypto from "node:crypto";
|
|
7
4
|
import * as DateTime from "effect/DateTime";
|
|
8
|
-
|
|
5
|
+
import * as Context from "effect/Context";
|
|
6
|
+
import * as Layer from "effect/Layer";
|
|
7
|
+
import * as Crypto from "node:crypto";
|
|
8
|
+
import * as SchemaTransformation from "effect/SchemaTransformation";
|
|
9
9
|
//#region src/internal/signature.ts
|
|
10
10
|
/**
|
|
11
11
|
* Signature verification service for Inngest requests.
|
|
@@ -14,19 +14,25 @@ import * as DateTime from "effect/DateTime";
|
|
|
14
14
|
/**
|
|
15
15
|
* @internal
|
|
16
16
|
*/
|
|
17
|
-
var SignatureError = class extends Schema.
|
|
18
|
-
reason: Schema.
|
|
17
|
+
var SignatureError = class extends Schema.TaggedErrorClass()("SignatureError", {
|
|
18
|
+
reason: Schema.Literals([
|
|
19
|
+
"missing_header",
|
|
20
|
+
"invalid_format",
|
|
21
|
+
"expired",
|
|
22
|
+
"invalid_signature",
|
|
23
|
+
"missing_signing_key"
|
|
24
|
+
]),
|
|
19
25
|
message: Schema.String
|
|
20
26
|
}) {};
|
|
21
27
|
/**
|
|
22
28
|
* @internal
|
|
23
29
|
*/
|
|
24
|
-
var Signature = class extends Context.
|
|
25
|
-
const TimestampSeconds = Schema.NumberFromString.pipe(Schema.
|
|
26
|
-
const SignatureHex = Schema.String.pipe(Schema.
|
|
30
|
+
var Signature = class extends Context.Service()("effect-inngest/Signature") {};
|
|
31
|
+
const TimestampSeconds = Schema.NumberFromString.pipe(Schema.check(Schema.isInt(), Schema.isGreaterThan(0)));
|
|
32
|
+
const SignatureHex = Schema.String.pipe(Schema.check(Schema.isPattern(/^[a-fA-F0-9]{64}$/)), Schema.decodeTo(Schema.String, SchemaTransformation.transform({
|
|
27
33
|
decode: (s) => s.toLowerCase(),
|
|
28
34
|
encode: (s) => s
|
|
29
|
-
}));
|
|
35
|
+
})));
|
|
30
36
|
const SignatureParams = Schema.Struct({
|
|
31
37
|
t: TimestampSeconds,
|
|
32
38
|
s: SignatureHex
|
|
@@ -38,7 +44,7 @@ const parseSignatureHeader = (header) => {
|
|
|
38
44
|
t: params.get("t") ?? "",
|
|
39
45
|
s: params.get("s") ?? ""
|
|
40
46
|
};
|
|
41
|
-
return Schema.
|
|
47
|
+
return Schema.decodeUnknownEffect(SignatureParams)(raw).pipe(Effect.mapError(() => new SignatureError({
|
|
42
48
|
reason: "invalid_format",
|
|
43
49
|
message: `Invalid signature format: expected t=<int>&s=<64-hex>, got: ${header}`
|
|
44
50
|
})));
|
|
@@ -47,6 +53,17 @@ const extractKeyBytes = (signingKey) => {
|
|
|
47
53
|
const keyWithoutPrefix = signingKey.replace(/^signkey-\w+-/, "");
|
|
48
54
|
return Buffer.from(keyWithoutPrefix, "hex");
|
|
49
55
|
};
|
|
56
|
+
/**
|
|
57
|
+
* SHA-256 hex of the signing key with the `signkey-{prefix}-` prefix stripped.
|
|
58
|
+
* Used as the Bearer token for outbound requests to the Inngest API per spec
|
|
59
|
+
* §4.1.1.
|
|
60
|
+
*
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
63
|
+
const hashSigningKey = (signingKey) => {
|
|
64
|
+
const keyWithoutPrefix = signingKey.replace(/^signkey-\w+-/, "");
|
|
65
|
+
return Crypto.createHash("sha256").update(keyWithoutPrefix).digest("hex");
|
|
66
|
+
};
|
|
50
67
|
const computeSignature = (keyBytes, body, timestamp) => Crypto.createHmac("sha256", keyBytes).update(body).update(timestamp).digest("hex");
|
|
51
68
|
const timingSafeEqual = (a, b) => {
|
|
52
69
|
if (a.length !== b.length) return false;
|
|
@@ -62,8 +79,8 @@ const checkSignature = (signature, body, timestamp, signingKey) => {
|
|
|
62
79
|
/**
|
|
63
80
|
* @internal
|
|
64
81
|
*/
|
|
65
|
-
const SignatureLive = Layer.
|
|
66
|
-
verify: ({ body, signatureHeader, signingKey, signingKeyFallback, isDev })
|
|
82
|
+
const SignatureLive = Layer.succeed(Signature, {
|
|
83
|
+
verify: Effect.fn("Signature.verify")(function* ({ body, signatureHeader, signingKey, signingKeyFallback, isDev }) {
|
|
67
84
|
if (isDev) return true;
|
|
68
85
|
if (!signingKey) return yield* new SignatureError({
|
|
69
86
|
reason: "missing_signing_key",
|
|
@@ -76,7 +93,7 @@ const SignatureLive = Layer.effect(Signature, Effect.succeed({
|
|
|
76
93
|
const { t: timestampSeconds, s: signature } = yield* parseSignatureHeader(signatureHeader);
|
|
77
94
|
const timestampMs = timestampSeconds * 1e3;
|
|
78
95
|
const now = yield* DateTime.now;
|
|
79
|
-
if (Math.abs(now.
|
|
96
|
+
if (Math.abs(now.epochMilliseconds - timestampMs) > SIGNATURE_VALIDITY_WINDOW_MS) return yield* new SignatureError({
|
|
80
97
|
reason: "expired",
|
|
81
98
|
message: `Signature expired: timestamp ${timestampSeconds} is outside the validity window`
|
|
82
99
|
});
|
|
@@ -88,10 +105,9 @@ const SignatureLive = Layer.effect(Signature, Effect.succeed({
|
|
|
88
105
|
});
|
|
89
106
|
}),
|
|
90
107
|
sign: (body, signingKey) => DateTime.now.pipe(Effect.map((now) => {
|
|
91
|
-
const ts = Math.floor(now.
|
|
108
|
+
const ts = Math.floor(now.epochMilliseconds / 1e3);
|
|
92
109
|
return `t=${ts}&s=${computeSignature(extractKeyBytes(signingKey), body, String(ts))}`;
|
|
93
110
|
}))
|
|
94
|
-
})
|
|
95
|
-
|
|
111
|
+
});
|
|
96
112
|
//#endregion
|
|
97
|
-
export { Signature, SignatureError, SignatureLive };
|
|
113
|
+
export { Signature, SignatureError, SignatureLive, hashSigningKey };
|
package/dist/internal/step.d.ts
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import { InngestFunction } from "../Function.js";
|
|
2
|
-
import "./protocol.js";
|
|
3
2
|
import { SendEventError, StepError } from "./errors.js";
|
|
4
|
-
import "./interrupts.js";
|
|
5
3
|
import * as Duration from "effect/Duration";
|
|
6
|
-
import "effect/Context";
|
|
7
4
|
import * as Effect from "effect/Effect";
|
|
8
5
|
import * as Option from "effect/Option";
|
|
9
6
|
import * as Schema from "effect/Schema";
|
|
10
|
-
import "effect/HashMap";
|
|
11
|
-
import "effect/Ref";
|
|
12
|
-
|
|
13
7
|
//#region src/internal/step.d.ts
|
|
14
8
|
interface StepOptions {
|
|
15
9
|
readonly id: string;
|
|
@@ -17,27 +11,27 @@ interface StepOptions {
|
|
|
17
11
|
}
|
|
18
12
|
type StepOptionsOrId = string | StepOptions;
|
|
19
13
|
interface WaitForEventOptions {
|
|
20
|
-
readonly timeout: Duration.
|
|
14
|
+
readonly timeout: Duration.Input;
|
|
21
15
|
readonly if?: string;
|
|
22
16
|
}
|
|
23
17
|
interface InvokeOptionsBase<F extends InngestFunction.Any> {
|
|
24
18
|
readonly function: F;
|
|
25
19
|
readonly user?: Record<string, unknown>;
|
|
26
20
|
readonly v?: string;
|
|
27
|
-
readonly timeout?: Duration.
|
|
21
|
+
readonly timeout?: Duration.Input;
|
|
28
22
|
}
|
|
29
23
|
type InvokeOptions<F extends InngestFunction.Any> = [InngestFunction.EventType<F>] extends [never] ? InvokeOptionsBase<F> : InvokeOptionsBase<F> & {
|
|
30
24
|
readonly data: InngestFunction.EventType<F>;
|
|
31
25
|
};
|
|
32
|
-
type EventSchema = Schema.
|
|
33
|
-
readonly
|
|
26
|
+
type EventSchema = Schema.Top & {
|
|
27
|
+
readonly identifier: string;
|
|
34
28
|
};
|
|
35
29
|
type TaggedEvent = {
|
|
36
30
|
readonly _tag: string;
|
|
37
31
|
};
|
|
38
32
|
interface StepTools {
|
|
39
33
|
readonly run: <A, Err, R>(id: StepOptionsOrId, effect: Effect.Effect<A, Err, R>) => Effect.Effect<A, StepError | Err, R>;
|
|
40
|
-
readonly sleep: (id: StepOptionsOrId, duration: Duration.
|
|
34
|
+
readonly sleep: (id: StepOptionsOrId, duration: Duration.Input) => Effect.Effect<void>;
|
|
41
35
|
readonly sleepUntil: (id: StepOptionsOrId, timestamp: Date | number | string) => Effect.Effect<void>;
|
|
42
36
|
readonly waitForEvent: <E extends EventSchema>(id: StepOptionsOrId, event: E, options: WaitForEventOptions) => Effect.Effect<Option.Option<Schema.Schema.Type<E>>>;
|
|
43
37
|
readonly invoke: <F extends InngestFunction.Any>(id: StepOptionsOrId, options: InvokeOptions<F>) => Effect.Effect<InngestFunction.Success<F>, StepError>;
|