compare-laermwerte: Testprogramm zum Vergleich von Laermwerten zweier API-Instanzen
Ruft dieselben Parameter (sensorid/data/span/datetime/peak) gegen zwei "getsensordata"-Endpunkte ab und diffed die Werte zeitstempelweise, generisch über alle Felder (funktioniert für live genauso wie für havg/davg/daynight/lden). Diente als Verifikation der VictoriaMetrics-Anbindung: live-Werte stimmen exakt, havg-Werte liegen weit innerhalb der Toleranz gegenüber der Influx-Produktion. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
// compare.js - vergleicht die Laermwerte zweier "noise"-API-Instanzen
|
||||
// (z.B. lokaler VictoriaMetrics-Test-Stack vs. Produktion) fuer denselben
|
||||
// Sensor/Zeitraum/Auswertungstyp. Siehe Laerm_API.md fuer die API-Parameter.
|
||||
//
|
||||
// Aufruf: node compare.js [-s sensorid] [-d live|havg|davg|daynight|lden] ...
|
||||
// node compare.js -h fuer alle Optionen
|
||||
|
||||
import axios from 'axios'
|
||||
import { DateTime } from 'luxon'
|
||||
import mod_getopt from 'posix-getopt'
|
||||
|
||||
const DEFAULT_URL_A = 'http://localhost:3003/api/getsensordata'
|
||||
const DEFAULT_URL_B = 'https://noise.citysensor.de/api/getsensordata'
|
||||
const DEFAULT_SENSORID = '37833'
|
||||
const DEFAULT_DATA = 'live'
|
||||
const DEFAULT_SPAN = '1'
|
||||
const DEFAULT_PEAK = '70'
|
||||
const DEFAULT_TOLERANCE = 0.05
|
||||
const MAX_EXAMPLES = 20
|
||||
|
||||
function parseArgs(argv) {
|
||||
let opts = {
|
||||
urlA: DEFAULT_URL_A,
|
||||
urlB: DEFAULT_URL_B,
|
||||
sensorid: DEFAULT_SENSORID,
|
||||
data: DEFAULT_DATA,
|
||||
span: DEFAULT_SPAN,
|
||||
datetime: null,
|
||||
peak: DEFAULT_PEAK,
|
||||
tolerance: DEFAULT_TOLERANCE,
|
||||
}
|
||||
let parser = new mod_getopt.BasicParser(
|
||||
'a:(urlA)b:(urlB)s:(sensorid)d:(data)p:(span)t:(datetime)k:(peak)e:(tolerance)h(help)v(version)',
|
||||
argv
|
||||
)
|
||||
let option
|
||||
while ((option = parser.getopt()) !== undefined) {
|
||||
switch (option.option) {
|
||||
case 'a': opts.urlA = option.optarg; break
|
||||
case 'b': opts.urlB = option.optarg; break
|
||||
case 's': opts.sensorid = option.optarg; break
|
||||
case 'd': opts.data = option.optarg; break
|
||||
case 'p': opts.span = option.optarg; break
|
||||
case 't': opts.datetime = option.optarg; break
|
||||
case 'k': opts.peak = option.optarg; break
|
||||
case 'e': opts.tolerance = parseFloat(option.optarg); break
|
||||
case 'v':
|
||||
console.log('compare-laermwerte 1.0.0')
|
||||
process.exit()
|
||||
break
|
||||
case 'h':
|
||||
console.log('Usage: node compare.js [options]')
|
||||
console.log('Options:')
|
||||
console.log(` -a urlA Basis-URL Implementierung A (default: ${DEFAULT_URL_A})`)
|
||||
console.log(` -b urlB Basis-URL Implementierung B (default: ${DEFAULT_URL_B})`)
|
||||
console.log(` -s sensorid Sensor-ID (default: ${DEFAULT_SENSORID})`)
|
||||
console.log(` -d data live|havg|davg|daynight|lden (default: ${DEFAULT_DATA})`)
|
||||
console.log(` -p span Zeitspanne in Tagen (default: ${DEFAULT_SPAN})`)
|
||||
console.log(' -t datetime Start-Zeitpunkt ISO8601 (default: unbelegt -> jetzt - span)')
|
||||
console.log(` -k peak dB-Schwelle fuer peakcount, nur havg/davg (default: ${DEFAULT_PEAK})`)
|
||||
console.log(` -e tolerance Toleranz fuer Zahlenvergleich, absolut (default: ${DEFAULT_TOLERANCE})`)
|
||||
console.log(' -v Version anzeigen')
|
||||
console.log(' -h diese Hilfe')
|
||||
process.exit()
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
function buildURL(base, opts) {
|
||||
let params = {
|
||||
sensorid: opts.sensorid,
|
||||
data: opts.data,
|
||||
span: opts.span,
|
||||
peak: opts.peak,
|
||||
}
|
||||
if (opts.datetime) {
|
||||
params.datetime = opts.datetime
|
||||
}
|
||||
let qs = new URLSearchParams(params).toString()
|
||||
return `${base}?${qs}`
|
||||
}
|
||||
|
||||
async function fetchSide(label, url) {
|
||||
try {
|
||||
let ret = await axios.get(url, { timeout: 15000 })
|
||||
if (ret.data && ret.data.err) {
|
||||
return { ok: false, err: `${label}: API-Fehler: ${ret.data.err}` }
|
||||
}
|
||||
return { ok: true, values: ret.data.values || [], options: ret.data.options }
|
||||
} catch (e) {
|
||||
let msg = e.response ? `HTTP ${e.response.status}` : e.message
|
||||
return { ok: false, err: `${label}: Request fehlgeschlagen: ${msg}` }
|
||||
}
|
||||
}
|
||||
|
||||
function toMap(values) {
|
||||
let map = new Map()
|
||||
for (let row of values) {
|
||||
let ms = DateTime.fromISO(row.datetime, { zone: 'utc' }).toMillis()
|
||||
if (Number.isNaN(ms)) {
|
||||
continue
|
||||
}
|
||||
map.set(ms, row)
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
function diffRows(mapA, mapB, tolerance) {
|
||||
let onlyA = []
|
||||
let onlyB = []
|
||||
let commonCount = 0
|
||||
let fieldStats = new Map() // field -> {count, sumDiff, maxDiff, mismatchCount}
|
||||
let examples = []
|
||||
|
||||
for (let ts of mapA.keys()) {
|
||||
if (!mapB.has(ts)) {
|
||||
onlyA.push(ts)
|
||||
}
|
||||
}
|
||||
for (let ts of mapB.keys()) {
|
||||
if (!mapA.has(ts)) {
|
||||
onlyB.push(ts)
|
||||
}
|
||||
}
|
||||
|
||||
for (let [ts, rowA] of mapA) {
|
||||
let rowB = mapB.get(ts)
|
||||
if (!rowB) {
|
||||
continue
|
||||
}
|
||||
commonCount++
|
||||
let fields = new Set([...Object.keys(rowA), ...Object.keys(rowB)])
|
||||
fields.delete('datetime')
|
||||
for (let field of fields) {
|
||||
let a = (field in rowA) ? rowA[field] : null
|
||||
let b = (field in rowB) ? rowB[field] : null
|
||||
if (a === null && b === null) {
|
||||
continue
|
||||
}
|
||||
let stat = fieldStats.get(field)
|
||||
if (!stat) {
|
||||
stat = { count: 0, sumDiff: 0, maxDiff: 0, mismatchCount: 0 }
|
||||
fieldStats.set(field, stat)
|
||||
}
|
||||
stat.count++
|
||||
if (typeof a !== 'number' || typeof b !== 'number') {
|
||||
stat.mismatchCount++
|
||||
if (examples.length < MAX_EXAMPLES) {
|
||||
examples.push({ ts, field, a, b, reason: 'null-mismatch' })
|
||||
}
|
||||
continue
|
||||
}
|
||||
let diff = Math.abs(a - b)
|
||||
stat.sumDiff += diff
|
||||
if (diff > stat.maxDiff) {
|
||||
stat.maxDiff = diff
|
||||
}
|
||||
if (diff > tolerance) {
|
||||
stat.mismatchCount++
|
||||
if (examples.length < MAX_EXAMPLES) {
|
||||
examples.push({ ts, field, a, b, diff })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { onlyA, onlyB, commonCount, fieldStats, examples }
|
||||
}
|
||||
|
||||
function fmtTs(ms) {
|
||||
return DateTime.fromMillis(ms, { zone: 'utc' }).toISO()
|
||||
}
|
||||
|
||||
function printSummary(opts, sideA, sideB, diff) {
|
||||
console.log('=== Vergleich Laermwerte ===')
|
||||
console.log(`Sensor: ${opts.sensorid} data: ${opts.data} span: ${opts.span}${opts.datetime ? ` datetime: ${opts.datetime}` : ''}`)
|
||||
console.log(`A: ${sideA.count} Werte`)
|
||||
console.log(`B: ${sideB.count} Werte`)
|
||||
console.log(`Gemeinsame Zeitstempel: ${diff.commonCount}`)
|
||||
console.log(`Nur in A: ${diff.onlyA.length}${diff.onlyA.length ? ` (z.B. ${diff.onlyA.slice(0, 3).map(fmtTs).join(', ')})` : ''}`)
|
||||
console.log(`Nur in B: ${diff.onlyB.length}${diff.onlyB.length ? ` (z.B. ${diff.onlyB.slice(0, 3).map(fmtTs).join(', ')})` : ''}`)
|
||||
console.log('')
|
||||
console.log('Felder (bei gemeinsamen Zeitstempeln):')
|
||||
let totalMismatches = 0
|
||||
for (let [field, stat] of diff.fieldStats) {
|
||||
let avg = stat.count ? stat.sumDiff / stat.count : 0
|
||||
totalMismatches += stat.mismatchCount
|
||||
console.log(` ${field.padEnd(14)} verglichen: ${String(stat.count).padStart(5)} max diff: ${stat.maxDiff.toFixed(4).padStart(10)} avg diff: ${avg.toFixed(4).padStart(10)} mismatches (>${opts.tolerance}): ${stat.mismatchCount}`)
|
||||
}
|
||||
if (diff.examples.length > 0) {
|
||||
console.log('')
|
||||
console.log(`Beispiel-Mismatches (max ${MAX_EXAMPLES}):`)
|
||||
for (let ex of diff.examples) {
|
||||
if (ex.reason === 'null-mismatch') {
|
||||
console.log(` ${fmtTs(ex.ts)} ${ex.field}: A=${ex.a} B=${ex.b}`)
|
||||
} else {
|
||||
console.log(` ${fmtTs(ex.ts)} ${ex.field}: A=${ex.a} B=${ex.b} diff=${ex.diff.toFixed(4)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log('')
|
||||
if (totalMismatches > 0) {
|
||||
console.log(`FEHLGESCHLAGEN: ${totalMismatches} Feld-Mismatches ausserhalb der Toleranz gefunden.`)
|
||||
} else {
|
||||
console.log('OK: Keine Mismatches ausserhalb der Toleranz bei gemeinsamen Zeitstempeln.')
|
||||
}
|
||||
return totalMismatches
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let opts = parseArgs(process.argv)
|
||||
let urlA = buildURL(opts.urlA, opts)
|
||||
let urlB = buildURL(opts.urlB, opts)
|
||||
console.log(`A: ${urlA}`)
|
||||
console.log(`B: ${urlB}`)
|
||||
console.log('')
|
||||
|
||||
let [sideA, sideB] = await Promise.all([
|
||||
fetchSide('A', urlA),
|
||||
fetchSide('B', urlB),
|
||||
])
|
||||
|
||||
if (!sideA.ok || !sideB.ok) {
|
||||
if (!sideA.ok) console.error(sideA.err)
|
||||
if (!sideB.ok) console.error(sideB.err)
|
||||
process.exitCode = 2
|
||||
return
|
||||
}
|
||||
|
||||
let mapA = toMap(sideA.values)
|
||||
let mapB = toMap(sideB.values)
|
||||
let diff = diffRows(mapA, mapB, opts.tolerance)
|
||||
let mismatches = printSummary(
|
||||
opts,
|
||||
{ count: sideA.values.length },
|
||||
{ count: sideB.values.length },
|
||||
diff
|
||||
)
|
||||
process.exitCode = mismatches > 0 ? 1 : 0
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error(e)
|
||||
process.exitCode = 2
|
||||
})
|
||||
Generated
+366
@@ -0,0 +1,366 @@
|
||||
{
|
||||
"name": "compare-laermwerte",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "compare-laermwerte",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"axios": "^1.12.0",
|
||||
"luxon": "^3.3.0",
|
||||
"posix-getopt": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz",
|
||||
"integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.16.0",
|
||||
"form-data": "^4.0.6",
|
||||
"https-proxy-agent": "^5.0.1",
|
||||
"proxy-from-env": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
|
||||
"integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-set-tostringtag": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
|
||||
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
|
||||
"integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.4",
|
||||
"mime-types": "^2.1.35"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
|
||||
"integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "6",
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/luxon": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz",
|
||||
"integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/posix-getopt": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/posix-getopt/-/posix-getopt-1.2.1.tgz",
|
||||
"integrity": "sha512-BbGTiH8MOWAuc6h5yITkSn9k3HP4+QOCV9t6I5F62OrH7zqTHRo08QNsgELRreTBxcvRhbSpMoUnAx77Dz4yUA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
|
||||
"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "compare-laermwerte",
|
||||
"version": "1.0.0",
|
||||
"description": "Vergleicht Laermwerte zweier API-Instanzen (z.B. lokaler Victoria-Test-Stack vs. Produktion)",
|
||||
"type": "module",
|
||||
"main": "compare.js",
|
||||
"scripts": {
|
||||
"start": "node compare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.12.0",
|
||||
"luxon": "^3.3.0",
|
||||
"posix-getopt": "^1.2.1"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user