@securitydept/client 0.2.0-beta.4 → 0.2.0-beta.5

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.
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
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";
2
- import { i as createJsonCodec, n as createEphemeralFlowStore, r as createKeyedEphemeralFlowStore, t as createInMemoryRecordStore } from "./persistence-Db2CIFbx.mjs";
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
+ import { i as createJsonCodec, n as createEphemeralFlowStore, r as createKeyedEphemeralFlowStore, t as createInMemoryRecordStore } from "./persistence-CSB8770k.mjs";
3
4
  //#region src/cancellation/cancellation-token.ts
4
5
  var CancellationToken = class {
5
6
  _isCancelled = false;
@@ -218,172 +219,6 @@ function readPrimaryAction(recovery, options) {
218
219
  };
219
220
  }
220
221
  //#endregion
221
- //#region src/events/event-stream.ts
222
- /**
223
- * Create a new event stream from a producer function.
224
- *
225
- * The producer receives an observer and should call `next()` to emit values,
226
- * `error()` to signal an error, and `complete()` to signal completion.
227
- * It may return a teardown function that will be called on unsubscribe.
228
- */
229
- function createEventStream(producer) {
230
- return { subscribe(observer) {
231
- let active = true;
232
- const teardown = producer({
233
- next(value) {
234
- if (active) observer.next?.(value);
235
- },
236
- error(err) {
237
- if (active) {
238
- active = false;
239
- observer.error?.(err);
240
- }
241
- },
242
- complete() {
243
- if (active) {
244
- active = false;
245
- observer.complete?.();
246
- }
247
- }
248
- });
249
- return { unsubscribe() {
250
- if (active) {
251
- active = false;
252
- teardown?.();
253
- }
254
- } };
255
- } };
256
- }
257
- //#endregion
258
- //#region src/events/operators.ts
259
- function pipe(source, ...operators) {
260
- return operators.reduce((acc, op) => op(acc), source);
261
- }
262
- function map(fn) {
263
- return (source) => createEventStream((observer) => {
264
- const sub = source.subscribe({
265
- next(value) {
266
- observer.next?.(fn(value));
267
- },
268
- error(err) {
269
- observer.error?.(err);
270
- },
271
- complete() {
272
- observer.complete?.();
273
- }
274
- });
275
- return () => sub.unsubscribe();
276
- });
277
- }
278
- function filter(predicate) {
279
- return (source) => createEventStream((observer) => {
280
- const sub = source.subscribe({
281
- next(value) {
282
- if (predicate(value)) observer.next?.(value);
283
- },
284
- error(err) {
285
- observer.error?.(err);
286
- },
287
- complete() {
288
- observer.complete?.();
289
- }
290
- });
291
- return () => sub.unsubscribe();
292
- });
293
- }
294
- function tap(fn) {
295
- return (source) => createEventStream((observer) => {
296
- const sub = source.subscribe({
297
- next(value) {
298
- fn(value);
299
- observer.next?.(value);
300
- },
301
- error(err) {
302
- observer.error?.(err);
303
- },
304
- complete() {
305
- observer.complete?.();
306
- }
307
- });
308
- return () => sub.unsubscribe();
309
- });
310
- }
311
- function takeUntil(notifier) {
312
- return (source) => createEventStream((observer) => {
313
- const sourceSub = source.subscribe({
314
- next(value) {
315
- observer.next?.(value);
316
- },
317
- error(err) {
318
- observer.error?.(err);
319
- },
320
- complete() {
321
- observer.complete?.();
322
- }
323
- });
324
- const notifierSub = notifier.subscribe({ next() {
325
- observer.complete?.();
326
- sourceSub.unsubscribe();
327
- notifierSub.unsubscribe();
328
- } });
329
- return () => {
330
- sourceSub.unsubscribe();
331
- notifierSub.unsubscribe();
332
- };
333
- });
334
- }
335
- function finalize(fn) {
336
- return (source) => createEventStream((observer) => {
337
- const sub = source.subscribe({
338
- next(value) {
339
- observer.next?.(value);
340
- },
341
- error(err) {
342
- observer.error?.(err);
343
- fn();
344
- },
345
- complete() {
346
- observer.complete?.();
347
- fn();
348
- }
349
- });
350
- return () => {
351
- sub.unsubscribe();
352
- fn();
353
- };
354
- });
355
- }
356
- function merge(...sources) {
357
- return createEventStream((observer) => {
358
- let completedCount = 0;
359
- const subs = sources.map((source) => source.subscribe({
360
- next(value) {
361
- observer.next?.(value);
362
- },
363
- error(err) {
364
- observer.error?.(err);
365
- },
366
- complete() {
367
- completedCount++;
368
- if (completedCount === sources.length) observer.complete?.();
369
- }
370
- }));
371
- return () => {
372
- for (const sub of subs) sub.unsubscribe();
373
- };
374
- });
375
- }
376
- //#endregion
377
- //#region src/events/types.ts
378
- const EventSourceKind = {
379
- User: "user",
380
- Timer: "timer",
381
- Http: "http",
382
- Storage: "storage",
383
- Framework: "framework",
384
- System: "system"
385
- };
386
- //#endregion
387
222
  //#region src/identity/principal.ts
388
223
  function readClaimsBag(value) {
389
224
  return typeof value === "object" && value !== null ? value : void 0;
@@ -656,6 +491,6 @@ function createSchema(options) {
656
491
  } };
657
492
  }
658
493
  //#endregion
659
- export { ClientError, ClientErrorKind, ClientErrorSource, ErrorPresentationTone, EventSourceKind, FetchTransportRedirectKind, LogLevel, OperationTraceEventType, PromiseSettlementKind, UserRecovery, createCancellationTokenSource, createComputed, createConsoleLogger, createDefaultClock, createDefaultScheduler, createEphemeralFlowStore, createEventStream, createInMemoryRecordStore, createJsonCodec, createKeyedEphemeralFlowStore, createLinkedCancellationToken, createNoopLogger, createOperationTracer, createRuntime, createSchema, createSignal, createTraceTimelineStore, filter, finalize, fromEventPattern, fromPromise, fromSignal, interval, map, merge, normalizeAuthenticatedPrincipal, normalizeAuthenticatedPrincipalWire, pipe, projectAuthenticatedPrincipal, readErrorPresentationDescriptor, readonlySignal, scheduleAt, takeUntil, tap, timer, validateWithSchema, validateWithSchemaSync };
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 };
660
495
 
661
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/events/event-stream.ts","../src/events/operators.ts","../src/events/types.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","import type {\n\tEventObserver,\n\tEventStreamTrait,\n\tEventSubscriptionTrait,\n} from \"./types\";\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 {\n\t\tsubscribe(observer: EventObserver<T>): EventSubscriptionTrait {\n\t\t\tlet active = true;\n\t\t\tconst safeObserver: EventObserver<T> = {\n\t\t\t\tnext(value: T) {\n\t\t\t\t\tif (active) observer.next?.(value);\n\t\t\t\t},\n\t\t\t\terror(err: unknown) {\n\t\t\t\t\tif (active) {\n\t\t\t\t\t\tactive = false;\n\t\t\t\t\t\tobserver.error?.(err);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tcomplete() {\n\t\t\t\t\tif (active) {\n\t\t\t\t\t\tactive = false;\n\t\t\t\t\t\tobserver.complete?.();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tconst teardown = producer(safeObserver);\n\n\t\t\treturn {\n\t\t\t\tunsubscribe() {\n\t\t\t\t\tif (active) {\n\t\t\t\t\t\tactive = false;\n\t\t\t\t\t\tteardown?.();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n","import { createEventStream } 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) =>\n\t\tcreateEventStream((observer) => {\n\t\t\tconst sub = source.subscribe({\n\t\t\t\tnext(value) {\n\t\t\t\t\tobserver.next?.(fn(value));\n\t\t\t\t},\n\t\t\t\terror(err) {\n\t\t\t\t\tobserver.error?.(err);\n\t\t\t\t},\n\t\t\t\tcomplete() {\n\t\t\t\t\tobserver.complete?.();\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn () => sub.unsubscribe();\n\t\t});\n}\n\n// --- filter ---\n\nexport function filter<A>(\n\tpredicate: (value: A) => boolean,\n): EventOperator<A, A> {\n\treturn (source) =>\n\t\tcreateEventStream((observer) => {\n\t\t\tconst sub = source.subscribe({\n\t\t\t\tnext(value) {\n\t\t\t\t\tif (predicate(value)) observer.next?.(value);\n\t\t\t\t},\n\t\t\t\terror(err) {\n\t\t\t\t\tobserver.error?.(err);\n\t\t\t\t},\n\t\t\t\tcomplete() {\n\t\t\t\t\tobserver.complete?.();\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn () => sub.unsubscribe();\n\t\t});\n}\n\n// --- tap ---\n\nexport function tap<A>(fn: (value: A) => void): EventOperator<A, A> {\n\treturn (source) =>\n\t\tcreateEventStream((observer) => {\n\t\t\tconst sub = source.subscribe({\n\t\t\t\tnext(value) {\n\t\t\t\t\tfn(value);\n\t\t\t\t\tobserver.next?.(value);\n\t\t\t\t},\n\t\t\t\terror(err) {\n\t\t\t\t\tobserver.error?.(err);\n\t\t\t\t},\n\t\t\t\tcomplete() {\n\t\t\t\t\tobserver.complete?.();\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn () => sub.unsubscribe();\n\t\t});\n}\n\n// --- takeUntil ---\n\nexport function takeUntil<A>(\n\tnotifier: EventStreamTrait<unknown>,\n): EventOperator<A, A> {\n\treturn (source) =>\n\t\tcreateEventStream((observer) => {\n\t\t\tconst sourceSub = source.subscribe({\n\t\t\t\tnext(value) {\n\t\t\t\t\tobserver.next?.(value);\n\t\t\t\t},\n\t\t\t\terror(err) {\n\t\t\t\t\tobserver.error?.(err);\n\t\t\t\t},\n\t\t\t\tcomplete() {\n\t\t\t\t\tobserver.complete?.();\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tconst notifierSub = notifier.subscribe({\n\t\t\t\tnext() {\n\t\t\t\t\tobserver.complete?.();\n\t\t\t\t\tsourceSub.unsubscribe();\n\t\t\t\t\tnotifierSub.unsubscribe();\n\t\t\t\t},\n\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tsourceSub.unsubscribe();\n\t\t\t\tnotifierSub.unsubscribe();\n\t\t\t};\n\t\t});\n}\n\n// --- finalize ---\n\nexport function finalize<A>(fn: () => void): EventOperator<A, A> {\n\treturn (source) =>\n\t\tcreateEventStream((observer) => {\n\t\t\tconst sub = source.subscribe({\n\t\t\t\tnext(value) {\n\t\t\t\t\tobserver.next?.(value);\n\t\t\t\t},\n\t\t\t\terror(err) {\n\t\t\t\t\tobserver.error?.(err);\n\t\t\t\t\tfn();\n\t\t\t\t},\n\t\t\t\tcomplete() {\n\t\t\t\t\tobserver.complete?.();\n\t\t\t\t\tfn();\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn () => {\n\t\t\t\tsub.unsubscribe();\n\t\t\t\tfn();\n\t\t\t};\n\t\t});\n}\n\n// --- merge ---\n\nexport function merge<A>(\n\t...sources: EventStreamTrait<A>[]\n): EventStreamTrait<A> {\n\treturn createEventStream((observer) => {\n\t\tlet completedCount = 0;\n\t\tconst subs = sources.map((source) =>\n\t\t\tsource.subscribe({\n\t\t\t\tnext(value) {\n\t\t\t\t\tobserver.next?.(value);\n\t\t\t\t},\n\t\t\t\terror(err) {\n\t\t\t\t\tobserver.error?.(err);\n\t\t\t\t},\n\t\t\t\tcomplete() {\n\t\t\t\t\tcompletedCount++;\n\t\t\t\t\tif (completedCount === sources.length) {\n\t\t\t\t\t\tobserver.complete?.();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t\treturn () => {\n\t\t\tfor (const sub of subs) {\n\t\t\t\tsub.unsubscribe();\n\t\t\t}\n\t\t};\n\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// --- 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","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":";;;;AAQA,IAAM,oBAAN,MAA0D;CACzD,eAAuB;CACvB;CACA,6BAA8B,IAAI,KAAgC;CAElE,IAAI,0BAAmC;AACtC,SAAO,KAAK;;CAGb,IAAI,SAAkB;AACrB,SAAO,KAAK;;;CAIb,QAAQ,QAAuB;AAC9B,MAAI,KAAK,aAAc;AACvB,OAAK,eAAe;AACpB,OAAK,UAAU;AACf,OAAK,MAAM,YAAY,KAAK,WAC3B,UAAS,OAAO;AAEjB,OAAK,WAAW,OAAO;;CAGxB,wBACC,UACkB;AAClB,MAAI,KAAK,cAAc;AAEtB,YAAS,KAAK,QAAQ;AACtB,UAAO,EAAE,UAAU,IAAI;;AAExB,OAAK,WAAW,IAAI,SAAS;AAC7B,SAAO,EACN,eAAe;AACd,QAAK,WAAW,OAAO,SAAS;KAEjC;;CAGF,+BAAqC;AACpC,MAAI,KAAK,aACR,OAAM,IAAI,YAAY;GACrB,MAAM,gBAAgB;GACtB,SAAS;GACT,OAAO,KAAK;GACZ,CAAC;;;;;;AAQL,SAAgB,gCAA8D;CAC7E,MAAM,KAAK,IAAI,mBAAmB;CAClC,IAAI,WAAW;AAEf,QAAO;EACN,IAAI,QAAgC;AACnC,UAAO;;EAER,OAAO,QAAkB;AACxB,OAAI,CAAC,SAAU,IAAG,QAAQ,OAAO;;EAElC,UAAU;AACT,OAAI,CAAC,UAAU;AACd,eAAW;AACX,OAAG,QACF,IAAI,YAAY;KACf,MAAM,gBAAgB;KACtB,SAAS;KACT,CAAC,CACF;;;EAGH;;;;;;;;;;;;;;;;;;ACnEF,SAAgB,8BACf,GAAG,SACsB;AAEzB,KAAI,QAAQ,WAAW,EACtB,QAAO,+BAA+B,CAAC;AAIxC,KAAI,QAAQ,WAAW,EACtB,QAAO,QAAQ;AAIhB,MAAK,MAAM,UAAU,QACpB,KAAI,OAAO,wBAAyB,QAAO;CAG5C,MAAM,SAAS,+BAA+B;CAC9C,MAAM,gBAAgB,QAAQ,KAAK,WAClC,OAAO,yBAAyB,WAAW,OAAO,OAAO,OAAO,CAAC,CACjE;AAGD,QAAO,MAAM,8BAA8B;AAC1C,OAAK,MAAM,OAAO,cAAe,KAAI,SAAS;GAC7C;AAEF,QAAO,OAAO;;;;ACxBf,MAAM,0BAAwD;EAC5D,aAAa,OAAO;EACpB,aAAa,QAAQ;EACrB,aAAa,cAAc;EAC3B,aAAa,iBAAiB;EAC9B,aAAa,iBAAiB;CAC/B;AAED,MAAM,sBAAsB;CAC3B,iBAAiB;EAChB,OAAO;EACP,aACC;EACD,MAAM,sBAAsB;EAC5B;CACD,wBAAwB;EACvB,OAAO;EACP,aACC;EACD,MAAM,sBAAsB;EAC5B;CACD,uBAAuB;EACtB,OAAO;EACP,aACC;EACD,MAAM,sBAAsB;EAC5B;CACD,qBAAqB;EACpB,OAAO;EACP,aACC;EACD,MAAM,sBAAsB;EAC5B;CACD;AAED,SAAgB,gCACf,OACA,UAAkD,EAAE,EACtB;AAC9B,KAAI,iBAAiB,YACpB,QAAO,gCAAgC,OAAO,QAAQ;CAGvD,MAAM,kBAAkB,sBAAsB,MAAM;AACpD,KAAI,gBACH,QAAO,oCAAoC,iBAAiB,QAAQ;AAGrE,KAAI,iBAAiB,MACpB,QAAO;EACN,MAAM;EACN,MAAM;EACN,OAAO,QAAQ,iBAAiB;EAChC,aACC,MAAM,WACN,QAAQ,uBACR;EACD,UAAU,aAAa;EACvB,WAAW;EACX,MAAM,sBAAsB;EAC5B,eAAe;EACf;AAGF,QAAO;EACN,MAAM;EACN,MAAM;EACN,OAAO,QAAQ,iBAAiB;EAChC,aACC,QAAQ,uBACR;EACD,UAAU,aAAa;EACvB,WAAW;EACX,MAAM,sBAAsB;EAC5B,eAAe;EACf;;AAGF,SAAS,gCACR,OACA,SAC8B;AAC9B,QAAO,oCACN;EACC,MAAM,MAAM;EACZ,MAAM,MAAM;EACZ,SAAS,MAAM;EACf,UAAU,MAAM;EAChB,WAAW,MAAM;EACjB,QAAQ,MAAM;EACd,cAAc,MAAM;EACpB,EACD,QACA;;AAGF,SAAS,oCACR,OACA,SAC8B;CAC9B,MAAM,oBACL,oBAAoB,MAAM;CAC3B,MAAM,QAAQ,mBAAmB,SAAS,qBAAqB,MAAM;CACrE,MAAM,cACL,mBAAmB,eACnB,MAAM,cAAc,WACpB,MAAM,WACN,QAAQ,uBACR;AAED,QAAO;EACN,MAAM,MAAM;EACZ,MAAM,MAAM;EACZ,QAAQ,MAAM;EACd;EACA;EACA,UAAU,MAAM;EAChB,WAAW,MAAM;EACjB,MAAM,mBAAmB,QAAQ,oBAAoB,MAAM;EAC3D,eAAe,kBAAkB,MAAM,UAAU,QAAQ;EACzD;;AAGF,SAAS,qBACR,OACS;AACT,KAAI,MAAM,SAAS,gBAAgB,gBAClC,QAAO;AAGR,KAAI,MAAM,SAAS,gBAAgB,aAClC,QAAO;AAGR,KAAI,MAAM,SAAS,gBAAgB,UAClC,QAAO;AAGR,KAAI,MAAM,SAAS,gBAAgB,QAClC,QAAO;AAGR,KAAI,MAAM,SAAS,gBAAgB,cAClC,QAAO;AAGR,KAAI,MAAM,SAAS,gBAAgB,WAClC,QAAO;AAGR,KAAI,MAAM,SAAS,gBAAgB,QAClC,QAAO;AAGR,KACC,MAAM,SAAS,gBAAgB,iBAC/B,MAAM,aAAa,aAAa,YAEhC,QAAO;AAGR,KAAI,MAAM,SAAS,gBAAgB,UAClC,QAAO;AAGR,KAAI,MAAM,SAAS,gBAAgB,OAClC,QAAO;AAGR,KAAI,MAAM,SAAS,gBAAgB,SAClC,QAAO;AAGR,QAAO;;AAGR,SAAS,oBACR,OACC;AACD,KAAI,MAAM,SAAS,gBAAgB,UAClC,QAAO,sBAAsB;AAG9B,KACC,MAAM,SAAS,gBAAgB,mBAC/B,MAAM,SAAS,gBAAgB,WAC/B,MAAM,SAAS,gBAAgB,aAC/B,MAAM,SAAS,gBAAgB,UAC/B,MAAM,aAAa,aAAa,SAChC,MAAM,aAAa,aAAa,eAChC,MAAM,aAAa,aAAa,eAEhC,QAAO,sBAAsB;AAG9B,QAAO,sBAAsB;;AAG9B,SAAS,sBAAsB,OAAwC;AACtE,KAAI,OAAO,UAAU,YAAY,UAAU,KAC1C,QAAO;CAGR,MAAM,YAAY;AAClB,KAAI,OAAO,UAAU,YAAY,SAChC,QAAO;AAGR,QAAO;EACN,MAAM,OAAO,UAAU,SAAS,WAAW,UAAU,OAAO;EAC5D,MACC,OAAO,UAAU,SAAS,WACtB,UAAU,OACX;EACJ,SAAS,UAAU;EACnB,UACC,OAAO,UAAU,aAAa,WAC1B,UAAU,WACX,aAAa;EACjB,WAAW,UAAU,cAAc;EACnC,QAAQ,OAAO,UAAU,WAAW,WAAW,UAAU,SAAS,KAAA;EAClE,cAAc,oBAAoB,UAAU,aAAa,GACtD,UAAU,eACV,KAAA;EACH;;AAGF,SAAS,oBAAoB,OAA4C;AACxE,QACC,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAAkC,SAAS,YACnD,OAAQ,MAAkC,YAAY,YACtD,OAAQ,MAAkC,aAAa;;AAIzD,SAAS,kBACR,UACA,SAC2C;AAC3C,KAAI,aAAa,aAAa,KAC7B,QAAO;CAGR,MAAM,QACL,QAAQ,iBAAiB,aAAa,wBAAwB;AAC/D,KAAI,CAAC,MACJ,QAAO;AAGR,QAAO;EACN;EACA;EACA,MAAM,QAAQ,gBAAgB,aAAa;EAC3C;;;;AC1QF,SAAS,cAAc,OAAqD;AAC3E,QAAO,OAAO,UAAU,YAAY,UAAU,OAC1C,QACD,KAAA;;AAGJ,SAAS,mBAAmB,OAAoC;AAC/D,QAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;;AAG5C,SAAS,mBAAmB,aAAsB,SAAyB;AAC1E,KAAI,OAAO,gBAAgB,YAAY,YAAY,MAAM,CAAC,SAAS,EAClE,QAAO;AAGR,QAAO;;AAGR,SAAgB,gCACf,OACgC;AAChC,KAAI,OAAO,MAAM,YAAY,YAAY,MAAM,QAAQ,MAAM,CAAC,WAAW,EACxE,QAAO;AAGR,QAAO;EACN,SAAS,MAAM;EACf,aAAa,mBAAmB,MAAM,aAAa,MAAM,QAAQ;EACjE,SAAS,mBAAmB,MAAM,QAAQ;EAC1C,QAAQ,mBAAmB,MAAM,OAAO;EACxC,QAAQ,cAAc,MAAM,OAAO;EACnC;;AAGF,SAAgB,oCACf,OACgC;AAChC,KAAI,OAAO,MAAM,YAAY,YAAY,MAAM,QAAQ,MAAM,CAAC,WAAW,EACxE,QAAO;AAGR,QAAO;EACN,SAAS,MAAM;EACf,aAAa,mBAAmB,MAAM,cAAc,MAAM,QAAQ;EAClE,SAAS,mBAAmB,MAAM,QAAQ;EAC1C,QAAQ,mBAAmB,MAAM,OAAO;EACxC,QAAQ,cAAc,MAAM,OAAO;EACnC;;AAWF,SAAgB,8BACf,SACyB;CACzB,MAAM,YAAY,QAAQ;CAC1B,MAAM,UACL,WAAW,WAAW,QAAQ,mBAAmB;AAIlD,QAAO;EACN;EACA,aAJA,WAAW,aAAa,MAAM,IAAI,QAAQ,uBAAuB;EAKjE,SAAS,WAAW;EACpB,QAAQ,WAAW,UAAU,QAAQ;EACrC,QAAQ,WAAW,UAAU,QAAQ;EACrC;;;;;AChFF,SAAgB,oBAAoB,OAA6B;AAChE,QAAO,EACN,IAAI,OAAiB;EAEpB,MAAM,QAAQ,IADC,MAAM,SAAS,SAAS,eACd;AAEzB,UAAQ,MAAM,OAAd;GACC,KAAK;AACJ,YAAQ,MAAM,OAAO,MAAM,SAAS,MAAM,cAAc,GAAG;AAC3D;GACD,KAAK;AACJ,YAAQ,KAAK,OAAO,MAAM,SAAS,MAAM,cAAc,GAAG;AAC1D;GACD,KAAK;AACJ,YAAQ,KAAK,OAAO,MAAM,SAAS,MAAM,cAAc,GAAG;AAC1D;GACD,KAAK;AACJ,YAAQ,MACP,OACA,MAAM,SACN,MAAM,SAAS,IACf,MAAM,cAAc,GACpB;AACD;;IAGH;;;;;AC1BF,SAAgB,mBAAgC;AAC/C,QAAO,EACN,MAAM,IACN;;;;ACOF,SAAgB,yBAAyB,QAAQ,KAAyB;CACzE,IAAI,SAAS;CACb,IAAI,UAAgC,EAAE;CACtC,MAAM,4BAAY,IAAI,KAAiB;CAEvC,SAAS,kBAAkB,IAAoB;AAC9C,MAAI,CAAC,OAAO,SAAS,GAAG,CACvB,QAAO;EAGR,MAAM,OAAO,IAAI,KAAK,GAAG;EACzB,MAAM,YAAY,KAAK,SAAS;AAChC,MAAI,CAAC,OAAO,SAAS,UAAU,CAC9B,QAAO;AAGR,SAAO,KAAK,aAAa;;CAG1B,SAAS,SAAS;AACjB,OAAK,MAAM,YAAY,UACtB,WAAU;;AAIZ,QAAO;EACN,OAAO,OAAO;GACb,MAAM,QAA4B;IACjC,GAAG;IACH,IAAI;IACJ,eAAe,kBAAkB,MAAM,GAAG;IAC1C;AACD,aAAU,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM;AAC3C,WAAQ;;EAET,MAAM;AACL,UAAO;;EAER,UAAU,UAAU;AACnB,aAAU,IAAI,SAAS;AACvB,gBAAa;AACZ,cAAU,OAAO,SAAS;;;EAG5B,QAAQ;AACP,OAAI,QAAQ,WAAW,EACtB;AAED,aAAU,EAAE;AACZ,WAAQ;;EAET;;;;;;;;;;;;;;;ACnDF,SAAgB,eACf,SACA,MACyB;CACzB,IAAI;CACJ,IAAI,QAAQ;CACZ,MAAM,4BAAY,IAAI,KAAiB;CAEvC,MAAM,kBAAkB;AACvB,MAAI,CAAC,OAAO;AACX,WAAQ;AACR,QAAK,MAAM,YAAY,UACtB,WAAU;;;AAMb,MAAK,MAAM,OAAO,KACjB,KAAI,UAAU,UAAU;AAGzB,QAAO;EACN,MAAM;AACL,OAAI,OAAO;AACV,aAAS,SAAS;AAClB,YAAQ;;AAET,UAAO;;EAER,UAAU,UAAkC;AAC3C,aAAU,IAAI,SAAS;AACvB,gBAAa;AACZ,cAAU,OAAO,SAAS;;;EAG5B;;;;;;;;AC3CF,SAAgB,aAAgB,SAAoC;CACnE,IAAI,UAAU;CACd,MAAM,4BAAY,IAAI,KAAiB;AAEvC,QAAO;EACN,MAAM;AACL,UAAO;;EAER,IAAI,OAAU;AACb,OAAI,OAAO,GAAG,SAAS,MAAM,CAAE;AAC/B,aAAU;AACV,QAAK,MAAM,YAAY,UACtB,WAAU;;EAGZ,UAAU,UAAkC;AAC3C,aAAU,IAAI,SAAS;AACvB,gBAAa;AACZ,cAAU,OAAO,SAAS;;;EAG5B;;;;;AAMF,SAAgB,eACf,QACyB;AACzB,QAAO;EACN,WAAW,OAAO,KAAK;EACvB,YAAY,aAAa,OAAO,UAAU,SAAS;EACnD;;;;;;;;;;;;;;;;;;;;;;;;;AC+BF,eAAsB,mBACrB,QACA,OAC+B;CAC/B,MAAM,SAAS,MAAM,OAAO,aAAa,SAAS,MAAM;AAExD,KAAI,WAAW,OACd,QAAO;EAAE,SAAS;EAAM,OAAO,OAAO;EAAO;AAG9C,QAAO;EAAE,SAAS;EAAO,QAAQ,OAAO;EAAQ;;;;;;;;AASjD,SAAgB,uBACf,QACA,OACsB;CACtB,MAAM,SAAS,OAAO,aAAa,SAAS,MAAM;AAElD,KAAI,kBAAkB,QACrB,OAAM,IAAI,MACT,qFACA;AAGF,KAAI,WAAW,OACd,QAAO;EAAE,SAAS;EAAM,OAAO,OAAO;EAAO;AAG9C,QAAO;EAAE,SAAS;EAAO,QAAQ,OAAO;EAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CjD,SAAgB,aACf,SAC+B;AAC/B,QAAO,EACN,aAAa;EACZ,SAAS;EACT,QAAQ;EACR,UAAU,QAAQ;EAClB,EACD"}
@@ -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-DKluXsWS.mjs";
2
- import { a as createEphemeralFlowStore, i as CreateKeyedEphemeralFlowStoreOptions, n as createJsonCodec, o as createKeyedEphemeralFlowStore, r as CreateEphemeralFlowStoreOptions, t as createInMemoryRecordStore } from "../index-XkwmYaJF.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-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";
3
3
  export { Codec, CreateEphemeralFlowStoreOptions, CreateKeyedEphemeralFlowStoreOptions, EphemeralFlowStore, KeyedEphemeralFlowStore, PersistentAuthStore, RecordStore, RecoverableStateStore, StoredEnvelope, createEphemeralFlowStore, createInMemoryRecordStore, createJsonCodec, createKeyedEphemeralFlowStore };
@@ -1,2 +1,2 @@
1
- import { i as createJsonCodec, n as createEphemeralFlowStore, r as createKeyedEphemeralFlowStore, t as createInMemoryRecordStore } from "../persistence-Db2CIFbx.mjs";
1
+ import { i as createJsonCodec, n as createEphemeralFlowStore, r as createKeyedEphemeralFlowStore, t as createInMemoryRecordStore } from "../persistence-CSB8770k.mjs";
2
2
  export { createEphemeralFlowStore, createInMemoryRecordStore, createJsonCodec, createKeyedEphemeralFlowStore };
@@ -1,4 +1,4 @@
1
- import { a as RecordStore } from "../../types-DKluXsWS.mjs";
1
+ import { a as RecordStore } from "../../types-C59fcM5W.mjs";
2
2
 
3
3
  //#region src/persistence/web/stores.d.ts
4
4
  /**
@@ -105,4 +105,4 @@ function createInMemoryRecordStore() {
105
105
  //#endregion
106
106
  export { createJsonCodec as i, createEphemeralFlowStore as n, createKeyedEphemeralFlowStore as r, createInMemoryRecordStore as t };
107
107
 
108
- //# sourceMappingURL=persistence-Db2CIFbx.mjs.map
108
+ //# sourceMappingURL=persistence-CSB8770k.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"persistence-Db2CIFbx.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;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"}
@@ -187,4 +187,4 @@ interface CreatePlannerHostOptions {
187
187
  declare function createPlannerHost(options?: CreatePlannerHostOptions): PlannerHost;
188
188
  //#endregion
189
189
  export { PlannerHostResult as a, ScopedRequirementsClientSet as c, PlannerHost as i, createPlannerHost as l, CandidateSelector as n, RequirementsClientSet as o, CreatePlannerHostOptions as r, RequirementsClientSetComposition as s, AuthGuardClientOption as t, resolveEffectiveClientSet as u };
190
- //# sourceMappingURL=planner-host-DUZCkLj3.d.mts.map
190
+ //# sourceMappingURL=planner-host-CPyGNyQV.d.mts.map
@@ -192,4 +192,4 @@ function createPlannerHost(options) {
192
192
  //#endregion
193
193
  export { RequirementPlannerError as a, PlanStatus as i, createPlannerHost as n, ResolutionStatus as o, resolveEffectiveClientSet as r, createRequirementPlanner as s, RequirementsClientSetComposition as t };
194
194
 
195
- //# sourceMappingURL=planner-host-CcEpcHws.mjs.map
195
+ //# sourceMappingURL=planner-host-CV38bMSn.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"planner-host-CcEpcHws.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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"}
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":"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"}
@@ -49,4 +49,4 @@ interface StoredEnvelope<T> {
49
49
  }
50
50
  //#endregion
51
51
  export { RecordStore as a, PersistentAuthStore as i, EphemeralFlowStore as n, RecoverableStateStore as o, KeyedEphemeralFlowStore as r, StoredEnvelope as s, Codec as t };
52
- //# sourceMappingURL=types-DKluXsWS.d.mts.map
52
+ //# sourceMappingURL=types-C59fcM5W.d.mts.map
@@ -0,0 +1,32 @@
1
+ //#region src/signals/types.d.ts
2
+ /**
3
+ * Read-only signal interface.
4
+ * Semantics align with TC39 Signals proposal, but uses an SDK-owned thin protocol
5
+ * to avoid coupling to any specific polyfill or standard implementation.
6
+ */
7
+ interface ReadableSignalTrait<T> {
8
+ /** Return the current snapshot value. */
9
+ get(): T;
10
+ /**
11
+ * Subscribe to value changes.
12
+ * The listener is called whenever the value changes (not on subscribe).
13
+ * @returns An unsubscribe function.
14
+ */
15
+ subscribe(listener: () => void): () => void;
16
+ }
17
+ /**
18
+ * Writable signal interface.
19
+ * State transitions should be controlled by the client / service layer;
20
+ * external consumers typically only see the read-only projection.
21
+ */
22
+ interface WritableSignalTrait<T> extends ReadableSignalTrait<T> {
23
+ set(value: T): void;
24
+ }
25
+ /**
26
+ * Computed (derived) signal — always read-only, value is derived from
27
+ * other signals automatically.
28
+ */
29
+ interface ComputedSignalTrait<T> extends ReadableSignalTrait<T> {}
30
+ //#endregion
31
+ export { ReadableSignalTrait as n, WritableSignalTrait as r, ComputedSignalTrait as t };
32
+ //# sourceMappingURL=types-DQc088ZP.d.mts.map