Skip to content

Commit 2093f17

Browse files
committed
fix: infer return type of Resource.provider.effect
1 parent 8a7a72f commit 2093f17

File tree

8 files changed

+47
-64
lines changed

8 files changed

+47
-64
lines changed

alchemy-effect-aws/src/lambda/function.provider.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { FileSystem } from "@effect/platform";
55
import * as Effect from "effect/Effect";
66
import * as Schedule from "effect/Schedule";
77

8-
import { App, DotAlchemy, type ProviderService } from "@alchemy.run/effect";
8+
import { App, DotAlchemy } from "@alchemy.run/effect";
99

1010
import type {
1111
CreateFunctionUrlConfigRequest,
@@ -362,7 +362,7 @@ export const functionProvider = () =>
362362
}`;
363363

364364
return {
365-
type: "AWS.Lambda.Function",
365+
// type: "AWS.Lambda.Function",
366366
read: Effect.fn(function* ({ id, output }) {
367367
if (output) {
368368
// example: refresh the function URL from the API
@@ -402,6 +402,7 @@ export const functionProvider = () =>
402402
return { action: "noop" };
403403
}),
404404
create: Effect.fn(function* ({ id, news, bindings, session }) {
405+
console.log({ id, news, bindings, session });
405406
const roleName = createRoleName(id);
406407
const policyName = createPolicyName(id);
407408
// const policyArn = `arn:aws:iam::${accountId}:policy/${policyName}`;
@@ -558,6 +559,6 @@ export const functionProvider = () =>
558559
.pipe(Effect.catchTag("NoSuchEntityException", () => Effect.void));
559560
return null as any;
560561
}),
561-
} as any satisfies ProviderService<Function>;
562+
};
562563
}),
563564
);

alchemy-effect-aws/src/lambda/function.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import type * as IAM from "../iam.ts";
44
export type { Context } from "aws-lambda";
55

66
export interface FunctionProps<Req = any> {
7+
functionName?: string;
8+
functionArn?: string;
79
main: string;
810
handler?: string;
911
memory?: number;

alchemy-effect/src/apply.ts

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import * as Effect from "effect/Effect";
33
import * as Option from "effect/Option";
44
import type { Simplify } from "effect/Types";
55
import { PlanReviewer, type PlanRejected } from "./approve.ts";
6-
import type { Capability } from "./capability.ts";
76
import type { ApplyEvent, ApplyStatus } from "./event.ts";
87
import { type BindNode, type CRUD, type Delete, type Plan } from "./plan.ts";
98
import type { Resource } from "./resource.ts";
@@ -77,28 +76,12 @@ export const apply = <P extends Plan, Err, Req>(
7776
status: node.action === "create" ? "created" : "updated",
7877
props: node.resource.props,
7978
output,
80-
bindings: node.bindings.map((binding) => ({
81-
...binding,
82-
resource: {
83-
type: binding.binding.capability.resource.type,
84-
id: binding.binding.capability.resource.id,
85-
},
86-
})),
79+
bindings: node.bindings,
8780
})
8881
.pipe(Effect.map(() => output)),
8982
),
9083
);
9184

92-
const hydrate = <A extends BindNode>(bindings: Capability[]) =>
93-
node.bindings.map(
94-
(binding, i) =>
95-
Object.assign(binding, {
96-
attributes: bindings[i],
97-
}) as A & {
98-
attributes: any;
99-
},
100-
);
101-
10285
const id = node.resource.id;
10386

10487
const scopedSession = {
@@ -130,7 +113,7 @@ export const apply = <P extends Plan, Err, Req>(
130113
.create({
131114
id,
132115
news: node.news,
133-
bindings: hydrate(bindings),
116+
bindings,
134117
session: scopedSession,
135118
})
136119
.pipe(
@@ -146,7 +129,7 @@ export const apply = <P extends Plan, Err, Req>(
146129
news: node.news,
147130
olds: node.olds,
148131
output: node.output,
149-
bindings: hydrate(bindings),
132+
bindings,
150133
session: scopedSession,
151134
})
152135
.pipe(
@@ -198,7 +181,7 @@ export const apply = <P extends Plan, Err, Req>(
198181
id,
199182
news: node.news,
200183
// TODO(sam): these need to only include attach actions
201-
bindings: hydrate(yield* apply(node.bindings)),
184+
bindings: yield* apply(node.bindings),
202185
session: scopedSession,
203186
})
204187
// TODO(sam): delete and create will conflict here, we need to extend the state store for replace

alchemy-effect/src/plan.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { Capability } from "./capability.ts";
55
import type { Phase } from "./phase.ts";
66
import type { Instance } from "./policy.ts";
77
import { type ProviderService } from "./provider.ts";
8-
import type { IResource, Resource } from "./resource.ts";
8+
import type { Resource, ResourceTags } from "./resource.ts";
99
import { isService, type Service } from "./service.ts";
1010
import { State, type ResourceState } from "./state.ts";
1111

@@ -59,14 +59,14 @@ export const isCRUD = (node: any): node is CRUD => {
5959
/**
6060
* A node in the plan that represents a resource CRUD operation.
6161
*/
62-
export type CRUD<R extends IResource = IResource> =
62+
export type CRUD<R extends Resource = Resource> =
6363
| Create<R>
6464
| Update<R>
6565
| Delete<R>
6666
| Replace<R>
6767
| NoopUpdate<R>;
6868

69-
export type Apply<R extends IResource = IResource> =
69+
export type Apply<R extends Resource = Resource> =
7070
| Create<R>
7171
| Update<R>
7272
| Replace<R>
@@ -82,7 +82,7 @@ const Node = <T extends Apply>(node: T) => ({
8282
},
8383
});
8484

85-
export type Create<R extends IResource> = {
85+
export type Create<R extends Resource> = {
8686
action: "create";
8787
resource: R;
8888
news: any;
@@ -91,7 +91,7 @@ export type Create<R extends IResource> = {
9191
bindings: BindNode[];
9292
};
9393

94-
export type Update<R extends IResource> = {
94+
export type Update<R extends Resource> = {
9595
action: "update";
9696
resource: R;
9797
olds: any;
@@ -102,7 +102,7 @@ export type Update<R extends IResource> = {
102102
bindings: BindNode[];
103103
};
104104

105-
export type Delete<R extends IResource> = {
105+
export type Delete<R extends Resource> = {
106106
action: "delete";
107107
resource: R;
108108
olds: any;
@@ -113,14 +113,14 @@ export type Delete<R extends IResource> = {
113113
downstream: string[];
114114
};
115115

116-
export type NoopUpdate<R extends IResource> = {
116+
export type NoopUpdate<R extends Resource> = {
117117
action: "noop";
118118
resource: R;
119119
attributes: R["attr"];
120120
bindings: BindNode[];
121121
};
122122

123-
export type Replace<R extends IResource> = {
123+
export type Replace<R extends Resource> = {
124124
action: "replace";
125125
resource: R;
126126
olds: any;
@@ -222,7 +222,9 @@ export const plan = <
222222
.map(
223223
Effect.fn(function* (node) {
224224
const id = node.id;
225-
const resource = node;
225+
const resource = node as Resource & {
226+
provider: ResourceTags<Resource>;
227+
};
226228
const news = isService(node)
227229
? node.runtime.props
228230
: resource.props;
@@ -355,7 +357,7 @@ export const plan = <
355357
type: oldState.type,
356358
attr: oldState.output,
357359
props: oldState.props,
358-
} satisfies IResource as Resource,
360+
} as Resource,
359361
downstream: downstream[id] ?? [],
360362
} satisfies Delete<Resource>,
361363
] as const;

alchemy-effect/src/resource.ts

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,28 @@ import type { Effect } from "effect/Effect";
33
import * as Layer from "effect/Layer";
44
import type { Provider, ProviderService } from "./provider.ts";
55

6-
export interface IResource<
6+
export interface Resource<
77
Type extends string = string,
88
ID extends string = string,
9-
Props = any,
10-
Attrs = any,
9+
Props = unknown,
10+
Attrs = unknown,
1111
> {
1212
id: ID;
1313
type: Type;
1414
props: Props;
1515
attr: Attrs;
1616
parent: unknown;
17-
}
18-
export interface Resource<
19-
Type extends string = string,
20-
ID extends string = string,
21-
Props = unknown,
22-
Attrs = unknown,
23-
> extends IResource<Type, ID, Props, Attrs> {
2417
// oxlint-disable-next-line no-misused-new
2518
new (): Resource<Type, ID, Props, Attrs>;
26-
provider: {
27-
tag: Context.TagClass<Provider<Resource>, Type, ProviderService<Resource>>;
28-
effect<Err, Req>(
29-
eff: Effect<ProviderService<Resource>, Err, Req>,
30-
): Layer.Layer<Provider<Resource>, Err, Req>;
31-
succeed(
32-
service: ProviderService<Resource>,
33-
): Layer.Layer<Provider<Resource>>;
34-
};
19+
}
20+
21+
export interface ResourceTags<R extends Resource> {
22+
of<S extends ProviderService<R>>(service: S): S;
23+
tag: Context.TagClass<Provider<R>, R["type"], ProviderService<R>>;
24+
effect<Err, Req>(
25+
eff: Effect<ProviderService<R>, Err, Req>,
26+
): Layer.Layer<Provider<R>, Err, Req>;
27+
succeed(service: ProviderService<R>): Layer.Layer<Provider<R>>;
3528
}
3629

3730
export const Resource = <Ctor extends (id: string, props: any) => Resource>(

alchemy-effect/src/runtime.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as Layer from "effect/Layer";
55
import type { Capability } from "./capability.ts";
66
import type { Policy } from "./policy.ts";
77
import type { ProviderService } from "./provider.ts";
8-
import type { Resource } from "./resource.ts";
8+
import type { Resource, ResourceTags } from "./resource.ts";
99
import type { IService, Service } from "./service.ts";
1010

1111
export type RuntimeHandler<
@@ -52,7 +52,6 @@ export interface Runtime<
5252
binding: unknown;
5353
/** @internal phantom */
5454
capability: unknown;
55-
new (): {};
5655
<
5756
const ID extends string,
5857
Inputs extends any[],
@@ -69,15 +68,18 @@ export interface Runtime<
6968
) => Service<ID, this, Handler, Props>;
7069
}
7170

71+
// export interface IRuntime<
72+
// Type extends string = string,
73+
// Handler = unknown,
74+
// Props = unknown,
75+
// > extends IResource<Type, string, Props, unknown> {
76+
77+
// }
78+
7279
export const Runtime =
7380
<const Type extends string>(type: Type) =>
7481
<Self extends Runtime>(): Self & {
75-
provider: {
76-
effect<Err, Req>(
77-
eff: Effect<ProviderService<Self>, Err, Req>,
78-
): Layer.Layer<Self, Err, Req>;
79-
succeed(service: ProviderService<Self>): Layer.Layer<Self>;
80-
};
82+
provider: ResourceTags<Self>;
8183
} => {
8284
const Tag = Context.Tag(type)();
8385
const provider = {

alchemy-effect/src/service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { IResource, Resource } from "./resource.ts";
1+
import type { Resource } from "./resource.ts";
22
import type { Runtime, RuntimeHandler, RuntimeProps } from "./runtime.ts";
33

44
export interface IService<
@@ -7,7 +7,7 @@ export interface IService<
77
Handler extends RuntimeHandler = RuntimeHandler,
88
Props extends RuntimeProps<F, any> = RuntimeProps<F, any>,
99
Attr = (F & { props: Props })["attr"],
10-
> extends IResource<F["type"], ID, Props, Attr> {
10+
> extends Resource<F["type"], ID, Props, Attr> {
1111
kind: "Service";
1212
type: F["type"];
1313
id: ID;

example/alchemy.run.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const stack = await plan.pipe(
2727
Effect.provide(NodeContext.layer),
2828
Effect.provide(FetchHttpClient.layer),
2929
Effect.tap((stack) => Effect.log(stack?.Api.functionUrl)),
30-
Effect.runPromiseExit,
30+
Effect.runPromise,
3131
);
3232

3333
if (stack) {

0 commit comments

Comments
 (0)