@actuarial-ts/core 0.5.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 +65 -226
- package/dist/capping.d.ts.map +1 -1
- package/dist/capping.js +39 -6
- package/dist/capping.js.map +1 -1
- package/dist/casualtyDiagnostics.d.ts +19 -49
- package/dist/casualtyDiagnostics.d.ts.map +1 -1
- package/dist/casualtyDiagnostics.js +84 -116
- package/dist/casualtyDiagnostics.js.map +1 -1
- package/dist/diagnosticAggregation.d.ts +32 -0
- package/dist/diagnosticAggregation.d.ts.map +1 -0
- package/dist/diagnosticAggregation.js +73 -0
- package/dist/diagnosticAggregation.js.map +1 -0
- package/dist/diagnosticDefinitions.d.ts +289 -0
- package/dist/diagnosticDefinitions.d.ts.map +1 -0
- package/dist/diagnosticDefinitions.js +1688 -0
- package/dist/diagnosticDefinitions.js.map +1 -0
- package/dist/diagnosticDerivations.d.ts +35 -0
- package/dist/diagnosticDerivations.d.ts.map +1 -0
- package/dist/diagnosticDerivations.js +224 -0
- package/dist/diagnosticDerivations.js.map +1 -0
- package/dist/diagnosticExposure.d.ts +51 -0
- package/dist/diagnosticExposure.d.ts.map +1 -0
- package/dist/diagnosticExposure.js +273 -0
- package/dist/diagnosticExposure.js.map +1 -0
- package/dist/diagnosticExpressions.d.ts +56 -0
- package/dist/diagnosticExpressions.d.ts.map +1 -0
- package/dist/diagnosticExpressions.js +124 -0
- package/dist/diagnosticExpressions.js.map +1 -0
- package/dist/diagnosticFormulas.d.ts +198 -0
- package/dist/diagnosticFormulas.d.ts.map +1 -0
- package/dist/diagnosticFormulas.js +219 -0
- package/dist/diagnosticFormulas.js.map +1 -0
- package/dist/diagnosticIdentity.d.ts +251 -0
- package/dist/diagnosticIdentity.d.ts.map +1 -0
- package/dist/diagnosticIdentity.js +721 -0
- package/dist/diagnosticIdentity.js.map +1 -0
- 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 +15 -0
- package/dist/diagnosticPeriods.d.ts.map +1 -0
- package/dist/diagnosticPeriods.js +50 -0
- package/dist/diagnosticPeriods.js.map +1 -0
- package/dist/diagnosticPreparation.d.ts +131 -0
- package/dist/diagnosticPreparation.d.ts.map +1 -0
- package/dist/diagnosticPreparation.js +1539 -0
- package/dist/diagnosticPreparation.js.map +1 -0
- package/dist/diagnosticReview.d.ts +66 -0
- package/dist/diagnosticReview.d.ts.map +1 -0
- package/dist/diagnosticReview.js +490 -0
- package/dist/diagnosticReview.js.map +1 -0
- package/dist/diagnosticRules.d.ts +32 -0
- package/dist/diagnosticRules.d.ts.map +1 -0
- package/dist/diagnosticRules.js +32 -0
- package/dist/diagnosticRules.js.map +1 -0
- package/dist/diagnosticRunner.d.ts +86 -0
- package/dist/diagnosticRunner.d.ts.map +1 -0
- package/dist/diagnosticRunner.js +703 -0
- package/dist/diagnosticRunner.js.map +1 -0
- 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 +28 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -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 +21 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +128 -0
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +4 -3
- package/src/capping.ts +109 -18
- package/src/casualtyDiagnostics.ts +79 -189
- package/src/diagnosticAggregation.ts +128 -0
- package/src/diagnosticDefinitions.ts +3624 -0
- package/src/diagnosticDerivations.ts +321 -0
- package/src/diagnosticExposure.ts +482 -0
- package/src/diagnosticExpressions.ts +255 -0
- package/src/diagnosticFormulas.ts +350 -0
- package/src/diagnosticIdentity.ts +1267 -0
- package/src/diagnosticOrdering.ts +106 -0
- package/src/diagnosticPeriodAxis.ts +92 -0
- package/src/diagnosticPeriods.ts +102 -0
- package/src/diagnosticPreparation.ts +2475 -0
- package/src/diagnosticReview.ts +897 -0
- package/src/diagnosticRules.ts +69 -0
- package/src/diagnosticRunner.ts +1114 -0
- package/src/diagnosticRuntime.ts +264 -0
- package/src/diagnosticSourceOrdering.ts +50 -0
- package/src/index.ts +184 -1
- package/src/mack.ts +72 -9
- package/src/triangle.ts +103 -13
- package/src/types.ts +196 -0
- package/src/version.ts +2 -0
- package/dist/metricDiagnostics.d.ts +0 -212
- package/dist/metricDiagnostics.d.ts.map +0 -1
- package/dist/metricDiagnostics.js +0 -627
- package/dist/metricDiagnostics.js.map +0 -1
- package/src/metricDiagnostics.ts +0 -876
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { canonicalJson } from "./canonical.js";
|
|
2
|
+
import type {
|
|
3
|
+
DiagnosticMeasureContribution,
|
|
4
|
+
DiagnosticStructuralBlocker,
|
|
5
|
+
} from "./diagnosticAggregation.js";
|
|
6
|
+
import type { DiagnosticMetricFinding } from "./diagnosticFormulas.js";
|
|
7
|
+
import { compareDiagnosticSourceLocations } from "./diagnosticSourceOrdering.js";
|
|
8
|
+
import type { DiagnosticSourceLocation } from "./diagnosticDefinitions.js";
|
|
9
|
+
|
|
10
|
+
/** Section 15 total order, for values already validated by a public boundary. */
|
|
11
|
+
export function compareDiagnosticIdentityValues(
|
|
12
|
+
left: unknown,
|
|
13
|
+
right: unknown,
|
|
14
|
+
): number {
|
|
15
|
+
if (left === right) return 0;
|
|
16
|
+
if (left === undefined) return -1;
|
|
17
|
+
if (right === undefined) return 1;
|
|
18
|
+
if (left === null) return -1;
|
|
19
|
+
if (right === null) return 1;
|
|
20
|
+
if (typeof left === "number" && typeof right === "number")
|
|
21
|
+
return left - right;
|
|
22
|
+
if (typeof left === "boolean" && typeof right === "boolean")
|
|
23
|
+
return Number(left) - Number(right);
|
|
24
|
+
if (Array.isArray(left) && Array.isArray(right)) {
|
|
25
|
+
for (let i = 0; i < Math.min(left.length, right.length); i++) {
|
|
26
|
+
const comparison = compareDiagnosticIdentityValues(left[i], right[i]);
|
|
27
|
+
if (comparison !== 0) return comparison;
|
|
28
|
+
}
|
|
29
|
+
return left.length - right.length;
|
|
30
|
+
}
|
|
31
|
+
const a = typeof left === "string" ? left : canonicalJson(left);
|
|
32
|
+
const b = typeof right === "string" ? right : canonicalJson(right);
|
|
33
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function compareSources(
|
|
37
|
+
left: readonly DiagnosticSourceLocation[],
|
|
38
|
+
right: readonly DiagnosticSourceLocation[],
|
|
39
|
+
): number {
|
|
40
|
+
for (let i = 0; i < Math.min(left.length, right.length); i++) {
|
|
41
|
+
const comparison = compareDiagnosticSourceLocations(left[i]!, right[i]!);
|
|
42
|
+
if (comparison !== 0) return comparison;
|
|
43
|
+
}
|
|
44
|
+
return left.length - right.length;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function compareDiagnosticFindings(
|
|
48
|
+
left: DiagnosticMetricFinding,
|
|
49
|
+
right: DiagnosticMetricFinding,
|
|
50
|
+
): number {
|
|
51
|
+
const fields = [
|
|
52
|
+
"category",
|
|
53
|
+
"severity",
|
|
54
|
+
"code",
|
|
55
|
+
"ruleId",
|
|
56
|
+
"instanceId",
|
|
57
|
+
"measureId",
|
|
58
|
+
"expressionPath",
|
|
59
|
+
"sourceGroup",
|
|
60
|
+
"group",
|
|
61
|
+
"origin",
|
|
62
|
+
"valuation",
|
|
63
|
+
"developmentAge",
|
|
64
|
+
"ageUnit",
|
|
65
|
+
"recordId",
|
|
66
|
+
"claimId",
|
|
67
|
+
"exposureKey",
|
|
68
|
+
"message",
|
|
69
|
+
] as const;
|
|
70
|
+
return (
|
|
71
|
+
compareDiagnosticIdentityValues(
|
|
72
|
+
fields.map((key) => left[key]),
|
|
73
|
+
fields.map((key) => right[key]),
|
|
74
|
+
) ||
|
|
75
|
+
compareDiagnosticIdentityValues(left.offendingKey, right.offendingKey) ||
|
|
76
|
+
compareSources(left.sources, right.sources)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function compareDiagnosticContributions(
|
|
81
|
+
left: DiagnosticMeasureContribution,
|
|
82
|
+
right: DiagnosticMeasureContribution,
|
|
83
|
+
): number {
|
|
84
|
+
return (
|
|
85
|
+
compareDiagnosticIdentityValues(
|
|
86
|
+
[left.sourceId, left.status, left.deduplicated],
|
|
87
|
+
[right.sourceId, right.status, right.deduplicated],
|
|
88
|
+
) ||
|
|
89
|
+
compareSources(left.sources, right.sources) ||
|
|
90
|
+
compareDiagnosticIdentityValues(left, right)
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function compareDiagnosticBlockers(
|
|
95
|
+
left: DiagnosticStructuralBlocker,
|
|
96
|
+
right: DiagnosticStructuralBlocker,
|
|
97
|
+
): number {
|
|
98
|
+
return (
|
|
99
|
+
compareDiagnosticIdentityValues(
|
|
100
|
+
[left.code, left.message, left.sourceIds],
|
|
101
|
+
[right.code, right.message, right.sourceIds],
|
|
102
|
+
) ||
|
|
103
|
+
compareSources(left.sources, right.sources) ||
|
|
104
|
+
compareDiagnosticIdentityValues(left.finding, right.finding)
|
|
105
|
+
);
|
|
106
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { DiagnosticPeriodAxis } from "./diagnosticDefinitions.js";
|
|
2
|
+
|
|
3
|
+
export interface NormalizedDiagnosticAxisPeriod {
|
|
4
|
+
readonly label: string;
|
|
5
|
+
readonly coordinate: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type DiagnosticPeriodSide = "origin" | "valuation";
|
|
9
|
+
|
|
10
|
+
function calendarCoordinate(
|
|
11
|
+
label: string,
|
|
12
|
+
cadence: "month" | "quarter" | "year",
|
|
13
|
+
anchor: "start" | "end",
|
|
14
|
+
): number | null {
|
|
15
|
+
let year: number;
|
|
16
|
+
let start: number;
|
|
17
|
+
let width: number;
|
|
18
|
+
if (cadence === "year") {
|
|
19
|
+
const match = /^(\d{4})$/.exec(label);
|
|
20
|
+
if (!match) return null;
|
|
21
|
+
year = Number(match[1]);
|
|
22
|
+
start = year * 12;
|
|
23
|
+
width = 12;
|
|
24
|
+
} else if (cadence === "month") {
|
|
25
|
+
const match = /^(\d{4})-(0[1-9]|1[0-2])$/.exec(label);
|
|
26
|
+
if (!match) return null;
|
|
27
|
+
year = Number(match[1]);
|
|
28
|
+
start = year * 12 + Number(match[2]) - 1;
|
|
29
|
+
width = 1;
|
|
30
|
+
} else {
|
|
31
|
+
const compact = /^(\d{4})Q([1-4])$/.exec(label);
|
|
32
|
+
const hyphenated = /^(\d{4})-Q([1-4])$/.exec(label);
|
|
33
|
+
const first = /^Q([1-4]) (\d{4})$/.exec(label);
|
|
34
|
+
const match = compact ?? hyphenated ?? first;
|
|
35
|
+
if (!match) return null;
|
|
36
|
+
const quarterFirst = match === first;
|
|
37
|
+
year = Number(match[quarterFirst ? 2 : 1]);
|
|
38
|
+
const quarter = Number(match[quarterFirst ? 1 : 2]);
|
|
39
|
+
start = year * 12 + (quarter - 1) * 3;
|
|
40
|
+
width = 3;
|
|
41
|
+
}
|
|
42
|
+
if (year < 1 || year > 9999) return null;
|
|
43
|
+
return start + (anchor === "end" ? width : 0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Pure axis-level period normalization shared by compilation and execution. */
|
|
47
|
+
export function normalizeDiagnosticPeriodWithAxis(
|
|
48
|
+
axis: DiagnosticPeriodAxis,
|
|
49
|
+
side: DiagnosticPeriodSide,
|
|
50
|
+
label: unknown,
|
|
51
|
+
): NormalizedDiagnosticAxisPeriod | null {
|
|
52
|
+
if (typeof label !== "string" || axis === null || typeof axis !== "object")
|
|
53
|
+
return null;
|
|
54
|
+
if (axis.kind === "calendar") {
|
|
55
|
+
const cadence =
|
|
56
|
+
side === "origin" ? axis.originCadence : axis.valuationCadence;
|
|
57
|
+
const anchor = side === "origin" ? axis.originAnchor : axis.valuationAnchor;
|
|
58
|
+
if (
|
|
59
|
+
!["month", "quarter", "year"].includes(cadence) ||
|
|
60
|
+
!["start", "end"].includes(anchor)
|
|
61
|
+
)
|
|
62
|
+
return null;
|
|
63
|
+
const coordinate = calendarCoordinate(label, cadence, anchor);
|
|
64
|
+
if (coordinate === null) return null;
|
|
65
|
+
const canonicalLabel =
|
|
66
|
+
cadence === "quarter"
|
|
67
|
+
? (() => {
|
|
68
|
+
const startCoordinate = calendarCoordinate(
|
|
69
|
+
label,
|
|
70
|
+
cadence,
|
|
71
|
+
"start",
|
|
72
|
+
)!;
|
|
73
|
+
const year = Math.floor(startCoordinate / 12);
|
|
74
|
+
return `${String(year).padStart(4, "0")}-Q${Math.floor((startCoordinate % 12) / 3) + 1}`;
|
|
75
|
+
})()
|
|
76
|
+
: label;
|
|
77
|
+
return { label: canonicalLabel, coordinate };
|
|
78
|
+
}
|
|
79
|
+
if (
|
|
80
|
+
axis.kind !== "ordered" ||
|
|
81
|
+
!Array.isArray(axis[side === "origin" ? "origins" : "valuations"])
|
|
82
|
+
)
|
|
83
|
+
return null;
|
|
84
|
+
const catalog = side === "origin" ? axis.origins : axis.valuations;
|
|
85
|
+
const item = catalog.find(
|
|
86
|
+
(coordinate) =>
|
|
87
|
+
coordinate?.label === label || coordinate?.aliases?.includes(label),
|
|
88
|
+
);
|
|
89
|
+
return item && typeof item.coordinate === "number"
|
|
90
|
+
? { label: item.label, coordinate: item.coordinate }
|
|
91
|
+
: null;
|
|
92
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CompiledDiagnosticDefinition,
|
|
3
|
+
DiagnosticPeriodAxis,
|
|
4
|
+
} from "./diagnosticDefinitions.js";
|
|
5
|
+
import { assertCompiledDiagnosticDefinition } from "./diagnosticDefinitions.js";
|
|
6
|
+
import {
|
|
7
|
+
normalizeDiagnosticPeriodWithAxis,
|
|
8
|
+
type DiagnosticPeriodSide,
|
|
9
|
+
} from "./diagnosticPeriodAxis.js";
|
|
10
|
+
import { DiagnosticValidationError } from "./types.js";
|
|
11
|
+
|
|
12
|
+
export interface DiagnosticNormalizedPeriod {
|
|
13
|
+
readonly label: string;
|
|
14
|
+
readonly coordinate: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function normalizeWithAxis(
|
|
18
|
+
axis: DiagnosticPeriodAxis,
|
|
19
|
+
side: DiagnosticPeriodSide,
|
|
20
|
+
label: string,
|
|
21
|
+
): DiagnosticNormalizedPeriod {
|
|
22
|
+
const normalized = normalizeDiagnosticPeriodWithAxis(axis, side, label);
|
|
23
|
+
if (!normalized)
|
|
24
|
+
throw new DiagnosticValidationError([
|
|
25
|
+
{
|
|
26
|
+
domain: "input",
|
|
27
|
+
code: "invalid-period",
|
|
28
|
+
path: `$.${side}`,
|
|
29
|
+
message:
|
|
30
|
+
axis.kind === "calendar"
|
|
31
|
+
? `Invalid ${side === "origin" ? axis.originCadence : axis.valuationCadence} ${side} label ${JSON.stringify(label)}`
|
|
32
|
+
: `Unknown ordered-axis ${side} label ${JSON.stringify(label)}`,
|
|
33
|
+
},
|
|
34
|
+
]);
|
|
35
|
+
return Object.freeze(normalized);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function normalizeDiagnosticPeriod(
|
|
39
|
+
definition: CompiledDiagnosticDefinition,
|
|
40
|
+
side: DiagnosticPeriodSide,
|
|
41
|
+
label: string,
|
|
42
|
+
): DiagnosticNormalizedPeriod {
|
|
43
|
+
assertCompiledDiagnosticDefinition(definition);
|
|
44
|
+
return normalizeWithAxis(
|
|
45
|
+
definition.definition.periodAxis as DiagnosticPeriodAxis,
|
|
46
|
+
side,
|
|
47
|
+
label,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function diagnosticDevelopmentAge(
|
|
52
|
+
definition: CompiledDiagnosticDefinition,
|
|
53
|
+
origin: string,
|
|
54
|
+
valuation: string,
|
|
55
|
+
): {
|
|
56
|
+
readonly origin: DiagnosticNormalizedPeriod;
|
|
57
|
+
readonly valuation: DiagnosticNormalizedPeriod;
|
|
58
|
+
readonly developmentAge: number;
|
|
59
|
+
readonly ageUnit: string;
|
|
60
|
+
} {
|
|
61
|
+
const normalizedOrigin = normalizeDiagnosticPeriod(
|
|
62
|
+
definition,
|
|
63
|
+
"origin",
|
|
64
|
+
origin,
|
|
65
|
+
);
|
|
66
|
+
const normalizedValuation = normalizeDiagnosticPeriod(
|
|
67
|
+
definition,
|
|
68
|
+
"valuation",
|
|
69
|
+
valuation,
|
|
70
|
+
);
|
|
71
|
+
const axis = definition.definition.periodAxis;
|
|
72
|
+
const developmentAge =
|
|
73
|
+
normalizedValuation.coordinate -
|
|
74
|
+
normalizedOrigin.coordinate +
|
|
75
|
+
axis.ageOffset;
|
|
76
|
+
if (!Number.isSafeInteger(developmentAge) || developmentAge < 0)
|
|
77
|
+
throw new DiagnosticValidationError([
|
|
78
|
+
{
|
|
79
|
+
domain: "input",
|
|
80
|
+
code: "invalid-period",
|
|
81
|
+
path: "$.valuation",
|
|
82
|
+
message:
|
|
83
|
+
"Valuation period precedes origin period or development age is unsafe",
|
|
84
|
+
},
|
|
85
|
+
]);
|
|
86
|
+
return Object.freeze({
|
|
87
|
+
origin: normalizedOrigin,
|
|
88
|
+
valuation: normalizedValuation,
|
|
89
|
+
developmentAge,
|
|
90
|
+
ageUnit: axis.ageUnit,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function compareDiagnosticPeriods(
|
|
95
|
+
left: DiagnosticNormalizedPeriod,
|
|
96
|
+
right: DiagnosticNormalizedPeriod,
|
|
97
|
+
): number {
|
|
98
|
+
return (
|
|
99
|
+
left.coordinate - right.coordinate ||
|
|
100
|
+
(left.label < right.label ? -1 : left.label > right.label ? 1 : 0)
|
|
101
|
+
);
|
|
102
|
+
}
|