@burdenoff/microfe-movethewheels 2026.1017.15 → 2026.1017.17

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.
@@ -0,0 +1,140 @@
1
+ import type {
2
+ MtwConsumedFuelPreviewInput,
3
+ MtwConsumedFuelPreviewQuery,
4
+ MtwConsumedFuelReason,
5
+ } from '../../generated/consumed-fuel-preview-operations';
6
+ import { strictFuelInstant } from './consumedFuelInput';
7
+ export type ConsumedFuelPreview = MtwConsumedFuelPreviewQuery['mtwConsumedFuelPreview'];
8
+ function invalid(): never {
9
+ throw new Error('The consumption preview response could not be verified.');
10
+ }
11
+ function isObject(value: unknown): value is Record<string, unknown> {
12
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
13
+ }
14
+ function object(value: unknown): Record<string, unknown> {
15
+ if (!isObject(value)) return invalid();
16
+ return value;
17
+ }
18
+ function keys(value: Record<string, unknown>, names: readonly string[]): void {
19
+ const allowed = new Set(names);
20
+ if (
21
+ names.some((k) => !Object.hasOwn(value, k)) ||
22
+ Object.keys(value).some((k) => k !== '__typename' && !allowed.has(k))
23
+ )
24
+ invalid();
25
+ }
26
+ function reason(value: unknown): MtwConsumedFuelReason {
27
+ switch (value) {
28
+ case 'DISTANCE_UNKNOWN':
29
+ case 'FUEL_UNKNOWN':
30
+ case 'SOURCE_UNKNOWN':
31
+ case 'NON_MEASURED_DISTANCE':
32
+ case 'UNSUPPORTED_FUEL_BASIS':
33
+ case 'INTERVAL_MISMATCH':
34
+ case 'PARTIAL_COVERAGE':
35
+ case 'ZERO_DISTANCE':
36
+ return value;
37
+ default:
38
+ return invalid();
39
+ }
40
+ }
41
+ export function validateConsumedFuelEnvelope(value: unknown): void {
42
+ const envelope = object(value);
43
+ keys(envelope, ['data', ...(Object.hasOwn(envelope, 'errors') ? ['errors'] : [])]);
44
+ if (
45
+ Object.hasOwn(envelope, 'errors') &&
46
+ (!Array.isArray(envelope.errors) || envelope.errors.length !== 0)
47
+ )
48
+ invalid();
49
+ const data = object(envelope.data);
50
+ keys(data, ['mtwConsumedFuelPreview']);
51
+ }
52
+ export function parseConsumedFuelPreview(
53
+ data: unknown,
54
+ input: MtwConsumedFuelPreviewInput
55
+ ): ConsumedFuelPreview {
56
+ const root = object(data);
57
+ keys(root, ['mtwConsumedFuelPreview']);
58
+ const row = object(root.mtwConsumedFuelPreview);
59
+ keys(row, ['status', 'litresPer100Km', 'reasons', 'qualification', 'distance', 'fuel']);
60
+ const normalized = (
61
+ value: NonNullable<
62
+ MtwConsumedFuelPreviewInput['distance'] | MtwConsumedFuelPreviewInput['fuel']
63
+ >
64
+ ) => ({
65
+ intervalFrom: strictFuelInstant(value.intervalFrom, 'response'),
66
+ intervalUntil: strictFuelInstant(value.intervalUntil, 'response'),
67
+ sourceReference: value.sourceReference?.trim() ?? null,
68
+ sourceRevision: value.sourceRevision?.trim() ?? null,
69
+ });
70
+ const distance = input.distance
71
+ ? { ...input.distance, ...normalized(input.distance), metres: input.distance.metres ?? null }
72
+ : null;
73
+ const fuel = input.fuel
74
+ ? { ...input.fuel, ...normalized(input.fuel), millilitres: input.fuel.millilitres ?? null }
75
+ : null;
76
+ const checkEcho = (value: unknown, expected: object | null): void => {
77
+ if (expected === null) {
78
+ if (value !== null) invalid();
79
+ return;
80
+ }
81
+ const record = object(value);
82
+ keys(record, Object.keys(expected));
83
+ for (const [field, wanted] of Object.entries(expected)) if (record[field] !== wanted) invalid();
84
+ };
85
+ checkEcho(row.distance, distance);
86
+ checkEcho(row.fuel, fuel);
87
+ if (
88
+ row.qualification !== 'OPERATOR_REPORTED_UNVERIFIED' ||
89
+ (row.status !== 'KNOWN' && row.status !== 'UNKNOWN') ||
90
+ !Array.isArray(row.reasons)
91
+ )
92
+ return invalid();
93
+ const reasons = row.reasons.map(reason);
94
+ if (new Set(reasons).size !== reasons.length) invalid();
95
+ const expectedReasons: MtwConsumedFuelReason[] = [];
96
+ if (distance?.metres == null) expectedReasons.push('DISTANCE_UNKNOWN');
97
+ if (fuel?.millilitres == null) expectedReasons.push('FUEL_UNKNOWN');
98
+ if (
99
+ !distance?.sourceReference ||
100
+ !distance.sourceRevision ||
101
+ !fuel?.sourceReference ||
102
+ !fuel.sourceRevision
103
+ )
104
+ expectedReasons.push('SOURCE_UNKNOWN');
105
+ if (distance && distance.basis !== 'MEASURED') expectedReasons.push('NON_MEASURED_DISTANCE');
106
+ if (fuel && fuel.basis !== 'MEASURED_CONSUMPTION') expectedReasons.push('UNSUPPORTED_FUEL_BASIS');
107
+ if (
108
+ distance &&
109
+ fuel &&
110
+ (distance.intervalFrom !== fuel.intervalFrom || distance.intervalUntil !== fuel.intervalUntil)
111
+ )
112
+ expectedReasons.push('INTERVAL_MISMATCH');
113
+ if (distance?.coverage === 'PARTIAL' || fuel?.coverage === 'PARTIAL')
114
+ expectedReasons.push('PARTIAL_COVERAGE');
115
+ if (distance?.metres === 0) expectedReasons.push('ZERO_DISTANCE');
116
+ // Owning preview reasons have a deterministic order; a changed list/order is a contract mismatch.
117
+ if (JSON.stringify(reasons) !== JSON.stringify(expectedReasons)) invalid();
118
+ // The owner uses the same JS IEEE 754 double (100 * mL) / m and JSON Float roundtrip; compare exactly.
119
+ const ratio = row.litresPer100Km;
120
+ if (reasons.length) {
121
+ if (row.status !== 'UNKNOWN' || ratio !== null) invalid();
122
+ } else if (
123
+ row.status !== 'KNOWN' ||
124
+ typeof ratio !== 'number' ||
125
+ !Number.isFinite(ratio) ||
126
+ ratio < 0 ||
127
+ !distance?.metres ||
128
+ fuel?.millilitres == null ||
129
+ ratio !== (100 * fuel.millilitres) / distance.metres
130
+ )
131
+ invalid();
132
+ return {
133
+ status: row.status,
134
+ litresPer100Km: ratio,
135
+ reasons,
136
+ qualification: 'OPERATOR_REPORTED_UNVERIFIED',
137
+ distance: distance ? { ...distance, metres: distance.metres ?? null } : null,
138
+ fuel: fuel ? { ...fuel, millilitres: fuel.millilitres ?? null } : null,
139
+ };
140
+ }
@@ -0,0 +1 @@
1
+ query MtwConsumedFuelPreview($input: MtwConsumedFuelPreviewInput!) { mtwConsumedFuelPreview(input: $input) { status litresPer100Km reasons qualification distance { metres intervalFrom intervalUntil basis coverage sourceReference sourceRevision } fuel { millilitres intervalFrom intervalUntil basis coverage sourceReference sourceRevision } } }