@actuarial-ts/data 0.3.0 → 0.5.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.
package/README.md CHANGED
@@ -1,19 +1,33 @@
1
1
  # @actuarial-ts/data
2
2
 
3
3
  Data ingestion and ASOP No. 23 data-quality review for the
4
- [actuarial-ts](../core) SDK. Pure functions, zero runtime dependencies
5
- (besides `@actuarial-ts/core`), fully typed.
4
+ [actuarial-ts](../core) SDK. Pure functions, fully typed, with runtime schemas
5
+ at object boundaries.
6
6
 
7
7
  - `parseCsv(text)` — minimal RFC 4180-subset CSV parser (quoted fields,
8
- escaped quotes, embedded commas/newlines, BOM, CRLF/LF).
8
+ escaped quotes, embedded commas/newlines, BOM, CRLF/LF); the result's
9
+ `rowLines` gives each row's 1-based physical start line in the file.
9
10
  - `parseLossRunCsv(text)` — loss-run import to `ClaimSnapshot[]` with
10
- per-row validation errors (1-based row numbers including the header).
11
+ per-row validation errors (errors cite 1-based physical file lines,
12
+ header = line 1).
13
+ - `annualDevelopmentToClaimSnapshots(rows, options)` — converts claim
14
+ valuations known only to annual precision without hiding the derived-date,
15
+ report-year, or duplicate-row conventions.
16
+ - `parseExposureCsv(text)` — imports earned premium and/or exposure units by
17
+ origin; extra source measures remain extra rather than being relabeled.
11
18
  - `triangleFromLongFormat(rows, { kind })` — pivots long-format
12
19
  `(origin, age, value)` rows into a `Triangle`.
13
20
  - `reviewClaimData(claims, { asOfDate? })` / `reviewTriangles(paid, incurred)`
14
21
  — the ASOP No. 23-oriented review; every check performed is listed in the
15
22
  report, pass or fail, so the actuary's disclosure can state what WAS
16
23
  reviewed, not just what was found.
24
+ - `validateDiagnosticDataset(value)` /
25
+ `validateAndReconcileDiagnosticExposures(value)` /
26
+ `runValidatedMetricDiagnostics(...)` — Zod-validated boundaries plus
27
+ stable-key exposure reconciliation for generic diagnostic rows.
28
+ - `reviewDiagnosticData(snapshots, exposures, options)` — quarterly aggregate,
29
+ exposure, layer, grouping, and cached-formula checks using the same
30
+ `DataReviewReport` status contract, with optional structured finding context.
17
31
 
18
32
  ## Checks
19
33
 
@@ -57,6 +71,74 @@ const { paid, incurred } = buildTriangles(claims, {
57
71
  const triangleReview = reviewTriangles(paid, incurred);
58
72
  ```
59
73
 
74
+ ### Annual-precision claim data
75
+
76
+ Do not label a source year as an exact accident or evaluation date. Normalize
77
+ the source fields to annual rows, then let the adapter derive calendar-year-end
78
+ compatibility dates and return the convention as a disclosure finding:
79
+
80
+ ```ts
81
+ import { annualDevelopmentToClaimSnapshots } from "@actuarial-ts/data";
82
+
83
+ const conversion = annualDevelopmentToClaimSnapshots(
84
+ [{
85
+ claimId: "1995-000001",
86
+ originYear: 1995,
87
+ evaluationYear: 1996,
88
+ paidToDate: 100,
89
+ incurredToDate: 140,
90
+ status: "open",
91
+ }],
92
+ { duplicatePolicy: "reject" },
93
+ );
94
+
95
+ console.log(conversion.conventions.derivedDateConvention); // calendar-year-end
96
+ console.log(conversion.findings); // date and inferred-report-year disclosures
97
+ ```
98
+
99
+ The default duplicate policy is `reject`. Selecting `combine` is an explicit
100
+ judgment: same-claim/same-year paid and incurred values are summed, and the
101
+ combined record is open if any component is open. The adapter reports how many
102
+ groups and rows were combined.
103
+
104
+ ### Exposure data
105
+
106
+ `parseExposureCsv` requires `origin` plus `earned_premium`, `exposure_units`,
107
+ or both. Each numeric measure may be blank independently. For example, a
108
+ source containing insurance-years and gross written premium should expose only
109
+ the insurance-years to the SDK unless an earned-premium transformation has
110
+ actually been performed:
111
+
112
+ ```csv
113
+ origin,exposure_units,gross_written_premium
114
+ 2024,125000,42000000
115
+ ```
116
+
117
+ The extra GWP column is retained in the source file but ignored by the parser;
118
+ it is never silently loaded as `earnedPremium`.
119
+
120
+ ### Quarterly diagnostic review
121
+
122
+ `reviewDiagnosticData` lists all 19 stable codes in
123
+ `DIAGNOSTIC_REVIEW_CHECK_CODES`. The suite covers duplicate aggregate and
124
+ exposure keys; invalid or mismatched development ages; valuation-before-origin;
125
+ count identities; paid/incurred and cumulative movement checks; reopen
126
+ signals; layer ordering and control totals; both sides of the loss/exposure
127
+ join; zero/incomplete exposure; grouping consistency; and lightweight cached
128
+ formula provenance.
129
+
130
+ The existing `DataCheck.details: string[]` field is unchanged. New checks also
131
+ populate `DataCheck.findings` with optional `origin`, `valuation`, `ageMonths`,
132
+ `group`, `sourceFile`, and `sourceRow` context so consumers need not parse
133
+ prose. Severities and numeric tolerances are caller-configurable. Optional
134
+ checks without configuration report `not-evaluated`, never a misleading pass.
135
+
136
+ For unknown objects, call `validateDiagnosticDataset` (or the convenience
137
+ `runValidatedMetricDiagnostics`) before the core analysis. For workbook input,
138
+ extract rows with the host's spreadsheet tooling and pass cached formula
139
+ metadata to the review; this package deliberately does not add a heavyweight
140
+ XLSX dependency.
141
+
60
142
  These utilities are designed to support the actuary's compliance with
61
143
  ASOP No. 23; responsibility for compliance remains with the credentialed
62
144
  actuary.
@@ -0,0 +1,94 @@
1
+ import type { ClaimSnapshot } from "@actuarial-ts/core";
2
+ import { z } from "zod";
3
+ /**
4
+ * A claim valuation known only to annual precision.
5
+ *
6
+ * `incurredToDate` is deliberately named for the interpretation already made
7
+ * by the caller. Source-specific adapters must document how their fields were
8
+ * mapped to paid and incurred before calling this function.
9
+ */
10
+ export declare const annualClaimDevelopmentRowSchema: z.ZodEffects<z.ZodObject<{
11
+ claimId: z.ZodString;
12
+ originYear: z.ZodNumber;
13
+ reportYear: z.ZodOptional<z.ZodNumber>;
14
+ evaluationYear: z.ZodNumber;
15
+ paidToDate: z.ZodNumber;
16
+ incurredToDate: z.ZodNumber;
17
+ status: z.ZodEnum<["open", "closed"]>;
18
+ }, "strict", z.ZodTypeAny, {
19
+ claimId: string;
20
+ originYear: number;
21
+ evaluationYear: number;
22
+ paidToDate: number;
23
+ incurredToDate: number;
24
+ status: "open" | "closed";
25
+ reportYear?: number | undefined;
26
+ }, {
27
+ claimId: string;
28
+ originYear: number;
29
+ evaluationYear: number;
30
+ paidToDate: number;
31
+ incurredToDate: number;
32
+ status: "open" | "closed";
33
+ reportYear?: number | undefined;
34
+ }>, {
35
+ claimId: string;
36
+ originYear: number;
37
+ evaluationYear: number;
38
+ paidToDate: number;
39
+ incurredToDate: number;
40
+ status: "open" | "closed";
41
+ reportYear?: number | undefined;
42
+ }, {
43
+ claimId: string;
44
+ originYear: number;
45
+ evaluationYear: number;
46
+ paidToDate: number;
47
+ incurredToDate: number;
48
+ status: "open" | "closed";
49
+ reportYear?: number | undefined;
50
+ }>;
51
+ export type AnnualClaimDevelopmentRow = z.infer<typeof annualClaimDevelopmentRowSchema>;
52
+ export type AnnualDuplicatePolicy = "reject" | "combine";
53
+ export interface AnnualDevelopmentOptions {
54
+ /**
55
+ * Same-claim, same-year records are ambiguous. The safe default rejects
56
+ * them. `combine` treats them as components of one dollar stream: paid and
57
+ * incurred are summed and the combined record is open when any component is
58
+ * open. The returned finding makes that judgment visible.
59
+ */
60
+ duplicatePolicy?: AnnualDuplicatePolicy;
61
+ }
62
+ export type AnnualDevelopmentFindingCode = "annual-dates-derived" | "report-year-inferred" | "duplicate-snapshots-combined";
63
+ export interface AnnualDevelopmentFinding {
64
+ code: AnnualDevelopmentFindingCode;
65
+ severity: "disclosure" | "warning";
66
+ message: string;
67
+ affectedClaims?: number;
68
+ affectedGroups?: number;
69
+ affectedRows?: number;
70
+ }
71
+ export interface AnnualDevelopmentConventions {
72
+ sourceDatePrecision: "year";
73
+ derivedDateConvention: "calendar-year-end";
74
+ missingReportYearConvention: "earliest-evaluation-year";
75
+ duplicatePolicy: AnnualDuplicatePolicy;
76
+ }
77
+ export interface AnnualDevelopmentConversion {
78
+ claims: ClaimSnapshot[];
79
+ findings: AnnualDevelopmentFinding[];
80
+ conventions: AnnualDevelopmentConventions;
81
+ sourceRowCount: number;
82
+ }
83
+ /**
84
+ * Converts year-precision claim valuations into the exact-date
85
+ * `ClaimSnapshot` contract without pretending the source supplied exact
86
+ * dates. Every derived date uses calendar year-end, and the convention is
87
+ * returned alongside the claims for disclosure.
88
+ *
89
+ * Missing report years are inferred as the earliest evaluation year for the
90
+ * claim. Duplicate claim/evaluation-year rows are rejected unless the caller
91
+ * explicitly selects the auditable `combine` policy.
92
+ */
93
+ export declare function annualDevelopmentToClaimSnapshots(input: readonly AnnualClaimDevelopmentRow[], options?: AnnualDevelopmentOptions): AnnualDevelopmentConversion;
94
+ //# sourceMappingURL=annualDevelopment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"annualDevelopment.d.ts","sourceRoot":"","sources":["../src/annualDevelopment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCxC,CAAC;AAEL,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEzD,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,qBAAqB,CAAC;CACzC;AAMD,MAAM,MAAM,4BAA4B,GACpC,sBAAsB,GACtB,sBAAsB,GACtB,8BAA8B,CAAC;AAEnC,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,4BAA4B,CAAC;IACnC,QAAQ,EAAE,YAAY,GAAG,SAAS,CAAC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,4BAA4B;IAC3C,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qBAAqB,EAAE,mBAAmB,CAAC;IAC3C,2BAA2B,EAAE,0BAA0B,CAAC;IACxD,eAAe,EAAE,qBAAqB,CAAC;CACxC;AAED,MAAM,WAAW,2BAA2B;IAC1C,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,QAAQ,EAAE,wBAAwB,EAAE,CAAC;IACrC,WAAW,EAAE,4BAA4B,CAAC;IAC1C,cAAc,EAAE,MAAM,CAAC;CACxB;AAqBD;;;;;;;;;GASG;AACH,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,SAAS,yBAAyB,EAAE,EAC3C,OAAO,GAAE,wBAA6B,GACrC,2BAA2B,CAyJ7B"}
@@ -0,0 +1,188 @@
1
+ import { ReservingError } from "@actuarial-ts/core";
2
+ import { z } from "zod";
3
+ /**
4
+ * A claim valuation known only to annual precision.
5
+ *
6
+ * `incurredToDate` is deliberately named for the interpretation already made
7
+ * by the caller. Source-specific adapters must document how their fields were
8
+ * mapped to paid and incurred before calling this function.
9
+ */
10
+ export const annualClaimDevelopmentRowSchema = z
11
+ .object({
12
+ claimId: z.string().trim().min(1),
13
+ originYear: z.number().int().min(1).max(9999),
14
+ reportYear: z.number().int().min(1).max(9999).optional(),
15
+ evaluationYear: z.number().int().min(1).max(9999),
16
+ paidToDate: z.number().finite(),
17
+ incurredToDate: z.number().finite(),
18
+ status: z.enum(["open", "closed"]),
19
+ })
20
+ .strict()
21
+ .superRefine((row, ctx) => {
22
+ if (row.evaluationYear < row.originYear) {
23
+ ctx.addIssue({
24
+ code: z.ZodIssueCode.custom,
25
+ path: ["evaluationYear"],
26
+ message: "evaluationYear must not precede originYear",
27
+ });
28
+ }
29
+ if (row.reportYear !== undefined && row.reportYear < row.originYear) {
30
+ ctx.addIssue({
31
+ code: z.ZodIssueCode.custom,
32
+ path: ["reportYear"],
33
+ message: "reportYear must not precede originYear",
34
+ });
35
+ }
36
+ if (row.reportYear !== undefined && row.evaluationYear < row.reportYear) {
37
+ ctx.addIssue({
38
+ code: z.ZodIssueCode.custom,
39
+ path: ["evaluationYear"],
40
+ message: "evaluationYear must not precede reportYear",
41
+ });
42
+ }
43
+ });
44
+ const annualDevelopmentOptionsSchema = z
45
+ .object({ duplicatePolicy: z.enum(["reject", "combine"]).default("reject") })
46
+ .strict();
47
+ function yearEnd(year) {
48
+ return `${String(year).padStart(4, "0")}-12-31`;
49
+ }
50
+ function issueMessage(error) {
51
+ return error.issues
52
+ .map((issue) => {
53
+ const path = issue.path.length > 0 ? `${issue.path.join(".")}: ` : "";
54
+ return `${path}${issue.message}`;
55
+ })
56
+ .join("; ");
57
+ }
58
+ /**
59
+ * Converts year-precision claim valuations into the exact-date
60
+ * `ClaimSnapshot` contract without pretending the source supplied exact
61
+ * dates. Every derived date uses calendar year-end, and the convention is
62
+ * returned alongside the claims for disclosure.
63
+ *
64
+ * Missing report years are inferred as the earliest evaluation year for the
65
+ * claim. Duplicate claim/evaluation-year rows are rejected unless the caller
66
+ * explicitly selects the auditable `combine` policy.
67
+ */
68
+ export function annualDevelopmentToClaimSnapshots(input, options = {}) {
69
+ const parsedOptions = annualDevelopmentOptionsSchema.safeParse(options);
70
+ if (!parsedOptions.success) {
71
+ throw new ReservingError("UNSUPPORTED_VALUE", `Invalid annual development options: ${issueMessage(parsedOptions.error)}`);
72
+ }
73
+ const { duplicatePolicy } = parsedOptions.data;
74
+ const rows = input.map((candidate, inputIndex) => {
75
+ const parsed = annualClaimDevelopmentRowSchema.safeParse(candidate);
76
+ if (!parsed.success) {
77
+ throw new ReservingError("SHAPE", `Annual claim development row ${inputIndex + 1}: ${issueMessage(parsed.error)}`);
78
+ }
79
+ return parsed.data;
80
+ });
81
+ const byClaim = new Map();
82
+ for (const row of rows) {
83
+ const existing = byClaim.get(row.claimId);
84
+ if (existing === undefined) {
85
+ byClaim.set(row.claimId, {
86
+ originYear: row.originYear,
87
+ reportYears: new Set(row.reportYear === undefined ? [] : [row.reportYear]),
88
+ rows: [row],
89
+ });
90
+ continue;
91
+ }
92
+ if (existing.originYear !== row.originYear) {
93
+ throw new ReservingError("SHAPE", `Claim ${row.claimId} has conflicting origin years ${existing.originYear} and ${row.originYear}`);
94
+ }
95
+ if (row.reportYear !== undefined)
96
+ existing.reportYears.add(row.reportYear);
97
+ existing.rows.push(row);
98
+ }
99
+ const findings = [
100
+ {
101
+ code: "annual-dates-derived",
102
+ severity: "disclosure",
103
+ affectedRows: rows.length,
104
+ message: "The source supplies calendar years, not exact dates; accident, report and evaluation dates were derived at calendar year-end.",
105
+ },
106
+ ];
107
+ const claims = [];
108
+ let inferredReportYears = 0;
109
+ let duplicateGroups = 0;
110
+ let duplicateRows = 0;
111
+ for (const [claimId, group] of byClaim) {
112
+ if (group.reportYears.size > 1) {
113
+ throw new ReservingError("SHAPE", `Claim ${claimId} has conflicting report years ${[...group.reportYears].sort((a, b) => a - b).join(", ")}`);
114
+ }
115
+ const firstEvaluationYear = group.rows.reduce((earliest, row) => Math.min(earliest, row.evaluationYear), group.rows[0].evaluationYear);
116
+ const suppliedReportYear = group.reportYears.values().next().value;
117
+ const reportYear = suppliedReportYear ?? firstEvaluationYear;
118
+ if (suppliedReportYear === undefined)
119
+ inferredReportYears += 1;
120
+ if (reportYear > firstEvaluationYear) {
121
+ throw new ReservingError("SHAPE", `Claim ${claimId} has report year ${reportYear} after its first evaluation year ${firstEvaluationYear}`);
122
+ }
123
+ const byEvaluationYear = new Map();
124
+ for (const row of group.rows) {
125
+ const sameYear = byEvaluationYear.get(row.evaluationYear);
126
+ if (sameYear === undefined)
127
+ byEvaluationYear.set(row.evaluationYear, [row]);
128
+ else
129
+ sameYear.push(row);
130
+ }
131
+ for (const evaluationYear of [...byEvaluationYear.keys()].sort((a, b) => a - b)) {
132
+ const sameYear = byEvaluationYear.get(evaluationYear);
133
+ if (sameYear.length > 1) {
134
+ duplicateGroups += 1;
135
+ duplicateRows += sameYear.length;
136
+ if (duplicatePolicy === "reject") {
137
+ throw new ReservingError("SHAPE", `Claim ${claimId} has ${sameYear.length} snapshots in evaluation year ${evaluationYear}; choose duplicatePolicy "combine" only after reviewing the source ambiguity`);
138
+ }
139
+ }
140
+ const paidToDate = sameYear.reduce((sum, row) => sum + row.paidToDate, 0);
141
+ const incurredToDate = sameYear.reduce((sum, row) => sum + row.incurredToDate, 0);
142
+ const caseReserve = incurredToDate - paidToDate;
143
+ if (!Number.isFinite(paidToDate) ||
144
+ !Number.isFinite(incurredToDate) ||
145
+ !Number.isFinite(caseReserve)) {
146
+ throw new ReservingError("SHAPE", `Claim ${claimId} evaluation year ${evaluationYear}: combined paid, incurred, and case reserve must remain finite`);
147
+ }
148
+ claims.push({
149
+ claimId,
150
+ accidentDate: yearEnd(group.originYear),
151
+ reportDate: yearEnd(reportYear),
152
+ evaluationDate: yearEnd(evaluationYear),
153
+ paidToDate,
154
+ caseReserve,
155
+ status: sameYear.some((row) => row.status === "open") ? "open" : "closed",
156
+ });
157
+ }
158
+ }
159
+ if (inferredReportYears > 0) {
160
+ findings.push({
161
+ code: "report-year-inferred",
162
+ severity: "disclosure",
163
+ affectedClaims: inferredReportYears,
164
+ message: "Report year was absent and was inferred as each claim's earliest evaluation year.",
165
+ });
166
+ }
167
+ if (duplicateGroups > 0) {
168
+ findings.push({
169
+ code: "duplicate-snapshots-combined",
170
+ severity: "warning",
171
+ affectedGroups: duplicateGroups,
172
+ affectedRows: duplicateRows,
173
+ message: "Same-claim, same-year rows were combined by summing paid and incurred; the combined status is open when any component is open.",
174
+ });
175
+ }
176
+ return {
177
+ claims,
178
+ findings,
179
+ conventions: {
180
+ sourceDatePrecision: "year",
181
+ derivedDateConvention: "calendar-year-end",
182
+ missingReportYearConvention: "earliest-evaluation-year",
183
+ duplicatePolicy,
184
+ },
185
+ sourceRowCount: rows.length,
186
+ };
187
+ }
188
+ //# sourceMappingURL=annualDevelopment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"annualDevelopment.js","sourceRoot":"","sources":["../src/annualDevelopment.ts"],"names":[],"mappings":"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"}
package/dist/csv.d.ts CHANGED
@@ -4,7 +4,9 @@
4
4
  * Supported: comma delimiter, quoted fields, doubled quotes ("") as escaped
5
5
  * quotes inside quoted fields, commas and newlines inside quoted fields,
6
6
  * CRLF and LF row endings, a leading UTF-8 BOM, and a tolerated trailing
7
- * newline. Lines that are completely empty outside quotes are skipped.
7
+ * newline. Lines that are completely empty outside quotes are skipped
8
+ * skipped lines still advance the physical line count reported in
9
+ * `rowLines`, so later rows' reported start lines stay correct.
8
10
  *
9
11
  * Deliberately NOT here: header handling, type coercion, and shape
10
12
  * validation. Ragged rows are preserved as-is — validation is the caller's
@@ -13,6 +15,14 @@
13
15
  export interface CsvParseResult {
14
16
  /** The parsed grid (rows x columns). Ragged rows preserved as-is. */
15
17
  rows: string[][];
18
+ /**
19
+ * Physical 1-based line number in the input where each row of `rows`
20
+ * STARTS. Parallel to `rows`. Differs from index+1 whenever blank lines
21
+ * were skipped or a quoted field contains newlines — callers reporting
22
+ * row-level problems must use this, not the grid index, or their
23
+ * diagnostics point at the wrong line of the file.
24
+ */
25
+ rowLines: number[];
16
26
  /**
17
27
  * Structural problems the parser recovered from. Content is still parsed
18
28
  * leniently — a warning here means the OUTPUT may not mean what the file's
package/dist/csv.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;IACjB;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,6EAA6E;AAC7E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAwFrD"}
1
+ {"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;IACjB;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,6EAA6E;AAC7E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CA6FrD"}
package/dist/csv.js CHANGED
@@ -4,7 +4,9 @@
4
4
  * Supported: comma delimiter, quoted fields, doubled quotes ("") as escaped
5
5
  * quotes inside quoted fields, commas and newlines inside quoted fields,
6
6
  * CRLF and LF row endings, a leading UTF-8 BOM, and a tolerated trailing
7
- * newline. Lines that are completely empty outside quotes are skipped.
7
+ * newline. Lines that are completely empty outside quotes are skipped
8
+ * skipped lines still advance the physical line count reported in
9
+ * `rowLines`, so later rows' reported start lines stay correct.
8
10
  *
9
11
  * Deliberately NOT here: header handling, type coercion, and shape
10
12
  * validation. Ragged rows are preserved as-is — validation is the caller's
@@ -16,12 +18,14 @@ export function parseCsv(text) {
16
18
  if (s.length > 0 && s.charCodeAt(0) === 0xfeff)
17
19
  s = s.slice(1);
18
20
  const rows = [];
21
+ const rowLines = [];
19
22
  const warnings = [];
20
23
  let row = [];
21
24
  let field = "";
22
25
  let inQuotes = false;
23
26
  let fieldWasQuoted = false;
24
27
  let line = 1;
28
+ let rowStartLine = 1;
25
29
  let quoteOpenedAtLine = 0;
26
30
  const endField = () => {
27
31
  row.push(field);
@@ -35,6 +39,7 @@ export function parseCsv(text) {
35
39
  return;
36
40
  endField();
37
41
  rows.push(row);
42
+ rowLines.push(rowStartLine);
38
43
  row = [];
39
44
  };
40
45
  let i = 0;
@@ -74,12 +79,14 @@ export function parseCsv(text) {
74
79
  i++;
75
80
  endRow();
76
81
  line++;
82
+ rowStartLine = line;
77
83
  i++;
78
84
  continue;
79
85
  }
80
86
  if (ch === "\n") {
81
87
  endRow();
82
88
  line++;
89
+ rowStartLine = line;
83
90
  i++;
84
91
  continue;
85
92
  }
@@ -96,6 +103,6 @@ export function parseCsv(text) {
96
103
  "input was consumed into a single field; check the file for a stray or unescaped quote");
97
104
  }
98
105
  endRow();
99
- return { rows, warnings };
106
+ return { rows, rowLines, warnings };
100
107
  }
101
108
  //# sourceMappingURL=csv.js.map
package/dist/csv.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"csv.js","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"csv.js","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type DiagnosticExposureRow, type DiagnosticLossRow, type MetricDiagnosticsResult, type ReconciledDiagnosticExposures, type RunMetricDiagnosticsInput } from "@actuarial-ts/core";
2
+ export interface ValidatedDiagnosticDataset {
3
+ losses: DiagnosticLossRow[];
4
+ /** Omitted when the caller did not supply exposure data. */
5
+ exposures?: DiagnosticExposureRow[];
6
+ }
7
+ /** Zod-validates unknown diagnostic rows at the data package boundary. */
8
+ export declare function validateDiagnosticDataset(value: unknown): ValidatedDiagnosticDataset;
9
+ /** Validates unknown exposure rows, then applies core's stable-key reconciliation. */
10
+ export declare function validateAndReconcileDiagnosticExposures(value: unknown): ReconciledDiagnosticExposures;
11
+ export type ValidatedMetricDiagnosticsOptions = Omit<RunMetricDiagnosticsInput, "losses" | "exposures">;
12
+ /** Convenience boundary: validate unknown rows, then run the dependency-free core engine. */
13
+ export declare function runValidatedMetricDiagnostics(dataset: unknown, options: ValidatedMetricDiagnosticsOptions): MetricDiagnosticsResult;
14
+ //# sourceMappingURL=diagnosticInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnosticInput.d.ts","sourceRoot":"","sources":["../src/diagnosticInput.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,6BAA6B,EAClC,KAAK,yBAAyB,EAC/B,MAAM,oBAAoB,CAAC;AA+B5B,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,4DAA4D;IAC5D,SAAS,CAAC,EAAE,qBAAqB,EAAE,CAAC;CACrC;AAED,0EAA0E;AAC1E,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,0BAA0B,CAYpF;AAED,sFAAsF;AACtF,wBAAgB,uCAAuC,CACrD,KAAK,EAAE,OAAO,GACb,6BAA6B,CAG/B;AAED,MAAM,MAAM,iCAAiC,GAAG,IAAI,CAAC,yBAAyB,EAAE,QAAQ,GAAG,WAAW,CAAC,CAAC;AAExG,6FAA6F;AAC7F,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,iCAAiC,GACzC,uBAAuB,CAGzB"}
@@ -0,0 +1,51 @@
1
+ import { ReservingError, reconcileDiagnosticExposureKeys, runMetricDiagnostics, } from "@actuarial-ts/core";
2
+ import { z } from "zod";
3
+ const measuresSchema = z.record(z.number().nullable());
4
+ const diagnosticLossRowSchema = z.object({
5
+ id: z.string().min(1),
6
+ group: z.string().min(1),
7
+ origin: z.string().min(1),
8
+ valuation: z.string().min(1),
9
+ ageMonths: z.number(),
10
+ policyPeriod: z.string().min(1).optional(),
11
+ dimensions: z.unknown().optional(),
12
+ measures: measuresSchema,
13
+ }).strict();
14
+ const diagnosticExposureRowSchema = z.object({
15
+ key: z.string().min(1),
16
+ group: z.string().min(1),
17
+ origin: z.string().min(1),
18
+ valuation: z.string().min(1).optional(),
19
+ measures: measuresSchema,
20
+ complete: z.boolean().optional(),
21
+ dimensions: z.unknown().optional(),
22
+ }).strict();
23
+ const diagnosticDatasetSchema = z.object({
24
+ losses: z.array(diagnosticLossRowSchema),
25
+ exposures: z.array(diagnosticExposureRowSchema).optional(),
26
+ }).strict();
27
+ /** Zod-validates unknown diagnostic rows at the data package boundary. */
28
+ export function validateDiagnosticDataset(value) {
29
+ const parsed = diagnosticDatasetSchema.safeParse(value);
30
+ if (!parsed.success) {
31
+ const details = parsed.error.issues
32
+ .map((issue) => `${issue.path.length > 0 ? issue.path.join(".") : "$"}: ${issue.message}`)
33
+ .join("; ");
34
+ throw new ReservingError("SHAPE", `Invalid diagnostic dataset: ${details}`);
35
+ }
36
+ return {
37
+ losses: parsed.data.losses,
38
+ ...(parsed.data.exposures !== undefined ? { exposures: parsed.data.exposures } : {}),
39
+ };
40
+ }
41
+ /** Validates unknown exposure rows, then applies core's stable-key reconciliation. */
42
+ export function validateAndReconcileDiagnosticExposures(value) {
43
+ const validated = validateDiagnosticDataset({ losses: [], exposures: value });
44
+ return reconcileDiagnosticExposureKeys(validated.exposures ?? []);
45
+ }
46
+ /** Convenience boundary: validate unknown rows, then run the dependency-free core engine. */
47
+ export function runValidatedMetricDiagnostics(dataset, options) {
48
+ const validated = validateDiagnosticDataset(dataset);
49
+ return runMetricDiagnostics({ ...options, ...validated });
50
+ }
51
+ //# sourceMappingURL=diagnosticInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnosticInput.js","sourceRoot":"","sources":["../src/diagnosticInput.ts"],"names":[],"mappings":"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"}