@actuarial-ts/core 0.6.0 → 0.6.1
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/README.md +3 -3
- package/dist/capping.d.ts.map +1 -1
- package/dist/capping.js +39 -6
- package/dist/capping.js.map +1 -1
- package/dist/diagnosticAggregation.d.ts +3 -0
- package/dist/diagnosticAggregation.d.ts.map +1 -1
- package/dist/diagnosticAggregation.js +30 -7
- package/dist/diagnosticAggregation.js.map +1 -1
- package/dist/diagnosticDefinitions.d.ts +3 -0
- package/dist/diagnosticDefinitions.d.ts.map +1 -1
- package/dist/diagnosticDefinitions.js +844 -182
- package/dist/diagnosticDefinitions.js.map +1 -1
- package/dist/diagnosticDerivations.d.ts +23 -0
- package/dist/diagnosticDerivations.d.ts.map +1 -1
- package/dist/diagnosticDerivations.js +187 -43
- package/dist/diagnosticDerivations.js.map +1 -1
- package/dist/diagnosticExposure.d.ts +2 -2
- package/dist/diagnosticExposure.d.ts.map +1 -1
- package/dist/diagnosticExposure.js +228 -20
- package/dist/diagnosticExposure.js.map +1 -1
- package/dist/diagnosticExpressions.d.ts +1 -1
- package/dist/diagnosticExpressions.d.ts.map +1 -1
- package/dist/diagnosticExpressions.js +35 -15
- package/dist/diagnosticExpressions.js.map +1 -1
- package/dist/diagnosticFormulas.d.ts +3 -0
- package/dist/diagnosticFormulas.d.ts.map +1 -1
- package/dist/diagnosticFormulas.js +160 -26
- package/dist/diagnosticFormulas.js.map +1 -1
- package/dist/diagnosticIdentity.d.ts +15 -1
- package/dist/diagnosticIdentity.d.ts.map +1 -1
- package/dist/diagnosticIdentity.js +318 -51
- package/dist/diagnosticIdentity.js.map +1 -1
- package/dist/diagnosticOrdering.d.ts +8 -0
- package/dist/diagnosticOrdering.d.ts.map +1 -0
- package/dist/diagnosticOrdering.js +73 -0
- package/dist/diagnosticOrdering.js.map +1 -0
- package/dist/diagnosticPeriodAxis.d.ts +9 -0
- package/dist/diagnosticPeriodAxis.d.ts.map +1 -0
- package/dist/diagnosticPeriodAxis.js +69 -0
- package/dist/diagnosticPeriodAxis.js.map +1 -0
- package/dist/diagnosticPeriods.d.ts +2 -3
- package/dist/diagnosticPeriods.d.ts.map +1 -1
- package/dist/diagnosticPeriods.js +20 -68
- package/dist/diagnosticPeriods.js.map +1 -1
- package/dist/diagnosticPreparation.d.ts +47 -14
- package/dist/diagnosticPreparation.d.ts.map +1 -1
- package/dist/diagnosticPreparation.js +1482 -112
- package/dist/diagnosticPreparation.js.map +1 -1
- package/dist/diagnosticReview.d.ts +36 -14
- package/dist/diagnosticReview.d.ts.map +1 -1
- package/dist/diagnosticReview.js +448 -42
- package/dist/diagnosticReview.js.map +1 -1
- package/dist/diagnosticRunner.d.ts +2 -1
- package/dist/diagnosticRunner.d.ts.map +1 -1
- package/dist/diagnosticRunner.js +656 -70
- package/dist/diagnosticRunner.js.map +1 -1
- package/dist/diagnosticRuntime.d.ts +26 -0
- package/dist/diagnosticRuntime.d.ts.map +1 -0
- package/dist/diagnosticRuntime.js +243 -0
- package/dist/diagnosticRuntime.js.map +1 -0
- package/dist/diagnosticSourceOrdering.d.ts +6 -0
- package/dist/diagnosticSourceOrdering.d.ts.map +1 -0
- package/dist/diagnosticSourceOrdering.js +38 -0
- package/dist/diagnosticSourceOrdering.js.map +1 -0
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/mack.d.ts +4 -0
- package/dist/mack.d.ts.map +1 -1
- package/dist/mack.js +32 -4
- package/dist/mack.js.map +1 -1
- package/dist/triangle.d.ts.map +1 -1
- package/dist/triangle.js +59 -9
- package/dist/triangle.js.map +1 -1
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +22 -8
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -2
- package/src/capping.ts +109 -18
- package/src/diagnosticAggregation.ts +69 -20
- package/src/diagnosticDefinitions.ts +2675 -426
- package/src/diagnosticDerivations.ts +281 -49
- package/src/diagnosticExposure.ts +441 -50
- package/src/diagnosticExpressions.ts +99 -17
- package/src/diagnosticFormulas.ts +239 -39
- package/src/diagnosticIdentity.ts +691 -191
- package/src/diagnosticOrdering.ts +106 -0
- package/src/diagnosticPeriodAxis.ts +92 -0
- package/src/diagnosticPeriods.ts +67 -82
- package/src/diagnosticPreparation.ts +2369 -127
- package/src/diagnosticReview.ts +864 -58
- package/src/diagnosticRunner.ts +1076 -72
- package/src/diagnosticRuntime.ts +264 -0
- package/src/diagnosticSourceOrdering.ts +50 -0
- package/src/index.ts +31 -3
- package/src/mack.ts +72 -9
- package/src/triangle.ts +103 -13
- package/src/types.ts +55 -20
- package/src/version.ts +1 -1
|
@@ -24,12 +24,244 @@ import type {
|
|
|
24
24
|
DiagnosticReviewRule,
|
|
25
25
|
DiagnosticRuleOperand,
|
|
26
26
|
DiagnosticSourceLocation,
|
|
27
|
+
DiagnosticsFilter,
|
|
27
28
|
} from "./diagnosticDefinitions.js";
|
|
28
29
|
import type {
|
|
29
30
|
DiagnosticClaimExpression,
|
|
30
31
|
DiagnosticMeasureExpression,
|
|
31
32
|
DiagnosticRoleExpression,
|
|
32
33
|
} from "./diagnosticExpressions.js";
|
|
34
|
+
import { normalizeDiagnosticPeriodWithAxis } from "./diagnosticPeriodAxis.js";
|
|
35
|
+
import {
|
|
36
|
+
diagnosticJsonPreflight,
|
|
37
|
+
isDiagnosticPlainRecord,
|
|
38
|
+
isDiagnosticToken,
|
|
39
|
+
normalizeDiagnosticNumber,
|
|
40
|
+
} from "./diagnosticRuntime.js";
|
|
41
|
+
import {
|
|
42
|
+
DiagnosticValidationError,
|
|
43
|
+
type DiagnosticValidationIssue,
|
|
44
|
+
} from "./types.js";
|
|
45
|
+
|
|
46
|
+
export type DiagnosticIdentityProjection<T> = T extends DiagnosticSourceLocation
|
|
47
|
+
? NormalizedDiagnosticSourceLocationIdentity
|
|
48
|
+
: T extends readonly (infer U)[]
|
|
49
|
+
? readonly DiagnosticIdentityProjection<U>[]
|
|
50
|
+
: T extends object
|
|
51
|
+
? { readonly [K in keyof T]: DiagnosticIdentityProjection<T[K]> }
|
|
52
|
+
: T;
|
|
53
|
+
|
|
54
|
+
export interface NormalizedDiagnosticsFilterIdentity
|
|
55
|
+
extends NormalizedDiagnosticReviewFilterIdentity {
|
|
56
|
+
readonly outputGroups: readonly string[] | null;
|
|
57
|
+
readonly instanceIds: readonly string[] | null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Validates selector shapes without interpreting periods against a definition. */
|
|
61
|
+
function validateFilterIdentityInput(filter: DiagnosticsFilter): void {
|
|
62
|
+
const issues: DiagnosticValidationIssue[] = [
|
|
63
|
+
...diagnosticJsonPreflight(filter, "configuration"),
|
|
64
|
+
];
|
|
65
|
+
if (issues.length) throw new DiagnosticValidationError(issues);
|
|
66
|
+
if (!isDiagnosticPlainRecord(filter))
|
|
67
|
+
throw new DiagnosticValidationError([
|
|
68
|
+
{
|
|
69
|
+
domain: "configuration",
|
|
70
|
+
code: "invalid-type",
|
|
71
|
+
path: "$",
|
|
72
|
+
message: "Filter must be a plain object or null",
|
|
73
|
+
},
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
const lists = [
|
|
77
|
+
"sourceGroups", "outputGroups", "origins", "valuations", "instanceIds",
|
|
78
|
+
];
|
|
79
|
+
const labels = [
|
|
80
|
+
"originFrom", "originThrough", "valuationFrom", "valuationThrough",
|
|
81
|
+
];
|
|
82
|
+
const bounds = ["minDevelopmentAge", "maxDevelopmentAge"];
|
|
83
|
+
const checkToken = (value: unknown, path: string) => {
|
|
84
|
+
if (!isDiagnosticToken(value))
|
|
85
|
+
issues.push({
|
|
86
|
+
domain: "configuration",
|
|
87
|
+
code: "invalid-string",
|
|
88
|
+
path,
|
|
89
|
+
message: "Filter selectors must use nonempty token strings",
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
for (const key of Object.keys(filter).sort()) {
|
|
93
|
+
const value: unknown = filter[key];
|
|
94
|
+
const path = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)
|
|
95
|
+
? `$.${key}`
|
|
96
|
+
: `$[${JSON.stringify(key)}]`;
|
|
97
|
+
if (lists.includes(key)) {
|
|
98
|
+
if (!Array.isArray(value))
|
|
99
|
+
issues.push({
|
|
100
|
+
domain: "configuration",
|
|
101
|
+
code: "invalid-type",
|
|
102
|
+
path,
|
|
103
|
+
message: "Filter selector must be an array",
|
|
104
|
+
});
|
|
105
|
+
else
|
|
106
|
+
value.forEach((item, index) => checkToken(item, `${path}[${index}]`));
|
|
107
|
+
} else if (labels.includes(key)) checkToken(value, path);
|
|
108
|
+
else if (bounds.includes(key)) {
|
|
109
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
|
|
110
|
+
issues.push({
|
|
111
|
+
domain: "configuration",
|
|
112
|
+
code: "invalid-number",
|
|
113
|
+
path,
|
|
114
|
+
message: "Development-age bounds must be nonnegative safe integers",
|
|
115
|
+
});
|
|
116
|
+
} else
|
|
117
|
+
issues.push({
|
|
118
|
+
domain: "configuration",
|
|
119
|
+
code: "unknown-key",
|
|
120
|
+
path,
|
|
121
|
+
message: `Unknown filter key ${key}`,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (
|
|
125
|
+
typeof filter.minDevelopmentAge === "number" &&
|
|
126
|
+
typeof filter.maxDevelopmentAge === "number" &&
|
|
127
|
+
filter.minDevelopmentAge > filter.maxDevelopmentAge
|
|
128
|
+
)
|
|
129
|
+
issues.push({
|
|
130
|
+
domain: "configuration",
|
|
131
|
+
code: "invalid-configuration",
|
|
132
|
+
path: "$",
|
|
133
|
+
message: "Minimum development age exceeds maximum",
|
|
134
|
+
});
|
|
135
|
+
if (issues.length) throw new DiagnosticValidationError(issues);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Materializes optional selectors; period aliases must already be canonical. */
|
|
139
|
+
export function normalizeDiagnosticsFilterIdentity(
|
|
140
|
+
filter: DiagnosticsFilter | null,
|
|
141
|
+
): NormalizedDiagnosticsFilterIdentity | null {
|
|
142
|
+
if (filter === null) return null;
|
|
143
|
+
validateFilterIdentityInput(filter);
|
|
144
|
+
return deepFreeze({
|
|
145
|
+
sourceGroups:
|
|
146
|
+
filter.sourceGroups === undefined
|
|
147
|
+
? null
|
|
148
|
+
: sortedUnique(filter.sourceGroups),
|
|
149
|
+
outputGroups:
|
|
150
|
+
filter.outputGroups === undefined
|
|
151
|
+
? null
|
|
152
|
+
: sortedUnique(filter.outputGroups),
|
|
153
|
+
origins: filter.origins === undefined ? null : sortedUnique(filter.origins),
|
|
154
|
+
originFrom: filter.originFrom ?? null,
|
|
155
|
+
originThrough: filter.originThrough ?? null,
|
|
156
|
+
valuations:
|
|
157
|
+
filter.valuations === undefined ? null : sortedUnique(filter.valuations),
|
|
158
|
+
valuationFrom: filter.valuationFrom ?? null,
|
|
159
|
+
valuationThrough: filter.valuationThrough ?? null,
|
|
160
|
+
minDevelopmentAge:
|
|
161
|
+
filter.minDevelopmentAge === undefined
|
|
162
|
+
? null
|
|
163
|
+
: normalizeDiagnosticNumber(filter.minDevelopmentAge),
|
|
164
|
+
maxDevelopmentAge:
|
|
165
|
+
filter.maxDevelopmentAge === undefined
|
|
166
|
+
? null
|
|
167
|
+
: normalizeDiagnosticNumber(filter.maxDevelopmentAge),
|
|
168
|
+
instanceIds:
|
|
169
|
+
filter.instanceIds === undefined ? null : sortedUnique(filter.instanceIds),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Owned identity copy. Source slots are schema-defined; free JSON dimensions
|
|
175
|
+
* must not be interpreted as source evidence just because a key looks similar.
|
|
176
|
+
*/
|
|
177
|
+
export function projectDiagnosticIdentity<T>(
|
|
178
|
+
value: T,
|
|
179
|
+
): DiagnosticIdentityProjection<T> {
|
|
180
|
+
const issues = diagnosticJsonPreflight(value, "input", {
|
|
181
|
+
maxNodes: Number.MAX_SAFE_INTEGER,
|
|
182
|
+
});
|
|
183
|
+
if (issues.length) throw new DiagnosticValidationError(issues);
|
|
184
|
+
const clone = (
|
|
185
|
+
item: unknown,
|
|
186
|
+
sourceSlot = false,
|
|
187
|
+
freeJson = false,
|
|
188
|
+
path = "$",
|
|
189
|
+
): unknown => {
|
|
190
|
+
if (item === null || typeof item !== "object")
|
|
191
|
+
return typeof item === "number" && Object.is(item, -0) ? 0 : item;
|
|
192
|
+
if (Array.isArray(item))
|
|
193
|
+
return Object.freeze(
|
|
194
|
+
item.map((child, index) =>
|
|
195
|
+
clone(child, sourceSlot, freeJson, `${path}[${index}]`),
|
|
196
|
+
),
|
|
197
|
+
);
|
|
198
|
+
const record = item as Record<string, unknown>;
|
|
199
|
+
if (sourceSlot && typeof record.artifactId === "string") {
|
|
200
|
+
const sourceIssues = [];
|
|
201
|
+
for (const key of Object.keys(record)) {
|
|
202
|
+
const fieldPath = `${path}.${key}`;
|
|
203
|
+
if (
|
|
204
|
+
![
|
|
205
|
+
"artifactId",
|
|
206
|
+
"sourceFile",
|
|
207
|
+
"sourceSheet",
|
|
208
|
+
"sourceRow",
|
|
209
|
+
"sourceCell",
|
|
210
|
+
].includes(key)
|
|
211
|
+
)
|
|
212
|
+
sourceIssues.push({
|
|
213
|
+
domain: "input" as const,
|
|
214
|
+
code: "unknown-key" as const,
|
|
215
|
+
path: fieldPath,
|
|
216
|
+
message: `Unknown source-location key ${key}`,
|
|
217
|
+
});
|
|
218
|
+
else if (key === "sourceRow") {
|
|
219
|
+
if (
|
|
220
|
+
record[key] !== null &&
|
|
221
|
+
!(
|
|
222
|
+
typeof record[key] === "number" &&
|
|
223
|
+
Number.isSafeInteger(record[key]) &&
|
|
224
|
+
record[key] >= 0
|
|
225
|
+
)
|
|
226
|
+
)
|
|
227
|
+
sourceIssues.push({
|
|
228
|
+
domain: "input" as const,
|
|
229
|
+
code: "invalid-number" as const,
|
|
230
|
+
path: fieldPath,
|
|
231
|
+
message: "Source row must be a nonnegative safe integer or null",
|
|
232
|
+
});
|
|
233
|
+
} else if (
|
|
234
|
+
!(key !== "artifactId" && record[key] === null) &&
|
|
235
|
+
!isDiagnosticToken(record[key])
|
|
236
|
+
)
|
|
237
|
+
sourceIssues.push({
|
|
238
|
+
domain: "input" as const,
|
|
239
|
+
code: "invalid-string" as const,
|
|
240
|
+
path: fieldPath,
|
|
241
|
+
message: "Source location must use nonempty token strings",
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
if (sourceIssues.length)
|
|
245
|
+
throw new DiagnosticValidationError(sourceIssues);
|
|
246
|
+
return normalizeDiagnosticSourceLocation(
|
|
247
|
+
record as unknown as DiagnosticSourceLocation,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
const result = Object.create(null) as Record<string, unknown>;
|
|
251
|
+
for (const key of Object.keys(record).sort()) {
|
|
252
|
+
const opaque =
|
|
253
|
+
freeJson || key === "groupDimensions" || key === "dimensions";
|
|
254
|
+
result[key] = clone(
|
|
255
|
+
record[key],
|
|
256
|
+
!opaque && (key === "source" || key === "sources"),
|
|
257
|
+
opaque,
|
|
258
|
+
`${path}.${key}`,
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
return Object.freeze(result);
|
|
262
|
+
};
|
|
263
|
+
return clone(value) as DiagnosticIdentityProjection<T>;
|
|
264
|
+
}
|
|
33
265
|
|
|
34
266
|
function compareCodeUnits(left: string, right: string): number {
|
|
35
267
|
return left === right ? 0 : left < right ? -1 : 1;
|
|
@@ -39,9 +271,13 @@ function sortedUnique(values: readonly string[]): readonly string[] {
|
|
|
39
271
|
return [...new Set(values)].sort(compareCodeUnits);
|
|
40
272
|
}
|
|
41
273
|
|
|
42
|
-
function sortedRecord<T>(
|
|
274
|
+
function sortedRecord<T>(
|
|
275
|
+
entries: readonly (readonly [string, T])[],
|
|
276
|
+
): Readonly<Record<string, T>> {
|
|
43
277
|
const result = Object.create(null) as Record<string, T>;
|
|
44
|
-
for (const [key, value] of [...entries].sort(([left], [right]) =>
|
|
278
|
+
for (const [key, value] of [...entries].sort(([left], [right]) =>
|
|
279
|
+
compareCodeUnits(left, right),
|
|
280
|
+
)) {
|
|
45
281
|
result[key] = value;
|
|
46
282
|
}
|
|
47
283
|
return result;
|
|
@@ -49,29 +285,39 @@ function sortedRecord<T>(entries: readonly (readonly [string, T])[]): Readonly<R
|
|
|
49
285
|
|
|
50
286
|
function normalizeJsonValue<T>(value: T): T {
|
|
51
287
|
if (typeof value === "number" && Object.is(value, -0)) return 0 as T;
|
|
52
|
-
if (Array.isArray(value))
|
|
288
|
+
if (Array.isArray(value))
|
|
289
|
+
return value.map((item) => normalizeJsonValue(item)) as T;
|
|
53
290
|
if (value !== null && typeof value === "object") {
|
|
54
|
-
return sortedRecord(
|
|
55
|
-
|
|
56
|
-
|
|
291
|
+
return sortedRecord(
|
|
292
|
+
Object.entries(value as Record<string, unknown>).map(
|
|
293
|
+
([key, item]) => [key, normalizeJsonValue(item)] as const,
|
|
294
|
+
),
|
|
295
|
+
) as T;
|
|
57
296
|
}
|
|
58
297
|
return value;
|
|
59
298
|
}
|
|
60
299
|
|
|
61
|
-
function deepFreeze<T>(
|
|
62
|
-
|
|
300
|
+
function deepFreeze<T>(
|
|
301
|
+
value: T,
|
|
302
|
+
seen = new WeakSet<object>(),
|
|
303
|
+
): DiagnosticDeepReadonly<T> {
|
|
304
|
+
if (value === null || typeof value !== "object")
|
|
305
|
+
return value as DiagnosticDeepReadonly<T>;
|
|
63
306
|
if (seen.has(value)) return value as DiagnosticDeepReadonly<T>;
|
|
64
307
|
seen.add(value);
|
|
65
|
-
for (const child of Object.values(value as Record<string, unknown>))
|
|
308
|
+
for (const child of Object.values(value as Record<string, unknown>))
|
|
309
|
+
deepFreeze(child, seen);
|
|
66
310
|
return Object.freeze(value) as DiagnosticDeepReadonly<T>;
|
|
67
311
|
}
|
|
68
312
|
|
|
69
313
|
function attributes(
|
|
70
314
|
value: Readonly<Record<string, string | number | boolean | null>> | undefined,
|
|
71
315
|
): Readonly<Record<string, string | number | boolean | null>> {
|
|
72
|
-
return sortedRecord(
|
|
73
|
-
|
|
74
|
-
|
|
316
|
+
return sortedRecord(
|
|
317
|
+
Object.entries(value ?? {}).map(
|
|
318
|
+
([key, item]) => [key, normalizeJsonValue(item)] as const,
|
|
319
|
+
),
|
|
320
|
+
);
|
|
75
321
|
}
|
|
76
322
|
|
|
77
323
|
export type NormalizedAmountLimitationIdentity =
|
|
@@ -80,7 +326,11 @@ export type NormalizedAmountLimitationIdentity =
|
|
|
80
326
|
readonly kind: "layer" | "pre-limited";
|
|
81
327
|
readonly attachment: number;
|
|
82
328
|
readonly limit: number | null;
|
|
83
|
-
readonly application:
|
|
329
|
+
readonly application:
|
|
330
|
+
| "claim"
|
|
331
|
+
| "occurrence"
|
|
332
|
+
| "policy"
|
|
333
|
+
| "source-defined";
|
|
84
334
|
readonly derivation:
|
|
85
335
|
| { readonly kind: "sdk" }
|
|
86
336
|
| {
|
|
@@ -111,7 +361,9 @@ export interface NormalizedDiagnosticReviewFilterIdentity {
|
|
|
111
361
|
export type NormalizedDiagnosticPeriodAxisIdentity =
|
|
112
362
|
| DiagnosticDeepReadonly<Extract<DiagnosticPeriodAxis, { kind: "calendar" }>>
|
|
113
363
|
| (Omit<
|
|
114
|
-
DiagnosticDeepReadonly<
|
|
364
|
+
DiagnosticDeepReadonly<
|
|
365
|
+
Extract<DiagnosticPeriodAxis, { kind: "ordered" }>
|
|
366
|
+
>,
|
|
115
367
|
"origins" | "valuations"
|
|
116
368
|
> & {
|
|
117
369
|
readonly origins: readonly {
|
|
@@ -129,11 +381,16 @@ export type NormalizedDiagnosticPeriodAxisIdentity =
|
|
|
129
381
|
export interface NormalizedDiagnosticFormulaIdentity {
|
|
130
382
|
readonly id: string;
|
|
131
383
|
readonly version: string;
|
|
132
|
-
readonly roles: Readonly<
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
384
|
+
readonly roles: Readonly<
|
|
385
|
+
Record<
|
|
386
|
+
string,
|
|
387
|
+
{
|
|
388
|
+
readonly kind: DiagnosticMeasureKind;
|
|
389
|
+
readonly compatibilityGroup: string | null;
|
|
390
|
+
readonly developmentSemantics: DiagnosticDevelopmentSemantics | null;
|
|
391
|
+
}
|
|
392
|
+
>
|
|
393
|
+
>;
|
|
137
394
|
readonly numerator: DiagnosticDeepReadonly<DiagnosticRoleExpression>;
|
|
138
395
|
readonly denominator: DiagnosticDeepReadonly<DiagnosticRoleExpression>;
|
|
139
396
|
readonly denominatorPolicy: "positive-or-null";
|
|
@@ -205,7 +462,9 @@ export interface NormalizedDiagnosticDefinitionIdentity {
|
|
|
205
462
|
readonly subject: DiagnosticCountPopulationDefinition["subject"];
|
|
206
463
|
readonly unit: string;
|
|
207
464
|
readonly description: string;
|
|
208
|
-
readonly attributes: Readonly<
|
|
465
|
+
readonly attributes: Readonly<
|
|
466
|
+
Record<string, string | number | boolean | null>
|
|
467
|
+
>;
|
|
209
468
|
}[];
|
|
210
469
|
readonly exposureBases: readonly {
|
|
211
470
|
readonly id: string;
|
|
@@ -214,7 +473,9 @@ export interface NormalizedDiagnosticDefinitionIdentity {
|
|
|
214
473
|
readonly unit: string;
|
|
215
474
|
readonly description: string;
|
|
216
475
|
readonly sourceDescription: string | null;
|
|
217
|
-
readonly attributes: Readonly<
|
|
476
|
+
readonly attributes: Readonly<
|
|
477
|
+
Record<string, string | number | boolean | null>
|
|
478
|
+
>;
|
|
218
479
|
}[];
|
|
219
480
|
readonly amountBases: readonly {
|
|
220
481
|
readonly id: string;
|
|
@@ -227,7 +488,9 @@ export interface NormalizedDiagnosticDefinitionIdentity {
|
|
|
227
488
|
readonly limitation: NormalizedAmountLimitationIdentity;
|
|
228
489
|
}[];
|
|
229
490
|
readonly sourceDescription: string | null;
|
|
230
|
-
readonly attributes: Readonly<
|
|
491
|
+
readonly attributes: Readonly<
|
|
492
|
+
Record<string, string | number | boolean | null>
|
|
493
|
+
>;
|
|
231
494
|
}[];
|
|
232
495
|
readonly derivedMeasures: readonly DiagnosticDeepReadonly<DiagnosticDerivedMeasureDefinition>[];
|
|
233
496
|
readonly formulas: readonly NormalizedDiagnosticFormulaIdentity[];
|
|
@@ -235,7 +498,9 @@ export interface NormalizedDiagnosticDefinitionIdentity {
|
|
|
235
498
|
readonly id: string;
|
|
236
499
|
readonly version: string;
|
|
237
500
|
readonly formulaId: string;
|
|
238
|
-
readonly bindings: Readonly<
|
|
501
|
+
readonly bindings: Readonly<
|
|
502
|
+
Record<string, DiagnosticDeepReadonly<DiagnosticMeasureExpression>>
|
|
503
|
+
>;
|
|
239
504
|
readonly presentation: DiagnosticDeepReadonly<DiagnosticMetricPresentation>;
|
|
240
505
|
readonly rules: readonly {
|
|
241
506
|
readonly id: string;
|
|
@@ -260,7 +525,9 @@ export interface NormalizedDiagnosticCalculationScope {
|
|
|
260
525
|
readonly id: string;
|
|
261
526
|
readonly version: string;
|
|
262
527
|
readonly formulaId: string;
|
|
263
|
-
readonly bindings: Readonly<
|
|
528
|
+
readonly bindings: Readonly<
|
|
529
|
+
Record<string, DiagnosticDeepReadonly<DiagnosticMeasureExpression>>
|
|
530
|
+
>;
|
|
264
531
|
};
|
|
265
532
|
readonly lossRowGrain: "claim" | "aggregate";
|
|
266
533
|
readonly measures: readonly {
|
|
@@ -280,13 +547,17 @@ export interface NormalizedDiagnosticCalculationScope {
|
|
|
280
547
|
readonly id: string;
|
|
281
548
|
readonly subject: DiagnosticCountPopulationDefinition["subject"];
|
|
282
549
|
readonly unit: string;
|
|
283
|
-
readonly attributes: Readonly<
|
|
550
|
+
readonly attributes: Readonly<
|
|
551
|
+
Record<string, string | number | boolean | null>
|
|
552
|
+
>;
|
|
284
553
|
}[];
|
|
285
554
|
readonly exposureBases: readonly {
|
|
286
555
|
readonly id: string;
|
|
287
556
|
readonly basis: DiagnosticExposureBasisDefinition["basis"];
|
|
288
557
|
readonly unit: string;
|
|
289
|
-
readonly attributes: Readonly<
|
|
558
|
+
readonly attributes: Readonly<
|
|
559
|
+
Record<string, string | number | boolean | null>
|
|
560
|
+
>;
|
|
290
561
|
}[];
|
|
291
562
|
readonly amountBases: readonly {
|
|
292
563
|
readonly id: string;
|
|
@@ -297,7 +568,9 @@ export interface NormalizedDiagnosticCalculationScope {
|
|
|
297
568
|
readonly treatment: "included" | "excluded" | "unknown";
|
|
298
569
|
readonly limitation: NormalizedAmountLimitationIdentity;
|
|
299
570
|
}[];
|
|
300
|
-
readonly attributes: Readonly<
|
|
571
|
+
readonly attributes: Readonly<
|
|
572
|
+
Record<string, string | number | boolean | null>
|
|
573
|
+
>;
|
|
301
574
|
}[];
|
|
302
575
|
readonly derivedMeasures: readonly DiagnosticDeepReadonly<DiagnosticDerivedMeasureDefinition>[];
|
|
303
576
|
}
|
|
@@ -310,36 +583,53 @@ export interface NormalizedDiagnosticSourceLocationIdentity {
|
|
|
310
583
|
readonly sourceCell: string | null;
|
|
311
584
|
}
|
|
312
585
|
|
|
313
|
-
function normalizeLimitation(
|
|
586
|
+
function normalizeLimitation(
|
|
587
|
+
limitation: AmountLimitation,
|
|
588
|
+
): NormalizedAmountLimitationIdentity {
|
|
314
589
|
if (limitation.kind === "unlimited") return { kind: "unlimited" };
|
|
315
590
|
if (limitation.kind === "unknown") {
|
|
316
591
|
return { kind: "unknown", description: limitation.description ?? null };
|
|
317
592
|
}
|
|
318
593
|
return {
|
|
319
594
|
kind: limitation.kind,
|
|
320
|
-
attachment: Object.is(limitation.attachment, -0)
|
|
321
|
-
|
|
595
|
+
attachment: Object.is(limitation.attachment, -0)
|
|
596
|
+
? 0
|
|
597
|
+
: limitation.attachment,
|
|
598
|
+
limit:
|
|
599
|
+
limitation.limit !== null && Object.is(limitation.limit, -0)
|
|
600
|
+
? 0
|
|
601
|
+
: limitation.limit,
|
|
322
602
|
application: limitation.application,
|
|
323
|
-
derivation:
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
603
|
+
derivation:
|
|
604
|
+
limitation.derivation.kind === "sdk"
|
|
605
|
+
? { kind: "sdk" }
|
|
606
|
+
: {
|
|
607
|
+
kind: "external",
|
|
608
|
+
actor: limitation.derivation.actor,
|
|
609
|
+
transformationRef: limitation.derivation.transformationRef,
|
|
610
|
+
},
|
|
330
611
|
};
|
|
331
612
|
}
|
|
332
613
|
|
|
333
|
-
function normalizeMeasureExpression<
|
|
334
|
-
|
|
335
|
-
): DiagnosticDeepReadonly<T> {
|
|
336
|
-
if (expression.op === "measure")
|
|
614
|
+
function normalizeMeasureExpression<
|
|
615
|
+
T extends DiagnosticMeasureExpression | DiagnosticClaimExpression,
|
|
616
|
+
>(expression: T): DiagnosticDeepReadonly<T> {
|
|
617
|
+
if (expression.op === "measure")
|
|
618
|
+
return {
|
|
619
|
+
op: "measure",
|
|
620
|
+
measureId: expression.measureId,
|
|
621
|
+
} as DiagnosticDeepReadonly<T>;
|
|
337
622
|
if (expression.op === "claim-layer") {
|
|
338
623
|
return {
|
|
339
624
|
op: "claim-layer",
|
|
340
625
|
measureId: expression.measureId,
|
|
341
|
-
attachment: Object.is(expression.attachment, -0)
|
|
342
|
-
|
|
626
|
+
attachment: Object.is(expression.attachment, -0)
|
|
627
|
+
? 0
|
|
628
|
+
: expression.attachment,
|
|
629
|
+
limit:
|
|
630
|
+
expression.limit !== null && Object.is(expression.limit, -0)
|
|
631
|
+
? 0
|
|
632
|
+
: expression.limit,
|
|
343
633
|
} as DiagnosticDeepReadonly<T>;
|
|
344
634
|
}
|
|
345
635
|
if (expression.op === "subtract") {
|
|
@@ -355,7 +645,9 @@ function normalizeMeasureExpression<T extends DiagnosticMeasureExpression | Diag
|
|
|
355
645
|
} as unknown as DiagnosticDeepReadonly<T>;
|
|
356
646
|
}
|
|
357
647
|
|
|
358
|
-
function normalizeRoleExpression(
|
|
648
|
+
function normalizeRoleExpression(
|
|
649
|
+
expression: DiagnosticRoleExpression,
|
|
650
|
+
): DiagnosticDeepReadonly<DiagnosticRoleExpression> {
|
|
359
651
|
if (expression.op === "role") return { op: "role", role: expression.role };
|
|
360
652
|
if (expression.op === "subtract") {
|
|
361
653
|
return {
|
|
@@ -371,43 +663,95 @@ function normalizeTolerance(
|
|
|
371
663
|
tolerance: { absolute?: number; relative?: number } | undefined,
|
|
372
664
|
): NormalizedDiagnosticToleranceIdentity {
|
|
373
665
|
return {
|
|
374
|
-
absolute: Object.is(tolerance?.absolute ?? 0, -0)
|
|
375
|
-
|
|
666
|
+
absolute: Object.is(tolerance?.absolute ?? 0, -0)
|
|
667
|
+
? 0
|
|
668
|
+
: (tolerance?.absolute ?? 0),
|
|
669
|
+
relative: Object.is(tolerance?.relative ?? 0, -0)
|
|
670
|
+
? 0
|
|
671
|
+
: (tolerance?.relative ?? 0),
|
|
376
672
|
};
|
|
377
673
|
}
|
|
378
674
|
|
|
379
|
-
function normalizeRuleOperand(
|
|
675
|
+
function normalizeRuleOperand(
|
|
676
|
+
operand: DiagnosticRuleOperand,
|
|
677
|
+
): DiagnosticDeepReadonly<DiagnosticRuleOperand> {
|
|
380
678
|
if (operand.source === "measure") {
|
|
381
|
-
return {
|
|
679
|
+
return {
|
|
680
|
+
source: "measure",
|
|
681
|
+
expression: normalizeMeasureExpression(operand.expression),
|
|
682
|
+
};
|
|
382
683
|
}
|
|
383
|
-
if (operand.source === "calculation")
|
|
384
|
-
|
|
684
|
+
if (operand.source === "calculation")
|
|
685
|
+
return { source: "calculation", field: operand.field };
|
|
686
|
+
return {
|
|
687
|
+
source: "constant",
|
|
688
|
+
value: Object.is(operand.value, -0) ? 0 : operand.value,
|
|
689
|
+
};
|
|
385
690
|
}
|
|
386
691
|
|
|
387
|
-
function normalizeReviewOperand(
|
|
388
|
-
|
|
692
|
+
function normalizeReviewOperand(
|
|
693
|
+
operand: DiagnosticReviewOperand,
|
|
694
|
+
): DiagnosticDeepReadonly<DiagnosticReviewOperand> {
|
|
695
|
+
if (operand.op === "constant")
|
|
696
|
+
return {
|
|
697
|
+
op: "constant",
|
|
698
|
+
value: Object.is(operand.value, -0) ? 0 : operand.value,
|
|
699
|
+
};
|
|
389
700
|
return normalizeMeasureExpression(operand);
|
|
390
701
|
}
|
|
391
702
|
|
|
392
|
-
function normalizeReviewFilter(
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
minDevelopmentAge: filter.minDevelopmentAge === undefined
|
|
703
|
+
function normalizeReviewFilter(
|
|
704
|
+
filter: DiagnosticReviewFilter,
|
|
705
|
+
axis: DiagnosticPeriodAxis,
|
|
706
|
+
): NormalizedDiagnosticReviewFilterIdentity {
|
|
707
|
+
const normalizeMany = (
|
|
708
|
+
side: "origin" | "valuation",
|
|
709
|
+
values: readonly string[] | undefined,
|
|
710
|
+
) =>
|
|
711
|
+
values === undefined
|
|
402
712
|
? null
|
|
403
|
-
:
|
|
404
|
-
|
|
713
|
+
: sortedUnique(
|
|
714
|
+
values.map(
|
|
715
|
+
(value) =>
|
|
716
|
+
normalizeDiagnosticPeriodWithAxis(axis, side, value)!.label,
|
|
717
|
+
),
|
|
718
|
+
);
|
|
719
|
+
const normalizeOne = (
|
|
720
|
+
side: "origin" | "valuation",
|
|
721
|
+
value: string | undefined,
|
|
722
|
+
) =>
|
|
723
|
+
value === undefined
|
|
405
724
|
? null
|
|
406
|
-
:
|
|
725
|
+
: normalizeDiagnosticPeriodWithAxis(axis, side, value)!.label;
|
|
726
|
+
return {
|
|
727
|
+
sourceGroups:
|
|
728
|
+
filter.sourceGroups === undefined
|
|
729
|
+
? null
|
|
730
|
+
: sortedUnique(filter.sourceGroups),
|
|
731
|
+
origins: normalizeMany("origin", filter.origins),
|
|
732
|
+
originFrom: normalizeOne("origin", filter.originFrom),
|
|
733
|
+
originThrough: normalizeOne("origin", filter.originThrough),
|
|
734
|
+
valuations: normalizeMany("valuation", filter.valuations),
|
|
735
|
+
valuationFrom: normalizeOne("valuation", filter.valuationFrom),
|
|
736
|
+
valuationThrough: normalizeOne("valuation", filter.valuationThrough),
|
|
737
|
+
minDevelopmentAge:
|
|
738
|
+
filter.minDevelopmentAge === undefined
|
|
739
|
+
? null
|
|
740
|
+
: Object.is(filter.minDevelopmentAge, -0)
|
|
741
|
+
? 0
|
|
742
|
+
: filter.minDevelopmentAge,
|
|
743
|
+
maxDevelopmentAge:
|
|
744
|
+
filter.maxDevelopmentAge === undefined
|
|
745
|
+
? null
|
|
746
|
+
: Object.is(filter.maxDevelopmentAge, -0)
|
|
747
|
+
? 0
|
|
748
|
+
: filter.maxDevelopmentAge,
|
|
407
749
|
};
|
|
408
750
|
}
|
|
409
751
|
|
|
410
|
-
function normalizePeriodAxis(
|
|
752
|
+
function normalizePeriodAxis(
|
|
753
|
+
axis: DiagnosticPeriodAxis,
|
|
754
|
+
): NormalizedDiagnosticPeriodAxisIdentity {
|
|
411
755
|
if (axis.kind === "calendar") {
|
|
412
756
|
return {
|
|
413
757
|
kind: "calendar",
|
|
@@ -419,11 +763,20 @@ function normalizePeriodAxis(axis: DiagnosticPeriodAxis): NormalizedDiagnosticPe
|
|
|
419
763
|
ageOffset: Object.is(axis.ageOffset, -0) ? 0 : axis.ageOffset,
|
|
420
764
|
};
|
|
421
765
|
}
|
|
422
|
-
const coordinates = (values: typeof axis.origins) =>
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
766
|
+
const coordinates = (values: typeof axis.origins) =>
|
|
767
|
+
values
|
|
768
|
+
.map((coordinate) => ({
|
|
769
|
+
label: coordinate.label,
|
|
770
|
+
aliases: sortedUnique(coordinate.aliases ?? []),
|
|
771
|
+
coordinate: Object.is(coordinate.coordinate, -0)
|
|
772
|
+
? 0
|
|
773
|
+
: coordinate.coordinate,
|
|
774
|
+
}))
|
|
775
|
+
.sort(
|
|
776
|
+
(left, right) =>
|
|
777
|
+
left.coordinate - right.coordinate ||
|
|
778
|
+
compareCodeUnits(left.label, right.label),
|
|
779
|
+
);
|
|
427
780
|
return {
|
|
428
781
|
kind: "ordered",
|
|
429
782
|
id: axis.id,
|
|
@@ -438,17 +791,26 @@ function normalizePeriodAxis(axis: DiagnosticPeriodAxis): NormalizedDiagnosticPe
|
|
|
438
791
|
function topologicalDerivations(
|
|
439
792
|
derivations: readonly DiagnosticDerivedMeasureDefinition[],
|
|
440
793
|
): readonly DiagnosticDerivedMeasureDefinition[] {
|
|
441
|
-
const byOutput = new Map(
|
|
442
|
-
|
|
794
|
+
const byOutput = new Map(
|
|
795
|
+
derivations.map((derivation) => [derivation.outputMeasureId, derivation]),
|
|
796
|
+
);
|
|
797
|
+
const remaining = new Map(
|
|
798
|
+
derivations.map((derivation) => [derivation.id, derivation]),
|
|
799
|
+
);
|
|
443
800
|
const emittedOutputs = new Set<string>();
|
|
444
801
|
const ordered: DiagnosticDerivedMeasureDefinition[] = [];
|
|
445
802
|
while (remaining.size > 0) {
|
|
446
|
-
const ready = [...remaining.values()]
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
803
|
+
const ready = [...remaining.values()]
|
|
804
|
+
.filter((derivation) =>
|
|
805
|
+
expressionDependencies(derivation.expression)
|
|
806
|
+
.filter((id) => byOutput.has(id))
|
|
807
|
+
.every((id) => emittedOutputs.has(id)),
|
|
808
|
+
)
|
|
809
|
+
.sort((left, right) => compareCodeUnits(left.id, right.id));
|
|
810
|
+
if (ready.length === 0)
|
|
811
|
+
return [...derivations].sort((left, right) =>
|
|
812
|
+
compareCodeUnits(left.id, right.id),
|
|
813
|
+
);
|
|
452
814
|
for (const derivation of ready) {
|
|
453
815
|
ordered.push(derivation);
|
|
454
816
|
emittedOutputs.add(derivation.outputMeasureId);
|
|
@@ -458,7 +820,10 @@ function topologicalDerivations(
|
|
|
458
820
|
return ordered;
|
|
459
821
|
}
|
|
460
822
|
|
|
461
|
-
function normalizeReviewRule(
|
|
823
|
+
function normalizeReviewRule(
|
|
824
|
+
rule: DiagnosticReviewRule,
|
|
825
|
+
axis: DiagnosticPeriodAxis,
|
|
826
|
+
): NormalizedDiagnosticReviewRuleIdentity {
|
|
462
827
|
const base: NormalizedDiagnosticReviewRuleBase = {
|
|
463
828
|
id: rule.id,
|
|
464
829
|
code: rule.code,
|
|
@@ -508,8 +873,21 @@ function normalizeReviewRule(rule: DiagnosticReviewRule): NormalizedDiagnosticRe
|
|
|
508
873
|
kind: "control-total",
|
|
509
874
|
expression: normalizeMeasureExpression(rule.expression),
|
|
510
875
|
expected: Object.is(rule.expected, -0) ? 0 : rule.expected,
|
|
511
|
-
filter:
|
|
512
|
-
|
|
876
|
+
filter:
|
|
877
|
+
rule.filter === undefined
|
|
878
|
+
? null
|
|
879
|
+
: normalizeReviewFilter(rule.filter, axis),
|
|
880
|
+
projection:
|
|
881
|
+
rule.projection.kind === "valuation"
|
|
882
|
+
? {
|
|
883
|
+
kind: "valuation",
|
|
884
|
+
valuation: normalizeDiagnosticPeriodWithAxis(
|
|
885
|
+
axis,
|
|
886
|
+
"valuation",
|
|
887
|
+
rule.projection.valuation,
|
|
888
|
+
)!.label,
|
|
889
|
+
}
|
|
890
|
+
: normalizeJsonValue(rule.projection),
|
|
513
891
|
};
|
|
514
892
|
}
|
|
515
893
|
|
|
@@ -521,80 +899,107 @@ export function normalizeDiagnosticDefinition(
|
|
|
521
899
|
id: definition.id,
|
|
522
900
|
version: definition.version,
|
|
523
901
|
lossRowGrain: definition.lossRowGrain,
|
|
524
|
-
measures: definition.measures
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
id
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
902
|
+
measures: definition.measures
|
|
903
|
+
.map((measure) => ({
|
|
904
|
+
id: measure.id,
|
|
905
|
+
displayName: measure.displayName,
|
|
906
|
+
description: measure.description,
|
|
907
|
+
source: measure.source,
|
|
908
|
+
kind: measure.kind,
|
|
909
|
+
unit: measure.unit,
|
|
910
|
+
developmentSemantics: measure.developmentSemantics,
|
|
911
|
+
aggregation: "sum" as const,
|
|
912
|
+
missing: measure.missing,
|
|
913
|
+
basisId: measure.basisId ?? null,
|
|
914
|
+
countPopulationId: measure.countPopulationId ?? null,
|
|
915
|
+
exposureBasisId: measure.exposureBasisId ?? null,
|
|
916
|
+
exposureTiming: measure.exposureTiming ?? null,
|
|
917
|
+
}))
|
|
918
|
+
.sort((left, right) => compareCodeUnits(left.id, right.id)),
|
|
919
|
+
countPopulations: definition.countPopulations
|
|
920
|
+
.map((population) => ({
|
|
921
|
+
id: population.id,
|
|
922
|
+
displayName: population.displayName,
|
|
923
|
+
subject: population.subject,
|
|
924
|
+
unit: population.unit,
|
|
925
|
+
description: population.description,
|
|
926
|
+
attributes: attributes(population.attributes),
|
|
927
|
+
}))
|
|
928
|
+
.sort((left, right) => compareCodeUnits(left.id, right.id)),
|
|
929
|
+
exposureBases: definition.exposureBases
|
|
930
|
+
.map((basis) => ({
|
|
931
|
+
id: basis.id,
|
|
932
|
+
displayName: basis.displayName,
|
|
933
|
+
basis: basis.basis,
|
|
934
|
+
unit: basis.unit,
|
|
935
|
+
description: basis.description,
|
|
936
|
+
sourceDescription: basis.sourceDescription ?? null,
|
|
937
|
+
attributes: attributes(basis.attributes),
|
|
938
|
+
}))
|
|
939
|
+
.sort((left, right) => compareCodeUnits(left.id, right.id)),
|
|
940
|
+
amountBases: definition.amountBases
|
|
941
|
+
.map((basis) => ({
|
|
942
|
+
id: basis.id,
|
|
943
|
+
displayName: basis.displayName,
|
|
944
|
+
currency: basis.currency,
|
|
945
|
+
perspective: basis.perspective,
|
|
946
|
+
components: basis.components
|
|
947
|
+
.map((component) => ({
|
|
948
|
+
id: component.id,
|
|
949
|
+
treatment: component.treatment,
|
|
950
|
+
limitation: normalizeLimitation(component.limitation),
|
|
951
|
+
}))
|
|
952
|
+
.sort((left, right) => compareCodeUnits(left.id, right.id)),
|
|
953
|
+
sourceDescription: basis.sourceDescription ?? null,
|
|
954
|
+
attributes: attributes(basis.attributes),
|
|
955
|
+
}))
|
|
956
|
+
.sort((left, right) => compareCodeUnits(left.id, right.id)),
|
|
957
|
+
derivedMeasures: topologicalDerivations(definition.derivedMeasures).map(
|
|
958
|
+
(derivation) => ({
|
|
959
|
+
id: derivation.id,
|
|
960
|
+
outputMeasureId: derivation.outputMeasureId,
|
|
961
|
+
expression: normalizeMeasureExpression(derivation.expression),
|
|
962
|
+
}),
|
|
963
|
+
),
|
|
964
|
+
formulas: definition.formulas
|
|
965
|
+
.map((formula) => ({
|
|
966
|
+
id: formula.id,
|
|
967
|
+
version: formula.version,
|
|
968
|
+
roles: sortedRecord(
|
|
969
|
+
Object.entries(formula.roles).map(
|
|
970
|
+
([name, role]) =>
|
|
971
|
+
[
|
|
972
|
+
name,
|
|
973
|
+
{
|
|
974
|
+
kind: role.kind,
|
|
975
|
+
compatibilityGroup: role.compatibilityGroup ?? null,
|
|
976
|
+
developmentSemantics: role.developmentSemantics ?? null,
|
|
977
|
+
},
|
|
978
|
+
] as const,
|
|
979
|
+
),
|
|
980
|
+
),
|
|
981
|
+
numerator: normalizeRoleExpression(formula.numerator),
|
|
982
|
+
denominator: normalizeRoleExpression(formula.denominator),
|
|
983
|
+
denominatorPolicy: "positive-or-null" as const,
|
|
984
|
+
}))
|
|
985
|
+
.sort((left, right) => compareCodeUnits(left.id, right.id)),
|
|
586
986
|
instances: definition.instances.map((instance) => ({
|
|
587
987
|
id: instance.id,
|
|
588
988
|
version: instance.version,
|
|
589
989
|
formulaId: instance.formulaId,
|
|
590
|
-
bindings: sortedRecord(
|
|
591
|
-
|
|
592
|
-
|
|
990
|
+
bindings: sortedRecord(
|
|
991
|
+
Object.entries(instance.bindings).map(
|
|
992
|
+
([role, expression]) =>
|
|
993
|
+
[role, normalizeMeasureExpression(expression)] as const,
|
|
994
|
+
),
|
|
995
|
+
),
|
|
593
996
|
presentation: {
|
|
594
997
|
displayName: instance.presentation.displayName,
|
|
595
998
|
description: instance.presentation.description,
|
|
596
999
|
displayUnit: instance.presentation.displayUnit,
|
|
597
|
-
scale: Object.is(instance.presentation.scale, -0)
|
|
1000
|
+
scale: Object.is(instance.presentation.scale, -0)
|
|
1001
|
+
? 0
|
|
1002
|
+
: instance.presentation.scale,
|
|
598
1003
|
numeratorLabel: instance.presentation.numeratorLabel,
|
|
599
1004
|
denominatorLabel: instance.presentation.denominatorLabel,
|
|
600
1005
|
},
|
|
@@ -611,7 +1016,9 @@ export function normalizeDiagnosticDefinition(
|
|
|
611
1016
|
},
|
|
612
1017
|
})),
|
|
613
1018
|
})),
|
|
614
|
-
reviewRules: definition.reviewRules.map(
|
|
1019
|
+
reviewRules: definition.reviewRules.map((rule) =>
|
|
1020
|
+
normalizeReviewRule(rule, definition.periodAxis),
|
|
1021
|
+
),
|
|
615
1022
|
periodAxis: normalizePeriodAxis(definition.periodAxis),
|
|
616
1023
|
};
|
|
617
1024
|
return deepFreeze(normalized);
|
|
@@ -624,60 +1031,116 @@ export function normalizeDiagnosticSourceLocation(
|
|
|
624
1031
|
artifactId: source.artifactId,
|
|
625
1032
|
sourceFile: source.sourceFile ?? null,
|
|
626
1033
|
sourceSheet: source.sourceSheet ?? null,
|
|
627
|
-
sourceRow:
|
|
1034
|
+
sourceRow:
|
|
1035
|
+
source.sourceRow === undefined
|
|
1036
|
+
? null
|
|
1037
|
+
: Object.is(source.sourceRow, -0)
|
|
1038
|
+
? 0
|
|
1039
|
+
: source.sourceRow,
|
|
628
1040
|
sourceCell: source.sourceCell ?? null,
|
|
629
1041
|
});
|
|
630
1042
|
}
|
|
631
1043
|
|
|
632
|
-
function expressionDependencies(
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
1044
|
+
function expressionDependencies(
|
|
1045
|
+
expression: DiagnosticMeasureExpression | DiagnosticClaimExpression,
|
|
1046
|
+
): readonly string[] {
|
|
1047
|
+
if (expression.op === "measure" || expression.op === "claim-layer")
|
|
1048
|
+
return [expression.measureId];
|
|
1049
|
+
if (expression.op === "subtract")
|
|
1050
|
+
return sortedUnique([
|
|
1051
|
+
...expressionDependencies(expression.left),
|
|
1052
|
+
...expressionDependencies(expression.right),
|
|
1053
|
+
]);
|
|
638
1054
|
return sortedUnique(expression.terms.flatMap(expressionDependencies));
|
|
639
1055
|
}
|
|
640
1056
|
|
|
641
|
-
function ruleMeasureDependencies(
|
|
642
|
-
|
|
1057
|
+
function ruleMeasureDependencies(
|
|
1058
|
+
operand: DiagnosticDeepReadonly<DiagnosticRuleOperand>,
|
|
1059
|
+
): readonly string[] {
|
|
1060
|
+
return operand.source === "measure"
|
|
1061
|
+
? expressionDependencies(operand.expression)
|
|
1062
|
+
: [];
|
|
643
1063
|
}
|
|
644
1064
|
|
|
645
1065
|
function tag<K extends string, T>(kind: K, property: string, value: T): string {
|
|
646
|
-
return `fnv1a64-jcs-v1:${fnv1a64(
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
1066
|
+
return `fnv1a64-jcs-v1:${fnv1a64(
|
|
1067
|
+
canonicalJson({
|
|
1068
|
+
identityVersion: 1,
|
|
1069
|
+
kind,
|
|
1070
|
+
[property]: value,
|
|
1071
|
+
}),
|
|
1072
|
+
)}`;
|
|
651
1073
|
}
|
|
652
1074
|
|
|
653
1075
|
export interface BuiltDiagnosticIdentities {
|
|
654
1076
|
readonly formulaFingerprints: Readonly<Record<string, string>>;
|
|
655
1077
|
readonly calculationFingerprints: Readonly<Record<string, string>>;
|
|
656
1078
|
readonly definitionIntegrity: string;
|
|
657
|
-
readonly calculationScopesByInstanceId: ReadonlyMap<
|
|
658
|
-
|
|
659
|
-
|
|
1079
|
+
readonly calculationScopesByInstanceId: ReadonlyMap<
|
|
1080
|
+
string,
|
|
1081
|
+
NormalizedDiagnosticCalculationScope
|
|
1082
|
+
>;
|
|
1083
|
+
readonly calculationDependenciesByInstanceId: ReadonlyMap<
|
|
1084
|
+
string,
|
|
1085
|
+
readonly string[]
|
|
1086
|
+
>;
|
|
1087
|
+
readonly evaluationDependenciesByInstanceId: ReadonlyMap<
|
|
1088
|
+
string,
|
|
1089
|
+
readonly string[]
|
|
1090
|
+
>;
|
|
660
1091
|
}
|
|
661
1092
|
|
|
662
1093
|
export function buildDiagnosticIdentities(
|
|
663
1094
|
definition: DiagnosticDeepReadonly<NormalizedDiagnosticDefinitionIdentity>,
|
|
664
1095
|
): BuiltDiagnosticIdentities {
|
|
665
|
-
const formulas = new Map(
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
const
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
1096
|
+
const formulas = new Map(
|
|
1097
|
+
definition.formulas.map((formula) => [formula.id, formula]),
|
|
1098
|
+
);
|
|
1099
|
+
const measures = new Map(
|
|
1100
|
+
definition.measures.map((measure) => [measure.id, measure]),
|
|
1101
|
+
);
|
|
1102
|
+
const derivations = new Map(
|
|
1103
|
+
definition.derivedMeasures.map((derivation) => [
|
|
1104
|
+
derivation.outputMeasureId,
|
|
1105
|
+
derivation,
|
|
1106
|
+
]),
|
|
1107
|
+
);
|
|
1108
|
+
const populations = new Map(
|
|
1109
|
+
definition.countPopulations.map((population) => [
|
|
1110
|
+
population.id,
|
|
1111
|
+
population,
|
|
1112
|
+
]),
|
|
1113
|
+
);
|
|
1114
|
+
const exposureBases = new Map(
|
|
1115
|
+
definition.exposureBases.map((basis) => [basis.id, basis]),
|
|
1116
|
+
);
|
|
1117
|
+
const amountBases = new Map(
|
|
1118
|
+
definition.amountBases.map((basis) => [basis.id, basis]),
|
|
1119
|
+
);
|
|
1120
|
+
|
|
1121
|
+
const formulaFingerprints = sortedRecord(
|
|
1122
|
+
definition.formulas.map(
|
|
1123
|
+
(formula) =>
|
|
1124
|
+
[formula.id, tag("diagnostic-formula", "formula", formula)] as const,
|
|
1125
|
+
),
|
|
1126
|
+
);
|
|
675
1127
|
const calculationFingerprints = Object.create(null) as Record<string, string>;
|
|
676
|
-
const calculationScopesByInstanceId = new Map<
|
|
677
|
-
|
|
678
|
-
|
|
1128
|
+
const calculationScopesByInstanceId = new Map<
|
|
1129
|
+
string,
|
|
1130
|
+
NormalizedDiagnosticCalculationScope
|
|
1131
|
+
>();
|
|
1132
|
+
const calculationDependenciesByInstanceId = new Map<
|
|
1133
|
+
string,
|
|
1134
|
+
readonly string[]
|
|
1135
|
+
>();
|
|
1136
|
+
const evaluationDependenciesByInstanceId = new Map<
|
|
1137
|
+
string,
|
|
1138
|
+
readonly string[]
|
|
1139
|
+
>();
|
|
679
1140
|
|
|
680
|
-
const transitiveDependencies = (
|
|
1141
|
+
const transitiveDependencies = (
|
|
1142
|
+
roots: readonly string[],
|
|
1143
|
+
): readonly string[] => {
|
|
681
1144
|
const found = new Set<string>();
|
|
682
1145
|
const stack = [...roots];
|
|
683
1146
|
while (stack.length > 0) {
|
|
@@ -685,7 +1148,8 @@ export function buildDiagnosticIdentities(
|
|
|
685
1148
|
if (found.has(id)) continue;
|
|
686
1149
|
found.add(id);
|
|
687
1150
|
const derivation = derivations.get(id);
|
|
688
|
-
if (derivation)
|
|
1151
|
+
if (derivation)
|
|
1152
|
+
stack.push(...expressionDependencies(derivation.expression));
|
|
689
1153
|
}
|
|
690
1154
|
return [...found].sort(compareCodeUnits);
|
|
691
1155
|
};
|
|
@@ -701,14 +1165,34 @@ export function buildDiagnosticIdentities(
|
|
|
701
1165
|
...ruleMeasureDependencies(rule.when.right),
|
|
702
1166
|
]),
|
|
703
1167
|
]);
|
|
704
|
-
calculationDependenciesByInstanceId.set(
|
|
1168
|
+
calculationDependenciesByInstanceId.set(
|
|
1169
|
+
instance.id,
|
|
1170
|
+
calculationDependencies,
|
|
1171
|
+
);
|
|
705
1172
|
evaluationDependenciesByInstanceId.set(instance.id, evaluationDependencies);
|
|
706
1173
|
|
|
707
|
-
const selectedMeasures = calculationDependencies
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
const
|
|
711
|
-
|
|
1174
|
+
const selectedMeasures = calculationDependencies
|
|
1175
|
+
.map((id) => measures.get(id)!)
|
|
1176
|
+
.filter(Boolean);
|
|
1177
|
+
const selectedDerivations = definition.derivedMeasures.filter(
|
|
1178
|
+
(derivation) =>
|
|
1179
|
+
calculationDependencies.includes(derivation.outputMeasureId),
|
|
1180
|
+
);
|
|
1181
|
+
const countPopulationIds = sortedUnique(
|
|
1182
|
+
selectedMeasures.flatMap((measure) =>
|
|
1183
|
+
measure.countPopulationId === null ? [] : [measure.countPopulationId],
|
|
1184
|
+
),
|
|
1185
|
+
);
|
|
1186
|
+
const exposureBasisIds = sortedUnique(
|
|
1187
|
+
selectedMeasures.flatMap((measure) =>
|
|
1188
|
+
measure.exposureBasisId === null ? [] : [measure.exposureBasisId],
|
|
1189
|
+
),
|
|
1190
|
+
);
|
|
1191
|
+
const amountBasisIds = sortedUnique(
|
|
1192
|
+
selectedMeasures.flatMap((measure) =>
|
|
1193
|
+
measure.basisId === null ? [] : [measure.basisId],
|
|
1194
|
+
),
|
|
1195
|
+
);
|
|
712
1196
|
const scope: NormalizedDiagnosticCalculationScope = {
|
|
713
1197
|
formulaFingerprint: formulaFingerprints[instance.formulaId]!,
|
|
714
1198
|
instance: {
|
|
@@ -733,11 +1217,21 @@ export function buildDiagnosticIdentities(
|
|
|
733
1217
|
})),
|
|
734
1218
|
countPopulations: countPopulationIds.map((id) => {
|
|
735
1219
|
const population = populations.get(id)!;
|
|
736
|
-
return {
|
|
1220
|
+
return {
|
|
1221
|
+
id,
|
|
1222
|
+
subject: population.subject,
|
|
1223
|
+
unit: population.unit,
|
|
1224
|
+
attributes: population.attributes,
|
|
1225
|
+
};
|
|
737
1226
|
}),
|
|
738
1227
|
exposureBases: exposureBasisIds.map((id) => {
|
|
739
1228
|
const basis = exposureBases.get(id)!;
|
|
740
|
-
return {
|
|
1229
|
+
return {
|
|
1230
|
+
id,
|
|
1231
|
+
basis: basis.basis,
|
|
1232
|
+
unit: basis.unit,
|
|
1233
|
+
attributes: basis.attributes,
|
|
1234
|
+
};
|
|
741
1235
|
}),
|
|
742
1236
|
amountBases: amountBasisIds.map((id) => {
|
|
743
1237
|
const basis = amountBases.get(id)!;
|
|
@@ -753,12 +1247,18 @@ export function buildDiagnosticIdentities(
|
|
|
753
1247
|
};
|
|
754
1248
|
const frozenScope = deepFreeze(scope);
|
|
755
1249
|
calculationScopesByInstanceId.set(instance.id, frozenScope);
|
|
756
|
-
calculationFingerprints[instance.id] = tag(
|
|
1250
|
+
calculationFingerprints[instance.id] = tag(
|
|
1251
|
+
"diagnostic-calculation",
|
|
1252
|
+
"calculation",
|
|
1253
|
+
frozenScope,
|
|
1254
|
+
);
|
|
757
1255
|
}
|
|
758
1256
|
|
|
759
1257
|
return {
|
|
760
1258
|
formulaFingerprints: deepFreeze(formulaFingerprints),
|
|
761
|
-
calculationFingerprints: deepFreeze(
|
|
1259
|
+
calculationFingerprints: deepFreeze(
|
|
1260
|
+
sortedRecord(Object.entries(calculationFingerprints)),
|
|
1261
|
+
),
|
|
762
1262
|
definitionIntegrity: tag("diagnostic-definition", "definition", definition),
|
|
763
1263
|
calculationScopesByInstanceId,
|
|
764
1264
|
calculationDependenciesByInstanceId,
|