@superblocksteam/telemetry 2.0.129 → 2.0.130-next.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.md +27 -12
- package/dist/common/index.d.ts +0 -2
- package/dist/common/index.d.ts.map +1 -1
- package/dist/common/index.js +0 -2
- package/dist/common/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/npm-registry/emitter.d.ts +227 -0
- package/dist/npm-registry/emitter.d.ts.map +1 -0
- package/dist/npm-registry/emitter.js +402 -0
- package/dist/npm-registry/emitter.js.map +1 -0
- package/dist/npm-registry/index.d.ts +12 -0
- package/dist/npm-registry/index.d.ts.map +1 -0
- package/dist/npm-registry/index.js +28 -0
- package/dist/npm-registry/index.js.map +1 -0
- package/dist/{common/sanitize-npm-attributes.d.ts → npm-registry/sanitize.d.ts} +19 -1
- package/dist/npm-registry/sanitize.d.ts.map +1 -0
- package/dist/{common/sanitize-npm-attributes.js → npm-registry/sanitize.js} +50 -2
- package/dist/npm-registry/sanitize.js.map +1 -0
- package/dist/npm-registry/spans.d.ts +61 -0
- package/dist/npm-registry/spans.d.ts.map +1 -0
- package/dist/npm-registry/spans.js +91 -0
- package/dist/npm-registry/spans.js.map +1 -0
- package/dist-esm/common/index.d.ts +0 -2
- package/dist-esm/common/index.d.ts.map +1 -1
- package/dist-esm/common/index.js +0 -2
- package/dist-esm/common/index.js.map +1 -1
- package/dist-esm/index.d.ts +1 -0
- package/dist-esm/index.d.ts.map +1 -1
- package/dist-esm/index.js +2 -0
- package/dist-esm/index.js.map +1 -1
- package/dist-esm/npm-registry/emitter.d.ts +227 -0
- package/dist-esm/npm-registry/emitter.d.ts.map +1 -0
- package/dist-esm/npm-registry/emitter.js +392 -0
- package/dist-esm/npm-registry/emitter.js.map +1 -0
- package/dist-esm/npm-registry/index.d.ts +12 -0
- package/dist-esm/npm-registry/index.d.ts.map +1 -0
- package/dist-esm/npm-registry/index.js +12 -0
- package/dist-esm/npm-registry/index.js.map +1 -0
- package/dist-esm/{common/sanitize-npm-attributes.d.ts → npm-registry/sanitize.d.ts} +19 -1
- package/dist-esm/npm-registry/sanitize.d.ts.map +1 -0
- package/dist-esm/{common/sanitize-npm-attributes.js → npm-registry/sanitize.js} +49 -2
- package/dist-esm/npm-registry/sanitize.js.map +1 -0
- package/dist-esm/npm-registry/spans.d.ts +61 -0
- package/dist-esm/npm-registry/spans.d.ts.map +1 -0
- package/dist-esm/npm-registry/spans.js +86 -0
- package/dist-esm/npm-registry/spans.js.map +1 -0
- package/package.json +4 -4
- package/dist/common/npm-install-metrics.d.ts +0 -88
- package/dist/common/npm-install-metrics.d.ts.map +0 -1
- package/dist/common/npm-install-metrics.js +0 -140
- package/dist/common/npm-install-metrics.js.map +0 -1
- package/dist/common/sanitize-npm-attributes.d.ts.map +0 -1
- package/dist/common/sanitize-npm-attributes.js.map +0 -1
- package/dist-esm/common/npm-install-metrics.d.ts +0 -88
- package/dist-esm/common/npm-install-metrics.d.ts.map +0 -1
- package/dist-esm/common/npm-install-metrics.js +0 -135
- package/dist-esm/common/npm-install-metrics.js.map +0 -1
- package/dist-esm/common/sanitize-npm-attributes.d.ts.map +0 -1
- package/dist-esm/common/sanitize-npm-attributes.js.map +0 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* NPM Registry Spans (APPS-4378)
|
|
4
|
+
*
|
|
5
|
+
* OTel-native `startActiveSpan` helpers for the four named npm-registry spans.
|
|
6
|
+
* Using the global tracer (`trace.getTracer`) means LLMObs interop is
|
|
7
|
+
* automatic: a span started inside an `@llmobs.decorate`'d context becomes a
|
|
8
|
+
* child of that task span (shared global tracer provider); outside any LLMObs
|
|
9
|
+
* context (server verify, CLI bootstrap) it is a root span.
|
|
10
|
+
*
|
|
11
|
+
* `org_id` rides on spans (per-trace context), never on a metric label. Raw
|
|
12
|
+
* registry hostnames are bucketed via {@link bucketNpmRegistryHost} before
|
|
13
|
+
* they reach a span attribute, matching the metric path — the one exception
|
|
14
|
+
* is the structured-log path, which keeps the raw host (see
|
|
15
|
+
* {@link auditNpmRegistryHost} in ./sanitize).
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.NPM_REGISTRY_SPAN_ATTR = exports.NPM_REGISTRY_SPAN = void 0;
|
|
19
|
+
exports.withNpmRegistrySpan = withNpmRegistrySpan;
|
|
20
|
+
exports.npmPackageUnpackedSizeBytes = npmPackageUnpackedSizeBytes;
|
|
21
|
+
const api_1 = require("@opentelemetry/api");
|
|
22
|
+
/** Instrumentation scope for every npm-registry span. */
|
|
23
|
+
const TRACER_NAME = "superblocks.npm-registry";
|
|
24
|
+
/** The four named spans (APPS-4378). */
|
|
25
|
+
exports.NPM_REGISTRY_SPAN = {
|
|
26
|
+
VERIFY: "npm_registry.verify",
|
|
27
|
+
LOOKUP_PACKAGE: "npm_registry.lookup_package",
|
|
28
|
+
PREPARE_FOR_PRIVATE_REGISTRY: "npm_registry.prepare_for_private_registry",
|
|
29
|
+
SYNC_HOME_NPMRC: "npm_registry.sync_home_npmrc",
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Canonical span-attribute keys, shared across the four spans so the
|
|
33
|
+
* dimension names never drift between call sites.
|
|
34
|
+
*/
|
|
35
|
+
exports.NPM_REGISTRY_SPAN_ATTR = {
|
|
36
|
+
ORG_ID: "org_id",
|
|
37
|
+
/** Bucketed (`public_npm | private | unknown`) — never the raw host. */
|
|
38
|
+
REGISTRY_HOST: "registry_host",
|
|
39
|
+
OUTCOME: "outcome",
|
|
40
|
+
DURATION_MS: "duration_ms",
|
|
41
|
+
/** Richer `NpmRegistryFetchResult.source` for drill-down. */
|
|
42
|
+
SOURCE: "source",
|
|
43
|
+
REGISTRY_CONFIGURED: "registry_configured",
|
|
44
|
+
/** Tri-valued: `true | false | "unknown"`. */
|
|
45
|
+
ALLOW_INSTALL_SCRIPTS: "allow_install_scripts",
|
|
46
|
+
PACKAGE_NAME_BUCKET: "package_name_bucket",
|
|
47
|
+
/** Span-attribute ONLY — raw byte counts are unbounded, never a metric label. */
|
|
48
|
+
PACKAGE_UNPACKED_SIZE_BYTES: "package_unpacked_size_bytes",
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Start an active npm-registry span over the global tracer, run `fn`, and
|
|
52
|
+
* end the span in `finally`. On throw, the error is recorded
|
|
53
|
+
* (`span.recordException`) and the span status set to ERROR before the throw
|
|
54
|
+
* propagates. `fn` receives the span so it can attach final attributes
|
|
55
|
+
* (outcome / duration) once the operation resolves.
|
|
56
|
+
*/
|
|
57
|
+
async function withNpmRegistrySpan(spanName, initialAttributes, fn) {
|
|
58
|
+
const tracer = api_1.trace.getTracer(TRACER_NAME);
|
|
59
|
+
return tracer.startActiveSpan(spanName, async (span) => {
|
|
60
|
+
if (Object.keys(initialAttributes).length > 0) {
|
|
61
|
+
span.setAttributes(initialAttributes);
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
return await fn(span);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
// `recordException` accepts Error | string; narrow rather than cast.
|
|
68
|
+
span.recordException(err instanceof Error ? err : String(err));
|
|
69
|
+
span.setStatus({ code: api_1.SpanStatusCode.ERROR });
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
span.end();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Coerce a registry-reported `dist.unpackedSize` to a span-safe value: a
|
|
79
|
+
* finite, non-negative number, else `undefined`. A hostile or buggy registry
|
|
80
|
+
* cannot smuggle a non-numeric byte count onto the span through this guard.
|
|
81
|
+
*
|
|
82
|
+
* Callers MUST omit the attribute when this returns `undefined` rather than
|
|
83
|
+
* calling `setAttribute(key, undefined)` (some OTel exporters mishandle
|
|
84
|
+
* undefined values). The lookup call site guards with `if (size !== undefined)`.
|
|
85
|
+
*/
|
|
86
|
+
function npmPackageUnpackedSizeBytes(raw) {
|
|
87
|
+
return typeof raw === "number" && Number.isFinite(raw) && raw >= 0
|
|
88
|
+
? raw
|
|
89
|
+
: undefined;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=spans.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spans.js","sourceRoot":"","sources":["../../src/npm-registry/spans.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;AA+CH,kDAqBC;AAWD,kEAIC;AAjFD,4CAK4B;AAE5B,yDAAyD;AACzD,MAAM,WAAW,GAAG,0BAA0B,CAAC;AAE/C,wCAAwC;AAC3B,QAAA,iBAAiB,GAAG;IAC/B,MAAM,EAAE,qBAAqB;IAC7B,cAAc,EAAE,6BAA6B;IAC7C,4BAA4B,EAAE,2CAA2C;IACzE,eAAe,EAAE,8BAA8B;CACvC,CAAC;AAEX;;;GAGG;AACU,QAAA,sBAAsB,GAAG;IACpC,MAAM,EAAE,QAAQ;IAChB,wEAAwE;IACxE,aAAa,EAAE,eAAe;IAC9B,OAAO,EAAE,SAAS;IAClB,WAAW,EAAE,aAAa;IAC1B,6DAA6D;IAC7D,MAAM,EAAE,QAAQ;IAChB,mBAAmB,EAAE,qBAAqB;IAC1C,8CAA8C;IAC9C,qBAAqB,EAAE,uBAAuB;IAC9C,mBAAmB,EAAE,qBAAqB;IAC1C,iFAAiF;IACjF,2BAA2B,EAAE,6BAA6B;CAClD,CAAC;AAEX;;;;;;GAMG;AACI,KAAK,UAAU,mBAAmB,CACvC,QAAgB,EAChB,iBAA6B,EAC7B,EAA8B;IAE9B,MAAM,MAAM,GAAG,WAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC,eAAe,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACrD,IAAI,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9C,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,qEAAqE;YACrE,IAAI,CAAC,eAAe,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC/D,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,oBAAc,CAAC,KAAK,EAAE,CAAC,CAAC;YAC/C,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,2BAA2B,CAAC,GAAY;IACtD,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAChE,CAAC,CAAC,GAAG;QACL,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC"}
|
|
@@ -14,7 +14,5 @@ export * from "./guardrails.js";
|
|
|
14
14
|
export * from "./contracts/tier2-traces.js";
|
|
15
15
|
export * from "./log-sanitizer.js";
|
|
16
16
|
export * from "./normalize-metric-path.js";
|
|
17
|
-
export * from "./sanitize-npm-attributes.js";
|
|
18
|
-
export * from "./npm-install-metrics.js";
|
|
19
17
|
export * from "./tier-hints.js";
|
|
20
18
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oBAAoB,CAAC;AACnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oBAAoB,CAAC;AACnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC"}
|
package/dist-esm/common/index.js
CHANGED
|
@@ -14,7 +14,5 @@ export * from "./guardrails.js";
|
|
|
14
14
|
export * from "./contracts/tier2-traces.js";
|
|
15
15
|
export * from "./log-sanitizer.js";
|
|
16
16
|
export * from "./normalize-metric-path.js";
|
|
17
|
-
export * from "./sanitize-npm-attributes.js";
|
|
18
|
-
export * from "./npm-install-metrics.js";
|
|
19
17
|
export * from "./tier-hints.js";
|
|
20
18
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oBAAoB,CAAC;AACnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oBAAoB,CAAC;AACnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC"}
|
package/dist-esm/index.d.ts
CHANGED
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
*/
|
|
39
39
|
export * from "./types/index.js";
|
|
40
40
|
export * from "./common/index.js";
|
|
41
|
+
export * from "./npm-registry/index.js";
|
|
41
42
|
export * from "./llmobs/index.js";
|
|
42
43
|
export { REDACTED_PLACEHOLDER } from "./common/index.js";
|
|
43
44
|
export { createSafeLogger, getLoggingPolicy, getMinimumPinoLevelForDeployment, getMinimumPinoLevelFromLoggingPolicy, getTelemetryInstance, initNodeTelemetry, isTelemetryInitialized, LogExportMode, redactForbiddenFields, resetTelemetry, type NodeTelemetryInstance, type LoggingPolicyConfig, type SafeLoggerLike, MetricsClient, type Labels, type CounterHandle, type GaugeHandle, type HistogramHandle, TracedSocket, type TracedSocketConfig, type TracedSocketOptions, } from "./node/index.js";
|
package/dist-esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAGH,cAAc,kBAAkB,CAAC;AAGjC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,mBAAmB,CAAC;AAIlC,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,gCAAgC,EAChC,oCAAoC,EACpC,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,EACtB,aAAa,EACb,qBAAqB,EACrB,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,aAAa,EACb,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,YAAY,EACZ,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAGH,cAAc,kBAAkB,CAAC;AAGjC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,yBAAyB,CAAC;AAGxC,cAAc,mBAAmB,CAAC;AAIlC,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,gCAAgC,EAChC,oCAAoC,EACpC,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,EACtB,aAAa,EACb,qBAAqB,EACrB,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,aAAa,EACb,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,YAAY,EACZ,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,iBAAiB,CAAC"}
|
package/dist-esm/index.js
CHANGED
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
export * from "./types/index.js";
|
|
41
41
|
// Re-export common utilities
|
|
42
42
|
export * from "./common/index.js";
|
|
43
|
+
// Re-export npm-registry telemetry (sanitization, metrics emitter, spans)
|
|
44
|
+
export * from "./npm-registry/index.js";
|
|
43
45
|
// Re-export LLMObs tiered telemetry
|
|
44
46
|
export * from "./llmobs/index.js";
|
|
45
47
|
// Re-export Node.js telemetry functions for convenience
|
package/dist-esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,kBAAkB;AAClB,cAAc,kBAAkB,CAAC;AAEjC,6BAA6B;AAC7B,cAAc,mBAAmB,CAAC;AAElC,oCAAoC;AACpC,cAAc,mBAAmB,CAAC;AAElC,wDAAwD;AACxD,2FAA2F;AAC3F,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,gCAAgC,EAChC,oCAAoC,EACpC,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,EACtB,aAAa,EACb,qBAAqB,EACrB,cAAc,EAId,aAAa,EAKb,YAAY,GAGb,MAAM,iBAAiB,CAAC;AAEzB,wFAAwF;AACxF,kBAAkB;AAClB,yCAAyC;AACzC,yCAAyC;AACzC,wCAAwC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,kBAAkB;AAClB,cAAc,kBAAkB,CAAC;AAEjC,6BAA6B;AAC7B,cAAc,mBAAmB,CAAC;AAElC,0EAA0E;AAC1E,cAAc,yBAAyB,CAAC;AAExC,oCAAoC;AACpC,cAAc,mBAAmB,CAAC;AAElC,wDAAwD;AACxD,2FAA2F;AAC3F,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,gCAAgC,EAChC,oCAAoC,EACpC,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,EACtB,aAAa,EACb,qBAAqB,EACrB,cAAc,EAId,aAAa,EAKb,YAAY,GAGb,MAAM,iBAAiB,CAAC;AAEzB,wFAAwF;AACxF,kBAAkB;AAClB,yCAAyC;AACzC,yCAAyC;AACzC,wCAAwC"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NPM Registry Metrics Emitter (APPS-4378 / APPS-4189)
|
|
3
|
+
*
|
|
4
|
+
* One emitter for the whole npm-registry subsystem. It owns every
|
|
5
|
+
* `superblocks.npm.*` metric and centralizes the sanitize-at-emit-boundary
|
|
6
|
+
* discipline: no raw registry hostname, package name, or token can escape via
|
|
7
|
+
* a metric label because every dimension is coerced to a closed enum or a
|
|
8
|
+
* bounded bucket here, the moment before it reaches an OTel instrument.
|
|
9
|
+
*
|
|
10
|
+
* Wiring follows the rest of the npm subsystem: the meter is resolved lazily
|
|
11
|
+
* from the OTel global (`metrics.getMeter`) and instruments are cached and
|
|
12
|
+
* re-bound when the global MeterProvider changes (the typical case is a test
|
|
13
|
+
* installing its own provider for isolation). The emitter is therefore a
|
|
14
|
+
* zero-config process singleton (`npmRegistryEmitter`) — no DI plumbing
|
|
15
|
+
* through AiServiceConfig / NpmRegistryClientDeps.
|
|
16
|
+
*
|
|
17
|
+
* Metrics (all `superblocks.npm.*`, durations in seconds):
|
|
18
|
+
*
|
|
19
|
+
* - `config.lookup_total` counter {result}
|
|
20
|
+
* - `config.lookup_duration_seconds` histogram {result} buckets [0.05,0.25,1,3,10,30]
|
|
21
|
+
* - `install.outcome_total` counter {outcome, configured, registry_host, runner}
|
|
22
|
+
* - `install.duration_seconds`histogram {…same…} buckets [0.5,2,5,15,60,300]
|
|
23
|
+
* - `install.status` gauge {…same…} last-seen 1=success/0=else
|
|
24
|
+
* - `probe.outcome_total` counter {outcome, registry_host}
|
|
25
|
+
* - `probe.duration_seconds` histogram {…same…} buckets [0.1,0.5,1,3,10,30]
|
|
26
|
+
* - `lookup.calls_total` counter {outcome, registry_host}
|
|
27
|
+
* - `lookup.duration_seconds` histogram {…same…} buckets [0.05,0.25,1,3,10,30]
|
|
28
|
+
* - `killswitch.enabled` gauge {} 1 when the feature is suppressed
|
|
29
|
+
*
|
|
30
|
+
* Cardinality budget (documented contract — must not regress without a
|
|
31
|
+
* migration plan; every dashboard / Datadog facet pins on these):
|
|
32
|
+
*
|
|
33
|
+
* - registry_host ≤ 3 (`public_npm | private | unknown`)
|
|
34
|
+
* - install.outcome ≤ 7 (`NPM_INSTALL_OUTCOMES`)
|
|
35
|
+
* - probe.outcome ≤ 5 (`NPM_PROBE_OUTCOMES`)
|
|
36
|
+
* - lookup.outcome ≤ 3 (`NPM_LOOKUP_TOOL_OUTCOMES`)
|
|
37
|
+
* - config.result ≤ 4 (`NPM_CONFIG_LOOKUP_RESULTS`)
|
|
38
|
+
* - configured ≤ 2, runner ≤ 4 (`NPM_INSTALL_RUNNERS`)
|
|
39
|
+
*
|
|
40
|
+
* Worst-case distinct series across every instrument is ≈ 560: the three
|
|
41
|
+
* install instruments (status gauge + counter + histogram) dominate at
|
|
42
|
+
* outcome×configured×registry_host×runner = 7×2×3×4 = 168 each → 504, plus
|
|
43
|
+
* probe 5×3×2=30, config 4×2=8, lookup 3×3×2=18, and the killswitch gauge 1.
|
|
44
|
+
* APPS-4544 raised the runner count from 3 to 4, which is why this grew from
|
|
45
|
+
* ≈430 (adding a runner value must increase the count, not decrease it). Still
|
|
46
|
+
* well under the internal 150K flag and the Datadog billing boundary. Per-pod
|
|
47
|
+
* `host` is stripped at the collector for the `superblocks.npm.*` family so
|
|
48
|
+
* series are aggregated by `service.name`.
|
|
49
|
+
*
|
|
50
|
+
* Each path has its OWN closed outcome enum + `normalize*` coercer because the
|
|
51
|
+
* paths legitimately diverge (probe has `ok`/`invalid_response`, install has
|
|
52
|
+
* `tls_failed`/`lockfile_url_mismatch`, …). `auth_failed` is deliberately a
|
|
53
|
+
* first-class outcome on install/probe — an expected, fast-failing
|
|
54
|
+
* customer-config mode — kept separable from infra failures (`unreachable` /
|
|
55
|
+
* `tls_failed`) so a future alert can page on infra spikes while routing auth
|
|
56
|
+
* to a dashboard. These short names diverge from the long-form `NPM_OUTCOMES`
|
|
57
|
+
* in `./sanitize` (which stays as the audit-event surface): the install path
|
|
58
|
+
* normalizes the long `NpmInstallBlocked.reason` values to the short forms.
|
|
59
|
+
*/
|
|
60
|
+
import { type NpmRegistryBucket } from "./sanitize.js";
|
|
61
|
+
/**
|
|
62
|
+
* Closed enum of npm install call sites that emit metrics. One value per call
|
|
63
|
+
* site Superblocks owns; renaming a value breaks every dashboard split on
|
|
64
|
+
* `runner`, so additions/removals need a migration plan.
|
|
65
|
+
*
|
|
66
|
+
* - `install_all` — full install from package.json (no specific
|
|
67
|
+
* packages). Covers both `AppShell.runNpmInstall`
|
|
68
|
+
* and `AppShell.runPackageManagerInstall`.
|
|
69
|
+
* - `install_specific` — `AppShell.installSpecificPackages` (`npm add`).
|
|
70
|
+
* - `prefetch` — `installTemplateModules` (template `node_modules`
|
|
71
|
+
* prefetch during AiService construction).
|
|
72
|
+
* - `auto_upgrade` — `upgradeCliWithPackageManager` in the CLI SDK
|
|
73
|
+
* (`automatic-upgrades.ts`). Owns the dev-server
|
|
74
|
+
* self-upgrade install; a perimeter block here
|
|
75
|
+
* crash-loops the live-edit pod, so the metric is
|
|
76
|
+
* the only signal that distinguishes that from a
|
|
77
|
+
* working upgrade. APPS-4544.
|
|
78
|
+
*/
|
|
79
|
+
export declare const NPM_INSTALL_RUNNERS: readonly ["auto_upgrade", "install_all", "install_specific", "prefetch"];
|
|
80
|
+
export type NpmInstallRunner = (typeof NPM_INSTALL_RUNNERS)[number];
|
|
81
|
+
/**
|
|
82
|
+
* `config.lookup_total` result. Derived inside `NpmRegistryClient.getConfig`
|
|
83
|
+
* (the only place that knows cache hit vs miss): in-TTL non-empty hit → `hit`;
|
|
84
|
+
* fresh fetch with a configured registry → `miss`; empty / 404 / flag-off →
|
|
85
|
+
* `not_configured`; `stale` / `unreachable` / any throw → `error`.
|
|
86
|
+
*/
|
|
87
|
+
export declare const NPM_CONFIG_LOOKUP_RESULTS: readonly ["hit", "miss", "not_configured", "error"];
|
|
88
|
+
export type NpmConfigLookupResult = (typeof NPM_CONFIG_LOOKUP_RESULTS)[number];
|
|
89
|
+
/** Unknown config-lookup states are an error condition, not a new series. */
|
|
90
|
+
export declare function normalizeConfigLookupResult(input: string | null | undefined): NpmConfigLookupResult;
|
|
91
|
+
/**
|
|
92
|
+
* `install.outcome_total` outcome. Short forms; the install path normalizes
|
|
93
|
+
* the long `NpmInstallBlocked.reason` values (`registry_auth_failed`,
|
|
94
|
+
* `registry_unreachable`) to these via {@link normalizeInstallOutcome}.
|
|
95
|
+
*/
|
|
96
|
+
export declare const NPM_INSTALL_OUTCOMES: readonly ["success", "auth_failed", "not_in_registry", "unreachable", "tls_failed", "lockfile_url_mismatch", "other"];
|
|
97
|
+
export type NpmInstallOutcome = (typeof NPM_INSTALL_OUTCOMES)[number];
|
|
98
|
+
export declare function normalizeInstallOutcome(input: string | null | undefined): NpmInstallOutcome;
|
|
99
|
+
/** `probe.outcome_total` outcome. Mapped from the server `ProbeResult`. */
|
|
100
|
+
export declare const NPM_PROBE_OUTCOMES: readonly ["ok", "auth_failed", "unreachable", "tls_failed", "invalid_response"];
|
|
101
|
+
export type NpmProbeOutcome = (typeof NPM_PROBE_OUTCOMES)[number];
|
|
102
|
+
/**
|
|
103
|
+
* Unknown → `unreachable`: the typed call site maps the closed `ProbeReason`
|
|
104
|
+
* exhaustively, so this floor only fires on a non-typed (JS) caller, where
|
|
105
|
+
* "we couldn't classify the probe" is safest reported as a connectivity
|
|
106
|
+
* failure rather than silently dropped or minted as a new series.
|
|
107
|
+
*/
|
|
108
|
+
export declare function normalizeProbeOutcome(input: string | null | undefined): NpmProbeOutcome;
|
|
109
|
+
/** `lookup.calls_total` outcome (the Clark `lookupNpmPackage` tool). */
|
|
110
|
+
export declare const NPM_LOOKUP_TOOL_OUTCOMES: readonly ["in_registry", "not_in_registry", "error"];
|
|
111
|
+
export type NpmLookupToolOutcome = (typeof NPM_LOOKUP_TOOL_OUTCOMES)[number];
|
|
112
|
+
/** Auth / unreachable / fail-closed / throw all collapse to `error` here. */
|
|
113
|
+
export declare function normalizeLookupToolOutcome(input: string | null | undefined): NpmLookupToolOutcome;
|
|
114
|
+
export interface NpmInstallMetricInput {
|
|
115
|
+
/** Which install call site emitted this event. */
|
|
116
|
+
runner: NpmInstallRunner;
|
|
117
|
+
/**
|
|
118
|
+
* Registry URL or bare hostname (`null`/`undefined` → `unknown`). Always
|
|
119
|
+
* bucketed via `bucketNpmRegistryHost`, so raw hostnames cannot leak.
|
|
120
|
+
*/
|
|
121
|
+
registryHost: string | null | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* Outcome key. Free-form on input; coerced to {@link NPM_INSTALL_OUTCOMES}
|
|
124
|
+
* at the emit boundary (long `NpmInstallBlocked.reason` forms are mapped to
|
|
125
|
+
* their short equivalents; unknown → `other`). Use `"success"` on the happy
|
|
126
|
+
* path.
|
|
127
|
+
*/
|
|
128
|
+
outcome: string | null | undefined;
|
|
129
|
+
/**
|
|
130
|
+
* Whether the org had a private registry configured for this install
|
|
131
|
+
* (`result.config.configured`). Lets dashboards separate
|
|
132
|
+
* configured-but-failed from never-configured.
|
|
133
|
+
*/
|
|
134
|
+
configured: boolean;
|
|
135
|
+
/** Elapsed wall-clock time in milliseconds; clamped at 0, emitted as seconds. */
|
|
136
|
+
durationMs: number;
|
|
137
|
+
}
|
|
138
|
+
export interface NpmProbeMetricInput {
|
|
139
|
+
registryHost: string | null | undefined;
|
|
140
|
+
outcome: string | null | undefined;
|
|
141
|
+
durationMs: number;
|
|
142
|
+
}
|
|
143
|
+
export interface NpmLookupToolMetricInput {
|
|
144
|
+
registryHost: string | null | undefined;
|
|
145
|
+
outcome: string | null | undefined;
|
|
146
|
+
durationMs: number;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Attribute set shaped for `llmobs.annotate({ tags })`. Returned by
|
|
150
|
+
* `recordInstall` so the active install span carries the exact values that
|
|
151
|
+
* went into the metric pipeline.
|
|
152
|
+
*/
|
|
153
|
+
export interface NpmInstallSpanAttributes {
|
|
154
|
+
"npm.registry_host": NpmRegistryBucket;
|
|
155
|
+
"npm.outcome": NpmInstallOutcome;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* The npm-registry metrics emitter. Constructed once as the
|
|
159
|
+
* {@link npmRegistryEmitter} singleton; no `Meter` is injected — instruments
|
|
160
|
+
* resolve lazily from the OTel global and re-bind on provider change.
|
|
161
|
+
*/
|
|
162
|
+
export declare class NpmRegistryEmitter {
|
|
163
|
+
/**
|
|
164
|
+
* Last-seen install status keyed by the attribute tuple. The observable
|
|
165
|
+
* gauge reads this each collection cycle. Every key component is coerced to
|
|
166
|
+
* a closed enum / bounded bucket at the emit boundary
|
|
167
|
+
* (`normalizeInstallOutcome`, `bucketNpmRegistryHost`, the `NpmInstallRunner`
|
|
168
|
+
* union, a boolean), so the map is **bounded by construction** at
|
|
169
|
+
* runner(4) × configured(2) × registry_host(3) × outcome(7) = 168 entries —
|
|
170
|
+
* it cannot grow with traffic, only with deliberate enum changes. Entries
|
|
171
|
+
* persist until process exit / `_resetForTesting` (a "last-seen for
|
|
172
|
+
* dashboards" gauge must not drop a combo and lie about the most recent
|
|
173
|
+
* outcome); no eviction is needed at this bound. The cardinality regression
|
|
174
|
+
* test pins the resulting series count.
|
|
175
|
+
*/
|
|
176
|
+
private readonly lastInstallStatus;
|
|
177
|
+
/** Returns `true` when the kill-switch is asserting (feature suppressed). */
|
|
178
|
+
private killswitchState;
|
|
179
|
+
private cachedMeter;
|
|
180
|
+
private cachedInstruments;
|
|
181
|
+
/**
|
|
182
|
+
* Lazy instrument factory. Re-creates instruments and re-registers the
|
|
183
|
+
* observable-gauge callbacks when the global MeterProvider changes (a stale
|
|
184
|
+
* instrument bound to a replaced provider silently drops data).
|
|
185
|
+
*/
|
|
186
|
+
private getInstruments;
|
|
187
|
+
/**
|
|
188
|
+
* Emit one `config.lookup_total` + `config.lookup_duration_seconds`
|
|
189
|
+
* observation. `durationMs` is the time `getConfig` spent resolving — sub-ms
|
|
190
|
+
* for a cache hit, the network round-trip (+ any JWT-refresh retry) for a
|
|
191
|
+
* miss — so dashboards can tell fast cache hits from slow cold fetches.
|
|
192
|
+
*/
|
|
193
|
+
recordConfigLookup(result: string | null | undefined, durationMs: number): void;
|
|
194
|
+
/**
|
|
195
|
+
* Emit the install outcome counter + duration histogram + last-seen status
|
|
196
|
+
* gauge, and return the matching span-attribute pair for `llmobs.annotate`.
|
|
197
|
+
* Safe to call from a `finally` block.
|
|
198
|
+
*/
|
|
199
|
+
recordInstall(input: NpmInstallMetricInput): NpmInstallSpanAttributes;
|
|
200
|
+
/** Emit one `probe.outcome_total` + `probe.duration_seconds` observation. */
|
|
201
|
+
recordProbe(input: NpmProbeMetricInput): void;
|
|
202
|
+
/** Emit one `lookup.calls_total` + `lookup.duration_seconds` observation. */
|
|
203
|
+
recordLookupTool(input: NpmLookupToolMetricInput): void;
|
|
204
|
+
/**
|
|
205
|
+
* Register the kill-switch state source. `getCurrentState` returns `true`
|
|
206
|
+
* when the feature is suppressed (LD flag off). Idempotent — the latest
|
|
207
|
+
* source wins; the observable gauge reads it on each scrape. Calling this
|
|
208
|
+
* also ensures the instruments (and thus the gauge callback) are wired.
|
|
209
|
+
*/
|
|
210
|
+
setKillswitch(getCurrentState: () => boolean): void;
|
|
211
|
+
/**
|
|
212
|
+
* Test-only: drop cached instruments, last-seen status, and the kill-switch
|
|
213
|
+
* source so the next emit re-binds to a freshly-installed test provider.
|
|
214
|
+
*/
|
|
215
|
+
_resetForTesting(): void;
|
|
216
|
+
}
|
|
217
|
+
/** Process-wide singleton. All npm-registry call sites emit through this. */
|
|
218
|
+
export declare const npmRegistryEmitter: NpmRegistryEmitter;
|
|
219
|
+
/**
|
|
220
|
+
* Back-compat free function for the install path (APPS-4189 call sites in
|
|
221
|
+
* `shell.ts` / `template-renderer.ts`). Delegates to the singleton and returns
|
|
222
|
+
* the span-attribute pair for `llmobs.annotate`.
|
|
223
|
+
*/
|
|
224
|
+
export declare function recordNpmInstall(input: NpmInstallMetricInput): NpmInstallSpanAttributes;
|
|
225
|
+
/** Test-only reset for the singleton (see {@link NpmRegistryEmitter._resetForTesting}). */
|
|
226
|
+
export declare function _resetNpmRegistryEmitterForTesting(): void;
|
|
227
|
+
//# sourceMappingURL=emitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../src/npm-registry/emitter.ts"],"names":[],"mappings":"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"}
|