@milaboratories/pl-middle-layer 1.37.4 → 1.37.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +40 -14
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +1747 -1468
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/mutator/context_export.d.ts +2 -2
  8. package/dist/mutator/context_export.d.ts.map +1 -1
  9. package/dist/mutator/template/render_template.d.ts +2 -2
  10. package/dist/mutator/template/render_template.d.ts.map +1 -1
  11. package/dist/network_check/network_check.d.ts +40 -0
  12. package/dist/network_check/network_check.d.ts.map +1 -0
  13. package/dist/network_check/network_check.test.d.ts.map +1 -0
  14. package/dist/network_check/pings.d.ts +32 -0
  15. package/dist/network_check/pings.d.ts.map +1 -0
  16. package/dist/network_check/template.d.ts +33 -0
  17. package/dist/network_check/template.d.ts.map +1 -0
  18. package/dist/network_check/template.test.d.ts +2 -0
  19. package/dist/network_check/template.test.d.ts.map +1 -0
  20. package/dist/network_check/test_utils.d.ts +6 -0
  21. package/dist/network_check/test_utils.d.ts.map +1 -0
  22. package/package.json +10 -10
  23. package/src/index.ts +1 -1
  24. package/src/mutator/template/render_template.ts +2 -2
  25. package/src/{network_check.test.ts → network_check/network_check.test.ts} +3 -3
  26. package/src/network_check/network_check.ts +369 -0
  27. package/src/network_check/pings.ts +154 -0
  28. package/src/network_check/template.test.ts +83 -0
  29. package/src/network_check/template.ts +330 -0
  30. package/src/network_check/test_utils.ts +9 -0
  31. package/dist/network_check.d.ts +0 -29
  32. package/dist/network_check.d.ts.map +0 -1
  33. package/dist/network_check.test.d.ts.map +0 -1
  34. package/src/network_check.ts +0 -329
  35. /package/dist/{network_check.test.d.ts → network_check/network_check.test.d.ts} +0 -0
@@ -1,329 +0,0 @@
1
- /** A utility to check network problems and gather statistics.
2
- * It's useful when we cannot connect to the server of a company
3
- * because of security reasons,
4
- * but they can send us and their DevOps team this report. */
5
-
6
- import { UnauthenticatedPlClient, plAddressToConfig } from '@milaboratories/pl-client';
7
- import type { ValueOrError } from '@milaboratories/ts-helpers';
8
- import { setTimeout } from 'node:timers/promises';
9
- import type { Dispatcher } from 'undici';
10
- import { request } from 'undici';
11
- import { channel } from 'node:diagnostics_channel';
12
-
13
- /** All reports we need to collect. */
14
- interface networkReports {
15
- plPings: networkReport<string>[];
16
-
17
- blockRegistryOverviewChecks: httpNetworkReport[];
18
- blockGARegistryOverviewChecks: httpNetworkReport[];
19
- blockRegistryUiChecks: httpNetworkReport[];
20
- blockGARegistryUiChecks: httpNetworkReport[];
21
-
22
- autoUpdateCdnChecks: httpNetworkReport[];
23
- }
24
-
25
- export interface CheckNetworkOpts {
26
- /** Platforma Backend pings options. */
27
- pingCheckDurationMs: number;
28
- pingTimeoutMs: number;
29
- maxPingsPerSecond: number;
30
-
31
- /** An options for CDN and block registry. */
32
- httpTimeoutMs: number;
33
-
34
- /** Block registry pings options. */
35
- blockRegistryDurationMs: number;
36
- maxRegistryChecksPerSecond: number;
37
- blockRegistryUrl: string;
38
- blockGARegistryUrl: string;
39
- blockOverviewPath: string;
40
- blockUiPath: string;
41
-
42
- /** CDN for auto-update pings options. */
43
- autoUpdateCdnDurationMs: number;
44
- maxAutoUpdateCdnChecksPerSecond: number;
45
- autoUpdateCdnUrl: string;
46
-
47
- bodyLimit: number;
48
- }
49
-
50
- /** A report about one concrete ping to the service. */
51
- interface networkReport<T> {
52
- elapsedMs: number;
53
- response: ValueOrError<T>;
54
- }
55
-
56
- type httpNetworkReport = networkReport<{
57
- statusCode: number;
58
- beginningOfBody: string;
59
- }>;
60
-
61
- // List of Undici diagnostic channels
62
- const undiciEvents: string[] = [
63
- 'undici:request:create', // When a new request is created
64
- 'undici:request:bodySent', // When the request body is sent
65
- 'undici:request:headers', // When request headers are sent
66
- 'undici:request:error', // When a request encounters an error
67
- 'undici:request:trailers', // When a response completes.
68
-
69
- 'undici:client:sendHeaders',
70
- 'undici:client:beforeConnect',
71
- 'undici:client:connected',
72
- 'undici:client:connectError',
73
-
74
- 'undici:socket:close', // When a socket is closed
75
- 'undici:socket:connect', // When a socket connects
76
- 'undici:socket:error', // When a socket encounters an error
77
-
78
- 'undici:pool:request', // When a request is added to the pool
79
- 'undici:pool:connect', // When a pool creates a new connection
80
- 'undici:pool:disconnect', // When a pool connection is closed
81
- 'undici:pool:destroy', // When a pool is destroyed
82
- 'undici:dispatcher:request', // When a dispatcher processes a request
83
- 'undici:dispatcher:connect', // When a dispatcher connects
84
- 'undici:dispatcher:disconnect', // When a dispatcher disconnects
85
- 'undici:dispatcher:retry', // When a dispatcher retries a request
86
- ];
87
-
88
- /** Checks connectivity to Platforma Backend, to block registry
89
- * and to auto-update CDN,
90
- * and generates a string report. */
91
- export async function checkNetwork(
92
- plCredentials: string,
93
- optsOverrides: Partial<CheckNetworkOpts> = {},
94
- ): Promise<string> {
95
- const ops: CheckNetworkOpts = {
96
- pingCheckDurationMs: 10000,
97
- pingTimeoutMs: 3000,
98
- maxPingsPerSecond: 50,
99
-
100
- httpTimeoutMs: 3000,
101
-
102
- blockRegistryDurationMs: 3000,
103
- maxRegistryChecksPerSecond: 1,
104
-
105
- blockRegistryUrl: 'https://blocks.pl-open.science',
106
- blockGARegistryUrl: 'https://blocks-ga.pl-open.science',
107
- blockOverviewPath: 'v2/overview.json',
108
- blockUiPath: 'v2/milaboratories/samples-and-data/1.7.0/ui.tgz',
109
-
110
- autoUpdateCdnDurationMs: 5000,
111
- maxAutoUpdateCdnChecksPerSecond: 1,
112
- autoUpdateCdnUrl:
113
- 'https://cdn.platforma.bio/software/platforma-desktop-v2/windows/amd64/latest.yml',
114
-
115
- bodyLimit: 300,
116
-
117
- ...optsOverrides,
118
- };
119
-
120
- const undiciLogs: any[] = [];
121
- // Subscribe to all Undici diagnostic events
122
- undiciEvents.forEach((event) => {
123
- const diagnosticChannel = channel(event);
124
- diagnosticChannel.subscribe((message: any) => {
125
- const timestamp = new Date().toISOString();
126
- if (message?.response?.headers)
127
- message.response.headers = message.response.headers.map((h: any) => h.toString());
128
-
129
- undiciLogs.push(
130
- JSON.stringify({
131
- timestamp,
132
- event,
133
- data: message,
134
- }),
135
- );
136
- });
137
- });
138
-
139
- const report: networkReports = {
140
- plPings: [],
141
- blockRegistryOverviewChecks: [],
142
- blockGARegistryOverviewChecks: [],
143
- blockRegistryUiChecks: [],
144
- blockGARegistryUiChecks: [],
145
-
146
- autoUpdateCdnChecks: [],
147
- };
148
-
149
- const plConfig = plAddressToConfig(plCredentials, { defaultRequestTimeout: ops.pingTimeoutMs });
150
-
151
- report.plPings = await recordPings(ops.pingCheckDurationMs, ops.maxPingsPerSecond, async () => {
152
- const uaClient = new UnauthenticatedPlClient(plConfig);
153
- const response = await uaClient.ping();
154
- return JSON.stringify(response).slice(0, ops.bodyLimit) + '...';
155
- });
156
-
157
- const uaClient = new UnauthenticatedPlClient(plConfig);
158
- const httpClient = uaClient.ll.httpDispatcher;
159
-
160
- report.blockRegistryOverviewChecks = await recordPings(
161
- ops.blockRegistryDurationMs,
162
- ops.maxRegistryChecksPerSecond,
163
- async () =>
164
- await requestUrl(new URL(ops.blockOverviewPath, ops.blockRegistryUrl), ops, httpClient),
165
- );
166
-
167
- report.blockGARegistryOverviewChecks = await recordPings(
168
- ops.blockRegistryDurationMs,
169
- ops.maxRegistryChecksPerSecond,
170
- async () =>
171
- await requestUrl(new URL(ops.blockOverviewPath, ops.blockGARegistryUrl), ops, httpClient),
172
- );
173
-
174
- report.blockRegistryUiChecks = await recordPings(
175
- ops.blockRegistryDurationMs,
176
- ops.maxRegistryChecksPerSecond,
177
- async () => await requestUrl(new URL(ops.blockUiPath, ops.blockRegistryUrl), ops, httpClient),
178
- );
179
-
180
- report.blockGARegistryUiChecks = await recordPings(
181
- ops.blockRegistryDurationMs,
182
- ops.maxRegistryChecksPerSecond,
183
- async () => await requestUrl(new URL(ops.blockUiPath, ops.blockGARegistryUrl), ops, httpClient),
184
- );
185
-
186
- report.autoUpdateCdnChecks = await recordPings(
187
- ops.autoUpdateCdnDurationMs,
188
- ops.maxAutoUpdateCdnChecksPerSecond,
189
- async () => await requestUrl(ops.autoUpdateCdnUrl, ops, httpClient),
190
- );
191
-
192
- return reportsToString(report, plCredentials, ops, undiciLogs);
193
- }
194
-
195
- /** Executes a body several times per second up to the given duration,
196
- * and returns results and elapsed time for every result. */
197
- async function recordPings<T>(
198
- pingCheckDurationMs: number,
199
- maxPingsPerSecond: number,
200
- body: () => Promise<T>,
201
- ): Promise<networkReport<T>[]> {
202
- const startPings = nowMs();
203
- const reports: networkReport<T>[] = [];
204
-
205
- while (elapsed(startPings) < pingCheckDurationMs) {
206
- const startPing = nowMs();
207
- let response: ValueOrError<T>;
208
- try {
209
- response = { ok: true, value: await body() };
210
- } catch (e) {
211
- response = { ok: false, error: e };
212
- }
213
- const elapsedPing = elapsed(startPing);
214
-
215
- reports.push({
216
- elapsedMs: elapsedPing,
217
- response,
218
- });
219
-
220
- const sleepBetweenPings = 1000 / maxPingsPerSecond - elapsedPing;
221
-
222
- if (sleepBetweenPings > 0) await setTimeout(sleepBetweenPings);
223
- }
224
-
225
- return reports;
226
- }
227
-
228
- async function requestUrl(url: string | URL, ops: CheckNetworkOpts, httpClient: Dispatcher) {
229
- const { body: rawBody, statusCode } = await request(url, {
230
- dispatcher: httpClient,
231
- headersTimeout: ops.httpTimeoutMs,
232
- bodyTimeout: ops.httpTimeoutMs,
233
- });
234
- const body = await rawBody.text();
235
-
236
- return {
237
- statusCode: statusCode,
238
- beginningOfBody: body.slice(0, ops.bodyLimit) + '...',
239
- };
240
- }
241
-
242
- function reportsToString(
243
- report: networkReports,
244
- plEndpoint: string,
245
- opts: CheckNetworkOpts,
246
- undiciLogs: any[],
247
- ): string {
248
- const successPings = report.plPings.filter((p) => p.response.ok);
249
- const failedPings = report.plPings.filter((p) => !p.response.ok);
250
- const successPingsBodies = [
251
- ...new Set(successPings.map((p) => JSON.stringify((p.response as any).value))),
252
- ];
253
-
254
- return `
255
- Network report:
256
- pl endpoint: ${plEndpoint};
257
- options: ${JSON.stringify(opts, null, 2)}.
258
-
259
- Platforma pings: ${reportToString(report.plPings)}
260
-
261
- Block registry overview responses: ${reportToString(report.blockRegistryOverviewChecks)}
262
-
263
- Block ga registry overview responses: ${reportToString(report.blockGARegistryOverviewChecks)}
264
-
265
- Block registry ui responses: ${reportToString(report.blockRegistryUiChecks)}
266
-
267
- Block ga registry ui responses: ${reportToString(report.blockGARegistryUiChecks)}
268
-
269
- Auto-update CDN responses: ${reportToString(report.autoUpdateCdnChecks)}
270
-
271
- Block registry overview dumps:
272
- ${JSON.stringify(report.blockRegistryOverviewChecks, null, 2)}
273
-
274
- Block ga registry overview dumps:
275
- ${JSON.stringify(report.blockGARegistryOverviewChecks, null, 2)}
276
-
277
- Block registry ui dumps:
278
- ${JSON.stringify(report.blockRegistryUiChecks, null, 2)}
279
-
280
- Block ga registry ui dumps:
281
- ${JSON.stringify(report.blockGARegistryUiChecks, null, 2)}
282
-
283
- Auto-update CDN dumps:
284
- ${JSON.stringify(report.autoUpdateCdnChecks, null, 2)}
285
-
286
- Platforma pings error dumps:
287
- ${JSON.stringify(failedPings, null, 2)}
288
-
289
- Platforma pings success dump examples:
290
- ${JSON.stringify(successPingsBodies, null, 2)}
291
-
292
- Undici logs:
293
- ${undiciLogs.join('\n')}
294
- `;
295
- }
296
-
297
- function reportToString<T>(report: networkReport<T>[]): string {
298
- const successes = report.filter((r) => r.response.ok);
299
- const { mean: mean, median: median } = elapsedStat(report);
300
-
301
- return `
302
- total: ${report.length};
303
- successes: ${successes.length};
304
- errors: ${report.length - successes.length};
305
- mean in ms: ${mean};
306
- median in ms: ${median};
307
- `;
308
- }
309
-
310
- function elapsedStat(reports: { elapsedMs: number }[]) {
311
- const checks = reports.map((p) => p.elapsedMs);
312
- const mean = checks.reduce((sum, p) => sum + p) / checks.length;
313
-
314
- let median = undefined;
315
- if (checks.length > 0) {
316
- const mid = Math.floor(checks.length / 2);
317
- median = checks.length % 2 ? checks[mid] : (checks[mid - 1] + checks[mid]) / 2;
318
- }
319
-
320
- return { mean, median };
321
- }
322
-
323
- function nowMs(): number {
324
- return Date.now();
325
- }
326
-
327
- function elapsed(startMs: number): number {
328
- return nowMs() - startMs;
329
- }