@securitydept/client 0.3.0-beta.2 → 0.3.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.
Files changed (57) hide show
  1. package/README.md +6 -0
  2. package/dist/create-Bm-XRA3G.mjs +1573 -0
  3. package/dist/create-Bm-XRA3G.mjs.map +1 -0
  4. package/dist/index-BT-u3qNI.d.mts +133 -0
  5. package/dist/index-Ch8lrE44.d.mts +625 -0
  6. package/dist/index-D5mW18cM.d.mts +1648 -0
  7. package/dist/index.d.mts +3 -353
  8. package/dist/index.mjs +1124 -362
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/router-BJ8LqNJ1.mjs +245 -0
  11. package/dist/router-BJ8LqNJ1.mjs.map +1 -0
  12. package/dist/rx/index.d.mts +69 -0
  13. package/dist/rx/index.mjs +2 -0
  14. package/dist/rx-BoB32Dzg.mjs +1106 -0
  15. package/dist/rx-BoB32Dzg.mjs.map +1 -0
  16. package/dist/server/index.d.mts +21 -0
  17. package/dist/server/index.mjs +29 -0
  18. package/dist/server/index.mjs.map +1 -0
  19. package/dist/test/index.d.mts +102 -0
  20. package/dist/test/index.mjs +270 -0
  21. package/dist/test/index.mjs.map +1 -0
  22. package/dist/uri-string-BfnIT4gJ.mjs +298 -0
  23. package/dist/uri-string-BfnIT4gJ.mjs.map +1 -0
  24. package/dist/web/index.d.mts +82 -336
  25. package/dist/web/index.mjs +328 -337
  26. package/dist/web/index.mjs.map +1 -1
  27. package/dist/webext/index.d.mts +74 -0
  28. package/dist/webext/index.mjs +229 -0
  29. package/dist/webext/index.mjs.map +1 -0
  30. package/package.json +28 -21
  31. package/dist/auth-coordination/index.d.mts +0 -198
  32. package/dist/auth-coordination/index.mjs +0 -107
  33. package/dist/auth-coordination/index.mjs.map +0 -1
  34. package/dist/events/index.d.mts +0 -2
  35. package/dist/events/index.mjs +0 -2
  36. package/dist/events-BUlfsRfl.mjs +0 -127
  37. package/dist/events-BUlfsRfl.mjs.map +0 -1
  38. package/dist/fetch-transport-Bd5RyWj2.d.mts +0 -339
  39. package/dist/fetch-transport-DtnSRWAH.mjs +0 -631
  40. package/dist/fetch-transport-DtnSRWAH.mjs.map +0 -1
  41. package/dist/index-C_wgl6dW.d.mts +0 -102
  42. package/dist/index-DbYSFHDe.d.mts +0 -39
  43. package/dist/persistence/index.d.mts +0 -3
  44. package/dist/persistence/index.mjs +0 -2
  45. package/dist/persistence/web/index.d.mts +0 -16
  46. package/dist/persistence/web/index.mjs +0 -51
  47. package/dist/persistence/web/index.mjs.map +0 -1
  48. package/dist/persistence-CSB8770k.mjs +0 -108
  49. package/dist/persistence-CSB8770k.mjs.map +0 -1
  50. package/dist/planner-host-CPyGNyQV.d.mts +0 -190
  51. package/dist/planner-host-CV38bMSn.mjs +0 -195
  52. package/dist/planner-host-CV38bMSn.mjs.map +0 -1
  53. package/dist/types-C59fcM5W.d.mts +0 -52
  54. package/dist/types-DQc088ZP.d.mts +0 -32
  55. package/dist/web-router/index.d.mts +0 -259
  56. package/dist/web-router/index.mjs +0 -395
  57. package/dist/web-router/index.mjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,69 +1,273 @@
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 { _ 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";
4
- //#region src/cancellation/cancellation-token.ts
5
- var CancellationToken = class {
6
- _isCancelled = false;
7
- _reason;
8
- _listeners = /* @__PURE__ */ new Set();
9
- get isCancellationRequested() {
10
- return this._isCancelled;
11
- }
12
- get reason() {
13
- return this._reason;
1
+ import { A as RxEventSubject, B as withDisposableStack, E as RxComputedSignal, F as SYMBOL_DISPOSE, G as UserRecovery, H as ClientErrorKind, I as SimpleDisposableStack, L as StdDisposableStack, M as isInteropObservableTrait, N as SYMBOL_ASYNC_DISPOSE, O as RxEventReplaySubject, P as SYMBOL_DISPOSABLE_STACK, R as createDisposableStack, U as ClientErrorSource, V as ClientError, W as ErrorPresentationTone, a as ResourceSnapshotUpdateKind, c as whenResourceSnapshotValue, d as ResourceErrorCode, f as CancellationToken, i as rxResource, j as SYMBOL_OBSERVABLE, k as RxEventStream, l as ResourceStatus, m as isCancellationTokenTrait, n as mapResource, o as flattenResourceSnapshot, p as createCancellationTokenSource, r as resourceFromSnapshots, s as reduceResourceSnapshot, u as ResourceError, z as injectDisposableStackFrom } from "./rx-BoB32Dzg.mjs";
2
+ import { $ as INJECTOR_TOKEN, A as appendOrReplaceCompatFragment, B as createJsonRpcServerAndClient, C as IDLE_CALLBACK_TRAIT_TOKEN, D as RouterNavigationMode, E as RouterNavigationIntent, F as PopupAttachFailureReason, G as validateWithSchemaSync, H as throwValidationClientError, I as PopupClientWindowHandle, J as PAGE_LIFECYCLE_TRAIT_TOKEN, K as PopupErrorCode, L as PopupServerWindowHandle, M as parseCompatFragment, N as takeCompatFragment, O as takeCompatFragmentFromRouter, P as POPUP_TRAIT_TOKEN, Q as readonlySignal, R as createJsonRpcClient, S as SPAN_TRAIT_TOKEN, T as ROUTER_TRAIT_TOKEN, U as validateTraitInput, V as formatValidationFailure, W as validateWithSchema, X as isEventStreamTrait, Y as EventSourceKind, Z as createSignal, _ as abortSignalToCancellationToken, a as TracingLevel, at as createProviderIfTokenMissing, b as abortSignalToEventStream, c as PERSISTENT_STORAGE_TRAIT_TOKEN, ct as readErrorPresentationDescriptor, d as StorageChangeEventOrigin, et as PARENT_INJECTOR_TOKEN, f as FetchTransportRedirectKind, g as createTimeForStd, h as TransportErrorCode, i as TRACING_TRAIT_TOKEN, it as SecuritydeptInjectionToken, j as isCompatFragmentBlock, k as SECURITYDEPT_COMPAT_FRAGMENT_VERSION, l as REALM_STORAGE_TRAIT_TOKEN, m as TRANSPORT_TRAIT_TOKEN, n as ENVIRONMENT_TOKEN, nt as toRuntimeInjector, o as createTracing, ot as getSecuritydeptProviderToken, p as createBaseTransportForStdFetch, q as readPopupErrorPresentationDescriptor, r as OperationTraceEventType, rt as SecurityDeptOptional, s as createInMemoryRecordStore, st as notMissingProvider, t as createFoundationEnvironment, tt as SecuritydeptInjector, u as SESSION_STORAGE_TRAIT_TOKEN, v as cancellationTokenToAbortSignal, w as TIME_TRAIT_TOKEN, x as createRootSpan, y as normalizeAbortError, z as createJsonRpcServer } from "./create-Bm-XRA3G.mjs";
3
+ import { i as UriString, n as UriReferenceString, r as UriRelativeString, t as UriParseError } from "./uri-string-BfnIT4gJ.mjs";
4
+ import { v7 } from "uuid";
5
+ import { inject as inject$1, runInInjectionContext as runInInjectionContext$1 } from "injection-js";
6
+ import { EMPTY, NEVER, ReplaySubject, filter, from, merge, take, takeUntil } from "rxjs";
7
+ import { type } from "arktype";
8
+ import { isPromise } from "es-toolkit/predicate";
9
+ //#region src/auth-coordination/contract.ts
10
+ var StaticAttrsAuthRequirement = class StaticAttrsAuthRequirement {
11
+ id;
12
+ label;
13
+ attributes;
14
+ constructor(id, label, attributes) {
15
+ this.id = id;
16
+ this.label = label;
17
+ this.attributes = attributes;
18
+ Object.freeze(this);
14
19
  }
15
- /** @internal — called by `CancellationTokenSource`. */
16
- _cancel(reason) {
17
- if (this._isCancelled) return;
18
- this._isCancelled = true;
19
- this._reason = reason;
20
- for (const listener of this._listeners) listener(reason);
21
- this._listeners.clear();
20
+ static create(input) {
21
+ return new StaticAttrsAuthRequirement(input?.id ?? v7(), input?.label, input?.attributes ? Object.freeze(input.attributes) : void 0);
22
22
  }
23
- onCancellationRequested(listener) {
24
- if (this._isCancelled) {
25
- listener(this._reason);
26
- return { dispose() {} };
27
- }
28
- this._listeners.add(listener);
29
- return { dispose: () => {
30
- this._listeners.delete(listener);
31
- } };
23
+ static createList(inputs) {
24
+ return inputs.map((input) => StaticAttrsAuthRequirement.create(input));
32
25
  }
33
- throwIfCancellationRequested() {
34
- if (this._isCancelled) throw new ClientError({
35
- kind: ClientErrorKind.Cancelled,
36
- message: "Operation was cancelled",
37
- cause: this._reason
38
- });
26
+ };
27
+ /** Outcome of resolving a single requirement. */
28
+ const ResolutionStatus = {
29
+ Fulfilled: "fulfilled",
30
+ Failed: "failed",
31
+ Skipped: "skipped"
32
+ };
33
+ /** Overall plan status. */
34
+ const PlanStatus = {
35
+ /** There are still pending requirements. */
36
+ Pending: "pending",
37
+ /** All requirements have been resolved. */
38
+ Settled: "settled"
39
+ };
40
+ /** Error thrown when a planner contract is violated (e.g. duplicate ids). */
41
+ var RequirementPlannerError = class extends Error {
42
+ constructor(message) {
43
+ super(message);
44
+ this.name = "RequirementPlannerError";
39
45
  }
40
46
  };
47
+ function isRouteStateSnapshotTrait(value) {
48
+ return typeof value === "object" && value !== null && "url" in value;
49
+ }
50
+ function isRouteBehaviourContextExtra(value) {
51
+ return typeof value === "object" && value !== null && "routeState" in value && isRouteStateSnapshotTrait(value.routeState);
52
+ }
53
+ /** Outcome of a pipeline step or run. */
54
+ const PipelineOutcome = {
55
+ /** No candidates remain — the plan is satisfied. */
56
+ Settled: "settled",
57
+ /** A candidate was resolved as fulfilled; more may remain. */
58
+ Resolved: "resolved",
59
+ /** A candidate's handler blocked progress. */
60
+ Blocked: "blocked",
61
+ /** A candidate's handler requested a redirect. */
62
+ Redirect: "redirect"
63
+ };
41
64
  /**
42
- * Create a `CancellationTokenSource` that produces a cooperative cancellation token.
65
+ * How a route segment's requirements compose with the inherited parent chain.
66
+ *
67
+ * - `inherit` — keep the parent requirements unchanged (segment adds nothing)
68
+ * - `merge` — inherit parent requirements, then append/override with the
69
+ * segment's own (same-id entries replace the parent's)
70
+ * - `replace` — discard the parent chain, use only the segment's requirements
43
71
  */
44
- function createCancellationTokenSource() {
45
- const ct = new CancellationToken();
46
- let disposed = false;
47
- return {
48
- get token() {
49
- return ct;
50
- },
51
- cancel(reason) {
52
- if (!disposed) ct._cancel(reason);
53
- },
54
- dispose() {
55
- if (!disposed) {
56
- disposed = true;
57
- ct._cancel(new ClientError({
58
- kind: ClientErrorKind.Cancelled,
59
- message: "Disposed"
60
- }));
61
- }
72
+ const RequirementsComposition = {
73
+ Inherit: "inherit",
74
+ Merge: "merge",
75
+ Replace: "replace"
76
+ };
77
+ /**
78
+ * Apply a route requirements declaration onto an effective requirement map.
79
+ *
80
+ * Reuse `effective` across a route chain so merge/replace fold in O(total
81
+ * requirements) without rebuilding parent state each segment. When omitted, a
82
+ * fresh map is created for one-shot composition.
83
+ */
84
+ function resolveEffectiveRequirements(child, effective = /* @__PURE__ */ new Map()) {
85
+ switch (child.composition) {
86
+ case RequirementsComposition.Inherit: return effective;
87
+ case RequirementsComposition.Merge:
88
+ for (const requirement of child.requirements) effective.set(requirement.id, requirement);
89
+ return effective;
90
+ case RequirementsComposition.Replace:
91
+ effective.clear();
92
+ for (const requirement of child.requirements) effective.set(requirement.id, requirement);
93
+ return effective;
94
+ default: {
95
+ const _exhaustive = child.composition;
96
+ throw new RequirementPlannerError(`Unknown requirements composition: ${String(_exhaustive)}`);
62
97
  }
98
+ }
99
+ }
100
+ //#endregion
101
+ //#region src/errors/error-attributes.ts
102
+ /** Extract secret-safe diagnostic attributes without copying runtime messages. */
103
+ function describeError(error) {
104
+ if (error instanceof ClientError) return {
105
+ errorName: error.name,
106
+ errorKind: error.kind,
107
+ errorCode: error.code,
108
+ recovery: error.recovery
63
109
  };
110
+ return { errorName: error instanceof Error ? error.name : typeof error };
111
+ }
112
+ //#endregion
113
+ //#region src/compat/base64.ts
114
+ const Base64CompatErrorCode = {
115
+ EncoderUnavailable: "compat.base64.encoder_unavailable",
116
+ DecoderUnavailable: "compat.base64.decoder_unavailable"
117
+ };
118
+ function encodeUint8ArrayToBase64(value) {
119
+ const toBase64 = globalThis.Uint8Array.prototype.toBase64;
120
+ if (typeof toBase64 === "function") return toBase64.call(value);
121
+ if (typeof globalThis.btoa === "function") {
122
+ let binary = "";
123
+ for (const byte of value) binary += String.fromCharCode(byte);
124
+ return globalThis.btoa(binary);
125
+ }
126
+ const BufferConstructor = Reflect.get(globalThis, "Buffer");
127
+ if (typeof BufferConstructor?.from === "function") return BufferConstructor.from(value).toString("base64");
128
+ throw new ClientError({
129
+ kind: ClientErrorKind.Configuration,
130
+ code: Base64CompatErrorCode.EncoderUnavailable,
131
+ message: "The current realm does not provide a Base64 encoder.",
132
+ source: "compat.base64"
133
+ });
134
+ }
135
+ function decodeBase64ToUint8Array(value) {
136
+ const Uint8ArrayConstructor = globalThis.Uint8Array;
137
+ if (typeof Uint8ArrayConstructor.fromBase64 === "function") return Uint8ArrayConstructor.fromBase64(value);
138
+ if (typeof globalThis.atob === "function") {
139
+ const binary = globalThis.atob(value);
140
+ const bytes = new Uint8Array(binary.length);
141
+ for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
142
+ return bytes;
143
+ }
144
+ const BufferConstructor = Reflect.get(globalThis, "Buffer");
145
+ if (typeof BufferConstructor?.from === "function") return Uint8ArrayConstructor.from(BufferConstructor.from(value, "base64"));
146
+ throw new ClientError({
147
+ kind: ClientErrorKind.Configuration,
148
+ code: Base64CompatErrorCode.DecoderUnavailable,
149
+ message: "The current realm does not provide a Base64 decoder.",
150
+ source: "compat.base64"
151
+ });
152
+ }
153
+ //#endregion
154
+ //#region src/compat/generator.ts
155
+ async function* promisesToRacedAsyncGenerator(promises) {
156
+ const pending = new Map(promises.map((promise, index) => [index, promise.then((value) => ({
157
+ value,
158
+ index
159
+ }))]));
160
+ while (pending.size > 0) {
161
+ const next = await Promise.race(pending.values());
162
+ pending.delete(next.index);
163
+ yield next.value;
164
+ }
64
165
  }
65
166
  //#endregion
66
- //#region src/cancellation/linked-cancellation-token.ts
167
+ //#region src/injection/context.ts
168
+ function inject(token, options) {
169
+ if (options?.optional === true) return inject$1(token, { optional: true });
170
+ return inject$1(token);
171
+ }
172
+ function tryInjectInInjectionContext(token, options) {
173
+ try {
174
+ if (options?.optional === true) return inject(token, { optional: true });
175
+ return inject(token);
176
+ } catch (error) {
177
+ if (isMissingInjectionContextError(error)) return null;
178
+ throw error;
179
+ }
180
+ }
181
+ function runInInjectionContext(injector, fn) {
182
+ return runInInjectionContext$1(toRuntimeInjector(injector), fn);
183
+ }
184
+ function isMissingInjectionContextError(error) {
185
+ return error instanceof Error && error.message.includes("can only be used within an injection context");
186
+ }
187
+ //#endregion
188
+ //#region src/injection/destroy.ts
189
+ var SecuritydeptDestroyRef = class {
190
+ [SYMBOL_DISPOSE]() {
191
+ this.dispose();
192
+ }
193
+ };
194
+ var ManagedSecuritydeptDestroyRef = class extends SecuritydeptDestroyRef {
195
+ destroyedSignal = createSignal(false);
196
+ get destroyed() {
197
+ return this.destroyedSignal.get();
198
+ }
199
+ onDestroy(callback) {
200
+ if (this.destroyedSignal.get()) {
201
+ callback();
202
+ return () => void 0;
203
+ }
204
+ const subscription = from(this.destroyedSignal).pipe(filter((destroyed) => destroyed), take(1)).subscribe({ next: callback });
205
+ return () => {
206
+ subscription.unsubscribe();
207
+ };
208
+ }
209
+ dispose() {
210
+ if (this.destroyedSignal.get()) return;
211
+ this.destroyedSignal.set(true);
212
+ }
213
+ destroy() {
214
+ this.dispose();
215
+ }
216
+ };
217
+ function createSecuritydeptDestroyRef() {
218
+ return new ManagedSecuritydeptDestroyRef();
219
+ }
220
+ //#endregion
221
+ //#region src/validation/schemas/uri.ts
222
+ const UriStringSchema = type("string").narrow((value, context) => UriString.tryParse(value) == null ? context.reject("an absolute URI string") : true);
223
+ const BaseURIStringSchema = UriStringSchema;
224
+ const UriRelativeStringSchema = type("string").narrow((value, context) => UriRelativeString.tryParse(value) == null ? context.reject("a relative URI reference string") : true);
225
+ const UriReferenceStringSchema = type("string").narrow((value, context) => UriReferenceString.tryParse(value) == null ? context.reject("a URI reference string") : true);
226
+ //#endregion
227
+ //#region src/cancellation/linked.ts
228
+ var LinkedCancellationToken = class {
229
+ sources;
230
+ destoryed$ = new ReplaySubject(1);
231
+ token;
232
+ constructor(sources) {
233
+ this.sources = sources;
234
+ if (sources.length === 0) this.token = new CancellationToken();
235
+ if (sources.length === 1) this.token = sources[0];
236
+ for (const source of sources) if (source.isCancellationRequested) {
237
+ this.token = source;
238
+ return;
239
+ }
240
+ const newToken = new CancellationToken();
241
+ merge(...sources).pipe(take(1), takeUntil(this.destoryed$)).subscribe(({ reason }) => {
242
+ newToken._cancel(reason);
243
+ });
244
+ this.token = newToken;
245
+ }
246
+ get isCancellationRequested() {
247
+ return this.token.isCancellationRequested;
248
+ }
249
+ get cancellationError() {
250
+ return this.token.cancellationError;
251
+ }
252
+ get reason() {
253
+ return this.token.reason;
254
+ }
255
+ onCancellationRequested(listener) {
256
+ return this.token.onCancellationRequested(listener);
257
+ }
258
+ throwIfCancellationRequested() {
259
+ this.token.throwIfCancellationRequested();
260
+ }
261
+ dispose() {
262
+ this.destoryed$.next(true);
263
+ }
264
+ [SYMBOL_DISPOSE]() {
265
+ this.dispose();
266
+ }
267
+ [SYMBOL_OBSERVABLE]() {
268
+ return this.token[SYMBOL_OBSERVABLE]();
269
+ }
270
+ };
67
271
  /**
68
272
  * Create a cancellation token that fires when ANY of the given source tokens
69
273
  * is cancelled.
@@ -79,218 +283,211 @@ function createCancellationTokenSource() {
79
283
  * await transport.execute({ ..., cancellationToken: linked });
80
284
  */
81
285
  function createLinkedCancellationToken(...sources) {
82
- if (sources.length === 0) return createCancellationTokenSource().token;
83
- if (sources.length === 1) return sources[0];
84
- for (const source of sources) if (source.isCancellationRequested) return source;
85
- const linked = createCancellationTokenSource();
86
- const subscriptions = sources.map((source) => source.onCancellationRequested((reason) => linked.cancel(reason)));
87
- linked.token.onCancellationRequested(() => {
88
- for (const sub of subscriptions) sub.dispose();
89
- });
90
- return linked.token;
286
+ return new LinkedCancellationToken(sources.filter((s) => !!s));
91
287
  }
92
288
  //#endregion
93
- //#region src/errors/presentation-descriptor.ts
94
- const DEFAULT_RECOVERY_LABELS = {
95
- [UserRecovery.None]: "",
96
- [UserRecovery.Retry]: "Try again",
97
- [UserRecovery.RestartFlow]: "Restart flow",
98
- [UserRecovery.Reauthenticate]: "Sign in again",
99
- [UserRecovery.ContactSupport]: "Contact support"
100
- };
101
- const POPUP_PRESENTATIONS = {
102
- "popup.blocked": {
103
- title: "Popup was blocked",
104
- description: "The browser blocked the popup window before the login flow could start. Allow popups for this site, then try again.",
105
- tone: ErrorPresentationTone.Warning
106
- },
107
- "popup.closed_by_user": {
108
- title: "Popup login was closed",
109
- description: "The popup window was closed before the OIDC provider returned a callback. Start the login flow again to continue.",
110
- tone: ErrorPresentationTone.Warning
111
- },
112
- "popup.relay_timeout": {
113
- title: "Popup relay timed out",
114
- description: "The popup callback never relayed back to the opener before the timeout window expired.",
115
- tone: ErrorPresentationTone.Warning
116
- },
117
- "popup.relay_error": {
118
- title: "Popup relay failed",
119
- description: "The popup callback page returned an error instead of a callback payload.",
120
- tone: ErrorPresentationTone.Danger
121
- }
122
- };
123
- function readErrorPresentationDescriptor(error, options = {}) {
124
- if (error instanceof ClientError) return describeClientErrorPresentation(error, options);
125
- const clientErrorLike = coerceClientErrorLike(error);
126
- if (clientErrorLike) return describeClientErrorLikePresentation(clientErrorLike, options);
127
- if (error instanceof Error) return {
128
- code: null,
129
- kind: null,
130
- title: options.fallbackTitle ?? "Operation failed",
131
- description: error.message || options.fallbackDescription || "An unexpected error prevented the operation from completing.",
132
- recovery: UserRecovery.None,
133
- retryable: false,
134
- tone: ErrorPresentationTone.Danger,
135
- primaryAction: null
136
- };
137
- return {
138
- code: null,
139
- kind: null,
140
- title: options.fallbackTitle ?? "Operation failed",
141
- description: options.fallbackDescription ?? "An unexpected error prevented the operation from completing.",
142
- recovery: UserRecovery.None,
143
- retryable: false,
144
- tone: ErrorPresentationTone.Danger,
145
- primaryAction: null
146
- };
147
- }
148
- function describeClientErrorPresentation(error, options) {
149
- return describeClientErrorLikePresentation({
150
- code: error.code,
151
- kind: error.kind,
152
- message: error.message,
153
- recovery: error.recovery,
154
- retryable: error.retryable,
155
- source: error.source,
156
- presentation: error.presentation
157
- }, options);
158
- }
159
- function describeClientErrorLikePresentation(error, options) {
160
- const popupPresentation = POPUP_PRESENTATIONS[error.code];
161
- const title = popupPresentation?.title ?? readClientErrorTitle(error);
162
- const description = popupPresentation?.description ?? error.presentation?.message ?? error.message ?? options.fallbackDescription ?? "The operation could not complete.";
163
- return {
164
- code: error.code,
165
- kind: error.kind,
166
- source: error.source,
167
- title,
168
- description,
169
- recovery: error.recovery,
170
- retryable: error.retryable,
171
- tone: popupPresentation?.tone ?? readClientErrorTone(error),
172
- primaryAction: readPrimaryAction(error.recovery, options)
173
- };
289
+ //#region src/signals/computed.ts
290
+ /**
291
+ * Create a lazy computed signal.
292
+ *
293
+ * Dependencies are collected dynamically while `compute()` runs.
294
+ */
295
+ function createComputed(compute, options) {
296
+ return RxComputedSignal.computed(compute, options);
174
297
  }
175
- function readClientErrorTitle(error) {
176
- if (error.kind === ClientErrorKind.Unauthenticated) return "Authentication required";
177
- if (error.kind === ClientErrorKind.Unauthorized) return "Access denied";
178
- if (error.kind === ClientErrorKind.Cancelled) return "Operation cancelled";
179
- if (error.kind === ClientErrorKind.Timeout) return "Request timed out";
180
- if (error.kind === ClientErrorKind.Configuration) return "Configuration error";
181
- if (error.kind === ClientErrorKind.Validation) return "Validation failed";
182
- if (error.kind === ClientErrorKind.Storage) return "Browser storage failed";
183
- if (error.kind === ClientErrorKind.Authorization && error.recovery === UserRecovery.RestartFlow) return "Flow needs to restart";
184
- if (error.kind === ClientErrorKind.Transport) return "Network request failed";
185
- if (error.kind === ClientErrorKind.Server) return "Server request failed";
186
- if (error.kind === ClientErrorKind.Protocol) return "Request failed";
187
- return "Operation failed";
188
- }
189
- function readClientErrorTone(error) {
190
- if (error.kind === ClientErrorKind.Cancelled) return ErrorPresentationTone.Neutral;
191
- if (error.kind === ClientErrorKind.Unauthenticated || error.kind === ClientErrorKind.Timeout || error.kind === ClientErrorKind.Transport || error.kind === ClientErrorKind.Server || error.recovery === UserRecovery.Retry || error.recovery === UserRecovery.RestartFlow || error.recovery === UserRecovery.Reauthenticate) return ErrorPresentationTone.Warning;
192
- return ErrorPresentationTone.Danger;
193
- }
194
- function coerceClientErrorLike(error) {
195
- if (typeof error !== "object" || error === null) return null;
196
- const candidate = error;
197
- if (typeof candidate.message !== "string") return null;
198
- return {
199
- code: typeof candidate.code === "string" ? candidate.code : null,
200
- kind: typeof candidate.kind === "string" ? candidate.kind : null,
201
- message: candidate.message,
202
- recovery: typeof candidate.recovery === "string" ? candidate.recovery : UserRecovery.None,
203
- retryable: candidate.retryable === true,
204
- source: typeof candidate.source === "string" ? candidate.source : void 0,
205
- presentation: isErrorPresentation(candidate.presentation) ? candidate.presentation : void 0
206
- };
298
+ //#endregion
299
+ //#region src/signals/resource.ts
300
+ function createResource(options) {
301
+ return rxResource({
302
+ ...options,
303
+ stream: (context) => from(options.stream(context))
304
+ });
207
305
  }
208
- function isErrorPresentation(value) {
209
- return typeof value === "object" && value !== null && typeof value.code === "string" && typeof value.message === "string" && typeof value.recovery === "string";
306
+ //#endregion
307
+ //#region src/router/metadata.ts
308
+ /** Route-data key holding the SecurityDept metadata blob for a segment. */
309
+ const SECURITYDEPT_ROUTE_METADATA_KEY = "__securitydept__";
310
+ /** Read SecurityDept route metadata from a route `data` bag. */
311
+ function readSecuritydeptRouteMetadata(data) {
312
+ const raw = data?.[SECURITYDEPT_ROUTE_METADATA_KEY];
313
+ if (raw === void 0 || raw === null) return;
314
+ return raw;
210
315
  }
211
- function readPrimaryAction(recovery, options) {
212
- if (recovery === UserRecovery.None) return null;
213
- const label = options.recoveryLabels?.[recovery] ?? DEFAULT_RECOVERY_LABELS[recovery];
214
- if (!label) return null;
215
- return {
216
- recovery,
217
- label,
218
- href: options.recoveryLinks?.[recovery] ?? null
316
+ /** Merge SecurityDept route metadata into a route `data` bag. */
317
+ function writeSecuritydeptRouteMetadata(base, patch) {
318
+ const data = { ...base ?? {} };
319
+ data[SECURITYDEPT_ROUTE_METADATA_KEY] = {
320
+ ...data["__securitydept__"] ?? {},
321
+ ...patch
219
322
  };
323
+ return data;
220
324
  }
221
325
  //#endregion
222
- //#region src/identity/principal.ts
223
- function readClaimsBag(value) {
224
- return typeof value === "object" && value !== null ? value : void 0;
225
- }
226
- function readOptionalString(value) {
227
- return typeof value === "string" ? value : void 0;
228
- }
229
- function resolveDisplayName(displayName, subject) {
230
- if (typeof displayName === "string" && displayName.trim().length > 0) return displayName;
231
- return subject;
232
- }
233
- function normalizeAuthenticatedPrincipal(input) {
234
- if (typeof input.subject !== "string" || input.subject.trim().length === 0) return null;
235
- return {
236
- subject: input.subject,
237
- displayName: resolveDisplayName(input.displayName, input.subject),
238
- picture: readOptionalString(input.picture),
239
- issuer: readOptionalString(input.issuer),
240
- claims: readClaimsBag(input.claims)
326
+ //#region src/std/transport/authorization-interceptor.ts
327
+ function createAuthorizationInterceptedFetch(fetchImpl, options) {
328
+ return async (input, init) => {
329
+ if (!await options.predicate(input, init)) return fetchImpl(input, init);
330
+ const authorization = "whenValue" in options.authorization ? await options.authorization.whenValue() : options.authorization.get();
331
+ if (!authorization) return fetchImpl(input, init);
332
+ const headers = new Headers(typeof Request !== "undefined" && input instanceof Request ? input.headers : void 0);
333
+ if (init?.headers) new Headers(init.headers).forEach((value, key) => {
334
+ headers.set(key, value);
335
+ });
336
+ headers.set("authorization", authorization);
337
+ return fetchImpl(input, {
338
+ ...init,
339
+ headers
340
+ });
241
341
  };
242
342
  }
243
- function normalizeAuthenticatedPrincipalWire(input) {
244
- if (typeof input.subject !== "string" || input.subject.trim().length === 0) return null;
245
- return {
246
- subject: input.subject,
247
- displayName: resolveDisplayName(input.display_name, input.subject),
248
- picture: readOptionalString(input.picture),
249
- issuer: readOptionalString(input.issuer),
250
- claims: readClaimsBag(input.claims)
343
+ //#endregion
344
+ //#region src/tracing/operation-runner.ts
345
+ var OperationSpan = class OperationSpan {
346
+ _span;
347
+ _environment;
348
+ _name;
349
+ _target;
350
+ constructor(_span, _environment, _name, _target) {
351
+ this._span = _span;
352
+ this._environment = _environment;
353
+ this._name = _name;
354
+ this._target = _target;
355
+ }
356
+ static start(options) {
357
+ const operationSpan = new OperationSpan(options.span.fork({
358
+ mutable: true,
359
+ idFactory: options.idFactory,
360
+ attributes: options.fields
361
+ }), options.environment, options.name, options.target);
362
+ operationSpan.recordStarted();
363
+ return operationSpan;
364
+ }
365
+ get id() {
366
+ return this._span.id;
367
+ }
368
+ get parent() {
369
+ return this._span.parent;
370
+ }
371
+ get attributes() {
372
+ return this._span.attributes;
373
+ }
374
+ fork(options = {}) {
375
+ return this._span.fork(options);
376
+ }
377
+ setAttributes(attributes) {
378
+ this._span.setAttributes(attributes);
379
+ }
380
+ addEvent(name, fields) {
381
+ this._record(OperationTraceEventType.Event, TracingLevel.Info, {
382
+ eventName: name,
383
+ ...fields ?? {}
384
+ });
385
+ }
386
+ recordError(error, fields) {
387
+ this._record(OperationTraceEventType.Error, TracingLevel.Error, {
388
+ ...describeError(error),
389
+ ...fields ?? {}
390
+ });
391
+ }
392
+ recordStarted() {
393
+ this._record(OperationTraceEventType.Started, TracingLevel.Info);
394
+ }
395
+ recordEnded(outcome) {
396
+ this._record(OperationTraceEventType.Ended, outcome === "succeeded" ? TracingLevel.Info : TracingLevel.Error, { outcome });
397
+ }
398
+ _record(name, level, fields) {
399
+ this._environment.tracing.record({
400
+ name,
401
+ at: this._environment.time.now(),
402
+ span: this,
403
+ level,
404
+ target: this._target,
405
+ fields: {
406
+ operationName: this._name,
407
+ ...this.attributes,
408
+ ...fields ?? {}
409
+ }
410
+ });
411
+ }
412
+ };
413
+ function runOperation(options) {
414
+ const operationSpan = OperationSpan.start(options);
415
+ const complete = (result) => {
416
+ operationSpan.recordEnded("succeeded");
417
+ return result;
418
+ };
419
+ const fail = (error) => {
420
+ const normalizedError = options.normalizeError?.(error) ?? error;
421
+ operationSpan.recordError(normalizedError);
422
+ operationSpan.recordEnded("failed");
423
+ throw normalizedError;
251
424
  };
425
+ const invoke = () => options.execute(operationSpan);
426
+ try {
427
+ const result = invoke();
428
+ if (isPromise(result)) return result.then(complete, fail);
429
+ return complete(result);
430
+ } catch (error) {
431
+ return fail(error);
432
+ }
252
433
  }
253
- function projectAuthenticatedPrincipal(options) {
254
- const principal = options.principal;
255
- const subject = principal?.subject ?? options.fallbackSubject ?? "context.anonymous";
256
- return {
257
- subject,
258
- displayName: principal?.displayName?.trim() || options.fallbackDisplayName || subject,
259
- picture: principal?.picture,
260
- issuer: principal?.issuer ?? options.fallbackIssuer,
261
- claims: principal?.claims ?? options.fallbackClaims
434
+ //#endregion
435
+ //#region src/tracing/operation-method.ts
436
+ function defineInstrumentMethodDecorator(factory) {
437
+ return (...factoryArgs) => (target, context) => {
438
+ const methodName = String(context.name);
439
+ const resolve = factory({
440
+ factoryArgs,
441
+ methodName
442
+ });
443
+ return function(...args) {
444
+ const resolvedOptions = resolve.call(this, {
445
+ args,
446
+ methodName
447
+ });
448
+ return runOperation({
449
+ environment: resolvedOptions.environment,
450
+ span: resolvedOptions.span,
451
+ name: resolvedOptions.name ?? methodName,
452
+ target: resolvedOptions.target,
453
+ fields: resolvedOptions.fields,
454
+ idFactory: resolvedOptions.idFactory,
455
+ normalizeError: resolvedOptions.normalizeError,
456
+ execute: (span) => target.call(this, ...args, span)
457
+ });
458
+ };
262
459
  };
263
460
  }
264
461
  //#endregion
265
- //#region src/logging/console-logger.ts
266
- /** Simple console-based logger implementation. */
267
- function createConsoleLogger(scope) {
268
- return { log(entry) {
269
- const label = `[${entry.scope ?? scope ?? "securitydept"}]`;
270
- switch (entry.level) {
462
+ //#region src/tracing/subscriber/console.ts
463
+ function createConsoleTracingSubscriber() {
464
+ return { record(event) {
465
+ const label = `[${event.target}] ${event.name}`;
466
+ switch (event.level) {
271
467
  case "debug":
272
- console.debug(label, entry.message, entry.attributes ?? "");
468
+ console.debug(label, event.fields ?? {});
273
469
  break;
274
470
  case "info":
275
- console.info(label, entry.message, entry.attributes ?? "");
471
+ console.info(label, event.fields ?? {});
276
472
  break;
277
473
  case "warn":
278
- console.warn(label, entry.message, entry.attributes ?? "");
474
+ console.warn(label, event.fields ?? {});
279
475
  break;
280
476
  case "error":
281
- console.error(label, entry.message, entry.error ?? "", entry.attributes ?? "");
477
+ console.error(label, event.fields ?? {});
282
478
  break;
283
479
  }
284
480
  } };
285
481
  }
286
482
  //#endregion
287
- //#region src/logging/noop-logger.ts
288
- /** No-op logger — default when no logger is provided. */
289
- function createNoopLogger() {
290
- return { log() {} };
291
- }
292
- //#endregion
293
- //#region src/logging/trace-timeline-store.ts
483
+ //#region src/tracing/subscriber/timeline-store.ts
484
+ /**
485
+ * Create a subscriber-side trace timeline store.
486
+ *
487
+ * This store consumes tracing events through `record(event)` and keeps a
488
+ * bounded local timeline for tests and UI surfaces. It is not the tracing
489
+ * runtime itself.
490
+ */
294
491
  function createTraceTimelineStore(limit = 200) {
295
492
  let nextId = 1;
296
493
  let entries = [];
@@ -332,165 +529,730 @@ function createTraceTimelineStore(limit = 200) {
332
529
  };
333
530
  }
334
531
  //#endregion
335
- //#region src/signals/computed.ts
532
+ //#region src/auth-coordination/planner-host.ts
533
+ const REQUIREMENT_PLANNER_HOST = new SecuritydeptInjectionToken("REQUIREMENT_PLANNER_HOST");
336
534
  /**
337
- * Create a computed signal that derives its value from other signals.
535
+ * Default candidate selector: first unauthenticated candidate as soon as it is
536
+ * discovered.
338
537
  *
339
- * Semantics: lazy + cached.
340
- * - The compute function is NOT called at construction time.
341
- * - `get()` recomputes only when dirty (a dependency changed since last read).
342
- * - Subscribers are notified only when a dependency changes (dirty-flagged),
343
- * and the next `get()` will produce the fresh value.
344
- *
345
- * Dependency subscriptions are permanent for the lifetime of the computed signal.
538
+ * If every requirement is already fulfilled the stream completes and the
539
+ * selector returns `undefined`.
346
540
  */
347
- function createComputed(compute, deps) {
348
- let cached;
349
- let dirty = true;
350
- const listeners = /* @__PURE__ */ new Set();
351
- const markDirty = () => {
352
- if (!dirty) {
353
- dirty = true;
354
- for (const listener of listeners) listener();
355
- }
541
+ function defaultSelectCandidate() {
542
+ return async function selectFirstCandidate(context) {
543
+ const result = await context.candidateList.next();
544
+ return result.done ? void 0 : result.value;
356
545
  };
357
- for (const dep of deps) dep.subscribe(markDirty);
546
+ }
547
+ /**
548
+ * A coordination-scope host that resolves behaviour through a parent chain.
549
+ *
550
+ * Construct via {@link RequirementPlannerHost.fromBehaviour}. Each behaviour
551
+ * field is optional at host construction time; missing required behaviour is
552
+ * resolved from `parent` and fails fast during planner build if no host in the
553
+ * chain provides it.
554
+ *
555
+ * @example
556
+ * ```ts
557
+ * const root = RequirementPlannerHost.fromBehaviour({
558
+ * checkAuthenticated: (req) => isAuthenticated(req),
559
+ * onUnauthenticated: (req) => `/login/${req.id}`,
560
+ * });
561
+ *
562
+ * // Feature scope overrides only the chooser, inheriting the rest.
563
+ * const feature = RequirementPlannerHost.fromBehaviour(
564
+ * { selectCandidate: async (ctx) => showChooser(ctx.candidateList) },
565
+ * { parent: root },
566
+ * );
567
+ * ```
568
+ */
569
+ var RequirementPlannerHost = class RequirementPlannerHost {
570
+ environment;
571
+ parent;
572
+ /** The parent host, or undefined at the root. */
573
+ _behaviour;
574
+ constructor(behaviour, environment, parent) {
575
+ this.environment = environment;
576
+ this.parent = parent;
577
+ this._behaviour = behaviour;
578
+ }
579
+ /** Create a host scope from a partial behaviour and optional parent. */
580
+ static fromBehaviour(behaviour, options = {}) {
581
+ return new RequirementPlannerHost(behaviour, options.environment, options.parent);
582
+ }
583
+ async resolveBehaviourOptionFor(key) {
584
+ return this._behaviour[key] ?? await this.parent?.resolveBehaviourOptionFor(key);
585
+ }
586
+ async resolveBehaviourFor(key, defaultValue) {
587
+ return await this.resolveBehaviourOptionFor(key) ?? await defaultValue();
588
+ }
589
+ async resolveEnvironmentOption() {
590
+ return this.environment ?? await this.parent?.resolveEnvironmentOption();
591
+ }
592
+ async resolveEnvironment(defaultValue) {
593
+ return await this.resolveEnvironmentOption() ?? await defaultValue();
594
+ }
595
+ };
596
+ function provideRequirementPlannerHost(behaviour, options = {}) {
358
597
  return {
359
- get() {
360
- if (dirty) {
361
- cached = compute();
362
- dirty = false;
363
- }
364
- return cached;
598
+ provide: REQUIREMENT_PLANNER_HOST,
599
+ useFactory: (parentInjector, environment) => {
600
+ const parent = options.parent ?? parentInjector?.get(REQUIREMENT_PLANNER_HOST, null) ?? void 0;
601
+ const resolved = typeof behaviour === "function" ? behaviour() : behaviour;
602
+ return RequirementPlannerHost.fromBehaviour(resolved, {
603
+ parent,
604
+ environment: options.environment ?? environment ?? void 0
605
+ });
365
606
  },
366
- subscribe(listener) {
367
- listeners.add(listener);
368
- return () => {
369
- listeners.delete(listener);
370
- };
371
- }
607
+ deps: [[new SecurityDeptOptional(), PARENT_INJECTOR_TOKEN], [new SecurityDeptOptional(), ENVIRONMENT_TOKEN]]
372
608
  };
373
609
  }
610
+ function injectRequirementPlannerHost(options) {
611
+ return options.injector.get(REQUIREMENT_PLANNER_HOST, null);
612
+ }
374
613
  //#endregion
375
- //#region src/signals/signal.ts
614
+ //#region src/auth-coordination/planner/base-planner.ts
376
615
  /**
377
- * Minimal writable signal implementation.
378
- * Snapshot-first: values are immutable snapshots, mutations go through `set()`.
616
+ * Abstract base for all requirement planners.
617
+ *
618
+ * Subclasses supply requirements via {@link buildRequirementList}. Pipeline
619
+ * state lives on {@link RequirementPlan}, produced by {@link buildPlan}.
379
620
  */
380
- function createSignal(initial) {
381
- let current = initial;
382
- const listeners = /* @__PURE__ */ new Set();
383
- return {
384
- get() {
385
- return current;
386
- },
387
- set(value) {
388
- if (Object.is(current, value)) return;
389
- current = value;
390
- for (const listener of listeners) listener();
391
- },
392
- subscribe(listener) {
393
- listeners.add(listener);
394
- return () => {
395
- listeners.delete(listener);
621
+ var BaseRequirementPlanner = class {
622
+ host;
623
+ initialResolutions;
624
+ constructor(host, initialResolutions = []) {
625
+ this.host = host;
626
+ this.initialResolutions = initialResolutions.map((resolution) => Object.freeze({ ...resolution }));
627
+ }
628
+ /**
629
+ * Pipeline step 0: resolve host behaviour and materialize requirements into
630
+ * a {@link RequirementPlan}.
631
+ *
632
+ * Behaviour and the requirement list stay fixed on the returned plan until
633
+ * the next `buildPlan()` call. Resolve host parent chains (and any
634
+ * injection-context capture) here — not between async steps.
635
+ */
636
+ async buildPlan() {
637
+ return {
638
+ planContext: this.planContext,
639
+ environment: await this.host.resolveEnvironment(() => {
640
+ throw new RequirementPlannerError("No environment resolved");
641
+ }),
642
+ checkAuthenticated: await this.host.resolveBehaviourFor("checkAuthenticated", () => {
643
+ throw new RequirementPlannerError("No checkAuthenticated resolved");
644
+ }),
645
+ onUnauthenticated: await this.host.resolveBehaviourFor("onUnauthenticated", () => {
646
+ throw new RequirementPlannerError("No onUnauthenticated behaviour resolved");
647
+ }),
648
+ selectCandidate: await this.host.resolveBehaviourFor("selectCandidate", defaultSelectCandidate),
649
+ requirements: await this.resolveRequirementList(),
650
+ resolutionList: [...this.initialResolutions]
651
+ };
652
+ }
653
+ /**
654
+ * Materialize the effective requirement list (also invoked by {@link buildPlan}).
655
+ */
656
+ async resolveRequirementList() {
657
+ return [...(await this.buildRequirementList()).values()];
658
+ }
659
+ /**
660
+ * Pipeline step 1 — one loop iteration: select a candidate, run
661
+ * `onUnauthenticated`, then reduce `plan` state.
662
+ */
663
+ async runStep(plan) {
664
+ const candidate = await plan.selectCandidate({
665
+ ...plan,
666
+ candidateList: this.createCandidateStream(plan)
667
+ });
668
+ if (!candidate) return { outcome: PipelineOutcome.Settled };
669
+ const action = await plan.onUnauthenticated(candidate, plan);
670
+ return this.reduceFromAction(plan, candidate, action);
671
+ }
672
+ reduceFromAction(plan, candidate, action) {
673
+ if (action === true) {
674
+ this.recordFulfilled(plan, candidate);
675
+ return {
676
+ outcome: PipelineOutcome.Resolved,
677
+ requirement: candidate
396
678
  };
397
679
  }
398
- };
399
- }
680
+ if (action === false) return {
681
+ outcome: PipelineOutcome.Blocked,
682
+ requirement: candidate
683
+ };
684
+ return {
685
+ outcome: PipelineOutcome.Redirect,
686
+ requirement: candidate,
687
+ location: action
688
+ };
689
+ }
690
+ /**
691
+ * Run the full pipeline: {@link buildPlan}, step 1, then loop step 2 until
692
+ * settled or a terminal outcome (`blocked`, `redirect`, or a never-settling
693
+ * action).
694
+ */
695
+ async runUntilSettled() {
696
+ const plan = await this.buildPlan();
697
+ return this.runUntilSettledWithPlan(plan);
698
+ }
699
+ async runUntilSettledWithPlan(plan) {
700
+ while (true) {
701
+ const step = await this.runStep(plan);
702
+ if (step.outcome === PipelineOutcome.Settled) return {
703
+ outcome: PipelineOutcome.Settled,
704
+ resolutions: plan.resolutionList
705
+ };
706
+ if (step.outcome === PipelineOutcome.Resolved) continue;
707
+ if (step.outcome === PipelineOutcome.Blocked) return step;
708
+ if (step.outcome === PipelineOutcome.Redirect) return step;
709
+ }
710
+ }
711
+ async *createCandidateStream(plan) {
712
+ const context = {
713
+ planContext: this.planContext,
714
+ requirements: plan.requirements,
715
+ resolutionList: plan.resolutionList,
716
+ environment: plan.environment
717
+ };
718
+ const checked = plan.requirements.filter((requirement) => !plan.resolutionList.some((resolution) => resolution.requirementId === requirement.id)).map(async (requirement) => {
719
+ return {
720
+ requirement,
721
+ satisfied: await plan.checkAuthenticated(requirement, context)
722
+ };
723
+ });
724
+ for await (const { requirement, satisfied } of promisesToRacedAsyncGenerator(checked)) {
725
+ if (satisfied) {
726
+ this.recordFulfilled(plan, requirement);
727
+ continue;
728
+ }
729
+ yield requirement;
730
+ }
731
+ }
732
+ recordFulfilled(plan, requirement) {
733
+ if (plan.resolutionList.some((resolution) => resolution.requirementId === requirement.id)) return;
734
+ plan.resolutionList.push(Object.freeze({
735
+ requirementId: requirement.id,
736
+ status: ResolutionStatus.Fulfilled
737
+ }));
738
+ }
739
+ };
740
+ //#endregion
741
+ //#region src/auth-coordination/planner/merge-planner.ts
400
742
  /**
401
- * Create a read-only view of a writable signal.
743
+ * A planner whose effective requirements are the ordered union of its source
744
+ * planners. Overlapping ids follow Map semantics (later source wins).
745
+ *
746
+ * @example
747
+ * ```ts
748
+ * const planner = MergeRequirementPlanner.fromPlanners(host, [
749
+ * sessionPlanner,
750
+ * oidcPlanner,
751
+ * ]);
752
+ * ```
402
753
  */
403
- function readonlySignal(signal) {
404
- return {
405
- get: () => signal.get(),
406
- subscribe: (listener) => signal.subscribe(listener)
407
- };
408
- }
754
+ var MergeRequirementPlanner = class MergeRequirementPlanner extends BaseRequirementPlanner {
755
+ sources;
756
+ get planContext() {
757
+ return Object.assign({}, ...this.sources.map((source) => source.planContext));
758
+ }
759
+ constructor(host, sources) {
760
+ super(host);
761
+ this.sources = sources;
762
+ }
763
+ /** Create a merge planner from an ordered list of source planners. */
764
+ static fromPlanners(host, planners) {
765
+ return new MergeRequirementPlanner(host, planners);
766
+ }
767
+ async buildRequirementList() {
768
+ const map = /* @__PURE__ */ new Map();
769
+ for (const source of this.sources) for (const requirement of await source.resolveRequirementList()) map.set(requirement.id, requirement);
770
+ return map;
771
+ }
772
+ };
409
773
  //#endregion
410
- //#region src/validation/index.ts
774
+ //#region src/auth-coordination/planner/route-composition-planner.ts
775
+ /** Fold a matched route chain into its effective requirement list. */
776
+ function foldSegments(segments) {
777
+ const effective = /* @__PURE__ */ new Map();
778
+ for (const segment of segments) resolveEffectiveRequirements({
779
+ composition: segment.composition ?? RequirementsComposition.Merge,
780
+ requirements: segment.requirements
781
+ }, effective);
782
+ return [...effective.values()];
783
+ }
784
+ /** Length of the shared requirement-id prefix between two effective lists. */
785
+ function sharedPrefixLength(previous, next) {
786
+ const limit = Math.min(previous.length, next.length);
787
+ let shared = 0;
788
+ while (shared < limit && previous[shared].id === next[shared].id) shared += 1;
789
+ return shared;
790
+ }
411
791
  /**
412
- * Validate an unknown input against a `@standard-schema`-compatible schema.
792
+ * A planner driven by a matched route chain with composition semantics.
413
793
  *
414
- * This is the foundation-level entry point for SDK validation. It accepts
415
- * any schema implementing `StandardSchemaV1` and returns a discriminated
416
- * `ValidationResult`.
794
+ * Construct via {@link fromRouteSegments} for a matched route chain, or
795
+ * {@link fromRouteTransition} for a transition that preserves shared-prefix
796
+ * resolutions from the previous planner instance.
797
+ */
798
+ var RouteCompositionRequirementPlanner = class RouteCompositionRequirementPlanner extends BaseRequirementPlanner {
799
+ _segments;
800
+ _effective;
801
+ _planContext;
802
+ constructor(host, segments, routeState, preservedResolutions = []) {
803
+ super(host, preservedResolutions);
804
+ this._segments = segments;
805
+ this._effective = foldSegments(segments);
806
+ this._planContext = Object.freeze({ routeState: Object.freeze({ ...routeState }) });
807
+ }
808
+ /** Build a planner from a matched route chain. */
809
+ static fromRouteSegments(host, segments, routeState) {
810
+ return new RouteCompositionRequirementPlanner(host, segments, routeState);
811
+ }
812
+ /**
813
+ * Build a planner for a route transition, preserving resolutions for the
814
+ * shared requirement prefix carried over from `previousPlan`.
815
+ */
816
+ static fromRouteTransition(host, currentSegments, routeState, previousPlanner, previousPlan) {
817
+ const nextEffective = foldSegments(currentSegments);
818
+ const shared = sharedPrefixLength(previousPlanner.effectiveRequirements, nextEffective);
819
+ const sharedIds = new Set(nextEffective.slice(0, shared).map((requirement) => requirement.id));
820
+ return new RouteCompositionRequirementPlanner(host, currentSegments, routeState, previousPlan.resolutionList.filter((resolution) => sharedIds.has(resolution.requirementId)));
821
+ }
822
+ get planContext() {
823
+ return this._planContext;
824
+ }
825
+ /** The matched route chain backing this planner. */
826
+ get segments() {
827
+ return this._segments;
828
+ }
829
+ /** The folded effective requirement list (composition applied). */
830
+ get effectiveRequirements() {
831
+ return this._effective;
832
+ }
833
+ buildRequirementList() {
834
+ return new Map(this._effective.map((requirement) => [requirement.id, requirement]));
835
+ }
836
+ };
837
+ //#endregion
838
+ //#region src/auth-coordination/planner/static-planner.ts
839
+ /**
840
+ * A planner whose effective requirement list is a fixed array supplied at
841
+ * construction time.
417
842
  *
418
843
  * @example
419
844
  * ```ts
420
- * import { validateWithSchema } from "@securitydept/client";
421
- * import { z } from "zod";
422
- *
423
- * const UserSchema = z.object({ name: z.string() });
424
- * const result = await validateWithSchema(UserSchema, { name: "Alice" });
425
- * if (result.success) {
426
- * console.log(result.value.name);
427
- * } else {
428
- * console.error(result.issues);
429
- * }
845
+ * const planner = StaticRequirementPlanner.fromRequirements(host, [
846
+ * StaticAttrsAuthRequirement.create({ id: "session" }),
847
+ * ] as Readonly<AuthRequirement[]>);
848
+ * const plan = await planner.buildPlan();
849
+ * const step = await planner.runStep(plan);
850
+ * // or: const result = await planner.runUntilSettled();
430
851
  * ```
431
852
  */
432
- async function validateWithSchema(schema, input) {
433
- const result = await schema["~standard"].validate(input);
434
- if ("value" in result) return {
435
- success: true,
436
- value: result.value
853
+ var StaticRequirementPlanner = class StaticRequirementPlanner extends BaseRequirementPlanner {
854
+ staticRequirements;
855
+ get planContext() {
856
+ return {};
857
+ }
858
+ constructor(host, requirements) {
859
+ super(host);
860
+ this.staticRequirements = requirements;
861
+ }
862
+ /** Create a static planner from an ordered requirement list. */
863
+ static fromRequirements(host, requirements) {
864
+ return new StaticRequirementPlanner(host, requirements);
865
+ }
866
+ buildRequirementList() {
867
+ const map = /* @__PURE__ */ new Map();
868
+ for (const requirement of this.staticRequirements) map.set(requirement.id, requirement);
869
+ return map;
870
+ }
871
+ };
872
+ //#endregion
873
+ //#region src/events/create.ts
874
+ /**
875
+ * Create a new event stream from a producer function.
876
+ *
877
+ * The producer receives an observer and should call `next()` to emit values,
878
+ * `error()` to signal an error, and `complete()` to signal completion.
879
+ * It may return a teardown function that will be called on unsubscribe.
880
+ */
881
+ function createEventStream(producer) {
882
+ return new RxEventStream(producer);
883
+ }
884
+ function createEventSubject() {
885
+ return new RxEventSubject();
886
+ }
887
+ function createEventReplaySubject(bufferSize = Infinity) {
888
+ return new RxEventReplaySubject(bufferSize);
889
+ }
890
+ function createEmptyEventStream() {
891
+ return RxEventStream.fromObservableInput(EMPTY);
892
+ }
893
+ function createNeverEventStream() {
894
+ return RxEventStream.fromObservableInput(NEVER);
895
+ }
896
+ //#endregion
897
+ //#region src/events/interop.ts
898
+ function toEventStream(input) {
899
+ if (isEventStreamTrait(input)) return input;
900
+ return RxEventStream.fromObservableInput(from(input));
901
+ }
902
+ //#endregion
903
+ //#region src/identity/schemas.ts
904
+ const IdentityPrincipalSchema = type({
905
+ subject: "string > 0",
906
+ "displayName?": "string | null | undefined",
907
+ "picture?": "string | null | undefined",
908
+ "issuer?": "string | null | undefined",
909
+ "claims?": "object | null | undefined"
910
+ }).pipe((input) => ({
911
+ subject: input.subject,
912
+ displayName: typeof input.displayName === "string" && input.displayName.trim().length > 0 ? input.displayName : input.subject,
913
+ picture: input.picture ?? void 0,
914
+ issuer: input.issuer ?? void 0,
915
+ claims: input.claims ?? void 0
916
+ }));
917
+ const IdentityPrincipalWireSchema = type({
918
+ subject: "string > 0",
919
+ "display_name?": "string | null | undefined",
920
+ "picture?": "string | null | undefined",
921
+ "issuer?": "string | null | undefined",
922
+ "claims?": "object | null | undefined"
923
+ }).pipe((input) => ({
924
+ subject: input.subject,
925
+ displayName: typeof input.display_name === "string" && input.display_name.trim().length > 0 ? input.display_name : input.subject,
926
+ picture: input.picture ?? void 0,
927
+ issuer: input.issuer ?? void 0,
928
+ claims: input.claims ?? void 0
929
+ }));
930
+ //#endregion
931
+ //#region src/identity/parsers.ts
932
+ function throwIdentityPrincipalParseError(options) {
933
+ throw new ClientError({
934
+ kind: ClientErrorKind.Protocol,
935
+ code: options.code,
936
+ message: `${options.messagePrefix}: ${formatValidationFailure(options.failure) ?? "schema validation failed"}.`,
937
+ source: "identity",
938
+ cause: options.failure.issues
939
+ });
940
+ }
941
+ function parseIdentityPrincipal(input) {
942
+ const result = validateWithSchemaSync(IdentityPrincipalSchema, input);
943
+ if (result.success) return result.value;
944
+ throwIdentityPrincipalParseError({
945
+ code: "identity.invalid_principal",
946
+ messagePrefix: "Identity principal payload is invalid",
947
+ failure: result
948
+ });
949
+ }
950
+ function parseIdentityPrincipalWire(input) {
951
+ const result = validateWithSchemaSync(IdentityPrincipalWireSchema, input);
952
+ if (result.success) return result.value;
953
+ throwIdentityPrincipalParseError({
954
+ code: "identity.invalid_principal_wire",
955
+ messagePrefix: "Identity principal wire payload is invalid",
956
+ failure: result
957
+ });
958
+ }
959
+ function projectIdentityPrincipal(options) {
960
+ const principal = options.principal;
961
+ const subject = principal?.subject ?? options.fallbackSubject ?? "context.anonymous";
962
+ return {
963
+ subject,
964
+ displayName: principal?.displayName?.trim() || options.fallbackDisplayName || subject,
965
+ picture: principal?.picture,
966
+ issuer: principal?.issuer ?? options.fallbackIssuer,
967
+ claims: principal?.claims ?? options.fallbackClaims
437
968
  };
969
+ }
970
+ //#endregion
971
+ //#region src/protocol/jwt/error.ts
972
+ const JwtDecodeErrorCode = {
973
+ InvalidFormat: "protocol.jwt.invalid_format",
974
+ InvalidPayload: "protocol.jwt.invalid_payload",
975
+ InvalidClaim: "protocol.jwt.invalid_claim"
976
+ };
977
+ var JwtDecodeError = class extends ClientError {
978
+ code;
979
+ constructor(options) {
980
+ super({
981
+ kind: ClientErrorKind.Protocol,
982
+ code: options.code,
983
+ message: options.message,
984
+ source: "protocol.jwt",
985
+ cause: options.cause
986
+ });
987
+ this.name = "JwtDecodeError";
988
+ this.code = options.code;
989
+ }
990
+ };
991
+ //#endregion
992
+ //#region src/protocol/jwt/decode.ts
993
+ function decodeJwtPayload(jwt, options = {}) {
994
+ const label = options.label ?? "JWT";
995
+ const parts = jwt.split(".");
996
+ if (parts.length !== 3) throw new JwtDecodeError({
997
+ code: JwtDecodeErrorCode.InvalidFormat,
998
+ message: `${label} must use compact JWS serialization`
999
+ });
1000
+ const claims = parseBase64UrlJsonObject(parts[1], `${label} payload`);
1001
+ validateRegisteredClaimTypes(claims, label);
1002
+ return claims;
1003
+ }
1004
+ function parseBase64UrlJsonObject(value, label) {
1005
+ let parsed;
1006
+ try {
1007
+ parsed = JSON.parse(decodeBase64UrlUtf8(value));
1008
+ } catch (cause) {
1009
+ throw new JwtDecodeError({
1010
+ code: JwtDecodeErrorCode.InvalidPayload,
1011
+ message: `${label} must be base64url-encoded JSON`,
1012
+ cause
1013
+ });
1014
+ }
1015
+ if (!isJsonObject(parsed)) throw new JwtDecodeError({
1016
+ code: JwtDecodeErrorCode.InvalidPayload,
1017
+ message: `${label} must be a JSON object`
1018
+ });
1019
+ return parsed;
1020
+ }
1021
+ function decodeBase64UrlUtf8(value) {
1022
+ const base64 = value.replace(/-/g, "+").replace(/_/g, "/");
1023
+ const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
1024
+ const binary = atob(padded);
1025
+ const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
1026
+ return new TextDecoder().decode(bytes);
1027
+ }
1028
+ function validateRegisteredClaimTypes(claims, label) {
1029
+ if (claims.exp !== void 0 && typeof claims.exp !== "number") throw new JwtDecodeError({
1030
+ code: JwtDecodeErrorCode.InvalidClaim,
1031
+ message: `${label} "exp" claim must be a number`
1032
+ });
1033
+ if (claims.iat !== void 0 && typeof claims.iat !== "number") throw new JwtDecodeError({
1034
+ code: JwtDecodeErrorCode.InvalidClaim,
1035
+ message: `${label} "iat" claim must be a number`
1036
+ });
1037
+ if (claims.nbf !== void 0 && typeof claims.nbf !== "number") throw new JwtDecodeError({
1038
+ code: JwtDecodeErrorCode.InvalidClaim,
1039
+ message: `${label} "nbf" claim must be a number`
1040
+ });
1041
+ if (claims.iss !== void 0 && typeof claims.iss !== "string") throw new JwtDecodeError({
1042
+ code: JwtDecodeErrorCode.InvalidClaim,
1043
+ message: `${label} "iss" claim must be a string`
1044
+ });
1045
+ if (claims.aud !== void 0 && typeof claims.aud !== "string" && !isStringArray(claims.aud)) throw new JwtDecodeError({
1046
+ code: JwtDecodeErrorCode.InvalidClaim,
1047
+ message: `${label} "aud" claim must be a string or string array`
1048
+ });
1049
+ }
1050
+ function isJsonObject(value) {
1051
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1052
+ }
1053
+ function isStringArray(value) {
1054
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
1055
+ }
1056
+ //#endregion
1057
+ //#region src/scheduling/duration.ts
1058
+ /**
1059
+ * Parse a human-readable duration string (for example `5m` or `300s`) into
1060
+ * milliseconds.
1061
+ */
1062
+ function parseDurationToMs(duration) {
1063
+ const match = duration.match(/^(\d+(?:\.\d+)?)\s*(s|ms|m|h)$/);
1064
+ if (!match) return 0;
1065
+ const value = Number(match[1]);
1066
+ switch (match[2]) {
1067
+ case "ms": return value;
1068
+ case "s": return value * 1e3;
1069
+ case "m": return value * 60 * 1e3;
1070
+ case "h": return value * 60 * 60 * 1e3;
1071
+ default: return 0;
1072
+ }
1073
+ }
1074
+ //#endregion
1075
+ //#region src/storage/json-codec.ts
1076
+ /** Default JSON codec. */
1077
+ function createJsonCodec() {
438
1078
  return {
439
- success: false,
440
- issues: result.issues
1079
+ encode(value) {
1080
+ return JSON.stringify(value);
1081
+ },
1082
+ decode(raw) {
1083
+ return JSON.parse(raw);
1084
+ }
441
1085
  };
442
1086
  }
1087
+ //#endregion
1088
+ //#region src/storage/ephemeral-flow-store.ts
1089
+ function resolveStorageKey(keyPrefix, key) {
1090
+ return keyPrefix.length > 0 ? `${keyPrefix}:${key}` : key;
1091
+ }
1092
+ function requireRecordStoreTake(store) {
1093
+ if (!store.take) throw new ClientError({
1094
+ kind: ClientErrorKind.Configuration,
1095
+ code: "storage.ephemeral.take_unavailable",
1096
+ message: "Ephemeral flow stores require StorageTrait.take() for atomic single-consume semantics",
1097
+ source: "storage.ephemeral"
1098
+ });
1099
+ return store.take.bind(store);
1100
+ }
1101
+ async function executeStorageOperation(operation) {
1102
+ try {
1103
+ return await operation();
1104
+ } catch (error) {
1105
+ if (error instanceof ClientError) throw error;
1106
+ throw new ClientError({
1107
+ kind: ClientErrorKind.Storage,
1108
+ code: "storage.ephemeral.io_failed",
1109
+ message: "The ephemeral flow store operation failed",
1110
+ source: "storage.ephemeral",
1111
+ cause: error
1112
+ });
1113
+ }
1114
+ }
1115
+ function decodeStoredValue(codec, value) {
1116
+ try {
1117
+ return codec.decode(value);
1118
+ } catch (error) {
1119
+ throw new ClientError({
1120
+ kind: ClientErrorKind.Protocol,
1121
+ code: "storage.ephemeral.invalid_payload",
1122
+ message: "The ephemeral flow store payload is invalid",
1123
+ source: "storage.ephemeral",
1124
+ cause: error
1125
+ });
1126
+ }
1127
+ }
443
1128
  /**
444
- * Validate an unknown input synchronously against a `@standard-schema`-compatible schema.
1129
+ * Create a typed ephemeral flow-state store backed by a low-level `StorageTrait`.
445
1130
  *
446
- * Only use this when you know the schema's validate function returns synchronously.
447
- * Throws if the schema returns a Promise.
1131
+ * Intended for short-lived browser/session coordination state such as pending
1132
+ * redirects or callback fragments.
448
1133
  */
449
- function validateWithSchemaSync(schema, input) {
450
- const result = schema["~standard"].validate(input);
451
- if (result instanceof Promise) throw new Error("validateWithSchemaSync: schema returned a Promise. Use validateWithSchema instead.");
452
- if ("value" in result) return {
453
- success: true,
454
- value: result.value
455
- };
1134
+ function createEphemeralFlowStore(options) {
1135
+ const codec = options.codec ?? createJsonCodec();
1136
+ const storeTake = requireRecordStoreTake(options.store);
1137
+ async function load() {
1138
+ const raw = await executeStorageOperation(() => options.store.get(options.key));
1139
+ return raw === null ? null : decodeStoredValue(codec, raw);
1140
+ }
1141
+ async function save(value) {
1142
+ await executeStorageOperation(() => options.store.set(options.key, codec.encode(value)));
1143
+ }
1144
+ async function consume() {
1145
+ const raw = await executeStorageOperation(() => storeTake(options.key));
1146
+ return raw === null ? null : decodeStoredValue(codec, raw);
1147
+ }
1148
+ async function clear() {
1149
+ await executeStorageOperation(() => options.store.remove(options.key));
1150
+ }
456
1151
  return {
457
- success: false,
458
- issues: result.issues
1152
+ load,
1153
+ save,
1154
+ consume,
1155
+ clear
459
1156
  };
460
1157
  }
461
1158
  /**
462
- * Create a minimal `@standard-schema`-compatible schema without requiring
463
- * an external validation library.
464
- *
465
- * This is useful for SDK-internal schemas where pulling in zod/valibot
466
- * would be excessive. For adopter-facing schemas, prefer a real validation
467
- * library.
468
- *
469
- * @example
470
- * ```ts
471
- * import { createSchema, validateWithSchema } from "@securitydept/client";
1159
+ * Create a typed keyed ephemeral flow-state store backed by a low-level
1160
+ * `StorageTrait`.
472
1161
  *
473
- * const NameSchema = createSchema<unknown, { name: string }>({
474
- * validate(input) {
475
- * if (typeof input === "object" && input !== null && "name" in input &&
476
- * typeof (input as any).name === "string") {
477
- * return { value: { name: (input as any).name } };
478
- * }
479
- * return { issues: [{ message: "Expected object with string 'name'" }] };
480
- * },
481
- * });
482
- *
483
- * const result = await validateWithSchema(NameSchema, { name: "Alice" });
484
- * ```
1162
+ * Intended for short-lived browser/session coordination state where multiple
1163
+ * pending records must coexist and be consumed independently.
485
1164
  */
486
- function createSchema(options) {
487
- return { "~standard": {
488
- version: 1,
489
- vendor: "securitydept",
490
- validate: options.validate
491
- } };
1165
+ function createKeyedEphemeralFlowStore(options) {
1166
+ const codec = options.codec ?? createJsonCodec();
1167
+ const storeTake = requireRecordStoreTake(options.store);
1168
+ async function load(key) {
1169
+ const raw = await executeStorageOperation(() => options.store.get(resolveStorageKey(options.keyPrefix, key)));
1170
+ return raw === null ? null : decodeStoredValue(codec, raw);
1171
+ }
1172
+ async function save(key, value) {
1173
+ await executeStorageOperation(() => options.store.set(resolveStorageKey(options.keyPrefix, key), codec.encode(value)));
1174
+ }
1175
+ async function take(key) {
1176
+ const raw = await executeStorageOperation(() => storeTake(resolveStorageKey(options.keyPrefix, key)));
1177
+ return raw === null ? null : decodeStoredValue(codec, raw);
1178
+ }
1179
+ async function clear(key) {
1180
+ await executeStorageOperation(() => options.store.remove(resolveStorageKey(options.keyPrefix, key)));
1181
+ }
1182
+ return {
1183
+ load,
1184
+ save,
1185
+ take,
1186
+ clear
1187
+ };
1188
+ }
1189
+ //#endregion
1190
+ //#region src/struct/async-lock.ts
1191
+ const OnceAsyncLockState = {
1192
+ Init: "init",
1193
+ Running: "running",
1194
+ Success: "success",
1195
+ Error: "error"
1196
+ };
1197
+ function createOnceAsyncLockCallable(fn) {
1198
+ const internalStateSignal = createSignal({ state: OnceAsyncLockState.Init });
1199
+ const coreFn = async function(...args) {
1200
+ const internalState = internalStateSignal.get();
1201
+ if (internalState.state === OnceAsyncLockState.Running) return internalState.promise;
1202
+ if (internalState.state === OnceAsyncLockState.Success) return internalState.data;
1203
+ let resolvePromise;
1204
+ let rejectPromise;
1205
+ const promise = new Promise((res, rej) => {
1206
+ resolvePromise = res;
1207
+ rejectPromise = rej;
1208
+ });
1209
+ promise.catch(() => void 0);
1210
+ internalStateSignal.set({
1211
+ state: OnceAsyncLockState.Running,
1212
+ promise
1213
+ });
1214
+ try {
1215
+ const result = await fn.apply(this, args);
1216
+ internalStateSignal.set({
1217
+ state: OnceAsyncLockState.Success,
1218
+ data: result
1219
+ });
1220
+ resolvePromise(result);
1221
+ return result;
1222
+ } catch (err) {
1223
+ internalStateSignal.set({
1224
+ state: OnceAsyncLockState.Error,
1225
+ error: err
1226
+ });
1227
+ rejectPromise(err);
1228
+ throw err;
1229
+ }
1230
+ };
1231
+ return new Proxy(coreFn, {
1232
+ get(target, prop, receiver) {
1233
+ const internalState = internalStateSignal.get();
1234
+ if (prop in internalState) return Reflect.get(internalState, prop);
1235
+ if (prop === "get" || prop === "watchStream" || prop === SYMBOL_OBSERVABLE) {
1236
+ const value = Reflect.get(internalStateSignal, prop, internalStateSignal);
1237
+ return typeof value === "function" ? value.bind(internalStateSignal) : value;
1238
+ }
1239
+ return Reflect.get(target, prop, receiver);
1240
+ },
1241
+ set() {
1242
+ return false;
1243
+ }
1244
+ });
1245
+ }
1246
+ //#endregion
1247
+ //#region src/transport/url.ts
1248
+ /**
1249
+ * Returns `true` for loopback HTTP URLs that browsers and local tooling often
1250
+ * permit during development flows.
1251
+ */
1252
+ function isLoopbackHttpUrl(url) {
1253
+ return url.protocol === "http:" && (url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1" || url.hostname === "[::1]");
492
1254
  }
493
1255
  //#endregion
494
- export { ClientError, ClientErrorKind, ClientErrorSource, ErrorPresentationTone, EventSourceKind, FetchTransportRedirectKind, LogLevel, OperationTraceEventType, PromiseSettlementKind, UserRecovery, concatMap, createCancellationTokenSource, createComputed, createConsoleLogger, createDefaultClock, createDefaultScheduler, createEphemeralFlowStore, createEventStream, createInMemoryRecordStore, createJsonCodec, createKeyedEphemeralFlowStore, createLinkedCancellationToken, createNoopLogger, createOperationTracer, createReplaySubject, createRuntime, createSchema, createSignal, createSubject, createTraceTimelineStore, debounceTime, exhaustMap, filter, finalize, fromEventPattern, fromPromise, fromRxObservable, fromSignal, interval, map, merge, normalizeAuthenticatedPrincipal, normalizeAuthenticatedPrincipalWire, pipe, projectAuthenticatedPrincipal, readErrorPresentationDescriptor, readonlySignal, scheduleAt, share, shareReplay, switchMap, takeUntil, tap, throttleTime, timer, toRxObservable, validateWithSchema, validateWithSchemaSync, withLatestFromSignal };
1256
+ export { Base64CompatErrorCode, BaseRequirementPlanner, BaseURIStringSchema, ClientError, ClientErrorKind, ClientErrorSource, ENVIRONMENT_TOKEN, ErrorPresentationTone, EventSourceKind, FetchTransportRedirectKind, IDLE_CALLBACK_TRAIT_TOKEN, INJECTOR_TOKEN, IdentityPrincipalSchema, IdentityPrincipalWireSchema, JwtDecodeError, JwtDecodeErrorCode, MergeRequirementPlanner, OnceAsyncLockState, OperationSpan, OperationTraceEventType, PAGE_LIFECYCLE_TRAIT_TOKEN, PERSISTENT_STORAGE_TRAIT_TOKEN, POPUP_TRAIT_TOKEN, PipelineOutcome, PlanStatus, PopupAttachFailureReason, PopupClientWindowHandle, PopupErrorCode, PopupServerWindowHandle, REALM_STORAGE_TRAIT_TOKEN, REQUIREMENT_PLANNER_HOST, ROUTER_TRAIT_TOKEN, RequirementPlannerError, RequirementPlannerHost, RequirementsComposition, ResolutionStatus, ResourceError, ResourceErrorCode, ResourceSnapshotUpdateKind, ResourceStatus, RouteCompositionRequirementPlanner, RouterNavigationIntent, RouterNavigationMode, SECURITYDEPT_COMPAT_FRAGMENT_VERSION, SECURITYDEPT_ROUTE_METADATA_KEY, SESSION_STORAGE_TRAIT_TOKEN, SPAN_TRAIT_TOKEN, SYMBOL_ASYNC_DISPOSE, SYMBOL_DISPOSABLE_STACK, SYMBOL_DISPOSE, SYMBOL_OBSERVABLE, SecurityDeptOptional, SecuritydeptDestroyRef, SecuritydeptInjectionToken, SecuritydeptInjector, SimpleDisposableStack, StaticAttrsAuthRequirement, StaticRequirementPlanner, StdDisposableStack, StorageChangeEventOrigin, TIME_TRAIT_TOKEN, TRACING_TRAIT_TOKEN, TRANSPORT_TRAIT_TOKEN, TracingLevel, TransportErrorCode, UriParseError, UriReferenceString, UriReferenceStringSchema, UriRelativeString, UriRelativeStringSchema, UriString, UriStringSchema, UserRecovery, abortSignalToCancellationToken, abortSignalToEventStream, appendOrReplaceCompatFragment, cancellationTokenToAbortSignal, createAuthorizationInterceptedFetch, createBaseTransportForStdFetch, createCancellationTokenSource, createComputed, createConsoleTracingSubscriber, createDisposableStack, createEmptyEventStream, createEphemeralFlowStore, createEventReplaySubject, createEventStream, createEventSubject, createFoundationEnvironment, createInMemoryRecordStore, createJsonCodec, createJsonRpcClient, createJsonRpcServer, createJsonRpcServerAndClient, createKeyedEphemeralFlowStore, createLinkedCancellationToken, createNeverEventStream, createOnceAsyncLockCallable, createProviderIfTokenMissing, createResource, createRootSpan, createSecuritydeptDestroyRef, createSignal, createTimeForStd, createTraceTimelineStore, createTracing, decodeBase64ToUint8Array, decodeJwtPayload, defaultSelectCandidate, defineInstrumentMethodDecorator, describeError, encodeUint8ArrayToBase64, flattenResourceSnapshot, formatValidationFailure, getSecuritydeptProviderToken, inject, injectDisposableStackFrom, injectRequirementPlannerHost, isCancellationTokenTrait, isCompatFragmentBlock, isInteropObservableTrait, isLoopbackHttpUrl, isRouteBehaviourContextExtra, isRouteStateSnapshotTrait, mapResource, normalizeAbortError, notMissingProvider, parseCompatFragment, parseDurationToMs, parseIdentityPrincipal, parseIdentityPrincipalWire, projectIdentityPrincipal, promisesToRacedAsyncGenerator, provideRequirementPlannerHost, readErrorPresentationDescriptor, readPopupErrorPresentationDescriptor, readSecuritydeptRouteMetadata, readonlySignal, reduceResourceSnapshot, resolveEffectiveRequirements, resourceFromSnapshots, runInInjectionContext, runOperation, takeCompatFragment, takeCompatFragmentFromRouter, throwValidationClientError, toEventStream, tryInjectInInjectionContext, validateTraitInput, validateWithSchema, validateWithSchemaSync, whenResourceSnapshotValue, withDisposableStack, writeSecuritydeptRouteMetadata };
495
1257
 
496
1258
  //# sourceMappingURL=index.mjs.map