|
| 1 | +import { type AsyncContextStorage, createAsyncContextStorage } from "./async-context.ts"; |
| 2 | + |
| 3 | +export type ReadonlyAcontext<T> = { |
| 4 | + readonly name: string; |
| 5 | + get(): T; |
| 6 | + getOrNull(): T | null; |
| 7 | + getOrElse<TElse>(else_fn: () => TElse): T | TElse; |
| 8 | +}; |
| 9 | + |
| 10 | +export type Acontext<T> = ReadonlyAcontext<T> & { |
| 11 | + readonly default?: () => T; |
| 12 | + readonly storage: AsyncContextStorage<{ value: T }>; |
| 13 | + run<R>(value: T, cb: () => Promise<R>): Promise<R>; |
| 14 | + asReadonly(): ReadonlyAcontext<T>; |
| 15 | +}; |
| 16 | + |
| 17 | +export const createAcontext = <T extends unknown>(name: string, default_fn?: Acontext<T>["default"]): Acontext<T> => { |
| 18 | + const storage = createAsyncContextStorage<{ value: T }>(); |
| 19 | + |
| 20 | + const handle: Acontext<T> = { |
| 21 | + storage: storage, |
| 22 | + default: default_fn, |
| 23 | + name: name, |
| 24 | + getOrElse: (else_fn) => { |
| 25 | + const item = storage.getStore(); |
| 26 | + if (item == null) { |
| 27 | + return default_fn ? default_fn() : else_fn(); |
| 28 | + } |
| 29 | + return item?.value; |
| 30 | + }, |
| 31 | + |
| 32 | + get: () => { |
| 33 | + return handle.getOrElse(() => { |
| 34 | + throw new Error(`No factory registered for capability handle: ${name}`); |
| 35 | + }); |
| 36 | + }, |
| 37 | + getOrNull: () => { |
| 38 | + return handle.getOrElse(() => null); |
| 39 | + }, |
| 40 | + run: (value, cb) => { |
| 41 | + return storage.run({ value }, cb); |
| 42 | + }, |
| 43 | + |
| 44 | + asReadonly: () => { |
| 45 | + return { |
| 46 | + name: handle.name, |
| 47 | + get: handle.get, |
| 48 | + getOrNull: handle.getOrNull, |
| 49 | + getOrElse: handle.getOrElse, |
| 50 | + }; |
| 51 | + }, |
| 52 | + }; |
| 53 | + |
| 54 | + return handle; |
| 55 | +}; |
| 56 | + |
| 57 | +type AContextValue<T> = T extends Acontext<infer R> ? R : never; |
| 58 | + |
| 59 | +type AContextsValues<T extends Array<any>> = T extends [] |
| 60 | + ? [] |
| 61 | + : T extends [infer Head, ...infer Tail] |
| 62 | + ? [AContextValue<Head>, ...AContextsValues<Tail>] |
| 63 | + : T extends Array<infer Head> |
| 64 | + ? AContextValue<Head>[] |
| 65 | + : never; |
| 66 | + |
| 67 | +export const useAcontexts = <const ACS extends Array<Acontext<any>>>(ctxs: ACS) => { |
| 68 | + return <R extends unknown>(values: AContextsValues<ACS>, run: () => Promise<R>) => { |
| 69 | + // 递归的内部辅助函数 |
| 70 | + const runner = async (index: number): Promise<R> => { |
| 71 | + // 基本情况:如果所有上下文都已应用,则执行最终的 run 函数 |
| 72 | + if (index >= ctxs.length) { |
| 73 | + return run(); |
| 74 | + } |
| 75 | + |
| 76 | + // 获取当前的 Acontext 和要设置的值 |
| 77 | + const context = ctxs[index]; |
| 78 | + const value = values[index]; |
| 79 | + |
| 80 | + // 递归步骤:在当前上下文中运行,并递归调用 runner 处理下一个上下文 |
| 81 | + return context.run(value, () => runner(index + 1)); |
| 82 | + }; |
| 83 | + |
| 84 | + // 从第一个上下文开始启动递归 |
| 85 | + return runner(0); |
| 86 | + }; |
| 87 | +}; |
| 88 | + |
| 89 | +export class AcontextMap { |
| 90 | + #map = new Map<Acontext<any>, any>(); |
| 91 | + set<T extends Acontext<unknown>>(context: T, value: AContextValue<T>) { |
| 92 | + this.#map.set(context, value); |
| 93 | + return this; |
| 94 | + } |
| 95 | + get<T>(context: Acontext<T>) { |
| 96 | + return this.#map.get(context); |
| 97 | + } |
| 98 | + has(context: Acontext<any>) { |
| 99 | + return this.#map.has(context); |
| 100 | + } |
| 101 | + run<T>(runner: () => Promise<T>) { |
| 102 | + return useAcontexts([...this.#map.keys()])([...this.#map.values()], runner); |
| 103 | + } |
| 104 | +} |
0 commit comments