()
| 112 | * coverage tracking keys on the name literal (runtime keys on reference). |
| 113 | */ |
| 114 | export function createCapability<TValue = unknown>(): < |
| 115 | const TName extends string, |
| 116 | >( |
| 117 | name: TName, |
| 118 | ) => Capability<TValue, TName> { |
| 119 | return <const TName extends string>( |
| 120 | name: TName, |
| 121 | ): Capability<TValue, TName> => { |
| 122 | // Each capability owns a typed WeakMap keyed by the context object. Because |
| 123 | // the value type is TValue, reads are typed with no assertion. |
| 124 | const values = new WeakMap<CapabilityContext, TValue>() |
| 125 | |
| 126 | function get(ctx: CapabilityContext): TValue |
| 127 | function get( |
| 128 | ctx: CapabilityContext, |
| 129 | opts: { optional: true }, |
| 130 | ): TValue | undefined |
| 131 | function get( |
| 132 | ctx: CapabilityContext, |
| 133 | opts?: CapabilityGetOptions, |
| 134 | ): TValue | undefined { |
| 135 | if (!values.has(ctx)) { |
| 136 | if (opts?.optional) return undefined |
| 137 | throw new Error( |
| 138 | `Capability "${name}" was requested but never provided. Ensure a ` + |
| 139 | `middleware provides it in setup(), ordered before this consumer.`, |
| 140 | ) |
| 141 | } |
| 142 | return values.get(ctx) |
| 143 | } |
| 144 | |
| 145 | const provide: CapabilityProvider<TValue> = (ctx, value) => { |
| 146 | values.set(ctx, value) |
| 147 | ctx.capabilities.markProvided(handle) |
| 148 | } |
| 149 | |
| 150 | const pair: readonly [ |
| 151 | CapabilityGetter<TValue>, |
| 152 | CapabilityProvider<TValue>, |
| 153 | ] = [get, provide] |
| 154 | // Object.assign's return type is the intersection of the tuple and the |
| 155 | // props, which IS Capability<TValue, TName> — no cast needed. |
| 156 | const handle = Object.assign(pair, { |
| 157 | capabilityName: name, |
| 158 | has: (ctx: CapabilityContext) => values.has(ctx), |
| 159 | }) |
| 160 | return handle |
| 161 | } |
| 162 | } |
no test coverage detected