ff-effect 0.0.10 → 0.0.11
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/for/ai/index.cjs +167 -0
- package/dist/for/ai/index.cjs.map +1 -0
- package/dist/for/ai/index.d.cts +47 -0
- package/dist/for/ai/index.d.ts +47 -0
- package/dist/for/ai/index.js +127 -0
- package/dist/for/ai/index.js.map +1 -0
- package/dist/for/drizzle/index.cjs.map +1 -1
- package/dist/for/drizzle/index.js.map +1 -1
- package/dist/for/inngest/index.cjs +3021 -0
- package/dist/for/inngest/index.cjs.map +1 -0
- package/dist/for/inngest/index.d.cts +78 -0
- package/dist/for/inngest/index.d.ts +78 -0
- package/dist/for/inngest/index.js +2987 -0
- package/dist/for/inngest/index.js.map +1 -0
- package/dist/for/orpc/index.cjs.map +1 -1
- package/dist/for/orpc/index.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +16 -3
- package/src/extract.ts +7 -6
- package/src/for/ai/index.test.ts +244 -0
- package/src/for/ai/index.ts +200 -0
- package/src/for/ai/schema.ts +20 -0
- package/src/for/drizzle/index.test.ts +1 -1
- package/src/for/drizzle/index.ts +1 -0
- package/src/for/inngest/cron.ts +21 -0
- package/src/for/inngest/index.test.ts +177 -0
- package/src/for/inngest/index.ts +187 -0
- package/src/for/inngest/step.ts +77 -0
- package/src/run-promise-unwrapped.ts +2 -2
- package/src/wrap-client.ts +1 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Cron } from 'effect';
|
|
2
|
+
import { Option } from 'effect';
|
|
3
|
+
|
|
4
|
+
function fieldToString(field: ReadonlySet<number>, max: number) {
|
|
5
|
+
if (field.size === 0 || field.size === max) return '*';
|
|
6
|
+
return Array.from(field)
|
|
7
|
+
.sort((a, b) => a - b)
|
|
8
|
+
.join(',');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function cronToString(cron: Cron.Cron) {
|
|
12
|
+
const minutes = fieldToString(cron.minutes, 60);
|
|
13
|
+
const hours = fieldToString(cron.hours, 24);
|
|
14
|
+
const days = fieldToString(cron.days, 31);
|
|
15
|
+
const months = fieldToString(cron.months, 12);
|
|
16
|
+
const weekdays = fieldToString(cron.weekdays, 7);
|
|
17
|
+
|
|
18
|
+
const expr = `${minutes} ${hours} ${days} ${months} ${weekdays}`;
|
|
19
|
+
if (Option.isSome(cron.tz)) return `TZ=${cron.tz.value} ${expr}`;
|
|
20
|
+
return expr;
|
|
21
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { Cron, Duration, Effect } from 'effect';
|
|
2
|
+
import { EventSchemas, Inngest as InngestSdk } from 'inngest';
|
|
3
|
+
import { describe, expect, expectTypeOf, test, vi } from 'vitest';
|
|
4
|
+
import { cronToString } from './cron';
|
|
5
|
+
import { createInngest } from './index';
|
|
6
|
+
import { wrapStep } from './step';
|
|
7
|
+
|
|
8
|
+
describe('cronToString', () => {
|
|
9
|
+
test('converts simple cron', () => {
|
|
10
|
+
const cron = Cron.unsafeParse('5 4 * * *');
|
|
11
|
+
expect(cronToString(cron)).toBe('5 4 * * *');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('converts cron with all fields', () => {
|
|
15
|
+
const cron = Cron.unsafeParse('0 12 1 6 3');
|
|
16
|
+
expect(cronToString(cron)).toBe('0 12 1 6 3');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('converts every-minute cron', () => {
|
|
20
|
+
const cron = Cron.unsafeParse('* * * * *');
|
|
21
|
+
expect(cronToString(cron)).toBe('* * * * *');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('converts cron with multiple values', () => {
|
|
25
|
+
const cron = Cron.unsafeParse('0,30 * * * *');
|
|
26
|
+
expect(cronToString(cron)).toBe('0,30 * * * *');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('wrapStep', () => {
|
|
31
|
+
const mockStep = {
|
|
32
|
+
run: vi.fn(),
|
|
33
|
+
sleep: vi.fn(),
|
|
34
|
+
sleepUntil: vi.fn(),
|
|
35
|
+
invoke: vi.fn(),
|
|
36
|
+
waitForEvent: vi.fn(),
|
|
37
|
+
sendEvent: vi.fn(),
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
test('step.run executes Effect callback', async () => {
|
|
41
|
+
mockStep.run.mockImplementation((_id: string, fn: () => Promise<unknown>) =>
|
|
42
|
+
fn(),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const wrapped = wrapStep(mockStep);
|
|
46
|
+
const result = await Effect.runPromise(
|
|
47
|
+
wrapped.run('test', () => Effect.succeed(42)),
|
|
48
|
+
);
|
|
49
|
+
expect(result).toBe(42);
|
|
50
|
+
expect(mockStep.run).toHaveBeenCalledWith('test', expect.any(Function));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('step.run wraps errors in InngestError', async () => {
|
|
54
|
+
mockStep.run.mockRejectedValue(new Error('boom'));
|
|
55
|
+
|
|
56
|
+
const wrapped = wrapStep(mockStep);
|
|
57
|
+
const exit = await Effect.runPromiseExit(
|
|
58
|
+
wrapped.run('fail', () => Effect.succeed(1)),
|
|
59
|
+
);
|
|
60
|
+
expect(exit._tag).toBe('Failure');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('step.sleep converts Duration to ms', async () => {
|
|
64
|
+
mockStep.sleep.mockResolvedValue(undefined);
|
|
65
|
+
|
|
66
|
+
const wrapped = wrapStep(mockStep);
|
|
67
|
+
await Effect.runPromise(wrapped.sleep('wait', Duration.hours(1)));
|
|
68
|
+
expect(mockStep.sleep).toHaveBeenCalledWith('wait', 3600000);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('step.sleepUntil passes through', async () => {
|
|
72
|
+
mockStep.sleepUntil.mockResolvedValue(undefined);
|
|
73
|
+
|
|
74
|
+
const wrapped = wrapStep(mockStep);
|
|
75
|
+
const date = new Date('2024-01-01');
|
|
76
|
+
await Effect.runPromise(wrapped.sleepUntil('until', date));
|
|
77
|
+
expect(mockStep.sleepUntil).toHaveBeenCalledWith('until', date);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('step.sendEvent returns ids', async () => {
|
|
81
|
+
mockStep.sendEvent.mockResolvedValue({ ids: ['id1'] });
|
|
82
|
+
|
|
83
|
+
const wrapped = wrapStep(mockStep);
|
|
84
|
+
const result = await Effect.runPromise(
|
|
85
|
+
wrapped.sendEvent('send', { name: 'test', data: {} }),
|
|
86
|
+
);
|
|
87
|
+
expect(result).toEqual({ ids: ['id1'] });
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('createInngest', () => {
|
|
92
|
+
test('creates builder with Tag and layer', () => {
|
|
93
|
+
const client = new InngestSdk({ id: 'test' });
|
|
94
|
+
const Inngest = createInngest(Effect.succeed(client));
|
|
95
|
+
|
|
96
|
+
expect(Inngest.Tag).toBeDefined();
|
|
97
|
+
expect(Inngest.layer).toBeDefined();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('custom tagId', () => {
|
|
101
|
+
const client = new InngestSdk({ id: 'test' });
|
|
102
|
+
const Inngest = createInngest(Effect.succeed(client), {
|
|
103
|
+
tagId: 'MyInngest',
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(Inngest.Tag).toBeDefined();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('createFunction returns an Effect', async () => {
|
|
110
|
+
const client = new InngestSdk({ id: 'test' });
|
|
111
|
+
const Inngest = createInngest(Effect.succeed(client));
|
|
112
|
+
|
|
113
|
+
const fnEffect = Inngest.createFunction(
|
|
114
|
+
{ id: 'my-fn' },
|
|
115
|
+
{ event: 'test/event' },
|
|
116
|
+
() => Effect.succeed('done'),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const fn = await Effect.runPromise(
|
|
120
|
+
fnEffect.pipe(Effect.provide(Inngest.layer), Effect.scoped),
|
|
121
|
+
);
|
|
122
|
+
expect(fn).toBeDefined();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('fetchHandler returns a fetch handler', async () => {
|
|
126
|
+
const client = new InngestSdk({ id: 'test' });
|
|
127
|
+
const Inngest = createInngest(Effect.succeed(client));
|
|
128
|
+
|
|
129
|
+
const handler = await Effect.runPromise(
|
|
130
|
+
Inngest.fetchHandler({ functions: [] }).pipe(
|
|
131
|
+
Effect.provide(Inngest.layer),
|
|
132
|
+
),
|
|
133
|
+
);
|
|
134
|
+
expect(typeof handler).toBe('function');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('httpHandler returns an Effect HttpApp', async () => {
|
|
138
|
+
const client = new InngestSdk({ id: 'test' });
|
|
139
|
+
const Inngest = createInngest(Effect.succeed(client));
|
|
140
|
+
|
|
141
|
+
const app = await Effect.runPromise(
|
|
142
|
+
Inngest.httpHandler({ functions: [] }).pipe(
|
|
143
|
+
Effect.provide(Inngest.layer),
|
|
144
|
+
),
|
|
145
|
+
);
|
|
146
|
+
expect(Effect.isEffect(app)).toBe(true);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test('event schema types flow through', async () => {
|
|
150
|
+
type Data = { email: string };
|
|
151
|
+
|
|
152
|
+
const client = new InngestSdk({
|
|
153
|
+
id: 'typed',
|
|
154
|
+
schemas: new EventSchemas().fromRecord<{
|
|
155
|
+
'user.signup': { data: Data };
|
|
156
|
+
}>(),
|
|
157
|
+
});
|
|
158
|
+
const Inngest = createInngest(Effect.succeed(client));
|
|
159
|
+
|
|
160
|
+
const fnEffect = Inngest.createFunction(
|
|
161
|
+
{ id: 'on-signup' },
|
|
162
|
+
{ event: 'user.signup' },
|
|
163
|
+
({ event }) => {
|
|
164
|
+
expectTypeOf(event).toHaveProperty('name');
|
|
165
|
+
expectTypeOf(event).toHaveProperty('data');
|
|
166
|
+
expectTypeOf(event.data).toEqualTypeOf<Data>();
|
|
167
|
+
const email: string = event.data.email;
|
|
168
|
+
return Effect.succeed(email);
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
const fn = await Effect.runPromise(
|
|
173
|
+
fnEffect.pipe(Effect.provide(Inngest.layer), Effect.scoped),
|
|
174
|
+
);
|
|
175
|
+
expect(fn).toBeDefined();
|
|
176
|
+
});
|
|
177
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { HttpApp } from '@effect/platform';
|
|
2
|
+
import { type Cron, Data, Effect, FiberSet, Layer } from 'effect';
|
|
3
|
+
import * as Context from 'effect/Context';
|
|
4
|
+
import * as Inspectable from 'effect/Inspectable';
|
|
5
|
+
import type { GetEvents, GetFunctionInput, Inngest } from 'inngest';
|
|
6
|
+
import { serve } from 'inngest/bun';
|
|
7
|
+
import { extract } from '../../extract';
|
|
8
|
+
import { cronToString } from './cron';
|
|
9
|
+
import { wrapStep } from './step';
|
|
10
|
+
|
|
11
|
+
export const TagTypeId = Context.TagTypeId;
|
|
12
|
+
export const NodeInspectSymbol = Inspectable.NodeInspectSymbol;
|
|
13
|
+
|
|
14
|
+
export class InngestError extends Data.TaggedError('ff-effect/InngestError')<{
|
|
15
|
+
message: string;
|
|
16
|
+
cause?: unknown;
|
|
17
|
+
}> {}
|
|
18
|
+
|
|
19
|
+
declare const InngestFunctionBrand: unique symbol;
|
|
20
|
+
|
|
21
|
+
/** Opaque wrapper around inngest's InngestFunction to avoid leaking internal types */
|
|
22
|
+
export type InngestFunction = { readonly [InngestFunctionBrand]: true };
|
|
23
|
+
|
|
24
|
+
// biome-ignore lint/suspicious/noExplicitAny: matches Inngest.Any
|
|
25
|
+
type AnyInngest = Inngest<any>;
|
|
26
|
+
|
|
27
|
+
type CreateFunctionParams<TClient extends AnyInngest> = Parameters<
|
|
28
|
+
TClient['createFunction']
|
|
29
|
+
>;
|
|
30
|
+
|
|
31
|
+
type FunctionConfig<TClient extends AnyInngest> =
|
|
32
|
+
CreateFunctionParams<TClient>[0];
|
|
33
|
+
|
|
34
|
+
type FunctionTrigger<TClient extends AnyInngest> =
|
|
35
|
+
CreateFunctionParams<TClient>[1];
|
|
36
|
+
|
|
37
|
+
type TriggerInput<TClient extends AnyInngest> =
|
|
38
|
+
| FunctionTrigger<TClient>
|
|
39
|
+
| CronTrigger;
|
|
40
|
+
|
|
41
|
+
type ExtractTriggerName<TClient extends AnyInngest, T> = T extends {
|
|
42
|
+
event: infer E extends keyof GetEvents<TClient, true> & string;
|
|
43
|
+
}
|
|
44
|
+
? E
|
|
45
|
+
: keyof GetEvents<TClient, true> & string;
|
|
46
|
+
|
|
47
|
+
type CronTrigger = { cron: Cron.Cron };
|
|
48
|
+
|
|
49
|
+
function isCronTrigger(trigger: unknown): trigger is CronTrigger {
|
|
50
|
+
return (
|
|
51
|
+
typeof trigger === 'object' &&
|
|
52
|
+
trigger !== null &&
|
|
53
|
+
'cron' in trigger &&
|
|
54
|
+
typeof (trigger as CronTrigger).cron === 'object' &&
|
|
55
|
+
(trigger as CronTrigger).cron !== null &&
|
|
56
|
+
'minutes' in (trigger as CronTrigger).cron
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function resolveTrigger<TClient extends AnyInngest>(
|
|
61
|
+
trigger: TriggerInput<TClient>,
|
|
62
|
+
): FunctionTrigger<TClient> {
|
|
63
|
+
if (Array.isArray(trigger)) {
|
|
64
|
+
return trigger.map((t) =>
|
|
65
|
+
resolveTrigger<TClient>(t),
|
|
66
|
+
) as FunctionTrigger<TClient>;
|
|
67
|
+
}
|
|
68
|
+
if (isCronTrigger(trigger)) {
|
|
69
|
+
return { cron: cronToString(trigger.cron) } as FunctionTrigger<TClient>;
|
|
70
|
+
}
|
|
71
|
+
return trigger as FunctionTrigger<TClient>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
type EffectHandlerCtx<
|
|
75
|
+
TClient extends AnyInngest,
|
|
76
|
+
TTriggerName extends keyof GetEvents<TClient, true> &
|
|
77
|
+
string = keyof GetEvents<TClient, true> & string,
|
|
78
|
+
> = Omit<GetFunctionInput<TClient, TTriggerName>, 'step'> & {
|
|
79
|
+
step: ReturnType<typeof wrapStep<unknown>>;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const defaultPrefix = '@ff-effect/Inngest' as const;
|
|
83
|
+
|
|
84
|
+
export function createInngest<
|
|
85
|
+
TClient extends AnyInngest,
|
|
86
|
+
E,
|
|
87
|
+
R,
|
|
88
|
+
T extends string = typeof defaultPrefix,
|
|
89
|
+
>(createClient: Effect.Effect<TClient, E, R>, opts?: { tagId?: T }) {
|
|
90
|
+
const tagId = (opts?.tagId ?? defaultPrefix) as T;
|
|
91
|
+
|
|
92
|
+
type Tag = typeof tagId;
|
|
93
|
+
const Tag = Context.Tag(tagId)<Tag, TClient>();
|
|
94
|
+
|
|
95
|
+
const send = (
|
|
96
|
+
payload: Parameters<TClient['send']>[0],
|
|
97
|
+
): Effect.Effect<{ ids: string[] }, InngestError, Tag> =>
|
|
98
|
+
Effect.gen(function* () {
|
|
99
|
+
const c = yield* Tag;
|
|
100
|
+
return yield* Effect.tryPromise({
|
|
101
|
+
// @ts-expect-error inngest generic variance issue between constrained and inferred client types
|
|
102
|
+
try: () => c.send(payload) as Promise<{ ids: string[] }>,
|
|
103
|
+
catch: (cause) =>
|
|
104
|
+
new InngestError({ message: 'Failed to send event', cause }),
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const createFunction = <TTrigger extends TriggerInput<TClient>, A, EH, RH>(
|
|
109
|
+
config: FunctionConfig<TClient>,
|
|
110
|
+
trigger: TTrigger,
|
|
111
|
+
handler: (
|
|
112
|
+
ctx: EffectHandlerCtx<TClient, ExtractTriggerName<TClient, TTrigger>>,
|
|
113
|
+
) => Effect.Effect<A, EH, RH>,
|
|
114
|
+
) =>
|
|
115
|
+
Effect.gen(function* () {
|
|
116
|
+
const c = yield* Tag;
|
|
117
|
+
const ext_handler = yield* extract(handler);
|
|
118
|
+
const resolvedTrigger = resolveTrigger<TClient>(trigger);
|
|
119
|
+
const runPromise = yield* FiberSet.makeRuntimePromise();
|
|
120
|
+
|
|
121
|
+
return c.createFunction(
|
|
122
|
+
config,
|
|
123
|
+
resolvedTrigger,
|
|
124
|
+
// biome-ignore lint/suspicious/noExplicitAny: inngest middleware produces unresolvable context type
|
|
125
|
+
async (ctx: any) => {
|
|
126
|
+
const effectStep = wrapStep(ctx.step);
|
|
127
|
+
return runPromise(
|
|
128
|
+
ext_handler({
|
|
129
|
+
...ctx,
|
|
130
|
+
step: effectStep,
|
|
131
|
+
} as unknown as EffectHandlerCtx<
|
|
132
|
+
TClient,
|
|
133
|
+
ExtractTriggerName<TClient, TTrigger>
|
|
134
|
+
>),
|
|
135
|
+
);
|
|
136
|
+
},
|
|
137
|
+
) as unknown as InngestFunction;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
type ServeOpts = {
|
|
141
|
+
functions: InngestFunction[];
|
|
142
|
+
servePath?: string;
|
|
143
|
+
signingKey?: string;
|
|
144
|
+
signingKeyFallback?: string;
|
|
145
|
+
logLevel?: 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'silent';
|
|
146
|
+
streaming?: 'allow' | 'force' | false;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
function buildServe(client: TClient, httpOpts: ServeOpts) {
|
|
150
|
+
return serve({
|
|
151
|
+
client,
|
|
152
|
+
functions: httpOpts.functions as unknown as Parameters<
|
|
153
|
+
typeof serve
|
|
154
|
+
>[0]['functions'],
|
|
155
|
+
...(httpOpts.servePath != null && { servePath: httpOpts.servePath }),
|
|
156
|
+
...(httpOpts.signingKey != null && {
|
|
157
|
+
signingKey: httpOpts.signingKey,
|
|
158
|
+
}),
|
|
159
|
+
...(httpOpts.signingKeyFallback != null && {
|
|
160
|
+
signingKeyFallback: httpOpts.signingKeyFallback,
|
|
161
|
+
}),
|
|
162
|
+
...(httpOpts.logLevel != null && { logLevel: httpOpts.logLevel }),
|
|
163
|
+
...(httpOpts.streaming != null && { streaming: httpOpts.streaming }),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const fetchHandler = (httpOpts: ServeOpts) =>
|
|
168
|
+
Effect.gen(function* () {
|
|
169
|
+
const c = yield* Tag;
|
|
170
|
+
return buildServe(c, httpOpts);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const httpHandler = (httpOpts: ServeOpts) =>
|
|
174
|
+
Effect.gen(function* () {
|
|
175
|
+
const c = yield* Tag;
|
|
176
|
+
return HttpApp.fromWebHandler(buildServe(c, httpOpts));
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
Tag,
|
|
181
|
+
layer: Layer.effect(Tag, createClient),
|
|
182
|
+
createFunction,
|
|
183
|
+
send,
|
|
184
|
+
fetchHandler,
|
|
185
|
+
httpHandler,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Duration, Effect } from 'effect';
|
|
2
|
+
import { runPromiseUnwrapped } from '../../run-promise-unwrapped';
|
|
3
|
+
import { InngestError } from './index';
|
|
4
|
+
|
|
5
|
+
type OriginalStep = {
|
|
6
|
+
run: (id: string, fn: () => Promise<unknown>) => Promise<unknown>;
|
|
7
|
+
sleep: (id: string, time: number | string) => Promise<void>;
|
|
8
|
+
sleepUntil: (id: string, time: Date | string) => Promise<void>;
|
|
9
|
+
invoke: (id: string, opts: unknown) => Promise<unknown>;
|
|
10
|
+
waitForEvent: (id: string, opts: unknown) => Promise<unknown>;
|
|
11
|
+
sendEvent: (id: string, payload: unknown) => Promise<unknown>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type WrappedStep<TStep> = ReturnType<typeof wrapStep<TStep>>;
|
|
15
|
+
|
|
16
|
+
export function wrapStep<TStep>(step: TStep) {
|
|
17
|
+
const s = step as unknown as OriginalStep;
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
run: <A, E>(id: string, fn: () => Effect.Effect<A, E, never>) =>
|
|
21
|
+
Effect.tryPromise({
|
|
22
|
+
try: () => s.run(id, () => runPromiseUnwrapped(fn())),
|
|
23
|
+
catch: (cause) =>
|
|
24
|
+
new InngestError({ message: `Step "${id}" failed`, cause }),
|
|
25
|
+
}) as Effect.Effect<A, InngestError>,
|
|
26
|
+
|
|
27
|
+
sleep: (id: string, duration: Duration.DurationInput) =>
|
|
28
|
+
Effect.tryPromise({
|
|
29
|
+
try: () => s.sleep(id, Duration.toMillis(Duration.decode(duration))),
|
|
30
|
+
catch: (cause) =>
|
|
31
|
+
new InngestError({
|
|
32
|
+
message: `Step sleep "${id}" failed`,
|
|
33
|
+
cause,
|
|
34
|
+
}),
|
|
35
|
+
}),
|
|
36
|
+
|
|
37
|
+
sleepUntil: (id: string, time: Date | string) =>
|
|
38
|
+
Effect.tryPromise({
|
|
39
|
+
try: () => s.sleepUntil(id, time),
|
|
40
|
+
catch: (cause) =>
|
|
41
|
+
new InngestError({
|
|
42
|
+
message: `Step sleepUntil "${id}" failed`,
|
|
43
|
+
cause,
|
|
44
|
+
}),
|
|
45
|
+
}),
|
|
46
|
+
|
|
47
|
+
invoke: <TResult = unknown>(id: string, opts: unknown) =>
|
|
48
|
+
Effect.tryPromise({
|
|
49
|
+
try: () => s.invoke(id, opts),
|
|
50
|
+
catch: (cause) =>
|
|
51
|
+
new InngestError({
|
|
52
|
+
message: `Step invoke "${id}" failed`,
|
|
53
|
+
cause,
|
|
54
|
+
}),
|
|
55
|
+
}) as Effect.Effect<TResult, InngestError>,
|
|
56
|
+
|
|
57
|
+
waitForEvent: <TEvent = unknown>(id: string, opts: unknown) =>
|
|
58
|
+
Effect.tryPromise({
|
|
59
|
+
try: () => s.waitForEvent(id, opts),
|
|
60
|
+
catch: (cause) =>
|
|
61
|
+
new InngestError({
|
|
62
|
+
message: `Step waitForEvent "${id}" failed`,
|
|
63
|
+
cause,
|
|
64
|
+
}),
|
|
65
|
+
}) as Effect.Effect<TEvent | null, InngestError>,
|
|
66
|
+
|
|
67
|
+
sendEvent: (id: string, payload: unknown) =>
|
|
68
|
+
Effect.tryPromise({
|
|
69
|
+
try: () => s.sendEvent(id, payload),
|
|
70
|
+
catch: (cause) =>
|
|
71
|
+
new InngestError({
|
|
72
|
+
message: `Step sendEvent "${id}" failed`,
|
|
73
|
+
cause,
|
|
74
|
+
}),
|
|
75
|
+
}) as Effect.Effect<{ ids: string[] }, InngestError>,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Cause, Effect, Exit } from
|
|
1
|
+
import { Cause, Effect, Exit } from 'effect';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A simple wrapper around Effect.runPromiseExit that throws the error if it's a failure
|
|
5
5
|
**/
|
|
6
6
|
export async function runPromiseUnwrapped<A, E>(
|
|
7
|
-
effect: Effect.Effect<A, E, never
|
|
7
|
+
effect: Effect.Effect<A, E, never>,
|
|
8
8
|
) {
|
|
9
9
|
const exit = await Effect.runPromiseExit(effect);
|
|
10
10
|
return Exit.match(exit, {
|
package/src/wrap-client.ts
CHANGED