import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import type { ScopedPlanStatusSession } from "./cli/service.ts"; import type { Diff } from "./diff.ts"; import type { Input } from "./input.ts"; import type { Resource } from "./resource.ts"; import type { Runtime } from "./runtime.ts"; import type { Service } from "./service.ts"; export interface Provider< R extends Resource | Service, > extends Context.TagClass< Provider, R["type"], ProviderService // TODO(sam): we are using any here because the R["type"] is enough and gaining access to the sub type (e.g. SQS.Queue) // is currently not possible in the current approach // preferred: // ProviderService > {} type BindingData = [Res] extends [Runtime] ? Res["binding"][] : any[]; type Props = Input.ResolveOpaque; export interface ProviderService< Res extends Resource = Resource, ReadReq = never, DiffReq = never, PrecreateReq = never, CreateReq = never, UpdateReq = never, DeleteReq = never, > { /** * The version of the provider. * * @default 0 */ version?: number; // tail(); // watch(); // replace(): Effect.Effect; // different interface that is persistent, watching, reloads // run?() {} read?(input: { id: string; instanceId: string; olds: Props; // what is the ARN? output: Res["attr"] | undefined; // current state -> synced state bindings: BindingData; }): Effect.Effect; /** * Properties that are always stable across any update. */ stables?: Extract[]; diff?(input: { id: string; olds: Props; instanceId: string; // Note: we do not resolve (Props) here because diff runs during plan // -> we need a way for the diff handlers to work with Outputs news: Res["props"]; output: Res["attr"]; }): Effect.Effect; precreate?(input: { id: string; news: Props; instanceId: string; session: ScopedPlanStatusSession; }): Effect.Effect; create(input: { id: string; instanceId: string; news: Props; session: ScopedPlanStatusSession; bindings: BindingData; }): Effect.Effect; update(input: { id: string; instanceId: string; news: Props; olds: Props; output: Res["attr"]; session: ScopedPlanStatusSession; bindings: BindingData; }): Effect.Effect; delete(input: { id: string; instanceId: string; olds: Props; output: Res["attr"]; session: ScopedPlanStatusSession; bindings: BindingData; }): Effect.Effect; } export const getProviderByType = Effect.fnUntraced(function* ( resourceType: string, ) { const context = yield* Effect.context(); const provider: ProviderService = context.unsafeMap.get(resourceType); if (!provider) { return yield* Effect.die( new Error(`Provider not found for ${resourceType}`), ); } return provider; });