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

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 (60) hide show
  1. package/README.md +6 -0
  2. package/dist/create-99Ksgl_O.mjs +952 -0
  3. package/dist/create-99Ksgl_O.mjs.map +1 -0
  4. package/dist/handle-TnxwtQ4T.mjs +634 -0
  5. package/dist/handle-TnxwtQ4T.mjs.map +1 -0
  6. package/dist/index-Cg_iY2yY.d.mts +608 -0
  7. package/dist/index-DmIapvr-.d.mts +1669 -0
  8. package/dist/index-O1A3CdBV.d.mts +119 -0
  9. package/dist/index.d.mts +3 -353
  10. package/dist/index.mjs +1127 -364
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/router-DUh3bhLy.mjs +245 -0
  13. package/dist/router-DUh3bhLy.mjs.map +1 -0
  14. package/dist/rx/index.d.mts +68 -0
  15. package/dist/rx/index.mjs +2 -0
  16. package/dist/rx-B3OU17wu.mjs +1105 -0
  17. package/dist/rx-B3OU17wu.mjs.map +1 -0
  18. package/dist/server/index.d.mts +20 -0
  19. package/dist/server/index.mjs +29 -0
  20. package/dist/server/index.mjs.map +1 -0
  21. package/dist/test/index.d.mts +101 -0
  22. package/dist/test/index.mjs +270 -0
  23. package/dist/test/index.mjs.map +1 -0
  24. package/dist/uri-string-BPNbIMgv.mjs +300 -0
  25. package/dist/uri-string-BPNbIMgv.mjs.map +1 -0
  26. package/dist/web/index.d.mts +93 -363
  27. package/dist/web/index.mjs +335 -428
  28. package/dist/web/index.mjs.map +1 -1
  29. package/dist/webext/index.d.mts +73 -0
  30. package/dist/webext/index.mjs +231 -0
  31. package/dist/webext/index.mjs.map +1 -0
  32. package/package.json +28 -21
  33. package/dist/auth-coordination/index.d.mts +0 -198
  34. package/dist/auth-coordination/index.mjs +0 -107
  35. package/dist/auth-coordination/index.mjs.map +0 -1
  36. package/dist/client-environment-CaumNmir.mjs +0 -812
  37. package/dist/client-environment-CaumNmir.mjs.map +0 -1
  38. package/dist/client-environment-CdHtVxKW.d.mts +0 -401
  39. package/dist/events/index.d.mts +0 -2
  40. package/dist/events/index.mjs +0 -2
  41. package/dist/events-BUlfsRfl.mjs +0 -127
  42. package/dist/events-BUlfsRfl.mjs.map +0 -1
  43. package/dist/index-C4xlKhG7.d.mts +0 -102
  44. package/dist/index-COUt5x9Z.d.mts +0 -39
  45. package/dist/persistence/index.d.mts +0 -3
  46. package/dist/persistence/index.mjs +0 -2
  47. package/dist/persistence/web/index.d.mts +0 -16
  48. package/dist/persistence/web/index.mjs +0 -2
  49. package/dist/persistence-CSB8770k.mjs +0 -108
  50. package/dist/persistence-CSB8770k.mjs.map +0 -1
  51. package/dist/planner-host-BTWie0lo.mjs +0 -197
  52. package/dist/planner-host-BTWie0lo.mjs.map +0 -1
  53. package/dist/planner-host-DN9dRsQx.d.mts +0 -190
  54. package/dist/types-BJqUdH2e.d.mts +0 -32
  55. package/dist/types-BwcnUGNl.d.mts +0 -52
  56. package/dist/web-DQmMqZnR.mjs +0 -51
  57. package/dist/web-DQmMqZnR.mjs.map +0 -1
  58. package/dist/web-router/index.d.mts +0 -259
  59. package/dist/web-router/index.mjs +0 -395
  60. package/dist/web-router/index.mjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,69 +1,274 @@
1
- import { A as interval, B as ClientErrorKind, D as fromEventPattern, E as PromiseSettlementKind, F as createDefaultClock, H as ErrorPresentationTone, I as createOperationTracer, L as LogLevel, M as timer, N as createClientEnvironment, O as fromPromise, P as createDefaultScheduler, R as OperationTraceEventType, U as UserRecovery, V as ClientErrorSource, j as scheduleAt, k as fromSignal, t as ClientEnvironmentPreset, y as FetchTransportRedirectKind, z as ClientError } from "./client-environment-CaumNmir.mjs";
2
- 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-B3OU17wu.mjs";
2
+ import { a as createJsonRpcServerAndClient, c as createSignal, i as createJsonRpcServer, l as readonlySignal, n as PopupServerWindowHandle, o as PopupErrorCode, r as createJsonRpcClient, s as readPopupErrorPresentationDescriptor, t as PopupClientWindowHandle, u as readErrorPresentationDescriptor } from "./handle-TnxwtQ4T.mjs";
3
+ import { A as appendOrReplaceCompatFragment, B as validateWithSchemaSync, C as IDLE_CALLBACK_TRAIT_TOKEN, D as RouterNavigationMode, E as RouterNavigationIntent, F as PopupAttachFailureReason, G as PARENT_INJECTOR_TOKEN, H as EventSourceKind, I as formatValidationFailure, J as SecurityDeptOptional, K as SecuritydeptInjector, L as throwValidationClientError, M as parseCompatFragment, N as takeCompatFragment, O as takeCompatFragmentFromRouter, P as POPUP_TRAIT_TOKEN, Q as notMissingProvider, R as validateTraitInput, S as SPAN_TRAIT_TOKEN, T as ROUTER_TRAIT_TOKEN, U as isEventStreamTrait, V as PAGE_LIFECYCLE_TRAIT_TOKEN, W as INJECTOR_TOKEN, X as createProviderIfTokenMissing, Y as SecuritydeptInjectionToken, Z as getSecuritydeptProviderToken, _ as abortSignalToCancellationToken, a as TracingLevel, b as abortSignalToEventStream, c as PERSISTENT_STORAGE_TRAIT_TOKEN, d as StorageChangeEventOrigin, f as FetchTransportRedirectKind, g as createTimeForStd, h as TransportErrorCode, i as TRACING_TRAIT_TOKEN, j as isCompatFragmentBlock, k as SECURITYDEPT_COMPAT_FRAGMENT_VERSION, l as REALM_STORAGE_TRAIT_TOKEN, m as TRANSPORT_TRAIT_TOKEN, n as ENVIRONMENT_TOKEN, o as createTracing, p as createBaseTransportForStdFetch, q as toRuntimeInjector, r as OperationTraceEventType, s as createInMemoryRecordStore, t as createFoundationEnvironment, u as SESSION_STORAGE_TRAIT_TOKEN, v as cancellationTokenToAbortSignal, w as TIME_TRAIT_TOKEN, x as createRootSpan, y as normalizeAbortError, z as validateWithSchema } from "./create-99Ksgl_O.mjs";
4
+ import { i as UriString, n as UriReferenceString, r as UriRelativeString, t as UriParseError } from "./uri-string-BPNbIMgv.mjs";
5
+ import { v7 } from "uuid";
6
+ import { inject as inject$1, runInInjectionContext as runInInjectionContext$1 } from "injection-js";
7
+ import { EMPTY, NEVER, ReplaySubject, filter, from, merge, take, takeUntil } from "rxjs";
8
+ import { type } from "arktype";
9
+ import { isPromise } from "es-toolkit/predicate";
10
+ //#region src/auth-coordination/contract.ts
11
+ var StaticAttrsAuthRequirement = class StaticAttrsAuthRequirement {
12
+ id;
13
+ label;
14
+ attributes;
15
+ constructor(id, label, attributes) {
16
+ this.id = id;
17
+ this.label = label;
18
+ this.attributes = attributes;
19
+ Object.freeze(this);
14
20
  }
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();
21
+ static create(input) {
22
+ return new StaticAttrsAuthRequirement(input?.id ?? v7(), input?.label, input?.attributes ? Object.freeze(input.attributes) : void 0);
22
23
  }
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
- } };
24
+ static createList(inputs) {
25
+ return inputs.map((input) => StaticAttrsAuthRequirement.create(input));
32
26
  }
33
- throwIfCancellationRequested() {
34
- if (this._isCancelled) throw new ClientError({
35
- kind: ClientErrorKind.Cancelled,
36
- message: "Operation was cancelled",
37
- cause: this._reason
38
- });
27
+ };
28
+ /** Outcome of resolving a single requirement. */
29
+ const ResolutionStatus = {
30
+ Fulfilled: "fulfilled",
31
+ Failed: "failed",
32
+ Skipped: "skipped"
33
+ };
34
+ /** Overall plan status. */
35
+ const PlanStatus = {
36
+ /** There are still pending requirements. */
37
+ Pending: "pending",
38
+ /** All requirements have been resolved. */
39
+ Settled: "settled"
40
+ };
41
+ /** Error thrown when a planner contract is violated (e.g. duplicate ids). */
42
+ var RequirementPlannerError = class extends Error {
43
+ constructor(message) {
44
+ super(message);
45
+ this.name = "RequirementPlannerError";
39
46
  }
40
47
  };
48
+ function isRouteStateSnapshotTrait(value) {
49
+ return typeof value === "object" && value !== null && "url" in value;
50
+ }
51
+ function isRouteBehaviourContextExtra(value) {
52
+ return typeof value === "object" && value !== null && "routeState" in value && isRouteStateSnapshotTrait(value.routeState);
53
+ }
54
+ /** Outcome of a pipeline step or run. */
55
+ const PipelineOutcome = {
56
+ /** No candidates remain — the plan is satisfied. */
57
+ Settled: "settled",
58
+ /** A candidate was resolved as fulfilled; more may remain. */
59
+ Resolved: "resolved",
60
+ /** A candidate's handler blocked progress. */
61
+ Blocked: "blocked",
62
+ /** A candidate's handler requested a redirect. */
63
+ Redirect: "redirect"
64
+ };
41
65
  /**
42
- * Create a `CancellationTokenSource` that produces a cooperative cancellation token.
66
+ * How a route segment's requirements compose with the inherited parent chain.
67
+ *
68
+ * - `inherit` — keep the parent requirements unchanged (segment adds nothing)
69
+ * - `merge` — inherit parent requirements, then append/override with the
70
+ * segment's own (same-id entries replace the parent's)
71
+ * - `replace` — discard the parent chain, use only the segment's requirements
43
72
  */
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
- }
73
+ const RequirementsComposition = {
74
+ Inherit: "inherit",
75
+ Merge: "merge",
76
+ Replace: "replace"
77
+ };
78
+ /**
79
+ * Apply a route requirements declaration onto an effective requirement map.
80
+ *
81
+ * Reuse `effective` across a route chain so merge/replace fold in O(total
82
+ * requirements) without rebuilding parent state each segment. When omitted, a
83
+ * fresh map is created for one-shot composition.
84
+ */
85
+ function resolveEffectiveRequirements(child, effective = /* @__PURE__ */ new Map()) {
86
+ switch (child.composition) {
87
+ case RequirementsComposition.Inherit: return effective;
88
+ case RequirementsComposition.Merge:
89
+ for (const requirement of child.requirements) effective.set(requirement.id, requirement);
90
+ return effective;
91
+ case RequirementsComposition.Replace:
92
+ effective.clear();
93
+ for (const requirement of child.requirements) effective.set(requirement.id, requirement);
94
+ return effective;
95
+ default: {
96
+ const _exhaustive = child.composition;
97
+ throw new RequirementPlannerError(`Unknown requirements composition: ${String(_exhaustive)}`);
62
98
  }
99
+ }
100
+ }
101
+ //#endregion
102
+ //#region src/errors/error-attributes.ts
103
+ /** Extract secret-safe diagnostic attributes without copying runtime messages. */
104
+ function describeError(error) {
105
+ if (error instanceof ClientError) return {
106
+ errorName: error.name,
107
+ errorKind: error.kind,
108
+ errorCode: error.code,
109
+ recovery: error.recovery
63
110
  };
111
+ return { errorName: error instanceof Error ? error.name : typeof error };
112
+ }
113
+ //#endregion
114
+ //#region src/compat/base64.ts
115
+ const Base64CompatErrorCode = {
116
+ EncoderUnavailable: "compat.base64.encoder_unavailable",
117
+ DecoderUnavailable: "compat.base64.decoder_unavailable"
118
+ };
119
+ function encodeUint8ArrayToBase64(value) {
120
+ const toBase64 = globalThis.Uint8Array.prototype.toBase64;
121
+ if (typeof toBase64 === "function") return toBase64.call(value);
122
+ if (typeof globalThis.btoa === "function") {
123
+ let binary = "";
124
+ for (const byte of value) binary += String.fromCharCode(byte);
125
+ return globalThis.btoa(binary);
126
+ }
127
+ const BufferConstructor = Reflect.get(globalThis, "Buffer");
128
+ if (typeof BufferConstructor?.from === "function") return BufferConstructor.from(value).toString("base64");
129
+ throw new ClientError({
130
+ kind: ClientErrorKind.Configuration,
131
+ code: Base64CompatErrorCode.EncoderUnavailable,
132
+ message: "The current realm does not provide a Base64 encoder.",
133
+ source: "compat.base64"
134
+ });
135
+ }
136
+ function decodeBase64ToUint8Array(value) {
137
+ const Uint8ArrayConstructor = globalThis.Uint8Array;
138
+ if (typeof Uint8ArrayConstructor.fromBase64 === "function") return Uint8ArrayConstructor.fromBase64(value);
139
+ if (typeof globalThis.atob === "function") {
140
+ const binary = globalThis.atob(value);
141
+ const bytes = new Uint8Array(binary.length);
142
+ for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
143
+ return bytes;
144
+ }
145
+ const BufferConstructor = Reflect.get(globalThis, "Buffer");
146
+ if (typeof BufferConstructor?.from === "function") return Uint8ArrayConstructor.from(BufferConstructor.from(value, "base64"));
147
+ throw new ClientError({
148
+ kind: ClientErrorKind.Configuration,
149
+ code: Base64CompatErrorCode.DecoderUnavailable,
150
+ message: "The current realm does not provide a Base64 decoder.",
151
+ source: "compat.base64"
152
+ });
153
+ }
154
+ //#endregion
155
+ //#region src/compat/generator.ts
156
+ async function* promisesToRacedAsyncGenerator(promises) {
157
+ const pending = new Map(promises.map((promise, index) => [index, promise.then((value) => ({
158
+ value,
159
+ index
160
+ }))]));
161
+ while (pending.size > 0) {
162
+ const next = await Promise.race(pending.values());
163
+ pending.delete(next.index);
164
+ yield next.value;
165
+ }
64
166
  }
65
167
  //#endregion
66
- //#region src/cancellation/linked-cancellation-token.ts
168
+ //#region src/injection/context.ts
169
+ function inject(token, options) {
170
+ if (options?.optional === true) return inject$1(token, { optional: true });
171
+ return inject$1(token);
172
+ }
173
+ function tryInjectInInjectionContext(token, options) {
174
+ try {
175
+ if (options?.optional === true) return inject(token, { optional: true });
176
+ return inject(token);
177
+ } catch (error) {
178
+ if (isMissingInjectionContextError(error)) return null;
179
+ throw error;
180
+ }
181
+ }
182
+ function runInInjectionContext(injector, fn) {
183
+ return runInInjectionContext$1(toRuntimeInjector(injector), fn);
184
+ }
185
+ function isMissingInjectionContextError(error) {
186
+ return error instanceof Error && error.message.includes("can only be used within an injection context");
187
+ }
188
+ //#endregion
189
+ //#region src/injection/destroy.ts
190
+ var SecuritydeptDestroyRef = class {
191
+ [SYMBOL_DISPOSE]() {
192
+ this.dispose();
193
+ }
194
+ };
195
+ var ManagedSecuritydeptDestroyRef = class extends SecuritydeptDestroyRef {
196
+ destroyedSignal = createSignal(false);
197
+ get destroyed() {
198
+ return this.destroyedSignal.get();
199
+ }
200
+ onDestroy(callback) {
201
+ if (this.destroyedSignal.get()) {
202
+ callback();
203
+ return () => void 0;
204
+ }
205
+ const subscription = from(this.destroyedSignal).pipe(filter((destroyed) => destroyed), take(1)).subscribe({ next: callback });
206
+ return () => {
207
+ subscription.unsubscribe();
208
+ };
209
+ }
210
+ dispose() {
211
+ if (this.destroyedSignal.get()) return;
212
+ this.destroyedSignal.set(true);
213
+ }
214
+ destroy() {
215
+ this.dispose();
216
+ }
217
+ };
218
+ function createSecuritydeptDestroyRef() {
219
+ return new ManagedSecuritydeptDestroyRef();
220
+ }
221
+ //#endregion
222
+ //#region src/validation/schemas/uri.ts
223
+ const UriStringSchema = type("string").narrow((value, context) => UriString.tryParse(value) == null ? context.reject("an absolute URI string") : true);
224
+ const BaseURIStringSchema = UriStringSchema;
225
+ const UriRelativeStringSchema = type("string").narrow((value, context) => UriRelativeString.tryParse(value) == null ? context.reject("a relative URI reference string") : true);
226
+ const UriReferenceStringSchema = type("string").narrow((value, context) => UriReferenceString.tryParse(value) == null ? context.reject("a URI reference string") : true);
227
+ //#endregion
228
+ //#region src/cancellation/linked.ts
229
+ var LinkedCancellationToken = class {
230
+ sources;
231
+ destoryed$ = new ReplaySubject(1);
232
+ token;
233
+ constructor(sources) {
234
+ this.sources = sources;
235
+ if (sources.length === 0) this.token = new CancellationToken();
236
+ if (sources.length === 1) this.token = sources[0];
237
+ for (const source of sources) if (source.isCancellationRequested) {
238
+ this.token = source;
239
+ return;
240
+ }
241
+ const newToken = new CancellationToken();
242
+ merge(...sources).pipe(take(1), takeUntil(this.destoryed$)).subscribe(({ reason }) => {
243
+ newToken._cancel(reason);
244
+ });
245
+ this.token = newToken;
246
+ }
247
+ get isCancellationRequested() {
248
+ return this.token.isCancellationRequested;
249
+ }
250
+ get cancellationError() {
251
+ return this.token.cancellationError;
252
+ }
253
+ get reason() {
254
+ return this.token.reason;
255
+ }
256
+ onCancellationRequested(listener) {
257
+ return this.token.onCancellationRequested(listener);
258
+ }
259
+ throwIfCancellationRequested() {
260
+ this.token.throwIfCancellationRequested();
261
+ }
262
+ dispose() {
263
+ this.destoryed$.next(true);
264
+ }
265
+ [SYMBOL_DISPOSE]() {
266
+ this.dispose();
267
+ }
268
+ [SYMBOL_OBSERVABLE]() {
269
+ return this.token[SYMBOL_OBSERVABLE]();
270
+ }
271
+ };
67
272
  /**
68
273
  * Create a cancellation token that fires when ANY of the given source tokens
69
274
  * is cancelled.
@@ -79,218 +284,210 @@ function createCancellationTokenSource() {
79
284
  * await transport.execute({ ..., cancellationToken: linked });
80
285
  */
81
286
  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;
287
+ return new LinkedCancellationToken(sources.filter((s) => !!s));
91
288
  }
92
289
  //#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
- };
290
+ //#region src/signals/computed.ts
291
+ /**
292
+ * Create a lazy computed signal.
293
+ *
294
+ * Dependencies are collected dynamically while `compute()` runs.
295
+ */
296
+ function createComputed(compute, options) {
297
+ return RxComputedSignal.computed(compute, options);
174
298
  }
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
- };
299
+ //#endregion
300
+ //#region src/signals/resource.ts
301
+ function createResource(options) {
302
+ return rxResource({
303
+ ...options,
304
+ stream: (context) => from(options.stream(context))
305
+ });
207
306
  }
208
- function isErrorPresentation(value) {
209
- return typeof value === "object" && value !== null && typeof value.code === "string" && typeof value.message === "string" && typeof value.recovery === "string";
307
+ //#endregion
308
+ //#region src/router/metadata.ts
309
+ /** Route-data key holding the SecurityDept metadata blob for a segment. */
310
+ const SECURITYDEPT_ROUTE_METADATA_KEY = "__securitydept__";
311
+ /** Read SecurityDept route metadata from a route `data` bag. */
312
+ function readSecuritydeptRouteMetadata(data) {
313
+ const raw = data?.[SECURITYDEPT_ROUTE_METADATA_KEY];
314
+ if (raw === void 0 || raw === null) return;
315
+ return raw;
210
316
  }
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
317
+ /** Merge SecurityDept route metadata into a route `data` bag. */
318
+ function writeSecuritydeptRouteMetadata(base, patch) {
319
+ const data = { ...base ?? {} };
320
+ const previous = data["__securitydept__"] ?? {};
321
+ data[SECURITYDEPT_ROUTE_METADATA_KEY] = {
322
+ ...previous,
323
+ ...patch
219
324
  };
325
+ return data;
220
326
  }
221
327
  //#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)
328
+ //#region src/std/transport/authorization-interceptor.ts
329
+ function createAuthorizationInterceptedFetch(fetchImpl, options) {
330
+ return async (input, init) => {
331
+ if (!await options.predicate(input, init)) return fetchImpl(input, init);
332
+ const authorization = "whenValue" in options.authorization ? await options.authorization.whenValue() : options.authorization.get();
333
+ if (!authorization) return fetchImpl(input, init);
334
+ const headers = new Headers(typeof Request !== "undefined" && input instanceof Request ? input.headers : void 0);
335
+ if (init?.headers) new Headers(init.headers).forEach((value, key) => {
336
+ headers.set(key, value);
337
+ });
338
+ headers.set("authorization", authorization);
339
+ return fetchImpl(input, {
340
+ ...init,
341
+ headers
342
+ });
241
343
  };
242
344
  }
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)
345
+ //#endregion
346
+ //#region src/tracing/operation-runner.ts
347
+ var OperationSpan = class OperationSpan {
348
+ _span;
349
+ _environment;
350
+ _name;
351
+ _target;
352
+ constructor(_span, _environment, _name, _target) {
353
+ this._span = _span;
354
+ this._environment = _environment;
355
+ this._name = _name;
356
+ this._target = _target;
357
+ }
358
+ static start(options) {
359
+ const operationSpan = new OperationSpan(options.span.fork({
360
+ mutable: true,
361
+ idFactory: options.idFactory,
362
+ attributes: options.fields
363
+ }), options.environment, options.name, options.target);
364
+ operationSpan.recordStarted();
365
+ return operationSpan;
366
+ }
367
+ get id() {
368
+ return this._span.id;
369
+ }
370
+ get parent() {
371
+ return this._span.parent;
372
+ }
373
+ get attributes() {
374
+ return this._span.attributes;
375
+ }
376
+ fork(options = {}) {
377
+ return this._span.fork(options);
378
+ }
379
+ setAttributes(attributes) {
380
+ this._span.setAttributes(attributes);
381
+ }
382
+ addEvent(name, fields) {
383
+ this._record(OperationTraceEventType.Event, TracingLevel.Info, {
384
+ eventName: name,
385
+ ...fields ?? {}
386
+ });
387
+ }
388
+ recordError(error, fields) {
389
+ this._record(OperationTraceEventType.Error, TracingLevel.Error, {
390
+ ...describeError(error),
391
+ ...fields ?? {}
392
+ });
393
+ }
394
+ recordStarted() {
395
+ this._record(OperationTraceEventType.Started, TracingLevel.Info);
396
+ }
397
+ recordEnded(outcome) {
398
+ this._record(OperationTraceEventType.Ended, outcome === "succeeded" ? TracingLevel.Info : TracingLevel.Error, { outcome });
399
+ }
400
+ _record(name, level, fields) {
401
+ this._environment.tracing.record({
402
+ name,
403
+ at: this._environment.time.now(),
404
+ span: this,
405
+ level,
406
+ target: this._target,
407
+ fields: {
408
+ operationName: this._name,
409
+ ...this.attributes,
410
+ ...fields ?? {}
411
+ }
412
+ });
413
+ }
414
+ };
415
+ function runOperation(options) {
416
+ const operationSpan = OperationSpan.start(options);
417
+ const complete = (result) => {
418
+ operationSpan.recordEnded("succeeded");
419
+ return result;
420
+ };
421
+ const fail = (error) => {
422
+ const normalizedError = options.normalizeError?.(error) ?? error;
423
+ operationSpan.recordError(normalizedError);
424
+ operationSpan.recordEnded("failed");
425
+ throw normalizedError;
251
426
  };
427
+ const invoke = () => options.execute(operationSpan);
428
+ try {
429
+ const result = invoke();
430
+ if (isPromise(result)) return result.then(complete, fail);
431
+ return complete(result);
432
+ } catch (error) {
433
+ return fail(error);
434
+ }
252
435
  }
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
436
+ //#endregion
437
+ //#region src/tracing/operation-method.ts
438
+ function defineInstrumentMethodDecorator(factory) {
439
+ return (...factoryArgs) => (target, context) => {
440
+ const methodName = String(context.name);
441
+ const resolve = factory({
442
+ factoryArgs,
443
+ methodName
444
+ });
445
+ return function(...args) {
446
+ const resolvedOptions = resolve.call(this, {
447
+ args,
448
+ methodName
449
+ });
450
+ return runOperation({
451
+ environment: resolvedOptions.environment,
452
+ span: resolvedOptions.span,
453
+ name: resolvedOptions.name ?? methodName,
454
+ target: resolvedOptions.target,
455
+ fields: resolvedOptions.fields,
456
+ idFactory: resolvedOptions.idFactory,
457
+ normalizeError: resolvedOptions.normalizeError,
458
+ execute: (span) => target.call(this, ...args, span)
459
+ });
460
+ };
262
461
  };
263
462
  }
264
463
  //#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) {
464
+ //#region src/tracing/subscriber/console.ts
465
+ function createConsoleTracingSubscriber() {
466
+ return { record(event) {
467
+ const label = `[${event.target}] ${event.name}`;
468
+ switch (event.level) {
271
469
  case "debug":
272
- console.debug(label, entry.message, entry.attributes ?? "");
470
+ console.debug(label, event.fields ?? {});
273
471
  break;
274
472
  case "info":
275
- console.info(label, entry.message, entry.attributes ?? "");
473
+ console.info(label, event.fields ?? {});
276
474
  break;
277
475
  case "warn":
278
- console.warn(label, entry.message, entry.attributes ?? "");
279
- break;
280
- case "error":
281
- console.error(label, entry.message, entry.error ?? "", entry.attributes ?? "");
476
+ console.warn(label, event.fields ?? {});
282
477
  break;
478
+ case "error": console.error(label, event.fields ?? {});
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,731 @@ 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.
338
- *
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.
535
+ * Default candidate selector: first unauthenticated candidate as soon as it is
536
+ * discovered.
344
537
  *
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
+ const preserved = previousPlan.resolutionList.filter((resolution) => sharedIds.has(resolution.requirementId));
821
+ return new RouteCompositionRequirementPlanner(host, currentSegments, routeState, preserved);
822
+ }
823
+ get planContext() {
824
+ return this._planContext;
825
+ }
826
+ /** The matched route chain backing this planner. */
827
+ get segments() {
828
+ return this._segments;
829
+ }
830
+ /** The folded effective requirement list (composition applied). */
831
+ get effectiveRequirements() {
832
+ return this._effective;
833
+ }
834
+ buildRequirementList() {
835
+ return new Map(this._effective.map((requirement) => [requirement.id, requirement]));
836
+ }
837
+ };
838
+ //#endregion
839
+ //#region src/auth-coordination/planner/static-planner.ts
840
+ /**
841
+ * A planner whose effective requirement list is a fixed array supplied at
842
+ * construction time.
417
843
  *
418
844
  * @example
419
845
  * ```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
- * }
846
+ * const planner = StaticRequirementPlanner.fromRequirements(host, [
847
+ * StaticAttrsAuthRequirement.create({ id: "session" }),
848
+ * ] as Readonly<AuthRequirement[]>);
849
+ * const plan = await planner.buildPlan();
850
+ * const step = await planner.runStep(plan);
851
+ * // or: const result = await planner.runUntilSettled();
430
852
  * ```
431
853
  */
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
854
+ var StaticRequirementPlanner = class StaticRequirementPlanner extends BaseRequirementPlanner {
855
+ staticRequirements;
856
+ get planContext() {
857
+ return {};
858
+ }
859
+ constructor(host, requirements) {
860
+ super(host);
861
+ this.staticRequirements = requirements;
862
+ }
863
+ /** Create a static planner from an ordered requirement list. */
864
+ static fromRequirements(host, requirements) {
865
+ return new StaticRequirementPlanner(host, requirements);
866
+ }
867
+ buildRequirementList() {
868
+ const map = /* @__PURE__ */ new Map();
869
+ for (const requirement of this.staticRequirements) map.set(requirement.id, requirement);
870
+ return map;
871
+ }
872
+ };
873
+ //#endregion
874
+ //#region src/events/create.ts
875
+ /**
876
+ * Create a new event stream from a producer function.
877
+ *
878
+ * The producer receives an observer and should call `next()` to emit values,
879
+ * `error()` to signal an error, and `complete()` to signal completion.
880
+ * It may return a teardown function that will be called on unsubscribe.
881
+ */
882
+ function createEventStream(producer) {
883
+ return new RxEventStream(producer);
884
+ }
885
+ function createEventSubject() {
886
+ return new RxEventSubject();
887
+ }
888
+ function createEventReplaySubject(bufferSize = Infinity) {
889
+ return new RxEventReplaySubject(bufferSize);
890
+ }
891
+ function createEmptyEventStream() {
892
+ return RxEventStream.fromObservableInput(EMPTY);
893
+ }
894
+ function createNeverEventStream() {
895
+ return RxEventStream.fromObservableInput(NEVER);
896
+ }
897
+ //#endregion
898
+ //#region src/events/interop.ts
899
+ function toEventStream(input) {
900
+ if (isEventStreamTrait(input)) return input;
901
+ return RxEventStream.fromObservableInput(from(input));
902
+ }
903
+ //#endregion
904
+ //#region src/identity/schemas.ts
905
+ const IdentityPrincipalSchema = type({
906
+ subject: "string > 0",
907
+ "displayName?": "string | null | undefined",
908
+ "picture?": "string | null | undefined",
909
+ "issuer?": "string | null | undefined",
910
+ "claims?": "object | null | undefined"
911
+ }).pipe((input) => ({
912
+ subject: input.subject,
913
+ displayName: typeof input.displayName === "string" && input.displayName.trim().length > 0 ? input.displayName : input.subject,
914
+ picture: input.picture ?? void 0,
915
+ issuer: input.issuer ?? void 0,
916
+ claims: input.claims ?? void 0
917
+ }));
918
+ const IdentityPrincipalWireSchema = type({
919
+ subject: "string > 0",
920
+ "display_name?": "string | null | undefined",
921
+ "picture?": "string | null | undefined",
922
+ "issuer?": "string | null | undefined",
923
+ "claims?": "object | null | undefined"
924
+ }).pipe((input) => ({
925
+ subject: input.subject,
926
+ displayName: typeof input.display_name === "string" && input.display_name.trim().length > 0 ? input.display_name : input.subject,
927
+ picture: input.picture ?? void 0,
928
+ issuer: input.issuer ?? void 0,
929
+ claims: input.claims ?? void 0
930
+ }));
931
+ //#endregion
932
+ //#region src/identity/parsers.ts
933
+ function throwIdentityPrincipalParseError(options) {
934
+ throw new ClientError({
935
+ kind: ClientErrorKind.Protocol,
936
+ code: options.code,
937
+ message: `${options.messagePrefix}: ${formatValidationFailure(options.failure) ?? "schema validation failed"}.`,
938
+ source: "identity",
939
+ cause: options.failure.issues
940
+ });
941
+ }
942
+ function parseIdentityPrincipal(input) {
943
+ const result = validateWithSchemaSync(IdentityPrincipalSchema, input);
944
+ if (result.success) return result.value;
945
+ throwIdentityPrincipalParseError({
946
+ code: "identity.invalid_principal",
947
+ messagePrefix: "Identity principal payload is invalid",
948
+ failure: result
949
+ });
950
+ }
951
+ function parseIdentityPrincipalWire(input) {
952
+ const result = validateWithSchemaSync(IdentityPrincipalWireSchema, input);
953
+ if (result.success) return result.value;
954
+ throwIdentityPrincipalParseError({
955
+ code: "identity.invalid_principal_wire",
956
+ messagePrefix: "Identity principal wire payload is invalid",
957
+ failure: result
958
+ });
959
+ }
960
+ function projectIdentityPrincipal(options) {
961
+ const principal = options.principal;
962
+ const subject = principal?.subject ?? options.fallbackSubject ?? "context.anonymous";
963
+ return {
964
+ subject,
965
+ displayName: principal?.displayName?.trim() || options.fallbackDisplayName || subject,
966
+ picture: principal?.picture,
967
+ issuer: principal?.issuer ?? options.fallbackIssuer,
968
+ claims: principal?.claims ?? options.fallbackClaims
437
969
  };
970
+ }
971
+ //#endregion
972
+ //#region src/protocol/jwt/error.ts
973
+ const JwtDecodeErrorCode = {
974
+ InvalidFormat: "protocol.jwt.invalid_format",
975
+ InvalidPayload: "protocol.jwt.invalid_payload",
976
+ InvalidClaim: "protocol.jwt.invalid_claim"
977
+ };
978
+ var JwtDecodeError = class extends ClientError {
979
+ code;
980
+ constructor(options) {
981
+ super({
982
+ kind: ClientErrorKind.Protocol,
983
+ code: options.code,
984
+ message: options.message,
985
+ source: "protocol.jwt",
986
+ cause: options.cause
987
+ });
988
+ this.name = "JwtDecodeError";
989
+ this.code = options.code;
990
+ }
991
+ };
992
+ //#endregion
993
+ //#region src/protocol/jwt/decode.ts
994
+ function decodeJwtPayload(jwt, options = {}) {
995
+ const label = options.label ?? "JWT";
996
+ const parts = jwt.split(".");
997
+ if (parts.length !== 3) throw new JwtDecodeError({
998
+ code: JwtDecodeErrorCode.InvalidFormat,
999
+ message: `${label} must use compact JWS serialization`
1000
+ });
1001
+ const claims = parseBase64UrlJsonObject(parts[1], `${label} payload`);
1002
+ validateRegisteredClaimTypes(claims, label);
1003
+ return claims;
1004
+ }
1005
+ function parseBase64UrlJsonObject(value, label) {
1006
+ let parsed;
1007
+ try {
1008
+ parsed = JSON.parse(decodeBase64UrlUtf8(value));
1009
+ } catch (cause) {
1010
+ throw new JwtDecodeError({
1011
+ code: JwtDecodeErrorCode.InvalidPayload,
1012
+ message: `${label} must be base64url-encoded JSON`,
1013
+ cause
1014
+ });
1015
+ }
1016
+ if (!isJsonObject(parsed)) throw new JwtDecodeError({
1017
+ code: JwtDecodeErrorCode.InvalidPayload,
1018
+ message: `${label} must be a JSON object`
1019
+ });
1020
+ return parsed;
1021
+ }
1022
+ function decodeBase64UrlUtf8(value) {
1023
+ const base64 = value.replace(/-/g, "+").replace(/_/g, "/");
1024
+ const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
1025
+ const binary = atob(padded);
1026
+ const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
1027
+ return new TextDecoder().decode(bytes);
1028
+ }
1029
+ function validateRegisteredClaimTypes(claims, label) {
1030
+ if (claims.exp !== void 0 && typeof claims.exp !== "number") throw new JwtDecodeError({
1031
+ code: JwtDecodeErrorCode.InvalidClaim,
1032
+ message: `${label} "exp" claim must be a number`
1033
+ });
1034
+ if (claims.iat !== void 0 && typeof claims.iat !== "number") throw new JwtDecodeError({
1035
+ code: JwtDecodeErrorCode.InvalidClaim,
1036
+ message: `${label} "iat" claim must be a number`
1037
+ });
1038
+ if (claims.nbf !== void 0 && typeof claims.nbf !== "number") throw new JwtDecodeError({
1039
+ code: JwtDecodeErrorCode.InvalidClaim,
1040
+ message: `${label} "nbf" claim must be a number`
1041
+ });
1042
+ if (claims.iss !== void 0 && typeof claims.iss !== "string") throw new JwtDecodeError({
1043
+ code: JwtDecodeErrorCode.InvalidClaim,
1044
+ message: `${label} "iss" claim must be a string`
1045
+ });
1046
+ if (claims.aud !== void 0 && typeof claims.aud !== "string" && !isStringArray(claims.aud)) throw new JwtDecodeError({
1047
+ code: JwtDecodeErrorCode.InvalidClaim,
1048
+ message: `${label} "aud" claim must be a string or string array`
1049
+ });
1050
+ }
1051
+ function isJsonObject(value) {
1052
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1053
+ }
1054
+ function isStringArray(value) {
1055
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
1056
+ }
1057
+ //#endregion
1058
+ //#region src/scheduling/duration.ts
1059
+ /**
1060
+ * Parse a human-readable duration string (for example `5m` or `300s`) into
1061
+ * milliseconds.
1062
+ */
1063
+ function parseDurationToMs(duration) {
1064
+ const match = duration.match(/^(\d+(?:\.\d+)?)\s*(s|ms|m|h)$/);
1065
+ if (!match) return 0;
1066
+ const value = Number(match[1]);
1067
+ switch (match[2]) {
1068
+ case "ms": return value;
1069
+ case "s": return value * 1e3;
1070
+ case "m": return value * 60 * 1e3;
1071
+ case "h": return value * 60 * 60 * 1e3;
1072
+ default: return 0;
1073
+ }
1074
+ }
1075
+ //#endregion
1076
+ //#region src/storage/json-codec.ts
1077
+ /** Default JSON codec. */
1078
+ function createJsonCodec() {
438
1079
  return {
439
- success: false,
440
- issues: result.issues
1080
+ encode(value) {
1081
+ return JSON.stringify(value);
1082
+ },
1083
+ decode(raw) {
1084
+ return JSON.parse(raw);
1085
+ }
441
1086
  };
442
1087
  }
1088
+ //#endregion
1089
+ //#region src/storage/ephemeral-flow-store.ts
1090
+ function resolveStorageKey(keyPrefix, key) {
1091
+ return keyPrefix.length > 0 ? `${keyPrefix}:${key}` : key;
1092
+ }
1093
+ function requireRecordStoreTake(store) {
1094
+ if (!store.take) throw new ClientError({
1095
+ kind: ClientErrorKind.Configuration,
1096
+ code: "storage.ephemeral.take_unavailable",
1097
+ message: "Ephemeral flow stores require StorageTrait.take() for atomic single-consume semantics",
1098
+ source: "storage.ephemeral"
1099
+ });
1100
+ return store.take.bind(store);
1101
+ }
1102
+ async function executeStorageOperation(operation) {
1103
+ try {
1104
+ return await operation();
1105
+ } catch (error) {
1106
+ if (error instanceof ClientError) throw error;
1107
+ throw new ClientError({
1108
+ kind: ClientErrorKind.Storage,
1109
+ code: "storage.ephemeral.io_failed",
1110
+ message: "The ephemeral flow store operation failed",
1111
+ source: "storage.ephemeral",
1112
+ cause: error
1113
+ });
1114
+ }
1115
+ }
1116
+ function decodeStoredValue(codec, value) {
1117
+ try {
1118
+ return codec.decode(value);
1119
+ } catch (error) {
1120
+ throw new ClientError({
1121
+ kind: ClientErrorKind.Protocol,
1122
+ code: "storage.ephemeral.invalid_payload",
1123
+ message: "The ephemeral flow store payload is invalid",
1124
+ source: "storage.ephemeral",
1125
+ cause: error
1126
+ });
1127
+ }
1128
+ }
443
1129
  /**
444
- * Validate an unknown input synchronously against a `@standard-schema`-compatible schema.
1130
+ * Create a typed ephemeral flow-state store backed by a low-level `StorageTrait`.
445
1131
  *
446
- * Only use this when you know the schema's validate function returns synchronously.
447
- * Throws if the schema returns a Promise.
1132
+ * Intended for short-lived browser/session coordination state such as pending
1133
+ * redirects or callback fragments.
448
1134
  */
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
- };
1135
+ function createEphemeralFlowStore(options) {
1136
+ const codec = options.codec ?? createJsonCodec();
1137
+ const storeTake = requireRecordStoreTake(options.store);
1138
+ async function load() {
1139
+ const raw = await executeStorageOperation(() => options.store.get(options.key));
1140
+ return raw === null ? null : decodeStoredValue(codec, raw);
1141
+ }
1142
+ async function save(value) {
1143
+ await executeStorageOperation(() => options.store.set(options.key, codec.encode(value)));
1144
+ }
1145
+ async function consume() {
1146
+ const raw = await executeStorageOperation(() => storeTake(options.key));
1147
+ return raw === null ? null : decodeStoredValue(codec, raw);
1148
+ }
1149
+ async function clear() {
1150
+ await executeStorageOperation(() => options.store.remove(options.key));
1151
+ }
456
1152
  return {
457
- success: false,
458
- issues: result.issues
1153
+ load,
1154
+ save,
1155
+ consume,
1156
+ clear
459
1157
  };
460
1158
  }
461
1159
  /**
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";
1160
+ * Create a typed keyed ephemeral flow-state store backed by a low-level
1161
+ * `StorageTrait`.
472
1162
  *
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
- * ```
1163
+ * Intended for short-lived browser/session coordination state where multiple
1164
+ * pending records must coexist and be consumed independently.
485
1165
  */
486
- function createSchema(options) {
487
- return { "~standard": {
488
- version: 1,
489
- vendor: "securitydept",
490
- validate: options.validate
491
- } };
1166
+ function createKeyedEphemeralFlowStore(options) {
1167
+ const codec = options.codec ?? createJsonCodec();
1168
+ const storeTake = requireRecordStoreTake(options.store);
1169
+ async function load(key) {
1170
+ const raw = await executeStorageOperation(() => options.store.get(resolveStorageKey(options.keyPrefix, key)));
1171
+ return raw === null ? null : decodeStoredValue(codec, raw);
1172
+ }
1173
+ async function save(key, value) {
1174
+ await executeStorageOperation(() => options.store.set(resolveStorageKey(options.keyPrefix, key), codec.encode(value)));
1175
+ }
1176
+ async function take(key) {
1177
+ const raw = await executeStorageOperation(() => storeTake(resolveStorageKey(options.keyPrefix, key)));
1178
+ return raw === null ? null : decodeStoredValue(codec, raw);
1179
+ }
1180
+ async function clear(key) {
1181
+ await executeStorageOperation(() => options.store.remove(resolveStorageKey(options.keyPrefix, key)));
1182
+ }
1183
+ return {
1184
+ load,
1185
+ save,
1186
+ take,
1187
+ clear
1188
+ };
1189
+ }
1190
+ //#endregion
1191
+ //#region src/struct/async-lock.ts
1192
+ const OnceAsyncLockState = {
1193
+ Init: "init",
1194
+ Running: "running",
1195
+ Success: "success",
1196
+ Error: "error"
1197
+ };
1198
+ function createOnceAsyncLockCallable(fn) {
1199
+ const internalStateSignal = createSignal({ state: OnceAsyncLockState.Init });
1200
+ const coreFn = async function(...args) {
1201
+ const internalState = internalStateSignal.get();
1202
+ if (internalState.state === OnceAsyncLockState.Running) return internalState.promise;
1203
+ if (internalState.state === OnceAsyncLockState.Success) return internalState.data;
1204
+ let resolvePromise;
1205
+ let rejectPromise;
1206
+ const promise = new Promise((res, rej) => {
1207
+ resolvePromise = res;
1208
+ rejectPromise = rej;
1209
+ });
1210
+ promise.catch(() => void 0);
1211
+ internalStateSignal.set({
1212
+ state: OnceAsyncLockState.Running,
1213
+ promise
1214
+ });
1215
+ try {
1216
+ const result = await fn.apply(this, args);
1217
+ internalStateSignal.set({
1218
+ state: OnceAsyncLockState.Success,
1219
+ data: result
1220
+ });
1221
+ resolvePromise(result);
1222
+ return result;
1223
+ } catch (err) {
1224
+ internalStateSignal.set({
1225
+ state: OnceAsyncLockState.Error,
1226
+ error: err
1227
+ });
1228
+ rejectPromise(err);
1229
+ throw err;
1230
+ }
1231
+ };
1232
+ return new Proxy(coreFn, {
1233
+ get(target, prop, receiver) {
1234
+ const internalState = internalStateSignal.get();
1235
+ if (prop in internalState) return Reflect.get(internalState, prop);
1236
+ if (prop === "get" || prop === "watchStream" || prop === SYMBOL_OBSERVABLE) {
1237
+ const value = Reflect.get(internalStateSignal, prop, internalStateSignal);
1238
+ return typeof value === "function" ? value.bind(internalStateSignal) : value;
1239
+ }
1240
+ return Reflect.get(target, prop, receiver);
1241
+ },
1242
+ set() {
1243
+ return false;
1244
+ }
1245
+ });
1246
+ }
1247
+ //#endregion
1248
+ //#region src/transport/url.ts
1249
+ /**
1250
+ * Returns `true` for loopback HTTP URLs that browsers and local tooling often
1251
+ * permit during development flows.
1252
+ */
1253
+ function isLoopbackHttpUrl(url) {
1254
+ return url.protocol === "http:" && (url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1" || url.hostname === "[::1]");
492
1255
  }
493
1256
  //#endregion
494
- export { ClientEnvironmentPreset, ClientError, ClientErrorKind, ClientErrorSource, ErrorPresentationTone, EventSourceKind, FetchTransportRedirectKind, LogLevel, OperationTraceEventType, PromiseSettlementKind, UserRecovery, concatMap, createCancellationTokenSource, createClientEnvironment, createComputed, createConsoleLogger, createDefaultClock, createDefaultScheduler, createEphemeralFlowStore, createEventStream, createInMemoryRecordStore, createJsonCodec, createKeyedEphemeralFlowStore, createLinkedCancellationToken, createNoopLogger, createOperationTracer, createReplaySubject, createSchema, createSignal, createSubject, createTraceTimelineStore, debounceTime, exhaustMap, filter, finalize, fromEventPattern, fromPromise, fromRxObservable, fromSignal, interval, map, merge, normalizeAuthenticatedPrincipal, normalizeAuthenticatedPrincipalWire, pipe, projectAuthenticatedPrincipal, readErrorPresentationDescriptor, readonlySignal, scheduleAt, share, shareReplay, switchMap, takeUntil, tap, throttleTime, timer, toRxObservable, validateWithSchema, validateWithSchemaSync, withLatestFromSignal };
1257
+ 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
1258
 
496
1259
  //# sourceMappingURL=index.mjs.map