@securitydept/client 0.3.0-beta.2 → 0.3.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/create-Bm-XRA3G.mjs +1573 -0
- package/dist/create-Bm-XRA3G.mjs.map +1 -0
- package/dist/index-BT-u3qNI.d.mts +133 -0
- package/dist/index-Ch8lrE44.d.mts +625 -0
- package/dist/index-D5mW18cM.d.mts +1648 -0
- package/dist/index.d.mts +3 -353
- package/dist/index.mjs +1124 -362
- package/dist/index.mjs.map +1 -1
- package/dist/router-BJ8LqNJ1.mjs +245 -0
- package/dist/router-BJ8LqNJ1.mjs.map +1 -0
- package/dist/rx/index.d.mts +69 -0
- package/dist/rx/index.mjs +2 -0
- package/dist/rx-BoB32Dzg.mjs +1106 -0
- package/dist/rx-BoB32Dzg.mjs.map +1 -0
- package/dist/server/index.d.mts +21 -0
- package/dist/server/index.mjs +29 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/test/index.d.mts +102 -0
- package/dist/test/index.mjs +270 -0
- package/dist/test/index.mjs.map +1 -0
- package/dist/uri-string-BfnIT4gJ.mjs +298 -0
- package/dist/uri-string-BfnIT4gJ.mjs.map +1 -0
- package/dist/web/index.d.mts +82 -336
- package/dist/web/index.mjs +328 -337
- package/dist/web/index.mjs.map +1 -1
- package/dist/webext/index.d.mts +74 -0
- package/dist/webext/index.mjs +229 -0
- package/dist/webext/index.mjs.map +1 -0
- package/package.json +28 -21
- package/dist/auth-coordination/index.d.mts +0 -198
- package/dist/auth-coordination/index.mjs +0 -107
- package/dist/auth-coordination/index.mjs.map +0 -1
- package/dist/events/index.d.mts +0 -2
- package/dist/events/index.mjs +0 -2
- package/dist/events-BUlfsRfl.mjs +0 -127
- package/dist/events-BUlfsRfl.mjs.map +0 -1
- package/dist/fetch-transport-Bd5RyWj2.d.mts +0 -339
- package/dist/fetch-transport-DtnSRWAH.mjs +0 -631
- package/dist/fetch-transport-DtnSRWAH.mjs.map +0 -1
- package/dist/index-C_wgl6dW.d.mts +0 -102
- package/dist/index-DbYSFHDe.d.mts +0 -39
- package/dist/persistence/index.d.mts +0 -3
- package/dist/persistence/index.mjs +0 -2
- package/dist/persistence/web/index.d.mts +0 -16
- package/dist/persistence/web/index.mjs +0 -51
- package/dist/persistence/web/index.mjs.map +0 -1
- package/dist/persistence-CSB8770k.mjs +0 -108
- package/dist/persistence-CSB8770k.mjs.map +0 -1
- package/dist/planner-host-CPyGNyQV.d.mts +0 -190
- package/dist/planner-host-CV38bMSn.mjs +0 -195
- package/dist/planner-host-CV38bMSn.mjs.map +0 -1
- package/dist/types-C59fcM5W.d.mts +0 -52
- package/dist/types-DQc088ZP.d.mts +0 -32
- package/dist/web-router/index.d.mts +0 -259
- package/dist/web-router/index.mjs +0 -395
- package/dist/web-router/index.mjs.map +0 -1
|
@@ -0,0 +1,1573 @@
|
|
|
1
|
+
import { A as RxEventSubject, D as RxStateSignal, F as SYMBOL_DISPOSE, G as UserRecovery, H as ClientErrorKind, M as isInteropObservableTrait, U as ClientErrorSource, V as ClientError, W as ErrorPresentationTone, h as createAsyncSchedulerWithTimestampProvider, j as SYMBOL_OBSERVABLE, k as RxEventStream } from "./rx-BoB32Dzg.mjs";
|
|
2
|
+
import { v7 } from "uuid";
|
|
3
|
+
import { InjectionToken, Injector, Optional, ReflectiveInjector } from "injection-js";
|
|
4
|
+
import { BehaviorSubject, EMPTY, ReplaySubject, Subject, catchError, delayWhen, distinctUntilChanged, exhaustMap, filter, from, fromEventPattern, map, merge, of, shareReplay, take, takeUntil, timeout, timer } from "rxjs";
|
|
5
|
+
import { type } from "arktype";
|
|
6
|
+
//#region src/errors/presentation-descriptor.ts
|
|
7
|
+
const DEFAULT_RECOVERY_LABELS = {
|
|
8
|
+
[UserRecovery.None]: "",
|
|
9
|
+
[UserRecovery.Retry]: "Try again",
|
|
10
|
+
[UserRecovery.RestartFlow]: "Restart flow",
|
|
11
|
+
[UserRecovery.Reauthenticate]: "Sign in again",
|
|
12
|
+
[UserRecovery.ContactSupport]: "Contact support"
|
|
13
|
+
};
|
|
14
|
+
const userRecoveryValues = new Set(Object.values(UserRecovery));
|
|
15
|
+
const clientErrorKindValues = new Set(Object.values(ClientErrorKind));
|
|
16
|
+
function readErrorPresentationDescriptor(error, options = {}) {
|
|
17
|
+
const clientError = error instanceof ClientError ? error : coerceClientErrorLike(error);
|
|
18
|
+
if (!clientError) return {
|
|
19
|
+
code: null,
|
|
20
|
+
kind: null,
|
|
21
|
+
title: options.fallbackTitle ?? "Operation failed",
|
|
22
|
+
description: options.fallbackDescription ?? "An unexpected error prevented the operation from completing.",
|
|
23
|
+
recovery: UserRecovery.None,
|
|
24
|
+
retryable: false,
|
|
25
|
+
tone: ErrorPresentationTone.Danger,
|
|
26
|
+
primaryAction: null
|
|
27
|
+
};
|
|
28
|
+
const code = clientError.presentation?.code ?? clientError.code;
|
|
29
|
+
const codePresentation = code ? options.codePresentations?.[code] : void 0;
|
|
30
|
+
const recovery = clientError.presentation?.recovery ?? codePresentation?.recovery ?? clientError.recovery;
|
|
31
|
+
const generic = readGenericPresentation(clientError.kind);
|
|
32
|
+
return {
|
|
33
|
+
code,
|
|
34
|
+
kind: clientError.kind,
|
|
35
|
+
source: clientError.source,
|
|
36
|
+
title: codePresentation?.title ?? generic.title,
|
|
37
|
+
description: clientError.presentation?.message ?? codePresentation?.description ?? generic.description,
|
|
38
|
+
recovery,
|
|
39
|
+
retryable: clientError.retryable,
|
|
40
|
+
tone: codePresentation?.tone ?? readClientErrorTone(clientError.kind, recovery),
|
|
41
|
+
primaryAction: readPrimaryAction(recovery, options)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function readGenericPresentation(kind) {
|
|
45
|
+
switch (kind) {
|
|
46
|
+
case ClientErrorKind.Unauthenticated: return {
|
|
47
|
+
title: "Authentication required",
|
|
48
|
+
description: "Sign in again to continue."
|
|
49
|
+
};
|
|
50
|
+
case ClientErrorKind.Unauthorized: return {
|
|
51
|
+
title: "Access denied",
|
|
52
|
+
description: "You do not have permission to complete this operation."
|
|
53
|
+
};
|
|
54
|
+
case ClientErrorKind.Cancelled: return {
|
|
55
|
+
title: "Operation cancelled",
|
|
56
|
+
description: "The operation was cancelled before it completed."
|
|
57
|
+
};
|
|
58
|
+
case ClientErrorKind.Timeout: return {
|
|
59
|
+
title: "Request timed out",
|
|
60
|
+
description: "The operation did not complete in time."
|
|
61
|
+
};
|
|
62
|
+
case ClientErrorKind.Configuration: return {
|
|
63
|
+
title: "Configuration error",
|
|
64
|
+
description: "The client is not configured to complete this operation."
|
|
65
|
+
};
|
|
66
|
+
case ClientErrorKind.Storage: return {
|
|
67
|
+
title: "Storage failed",
|
|
68
|
+
description: "The client could not access required stored data."
|
|
69
|
+
};
|
|
70
|
+
case ClientErrorKind.Transport: return {
|
|
71
|
+
title: "Network request failed",
|
|
72
|
+
description: "The client could not reach the service."
|
|
73
|
+
};
|
|
74
|
+
case ClientErrorKind.Server: return {
|
|
75
|
+
title: "Server request failed",
|
|
76
|
+
description: "The service could not complete the request."
|
|
77
|
+
};
|
|
78
|
+
case ClientErrorKind.Protocol: return {
|
|
79
|
+
title: "Invalid response",
|
|
80
|
+
description: "The service response did not satisfy the expected protocol."
|
|
81
|
+
};
|
|
82
|
+
case ClientErrorKind.Authorization: return {
|
|
83
|
+
title: "Authorization failed",
|
|
84
|
+
description: "The authorization flow could not be completed."
|
|
85
|
+
};
|
|
86
|
+
default: return {
|
|
87
|
+
title: "Operation failed",
|
|
88
|
+
description: "The operation could not be completed."
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function readClientErrorTone(kind, recovery) {
|
|
93
|
+
if (kind === ClientErrorKind.Cancelled) return ErrorPresentationTone.Neutral;
|
|
94
|
+
if (kind === ClientErrorKind.Unauthenticated || kind === ClientErrorKind.Timeout || kind === ClientErrorKind.Transport || kind === ClientErrorKind.Server || recovery === UserRecovery.Retry || recovery === UserRecovery.RestartFlow || recovery === UserRecovery.Reauthenticate) return ErrorPresentationTone.Warning;
|
|
95
|
+
return ErrorPresentationTone.Danger;
|
|
96
|
+
}
|
|
97
|
+
function coerceClientErrorLike(error) {
|
|
98
|
+
if (typeof error !== "object" || error === null) return null;
|
|
99
|
+
const candidate = error;
|
|
100
|
+
if (typeof candidate.kind !== "string" || !clientErrorKindValues.has(candidate.kind)) return null;
|
|
101
|
+
return {
|
|
102
|
+
code: typeof candidate.code === "string" ? candidate.code : null,
|
|
103
|
+
kind: candidate.kind,
|
|
104
|
+
recovery: typeof candidate.recovery === "string" && userRecoveryValues.has(candidate.recovery) ? candidate.recovery : UserRecovery.None,
|
|
105
|
+
retryable: candidate.retryable === true,
|
|
106
|
+
source: typeof candidate.source === "string" ? candidate.source : void 0,
|
|
107
|
+
presentation: isErrorPresentation(candidate.presentation) ? candidate.presentation : void 0
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function isErrorPresentation(value) {
|
|
111
|
+
return typeof value === "object" && value !== null && typeof value.code === "string" && typeof value.message === "string" && typeof value.recovery === "string" && userRecoveryValues.has(value.recovery);
|
|
112
|
+
}
|
|
113
|
+
function readPrimaryAction(recovery, options) {
|
|
114
|
+
if (recovery === UserRecovery.None) return null;
|
|
115
|
+
const label = options.recoveryLabels?.[recovery] ?? DEFAULT_RECOVERY_LABELS[recovery];
|
|
116
|
+
if (!label) return null;
|
|
117
|
+
return {
|
|
118
|
+
recovery,
|
|
119
|
+
label,
|
|
120
|
+
href: options.recoveryLinks?.[recovery] ?? null
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/injection/types.ts
|
|
125
|
+
var SecuritydeptInjectionToken = class extends InjectionToken {};
|
|
126
|
+
const SecurityDeptOptional = Optional;
|
|
127
|
+
function getSecuritydeptProviderToken(provider) {
|
|
128
|
+
if (typeof provider === "function") return provider;
|
|
129
|
+
return provider.provide;
|
|
130
|
+
}
|
|
131
|
+
function createProviderIfTokenMissing(providedTokens, token, createProvider) {
|
|
132
|
+
return providedTokens.has(token) ? null : createProvider();
|
|
133
|
+
}
|
|
134
|
+
function notMissingProvider(provider) {
|
|
135
|
+
return provider !== null;
|
|
136
|
+
}
|
|
137
|
+
//#endregion
|
|
138
|
+
//#region src/injection/injector.ts
|
|
139
|
+
var SecuritydeptInjector = class SecuritydeptInjector {
|
|
140
|
+
runtimeInjector;
|
|
141
|
+
static resolveAndCreate(providers) {
|
|
142
|
+
const normalized = normalizeProviders([...providers, createInjectorSelfProvider()]);
|
|
143
|
+
return getSecuritydeptInjectorFromRuntime(ReflectiveInjector.resolveAndCreate(normalized.providers));
|
|
144
|
+
}
|
|
145
|
+
static fromParentInjector(parent, providers) {
|
|
146
|
+
const normalized = normalizeProviders([
|
|
147
|
+
{
|
|
148
|
+
provide: PARENT_INJECTOR_TOKEN,
|
|
149
|
+
useValue: parent
|
|
150
|
+
},
|
|
151
|
+
...providers,
|
|
152
|
+
createInjectorSelfProvider()
|
|
153
|
+
]);
|
|
154
|
+
return getSecuritydeptInjectorFromRuntime(ReflectiveInjector.resolveAndCreate(normalized.providers, toRuntimeInjector(parent)));
|
|
155
|
+
}
|
|
156
|
+
static fromRuntimeInjector(runtimeInjector) {
|
|
157
|
+
return new SecuritydeptInjector(runtimeInjector);
|
|
158
|
+
}
|
|
159
|
+
constructor(runtimeInjector) {
|
|
160
|
+
this.runtimeInjector = runtimeInjector;
|
|
161
|
+
}
|
|
162
|
+
get(token, notFoundValue) {
|
|
163
|
+
try {
|
|
164
|
+
if (arguments.length === 1) return this.runtimeInjector.get(token);
|
|
165
|
+
return this.runtimeInjector.get(token, notFoundValue);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
throw createInjectorResolutionError(token, error);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
const INJECTOR_TOKEN = new SecuritydeptInjectionToken("INJECTOR_TOKEN");
|
|
172
|
+
const PARENT_INJECTOR_TOKEN = new SecuritydeptInjectionToken("PARENT_INJECTOR_TOKEN");
|
|
173
|
+
function normalizeProviders(providers) {
|
|
174
|
+
return { providers: providers.map((provider) => {
|
|
175
|
+
if (typeof provider === "function") return provider;
|
|
176
|
+
if ("useValue" in provider) return {
|
|
177
|
+
provide: provider.provide,
|
|
178
|
+
useValue: provider.useValue
|
|
179
|
+
};
|
|
180
|
+
if ("useClass" in provider) return {
|
|
181
|
+
provide: provider.provide,
|
|
182
|
+
useFactory: (...deps) => new provider.useClass(...deps),
|
|
183
|
+
deps: provider.deps ? [...provider.deps] : void 0
|
|
184
|
+
};
|
|
185
|
+
if ("useExisting" in provider) return {
|
|
186
|
+
provide: provider.provide,
|
|
187
|
+
useExisting: provider.useExisting
|
|
188
|
+
};
|
|
189
|
+
return {
|
|
190
|
+
provide: provider.provide,
|
|
191
|
+
useFactory: provider.useFactory,
|
|
192
|
+
deps: provider.deps ? [...provider.deps] : void 0
|
|
193
|
+
};
|
|
194
|
+
}) };
|
|
195
|
+
}
|
|
196
|
+
function createInjectorSelfProvider() {
|
|
197
|
+
return {
|
|
198
|
+
provide: INJECTOR_TOKEN,
|
|
199
|
+
useFactory: (runtimeInjector) => SecuritydeptInjector.fromRuntimeInjector(runtimeInjector),
|
|
200
|
+
deps: [Injector]
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function getSecuritydeptInjectorFromRuntime(runtimeInjector) {
|
|
204
|
+
return runtimeInjector.get(INJECTOR_TOKEN);
|
|
205
|
+
}
|
|
206
|
+
var SecuritydeptRuntimeParentAdapter = class extends Injector {
|
|
207
|
+
parent;
|
|
208
|
+
constructor(parent) {
|
|
209
|
+
super();
|
|
210
|
+
this.parent = parent;
|
|
211
|
+
}
|
|
212
|
+
get(token, notFoundValue) {
|
|
213
|
+
if (arguments.length === 1) return this.parent.get(token);
|
|
214
|
+
return this.parent.get(token, notFoundValue);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
function toRuntimeInjector(parent) {
|
|
218
|
+
return new SecuritydeptRuntimeParentAdapter(parent);
|
|
219
|
+
}
|
|
220
|
+
function createInjectorResolutionError(token, error) {
|
|
221
|
+
const tokenLabel = describeDependencyToken(token);
|
|
222
|
+
const originalMessage = error instanceof Error ? error.message : String(error);
|
|
223
|
+
const message = /No provider/i.test(originalMessage) ? `[SecuritydeptInjector] No provider found for ${tokenLabel}.` : `[SecuritydeptInjector] Failed to resolve ${tokenLabel}: ${originalMessage}`;
|
|
224
|
+
return new Error(message, { cause: error instanceof Error ? error : void 0 });
|
|
225
|
+
}
|
|
226
|
+
function describeDependencyToken(token) {
|
|
227
|
+
if (token instanceof InjectionToken) return token.toString().replace(/^InjectionToken\s+/, "Token ");
|
|
228
|
+
if (typeof token === "function" && token.name) return token.name;
|
|
229
|
+
return String(token);
|
|
230
|
+
}
|
|
231
|
+
//#endregion
|
|
232
|
+
//#region src/signals/signal.ts
|
|
233
|
+
/**
|
|
234
|
+
* Minimal writable signal implementation.
|
|
235
|
+
* `set()` always publishes a write. Equality-based value-version suppression is
|
|
236
|
+
* handled by the underlying Rx signal for computed dependency validation.
|
|
237
|
+
*/
|
|
238
|
+
function createSignal(initial, options) {
|
|
239
|
+
return RxStateSignal.fromInitialValue(initial, options);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Create a read-only view of a writable signal.
|
|
243
|
+
*/
|
|
244
|
+
function readonlySignal(signal) {
|
|
245
|
+
return signal;
|
|
246
|
+
}
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region src/events/types.ts
|
|
249
|
+
const EventStreamTraitSchema = type({ subscribe: "Function" });
|
|
250
|
+
const EventSourceKind = {
|
|
251
|
+
User: "user",
|
|
252
|
+
Timer: "timer",
|
|
253
|
+
Http: "http",
|
|
254
|
+
Storage: "storage",
|
|
255
|
+
Framework: "framework",
|
|
256
|
+
System: "system"
|
|
257
|
+
};
|
|
258
|
+
function isEventStreamTrait(value) {
|
|
259
|
+
return typeof value === "object" && value !== null && typeof value.subscribe === "function" && isInteropObservableTrait(value);
|
|
260
|
+
}
|
|
261
|
+
//#endregion
|
|
262
|
+
//#region src/page/index.ts
|
|
263
|
+
const PageLifecycleTraitSchema = type({ resume: EventStreamTraitSchema });
|
|
264
|
+
const PAGE_LIFECYCLE_TRAIT_TOKEN = new SecuritydeptInjectionToken("PAGE_LIFECYCLE_TRAIT_TOKEN");
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/popup/errors.ts
|
|
267
|
+
const PopupErrorCode = {
|
|
268
|
+
Blocked: "popup.blocked",
|
|
269
|
+
Closed: "popup.closed_by_user"
|
|
270
|
+
};
|
|
271
|
+
function readPopupErrorPresentationDescriptor(error, options = {}) {
|
|
272
|
+
return readErrorPresentationDescriptor(error, {
|
|
273
|
+
...options,
|
|
274
|
+
codePresentations: {
|
|
275
|
+
[PopupErrorCode.Blocked]: {
|
|
276
|
+
title: "Popup was blocked",
|
|
277
|
+
description: "The browser blocked the popup window. Allow popups for this site, then try again.",
|
|
278
|
+
recovery: UserRecovery.Retry,
|
|
279
|
+
tone: ErrorPresentationTone.Warning
|
|
280
|
+
},
|
|
281
|
+
[PopupErrorCode.Closed]: {
|
|
282
|
+
title: "Popup was closed",
|
|
283
|
+
description: "The popup window was closed before the operation completed. Start the flow again.",
|
|
284
|
+
recovery: UserRecovery.RestartFlow,
|
|
285
|
+
tone: ErrorPresentationTone.Warning
|
|
286
|
+
},
|
|
287
|
+
...options.codePresentations
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
//#endregion
|
|
292
|
+
//#region src/validation/validator.ts
|
|
293
|
+
function formatValidationFailure(failure) {
|
|
294
|
+
if (!failure) return null;
|
|
295
|
+
const summary = failure.issues.map((issue) => issue.path && issue.path.length > 0 ? `${issue.path.join(".")}: ${issue.message}` : issue.message).join("; ");
|
|
296
|
+
return summary.length > 0 ? summary : null;
|
|
297
|
+
}
|
|
298
|
+
function throwValidationClientError(options) {
|
|
299
|
+
const issueSummary = formatValidationFailure(options.failure);
|
|
300
|
+
throw new ClientError({
|
|
301
|
+
kind: ClientErrorKind.Configuration,
|
|
302
|
+
code: options.code,
|
|
303
|
+
message: issueSummary ? `${options.messagePrefix}: ${issueSummary}.` : `${options.messagePrefix}.`,
|
|
304
|
+
recovery: UserRecovery.RestartFlow,
|
|
305
|
+
source: options.source,
|
|
306
|
+
cause: options.failure?.issues
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Validate an unknown input against a `@standard-schema`-compatible schema.
|
|
311
|
+
*
|
|
312
|
+
* This is the foundation-level entry point for SDK validation. It accepts
|
|
313
|
+
* any schema implementing `StandardSchemaV1` and returns a discriminated
|
|
314
|
+
* `ValidationResult`.
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* ```ts
|
|
318
|
+
* import { validateWithSchema } from "@securitydept/client";
|
|
319
|
+
* import { z } from "zod";
|
|
320
|
+
*
|
|
321
|
+
* const UserSchema = z.object({ name: z.string() });
|
|
322
|
+
* const result = await validateWithSchema(UserSchema, { name: "Alice" });
|
|
323
|
+
* if (result.success) {
|
|
324
|
+
* console.log(result.value.name);
|
|
325
|
+
* } else {
|
|
326
|
+
* console.error(result.issues);
|
|
327
|
+
* }
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
async function validateWithSchema(schema, input) {
|
|
331
|
+
const result = await schema["~standard"].validate(input);
|
|
332
|
+
if ("value" in result) return {
|
|
333
|
+
success: true,
|
|
334
|
+
value: result.value
|
|
335
|
+
};
|
|
336
|
+
return {
|
|
337
|
+
success: false,
|
|
338
|
+
issues: result.issues
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Validate an unknown input synchronously against a `@standard-schema`-compatible schema.
|
|
343
|
+
*
|
|
344
|
+
* Only use this when you know the schema's validate function returns synchronously.
|
|
345
|
+
* Throws if the schema returns a Promise.
|
|
346
|
+
*/
|
|
347
|
+
function validateWithSchemaSync(schema, input) {
|
|
348
|
+
const result = schema["~standard"].validate(input);
|
|
349
|
+
if (result instanceof Promise) throw new Error("validateWithSchemaSync: schema returned a Promise. Use validateWithSchema instead.");
|
|
350
|
+
if ("value" in result) return {
|
|
351
|
+
success: true,
|
|
352
|
+
value: result.value
|
|
353
|
+
};
|
|
354
|
+
return {
|
|
355
|
+
success: false,
|
|
356
|
+
issues: result.issues
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function validateTraitInput(options) {
|
|
360
|
+
if (options.optional === true && (options.value === void 0 || options.value === null)) return;
|
|
361
|
+
const result = validateWithSchemaSync(options.validator ?? options.bundledSchema, options.value);
|
|
362
|
+
if (result.success) return;
|
|
363
|
+
options.onInvalid(result);
|
|
364
|
+
}
|
|
365
|
+
//#endregion
|
|
366
|
+
//#region src/protocol/json-rpc/types.ts
|
|
367
|
+
const JsonRpcIdSchema = type("string | number");
|
|
368
|
+
const JsonRpcErrorPayloadSchema = type({
|
|
369
|
+
code: "number",
|
|
370
|
+
message: "string",
|
|
371
|
+
data: "unknown?"
|
|
372
|
+
});
|
|
373
|
+
const JsonRpcRequestMessageSchema = type({
|
|
374
|
+
jsonrpc: "\"2.0\"",
|
|
375
|
+
id: JsonRpcIdSchema,
|
|
376
|
+
method: "string",
|
|
377
|
+
params: "unknown?"
|
|
378
|
+
});
|
|
379
|
+
const JsonRpcNotificationMessageSchema = type({
|
|
380
|
+
jsonrpc: "\"2.0\"",
|
|
381
|
+
method: "string",
|
|
382
|
+
params: "unknown?"
|
|
383
|
+
});
|
|
384
|
+
const JsonRpcSuccessResponseMessageSchema = type({
|
|
385
|
+
jsonrpc: "\"2.0\"",
|
|
386
|
+
id: JsonRpcIdSchema,
|
|
387
|
+
result: "unknown"
|
|
388
|
+
});
|
|
389
|
+
const JsonRpcErrorResponseMessageSchema = type({
|
|
390
|
+
jsonrpc: "\"2.0\"",
|
|
391
|
+
id: JsonRpcIdSchema,
|
|
392
|
+
error: JsonRpcErrorPayloadSchema
|
|
393
|
+
});
|
|
394
|
+
const JsonRpcMessageSchema = JsonRpcRequestMessageSchema.or(JsonRpcNotificationMessageSchema).or(JsonRpcSuccessResponseMessageSchema).or(JsonRpcErrorResponseMessageSchema);
|
|
395
|
+
//#endregion
|
|
396
|
+
//#region src/protocol/json-rpc/create.ts
|
|
397
|
+
function createJsonRpcClient(options) {
|
|
398
|
+
return new JsonRpcClient(options);
|
|
399
|
+
}
|
|
400
|
+
function createJsonRpcServer(options) {
|
|
401
|
+
return new JsonRpcServer(options);
|
|
402
|
+
}
|
|
403
|
+
function createJsonRpcServerAndClient(options) {
|
|
404
|
+
return new JsonRpcServerAndClient(options);
|
|
405
|
+
}
|
|
406
|
+
var JsonRpcClient = class {
|
|
407
|
+
options;
|
|
408
|
+
onNotification;
|
|
409
|
+
notificationSubject = new RxEventSubject();
|
|
410
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
411
|
+
disposed = false;
|
|
412
|
+
nextRequestId = 1;
|
|
413
|
+
constructor(options) {
|
|
414
|
+
this.options = options;
|
|
415
|
+
this.onNotification = this.notificationSubject;
|
|
416
|
+
}
|
|
417
|
+
request(method, params, requestOptions = {}) {
|
|
418
|
+
if (this.disposed) return Promise.reject(createJsonRpcDisposedError("client"));
|
|
419
|
+
const id = this.nextRequestId++;
|
|
420
|
+
const timeoutMs = requestOptions.timeoutMs ?? this.options.defaultRequestTimeoutMs;
|
|
421
|
+
return new Promise((resolve, reject) => {
|
|
422
|
+
const pendingRequest = {
|
|
423
|
+
method,
|
|
424
|
+
reject,
|
|
425
|
+
resolve
|
|
426
|
+
};
|
|
427
|
+
if (timeoutMs !== void 0) pendingRequest.timeoutHandle = this.options.time.setTimeout(() => {
|
|
428
|
+
this.pendingRequests.delete(id);
|
|
429
|
+
reject(/* @__PURE__ */ new Error(`JSON-RPC request timed out for ${method}.`));
|
|
430
|
+
}, timeoutMs);
|
|
431
|
+
this.pendingRequests.set(id, pendingRequest);
|
|
432
|
+
try {
|
|
433
|
+
this.options.send({
|
|
434
|
+
jsonrpc: "2.0",
|
|
435
|
+
id,
|
|
436
|
+
method,
|
|
437
|
+
params
|
|
438
|
+
});
|
|
439
|
+
} catch (error) {
|
|
440
|
+
this.clearPendingRequest(id);
|
|
441
|
+
reject(error);
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
notify(method, params) {
|
|
446
|
+
if (this.disposed) return;
|
|
447
|
+
this.options.send({
|
|
448
|
+
jsonrpc: "2.0",
|
|
449
|
+
method,
|
|
450
|
+
params
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
receive(message) {
|
|
454
|
+
if (this.disposed) return;
|
|
455
|
+
for (const parsedMessage of parseIncomingMessages(message)) {
|
|
456
|
+
if (isJsonRpcResponseMessage(parsedMessage)) {
|
|
457
|
+
this.resolvePendingRequest(parsedMessage);
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
if (isJsonRpcNotificationMessage(parsedMessage)) this.notificationSubject.next({
|
|
461
|
+
method: parsedMessage.method,
|
|
462
|
+
params: parsedMessage.params
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
rejectAllPendingRequests(reason) {
|
|
467
|
+
for (const [id, pendingRequest] of this.pendingRequests) {
|
|
468
|
+
this.clearPendingRequest(id);
|
|
469
|
+
pendingRequest.reject(reason);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
dispose() {
|
|
473
|
+
if (this.disposed) return;
|
|
474
|
+
this.disposed = true;
|
|
475
|
+
this.rejectAllPendingRequests(/* @__PURE__ */ new Error("JSON-RPC client disposed."));
|
|
476
|
+
this.notificationSubject.complete();
|
|
477
|
+
}
|
|
478
|
+
[SYMBOL_DISPOSE]() {
|
|
479
|
+
this.dispose();
|
|
480
|
+
}
|
|
481
|
+
resolvePendingRequest(message) {
|
|
482
|
+
const pendingRequest = this.pendingRequests.get(message.id);
|
|
483
|
+
if (!pendingRequest) return;
|
|
484
|
+
this.clearPendingRequest(message.id);
|
|
485
|
+
if ("error" in message) {
|
|
486
|
+
pendingRequest.reject(/* @__PURE__ */ new Error(`JSON-RPC request failed for ${pendingRequest.method}: ${message.error.message}`));
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
pendingRequest.resolve(message.result);
|
|
490
|
+
}
|
|
491
|
+
clearPendingRequest(id) {
|
|
492
|
+
const pendingRequest = this.pendingRequests.get(id);
|
|
493
|
+
if (!pendingRequest) return;
|
|
494
|
+
if (pendingRequest.timeoutHandle !== void 0) this.options.time.clearTimeout(pendingRequest.timeoutHandle);
|
|
495
|
+
this.pendingRequests.delete(id);
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
var JsonRpcServer = class {
|
|
499
|
+
options;
|
|
500
|
+
onRequest;
|
|
501
|
+
onNotification;
|
|
502
|
+
requestSubject = new RxEventSubject();
|
|
503
|
+
notificationSubject = new RxEventSubject();
|
|
504
|
+
disposed = false;
|
|
505
|
+
constructor(options) {
|
|
506
|
+
this.options = options;
|
|
507
|
+
this.onRequest = this.requestSubject;
|
|
508
|
+
this.onNotification = this.notificationSubject;
|
|
509
|
+
}
|
|
510
|
+
notify(method, params) {
|
|
511
|
+
if (this.disposed) return;
|
|
512
|
+
this.options.send({
|
|
513
|
+
jsonrpc: "2.0",
|
|
514
|
+
method,
|
|
515
|
+
params
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
receive(message) {
|
|
519
|
+
if (this.disposed) return;
|
|
520
|
+
for (const parsedMessage of parseIncomingMessages(message)) {
|
|
521
|
+
if (isJsonRpcRequestMessage(parsedMessage)) {
|
|
522
|
+
this.requestSubject.next({
|
|
523
|
+
id: parsedMessage.id,
|
|
524
|
+
method: parsedMessage.method,
|
|
525
|
+
params: parsedMessage.params
|
|
526
|
+
});
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
if (isJsonRpcNotificationMessage(parsedMessage)) this.notificationSubject.next({
|
|
530
|
+
method: parsedMessage.method,
|
|
531
|
+
params: parsedMessage.params
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
respondSuccess(id, result) {
|
|
536
|
+
if (this.disposed) return;
|
|
537
|
+
this.options.send({
|
|
538
|
+
jsonrpc: "2.0",
|
|
539
|
+
id,
|
|
540
|
+
result
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
respondError(id, error) {
|
|
544
|
+
if (this.disposed) return;
|
|
545
|
+
this.options.send({
|
|
546
|
+
jsonrpc: "2.0",
|
|
547
|
+
id,
|
|
548
|
+
error
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
dispose() {
|
|
552
|
+
if (this.disposed) return;
|
|
553
|
+
this.disposed = true;
|
|
554
|
+
this.requestSubject.complete();
|
|
555
|
+
this.notificationSubject.complete();
|
|
556
|
+
}
|
|
557
|
+
[SYMBOL_DISPOSE]() {
|
|
558
|
+
this.dispose();
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
var JsonRpcServerAndClient = class {
|
|
562
|
+
onNotification;
|
|
563
|
+
onRequest;
|
|
564
|
+
client;
|
|
565
|
+
server;
|
|
566
|
+
disposed = false;
|
|
567
|
+
constructor(options) {
|
|
568
|
+
this.client = new JsonRpcClient(options);
|
|
569
|
+
this.server = new JsonRpcServer(options);
|
|
570
|
+
this.onNotification = this.server.onNotification;
|
|
571
|
+
this.onRequest = this.server.onRequest;
|
|
572
|
+
}
|
|
573
|
+
request(method, params, options) {
|
|
574
|
+
return this.client.request(method, params, options);
|
|
575
|
+
}
|
|
576
|
+
notify(method, params) {
|
|
577
|
+
this.client.notify(method, params);
|
|
578
|
+
}
|
|
579
|
+
receive(message) {
|
|
580
|
+
for (const parsedMessage of parseIncomingMessages(message)) {
|
|
581
|
+
if (isJsonRpcResponseMessage(parsedMessage)) {
|
|
582
|
+
this.client.receive(parsedMessage);
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
this.server.receive(parsedMessage);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
rejectAllPendingRequests(reason) {
|
|
589
|
+
this.client.rejectAllPendingRequests(reason);
|
|
590
|
+
}
|
|
591
|
+
respondSuccess(id, result) {
|
|
592
|
+
this.server.respondSuccess(id, result);
|
|
593
|
+
}
|
|
594
|
+
respondError(id, error) {
|
|
595
|
+
this.server.respondError(id, error);
|
|
596
|
+
}
|
|
597
|
+
dispose() {
|
|
598
|
+
if (this.disposed) return;
|
|
599
|
+
this.disposed = true;
|
|
600
|
+
this.client.dispose();
|
|
601
|
+
this.server.dispose();
|
|
602
|
+
}
|
|
603
|
+
[SYMBOL_DISPOSE]() {
|
|
604
|
+
this.dispose();
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
function parseIncomingMessages(message) {
|
|
608
|
+
if (!Array.isArray(message)) return parseJsonRpcMessage(message);
|
|
609
|
+
const parsedMessages = [];
|
|
610
|
+
for (const entry of message) parsedMessages.push(...parseJsonRpcMessage(entry));
|
|
611
|
+
return parsedMessages;
|
|
612
|
+
}
|
|
613
|
+
function parseJsonRpcMessage(message) {
|
|
614
|
+
const result = validateWithSchemaSync(JsonRpcMessageSchema, message);
|
|
615
|
+
return result.success ? [result.value] : [];
|
|
616
|
+
}
|
|
617
|
+
function isJsonRpcRequestMessage(message) {
|
|
618
|
+
return "method" in message && "id" in message;
|
|
619
|
+
}
|
|
620
|
+
function isJsonRpcResponseMessage(message) {
|
|
621
|
+
return "id" in message && !("method" in message);
|
|
622
|
+
}
|
|
623
|
+
function isJsonRpcNotificationMessage(message) {
|
|
624
|
+
return "method" in message && !("id" in message);
|
|
625
|
+
}
|
|
626
|
+
function createJsonRpcDisposedError(role) {
|
|
627
|
+
return /* @__PURE__ */ new Error(`JSON-RPC ${role} disposed.`);
|
|
628
|
+
}
|
|
629
|
+
//#endregion
|
|
630
|
+
//#region src/popup/session.ts
|
|
631
|
+
const PopupSessionControlMethod = {
|
|
632
|
+
Ready: "securitydept.popup.session.ready",
|
|
633
|
+
Ping: "securitydept.popup.session.ping",
|
|
634
|
+
Pong: "securitydept.popup.session.pong"
|
|
635
|
+
};
|
|
636
|
+
const DEFAULT_READY_TIMEOUT_MS = 1e4;
|
|
637
|
+
const DEFAULT_HEARTBEAT_INTERVAL_MS = 5e3;
|
|
638
|
+
const DEFAULT_HEARTBEAT_TIMEOUT_MS = 15e3;
|
|
639
|
+
var PopupClientSession = class {
|
|
640
|
+
failure = createSignal(null);
|
|
641
|
+
isActive = createSignal(false);
|
|
642
|
+
init$ = new ReplaySubject();
|
|
643
|
+
destroyed = createSignal(false);
|
|
644
|
+
destroyed$ = from(this.destroyed).pipe(distinctUntilChanged(), filter(Boolean), shareReplay(1));
|
|
645
|
+
constructor(options) {
|
|
646
|
+
const timestampScheduler = createAsyncSchedulerWithTimestampProvider(options.time);
|
|
647
|
+
const ready$ = new Subject();
|
|
648
|
+
const pong$ = new Subject();
|
|
649
|
+
const closed$ = from(options.closedStream).pipe(filter(Boolean));
|
|
650
|
+
const ping$ = merge(ready$.pipe(filter(Boolean)), pong$.pipe(filter(Boolean))).pipe(delayWhen(() => timer(options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS, timestampScheduler)));
|
|
651
|
+
from(this.init$).pipe(exhaustMap(() => from(options.jsonRpc.onNotification).pipe(filter(function isReadyNotification(event) {
|
|
652
|
+
return event.method === PopupSessionControlMethod.Ready;
|
|
653
|
+
}), take(1), timeout({
|
|
654
|
+
each: options.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS,
|
|
655
|
+
scheduler: timestampScheduler
|
|
656
|
+
}), map(() => true), catchError(() => of(false)))), takeUntil(this.destroyed$)).subscribe(ready$);
|
|
657
|
+
ping$.pipe(exhaustMap(() => from(options.jsonRpc.onNotification).pipe(filter(function isPongNotification(event) {
|
|
658
|
+
return event.method === PopupSessionControlMethod.Pong;
|
|
659
|
+
}), take(1), timeout({
|
|
660
|
+
first: options.heartbeatTimeoutMs ?? DEFAULT_HEARTBEAT_TIMEOUT_MS,
|
|
661
|
+
scheduler: timestampScheduler
|
|
662
|
+
}), map(() => true), catchError(() => of(false)))), takeUntil(this.destroyed$)).subscribe(pong$);
|
|
663
|
+
merge(closed$.pipe(map(() => new ClientError({
|
|
664
|
+
kind: ClientErrorKind.Authorization,
|
|
665
|
+
code: PopupErrorCode.Closed,
|
|
666
|
+
message: "Popup window was closed before completing the login flow.",
|
|
667
|
+
recovery: UserRecovery.RestartFlow,
|
|
668
|
+
source: "popup"
|
|
669
|
+
}))), ready$.pipe(map((success) => !success ? new ClientError({
|
|
670
|
+
kind: ClientErrorKind.Protocol,
|
|
671
|
+
message: "Did not receive ready notification from popup within the expected time.",
|
|
672
|
+
recovery: UserRecovery.RestartFlow,
|
|
673
|
+
source: "popup"
|
|
674
|
+
}) : null)), pong$.pipe(map((success) => !success ? new ClientError({
|
|
675
|
+
kind: ClientErrorKind.Protocol,
|
|
676
|
+
message: "Did not receive pong notification from popup within the expected time.",
|
|
677
|
+
recovery: UserRecovery.RestartFlow,
|
|
678
|
+
source: "popup"
|
|
679
|
+
}) : null))).pipe(takeUntil(this.destroyed$), shareReplay({
|
|
680
|
+
bufferSize: 1,
|
|
681
|
+
refCount: false
|
|
682
|
+
})).subscribe((error) => this.failure.set(error));
|
|
683
|
+
merge(closed$.pipe(map(() => false)), ready$.pipe(map(Boolean)), pong$.pipe(map(Boolean))).pipe(takeUntil(this.destroyed$)).subscribe((nextIsActive) => {
|
|
684
|
+
this.isActive.set(nextIsActive);
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
init() {
|
|
688
|
+
this.init$.next(true);
|
|
689
|
+
}
|
|
690
|
+
dispose() {
|
|
691
|
+
this.destroyed.set(true);
|
|
692
|
+
}
|
|
693
|
+
[SYMBOL_DISPOSE]() {
|
|
694
|
+
this.dispose();
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
var PopupServerSession = class {
|
|
698
|
+
init$ = new ReplaySubject();
|
|
699
|
+
destroyed$$ = new BehaviorSubject(false);
|
|
700
|
+
destroyed$ = this.destroyed$$.pipe(distinctUntilChanged(), filter(Boolean), shareReplay(1));
|
|
701
|
+
constructor(options) {
|
|
702
|
+
const ready$ = from(this.init$);
|
|
703
|
+
const pong$ = from(options.jsonRpc.onNotification).pipe(filter(function isPing(event) {
|
|
704
|
+
return event.method === PopupSessionControlMethod.Ping;
|
|
705
|
+
}));
|
|
706
|
+
merge(ready$.pipe(map(function createReadyNotify() {
|
|
707
|
+
return { method: PopupSessionControlMethod.Ready };
|
|
708
|
+
})), pong$.pipe(map(function createPongNotify() {
|
|
709
|
+
return { method: PopupSessionControlMethod.Pong };
|
|
710
|
+
}))).pipe(takeUntil(this.destroyed$)).subscribe((event) => {
|
|
711
|
+
options.jsonRpc.notify(event.method, event.params);
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
init() {
|
|
715
|
+
this.init$.next(true);
|
|
716
|
+
}
|
|
717
|
+
dispose() {
|
|
718
|
+
this.destroyed$$.next(true);
|
|
719
|
+
}
|
|
720
|
+
[SYMBOL_DISPOSE]() {
|
|
721
|
+
this.dispose();
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
//#endregion
|
|
725
|
+
//#region src/popup/handle.ts
|
|
726
|
+
/**
|
|
727
|
+
* popup opener
|
|
728
|
+
*/
|
|
729
|
+
var PopupClientWindowHandle = class {
|
|
730
|
+
messaging;
|
|
731
|
+
disposed = false;
|
|
732
|
+
jsonRpc;
|
|
733
|
+
session;
|
|
734
|
+
incomingSubscription;
|
|
735
|
+
closePopup;
|
|
736
|
+
constructor(options) {
|
|
737
|
+
this.messaging = options.messaging;
|
|
738
|
+
this.closePopup = options.close;
|
|
739
|
+
this.jsonRpc = createJsonRpcClient({
|
|
740
|
+
send: (message) => {
|
|
741
|
+
options.messaging.outgoing.next(message);
|
|
742
|
+
},
|
|
743
|
+
time: options.time
|
|
744
|
+
});
|
|
745
|
+
this.session = new PopupClientSession({
|
|
746
|
+
jsonRpc: this.jsonRpc,
|
|
747
|
+
time: options.time,
|
|
748
|
+
closedStream: options.closedStream
|
|
749
|
+
});
|
|
750
|
+
this.incomingSubscription = options.messaging.incoming.subscribe({ next: (message) => {
|
|
751
|
+
this.jsonRpc.receive(message);
|
|
752
|
+
} });
|
|
753
|
+
this.session.init();
|
|
754
|
+
}
|
|
755
|
+
get onNotification() {
|
|
756
|
+
return this.jsonRpc.onNotification;
|
|
757
|
+
}
|
|
758
|
+
get failure() {
|
|
759
|
+
return this.session.failure;
|
|
760
|
+
}
|
|
761
|
+
get isActive() {
|
|
762
|
+
return this.session.isActive;
|
|
763
|
+
}
|
|
764
|
+
async notify(method, params) {
|
|
765
|
+
this.jsonRpc.notify(method, params);
|
|
766
|
+
}
|
|
767
|
+
request(method, params, options) {
|
|
768
|
+
return this.jsonRpc.request(method, params, options);
|
|
769
|
+
}
|
|
770
|
+
close() {
|
|
771
|
+
this.closePopup();
|
|
772
|
+
}
|
|
773
|
+
dispose() {
|
|
774
|
+
if (this.disposed) return;
|
|
775
|
+
this.disposed = true;
|
|
776
|
+
this.session.dispose();
|
|
777
|
+
this.jsonRpc.dispose();
|
|
778
|
+
this.incomingSubscription.unsubscribe();
|
|
779
|
+
this.messaging.outgoing.complete();
|
|
780
|
+
}
|
|
781
|
+
[SYMBOL_DISPOSE]() {
|
|
782
|
+
this.dispose();
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
/**
|
|
786
|
+
* popup target
|
|
787
|
+
*/
|
|
788
|
+
var PopupServerWindowHandle = class {
|
|
789
|
+
messaging;
|
|
790
|
+
disposed = false;
|
|
791
|
+
jsonRpc;
|
|
792
|
+
session;
|
|
793
|
+
incomingSubscription;
|
|
794
|
+
closePopup;
|
|
795
|
+
constructor(options) {
|
|
796
|
+
this.messaging = options.messaging;
|
|
797
|
+
this.closePopup = options.close;
|
|
798
|
+
this.jsonRpc = createJsonRpcServer({ send: (message) => {
|
|
799
|
+
options.messaging.outgoing.next(message);
|
|
800
|
+
} });
|
|
801
|
+
this.session = new PopupServerSession({ jsonRpc: this.jsonRpc });
|
|
802
|
+
this.incomingSubscription = options.messaging.incoming.subscribe({ next: (message) => {
|
|
803
|
+
this.jsonRpc.receive(message);
|
|
804
|
+
} });
|
|
805
|
+
this.session.init();
|
|
806
|
+
}
|
|
807
|
+
get onNotification() {
|
|
808
|
+
return this.jsonRpc.onNotification;
|
|
809
|
+
}
|
|
810
|
+
get onRequest() {
|
|
811
|
+
return this.jsonRpc.onRequest;
|
|
812
|
+
}
|
|
813
|
+
notify(method, params) {
|
|
814
|
+
this.jsonRpc.notify(method, params);
|
|
815
|
+
return Promise.resolve();
|
|
816
|
+
}
|
|
817
|
+
close() {
|
|
818
|
+
this.closePopup();
|
|
819
|
+
}
|
|
820
|
+
dispose() {
|
|
821
|
+
if (this.disposed) return;
|
|
822
|
+
this.disposed = true;
|
|
823
|
+
this.session.dispose();
|
|
824
|
+
this.jsonRpc.dispose();
|
|
825
|
+
this.incomingSubscription.unsubscribe();
|
|
826
|
+
this.messaging.outgoing.complete();
|
|
827
|
+
}
|
|
828
|
+
[SYMBOL_DISPOSE]() {
|
|
829
|
+
this.dispose();
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
//#endregion
|
|
833
|
+
//#region src/popup/index.ts
|
|
834
|
+
const PopupAttachFailureReason = {
|
|
835
|
+
MissingOpener: "missing_opener",
|
|
836
|
+
MissingOrigin: "missing_origin"
|
|
837
|
+
};
|
|
838
|
+
const PopupTraitSchema = type({
|
|
839
|
+
open: "Function",
|
|
840
|
+
attach: "Function"
|
|
841
|
+
});
|
|
842
|
+
const POPUP_TRAIT_TOKEN = new SecuritydeptInjectionToken("POPUP_TRAIT_TOKEN");
|
|
843
|
+
//#endregion
|
|
844
|
+
//#region src/protocol/compat-fragment.ts
|
|
845
|
+
const SECURITYDEPT_COMPAT_FRAGMENT_VERSION = "v1";
|
|
846
|
+
function appendOrReplaceCompatFragment(input, options, update) {
|
|
847
|
+
const blocks = splitFragmentBlocks(readUriFragmentHash(input));
|
|
848
|
+
if (isCompatFragmentBlock(blocks.at(-1) ?? "")) blocks.pop();
|
|
849
|
+
blocks.push(buildCompatFragmentBlock(options));
|
|
850
|
+
const fragment = joinFragmentBlocks(blocks);
|
|
851
|
+
return {
|
|
852
|
+
fragment,
|
|
853
|
+
url: update(input, fragment)
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
function parseCompatFragment(input) {
|
|
857
|
+
const block = splitFragmentBlocks(readUriFragmentHash(input)).at(-1);
|
|
858
|
+
if (!block || !isCompatFragmentBlock(block)) return null;
|
|
859
|
+
const blockParameters = new URLSearchParams(block);
|
|
860
|
+
blockParameters.delete("securitydept");
|
|
861
|
+
const parameters = {};
|
|
862
|
+
blockParameters.forEach((value, key) => {
|
|
863
|
+
parameters[key] = value;
|
|
864
|
+
});
|
|
865
|
+
return {
|
|
866
|
+
payload: blockParameters.toString(),
|
|
867
|
+
parameters
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
function takeCompatFragment(input, options) {
|
|
871
|
+
const currentFragment = readUriFragmentHash(input);
|
|
872
|
+
const blocks = splitFragmentBlocks(currentFragment);
|
|
873
|
+
const block = blocks.at(-1);
|
|
874
|
+
if (!block || !isCompatFragmentBlock(block)) return {
|
|
875
|
+
compatFragment: null,
|
|
876
|
+
fragment: currentFragment,
|
|
877
|
+
url: input
|
|
878
|
+
};
|
|
879
|
+
const compatFragment = parseCompatFragment(block);
|
|
880
|
+
if (!compatFragment || options.condition && !options.condition(compatFragment)) return {
|
|
881
|
+
compatFragment: null,
|
|
882
|
+
fragment: currentFragment,
|
|
883
|
+
url: input
|
|
884
|
+
};
|
|
885
|
+
blocks.pop();
|
|
886
|
+
const fragment = joinFragmentBlocks(blocks);
|
|
887
|
+
return {
|
|
888
|
+
compatFragment,
|
|
889
|
+
fragment,
|
|
890
|
+
url: options.update(input, fragment)
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
function isCompatFragmentBlock(block) {
|
|
894
|
+
return new URLSearchParams(block.startsWith("#") ? block.slice(1) : block).get("securitydept") === "v1";
|
|
895
|
+
}
|
|
896
|
+
function readUriFragmentHash(input) {
|
|
897
|
+
if (typeof input === "string") return input.startsWith("#") ? input : `#${input}`;
|
|
898
|
+
return input.hash;
|
|
899
|
+
}
|
|
900
|
+
function joinFragmentBlocks(blocks) {
|
|
901
|
+
return blocks.length > 0 ? `#${blocks.join("#")}` : "";
|
|
902
|
+
}
|
|
903
|
+
function splitFragmentBlocks(fragment) {
|
|
904
|
+
if (fragment === "#") return [""];
|
|
905
|
+
const normalized = fragment.startsWith("#") ? fragment.slice(1) : fragment;
|
|
906
|
+
return normalized ? normalized.split("#") : [];
|
|
907
|
+
}
|
|
908
|
+
function buildCompatFragmentBlock(options) {
|
|
909
|
+
const block = new URLSearchParams();
|
|
910
|
+
block.set("securitydept", "v1");
|
|
911
|
+
const payload = typeof options.payload === "string" ? new URLSearchParams(options.payload.startsWith("#") ? options.payload.slice(1) : options.payload) : new URLSearchParams(options.payload);
|
|
912
|
+
for (const [key, value] of payload) if (key !== "securitydept") block.append(key, value);
|
|
913
|
+
return block.toString();
|
|
914
|
+
}
|
|
915
|
+
//#endregion
|
|
916
|
+
//#region src/router/router.ts
|
|
917
|
+
const RouterNavigationIntent = {
|
|
918
|
+
AuthRedirect: "auth_redirect",
|
|
919
|
+
PostAuthRedirect: "post_auth_redirect",
|
|
920
|
+
CallbackCleanup: "callback_cleanup",
|
|
921
|
+
ExternalOpen: "external_open"
|
|
922
|
+
};
|
|
923
|
+
const RouterNavigationMode = {
|
|
924
|
+
Push: "push",
|
|
925
|
+
Replace: "replace",
|
|
926
|
+
External: "external"
|
|
927
|
+
};
|
|
928
|
+
async function takeCompatFragmentFromRouter(router, options = {}) {
|
|
929
|
+
const currentUrl = router.currentUrl();
|
|
930
|
+
if (!currentUrl) return null;
|
|
931
|
+
const { compatFragment, url: cleanedUrl } = takeCompatFragment(currentUrl, {
|
|
932
|
+
condition: options.condition,
|
|
933
|
+
update: (ref, hash) => ref.setHash(hash)
|
|
934
|
+
});
|
|
935
|
+
if (!compatFragment) return null;
|
|
936
|
+
await router.navigate({
|
|
937
|
+
url: cleanedUrl,
|
|
938
|
+
intent: RouterNavigationIntent.CallbackCleanup,
|
|
939
|
+
mode: RouterNavigationMode.Replace
|
|
940
|
+
});
|
|
941
|
+
return compatFragment;
|
|
942
|
+
}
|
|
943
|
+
const RouterTraitSchema = type({
|
|
944
|
+
currentUrl: "Function",
|
|
945
|
+
navigate: "Function"
|
|
946
|
+
});
|
|
947
|
+
const ROUTER_TRAIT_TOKEN = new SecuritydeptInjectionToken("ROUTER_TRAIT_TOKEN");
|
|
948
|
+
//#endregion
|
|
949
|
+
//#region src/scheduling/types.ts
|
|
950
|
+
const TimeTraitSchema = type({
|
|
951
|
+
now: "Function",
|
|
952
|
+
setTimeout: "Function",
|
|
953
|
+
clearTimeout: "Function"
|
|
954
|
+
});
|
|
955
|
+
const TIME_TRAIT_TOKEN = new SecuritydeptInjectionToken("TIME_TRAIT_TOKEN");
|
|
956
|
+
const IDLE_CALLBACK_TRAIT_TOKEN = new SecuritydeptInjectionToken("IDLE_CALLBACK_TRAIT_TOKEN");
|
|
957
|
+
const IdleCallbackTraitSchema = type({
|
|
958
|
+
requestIdleCallback: "Function",
|
|
959
|
+
cancelIdleCallback: "Function"
|
|
960
|
+
});
|
|
961
|
+
//#endregion
|
|
962
|
+
//#region src/span/types.ts
|
|
963
|
+
const SpanCreateOptionsSchema = type({
|
|
964
|
+
idFactory: "Function",
|
|
965
|
+
attributes: "object"
|
|
966
|
+
});
|
|
967
|
+
const SpanTraitSchema = type({
|
|
968
|
+
id: "string",
|
|
969
|
+
fork: "Function",
|
|
970
|
+
attributes: "object"
|
|
971
|
+
});
|
|
972
|
+
const SPAN_TRAIT_TOKEN = new SecuritydeptInjectionToken("SPAN_TRAIT_TOKEN");
|
|
973
|
+
//#endregion
|
|
974
|
+
//#region src/span/span.ts
|
|
975
|
+
function createDefaultSpanId() {
|
|
976
|
+
return `span_${v7()}`;
|
|
977
|
+
}
|
|
978
|
+
var DefaultSpan = class DefaultSpan {
|
|
979
|
+
id;
|
|
980
|
+
parent;
|
|
981
|
+
_attributes;
|
|
982
|
+
_idFactory;
|
|
983
|
+
get attributes() {
|
|
984
|
+
return this._attributes;
|
|
985
|
+
}
|
|
986
|
+
constructor(parent, idFactory, attributes) {
|
|
987
|
+
this._idFactory = idFactory;
|
|
988
|
+
this.id = idFactory();
|
|
989
|
+
this.parent = parent;
|
|
990
|
+
this._attributes = attributes;
|
|
991
|
+
}
|
|
992
|
+
static root(options = {}) {
|
|
993
|
+
return new DefaultSpan(void 0, options.idFactory ?? createDefaultSpanId, Object.freeze({ ...options.attributes ?? {} }));
|
|
994
|
+
}
|
|
995
|
+
fork(options = {}) {
|
|
996
|
+
return new DefaultSpan(this, options.idFactory ?? this._idFactory, Object.freeze({ ...options.attributes }));
|
|
997
|
+
}
|
|
998
|
+
setAttributes(attributes) {
|
|
999
|
+
this._attributes = Object.freeze({
|
|
1000
|
+
...this._attributes,
|
|
1001
|
+
...attributes
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
};
|
|
1005
|
+
function createRootSpan(options = {}) {
|
|
1006
|
+
const { validators, ...resolvedOptions } = options;
|
|
1007
|
+
const resolvedCreateOptions = {
|
|
1008
|
+
idFactory: resolvedOptions.idFactory ?? createDefaultSpanId,
|
|
1009
|
+
attributes: resolvedOptions.attributes ?? {}
|
|
1010
|
+
};
|
|
1011
|
+
validateTraitInput({
|
|
1012
|
+
value: resolvedCreateOptions,
|
|
1013
|
+
bundledSchema: SpanCreateOptionsSchema,
|
|
1014
|
+
validator: validators,
|
|
1015
|
+
onInvalid: (failure) => throwValidationClientError({
|
|
1016
|
+
code: "span.invalid_options",
|
|
1017
|
+
source: "span",
|
|
1018
|
+
messagePrefix: "createRootSpan could not validate spanCreateOptions",
|
|
1019
|
+
failure
|
|
1020
|
+
})
|
|
1021
|
+
});
|
|
1022
|
+
return DefaultSpan.root(resolvedCreateOptions);
|
|
1023
|
+
}
|
|
1024
|
+
//#endregion
|
|
1025
|
+
//#region src/std/events/from-abort.ts
|
|
1026
|
+
/**
|
|
1027
|
+
* Adapt an `AbortSignal`-like source into an event stream of abort reasons.
|
|
1028
|
+
*/
|
|
1029
|
+
function abortSignalToEventStream(signal) {
|
|
1030
|
+
return RxEventStream.fromObservableInput(merge(signal.aborted ? of(signal.reason) : EMPTY, fromEventPattern((handler) => {
|
|
1031
|
+
signal.addEventListener?.("abort", handler);
|
|
1032
|
+
}, (handler) => {
|
|
1033
|
+
signal.removeEventListener?.("abort", handler);
|
|
1034
|
+
}).pipe(map(() => signal.reason))));
|
|
1035
|
+
}
|
|
1036
|
+
//#endregion
|
|
1037
|
+
//#region src/std/cancellation/abort-signal.ts
|
|
1038
|
+
function abortSignalToCancellationToken(signal) {
|
|
1039
|
+
if (!signal) return;
|
|
1040
|
+
return {
|
|
1041
|
+
get isCancellationRequested() {
|
|
1042
|
+
return signal.aborted;
|
|
1043
|
+
},
|
|
1044
|
+
get reason() {
|
|
1045
|
+
return signal.reason;
|
|
1046
|
+
},
|
|
1047
|
+
get cancellationError() {
|
|
1048
|
+
return signal.aborted ? new ClientError({
|
|
1049
|
+
kind: ClientErrorKind.Cancelled,
|
|
1050
|
+
message: "Request was cancelled via AbortSignal",
|
|
1051
|
+
code: "client.cancelled",
|
|
1052
|
+
source: ClientErrorSource.Transport,
|
|
1053
|
+
cause: signal.reason
|
|
1054
|
+
}) : void 0;
|
|
1055
|
+
},
|
|
1056
|
+
onCancellationRequested(listener) {
|
|
1057
|
+
const subscription = abortSignalToEventStream(signal).subscribe({ next: (reason) => listener({
|
|
1058
|
+
reason,
|
|
1059
|
+
cancellationError: new ClientError({
|
|
1060
|
+
kind: ClientErrorKind.Cancelled,
|
|
1061
|
+
message: "Request was cancelled via AbortSignal",
|
|
1062
|
+
code: "client.cancelled",
|
|
1063
|
+
source: ClientErrorSource.Transport,
|
|
1064
|
+
cause: reason
|
|
1065
|
+
})
|
|
1066
|
+
}) });
|
|
1067
|
+
return {
|
|
1068
|
+
dispose() {
|
|
1069
|
+
subscription.unsubscribe();
|
|
1070
|
+
},
|
|
1071
|
+
[SYMBOL_DISPOSE]: () => {
|
|
1072
|
+
subscription.unsubscribe();
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
},
|
|
1076
|
+
[SYMBOL_OBSERVABLE]() {
|
|
1077
|
+
return from(abortSignalToEventStream(signal)).pipe(map((reason) => ({
|
|
1078
|
+
reason,
|
|
1079
|
+
cancellationError: new ClientError({
|
|
1080
|
+
kind: ClientErrorKind.Cancelled,
|
|
1081
|
+
message: "Request was cancelled via AbortSignal",
|
|
1082
|
+
code: "client.cancelled",
|
|
1083
|
+
source: ClientErrorSource.Transport,
|
|
1084
|
+
cause: reason
|
|
1085
|
+
})
|
|
1086
|
+
})));
|
|
1087
|
+
},
|
|
1088
|
+
throwIfCancellationRequested() {
|
|
1089
|
+
if (signal.aborted) throw new ClientError({
|
|
1090
|
+
kind: ClientErrorKind.Cancelled,
|
|
1091
|
+
message: "Request was cancelled via AbortSignal",
|
|
1092
|
+
code: "client.cancelled",
|
|
1093
|
+
source: ClientErrorSource.Transport,
|
|
1094
|
+
cause: signal.reason
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
function cancellationTokenToAbortSignal(token) {
|
|
1100
|
+
if (!token) return;
|
|
1101
|
+
const controller = new AbortController();
|
|
1102
|
+
if (token.isCancellationRequested) controller.abort(token.reason);
|
|
1103
|
+
let subscription = token.onCancellationRequested(({ reason }) => {
|
|
1104
|
+
controller.abort(reason);
|
|
1105
|
+
});
|
|
1106
|
+
const dispose = () => {
|
|
1107
|
+
if (subscription) {
|
|
1108
|
+
subscription.dispose();
|
|
1109
|
+
subscription = null;
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
return {
|
|
1113
|
+
signal: controller.signal,
|
|
1114
|
+
dispose,
|
|
1115
|
+
[SYMBOL_DISPOSE]: dispose
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
function normalizeAbortError(token, error) {
|
|
1119
|
+
if (!isAbortError(error) && !token?.isCancellationRequested) return error;
|
|
1120
|
+
if (token?.cancellationError) return token.cancellationError;
|
|
1121
|
+
return new ClientError({
|
|
1122
|
+
kind: ClientErrorKind.Cancelled,
|
|
1123
|
+
message: "HTTP request was cancelled",
|
|
1124
|
+
code: "client.cancelled",
|
|
1125
|
+
source: ClientErrorSource.Transport,
|
|
1126
|
+
cause: token?.reason ?? error
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
function isAbortError(error) {
|
|
1130
|
+
return typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
|
|
1131
|
+
}
|
|
1132
|
+
const TimeForStdCreateOptionsSchema = type({ host: type({
|
|
1133
|
+
Date: type({ now: "Function" }),
|
|
1134
|
+
setTimeout: "Function",
|
|
1135
|
+
clearTimeout: "Function"
|
|
1136
|
+
}) });
|
|
1137
|
+
function createTimeForStd(options = {}) {
|
|
1138
|
+
const { validators, ...createOptions } = options;
|
|
1139
|
+
const resolvedCreateOptions = { host: createOptions.host ?? globalThis };
|
|
1140
|
+
validateTraitInput({
|
|
1141
|
+
value: resolvedCreateOptions,
|
|
1142
|
+
bundledSchema: TimeForStdCreateOptionsSchema,
|
|
1143
|
+
validator: validators,
|
|
1144
|
+
onInvalid: (failure) => throwValidationClientError({
|
|
1145
|
+
code: "time.invalid_std_options",
|
|
1146
|
+
source: "time",
|
|
1147
|
+
messagePrefix: "createTimeForStd could not validate timeForStdCreateOptions",
|
|
1148
|
+
failure
|
|
1149
|
+
})
|
|
1150
|
+
});
|
|
1151
|
+
const host = resolvedCreateOptions.host;
|
|
1152
|
+
return {
|
|
1153
|
+
now: () => host.Date.now(),
|
|
1154
|
+
setTimeout: (handler, delayMs) => host.setTimeout(handler, delayMs),
|
|
1155
|
+
clearTimeout: (handle) => host.clearTimeout(handle)
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
//#endregion
|
|
1159
|
+
//#region src/transport/types.ts
|
|
1160
|
+
const TransportErrorCode = {
|
|
1161
|
+
RequestFailed: "transport.request_failed",
|
|
1162
|
+
ResponseDecodeFailed: "transport.response_decode_failed",
|
|
1163
|
+
InvalidStdFetchOptions: "transport.invalid_std_fetch_options",
|
|
1164
|
+
InvalidAngularOptions: "transport.invalid_angular_options"
|
|
1165
|
+
};
|
|
1166
|
+
const BaseTransportTraitSchema = type({ execute: "Function" });
|
|
1167
|
+
const TRANSPORT_TRAIT_TOKEN = new SecuritydeptInjectionToken("TRANSPORT_TRAIT_TOKEN");
|
|
1168
|
+
//#endregion
|
|
1169
|
+
//#region src/std/transport/base-transport.ts
|
|
1170
|
+
const FetchTransportRedirectKind = {
|
|
1171
|
+
Follow: "follow",
|
|
1172
|
+
Manual: "manual"
|
|
1173
|
+
};
|
|
1174
|
+
const BaseTransportForStdFetchCreateOptionsSchema = type({
|
|
1175
|
+
redirect: "\"follow\" | \"manual\"",
|
|
1176
|
+
fetch: "Function",
|
|
1177
|
+
baseUrl: "unknown"
|
|
1178
|
+
});
|
|
1179
|
+
/**
|
|
1180
|
+
* Default `BaseTransportTrait` backed by the global `fetch` API.
|
|
1181
|
+
*
|
|
1182
|
+
* The caller is responsible for ensuring `fetch` is available in the runtime.
|
|
1183
|
+
* No polyfill is injected.
|
|
1184
|
+
*/
|
|
1185
|
+
function createBaseTransportForStdFetch(options = {}) {
|
|
1186
|
+
const { validators, ...createOptions } = options;
|
|
1187
|
+
const resolvedCreateOptions = {
|
|
1188
|
+
redirect: createOptions.redirect ?? FetchTransportRedirectKind.Manual,
|
|
1189
|
+
fetch: createOptions.fetch ?? fetch,
|
|
1190
|
+
baseUrl: createOptions.baseUrl
|
|
1191
|
+
};
|
|
1192
|
+
validateTraitInput({
|
|
1193
|
+
value: resolvedCreateOptions,
|
|
1194
|
+
bundledSchema: BaseTransportForStdFetchCreateOptionsSchema,
|
|
1195
|
+
validator: validators,
|
|
1196
|
+
onInvalid: (failure) => throwValidationClientError({
|
|
1197
|
+
code: TransportErrorCode.InvalidStdFetchOptions,
|
|
1198
|
+
source: "transport",
|
|
1199
|
+
messagePrefix: "createBaseTransportForStdFetch could not validate transportForStdFetchCreateOptions",
|
|
1200
|
+
failure
|
|
1201
|
+
})
|
|
1202
|
+
});
|
|
1203
|
+
const redirect = resolvedCreateOptions.redirect;
|
|
1204
|
+
const fetchImpl = resolvedCreateOptions.fetch;
|
|
1205
|
+
const baseUrl = resolvedCreateOptions.baseUrl;
|
|
1206
|
+
return { async execute(request) {
|
|
1207
|
+
const requestUrl = baseUrl ? new URL(request.url, baseUrl).toString() : request.url;
|
|
1208
|
+
const abortBridge = cancellationTokenToAbortSignal(request.cancellationToken);
|
|
1209
|
+
const init = {
|
|
1210
|
+
method: request.method,
|
|
1211
|
+
headers: request.headers,
|
|
1212
|
+
redirect,
|
|
1213
|
+
signal: abortBridge?.signal
|
|
1214
|
+
};
|
|
1215
|
+
if (request.body !== void 0 && request.method !== "GET" && request.method !== "HEAD") init.body = typeof request.body === "string" ? request.body : JSON.stringify(request.body);
|
|
1216
|
+
try {
|
|
1217
|
+
let res;
|
|
1218
|
+
try {
|
|
1219
|
+
res = await fetchImpl(requestUrl, init);
|
|
1220
|
+
} catch (error) {
|
|
1221
|
+
const normalized = normalizeAbortError(request.cancellationToken, error);
|
|
1222
|
+
if (normalized instanceof ClientError) throw normalized;
|
|
1223
|
+
throw new ClientError({
|
|
1224
|
+
kind: ClientErrorKind.Transport,
|
|
1225
|
+
message: "The HTTP request could not reach the remote service",
|
|
1226
|
+
code: TransportErrorCode.RequestFailed,
|
|
1227
|
+
source: ClientErrorSource.Transport,
|
|
1228
|
+
retryable: true,
|
|
1229
|
+
cause: error
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
const responseHeaders = {};
|
|
1233
|
+
res.headers.forEach((value, key) => {
|
|
1234
|
+
responseHeaders[key] = value;
|
|
1235
|
+
});
|
|
1236
|
+
let body;
|
|
1237
|
+
try {
|
|
1238
|
+
if (res.headers.get("content-type")?.includes("application/json")) body = await res.json();
|
|
1239
|
+
else if (res.status !== 302 && res.status !== 303 && res.status !== 301) body = await res.text();
|
|
1240
|
+
} catch (error) {
|
|
1241
|
+
throw new ClientError({
|
|
1242
|
+
kind: ClientErrorKind.Protocol,
|
|
1243
|
+
message: "The HTTP response body could not be decoded",
|
|
1244
|
+
code: TransportErrorCode.ResponseDecodeFailed,
|
|
1245
|
+
source: ClientErrorSource.Transport,
|
|
1246
|
+
cause: error
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
return {
|
|
1250
|
+
status: res.status,
|
|
1251
|
+
headers: responseHeaders,
|
|
1252
|
+
body
|
|
1253
|
+
};
|
|
1254
|
+
} finally {
|
|
1255
|
+
abortBridge?.dispose();
|
|
1256
|
+
}
|
|
1257
|
+
} };
|
|
1258
|
+
}
|
|
1259
|
+
//#endregion
|
|
1260
|
+
//#region src/storage/types.ts
|
|
1261
|
+
const StorageChangeEventOrigin = {
|
|
1262
|
+
Local: "local",
|
|
1263
|
+
External: "external"
|
|
1264
|
+
};
|
|
1265
|
+
const StorageTraitSchema = type({
|
|
1266
|
+
"storageEvent?": { subscribe: "Function" },
|
|
1267
|
+
get: "Function",
|
|
1268
|
+
set: "Function",
|
|
1269
|
+
take: "Function?",
|
|
1270
|
+
remove: "Function"
|
|
1271
|
+
});
|
|
1272
|
+
const REALM_STORAGE_TRAIT_TOKEN = new SecuritydeptInjectionToken("REALM_STORAGE_TRAIT_TOKEN");
|
|
1273
|
+
const PERSISTENT_STORAGE_TRAIT_TOKEN = new SecuritydeptInjectionToken("PERSISTENT_STORAGE_TRAIT_TOKEN");
|
|
1274
|
+
const SESSION_STORAGE_TRAIT_TOKEN = new SecuritydeptInjectionToken("SESSION_STORAGE_TRAIT_TOKEN");
|
|
1275
|
+
//#endregion
|
|
1276
|
+
//#region src/storage/memory-store.ts
|
|
1277
|
+
/** In-memory `SyncStorageTrait` — useful for realm state, tests, and SSR. */
|
|
1278
|
+
function createInMemoryRecordStore() {
|
|
1279
|
+
const store = /* @__PURE__ */ new Map();
|
|
1280
|
+
const storageEvent = new RxEventSubject();
|
|
1281
|
+
return {
|
|
1282
|
+
storageEvent,
|
|
1283
|
+
get(key) {
|
|
1284
|
+
return store.get(key) ?? null;
|
|
1285
|
+
},
|
|
1286
|
+
set(key, value) {
|
|
1287
|
+
const oldValue = store.get(key) ?? null;
|
|
1288
|
+
store.set(key, value);
|
|
1289
|
+
storageEvent.next({
|
|
1290
|
+
origin: StorageChangeEventOrigin.Local,
|
|
1291
|
+
key,
|
|
1292
|
+
oldValue,
|
|
1293
|
+
newValue: value
|
|
1294
|
+
});
|
|
1295
|
+
},
|
|
1296
|
+
take(key) {
|
|
1297
|
+
const value = store.get(key) ?? null;
|
|
1298
|
+
if (value !== null) {
|
|
1299
|
+
store.delete(key);
|
|
1300
|
+
storageEvent.next({
|
|
1301
|
+
origin: StorageChangeEventOrigin.Local,
|
|
1302
|
+
key,
|
|
1303
|
+
oldValue: value,
|
|
1304
|
+
newValue: null
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
return value;
|
|
1308
|
+
},
|
|
1309
|
+
remove(key) {
|
|
1310
|
+
const oldValue = store.get(key) ?? null;
|
|
1311
|
+
if (oldValue !== null) {
|
|
1312
|
+
store.delete(key);
|
|
1313
|
+
storageEvent.next({
|
|
1314
|
+
origin: StorageChangeEventOrigin.Local,
|
|
1315
|
+
key,
|
|
1316
|
+
oldValue,
|
|
1317
|
+
newValue: null
|
|
1318
|
+
});
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
//#endregion
|
|
1324
|
+
//#region src/tracing/create.ts
|
|
1325
|
+
const TracingCreateOptionsSchema = type({ subscribers: type({ record: "Function" }).array() });
|
|
1326
|
+
function createTracing(options) {
|
|
1327
|
+
const { validators, ...resolvedOptions } = options ?? {};
|
|
1328
|
+
const resolvedCreateOptions = { subscribers: resolvedOptions.subscribers ?? [] };
|
|
1329
|
+
validateTraitInput({
|
|
1330
|
+
value: resolvedCreateOptions,
|
|
1331
|
+
bundledSchema: TracingCreateOptionsSchema,
|
|
1332
|
+
validator: validators,
|
|
1333
|
+
onInvalid: (failure) => throwValidationClientError({
|
|
1334
|
+
code: "tracing.invalid_options",
|
|
1335
|
+
source: "tracing",
|
|
1336
|
+
messagePrefix: "createTracing could not validate tracingCreateOptions",
|
|
1337
|
+
failure
|
|
1338
|
+
})
|
|
1339
|
+
});
|
|
1340
|
+
const subject = new RxEventSubject();
|
|
1341
|
+
const events = subject;
|
|
1342
|
+
const subscriptions = [];
|
|
1343
|
+
for (const subscriber of resolvedCreateOptions.subscribers) subscriptions.push(events.subscribe({ next(event) {
|
|
1344
|
+
subscriber.record(event);
|
|
1345
|
+
} }));
|
|
1346
|
+
return {
|
|
1347
|
+
record(event) {
|
|
1348
|
+
subject.next(event);
|
|
1349
|
+
},
|
|
1350
|
+
events,
|
|
1351
|
+
dispose() {
|
|
1352
|
+
for (const subscription of subscriptions.splice(0)) subscription.unsubscribe();
|
|
1353
|
+
},
|
|
1354
|
+
[SYMBOL_DISPOSE]() {
|
|
1355
|
+
this.dispose();
|
|
1356
|
+
}
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
//#endregion
|
|
1360
|
+
//#region src/tracing/types.ts
|
|
1361
|
+
const TracingLevel = {
|
|
1362
|
+
Debug: "debug",
|
|
1363
|
+
Info: "info",
|
|
1364
|
+
Warn: "warn",
|
|
1365
|
+
Error: "error"
|
|
1366
|
+
};
|
|
1367
|
+
const OperationTraceEventType = {
|
|
1368
|
+
Started: "operation.started",
|
|
1369
|
+
Event: "operation.event",
|
|
1370
|
+
Error: "operation.error",
|
|
1371
|
+
Ended: "operation.ended"
|
|
1372
|
+
};
|
|
1373
|
+
const TracingTraitSchema = type({
|
|
1374
|
+
record: "Function",
|
|
1375
|
+
dispose: "Function",
|
|
1376
|
+
events: EventStreamTraitSchema
|
|
1377
|
+
});
|
|
1378
|
+
const TRACING_TRAIT_TOKEN = new SecuritydeptInjectionToken("TRACING_TRAIT_TOKEN");
|
|
1379
|
+
//#endregion
|
|
1380
|
+
//#region src/environment/types.ts
|
|
1381
|
+
const ENVIRONMENT_TOKEN = new SecuritydeptInjectionToken("ENVIRONMENT_TOKEN");
|
|
1382
|
+
//#endregion
|
|
1383
|
+
//#region src/environment/create.ts
|
|
1384
|
+
/**
|
|
1385
|
+
* Create a `FoundationEnvironment` from explicit trait units and defaults.
|
|
1386
|
+
*
|
|
1387
|
+
* This helper owns foundation-wide baseline resolution and trait-graph wiring.
|
|
1388
|
+
*
|
|
1389
|
+
* It may resolve:
|
|
1390
|
+
* - public std adapters such as
|
|
1391
|
+
* `transportForStdFetchCreateOptions -> transport` and `timeForStdCreateOptions -> time`
|
|
1392
|
+
* - public hostless baseline constructors such as
|
|
1393
|
+
* `spanCreateOptions -> span` and `tracingCreateOptions -> tracing`
|
|
1394
|
+
*
|
|
1395
|
+
* Host-material discovery such as resolving `window`, `location`, `history`,
|
|
1396
|
+
* or browser storage handles belongs to explicit host adapters such as
|
|
1397
|
+
* `@securitydept/client/web`, which must first resolve those inputs into
|
|
1398
|
+
* concrete traits before calling this helper.
|
|
1399
|
+
*/
|
|
1400
|
+
function createFoundationEnvironment(overrides) {
|
|
1401
|
+
const providers = overrides.providers ?? [];
|
|
1402
|
+
const externalProviderTokens = new Set(providers.map((provider) => getSecuritydeptProviderToken(provider)));
|
|
1403
|
+
if (externalProviderTokens.has(ENVIRONMENT_TOKEN)) throwValidationClientError({
|
|
1404
|
+
code: "environment.foundation_provider_reserved",
|
|
1405
|
+
source: "environment",
|
|
1406
|
+
messagePrefix: "createFoundationEnvironment does not allow overriding ENVIRONMENT_TOKEN"
|
|
1407
|
+
});
|
|
1408
|
+
return SecuritydeptInjector.resolveAndCreate([
|
|
1409
|
+
...createBuiltInTraitUnits(overrides, externalProviderTokens),
|
|
1410
|
+
...providers,
|
|
1411
|
+
createFoundationEnvironmentUnit()
|
|
1412
|
+
]).get(ENVIRONMENT_TOKEN);
|
|
1413
|
+
}
|
|
1414
|
+
function createBuiltInTraitUnits(overrides, externalProviderTokens) {
|
|
1415
|
+
return [
|
|
1416
|
+
createProviderIfTokenMissing(externalProviderTokens, TRANSPORT_TRAIT_TOKEN, () => createBuiltInTraitUnit({
|
|
1417
|
+
token: TRANSPORT_TRAIT_TOKEN,
|
|
1418
|
+
traitName: "transport",
|
|
1419
|
+
bundledSchema: BaseTransportTraitSchema,
|
|
1420
|
+
validator: overrides.validators?.transport,
|
|
1421
|
+
createValue: () => overrides.transport ?? createBaseTransportForStdFetch({
|
|
1422
|
+
...overrides.transportForStdFetchCreateOptions,
|
|
1423
|
+
validators: overrides.validators?.transportForStdFetchCreateOptions
|
|
1424
|
+
})
|
|
1425
|
+
})),
|
|
1426
|
+
createProviderIfTokenMissing(externalProviderTokens, TIME_TRAIT_TOKEN, () => createBuiltInTraitUnit({
|
|
1427
|
+
token: TIME_TRAIT_TOKEN,
|
|
1428
|
+
traitName: "time",
|
|
1429
|
+
bundledSchema: TimeTraitSchema,
|
|
1430
|
+
validator: overrides.validators?.time,
|
|
1431
|
+
createValue: () => overrides.time ?? createTimeForStd({
|
|
1432
|
+
...overrides.timeForStdCreateOptions,
|
|
1433
|
+
validators: overrides.validators?.timeForStdCreateOptions
|
|
1434
|
+
})
|
|
1435
|
+
})),
|
|
1436
|
+
createProviderIfTokenMissing(externalProviderTokens, REALM_STORAGE_TRAIT_TOKEN, () => createBuiltInTraitUnit({
|
|
1437
|
+
token: REALM_STORAGE_TRAIT_TOKEN,
|
|
1438
|
+
traitName: "realmStorage",
|
|
1439
|
+
bundledSchema: StorageTraitSchema,
|
|
1440
|
+
validator: overrides.validators?.realmStorage,
|
|
1441
|
+
createValue: () => overrides.realmStorage ?? createInMemoryRecordStore()
|
|
1442
|
+
})),
|
|
1443
|
+
createProviderIfTokenMissing(externalProviderTokens, SPAN_TRAIT_TOKEN, () => createBuiltInTraitUnit({
|
|
1444
|
+
token: SPAN_TRAIT_TOKEN,
|
|
1445
|
+
traitName: "span",
|
|
1446
|
+
bundledSchema: SpanTraitSchema,
|
|
1447
|
+
validator: overrides.validators?.span,
|
|
1448
|
+
createValue: () => overrides.span ?? createRootSpan({
|
|
1449
|
+
...overrides.spanCreateOptions,
|
|
1450
|
+
validators: overrides.validators?.spanCreateOptions
|
|
1451
|
+
})
|
|
1452
|
+
})),
|
|
1453
|
+
createProviderIfTokenMissing(externalProviderTokens, TRACING_TRAIT_TOKEN, () => createBuiltInTraitUnit({
|
|
1454
|
+
token: TRACING_TRAIT_TOKEN,
|
|
1455
|
+
traitName: "tracing",
|
|
1456
|
+
bundledSchema: TracingTraitSchema,
|
|
1457
|
+
validator: overrides.validators?.tracing,
|
|
1458
|
+
createValue: () => overrides.tracing ?? createTracing({
|
|
1459
|
+
...overrides.tracingCreateOptions,
|
|
1460
|
+
validators: overrides.validators?.tracingCreateOptions
|
|
1461
|
+
})
|
|
1462
|
+
})),
|
|
1463
|
+
createProviderIfTokenMissing(externalProviderTokens, IDLE_CALLBACK_TRAIT_TOKEN, () => createBuiltInTraitUnit({
|
|
1464
|
+
token: IDLE_CALLBACK_TRAIT_TOKEN,
|
|
1465
|
+
traitName: "idleCallback",
|
|
1466
|
+
bundledSchema: IdleCallbackTraitSchema,
|
|
1467
|
+
validator: overrides.validators?.idleCallback,
|
|
1468
|
+
optional: true,
|
|
1469
|
+
createValue: () => overrides.idleCallback ?? null
|
|
1470
|
+
})),
|
|
1471
|
+
createProviderIfTokenMissing(externalProviderTokens, PERSISTENT_STORAGE_TRAIT_TOKEN, () => createBuiltInTraitUnit({
|
|
1472
|
+
token: PERSISTENT_STORAGE_TRAIT_TOKEN,
|
|
1473
|
+
traitName: "persistentStorage",
|
|
1474
|
+
bundledSchema: StorageTraitSchema,
|
|
1475
|
+
validator: overrides.validators?.persistentStorage,
|
|
1476
|
+
optional: true,
|
|
1477
|
+
createValue: () => overrides.persistentStorage ?? null
|
|
1478
|
+
})),
|
|
1479
|
+
createProviderIfTokenMissing(externalProviderTokens, SESSION_STORAGE_TRAIT_TOKEN, () => createBuiltInTraitUnit({
|
|
1480
|
+
token: SESSION_STORAGE_TRAIT_TOKEN,
|
|
1481
|
+
traitName: "sessionStorage",
|
|
1482
|
+
bundledSchema: StorageTraitSchema,
|
|
1483
|
+
validator: overrides.validators?.sessionStorage,
|
|
1484
|
+
optional: true,
|
|
1485
|
+
createValue: () => overrides.sessionStorage ?? null
|
|
1486
|
+
})),
|
|
1487
|
+
createProviderIfTokenMissing(externalProviderTokens, ROUTER_TRAIT_TOKEN, () => createBuiltInTraitUnit({
|
|
1488
|
+
token: ROUTER_TRAIT_TOKEN,
|
|
1489
|
+
traitName: "router",
|
|
1490
|
+
bundledSchema: RouterTraitSchema,
|
|
1491
|
+
validator: overrides.validators?.router,
|
|
1492
|
+
optional: true,
|
|
1493
|
+
createValue: () => overrides.router ?? null
|
|
1494
|
+
})),
|
|
1495
|
+
createProviderIfTokenMissing(externalProviderTokens, PAGE_LIFECYCLE_TRAIT_TOKEN, () => createBuiltInTraitUnit({
|
|
1496
|
+
token: PAGE_LIFECYCLE_TRAIT_TOKEN,
|
|
1497
|
+
traitName: "pageLifecycle",
|
|
1498
|
+
bundledSchema: PageLifecycleTraitSchema,
|
|
1499
|
+
validator: overrides.validators?.pageLifecycle,
|
|
1500
|
+
optional: true,
|
|
1501
|
+
createValue: () => overrides.pageLifecycle ?? null
|
|
1502
|
+
})),
|
|
1503
|
+
createProviderIfTokenMissing(externalProviderTokens, POPUP_TRAIT_TOKEN, () => createBuiltInTraitUnit({
|
|
1504
|
+
token: POPUP_TRAIT_TOKEN,
|
|
1505
|
+
traitName: "popup",
|
|
1506
|
+
bundledSchema: PopupTraitSchema,
|
|
1507
|
+
validator: overrides.validators?.popup,
|
|
1508
|
+
optional: true,
|
|
1509
|
+
createValue: () => overrides.popup ?? null
|
|
1510
|
+
}))
|
|
1511
|
+
].filter(notMissingProvider);
|
|
1512
|
+
}
|
|
1513
|
+
function createBuiltInTraitUnit(options) {
|
|
1514
|
+
return {
|
|
1515
|
+
provide: options.token,
|
|
1516
|
+
useFactory: ((...deps) => {
|
|
1517
|
+
const value = options.createValue(...deps);
|
|
1518
|
+
validateTraitInput({
|
|
1519
|
+
value,
|
|
1520
|
+
bundledSchema: options.bundledSchema,
|
|
1521
|
+
validator: options.validator,
|
|
1522
|
+
optional: options.optional,
|
|
1523
|
+
onInvalid: (failure) => throwValidationClientError({
|
|
1524
|
+
code: "environment.trait_validation_failed",
|
|
1525
|
+
source: "environment",
|
|
1526
|
+
messagePrefix: `createFoundationEnvironment could not validate ${options.traitName}`,
|
|
1527
|
+
failure
|
|
1528
|
+
})
|
|
1529
|
+
});
|
|
1530
|
+
return value;
|
|
1531
|
+
}),
|
|
1532
|
+
deps: options.deps
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1535
|
+
function createFoundationEnvironmentUnit() {
|
|
1536
|
+
return {
|
|
1537
|
+
provide: ENVIRONMENT_TOKEN,
|
|
1538
|
+
useFactory: ((injector, transport, time, realmStorage, span, tracing, idleCallback, persistentStorage, sessionStorage, router, pageLifecycle, popup) => {
|
|
1539
|
+
return {
|
|
1540
|
+
injector,
|
|
1541
|
+
transport,
|
|
1542
|
+
time,
|
|
1543
|
+
realmStorage,
|
|
1544
|
+
idleCallback: idleCallback ?? void 0,
|
|
1545
|
+
persistentStorage: persistentStorage ?? void 0,
|
|
1546
|
+
sessionStorage: sessionStorage ?? void 0,
|
|
1547
|
+
span,
|
|
1548
|
+
tracing,
|
|
1549
|
+
router: router ?? void 0,
|
|
1550
|
+
pageLifecycle: pageLifecycle ?? void 0,
|
|
1551
|
+
popup: popup ?? void 0
|
|
1552
|
+
};
|
|
1553
|
+
}),
|
|
1554
|
+
deps: [
|
|
1555
|
+
INJECTOR_TOKEN,
|
|
1556
|
+
TRANSPORT_TRAIT_TOKEN,
|
|
1557
|
+
TIME_TRAIT_TOKEN,
|
|
1558
|
+
REALM_STORAGE_TRAIT_TOKEN,
|
|
1559
|
+
SPAN_TRAIT_TOKEN,
|
|
1560
|
+
TRACING_TRAIT_TOKEN,
|
|
1561
|
+
IDLE_CALLBACK_TRAIT_TOKEN,
|
|
1562
|
+
PERSISTENT_STORAGE_TRAIT_TOKEN,
|
|
1563
|
+
SESSION_STORAGE_TRAIT_TOKEN,
|
|
1564
|
+
ROUTER_TRAIT_TOKEN,
|
|
1565
|
+
PAGE_LIFECYCLE_TRAIT_TOKEN,
|
|
1566
|
+
POPUP_TRAIT_TOKEN
|
|
1567
|
+
]
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
//#endregion
|
|
1571
|
+
export { INJECTOR_TOKEN as $, appendOrReplaceCompatFragment as A, createJsonRpcServerAndClient as B, IDLE_CALLBACK_TRAIT_TOKEN as C, RouterNavigationMode as D, RouterNavigationIntent as E, PopupAttachFailureReason as F, validateWithSchemaSync as G, throwValidationClientError as H, PopupClientWindowHandle as I, PAGE_LIFECYCLE_TRAIT_TOKEN as J, PopupErrorCode as K, PopupServerWindowHandle as L, parseCompatFragment as M, takeCompatFragment as N, takeCompatFragmentFromRouter as O, POPUP_TRAIT_TOKEN as P, readonlySignal as Q, createJsonRpcClient as R, SPAN_TRAIT_TOKEN as S, ROUTER_TRAIT_TOKEN as T, validateTraitInput as U, formatValidationFailure as V, validateWithSchema as W, isEventStreamTrait as X, EventSourceKind as Y, createSignal as Z, abortSignalToCancellationToken as _, TracingLevel as a, createProviderIfTokenMissing as at, abortSignalToEventStream as b, PERSISTENT_STORAGE_TRAIT_TOKEN as c, readErrorPresentationDescriptor as ct, StorageChangeEventOrigin as d, PARENT_INJECTOR_TOKEN as et, FetchTransportRedirectKind as f, createTimeForStd as g, TransportErrorCode as h, TRACING_TRAIT_TOKEN as i, SecuritydeptInjectionToken as it, isCompatFragmentBlock as j, SECURITYDEPT_COMPAT_FRAGMENT_VERSION as k, REALM_STORAGE_TRAIT_TOKEN as l, TRANSPORT_TRAIT_TOKEN as m, ENVIRONMENT_TOKEN as n, toRuntimeInjector as nt, createTracing as o, getSecuritydeptProviderToken as ot, createBaseTransportForStdFetch as p, readPopupErrorPresentationDescriptor as q, OperationTraceEventType as r, SecurityDeptOptional as rt, createInMemoryRecordStore as s, notMissingProvider as st, createFoundationEnvironment as t, SecuritydeptInjector as tt, SESSION_STORAGE_TRAIT_TOKEN as u, cancellationTokenToAbortSignal as v, TIME_TRAIT_TOKEN as w, createRootSpan as x, normalizeAbortError as y, createJsonRpcServer as z };
|
|
1572
|
+
|
|
1573
|
+
//# sourceMappingURL=create-Bm-XRA3G.mjs.map
|