@netgreener/runtime 0.1.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 +16 -0
- package/README.md +28 -0
- package/TUTORIAL.md +495 -0
- package/dist/aggregator.d.ts +43 -0
- package/dist/aggregator.js +270 -0
- package/dist/analyze/analyzeGate.d.ts +44 -0
- package/dist/analyze/analyzeGate.js +114 -0
- package/dist/analyze/apiOverconsumptionDetector.d.ts +13 -0
- package/dist/analyze/apiOverconsumptionDetector.js +75 -0
- package/dist/analyze/astDetectors.d.ts +38 -0
- package/dist/analyze/astDetectors.js +566 -0
- package/dist/analyze/buildResourceFindings.d.ts +34 -0
- package/dist/analyze/buildResourceFindings.js +86 -0
- package/dist/analyze/cli.d.ts +20 -0
- package/dist/analyze/cli.js +186 -0
- package/dist/analyze/cpuResourceDetectors.d.ts +35 -0
- package/dist/analyze/cpuResourceDetectors.js +163 -0
- package/dist/analyze/heuristicScan.d.ts +61 -0
- package/dist/analyze/heuristicScan.js +91 -0
- package/dist/analyze/index.d.ts +15 -0
- package/dist/analyze/index.js +15 -0
- package/dist/analyze/loadTypescript.d.ts +8 -0
- package/dist/analyze/loadTypescript.js +23 -0
- package/dist/analyze/memoryIoDetectors.d.ts +15 -0
- package/dist/analyze/memoryIoDetectors.js +68 -0
- package/dist/analyze/preferAstDetectors.d.ts +18 -0
- package/dist/analyze/preferAstDetectors.js +91 -0
- package/dist/analyze/projectScan.d.ts +19 -0
- package/dist/analyze/projectScan.js +115 -0
- package/dist/analyze/resourceAdmission.d.ts +33 -0
- package/dist/analyze/resourceAdmission.js +91 -0
- package/dist/analyze/resourceFindingCandidate.d.ts +61 -0
- package/dist/analyze/resourceFindingCandidate.js +75 -0
- package/dist/analyze/retryAmplificationDetector.d.ts +11 -0
- package/dist/analyze/retryAmplificationDetector.js +55 -0
- package/dist/analyze/schemas/resource-finding.schema.json +352 -0
- package/dist/analyze/serviceDiscovery.d.ts +66 -0
- package/dist/analyze/serviceDiscovery.js +210 -0
- package/dist/analyze/unboundedParallelismDetector.d.ts +11 -0
- package/dist/analyze/unboundedParallelismDetector.js +57 -0
- package/dist/analyze/validateResourceFinding.d.ts +19 -0
- package/dist/analyze/validateResourceFinding.js +46 -0
- package/dist/bullmq.d.ts +33 -0
- package/dist/bullmq.js +106 -0
- package/dist/cli/helpText.d.ts +8 -0
- package/dist/cli/helpText.js +99 -0
- package/dist/cli/netgreener.d.ts +12 -0
- package/dist/cli/netgreener.js +65 -0
- package/dist/collectorIpc.d.ts +28 -0
- package/dist/collectorIpc.js +189 -0
- package/dist/collectorMetadata.d.ts +15 -0
- package/dist/collectorMetadata.js +33 -0
- package/dist/config.d.ts +41 -0
- package/dist/config.js +97 -0
- package/dist/contract/index.d.ts +1 -0
- package/dist/contract/index.js +1 -0
- package/dist/contract/observation-envelope.schema.json +331 -0
- package/dist/contract/observation-protobuf-view.schema.json +773 -0
- package/dist/contract/observation-v1.d.mts +42 -0
- package/dist/contract/observation-v1.mjs +555 -0
- package/dist/contract/observationIdentity.d.ts +11 -0
- package/dist/contract/observationIdentity.js +48 -0
- package/dist/contract/observationProjection.d.ts +3 -0
- package/dist/contract/observationProjection.js +29 -0
- package/dist/contract/validate.d.ts +20 -0
- package/dist/contract/validate.js +227 -0
- package/dist/dogfood/mp3WorkerGates.d.ts +54 -0
- package/dist/dogfood/mp3WorkerGates.js +86 -0
- package/dist/dogfood/retainDryRunArtifact.d.ts +45 -0
- package/dist/dogfood/retainDryRunArtifact.js +103 -0
- package/dist/dogfood/tenantDogfoodGates.d.ts +76 -0
- package/dist/dogfood/tenantDogfoodGates.js +206 -0
- package/dist/exporter.d.ts +56 -0
- package/dist/exporter.js +304 -0
- package/dist/express.d.ts +44 -0
- package/dist/express.js +118 -0
- package/dist/externalApiMeter.d.ts +72 -0
- package/dist/externalApiMeter.js +1167 -0
- package/dist/fastify.d.ts +53 -0
- package/dist/fastify.js +148 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +31 -0
- package/dist/n4/deploymentMatrix.d.ts +22 -0
- package/dist/n4/deploymentMatrix.js +57 -0
- package/dist/n4/missingnessInventory.d.ts +22 -0
- package/dist/n4/missingnessInventory.js +72 -0
- package/dist/nest.d.ts +42 -0
- package/dist/nest.js +87 -0
- package/dist/processResources.d.ts +28 -0
- package/dist/processResources.js +31 -0
- package/dist/processRuntime.d.ts +35 -0
- package/dist/processRuntime.js +96 -0
- package/dist/requestContext.d.ts +21 -0
- package/dist/requestContext.js +33 -0
- package/dist/runtime.d.ts +60 -0
- package/dist/runtime.js +289 -0
- package/dist/runtimeHealth.d.ts +35 -0
- package/dist/runtimeHealth.js +121 -0
- package/dist/serverlessHints.d.ts +26 -0
- package/dist/serverlessHints.js +52 -0
- package/dist/tenantContext.d.ts +78 -0
- package/dist/tenantContext.js +226 -0
- package/dist/tenantDefaults.d.ts +9 -0
- package/dist/tenantDefaults.js +9 -0
- package/dist/types.d.ts +131 -0
- package/dist/types.js +2 -0
- package/dist/uploader.d.ts +23 -0
- package/dist/uploader.js +52 -0
- package/dist/windowId.d.ts +2 -0
- package/dist/windowId.js +7 -0
- package/examples/analyze-manifest.mjs +30 -0
- package/examples/bullmq-live-smoke.mjs +178 -0
- package/examples/bullmq-mp3-worker-gate.mjs +237 -0
- package/examples/express-dry-run.mjs +72 -0
- package/examples/process-mp3-restart-gate.mjs +135 -0
- package/examples/retain-dry-run.mjs +110 -0
- package/examples/tenant-dogfood.mjs +269 -0
- package/fixtures/analyze-sample/architectureNoise.ts +13 -0
- package/fixtures/analyze-sample/cpuHotPaths.ts +27 -0
- package/fixtures/analyze-sample/fanOutClient.ts +15 -0
- package/fixtures/analyze-sample/inferenceHotPaths.ts +22 -0
- package/fixtures/analyze-sample/memoryIoHotPaths.ts +21 -0
- package/fixtures/analyze-sample/modelLoadHotPaths.ts +11 -0
- package/fixtures/analyze-sample/nestedLookup.ts +15 -0
- package/fixtures/analyze-sample/retryClient.ts +10 -0
- package/fixtures/analyze-sample/securitySmell.ts +8 -0
- package/fixtures/analyze-sample/server.ts +11 -0
- package/fixtures/analyze-sample/styleOnly.ts +7 -0
- package/fixtures/analyze-sample/worker.ts +4 -0
- package/fixtures/dual-view-projection.v1.json +53 -0
- package/fixtures/identity-preimages.v1.json +42 -0
- package/fixtures/mp2_shadow_digest_golden.v1.json +47 -0
- package/fixtures/projection-batch-document.v1.json +170 -0
- package/fixtures/projection-batch.v1.json +9 -0
- package/fixtures/python_reference_shapes.md +24 -0
- package/fixtures/session_metadata_runtime_v0.json +120 -0
- package/fixtures/session_metadata_with_tenant.json +85 -0
- package/package.json +137 -0
package/dist/express.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { getRuntime, } from './runtime.js';
|
|
2
|
+
import { getTenantStore, resolveTenantFromHttpRequest, runWithTenantContext, tenantConfigFromRuntime, } from './tenantContext.js';
|
|
3
|
+
import { runWithRequestAttribution } from './requestContext.js';
|
|
4
|
+
import { beginProcessResourceSample, endProcessResourceSample, } from './processResources.js';
|
|
5
|
+
/**
|
|
6
|
+
* Bounded template for service_unit attribution.
|
|
7
|
+
* Never fall back to raw customer paths, baseUrl mounts, or query strings.
|
|
8
|
+
*/
|
|
9
|
+
export const UNKNOWN_ROUTE_TEMPLATE = '<unmatched>';
|
|
10
|
+
const MAX_ROUTE_TEMPLATE_LEN = 512;
|
|
11
|
+
/**
|
|
12
|
+
* Return only a framework-owned route template.
|
|
13
|
+
*
|
|
14
|
+
* Express ``req.path``, ``req.url``, and ``req.baseUrl`` may contain customer
|
|
15
|
+
* identifiers or query values — they are never safe metering fallbacks.
|
|
16
|
+
* Prefer resolving this at finish/close (after routing) rather than at entry.
|
|
17
|
+
*/
|
|
18
|
+
export function expressRouteTemplate(req) {
|
|
19
|
+
const route = req.route?.path;
|
|
20
|
+
if (typeof route !== 'string')
|
|
21
|
+
return UNKNOWN_ROUTE_TEMPLATE;
|
|
22
|
+
const template = route.trim();
|
|
23
|
+
if (!template ||
|
|
24
|
+
template.length > MAX_ROUTE_TEMPLATE_LEN ||
|
|
25
|
+
template.includes('?') ||
|
|
26
|
+
template.includes('#')) {
|
|
27
|
+
return UNKNOWN_ROUTE_TEMPLATE;
|
|
28
|
+
}
|
|
29
|
+
return template;
|
|
30
|
+
}
|
|
31
|
+
function httpServiceUnit(method, req) {
|
|
32
|
+
return `${method} ${expressRouteTemplate(req)}`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Express middleware: record per-route latency/status into NetGreenerRuntime.
|
|
36
|
+
*
|
|
37
|
+
* Enable with NETGREENER_SERVICE_RUNTIME=1 and credentials (see README).
|
|
38
|
+
* Also installs outbound ``fetch`` metering (N2) when runtime is enabled.
|
|
39
|
+
*
|
|
40
|
+
* Route identity is resolved when the response finishes (or on sync handler
|
|
41
|
+
* throw), so Express can attach ``req.route`` after middleware entry.
|
|
42
|
+
*
|
|
43
|
+
* Manual ``setTenantContext`` after auth mutates the request tenant store in
|
|
44
|
+
* place; finish/close read that store rather than freezing the entry-time value.
|
|
45
|
+
*/
|
|
46
|
+
export function netgreenerExpressMiddleware(opts = {}) {
|
|
47
|
+
const runtime = getRuntime({
|
|
48
|
+
collector: 'express_middleware',
|
|
49
|
+
framework: 'express',
|
|
50
|
+
...opts,
|
|
51
|
+
});
|
|
52
|
+
runtime.start();
|
|
53
|
+
const onSignal = () => {
|
|
54
|
+
void runtime.shutdown();
|
|
55
|
+
};
|
|
56
|
+
process.once('beforeExit', onSignal);
|
|
57
|
+
process.once('SIGINT', onSignal);
|
|
58
|
+
process.once('SIGTERM', onSignal);
|
|
59
|
+
// Prefer programmatic RuntimeConfig over ambient process.env.
|
|
60
|
+
const tenantConfig = tenantConfigFromRuntime(runtime.config);
|
|
61
|
+
return function netgreenerMiddleware(req, res, next) {
|
|
62
|
+
if (!runtime.enabled) {
|
|
63
|
+
next();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const entryTenant = resolveTenantFromHttpRequest(req, tenantConfig);
|
|
67
|
+
const method = (req.method || 'GET').toUpperCase();
|
|
68
|
+
const resolveServiceUnit = () => httpServiceUnit(method, req);
|
|
69
|
+
const handle = () => {
|
|
70
|
+
// Same mutable cell ``setTenantContext`` updates (manual auth binding).
|
|
71
|
+
const tenantStore = getTenantStore() ?? { current: entryTenant };
|
|
72
|
+
const started = process.hrtime.bigint();
|
|
73
|
+
const resourceMark = beginProcessResourceSample();
|
|
74
|
+
let recorded = false;
|
|
75
|
+
const finish = () => {
|
|
76
|
+
if (recorded)
|
|
77
|
+
return;
|
|
78
|
+
recorded = true;
|
|
79
|
+
const ended = process.hrtime.bigint();
|
|
80
|
+
const durationMs = Number(ended - started) / 1e6;
|
|
81
|
+
const resources = endProcessResourceSample(resourceMark);
|
|
82
|
+
const path = expressRouteTemplate(req);
|
|
83
|
+
const statusCode = typeof res.statusCode === 'number' ? res.statusCode : 200;
|
|
84
|
+
// finish/close may run outside the request ALS — re-enter the live store.
|
|
85
|
+
runWithTenantContext(tenantStore.current, () => {
|
|
86
|
+
runtime.recordHttp({
|
|
87
|
+
method,
|
|
88
|
+
path,
|
|
89
|
+
statusCode,
|
|
90
|
+
durationMs,
|
|
91
|
+
cpuTimeMs: resources.cpuTimeMs,
|
|
92
|
+
peakRssKb: resources.rssKb,
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
res.on('finish', finish);
|
|
97
|
+
res.on('close', finish);
|
|
98
|
+
try {
|
|
99
|
+
next();
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
// Do not record here: status may still change before finish/close.
|
|
103
|
+
// Preserve the original error without mutating response status.
|
|
104
|
+
throw err;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const withAttribution = () => runWithRequestAttribution({
|
|
108
|
+
resolveServiceUnit,
|
|
109
|
+
// Snapshot for workers / fallback; outbound captures at call entry.
|
|
110
|
+
serviceUnit: resolveServiceUnit(),
|
|
111
|
+
}, handle);
|
|
112
|
+
// Always enter a request-local tenant store (null clears inheritance).
|
|
113
|
+
runWithTenantContext(entryTenant, withAttribution);
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
export function getExpressRuntime() {
|
|
117
|
+
return getRuntime({ collector: 'express_middleware', framework: 'express' });
|
|
118
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outbound external-API call metering (N2).
|
|
3
|
+
*
|
|
4
|
+
* Additive ``external_api_v0`` for Node flushes — usage metadata only
|
|
5
|
+
* (no prompts/bodies). Soft-wraps:
|
|
6
|
+
* - global ``fetch`` (primary)
|
|
7
|
+
* - ``axios`` Axios.prototype.request when axios is resolvable (optional peer)
|
|
8
|
+
* - ``undici.request`` / Dispatcher.request when undici is resolvable
|
|
9
|
+
*
|
|
10
|
+
* Uploads restore unwrapped ``fetch``. Axios→fetch adapter paths skip the
|
|
11
|
+
* fetch meter via ALS so the same call is not double-counted.
|
|
12
|
+
*/
|
|
13
|
+
import type { ExternalApiV0 } from './types.js';
|
|
14
|
+
export declare function providerForHost(host: string): string;
|
|
15
|
+
export declare function hostFromUrl(url: unknown): string;
|
|
16
|
+
export declare function modelFromUrl(url: unknown): string | null;
|
|
17
|
+
export declare function tokensFromUsage(usage: Record<string, unknown>): [number, number];
|
|
18
|
+
export declare function usageFromSseText(text: string): {
|
|
19
|
+
input: number;
|
|
20
|
+
output: number;
|
|
21
|
+
model: string | null;
|
|
22
|
+
};
|
|
23
|
+
export declare function extractUsageFromResponse(response: Response, url: string): Promise<{
|
|
24
|
+
input: number;
|
|
25
|
+
output: number;
|
|
26
|
+
model: string | null;
|
|
27
|
+
}>;
|
|
28
|
+
/**
|
|
29
|
+
* Usage extraction for axios (and similar) bodies already buffered by the client.
|
|
30
|
+
* Does not read streams; returns zeros for stream/Buffer/unknown shapes.
|
|
31
|
+
*/
|
|
32
|
+
export declare function usageFromClientBody(data: unknown, url: string, contentType?: string | null): {
|
|
33
|
+
input: number;
|
|
34
|
+
output: number;
|
|
35
|
+
model: string | null;
|
|
36
|
+
};
|
|
37
|
+
export declare class ExternalApiAggregator {
|
|
38
|
+
private providers;
|
|
39
|
+
private byServiceUnit;
|
|
40
|
+
private byTenant;
|
|
41
|
+
private excludedHosts;
|
|
42
|
+
setExcludedHosts(hosts: Iterable<string>): void;
|
|
43
|
+
private isExcluded;
|
|
44
|
+
hasData(): boolean;
|
|
45
|
+
private recordProvider;
|
|
46
|
+
record(opts: {
|
|
47
|
+
host: string;
|
|
48
|
+
durationMs?: number;
|
|
49
|
+
error?: boolean;
|
|
50
|
+
inputTokens?: number;
|
|
51
|
+
outputTokens?: number;
|
|
52
|
+
serviceUnit?: string | null;
|
|
53
|
+
model?: string | null;
|
|
54
|
+
tenantId?: string | null;
|
|
55
|
+
}): void;
|
|
56
|
+
private providerRow;
|
|
57
|
+
buildV0(): ExternalApiV0 | null;
|
|
58
|
+
takeSnapshot(): ExternalApiAggregator;
|
|
59
|
+
private static mergeProvider;
|
|
60
|
+
mergeSnapshot(snapshot: ExternalApiAggregator): void;
|
|
61
|
+
clear(): void;
|
|
62
|
+
}
|
|
63
|
+
export declare function _awaitPendingUsageParses(): Promise<void>;
|
|
64
|
+
export declare function getExternalAggregator(): ExternalApiAggregator;
|
|
65
|
+
export declare function getOriginalFetch(): typeof fetch | null;
|
|
66
|
+
export declare function buildExternalV0(): ExternalApiV0 | null;
|
|
67
|
+
export declare function clearExternalAggregator(): void;
|
|
68
|
+
/** Test helper — reset install state. */
|
|
69
|
+
export declare function _resetOutboundInstrumentationForTests(): void;
|
|
70
|
+
export declare function installOutboundInstrumentation(opts?: {
|
|
71
|
+
excludedHosts?: Iterable<string>;
|
|
72
|
+
}): void;
|