@foxford/den-traces 0.1.0 → 2.0.0

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.mdx CHANGED
@@ -41,9 +41,26 @@ export default defineAppConfig({
41
41
  3. Импорты меток и походов — из подпути `./timings` (без серверных зависимостей):
42
42
 
43
43
  ```typescript
44
- import { measure, trackFetch } from '@foxford/den-traces/timings'
44
+ import { measure, startRequest } from '@foxford/den-traces/timings'
45
45
  ```
46
46
 
47
+ `startRequest(name, url)` начинает поход наружу и отдаёт `RequestSpan`: `finish(status, bytes?)` —
48
+ ответ получен, `fail(error)` — до ответа не дошли. Транспорта пакет не знает: кто ходил, тот и
49
+ сообщает о походе — клиент `@foxford/services` через плагины ниже, всё остальное — этой парой
50
+ вызовов вокруг своего запроса.
51
+
52
+ 4. Приложение, которое ходит в бэкенд клиентом `@foxford/services` (в том числе SDK
53
+ `@foxford/sdk`), подключает плагины сбора из подпути `./services` — тогда каждый запрос
54
+ клиента становится походом наружу без единой строки сбора в прикладном коде:
55
+
56
+ ```typescript
57
+ import { createTracePlugins } from '@foxford/den-traces/services'
58
+
59
+ const sdk = new Foxford({ api: { config: { headers: { cookie } }, plugins: createTracePlugins() } })
60
+ ```
61
+
62
+ Имя похода — `traceName` в конфиге запроса, без него — метод и путь (`get /api/pages`).
63
+
47
64
  `installServerTrace` ставит `setTimingSink` / `setRequestSink`. Приёмник upstream-ошибок
48
65
  (`setUpstreamErrorSink`) цепляет `installServerSentry`.
49
66
 
@@ -52,4 +69,5 @@ import { measure, trackFetch } from '@foxford/den-traces/timings'
52
69
  | Путь | Содержимое |
53
70
  |---|---|
54
71
  | `@foxford/den-traces` | `installServerTrace`, `recordServerTiming`, `describeDeclarations`, sinks |
55
- | `@foxford/den-traces/timings` | `measure`, `trackFetch`, `setTimingSink`, `setRequestSink`, `setUpstreamErrorSink` |
72
+ | `@foxford/den-traces/timings` | `measure`, `startRequest`, `setTimingSink`, `setRequestSink`, `setUpstreamErrorSink` |
73
+ | `@foxford/den-traces/services` | `createTracePlugins`, `traceNameOf`, `traceUrlOf` — плагины клиента `@foxford/services` |
@@ -0,0 +1,64 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/timings/index.ts
2
+ var sink = null;
3
+ var requestSink = null;
4
+ var upstreamErrorSink = null;
5
+ function setTimingSink(next) {
6
+ sink = next;
7
+ }
8
+ function setRequestSink(next) {
9
+ requestSink = next;
10
+ }
11
+ function setUpstreamErrorSink(next) {
12
+ upstreamErrorSink = next;
13
+ }
14
+ async function measure(name, work) {
15
+ if (sink === null) {
16
+ return work();
17
+ }
18
+ const startedAt = performance.now();
19
+ try {
20
+ return await work();
21
+ } finally {
22
+ sink(name, performance.now() - startedAt, startedAt);
23
+ }
24
+ }
25
+ function startRequest(name, url) {
26
+ const startedAt = performance.now();
27
+ let done = false;
28
+ return {
29
+ fail(error) {
30
+ if (done) return;
31
+ done = true;
32
+ const ms = performance.now() - startedAt;
33
+ _optionalChain([sink, 'optionalCall', _ => _(name, ms, startedAt)]);
34
+ _optionalChain([requestSink, 'optionalCall', _2 => _2({ bytes: null, error: String(error), ms, name, startedAt, status: null, url })]);
35
+ _optionalChain([upstreamErrorSink, 'optionalCall', _3 => _3({ name, reason: "network", status: null, url })]);
36
+ },
37
+ finish(status, bytes = null) {
38
+ if (done) return;
39
+ done = true;
40
+ const ms = performance.now() - startedAt;
41
+ _optionalChain([sink, 'optionalCall', _4 => _4(name, ms, startedAt)]);
42
+ _optionalChain([requestSink, 'optionalCall', _5 => _5({ bytes, ms, name, startedAt, status, url })]);
43
+ if (status < 200 || status >= 300) {
44
+ _optionalChain([upstreamErrorSink, 'optionalCall', _6 => _6({ name, reason: `http_${String(status)}`, status, url })]);
45
+ }
46
+ }
47
+ };
48
+ }
49
+ function contentLength(value) {
50
+ if (value === null || value === void 0 || value === "") {
51
+ return null;
52
+ }
53
+ const parsed = Number(value);
54
+ return Number.isNaN(parsed) ? null : parsed;
55
+ }
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+ exports.setTimingSink = setTimingSink; exports.setRequestSink = setRequestSink; exports.setUpstreamErrorSink = setUpstreamErrorSink; exports.measure = measure; exports.startRequest = startRequest; exports.contentLength = contentLength;
@@ -0,0 +1,64 @@
1
+ // src/timings/index.ts
2
+ var sink = null;
3
+ var requestSink = null;
4
+ var upstreamErrorSink = null;
5
+ function setTimingSink(next) {
6
+ sink = next;
7
+ }
8
+ function setRequestSink(next) {
9
+ requestSink = next;
10
+ }
11
+ function setUpstreamErrorSink(next) {
12
+ upstreamErrorSink = next;
13
+ }
14
+ async function measure(name, work) {
15
+ if (sink === null) {
16
+ return work();
17
+ }
18
+ const startedAt = performance.now();
19
+ try {
20
+ return await work();
21
+ } finally {
22
+ sink(name, performance.now() - startedAt, startedAt);
23
+ }
24
+ }
25
+ function startRequest(name, url) {
26
+ const startedAt = performance.now();
27
+ let done = false;
28
+ return {
29
+ fail(error) {
30
+ if (done) return;
31
+ done = true;
32
+ const ms = performance.now() - startedAt;
33
+ sink?.(name, ms, startedAt);
34
+ requestSink?.({ bytes: null, error: String(error), ms, name, startedAt, status: null, url });
35
+ upstreamErrorSink?.({ name, reason: "network", status: null, url });
36
+ },
37
+ finish(status, bytes = null) {
38
+ if (done) return;
39
+ done = true;
40
+ const ms = performance.now() - startedAt;
41
+ sink?.(name, ms, startedAt);
42
+ requestSink?.({ bytes, ms, name, startedAt, status, url });
43
+ if (status < 200 || status >= 300) {
44
+ upstreamErrorSink?.({ name, reason: `http_${String(status)}`, status, url });
45
+ }
46
+ }
47
+ };
48
+ }
49
+ function contentLength(value) {
50
+ if (value === null || value === void 0 || value === "") {
51
+ return null;
52
+ }
53
+ const parsed = Number(value);
54
+ return Number.isNaN(parsed) ? null : parsed;
55
+ }
56
+
57
+ export {
58
+ setTimingSink,
59
+ setRequestSink,
60
+ setUpstreamErrorSink,
61
+ measure,
62
+ startRequest,
63
+ contentLength
64
+ };
package/index.cjs CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
 
6
6
 
7
- var _chunkF4ITEBDQcjs = require('./chunk-F4ITEBDQ.cjs');
7
+ var _chunkDAPDESIHcjs = require('./chunk-DAPDESIH.cjs');
8
8
 
9
9
  // src/server/install.ts
10
10
  var _async_hooks = require('async_hooks');
@@ -61,8 +61,8 @@ function asked(headers) {
61
61
  return headers[TRACE_HEADER] !== void 0;
62
62
  }
63
63
  function installServerTrace(instance, options = {}) {
64
- _chunkF4ITEBDQcjs.setTimingSink.call(void 0, recordServerTiming);
65
- _chunkF4ITEBDQcjs.setRequestSink.call(void 0, (request) => _optionalChain([storage, 'access', _4 => _4.getStore, 'call', _5 => _5(), 'optionalAccess', _6 => _6.requests, 'access', _7 => _7.push, 'call', _8 => _8(request)]));
64
+ _chunkDAPDESIHcjs.setTimingSink.call(void 0, recordServerTiming);
65
+ _chunkDAPDESIHcjs.setRequestSink.call(void 0, (request) => _optionalChain([storage, 'access', _4 => _4.getStore, 'call', _5 => _5(), 'optionalAccess', _6 => _6.requests, 'access', _7 => _7.push, 'call', _8 => _8(request)]));
66
66
  instance.addHook("onRequest", (_request, _reply, done) => {
67
67
  storage.run({ marks: [], requests: [], startedAt: performance.now() }, done);
68
68
  });
@@ -94,4 +94,4 @@ function installServerTrace(instance, options = {}) {
94
94
 
95
95
 
96
96
 
97
- exports.describeDeclarations = describeDeclarations; exports.installServerTrace = installServerTrace; exports.measure = _chunkF4ITEBDQcjs.measure; exports.recordServerTiming = recordServerTiming; exports.setRequestSink = _chunkF4ITEBDQcjs.setRequestSink; exports.setTimingSink = _chunkF4ITEBDQcjs.setTimingSink; exports.setUpstreamErrorSink = _chunkF4ITEBDQcjs.setUpstreamErrorSink; exports.traceHeader = traceHeader; exports.trackFetch = _chunkF4ITEBDQcjs.trackFetch;
97
+ exports.describeDeclarations = describeDeclarations; exports.installServerTrace = installServerTrace; exports.measure = _chunkDAPDESIHcjs.measure; exports.recordServerTiming = recordServerTiming; exports.setRequestSink = _chunkDAPDESIHcjs.setRequestSink; exports.setTimingSink = _chunkDAPDESIHcjs.setTimingSink; exports.setUpstreamErrorSink = _chunkDAPDESIHcjs.setUpstreamErrorSink; exports.startRequest = _chunkDAPDESIHcjs.startRequest; exports.traceHeader = traceHeader;
package/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { TrackedRequest } from './timings/index.cjs';
2
- export { RequestSink, TimingSink, UpstreamErrorContext, UpstreamErrorSink, measure, setRequestSink, setTimingSink, setUpstreamErrorSink, trackFetch } from './timings/index.cjs';
2
+ export { RequestSink, RequestSpan, TimingSink, UpstreamErrorContext, UpstreamErrorSink, measure, setRequestSink, setTimingSink, setUpstreamErrorSink, startRequest } from './timings/index.cjs';
3
3
 
4
4
  /**
5
5
  * Хуки инстанса, которыми пользуется `@foxford/den`.
package/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { TrackedRequest } from './timings/index.js';
2
- export { RequestSink, TimingSink, UpstreamErrorContext, UpstreamErrorSink, measure, setRequestSink, setTimingSink, setUpstreamErrorSink, trackFetch } from './timings/index.js';
2
+ export { RequestSink, RequestSpan, TimingSink, UpstreamErrorContext, UpstreamErrorSink, measure, setRequestSink, setTimingSink, setUpstreamErrorSink, startRequest } from './timings/index.js';
3
3
 
4
4
  /**
5
5
  * Хуки инстанса, которыми пользуется `@foxford/den`.
package/index.js CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  setRequestSink,
4
4
  setTimingSink,
5
5
  setUpstreamErrorSink,
6
- trackFetch
7
- } from "./chunk-HN4BBSRG.js";
6
+ startRequest
7
+ } from "./chunk-PMYUYUJQ.js";
8
8
 
9
9
  // src/server/install.ts
10
10
  import { AsyncLocalStorage } from "async_hooks";
@@ -92,6 +92,6 @@ export {
92
92
  setRequestSink,
93
93
  setTimingSink,
94
94
  setUpstreamErrorSink,
95
- traceHeader,
96
- trackFetch
95
+ startRequest,
96
+ traceHeader
97
97
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxford/den-traces",
3
- "version": "0.1.0",
3
+ "version": "2.0.0",
4
4
  "description": "Метки времени, походы наружу и дев-трасса для den-приложений",
5
5
  "keywords": [
6
6
  "foxford",
@@ -32,7 +32,7 @@
32
32
  ],
33
33
  "type": "module",
34
34
  "peerDependencies": {
35
- "@foxford/den": "^3.3.0"
35
+ "@foxford/den": "^3.5.0"
36
36
  },
37
37
  "main": "./index.cjs",
38
38
  "module": "./index.js",
@@ -42,6 +42,11 @@
42
42
  "require": "./index.cjs",
43
43
  "types": "./index.d.ts"
44
44
  },
45
+ "./services": {
46
+ "import": "./services/index.js",
47
+ "require": "./services/index.cjs",
48
+ "types": "./services/index.d.ts"
49
+ },
45
50
  "./timings": {
46
51
  "import": "./timings/index.js",
47
52
  "require": "./timings/index.cjs",
@@ -50,15 +55,16 @@
50
55
  },
51
56
  "files": [
52
57
  "README.mdx",
53
- "chunk-F4ITEBDQ.cjs",
54
- "chunk-HN4BBSRG.js",
58
+ "chunk-DAPDESIH.cjs",
59
+ "chunk-PMYUYUJQ.js",
55
60
  "index.cjs",
56
61
  "index.d.cts",
57
62
  "index.d.ts",
58
63
  "index.js",
59
64
  "package.json",
65
+ "services",
60
66
  "timings"
61
67
  ],
62
- "sha": "5638b60",
68
+ "sha": "a0e9213",
63
69
  "scripts": {}
64
70
  }
@@ -0,0 +1,70 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+
4
+ var _chunkDAPDESIHcjs = require('../chunk-DAPDESIH.cjs');
5
+
6
+ // src/services/index.ts
7
+ var SPAN = /* @__PURE__ */ Symbol("den-traces:span");
8
+ function traceNameOf(config) {
9
+ if (typeof config.traceName === "string" && config.traceName !== "") {
10
+ return config.traceName;
11
+ }
12
+ const path = _nullishCoalesce((_nullishCoalesce(config.url, () => ( ""))).split("?")[0], () => ( ""));
13
+ return `${(_nullishCoalesce(config.method, () => ( "get"))).toLowerCase()} ${path}`;
14
+ }
15
+ function traceUrlOf(config) {
16
+ const url = _nullishCoalesce(config.url, () => ( ""));
17
+ if (/^https?:\/\//.test(url) || !config.baseURL) {
18
+ return url;
19
+ }
20
+ return `${config.baseURL.replace(/\/$/, "")}/${url.replace(/^\//, "")}`;
21
+ }
22
+ function spanOf(config) {
23
+ const span = _optionalChain([config, 'optionalAccess', _ => _[SPAN]]);
24
+ return typeof span === "object" && span !== null ? span : void 0;
25
+ }
26
+ function lengthOf(headers) {
27
+ const value = _optionalChain([headers, 'optionalAccess', _2 => _2["content-length"]]);
28
+ return _chunkDAPDESIHcjs.contentLength.call(void 0, typeof value === "string" ? value : typeof value === "number" ? String(value) : null);
29
+ }
30
+ function createTracePlugins() {
31
+ return [
32
+ {
33
+ execute: (config) => {
34
+ config[SPAN] = _chunkDAPDESIHcjs.startRequest.call(void 0, traceNameOf(config), traceUrlOf(config));
35
+ return Promise.resolve(config);
36
+ },
37
+ isApplicable: () => true,
38
+ pluginName: "den-traces:request",
39
+ pluginType: "request"
40
+ },
41
+ {
42
+ execute: (response) => {
43
+ _optionalChain([spanOf, 'call', _3 => _3(response.config), 'optionalAccess', _4 => _4.finish, 'call', _5 => _5(response.status, lengthOf(response.headers))]);
44
+ return Promise.resolve(response);
45
+ },
46
+ isApplicable: () => true,
47
+ pluginName: "den-traces:response",
48
+ pluginType: "responseSuccess"
49
+ },
50
+ {
51
+ execute: (error) => {
52
+ const span = spanOf(error.config);
53
+ if (error.response) {
54
+ _optionalChain([span, 'optionalAccess', _6 => _6.finish, 'call', _7 => _7(error.response.status, lengthOf(error.response.headers))]);
55
+ } else {
56
+ _optionalChain([span, 'optionalAccess', _8 => _8.fail, 'call', _9 => _9(error)]);
57
+ }
58
+ return Promise.resolve(error);
59
+ },
60
+ isApplicable: () => true,
61
+ pluginName: "den-traces:fail",
62
+ pluginType: "responseFail"
63
+ }
64
+ ];
65
+ }
66
+
67
+
68
+
69
+
70
+ exports.createTracePlugins = createTracePlugins; exports.traceNameOf = traceNameOf; exports.traceUrlOf = traceUrlOf;
@@ -0,0 +1,59 @@
1
+ /** Часть конфига запроса клиента, которую читают плагины. */
2
+ interface TracedRequestConfig {
3
+ url?: string;
4
+ baseURL?: string;
5
+ method?: string;
6
+ /** Имя куска работы для меток; без него — метод и путь. */
7
+ traceName?: string;
8
+ [key: string | symbol]: unknown;
9
+ }
10
+ /** Часть ответа клиента, которую читают плагины. */
11
+ interface TracedResponse {
12
+ status: number;
13
+ headers?: Record<string, unknown>;
14
+ config: TracedRequestConfig;
15
+ [key: string]: unknown;
16
+ }
17
+ /** Часть ошибки клиента, которую читают плагины. */
18
+ interface TracedError {
19
+ config?: TracedRequestConfig;
20
+ response?: {
21
+ status: number;
22
+ headers?: Record<string, unknown>;
23
+ };
24
+ [key: string]: unknown;
25
+ }
26
+ /** Плагин запроса — форма `RequestPluginInterface` из `@foxford/services`. */
27
+ interface TraceRequestPlugin {
28
+ pluginType: 'request';
29
+ pluginName: string;
30
+ isApplicable(config: TracedRequestConfig): boolean;
31
+ execute(config: TracedRequestConfig): Promise<TracedRequestConfig>;
32
+ }
33
+ /** Плагин успешного ответа — форма `ResponseSuccessPluginInterface`. */
34
+ interface TraceResponsePlugin {
35
+ pluginType: 'responseSuccess';
36
+ pluginName: string;
37
+ isApplicable(response: TracedResponse): boolean;
38
+ execute(response: TracedResponse): Promise<TracedResponse>;
39
+ }
40
+ /** Плагин ошибки — форма `ResponseFailPluginInterface`: ошибку возвращает как есть. */
41
+ interface TraceFailPlugin {
42
+ pluginType: 'responseFail';
43
+ pluginName: string;
44
+ isApplicable(error: TracedError): boolean;
45
+ execute(error: TracedError): Promise<TracedError>;
46
+ }
47
+ /** Имя куска работы: явное из конфига либо метод и путь без строки запроса. */
48
+ declare function traceNameOf(config: TracedRequestConfig): string;
49
+ /** Полный адрес похода: база клиента плюс путь, если путь относительный. */
50
+ declare function traceUrlOf(config: TracedRequestConfig): string;
51
+ /**
52
+ * Плагины сбора походов для клиента `@foxford/services`: подключаются в `plugins`
53
+ * при создании инстанса (`createFoxApiInstance`) либо в `api.plugins` конфига SDK.
54
+ *
55
+ * @returns Тройка плагинов: начать поход, закончить ответом, закончить ошибкой
56
+ */
57
+ declare function createTracePlugins(): [TraceRequestPlugin, TraceResponsePlugin, TraceFailPlugin];
58
+
59
+ export { type TraceFailPlugin, type TraceRequestPlugin, type TraceResponsePlugin, type TracedError, type TracedRequestConfig, type TracedResponse, createTracePlugins, traceNameOf, traceUrlOf };
@@ -0,0 +1,59 @@
1
+ /** Часть конфига запроса клиента, которую читают плагины. */
2
+ interface TracedRequestConfig {
3
+ url?: string;
4
+ baseURL?: string;
5
+ method?: string;
6
+ /** Имя куска работы для меток; без него — метод и путь. */
7
+ traceName?: string;
8
+ [key: string | symbol]: unknown;
9
+ }
10
+ /** Часть ответа клиента, которую читают плагины. */
11
+ interface TracedResponse {
12
+ status: number;
13
+ headers?: Record<string, unknown>;
14
+ config: TracedRequestConfig;
15
+ [key: string]: unknown;
16
+ }
17
+ /** Часть ошибки клиента, которую читают плагины. */
18
+ interface TracedError {
19
+ config?: TracedRequestConfig;
20
+ response?: {
21
+ status: number;
22
+ headers?: Record<string, unknown>;
23
+ };
24
+ [key: string]: unknown;
25
+ }
26
+ /** Плагин запроса — форма `RequestPluginInterface` из `@foxford/services`. */
27
+ interface TraceRequestPlugin {
28
+ pluginType: 'request';
29
+ pluginName: string;
30
+ isApplicable(config: TracedRequestConfig): boolean;
31
+ execute(config: TracedRequestConfig): Promise<TracedRequestConfig>;
32
+ }
33
+ /** Плагин успешного ответа — форма `ResponseSuccessPluginInterface`. */
34
+ interface TraceResponsePlugin {
35
+ pluginType: 'responseSuccess';
36
+ pluginName: string;
37
+ isApplicable(response: TracedResponse): boolean;
38
+ execute(response: TracedResponse): Promise<TracedResponse>;
39
+ }
40
+ /** Плагин ошибки — форма `ResponseFailPluginInterface`: ошибку возвращает как есть. */
41
+ interface TraceFailPlugin {
42
+ pluginType: 'responseFail';
43
+ pluginName: string;
44
+ isApplicable(error: TracedError): boolean;
45
+ execute(error: TracedError): Promise<TracedError>;
46
+ }
47
+ /** Имя куска работы: явное из конфига либо метод и путь без строки запроса. */
48
+ declare function traceNameOf(config: TracedRequestConfig): string;
49
+ /** Полный адрес похода: база клиента плюс путь, если путь относительный. */
50
+ declare function traceUrlOf(config: TracedRequestConfig): string;
51
+ /**
52
+ * Плагины сбора походов для клиента `@foxford/services`: подключаются в `plugins`
53
+ * при создании инстанса (`createFoxApiInstance`) либо в `api.plugins` конфига SDK.
54
+ *
55
+ * @returns Тройка плагинов: начать поход, закончить ответом, закончить ошибкой
56
+ */
57
+ declare function createTracePlugins(): [TraceRequestPlugin, TraceResponsePlugin, TraceFailPlugin];
58
+
59
+ export { type TraceFailPlugin, type TraceRequestPlugin, type TraceResponsePlugin, type TracedError, type TracedRequestConfig, type TracedResponse, createTracePlugins, traceNameOf, traceUrlOf };
@@ -0,0 +1,70 @@
1
+ import {
2
+ contentLength,
3
+ startRequest
4
+ } from "../chunk-PMYUYUJQ.js";
5
+
6
+ // src/services/index.ts
7
+ var SPAN = /* @__PURE__ */ Symbol("den-traces:span");
8
+ function traceNameOf(config) {
9
+ if (typeof config.traceName === "string" && config.traceName !== "") {
10
+ return config.traceName;
11
+ }
12
+ const path = (config.url ?? "").split("?")[0] ?? "";
13
+ return `${(config.method ?? "get").toLowerCase()} ${path}`;
14
+ }
15
+ function traceUrlOf(config) {
16
+ const url = config.url ?? "";
17
+ if (/^https?:\/\//.test(url) || !config.baseURL) {
18
+ return url;
19
+ }
20
+ return `${config.baseURL.replace(/\/$/, "")}/${url.replace(/^\//, "")}`;
21
+ }
22
+ function spanOf(config) {
23
+ const span = config?.[SPAN];
24
+ return typeof span === "object" && span !== null ? span : void 0;
25
+ }
26
+ function lengthOf(headers) {
27
+ const value = headers?.["content-length"];
28
+ return contentLength(typeof value === "string" ? value : typeof value === "number" ? String(value) : null);
29
+ }
30
+ function createTracePlugins() {
31
+ return [
32
+ {
33
+ execute: (config) => {
34
+ config[SPAN] = startRequest(traceNameOf(config), traceUrlOf(config));
35
+ return Promise.resolve(config);
36
+ },
37
+ isApplicable: () => true,
38
+ pluginName: "den-traces:request",
39
+ pluginType: "request"
40
+ },
41
+ {
42
+ execute: (response) => {
43
+ spanOf(response.config)?.finish(response.status, lengthOf(response.headers));
44
+ return Promise.resolve(response);
45
+ },
46
+ isApplicable: () => true,
47
+ pluginName: "den-traces:response",
48
+ pluginType: "responseSuccess"
49
+ },
50
+ {
51
+ execute: (error) => {
52
+ const span = spanOf(error.config);
53
+ if (error.response) {
54
+ span?.finish(error.response.status, lengthOf(error.response.headers));
55
+ } else {
56
+ span?.fail(error);
57
+ }
58
+ return Promise.resolve(error);
59
+ },
60
+ isApplicable: () => true,
61
+ pluginName: "den-traces:fail",
62
+ pluginType: "responseFail"
63
+ }
64
+ ];
65
+ }
66
+ export {
67
+ createTracePlugins,
68
+ traceNameOf,
69
+ traceUrlOf
70
+ };
package/timings/index.cjs CHANGED
@@ -4,11 +4,13 @@
4
4
 
5
5
 
6
6
 
7
- var _chunkF4ITEBDQcjs = require('../chunk-F4ITEBDQ.cjs');
8
7
 
8
+ var _chunkDAPDESIHcjs = require('../chunk-DAPDESIH.cjs');
9
9
 
10
10
 
11
11
 
12
12
 
13
13
 
14
- exports.measure = _chunkF4ITEBDQcjs.measure; exports.setRequestSink = _chunkF4ITEBDQcjs.setRequestSink; exports.setTimingSink = _chunkF4ITEBDQcjs.setTimingSink; exports.setUpstreamErrorSink = _chunkF4ITEBDQcjs.setUpstreamErrorSink; exports.trackFetch = _chunkF4ITEBDQcjs.trackFetch;
14
+
15
+
16
+ exports.contentLength = _chunkDAPDESIHcjs.contentLength; exports.measure = _chunkDAPDESIHcjs.measure; exports.setRequestSink = _chunkDAPDESIHcjs.setRequestSink; exports.setTimingSink = _chunkDAPDESIHcjs.setTimingSink; exports.setUpstreamErrorSink = _chunkDAPDESIHcjs.setUpstreamErrorSink; exports.startRequest = _chunkDAPDESIHcjs.startRequest;
@@ -42,7 +42,26 @@ declare function setRequestSink(next: RequestSink | null): void;
42
42
  declare function setUpstreamErrorSink(next: UpstreamErrorSink | null): void;
43
43
  /** Измеряет работу и отдаёт её длительность приёмнику. */
44
44
  declare function measure<T>(name: string, work: () => Promise<T>): Promise<T>;
45
- /** Ходит в ручку и рассказывает о походе: длительность — меткой, адрес и исход — приёмнику. */
46
- declare function trackFetch(name: string, url: string, init?: RequestInit): Promise<Response>;
45
+ /**
46
+ * Начатый поход наружу: чем он кончился, сообщает тот, кто его вёл.
47
+ *
48
+ * Так сбор отделён от транспорта: `fetch`, axios из SDK или что угодно ещё сообщают о походе
49
+ * одной формой, а приёмники (`Server-Timing`, дев-трасса, Sentry) не знают, кто ходил.
50
+ */
51
+ interface RequestSpan {
52
+ /** Ответ получен; не-OK статус уходит и приёмнику upstream-ошибок. */
53
+ finish(status: number, bytes?: number | null): void;
54
+ /** До ответа не дошли: сеть, таймаут, отмена. */
55
+ fail(error: unknown): void;
56
+ }
57
+ /**
58
+ * Начинает поход наружу: метка времени и запись о походе появятся, когда он кончится.
59
+ *
60
+ * @param name - Имя куска работы — то же, что у метки времени
61
+ * @param url - Адрес, куда ходили
62
+ */
63
+ declare function startRequest(name: string, url: string): RequestSpan;
64
+ /** Размер ответа по `content-length`; `null` — бэкенд его не прислал. */
65
+ declare function contentLength(value: string | null | undefined): number | null;
47
66
 
48
- export { type RequestSink, type TimingSink, type TrackedRequest, type UpstreamErrorContext, type UpstreamErrorSink, measure, setRequestSink, setTimingSink, setUpstreamErrorSink, trackFetch };
67
+ export { type RequestSink, type RequestSpan, type TimingSink, type TrackedRequest, type UpstreamErrorContext, type UpstreamErrorSink, contentLength, measure, setRequestSink, setTimingSink, setUpstreamErrorSink, startRequest };
@@ -42,7 +42,26 @@ declare function setRequestSink(next: RequestSink | null): void;
42
42
  declare function setUpstreamErrorSink(next: UpstreamErrorSink | null): void;
43
43
  /** Измеряет работу и отдаёт её длительность приёмнику. */
44
44
  declare function measure<T>(name: string, work: () => Promise<T>): Promise<T>;
45
- /** Ходит в ручку и рассказывает о походе: длительность — меткой, адрес и исход — приёмнику. */
46
- declare function trackFetch(name: string, url: string, init?: RequestInit): Promise<Response>;
45
+ /**
46
+ * Начатый поход наружу: чем он кончился, сообщает тот, кто его вёл.
47
+ *
48
+ * Так сбор отделён от транспорта: `fetch`, axios из SDK или что угодно ещё сообщают о походе
49
+ * одной формой, а приёмники (`Server-Timing`, дев-трасса, Sentry) не знают, кто ходил.
50
+ */
51
+ interface RequestSpan {
52
+ /** Ответ получен; не-OK статус уходит и приёмнику upstream-ошибок. */
53
+ finish(status: number, bytes?: number | null): void;
54
+ /** До ответа не дошли: сеть, таймаут, отмена. */
55
+ fail(error: unknown): void;
56
+ }
57
+ /**
58
+ * Начинает поход наружу: метка времени и запись о походе появятся, когда он кончится.
59
+ *
60
+ * @param name - Имя куска работы — то же, что у метки времени
61
+ * @param url - Адрес, куда ходили
62
+ */
63
+ declare function startRequest(name: string, url: string): RequestSpan;
64
+ /** Размер ответа по `content-length`; `null` — бэкенд его не прислал. */
65
+ declare function contentLength(value: string | null | undefined): number | null;
47
66
 
48
- export { type RequestSink, type TimingSink, type TrackedRequest, type UpstreamErrorContext, type UpstreamErrorSink, measure, setRequestSink, setTimingSink, setUpstreamErrorSink, trackFetch };
67
+ export { type RequestSink, type RequestSpan, type TimingSink, type TrackedRequest, type UpstreamErrorContext, type UpstreamErrorSink, contentLength, measure, setRequestSink, setTimingSink, setUpstreamErrorSink, startRequest };
package/timings/index.js CHANGED
@@ -1,14 +1,16 @@
1
1
  import {
2
+ contentLength,
2
3
  measure,
3
4
  setRequestSink,
4
5
  setTimingSink,
5
6
  setUpstreamErrorSink,
6
- trackFetch
7
- } from "../chunk-HN4BBSRG.js";
7
+ startRequest
8
+ } from "../chunk-PMYUYUJQ.js";
8
9
  export {
10
+ contentLength,
9
11
  measure,
10
12
  setRequestSink,
11
13
  setTimingSink,
12
14
  setUpstreamErrorSink,
13
- trackFetch
15
+ startRequest
14
16
  };
@@ -1,73 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/timings/index.ts
2
- var sink = null;
3
- var requestSink = null;
4
- var upstreamErrorSink = null;
5
- function setTimingSink(next) {
6
- sink = next;
7
- }
8
- function setRequestSink(next) {
9
- requestSink = next;
10
- }
11
- function setUpstreamErrorSink(next) {
12
- upstreamErrorSink = next;
13
- }
14
- async function measure(name, work) {
15
- if (sink === null) {
16
- return work();
17
- }
18
- const startedAt = performance.now();
19
- try {
20
- return await work();
21
- } finally {
22
- sink(name, performance.now() - startedAt, startedAt);
23
- }
24
- }
25
- async function trackFetch(name, url, init) {
26
- const startedAt = performance.now();
27
- try {
28
- const response = await measure(name, () => fetch(url, init));
29
- const length = response.headers.get("content-length");
30
- _optionalChain([requestSink, 'optionalCall', _ => _({
31
- bytes: length === null ? null : Number(length),
32
- ms: performance.now() - startedAt,
33
- name,
34
- startedAt,
35
- status: response.status,
36
- url
37
- })]);
38
- if (!response.ok) {
39
- _optionalChain([upstreamErrorSink, 'optionalCall', _2 => _2({
40
- name,
41
- reason: `http_${String(response.status)}`,
42
- status: response.status,
43
- url
44
- })]);
45
- }
46
- return response;
47
- } catch (error) {
48
- _optionalChain([requestSink, 'optionalCall', _3 => _3({
49
- bytes: null,
50
- error: String(error),
51
- ms: performance.now() - startedAt,
52
- name,
53
- startedAt,
54
- status: null,
55
- url
56
- })]);
57
- _optionalChain([upstreamErrorSink, 'optionalCall', _4 => _4({
58
- name,
59
- reason: "network",
60
- status: null,
61
- url
62
- })]);
63
- throw error;
64
- }
65
- }
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
- exports.setTimingSink = setTimingSink; exports.setRequestSink = setRequestSink; exports.setUpstreamErrorSink = setUpstreamErrorSink; exports.measure = measure; exports.trackFetch = trackFetch;
package/chunk-HN4BBSRG.js DELETED
@@ -1,73 +0,0 @@
1
- // src/timings/index.ts
2
- var sink = null;
3
- var requestSink = null;
4
- var upstreamErrorSink = null;
5
- function setTimingSink(next) {
6
- sink = next;
7
- }
8
- function setRequestSink(next) {
9
- requestSink = next;
10
- }
11
- function setUpstreamErrorSink(next) {
12
- upstreamErrorSink = next;
13
- }
14
- async function measure(name, work) {
15
- if (sink === null) {
16
- return work();
17
- }
18
- const startedAt = performance.now();
19
- try {
20
- return await work();
21
- } finally {
22
- sink(name, performance.now() - startedAt, startedAt);
23
- }
24
- }
25
- async function trackFetch(name, url, init) {
26
- const startedAt = performance.now();
27
- try {
28
- const response = await measure(name, () => fetch(url, init));
29
- const length = response.headers.get("content-length");
30
- requestSink?.({
31
- bytes: length === null ? null : Number(length),
32
- ms: performance.now() - startedAt,
33
- name,
34
- startedAt,
35
- status: response.status,
36
- url
37
- });
38
- if (!response.ok) {
39
- upstreamErrorSink?.({
40
- name,
41
- reason: `http_${String(response.status)}`,
42
- status: response.status,
43
- url
44
- });
45
- }
46
- return response;
47
- } catch (error) {
48
- requestSink?.({
49
- bytes: null,
50
- error: String(error),
51
- ms: performance.now() - startedAt,
52
- name,
53
- startedAt,
54
- status: null,
55
- url
56
- });
57
- upstreamErrorSink?.({
58
- name,
59
- reason: "network",
60
- status: null,
61
- url
62
- });
63
- throw error;
64
- }
65
- }
66
-
67
- export {
68
- setTimingSink,
69
- setRequestSink,
70
- setUpstreamErrorSink,
71
- measure,
72
- trackFetch
73
- };