| 1 | /** |
| 2 | * @title Creating Layers from configuration and/or Effects |
| 3 | * |
| 4 | * Build a layer dynamically from an Effect / Config with `Layer.unwrap`. |
| 5 | */ |
| 6 | import { Config, Context, Effect, Layer, Schema } from "effect" |
| 7 | |
| 8 | export class MessageStoreError extends Schema.TaggedError<MessageStoreError>()("MessageStoreError", { |
| 9 | cause: Schema.Defect() |
| 10 | }) {} |
| 11 | |
| 12 | export class MessageStore extends Context.Service<MessageStore, { |
| 13 | append(message: string): Effect.Effect<void> |
| 14 | readonly all: Effect.Effect<ReadonlyArray<string>> |
| 15 | }>()("myapp/MessageStore") { |
| 16 | static readonly layerInMemory = Layer.effect( |
| 17 | MessageStore, |
| 18 | Effect.sync(() => { |
| 19 | const messages: Array<string> = [] |
| 20 | |
| 21 | return MessageStore.of({ |
| 22 | append: (message) => |
| 23 | Effect.sync(() => { |
| 24 | messages.push(message) |
| 25 | }), |
| 26 | all: Effect.sync(() => [...messages]) |
| 27 | }) |
| 28 | }) |
| 29 | ) |
| 30 | |
| 31 | static readonly layerRemote = (url: URL) => |
| 32 | Layer.effect( |
| 33 | MessageStore, |
| 34 | Effect.try({ |
| 35 | try: () => { |
| 36 | // In a real app this is where you would open a network connection. |
| 37 | const messages: Array<string> = [] |
| 38 | |
| 39 | return MessageStore.of({ |
| 40 | append: (message) => |
| 41 | Effect.sync(() => { |
| 42 | messages.push(`[${url.host}] ${message}`) |
| 43 | }), |
| 44 | all: Effect.sync(() => [...messages]) |
| 45 | }) |
| 46 | }, |
| 47 | catch: (cause) => new MessageStoreError({ cause }) |
| 48 | }) |
| 49 | ) |
| 50 | |
| 51 | static readonly layer = Layer.unwrap( |
| 52 | Effect.gen(function*() { |
| 53 | // Read config inside an Effect, then choose which concrete layer to use. |
| 54 | const useInMemory = yield* Config.Boolean("MESSAGE_STORE_IN_MEMORY").pipe( |
| 55 | Config.withDefault(false) |
| 56 | ) |
| 57 | |
| 58 | if (useInMemory) { |
| 59 | return MessageStore.layerInMemory |
| 60 | } |
| 61 | |
| 62 | const remoteUrl = yield* Config.URL("MESSAGE_STORE_URL") |
| 63 | return MessageStore.layerRemote(remoteUrl) |
| 64 | }) |
| 65 | ) |
| 66 | } |