@securitydept/client 0.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth-coordination/index.d.mts +198 -0
- package/dist/auth-coordination/index.mjs +107 -0
- package/dist/auth-coordination/index.mjs.map +1 -0
- package/dist/fetch-transport-CMG4tfDu.d.mts +368 -0
- package/dist/fetch-transport-DtnSRWAH.mjs +631 -0
- package/dist/fetch-transport-DtnSRWAH.mjs.map +1 -0
- package/dist/index-XkwmYaJF.d.mts +39 -0
- package/dist/index.d.mts +426 -0
- package/dist/index.mjs +661 -0
- package/dist/index.mjs.map +1 -0
- package/dist/persistence/index.d.mts +3 -0
- package/dist/persistence/index.mjs +2 -0
- package/dist/persistence/web/index.d.mts +16 -0
- package/dist/persistence/web/index.mjs +51 -0
- package/dist/persistence/web/index.mjs.map +1 -0
- package/dist/persistence-Db2CIFbx.mjs +108 -0
- package/dist/persistence-Db2CIFbx.mjs.map +1 -0
- package/dist/planner-host-CcEpcHws.mjs +195 -0
- package/dist/planner-host-CcEpcHws.mjs.map +1 -0
- package/dist/planner-host-DUZCkLj3.d.mts +190 -0
- package/dist/types-DKluXsWS.d.mts +52 -0
- package/dist/web/index.d.mts +329 -0
- package/dist/web/index.mjs +292 -0
- package/dist/web/index.mjs.map +1 -0
- package/dist/web-router/index.d.mts +259 -0
- package/dist/web-router/index.mjs +395 -0
- package/dist/web-router/index.mjs.map +1 -0
- package/package.json +48 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,661 @@
|
|
|
1
|
+
import { C as ClientErrorSource, S as ClientErrorKind, T as UserRecovery, _ as createDefaultClock, b as OperationTraceEventType, c as PromiseSettlementKind, d as fromSignal, f as interval, g as createDefaultScheduler, h as createRuntime, l as fromEventPattern, m as timer, p as scheduleAt, t as FetchTransportRedirectKind, u as fromPromise, v as createOperationTracer, w as ErrorPresentationTone, x as ClientError, y as LogLevel } from "./fetch-transport-DtnSRWAH.mjs";
|
|
2
|
+
import { i as createJsonCodec, n as createEphemeralFlowStore, r as createKeyedEphemeralFlowStore, t as createInMemoryRecordStore } from "./persistence-Db2CIFbx.mjs";
|
|
3
|
+
//#region src/cancellation/cancellation-token.ts
|
|
4
|
+
var CancellationToken = class {
|
|
5
|
+
_isCancelled = false;
|
|
6
|
+
_reason;
|
|
7
|
+
_listeners = /* @__PURE__ */ new Set();
|
|
8
|
+
get isCancellationRequested() {
|
|
9
|
+
return this._isCancelled;
|
|
10
|
+
}
|
|
11
|
+
get reason() {
|
|
12
|
+
return this._reason;
|
|
13
|
+
}
|
|
14
|
+
/** @internal — called by `CancellationTokenSource`. */
|
|
15
|
+
_cancel(reason) {
|
|
16
|
+
if (this._isCancelled) return;
|
|
17
|
+
this._isCancelled = true;
|
|
18
|
+
this._reason = reason;
|
|
19
|
+
for (const listener of this._listeners) listener(reason);
|
|
20
|
+
this._listeners.clear();
|
|
21
|
+
}
|
|
22
|
+
onCancellationRequested(listener) {
|
|
23
|
+
if (this._isCancelled) {
|
|
24
|
+
listener(this._reason);
|
|
25
|
+
return { dispose() {} };
|
|
26
|
+
}
|
|
27
|
+
this._listeners.add(listener);
|
|
28
|
+
return { dispose: () => {
|
|
29
|
+
this._listeners.delete(listener);
|
|
30
|
+
} };
|
|
31
|
+
}
|
|
32
|
+
throwIfCancellationRequested() {
|
|
33
|
+
if (this._isCancelled) throw new ClientError({
|
|
34
|
+
kind: ClientErrorKind.Cancelled,
|
|
35
|
+
message: "Operation was cancelled",
|
|
36
|
+
cause: this._reason
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Create a `CancellationTokenSource` that produces a cooperative cancellation token.
|
|
42
|
+
*/
|
|
43
|
+
function createCancellationTokenSource() {
|
|
44
|
+
const ct = new CancellationToken();
|
|
45
|
+
let disposed = false;
|
|
46
|
+
return {
|
|
47
|
+
get token() {
|
|
48
|
+
return ct;
|
|
49
|
+
},
|
|
50
|
+
cancel(reason) {
|
|
51
|
+
if (!disposed) ct._cancel(reason);
|
|
52
|
+
},
|
|
53
|
+
dispose() {
|
|
54
|
+
if (!disposed) {
|
|
55
|
+
disposed = true;
|
|
56
|
+
ct._cancel(new ClientError({
|
|
57
|
+
kind: ClientErrorKind.Cancelled,
|
|
58
|
+
message: "Disposed"
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/cancellation/linked-cancellation-token.ts
|
|
66
|
+
/**
|
|
67
|
+
* Create a cancellation token that fires when ANY of the given source tokens
|
|
68
|
+
* is cancelled.
|
|
69
|
+
*
|
|
70
|
+
* - Zero sources → returns a token that is never cancelled.
|
|
71
|
+
* - One source → returns that source directly (no allocation).
|
|
72
|
+
* - N sources → creates a linked source, subscribes to all, and cleans up
|
|
73
|
+
* subscriptions once the linked token fires.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* const cts = createCancellationTokenSource();
|
|
77
|
+
* const linked = createLinkedCancellationToken(clientRoot.token, cts.token);
|
|
78
|
+
* await transport.execute({ ..., cancellationToken: linked });
|
|
79
|
+
*/
|
|
80
|
+
function createLinkedCancellationToken(...sources) {
|
|
81
|
+
if (sources.length === 0) return createCancellationTokenSource().token;
|
|
82
|
+
if (sources.length === 1) return sources[0];
|
|
83
|
+
for (const source of sources) if (source.isCancellationRequested) return source;
|
|
84
|
+
const linked = createCancellationTokenSource();
|
|
85
|
+
const subscriptions = sources.map((source) => source.onCancellationRequested((reason) => linked.cancel(reason)));
|
|
86
|
+
linked.token.onCancellationRequested(() => {
|
|
87
|
+
for (const sub of subscriptions) sub.dispose();
|
|
88
|
+
});
|
|
89
|
+
return linked.token;
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/errors/presentation-descriptor.ts
|
|
93
|
+
const DEFAULT_RECOVERY_LABELS = {
|
|
94
|
+
[UserRecovery.None]: "",
|
|
95
|
+
[UserRecovery.Retry]: "Try again",
|
|
96
|
+
[UserRecovery.RestartFlow]: "Restart flow",
|
|
97
|
+
[UserRecovery.Reauthenticate]: "Sign in again",
|
|
98
|
+
[UserRecovery.ContactSupport]: "Contact support"
|
|
99
|
+
};
|
|
100
|
+
const POPUP_PRESENTATIONS = {
|
|
101
|
+
"popup.blocked": {
|
|
102
|
+
title: "Popup was blocked",
|
|
103
|
+
description: "The browser blocked the popup window before the login flow could start. Allow popups for this site, then try again.",
|
|
104
|
+
tone: ErrorPresentationTone.Warning
|
|
105
|
+
},
|
|
106
|
+
"popup.closed_by_user": {
|
|
107
|
+
title: "Popup login was closed",
|
|
108
|
+
description: "The popup window was closed before the OIDC provider returned a callback. Start the login flow again to continue.",
|
|
109
|
+
tone: ErrorPresentationTone.Warning
|
|
110
|
+
},
|
|
111
|
+
"popup.relay_timeout": {
|
|
112
|
+
title: "Popup relay timed out",
|
|
113
|
+
description: "The popup callback never relayed back to the opener before the timeout window expired.",
|
|
114
|
+
tone: ErrorPresentationTone.Warning
|
|
115
|
+
},
|
|
116
|
+
"popup.relay_error": {
|
|
117
|
+
title: "Popup relay failed",
|
|
118
|
+
description: "The popup callback page returned an error instead of a callback payload.",
|
|
119
|
+
tone: ErrorPresentationTone.Danger
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
function readErrorPresentationDescriptor(error, options = {}) {
|
|
123
|
+
if (error instanceof ClientError) return describeClientErrorPresentation(error, options);
|
|
124
|
+
const clientErrorLike = coerceClientErrorLike(error);
|
|
125
|
+
if (clientErrorLike) return describeClientErrorLikePresentation(clientErrorLike, options);
|
|
126
|
+
if (error instanceof Error) return {
|
|
127
|
+
code: null,
|
|
128
|
+
kind: null,
|
|
129
|
+
title: options.fallbackTitle ?? "Operation failed",
|
|
130
|
+
description: error.message || options.fallbackDescription || "An unexpected error prevented the operation from completing.",
|
|
131
|
+
recovery: UserRecovery.None,
|
|
132
|
+
retryable: false,
|
|
133
|
+
tone: ErrorPresentationTone.Danger,
|
|
134
|
+
primaryAction: null
|
|
135
|
+
};
|
|
136
|
+
return {
|
|
137
|
+
code: null,
|
|
138
|
+
kind: null,
|
|
139
|
+
title: options.fallbackTitle ?? "Operation failed",
|
|
140
|
+
description: options.fallbackDescription ?? "An unexpected error prevented the operation from completing.",
|
|
141
|
+
recovery: UserRecovery.None,
|
|
142
|
+
retryable: false,
|
|
143
|
+
tone: ErrorPresentationTone.Danger,
|
|
144
|
+
primaryAction: null
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function describeClientErrorPresentation(error, options) {
|
|
148
|
+
return describeClientErrorLikePresentation({
|
|
149
|
+
code: error.code,
|
|
150
|
+
kind: error.kind,
|
|
151
|
+
message: error.message,
|
|
152
|
+
recovery: error.recovery,
|
|
153
|
+
retryable: error.retryable,
|
|
154
|
+
source: error.source,
|
|
155
|
+
presentation: error.presentation
|
|
156
|
+
}, options);
|
|
157
|
+
}
|
|
158
|
+
function describeClientErrorLikePresentation(error, options) {
|
|
159
|
+
const popupPresentation = POPUP_PRESENTATIONS[error.code];
|
|
160
|
+
const title = popupPresentation?.title ?? readClientErrorTitle(error);
|
|
161
|
+
const description = popupPresentation?.description ?? error.presentation?.message ?? error.message ?? options.fallbackDescription ?? "The operation could not complete.";
|
|
162
|
+
return {
|
|
163
|
+
code: error.code,
|
|
164
|
+
kind: error.kind,
|
|
165
|
+
source: error.source,
|
|
166
|
+
title,
|
|
167
|
+
description,
|
|
168
|
+
recovery: error.recovery,
|
|
169
|
+
retryable: error.retryable,
|
|
170
|
+
tone: popupPresentation?.tone ?? readClientErrorTone(error),
|
|
171
|
+
primaryAction: readPrimaryAction(error.recovery, options)
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function readClientErrorTitle(error) {
|
|
175
|
+
if (error.kind === ClientErrorKind.Unauthenticated) return "Authentication required";
|
|
176
|
+
if (error.kind === ClientErrorKind.Unauthorized) return "Access denied";
|
|
177
|
+
if (error.kind === ClientErrorKind.Cancelled) return "Operation cancelled";
|
|
178
|
+
if (error.kind === ClientErrorKind.Timeout) return "Request timed out";
|
|
179
|
+
if (error.kind === ClientErrorKind.Configuration) return "Configuration error";
|
|
180
|
+
if (error.kind === ClientErrorKind.Validation) return "Validation failed";
|
|
181
|
+
if (error.kind === ClientErrorKind.Storage) return "Browser storage failed";
|
|
182
|
+
if (error.kind === ClientErrorKind.Authorization && error.recovery === UserRecovery.RestartFlow) return "Flow needs to restart";
|
|
183
|
+
if (error.kind === ClientErrorKind.Transport) return "Network request failed";
|
|
184
|
+
if (error.kind === ClientErrorKind.Server) return "Server request failed";
|
|
185
|
+
if (error.kind === ClientErrorKind.Protocol) return "Request failed";
|
|
186
|
+
return "Operation failed";
|
|
187
|
+
}
|
|
188
|
+
function readClientErrorTone(error) {
|
|
189
|
+
if (error.kind === ClientErrorKind.Cancelled) return ErrorPresentationTone.Neutral;
|
|
190
|
+
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;
|
|
191
|
+
return ErrorPresentationTone.Danger;
|
|
192
|
+
}
|
|
193
|
+
function coerceClientErrorLike(error) {
|
|
194
|
+
if (typeof error !== "object" || error === null) return null;
|
|
195
|
+
const candidate = error;
|
|
196
|
+
if (typeof candidate.message !== "string") return null;
|
|
197
|
+
return {
|
|
198
|
+
code: typeof candidate.code === "string" ? candidate.code : null,
|
|
199
|
+
kind: typeof candidate.kind === "string" ? candidate.kind : null,
|
|
200
|
+
message: candidate.message,
|
|
201
|
+
recovery: typeof candidate.recovery === "string" ? candidate.recovery : UserRecovery.None,
|
|
202
|
+
retryable: candidate.retryable === true,
|
|
203
|
+
source: typeof candidate.source === "string" ? candidate.source : void 0,
|
|
204
|
+
presentation: isErrorPresentation(candidate.presentation) ? candidate.presentation : void 0
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function isErrorPresentation(value) {
|
|
208
|
+
return typeof value === "object" && value !== null && typeof value.code === "string" && typeof value.message === "string" && typeof value.recovery === "string";
|
|
209
|
+
}
|
|
210
|
+
function readPrimaryAction(recovery, options) {
|
|
211
|
+
if (recovery === UserRecovery.None) return null;
|
|
212
|
+
const label = options.recoveryLabels?.[recovery] ?? DEFAULT_RECOVERY_LABELS[recovery];
|
|
213
|
+
if (!label) return null;
|
|
214
|
+
return {
|
|
215
|
+
recovery,
|
|
216
|
+
label,
|
|
217
|
+
href: options.recoveryLinks?.[recovery] ?? null
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/events/event-stream.ts
|
|
222
|
+
/**
|
|
223
|
+
* Create a new event stream from a producer function.
|
|
224
|
+
*
|
|
225
|
+
* The producer receives an observer and should call `next()` to emit values,
|
|
226
|
+
* `error()` to signal an error, and `complete()` to signal completion.
|
|
227
|
+
* It may return a teardown function that will be called on unsubscribe.
|
|
228
|
+
*/
|
|
229
|
+
function createEventStream(producer) {
|
|
230
|
+
return { subscribe(observer) {
|
|
231
|
+
let active = true;
|
|
232
|
+
const teardown = producer({
|
|
233
|
+
next(value) {
|
|
234
|
+
if (active) observer.next?.(value);
|
|
235
|
+
},
|
|
236
|
+
error(err) {
|
|
237
|
+
if (active) {
|
|
238
|
+
active = false;
|
|
239
|
+
observer.error?.(err);
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
complete() {
|
|
243
|
+
if (active) {
|
|
244
|
+
active = false;
|
|
245
|
+
observer.complete?.();
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
return { unsubscribe() {
|
|
250
|
+
if (active) {
|
|
251
|
+
active = false;
|
|
252
|
+
teardown?.();
|
|
253
|
+
}
|
|
254
|
+
} };
|
|
255
|
+
} };
|
|
256
|
+
}
|
|
257
|
+
//#endregion
|
|
258
|
+
//#region src/events/operators.ts
|
|
259
|
+
function pipe(source, ...operators) {
|
|
260
|
+
return operators.reduce((acc, op) => op(acc), source);
|
|
261
|
+
}
|
|
262
|
+
function map(fn) {
|
|
263
|
+
return (source) => createEventStream((observer) => {
|
|
264
|
+
const sub = source.subscribe({
|
|
265
|
+
next(value) {
|
|
266
|
+
observer.next?.(fn(value));
|
|
267
|
+
},
|
|
268
|
+
error(err) {
|
|
269
|
+
observer.error?.(err);
|
|
270
|
+
},
|
|
271
|
+
complete() {
|
|
272
|
+
observer.complete?.();
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
return () => sub.unsubscribe();
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
function filter(predicate) {
|
|
279
|
+
return (source) => createEventStream((observer) => {
|
|
280
|
+
const sub = source.subscribe({
|
|
281
|
+
next(value) {
|
|
282
|
+
if (predicate(value)) observer.next?.(value);
|
|
283
|
+
},
|
|
284
|
+
error(err) {
|
|
285
|
+
observer.error?.(err);
|
|
286
|
+
},
|
|
287
|
+
complete() {
|
|
288
|
+
observer.complete?.();
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
return () => sub.unsubscribe();
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
function tap(fn) {
|
|
295
|
+
return (source) => createEventStream((observer) => {
|
|
296
|
+
const sub = source.subscribe({
|
|
297
|
+
next(value) {
|
|
298
|
+
fn(value);
|
|
299
|
+
observer.next?.(value);
|
|
300
|
+
},
|
|
301
|
+
error(err) {
|
|
302
|
+
observer.error?.(err);
|
|
303
|
+
},
|
|
304
|
+
complete() {
|
|
305
|
+
observer.complete?.();
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
return () => sub.unsubscribe();
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
function takeUntil(notifier) {
|
|
312
|
+
return (source) => createEventStream((observer) => {
|
|
313
|
+
const sourceSub = source.subscribe({
|
|
314
|
+
next(value) {
|
|
315
|
+
observer.next?.(value);
|
|
316
|
+
},
|
|
317
|
+
error(err) {
|
|
318
|
+
observer.error?.(err);
|
|
319
|
+
},
|
|
320
|
+
complete() {
|
|
321
|
+
observer.complete?.();
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
const notifierSub = notifier.subscribe({ next() {
|
|
325
|
+
observer.complete?.();
|
|
326
|
+
sourceSub.unsubscribe();
|
|
327
|
+
notifierSub.unsubscribe();
|
|
328
|
+
} });
|
|
329
|
+
return () => {
|
|
330
|
+
sourceSub.unsubscribe();
|
|
331
|
+
notifierSub.unsubscribe();
|
|
332
|
+
};
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
function finalize(fn) {
|
|
336
|
+
return (source) => createEventStream((observer) => {
|
|
337
|
+
const sub = source.subscribe({
|
|
338
|
+
next(value) {
|
|
339
|
+
observer.next?.(value);
|
|
340
|
+
},
|
|
341
|
+
error(err) {
|
|
342
|
+
observer.error?.(err);
|
|
343
|
+
fn();
|
|
344
|
+
},
|
|
345
|
+
complete() {
|
|
346
|
+
observer.complete?.();
|
|
347
|
+
fn();
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
return () => {
|
|
351
|
+
sub.unsubscribe();
|
|
352
|
+
fn();
|
|
353
|
+
};
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
function merge(...sources) {
|
|
357
|
+
return createEventStream((observer) => {
|
|
358
|
+
let completedCount = 0;
|
|
359
|
+
const subs = sources.map((source) => source.subscribe({
|
|
360
|
+
next(value) {
|
|
361
|
+
observer.next?.(value);
|
|
362
|
+
},
|
|
363
|
+
error(err) {
|
|
364
|
+
observer.error?.(err);
|
|
365
|
+
},
|
|
366
|
+
complete() {
|
|
367
|
+
completedCount++;
|
|
368
|
+
if (completedCount === sources.length) observer.complete?.();
|
|
369
|
+
}
|
|
370
|
+
}));
|
|
371
|
+
return () => {
|
|
372
|
+
for (const sub of subs) sub.unsubscribe();
|
|
373
|
+
};
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
//#endregion
|
|
377
|
+
//#region src/events/types.ts
|
|
378
|
+
const EventSourceKind = {
|
|
379
|
+
User: "user",
|
|
380
|
+
Timer: "timer",
|
|
381
|
+
Http: "http",
|
|
382
|
+
Storage: "storage",
|
|
383
|
+
Framework: "framework",
|
|
384
|
+
System: "system"
|
|
385
|
+
};
|
|
386
|
+
//#endregion
|
|
387
|
+
//#region src/identity/principal.ts
|
|
388
|
+
function readClaimsBag(value) {
|
|
389
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
390
|
+
}
|
|
391
|
+
function readOptionalString(value) {
|
|
392
|
+
return typeof value === "string" ? value : void 0;
|
|
393
|
+
}
|
|
394
|
+
function resolveDisplayName(displayName, subject) {
|
|
395
|
+
if (typeof displayName === "string" && displayName.trim().length > 0) return displayName;
|
|
396
|
+
return subject;
|
|
397
|
+
}
|
|
398
|
+
function normalizeAuthenticatedPrincipal(input) {
|
|
399
|
+
if (typeof input.subject !== "string" || input.subject.trim().length === 0) return null;
|
|
400
|
+
return {
|
|
401
|
+
subject: input.subject,
|
|
402
|
+
displayName: resolveDisplayName(input.displayName, input.subject),
|
|
403
|
+
picture: readOptionalString(input.picture),
|
|
404
|
+
issuer: readOptionalString(input.issuer),
|
|
405
|
+
claims: readClaimsBag(input.claims)
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function normalizeAuthenticatedPrincipalWire(input) {
|
|
409
|
+
if (typeof input.subject !== "string" || input.subject.trim().length === 0) return null;
|
|
410
|
+
return {
|
|
411
|
+
subject: input.subject,
|
|
412
|
+
displayName: resolveDisplayName(input.display_name, input.subject),
|
|
413
|
+
picture: readOptionalString(input.picture),
|
|
414
|
+
issuer: readOptionalString(input.issuer),
|
|
415
|
+
claims: readClaimsBag(input.claims)
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
function projectAuthenticatedPrincipal(options) {
|
|
419
|
+
const principal = options.principal;
|
|
420
|
+
const subject = principal?.subject ?? options.fallbackSubject ?? "context.anonymous";
|
|
421
|
+
return {
|
|
422
|
+
subject,
|
|
423
|
+
displayName: principal?.displayName?.trim() || options.fallbackDisplayName || subject,
|
|
424
|
+
picture: principal?.picture,
|
|
425
|
+
issuer: principal?.issuer ?? options.fallbackIssuer,
|
|
426
|
+
claims: principal?.claims ?? options.fallbackClaims
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
//#endregion
|
|
430
|
+
//#region src/logging/console-logger.ts
|
|
431
|
+
/** Simple console-based logger implementation. */
|
|
432
|
+
function createConsoleLogger(scope) {
|
|
433
|
+
return { log(entry) {
|
|
434
|
+
const label = `[${entry.scope ?? scope ?? "securitydept"}]`;
|
|
435
|
+
switch (entry.level) {
|
|
436
|
+
case "debug":
|
|
437
|
+
console.debug(label, entry.message, entry.attributes ?? "");
|
|
438
|
+
break;
|
|
439
|
+
case "info":
|
|
440
|
+
console.info(label, entry.message, entry.attributes ?? "");
|
|
441
|
+
break;
|
|
442
|
+
case "warn":
|
|
443
|
+
console.warn(label, entry.message, entry.attributes ?? "");
|
|
444
|
+
break;
|
|
445
|
+
case "error":
|
|
446
|
+
console.error(label, entry.message, entry.error ?? "", entry.attributes ?? "");
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
} };
|
|
450
|
+
}
|
|
451
|
+
//#endregion
|
|
452
|
+
//#region src/logging/noop-logger.ts
|
|
453
|
+
/** No-op logger — default when no logger is provided. */
|
|
454
|
+
function createNoopLogger() {
|
|
455
|
+
return { log() {} };
|
|
456
|
+
}
|
|
457
|
+
//#endregion
|
|
458
|
+
//#region src/logging/trace-timeline-store.ts
|
|
459
|
+
function createTraceTimelineStore(limit = 200) {
|
|
460
|
+
let nextId = 1;
|
|
461
|
+
let entries = [];
|
|
462
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
463
|
+
function readRecordedAtIso(at) {
|
|
464
|
+
if (!Number.isFinite(at)) return "invalid-timestamp";
|
|
465
|
+
const date = new Date(at);
|
|
466
|
+
const timestamp = date.getTime();
|
|
467
|
+
if (!Number.isFinite(timestamp)) return "invalid-timestamp";
|
|
468
|
+
return date.toISOString();
|
|
469
|
+
}
|
|
470
|
+
function notify() {
|
|
471
|
+
for (const listener of listeners) listener();
|
|
472
|
+
}
|
|
473
|
+
return {
|
|
474
|
+
record(event) {
|
|
475
|
+
const entry = {
|
|
476
|
+
...event,
|
|
477
|
+
id: nextId++,
|
|
478
|
+
recordedAtIso: readRecordedAtIso(event.at)
|
|
479
|
+
};
|
|
480
|
+
entries = [...entries, entry].slice(-limit);
|
|
481
|
+
notify();
|
|
482
|
+
},
|
|
483
|
+
get() {
|
|
484
|
+
return entries;
|
|
485
|
+
},
|
|
486
|
+
subscribe(listener) {
|
|
487
|
+
listeners.add(listener);
|
|
488
|
+
return () => {
|
|
489
|
+
listeners.delete(listener);
|
|
490
|
+
};
|
|
491
|
+
},
|
|
492
|
+
clear() {
|
|
493
|
+
if (entries.length === 0) return;
|
|
494
|
+
entries = [];
|
|
495
|
+
notify();
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
//#endregion
|
|
500
|
+
//#region src/signals/computed.ts
|
|
501
|
+
/**
|
|
502
|
+
* Create a computed signal that derives its value from other signals.
|
|
503
|
+
*
|
|
504
|
+
* Semantics: lazy + cached.
|
|
505
|
+
* - The compute function is NOT called at construction time.
|
|
506
|
+
* - `get()` recomputes only when dirty (a dependency changed since last read).
|
|
507
|
+
* - Subscribers are notified only when a dependency changes (dirty-flagged),
|
|
508
|
+
* and the next `get()` will produce the fresh value.
|
|
509
|
+
*
|
|
510
|
+
* Dependency subscriptions are permanent for the lifetime of the computed signal.
|
|
511
|
+
*/
|
|
512
|
+
function createComputed(compute, deps) {
|
|
513
|
+
let cached;
|
|
514
|
+
let dirty = true;
|
|
515
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
516
|
+
const markDirty = () => {
|
|
517
|
+
if (!dirty) {
|
|
518
|
+
dirty = true;
|
|
519
|
+
for (const listener of listeners) listener();
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
for (const dep of deps) dep.subscribe(markDirty);
|
|
523
|
+
return {
|
|
524
|
+
get() {
|
|
525
|
+
if (dirty) {
|
|
526
|
+
cached = compute();
|
|
527
|
+
dirty = false;
|
|
528
|
+
}
|
|
529
|
+
return cached;
|
|
530
|
+
},
|
|
531
|
+
subscribe(listener) {
|
|
532
|
+
listeners.add(listener);
|
|
533
|
+
return () => {
|
|
534
|
+
listeners.delete(listener);
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
//#endregion
|
|
540
|
+
//#region src/signals/signal.ts
|
|
541
|
+
/**
|
|
542
|
+
* Minimal writable signal implementation.
|
|
543
|
+
* Snapshot-first: values are immutable snapshots, mutations go through `set()`.
|
|
544
|
+
*/
|
|
545
|
+
function createSignal(initial) {
|
|
546
|
+
let current = initial;
|
|
547
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
548
|
+
return {
|
|
549
|
+
get() {
|
|
550
|
+
return current;
|
|
551
|
+
},
|
|
552
|
+
set(value) {
|
|
553
|
+
if (Object.is(current, value)) return;
|
|
554
|
+
current = value;
|
|
555
|
+
for (const listener of listeners) listener();
|
|
556
|
+
},
|
|
557
|
+
subscribe(listener) {
|
|
558
|
+
listeners.add(listener);
|
|
559
|
+
return () => {
|
|
560
|
+
listeners.delete(listener);
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Create a read-only view of a writable signal.
|
|
567
|
+
*/
|
|
568
|
+
function readonlySignal(signal) {
|
|
569
|
+
return {
|
|
570
|
+
get: () => signal.get(),
|
|
571
|
+
subscribe: (listener) => signal.subscribe(listener)
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
//#endregion
|
|
575
|
+
//#region src/validation/index.ts
|
|
576
|
+
/**
|
|
577
|
+
* Validate an unknown input against a `@standard-schema`-compatible schema.
|
|
578
|
+
*
|
|
579
|
+
* This is the foundation-level entry point for SDK validation. It accepts
|
|
580
|
+
* any schema implementing `StandardSchemaV1` and returns a discriminated
|
|
581
|
+
* `ValidationResult`.
|
|
582
|
+
*
|
|
583
|
+
* @example
|
|
584
|
+
* ```ts
|
|
585
|
+
* import { validateWithSchema } from "@securitydept/client";
|
|
586
|
+
* import { z } from "zod";
|
|
587
|
+
*
|
|
588
|
+
* const UserSchema = z.object({ name: z.string() });
|
|
589
|
+
* const result = await validateWithSchema(UserSchema, { name: "Alice" });
|
|
590
|
+
* if (result.success) {
|
|
591
|
+
* console.log(result.value.name);
|
|
592
|
+
* } else {
|
|
593
|
+
* console.error(result.issues);
|
|
594
|
+
* }
|
|
595
|
+
* ```
|
|
596
|
+
*/
|
|
597
|
+
async function validateWithSchema(schema, input) {
|
|
598
|
+
const result = await schema["~standard"].validate(input);
|
|
599
|
+
if ("value" in result) return {
|
|
600
|
+
success: true,
|
|
601
|
+
value: result.value
|
|
602
|
+
};
|
|
603
|
+
return {
|
|
604
|
+
success: false,
|
|
605
|
+
issues: result.issues
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Validate an unknown input synchronously against a `@standard-schema`-compatible schema.
|
|
610
|
+
*
|
|
611
|
+
* Only use this when you know the schema's validate function returns synchronously.
|
|
612
|
+
* Throws if the schema returns a Promise.
|
|
613
|
+
*/
|
|
614
|
+
function validateWithSchemaSync(schema, input) {
|
|
615
|
+
const result = schema["~standard"].validate(input);
|
|
616
|
+
if (result instanceof Promise) throw new Error("validateWithSchemaSync: schema returned a Promise. Use validateWithSchema instead.");
|
|
617
|
+
if ("value" in result) return {
|
|
618
|
+
success: true,
|
|
619
|
+
value: result.value
|
|
620
|
+
};
|
|
621
|
+
return {
|
|
622
|
+
success: false,
|
|
623
|
+
issues: result.issues
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Create a minimal `@standard-schema`-compatible schema without requiring
|
|
628
|
+
* an external validation library.
|
|
629
|
+
*
|
|
630
|
+
* This is useful for SDK-internal schemas where pulling in zod/valibot
|
|
631
|
+
* would be excessive. For adopter-facing schemas, prefer a real validation
|
|
632
|
+
* library.
|
|
633
|
+
*
|
|
634
|
+
* @example
|
|
635
|
+
* ```ts
|
|
636
|
+
* import { createSchema, validateWithSchema } from "@securitydept/client";
|
|
637
|
+
*
|
|
638
|
+
* const NameSchema = createSchema<unknown, { name: string }>({
|
|
639
|
+
* validate(input) {
|
|
640
|
+
* if (typeof input === "object" && input !== null && "name" in input &&
|
|
641
|
+
* typeof (input as any).name === "string") {
|
|
642
|
+
* return { value: { name: (input as any).name } };
|
|
643
|
+
* }
|
|
644
|
+
* return { issues: [{ message: "Expected object with string 'name'" }] };
|
|
645
|
+
* },
|
|
646
|
+
* });
|
|
647
|
+
*
|
|
648
|
+
* const result = await validateWithSchema(NameSchema, { name: "Alice" });
|
|
649
|
+
* ```
|
|
650
|
+
*/
|
|
651
|
+
function createSchema(options) {
|
|
652
|
+
return { "~standard": {
|
|
653
|
+
version: 1,
|
|
654
|
+
vendor: "securitydept",
|
|
655
|
+
validate: options.validate
|
|
656
|
+
} };
|
|
657
|
+
}
|
|
658
|
+
//#endregion
|
|
659
|
+
export { ClientError, ClientErrorKind, ClientErrorSource, ErrorPresentationTone, EventSourceKind, FetchTransportRedirectKind, LogLevel, OperationTraceEventType, PromiseSettlementKind, UserRecovery, createCancellationTokenSource, createComputed, createConsoleLogger, createDefaultClock, createDefaultScheduler, createEphemeralFlowStore, createEventStream, createInMemoryRecordStore, createJsonCodec, createKeyedEphemeralFlowStore, createLinkedCancellationToken, createNoopLogger, createOperationTracer, createRuntime, createSchema, createSignal, createTraceTimelineStore, filter, finalize, fromEventPattern, fromPromise, fromSignal, interval, map, merge, normalizeAuthenticatedPrincipal, normalizeAuthenticatedPrincipalWire, pipe, projectAuthenticatedPrincipal, readErrorPresentationDescriptor, readonlySignal, scheduleAt, takeUntil, tap, timer, validateWithSchema, validateWithSchemaSync };
|
|
660
|
+
|
|
661
|
+
//# sourceMappingURL=index.mjs.map
|