@securitydept/client 0.3.0-beta.3 → 0.3.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/create-99Ksgl_O.mjs +952 -0
- package/dist/create-99Ksgl_O.mjs.map +1 -0
- package/dist/handle-TnxwtQ4T.mjs +634 -0
- package/dist/handle-TnxwtQ4T.mjs.map +1 -0
- package/dist/index-Cg_iY2yY.d.mts +608 -0
- package/dist/index-DmIapvr-.d.mts +1669 -0
- package/dist/index-O1A3CdBV.d.mts +119 -0
- package/dist/index.d.mts +3 -353
- package/dist/index.mjs +1127 -364
- package/dist/index.mjs.map +1 -1
- package/dist/router-DUh3bhLy.mjs +245 -0
- package/dist/router-DUh3bhLy.mjs.map +1 -0
- package/dist/rx/index.d.mts +68 -0
- package/dist/rx/index.mjs +2 -0
- package/dist/rx-B3OU17wu.mjs +1105 -0
- package/dist/rx-B3OU17wu.mjs.map +1 -0
- package/dist/server/index.d.mts +20 -0
- package/dist/server/index.mjs +29 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/test/index.d.mts +101 -0
- package/dist/test/index.mjs +270 -0
- package/dist/test/index.mjs.map +1 -0
- package/dist/uri-string-BPNbIMgv.mjs +300 -0
- package/dist/uri-string-BPNbIMgv.mjs.map +1 -0
- package/dist/web/index.d.mts +93 -363
- package/dist/web/index.mjs +335 -428
- package/dist/web/index.mjs.map +1 -1
- package/dist/webext/index.d.mts +73 -0
- package/dist/webext/index.mjs +231 -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/client-environment-CaumNmir.mjs +0 -812
- package/dist/client-environment-CaumNmir.mjs.map +0 -1
- package/dist/client-environment-CdHtVxKW.d.mts +0 -401
- 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/index-C4xlKhG7.d.mts +0 -102
- package/dist/index-COUt5x9Z.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 -2
- package/dist/persistence-CSB8770k.mjs +0 -108
- package/dist/persistence-CSB8770k.mjs.map +0 -1
- package/dist/planner-host-BTWie0lo.mjs +0 -197
- package/dist/planner-host-BTWie0lo.mjs.map +0 -1
- package/dist/planner-host-DN9dRsQx.d.mts +0 -190
- package/dist/types-BJqUdH2e.d.mts +0 -32
- package/dist/types-BwcnUGNl.d.mts +0 -52
- package/dist/web-DQmMqZnR.mjs +0 -51
- package/dist/web-DQmMqZnR.mjs.map +0 -1
- 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,634 @@
|
|
|
1
|
+
import { A as RxEventSubject, D as RxStateSignal, F as SYMBOL_DISPOSE, G as UserRecovery, H as ClientErrorKind, V as ClientError, W as ErrorPresentationTone, h as createAsyncSchedulerWithTimestampProvider } from "./rx-B3OU17wu.mjs";
|
|
2
|
+
import { B as validateWithSchemaSync } from "./create-99Ksgl_O.mjs";
|
|
3
|
+
import { BehaviorSubject, ReplaySubject, Subject, catchError, delayWhen, distinctUntilChanged, exhaustMap, filter, from, map, merge, of, shareReplay, take, takeUntil, timeout, timer } from "rxjs";
|
|
4
|
+
import { type } from "arktype";
|
|
5
|
+
//#region src/errors/presentation-descriptor.ts
|
|
6
|
+
const DEFAULT_RECOVERY_LABELS = {
|
|
7
|
+
[UserRecovery.None]: "",
|
|
8
|
+
[UserRecovery.Retry]: "Try again",
|
|
9
|
+
[UserRecovery.RestartFlow]: "Restart flow",
|
|
10
|
+
[UserRecovery.Reauthenticate]: "Sign in again",
|
|
11
|
+
[UserRecovery.ContactSupport]: "Contact support"
|
|
12
|
+
};
|
|
13
|
+
const userRecoveryValues = new Set(Object.values(UserRecovery));
|
|
14
|
+
const clientErrorKindValues = new Set(Object.values(ClientErrorKind));
|
|
15
|
+
function readErrorPresentationDescriptor(error, options = {}) {
|
|
16
|
+
const clientError = error instanceof ClientError ? error : coerceClientErrorLike(error);
|
|
17
|
+
if (!clientError) return {
|
|
18
|
+
code: null,
|
|
19
|
+
kind: null,
|
|
20
|
+
title: options.fallbackTitle ?? "Operation failed",
|
|
21
|
+
description: options.fallbackDescription ?? "An unexpected error prevented the operation from completing.",
|
|
22
|
+
recovery: UserRecovery.None,
|
|
23
|
+
retryable: false,
|
|
24
|
+
tone: ErrorPresentationTone.Danger,
|
|
25
|
+
primaryAction: null
|
|
26
|
+
};
|
|
27
|
+
const code = clientError.presentation?.code ?? clientError.code;
|
|
28
|
+
const codePresentation = code ? options.codePresentations?.[code] : void 0;
|
|
29
|
+
const recovery = clientError.presentation?.recovery ?? codePresentation?.recovery ?? clientError.recovery;
|
|
30
|
+
const generic = readGenericPresentation(clientError.kind);
|
|
31
|
+
return {
|
|
32
|
+
code,
|
|
33
|
+
kind: clientError.kind,
|
|
34
|
+
source: clientError.source,
|
|
35
|
+
title: codePresentation?.title ?? generic.title,
|
|
36
|
+
description: clientError.presentation?.message ?? codePresentation?.description ?? generic.description,
|
|
37
|
+
recovery,
|
|
38
|
+
retryable: clientError.retryable,
|
|
39
|
+
tone: codePresentation?.tone ?? readClientErrorTone(clientError.kind, recovery),
|
|
40
|
+
primaryAction: readPrimaryAction(recovery, options)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function readGenericPresentation(kind) {
|
|
44
|
+
switch (kind) {
|
|
45
|
+
case ClientErrorKind.Unauthenticated: return {
|
|
46
|
+
title: "Authentication required",
|
|
47
|
+
description: "Sign in again to continue."
|
|
48
|
+
};
|
|
49
|
+
case ClientErrorKind.Unauthorized: return {
|
|
50
|
+
title: "Access denied",
|
|
51
|
+
description: "You do not have permission to complete this operation."
|
|
52
|
+
};
|
|
53
|
+
case ClientErrorKind.Cancelled: return {
|
|
54
|
+
title: "Operation cancelled",
|
|
55
|
+
description: "The operation was cancelled before it completed."
|
|
56
|
+
};
|
|
57
|
+
case ClientErrorKind.Timeout: return {
|
|
58
|
+
title: "Request timed out",
|
|
59
|
+
description: "The operation did not complete in time."
|
|
60
|
+
};
|
|
61
|
+
case ClientErrorKind.Configuration: return {
|
|
62
|
+
title: "Configuration error",
|
|
63
|
+
description: "The client is not configured to complete this operation."
|
|
64
|
+
};
|
|
65
|
+
case ClientErrorKind.Storage: return {
|
|
66
|
+
title: "Storage failed",
|
|
67
|
+
description: "The client could not access required stored data."
|
|
68
|
+
};
|
|
69
|
+
case ClientErrorKind.Transport: return {
|
|
70
|
+
title: "Network request failed",
|
|
71
|
+
description: "The client could not reach the service."
|
|
72
|
+
};
|
|
73
|
+
case ClientErrorKind.Server: return {
|
|
74
|
+
title: "Server request failed",
|
|
75
|
+
description: "The service could not complete the request."
|
|
76
|
+
};
|
|
77
|
+
case ClientErrorKind.Protocol: return {
|
|
78
|
+
title: "Invalid response",
|
|
79
|
+
description: "The service response did not satisfy the expected protocol."
|
|
80
|
+
};
|
|
81
|
+
case ClientErrorKind.Authorization: return {
|
|
82
|
+
title: "Authorization failed",
|
|
83
|
+
description: "The authorization flow could not be completed."
|
|
84
|
+
};
|
|
85
|
+
default: return {
|
|
86
|
+
title: "Operation failed",
|
|
87
|
+
description: "The operation could not be completed."
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function readClientErrorTone(kind, recovery) {
|
|
92
|
+
if (kind === ClientErrorKind.Cancelled) return ErrorPresentationTone.Neutral;
|
|
93
|
+
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;
|
|
94
|
+
return ErrorPresentationTone.Danger;
|
|
95
|
+
}
|
|
96
|
+
function coerceClientErrorLike(error) {
|
|
97
|
+
if (typeof error !== "object" || error === null) return null;
|
|
98
|
+
const candidate = error;
|
|
99
|
+
if (typeof candidate.kind !== "string" || !clientErrorKindValues.has(candidate.kind)) return null;
|
|
100
|
+
return {
|
|
101
|
+
code: typeof candidate.code === "string" ? candidate.code : null,
|
|
102
|
+
kind: candidate.kind,
|
|
103
|
+
recovery: typeof candidate.recovery === "string" && userRecoveryValues.has(candidate.recovery) ? candidate.recovery : UserRecovery.None,
|
|
104
|
+
retryable: candidate.retryable === true,
|
|
105
|
+
source: typeof candidate.source === "string" ? candidate.source : void 0,
|
|
106
|
+
presentation: isErrorPresentation(candidate.presentation) ? candidate.presentation : void 0
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function isErrorPresentation(value) {
|
|
110
|
+
return typeof value === "object" && value !== null && typeof value.code === "string" && typeof value.message === "string" && typeof value.recovery === "string" && userRecoveryValues.has(value.recovery);
|
|
111
|
+
}
|
|
112
|
+
function readPrimaryAction(recovery, options) {
|
|
113
|
+
if (recovery === UserRecovery.None) return null;
|
|
114
|
+
const label = options.recoveryLabels?.[recovery] ?? DEFAULT_RECOVERY_LABELS[recovery];
|
|
115
|
+
if (!label) return null;
|
|
116
|
+
return {
|
|
117
|
+
recovery,
|
|
118
|
+
label,
|
|
119
|
+
href: options.recoveryLinks?.[recovery] ?? null
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/signals/signal.ts
|
|
124
|
+
/**
|
|
125
|
+
* Minimal writable signal implementation.
|
|
126
|
+
* `set()` always publishes a write. Equality-based value-version suppression is
|
|
127
|
+
* handled by the underlying Rx signal for computed dependency validation.
|
|
128
|
+
*/
|
|
129
|
+
function createSignal(initial, options) {
|
|
130
|
+
return RxStateSignal.fromInitialValue(initial, options);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Create a read-only view of a writable signal.
|
|
134
|
+
*/
|
|
135
|
+
function readonlySignal(signal) {
|
|
136
|
+
return signal;
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/popup/errors.ts
|
|
140
|
+
const PopupErrorCode = {
|
|
141
|
+
Blocked: "popup.blocked",
|
|
142
|
+
Closed: "popup.closed_by_user"
|
|
143
|
+
};
|
|
144
|
+
function readPopupErrorPresentationDescriptor(error, options = {}) {
|
|
145
|
+
return readErrorPresentationDescriptor(error, {
|
|
146
|
+
...options,
|
|
147
|
+
codePresentations: {
|
|
148
|
+
[PopupErrorCode.Blocked]: {
|
|
149
|
+
title: "Popup was blocked",
|
|
150
|
+
description: "The browser blocked the popup window. Allow popups for this site, then try again.",
|
|
151
|
+
recovery: UserRecovery.Retry,
|
|
152
|
+
tone: ErrorPresentationTone.Warning
|
|
153
|
+
},
|
|
154
|
+
[PopupErrorCode.Closed]: {
|
|
155
|
+
title: "Popup was closed",
|
|
156
|
+
description: "The popup window was closed before the operation completed. Start the flow again.",
|
|
157
|
+
recovery: UserRecovery.RestartFlow,
|
|
158
|
+
tone: ErrorPresentationTone.Warning
|
|
159
|
+
},
|
|
160
|
+
...options.codePresentations
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region src/protocol/json-rpc/types.ts
|
|
166
|
+
const JsonRpcIdSchema = type("string | number");
|
|
167
|
+
const JsonRpcErrorPayloadSchema = type({
|
|
168
|
+
code: "number",
|
|
169
|
+
message: "string",
|
|
170
|
+
data: "unknown?"
|
|
171
|
+
});
|
|
172
|
+
const JsonRpcRequestMessageSchema = type({
|
|
173
|
+
jsonrpc: "\"2.0\"",
|
|
174
|
+
id: JsonRpcIdSchema,
|
|
175
|
+
method: "string",
|
|
176
|
+
params: "unknown?"
|
|
177
|
+
});
|
|
178
|
+
const JsonRpcNotificationMessageSchema = type({
|
|
179
|
+
jsonrpc: "\"2.0\"",
|
|
180
|
+
method: "string",
|
|
181
|
+
params: "unknown?"
|
|
182
|
+
});
|
|
183
|
+
const JsonRpcSuccessResponseMessageSchema = type({
|
|
184
|
+
jsonrpc: "\"2.0\"",
|
|
185
|
+
id: JsonRpcIdSchema,
|
|
186
|
+
result: "unknown"
|
|
187
|
+
});
|
|
188
|
+
const JsonRpcErrorResponseMessageSchema = type({
|
|
189
|
+
jsonrpc: "\"2.0\"",
|
|
190
|
+
id: JsonRpcIdSchema,
|
|
191
|
+
error: JsonRpcErrorPayloadSchema
|
|
192
|
+
});
|
|
193
|
+
const JsonRpcMessageSchema = JsonRpcRequestMessageSchema.or(JsonRpcNotificationMessageSchema).or(JsonRpcSuccessResponseMessageSchema).or(JsonRpcErrorResponseMessageSchema);
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/protocol/json-rpc/create.ts
|
|
196
|
+
function createJsonRpcClient(options) {
|
|
197
|
+
return new JsonRpcClient(options);
|
|
198
|
+
}
|
|
199
|
+
function createJsonRpcServer(options) {
|
|
200
|
+
return new JsonRpcServer(options);
|
|
201
|
+
}
|
|
202
|
+
function createJsonRpcServerAndClient(options) {
|
|
203
|
+
return new JsonRpcServerAndClient(options);
|
|
204
|
+
}
|
|
205
|
+
var JsonRpcClient = class {
|
|
206
|
+
options;
|
|
207
|
+
onNotification;
|
|
208
|
+
notificationSubject = new RxEventSubject();
|
|
209
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
210
|
+
disposed = false;
|
|
211
|
+
nextRequestId = 1;
|
|
212
|
+
constructor(options) {
|
|
213
|
+
this.options = options;
|
|
214
|
+
this.onNotification = this.notificationSubject;
|
|
215
|
+
}
|
|
216
|
+
request(method, params, requestOptions = {}) {
|
|
217
|
+
if (this.disposed) return Promise.reject(createJsonRpcDisposedError("client"));
|
|
218
|
+
const id = this.nextRequestId++;
|
|
219
|
+
const timeoutMs = requestOptions.timeoutMs ?? this.options.defaultRequestTimeoutMs;
|
|
220
|
+
return new Promise((resolve, reject) => {
|
|
221
|
+
const pendingRequest = {
|
|
222
|
+
method,
|
|
223
|
+
reject,
|
|
224
|
+
resolve
|
|
225
|
+
};
|
|
226
|
+
if (timeoutMs !== void 0) pendingRequest.timeoutHandle = this.options.time.setTimeout(() => {
|
|
227
|
+
this.pendingRequests.delete(id);
|
|
228
|
+
reject(/* @__PURE__ */ new Error(`JSON-RPC request timed out for ${method}.`));
|
|
229
|
+
}, timeoutMs);
|
|
230
|
+
this.pendingRequests.set(id, pendingRequest);
|
|
231
|
+
try {
|
|
232
|
+
this.options.send({
|
|
233
|
+
jsonrpc: "2.0",
|
|
234
|
+
id,
|
|
235
|
+
method,
|
|
236
|
+
params
|
|
237
|
+
});
|
|
238
|
+
} catch (error) {
|
|
239
|
+
this.clearPendingRequest(id);
|
|
240
|
+
reject(error);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
notify(method, params) {
|
|
245
|
+
if (this.disposed) return;
|
|
246
|
+
this.options.send({
|
|
247
|
+
jsonrpc: "2.0",
|
|
248
|
+
method,
|
|
249
|
+
params
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
receive(message) {
|
|
253
|
+
if (this.disposed) return;
|
|
254
|
+
for (const parsedMessage of parseIncomingMessages(message)) {
|
|
255
|
+
if (isJsonRpcResponseMessage(parsedMessage)) {
|
|
256
|
+
this.resolvePendingRequest(parsedMessage);
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (isJsonRpcNotificationMessage(parsedMessage)) this.notificationSubject.next({
|
|
260
|
+
method: parsedMessage.method,
|
|
261
|
+
params: parsedMessage.params
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
rejectAllPendingRequests(reason) {
|
|
266
|
+
for (const [id, pendingRequest] of this.pendingRequests) {
|
|
267
|
+
this.clearPendingRequest(id);
|
|
268
|
+
pendingRequest.reject(reason);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
dispose() {
|
|
272
|
+
if (this.disposed) return;
|
|
273
|
+
this.disposed = true;
|
|
274
|
+
this.rejectAllPendingRequests(/* @__PURE__ */ new Error("JSON-RPC client disposed."));
|
|
275
|
+
this.notificationSubject.complete();
|
|
276
|
+
}
|
|
277
|
+
[SYMBOL_DISPOSE]() {
|
|
278
|
+
this.dispose();
|
|
279
|
+
}
|
|
280
|
+
resolvePendingRequest(message) {
|
|
281
|
+
const pendingRequest = this.pendingRequests.get(message.id);
|
|
282
|
+
if (!pendingRequest) return;
|
|
283
|
+
this.clearPendingRequest(message.id);
|
|
284
|
+
if ("error" in message) {
|
|
285
|
+
pendingRequest.reject(/* @__PURE__ */ new Error(`JSON-RPC request failed for ${pendingRequest.method}: ${message.error.message}`));
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
pendingRequest.resolve(message.result);
|
|
289
|
+
}
|
|
290
|
+
clearPendingRequest(id) {
|
|
291
|
+
const pendingRequest = this.pendingRequests.get(id);
|
|
292
|
+
if (!pendingRequest) return;
|
|
293
|
+
if (pendingRequest.timeoutHandle !== void 0) this.options.time.clearTimeout(pendingRequest.timeoutHandle);
|
|
294
|
+
this.pendingRequests.delete(id);
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
var JsonRpcServer = class {
|
|
298
|
+
options;
|
|
299
|
+
onRequest;
|
|
300
|
+
onNotification;
|
|
301
|
+
requestSubject = new RxEventSubject();
|
|
302
|
+
notificationSubject = new RxEventSubject();
|
|
303
|
+
disposed = false;
|
|
304
|
+
constructor(options) {
|
|
305
|
+
this.options = options;
|
|
306
|
+
this.onRequest = this.requestSubject;
|
|
307
|
+
this.onNotification = this.notificationSubject;
|
|
308
|
+
}
|
|
309
|
+
notify(method, params) {
|
|
310
|
+
if (this.disposed) return;
|
|
311
|
+
this.options.send({
|
|
312
|
+
jsonrpc: "2.0",
|
|
313
|
+
method,
|
|
314
|
+
params
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
receive(message) {
|
|
318
|
+
if (this.disposed) return;
|
|
319
|
+
for (const parsedMessage of parseIncomingMessages(message)) {
|
|
320
|
+
if (isJsonRpcRequestMessage(parsedMessage)) {
|
|
321
|
+
this.requestSubject.next({
|
|
322
|
+
id: parsedMessage.id,
|
|
323
|
+
method: parsedMessage.method,
|
|
324
|
+
params: parsedMessage.params
|
|
325
|
+
});
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (isJsonRpcNotificationMessage(parsedMessage)) this.notificationSubject.next({
|
|
329
|
+
method: parsedMessage.method,
|
|
330
|
+
params: parsedMessage.params
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
respondSuccess(id, result) {
|
|
335
|
+
if (this.disposed) return;
|
|
336
|
+
this.options.send({
|
|
337
|
+
jsonrpc: "2.0",
|
|
338
|
+
id,
|
|
339
|
+
result
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
respondError(id, error) {
|
|
343
|
+
if (this.disposed) return;
|
|
344
|
+
this.options.send({
|
|
345
|
+
jsonrpc: "2.0",
|
|
346
|
+
id,
|
|
347
|
+
error
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
dispose() {
|
|
351
|
+
if (this.disposed) return;
|
|
352
|
+
this.disposed = true;
|
|
353
|
+
this.requestSubject.complete();
|
|
354
|
+
this.notificationSubject.complete();
|
|
355
|
+
}
|
|
356
|
+
[SYMBOL_DISPOSE]() {
|
|
357
|
+
this.dispose();
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
var JsonRpcServerAndClient = class {
|
|
361
|
+
onNotification;
|
|
362
|
+
onRequest;
|
|
363
|
+
client;
|
|
364
|
+
server;
|
|
365
|
+
disposed = false;
|
|
366
|
+
constructor(options) {
|
|
367
|
+
this.client = new JsonRpcClient(options);
|
|
368
|
+
this.server = new JsonRpcServer(options);
|
|
369
|
+
this.onNotification = this.server.onNotification;
|
|
370
|
+
this.onRequest = this.server.onRequest;
|
|
371
|
+
}
|
|
372
|
+
request(method, params, options) {
|
|
373
|
+
return this.client.request(method, params, options);
|
|
374
|
+
}
|
|
375
|
+
notify(method, params) {
|
|
376
|
+
this.client.notify(method, params);
|
|
377
|
+
}
|
|
378
|
+
receive(message) {
|
|
379
|
+
for (const parsedMessage of parseIncomingMessages(message)) {
|
|
380
|
+
if (isJsonRpcResponseMessage(parsedMessage)) {
|
|
381
|
+
this.client.receive(parsedMessage);
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
this.server.receive(parsedMessage);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
rejectAllPendingRequests(reason) {
|
|
388
|
+
this.client.rejectAllPendingRequests(reason);
|
|
389
|
+
}
|
|
390
|
+
respondSuccess(id, result) {
|
|
391
|
+
this.server.respondSuccess(id, result);
|
|
392
|
+
}
|
|
393
|
+
respondError(id, error) {
|
|
394
|
+
this.server.respondError(id, error);
|
|
395
|
+
}
|
|
396
|
+
dispose() {
|
|
397
|
+
if (this.disposed) return;
|
|
398
|
+
this.disposed = true;
|
|
399
|
+
this.client.dispose();
|
|
400
|
+
this.server.dispose();
|
|
401
|
+
}
|
|
402
|
+
[SYMBOL_DISPOSE]() {
|
|
403
|
+
this.dispose();
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
function parseIncomingMessages(message) {
|
|
407
|
+
if (!Array.isArray(message)) return parseJsonRpcMessage(message);
|
|
408
|
+
const parsedMessages = [];
|
|
409
|
+
for (const entry of message) parsedMessages.push(...parseJsonRpcMessage(entry));
|
|
410
|
+
return parsedMessages;
|
|
411
|
+
}
|
|
412
|
+
function parseJsonRpcMessage(message) {
|
|
413
|
+
const result = validateWithSchemaSync(JsonRpcMessageSchema, message);
|
|
414
|
+
return result.success ? [result.value] : [];
|
|
415
|
+
}
|
|
416
|
+
function isJsonRpcRequestMessage(message) {
|
|
417
|
+
return "method" in message && "id" in message;
|
|
418
|
+
}
|
|
419
|
+
function isJsonRpcResponseMessage(message) {
|
|
420
|
+
return "id" in message && !("method" in message);
|
|
421
|
+
}
|
|
422
|
+
function isJsonRpcNotificationMessage(message) {
|
|
423
|
+
return "method" in message && !("id" in message);
|
|
424
|
+
}
|
|
425
|
+
function createJsonRpcDisposedError(role) {
|
|
426
|
+
return /* @__PURE__ */ new Error(`JSON-RPC ${role} disposed.`);
|
|
427
|
+
}
|
|
428
|
+
//#endregion
|
|
429
|
+
//#region src/popup/session.ts
|
|
430
|
+
const PopupSessionControlMethod = {
|
|
431
|
+
Ready: "securitydept.popup.session.ready",
|
|
432
|
+
Ping: "securitydept.popup.session.ping",
|
|
433
|
+
Pong: "securitydept.popup.session.pong"
|
|
434
|
+
};
|
|
435
|
+
const DEFAULT_READY_TIMEOUT_MS = 1e4;
|
|
436
|
+
const DEFAULT_HEARTBEAT_INTERVAL_MS = 5e3;
|
|
437
|
+
const DEFAULT_HEARTBEAT_TIMEOUT_MS = 15e3;
|
|
438
|
+
var PopupClientSession = class {
|
|
439
|
+
failure = createSignal(null);
|
|
440
|
+
isActive = createSignal(false);
|
|
441
|
+
init$ = new ReplaySubject();
|
|
442
|
+
destroyed = createSignal(false);
|
|
443
|
+
destroyed$ = from(this.destroyed).pipe(distinctUntilChanged(), filter(Boolean), shareReplay(1));
|
|
444
|
+
constructor(options) {
|
|
445
|
+
const timestampScheduler = createAsyncSchedulerWithTimestampProvider(options.time);
|
|
446
|
+
const ready$ = new Subject();
|
|
447
|
+
const pong$ = new Subject();
|
|
448
|
+
const closed$ = from(options.closedStream).pipe(filter(Boolean));
|
|
449
|
+
const ping$ = merge(ready$.pipe(filter(Boolean)), pong$.pipe(filter(Boolean))).pipe(delayWhen(() => timer(options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS, timestampScheduler)));
|
|
450
|
+
from(this.init$).pipe(exhaustMap(() => from(options.jsonRpc.onNotification).pipe(filter(function isReadyNotification(event) {
|
|
451
|
+
return event.method === PopupSessionControlMethod.Ready;
|
|
452
|
+
}), take(1), timeout({
|
|
453
|
+
each: options.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS,
|
|
454
|
+
scheduler: timestampScheduler
|
|
455
|
+
}), map(() => true), catchError(() => of(false)))), takeUntil(this.destroyed$)).subscribe(ready$);
|
|
456
|
+
ping$.pipe(exhaustMap(() => from(options.jsonRpc.onNotification).pipe(filter(function isPongNotification(event) {
|
|
457
|
+
return event.method === PopupSessionControlMethod.Pong;
|
|
458
|
+
}), take(1), timeout({
|
|
459
|
+
first: options.heartbeatTimeoutMs ?? DEFAULT_HEARTBEAT_TIMEOUT_MS,
|
|
460
|
+
scheduler: timestampScheduler
|
|
461
|
+
}), map(() => true), catchError(() => of(false)))), takeUntil(this.destroyed$)).subscribe(pong$);
|
|
462
|
+
merge(closed$.pipe(map(() => new ClientError({
|
|
463
|
+
kind: ClientErrorKind.Authorization,
|
|
464
|
+
code: PopupErrorCode.Closed,
|
|
465
|
+
message: "Popup window was closed before completing the login flow.",
|
|
466
|
+
recovery: UserRecovery.RestartFlow,
|
|
467
|
+
source: "popup"
|
|
468
|
+
}))), ready$.pipe(map((success) => !success ? new ClientError({
|
|
469
|
+
kind: ClientErrorKind.Protocol,
|
|
470
|
+
message: "Did not receive ready notification from popup within the expected time.",
|
|
471
|
+
recovery: UserRecovery.RestartFlow,
|
|
472
|
+
source: "popup"
|
|
473
|
+
}) : null)), pong$.pipe(map((success) => !success ? new ClientError({
|
|
474
|
+
kind: ClientErrorKind.Protocol,
|
|
475
|
+
message: "Did not receive pong notification from popup within the expected time.",
|
|
476
|
+
recovery: UserRecovery.RestartFlow,
|
|
477
|
+
source: "popup"
|
|
478
|
+
}) : null))).pipe(takeUntil(this.destroyed$), shareReplay({
|
|
479
|
+
bufferSize: 1,
|
|
480
|
+
refCount: false
|
|
481
|
+
})).subscribe((error) => this.failure.set(error));
|
|
482
|
+
merge(closed$.pipe(map(() => false)), ready$.pipe(map(Boolean)), pong$.pipe(map(Boolean))).pipe(takeUntil(this.destroyed$)).subscribe((nextIsActive) => {
|
|
483
|
+
this.isActive.set(nextIsActive);
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
init() {
|
|
487
|
+
this.init$.next(true);
|
|
488
|
+
}
|
|
489
|
+
dispose() {
|
|
490
|
+
this.destroyed.set(true);
|
|
491
|
+
}
|
|
492
|
+
[SYMBOL_DISPOSE]() {
|
|
493
|
+
this.dispose();
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
var PopupServerSession = class {
|
|
497
|
+
init$ = new ReplaySubject();
|
|
498
|
+
destroyed$$ = new BehaviorSubject(false);
|
|
499
|
+
destroyed$ = this.destroyed$$.pipe(distinctUntilChanged(), filter(Boolean), shareReplay(1));
|
|
500
|
+
constructor(options) {
|
|
501
|
+
const ready$ = from(this.init$);
|
|
502
|
+
const pong$ = from(options.jsonRpc.onNotification).pipe(filter(function isPing(event) {
|
|
503
|
+
return event.method === PopupSessionControlMethod.Ping;
|
|
504
|
+
}));
|
|
505
|
+
merge(ready$.pipe(map(function createReadyNotify() {
|
|
506
|
+
return { method: PopupSessionControlMethod.Ready };
|
|
507
|
+
})), pong$.pipe(map(function createPongNotify() {
|
|
508
|
+
return { method: PopupSessionControlMethod.Pong };
|
|
509
|
+
}))).pipe(takeUntil(this.destroyed$)).subscribe((event) => {
|
|
510
|
+
options.jsonRpc.notify(event.method, event.params);
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
init() {
|
|
514
|
+
this.init$.next(true);
|
|
515
|
+
}
|
|
516
|
+
dispose() {
|
|
517
|
+
this.destroyed$$.next(true);
|
|
518
|
+
}
|
|
519
|
+
[SYMBOL_DISPOSE]() {
|
|
520
|
+
this.dispose();
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
//#endregion
|
|
524
|
+
//#region src/popup/handle.ts
|
|
525
|
+
/**
|
|
526
|
+
* popup opener
|
|
527
|
+
*/
|
|
528
|
+
var PopupClientWindowHandle = class {
|
|
529
|
+
messaging;
|
|
530
|
+
disposed = false;
|
|
531
|
+
jsonRpc;
|
|
532
|
+
session;
|
|
533
|
+
incomingSubscription;
|
|
534
|
+
closePopup;
|
|
535
|
+
constructor(options) {
|
|
536
|
+
this.messaging = options.messaging;
|
|
537
|
+
this.closePopup = options.close;
|
|
538
|
+
this.jsonRpc = createJsonRpcClient({
|
|
539
|
+
send: (message) => {
|
|
540
|
+
options.messaging.outgoing.next(message);
|
|
541
|
+
},
|
|
542
|
+
time: options.time
|
|
543
|
+
});
|
|
544
|
+
this.session = new PopupClientSession({
|
|
545
|
+
jsonRpc: this.jsonRpc,
|
|
546
|
+
time: options.time,
|
|
547
|
+
closedStream: options.closedStream
|
|
548
|
+
});
|
|
549
|
+
this.incomingSubscription = options.messaging.incoming.subscribe({ next: (message) => {
|
|
550
|
+
this.jsonRpc.receive(message);
|
|
551
|
+
} });
|
|
552
|
+
this.session.init();
|
|
553
|
+
}
|
|
554
|
+
get onNotification() {
|
|
555
|
+
return this.jsonRpc.onNotification;
|
|
556
|
+
}
|
|
557
|
+
get failure() {
|
|
558
|
+
return this.session.failure;
|
|
559
|
+
}
|
|
560
|
+
get isActive() {
|
|
561
|
+
return this.session.isActive;
|
|
562
|
+
}
|
|
563
|
+
async notify(method, params) {
|
|
564
|
+
this.jsonRpc.notify(method, params);
|
|
565
|
+
}
|
|
566
|
+
request(method, params, options) {
|
|
567
|
+
return this.jsonRpc.request(method, params, options);
|
|
568
|
+
}
|
|
569
|
+
close() {
|
|
570
|
+
this.closePopup();
|
|
571
|
+
}
|
|
572
|
+
dispose() {
|
|
573
|
+
if (this.disposed) return;
|
|
574
|
+
this.disposed = true;
|
|
575
|
+
this.session.dispose();
|
|
576
|
+
this.jsonRpc.dispose();
|
|
577
|
+
this.incomingSubscription.unsubscribe();
|
|
578
|
+
this.messaging.outgoing.complete();
|
|
579
|
+
}
|
|
580
|
+
[SYMBOL_DISPOSE]() {
|
|
581
|
+
this.dispose();
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
/**
|
|
585
|
+
* popup target
|
|
586
|
+
*/
|
|
587
|
+
var PopupServerWindowHandle = class {
|
|
588
|
+
messaging;
|
|
589
|
+
disposed = false;
|
|
590
|
+
jsonRpc;
|
|
591
|
+
session;
|
|
592
|
+
incomingSubscription;
|
|
593
|
+
closePopup;
|
|
594
|
+
constructor(options) {
|
|
595
|
+
this.messaging = options.messaging;
|
|
596
|
+
this.closePopup = options.close;
|
|
597
|
+
this.jsonRpc = createJsonRpcServer({ send: (message) => {
|
|
598
|
+
options.messaging.outgoing.next(message);
|
|
599
|
+
} });
|
|
600
|
+
this.session = new PopupServerSession({ jsonRpc: this.jsonRpc });
|
|
601
|
+
this.incomingSubscription = options.messaging.incoming.subscribe({ next: (message) => {
|
|
602
|
+
this.jsonRpc.receive(message);
|
|
603
|
+
} });
|
|
604
|
+
this.session.init();
|
|
605
|
+
}
|
|
606
|
+
get onNotification() {
|
|
607
|
+
return this.jsonRpc.onNotification;
|
|
608
|
+
}
|
|
609
|
+
get onRequest() {
|
|
610
|
+
return this.jsonRpc.onRequest;
|
|
611
|
+
}
|
|
612
|
+
notify(method, params) {
|
|
613
|
+
this.jsonRpc.notify(method, params);
|
|
614
|
+
return Promise.resolve();
|
|
615
|
+
}
|
|
616
|
+
close() {
|
|
617
|
+
this.closePopup();
|
|
618
|
+
}
|
|
619
|
+
dispose() {
|
|
620
|
+
if (this.disposed) return;
|
|
621
|
+
this.disposed = true;
|
|
622
|
+
this.session.dispose();
|
|
623
|
+
this.jsonRpc.dispose();
|
|
624
|
+
this.incomingSubscription.unsubscribe();
|
|
625
|
+
this.messaging.outgoing.complete();
|
|
626
|
+
}
|
|
627
|
+
[SYMBOL_DISPOSE]() {
|
|
628
|
+
this.dispose();
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
//#endregion
|
|
632
|
+
export { createJsonRpcServerAndClient as a, createSignal as c, createJsonRpcServer as i, readonlySignal as l, PopupServerWindowHandle as n, PopupErrorCode as o, createJsonRpcClient as r, readPopupErrorPresentationDescriptor as s, PopupClientWindowHandle as t, readErrorPresentationDescriptor as u };
|
|
633
|
+
|
|
634
|
+
//# sourceMappingURL=handle-TnxwtQ4T.mjs.map
|