@securitydept/client 0.3.0-beta.2 → 0.3.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/auth-coordination/index.d.mts +1 -1
  2. package/dist/auth-coordination/index.mjs +1 -1
  3. package/dist/auth-coordination/index.mjs.map +1 -1
  4. package/dist/{fetch-transport-DtnSRWAH.mjs → client-environment-CaumNmir.mjs} +187 -6
  5. package/dist/client-environment-CaumNmir.mjs.map +1 -0
  6. package/dist/{fetch-transport-Bd5RyWj2.d.mts → client-environment-CdHtVxKW.d.mts} +70 -8
  7. package/dist/events/index.d.mts +1 -1
  8. package/dist/events-BUlfsRfl.mjs.map +1 -1
  9. package/dist/{index-C_wgl6dW.d.mts → index-C4xlKhG7.d.mts} +2 -2
  10. package/dist/{index-DbYSFHDe.d.mts → index-COUt5x9Z.d.mts} +2 -2
  11. package/dist/index.d.mts +10 -10
  12. package/dist/index.mjs +2 -2
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/persistence/index.d.mts +2 -2
  15. package/dist/persistence/web/index.d.mts +1 -1
  16. package/dist/persistence/web/index.mjs +1 -50
  17. package/dist/persistence-CSB8770k.mjs.map +1 -1
  18. package/dist/{planner-host-CV38bMSn.mjs → planner-host-BTWie0lo.mjs} +3 -1
  19. package/dist/{planner-host-CV38bMSn.mjs.map → planner-host-BTWie0lo.mjs.map} +1 -1
  20. package/dist/{planner-host-CPyGNyQV.d.mts → planner-host-DN9dRsQx.d.mts} +1 -1
  21. package/dist/{types-DQc088ZP.d.mts → types-BJqUdH2e.d.mts} +1 -1
  22. package/dist/{types-C59fcM5W.d.mts → types-BwcnUGNl.d.mts} +1 -1
  23. package/dist/web/index.d.mts +40 -12
  24. package/dist/web/index.mjs +104 -14
  25. package/dist/web/index.mjs.map +1 -1
  26. package/dist/web-DQmMqZnR.mjs +51 -0
  27. package/dist/web-DQmMqZnR.mjs.map +1 -0
  28. package/dist/web-router/index.d.mts +1 -1
  29. package/dist/web-router/index.mjs +1 -1
  30. package/dist/web-router/index.mjs.map +1 -1
  31. package/package.json +2 -2
  32. package/dist/fetch-transport-DtnSRWAH.mjs.map +0 -1
  33. package/dist/persistence/web/index.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"events-BUlfsRfl.mjs","names":["map","rxMap","filter","rxFilter","tap","rxTap","takeUntil","rxTakeUntil","finalize","rxFinalize","merge","rxMerge","share","rxShare","shareReplay","rxShareReplay","switchMap","rxSwitchMap","concatMap","rxConcatMap","exhaustMap","rxExhaustMap","debounceTime","rxDebounceTime","throttleTime","rxThrottleTime","rxWithLatestFrom"],"sources":["../src/events/event-stream.ts","../src/events/operators.ts","../src/events/types.ts"],"sourcesContent":["import { Observable, ReplaySubject, Subject } from \"rxjs\";\nimport type {\n\tEventObserver,\n\tEventStreamTrait,\n\tEventSubscriptionTrait,\n\tReplaySubjectTrait,\n\tSubjectTrait,\n} from \"./types\";\n\nconst RX_OBSERVABLE = Symbol(\"securitydept.rxObservable\");\n\ntype RxBackedEventStream<T> = EventStreamTrait<T> & {\n\treadonly [RX_OBSERVABLE]: Observable<T>;\n};\n\n/**\n * Create a new event stream from a producer function.\n *\n * The producer receives an observer and should call `next()` to emit values,\n * `error()` to signal an error, and `complete()` to signal completion.\n * It may return a teardown function that will be called on unsubscribe.\n */\nexport function createEventStream<T>(\n\tproducer: (observer: EventObserver<T>) => (() => void) | void,\n): EventStreamTrait<T> {\n\treturn fromRxObservable(\n\t\tnew Observable<T>((subscriber) => {\n\t\t\tconst teardown = producer({\n\t\t\t\tnext: (value) => subscriber.next(value),\n\t\t\t\terror: (error) => subscriber.error(error),\n\t\t\t\tcomplete: () => subscriber.complete(),\n\t\t\t});\n\t\t\treturn () => teardown?.();\n\t\t}),\n\t);\n}\n\nexport function createSubject<T>(): SubjectTrait<T> {\n\treturn wrapSubject(new Subject<T>());\n}\n\nexport function createReplaySubject<T>(bufferSize = 1): ReplaySubjectTrait<T> {\n\tconst subject = wrapSubject(new ReplaySubject<T>(bufferSize));\n\treturn Object.assign(subject, { bufferSize });\n}\n\nexport function toRxObservable<T>(stream: EventStreamTrait<T>): Observable<T> {\n\tconst maybeBacked = stream as Partial<RxBackedEventStream<T>>;\n\tif (maybeBacked[RX_OBSERVABLE]) {\n\t\treturn maybeBacked[RX_OBSERVABLE];\n\t}\n\n\treturn new Observable<T>((subscriber) => {\n\t\tconst subscription = stream.subscribe({\n\t\t\tnext: (value) => subscriber.next(value),\n\t\t\terror: (error) => subscriber.error(error),\n\t\t\tcomplete: () => subscriber.complete(),\n\t\t});\n\t\treturn () => subscription.unsubscribe();\n\t});\n}\n\nexport function fromRxObservable<T>(\n\tobservable: Observable<T>,\n): EventStreamTrait<T> {\n\treturn {\n\t\t[RX_OBSERVABLE]: observable,\n\t\tsubscribe(observer: EventObserver<T>): EventSubscriptionTrait {\n\t\t\tconst subscription = observable.subscribe({\n\t\t\t\tnext: (value) => observer.next?.(value),\n\t\t\t\terror: (error) => observer.error?.(error),\n\t\t\t\tcomplete: () => observer.complete?.(),\n\t\t\t});\n\t\t\treturn { unsubscribe: () => subscription.unsubscribe() };\n\t\t},\n\t} as RxBackedEventStream<T>;\n}\n\nfunction wrapSubject<T>(subject: Subject<T>): SubjectTrait<T> {\n\treturn Object.assign(fromRxObservable(subject.asObservable()), {\n\t\tnext: (value: T) => subject.next(value),\n\t\terror: (error: unknown) => subject.error(error),\n\t\tcomplete: () => subject.complete(),\n\t});\n}\n","import { merge as rxMerge } from \"rxjs\";\nimport {\n\tconcatMap as rxConcatMap,\n\tdebounceTime as rxDebounceTime,\n\texhaustMap as rxExhaustMap,\n\tfilter as rxFilter,\n\tfinalize as rxFinalize,\n\tmap as rxMap,\n\tshare as rxShare,\n\tshareReplay as rxShareReplay,\n\tswitchMap as rxSwitchMap,\n\ttakeUntil as rxTakeUntil,\n\ttap as rxTap,\n\tthrottleTime as rxThrottleTime,\n\twithLatestFrom as rxWithLatestFrom,\n} from \"rxjs/operators\";\nimport type { ReadableSignalTrait } from \"../signals\";\nimport {\n\tcreateEventStream,\n\tfromRxObservable,\n\ttoRxObservable,\n} from \"./event-stream\";\nimport type { EventStreamTrait } from \"./types\";\n\n// --- Operator types ---\n\nexport type EventOperator<A, B> = (\n\tsource: EventStreamTrait<A>,\n) => EventStreamTrait<B>;\n\n// --- pipe ---\n\nexport function pipe<A>(source: EventStreamTrait<A>): EventStreamTrait<A>;\nexport function pipe<A, B>(\n\tsource: EventStreamTrait<A>,\n\top1: EventOperator<A, B>,\n): EventStreamTrait<B>;\nexport function pipe<A, B, C>(\n\tsource: EventStreamTrait<A>,\n\top1: EventOperator<A, B>,\n\top2: EventOperator<B, C>,\n): EventStreamTrait<C>;\nexport function pipe<A, B, C, D>(\n\tsource: EventStreamTrait<A>,\n\top1: EventOperator<A, B>,\n\top2: EventOperator<B, C>,\n\top3: EventOperator<C, D>,\n): EventStreamTrait<D>;\nexport function pipe(\n\tsource: EventStreamTrait<unknown>,\n\t...operators: EventOperator<unknown, unknown>[]\n): EventStreamTrait<unknown> {\n\treturn operators.reduce((acc, op) => op(acc), source);\n}\n\n// --- map ---\n\nexport function map<A, B>(fn: (value: A) => B): EventOperator<A, B> {\n\treturn (source) => fromRxObservable(toRxObservable(source).pipe(rxMap(fn)));\n}\n\n// --- filter ---\n\nexport function filter<A>(\n\tpredicate: (value: A) => boolean,\n): EventOperator<A, A> {\n\treturn (source) =>\n\t\tfromRxObservable(toRxObservable(source).pipe(rxFilter(predicate)));\n}\n\n// --- tap ---\n\nexport function tap<A>(fn: (value: A) => void): EventOperator<A, A> {\n\treturn (source) => fromRxObservable(toRxObservable(source).pipe(rxTap(fn)));\n}\n\n// --- takeUntil ---\n\nexport function takeUntil<A>(\n\tnotifier: EventStreamTrait<unknown>,\n): EventOperator<A, A> {\n\treturn (source) =>\n\t\tfromRxObservable(\n\t\t\ttoRxObservable(source).pipe(rxTakeUntil(toRxObservable(notifier))),\n\t\t);\n}\n\n// --- finalize ---\n\nexport function finalize<A>(fn: () => void): EventOperator<A, A> {\n\treturn (source) =>\n\t\tfromRxObservable(toRxObservable(source).pipe(rxFinalize(fn)));\n}\n\n// --- merge ---\n\nexport function merge<A>(\n\t...sources: EventStreamTrait<A>[]\n): EventStreamTrait<A> {\n\treturn fromRxObservable(rxMerge(...sources.map(toRxObservable)));\n}\n\nexport function share<A>(): EventOperator<A, A> {\n\treturn (source) => fromRxObservable(toRxObservable(source).pipe(rxShare()));\n}\n\nexport function shareReplay<A>(bufferSize = 1): EventOperator<A, A> {\n\treturn (source) =>\n\t\tfromRxObservable(\n\t\t\ttoRxObservable(source).pipe(\n\t\t\t\trxShareReplay({ bufferSize, refCount: true }),\n\t\t\t),\n\t\t);\n}\n\nexport function switchMap<A, B>(\n\tfn: (value: A) => EventStreamTrait<B>,\n): EventOperator<A, B> {\n\treturn (source) =>\n\t\tfromRxObservable(\n\t\t\ttoRxObservable(source).pipe(\n\t\t\t\trxSwitchMap((value) => toRxObservable(fn(value))),\n\t\t\t),\n\t\t);\n}\n\nexport function concatMap<A, B>(\n\tfn: (value: A) => EventStreamTrait<B>,\n): EventOperator<A, B> {\n\treturn (source) =>\n\t\tfromRxObservable(\n\t\t\ttoRxObservable(source).pipe(\n\t\t\t\trxConcatMap((value) => toRxObservable(fn(value))),\n\t\t\t),\n\t\t);\n}\n\nexport function exhaustMap<A, B>(\n\tfn: (value: A) => EventStreamTrait<B>,\n): EventOperator<A, B> {\n\treturn (source) =>\n\t\tfromRxObservable(\n\t\t\ttoRxObservable(source).pipe(\n\t\t\t\trxExhaustMap((value) => toRxObservable(fn(value))),\n\t\t\t),\n\t\t);\n}\n\nexport function debounceTime<A>(durationMs: number): EventOperator<A, A> {\n\treturn (source) =>\n\t\tfromRxObservable(toRxObservable(source).pipe(rxDebounceTime(durationMs)));\n}\n\nexport function throttleTime<A>(durationMs: number): EventOperator<A, A> {\n\treturn (source) =>\n\t\tfromRxObservable(toRxObservable(source).pipe(rxThrottleTime(durationMs)));\n}\n\nexport function withLatestFromSignal<A, B>(\n\tsignal: ReadableSignalTrait<B>,\n): EventOperator<A, [A, B]> {\n\treturn (source) =>\n\t\tfromRxObservable(\n\t\t\ttoRxObservable(source).pipe(\n\t\t\t\trxWithLatestFrom(\n\t\t\t\t\ttoRxObservable(\n\t\t\t\t\t\tcreateEventStream<B>((observer) => {\n\t\t\t\t\t\t\tobserver.next?.(signal.get());\n\t\t\t\t\t\t\treturn signal.subscribe(() => observer.next?.(signal.get()));\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t),\n\t\t);\n}\n","// --- Event system trait types ---\n\n/** Subscription handle with explicit unsubscribe. */\nexport interface EventSubscriptionTrait {\n\tunsubscribe(): void;\n}\n\n/** Observer for event streams — mirrors the Observable observer pattern. */\nexport interface EventObserver<T> {\n\tnext?(value: T): void;\n\terror?(error: unknown): void;\n\tcomplete?(): void;\n}\n\n/** Read-only event stream — lazy push-based sequence. */\nexport interface EventStreamTrait<T> {\n\tsubscribe(observer: EventObserver<T>): EventSubscriptionTrait;\n}\n\n/** Hot event producer. */\nexport interface SubjectTrait<T> extends EventStreamTrait<T> {\n\tnext(value: T): void;\n\terror(error: unknown): void;\n\tcomplete(): void;\n}\n\n/** Hot event producer that replays recent values to late subscribers. */\nexport interface ReplaySubjectTrait<T> extends SubjectTrait<T> {\n\treadonly bufferSize: number;\n}\n\n// --- Event envelope ---\n\nexport const EventSourceKind = {\n\tUser: \"user\",\n\tTimer: \"timer\",\n\tHttp: \"http\",\n\tStorage: \"storage\",\n\tFramework: \"framework\",\n\tSystem: \"system\",\n} as const;\n\nexport type EventSourceKind =\n\t(typeof EventSourceKind)[keyof typeof EventSourceKind];\n\nexport type EventSource =\n\t| { kind: typeof EventSourceKind.User; actor?: string }\n\t| { kind: typeof EventSourceKind.Timer; timer: string }\n\t| { kind: typeof EventSourceKind.Http; requestId: string; endpoint?: string }\n\t| { kind: typeof EventSourceKind.Storage; operation: string }\n\t| { kind: typeof EventSourceKind.Framework; name: string }\n\t| { kind: typeof EventSourceKind.System; subsystem: string };\n\nexport interface RuntimeEventEnvelope<TType extends string, TPayload> {\n\tid: string;\n\ttype: TType;\n\tat: number;\n\tsource: EventSource;\n\tpayload: TPayload;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"events-BUlfsRfl.mjs","names":["map","rxMap","filter","rxFilter","tap","rxTap","takeUntil","rxTakeUntil","finalize","rxFinalize","merge","rxMerge","share","rxShare","shareReplay","rxShareReplay","switchMap","rxSwitchMap","concatMap","rxConcatMap","exhaustMap","rxExhaustMap","debounceTime","rxDebounceTime","throttleTime","rxThrottleTime","rxWithLatestFrom"],"sources":["../src/events/event-stream.ts","../src/events/operators.ts","../src/events/types.ts"],"sourcesContent":["import { Observable, ReplaySubject, Subject } from \"rxjs\";\nimport type {\n\tEventObserver,\n\tEventStreamTrait,\n\tEventSubscriptionTrait,\n\tReplaySubjectTrait,\n\tSubjectTrait,\n} from \"./types\";\n\nconst RX_OBSERVABLE = Symbol(\"securitydept.rxObservable\");\n\ntype RxBackedEventStream<T> = EventStreamTrait<T> & {\n\treadonly [RX_OBSERVABLE]: Observable<T>;\n};\n\n/**\n * Create a new event stream from a producer function.\n *\n * The producer receives an observer and should call `next()` to emit values,\n * `error()` to signal an error, and `complete()` to signal completion.\n * It may return a teardown function that will be called on unsubscribe.\n */\nexport function createEventStream<T>(\n\tproducer: (observer: EventObserver<T>) => (() => void) | void,\n): EventStreamTrait<T> {\n\treturn fromRxObservable(\n\t\tnew Observable<T>((subscriber) => {\n\t\t\tconst teardown = producer({\n\t\t\t\tnext: (value) => subscriber.next(value),\n\t\t\t\terror: (error) => subscriber.error(error),\n\t\t\t\tcomplete: () => subscriber.complete(),\n\t\t\t});\n\t\t\treturn () => teardown?.();\n\t\t}),\n\t);\n}\n\nexport function createSubject<T>(): SubjectTrait<T> {\n\treturn wrapSubject(new Subject<T>());\n}\n\nexport function createReplaySubject<T>(bufferSize = 1): ReplaySubjectTrait<T> {\n\tconst subject = wrapSubject(new ReplaySubject<T>(bufferSize));\n\treturn Object.assign(subject, { bufferSize });\n}\n\nexport function toRxObservable<T>(stream: EventStreamTrait<T>): Observable<T> {\n\tconst maybeBacked = stream as Partial<RxBackedEventStream<T>>;\n\tif (maybeBacked[RX_OBSERVABLE]) {\n\t\treturn maybeBacked[RX_OBSERVABLE];\n\t}\n\n\treturn new Observable<T>((subscriber) => {\n\t\tconst subscription = stream.subscribe({\n\t\t\tnext: (value) => subscriber.next(value),\n\t\t\terror: (error) => subscriber.error(error),\n\t\t\tcomplete: () => subscriber.complete(),\n\t\t});\n\t\treturn () => subscription.unsubscribe();\n\t});\n}\n\nexport function fromRxObservable<T>(\n\tobservable: Observable<T>,\n): EventStreamTrait<T> {\n\treturn {\n\t\t[RX_OBSERVABLE]: observable,\n\t\tsubscribe(observer: EventObserver<T>): EventSubscriptionTrait {\n\t\t\tconst subscription = observable.subscribe({\n\t\t\t\tnext: (value) => observer.next?.(value),\n\t\t\t\terror: (error) => observer.error?.(error),\n\t\t\t\tcomplete: () => observer.complete?.(),\n\t\t\t});\n\t\t\treturn { unsubscribe: () => subscription.unsubscribe() };\n\t\t},\n\t} as RxBackedEventStream<T>;\n}\n\nfunction wrapSubject<T>(subject: Subject<T>): SubjectTrait<T> {\n\treturn Object.assign(fromRxObservable(subject.asObservable()), {\n\t\tnext: (value: T) => subject.next(value),\n\t\terror: (error: unknown) => subject.error(error),\n\t\tcomplete: () => subject.complete(),\n\t});\n}\n","import { merge as rxMerge } from \"rxjs\";\nimport {\n\tconcatMap as rxConcatMap,\n\tdebounceTime as rxDebounceTime,\n\texhaustMap as rxExhaustMap,\n\tfilter as rxFilter,\n\tfinalize as rxFinalize,\n\tmap as rxMap,\n\tshare as rxShare,\n\tshareReplay as rxShareReplay,\n\tswitchMap as rxSwitchMap,\n\ttakeUntil as rxTakeUntil,\n\ttap as rxTap,\n\tthrottleTime as rxThrottleTime,\n\twithLatestFrom as rxWithLatestFrom,\n} from \"rxjs/operators\";\nimport type { ReadableSignalTrait } from \"../signals\";\nimport {\n\tcreateEventStream,\n\tfromRxObservable,\n\ttoRxObservable,\n} from \"./event-stream\";\nimport type { EventStreamTrait } from \"./types\";\n\n// --- Operator types ---\n\nexport type EventOperator<A, B> = (\n\tsource: EventStreamTrait<A>,\n) => EventStreamTrait<B>;\n\n// --- pipe ---\n\nexport function pipe<A>(source: EventStreamTrait<A>): EventStreamTrait<A>;\nexport function pipe<A, B>(\n\tsource: EventStreamTrait<A>,\n\top1: EventOperator<A, B>,\n): EventStreamTrait<B>;\nexport function pipe<A, B, C>(\n\tsource: EventStreamTrait<A>,\n\top1: EventOperator<A, B>,\n\top2: EventOperator<B, C>,\n): EventStreamTrait<C>;\nexport function pipe<A, B, C, D>(\n\tsource: EventStreamTrait<A>,\n\top1: EventOperator<A, B>,\n\top2: EventOperator<B, C>,\n\top3: EventOperator<C, D>,\n): EventStreamTrait<D>;\nexport function pipe(\n\tsource: EventStreamTrait<unknown>,\n\t...operators: EventOperator<unknown, unknown>[]\n): EventStreamTrait<unknown> {\n\treturn operators.reduce((acc, op) => op(acc), source);\n}\n\n// --- map ---\n\nexport function map<A, B>(fn: (value: A) => B): EventOperator<A, B> {\n\treturn (source) => fromRxObservable(toRxObservable(source).pipe(rxMap(fn)));\n}\n\n// --- filter ---\n\nexport function filter<A>(\n\tpredicate: (value: A) => boolean,\n): EventOperator<A, A> {\n\treturn (source) =>\n\t\tfromRxObservable(toRxObservable(source).pipe(rxFilter(predicate)));\n}\n\n// --- tap ---\n\nexport function tap<A>(fn: (value: A) => void): EventOperator<A, A> {\n\treturn (source) => fromRxObservable(toRxObservable(source).pipe(rxTap(fn)));\n}\n\n// --- takeUntil ---\n\nexport function takeUntil<A>(\n\tnotifier: EventStreamTrait<unknown>,\n): EventOperator<A, A> {\n\treturn (source) =>\n\t\tfromRxObservable(\n\t\t\ttoRxObservable(source).pipe(rxTakeUntil(toRxObservable(notifier))),\n\t\t);\n}\n\n// --- finalize ---\n\nexport function finalize<A>(fn: () => void): EventOperator<A, A> {\n\treturn (source) =>\n\t\tfromRxObservable(toRxObservable(source).pipe(rxFinalize(fn)));\n}\n\n// --- merge ---\n\nexport function merge<A>(\n\t...sources: EventStreamTrait<A>[]\n): EventStreamTrait<A> {\n\treturn fromRxObservable(rxMerge(...sources.map(toRxObservable)));\n}\n\nexport function share<A>(): EventOperator<A, A> {\n\treturn (source) => fromRxObservable(toRxObservable(source).pipe(rxShare()));\n}\n\nexport function shareReplay<A>(bufferSize = 1): EventOperator<A, A> {\n\treturn (source) =>\n\t\tfromRxObservable(\n\t\t\ttoRxObservable(source).pipe(\n\t\t\t\trxShareReplay({ bufferSize, refCount: true }),\n\t\t\t),\n\t\t);\n}\n\nexport function switchMap<A, B>(\n\tfn: (value: A) => EventStreamTrait<B>,\n): EventOperator<A, B> {\n\treturn (source) =>\n\t\tfromRxObservable(\n\t\t\ttoRxObservable(source).pipe(\n\t\t\t\trxSwitchMap((value) => toRxObservable(fn(value))),\n\t\t\t),\n\t\t);\n}\n\nexport function concatMap<A, B>(\n\tfn: (value: A) => EventStreamTrait<B>,\n): EventOperator<A, B> {\n\treturn (source) =>\n\t\tfromRxObservable(\n\t\t\ttoRxObservable(source).pipe(\n\t\t\t\trxConcatMap((value) => toRxObservable(fn(value))),\n\t\t\t),\n\t\t);\n}\n\nexport function exhaustMap<A, B>(\n\tfn: (value: A) => EventStreamTrait<B>,\n): EventOperator<A, B> {\n\treturn (source) =>\n\t\tfromRxObservable(\n\t\t\ttoRxObservable(source).pipe(\n\t\t\t\trxExhaustMap((value) => toRxObservable(fn(value))),\n\t\t\t),\n\t\t);\n}\n\nexport function debounceTime<A>(durationMs: number): EventOperator<A, A> {\n\treturn (source) =>\n\t\tfromRxObservable(toRxObservable(source).pipe(rxDebounceTime(durationMs)));\n}\n\nexport function throttleTime<A>(durationMs: number): EventOperator<A, A> {\n\treturn (source) =>\n\t\tfromRxObservable(toRxObservable(source).pipe(rxThrottleTime(durationMs)));\n}\n\nexport function withLatestFromSignal<A, B>(\n\tsignal: ReadableSignalTrait<B>,\n): EventOperator<A, [A, B]> {\n\treturn (source) =>\n\t\tfromRxObservable(\n\t\t\ttoRxObservable(source).pipe(\n\t\t\t\trxWithLatestFrom(\n\t\t\t\t\ttoRxObservable(\n\t\t\t\t\t\tcreateEventStream<B>((observer) => {\n\t\t\t\t\t\t\tobserver.next?.(signal.get());\n\t\t\t\t\t\t\treturn signal.subscribe(() => observer.next?.(signal.get()));\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t),\n\t\t);\n}\n","// --- Event system trait types ---\n\n/** Subscription handle with explicit unsubscribe. */\nexport interface EventSubscriptionTrait {\n\tunsubscribe(): void;\n}\n\n/** Observer for event streams — mirrors the Observable observer pattern. */\nexport interface EventObserver<T> {\n\tnext?(value: T): void;\n\terror?(error: unknown): void;\n\tcomplete?(): void;\n}\n\n/** Read-only event stream — lazy push-based sequence. */\nexport interface EventStreamTrait<T> {\n\tsubscribe(observer: EventObserver<T>): EventSubscriptionTrait;\n}\n\n/** Hot event producer. */\nexport interface SubjectTrait<T> extends EventStreamTrait<T> {\n\tnext(value: T): void;\n\terror(error: unknown): void;\n\tcomplete(): void;\n}\n\n/** Hot event producer that replays recent values to late subscribers. */\nexport interface ReplaySubjectTrait<T> extends SubjectTrait<T> {\n\treadonly bufferSize: number;\n}\n\n// --- Event envelope ---\n\nexport const EventSourceKind = {\n\tUser: \"user\",\n\tTimer: \"timer\",\n\tHttp: \"http\",\n\tStorage: \"storage\",\n\tFramework: \"framework\",\n\tSystem: \"system\",\n} as const;\n\nexport type EventSourceKind =\n\t(typeof EventSourceKind)[keyof typeof EventSourceKind];\n\nexport type EventSource =\n\t| { kind: typeof EventSourceKind.User; actor?: string }\n\t| { kind: typeof EventSourceKind.Timer; timer: string }\n\t| { kind: typeof EventSourceKind.Http; requestId: string; endpoint?: string }\n\t| { kind: typeof EventSourceKind.Storage; operation: string }\n\t| { kind: typeof EventSourceKind.Framework; name: string }\n\t| { kind: typeof EventSourceKind.System; subsystem: string };\n\nexport interface RuntimeEventEnvelope<TType extends string, TPayload> {\n\tid: string;\n\ttype: TType;\n\tat: number;\n\tsource: EventSource;\n\tpayload: TPayload;\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { n as ReadableSignalTrait } from "./types-DQc088ZP.mjs";
1
+ import { n as ReadableSignalTrait } from "./types-BJqUdH2e.mjs";
2
2
  import { Observable } from "rxjs";
3
3
 
4
4
  //#region src/events/types.d.ts
@@ -99,4 +99,4 @@ declare function throttleTime<A>(durationMs: number): EventOperator<A, A>;
99
99
  declare function withLatestFromSignal<A, B>(signal: ReadableSignalTrait<B>): EventOperator<A, [A, B]>;
100
100
  //#endregion
101
101
  export { EventSource as C, ReplaySubjectTrait as D, EventSubscriptionTrait as E, RuntimeEventEnvelope as O, EventObserver as S, EventStreamTrait as T, createEventStream as _, filter as a, fromRxObservable as b, merge$1 as c, shareReplay as d, switchMap as f, withLatestFromSignal as g, throttleTime as h, exhaustMap as i, SubjectTrait as k, pipe as l, tap as m, concatMap as n, finalize as o, takeUntil as p, debounceTime as r, map as s, EventOperator as t, share as u, createReplaySubject as v, EventSourceKind as w, toRxObservable as x, createSubject as y };
102
- //# sourceMappingURL=index-C_wgl6dW.d.mts.map
102
+ //# sourceMappingURL=index-C4xlKhG7.d.mts.map
@@ -1,4 +1,4 @@
1
- import { a as RecordStore, n as EphemeralFlowStore, r as KeyedEphemeralFlowStore, t as Codec } from "./types-C59fcM5W.mjs";
1
+ import { a as RecordStore, n as EphemeralFlowStore, r as KeyedEphemeralFlowStore, t as Codec } from "./types-BwcnUGNl.mjs";
2
2
 
3
3
  //#region src/persistence/ephemeral-flow-store.d.ts
4
4
  interface CreateEphemeralFlowStoreOptions<T> {
@@ -36,4 +36,4 @@ declare function createJsonCodec<T>(): Codec<T>;
36
36
  declare function createInMemoryRecordStore(): RecordStore;
37
37
  //#endregion
38
38
  export { createEphemeralFlowStore as a, CreateKeyedEphemeralFlowStoreOptions as i, createJsonCodec as n, createKeyedEphemeralFlowStore as o, CreateEphemeralFlowStoreOptions as r, createInMemoryRecordStore as t };
39
- //# sourceMappingURL=index-DbYSFHDe.d.mts.map
39
+ //# sourceMappingURL=index-COUt5x9Z.d.mts.map
package/dist/index.d.mts CHANGED
@@ -1,8 +1,8 @@
1
- import { C as EventSource, D as ReplaySubjectTrait, E as EventSubscriptionTrait, O as RuntimeEventEnvelope, S as EventObserver, T as EventStreamTrait, _ as createEventStream, a as filter, b as fromRxObservable, c as merge, d as shareReplay, f as switchMap, g as withLatestFromSignal, h as throttleTime, i as exhaustMap, k as SubjectTrait, l as pipe, m as tap, n as concatMap, o as finalize, p as takeUntil, r as debounceTime, s as map, t as EventOperator, u as share, v as createReplaySubject, w as EventSourceKind, x as toRxObservable, y as createSubject } from "./index-C_wgl6dW.mjs";
2
- import { n as ReadableSignalTrait, r as WritableSignalTrait, t as ComputedSignalTrait } from "./types-DQc088ZP.mjs";
3
- import { A as OperationTraceEventType, C as CancelableHandle$1, D as LogLevel, E as LogEntry, F as CancellationTokenSourceTrait, I as CancellationTokenTrait, L as DisposableTrait, M as TraceEvent, N as TraceEventSinkTrait, O as LoggerTrait, P as CancelableHandle, S as HttpTransport, T as Scheduler, _ as scheduleAt, a as FromPromiseOptions, b as HttpRequest, c as PromiseSettlement, d as Subscription, f as TimerOptions, g as interval, h as fromSignal, i as FromEventPatternOptions, j as OperationTracerTrait, k as OperationScope, l as PromiseSettlementKind, m as fromPromise, n as FetchTransportRedirectKind, o as FromSignalOptions, p as fromEventPattern, s as IntervalOptions, u as ScheduleAtOptions, v as timer, w as Clock, x as HttpResponse, y as ClientRuntime } from "./fetch-transport-Bd5RyWj2.mjs";
4
- import { a as RecordStore, i as PersistentAuthStore, n as EphemeralFlowStore, o as RecoverableStateStore, r as KeyedEphemeralFlowStore, s as StoredEnvelope, t as Codec } from "./types-C59fcM5W.mjs";
5
- import { a as createEphemeralFlowStore, n as createJsonCodec, o as createKeyedEphemeralFlowStore, t as createInMemoryRecordStore } from "./index-DbYSFHDe.mjs";
1
+ import { C as EventSource, D as ReplaySubjectTrait, E as EventSubscriptionTrait, O as RuntimeEventEnvelope, S as EventObserver, T as EventStreamTrait, _ as createEventStream, a as filter, b as fromRxObservable, c as merge, d as shareReplay, f as switchMap, g as withLatestFromSignal, h as throttleTime, i as exhaustMap, k as SubjectTrait, l as pipe, m as tap, n as concatMap, o as finalize, p as takeUntil, r as debounceTime, s as map, t as EventOperator, u as share, v as createReplaySubject, w as EventSourceKind, x as toRxObservable, y as createSubject } from "./index-C4xlKhG7.mjs";
2
+ import { n as ReadableSignalTrait, r as WritableSignalTrait, t as ComputedSignalTrait } from "./types-BJqUdH2e.mjs";
3
+ import { $ as LogEntry, B as fromEventPattern, F as PromiseSettlement, G as timer, H as fromSignal, I as PromiseSettlementKind, J as HttpResponse, K as ClientEnvironment, L as ScheduleAtOptions, M as FromPromiseOptions, N as FromSignalOptions, P as IntervalOptions, Q as Scheduler, R as Subscription, U as interval, V as fromPromise, W as scheduleAt, X as CancelableHandle$1, Y as HttpTransport, Z as Clock, a as PageHistoryLike, at as TraceEvent, c as PageLocationLike, ct as CancellationTokenSourceTrait, et as LogLevel, i as PageClientEnvironment, it as OperationTracerTrait, j as FromEventPatternOptions, k as FetchTransportRedirectKind, lt as CancellationTokenTrait, nt as OperationScope, o as PageLocationCapability, ot as TraceEventSinkTrait, q as HttpRequest, r as CreateWebClientEnvironmentOptions, rt as OperationTraceEventType, s as PageLocationHistoryCapability, st as CancelableHandle, t as ClientEnvironmentPreset, tt as LoggerTrait, u as WebClientEnvironment, ut as DisposableTrait, z as TimerOptions } from "./client-environment-CdHtVxKW.mjs";
4
+ import { a as RecordStore, i as PersistentAuthStore, n as EphemeralFlowStore, o as RecoverableStateStore, r as KeyedEphemeralFlowStore, s as StoredEnvelope, t as Codec } from "./types-BwcnUGNl.mjs";
5
+ import { a as createEphemeralFlowStore, n as createJsonCodec, o as createKeyedEphemeralFlowStore, t as createInMemoryRecordStore } from "./index-COUt5x9Z.mjs";
6
6
  import { StandardSchemaV1 } from "@standard-schema/spec";
7
7
 
8
8
  //#region src/signals/computed.d.ts
@@ -120,7 +120,7 @@ type ClientErrorKind = (typeof ClientErrorKind)[keyof typeof ClientErrorKind];
120
120
  declare const ClientErrorSource: {
121
121
  readonly Transport: "transport";
122
122
  readonly Server: "server";
123
- readonly ClientRuntime: "client_runtime";
123
+ readonly ClientEnvironment: "client_environment";
124
124
  };
125
125
  type ClientErrorSource = (typeof ClientErrorSource)[keyof typeof ClientErrorSource];
126
126
  //#endregion
@@ -235,18 +235,18 @@ interface TraceTimelineStore extends TraceEventSinkTrait {
235
235
  declare function createTraceTimelineStore(limit?: number): TraceTimelineStore;
236
236
  //#endregion
237
237
  //#region src/runtime/create-runtime.d.ts
238
- interface CreateRuntimeOptions extends Partial<Omit<ClientRuntime, "transport">> {
238
+ interface CreateClientEnvironmentOptions extends Partial<Omit<ClientEnvironment, "transport">> {
239
239
  transport: HttpTransport;
240
240
  }
241
241
  /**
242
- * Create a `ClientRuntime` with non-host-specific defaults filled in for
242
+ * Create a `ClientEnvironment` with non-host-specific defaults filled in for
243
243
  * convenience.
244
244
  *
245
245
  * Host-specific capabilities such as browser `fetch` should be supplied
246
246
  * explicitly or composed via adapter-specific helpers such as
247
247
  * `@securitydept/client/web`.
248
248
  */
249
- declare function createRuntime(overrides: CreateRuntimeOptions): ClientRuntime;
249
+ declare function createClientEnvironment(overrides: CreateClientEnvironmentOptions): ClientEnvironment;
250
250
  //#endregion
251
251
  //#region src/scheduling/default-clock.d.ts
252
252
  /** Default clock backed by `Date.now()`. */
@@ -349,5 +349,5 @@ interface CreateSchemaOptions<O> {
349
349
  */
350
350
  declare function createSchema<O>(options: CreateSchemaOptions<O>): StandardSchemaV1<unknown, O>;
351
351
  //#endregion
352
- export { type AuthenticatedPrincipal, type CancelableHandle, type CancellationTokenSourceTrait, type CancellationTokenTrait, ClientError, ClientErrorKind, ClientErrorSource, type ClientRuntime, type Clock, type Codec, type ComputedSignalTrait, type CreateOperationTracerOptions, type CreateRuntimeOptions, type DisposableTrait, type EphemeralFlowStore, type ErrorPresentation, type ErrorPresentationActionDescriptor, type ErrorPresentationDescriptor, ErrorPresentationTone, type EventObserver, type EventOperator, type EventSource, EventSourceKind, type EventStreamTrait, type EventSubscriptionTrait, FetchTransportRedirectKind, type FromEventPatternOptions, type FromPromiseOptions, type FromSignalOptions, type HttpRequest, type HttpResponse, type HttpTransport, type IntervalOptions, type KeyedEphemeralFlowStore, type LogEntry, LogLevel, type LoggerTrait, type OperationScope, OperationTraceEventType, type OperationTracerTrait, type PersistentAuthStore, type ProjectAuthenticatedPrincipalOptions, type PromiseSettlement, PromiseSettlementKind, type ReadErrorPresentationDescriptorOptions, type ReadableSignalTrait, type RecordStore, type RecoverableStateStore, type ReplaySubjectTrait, type RuntimeEventEnvelope, type ScheduleAtOptions, type Scheduler, type CancelableHandle$1 as SchedulerCancelableHandle, type StoredEnvelope, type SubjectTrait, type Subscription, type TimerOptions, type TraceEvent, type TraceEventSinkTrait, type TraceTimelineEntry, type TraceTimelineStore, UserRecovery, type ValidationFailure, type ValidationResult, type ValidationSuccess, type WritableSignalTrait, concatMap, createCancellationTokenSource, createComputed, createConsoleLogger, createDefaultClock, createDefaultScheduler, createEphemeralFlowStore, createEventStream, createInMemoryRecordStore, createJsonCodec, createKeyedEphemeralFlowStore, createLinkedCancellationToken, createNoopLogger, createOperationTracer, createReplaySubject, createRuntime, createSchema, createSignal, createSubject, createTraceTimelineStore, debounceTime, exhaustMap, filter, finalize, fromEventPattern, fromPromise, fromRxObservable, fromSignal, interval, map, merge, normalizeAuthenticatedPrincipal, normalizeAuthenticatedPrincipalWire, pipe, projectAuthenticatedPrincipal, readErrorPresentationDescriptor, readonlySignal, scheduleAt, share, shareReplay, switchMap, takeUntil, tap, throttleTime, timer, toRxObservable, validateWithSchema, validateWithSchemaSync, withLatestFromSignal };
352
+ export { type AuthenticatedPrincipal, type CancelableHandle, type CancellationTokenSourceTrait, type CancellationTokenTrait, type ClientEnvironment, ClientEnvironmentPreset, ClientError, ClientErrorKind, ClientErrorSource, type Clock, type Codec, type ComputedSignalTrait, type CreateClientEnvironmentOptions, type CreateOperationTracerOptions, type CreateWebClientEnvironmentOptions, type DisposableTrait, type EphemeralFlowStore, type ErrorPresentation, type ErrorPresentationActionDescriptor, type ErrorPresentationDescriptor, ErrorPresentationTone, type EventObserver, type EventOperator, type EventSource, EventSourceKind, type EventStreamTrait, type EventSubscriptionTrait, FetchTransportRedirectKind, type FromEventPatternOptions, type FromPromiseOptions, type FromSignalOptions, type HttpRequest, type HttpResponse, type HttpTransport, type IntervalOptions, type KeyedEphemeralFlowStore, type LogEntry, LogLevel, type LoggerTrait, type OperationScope, OperationTraceEventType, type OperationTracerTrait, type PageClientEnvironment, type PageHistoryLike, type PageLocationCapability, type PageLocationHistoryCapability, type PageLocationLike, type PersistentAuthStore, type ProjectAuthenticatedPrincipalOptions, type PromiseSettlement, PromiseSettlementKind, type ReadErrorPresentationDescriptorOptions, type ReadableSignalTrait, type RecordStore, type RecoverableStateStore, type ReplaySubjectTrait, type RuntimeEventEnvelope, type ScheduleAtOptions, type Scheduler, type CancelableHandle$1 as SchedulerCancelableHandle, type StoredEnvelope, type SubjectTrait, type Subscription, type TimerOptions, type TraceEvent, type TraceEventSinkTrait, type TraceTimelineEntry, type TraceTimelineStore, UserRecovery, type ValidationFailure, type ValidationResult, type ValidationSuccess, type WebClientEnvironment, type WritableSignalTrait, concatMap, createCancellationTokenSource, createClientEnvironment, createComputed, createConsoleLogger, createDefaultClock, createDefaultScheduler, createEphemeralFlowStore, createEventStream, createInMemoryRecordStore, createJsonCodec, createKeyedEphemeralFlowStore, createLinkedCancellationToken, createNoopLogger, createOperationTracer, createReplaySubject, createSchema, createSignal, createSubject, createTraceTimelineStore, debounceTime, exhaustMap, filter, finalize, fromEventPattern, fromPromise, fromRxObservable, fromSignal, interval, map, merge, normalizeAuthenticatedPrincipal, normalizeAuthenticatedPrincipalWire, pipe, projectAuthenticatedPrincipal, readErrorPresentationDescriptor, readonlySignal, scheduleAt, share, shareReplay, switchMap, takeUntil, tap, throttleTime, timer, toRxObservable, validateWithSchema, validateWithSchemaSync, withLatestFromSignal };
353
353
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { C as ClientErrorSource, S as ClientErrorKind, T as UserRecovery, _ as createDefaultClock, b as OperationTraceEventType, c as PromiseSettlementKind, d as fromSignal, f as interval, g as createDefaultScheduler, h as createRuntime, l as fromEventPattern, m as timer, p as scheduleAt, t as FetchTransportRedirectKind, u as fromPromise, v as createOperationTracer, w as ErrorPresentationTone, x as ClientError, y as LogLevel } from "./fetch-transport-DtnSRWAH.mjs";
1
+ import { A as interval, B as ClientErrorKind, D as fromEventPattern, E as PromiseSettlementKind, F as createDefaultClock, H as ErrorPresentationTone, I as createOperationTracer, L as LogLevel, M as timer, N as createClientEnvironment, O as fromPromise, P as createDefaultScheduler, R as OperationTraceEventType, U as UserRecovery, V as ClientErrorSource, j as scheduleAt, k as fromSignal, t as ClientEnvironmentPreset, y as FetchTransportRedirectKind, z as ClientError } from "./client-environment-CaumNmir.mjs";
2
2
  import { _ as createEventStream, a as filter, b as fromRxObservable, c as merge, d as shareReplay, f as switchMap, g as withLatestFromSignal, h as throttleTime, i as exhaustMap, l as pipe, m as tap, n as concatMap, o as finalize, p as takeUntil, r as debounceTime, s as map, t as EventSourceKind, u as share, v as createReplaySubject, x as toRxObservable, y as createSubject } from "./events-BUlfsRfl.mjs";
3
3
  import { i as createJsonCodec, n as createEphemeralFlowStore, r as createKeyedEphemeralFlowStore, t as createInMemoryRecordStore } from "./persistence-CSB8770k.mjs";
4
4
  //#region src/cancellation/cancellation-token.ts
@@ -491,6 +491,6 @@ function createSchema(options) {
491
491
  } };
492
492
  }
493
493
  //#endregion
494
- export { ClientError, ClientErrorKind, ClientErrorSource, ErrorPresentationTone, EventSourceKind, FetchTransportRedirectKind, LogLevel, OperationTraceEventType, PromiseSettlementKind, UserRecovery, concatMap, createCancellationTokenSource, createComputed, createConsoleLogger, createDefaultClock, createDefaultScheduler, createEphemeralFlowStore, createEventStream, createInMemoryRecordStore, createJsonCodec, createKeyedEphemeralFlowStore, createLinkedCancellationToken, createNoopLogger, createOperationTracer, createReplaySubject, createRuntime, createSchema, createSignal, createSubject, createTraceTimelineStore, debounceTime, exhaustMap, filter, finalize, fromEventPattern, fromPromise, fromRxObservable, fromSignal, interval, map, merge, normalizeAuthenticatedPrincipal, normalizeAuthenticatedPrincipalWire, pipe, projectAuthenticatedPrincipal, readErrorPresentationDescriptor, readonlySignal, scheduleAt, share, shareReplay, switchMap, takeUntil, tap, throttleTime, timer, toRxObservable, validateWithSchema, validateWithSchemaSync, withLatestFromSignal };
494
+ export { ClientEnvironmentPreset, ClientError, ClientErrorKind, ClientErrorSource, ErrorPresentationTone, EventSourceKind, FetchTransportRedirectKind, LogLevel, OperationTraceEventType, PromiseSettlementKind, UserRecovery, concatMap, createCancellationTokenSource, createClientEnvironment, createComputed, createConsoleLogger, createDefaultClock, createDefaultScheduler, createEphemeralFlowStore, createEventStream, createInMemoryRecordStore, createJsonCodec, createKeyedEphemeralFlowStore, createLinkedCancellationToken, createNoopLogger, createOperationTracer, createReplaySubject, createSchema, createSignal, createSubject, createTraceTimelineStore, debounceTime, exhaustMap, filter, finalize, fromEventPattern, fromPromise, fromRxObservable, fromSignal, interval, map, merge, normalizeAuthenticatedPrincipal, normalizeAuthenticatedPrincipalWire, pipe, projectAuthenticatedPrincipal, readErrorPresentationDescriptor, readonlySignal, scheduleAt, share, shareReplay, switchMap, takeUntil, tap, throttleTime, timer, toRxObservable, validateWithSchema, validateWithSchemaSync, withLatestFromSignal };
495
495
 
496
496
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/cancellation/cancellation-token.ts","../src/cancellation/linked-cancellation-token.ts","../src/errors/presentation-descriptor.ts","../src/identity/principal.ts","../src/logging/console-logger.ts","../src/logging/noop-logger.ts","../src/logging/trace-timeline-store.ts","../src/signals/computed.ts","../src/signals/signal.ts","../src/validation/index.ts"],"sourcesContent":["import { ClientError } from \"../errors/client-error\";\nimport { ClientErrorKind } from \"../errors/types\";\nimport type {\n\tCancellationTokenSourceTrait,\n\tCancellationTokenTrait,\n\tDisposableTrait,\n} from \"./types\";\n\nclass CancellationToken implements CancellationTokenTrait {\n\tprivate _isCancelled = false;\n\tprivate _reason: unknown;\n\tprivate readonly _listeners = new Set<(reason: unknown) => void>();\n\n\tget isCancellationRequested(): boolean {\n\t\treturn this._isCancelled;\n\t}\n\n\tget reason(): unknown {\n\t\treturn this._reason;\n\t}\n\n\t/** @internal — called by `CancellationTokenSource`. */\n\t_cancel(reason: unknown): void {\n\t\tif (this._isCancelled) return;\n\t\tthis._isCancelled = true;\n\t\tthis._reason = reason;\n\t\tfor (const listener of this._listeners) {\n\t\t\tlistener(reason);\n\t\t}\n\t\tthis._listeners.clear();\n\t}\n\n\tonCancellationRequested(\n\t\tlistener: (reason: unknown) => void,\n\t): DisposableTrait {\n\t\tif (this._isCancelled) {\n\t\t\t// Already cancelled — invoke immediately.\n\t\t\tlistener(this._reason);\n\t\t\treturn { dispose() {} };\n\t\t}\n\t\tthis._listeners.add(listener);\n\t\treturn {\n\t\t\tdispose: () => {\n\t\t\t\tthis._listeners.delete(listener);\n\t\t\t},\n\t\t};\n\t}\n\n\tthrowIfCancellationRequested(): void {\n\t\tif (this._isCancelled) {\n\t\t\tthrow new ClientError({\n\t\t\t\tkind: ClientErrorKind.Cancelled,\n\t\t\t\tmessage: \"Operation was cancelled\",\n\t\t\t\tcause: this._reason,\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Create a `CancellationTokenSource` that produces a cooperative cancellation token.\n */\nexport function createCancellationTokenSource(): CancellationTokenSourceTrait {\n\tconst ct = new CancellationToken();\n\tlet disposed = false;\n\n\treturn {\n\t\tget token(): CancellationTokenTrait {\n\t\t\treturn ct;\n\t\t},\n\t\tcancel(reason?: unknown) {\n\t\t\tif (!disposed) ct._cancel(reason);\n\t\t},\n\t\tdispose() {\n\t\t\tif (!disposed) {\n\t\t\t\tdisposed = true;\n\t\t\t\tct._cancel(\n\t\t\t\t\tnew ClientError({\n\t\t\t\t\t\tkind: ClientErrorKind.Cancelled,\n\t\t\t\t\t\tmessage: \"Disposed\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t};\n}\n","import { createCancellationTokenSource } from \"./cancellation-token\";\nimport type { CancellationTokenTrait } from \"./types\";\n\n/**\n * Create a cancellation token that fires when ANY of the given source tokens\n * is cancelled.\n *\n * - Zero sources → returns a token that is never cancelled.\n * - One source → returns that source directly (no allocation).\n * - N sources → creates a linked source, subscribes to all, and cleans up\n * subscriptions once the linked token fires.\n *\n * @example\n * const cts = createCancellationTokenSource();\n * const linked = createLinkedCancellationToken(clientRoot.token, cts.token);\n * await transport.execute({ ..., cancellationToken: linked });\n */\nexport function createLinkedCancellationToken(\n\t...sources: CancellationTokenTrait[]\n): CancellationTokenTrait {\n\t// Zero sources — return a token that is never cancelled.\n\tif (sources.length === 0) {\n\t\treturn createCancellationTokenSource().token;\n\t}\n\n\t// One source — return directly; no allocation needed.\n\tif (sources.length === 1) {\n\t\treturn sources[0];\n\t}\n\n\t// Fast-path: return the first already-cancelled source immediately.\n\tfor (const source of sources) {\n\t\tif (source.isCancellationRequested) return source;\n\t}\n\n\tconst linked = createCancellationTokenSource();\n\tconst subscriptions = sources.map((source) =>\n\t\tsource.onCancellationRequested((reason) => linked.cancel(reason)),\n\t);\n\n\t// Clean up all subscriptions once the linked token fires.\n\tlinked.token.onCancellationRequested(() => {\n\t\tfor (const sub of subscriptions) sub.dispose();\n\t});\n\n\treturn linked.token;\n}\n","import { ClientError } from \"./client-error\";\nimport {\n\tClientErrorKind,\n\ttype ErrorPresentation,\n\ttype ErrorPresentationActionDescriptor,\n\ttype ErrorPresentationDescriptor,\n\tErrorPresentationTone,\n\ttype ReadErrorPresentationDescriptorOptions,\n\tUserRecovery,\n} from \"./types\";\n\ninterface ClientErrorLike {\n\tcode: string | null;\n\tkind: ClientErrorKind | null;\n\tmessage: string;\n\trecovery: UserRecovery;\n\tretryable: boolean;\n\tsource?: string;\n\tpresentation?: ErrorPresentation;\n}\n\nconst DEFAULT_RECOVERY_LABELS: Record<UserRecovery, string> = {\n\t[UserRecovery.None]: \"\",\n\t[UserRecovery.Retry]: \"Try again\",\n\t[UserRecovery.RestartFlow]: \"Restart flow\",\n\t[UserRecovery.Reauthenticate]: \"Sign in again\",\n\t[UserRecovery.ContactSupport]: \"Contact support\",\n};\n\nconst POPUP_PRESENTATIONS = {\n\t\"popup.blocked\": {\n\t\ttitle: \"Popup was blocked\",\n\t\tdescription:\n\t\t\t\"The browser blocked the popup window before the login flow could start. Allow popups for this site, then try again.\",\n\t\ttone: ErrorPresentationTone.Warning,\n\t},\n\t\"popup.closed_by_user\": {\n\t\ttitle: \"Popup login was closed\",\n\t\tdescription:\n\t\t\t\"The popup window was closed before the OIDC provider returned a callback. Start the login flow again to continue.\",\n\t\ttone: ErrorPresentationTone.Warning,\n\t},\n\t\"popup.relay_timeout\": {\n\t\ttitle: \"Popup relay timed out\",\n\t\tdescription:\n\t\t\t\"The popup callback never relayed back to the opener before the timeout window expired.\",\n\t\ttone: ErrorPresentationTone.Warning,\n\t},\n\t\"popup.relay_error\": {\n\t\ttitle: \"Popup relay failed\",\n\t\tdescription:\n\t\t\t\"The popup callback page returned an error instead of a callback payload.\",\n\t\ttone: ErrorPresentationTone.Danger,\n\t},\n} as const;\n\nexport function readErrorPresentationDescriptor(\n\terror: unknown,\n\toptions: ReadErrorPresentationDescriptorOptions = {},\n): ErrorPresentationDescriptor {\n\tif (error instanceof ClientError) {\n\t\treturn describeClientErrorPresentation(error, options);\n\t}\n\n\tconst clientErrorLike = coerceClientErrorLike(error);\n\tif (clientErrorLike) {\n\t\treturn describeClientErrorLikePresentation(clientErrorLike, options);\n\t}\n\n\tif (error instanceof Error) {\n\t\treturn {\n\t\t\tcode: null,\n\t\t\tkind: null,\n\t\t\ttitle: options.fallbackTitle ?? \"Operation failed\",\n\t\t\tdescription:\n\t\t\t\terror.message ||\n\t\t\t\toptions.fallbackDescription ||\n\t\t\t\t\"An unexpected error prevented the operation from completing.\",\n\t\t\trecovery: UserRecovery.None,\n\t\t\tretryable: false,\n\t\t\ttone: ErrorPresentationTone.Danger,\n\t\t\tprimaryAction: null,\n\t\t};\n\t}\n\n\treturn {\n\t\tcode: null,\n\t\tkind: null,\n\t\ttitle: options.fallbackTitle ?? \"Operation failed\",\n\t\tdescription:\n\t\t\toptions.fallbackDescription ??\n\t\t\t\"An unexpected error prevented the operation from completing.\",\n\t\trecovery: UserRecovery.None,\n\t\tretryable: false,\n\t\ttone: ErrorPresentationTone.Danger,\n\t\tprimaryAction: null,\n\t};\n}\n\nfunction describeClientErrorPresentation(\n\terror: ClientError,\n\toptions: ReadErrorPresentationDescriptorOptions,\n): ErrorPresentationDescriptor {\n\treturn describeClientErrorLikePresentation(\n\t\t{\n\t\t\tcode: error.code,\n\t\t\tkind: error.kind,\n\t\t\tmessage: error.message,\n\t\t\trecovery: error.recovery,\n\t\t\tretryable: error.retryable,\n\t\t\tsource: error.source,\n\t\t\tpresentation: error.presentation,\n\t\t},\n\t\toptions,\n\t);\n}\n\nfunction describeClientErrorLikePresentation(\n\terror: ClientErrorLike,\n\toptions: ReadErrorPresentationDescriptorOptions,\n): ErrorPresentationDescriptor {\n\tconst popupPresentation =\n\t\tPOPUP_PRESENTATIONS[error.code as keyof typeof POPUP_PRESENTATIONS];\n\tconst title = popupPresentation?.title ?? readClientErrorTitle(error);\n\tconst description =\n\t\tpopupPresentation?.description ??\n\t\terror.presentation?.message ??\n\t\terror.message ??\n\t\toptions.fallbackDescription ??\n\t\t\"The operation could not complete.\";\n\n\treturn {\n\t\tcode: error.code,\n\t\tkind: error.kind,\n\t\tsource: error.source,\n\t\ttitle,\n\t\tdescription,\n\t\trecovery: error.recovery,\n\t\tretryable: error.retryable,\n\t\ttone: popupPresentation?.tone ?? readClientErrorTone(error),\n\t\tprimaryAction: readPrimaryAction(error.recovery, options),\n\t};\n}\n\nfunction readClientErrorTitle(\n\terror: Pick<ClientErrorLike, \"kind\" | \"recovery\">,\n): string {\n\tif (error.kind === ClientErrorKind.Unauthenticated) {\n\t\treturn \"Authentication required\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Unauthorized) {\n\t\treturn \"Access denied\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Cancelled) {\n\t\treturn \"Operation cancelled\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Timeout) {\n\t\treturn \"Request timed out\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Configuration) {\n\t\treturn \"Configuration error\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Validation) {\n\t\treturn \"Validation failed\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Storage) {\n\t\treturn \"Browser storage failed\";\n\t}\n\n\tif (\n\t\terror.kind === ClientErrorKind.Authorization &&\n\t\terror.recovery === UserRecovery.RestartFlow\n\t) {\n\t\treturn \"Flow needs to restart\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Transport) {\n\t\treturn \"Network request failed\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Server) {\n\t\treturn \"Server request failed\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Protocol) {\n\t\treturn \"Request failed\";\n\t}\n\n\treturn \"Operation failed\";\n}\n\nfunction readClientErrorTone(\n\terror: Pick<ClientErrorLike, \"kind\" | \"recovery\">,\n) {\n\tif (error.kind === ClientErrorKind.Cancelled) {\n\t\treturn ErrorPresentationTone.Neutral;\n\t}\n\n\tif (\n\t\terror.kind === ClientErrorKind.Unauthenticated ||\n\t\terror.kind === ClientErrorKind.Timeout ||\n\t\terror.kind === ClientErrorKind.Transport ||\n\t\terror.kind === ClientErrorKind.Server ||\n\t\terror.recovery === UserRecovery.Retry ||\n\t\terror.recovery === UserRecovery.RestartFlow ||\n\t\terror.recovery === UserRecovery.Reauthenticate\n\t) {\n\t\treturn ErrorPresentationTone.Warning;\n\t}\n\n\treturn ErrorPresentationTone.Danger;\n}\n\nfunction coerceClientErrorLike(error: unknown): ClientErrorLike | null {\n\tif (typeof error !== \"object\" || error === null) {\n\t\treturn null;\n\t}\n\n\tconst candidate = error as Record<string, unknown>;\n\tif (typeof candidate.message !== \"string\") {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tcode: typeof candidate.code === \"string\" ? candidate.code : null,\n\t\tkind:\n\t\t\ttypeof candidate.kind === \"string\"\n\t\t\t\t? (candidate.kind as ClientErrorKind)\n\t\t\t\t: null,\n\t\tmessage: candidate.message,\n\t\trecovery:\n\t\t\ttypeof candidate.recovery === \"string\"\n\t\t\t\t? (candidate.recovery as UserRecovery)\n\t\t\t\t: UserRecovery.None,\n\t\tretryable: candidate.retryable === true,\n\t\tsource: typeof candidate.source === \"string\" ? candidate.source : undefined,\n\t\tpresentation: isErrorPresentation(candidate.presentation)\n\t\t\t? candidate.presentation\n\t\t\t: undefined,\n\t};\n}\n\nfunction isErrorPresentation(value: unknown): value is ErrorPresentation {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\ttypeof (value as Record<string, unknown>).code === \"string\" &&\n\t\ttypeof (value as Record<string, unknown>).message === \"string\" &&\n\t\ttypeof (value as Record<string, unknown>).recovery === \"string\"\n\t);\n}\n\nfunction readPrimaryAction(\n\trecovery: UserRecovery,\n\toptions: ReadErrorPresentationDescriptorOptions,\n): ErrorPresentationActionDescriptor | null {\n\tif (recovery === UserRecovery.None) {\n\t\treturn null;\n\t}\n\n\tconst label =\n\t\toptions.recoveryLabels?.[recovery] ?? DEFAULT_RECOVERY_LABELS[recovery];\n\tif (!label) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\trecovery,\n\t\tlabel,\n\t\thref: options.recoveryLinks?.[recovery] ?? null,\n\t};\n}\n","export interface AuthenticatedPrincipal {\n\tsubject: string;\n\tdisplayName: string;\n\tpicture?: string;\n\tissuer?: string;\n\tclaims?: Record<string, unknown>;\n}\n\ntype PrincipalObject = Record<string, unknown>;\n\nfunction readClaimsBag(value: unknown): Record<string, unknown> | undefined {\n\treturn typeof value === \"object\" && value !== null\n\t\t? (value as Record<string, unknown>)\n\t\t: undefined;\n}\n\nfunction readOptionalString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" ? value : undefined;\n}\n\nfunction resolveDisplayName(displayName: unknown, subject: string): string {\n\tif (typeof displayName === \"string\" && displayName.trim().length > 0) {\n\t\treturn displayName;\n\t}\n\n\treturn subject;\n}\n\nexport function normalizeAuthenticatedPrincipal(\n\tinput: PrincipalObject,\n): AuthenticatedPrincipal | null {\n\tif (typeof input.subject !== \"string\" || input.subject.trim().length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tsubject: input.subject,\n\t\tdisplayName: resolveDisplayName(input.displayName, input.subject),\n\t\tpicture: readOptionalString(input.picture),\n\t\tissuer: readOptionalString(input.issuer),\n\t\tclaims: readClaimsBag(input.claims),\n\t};\n}\n\nexport function normalizeAuthenticatedPrincipalWire(\n\tinput: PrincipalObject,\n): AuthenticatedPrincipal | null {\n\tif (typeof input.subject !== \"string\" || input.subject.trim().length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tsubject: input.subject,\n\t\tdisplayName: resolveDisplayName(input.display_name, input.subject),\n\t\tpicture: readOptionalString(input.picture),\n\t\tissuer: readOptionalString(input.issuer),\n\t\tclaims: readClaimsBag(input.claims),\n\t};\n}\n\nexport interface ProjectAuthenticatedPrincipalOptions {\n\tprincipal?: AuthenticatedPrincipal | null;\n\tfallbackDisplayName?: string;\n\tfallbackSubject?: string;\n\tfallbackIssuer?: string;\n\tfallbackClaims?: Record<string, unknown>;\n}\n\nexport function projectAuthenticatedPrincipal(\n\toptions: ProjectAuthenticatedPrincipalOptions,\n): AuthenticatedPrincipal {\n\tconst principal = options.principal;\n\tconst subject =\n\t\tprincipal?.subject ?? options.fallbackSubject ?? \"context.anonymous\";\n\tconst displayName =\n\t\tprincipal?.displayName?.trim() || options.fallbackDisplayName || subject;\n\n\treturn {\n\t\tsubject,\n\t\tdisplayName,\n\t\tpicture: principal?.picture,\n\t\tissuer: principal?.issuer ?? options.fallbackIssuer,\n\t\tclaims: principal?.claims ?? options.fallbackClaims,\n\t};\n}\n","import type { LogEntry, LoggerTrait } from \"./types\";\n\n/** Simple console-based logger implementation. */\nexport function createConsoleLogger(scope?: string): LoggerTrait {\n\treturn {\n\t\tlog(entry: LogEntry) {\n\t\t\tconst prefix = entry.scope ?? scope ?? \"securitydept\";\n\t\t\tconst label = `[${prefix}]`;\n\n\t\t\tswitch (entry.level) {\n\t\t\t\tcase \"debug\":\n\t\t\t\t\tconsole.debug(label, entry.message, entry.attributes ?? \"\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"info\":\n\t\t\t\t\tconsole.info(label, entry.message, entry.attributes ?? \"\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"warn\":\n\t\t\t\t\tconsole.warn(label, entry.message, entry.attributes ?? \"\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"error\":\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\tlabel,\n\t\t\t\t\t\tentry.message,\n\t\t\t\t\t\tentry.error ?? \"\",\n\t\t\t\t\t\tentry.attributes ?? \"\",\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\t};\n}\n","import type { LoggerTrait } from \"./types\";\n\n/** No-op logger — default when no logger is provided. */\nexport function createNoopLogger(): LoggerTrait {\n\treturn {\n\t\tlog() {},\n\t};\n}\n","import type { TraceEvent, TraceEventSinkTrait } from \"./types\";\n\nexport interface TraceTimelineEntry extends TraceEvent {\n\tid: number;\n\trecordedAtIso: string;\n}\n\nexport interface TraceTimelineStore extends TraceEventSinkTrait {\n\tget(): readonly TraceTimelineEntry[];\n\tsubscribe(listener: () => void): () => void;\n\tclear(): void;\n}\n\nexport function createTraceTimelineStore(limit = 200): TraceTimelineStore {\n\tlet nextId = 1;\n\tlet entries: TraceTimelineEntry[] = [];\n\tconst listeners = new Set<() => void>();\n\n\tfunction readRecordedAtIso(at: number): string {\n\t\tif (!Number.isFinite(at)) {\n\t\t\treturn \"invalid-timestamp\";\n\t\t}\n\n\t\tconst date = new Date(at);\n\t\tconst timestamp = date.getTime();\n\t\tif (!Number.isFinite(timestamp)) {\n\t\t\treturn \"invalid-timestamp\";\n\t\t}\n\n\t\treturn date.toISOString();\n\t}\n\n\tfunction notify() {\n\t\tfor (const listener of listeners) {\n\t\t\tlistener();\n\t\t}\n\t}\n\n\treturn {\n\t\trecord(event) {\n\t\t\tconst entry: TraceTimelineEntry = {\n\t\t\t\t...event,\n\t\t\t\tid: nextId++,\n\t\t\t\trecordedAtIso: readRecordedAtIso(event.at),\n\t\t\t};\n\t\t\tentries = [...entries, entry].slice(-limit);\n\t\t\tnotify();\n\t\t},\n\t\tget() {\n\t\t\treturn entries;\n\t\t},\n\t\tsubscribe(listener) {\n\t\t\tlisteners.add(listener);\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener);\n\t\t\t};\n\t\t},\n\t\tclear() {\n\t\t\tif (entries.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tentries = [];\n\t\t\tnotify();\n\t\t},\n\t};\n}\n","import type { ComputedSignalTrait, ReadableSignalTrait } from \"./types\";\n\n/**\n * Create a computed signal that derives its value from other signals.\n *\n * Semantics: lazy + cached.\n * - The compute function is NOT called at construction time.\n * - `get()` recomputes only when dirty (a dependency changed since last read).\n * - Subscribers are notified only when a dependency changes (dirty-flagged),\n * and the next `get()` will produce the fresh value.\n *\n * Dependency subscriptions are permanent for the lifetime of the computed signal.\n */\nexport function createComputed<T>(\n\tcompute: () => T,\n\tdeps: ReadableSignalTrait<unknown>[],\n): ComputedSignalTrait<T> {\n\tlet cached: T | undefined;\n\tlet dirty = true;\n\tconst listeners = new Set<() => void>();\n\n\tconst markDirty = () => {\n\t\tif (!dirty) {\n\t\t\tdirty = true;\n\t\t\tfor (const listener of listeners) {\n\t\t\t\tlistener();\n\t\t\t}\n\t\t}\n\t};\n\n\t// Dependency subscriptions are permanent.\n\tfor (const dep of deps) {\n\t\tdep.subscribe(markDirty);\n\t}\n\n\treturn {\n\t\tget() {\n\t\t\tif (dirty) {\n\t\t\t\tcached = compute();\n\t\t\t\tdirty = false;\n\t\t\t}\n\t\t\treturn cached as T;\n\t\t},\n\t\tsubscribe(listener: () => void): () => void {\n\t\t\tlisteners.add(listener);\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener);\n\t\t\t};\n\t\t},\n\t};\n}\n","import type { ReadableSignalTrait, WritableSignalTrait } from \"./types\";\n\n/**\n * Minimal writable signal implementation.\n * Snapshot-first: values are immutable snapshots, mutations go through `set()`.\n */\nexport function createSignal<T>(initial: T): WritableSignalTrait<T> {\n\tlet current = initial;\n\tconst listeners = new Set<() => void>();\n\n\treturn {\n\t\tget() {\n\t\t\treturn current;\n\t\t},\n\t\tset(value: T) {\n\t\t\tif (Object.is(current, value)) return;\n\t\t\tcurrent = value;\n\t\t\tfor (const listener of listeners) {\n\t\t\t\tlistener();\n\t\t\t}\n\t\t},\n\t\tsubscribe(listener: () => void): () => void {\n\t\t\tlisteners.add(listener);\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener);\n\t\t\t};\n\t\t},\n\t};\n}\n\n/**\n * Create a read-only view of a writable signal.\n */\nexport function readonlySignal<T>(\n\tsignal: WritableSignalTrait<T>,\n): ReadableSignalTrait<T> {\n\treturn {\n\t\tget: () => signal.get(),\n\t\tsubscribe: (listener) => signal.subscribe(listener),\n\t};\n}\n","// @securitydept/client — Foundation validation baseline\n//\n// Provides a vendor-neutral validation entry aligned with @standard-schema/spec.\n// Any StandardSchemaV1-compatible schema (zod, valibot, arktype, etc.) can be\n// used as the validation contract.\n//\n// Design principles:\n// - The foundation owns the validate-and-interpret contract\n// - Individual SDK packages supply schemas that implement StandardSchemaV1\n// - Consumers never need to know which validation library produced the schema\n\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n// ---------------------------------------------------------------------------\n// Result types\n// ---------------------------------------------------------------------------\n\n/**\n * Successful validation result.\n *\n * Contains the parsed, typed output value from the schema.\n */\nexport interface ValidationSuccess<T> {\n\treadonly success: true;\n\treadonly value: T;\n}\n\n/**\n * Failed validation result.\n *\n * Contains the list of issues reported by the schema.\n */\nexport interface ValidationFailure {\n\treadonly success: false;\n\treadonly issues: ReadonlyArray<StandardSchemaV1.Issue>;\n}\n\n/**\n * Discriminated union of validation outcomes.\n *\n * Consumers can narrow on `success` to access either the typed value\n * or the issue list.\n */\nexport type ValidationResult<T> = ValidationSuccess<T> | ValidationFailure;\n\n// ---------------------------------------------------------------------------\n// Core validation function\n// ---------------------------------------------------------------------------\n\n/**\n * Validate an unknown input against a `@standard-schema`-compatible schema.\n *\n * This is the foundation-level entry point for SDK validation. It accepts\n * any schema implementing `StandardSchemaV1` and returns a discriminated\n * `ValidationResult`.\n *\n * @example\n * ```ts\n * import { validateWithSchema } from \"@securitydept/client\";\n * import { z } from \"zod\";\n *\n * const UserSchema = z.object({ name: z.string() });\n * const result = await validateWithSchema(UserSchema, { name: \"Alice\" });\n * if (result.success) {\n * console.log(result.value.name);\n * } else {\n * console.error(result.issues);\n * }\n * ```\n */\nexport async function validateWithSchema<I, O>(\n\tschema: StandardSchemaV1<I, O>,\n\tinput: unknown,\n): Promise<ValidationResult<O>> {\n\tconst result = await schema[\"~standard\"].validate(input);\n\n\tif (\"value\" in result) {\n\t\treturn { success: true, value: result.value };\n\t}\n\n\treturn { success: false, issues: result.issues };\n}\n\n/**\n * Validate an unknown input synchronously against a `@standard-schema`-compatible schema.\n *\n * Only use this when you know the schema's validate function returns synchronously.\n * Throws if the schema returns a Promise.\n */\nexport function validateWithSchemaSync<I, O>(\n\tschema: StandardSchemaV1<I, O>,\n\tinput: unknown,\n): ValidationResult<O> {\n\tconst result = schema[\"~standard\"].validate(input);\n\n\tif (result instanceof Promise) {\n\t\tthrow new Error(\n\t\t\t\"validateWithSchemaSync: schema returned a Promise. Use validateWithSchema instead.\",\n\t\t);\n\t}\n\n\tif (\"value\" in result) {\n\t\treturn { success: true, value: result.value };\n\t}\n\n\treturn { success: false, issues: result.issues };\n}\n\n// ---------------------------------------------------------------------------\n// Schema creation helper (minimal, vendor-neutral)\n// ---------------------------------------------------------------------------\n\n/**\n * Options for creating a foundation schema.\n */\nexport interface CreateSchemaOptions<O> {\n\t/** Validation function: returns typed output or returns issues. */\n\treadonly validate: (\n\t\tinput: unknown,\n\t) =>\n\t\t| { readonly value: O }\n\t\t| { readonly issues: ReadonlyArray<StandardSchemaV1.Issue> };\n}\n\n/**\n * Create a minimal `@standard-schema`-compatible schema without requiring\n * an external validation library.\n *\n * This is useful for SDK-internal schemas where pulling in zod/valibot\n * would be excessive. For adopter-facing schemas, prefer a real validation\n * library.\n *\n * @example\n * ```ts\n * import { createSchema, validateWithSchema } from \"@securitydept/client\";\n *\n * const NameSchema = createSchema<unknown, { name: string }>({\n * validate(input) {\n * if (typeof input === \"object\" && input !== null && \"name\" in input &&\n * typeof (input as any).name === \"string\") {\n * return { value: { name: (input as any).name } };\n * }\n * return { issues: [{ message: \"Expected object with string 'name'\" }] };\n * },\n * });\n *\n * const result = await validateWithSchema(NameSchema, { name: \"Alice\" });\n * ```\n */\nexport function createSchema<O>(\n\toptions: CreateSchemaOptions<O>,\n): StandardSchemaV1<unknown, O> {\n\treturn {\n\t\t\"~standard\": {\n\t\t\tversion: 1,\n\t\t\tvendor: \"securitydept\",\n\t\t\tvalidate: options.validate,\n\t\t},\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/cancellation/cancellation-token.ts","../src/cancellation/linked-cancellation-token.ts","../src/errors/presentation-descriptor.ts","../src/identity/principal.ts","../src/logging/console-logger.ts","../src/logging/noop-logger.ts","../src/logging/trace-timeline-store.ts","../src/signals/computed.ts","../src/signals/signal.ts","../src/validation/index.ts"],"sourcesContent":["import { ClientError } from \"../errors/client-error\";\nimport { ClientErrorKind } from \"../errors/types\";\nimport type {\n\tCancellationTokenSourceTrait,\n\tCancellationTokenTrait,\n\tDisposableTrait,\n} from \"./types\";\n\nclass CancellationToken implements CancellationTokenTrait {\n\tprivate _isCancelled = false;\n\tprivate _reason: unknown;\n\tprivate readonly _listeners = new Set<(reason: unknown) => void>();\n\n\tget isCancellationRequested(): boolean {\n\t\treturn this._isCancelled;\n\t}\n\n\tget reason(): unknown {\n\t\treturn this._reason;\n\t}\n\n\t/** @internal — called by `CancellationTokenSource`. */\n\t_cancel(reason: unknown): void {\n\t\tif (this._isCancelled) return;\n\t\tthis._isCancelled = true;\n\t\tthis._reason = reason;\n\t\tfor (const listener of this._listeners) {\n\t\t\tlistener(reason);\n\t\t}\n\t\tthis._listeners.clear();\n\t}\n\n\tonCancellationRequested(\n\t\tlistener: (reason: unknown) => void,\n\t): DisposableTrait {\n\t\tif (this._isCancelled) {\n\t\t\t// Already cancelled — invoke immediately.\n\t\t\tlistener(this._reason);\n\t\t\treturn { dispose() {} };\n\t\t}\n\t\tthis._listeners.add(listener);\n\t\treturn {\n\t\t\tdispose: () => {\n\t\t\t\tthis._listeners.delete(listener);\n\t\t\t},\n\t\t};\n\t}\n\n\tthrowIfCancellationRequested(): void {\n\t\tif (this._isCancelled) {\n\t\t\tthrow new ClientError({\n\t\t\t\tkind: ClientErrorKind.Cancelled,\n\t\t\t\tmessage: \"Operation was cancelled\",\n\t\t\t\tcause: this._reason,\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Create a `CancellationTokenSource` that produces a cooperative cancellation token.\n */\nexport function createCancellationTokenSource(): CancellationTokenSourceTrait {\n\tconst ct = new CancellationToken();\n\tlet disposed = false;\n\n\treturn {\n\t\tget token(): CancellationTokenTrait {\n\t\t\treturn ct;\n\t\t},\n\t\tcancel(reason?: unknown) {\n\t\t\tif (!disposed) ct._cancel(reason);\n\t\t},\n\t\tdispose() {\n\t\t\tif (!disposed) {\n\t\t\t\tdisposed = true;\n\t\t\t\tct._cancel(\n\t\t\t\t\tnew ClientError({\n\t\t\t\t\t\tkind: ClientErrorKind.Cancelled,\n\t\t\t\t\t\tmessage: \"Disposed\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t};\n}\n","import { createCancellationTokenSource } from \"./cancellation-token\";\nimport type { CancellationTokenTrait } from \"./types\";\n\n/**\n * Create a cancellation token that fires when ANY of the given source tokens\n * is cancelled.\n *\n * - Zero sources → returns a token that is never cancelled.\n * - One source → returns that source directly (no allocation).\n * - N sources → creates a linked source, subscribes to all, and cleans up\n * subscriptions once the linked token fires.\n *\n * @example\n * const cts = createCancellationTokenSource();\n * const linked = createLinkedCancellationToken(clientRoot.token, cts.token);\n * await transport.execute({ ..., cancellationToken: linked });\n */\nexport function createLinkedCancellationToken(\n\t...sources: CancellationTokenTrait[]\n): CancellationTokenTrait {\n\t// Zero sources — return a token that is never cancelled.\n\tif (sources.length === 0) {\n\t\treturn createCancellationTokenSource().token;\n\t}\n\n\t// One source — return directly; no allocation needed.\n\tif (sources.length === 1) {\n\t\treturn sources[0];\n\t}\n\n\t// Fast-path: return the first already-cancelled source immediately.\n\tfor (const source of sources) {\n\t\tif (source.isCancellationRequested) return source;\n\t}\n\n\tconst linked = createCancellationTokenSource();\n\tconst subscriptions = sources.map((source) =>\n\t\tsource.onCancellationRequested((reason) => linked.cancel(reason)),\n\t);\n\n\t// Clean up all subscriptions once the linked token fires.\n\tlinked.token.onCancellationRequested(() => {\n\t\tfor (const sub of subscriptions) sub.dispose();\n\t});\n\n\treturn linked.token;\n}\n","import { ClientError } from \"./client-error\";\nimport {\n\tClientErrorKind,\n\ttype ErrorPresentation,\n\ttype ErrorPresentationActionDescriptor,\n\ttype ErrorPresentationDescriptor,\n\tErrorPresentationTone,\n\ttype ReadErrorPresentationDescriptorOptions,\n\tUserRecovery,\n} from \"./types\";\n\ninterface ClientErrorLike {\n\tcode: string | null;\n\tkind: ClientErrorKind | null;\n\tmessage: string;\n\trecovery: UserRecovery;\n\tretryable: boolean;\n\tsource?: string;\n\tpresentation?: ErrorPresentation;\n}\n\nconst DEFAULT_RECOVERY_LABELS: Record<UserRecovery, string> = {\n\t[UserRecovery.None]: \"\",\n\t[UserRecovery.Retry]: \"Try again\",\n\t[UserRecovery.RestartFlow]: \"Restart flow\",\n\t[UserRecovery.Reauthenticate]: \"Sign in again\",\n\t[UserRecovery.ContactSupport]: \"Contact support\",\n};\n\nconst POPUP_PRESENTATIONS = {\n\t\"popup.blocked\": {\n\t\ttitle: \"Popup was blocked\",\n\t\tdescription:\n\t\t\t\"The browser blocked the popup window before the login flow could start. Allow popups for this site, then try again.\",\n\t\ttone: ErrorPresentationTone.Warning,\n\t},\n\t\"popup.closed_by_user\": {\n\t\ttitle: \"Popup login was closed\",\n\t\tdescription:\n\t\t\t\"The popup window was closed before the OIDC provider returned a callback. Start the login flow again to continue.\",\n\t\ttone: ErrorPresentationTone.Warning,\n\t},\n\t\"popup.relay_timeout\": {\n\t\ttitle: \"Popup relay timed out\",\n\t\tdescription:\n\t\t\t\"The popup callback never relayed back to the opener before the timeout window expired.\",\n\t\ttone: ErrorPresentationTone.Warning,\n\t},\n\t\"popup.relay_error\": {\n\t\ttitle: \"Popup relay failed\",\n\t\tdescription:\n\t\t\t\"The popup callback page returned an error instead of a callback payload.\",\n\t\ttone: ErrorPresentationTone.Danger,\n\t},\n} as const;\n\nexport function readErrorPresentationDescriptor(\n\terror: unknown,\n\toptions: ReadErrorPresentationDescriptorOptions = {},\n): ErrorPresentationDescriptor {\n\tif (error instanceof ClientError) {\n\t\treturn describeClientErrorPresentation(error, options);\n\t}\n\n\tconst clientErrorLike = coerceClientErrorLike(error);\n\tif (clientErrorLike) {\n\t\treturn describeClientErrorLikePresentation(clientErrorLike, options);\n\t}\n\n\tif (error instanceof Error) {\n\t\treturn {\n\t\t\tcode: null,\n\t\t\tkind: null,\n\t\t\ttitle: options.fallbackTitle ?? \"Operation failed\",\n\t\t\tdescription:\n\t\t\t\terror.message ||\n\t\t\t\toptions.fallbackDescription ||\n\t\t\t\t\"An unexpected error prevented the operation from completing.\",\n\t\t\trecovery: UserRecovery.None,\n\t\t\tretryable: false,\n\t\t\ttone: ErrorPresentationTone.Danger,\n\t\t\tprimaryAction: null,\n\t\t};\n\t}\n\n\treturn {\n\t\tcode: null,\n\t\tkind: null,\n\t\ttitle: options.fallbackTitle ?? \"Operation failed\",\n\t\tdescription:\n\t\t\toptions.fallbackDescription ??\n\t\t\t\"An unexpected error prevented the operation from completing.\",\n\t\trecovery: UserRecovery.None,\n\t\tretryable: false,\n\t\ttone: ErrorPresentationTone.Danger,\n\t\tprimaryAction: null,\n\t};\n}\n\nfunction describeClientErrorPresentation(\n\terror: ClientError,\n\toptions: ReadErrorPresentationDescriptorOptions,\n): ErrorPresentationDescriptor {\n\treturn describeClientErrorLikePresentation(\n\t\t{\n\t\t\tcode: error.code,\n\t\t\tkind: error.kind,\n\t\t\tmessage: error.message,\n\t\t\trecovery: error.recovery,\n\t\t\tretryable: error.retryable,\n\t\t\tsource: error.source,\n\t\t\tpresentation: error.presentation,\n\t\t},\n\t\toptions,\n\t);\n}\n\nfunction describeClientErrorLikePresentation(\n\terror: ClientErrorLike,\n\toptions: ReadErrorPresentationDescriptorOptions,\n): ErrorPresentationDescriptor {\n\tconst popupPresentation =\n\t\tPOPUP_PRESENTATIONS[error.code as keyof typeof POPUP_PRESENTATIONS];\n\tconst title = popupPresentation?.title ?? readClientErrorTitle(error);\n\tconst description =\n\t\tpopupPresentation?.description ??\n\t\terror.presentation?.message ??\n\t\terror.message ??\n\t\toptions.fallbackDescription ??\n\t\t\"The operation could not complete.\";\n\n\treturn {\n\t\tcode: error.code,\n\t\tkind: error.kind,\n\t\tsource: error.source,\n\t\ttitle,\n\t\tdescription,\n\t\trecovery: error.recovery,\n\t\tretryable: error.retryable,\n\t\ttone: popupPresentation?.tone ?? readClientErrorTone(error),\n\t\tprimaryAction: readPrimaryAction(error.recovery, options),\n\t};\n}\n\nfunction readClientErrorTitle(\n\terror: Pick<ClientErrorLike, \"kind\" | \"recovery\">,\n): string {\n\tif (error.kind === ClientErrorKind.Unauthenticated) {\n\t\treturn \"Authentication required\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Unauthorized) {\n\t\treturn \"Access denied\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Cancelled) {\n\t\treturn \"Operation cancelled\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Timeout) {\n\t\treturn \"Request timed out\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Configuration) {\n\t\treturn \"Configuration error\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Validation) {\n\t\treturn \"Validation failed\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Storage) {\n\t\treturn \"Browser storage failed\";\n\t}\n\n\tif (\n\t\terror.kind === ClientErrorKind.Authorization &&\n\t\terror.recovery === UserRecovery.RestartFlow\n\t) {\n\t\treturn \"Flow needs to restart\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Transport) {\n\t\treturn \"Network request failed\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Server) {\n\t\treturn \"Server request failed\";\n\t}\n\n\tif (error.kind === ClientErrorKind.Protocol) {\n\t\treturn \"Request failed\";\n\t}\n\n\treturn \"Operation failed\";\n}\n\nfunction readClientErrorTone(\n\terror: Pick<ClientErrorLike, \"kind\" | \"recovery\">,\n) {\n\tif (error.kind === ClientErrorKind.Cancelled) {\n\t\treturn ErrorPresentationTone.Neutral;\n\t}\n\n\tif (\n\t\terror.kind === ClientErrorKind.Unauthenticated ||\n\t\terror.kind === ClientErrorKind.Timeout ||\n\t\terror.kind === ClientErrorKind.Transport ||\n\t\terror.kind === ClientErrorKind.Server ||\n\t\terror.recovery === UserRecovery.Retry ||\n\t\terror.recovery === UserRecovery.RestartFlow ||\n\t\terror.recovery === UserRecovery.Reauthenticate\n\t) {\n\t\treturn ErrorPresentationTone.Warning;\n\t}\n\n\treturn ErrorPresentationTone.Danger;\n}\n\nfunction coerceClientErrorLike(error: unknown): ClientErrorLike | null {\n\tif (typeof error !== \"object\" || error === null) {\n\t\treturn null;\n\t}\n\n\tconst candidate = error as Record<string, unknown>;\n\tif (typeof candidate.message !== \"string\") {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tcode: typeof candidate.code === \"string\" ? candidate.code : null,\n\t\tkind:\n\t\t\ttypeof candidate.kind === \"string\"\n\t\t\t\t? (candidate.kind as ClientErrorKind)\n\t\t\t\t: null,\n\t\tmessage: candidate.message,\n\t\trecovery:\n\t\t\ttypeof candidate.recovery === \"string\"\n\t\t\t\t? (candidate.recovery as UserRecovery)\n\t\t\t\t: UserRecovery.None,\n\t\tretryable: candidate.retryable === true,\n\t\tsource: typeof candidate.source === \"string\" ? candidate.source : undefined,\n\t\tpresentation: isErrorPresentation(candidate.presentation)\n\t\t\t? candidate.presentation\n\t\t\t: undefined,\n\t};\n}\n\nfunction isErrorPresentation(value: unknown): value is ErrorPresentation {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\ttypeof (value as Record<string, unknown>).code === \"string\" &&\n\t\ttypeof (value as Record<string, unknown>).message === \"string\" &&\n\t\ttypeof (value as Record<string, unknown>).recovery === \"string\"\n\t);\n}\n\nfunction readPrimaryAction(\n\trecovery: UserRecovery,\n\toptions: ReadErrorPresentationDescriptorOptions,\n): ErrorPresentationActionDescriptor | null {\n\tif (recovery === UserRecovery.None) {\n\t\treturn null;\n\t}\n\n\tconst label =\n\t\toptions.recoveryLabels?.[recovery] ?? DEFAULT_RECOVERY_LABELS[recovery];\n\tif (!label) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\trecovery,\n\t\tlabel,\n\t\thref: options.recoveryLinks?.[recovery] ?? null,\n\t};\n}\n","export interface AuthenticatedPrincipal {\n\tsubject: string;\n\tdisplayName: string;\n\tpicture?: string;\n\tissuer?: string;\n\tclaims?: Record<string, unknown>;\n}\n\ntype PrincipalObject = Record<string, unknown>;\n\nfunction readClaimsBag(value: unknown): Record<string, unknown> | undefined {\n\treturn typeof value === \"object\" && value !== null\n\t\t? (value as Record<string, unknown>)\n\t\t: undefined;\n}\n\nfunction readOptionalString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" ? value : undefined;\n}\n\nfunction resolveDisplayName(displayName: unknown, subject: string): string {\n\tif (typeof displayName === \"string\" && displayName.trim().length > 0) {\n\t\treturn displayName;\n\t}\n\n\treturn subject;\n}\n\nexport function normalizeAuthenticatedPrincipal(\n\tinput: PrincipalObject,\n): AuthenticatedPrincipal | null {\n\tif (typeof input.subject !== \"string\" || input.subject.trim().length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tsubject: input.subject,\n\t\tdisplayName: resolveDisplayName(input.displayName, input.subject),\n\t\tpicture: readOptionalString(input.picture),\n\t\tissuer: readOptionalString(input.issuer),\n\t\tclaims: readClaimsBag(input.claims),\n\t};\n}\n\nexport function normalizeAuthenticatedPrincipalWire(\n\tinput: PrincipalObject,\n): AuthenticatedPrincipal | null {\n\tif (typeof input.subject !== \"string\" || input.subject.trim().length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tsubject: input.subject,\n\t\tdisplayName: resolveDisplayName(input.display_name, input.subject),\n\t\tpicture: readOptionalString(input.picture),\n\t\tissuer: readOptionalString(input.issuer),\n\t\tclaims: readClaimsBag(input.claims),\n\t};\n}\n\nexport interface ProjectAuthenticatedPrincipalOptions {\n\tprincipal?: AuthenticatedPrincipal | null;\n\tfallbackDisplayName?: string;\n\tfallbackSubject?: string;\n\tfallbackIssuer?: string;\n\tfallbackClaims?: Record<string, unknown>;\n}\n\nexport function projectAuthenticatedPrincipal(\n\toptions: ProjectAuthenticatedPrincipalOptions,\n): AuthenticatedPrincipal {\n\tconst principal = options.principal;\n\tconst subject =\n\t\tprincipal?.subject ?? options.fallbackSubject ?? \"context.anonymous\";\n\tconst displayName =\n\t\tprincipal?.displayName?.trim() || options.fallbackDisplayName || subject;\n\n\treturn {\n\t\tsubject,\n\t\tdisplayName,\n\t\tpicture: principal?.picture,\n\t\tissuer: principal?.issuer ?? options.fallbackIssuer,\n\t\tclaims: principal?.claims ?? options.fallbackClaims,\n\t};\n}\n","import type { LogEntry, LoggerTrait } from \"./types\";\n\n/** Simple console-based logger implementation. */\nexport function createConsoleLogger(scope?: string): LoggerTrait {\n\treturn {\n\t\tlog(entry: LogEntry) {\n\t\t\tconst prefix = entry.scope ?? scope ?? \"securitydept\";\n\t\t\tconst label = `[${prefix}]`;\n\n\t\t\tswitch (entry.level) {\n\t\t\t\tcase \"debug\":\n\t\t\t\t\tconsole.debug(label, entry.message, entry.attributes ?? \"\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"info\":\n\t\t\t\t\tconsole.info(label, entry.message, entry.attributes ?? \"\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"warn\":\n\t\t\t\t\tconsole.warn(label, entry.message, entry.attributes ?? \"\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"error\":\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\tlabel,\n\t\t\t\t\t\tentry.message,\n\t\t\t\t\t\tentry.error ?? \"\",\n\t\t\t\t\t\tentry.attributes ?? \"\",\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\t};\n}\n","import type { LoggerTrait } from \"./types\";\n\n/** No-op logger — default when no logger is provided. */\nexport function createNoopLogger(): LoggerTrait {\n\treturn {\n\t\tlog() {},\n\t};\n}\n","import type { TraceEvent, TraceEventSinkTrait } from \"./types\";\n\nexport interface TraceTimelineEntry extends TraceEvent {\n\tid: number;\n\trecordedAtIso: string;\n}\n\nexport interface TraceTimelineStore extends TraceEventSinkTrait {\n\tget(): readonly TraceTimelineEntry[];\n\tsubscribe(listener: () => void): () => void;\n\tclear(): void;\n}\n\nexport function createTraceTimelineStore(limit = 200): TraceTimelineStore {\n\tlet nextId = 1;\n\tlet entries: TraceTimelineEntry[] = [];\n\tconst listeners = new Set<() => void>();\n\n\tfunction readRecordedAtIso(at: number): string {\n\t\tif (!Number.isFinite(at)) {\n\t\t\treturn \"invalid-timestamp\";\n\t\t}\n\n\t\tconst date = new Date(at);\n\t\tconst timestamp = date.getTime();\n\t\tif (!Number.isFinite(timestamp)) {\n\t\t\treturn \"invalid-timestamp\";\n\t\t}\n\n\t\treturn date.toISOString();\n\t}\n\n\tfunction notify() {\n\t\tfor (const listener of listeners) {\n\t\t\tlistener();\n\t\t}\n\t}\n\n\treturn {\n\t\trecord(event) {\n\t\t\tconst entry: TraceTimelineEntry = {\n\t\t\t\t...event,\n\t\t\t\tid: nextId++,\n\t\t\t\trecordedAtIso: readRecordedAtIso(event.at),\n\t\t\t};\n\t\t\tentries = [...entries, entry].slice(-limit);\n\t\t\tnotify();\n\t\t},\n\t\tget() {\n\t\t\treturn entries;\n\t\t},\n\t\tsubscribe(listener) {\n\t\t\tlisteners.add(listener);\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener);\n\t\t\t};\n\t\t},\n\t\tclear() {\n\t\t\tif (entries.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tentries = [];\n\t\t\tnotify();\n\t\t},\n\t};\n}\n","import type { ComputedSignalTrait, ReadableSignalTrait } from \"./types\";\n\n/**\n * Create a computed signal that derives its value from other signals.\n *\n * Semantics: lazy + cached.\n * - The compute function is NOT called at construction time.\n * - `get()` recomputes only when dirty (a dependency changed since last read).\n * - Subscribers are notified only when a dependency changes (dirty-flagged),\n * and the next `get()` will produce the fresh value.\n *\n * Dependency subscriptions are permanent for the lifetime of the computed signal.\n */\nexport function createComputed<T>(\n\tcompute: () => T,\n\tdeps: ReadableSignalTrait<unknown>[],\n): ComputedSignalTrait<T> {\n\tlet cached: T | undefined;\n\tlet dirty = true;\n\tconst listeners = new Set<() => void>();\n\n\tconst markDirty = () => {\n\t\tif (!dirty) {\n\t\t\tdirty = true;\n\t\t\tfor (const listener of listeners) {\n\t\t\t\tlistener();\n\t\t\t}\n\t\t}\n\t};\n\n\t// Dependency subscriptions are permanent.\n\tfor (const dep of deps) {\n\t\tdep.subscribe(markDirty);\n\t}\n\n\treturn {\n\t\tget() {\n\t\t\tif (dirty) {\n\t\t\t\tcached = compute();\n\t\t\t\tdirty = false;\n\t\t\t}\n\t\t\treturn cached as T;\n\t\t},\n\t\tsubscribe(listener: () => void): () => void {\n\t\t\tlisteners.add(listener);\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener);\n\t\t\t};\n\t\t},\n\t};\n}\n","import type { ReadableSignalTrait, WritableSignalTrait } from \"./types\";\n\n/**\n * Minimal writable signal implementation.\n * Snapshot-first: values are immutable snapshots, mutations go through `set()`.\n */\nexport function createSignal<T>(initial: T): WritableSignalTrait<T> {\n\tlet current = initial;\n\tconst listeners = new Set<() => void>();\n\n\treturn {\n\t\tget() {\n\t\t\treturn current;\n\t\t},\n\t\tset(value: T) {\n\t\t\tif (Object.is(current, value)) return;\n\t\t\tcurrent = value;\n\t\t\tfor (const listener of listeners) {\n\t\t\t\tlistener();\n\t\t\t}\n\t\t},\n\t\tsubscribe(listener: () => void): () => void {\n\t\t\tlisteners.add(listener);\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener);\n\t\t\t};\n\t\t},\n\t};\n}\n\n/**\n * Create a read-only view of a writable signal.\n */\nexport function readonlySignal<T>(\n\tsignal: WritableSignalTrait<T>,\n): ReadableSignalTrait<T> {\n\treturn {\n\t\tget: () => signal.get(),\n\t\tsubscribe: (listener) => signal.subscribe(listener),\n\t};\n}\n","// @securitydept/client — Foundation validation baseline\n//\n// Provides a vendor-neutral validation entry aligned with @standard-schema/spec.\n// Any StandardSchemaV1-compatible schema (zod, valibot, arktype, etc.) can be\n// used as the validation contract.\n//\n// Design principles:\n// - The foundation owns the validate-and-interpret contract\n// - Individual SDK packages supply schemas that implement StandardSchemaV1\n// - Consumers never need to know which validation library produced the schema\n\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n// ---------------------------------------------------------------------------\n// Result types\n// ---------------------------------------------------------------------------\n\n/**\n * Successful validation result.\n *\n * Contains the parsed, typed output value from the schema.\n */\nexport interface ValidationSuccess<T> {\n\treadonly success: true;\n\treadonly value: T;\n}\n\n/**\n * Failed validation result.\n *\n * Contains the list of issues reported by the schema.\n */\nexport interface ValidationFailure {\n\treadonly success: false;\n\treadonly issues: ReadonlyArray<StandardSchemaV1.Issue>;\n}\n\n/**\n * Discriminated union of validation outcomes.\n *\n * Consumers can narrow on `success` to access either the typed value\n * or the issue list.\n */\nexport type ValidationResult<T> = ValidationSuccess<T> | ValidationFailure;\n\n// ---------------------------------------------------------------------------\n// Core validation function\n// ---------------------------------------------------------------------------\n\n/**\n * Validate an unknown input against a `@standard-schema`-compatible schema.\n *\n * This is the foundation-level entry point for SDK validation. It accepts\n * any schema implementing `StandardSchemaV1` and returns a discriminated\n * `ValidationResult`.\n *\n * @example\n * ```ts\n * import { validateWithSchema } from \"@securitydept/client\";\n * import { z } from \"zod\";\n *\n * const UserSchema = z.object({ name: z.string() });\n * const result = await validateWithSchema(UserSchema, { name: \"Alice\" });\n * if (result.success) {\n * console.log(result.value.name);\n * } else {\n * console.error(result.issues);\n * }\n * ```\n */\nexport async function validateWithSchema<I, O>(\n\tschema: StandardSchemaV1<I, O>,\n\tinput: unknown,\n): Promise<ValidationResult<O>> {\n\tconst result = await schema[\"~standard\"].validate(input);\n\n\tif (\"value\" in result) {\n\t\treturn { success: true, value: result.value };\n\t}\n\n\treturn { success: false, issues: result.issues };\n}\n\n/**\n * Validate an unknown input synchronously against a `@standard-schema`-compatible schema.\n *\n * Only use this when you know the schema's validate function returns synchronously.\n * Throws if the schema returns a Promise.\n */\nexport function validateWithSchemaSync<I, O>(\n\tschema: StandardSchemaV1<I, O>,\n\tinput: unknown,\n): ValidationResult<O> {\n\tconst result = schema[\"~standard\"].validate(input);\n\n\tif (result instanceof Promise) {\n\t\tthrow new Error(\n\t\t\t\"validateWithSchemaSync: schema returned a Promise. Use validateWithSchema instead.\",\n\t\t);\n\t}\n\n\tif (\"value\" in result) {\n\t\treturn { success: true, value: result.value };\n\t}\n\n\treturn { success: false, issues: result.issues };\n}\n\n// ---------------------------------------------------------------------------\n// Schema creation helper (minimal, vendor-neutral)\n// ---------------------------------------------------------------------------\n\n/**\n * Options for creating a foundation schema.\n */\nexport interface CreateSchemaOptions<O> {\n\t/** Validation function: returns typed output or returns issues. */\n\treadonly validate: (\n\t\tinput: unknown,\n\t) =>\n\t\t| { readonly value: O }\n\t\t| { readonly issues: ReadonlyArray<StandardSchemaV1.Issue> };\n}\n\n/**\n * Create a minimal `@standard-schema`-compatible schema without requiring\n * an external validation library.\n *\n * This is useful for SDK-internal schemas where pulling in zod/valibot\n * would be excessive. For adopter-facing schemas, prefer a real validation\n * library.\n *\n * @example\n * ```ts\n * import { createSchema, validateWithSchema } from \"@securitydept/client\";\n *\n * const NameSchema = createSchema<unknown, { name: string }>({\n * validate(input) {\n * if (typeof input === \"object\" && input !== null && \"name\" in input &&\n * typeof (input as any).name === \"string\") {\n * return { value: { name: (input as any).name } };\n * }\n * return { issues: [{ message: \"Expected object with string 'name'\" }] };\n * },\n * });\n *\n * const result = await validateWithSchema(NameSchema, { name: \"Alice\" });\n * ```\n */\nexport function createSchema<O>(\n\toptions: CreateSchemaOptions<O>,\n): StandardSchemaV1<unknown, O> {\n\treturn {\n\t\t\"~standard\": {\n\t\t\tversion: 1,\n\t\t\tvendor: \"securitydept\",\n\t\t\tvalidate: options.validate,\n\t\t},\n\t};\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { a as RecordStore, i as PersistentAuthStore, n as EphemeralFlowStore, o as RecoverableStateStore, r as KeyedEphemeralFlowStore, s as StoredEnvelope, t as Codec } from "../types-C59fcM5W.mjs";
2
- import { a as createEphemeralFlowStore, i as CreateKeyedEphemeralFlowStoreOptions, n as createJsonCodec, o as createKeyedEphemeralFlowStore, r as CreateEphemeralFlowStoreOptions, t as createInMemoryRecordStore } from "../index-DbYSFHDe.mjs";
1
+ import { a as RecordStore, i as PersistentAuthStore, n as EphemeralFlowStore, o as RecoverableStateStore, r as KeyedEphemeralFlowStore, s as StoredEnvelope, t as Codec } from "../types-BwcnUGNl.mjs";
2
+ import { a as createEphemeralFlowStore, i as CreateKeyedEphemeralFlowStoreOptions, n as createJsonCodec, o as createKeyedEphemeralFlowStore, r as CreateEphemeralFlowStoreOptions, t as createInMemoryRecordStore } from "../index-COUt5x9Z.mjs";
3
3
  export { Codec, CreateEphemeralFlowStoreOptions, CreateKeyedEphemeralFlowStoreOptions, EphemeralFlowStore, KeyedEphemeralFlowStore, PersistentAuthStore, RecordStore, RecoverableStateStore, StoredEnvelope, createEphemeralFlowStore, createInMemoryRecordStore, createJsonCodec, createKeyedEphemeralFlowStore };
@@ -1,4 +1,4 @@
1
- import { a as RecordStore } from "../../types-C59fcM5W.mjs";
1
+ import { a as RecordStore } from "../../types-BwcnUGNl.mjs";
2
2
 
3
3
  //#region src/persistence/web/stores.d.ts
4
4
  /**
@@ -1,51 +1,2 @@
1
- //#region src/persistence/web/stores.ts
2
- /**
3
- * `RecordStore` backed by `localStorage`.
4
- * @param prefix Optional key prefix to isolate the keyspace.
5
- */
6
- function createLocalStorageStore(prefix = "") {
7
- return {
8
- async get(key) {
9
- return globalThis.localStorage.getItem(prefix + key);
10
- },
11
- async set(key, value) {
12
- globalThis.localStorage.setItem(prefix + key, value);
13
- },
14
- async take(key) {
15
- const storageKey = prefix + key;
16
- const value = globalThis.localStorage.getItem(storageKey);
17
- if (value !== null) globalThis.localStorage.removeItem(storageKey);
18
- return value;
19
- },
20
- async remove(key) {
21
- globalThis.localStorage.removeItem(prefix + key);
22
- }
23
- };
24
- }
25
- /**
26
- * `RecordStore` backed by `sessionStorage`.
27
- * @param prefix Optional key prefix to isolate the keyspace.
28
- */
29
- function createSessionStorageStore(prefix = "") {
30
- return {
31
- async get(key) {
32
- return globalThis.sessionStorage.getItem(prefix + key);
33
- },
34
- async set(key, value) {
35
- globalThis.sessionStorage.setItem(prefix + key, value);
36
- },
37
- async take(key) {
38
- const storageKey = prefix + key;
39
- const value = globalThis.sessionStorage.getItem(storageKey);
40
- if (value !== null) globalThis.sessionStorage.removeItem(storageKey);
41
- return value;
42
- },
43
- async remove(key) {
44
- globalThis.sessionStorage.removeItem(prefix + key);
45
- }
46
- };
47
- }
48
- //#endregion
1
+ import { n as createSessionStorageStore, t as createLocalStorageStore } from "../../web-DQmMqZnR.mjs";
49
2
  export { createLocalStorageStore, createSessionStorageStore };
50
-
51
- //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"persistence-CSB8770k.mjs","names":[],"sources":["../src/persistence/json-codec.ts","../src/persistence/ephemeral-flow-store.ts","../src/persistence/memory-store.ts"],"sourcesContent":["import type { Codec } from \"./types\";\n\n/** Default JSON codec. */\nexport function createJsonCodec<T>(): Codec<T> {\n\treturn {\n\t\tencode(value: T): string {\n\t\t\treturn JSON.stringify(value);\n\t\t},\n\t\tdecode(raw: string): T {\n\t\t\treturn JSON.parse(raw) as T;\n\t\t},\n\t};\n}\n","import { createJsonCodec } from \"./json-codec\";\nimport type {\n\tCodec,\n\tEphemeralFlowStore,\n\tKeyedEphemeralFlowStore,\n\tRecordStore,\n} from \"./types\";\n\nexport interface CreateEphemeralFlowStoreOptions<T> {\n\tstore: RecordStore;\n\tkey: string;\n\tcodec?: Codec<T>;\n}\n\nexport interface CreateKeyedEphemeralFlowStoreOptions<T> {\n\tstore: RecordStore;\n\tkeyPrefix: string;\n\tcodec?: Codec<T>;\n}\n\nfunction resolveStorageKey(keyPrefix: string, key: string): string {\n\treturn keyPrefix.length > 0 ? `${keyPrefix}:${key}` : key;\n}\n\nfunction requireRecordStoreTake(\n\tstore: RecordStore,\n): NonNullable<RecordStore[\"take\"]> {\n\tif (!store.take) {\n\t\tthrow new Error(\n\t\t\t\"Ephemeral flow stores require RecordStore.take() for atomic single-consume semantics\",\n\t\t);\n\t}\n\n\treturn store.take.bind(store);\n}\n\n/**\n * Create a typed ephemeral flow-state store backed by a low-level `RecordStore`.\n *\n * Intended for short-lived browser/session coordination state such as pending\n * redirects or callback fragments.\n */\nexport function createEphemeralFlowStore<T>(\n\toptions: CreateEphemeralFlowStoreOptions<T>,\n): EphemeralFlowStore<T> {\n\tconst codec = options.codec ?? createJsonCodec<T>();\n\tconst storeTake = requireRecordStoreTake(options.store);\n\n\tasync function load(): Promise<T | null> {\n\t\tconst raw = await options.store.get(options.key);\n\t\treturn raw === null ? null : codec.decode(raw);\n\t}\n\n\tasync function save(value: T): Promise<void> {\n\t\tawait options.store.set(options.key, codec.encode(value));\n\t}\n\n\tasync function consume(): Promise<T | null> {\n\t\tconst raw = await storeTake(options.key);\n\t\treturn raw === null ? null : codec.decode(raw);\n\t}\n\n\tasync function clear(): Promise<void> {\n\t\tawait options.store.remove(options.key);\n\t}\n\n\treturn {\n\t\tload,\n\t\tsave,\n\t\tconsume,\n\t\tclear,\n\t};\n}\n\n/**\n * Create a typed keyed ephemeral flow-state store backed by a low-level\n * `RecordStore`.\n *\n * Intended for short-lived browser/session coordination state where multiple\n * pending records must coexist and be consumed independently.\n */\nexport function createKeyedEphemeralFlowStore<T>(\n\toptions: CreateKeyedEphemeralFlowStoreOptions<T>,\n): KeyedEphemeralFlowStore<T> {\n\tconst codec = options.codec ?? createJsonCodec<T>();\n\tconst storeTake = requireRecordStoreTake(options.store);\n\n\tasync function load(key: string): Promise<T | null> {\n\t\tconst raw = await options.store.get(\n\t\t\tresolveStorageKey(options.keyPrefix, key),\n\t\t);\n\t\treturn raw === null ? null : codec.decode(raw);\n\t}\n\n\tasync function save(key: string, value: T): Promise<void> {\n\t\tawait options.store.set(\n\t\t\tresolveStorageKey(options.keyPrefix, key),\n\t\t\tcodec.encode(value),\n\t\t);\n\t}\n\n\tasync function take(key: string): Promise<T | null> {\n\t\tconst raw = await storeTake(resolveStorageKey(options.keyPrefix, key));\n\t\treturn raw === null ? null : codec.decode(raw);\n\t}\n\n\tasync function clear(key: string): Promise<void> {\n\t\tawait options.store.remove(resolveStorageKey(options.keyPrefix, key));\n\t}\n\n\treturn {\n\t\tload,\n\t\tsave,\n\t\ttake,\n\t\tclear,\n\t};\n}\n","import type { RecordStore } from \"./types\";\n\n/** In-memory `RecordStore` — useful for tests and SSR. */\nexport function createInMemoryRecordStore(): RecordStore {\n\tconst store = new Map<string, string>();\n\n\treturn {\n\t\tasync get(key: string): Promise<string | null> {\n\t\t\treturn store.get(key) ?? null;\n\t\t},\n\t\tasync set(key: string, value: string): Promise<void> {\n\t\t\tstore.set(key, value);\n\t\t},\n\t\tasync take(key: string): Promise<string | null> {\n\t\t\tconst value = store.get(key) ?? null;\n\t\t\tif (value !== null) {\n\t\t\t\tstore.delete(key);\n\t\t\t}\n\t\t\treturn value;\n\t\t},\n\t\tasync remove(key: string): Promise<void> {\n\t\t\tstore.delete(key);\n\t\t},\n\t};\n}\n"],"mappings":";;AAGA,SAAgB,kBAA+B;AAC9C,QAAO;EACN,OAAO,OAAkB;AACxB,UAAO,KAAK,UAAU,MAAM;;EAE7B,OAAO,KAAgB;AACtB,UAAO,KAAK,MAAM,IAAI;;EAEvB;;;;ACSF,SAAS,kBAAkB,WAAmB,KAAqB;AAClE,QAAO,UAAU,SAAS,IAAI,GAAG,UAAU,GAAG,QAAQ;;AAGvD,SAAS,uBACR,OACmC;AACnC,KAAI,CAAC,MAAM,KACV,OAAM,IAAI,MACT,uFACA;AAGF,QAAO,MAAM,KAAK,KAAK,MAAM;;;;;;;;AAS9B,SAAgB,yBACf,SACwB;CACxB,MAAM,QAAQ,QAAQ,SAAS,iBAAoB;CACnD,MAAM,YAAY,uBAAuB,QAAQ,MAAM;CAEvD,eAAe,OAA0B;EACxC,MAAM,MAAM,MAAM,QAAQ,MAAM,IAAI,QAAQ,IAAI;AAChD,SAAO,QAAQ,OAAO,OAAO,MAAM,OAAO,IAAI;;CAG/C,eAAe,KAAK,OAAyB;AAC5C,QAAM,QAAQ,MAAM,IAAI,QAAQ,KAAK,MAAM,OAAO,MAAM,CAAC;;CAG1D,eAAe,UAA6B;EAC3C,MAAM,MAAM,MAAM,UAAU,QAAQ,IAAI;AACxC,SAAO,QAAQ,OAAO,OAAO,MAAM,OAAO,IAAI;;CAG/C,eAAe,QAAuB;AACrC,QAAM,QAAQ,MAAM,OAAO,QAAQ,IAAI;;AAGxC,QAAO;EACN;EACA;EACA;EACA;EACA;;;;;;;;;AAUF,SAAgB,8BACf,SAC6B;CAC7B,MAAM,QAAQ,QAAQ,SAAS,iBAAoB;CACnD,MAAM,YAAY,uBAAuB,QAAQ,MAAM;CAEvD,eAAe,KAAK,KAAgC;EACnD,MAAM,MAAM,MAAM,QAAQ,MAAM,IAC/B,kBAAkB,QAAQ,WAAW,IAAI,CACzC;AACD,SAAO,QAAQ,OAAO,OAAO,MAAM,OAAO,IAAI;;CAG/C,eAAe,KAAK,KAAa,OAAyB;AACzD,QAAM,QAAQ,MAAM,IACnB,kBAAkB,QAAQ,WAAW,IAAI,EACzC,MAAM,OAAO,MAAM,CACnB;;CAGF,eAAe,KAAK,KAAgC;EACnD,MAAM,MAAM,MAAM,UAAU,kBAAkB,QAAQ,WAAW,IAAI,CAAC;AACtE,SAAO,QAAQ,OAAO,OAAO,MAAM,OAAO,IAAI;;CAG/C,eAAe,MAAM,KAA4B;AAChD,QAAM,QAAQ,MAAM,OAAO,kBAAkB,QAAQ,WAAW,IAAI,CAAC;;AAGtE,QAAO;EACN;EACA;EACA;EACA;EACA;;;;;AChHF,SAAgB,4BAAyC;CACxD,MAAM,wBAAQ,IAAI,KAAqB;AAEvC,QAAO;EACN,MAAM,IAAI,KAAqC;AAC9C,UAAO,MAAM,IAAI,IAAI,IAAI;;EAE1B,MAAM,IAAI,KAAa,OAA8B;AACpD,SAAM,IAAI,KAAK,MAAM;;EAEtB,MAAM,KAAK,KAAqC;GAC/C,MAAM,QAAQ,MAAM,IAAI,IAAI,IAAI;AAChC,OAAI,UAAU,KACb,OAAM,OAAO,IAAI;AAElB,UAAO;;EAER,MAAM,OAAO,KAA4B;AACxC,SAAM,OAAO,IAAI;;EAElB"}
1
+ {"version":3,"file":"persistence-CSB8770k.mjs","names":[],"sources":["../src/persistence/json-codec.ts","../src/persistence/ephemeral-flow-store.ts","../src/persistence/memory-store.ts"],"sourcesContent":["import type { Codec } from \"./types\";\n\n/** Default JSON codec. */\nexport function createJsonCodec<T>(): Codec<T> {\n\treturn {\n\t\tencode(value: T): string {\n\t\t\treturn JSON.stringify(value);\n\t\t},\n\t\tdecode(raw: string): T {\n\t\t\treturn JSON.parse(raw) as T;\n\t\t},\n\t};\n}\n","import { createJsonCodec } from \"./json-codec\";\nimport type {\n\tCodec,\n\tEphemeralFlowStore,\n\tKeyedEphemeralFlowStore,\n\tRecordStore,\n} from \"./types\";\n\nexport interface CreateEphemeralFlowStoreOptions<T> {\n\tstore: RecordStore;\n\tkey: string;\n\tcodec?: Codec<T>;\n}\n\nexport interface CreateKeyedEphemeralFlowStoreOptions<T> {\n\tstore: RecordStore;\n\tkeyPrefix: string;\n\tcodec?: Codec<T>;\n}\n\nfunction resolveStorageKey(keyPrefix: string, key: string): string {\n\treturn keyPrefix.length > 0 ? `${keyPrefix}:${key}` : key;\n}\n\nfunction requireRecordStoreTake(\n\tstore: RecordStore,\n): NonNullable<RecordStore[\"take\"]> {\n\tif (!store.take) {\n\t\tthrow new Error(\n\t\t\t\"Ephemeral flow stores require RecordStore.take() for atomic single-consume semantics\",\n\t\t);\n\t}\n\n\treturn store.take.bind(store);\n}\n\n/**\n * Create a typed ephemeral flow-state store backed by a low-level `RecordStore`.\n *\n * Intended for short-lived browser/session coordination state such as pending\n * redirects or callback fragments.\n */\nexport function createEphemeralFlowStore<T>(\n\toptions: CreateEphemeralFlowStoreOptions<T>,\n): EphemeralFlowStore<T> {\n\tconst codec = options.codec ?? createJsonCodec<T>();\n\tconst storeTake = requireRecordStoreTake(options.store);\n\n\tasync function load(): Promise<T | null> {\n\t\tconst raw = await options.store.get(options.key);\n\t\treturn raw === null ? null : codec.decode(raw);\n\t}\n\n\tasync function save(value: T): Promise<void> {\n\t\tawait options.store.set(options.key, codec.encode(value));\n\t}\n\n\tasync function consume(): Promise<T | null> {\n\t\tconst raw = await storeTake(options.key);\n\t\treturn raw === null ? null : codec.decode(raw);\n\t}\n\n\tasync function clear(): Promise<void> {\n\t\tawait options.store.remove(options.key);\n\t}\n\n\treturn {\n\t\tload,\n\t\tsave,\n\t\tconsume,\n\t\tclear,\n\t};\n}\n\n/**\n * Create a typed keyed ephemeral flow-state store backed by a low-level\n * `RecordStore`.\n *\n * Intended for short-lived browser/session coordination state where multiple\n * pending records must coexist and be consumed independently.\n */\nexport function createKeyedEphemeralFlowStore<T>(\n\toptions: CreateKeyedEphemeralFlowStoreOptions<T>,\n): KeyedEphemeralFlowStore<T> {\n\tconst codec = options.codec ?? createJsonCodec<T>();\n\tconst storeTake = requireRecordStoreTake(options.store);\n\n\tasync function load(key: string): Promise<T | null> {\n\t\tconst raw = await options.store.get(\n\t\t\tresolveStorageKey(options.keyPrefix, key),\n\t\t);\n\t\treturn raw === null ? null : codec.decode(raw);\n\t}\n\n\tasync function save(key: string, value: T): Promise<void> {\n\t\tawait options.store.set(\n\t\t\tresolveStorageKey(options.keyPrefix, key),\n\t\t\tcodec.encode(value),\n\t\t);\n\t}\n\n\tasync function take(key: string): Promise<T | null> {\n\t\tconst raw = await storeTake(resolveStorageKey(options.keyPrefix, key));\n\t\treturn raw === null ? null : codec.decode(raw);\n\t}\n\n\tasync function clear(key: string): Promise<void> {\n\t\tawait options.store.remove(resolveStorageKey(options.keyPrefix, key));\n\t}\n\n\treturn {\n\t\tload,\n\t\tsave,\n\t\ttake,\n\t\tclear,\n\t};\n}\n","import type { RecordStore } from \"./types\";\n\n/** In-memory `RecordStore` — useful for tests and SSR. */\nexport function createInMemoryRecordStore(): RecordStore {\n\tconst store = new Map<string, string>();\n\n\treturn {\n\t\tasync get(key: string): Promise<string | null> {\n\t\t\treturn store.get(key) ?? null;\n\t\t},\n\t\tasync set(key: string, value: string): Promise<void> {\n\t\t\tstore.set(key, value);\n\t\t},\n\t\tasync take(key: string): Promise<string | null> {\n\t\t\tconst value = store.get(key) ?? null;\n\t\t\tif (value !== null) {\n\t\t\t\tstore.delete(key);\n\t\t\t}\n\t\t\treturn value;\n\t\t},\n\t\tasync remove(key: string): Promise<void> {\n\t\t\tstore.delete(key);\n\t\t},\n\t};\n}\n"],"mappings":";;AAGA,SAAgB,kBAA+B;CAC9C,OAAO;EACN,OAAO,OAAkB;GACxB,OAAO,KAAK,UAAU,MAAM;;EAE7B,OAAO,KAAgB;GACtB,OAAO,KAAK,MAAM,IAAI;;EAEvB;;;;ACSF,SAAS,kBAAkB,WAAmB,KAAqB;CAClE,OAAO,UAAU,SAAS,IAAI,GAAG,UAAU,GAAG,QAAQ;;AAGvD,SAAS,uBACR,OACmC;CACnC,IAAI,CAAC,MAAM,MACV,MAAM,IAAI,MACT,uFACA;CAGF,OAAO,MAAM,KAAK,KAAK,MAAM;;;;;;;;AAS9B,SAAgB,yBACf,SACwB;CACxB,MAAM,QAAQ,QAAQ,SAAS,iBAAoB;CACnD,MAAM,YAAY,uBAAuB,QAAQ,MAAM;CAEvD,eAAe,OAA0B;EACxC,MAAM,MAAM,MAAM,QAAQ,MAAM,IAAI,QAAQ,IAAI;EAChD,OAAO,QAAQ,OAAO,OAAO,MAAM,OAAO,IAAI;;CAG/C,eAAe,KAAK,OAAyB;EAC5C,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAAK,MAAM,OAAO,MAAM,CAAC;;CAG1D,eAAe,UAA6B;EAC3C,MAAM,MAAM,MAAM,UAAU,QAAQ,IAAI;EACxC,OAAO,QAAQ,OAAO,OAAO,MAAM,OAAO,IAAI;;CAG/C,eAAe,QAAuB;EACrC,MAAM,QAAQ,MAAM,OAAO,QAAQ,IAAI;;CAGxC,OAAO;EACN;EACA;EACA;EACA;EACA;;;;;;;;;AAUF,SAAgB,8BACf,SAC6B;CAC7B,MAAM,QAAQ,QAAQ,SAAS,iBAAoB;CACnD,MAAM,YAAY,uBAAuB,QAAQ,MAAM;CAEvD,eAAe,KAAK,KAAgC;EACnD,MAAM,MAAM,MAAM,QAAQ,MAAM,IAC/B,kBAAkB,QAAQ,WAAW,IAAI,CACzC;EACD,OAAO,QAAQ,OAAO,OAAO,MAAM,OAAO,IAAI;;CAG/C,eAAe,KAAK,KAAa,OAAyB;EACzD,MAAM,QAAQ,MAAM,IACnB,kBAAkB,QAAQ,WAAW,IAAI,EACzC,MAAM,OAAO,MAAM,CACnB;;CAGF,eAAe,KAAK,KAAgC;EACnD,MAAM,MAAM,MAAM,UAAU,kBAAkB,QAAQ,WAAW,IAAI,CAAC;EACtE,OAAO,QAAQ,OAAO,OAAO,MAAM,OAAO,IAAI;;CAG/C,eAAe,MAAM,KAA4B;EAChD,MAAM,QAAQ,MAAM,OAAO,kBAAkB,QAAQ,WAAW,IAAI,CAAC;;CAGtE,OAAO;EACN;EACA;EACA;EACA;EACA;;;;;AChHF,SAAgB,4BAAyC;CACxD,MAAM,wBAAQ,IAAI,KAAqB;CAEvC,OAAO;EACN,MAAM,IAAI,KAAqC;GAC9C,OAAO,MAAM,IAAI,IAAI,IAAI;;EAE1B,MAAM,IAAI,KAAa,OAA8B;GACpD,MAAM,IAAI,KAAK,MAAM;;EAEtB,MAAM,KAAK,KAAqC;GAC/C,MAAM,QAAQ,MAAM,IAAI,IAAI,IAAI;GAChC,IAAI,UAAU,MACb,MAAM,OAAO,IAAI;GAElB,OAAO;;EAER,MAAM,OAAO,KAA4B;GACxC,MAAM,OAAO,IAAI;;EAElB"}
@@ -7,7 +7,9 @@ const ResolutionStatus = {
7
7
  };
8
8
  /** Overall plan status. */
9
9
  const PlanStatus = {
10
+ /** There are still pending requirements. */
10
11
  Pending: "pending",
12
+ /** All requirements have been resolved. */
11
13
  Settled: "settled"
12
14
  };
13
15
  /** Error thrown when a planner operation is invalid. */
@@ -192,4 +194,4 @@ function createPlannerHost(options) {
192
194
  //#endregion
193
195
  export { RequirementPlannerError as a, PlanStatus as i, createPlannerHost as n, ResolutionStatus as o, resolveEffectiveClientSet as r, createRequirementPlanner as s, RequirementsClientSetComposition as t };
194
196
 
195
- //# sourceMappingURL=planner-host-CV38bMSn.mjs.map
197
+ //# sourceMappingURL=planner-host-BTWie0lo.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"planner-host-CV38bMSn.mjs","names":[],"sources":["../src/auth-coordination/requirement-planner.ts","../src/auth-coordination/planner-host.ts"],"sourcesContent":["// Shared auth requirement planner — headless multi-requirement coordination\n//\n// Canonical location: @securitydept/client/auth-coordination\n//\n// Provides a mode-agnostic, framework-agnostic primitive for expressing\n// multi-requirement / multi-auth-context orchestration flows.\n//\n// Core concept:\n// 1. Define a list of AuthRequirements (e.g. \"need session\", \"need oidc token\")\n// 2. The RequirementPlanner tells you which requirement is pending next\n// 3. When a requirement is resolved, it advances to the next one\n// 4. When all are resolved, the plan is settled\n//\n// The planner is intentionally headless — it does NOT:\n// - Know about specific auth modes or protocols\n// - Render UI or manage popup/redirect flows\n// - Own cross-tab state or leader election\n//\n// Owner rationale: This module does not depend on token-set domain objects\n// (token snapshots, bearer propagation, OIDC callback, refresh). Its\n// RequirementKind vocabulary covers session, OIDC, and custom — clearly\n// cross-auth-context. It is a shared coordination capability, not a\n// token-set product surface. Canonical owner is @securitydept/client.\n\n// ---------------------------------------------------------------------------\n// Requirement definition\n// ---------------------------------------------------------------------------\n\n/** A single auth requirement within an orchestration plan. */\nexport interface AuthRequirement {\n\t/** Unique identifier for this requirement within the plan. */\n\tid: string;\n\t/**\n\t * The kind of auth requirement.\n\t *\n\t * An opaque string. Each auth-context family (token-set, session, basic-auth)\n\t * or adopter project may define its own named constants.\n\t *\n\t * @example \"session\", \"frontend_oidc\", \"backend_oidc\", \"custom\"\n\t */\n\tkind: string;\n\t/** Human-readable label (for logging / debugging). */\n\tlabel?: string;\n\t/** Arbitrary metadata the adopter can attach. */\n\tattributes?: Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Resolution\n// ---------------------------------------------------------------------------\n\n/** Outcome of resolving a single requirement. */\nexport const ResolutionStatus = {\n\tFulfilled: \"fulfilled\",\n\tFailed: \"failed\",\n\tSkipped: \"skipped\",\n} as const;\n\nexport type ResolutionStatus =\n\t(typeof ResolutionStatus)[keyof typeof ResolutionStatus];\n\n/** The resolution result for a single requirement. */\nexport interface RequirementResolution {\n\t/** The requirement ID this resolution applies to. */\n\trequirementId: string;\n\t/** Outcome status. */\n\tstatus: ResolutionStatus;\n\t/** Optional error or reason for failure/skip. */\n\treason?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Plan state\n// ---------------------------------------------------------------------------\n\n/** Overall plan status. */\nexport const PlanStatus = {\n\t/** There are still pending requirements. */\n\tPending: \"pending\",\n\t/** All requirements have been resolved. */\n\tSettled: \"settled\",\n} as const;\n\nexport type PlanStatus = (typeof PlanStatus)[keyof typeof PlanStatus];\n\n/** Internal state for a single requirement within the plan. */\ninterface RequirementState {\n\trequirement: AuthRequirement;\n\tresolution: RequirementResolution | null;\n}\n\n/** Read-only snapshot of the current plan state. */\nexport interface PlanSnapshot {\n\t/** Overall plan status. */\n\tstatus: PlanStatus;\n\t/** The next pending requirement, or `null` if settled. */\n\tnextPending: AuthRequirement | null;\n\t/** All resolutions collected so far (in order). */\n\tresolutions: readonly RequirementResolution[];\n\t/** Total requirement count. */\n\ttotal: number;\n\t/** Number of resolved requirements. */\n\tresolved: number;\n}\n\n// ---------------------------------------------------------------------------\n// Planner errors\n// ---------------------------------------------------------------------------\n\n/** Error thrown when a planner operation is invalid. */\nexport class RequirementPlannerError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"RequirementPlannerError\";\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// RequirementPlanner\n// ---------------------------------------------------------------------------\n\n/** Options for creating a {@link RequirementPlanner}. */\nexport interface CreateRequirementPlannerOptions {\n\t/** Ordered list of requirements to fulfill. */\n\trequirements: readonly AuthRequirement[];\n}\n\n/**\n * Headless multi-requirement planner.\n *\n * Drives a sequential requirement fulfillment flow:\n * 1. Call {@link snapshot} to see what's pending.\n * 2. Resolve the {@link PlanSnapshot.nextPending} requirement.\n * 3. Call {@link resolve} with the result.\n * 4. Repeat until {@link PlanSnapshot.status} is `\"settled\"`.\n *\n * @example\n * ```ts\n * import { createRequirementPlanner, PlanStatus, ResolutionStatus } from \"@securitydept/client/auth-coordination\";\n *\n * const planner = createRequirementPlanner({\n * requirements: [\n * { id: \"session\", kind: \"session\" },\n * { id: \"api-token\", kind: \"backend_oidc\" },\n * ],\n * });\n *\n * let snap = planner.snapshot();\n * while (snap.status === PlanStatus.Pending) {\n * const req = snap.nextPending!;\n * // ... perform auth action for req.kind ...\n * planner.resolve({ requirementId: req.id, status: ResolutionStatus.Fulfilled });\n * snap = planner.snapshot();\n * }\n * // snap.status === PlanStatus.Settled\n * ```\n */\nexport interface RequirementPlanner {\n\t/** Get a read-only snapshot of the current plan state. */\n\tsnapshot(): PlanSnapshot;\n\t/** Resolve the next pending requirement. */\n\tresolve(resolution: RequirementResolution): void;\n\t/** Reset the planner to its initial state. */\n\treset(): void;\n}\n\n/**\n * Create a headless requirement planner.\n *\n * @see {@link RequirementPlanner} for usage.\n */\nexport function createRequirementPlanner(\n\toptions: CreateRequirementPlannerOptions,\n): RequirementPlanner {\n\tif (options.requirements.length === 0) {\n\t\tthrow new RequirementPlannerError(\n\t\t\t\"Cannot create a planner with zero requirements\",\n\t\t);\n\t}\n\n\t// Validate uniqueness of requirement IDs.\n\tconst ids = new Set<string>();\n\tfor (const req of options.requirements) {\n\t\tif (ids.has(req.id)) {\n\t\t\tthrow new RequirementPlannerError(\n\t\t\t\t`Duplicate requirement ID: \"${req.id}\"`,\n\t\t\t);\n\t\t}\n\t\tids.add(req.id);\n\t}\n\n\tlet states: RequirementState[] = [];\n\n\t/** Shallow-clone a requirement to prevent external mutation. */\n\tfunction cloneRequirement(r: AuthRequirement): AuthRequirement {\n\t\treturn {\n\t\t\t...r,\n\t\t\tattributes: r.attributes ? { ...r.attributes } : undefined,\n\t\t};\n\t}\n\n\tfunction initStates(): void {\n\t\tstates = options.requirements.map((r) => ({\n\t\t\trequirement: cloneRequirement(r),\n\t\t\tresolution: null,\n\t\t}));\n\t}\n\n\tinitStates();\n\n\tfunction findNextPending(): RequirementState | null {\n\t\treturn states.find((s) => s.resolution === null) ?? null;\n\t}\n\n\treturn {\n\t\tsnapshot(): PlanSnapshot {\n\t\t\tconst next = findNextPending();\n\t\t\tconst resolutions: RequirementResolution[] = [];\n\t\t\tfor (const s of states) {\n\t\t\t\tif (s.resolution !== null) resolutions.push({ ...s.resolution });\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstatus: next === null ? PlanStatus.Settled : PlanStatus.Pending,\n\t\t\t\tnextPending: next ? cloneRequirement(next.requirement) : null,\n\t\t\t\tresolutions,\n\t\t\t\ttotal: states.length,\n\t\t\t\tresolved: resolutions.length,\n\t\t\t};\n\t\t},\n\n\t\tresolve(resolution: RequirementResolution): void {\n\t\t\tconst next = findNextPending();\n\t\t\tif (next === null) {\n\t\t\t\tthrow new RequirementPlannerError(\n\t\t\t\t\t\"Cannot resolve: all requirements are already settled\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (resolution.requirementId !== next.requirement.id) {\n\t\t\t\tthrow new RequirementPlannerError(\n\t\t\t\t\t`Cannot resolve requirement \"${resolution.requirementId}\": ` +\n\t\t\t\t\t\t`the next pending requirement is \"${next.requirement.id}\"`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tnext.resolution = resolution;\n\t\t},\n\n\t\treset(): void {\n\t\t\tinitStates();\n\t\t},\n\t};\n}\n","// Shared planner-host — route-scoped auth coordination host layer\n//\n// Canonical location: @securitydept/client/auth-coordination\n//\n// Provides the host-layer contract for multi-requirement auth guards:\n// - AuthGuardClientOption: a single requirement candidate with its\n// authentication check and unauthenticated action\n// - RequirementsClientSet / ScopedRequirementsClientSet: composable\n// requirement client collections with inherit/merge/replace semantics\n// - PlannerHost: the coordination contract that selects which pending\n// candidate to act on, built on RequirementPlanner for sequential\n// resolution discipline\n//\n// The planner-host is intentionally headless and framework-agnostic.\n// Framework-specific provider/lookup (Angular DI, React Context) is handled\n// by the framework adapter packages (@securitydept/client-angular,\n// @securitydept/client-react).\n//\n// Architecture:\n// PlannerHost.evaluate() uses createRequirementPlanner() internally:\n// 1. Build AuthRequirement[] from candidates' requirementId/kind/label/attributes\n// 2. Auto-resolve already-authenticated requirements as Fulfilled\n// 3. Ask the planner for nextPending — respects declaration order and\n// skips already-satisfied requirements\n// 4. Among candidates still unauthenticated, use selectCandidate() to\n// pick the one to act on (default: the one matching nextPending first)\n//\n// Owner rationale: This module does not depend on token-set, session, or\n// basic-auth domain objects. It is a shared coordination capability.\n// Canonical owner is @securitydept/client.\n\nimport {\n\ttype AuthRequirement,\n\tcreateRequirementPlanner,\n\tPlanStatus,\n\tResolutionStatus,\n} from \"./requirement-planner\";\n\n// ---------------------------------------------------------------------------\n// Auth guard client option — a single requirement candidate\n// ---------------------------------------------------------------------------\n\n/**\n * A single requirement candidate that a guard submits to the planner-host.\n *\n * Each option describes:\n * 1. What auth requirement it represents (id, kind, label)\n * 2. How to check whether it's already satisfied\n * 3. What to do when the planner selects it as the next pending action\n */\nexport interface AuthGuardClientOption {\n\t/** Unique identifier for this requirement candidate. */\n\trequirementId: string;\n\n\t/**\n\t * The kind of auth requirement (opaque string).\n\t * @example \"session\", \"frontend_oidc\", \"backend_oidc\"\n\t */\n\trequirementKind: string;\n\n\t/** Human-readable label (for logging / debugging / chooser UI). */\n\tlabel?: string;\n\n\t/** Arbitrary metadata the adopter can attach. */\n\tattributes?: Record<string, unknown>;\n\n\t/**\n\t * Check whether this requirement candidate is already authenticated.\n\t * Called synchronously by the planner-host during evaluation.\n\t */\n\tcheckAuthenticated: () => boolean;\n\n\t/**\n\t * Action to execute when this candidate is selected as the pending\n\t * requirement by the planner-host.\n\t *\n\t * Return value semantics:\n\t * - `true` — allow navigation (requirement will be resolved externally)\n\t * - `false` — block navigation\n\t * - `string` — redirect to the given URL\n\t * - `Promise<...>` — async version of the above\n\t */\n\tonUnauthenticated: () => boolean | string | Promise<boolean | string>;\n}\n\n// ---------------------------------------------------------------------------\n// Requirements client set — composable requirement collections\n// ---------------------------------------------------------------------------\n\n/** A flat collection of requirement candidates. */\nexport interface RequirementsClientSet {\n\treadonly options: readonly AuthGuardClientOption[];\n}\n\n/**\n * How a child scope's requirements client set relates to its parent.\n *\n * - `inherit` — use the parent's set as-is (child adds nothing)\n * - `merge` — inherit parent's options, then append/override with child's\n * (child options with the same `requirementId` replace parent ones)\n * - `replace` — discard parent's set entirely, use child's set only\n */\nexport const RequirementsClientSetComposition = {\n\tInherit: \"inherit\",\n\tMerge: \"merge\",\n\tReplace: \"replace\",\n} as const;\n\nexport type RequirementsClientSetComposition =\n\t(typeof RequirementsClientSetComposition)[keyof typeof RequirementsClientSetComposition];\n\n/** A requirements client set annotated with its composition strategy. */\nexport interface ScopedRequirementsClientSet {\n\t/** How this set composes with its parent scope. */\n\tcomposition: RequirementsClientSetComposition;\n\t/** The requirement candidates declared at this scope. */\n\toptions: readonly AuthGuardClientOption[];\n}\n\n// ---------------------------------------------------------------------------\n// Resolve effective client set (parent + child composition)\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the effective requirements client set by composing a parent set\n * with a child scope's declaration.\n *\n * @param parentOptions - The parent scope's resolved options (or empty).\n * @param child - The child scope's scoped set (with composition strategy).\n * @returns The effective options after composition.\n */\nexport function resolveEffectiveClientSet(\n\tparentOptions: readonly AuthGuardClientOption[],\n\tchild: ScopedRequirementsClientSet,\n): AuthGuardClientOption[] {\n\tswitch (child.composition) {\n\t\tcase RequirementsClientSetComposition.Inherit:\n\t\t\treturn [...parentOptions];\n\n\t\tcase RequirementsClientSetComposition.Merge: {\n\t\t\t// Start with parent options, then override/append with child options.\n\t\t\t// Child options with the same requirementId replace parent ones.\n\t\t\tconst merged = new Map<string, AuthGuardClientOption>();\n\t\t\tfor (const opt of parentOptions) {\n\t\t\t\tmerged.set(opt.requirementId, opt);\n\t\t\t}\n\t\t\tfor (const opt of child.options) {\n\t\t\t\tmerged.set(opt.requirementId, opt);\n\t\t\t}\n\t\t\treturn [...merged.values()];\n\t\t}\n\n\t\tcase RequirementsClientSetComposition.Replace:\n\t\t\treturn [...child.options];\n\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = child.composition;\n\t\t\tthrow new Error(\n\t\t\t\t`[resolveEffectiveClientSet] Unknown composition: ${_exhaustive}`,\n\t\t\t);\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Planner-host result\n// ---------------------------------------------------------------------------\n\n/** Result of a planner-host evaluation. */\nexport interface PlannerHostResult {\n\t/** Whether all candidates are authenticated (route can activate). */\n\tallAuthenticated: boolean;\n\n\t/** All candidates that are not yet authenticated. */\n\tunauthenticatedCandidates: readonly AuthGuardClientOption[];\n\n\t/**\n\t * The candidate selected by the planner as the next one to act on.\n\t * `null` when `allAuthenticated` is true.\n\t *\n\t * Determined by the RequirementPlanner's sequential resolution discipline:\n\t * the first unresolved requirement in declaration order that is still\n\t * unauthenticated, then optionally further narrowed by `selectCandidate`.\n\t */\n\tpendingCandidate: AuthGuardClientOption | null;\n}\n\n// ---------------------------------------------------------------------------\n// Planner-host contract and factory\n// ---------------------------------------------------------------------------\n\n/**\n * A candidate selection strategy.\n *\n * Receives the list of unauthenticated candidates (in RequirementPlanner\n * declaration order) and returns the one that should be acted on next.\n * The default strategy picks the first one (matching the planner's\n * `nextPending` requirement).\n *\n * Async selectors are supported: return a `Promise<AuthGuardClientOption>`\n * to implement interactive chooser UIs (e.g. show a dialog and await the\n * user's pick before navigation continues).\n *\n * @example\n * ```ts\n * // Async chooser: show a dialog and await user's selection\n * const selectCandidate: CandidateSelector = async (candidates) => {\n * const chosen = await showChooserDialog(candidates);\n * return chosen;\n * };\n * ```\n */\nexport type CandidateSelector = (\n\tunauthenticated: readonly AuthGuardClientOption[],\n) => AuthGuardClientOption | Promise<AuthGuardClientOption>;\n\n/** The planner-host coordination contract. */\nexport interface PlannerHost {\n\t/**\n\t * Evaluate a set of requirement candidates using RequirementPlanner's\n\t * sequential resolution discipline.\n\t *\n\t * Always returns a Promise so that custom async selectors (e.g. chooser\n\t * dialog) are fully supported. Callers should always `await` the result.\n\t *\n\t * @param candidates - The resolved candidates from the guard's client options.\n\t * @returns Promise resolving to the evaluation result.\n\t */\n\tevaluate(\n\t\tcandidates: readonly AuthGuardClientOption[],\n\t): Promise<PlannerHostResult>;\n}\n\n/** Options for {@link createPlannerHost}. */\nexport interface CreatePlannerHostOptions {\n\t/**\n\t * Custom candidate selection strategy.\n\t *\n\t * The default strategy picks the candidate that corresponds to the\n\t * RequirementPlanner's `nextPending` requirement (first unauthenticated\n\t * in declaration order).\n\t *\n\t * A custom strategy can further filter or reorder the unauthenticated\n\t * candidates, e.g. to implement a chooser UI that lets the user pick.\n\t *\n\t * @default first-unauthenticated-matching-planner-nextPending\n\t *\n\t * @example\n\t * ```ts\n\t * // Async chooser: present all options and await user's pick\n\t * createPlannerHost({\n\t * selectCandidate: async (candidates) => {\n\t * return await showChooserDialog(candidates);\n\t * },\n\t * })\n\t * ```\n\t */\n\tselectCandidate?: CandidateSelector;\n}\n\n/**\n * Default candidate selector: picks the first unauthenticated candidate\n * in declaration order (which aligns with RequirementPlanner's nextPending).\n */\nfunction defaultSelectCandidate(\n\tunauthenticated: readonly AuthGuardClientOption[],\n): AuthGuardClientOption {\n\treturn unauthenticated[0];\n}\n\n/**\n * Create a planner-host instance backed by {@link createRequirementPlanner}.\n *\n * **How it works:**\n *\n * 1. `evaluate()` builds an `AuthRequirement[]` from `candidates`\n * (using each candidate's `requirementId`, `requirementKind`, `label`,\n * and `attributes`).\n * 2. A `RequirementPlanner` is created for this evaluation cycle.\n * 3. Already-authenticated requirements are auto-resolved as `Fulfilled`,\n * respecting the planner's sequential discipline.\n * 4. The planner's `nextPending` determines the first unresolved requirement\n * in declaration order.\n * 5. `selectCandidate` is called with all unauthenticated candidates so\n * adopters can implement custom chooser strategies (e.g. UI dialog).\n * The default strategy picks the first candidate (matching nextPending).\n *\n * `evaluate()` always returns a `Promise` to accommodate async selectors.\n * For synchronous selectors the Promise resolves in the same microtask tick.\n *\n * @example\n * ```ts\n * import { createPlannerHost } from \"@securitydept/client/auth-coordination\";\n *\n * // Default sequential strategy (sync selector, wrapped in Promise)\n * const host = createPlannerHost();\n * const result = await host.evaluate(candidates);\n *\n * // Async chooser strategy — show a dialog and await user's pick\n * const hostWithChooser = createPlannerHost({\n * selectCandidate: async (candidates) => {\n * return await showChooserDialog(candidates);\n * },\n * });\n * ```\n */\nexport function createPlannerHost(\n\toptions?: CreatePlannerHostOptions,\n): PlannerHost {\n\tconst selector = options?.selectCandidate ?? defaultSelectCandidate;\n\n\treturn {\n\t\tasync evaluate(\n\t\t\tcandidates: readonly AuthGuardClientOption[],\n\t\t): Promise<PlannerHostResult> {\n\t\t\tif (candidates.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tallAuthenticated: true,\n\t\t\t\t\tunauthenticatedCandidates: [],\n\t\t\t\t\tpendingCandidate: null,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Build AuthRequirement[] from candidates for the planner.\n\t\t\tconst requirements: AuthRequirement[] = candidates.map((c) => ({\n\t\t\t\tid: c.requirementId,\n\t\t\t\tkind: c.requirementKind,\n\t\t\t\tlabel: c.label,\n\t\t\t\tattributes: c.attributes,\n\t\t\t}));\n\n\t\t\t// Create a fresh planner for this evaluation cycle.\n\t\t\tconst planner = createRequirementPlanner({ requirements });\n\n\t\t\t// Auto-resolve already-authenticated requirements in declaration order.\n\t\t\t// The planner enforces sequential discipline: we must resolve in order.\n\t\t\tfor (const candidate of candidates) {\n\t\t\t\tconst snap = planner.snapshot();\n\t\t\t\tif (snap.status === PlanStatus.Settled) break;\n\n\t\t\t\t// Resolve the planner's current nextPending if it's authenticated.\n\t\t\t\t// Only advance the planner for requirements that are currently pending\n\t\t\t\t// in order — skip if the current nextPending doesn't match this candidate.\n\t\t\t\tif (\n\t\t\t\t\tsnap.nextPending?.id === candidate.requirementId &&\n\t\t\t\t\tcandidate.checkAuthenticated()\n\t\t\t\t) {\n\t\t\t\t\tplanner.resolve({\n\t\t\t\t\t\trequirementId: candidate.requirementId,\n\t\t\t\t\t\tstatus: ResolutionStatus.Fulfilled,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check overall status after resolving all authenticated requirements.\n\t\t\tconst finalSnap = planner.snapshot();\n\n\t\t\t// Collect all unauthenticated candidates.\n\t\t\tconst unauthenticated = candidates.filter((c) => !c.checkAuthenticated());\n\n\t\t\tif (\n\t\t\t\tfinalSnap.status === PlanStatus.Settled ||\n\t\t\t\tunauthenticated.length === 0\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\tallAuthenticated: true,\n\t\t\t\t\tunauthenticatedCandidates: [],\n\t\t\t\t\tpendingCandidate: null,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Await selection — supports both sync and async selectors.\n\t\t\t// The selector receives the unauthenticated candidates in declaration\n\t\t\t// order so adopters can implement any picking strategy.\n\t\t\tconst pendingCandidate = await selector(unauthenticated);\n\t\t\treturn {\n\t\t\t\tallAuthenticated: false,\n\t\t\t\tunauthenticatedCandidates: unauthenticated,\n\t\t\t\tpendingCandidate,\n\t\t\t};\n\t\t},\n\t};\n}\n"],"mappings":";;AAoDA,MAAa,mBAAmB;CAC/B,WAAW;CACX,QAAQ;CACR,SAAS;CACT;;AAoBD,MAAa,aAAa;CAEzB,SAAS;CAET,SAAS;CACT;;AA6BD,IAAa,0BAAb,cAA6C,MAAM;CAClD,YAAY,SAAiB;AAC5B,QAAM,QAAQ;AACd,OAAK,OAAO;;;;;;;;AA0Dd,SAAgB,yBACf,SACqB;AACrB,KAAI,QAAQ,aAAa,WAAW,EACnC,OAAM,IAAI,wBACT,iDACA;CAIF,MAAM,sBAAM,IAAI,KAAa;AAC7B,MAAK,MAAM,OAAO,QAAQ,cAAc;AACvC,MAAI,IAAI,IAAI,IAAI,GAAG,CAClB,OAAM,IAAI,wBACT,8BAA8B,IAAI,GAAG,GACrC;AAEF,MAAI,IAAI,IAAI,GAAG;;CAGhB,IAAI,SAA6B,EAAE;;CAGnC,SAAS,iBAAiB,GAAqC;AAC9D,SAAO;GACN,GAAG;GACH,YAAY,EAAE,aAAa,EAAE,GAAG,EAAE,YAAY,GAAG,KAAA;GACjD;;CAGF,SAAS,aAAmB;AAC3B,WAAS,QAAQ,aAAa,KAAK,OAAO;GACzC,aAAa,iBAAiB,EAAE;GAChC,YAAY;GACZ,EAAE;;AAGJ,aAAY;CAEZ,SAAS,kBAA2C;AACnD,SAAO,OAAO,MAAM,MAAM,EAAE,eAAe,KAAK,IAAI;;AAGrD,QAAO;EACN,WAAyB;GACxB,MAAM,OAAO,iBAAiB;GAC9B,MAAM,cAAuC,EAAE;AAC/C,QAAK,MAAM,KAAK,OACf,KAAI,EAAE,eAAe,KAAM,aAAY,KAAK,EAAE,GAAG,EAAE,YAAY,CAAC;AAEjE,UAAO;IACN,QAAQ,SAAS,OAAO,WAAW,UAAU,WAAW;IACxD,aAAa,OAAO,iBAAiB,KAAK,YAAY,GAAG;IACzD;IACA,OAAO,OAAO;IACd,UAAU,YAAY;IACtB;;EAGF,QAAQ,YAAyC;GAChD,MAAM,OAAO,iBAAiB;AAC9B,OAAI,SAAS,KACZ,OAAM,IAAI,wBACT,uDACA;AAEF,OAAI,WAAW,kBAAkB,KAAK,YAAY,GACjD,OAAM,IAAI,wBACT,+BAA+B,WAAW,cAAc,sCACnB,KAAK,YAAY,GAAG,GACzD;AAEF,QAAK,aAAa;;EAGnB,QAAc;AACb,eAAY;;EAEb;;;;;;;;;;;;ACnJF,MAAa,mCAAmC;CAC/C,SAAS;CACT,OAAO;CACP,SAAS;CACT;;;;;;;;;AAyBD,SAAgB,0BACf,eACA,OAC0B;AAC1B,SAAQ,MAAM,aAAd;EACC,KAAK,iCAAiC,QACrC,QAAO,CAAC,GAAG,cAAc;EAE1B,KAAK,iCAAiC,OAAO;GAG5C,MAAM,yBAAS,IAAI,KAAoC;AACvD,QAAK,MAAM,OAAO,cACjB,QAAO,IAAI,IAAI,eAAe,IAAI;AAEnC,QAAK,MAAM,OAAO,MAAM,QACvB,QAAO,IAAI,IAAI,eAAe,IAAI;AAEnC,UAAO,CAAC,GAAG,OAAO,QAAQ,CAAC;;EAG5B,KAAK,iCAAiC,QACrC,QAAO,CAAC,GAAG,MAAM,QAAQ;EAE1B,SAAS;GACR,MAAM,cAAqB,MAAM;AACjC,SAAM,IAAI,MACT,oDAAoD,cACpD;;;;;;;;AAyGJ,SAAS,uBACR,iBACwB;AACxB,QAAO,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCxB,SAAgB,kBACf,SACc;CACd,MAAM,WAAW,SAAS,mBAAmB;AAE7C,QAAO,EACN,MAAM,SACL,YAC6B;AAC7B,MAAI,WAAW,WAAW,EACzB,QAAO;GACN,kBAAkB;GAClB,2BAA2B,EAAE;GAC7B,kBAAkB;GAClB;EAYF,MAAM,UAAU,yBAAyB,EAAE,cARH,WAAW,KAAK,OAAO;GAC9D,IAAI,EAAE;GACN,MAAM,EAAE;GACR,OAAO,EAAE;GACT,YAAY,EAAE;GACd,EAAE,EAGsD,CAAC;AAI1D,OAAK,MAAM,aAAa,YAAY;GACnC,MAAM,OAAO,QAAQ,UAAU;AAC/B,OAAI,KAAK,WAAW,WAAW,QAAS;AAKxC,OACC,KAAK,aAAa,OAAO,UAAU,iBACnC,UAAU,oBAAoB,CAE9B,SAAQ,QAAQ;IACf,eAAe,UAAU;IACzB,QAAQ,iBAAiB;IACzB,CAAC;;EAKJ,MAAM,YAAY,QAAQ,UAAU;EAGpC,MAAM,kBAAkB,WAAW,QAAQ,MAAM,CAAC,EAAE,oBAAoB,CAAC;AAEzE,MACC,UAAU,WAAW,WAAW,WAChC,gBAAgB,WAAW,EAE3B,QAAO;GACN,kBAAkB;GAClB,2BAA2B,EAAE;GAC7B,kBAAkB;GAClB;AAOF,SAAO;GACN,kBAAkB;GAClB,2BAA2B;GAC3B,kBAJwB,MAAM,SAAS,gBAAgB;GAKvD;IAEF"}
1
+ {"version":3,"file":"planner-host-BTWie0lo.mjs","names":[],"sources":["../src/auth-coordination/requirement-planner.ts","../src/auth-coordination/planner-host.ts"],"sourcesContent":["// Shared auth requirement planner — headless multi-requirement coordination\n//\n// Canonical location: @securitydept/client/auth-coordination\n//\n// Provides a mode-agnostic, framework-agnostic primitive for expressing\n// multi-requirement / multi-auth-context orchestration flows.\n//\n// Core concept:\n// 1. Define a list of AuthRequirements (e.g. \"need session\", \"need oidc token\")\n// 2. The RequirementPlanner tells you which requirement is pending next\n// 3. When a requirement is resolved, it advances to the next one\n// 4. When all are resolved, the plan is settled\n//\n// The planner is intentionally headless — it does NOT:\n// - Know about specific auth modes or protocols\n// - Render UI or manage popup/redirect flows\n// - Own cross-tab state or leader election\n//\n// Owner rationale: This module does not depend on token-set domain objects\n// (token snapshots, bearer propagation, OIDC callback, refresh). Its\n// RequirementKind vocabulary covers session, OIDC, and custom — clearly\n// cross-auth-context. It is a shared coordination capability, not a\n// token-set product surface. Canonical owner is @securitydept/client.\n\n// ---------------------------------------------------------------------------\n// Requirement definition\n// ---------------------------------------------------------------------------\n\n/** A single auth requirement within an orchestration plan. */\nexport interface AuthRequirement {\n\t/** Unique identifier for this requirement within the plan. */\n\tid: string;\n\t/**\n\t * The kind of auth requirement.\n\t *\n\t * An opaque string. Each auth-context family (token-set, session, basic-auth)\n\t * or adopter project may define its own named constants.\n\t *\n\t * @example \"session\", \"frontend_oidc\", \"backend_oidc\", \"custom\"\n\t */\n\tkind: string;\n\t/** Human-readable label (for logging / debugging). */\n\tlabel?: string;\n\t/** Arbitrary metadata the adopter can attach. */\n\tattributes?: Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Resolution\n// ---------------------------------------------------------------------------\n\n/** Outcome of resolving a single requirement. */\nexport const ResolutionStatus = {\n\tFulfilled: \"fulfilled\",\n\tFailed: \"failed\",\n\tSkipped: \"skipped\",\n} as const;\n\nexport type ResolutionStatus =\n\t(typeof ResolutionStatus)[keyof typeof ResolutionStatus];\n\n/** The resolution result for a single requirement. */\nexport interface RequirementResolution {\n\t/** The requirement ID this resolution applies to. */\n\trequirementId: string;\n\t/** Outcome status. */\n\tstatus: ResolutionStatus;\n\t/** Optional error or reason for failure/skip. */\n\treason?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Plan state\n// ---------------------------------------------------------------------------\n\n/** Overall plan status. */\nexport const PlanStatus = {\n\t/** There are still pending requirements. */\n\tPending: \"pending\",\n\t/** All requirements have been resolved. */\n\tSettled: \"settled\",\n} as const;\n\nexport type PlanStatus = (typeof PlanStatus)[keyof typeof PlanStatus];\n\n/** Internal state for a single requirement within the plan. */\ninterface RequirementState {\n\trequirement: AuthRequirement;\n\tresolution: RequirementResolution | null;\n}\n\n/** Read-only snapshot of the current plan state. */\nexport interface PlanSnapshot {\n\t/** Overall plan status. */\n\tstatus: PlanStatus;\n\t/** The next pending requirement, or `null` if settled. */\n\tnextPending: AuthRequirement | null;\n\t/** All resolutions collected so far (in order). */\n\tresolutions: readonly RequirementResolution[];\n\t/** Total requirement count. */\n\ttotal: number;\n\t/** Number of resolved requirements. */\n\tresolved: number;\n}\n\n// ---------------------------------------------------------------------------\n// Planner errors\n// ---------------------------------------------------------------------------\n\n/** Error thrown when a planner operation is invalid. */\nexport class RequirementPlannerError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"RequirementPlannerError\";\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// RequirementPlanner\n// ---------------------------------------------------------------------------\n\n/** Options for creating a {@link RequirementPlanner}. */\nexport interface CreateRequirementPlannerOptions {\n\t/** Ordered list of requirements to fulfill. */\n\trequirements: readonly AuthRequirement[];\n}\n\n/**\n * Headless multi-requirement planner.\n *\n * Drives a sequential requirement fulfillment flow:\n * 1. Call {@link snapshot} to see what's pending.\n * 2. Resolve the {@link PlanSnapshot.nextPending} requirement.\n * 3. Call {@link resolve} with the result.\n * 4. Repeat until {@link PlanSnapshot.status} is `\"settled\"`.\n *\n * @example\n * ```ts\n * import { createRequirementPlanner, PlanStatus, ResolutionStatus } from \"@securitydept/client/auth-coordination\";\n *\n * const planner = createRequirementPlanner({\n * requirements: [\n * { id: \"session\", kind: \"session\" },\n * { id: \"api-token\", kind: \"backend_oidc\" },\n * ],\n * });\n *\n * let snap = planner.snapshot();\n * while (snap.status === PlanStatus.Pending) {\n * const req = snap.nextPending!;\n * // ... perform auth action for req.kind ...\n * planner.resolve({ requirementId: req.id, status: ResolutionStatus.Fulfilled });\n * snap = planner.snapshot();\n * }\n * // snap.status === PlanStatus.Settled\n * ```\n */\nexport interface RequirementPlanner {\n\t/** Get a read-only snapshot of the current plan state. */\n\tsnapshot(): PlanSnapshot;\n\t/** Resolve the next pending requirement. */\n\tresolve(resolution: RequirementResolution): void;\n\t/** Reset the planner to its initial state. */\n\treset(): void;\n}\n\n/**\n * Create a headless requirement planner.\n *\n * @see {@link RequirementPlanner} for usage.\n */\nexport function createRequirementPlanner(\n\toptions: CreateRequirementPlannerOptions,\n): RequirementPlanner {\n\tif (options.requirements.length === 0) {\n\t\tthrow new RequirementPlannerError(\n\t\t\t\"Cannot create a planner with zero requirements\",\n\t\t);\n\t}\n\n\t// Validate uniqueness of requirement IDs.\n\tconst ids = new Set<string>();\n\tfor (const req of options.requirements) {\n\t\tif (ids.has(req.id)) {\n\t\t\tthrow new RequirementPlannerError(\n\t\t\t\t`Duplicate requirement ID: \"${req.id}\"`,\n\t\t\t);\n\t\t}\n\t\tids.add(req.id);\n\t}\n\n\tlet states: RequirementState[] = [];\n\n\t/** Shallow-clone a requirement to prevent external mutation. */\n\tfunction cloneRequirement(r: AuthRequirement): AuthRequirement {\n\t\treturn {\n\t\t\t...r,\n\t\t\tattributes: r.attributes ? { ...r.attributes } : undefined,\n\t\t};\n\t}\n\n\tfunction initStates(): void {\n\t\tstates = options.requirements.map((r) => ({\n\t\t\trequirement: cloneRequirement(r),\n\t\t\tresolution: null,\n\t\t}));\n\t}\n\n\tinitStates();\n\n\tfunction findNextPending(): RequirementState | null {\n\t\treturn states.find((s) => s.resolution === null) ?? null;\n\t}\n\n\treturn {\n\t\tsnapshot(): PlanSnapshot {\n\t\t\tconst next = findNextPending();\n\t\t\tconst resolutions: RequirementResolution[] = [];\n\t\t\tfor (const s of states) {\n\t\t\t\tif (s.resolution !== null) resolutions.push({ ...s.resolution });\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstatus: next === null ? PlanStatus.Settled : PlanStatus.Pending,\n\t\t\t\tnextPending: next ? cloneRequirement(next.requirement) : null,\n\t\t\t\tresolutions,\n\t\t\t\ttotal: states.length,\n\t\t\t\tresolved: resolutions.length,\n\t\t\t};\n\t\t},\n\n\t\tresolve(resolution: RequirementResolution): void {\n\t\t\tconst next = findNextPending();\n\t\t\tif (next === null) {\n\t\t\t\tthrow new RequirementPlannerError(\n\t\t\t\t\t\"Cannot resolve: all requirements are already settled\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (resolution.requirementId !== next.requirement.id) {\n\t\t\t\tthrow new RequirementPlannerError(\n\t\t\t\t\t`Cannot resolve requirement \"${resolution.requirementId}\": ` +\n\t\t\t\t\t\t`the next pending requirement is \"${next.requirement.id}\"`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tnext.resolution = resolution;\n\t\t},\n\n\t\treset(): void {\n\t\t\tinitStates();\n\t\t},\n\t};\n}\n","// Shared planner-host — route-scoped auth coordination host layer\n//\n// Canonical location: @securitydept/client/auth-coordination\n//\n// Provides the host-layer contract for multi-requirement auth guards:\n// - AuthGuardClientOption: a single requirement candidate with its\n// authentication check and unauthenticated action\n// - RequirementsClientSet / ScopedRequirementsClientSet: composable\n// requirement client collections with inherit/merge/replace semantics\n// - PlannerHost: the coordination contract that selects which pending\n// candidate to act on, built on RequirementPlanner for sequential\n// resolution discipline\n//\n// The planner-host is intentionally headless and framework-agnostic.\n// Framework-specific provider/lookup (Angular DI, React Context) is handled\n// by the framework adapter packages (@securitydept/client-angular,\n// @securitydept/client-react).\n//\n// Architecture:\n// PlannerHost.evaluate() uses createRequirementPlanner() internally:\n// 1. Build AuthRequirement[] from candidates' requirementId/kind/label/attributes\n// 2. Auto-resolve already-authenticated requirements as Fulfilled\n// 3. Ask the planner for nextPending — respects declaration order and\n// skips already-satisfied requirements\n// 4. Among candidates still unauthenticated, use selectCandidate() to\n// pick the one to act on (default: the one matching nextPending first)\n//\n// Owner rationale: This module does not depend on token-set, session, or\n// basic-auth domain objects. It is a shared coordination capability.\n// Canonical owner is @securitydept/client.\n\nimport {\n\ttype AuthRequirement,\n\tcreateRequirementPlanner,\n\tPlanStatus,\n\tResolutionStatus,\n} from \"./requirement-planner\";\n\n// ---------------------------------------------------------------------------\n// Auth guard client option — a single requirement candidate\n// ---------------------------------------------------------------------------\n\n/**\n * A single requirement candidate that a guard submits to the planner-host.\n *\n * Each option describes:\n * 1. What auth requirement it represents (id, kind, label)\n * 2. How to check whether it's already satisfied\n * 3. What to do when the planner selects it as the next pending action\n */\nexport interface AuthGuardClientOption {\n\t/** Unique identifier for this requirement candidate. */\n\trequirementId: string;\n\n\t/**\n\t * The kind of auth requirement (opaque string).\n\t * @example \"session\", \"frontend_oidc\", \"backend_oidc\"\n\t */\n\trequirementKind: string;\n\n\t/** Human-readable label (for logging / debugging / chooser UI). */\n\tlabel?: string;\n\n\t/** Arbitrary metadata the adopter can attach. */\n\tattributes?: Record<string, unknown>;\n\n\t/**\n\t * Check whether this requirement candidate is already authenticated.\n\t * Called synchronously by the planner-host during evaluation.\n\t */\n\tcheckAuthenticated: () => boolean;\n\n\t/**\n\t * Action to execute when this candidate is selected as the pending\n\t * requirement by the planner-host.\n\t *\n\t * Return value semantics:\n\t * - `true` — allow navigation (requirement will be resolved externally)\n\t * - `false` — block navigation\n\t * - `string` — redirect to the given URL\n\t * - `Promise<...>` — async version of the above\n\t */\n\tonUnauthenticated: () => boolean | string | Promise<boolean | string>;\n}\n\n// ---------------------------------------------------------------------------\n// Requirements client set — composable requirement collections\n// ---------------------------------------------------------------------------\n\n/** A flat collection of requirement candidates. */\nexport interface RequirementsClientSet {\n\treadonly options: readonly AuthGuardClientOption[];\n}\n\n/**\n * How a child scope's requirements client set relates to its parent.\n *\n * - `inherit` — use the parent's set as-is (child adds nothing)\n * - `merge` — inherit parent's options, then append/override with child's\n * (child options with the same `requirementId` replace parent ones)\n * - `replace` — discard parent's set entirely, use child's set only\n */\nexport const RequirementsClientSetComposition = {\n\tInherit: \"inherit\",\n\tMerge: \"merge\",\n\tReplace: \"replace\",\n} as const;\n\nexport type RequirementsClientSetComposition =\n\t(typeof RequirementsClientSetComposition)[keyof typeof RequirementsClientSetComposition];\n\n/** A requirements client set annotated with its composition strategy. */\nexport interface ScopedRequirementsClientSet {\n\t/** How this set composes with its parent scope. */\n\tcomposition: RequirementsClientSetComposition;\n\t/** The requirement candidates declared at this scope. */\n\toptions: readonly AuthGuardClientOption[];\n}\n\n// ---------------------------------------------------------------------------\n// Resolve effective client set (parent + child composition)\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the effective requirements client set by composing a parent set\n * with a child scope's declaration.\n *\n * @param parentOptions - The parent scope's resolved options (or empty).\n * @param child - The child scope's scoped set (with composition strategy).\n * @returns The effective options after composition.\n */\nexport function resolveEffectiveClientSet(\n\tparentOptions: readonly AuthGuardClientOption[],\n\tchild: ScopedRequirementsClientSet,\n): AuthGuardClientOption[] {\n\tswitch (child.composition) {\n\t\tcase RequirementsClientSetComposition.Inherit:\n\t\t\treturn [...parentOptions];\n\n\t\tcase RequirementsClientSetComposition.Merge: {\n\t\t\t// Start with parent options, then override/append with child options.\n\t\t\t// Child options with the same requirementId replace parent ones.\n\t\t\tconst merged = new Map<string, AuthGuardClientOption>();\n\t\t\tfor (const opt of parentOptions) {\n\t\t\t\tmerged.set(opt.requirementId, opt);\n\t\t\t}\n\t\t\tfor (const opt of child.options) {\n\t\t\t\tmerged.set(opt.requirementId, opt);\n\t\t\t}\n\t\t\treturn [...merged.values()];\n\t\t}\n\n\t\tcase RequirementsClientSetComposition.Replace:\n\t\t\treturn [...child.options];\n\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = child.composition;\n\t\t\tthrow new Error(\n\t\t\t\t`[resolveEffectiveClientSet] Unknown composition: ${_exhaustive}`,\n\t\t\t);\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Planner-host result\n// ---------------------------------------------------------------------------\n\n/** Result of a planner-host evaluation. */\nexport interface PlannerHostResult {\n\t/** Whether all candidates are authenticated (route can activate). */\n\tallAuthenticated: boolean;\n\n\t/** All candidates that are not yet authenticated. */\n\tunauthenticatedCandidates: readonly AuthGuardClientOption[];\n\n\t/**\n\t * The candidate selected by the planner as the next one to act on.\n\t * `null` when `allAuthenticated` is true.\n\t *\n\t * Determined by the RequirementPlanner's sequential resolution discipline:\n\t * the first unresolved requirement in declaration order that is still\n\t * unauthenticated, then optionally further narrowed by `selectCandidate`.\n\t */\n\tpendingCandidate: AuthGuardClientOption | null;\n}\n\n// ---------------------------------------------------------------------------\n// Planner-host contract and factory\n// ---------------------------------------------------------------------------\n\n/**\n * A candidate selection strategy.\n *\n * Receives the list of unauthenticated candidates (in RequirementPlanner\n * declaration order) and returns the one that should be acted on next.\n * The default strategy picks the first one (matching the planner's\n * `nextPending` requirement).\n *\n * Async selectors are supported: return a `Promise<AuthGuardClientOption>`\n * to implement interactive chooser UIs (e.g. show a dialog and await the\n * user's pick before navigation continues).\n *\n * @example\n * ```ts\n * // Async chooser: show a dialog and await user's selection\n * const selectCandidate: CandidateSelector = async (candidates) => {\n * const chosen = await showChooserDialog(candidates);\n * return chosen;\n * };\n * ```\n */\nexport type CandidateSelector = (\n\tunauthenticated: readonly AuthGuardClientOption[],\n) => AuthGuardClientOption | Promise<AuthGuardClientOption>;\n\n/** The planner-host coordination contract. */\nexport interface PlannerHost {\n\t/**\n\t * Evaluate a set of requirement candidates using RequirementPlanner's\n\t * sequential resolution discipline.\n\t *\n\t * Always returns a Promise so that custom async selectors (e.g. chooser\n\t * dialog) are fully supported. Callers should always `await` the result.\n\t *\n\t * @param candidates - The resolved candidates from the guard's client options.\n\t * @returns Promise resolving to the evaluation result.\n\t */\n\tevaluate(\n\t\tcandidates: readonly AuthGuardClientOption[],\n\t): Promise<PlannerHostResult>;\n}\n\n/** Options for {@link createPlannerHost}. */\nexport interface CreatePlannerHostOptions {\n\t/**\n\t * Custom candidate selection strategy.\n\t *\n\t * The default strategy picks the candidate that corresponds to the\n\t * RequirementPlanner's `nextPending` requirement (first unauthenticated\n\t * in declaration order).\n\t *\n\t * A custom strategy can further filter or reorder the unauthenticated\n\t * candidates, e.g. to implement a chooser UI that lets the user pick.\n\t *\n\t * @default first-unauthenticated-matching-planner-nextPending\n\t *\n\t * @example\n\t * ```ts\n\t * // Async chooser: present all options and await user's pick\n\t * createPlannerHost({\n\t * selectCandidate: async (candidates) => {\n\t * return await showChooserDialog(candidates);\n\t * },\n\t * })\n\t * ```\n\t */\n\tselectCandidate?: CandidateSelector;\n}\n\n/**\n * Default candidate selector: picks the first unauthenticated candidate\n * in declaration order (which aligns with RequirementPlanner's nextPending).\n */\nfunction defaultSelectCandidate(\n\tunauthenticated: readonly AuthGuardClientOption[],\n): AuthGuardClientOption {\n\treturn unauthenticated[0];\n}\n\n/**\n * Create a planner-host instance backed by {@link createRequirementPlanner}.\n *\n * **How it works:**\n *\n * 1. `evaluate()` builds an `AuthRequirement[]` from `candidates`\n * (using each candidate's `requirementId`, `requirementKind`, `label`,\n * and `attributes`).\n * 2. A `RequirementPlanner` is created for this evaluation cycle.\n * 3. Already-authenticated requirements are auto-resolved as `Fulfilled`,\n * respecting the planner's sequential discipline.\n * 4. The planner's `nextPending` determines the first unresolved requirement\n * in declaration order.\n * 5. `selectCandidate` is called with all unauthenticated candidates so\n * adopters can implement custom chooser strategies (e.g. UI dialog).\n * The default strategy picks the first candidate (matching nextPending).\n *\n * `evaluate()` always returns a `Promise` to accommodate async selectors.\n * For synchronous selectors the Promise resolves in the same microtask tick.\n *\n * @example\n * ```ts\n * import { createPlannerHost } from \"@securitydept/client/auth-coordination\";\n *\n * // Default sequential strategy (sync selector, wrapped in Promise)\n * const host = createPlannerHost();\n * const result = await host.evaluate(candidates);\n *\n * // Async chooser strategy — show a dialog and await user's pick\n * const hostWithChooser = createPlannerHost({\n * selectCandidate: async (candidates) => {\n * return await showChooserDialog(candidates);\n * },\n * });\n * ```\n */\nexport function createPlannerHost(\n\toptions?: CreatePlannerHostOptions,\n): PlannerHost {\n\tconst selector = options?.selectCandidate ?? defaultSelectCandidate;\n\n\treturn {\n\t\tasync evaluate(\n\t\t\tcandidates: readonly AuthGuardClientOption[],\n\t\t): Promise<PlannerHostResult> {\n\t\t\tif (candidates.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tallAuthenticated: true,\n\t\t\t\t\tunauthenticatedCandidates: [],\n\t\t\t\t\tpendingCandidate: null,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Build AuthRequirement[] from candidates for the planner.\n\t\t\tconst requirements: AuthRequirement[] = candidates.map((c) => ({\n\t\t\t\tid: c.requirementId,\n\t\t\t\tkind: c.requirementKind,\n\t\t\t\tlabel: c.label,\n\t\t\t\tattributes: c.attributes,\n\t\t\t}));\n\n\t\t\t// Create a fresh planner for this evaluation cycle.\n\t\t\tconst planner = createRequirementPlanner({ requirements });\n\n\t\t\t// Auto-resolve already-authenticated requirements in declaration order.\n\t\t\t// The planner enforces sequential discipline: we must resolve in order.\n\t\t\tfor (const candidate of candidates) {\n\t\t\t\tconst snap = planner.snapshot();\n\t\t\t\tif (snap.status === PlanStatus.Settled) break;\n\n\t\t\t\t// Resolve the planner's current nextPending if it's authenticated.\n\t\t\t\t// Only advance the planner for requirements that are currently pending\n\t\t\t\t// in order — skip if the current nextPending doesn't match this candidate.\n\t\t\t\tif (\n\t\t\t\t\tsnap.nextPending?.id === candidate.requirementId &&\n\t\t\t\t\tcandidate.checkAuthenticated()\n\t\t\t\t) {\n\t\t\t\t\tplanner.resolve({\n\t\t\t\t\t\trequirementId: candidate.requirementId,\n\t\t\t\t\t\tstatus: ResolutionStatus.Fulfilled,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check overall status after resolving all authenticated requirements.\n\t\t\tconst finalSnap = planner.snapshot();\n\n\t\t\t// Collect all unauthenticated candidates.\n\t\t\tconst unauthenticated = candidates.filter((c) => !c.checkAuthenticated());\n\n\t\t\tif (\n\t\t\t\tfinalSnap.status === PlanStatus.Settled ||\n\t\t\t\tunauthenticated.length === 0\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\tallAuthenticated: true,\n\t\t\t\t\tunauthenticatedCandidates: [],\n\t\t\t\t\tpendingCandidate: null,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Await selection — supports both sync and async selectors.\n\t\t\t// The selector receives the unauthenticated candidates in declaration\n\t\t\t// order so adopters can implement any picking strategy.\n\t\t\tconst pendingCandidate = await selector(unauthenticated);\n\t\t\treturn {\n\t\t\t\tallAuthenticated: false,\n\t\t\t\tunauthenticatedCandidates: unauthenticated,\n\t\t\t\tpendingCandidate,\n\t\t\t};\n\t\t},\n\t};\n}\n"],"mappings":"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"}