@actuarial-ts/core 0.6.0 → 0.7.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 (133) hide show
  1. package/README.md +12 -3
  2. package/dist/canonical.d.ts +11 -0
  3. package/dist/canonical.d.ts.map +1 -1
  4. package/dist/canonical.js +136 -26
  5. package/dist/canonical.js.map +1 -1
  6. package/dist/capping.d.ts.map +1 -1
  7. package/dist/capping.js +39 -6
  8. package/dist/capping.js.map +1 -1
  9. package/dist/diagnosticAggregation.d.ts +3 -0
  10. package/dist/diagnosticAggregation.d.ts.map +1 -1
  11. package/dist/diagnosticAggregation.js +30 -7
  12. package/dist/diagnosticAggregation.js.map +1 -1
  13. package/dist/diagnosticDefinitions.d.ts +3 -0
  14. package/dist/diagnosticDefinitions.d.ts.map +1 -1
  15. package/dist/diagnosticDefinitions.js +844 -182
  16. package/dist/diagnosticDefinitions.js.map +1 -1
  17. package/dist/diagnosticDerivations.d.ts +23 -0
  18. package/dist/diagnosticDerivations.d.ts.map +1 -1
  19. package/dist/diagnosticDerivations.js +187 -43
  20. package/dist/diagnosticDerivations.js.map +1 -1
  21. package/dist/diagnosticEvidenceIntern.d.ts +16 -0
  22. package/dist/diagnosticEvidenceIntern.d.ts.map +1 -0
  23. package/dist/diagnosticEvidenceIntern.js +139 -0
  24. package/dist/diagnosticEvidenceIntern.js.map +1 -0
  25. package/dist/diagnosticExposure.d.ts +2 -2
  26. package/dist/diagnosticExposure.d.ts.map +1 -1
  27. package/dist/diagnosticExposure.js +319 -21
  28. package/dist/diagnosticExposure.js.map +1 -1
  29. package/dist/diagnosticExpressions.d.ts +1 -1
  30. package/dist/diagnosticExpressions.d.ts.map +1 -1
  31. package/dist/diagnosticExpressions.js +35 -15
  32. package/dist/diagnosticExpressions.js.map +1 -1
  33. package/dist/diagnosticFormulas.d.ts +3 -0
  34. package/dist/diagnosticFormulas.d.ts.map +1 -1
  35. package/dist/diagnosticFormulas.js +160 -26
  36. package/dist/diagnosticFormulas.js.map +1 -1
  37. package/dist/diagnosticIdentity.d.ts +15 -1
  38. package/dist/diagnosticIdentity.d.ts.map +1 -1
  39. package/dist/diagnosticIdentity.js +321 -51
  40. package/dist/diagnosticIdentity.js.map +1 -1
  41. package/dist/diagnosticIdentityStream.d.ts +56 -0
  42. package/dist/diagnosticIdentityStream.d.ts.map +1 -0
  43. package/dist/diagnosticIdentityStream.js +472 -0
  44. package/dist/diagnosticIdentityStream.js.map +1 -0
  45. package/dist/diagnosticOrdering.d.ts +8 -0
  46. package/dist/diagnosticOrdering.d.ts.map +1 -0
  47. package/dist/diagnosticOrdering.js +73 -0
  48. package/dist/diagnosticOrdering.js.map +1 -0
  49. package/dist/diagnosticPeriodAxis.d.ts +9 -0
  50. package/dist/diagnosticPeriodAxis.d.ts.map +1 -0
  51. package/dist/diagnosticPeriodAxis.js +69 -0
  52. package/dist/diagnosticPeriodAxis.js.map +1 -0
  53. package/dist/diagnosticPeriods.d.ts +2 -3
  54. package/dist/diagnosticPeriods.d.ts.map +1 -1
  55. package/dist/diagnosticPeriods.js +20 -68
  56. package/dist/diagnosticPeriods.js.map +1 -1
  57. package/dist/diagnosticPreparation.d.ts +75 -14
  58. package/dist/diagnosticPreparation.d.ts.map +1 -1
  59. package/dist/diagnosticPreparation.js +1606 -112
  60. package/dist/diagnosticPreparation.js.map +1 -1
  61. package/dist/diagnosticReview.d.ts +40 -15
  62. package/dist/diagnosticReview.d.ts.map +1 -1
  63. package/dist/diagnosticReview.js +600 -42
  64. package/dist/diagnosticReview.js.map +1 -1
  65. package/dist/diagnosticReviewSources.d.ts +18 -0
  66. package/dist/diagnosticReviewSources.d.ts.map +1 -0
  67. package/dist/diagnosticReviewSources.js +76 -0
  68. package/dist/diagnosticReviewSources.js.map +1 -0
  69. package/dist/diagnosticReviewStore.d.ts +88 -0
  70. package/dist/diagnosticReviewStore.d.ts.map +1 -0
  71. package/dist/diagnosticReviewStore.js +455 -0
  72. package/dist/diagnosticReviewStore.js.map +1 -0
  73. package/dist/diagnosticRunner.d.ts +27 -2
  74. package/dist/diagnosticRunner.d.ts.map +1 -1
  75. package/dist/diagnosticRunner.js +764 -70
  76. package/dist/diagnosticRunner.js.map +1 -1
  77. package/dist/diagnosticRuntime.d.ts +26 -0
  78. package/dist/diagnosticRuntime.d.ts.map +1 -0
  79. package/dist/diagnosticRuntime.js +243 -0
  80. package/dist/diagnosticRuntime.js.map +1 -0
  81. package/dist/diagnosticSourceOrdering.d.ts +6 -0
  82. package/dist/diagnosticSourceOrdering.d.ts.map +1 -0
  83. package/dist/diagnosticSourceOrdering.js +38 -0
  84. package/dist/diagnosticSourceOrdering.js.map +1 -0
  85. package/dist/fnvAccumulator.d.ts +6 -0
  86. package/dist/fnvAccumulator.d.ts.map +1 -0
  87. package/dist/fnvAccumulator.js +21 -0
  88. package/dist/fnvAccumulator.js.map +1 -0
  89. package/dist/index.d.ts +15 -8
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +10 -4
  92. package/dist/index.js.map +1 -1
  93. package/dist/mack.d.ts +4 -0
  94. package/dist/mack.d.ts.map +1 -1
  95. package/dist/mack.js +32 -4
  96. package/dist/mack.js.map +1 -1
  97. package/dist/triangle.d.ts.map +1 -1
  98. package/dist/triangle.js +59 -9
  99. package/dist/triangle.js.map +1 -1
  100. package/dist/types.d.ts +4 -0
  101. package/dist/types.d.ts.map +1 -1
  102. package/dist/types.js +22 -8
  103. package/dist/types.js.map +1 -1
  104. package/dist/version.d.ts +1 -1
  105. package/dist/version.js +1 -1
  106. package/package.json +3 -2
  107. package/src/canonical.ts +144 -26
  108. package/src/capping.ts +109 -18
  109. package/src/diagnosticAggregation.ts +69 -20
  110. package/src/diagnosticDefinitions.ts +2675 -426
  111. package/src/diagnosticDerivations.ts +281 -49
  112. package/src/diagnosticEvidenceIntern.ts +151 -0
  113. package/src/diagnosticExposure.ts +550 -50
  114. package/src/diagnosticExpressions.ts +99 -17
  115. package/src/diagnosticFormulas.ts +239 -39
  116. package/src/diagnosticIdentity.ts +700 -191
  117. package/src/diagnosticIdentityStream.ts +553 -0
  118. package/src/diagnosticOrdering.ts +106 -0
  119. package/src/diagnosticPeriodAxis.ts +92 -0
  120. package/src/diagnosticPeriods.ts +67 -82
  121. package/src/diagnosticPreparation.ts +2558 -132
  122. package/src/diagnosticReview.ts +1072 -58
  123. package/src/diagnosticReviewSources.ts +85 -0
  124. package/src/diagnosticReviewStore.ts +692 -0
  125. package/src/diagnosticRunner.ts +1273 -72
  126. package/src/diagnosticRuntime.ts +264 -0
  127. package/src/diagnosticSourceOrdering.ts +50 -0
  128. package/src/fnvAccumulator.ts +20 -0
  129. package/src/index.ts +69 -4
  130. package/src/mack.ts +72 -9
  131. package/src/triangle.ts +103 -13
  132. package/src/types.ts +55 -20
  133. package/src/version.ts +1 -1
@@ -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
+ }
@@ -1,5 +1,12 @@
1
- import type { CompiledDiagnosticDefinition, DiagnosticPeriodAxis } from "./diagnosticDefinitions.js";
1
+ import type {
2
+ CompiledDiagnosticDefinition,
3
+ DiagnosticPeriodAxis,
4
+ } from "./diagnosticDefinitions.js";
2
5
  import { assertCompiledDiagnosticDefinition } from "./diagnosticDefinitions.js";
6
+ import {
7
+ normalizeDiagnosticPeriodWithAxis,
8
+ type DiagnosticPeriodSide,
9
+ } from "./diagnosticPeriodAxis.js";
3
10
  import { DiagnosticValidationError } from "./types.js";
4
11
 
5
12
  export interface DiagnosticNormalizedPeriod {
@@ -7,103 +14,75 @@ export interface DiagnosticNormalizedPeriod {
7
14
  readonly coordinate: number;
8
15
  }
9
16
 
10
- type PeriodSide = "origin" | "valuation";
11
-
12
- function calendarCoordinate(
13
- label: string,
14
- cadence: "month" | "quarter" | "year",
15
- anchor: "start" | "end",
16
- ): number | null {
17
- let year: number;
18
- let start: number;
19
- let width: number;
20
- if (cadence === "year") {
21
- const match = /^(\d{4})$/.exec(label);
22
- if (!match) return null;
23
- year = Number(match[1]);
24
- start = year * 12;
25
- width = 12;
26
- } else if (cadence === "month") {
27
- const match = /^(\d{4})-(0[1-9]|1[0-2])$/.exec(label);
28
- if (!match) return null;
29
- year = Number(match[1]);
30
- start = year * 12 + Number(match[2]) - 1;
31
- width = 1;
32
- } else {
33
- const compact = /^(\d{4})Q([1-4])$/.exec(label);
34
- const hyphenated = /^(\d{4})-Q([1-4])$/.exec(label);
35
- const first = /^Q([1-4]) (\d{4})$/.exec(label);
36
- const match = compact ?? hyphenated ?? first;
37
- if (!match) return null;
38
- const quarterFirst = match === first;
39
- year = Number(match[quarterFirst ? 2 : 1]);
40
- const quarter = Number(match[quarterFirst ? 1 : 2]);
41
- start = year * 12 + (quarter - 1) * 3;
42
- width = 3;
43
- }
44
- if (year < 1 || year > 9999) return null;
45
- return start + (anchor === "end" ? width : 0);
46
- }
47
-
48
17
  function normalizeWithAxis(
49
18
  axis: DiagnosticPeriodAxis,
50
- side: PeriodSide,
19
+ side: DiagnosticPeriodSide,
51
20
  label: string,
52
21
  ): DiagnosticNormalizedPeriod {
53
- if (axis.kind === "calendar") {
54
- const cadence = side === "origin" ? axis.originCadence : axis.valuationCadence;
55
- const anchor = side === "origin" ? axis.originAnchor : axis.valuationAnchor;
56
- const coordinate = calendarCoordinate(label, cadence, anchor);
57
- if (coordinate === null) throw new DiagnosticValidationError([{
58
- domain: "input",
59
- code: "invalid-period",
60
- path: `$.${side}`,
61
- message: `Invalid ${cadence} ${side} label ${JSON.stringify(label)}`,
62
- }]);
63
- const canonicalLabel = cadence === "quarter"
64
- ? (() => {
65
- const startCoordinate = calendarCoordinate(label, cadence, "start")!;
66
- const year = Math.floor(startCoordinate / 12);
67
- return `${String(year).padStart(4, "0")}Q${Math.floor((startCoordinate % 12) / 3) + 1}`;
68
- })()
69
- : label;
70
- return Object.freeze({ label: canonicalLabel, coordinate });
71
- }
72
- const catalog = side === "origin" ? axis.origins : axis.valuations;
73
- const item = catalog.find((coordinate) => coordinate.label === label || coordinate.aliases?.includes(label));
74
- if (!item) throw new DiagnosticValidationError([{
75
- domain: "input",
76
- code: "invalid-period",
77
- path: `$.${side}`,
78
- message: `Unknown ordered-axis ${side} label ${JSON.stringify(label)}`,
79
- }]);
80
- return Object.freeze({ label: item.label, coordinate: item.coordinate });
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);
81
36
  }
82
37
 
83
38
  export function normalizeDiagnosticPeriod(
84
39
  definition: CompiledDiagnosticDefinition,
85
- side: PeriodSide,
40
+ side: DiagnosticPeriodSide,
86
41
  label: string,
87
42
  ): DiagnosticNormalizedPeriod {
88
43
  assertCompiledDiagnosticDefinition(definition);
89
- return normalizeWithAxis(definition.definition.periodAxis as DiagnosticPeriodAxis, side, label);
44
+ return normalizeWithAxis(
45
+ definition.definition.periodAxis as DiagnosticPeriodAxis,
46
+ side,
47
+ label,
48
+ );
90
49
  }
91
50
 
92
51
  export function diagnosticDevelopmentAge(
93
52
  definition: CompiledDiagnosticDefinition,
94
53
  origin: string,
95
54
  valuation: string,
96
- ): { readonly origin: DiagnosticNormalizedPeriod; readonly valuation: DiagnosticNormalizedPeriod; readonly developmentAge: number; readonly ageUnit: string } {
97
- const normalizedOrigin = normalizeDiagnosticPeriod(definition, "origin", origin);
98
- const normalizedValuation = normalizeDiagnosticPeriod(definition, "valuation", valuation);
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
+ );
99
71
  const axis = definition.definition.periodAxis;
100
- const developmentAge = normalizedValuation.coordinate - normalizedOrigin.coordinate + axis.ageOffset;
101
- if (!Number.isSafeInteger(developmentAge) || developmentAge < 0) throw new DiagnosticValidationError([{
102
- domain: "input",
103
- code: "invalid-period",
104
- path: "$.valuation",
105
- message: "Valuation period precedes origin period or development age is unsafe",
106
- }]);
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
+ ]);
107
86
  return Object.freeze({
108
87
  origin: normalizedOrigin,
109
88
  valuation: normalizedValuation,
@@ -112,6 +91,12 @@ export function diagnosticDevelopmentAge(
112
91
  });
113
92
  }
114
93
 
115
- export function compareDiagnosticPeriods(left: DiagnosticNormalizedPeriod, right: DiagnosticNormalizedPeriod): number {
116
- return left.coordinate - right.coordinate || (left.label < right.label ? -1 : left.label > right.label ? 1 : 0);
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
+ );
117
102
  }