@kortyx/usage-telemetry 0.3.0 → 0.4.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/CHANGELOG.md +8 -0
- package/README.md +86 -4
- package/dist/browser-node.d.ts +11 -3
- package/dist/browser-node.js +432 -23
- package/dist/browser-node.js.map +1 -1
- package/dist/{client-rVwo4XAO.d.ts → client-BPjAx5oh.d.ts} +8 -5
- package/dist/index.d.ts +12 -3
- package/dist/index.js +426 -23
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +10 -4
- package/dist/node.js +445 -24
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
package/dist/node.js
CHANGED
|
@@ -23,8 +23,10 @@ __export(node_exports, {
|
|
|
23
23
|
configureUsageTelemetry: () => configureUsageTelemetry,
|
|
24
24
|
createNodeUsageTelemetry: () => createNodeUsageTelemetry,
|
|
25
25
|
createUsageTelemetry: () => createUsageTelemetry,
|
|
26
|
+
flushUsageTelemetry: () => flushUsageTelemetry,
|
|
26
27
|
getUsageTelemetry: () => getUsageTelemetry,
|
|
27
28
|
publicUsageConfiguration: () => publicUsageConfiguration,
|
|
29
|
+
runtimeUsageContext: () => runtimeUsageContext,
|
|
28
30
|
scale: () => scale,
|
|
29
31
|
usageDisabled: () => usageDisabled
|
|
30
32
|
});
|
|
@@ -76,6 +78,10 @@ var operations = [
|
|
|
76
78
|
"studio.settings",
|
|
77
79
|
"studio.update",
|
|
78
80
|
"studio.ingest",
|
|
81
|
+
"cli.command",
|
|
82
|
+
"studio.eval.execution",
|
|
83
|
+
"studio.diagnostic",
|
|
84
|
+
"studio.table.layout",
|
|
79
85
|
"cli.topology",
|
|
80
86
|
"cli.eval"
|
|
81
87
|
];
|
|
@@ -90,9 +96,143 @@ var outcomes = [
|
|
|
90
96
|
"ungraded"
|
|
91
97
|
];
|
|
92
98
|
var publicCatalog = {
|
|
93
|
-
products: ["sdk", "oss_studio"],
|
|
99
|
+
products: ["sdk", "oss_studio", "cli"],
|
|
94
100
|
operations,
|
|
95
101
|
properties: {
|
|
102
|
+
measurement_version: ["2"],
|
|
103
|
+
measurement_kind: [
|
|
104
|
+
"invocation",
|
|
105
|
+
"stage",
|
|
106
|
+
"execution",
|
|
107
|
+
"attempt",
|
|
108
|
+
"chunk",
|
|
109
|
+
"output",
|
|
110
|
+
"update",
|
|
111
|
+
"poll",
|
|
112
|
+
"request",
|
|
113
|
+
"call",
|
|
114
|
+
"feature",
|
|
115
|
+
"observation"
|
|
116
|
+
],
|
|
117
|
+
traffic_source: ["internal", "synthetic", "external", "unknown"],
|
|
118
|
+
layout_action: [
|
|
119
|
+
"visibility",
|
|
120
|
+
"show_all",
|
|
121
|
+
"reset",
|
|
122
|
+
"apply",
|
|
123
|
+
"reset_width",
|
|
124
|
+
"pin",
|
|
125
|
+
"unpin",
|
|
126
|
+
"resize",
|
|
127
|
+
"reorder"
|
|
128
|
+
],
|
|
129
|
+
eval_phase: [
|
|
130
|
+
"params",
|
|
131
|
+
"setup",
|
|
132
|
+
"execute",
|
|
133
|
+
"responder",
|
|
134
|
+
"reference",
|
|
135
|
+
"grading",
|
|
136
|
+
"verification",
|
|
137
|
+
"cleanup",
|
|
138
|
+
"reporting"
|
|
139
|
+
],
|
|
140
|
+
cli_resource: ["evals.runs", "evals.suites"],
|
|
141
|
+
retryable: "boolean",
|
|
142
|
+
retried: "boolean",
|
|
143
|
+
recovered: "boolean",
|
|
144
|
+
retry_attempt: ["1", "2-5", "6-20", "21-100", "101+"],
|
|
145
|
+
batch_size: ["1", "2-5", "6-20", "21-100", "101+"],
|
|
146
|
+
http_status_class: ["1xx", "2xx", "3xx", "4xx", "5xx"],
|
|
147
|
+
cause_category: [
|
|
148
|
+
"configuration",
|
|
149
|
+
"provider",
|
|
150
|
+
"validation",
|
|
151
|
+
"domain",
|
|
152
|
+
"workflow",
|
|
153
|
+
"persistence",
|
|
154
|
+
"request",
|
|
155
|
+
"transport",
|
|
156
|
+
"internal",
|
|
157
|
+
"unknown"
|
|
158
|
+
],
|
|
159
|
+
cli_command: [
|
|
160
|
+
"studio.start",
|
|
161
|
+
"studio.stop",
|
|
162
|
+
"studio.restart",
|
|
163
|
+
"studio.status",
|
|
164
|
+
"studio.logs",
|
|
165
|
+
"studio.credentials",
|
|
166
|
+
"studio.reset",
|
|
167
|
+
"studio.prompts",
|
|
168
|
+
"studio.evals",
|
|
169
|
+
"studio.runs",
|
|
170
|
+
"studio.sessions",
|
|
171
|
+
"studio.interrupts",
|
|
172
|
+
"studio.workflows",
|
|
173
|
+
"studio.diagnostics",
|
|
174
|
+
"studio.inspect",
|
|
175
|
+
"studio.doctor",
|
|
176
|
+
"studio.catalogs",
|
|
177
|
+
"connections",
|
|
178
|
+
"evals.run",
|
|
179
|
+
"evals.list",
|
|
180
|
+
"topology.push",
|
|
181
|
+
"help",
|
|
182
|
+
"unknown"
|
|
183
|
+
],
|
|
184
|
+
cli_action: [
|
|
185
|
+
"start",
|
|
186
|
+
"stop",
|
|
187
|
+
"restart",
|
|
188
|
+
"status",
|
|
189
|
+
"logs",
|
|
190
|
+
"credentials",
|
|
191
|
+
"reset",
|
|
192
|
+
"run",
|
|
193
|
+
"list",
|
|
194
|
+
"get",
|
|
195
|
+
"show",
|
|
196
|
+
"inspect",
|
|
197
|
+
"analyze",
|
|
198
|
+
"download",
|
|
199
|
+
"doctor",
|
|
200
|
+
"cancel",
|
|
201
|
+
"compare",
|
|
202
|
+
"create",
|
|
203
|
+
"update",
|
|
204
|
+
"delete",
|
|
205
|
+
"import",
|
|
206
|
+
"export",
|
|
207
|
+
"pull",
|
|
208
|
+
"push",
|
|
209
|
+
"add",
|
|
210
|
+
"remove",
|
|
211
|
+
"use",
|
|
212
|
+
"verify",
|
|
213
|
+
"versions",
|
|
214
|
+
"diff",
|
|
215
|
+
"validate",
|
|
216
|
+
"action",
|
|
217
|
+
"tag",
|
|
218
|
+
"untag",
|
|
219
|
+
"rollback",
|
|
220
|
+
"promote",
|
|
221
|
+
"archive",
|
|
222
|
+
"restore",
|
|
223
|
+
"test",
|
|
224
|
+
"copy",
|
|
225
|
+
"apply",
|
|
226
|
+
"wait",
|
|
227
|
+
"catalogs",
|
|
228
|
+
"help",
|
|
229
|
+
"unknown"
|
|
230
|
+
],
|
|
231
|
+
dry_run: "boolean",
|
|
232
|
+
json_output: "boolean",
|
|
233
|
+
settings_override: "boolean",
|
|
234
|
+
case_timeout_override: "boolean",
|
|
235
|
+
run_timeout_override: "boolean",
|
|
96
236
|
phase: [
|
|
97
237
|
"admission",
|
|
98
238
|
"execution",
|
|
@@ -237,6 +377,7 @@ var publicCatalog = {
|
|
|
237
377
|
"pending",
|
|
238
378
|
"expired"
|
|
239
379
|
],
|
|
380
|
+
match: ["all", "any"],
|
|
240
381
|
filter: [
|
|
241
382
|
"search",
|
|
242
383
|
"status",
|
|
@@ -269,6 +410,7 @@ var publicCatalog = {
|
|
|
269
410
|
"401",
|
|
270
411
|
"403",
|
|
271
412
|
"404",
|
|
413
|
+
"408",
|
|
272
414
|
"409",
|
|
273
415
|
"413",
|
|
274
416
|
"422",
|
|
@@ -292,6 +434,47 @@ var publicCatalog = {
|
|
|
292
434
|
},
|
|
293
435
|
// Built-in contracts only; application codes and provider response text are excluded.
|
|
294
436
|
errorCodes: [
|
|
437
|
+
"UNCLASSIFIED_FAILURE",
|
|
438
|
+
"HTTP_UNAUTHORIZED",
|
|
439
|
+
"HTTP_FORBIDDEN",
|
|
440
|
+
"HTTP_NOT_FOUND",
|
|
441
|
+
"HTTP_TIMEOUT",
|
|
442
|
+
"HTTP_CONFLICT",
|
|
443
|
+
"HTTP_TOO_LARGE",
|
|
444
|
+
"HTTP_RATE_LIMITED",
|
|
445
|
+
"HTTP_SERVER_ERROR",
|
|
446
|
+
"HTTP_INVALID_REQUEST",
|
|
447
|
+
"NETWORK_DNS_FAILED",
|
|
448
|
+
"NETWORK_CONNECTION_REFUSED",
|
|
449
|
+
"NETWORK_CONNECTION_RESET",
|
|
450
|
+
"NETWORK_TIMEOUT",
|
|
451
|
+
"NETWORK_TLS_FAILED",
|
|
452
|
+
"CLI_ARGUMENT_INVALID",
|
|
453
|
+
"CLI_EXIT_FAILED",
|
|
454
|
+
"STUDIO_NOT_CONFIGURED",
|
|
455
|
+
"STUDIO_RESPONSE_INVALID",
|
|
456
|
+
"EVAL_CONCURRENCY_CHANGED",
|
|
457
|
+
"EVAL_CONSUMER_FAILED",
|
|
458
|
+
"EVAL_CONTRACT_CHANGED",
|
|
459
|
+
"EVAL_DISCOVERY_REJECTED",
|
|
460
|
+
"EVAL_EXECUTION_CONFLICT",
|
|
461
|
+
"EVAL_EXECUTION_STORE_CHANGED",
|
|
462
|
+
"EVAL_EXECUTION_UNKNOWN",
|
|
463
|
+
"EVAL_EXECUTOR_BUSY",
|
|
464
|
+
"EVAL_EXECUTOR_FAILED",
|
|
465
|
+
"EVAL_EXECUTOR_REJECTED",
|
|
466
|
+
"EVAL_EXECUTOR_UNAUTHORIZED",
|
|
467
|
+
"EVAL_GRADING_FAILED",
|
|
468
|
+
"EVAL_GRADING_TIMEOUT",
|
|
469
|
+
"EVAL_JUDGE_UNAVAILABLE",
|
|
470
|
+
"EVAL_LEASE_LOST",
|
|
471
|
+
"EVAL_RETRY_DELAY_EXCEEDED",
|
|
472
|
+
"EVAL_SETUP_TIMEOUT",
|
|
473
|
+
"EVAL_TRANSPORT_UNAVAILABLE",
|
|
474
|
+
"EVAL_VERIFICATION_TIMEOUT",
|
|
475
|
+
"EVAL_RESULT_INVALID",
|
|
476
|
+
"EVAL_RUN_TIMEOUT",
|
|
477
|
+
"EVAL_SETTINGS_INVALID",
|
|
295
478
|
"EXECUTION_FAILED",
|
|
296
479
|
"INVALID_REQUEST",
|
|
297
480
|
"INVALID_RESUME",
|
|
@@ -359,8 +542,8 @@ function scale(value) {
|
|
|
359
542
|
return value <= 1 ? "1" : value <= 5 ? "2-5" : value <= 20 ? "6-20" : value <= 100 ? "21-100" : "101+";
|
|
360
543
|
}
|
|
361
544
|
|
|
362
|
-
// src/
|
|
363
|
-
function
|
|
545
|
+
// src/failure.ts
|
|
546
|
+
function usageErrorProperty(value, key) {
|
|
364
547
|
try {
|
|
365
548
|
if (!value || typeof value !== "object") return void 0;
|
|
366
549
|
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
@@ -369,40 +552,201 @@ function property(value, key) {
|
|
|
369
552
|
return void 0;
|
|
370
553
|
}
|
|
371
554
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
555
|
+
var networkCodes = {
|
|
556
|
+
ENOTFOUND: "NETWORK_DNS_FAILED",
|
|
557
|
+
EAI_AGAIN: "NETWORK_DNS_FAILED",
|
|
558
|
+
ECONNREFUSED: "NETWORK_CONNECTION_REFUSED",
|
|
559
|
+
ECONNRESET: "NETWORK_CONNECTION_RESET",
|
|
560
|
+
EPIPE: "NETWORK_CONNECTION_RESET",
|
|
561
|
+
ETIMEDOUT: "NETWORK_TIMEOUT",
|
|
562
|
+
UND_ERR_CONNECT_TIMEOUT: "NETWORK_TIMEOUT",
|
|
563
|
+
UND_ERR_HEADERS_TIMEOUT: "NETWORK_TIMEOUT",
|
|
564
|
+
UND_ERR_BODY_TIMEOUT: "NETWORK_TIMEOUT",
|
|
565
|
+
CERT_HAS_EXPIRED: "NETWORK_TLS_FAILED",
|
|
566
|
+
DEPTH_ZERO_SELF_SIGNED_CERT: "NETWORK_TLS_FAILED",
|
|
567
|
+
UNABLE_TO_VERIFY_LEAF_SIGNATURE: "NETWORK_TLS_FAILED"
|
|
568
|
+
};
|
|
569
|
+
function classifyUsageHttpStatus(status) {
|
|
570
|
+
if (!Number.isInteger(status) || status < 100 || status > 599)
|
|
571
|
+
return {
|
|
572
|
+
outcome: "failed",
|
|
573
|
+
properties: {
|
|
574
|
+
error_code: "UNCLASSIFIED_FAILURE",
|
|
575
|
+
error_category: "unknown"
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
if (status < 400)
|
|
579
|
+
return {
|
|
580
|
+
outcome: "success",
|
|
581
|
+
properties: { http_status_class: `${Math.floor(status / 100)}xx` }
|
|
582
|
+
};
|
|
583
|
+
const code = status === 401 ? "HTTP_UNAUTHORIZED" : status === 403 ? "HTTP_FORBIDDEN" : status === 404 ? "HTTP_NOT_FOUND" : status === 408 || status === 504 ? "HTTP_TIMEOUT" : status === 409 ? "HTTP_CONFLICT" : status === 413 ? "HTTP_TOO_LARGE" : status === 429 ? "HTTP_RATE_LIMITED" : status >= 500 ? "HTTP_SERVER_ERROR" : "HTTP_INVALID_REQUEST";
|
|
584
|
+
return {
|
|
585
|
+
outcome: "failed",
|
|
586
|
+
properties: {
|
|
587
|
+
error_code: code,
|
|
588
|
+
error_category: status >= 500 || status === 408 ? "transport" : "request",
|
|
589
|
+
http_status: String(status),
|
|
590
|
+
http_status_class: `${Math.floor(status / 100)}xx`,
|
|
591
|
+
retryable: status === 408 || status === 429 || status >= 500
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
function nativeExceptionName(value) {
|
|
596
|
+
try {
|
|
597
|
+
if (typeof DOMException !== "undefined")
|
|
598
|
+
return Object.getOwnPropertyDescriptor(
|
|
599
|
+
DOMException.prototype,
|
|
600
|
+
"name"
|
|
601
|
+
)?.get?.call(value);
|
|
602
|
+
} catch {
|
|
603
|
+
}
|
|
604
|
+
return void 0;
|
|
605
|
+
}
|
|
606
|
+
function classifyOne(error, catalog) {
|
|
607
|
+
const read = (key) => usageErrorProperty(error, key);
|
|
608
|
+
let name = read("name") ?? nativeExceptionName(error);
|
|
609
|
+
try {
|
|
610
|
+
if (name === void 0 && error instanceof SyntaxError)
|
|
611
|
+
name = "SyntaxError";
|
|
612
|
+
} catch {
|
|
613
|
+
}
|
|
614
|
+
const code = read("code");
|
|
615
|
+
if (name === "AbortError" || code === "EXECUTION_CANCELLED" || code === "ABORT_ERR")
|
|
376
616
|
return { outcome: "cancelled", properties: {} };
|
|
377
617
|
if (code === "EXECUTION_LIMIT_REACHED" || name === "ExecutionLimitReached")
|
|
378
618
|
return { outcome: "limit_reached", properties: {} };
|
|
379
|
-
if (
|
|
619
|
+
if (typeof name === "string" && [
|
|
620
|
+
"GraphInterrupt",
|
|
621
|
+
"NodeInterrupt",
|
|
622
|
+
"Interrupt",
|
|
623
|
+
"ParallelChildWaiting"
|
|
624
|
+
].includes(name))
|
|
380
625
|
return { outcome: "suspended", properties: {} };
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
626
|
+
if (name === "EvalConfigurationError")
|
|
627
|
+
return {
|
|
628
|
+
outcome: "failed",
|
|
629
|
+
properties: {
|
|
630
|
+
error_code: "EVAL_SETTINGS_INVALID",
|
|
631
|
+
error_category: "configuration",
|
|
632
|
+
retryable: false
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
if (name === "TimeoutError")
|
|
636
|
+
return {
|
|
637
|
+
outcome: "failed",
|
|
638
|
+
properties: {
|
|
639
|
+
error_code: "NETWORK_TIMEOUT",
|
|
640
|
+
error_category: "transport",
|
|
641
|
+
retryable: true
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
if (typeof code === "string" && Object.hasOwn(networkCodes, code))
|
|
645
|
+
return {
|
|
646
|
+
outcome: "failed",
|
|
647
|
+
properties: {
|
|
648
|
+
error_code: networkCodes[code] ?? "UNCLASSIFIED_FAILURE",
|
|
649
|
+
error_category: "transport",
|
|
650
|
+
retryable: true
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
if (typeof code === "string" && catalog.errorCodes.includes(code)) {
|
|
654
|
+
const failure = usageErrorProperty(error, "failure");
|
|
655
|
+
const category = read("category") ?? usageErrorProperty(failure, "category");
|
|
656
|
+
const retryable = read("retryable") ?? usageErrorProperty(failure, "retryable");
|
|
657
|
+
const status2 = read("status") ?? usageErrorProperty(failure, "status");
|
|
658
|
+
return {
|
|
659
|
+
outcome: "failed",
|
|
660
|
+
properties: {
|
|
661
|
+
...typeof status2 === "number" ? classifyUsageHttpStatus(status2).properties : {},
|
|
662
|
+
// The framework code is more specific than its HTTP projection.
|
|
663
|
+
error_code: code,
|
|
664
|
+
error_category: code !== "EXECUTION_FAILED" && typeof category === "string" && Array.isArray(catalog.properties.error_category) && catalog.properties.error_category.includes(category) ? category : "unknown",
|
|
665
|
+
...typeof retryable === "boolean" ? { retryable } : {}
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
const status = read("status") ?? read("statusCode");
|
|
670
|
+
if (typeof status === "number" && Number.isInteger(status) && status >= 400 && status <= 599)
|
|
671
|
+
return classifyUsageHttpStatus(status);
|
|
672
|
+
if (name === "ZodError" || name === "SyntaxError")
|
|
673
|
+
return {
|
|
674
|
+
outcome: "failed",
|
|
675
|
+
properties: {
|
|
676
|
+
error_code: "SCHEMA_VALIDATION",
|
|
677
|
+
error_category: "validation"
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
return void 0;
|
|
681
|
+
}
|
|
682
|
+
function classifyUsageError(error, catalog = publicCatalog) {
|
|
683
|
+
const seen = /* @__PURE__ */ new Set();
|
|
684
|
+
const pending = [{ value: error, depth: 0 }];
|
|
685
|
+
let result;
|
|
686
|
+
while (pending.length && seen.size < 8) {
|
|
687
|
+
const current = pending.shift();
|
|
688
|
+
if (!current || seen.has(current.value)) continue;
|
|
689
|
+
seen.add(current.value);
|
|
690
|
+
const found = classifyOne(current.value, catalog);
|
|
691
|
+
if (found) {
|
|
692
|
+
if (!result) result = found;
|
|
693
|
+
else if (found.properties.error_code && found.outcome === "failed" && found.properties.error_code !== result.properties.error_code) {
|
|
694
|
+
result.properties.cause_code = found.properties.error_code;
|
|
695
|
+
result.properties.cause_category = found.properties.error_category ?? "unknown";
|
|
696
|
+
if (result.properties.retryable === void 0 && typeof found.properties.retryable === "boolean")
|
|
697
|
+
result.properties.retryable = found.properties.retryable;
|
|
698
|
+
break;
|
|
699
|
+
}
|
|
700
|
+
if (result === found && found.outcome !== "failed") return found;
|
|
390
701
|
}
|
|
702
|
+
if (current.depth < 3)
|
|
703
|
+
for (const key of ["failure", "cause", "error"]) {
|
|
704
|
+
const value = usageErrorProperty(current.value, key);
|
|
705
|
+
if (value && typeof value === "object")
|
|
706
|
+
pending.push({ value, depth: current.depth + 1 });
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return result ?? {
|
|
710
|
+
outcome: "failed",
|
|
711
|
+
properties: { error_code: "other", error_category: "unknown" }
|
|
391
712
|
};
|
|
392
713
|
}
|
|
714
|
+
|
|
715
|
+
// src/measurement.ts
|
|
716
|
+
function usageMeasurement(event, operation, input) {
|
|
717
|
+
if (event === "usage.feature")
|
|
718
|
+
return usageErrorProperty(input, "origin") === "background" || usageErrorProperty(input, "origin") === "server" ? "observation" : "feature";
|
|
719
|
+
if (operation === "cli.command" || operation === "sdk.eval")
|
|
720
|
+
return "invocation";
|
|
721
|
+
if (operation === "studio.setup") return "stage";
|
|
722
|
+
if (operation === "sdk.execution" || operation === "studio.eval.execution")
|
|
723
|
+
return "execution";
|
|
724
|
+
if (operation === "sdk.export" || operation === "sdk.model") return "attempt";
|
|
725
|
+
if (operation === "sdk.structured") {
|
|
726
|
+
const format = usageErrorProperty(input, "format");
|
|
727
|
+
return format === "text-delta" ? "chunk" : format === "final" ? "output" : "update";
|
|
728
|
+
}
|
|
729
|
+
if (usageErrorProperty(input, "origin") === "background") return "poll";
|
|
730
|
+
if (operation.startsWith("studio.")) return "request";
|
|
731
|
+
return "call";
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// src/client.ts
|
|
393
735
|
function sanitizeProperties(input, catalog = publicCatalog) {
|
|
394
736
|
const safe = {};
|
|
395
737
|
for (const [key, rule] of Object.entries(catalog.properties)) {
|
|
396
|
-
const value =
|
|
738
|
+
const value = usageErrorProperty(input, key);
|
|
397
739
|
if (value === void 0) continue;
|
|
398
740
|
if (rule === "boolean") {
|
|
399
741
|
if (typeof value === "boolean") safe[key] = value;
|
|
400
742
|
} else if (typeof value === "string")
|
|
401
743
|
safe[key] = rule.includes(value) ? value : "other";
|
|
402
744
|
}
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
745
|
+
for (const key of ["error_code", "cause_code"]) {
|
|
746
|
+
const code = usageErrorProperty(input, key);
|
|
747
|
+
if (typeof code === "string")
|
|
748
|
+
safe[key] = catalog.errorCodes.includes(code) ? code : "other";
|
|
749
|
+
}
|
|
406
750
|
return safe;
|
|
407
751
|
}
|
|
408
752
|
var durationBounds = [10, 100, 1e3, 1e4, 6e4];
|
|
@@ -423,6 +767,16 @@ function createUsageTelemetry(options) {
|
|
|
423
767
|
let disposed = false;
|
|
424
768
|
let dropped = 0;
|
|
425
769
|
let delivered = 0;
|
|
770
|
+
let lostObservations = 0;
|
|
771
|
+
let failedBatches = 0;
|
|
772
|
+
let reportedLoss = 0;
|
|
773
|
+
let reportedFailures = 0;
|
|
774
|
+
const lose = (count) => {
|
|
775
|
+
lostObservations = Math.min(
|
|
776
|
+
Number.MAX_SAFE_INTEGER,
|
|
777
|
+
lostObservations + count
|
|
778
|
+
);
|
|
779
|
+
};
|
|
426
780
|
let hour = Math.floor(now() / 36e5);
|
|
427
781
|
let sent = 0;
|
|
428
782
|
const active = () => {
|
|
@@ -458,6 +812,10 @@ function createUsageTelemetry(options) {
|
|
|
458
812
|
pending.clear();
|
|
459
813
|
if (!records.length) return;
|
|
460
814
|
let attempted = records.length;
|
|
815
|
+
let attemptedObservations = records.reduce(
|
|
816
|
+
(total, record2) => total + record2.count,
|
|
817
|
+
0
|
|
818
|
+
);
|
|
461
819
|
flight = (async () => {
|
|
462
820
|
try {
|
|
463
821
|
if (options.debug?.()) {
|
|
@@ -475,7 +833,14 @@ function createUsageTelemetry(options) {
|
|
|
475
833
|
);
|
|
476
834
|
const batch = records.slice(0, allowance);
|
|
477
835
|
dropped += records.length - batch.length;
|
|
836
|
+
lose(
|
|
837
|
+
records.slice(allowance).reduce((total, record2) => total + record2.count, 0)
|
|
838
|
+
);
|
|
478
839
|
attempted = batch.length;
|
|
840
|
+
attemptedObservations = batch.reduce(
|
|
841
|
+
(total, record2) => total + record2.count,
|
|
842
|
+
0
|
|
843
|
+
);
|
|
479
844
|
if (!batch.length || !active()) return;
|
|
480
845
|
const identified = await options.identity();
|
|
481
846
|
const identity2 = typeof identified === "string" ? identified : identified.id;
|
|
@@ -486,13 +851,25 @@ function createUsageTelemetry(options) {
|
|
|
486
851
|
identity2
|
|
487
852
|
)) {
|
|
488
853
|
dropped += batch.length;
|
|
854
|
+
lose(attemptedObservations);
|
|
489
855
|
return;
|
|
490
856
|
}
|
|
491
857
|
sent += batch.length;
|
|
858
|
+
const lossSnapshot = lostObservations;
|
|
859
|
+
const failureSnapshot = failedBatches;
|
|
860
|
+
if (batch[0] && (lossSnapshot > reportedLoss || failureSnapshot > reportedFailures))
|
|
861
|
+
batch[0].delivery_health = {
|
|
862
|
+
dropped_observations: lossSnapshot - reportedLoss,
|
|
863
|
+
failed_batches: failureSnapshot - reportedFailures
|
|
864
|
+
};
|
|
492
865
|
await options.transport(batch, identity2);
|
|
866
|
+
reportedLoss = lossSnapshot;
|
|
867
|
+
reportedFailures = failureSnapshot;
|
|
493
868
|
delivered += batch.length;
|
|
494
869
|
} catch {
|
|
495
870
|
dropped += attempted;
|
|
871
|
+
lose(attemptedObservations);
|
|
872
|
+
failedBatches = Math.min(Number.MAX_SAFE_INTEGER, failedBatches + 1);
|
|
496
873
|
}
|
|
497
874
|
})().finally(() => {
|
|
498
875
|
flight = void 0;
|
|
@@ -516,12 +893,29 @@ function createUsageTelemetry(options) {
|
|
|
516
893
|
}
|
|
517
894
|
if (!configured || !catalog.operations.includes(operation) || !outcomes.includes(outcome))
|
|
518
895
|
return;
|
|
519
|
-
const
|
|
896
|
+
const failed = outcome === "failed";
|
|
897
|
+
const status = usageErrorProperty(input, "http_status");
|
|
898
|
+
const http = typeof status === "string" && /^\d{3}$/.test(status) ? classifyUsageHttpStatus(Number(status)).properties : {};
|
|
899
|
+
const properties = sanitizeProperties(
|
|
900
|
+
{
|
|
901
|
+
measurement_version: "2",
|
|
902
|
+
measurement_kind: usageMeasurement(event, operation, input),
|
|
903
|
+
...failed ? {
|
|
904
|
+
error_code: "UNCLASSIFIED_FAILURE",
|
|
905
|
+
error_category: "unknown",
|
|
906
|
+
...http
|
|
907
|
+
} : {},
|
|
908
|
+
...sanitizeProperties(input, catalog),
|
|
909
|
+
traffic_source: options.context.trafficSource ?? "unknown"
|
|
910
|
+
},
|
|
911
|
+
catalog
|
|
912
|
+
);
|
|
520
913
|
const key = JSON.stringify([event, operation, outcome, properties]);
|
|
521
914
|
let existing = pending.get(key);
|
|
522
915
|
if (!existing) {
|
|
523
916
|
if (pending.size >= (options.maxGroups ?? 64)) {
|
|
524
917
|
dropped++;
|
|
918
|
+
lose(1);
|
|
525
919
|
return;
|
|
526
920
|
}
|
|
527
921
|
existing = {
|
|
@@ -533,7 +927,9 @@ function createUsageTelemetry(options) {
|
|
|
533
927
|
component: options.context.component,
|
|
534
928
|
version: options.context.version,
|
|
535
929
|
analytics_environment: options.context.environment ?? "production",
|
|
536
|
-
runtime_mode:
|
|
930
|
+
runtime_mode: ["development", "production", "ci"].includes(
|
|
931
|
+
options.context.runtimeMode ?? ""
|
|
932
|
+
) ? options.context.runtimeMode ?? "unknown" : "unknown",
|
|
537
933
|
identity_scope: options.context.identityScope ?? "installation",
|
|
538
934
|
operation,
|
|
539
935
|
outcome,
|
|
@@ -578,8 +974,11 @@ function createUsageTelemetry(options) {
|
|
|
578
974
|
if (ended) return;
|
|
579
975
|
ended = true;
|
|
580
976
|
try {
|
|
977
|
+
const status = usageErrorProperty(extra, "http_status");
|
|
978
|
+
const http = outcome === "failed" && typeof status === "string" && /^\d{3}$/.test(status) ? classifyUsageHttpStatus(Number(status)).properties : {};
|
|
581
979
|
record("usage.operation", operation, outcome, now() - began, {
|
|
582
980
|
...initial,
|
|
981
|
+
...http,
|
|
583
982
|
...sanitizeProperties(extra, catalog)
|
|
584
983
|
});
|
|
585
984
|
} catch {
|
|
@@ -666,6 +1065,10 @@ function createPostHogTransport(options) {
|
|
|
666
1065
|
count: record.count,
|
|
667
1066
|
duration_total_ms: record.duration_total_ms,
|
|
668
1067
|
duration_buckets: record.duration_buckets,
|
|
1068
|
+
...record.delivery_health ? {
|
|
1069
|
+
usage_dropped_observations: record.delivery_health.dropped_observations,
|
|
1070
|
+
usage_failed_batches: record.delivery_health.failed_batches
|
|
1071
|
+
} : {},
|
|
669
1072
|
$process_person_profile: false,
|
|
670
1073
|
$geoip_disable: true,
|
|
671
1074
|
$ip: null
|
|
@@ -761,6 +1164,13 @@ async function installationIdentity() {
|
|
|
761
1164
|
})();
|
|
762
1165
|
return identity;
|
|
763
1166
|
}
|
|
1167
|
+
function runtimeUsageContext() {
|
|
1168
|
+
const traffic = process.env.KORTYX_USAGE_TELEMETRY_TRAFFIC_SOURCE;
|
|
1169
|
+
return {
|
|
1170
|
+
runtimeMode: process.env.CI && !["0", "false"].includes(process.env.CI.toLowerCase()) ? "ci" : process.env.NODE_ENV === "production" ? "production" : process.env.NODE_ENV === "development" ? "development" : "unknown",
|
|
1171
|
+
trafficSource: traffic === "internal" || traffic === "synthetic" || traffic === "external" ? traffic : "unknown"
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
764
1174
|
function usageDisabled() {
|
|
765
1175
|
return ["1", "true"].includes(process.env.KORTYX_USAGE_TELEMETRY_DISABLED ?? "") || process.env.NODE_ENV === "test";
|
|
766
1176
|
}
|
|
@@ -784,6 +1194,11 @@ function createNodeUsageTelemetry(options) {
|
|
|
784
1194
|
}
|
|
785
1195
|
return createUsageTelemetry({
|
|
786
1196
|
...options,
|
|
1197
|
+
context: {
|
|
1198
|
+
...options.context,
|
|
1199
|
+
runtimeMode: options.context.runtimeMode ?? runtimeUsageContext().runtimeMode,
|
|
1200
|
+
trafficSource: options.context.trafficSource ?? runtimeUsageContext().trafficSource
|
|
1201
|
+
},
|
|
787
1202
|
transport,
|
|
788
1203
|
identity: options.identity ?? installationIdentity,
|
|
789
1204
|
enabled: () => valid && !usageDisabled() && (options.enabled?.() ?? true),
|
|
@@ -792,6 +1207,9 @@ function createNodeUsageTelemetry(options) {
|
|
|
792
1207
|
});
|
|
793
1208
|
}
|
|
794
1209
|
var clients = /* @__PURE__ */ new Map();
|
|
1210
|
+
async function flushUsageTelemetry() {
|
|
1211
|
+
await Promise.all([...clients.values()].map((client) => client.flush()));
|
|
1212
|
+
}
|
|
795
1213
|
function configureUsageTelemetry(component, client) {
|
|
796
1214
|
if (clients.get(component) === client) return;
|
|
797
1215
|
clients.get(component)?.dispose();
|
|
@@ -806,7 +1224,7 @@ function getUsageTelemetry(component, version = "unknown", product = "sdk") {
|
|
|
806
1224
|
component,
|
|
807
1225
|
version,
|
|
808
1226
|
environment: process.env.KORTYX_USAGE_TELEMETRY_ENVIRONMENT === "staging" ? "staging" : "production",
|
|
809
|
-
|
|
1227
|
+
...runtimeUsageContext()
|
|
810
1228
|
}
|
|
811
1229
|
});
|
|
812
1230
|
clients.set(component, client);
|
|
@@ -832,6 +1250,7 @@ function publicUsageConfiguration() {
|
|
|
832
1250
|
}
|
|
833
1251
|
return {
|
|
834
1252
|
enabled: valid && !usageDisabled(),
|
|
1253
|
+
...runtimeUsageContext(),
|
|
835
1254
|
debug: process.env.KORTYX_USAGE_TELEMETRY_DEBUG === "1",
|
|
836
1255
|
environment: process.env.KORTYX_USAGE_TELEMETRY_ENVIRONMENT === "staging" ? "staging" : "production",
|
|
837
1256
|
// A mistakenly supplied personal token or credential-bearing URL must never reach HTML.
|
|
@@ -845,8 +1264,10 @@ function publicUsageConfiguration() {
|
|
|
845
1264
|
configureUsageTelemetry,
|
|
846
1265
|
createNodeUsageTelemetry,
|
|
847
1266
|
createUsageTelemetry,
|
|
1267
|
+
flushUsageTelemetry,
|
|
848
1268
|
getUsageTelemetry,
|
|
849
1269
|
publicUsageConfiguration,
|
|
1270
|
+
runtimeUsageContext,
|
|
850
1271
|
scale,
|
|
851
1272
|
usageDisabled
|
|
852
1273
|
});
|