diff --git a/src/multi_chain.ts b/src/multi_chain.ts index 2c3e81d..99eefb9 100644 --- a/src/multi_chain.ts +++ b/src/multi_chain.ts @@ -3,27 +3,6 @@ import { UnionFromTuple } from "./object_builders.ts" import { Tagged } from "./tagged.ts" import { UPObjectChain, ObjectChainsInputU } from "./object_chain.ts" -// to type the multi-chain handler, need something like -// a conditional type which will look up return types from the program map object - -// takes a list of EventHandlerPrograms and builds a new program which handles any of the events -// handled by the individual programs -// { -// // get output types for any tag -// eventPrograms: {eventTag: program} -// program: (ev) => Effect... -// } -// the event type input to the program will be the union of all the handled event types, -// while the Effect types will be chosen based on the type of the event - basically the type -// of the individual handler program for that event -// -// maybe want a MultiEventHandlerProgram type capturing the above ... and -// which would be composable -// -// the returned program will have type: -// Effect export type ProgramDeps = ReturnType extends Effect.Effect ? R @@ -68,11 +47,11 @@ export type DistributeObjectChainValueTypes +export function multiChainProgram - (eventHandlerPrograms: readonly [...Chains]) { + (chains: readonly [...Chains]) { - const progsByEventTag = eventHandlerPrograms.reduce( + const progsByEventTag = chains.reduce( (m, p) => { m[p.tagStr] = p; return m }, {} as { [index: string]: UPObjectChain }) @@ -91,4 +70,28 @@ export function multiChain } else throw "NoProgram for tag: " + i.tag } -} \ No newline at end of file +} + +export type MultiChain = { + readonly chains: Chains + readonly program: >(i: Input) => Effect.Effect, + ProgramsErrorsU, + Extract, Input>> +} + +export function multiChain + (chains: Chains) { + + return { + chains: chains, + program: multiChainProgram(chains) + } as MultiChain +} + +export function addChains + (mc: MultiChain, + additionalChains: AdditionalChains) { + + return multiChain([...mc.chains, ...additionalChains]) +} diff --git a/src/multi_chain_test.ts b/src/multi_chain_test.ts index d9d5396..aa8e3de 100644 --- a/src/multi_chain_test.ts +++ b/src/multi_chain_test.ts @@ -3,7 +3,7 @@ import { Effect, Context } from "effect" import { Org, OrgService, getOrgByNick, User, UserService, getUserByIds, PushNotificationService, sendPush } from "./test_services.ts" import { tag } from "./tagged.ts" import { objectChain } from "./object_chain.ts" -import { multiChain } from "./multi_chain.ts" +import { multiChainProgram, multiChain, addChains } from "./multi_chain.ts" //////////////////// some steps ////////////////////////////////// @@ -85,13 +85,13 @@ const echoContext = Context.empty().pipe( const programs = [getOrgProg, sendWelcomePushProg] as const -const multiChainProgram = multiChain(programs) +const prog = multiChainProgram(programs) -Deno.test("makeHandlerProgram", () => { +Deno.test("multiChainProgram", () => { const getOrgInput: GetOrgInput = { tag: "GetOrg", data: { org_nick: "foo" } } // note the inferred Effect value type selects the output of the getOrg chain - const getOrgEffect = multiChainProgram(getOrgInput) + const getOrgEffect = prog(getOrgInput) const getOrgRunnable = Effect.provide(getOrgEffect, echoContext) const getOrgResult = Effect.runSync(getOrgRunnable) @@ -104,7 +104,7 @@ Deno.test("makeHandlerProgram", () => { const sendWelcomePushInput: SendWelcomePushInput = { tag: "SendWelcomePush", data: { org_nick: "foo", user_id: "100" } } // note the inferred Effect value type selects the output of the sendWelcomePush chain - const sendWelcomePushEffect = multiChainProgram(sendWelcomePushInput) + const sendWelcomePushEffect = prog(sendWelcomePushInput) const sendWelcomePushRunnable = Effect.provide(sendWelcomePushEffect, echoContext) const sendWelcomePushResult = Effect.runSync(sendWelcomePushRunnable) @@ -115,4 +115,39 @@ Deno.test("makeHandlerProgram", () => { formatWelcomePush: "Welcome Bar of Foo", sendPush: "push sent OK: Welcome Bar of Foo" }) +}) + +Deno.test("multiChain", () => { + const mc = multiChain(programs) + + const getOrgInput: GetOrgInput = { tag: "GetOrg", data: { org_nick: "foo" } } + + // note the inferred Effect value type selects the output of the getOrg chain + const getOrgEffect = mc.program(getOrgInput) + const getOrgRunnable = Effect.provide(getOrgEffect, echoContext) + const getOrgResult = Effect.runSync(getOrgRunnable) + + assertEquals(getOrgResult, { + ...getOrgInput, + org: { id: "foo", name: "Foo" }, + apiResponse: { org: { id: "foo", name: "Foo" } } + }) +}) + +Deno.test("addChains", () => { + const emptyMultiChain = multiChain([]) + const mc = addChains(emptyMultiChain, programs) + + const getOrgInput: GetOrgInput = { tag: "GetOrg", data: { org_nick: "foo" } } + + // note the inferred Effect value type selects the output of the getOrg chain + const getOrgEffect = mc.program(getOrgInput) + const getOrgRunnable = Effect.provide(getOrgEffect, echoContext) + const getOrgResult = Effect.runSync(getOrgRunnable) + + assertEquals(getOrgResult, { + ...getOrgInput, + org: { id: "foo", name: "Foo" }, + apiResponse: { org: { id: "foo", name: "Foo" } } + }) }) \ No newline at end of file