@lwmacct/260529-promclient 0.13.260628 → 0.15.260628

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 ADDED
@@ -0,0 +1,435 @@
1
+ # @lwmacct/260529-promclient
2
+
3
+ Small Prometheus-compatible HTTP API client with PromQL helpers and response transforms.
4
+
5
+ 这是一个轻量 TypeScript 共享库,用于在业务项目中访问 Prometheus 兼容 HTTP API。它封装了 instant query、range query、批量查询、PromQL label selector 构造、时间步长计算,以及常见响应数据转换。
6
+
7
+ ## 特性
8
+
9
+ - 调用 `/api/v1/query` 和 `/api/v1/query_range`
10
+ - 支持批量查询,可并行或串行执行
11
+ - URL 较短时使用 `GET`,超过阈值后自动切换为 `POST`
12
+ - 支持全局 headers、单次请求 headers、动态 headers 和 `AbortSignal`
13
+ - 提供 Prometheus API 响应的 TypeScript 类型
14
+ - 提供 PromQL selector / label matcher 转义工具
15
+ - 提供 vector、matrix、scalar、string 响应转换工具
16
+ - 无运行时依赖
17
+
18
+ ## 安装
19
+
20
+ ```bash
21
+ npm install @lwmacct/260529-promclient
22
+ ```
23
+
24
+ 运行环境需要提供 `fetch`。如果当前环境没有全局 `fetch`,可以通过 `fetcher` 传入兼容实现。
25
+
26
+ ## 快速开始
27
+
28
+ ```ts
29
+ import { PromClient, getVectorItems, selector } from "@lwmacct/260529-promclient";
30
+
31
+ const client = new PromClient({
32
+ baseUrl: "https://prometheus.example.com",
33
+ headers: {
34
+ Authorization: `Bearer ${process.env.PROM_TOKEN}`,
35
+ },
36
+ });
37
+
38
+ const query = selector("up", [
39
+ { name: "job", operator: "=", value: "api" },
40
+ ]);
41
+
42
+ const response = await client.query(query);
43
+ const items = getVectorItems(response);
44
+
45
+ for (const item of items) {
46
+ console.log(item.metric.instance, Number(item.value[1]));
47
+ }
48
+ ```
49
+
50
+ ## Client
51
+
52
+ ### 创建客户端
53
+
54
+ ```ts
55
+ import { PromClient } from "@lwmacct/260529-promclient";
56
+
57
+ const client = new PromClient({
58
+ baseUrl: "http://localhost:9090",
59
+ });
60
+ ```
61
+
62
+ 可用选项:
63
+
64
+ | 选项 | 类型 | 说明 |
65
+ | --- | --- | --- |
66
+ | `baseUrl` | `string` | Prometheus 服务地址,必填 |
67
+ | `fetcher` | `typeof fetch` | 自定义 fetch 实现 |
68
+ | `headers` | `HeadersInit \| (() => HeadersInit \| Promise<HeadersInit>)` | 全局请求头,支持动态返回 |
69
+ | `maxGetUrlLength` | `number` | GET URL 最大长度,默认 `2000` |
70
+
71
+ ### Instant Query
72
+
73
+ ```ts
74
+ const response = await client.query("up", {
75
+ time: new Date(),
76
+ timeout: "10s",
77
+ limit: 100,
78
+ });
79
+ ```
80
+
81
+ `query` 返回 `PromSuccessResponse<PromInstantData>`。`PromInstantData` 可能是 `vector`、`scalar` 或 `string`。
82
+
83
+ ### Range Query
84
+
85
+ ```ts
86
+ const response = await client.queryRange("rate(http_requests_total[5m])", {
87
+ start: Date.now() / 1000 - 3600,
88
+ end: Date.now() / 1000,
89
+ step: "1m",
90
+ timeout: "10s",
91
+ });
92
+ ```
93
+
94
+ `queryRange` 返回 `PromSuccessResponse<PromMatrixData>`。
95
+
96
+ 时间参数支持:
97
+
98
+ - `Date`
99
+ - Unix 秒时间戳
100
+ - Prometheus 可接受的字符串时间
101
+
102
+ ### Batch Query
103
+
104
+ ```ts
105
+ const responses = await client.batch([
106
+ { query: "up" },
107
+ {
108
+ type: "queryRange",
109
+ query: "rate(http_requests_total[5m])",
110
+ options: {
111
+ start: Date.now() / 1000 - 3600,
112
+ end: Date.now() / 1000,
113
+ step: "1m",
114
+ },
115
+ },
116
+ ]);
117
+ ```
118
+
119
+ 默认并行执行。需要串行执行时:
120
+
121
+ ```ts
122
+ const responses = await client.batch(requests, { parallel: false });
123
+ ```
124
+
125
+ ## PromQL 工具
126
+
127
+ ### Label Matcher
128
+
129
+ ```ts
130
+ import { labelMatcher, regexLabelMatcher, selector } from "@lwmacct/260529-promclient";
131
+
132
+ labelMatcher("job", "=", "api");
133
+ // job="api"
134
+
135
+ regexLabelMatcher("instance", ["10.0.0.1:9100", "10.0.0.2:9100"]);
136
+ // instance=~"10\\.0\\.0\\.1:9100|10\\.0\\.0\\.2:9100"
137
+
138
+ selector("node_cpu_seconds_total", [
139
+ { name: "mode", operator: "!=", value: "idle" },
140
+ { name: "job", operator: "=", value: "node" },
141
+ ]);
142
+ // node_cpu_seconds_total{mode!="idle",job="node"}
143
+ ```
144
+
145
+ ### 转义函数
146
+
147
+ ```ts
148
+ import { escapeLabelValue, escapeRegex, regexList } from "@lwmacct/260529-promclient";
149
+ ```
150
+
151
+ - `escapeLabelValue`:转义 PromQL label value 中的反斜线、双引号和换行
152
+ - `escapeRegex`:转义正则特殊字符
153
+ - `regexList`:把字符串数组转换成安全的正则 alternation
154
+
155
+ ## 时间工具
156
+
157
+ ```ts
158
+ import { getAdaptiveStep, serializeTime } from "@lwmacct/260529-promclient";
159
+
160
+ const step = getAdaptiveStep(24 * 60 * 60, 600);
161
+ // "5m"
162
+
163
+ const time = serializeTime(new Date());
164
+ // Unix 秒字符串
165
+ ```
166
+
167
+ `getAdaptiveStep(seconds, maxPoints)` 会根据查询时间范围和最大点数,从内置 step 列表中选择合适的 Prometheus step。
168
+
169
+ 内置 step 包括:
170
+
171
+ ```ts
172
+ ["1m", "5m", "10m", "15m", "30m", "1h", "2h", "3h", "4h", "6h", "12h", "1d"]
173
+ ```
174
+
175
+ ## 响应转换
176
+
177
+ Prometheus 的 sample value 是字符串。转换工具会在需要时解析为 number,并过滤 `NaN`。
178
+
179
+ ### Label Record Transform
180
+
181
+ 有些指标会把表格结构编码到 labels 中,例如用某些 labels 表示 `index`、`key`、`field`,再用 sample value 或 `value` label 表示字段值。可以用 `mapVectorToFieldRows` 先转成标准行,再按 key 或 index/key pivot 成对象表。
182
+
183
+ 示例一:sample value 作为字段值。
184
+
185
+ 假设查询返回这些 vector samples:
186
+
187
+ ```promql
188
+ demo_service_quota{tenant="acme", service="api", resource="cpu"} 4
189
+ demo_service_quota{tenant="acme", service="api", resource="memory_gb"} 16
190
+ demo_service_quota{tenant="acme", service="worker", resource="cpu"} 2
191
+ ```
192
+
193
+ 可以把它理解为:
194
+
195
+ | index | key | field | value |
196
+ | --- | --- | --- | --- |
197
+ | `acme` | `api` | `cpu` | `4` |
198
+ | `acme` | `api` | `memory_gb` | `16` |
199
+ | `acme` | `worker` | `cpu` | `2` |
200
+
201
+ ```ts
202
+ import {
203
+ mapFieldRowsByIndexKey,
204
+ mapVectorToFieldRows,
205
+ } from "@lwmacct/260529-promclient";
206
+
207
+ const response = await client.query("demo_service_quota");
208
+
209
+ const rows = mapVectorToFieldRows(response, {
210
+ indexLabels: ["tenant"],
211
+ keyLabels: ["service"],
212
+ fieldLabels: ["resource"],
213
+ valueSource: "sample",
214
+ });
215
+
216
+ const table = mapFieldRowsByIndexKey(rows);
217
+ // {
218
+ // acme: {
219
+ // api: {
220
+ // cpu: 4,
221
+ // memory_gb: 16
222
+ // },
223
+ // worker: {
224
+ // cpu: 2
225
+ // }
226
+ // }
227
+ // }
228
+ ```
229
+
230
+ 如果字段由多个 label 共同决定,可以把多个 label 组合成字段名:
231
+
232
+ ```ts
233
+ const rows = mapVectorToFieldRows(response, {
234
+ indexLabels: ["tenant"],
235
+ keyLabels: ["service"],
236
+ fieldLabels: ["method", "status"],
237
+ });
238
+ // method="GET", status="200" -> field: "GET.200"
239
+ ```
240
+
241
+ 示例二:`value` label 作为字段值。
242
+
243
+ 这类指标通常用 sample value `1` 表示“这条信息存在”,真正的字段值在 label 中:
244
+
245
+ ```promql
246
+ demo_asset_info{asset="srv-01", field="region", value="us-east"} 1
247
+ demo_asset_info{asset="srv-01", field="owner", value="platform"} 1
248
+ demo_asset_info{asset="srv-02", field="region", value="eu-west"} 1
249
+ ```
250
+
251
+ 可以把它理解为:
252
+
253
+ | key | field | value |
254
+ | --- | --- | --- |
255
+ | `srv-01` | `region` | `us-east` |
256
+ | `srv-01` | `owner` | `platform` |
257
+ | `srv-02` | `region` | `eu-west` |
258
+
259
+ ```ts
260
+ import {
261
+ mapFieldRowsByKey,
262
+ mapVectorToFieldRows,
263
+ } from "@lwmacct/260529-promclient";
264
+
265
+ const response = await client.query("demo_asset_info");
266
+
267
+ const rows = mapVectorToFieldRows(response, {
268
+ keyLabels: ["asset"],
269
+ fieldLabels: ["field"],
270
+ valueLabel: "value",
271
+ valueSource: "auto",
272
+ });
273
+
274
+ const table = mapFieldRowsByKey(rows);
275
+ // {
276
+ // "srv-01": {
277
+ // region: "us-east",
278
+ // owner: "platform"
279
+ // },
280
+ // "srv-02": {
281
+ // region: "eu-west"
282
+ // }
283
+ // }
284
+ ```
285
+
286
+ `valueSource` 支持:
287
+
288
+ | 值 | 说明 |
289
+ | --- | --- |
290
+ | `"auto"` | 优先读取 `valueLabel` 指定的 label;不存在时读取 sample value,默认值 |
291
+ | `"label"` | 只读取 `valueLabel` 指定的 label |
292
+ | `"sample"` | 只读取 Prometheus sample value |
293
+
294
+ 重复字段默认采用后出现的值。需要保留第一条、保留数组或发现重复时报错时,可以配置 pivot 函数:
295
+
296
+ ```ts
297
+ mapFieldRowsByKey(rows, { duplicate: "first" });
298
+ mapFieldRowsByKey(rows, { duplicate: "array" });
299
+ mapFieldRowsByKey(rows, { duplicate: "error" });
300
+ ```
301
+
302
+ ### Instant Response
303
+
304
+ ```ts
305
+ import {
306
+ getVectorItems,
307
+ getScalarValue,
308
+ getScalarNumber,
309
+ mapVector,
310
+ mapVectorByLabel,
311
+ } from "@lwmacct/260529-promclient";
312
+ ```
313
+
314
+ 常用函数:
315
+
316
+ | 函数 | 说明 |
317
+ | --- | --- |
318
+ | `getVectorItems(response)` | 从 instant response 中取出 vector items,非 vector 时返回空数组 |
319
+ | `getScalarValue(response)` | 从 scalar/string response 中取出原始字符串值 |
320
+ | `getScalarNumber(response, defaultValue?)` | 从 scalar response 中解析数字 |
321
+ | `mapVectorByLabel(response, labelName, parser?)` | 按指定 label 聚合 vector 数值 |
322
+ | `mapVector(response, mapper)` | 自定义映射 vector items |
323
+
324
+ ### Range Response
325
+
326
+ ```ts
327
+ import {
328
+ mapMatrixItemToSeries,
329
+ mapMatrixToSeries,
330
+ mapMatrixByLabel,
331
+ } from "@lwmacct/260529-promclient";
332
+ ```
333
+
334
+ 常用函数:
335
+
336
+ | 函数 | 说明 |
337
+ | --- | --- |
338
+ | `mapMatrixItemToSeries(item, parser?)` | 把单条 matrix series 转为 `[timestampMs, value]` 数组 |
339
+ | `mapMatrixToSeries(response, parser?, filter?)` | 把 matrix response 展平为按时间升序排列的点数组 |
340
+ | `mapMatrixByLabel(response, labelName, parser?)` | 按指定 label 输出多条时间序列 |
341
+
342
+ 示例:
343
+
344
+ ```ts
345
+ const response = await client.queryRange("rate(http_requests_total[5m])", {
346
+ start: Date.now() / 1000 - 3600,
347
+ end: Date.now() / 1000,
348
+ step: "1m",
349
+ });
350
+
351
+ const seriesByInstance = mapMatrixByLabel(response, "instance");
352
+ ```
353
+
354
+ ## 类型守卫和基础工具
355
+
356
+ ```ts
357
+ import {
358
+ hasResults,
359
+ isMatrixData,
360
+ isScalarData,
361
+ isStringData,
362
+ isSuccessResponse,
363
+ isVectorData,
364
+ safeParseFloat,
365
+ toMilliseconds,
366
+ } from "@lwmacct/260529-promclient";
367
+ ```
368
+
369
+ 这些工具适合在业务侧处理 Prometheus 原始响应时做类型缩窄和基础转换。
370
+
371
+ ## 错误处理
372
+
373
+ 客户端会区分三类错误:
374
+
375
+ | 错误 | 触发条件 |
376
+ | --- | --- |
377
+ | `PromHttpError` | HTTP 状态码非 2xx |
378
+ | `PromApiError` | Prometheus API 返回 `status: "error"` |
379
+ | `PromParseError` | 响应 JSON 解析失败,或响应状态不符合预期 |
380
+
381
+ ```ts
382
+ import {
383
+ PromApiError,
384
+ PromHttpError,
385
+ PromParseError,
386
+ } from "@lwmacct/260529-promclient";
387
+
388
+ try {
389
+ await client.query("up");
390
+ } catch (error) {
391
+ if (error instanceof PromHttpError) {
392
+ console.error(error.status, error.statusText, error.url);
393
+ } else if (error instanceof PromApiError) {
394
+ console.error(error.errorType, error.response.error);
395
+ } else if (error instanceof PromParseError) {
396
+ console.error(error.message);
397
+ }
398
+ }
399
+ ```
400
+
401
+ ## 子路径导入
402
+
403
+ 包提供以下导出入口:
404
+
405
+ ```ts
406
+ import { PromClient } from "@lwmacct/260529-promclient/client";
407
+ import { PromHttpError } from "@lwmacct/260529-promclient/errors";
408
+ import { selector } from "@lwmacct/260529-promclient/promql";
409
+ import { getAdaptiveStep } from "@lwmacct/260529-promclient/time";
410
+ import { mapMatrixByLabel } from "@lwmacct/260529-promclient/transform";
411
+ ```
412
+
413
+ 完整入口 `@lwmacct/260529-promclient` 会导出所有公共 API。
414
+
415
+ ## 开发
416
+
417
+ ```bash
418
+ npm install
419
+ npm run typecheck
420
+ npm run build
421
+ ```
422
+
423
+ 当前仓库没有测试脚本,发布前至少需要通过类型检查和构建。
424
+
425
+ ## 发布
426
+
427
+ 仓库在推送 `v*` tag 时通过 GitHub Actions 发布 npm 包和 GitHub Release asset。
428
+
429
+ ```bash
430
+ npm run typecheck
431
+ npm run build
432
+ task git:tag:next
433
+ ```
434
+
435
+ `task git:tag:next` 来自远程 Taskfile,会创建并推送下一个版本标签。
@@ -1,4 +1,5 @@
1
1
  export * from "./core";
2
2
  export * from "./instant";
3
3
  export * from "./range";
4
+ export * from "./records";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transform/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transform/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export * from "./core";
2
2
  export * from "./instant";
3
3
  export * from "./range";
4
+ export * from "./records";
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transform/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transform/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { PromInstantData, PromLabelSet, PromVectorItem, PromSuccessResponse } from "../types";
2
+ export type PromFieldValue = string | number;
3
+ export type PromFieldRow = {
4
+ index?: string;
5
+ key: string;
6
+ field: string;
7
+ value: PromFieldValue;
8
+ labels: PromLabelSet;
9
+ sampleValue: string;
10
+ timestampMs: number;
11
+ };
12
+ export type PromFieldValueSource = "auto" | "label" | "sample";
13
+ export type MapVectorToFieldRowsOptions = {
14
+ indexLabels?: readonly string[];
15
+ keyLabels: readonly string[];
16
+ fieldLabels: readonly string[];
17
+ valueLabel?: string;
18
+ valueSource?: PromFieldValueSource;
19
+ labelSeparator?: string;
20
+ sampleParser?: (value: string, item: PromVectorItem) => PromFieldValue;
21
+ };
22
+ export type DuplicateFieldRowStrategy = "last" | "first" | "array" | "error";
23
+ export type MapFieldRowsOptions = {
24
+ duplicate?: DuplicateFieldRowStrategy;
25
+ };
26
+ export type PromFieldRecord = Record<string, PromFieldValue | PromFieldValue[]>;
27
+ export type PromFieldTable = Record<string, PromFieldRecord>;
28
+ export type PromIndexedFieldTable = Record<string, PromFieldTable>;
29
+ export declare const mapVectorItemToFieldRow: (item: PromVectorItem, options: MapVectorToFieldRowsOptions) => PromFieldRow | undefined;
30
+ export declare const mapVectorToFieldRows: (response: PromSuccessResponse<PromInstantData>, options: MapVectorToFieldRowsOptions) => PromFieldRow[];
31
+ export declare const mapFieldRowsByKey: (rows: readonly PromFieldRow[], options?: MapFieldRowsOptions) => PromFieldTable;
32
+ export declare const mapFieldRowsByIndexKey: (rows: readonly PromFieldRow[], options?: MapFieldRowsOptions) => PromIndexedFieldTable;
33
+ //# sourceMappingURL=records.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../src/transform/records.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,cAAc,EACd,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAGlB,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,CAAC;AAE7C,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,YAAY,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE/D,MAAM,MAAM,2BAA2B,GAAG;IACxC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,oBAAoB,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,KAAK,cAAc,CAAC;CACxE,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAE7E,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,CAAC,EAAE,yBAAyB,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,EAAE,CAAC,CAAC;AAEhF,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAE7D,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AA2CnE,eAAO,MAAM,uBAAuB,GAClC,MAAM,cAAc,EACpB,SAAS,2BAA2B,KACnC,YAAY,GAAG,SAwCjB,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,UAAU,mBAAmB,CAAC,eAAe,CAAC,EAC9C,SAAS,2BAA2B,KACnC,YAAY,EAYd,CAAC;AAiCF,eAAO,MAAM,iBAAiB,GAC5B,MAAM,SAAS,YAAY,EAAE,EAC7B,UAAS,mBAAwB,KAChC,cASF,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,MAAM,SAAS,YAAY,EAAE,EAC7B,UAAS,mBAAwB,KAChC,qBAgBF,CAAC"}
@@ -0,0 +1,113 @@
1
+ import { isVectorData, safeParseFloat, toMilliseconds } from "./core";
2
+ const defaultLabelSeparator = ".";
3
+ const defaultValueLabel = "value";
4
+ const joinLabelValues = (labels, labelNames, separator) => {
5
+ const values = [];
6
+ for (const labelName of labelNames) {
7
+ const value = labels[labelName];
8
+ if (value === undefined) {
9
+ return undefined;
10
+ }
11
+ values.push(value);
12
+ }
13
+ return values.join(separator);
14
+ };
15
+ const resolveFieldValue = (item, options) => {
16
+ const labelValue = item.metric[options.valueLabel];
17
+ if (options.valueSource === "label") {
18
+ return labelValue;
19
+ }
20
+ if (options.valueSource === "auto" && labelValue !== undefined) {
21
+ return labelValue;
22
+ }
23
+ return options.sampleParser(item.value[1], item);
24
+ };
25
+ export const mapVectorItemToFieldRow = (item, options) => {
26
+ const labelSeparator = options.labelSeparator ?? defaultLabelSeparator;
27
+ const indexLabels = options.indexLabels ?? [];
28
+ const valueOptions = {
29
+ sampleParser: options.sampleParser ??
30
+ ((value) => safeParseFloat(value)),
31
+ valueLabel: options.valueLabel ?? defaultValueLabel,
32
+ valueSource: options.valueSource ?? "auto",
33
+ };
34
+ const index = indexLabels.length > 0
35
+ ? joinLabelValues(item.metric, indexLabels, labelSeparator)
36
+ : undefined;
37
+ if (indexLabels.length > 0 && index === undefined) {
38
+ return undefined;
39
+ }
40
+ const key = joinLabelValues(item.metric, options.keyLabels, labelSeparator);
41
+ const field = joinLabelValues(item.metric, options.fieldLabels, labelSeparator);
42
+ const value = resolveFieldValue(item, valueOptions);
43
+ if (key === undefined || field === undefined || value === undefined) {
44
+ return undefined;
45
+ }
46
+ return {
47
+ index,
48
+ key,
49
+ field,
50
+ value,
51
+ labels: { ...item.metric },
52
+ sampleValue: item.value[1],
53
+ timestampMs: toMilliseconds(item.value[0]),
54
+ };
55
+ };
56
+ export const mapVectorToFieldRows = (response, options) => {
57
+ if (!isVectorData(response.data)) {
58
+ return [];
59
+ }
60
+ return response.data.result.reduce((rows, item) => {
61
+ const row = mapVectorItemToFieldRow(item, options);
62
+ if (row !== undefined) {
63
+ rows.push(row);
64
+ }
65
+ return rows;
66
+ }, []);
67
+ };
68
+ const setFieldValue = (record, field, value, duplicate) => {
69
+ const current = record[field];
70
+ if (current === undefined) {
71
+ record[field] = value;
72
+ return;
73
+ }
74
+ if (duplicate === "first") {
75
+ return;
76
+ }
77
+ if (duplicate === "last") {
78
+ record[field] = value;
79
+ return;
80
+ }
81
+ if (duplicate === "array") {
82
+ record[field] = Array.isArray(current)
83
+ ? [...current, value]
84
+ : [current, value];
85
+ return;
86
+ }
87
+ throw new Error(`Duplicate field row for field "${field}".`);
88
+ };
89
+ export const mapFieldRowsByKey = (rows, options = {}) => {
90
+ const duplicate = options.duplicate ?? "last";
91
+ return rows.reduce((table, row) => {
92
+ const record = table[row.key] ?? {};
93
+ setFieldValue(record, row.field, row.value, duplicate);
94
+ table[row.key] = record;
95
+ return table;
96
+ }, {});
97
+ };
98
+ export const mapFieldRowsByIndexKey = (rows, options = {}) => {
99
+ const duplicate = options.duplicate ?? "last";
100
+ return rows.reduce((table, row) => {
101
+ const index = row.index;
102
+ if (index === undefined) {
103
+ return table;
104
+ }
105
+ const records = table[index] ?? {};
106
+ const record = records[row.key] ?? {};
107
+ setFieldValue(record, row.field, row.value, duplicate);
108
+ records[row.key] = record;
109
+ table[index] = records;
110
+ return table;
111
+ }, {});
112
+ };
113
+ //# sourceMappingURL=records.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"records.js","sourceRoot":"","sources":["../../src/transform/records.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lwmacct/260529-promclient",
3
- "version": "0.13.260628",
3
+ "version": "0.15.260628",
4
4
  "private": false,
5
5
  "description": "Small Prometheus-compatible HTTP API client with PromQL helpers and response transforms.",
6
6
  "type": "module",