@openmixer/plugin-qualify 0.0.0-stage → 0.3.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.
Files changed (114) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +203 -2
  3. package/bin/plugin-qualify.mjs +77 -0
  4. package/dist/clap-loader.d.ts +147 -0
  5. package/dist/clap-loader.js +252 -0
  6. package/dist/clap-loader.js.map +1 -0
  7. package/dist/clap-qualify.d.ts +126 -0
  8. package/dist/clap-qualify.js +272 -0
  9. package/dist/clap-qualify.js.map +1 -0
  10. package/dist/host-profiles.d.ts +42 -0
  11. package/dist/host-profiles.js +106 -0
  12. package/dist/host-profiles.js.map +1 -0
  13. package/dist/hosting-codes.d.ts +62 -0
  14. package/dist/hosting-codes.js +72 -0
  15. package/dist/hosting-codes.js.map +1 -0
  16. package/dist/hosting-suitability.d.ts +354 -0
  17. package/dist/hosting-suitability.js +654 -0
  18. package/dist/hosting-suitability.js.map +1 -0
  19. package/dist/index.d.ts +17 -0
  20. package/dist/index.js +20 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/loader.d.ts +26 -0
  23. package/dist/loader.js +9 -0
  24. package/dist/loader.js.map +1 -0
  25. package/dist/lv2-loader.d.ts +7 -0
  26. package/dist/lv2-loader.js +50 -0
  27. package/dist/lv2-loader.js.map +1 -0
  28. package/dist/measurement/cli.d.ts +168 -0
  29. package/dist/measurement/cli.js +353 -0
  30. package/dist/measurement/cli.js.map +1 -0
  31. package/dist/measurement/fixtures.d.ts +23 -0
  32. package/dist/measurement/fixtures.js +129 -0
  33. package/dist/measurement/fixtures.js.map +1 -0
  34. package/dist/measurement/format.d.ts +483 -0
  35. package/dist/measurement/format.js +627 -0
  36. package/dist/measurement/format.js.map +1 -0
  37. package/dist/measurement/guards.d.ts +33 -0
  38. package/dist/measurement/guards.js +61 -0
  39. package/dist/measurement/guards.js.map +1 -0
  40. package/dist/measurement/index.d.ts +36 -0
  41. package/dist/measurement/index.js +39 -0
  42. package/dist/measurement/index.js.map +1 -0
  43. package/dist/measurement/merge.d.ts +143 -0
  44. package/dist/measurement/merge.js +315 -0
  45. package/dist/measurement/merge.js.map +1 -0
  46. package/dist/measurement/plan.d.ts +127 -0
  47. package/dist/measurement/plan.js +141 -0
  48. package/dist/measurement/plan.js.map +1 -0
  49. package/dist/measurement/probe-json.d.ts +61 -0
  50. package/dist/measurement/probe-json.js +253 -0
  51. package/dist/measurement/probe-json.js.map +1 -0
  52. package/dist/measurement/provenance.d.ts +91 -0
  53. package/dist/measurement/provenance.js +173 -0
  54. package/dist/measurement/provenance.js.map +1 -0
  55. package/dist/measurement/sweep-jsonl.d.ts +40 -0
  56. package/dist/measurement/sweep-jsonl.js +154 -0
  57. package/dist/measurement/sweep-jsonl.js.map +1 -0
  58. package/dist/measurement/vocabulary.d.ts +125 -0
  59. package/dist/measurement/vocabulary.js +217 -0
  60. package/dist/measurement/vocabulary.js.map +1 -0
  61. package/dist/qualify-cli.d.ts +83 -0
  62. package/dist/qualify-cli.js +224 -0
  63. package/dist/qualify-cli.js.map +1 -0
  64. package/dist/qualify-declarations.d.ts +98 -0
  65. package/dist/qualify-declarations.js +76 -0
  66. package/dist/qualify-declarations.js.map +1 -0
  67. package/dist/qualify-options.d.ts +22 -0
  68. package/dist/qualify-options.js +36 -0
  69. package/dist/qualify-options.js.map +1 -0
  70. package/dist/rating.d.ts +32 -0
  71. package/dist/rating.js +30 -0
  72. package/dist/rating.js.map +1 -0
  73. package/dist/tool-paths.d.ts +12 -0
  74. package/dist/tool-paths.js +29 -0
  75. package/dist/tool-paths.js.map +1 -0
  76. package/fixtures/clap-scan/controls.json +8 -0
  77. package/fixtures/clap-scan/fake.json +16 -0
  78. package/fixtures/clap-scan/mixed.json +18 -0
  79. package/fixtures/zero-path.lv2/manifest.ttl +3 -0
  80. package/fixtures/zero-path.lv2/zero-path.c +85 -0
  81. package/fixtures/zero-path.lv2/zero-path.ttl +21 -0
  82. package/package.json +91 -3
  83. package/src/clap-loader.ts +393 -0
  84. package/src/clap-qualify.ts +387 -0
  85. package/src/host-profiles.ts +122 -0
  86. package/src/hosting-codes.ts +71 -0
  87. package/src/hosting-suitability.ts +982 -0
  88. package/src/index.ts +19 -0
  89. package/src/loader.ts +30 -0
  90. package/src/lv2-loader.ts +50 -0
  91. package/src/measurement/cli.ts +458 -0
  92. package/src/measurement/fixtures.ts +141 -0
  93. package/src/measurement/format.ts +1064 -0
  94. package/src/measurement/guards.ts +64 -0
  95. package/src/measurement/index.ts +39 -0
  96. package/src/measurement/merge.ts +434 -0
  97. package/src/measurement/plan.ts +219 -0
  98. package/src/measurement/probe-json.ts +271 -0
  99. package/src/measurement/provenance.ts +220 -0
  100. package/src/measurement/sweep-jsonl.ts +173 -0
  101. package/src/measurement/vocabulary.ts +298 -0
  102. package/src/qualify-cli.ts +277 -0
  103. package/src/qualify-declarations.ts +117 -0
  104. package/src/qualify-options.ts +49 -0
  105. package/src/rating.ts +53 -0
  106. package/src/tool-paths.ts +32 -0
  107. package/tools/_declarations.py +21 -0
  108. package/tools/benchmark.py +2462 -0
  109. package/tools/gen-python-declarations.d.mts +4 -0
  110. package/tools/gen-python-declarations.mjs +92 -0
  111. package/tools/hosting-sweep.mjs +51 -0
  112. package/tools/lv2-measure.mjs +204 -0
  113. package/tools/lv2_presets.py +132 -0
  114. package/tools/scan.py +432 -0
@@ -0,0 +1,127 @@
1
+ /**
2
+ * What a run is going to do, decided before it starts.
3
+ *
4
+ * Two questions, answered in the same breath: *"Measure at least the rates this
5
+ * installation can actually use … but it must not silently fall back to a rate it does not
6
+ * use"*, and *"show progress"* — which needs a duration, which nobody can honestly produce
7
+ * for a machine they have never seen.
8
+ *
9
+ * ## Rates are data, not a constant
10
+ *
11
+ * There is no default rate list in this module and there must never be one. A console that
12
+ * runs 48 k and 96 k does not need 192 k figures; a console whose driver only offers 44.1 k
13
+ * must not be handed a 48 k measurement wearing its label. The plan is derived from what the
14
+ * installation reports — its live rate and its device's supported list — and a request for a
15
+ * rate the hardware does not offer is DROPPED and reported, never honoured.
16
+ *
17
+ * A plan with no rates is a legitimate outcome: it means nobody has told us what this
18
+ * installation runs at, and the correct behaviour is to refuse and say so rather than pick
19
+ * 48 000 because it is a common number.
20
+ *
21
+ * ## Duration is an estimate or it is nothing
22
+ *
23
+ * {@link estimateRunSeconds} scales a REFERENCE run's measured wall time by the work the new
24
+ * plan asks for. With no reference run — or a reference that never recorded its own duration
25
+ * — it returns `undefined`. There is no fallback constant: "about 15 minutes" measured on a
26
+ * Core Ultra 9 is not an estimate for a NUC, and a wrong progress estimate is how an operator
27
+ * ends up cancelling a run that was nearly done.
28
+ */
29
+ import { type MeasurementDimension } from "./vocabulary.js";
30
+ import type { MeasurementDocument, MeasurementRun } from "./format.js";
31
+ /** What the installation knows about its own clock, as far as the plan is concerned. */
32
+ export interface InstallationRates {
33
+ /** The rate the graph is running at right now, when the console knows it. */
34
+ readonly liveRate?: number;
35
+ /** Rates the active device actually offers. Empty/absent = the console has not probed. */
36
+ readonly supportedRates?: readonly number[];
37
+ /** Rates the operator asked for. Absent = "whatever this installation can use". */
38
+ readonly requestedRates?: readonly number[];
39
+ }
40
+ /** The rates a run will measure at, and what was left out on the way there. */
41
+ export interface RatePlan {
42
+ /** Rates to measure, ascending. Empty when nothing could be resolved. */
43
+ readonly rates: readonly number[];
44
+ /**
45
+ * Rates the operator asked for that the installation does not offer. Reported, not
46
+ * measured — a figure at a rate the rig cannot run is a figure nobody will ever use, and
47
+ * quietly measuring it would make the run longer for nothing.
48
+ */
49
+ readonly dropped: readonly number[];
50
+ /**
51
+ * Set when {@link rates} is empty: `no-rates-known`. The console could name neither a
52
+ * live rate nor a supported list, so there is nothing to measure and no safe guess.
53
+ */
54
+ readonly problem?: string;
55
+ }
56
+ /**
57
+ * Resolve the rates a local run should measure at.
58
+ *
59
+ * The live rate is always included when known — a console must have figures for the rate it
60
+ * is running at, whatever else the operator asked for. Beyond that: the requested rates
61
+ * intersected with what the device offers, or, absent a request, the whole supported list.
62
+ */
63
+ export declare function ratesForInstallation(input: InstallationRates): RatePlan;
64
+ /** A run, fully specified, before anything is probed. */
65
+ export interface MeasurementPlan {
66
+ readonly rates: readonly number[];
67
+ readonly dimensions: readonly MeasurementDimension[];
68
+ /** Rates asked for and dropped as unusable here — carried through so the UI can say so. */
69
+ readonly droppedRates: readonly number[];
70
+ /** How many plugins the run will visit, when a scan has already told us. */
71
+ readonly pluginCount?: number;
72
+ /** Set when the plan cannot run: `no-rates-known` / `no-dimensions`. */
73
+ readonly problem?: string;
74
+ }
75
+ /** Build a plan from resolved rates and the dimensions the operator chose. */
76
+ export declare function measurementPlan(ratePlan: RatePlan, dimensions: readonly MeasurementDimension[], pluginCount?: number): MeasurementPlan;
77
+ /** True when a plan has enough to run: at least one rate and at least one dimension. */
78
+ export declare function isRunnablePlan(plan: MeasurementPlan): boolean;
79
+ /**
80
+ * The unit of work a plan represents: plugins × rates × dimensions.
81
+ *
82
+ * Crude on purpose. A convolution reverb costs more than a gain, but the prober caps each
83
+ * plugin's wall time, so over a whole catalogue the product is close enough to scale a
84
+ * measured duration by — and a more elaborate model would imply a precision the estimate
85
+ * does not have.
86
+ */
87
+ export declare function planWorkUnits(plan: MeasurementPlan): number | undefined;
88
+ /** A duration estimate, with the run it was extrapolated from named. */
89
+ export interface RunEstimate {
90
+ readonly seconds: number;
91
+ /** The reference run's id, host and duration — everything needed to render "on <host>,
92
+ * <n> plugins took <t>", so the operator can judge the extrapolation themselves. */
93
+ readonly basis: {
94
+ readonly runId: string;
95
+ readonly host: string;
96
+ readonly seconds: number;
97
+ readonly workUnits: number;
98
+ };
99
+ }
100
+ /**
101
+ * How long this plan is likely to take, extrapolated from a run that actually happened.
102
+ *
103
+ * `undefined` whenever it cannot be derived — no reference document, the reference never
104
+ * recorded `elapsedSeconds`, or the plan does not yet know its plugin count. An unknown
105
+ * duration is reported as unknown; there is no constant to fall back on and inventing one
106
+ * would be the exact dishonesty the provenance discipline exists to prevent.
107
+ *
108
+ * When the reference ran on ANOTHER machine the estimate is still only an order of
109
+ * magnitude — which is why {@link RunEstimate.basis} names the host rather than hiding it.
110
+ */
111
+ export declare function estimateRunSeconds(plan: MeasurementPlan, reference: MeasurementDocument | undefined): RunEstimate | undefined;
112
+ /**
113
+ * Rates a completed run covers that the installation no longer cares about, and rates the
114
+ * installation now uses that the run never covered.
115
+ *
116
+ * The second list is the rate-change case: the operator moved the rig to 96 kHz, so the
117
+ * figures for 96 kHz are MISSING rather than wrong, and the console should offer to measure
118
+ * them instead of interpolating across a boundary 19 nonconforming plugins prove cannot be
119
+ * interpolated across.
120
+ */
121
+ export interface RateCoverage {
122
+ readonly covered: readonly number[];
123
+ readonly missing: readonly number[];
124
+ readonly extra: readonly number[];
125
+ }
126
+ /** Compare a run's rates against the rates an installation now uses. */
127
+ export declare function rateCoverage(run: MeasurementRun | undefined, needed: readonly number[]): RateCoverage;
@@ -0,0 +1,141 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ // Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
3
+ /**
4
+ * What a run is going to do, decided before it starts.
5
+ *
6
+ * Two questions, answered in the same breath: *"Measure at least the rates this
7
+ * installation can actually use … but it must not silently fall back to a rate it does not
8
+ * use"*, and *"show progress"* — which needs a duration, which nobody can honestly produce
9
+ * for a machine they have never seen.
10
+ *
11
+ * ## Rates are data, not a constant
12
+ *
13
+ * There is no default rate list in this module and there must never be one. A console that
14
+ * runs 48 k and 96 k does not need 192 k figures; a console whose driver only offers 44.1 k
15
+ * must not be handed a 48 k measurement wearing its label. The plan is derived from what the
16
+ * installation reports — its live rate and its device's supported list — and a request for a
17
+ * rate the hardware does not offer is DROPPED and reported, never honoured.
18
+ *
19
+ * A plan with no rates is a legitimate outcome: it means nobody has told us what this
20
+ * installation runs at, and the correct behaviour is to refuse and say so rather than pick
21
+ * 48 000 because it is a common number.
22
+ *
23
+ * ## Duration is an estimate or it is nothing
24
+ *
25
+ * {@link estimateRunSeconds} scales a REFERENCE run's measured wall time by the work the new
26
+ * plan asks for. With no reference run — or a reference that never recorded its own duration
27
+ * — it returns `undefined`. There is no fallback constant: "about 15 minutes" measured on a
28
+ * Core Ultra 9 is not an estimate for a NUC, and a wrong progress estimate is how an operator
29
+ * ends up cancelling a run that was nearly done.
30
+ */
31
+ import { MEASUREMENT_DIMENSIONS } from "./vocabulary.js";
32
+ /**
33
+ * Resolve the rates a local run should measure at.
34
+ *
35
+ * The live rate is always included when known — a console must have figures for the rate it
36
+ * is running at, whatever else the operator asked for. Beyond that: the requested rates
37
+ * intersected with what the device offers, or, absent a request, the whole supported list.
38
+ */
39
+ export function ratesForInstallation(input) {
40
+ const supported = [...new Set((input.supportedRates ?? []).filter((r) => Number.isFinite(r) && r > 0))];
41
+ const live = input.liveRate !== undefined && Number.isFinite(input.liveRate) && input.liveRate > 0
42
+ ? input.liveRate
43
+ : undefined;
44
+ // What this installation can actually use: the device list, plus the rate it is demonstrably
45
+ // running at (a live rate absent from the probed list is the device telling us the list is
46
+ // incomplete, not the other way round).
47
+ const usable = new Set(supported);
48
+ if (live !== undefined)
49
+ usable.add(live);
50
+ const requested = input.requestedRates?.filter((r) => Number.isFinite(r) && r > 0);
51
+ if (requested === undefined || requested.length === 0) {
52
+ const rates = [...usable].sort((a, b) => a - b);
53
+ return rates.length > 0
54
+ ? { rates, dropped: [] }
55
+ : { rates: [], dropped: [], problem: "no-rates-known" };
56
+ }
57
+ const wanted = new Set(requested);
58
+ if (live !== undefined)
59
+ wanted.add(live);
60
+ const rates = [...wanted].filter((r) => usable.has(r)).sort((a, b) => a - b);
61
+ const dropped = [...wanted].filter((r) => !usable.has(r)).sort((a, b) => a - b);
62
+ return rates.length > 0
63
+ ? { rates, dropped }
64
+ : { rates: [], dropped, problem: "no-rates-known" };
65
+ }
66
+ /** Build a plan from resolved rates and the dimensions the operator chose. */
67
+ export function measurementPlan(ratePlan, dimensions, pluginCount) {
68
+ const wanted = MEASUREMENT_DIMENSIONS.filter((d) => dimensions.includes(d));
69
+ const problem = ratePlan.problem ?? (wanted.length === 0 ? "no-dimensions" : undefined);
70
+ return {
71
+ rates: ratePlan.rates,
72
+ dimensions: wanted,
73
+ droppedRates: ratePlan.dropped,
74
+ ...(pluginCount === undefined ? {} : { pluginCount }),
75
+ ...(problem === undefined ? {} : { problem }),
76
+ };
77
+ }
78
+ /** True when a plan has enough to run: at least one rate and at least one dimension. */
79
+ export function isRunnablePlan(plan) {
80
+ return plan.problem === undefined && plan.rates.length > 0 && plan.dimensions.length > 0;
81
+ }
82
+ /**
83
+ * The unit of work a plan represents: plugins × rates × dimensions.
84
+ *
85
+ * Crude on purpose. A convolution reverb costs more than a gain, but the prober caps each
86
+ * plugin's wall time, so over a whole catalogue the product is close enough to scale a
87
+ * measured duration by — and a more elaborate model would imply a precision the estimate
88
+ * does not have.
89
+ */
90
+ export function planWorkUnits(plan) {
91
+ if (plan.pluginCount === undefined)
92
+ return undefined;
93
+ return plan.pluginCount * plan.rates.length * plan.dimensions.length;
94
+ }
95
+ /** The work a completed run represents, from its own document. */
96
+ function runWorkUnits(document) {
97
+ const units = document.plugins.length * document.run.rates.length * document.run.dimensions.length;
98
+ return units > 0 ? units : undefined;
99
+ }
100
+ /**
101
+ * How long this plan is likely to take, extrapolated from a run that actually happened.
102
+ *
103
+ * `undefined` whenever it cannot be derived — no reference document, the reference never
104
+ * recorded `elapsedSeconds`, or the plan does not yet know its plugin count. An unknown
105
+ * duration is reported as unknown; there is no constant to fall back on and inventing one
106
+ * would be the exact dishonesty the provenance discipline exists to prevent.
107
+ *
108
+ * When the reference ran on ANOTHER machine the estimate is still only an order of
109
+ * magnitude — which is why {@link RunEstimate.basis} names the host rather than hiding it.
110
+ */
111
+ export function estimateRunSeconds(plan, reference) {
112
+ if (reference === undefined)
113
+ return undefined;
114
+ const elapsed = reference.run.elapsedSeconds;
115
+ if (elapsed === undefined || !(elapsed > 0))
116
+ return undefined;
117
+ const referenceUnits = runWorkUnits(reference);
118
+ const planUnits = planWorkUnits(plan);
119
+ if (referenceUnits === undefined || planUnits === undefined || planUnits === 0)
120
+ return undefined;
121
+ return {
122
+ seconds: (elapsed * planUnits) / referenceUnits,
123
+ basis: {
124
+ runId: reference.run.id,
125
+ host: reference.run.host.hostname,
126
+ seconds: elapsed,
127
+ workUnits: referenceUnits,
128
+ },
129
+ };
130
+ }
131
+ /** Compare a run's rates against the rates an installation now uses. */
132
+ export function rateCoverage(run, needed) {
133
+ const have = new Set(run?.rates ?? []);
134
+ const want = [...new Set(needed)].sort((a, b) => a - b);
135
+ return {
136
+ covered: want.filter((rate) => have.has(rate)),
137
+ missing: want.filter((rate) => !have.has(rate)),
138
+ extra: [...have].filter((rate) => !want.includes(rate)).sort((a, b) => a - b),
139
+ };
140
+ }
141
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/measurement/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * The prober's JSON → the interchange format.
3
+ *
4
+ * `tools/scan.py` and `tools/benchmark.py` write a flat array of annotated descriptors —
5
+ * their own working shape, which predates this format and carries fields (curation tiers,
6
+ * parameter lists, RPM ownership) that have nothing to do with a measurement. This module
7
+ * is the one place that knows both shapes, and it is where three deliberate conversions
8
+ * happen:
9
+ *
10
+ * 1. **Free text becomes vocabulary.** The prober says `"worker crashed (…)"`,
11
+ * `"probe timed out"`, `"non-finite output"` — useful to a human, unmergeable between
12
+ * projects. Each is mapped to a term from {@link ./vocabulary.ts}, and the prober's own
13
+ * sentence is preserved verbatim next to it so the coarser code never destroys the
14
+ * detail.
15
+ * 2. **Derived figures are dropped.** `coreFractionP95` and `instancesPerCoreP95` are
16
+ * `nsPerSample × rate` and its reciprocal. Storing them invites the two to disagree
17
+ * after an edit; the format keeps the measured primitive and lets consumers derive.
18
+ * 3. **`nsPerSampleP95` becomes `nsPerSamplePercentile`.** The percentile is a property of
19
+ * the RUN, declared once in {@link ../format.CostMethod}. Baking `95` into every field
20
+ * name would make a p99 run unrepresentable.
21
+ *
22
+ * The reader is structural: it validates the fields it needs and ignores everything else,
23
+ * so it survives the prober growing new ones.
24
+ */
25
+ import type { MeasuredPlugin } from "./format.js";
26
+ import type { MeasurementDimension } from "./vocabulary.js";
27
+ /** One free-text prober failure, and the vocabulary term it means. */
28
+ interface ReasonRule {
29
+ /** Matched against the prober's lowercased text. */
30
+ readonly match: RegExp;
31
+ readonly code: string;
32
+ }
33
+ /**
34
+ * The prober's words → the controlled vocabulary. Ordered: the first match wins, so the
35
+ * specific patterns come before the general ones.
36
+ *
37
+ * A table rather than a switch precisely so the next prober — someone else's — can be
38
+ * supported by adding rows, which is the whole ambition of publishing a format.
39
+ */
40
+ export declare const PROBE_REASON_RULES: readonly ReasonRule[];
41
+ /**
42
+ * The vocabulary code for a prober reason.
43
+ *
44
+ * Anything unmatched becomes `probe-error` — never a guessed structural reason. A reason we
45
+ * cannot classify must not be allowed to claim the plugin has nothing to measure, because
46
+ * that claim would override a good figure from another machine ({@link ./merge.ts}).
47
+ */
48
+ export declare function probeReasonCode(text: string): string;
49
+ /**
50
+ * One prober descriptor as a {@link MeasuredPlugin}, or `undefined` when it is not one.
51
+ *
52
+ * `dimensions` says which dimensions the run covered: a dimension NOT covered yields no
53
+ * block at all (the run never asked), while a covered dimension always yields one, carrying
54
+ * either readings or a reason. That distinction is what makes a partial run readable. The
55
+ * prober's JSON carries only `latency` and `cost`; a hosting dimension listed here yields no
56
+ * block, because this reader has nothing to read it from (`sweep-jsonl.ts` does).
57
+ */
58
+ export declare function measuredPluginFromProbeEntry(value: unknown, dimensions: readonly MeasurementDimension[]): MeasuredPlugin | undefined;
59
+ /** Every readable descriptor in a prober output array, as measured plugins. */
60
+ export declare function measuredPluginsFromProbeOutput(value: unknown, dimensions: readonly MeasurementDimension[]): MeasuredPlugin[];
61
+ export {};
@@ -0,0 +1,253 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ // Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
3
+ /**
4
+ * The prober's JSON → the interchange format.
5
+ *
6
+ * `tools/scan.py` and `tools/benchmark.py` write a flat array of annotated descriptors —
7
+ * their own working shape, which predates this format and carries fields (curation tiers,
8
+ * parameter lists, RPM ownership) that have nothing to do with a measurement. This module
9
+ * is the one place that knows both shapes, and it is where three deliberate conversions
10
+ * happen:
11
+ *
12
+ * 1. **Free text becomes vocabulary.** The prober says `"worker crashed (…)"`,
13
+ * `"probe timed out"`, `"non-finite output"` — useful to a human, unmergeable between
14
+ * projects. Each is mapped to a term from {@link ./vocabulary.ts}, and the prober's own
15
+ * sentence is preserved verbatim next to it so the coarser code never destroys the
16
+ * detail.
17
+ * 2. **Derived figures are dropped.** `coreFractionP95` and `instancesPerCoreP95` are
18
+ * `nsPerSample × rate` and its reciprocal. Storing them invites the two to disagree
19
+ * after an edit; the format keeps the measured primitive and lets consumers derive.
20
+ * 3. **`nsPerSampleP95` becomes `nsPerSamplePercentile`.** The percentile is a property of
21
+ * the RUN, declared once in {@link ../format.CostMethod}. Baking `95` into every field
22
+ * name would make a p99 run unrepresentable.
23
+ *
24
+ * The reader is structural: it validates the fields it needs and ignores everything else,
25
+ * so it survives the prober growing new ones.
26
+ */
27
+ import { booleanValue, finiteNumber, isRecord, nonEmptyString, optional, positiveNumber } from "./guards.js";
28
+ /**
29
+ * The prober's words → the controlled vocabulary. Ordered: the first match wins, so the
30
+ * specific patterns come before the general ones.
31
+ *
32
+ * A table rather than a switch precisely so the next prober — someone else's — can be
33
+ * supported by adding rows, which is the whole ambition of publishing a format.
34
+ */
35
+ export const PROBE_REASON_RULES = [
36
+ { match: /^no-audio-in$/, code: "no-audio-in" },
37
+ { match: /^no-audio-out$/, code: "no-audio-out" },
38
+ { match: /^silent-output$/, code: "silent-output" },
39
+ { match: /worker crashed|worker desync|worker unavailable/, code: "probe-crashed" },
40
+ { match: /timed out|timeout/, code: "probe-timeout" },
41
+ { match: /non-finite/, code: "non-finite-output" },
42
+ { match: /not installed/, code: "not-installed" },
43
+ { match: /refus|instantiat/, code: "refused-instantiation" },
44
+ ];
45
+ /**
46
+ * The vocabulary code for a prober reason.
47
+ *
48
+ * Anything unmatched becomes `probe-error` — never a guessed structural reason. A reason we
49
+ * cannot classify must not be allowed to claim the plugin has nothing to measure, because
50
+ * that claim would override a good figure from another machine ({@link ./merge.ts}).
51
+ */
52
+ export function probeReasonCode(text) {
53
+ const lower = text.toLowerCase();
54
+ return PROBE_REASON_RULES.find((rule) => rule.match.test(lower))?.code ?? "probe-error";
55
+ }
56
+ function reasonFields(text) {
57
+ if (text === undefined)
58
+ return {};
59
+ const code = probeReasonCode(text);
60
+ return {
61
+ unmeasuredReason: code,
62
+ // Keep the prober's sentence only when it says more than the code already does.
63
+ ...(text === code ? {} : { unmeasuredDetail: text }),
64
+ };
65
+ }
66
+ function readTopology(entry) {
67
+ const audioInputs = finiteNumber(entry.audioInputs);
68
+ const audioOutputs = finiteNumber(entry.audioOutputs);
69
+ if (audioInputs === undefined || audioOutputs === undefined)
70
+ return undefined;
71
+ const midiInputs = entry.hasMidiIn === true ? 1 : entry.hasMidiIn === false ? 0 : undefined;
72
+ const controlInputs = Array.isArray(entry.params)
73
+ ? entry.params.filter((p) => isRecord(p) && p.kind === "control").length
74
+ : undefined;
75
+ return {
76
+ audioInputs,
77
+ audioOutputs,
78
+ ...optional("midiInputs", midiInputs),
79
+ ...optional("controlInputs", controlInputs),
80
+ };
81
+ }
82
+ function readSweep(value) {
83
+ if (!isRecord(value))
84
+ return undefined;
85
+ const rate = positiveNumber(value.rate);
86
+ const defaultFrames = finiteNumber(value.defaultFrames);
87
+ const minFrames = finiteNumber(value.minFrames);
88
+ const maxFrames = finiteNumber(value.maxFrames);
89
+ if (rate === undefined || defaultFrames === undefined || minFrames === undefined || maxFrames === undefined) {
90
+ return undefined;
91
+ }
92
+ const controls = {};
93
+ if (isRecord(value.controls)) {
94
+ for (const [symbol, span] of Object.entries(value.controls)) {
95
+ if (!isRecord(span))
96
+ continue;
97
+ const lo = finiteNumber(span.minFrames);
98
+ const hi = finiteNumber(span.maxFrames);
99
+ if (lo === undefined || hi === undefined)
100
+ continue;
101
+ controls[symbol] = { minFrames: lo, maxFrames: hi };
102
+ }
103
+ }
104
+ return { rate, defaultFrames, minFrames, maxFrames, controls };
105
+ }
106
+ /**
107
+ * Rates the prober condemned, as `rate → reason`. In the catalog shape these live in a
108
+ * parallel `unreliableRates` array; the format puts the exclusion ON the reading it
109
+ * disqualifies, where a consumer cannot fail to notice it.
110
+ */
111
+ function excludedRates(value) {
112
+ const out = new Map();
113
+ if (!Array.isArray(value))
114
+ return out;
115
+ for (const item of value) {
116
+ if (!isRecord(item))
117
+ continue;
118
+ const rate = positiveNumber(item.rate);
119
+ const reason = nonEmptyString(item.reason);
120
+ if (rate === undefined || reason === undefined)
121
+ continue;
122
+ out.set(String(rate), reason);
123
+ }
124
+ return out;
125
+ }
126
+ function readLatency(value) {
127
+ if (!isRecord(value))
128
+ return undefined;
129
+ const excluded = excludedRates(value.unreliableRates);
130
+ const perRate = {};
131
+ if (isRecord(value.perRate)) {
132
+ for (const [key, reading] of Object.entries(value.perRate)) {
133
+ const rate = positiveNumber(Number(key));
134
+ if (rate === undefined || !isRecord(reading))
135
+ continue;
136
+ const frames = finiteNumber(reading.frames);
137
+ const ms = finiteNumber(reading.ms);
138
+ if (frames === undefined || ms === undefined)
139
+ continue;
140
+ perRate[String(rate)] = {
141
+ frames,
142
+ ms,
143
+ ...optional("stimulus", nonEmptyString(value.stimulus)),
144
+ ...optional("excludedReason", excluded.get(String(rate))),
145
+ };
146
+ }
147
+ }
148
+ const unmeasurable = nonEmptyString(value.unmeasurable);
149
+ const measurement = {
150
+ perRate,
151
+ ...optional("scalingClass", nonEmptyString(value.scalingClass)),
152
+ ...optional("scalingNote", nonEmptyString(value.scalingNote)),
153
+ ...optional("declaredPortSymbol", nonEmptyString(value.portSymbol)),
154
+ ...optional("declaredFrames", finiteNumber(value.reportedFrames)),
155
+ ...optional("declaredMismatch", booleanValue(value.declaredMismatch)),
156
+ ...optional("paramSweep", readSweep(value.paramSweep)),
157
+ // A reason only when there is no reading: a figure and a reason are mutually exclusive.
158
+ ...(Object.keys(perRate).length === 0 ? reasonFields(unmeasurable) : {}),
159
+ };
160
+ return measurement;
161
+ }
162
+ function readCost(value) {
163
+ if (!isRecord(value))
164
+ return undefined;
165
+ const perRate = {};
166
+ if (isRecord(value.perRate)) {
167
+ for (const [key, reading] of Object.entries(value.perRate)) {
168
+ const rate = positiveNumber(Number(key));
169
+ if (rate === undefined || !isRecord(reading))
170
+ continue;
171
+ const median = finiteNumber(reading.nsPerSampleMedian);
172
+ const high = finiteNumber(reading.nsPerSampleP95);
173
+ const blocks = finiteNumber(reading.blocks);
174
+ const blockFrames = positiveNumber(reading.blockFrames);
175
+ if (median === undefined || high === undefined || blocks === undefined || blockFrames === undefined) {
176
+ continue;
177
+ }
178
+ perRate[String(rate)] = {
179
+ nsPerSampleMedian: median,
180
+ nsPerSamplePercentile: high,
181
+ blocks,
182
+ blockFrames,
183
+ ...optional("nsPerSampleMin", finiteNumber(reading.nsPerSampleMin)),
184
+ ...optional("nsPerSampleMax", finiteNumber(reading.nsPerSampleMax)),
185
+ ...optional("warmupNsPerSampleMedian", finiteNumber(reading.warmupNsPerSampleMedian)),
186
+ ...optional("silentOutput", booleanValue(reading.silentOutput)),
187
+ };
188
+ }
189
+ }
190
+ const reason = nonEmptyString(value.unmeasurable) ?? nonEmptyString(value.failed);
191
+ return {
192
+ perRate,
193
+ ...(Object.keys(perRate).length === 0 ? reasonFields(reason) : {}),
194
+ };
195
+ }
196
+ /**
197
+ * The plugin's own version, `"<minor>.<micro>"`, when the scan recorded one.
198
+ *
199
+ * Absent rather than `"0.0"` when it did not: a version of zero is a real LV2 answer
200
+ * (unstable/development), so it must not double as "we do not know".
201
+ */
202
+ function readVersion(entry) {
203
+ const minor = finiteNumber(entry.minorVersion);
204
+ const micro = finiteNumber(entry.microVersion);
205
+ if (minor === undefined || micro === undefined)
206
+ return nonEmptyString(entry.version);
207
+ return `${minor}.${micro}`;
208
+ }
209
+ /**
210
+ * One prober descriptor as a {@link MeasuredPlugin}, or `undefined` when it is not one.
211
+ *
212
+ * `dimensions` says which dimensions the run covered: a dimension NOT covered yields no
213
+ * block at all (the run never asked), while a covered dimension always yields one, carrying
214
+ * either readings or a reason. That distinction is what makes a partial run readable. The
215
+ * prober's JSON carries only `latency` and `cost`; a hosting dimension listed here yields no
216
+ * block, because this reader has nothing to read it from (`sweep-jsonl.ts` does).
217
+ */
218
+ export function measuredPluginFromProbeEntry(value, dimensions) {
219
+ if (!isRecord(value))
220
+ return undefined;
221
+ const uri = nonEmptyString(value.uri);
222
+ if (uri === undefined)
223
+ return undefined;
224
+ const topology = readTopology(value);
225
+ if (topology === undefined)
226
+ return undefined;
227
+ const latency = dimensions.includes("latency")
228
+ ? readLatency(value.latency) ?? { perRate: {}, unmeasuredReason: "not-attempted" }
229
+ : undefined;
230
+ const cost = dimensions.includes("cost")
231
+ ? readCost(value.cpuCost) ?? { perRate: {}, unmeasuredReason: "not-attempted" }
232
+ : undefined;
233
+ return {
234
+ uri,
235
+ topology,
236
+ ...optional("name", nonEmptyString(value.name)),
237
+ ...optional("version", readVersion(value)),
238
+ ...optional("bundle", nonEmptyString(value.bundlePath)),
239
+ ...optional("lv2Class", nonEmptyString(value.lv2Class)),
240
+ ...optional("latency", latency),
241
+ ...optional("cost", cost),
242
+ };
243
+ }
244
+ /** Every readable descriptor in a prober output array, as measured plugins. */
245
+ export function measuredPluginsFromProbeOutput(value, dimensions) {
246
+ if (!Array.isArray(value))
247
+ return [];
248
+ return value.flatMap((entry) => {
249
+ const plugin = measuredPluginFromProbeEntry(entry, dimensions);
250
+ return plugin === undefined ? [] : [plugin];
251
+ });
252
+ }
253
+ //# sourceMappingURL=probe-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe-json.js","sourceRoot":"","sources":["../../src/measurement/probe-json.ts"],"names":[],"mappings":"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"}