@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
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://schemas.netgreener.com/contracts/resource/v1/resource-finding.schema.json",
|
|
4
|
+
"title": "NetGreener resource_finding_v1 (RD1 draft)",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"schema_version",
|
|
9
|
+
"finding_id",
|
|
10
|
+
"project_id",
|
|
11
|
+
"evidence_state",
|
|
12
|
+
"comparison_origin",
|
|
13
|
+
"lifecycle_status",
|
|
14
|
+
"verification_outcome",
|
|
15
|
+
"primary_domain",
|
|
16
|
+
"mechanism_id",
|
|
17
|
+
"title",
|
|
18
|
+
"scope",
|
|
19
|
+
"lineage"
|
|
20
|
+
],
|
|
21
|
+
"properties": {
|
|
22
|
+
"$schema": { "type": "string" },
|
|
23
|
+
"schema_version": { "const": "resource_finding_v1" },
|
|
24
|
+
"finding_id": { "type": "string", "minLength": 8, "maxLength": 128 },
|
|
25
|
+
"project_id": { "type": "string", "minLength": 1, "maxLength": 64 },
|
|
26
|
+
"evidence_state": {
|
|
27
|
+
"enum": ["candidate", "observed", "regression_supported", "causal_supported"]
|
|
28
|
+
},
|
|
29
|
+
"comparison_origin": {
|
|
30
|
+
"enum": ["none", "ci", "production", "benchmark", "manual_experiment"]
|
|
31
|
+
},
|
|
32
|
+
"lifecycle_status": {
|
|
33
|
+
"enum": ["pending_admission", "open", "acknowledged", "resolved", "dismissed", "reopened"]
|
|
34
|
+
},
|
|
35
|
+
"verification_outcome": {
|
|
36
|
+
"enum": [
|
|
37
|
+
"not_run",
|
|
38
|
+
"verified_improvement",
|
|
39
|
+
"no_material_change",
|
|
40
|
+
"regressed",
|
|
41
|
+
"inconclusive",
|
|
42
|
+
"not_applicable",
|
|
43
|
+
"failed_guardrail"
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"primary_domain": {
|
|
47
|
+
"enum": [
|
|
48
|
+
"compute.cpu",
|
|
49
|
+
"compute.gpu",
|
|
50
|
+
"memory.host",
|
|
51
|
+
"memory.gpu",
|
|
52
|
+
"storage.io",
|
|
53
|
+
"network.io",
|
|
54
|
+
"external_api"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"secondary_domains": {
|
|
58
|
+
"type": "array",
|
|
59
|
+
"uniqueItems": true,
|
|
60
|
+
"items": {
|
|
61
|
+
"enum": [
|
|
62
|
+
"compute.cpu",
|
|
63
|
+
"compute.gpu",
|
|
64
|
+
"memory.host",
|
|
65
|
+
"memory.gpu",
|
|
66
|
+
"storage.io",
|
|
67
|
+
"network.io",
|
|
68
|
+
"external_api"
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"mechanism_id": { "type": "string", "pattern": "^[a-z][a-z0-9_]{2,63}$" },
|
|
73
|
+
"title": { "type": "string", "minLength": 8, "maxLength": 200 },
|
|
74
|
+
"scope": { "$ref": "#/$defs/scope" },
|
|
75
|
+
"workload": { "$ref": "#/$defs/workload" },
|
|
76
|
+
"observed": { "$ref": "#/$defs/metricPoint" },
|
|
77
|
+
"baseline": { "$ref": "#/$defs/baseline" },
|
|
78
|
+
"impact": { "$ref": "#/$defs/impact" },
|
|
79
|
+
"materiality": { "$ref": "#/$defs/materiality" },
|
|
80
|
+
"evidence": {
|
|
81
|
+
"type": "array",
|
|
82
|
+
"items": { "$ref": "#/$defs/evidenceItem" }
|
|
83
|
+
},
|
|
84
|
+
"confidence": { "$ref": "#/$defs/confidence" },
|
|
85
|
+
"optimization": { "$ref": "#/$defs/optimization" },
|
|
86
|
+
"lineage": { "$ref": "#/$defs/lineage" },
|
|
87
|
+
"compatibility_fingerprint": { "$ref": "#/$defs/compatibilityFingerprint" }
|
|
88
|
+
},
|
|
89
|
+
"allOf": [
|
|
90
|
+
{
|
|
91
|
+
"if": { "properties": { "evidence_state": { "const": "candidate" } } },
|
|
92
|
+
"then": {
|
|
93
|
+
"properties": {
|
|
94
|
+
"comparison_origin": { "const": "none" },
|
|
95
|
+
"lifecycle_status": { "const": "pending_admission" },
|
|
96
|
+
"verification_outcome": { "const": "not_run" }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"if": {
|
|
102
|
+
"properties": {
|
|
103
|
+
"evidence_state": { "enum": ["regression_supported", "causal_supported"] }
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"then": {
|
|
107
|
+
"required": ["workload", "observed", "baseline", "impact", "materiality", "evidence", "confidence", "optimization"]
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"$defs": {
|
|
112
|
+
"scope": {
|
|
113
|
+
"type": "object",
|
|
114
|
+
"additionalProperties": false,
|
|
115
|
+
"required": ["kind", "id", "language"],
|
|
116
|
+
"properties": {
|
|
117
|
+
"kind": { "enum": ["service_unit", "task", "process", "ci_step", "script", "function"] },
|
|
118
|
+
"id": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
119
|
+
"language": { "type": "string", "pattern": "^[a-z][a-z0-9+-]{0,31}$" },
|
|
120
|
+
"runtime": { "type": "string", "maxLength": 80 },
|
|
121
|
+
"release_id": { "type": "string", "maxLength": 120 },
|
|
122
|
+
"git_sha": { "type": "string", "maxLength": 64 },
|
|
123
|
+
"source_locations": {
|
|
124
|
+
"type": "array",
|
|
125
|
+
"items": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"additionalProperties": false,
|
|
128
|
+
"required": ["path"],
|
|
129
|
+
"properties": {
|
|
130
|
+
"path": { "type": "string", "maxLength": 512 },
|
|
131
|
+
"start_line": { "type": "integer", "minimum": 1 },
|
|
132
|
+
"end_line": { "type": "integer", "minimum": 1 },
|
|
133
|
+
"symbol": { "type": "string", "maxLength": 120 }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"workload": {
|
|
140
|
+
"type": "object",
|
|
141
|
+
"additionalProperties": false,
|
|
142
|
+
"required": ["functional_unit"],
|
|
143
|
+
"properties": {
|
|
144
|
+
"functional_unit": { "type": "string", "minLength": 1, "maxLength": 80 },
|
|
145
|
+
"units": { "type": "number", "minimum": 0 },
|
|
146
|
+
"input_fingerprint": { "type": "string", "maxLength": 128 },
|
|
147
|
+
"quality_guards": {
|
|
148
|
+
"type": "array",
|
|
149
|
+
"items": {
|
|
150
|
+
"type": "object",
|
|
151
|
+
"additionalProperties": false,
|
|
152
|
+
"required": ["metric", "operator", "threshold", "passed"],
|
|
153
|
+
"properties": {
|
|
154
|
+
"metric": { "type": "string" },
|
|
155
|
+
"operator": { "enum": [">=", "<=", "=="] },
|
|
156
|
+
"threshold": { "type": "number" },
|
|
157
|
+
"observed": { "type": ["number", "null"] },
|
|
158
|
+
"passed": { "type": "boolean" }
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"metricPoint": {
|
|
165
|
+
"type": "object",
|
|
166
|
+
"additionalProperties": false,
|
|
167
|
+
"required": ["metric", "value", "unit"],
|
|
168
|
+
"properties": {
|
|
169
|
+
"metric": { "type": "string", "minLength": 1 },
|
|
170
|
+
"value": { "type": "number" },
|
|
171
|
+
"unit": { "type": "string", "minLength": 1 },
|
|
172
|
+
"window_start": { "type": "string", "format": "date-time" },
|
|
173
|
+
"window_end": { "type": "string", "format": "date-time" }
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
"baseline": {
|
|
177
|
+
"type": "object",
|
|
178
|
+
"additionalProperties": false,
|
|
179
|
+
"required": ["value", "unit", "sample_count", "cohort_key", "statistic"],
|
|
180
|
+
"properties": {
|
|
181
|
+
"value": { "type": "number" },
|
|
182
|
+
"unit": { "type": "string" },
|
|
183
|
+
"sample_count": { "type": "integer", "minimum": 1 },
|
|
184
|
+
"cohort_key": { "type": "string", "minLength": 8 },
|
|
185
|
+
"statistic": { "enum": ["median", "mean", "p50", "robust_mean"] },
|
|
186
|
+
"uncertainty": { "$ref": "#/$defs/uncertainty" }
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
"uncertainty": {
|
|
190
|
+
"type": "object",
|
|
191
|
+
"additionalProperties": false,
|
|
192
|
+
"required": ["method", "low", "high", "unit"],
|
|
193
|
+
"properties": {
|
|
194
|
+
"method": { "type": "string" },
|
|
195
|
+
"low": { "type": "number" },
|
|
196
|
+
"high": { "type": "number" },
|
|
197
|
+
"unit": { "type": "string" }
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
"impact": {
|
|
201
|
+
"type": "object",
|
|
202
|
+
"additionalProperties": false,
|
|
203
|
+
"required": ["impact_state"],
|
|
204
|
+
"properties": {
|
|
205
|
+
"normalized_delta": { "$ref": "#/$defs/metricPoint" },
|
|
206
|
+
"projected_excess": { "$ref": "#/$defs/metricPoint" },
|
|
207
|
+
"relative_delta_fraction": { "type": ["number", "null"] },
|
|
208
|
+
"recoverable_low": { "type": ["number", "null"] },
|
|
209
|
+
"recoverable_likely": { "type": ["number", "null"] },
|
|
210
|
+
"recoverable_high": { "type": ["number", "null"] },
|
|
211
|
+
"cost_usd": { "type": ["number", "null"] },
|
|
212
|
+
"energy_kwh": { "type": ["number", "null"] },
|
|
213
|
+
"impact_state": { "enum": ["unknown", "modeled", "measured"] }
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
"materiality": {
|
|
217
|
+
"type": "object",
|
|
218
|
+
"additionalProperties": false,
|
|
219
|
+
"required": ["policy_id", "decision"],
|
|
220
|
+
"properties": {
|
|
221
|
+
"policy_id": { "type": "string" },
|
|
222
|
+
"absolute_floor_passed": { "type": "boolean" },
|
|
223
|
+
"relative_floor_passed": { "type": "boolean" },
|
|
224
|
+
"sample_floor_passed": { "type": "boolean" },
|
|
225
|
+
"decision": { "enum": ["pending", "material", "not_material", "abstain"] }
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
"evidenceItem": {
|
|
229
|
+
"type": "object",
|
|
230
|
+
"additionalProperties": false,
|
|
231
|
+
"required": ["evidence_id", "kind", "captured_at", "value_state"],
|
|
232
|
+
"properties": {
|
|
233
|
+
"evidence_id": { "type": "string", "minLength": 8 },
|
|
234
|
+
"kind": {
|
|
235
|
+
"enum": [
|
|
236
|
+
"source",
|
|
237
|
+
"observation",
|
|
238
|
+
"profile",
|
|
239
|
+
"ci_comparison",
|
|
240
|
+
"runtime_baseline",
|
|
241
|
+
"provider_usage",
|
|
242
|
+
"billing",
|
|
243
|
+
"benchmark",
|
|
244
|
+
"user_disposition"
|
|
245
|
+
]
|
|
246
|
+
},
|
|
247
|
+
"captured_at": { "type": "string", "format": "date-time" },
|
|
248
|
+
"scope": {
|
|
249
|
+
"type": "object",
|
|
250
|
+
"additionalProperties": false,
|
|
251
|
+
"required": ["kind", "id"],
|
|
252
|
+
"properties": {
|
|
253
|
+
"kind": { "type": "string" },
|
|
254
|
+
"id": { "type": "string" }
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
"temporality": { "enum": ["delta", "cumulative", "instant"] },
|
|
258
|
+
"interval": {
|
|
259
|
+
"type": "object",
|
|
260
|
+
"additionalProperties": false,
|
|
261
|
+
"properties": {
|
|
262
|
+
"start": { "type": "string", "format": "date-time" },
|
|
263
|
+
"end": { "type": "string", "format": "date-time" }
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
"observation_ids": { "type": "array", "items": { "type": "string" } },
|
|
267
|
+
"metric": { "type": "string" },
|
|
268
|
+
"value": { "type": "number" },
|
|
269
|
+
"unit": { "type": "string" },
|
|
270
|
+
"aggregation": { "type": "string" },
|
|
271
|
+
"functional_unit": { "type": "string" },
|
|
272
|
+
"method": { "type": "string" },
|
|
273
|
+
"method_version": { "type": "string" },
|
|
274
|
+
"evidence_grade": { "type": "string" },
|
|
275
|
+
"coverage_fraction": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
276
|
+
"dropped_event_state": {
|
|
277
|
+
"type": "object",
|
|
278
|
+
"additionalProperties": false,
|
|
279
|
+
"required": ["dropped", "count"],
|
|
280
|
+
"properties": {
|
|
281
|
+
"dropped": { "type": "boolean" },
|
|
282
|
+
"count": { "type": "integer", "minimum": 0 }
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
"uncertainty": { "$ref": "#/$defs/uncertainty" },
|
|
286
|
+
"value_state": { "enum": ["observed", "estimated", "derived", "missing"] }
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
"confidence": {
|
|
290
|
+
"type": "object",
|
|
291
|
+
"additionalProperties": false,
|
|
292
|
+
"required": [
|
|
293
|
+
"detection_confidence",
|
|
294
|
+
"attribution_confidence",
|
|
295
|
+
"measurement_confidence",
|
|
296
|
+
"comparison_confidence",
|
|
297
|
+
"optimization_confidence",
|
|
298
|
+
"causal_confidence"
|
|
299
|
+
],
|
|
300
|
+
"properties": {
|
|
301
|
+
"detection_confidence": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
302
|
+
"attribution_confidence": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
303
|
+
"measurement_confidence": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
304
|
+
"comparison_confidence": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
305
|
+
"optimization_confidence": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
306
|
+
"causal_confidence": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
307
|
+
"reason_codes": { "type": "array", "items": { "type": "string" } }
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
"optimization": {
|
|
311
|
+
"type": "object",
|
|
312
|
+
"additionalProperties": false,
|
|
313
|
+
"required": ["hypothesis_id", "summary", "risk"],
|
|
314
|
+
"properties": {
|
|
315
|
+
"hypothesis_id": { "type": "string", "minLength": 4 },
|
|
316
|
+
"summary": { "type": "string", "minLength": 8, "maxLength": 400 },
|
|
317
|
+
"verification_plan": { "type": "object" },
|
|
318
|
+
"risk": { "enum": ["low", "medium", "high"] }
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
"lineage": {
|
|
322
|
+
"type": "object",
|
|
323
|
+
"additionalProperties": false,
|
|
324
|
+
"required": ["detector_id", "detector_version", "policy_version"],
|
|
325
|
+
"properties": {
|
|
326
|
+
"detector_id": { "type": "string", "minLength": 1 },
|
|
327
|
+
"detector_version": { "type": "string", "minLength": 1 },
|
|
328
|
+
"policy_version": { "type": "string", "minLength": 1 },
|
|
329
|
+
"observation_ids": { "type": "array", "items": { "type": "string" } },
|
|
330
|
+
"benchmark_result_ids": { "type": "array", "items": { "type": "string" } }
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
"compatibilityFingerprint": {
|
|
334
|
+
"type": "object",
|
|
335
|
+
"additionalProperties": false,
|
|
336
|
+
"properties": {
|
|
337
|
+
"language": { "type": "string" },
|
|
338
|
+
"runtime": { "type": "string" },
|
|
339
|
+
"framework": { "type": "string" },
|
|
340
|
+
"dependency_lock_digest": { "type": "string" },
|
|
341
|
+
"hardware_model": { "type": "string" },
|
|
342
|
+
"cpu_quota": { "type": "string" },
|
|
343
|
+
"memory_limit": { "type": "string" },
|
|
344
|
+
"workload_id": { "type": "string" },
|
|
345
|
+
"functional_unit": { "type": "string" },
|
|
346
|
+
"environment": { "type": "string" },
|
|
347
|
+
"release_id": { "type": "string" },
|
|
348
|
+
"ci_workflow_sha": { "type": "string" }
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MP4 Analyze scaffold — JS/TS service discovery (experimental).
|
|
3
|
+
*
|
|
4
|
+
* Twin of Python ``netgreener.service_discovery`` for Node ecosystems.
|
|
5
|
+
* Heuristic / regex extraction only (no TypeScript AST yet). Additive: does
|
|
6
|
+
* **not** wire into CLI upload, VSCE, or the RunSession pipeline.
|
|
7
|
+
*
|
|
8
|
+
* Canonical ``service_unit`` keys match runtime:
|
|
9
|
+
* - endpoints: ``"<METHOD> <path>"``
|
|
10
|
+
* - tasks: ``"task:<name>"``
|
|
11
|
+
*/
|
|
12
|
+
export type ServiceEndpointUnit = {
|
|
13
|
+
service_unit: string;
|
|
14
|
+
unit_type: 'endpoint';
|
|
15
|
+
method: string;
|
|
16
|
+
path: string;
|
|
17
|
+
handler: string;
|
|
18
|
+
file: string;
|
|
19
|
+
line: number;
|
|
20
|
+
end_line: number;
|
|
21
|
+
is_async: boolean;
|
|
22
|
+
dynamic: boolean;
|
|
23
|
+
prefix_resolved: boolean;
|
|
24
|
+
/** Heuristic extractor confidence — not measured runtime evidence. */
|
|
25
|
+
evidence: 'heuristic_source';
|
|
26
|
+
};
|
|
27
|
+
export type ServiceTaskUnit = {
|
|
28
|
+
service_unit: string;
|
|
29
|
+
unit_type: 'task';
|
|
30
|
+
name: string;
|
|
31
|
+
handler: string;
|
|
32
|
+
file: string;
|
|
33
|
+
line: number;
|
|
34
|
+
end_line: number;
|
|
35
|
+
evidence: 'heuristic_source';
|
|
36
|
+
};
|
|
37
|
+
export type ServiceFileExtraction = {
|
|
38
|
+
frameworks: string[];
|
|
39
|
+
endpoints: ServiceEndpointUnit[];
|
|
40
|
+
tasks: ServiceTaskUnit[];
|
|
41
|
+
};
|
|
42
|
+
export type ServiceManifestV0 = {
|
|
43
|
+
schema: 'service_manifest_v0';
|
|
44
|
+
contract_status: 'mp4_scaffold';
|
|
45
|
+
framework: string | null;
|
|
46
|
+
frameworks_detected: string[];
|
|
47
|
+
entry_files: string[];
|
|
48
|
+
endpoints: ServiceEndpointUnit[];
|
|
49
|
+
tasks: ServiceTaskUnit[];
|
|
50
|
+
stats: {
|
|
51
|
+
endpoints: number;
|
|
52
|
+
tasks: number;
|
|
53
|
+
files_with_units: number;
|
|
54
|
+
};
|
|
55
|
+
notes: string[];
|
|
56
|
+
};
|
|
57
|
+
/** Frameworks whose imports / requires appear in ``source``. */
|
|
58
|
+
export declare function detectFrameworks(source: string): string[];
|
|
59
|
+
/**
|
|
60
|
+
* Extract service units from one JS/TS source string.
|
|
61
|
+
* Never throws on bad source — returns empty collections.
|
|
62
|
+
*/
|
|
63
|
+
export declare function extractServiceUnits(sourceCode: string, relPath?: string): ServiceFileExtraction;
|
|
64
|
+
/** Project-level ``service_manifest_v0`` from ``{relPath: source}``. */
|
|
65
|
+
export declare function buildServiceManifest(fileSources: Record<string, string>): ServiceManifestV0;
|
|
66
|
+
export declare function isServiceProject(manifest: ServiceManifestV0): boolean;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MP4 Analyze scaffold — JS/TS service discovery (experimental).
|
|
3
|
+
*
|
|
4
|
+
* Twin of Python ``netgreener.service_discovery`` for Node ecosystems.
|
|
5
|
+
* Heuristic / regex extraction only (no TypeScript AST yet). Additive: does
|
|
6
|
+
* **not** wire into CLI upload, VSCE, or the RunSession pipeline.
|
|
7
|
+
*
|
|
8
|
+
* Canonical ``service_unit`` keys match runtime:
|
|
9
|
+
* - endpoints: ``"<METHOD> <path>"``
|
|
10
|
+
* - tasks: ``"task:<name>"``
|
|
11
|
+
*/
|
|
12
|
+
const FRAMEWORK_IMPORT_HINTS = [
|
|
13
|
+
{ id: 'express', pattern: /\bfrom\s+['"]express['"]|\brequire\(\s*['"]express['"]\s*\)/ },
|
|
14
|
+
{ id: 'fastify', pattern: /\bfrom\s+['"]fastify['"]|\brequire\(\s*['"]fastify['"]\s*\)/ },
|
|
15
|
+
{
|
|
16
|
+
id: 'nest',
|
|
17
|
+
pattern: /\bfrom\s+['"]@nestjs\/(common|core|platform-express|platform-fastify)['"]/,
|
|
18
|
+
},
|
|
19
|
+
{ id: 'bullmq', pattern: /\bfrom\s+['"]bullmq['"]|\brequire\(\s*['"]bullmq['"]\s*\)/ },
|
|
20
|
+
{ id: 'next', pattern: /\bfrom\s+['"]next(\/|['"])|\brequire\(\s*['"]next['"]\s*\)/ },
|
|
21
|
+
];
|
|
22
|
+
const HTTP_ROUTE = /\b(?:app|router|server|fastify|instance)\.(get|post|put|delete|patch|head|options)\(\s*['"`]([^'"`]+)['"`]/gi;
|
|
23
|
+
const NEST_MAPPING = /@(Get|Post|Put|Delete|Patch|Head|Options)\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g;
|
|
24
|
+
const BULLMQ_QUEUE_ADD = /\.add\(\s*['"`]([A-Za-z0-9_.:-]+)['"`]/g;
|
|
25
|
+
function lineOf(source, index) {
|
|
26
|
+
if (index <= 0)
|
|
27
|
+
return 1;
|
|
28
|
+
let line = 1;
|
|
29
|
+
for (let i = 0; i < index && i < source.length; i += 1) {
|
|
30
|
+
if (source.charCodeAt(i) === 10)
|
|
31
|
+
line += 1;
|
|
32
|
+
}
|
|
33
|
+
return line;
|
|
34
|
+
}
|
|
35
|
+
function looksDynamic(path) {
|
|
36
|
+
return path.includes(':') || path.includes('*') || path.includes('{');
|
|
37
|
+
}
|
|
38
|
+
function normalizePath(path) {
|
|
39
|
+
if (!path)
|
|
40
|
+
return '/';
|
|
41
|
+
return path.startsWith('/') ? path : `/${path}`;
|
|
42
|
+
}
|
|
43
|
+
/** Frameworks whose imports / requires appear in ``source``. */
|
|
44
|
+
export function detectFrameworks(source) {
|
|
45
|
+
const found = [];
|
|
46
|
+
for (const hint of FRAMEWORK_IMPORT_HINTS) {
|
|
47
|
+
if (hint.pattern.test(source) && !found.includes(hint.id)) {
|
|
48
|
+
found.push(hint.id);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return found;
|
|
52
|
+
}
|
|
53
|
+
function collectHttpRoutes(source, relPath) {
|
|
54
|
+
const endpoints = [];
|
|
55
|
+
const re = new RegExp(HTTP_ROUTE.source, HTTP_ROUTE.flags);
|
|
56
|
+
let match;
|
|
57
|
+
while ((match = re.exec(source)) !== null) {
|
|
58
|
+
const method = (match[1] || 'get').toUpperCase();
|
|
59
|
+
const path = normalizePath(match[2] || '/');
|
|
60
|
+
const start = lineOf(source, match.index);
|
|
61
|
+
endpoints.push({
|
|
62
|
+
service_unit: `${method} ${path}`,
|
|
63
|
+
unit_type: 'endpoint',
|
|
64
|
+
method,
|
|
65
|
+
path,
|
|
66
|
+
handler: '<heuristic>',
|
|
67
|
+
file: relPath,
|
|
68
|
+
line: start,
|
|
69
|
+
end_line: start,
|
|
70
|
+
is_async: false,
|
|
71
|
+
dynamic: looksDynamic(path),
|
|
72
|
+
prefix_resolved: false,
|
|
73
|
+
evidence: 'heuristic_source',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return endpoints;
|
|
77
|
+
}
|
|
78
|
+
function collectNestRoutes(source, relPath) {
|
|
79
|
+
const endpoints = [];
|
|
80
|
+
const re = new RegExp(NEST_MAPPING.source, NEST_MAPPING.flags);
|
|
81
|
+
let match;
|
|
82
|
+
while ((match = re.exec(source)) !== null) {
|
|
83
|
+
const method = (match[1] || 'Get').toUpperCase();
|
|
84
|
+
const pathRaw = match[2];
|
|
85
|
+
const path = pathRaw && pathRaw.length ? normalizePath(pathRaw) : '/';
|
|
86
|
+
const start = lineOf(source, match.index);
|
|
87
|
+
endpoints.push({
|
|
88
|
+
service_unit: `${method} ${path}`,
|
|
89
|
+
unit_type: 'endpoint',
|
|
90
|
+
method,
|
|
91
|
+
path,
|
|
92
|
+
handler: '<nest-heuristic>',
|
|
93
|
+
file: relPath,
|
|
94
|
+
line: start,
|
|
95
|
+
end_line: start,
|
|
96
|
+
is_async: false,
|
|
97
|
+
dynamic: looksDynamic(path),
|
|
98
|
+
prefix_resolved: false,
|
|
99
|
+
evidence: 'heuristic_source',
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return endpoints;
|
|
103
|
+
}
|
|
104
|
+
function collectBullMqTasks(source, relPath) {
|
|
105
|
+
const tasks = [];
|
|
106
|
+
const seen = new Set();
|
|
107
|
+
const re = new RegExp(BULLMQ_QUEUE_ADD.source, BULLMQ_QUEUE_ADD.flags);
|
|
108
|
+
let match;
|
|
109
|
+
while ((match = re.exec(source)) !== null) {
|
|
110
|
+
const name = String(match[1] || '').trim();
|
|
111
|
+
if (!name || seen.has(name))
|
|
112
|
+
continue;
|
|
113
|
+
seen.add(name);
|
|
114
|
+
const start = lineOf(source, match.index);
|
|
115
|
+
tasks.push({
|
|
116
|
+
service_unit: `task:${name}`,
|
|
117
|
+
unit_type: 'task',
|
|
118
|
+
name,
|
|
119
|
+
handler: '<bullmq-heuristic>',
|
|
120
|
+
file: relPath,
|
|
121
|
+
line: start,
|
|
122
|
+
end_line: start,
|
|
123
|
+
evidence: 'heuristic_source',
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return tasks;
|
|
127
|
+
}
|
|
128
|
+
function dedupeUnits(items) {
|
|
129
|
+
const seen = new Set();
|
|
130
|
+
const out = [];
|
|
131
|
+
for (const item of items) {
|
|
132
|
+
const key = `${item.service_unit}|${item.file}|${item.line}`;
|
|
133
|
+
if (seen.has(key))
|
|
134
|
+
continue;
|
|
135
|
+
seen.add(key);
|
|
136
|
+
out.push(item);
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Extract service units from one JS/TS source string.
|
|
142
|
+
* Never throws on bad source — returns empty collections.
|
|
143
|
+
*/
|
|
144
|
+
export function extractServiceUnits(sourceCode, relPath = '<unknown>') {
|
|
145
|
+
const source = String(sourceCode || '');
|
|
146
|
+
const frameworks = detectFrameworks(source);
|
|
147
|
+
const endpoints = [
|
|
148
|
+
...collectHttpRoutes(source, relPath),
|
|
149
|
+
...(frameworks.includes('nest') ? collectNestRoutes(source, relPath) : []),
|
|
150
|
+
];
|
|
151
|
+
const tasks = frameworks.includes('bullmq') || /\bbullmq\b/.test(source)
|
|
152
|
+
? collectBullMqTasks(source, relPath)
|
|
153
|
+
: [];
|
|
154
|
+
return {
|
|
155
|
+
frameworks,
|
|
156
|
+
endpoints: dedupeUnits(endpoints),
|
|
157
|
+
tasks: dedupeUnits(tasks),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/** Project-level ``service_manifest_v0`` from ``{relPath: source}``. */
|
|
161
|
+
export function buildServiceManifest(fileSources) {
|
|
162
|
+
const frameworksDetected = [];
|
|
163
|
+
const endpoints = [];
|
|
164
|
+
const tasks = [];
|
|
165
|
+
const entryFiles = new Set();
|
|
166
|
+
for (const [relPath, source] of Object.entries(fileSources)) {
|
|
167
|
+
const extracted = extractServiceUnits(source, relPath);
|
|
168
|
+
for (const fw of extracted.frameworks) {
|
|
169
|
+
if (!frameworksDetected.includes(fw))
|
|
170
|
+
frameworksDetected.push(fw);
|
|
171
|
+
}
|
|
172
|
+
if (extracted.endpoints.length || extracted.tasks.length) {
|
|
173
|
+
entryFiles.add(relPath);
|
|
174
|
+
}
|
|
175
|
+
endpoints.push(...extracted.endpoints);
|
|
176
|
+
tasks.push(...extracted.tasks);
|
|
177
|
+
}
|
|
178
|
+
let framework = null;
|
|
179
|
+
if (endpoints.length && frameworksDetected.includes('express'))
|
|
180
|
+
framework = 'express';
|
|
181
|
+
else if (endpoints.length && frameworksDetected.includes('fastify'))
|
|
182
|
+
framework = 'fastify';
|
|
183
|
+
else if (endpoints.length && frameworksDetected.includes('nest'))
|
|
184
|
+
framework = 'nest';
|
|
185
|
+
else if (tasks.length && frameworksDetected.includes('bullmq'))
|
|
186
|
+
framework = 'bullmq';
|
|
187
|
+
else if (frameworksDetected.length)
|
|
188
|
+
framework = frameworksDetected[0];
|
|
189
|
+
return {
|
|
190
|
+
schema: 'service_manifest_v0',
|
|
191
|
+
contract_status: 'mp4_scaffold',
|
|
192
|
+
framework,
|
|
193
|
+
frameworks_detected: frameworksDetected,
|
|
194
|
+
entry_files: [...entryFiles].sort(),
|
|
195
|
+
endpoints,
|
|
196
|
+
tasks,
|
|
197
|
+
stats: {
|
|
198
|
+
endpoints: endpoints.length,
|
|
199
|
+
tasks: tasks.length,
|
|
200
|
+
files_with_units: entryFiles.size,
|
|
201
|
+
},
|
|
202
|
+
notes: [
|
|
203
|
+
'MP4 scaffold: heuristic JS/TS discovery only — not TypeScript AST, not CLI/VSCE wired, not resource findings',
|
|
204
|
+
'Does not modify RunSession upload or runtime metering pipelines',
|
|
205
|
+
],
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
export function isServiceProject(manifest) {
|
|
209
|
+
return Boolean(manifest.stats.endpoints || manifest.stats.tasks);
|
|
210
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heuristic JS/TS twin of Python ``unbounded_asyncio_gather_with_io``.
|
|
3
|
+
*
|
|
4
|
+
* RD0 disposition → mechanism ``unbounded_parallelism`` / ``network.io``.
|
|
5
|
+
* Flags ``Promise.all(…map…fetch)`` style fan-out without an obvious concurrency cap.
|
|
6
|
+
*/
|
|
7
|
+
import { type ResourceCandidateHit } from './resourceFindingCandidate.js';
|
|
8
|
+
/**
|
|
9
|
+
* Detect unbounded Promise.all + API fan-out in one source file.
|
|
10
|
+
*/
|
|
11
|
+
export declare function detectUnboundedParallelismHits(source: string, relPath: string): ResourceCandidateHit[];
|