@actuarial-ts/compliance 0.4.0 → 0.6.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.
@@ -0,0 +1,148 @@
1
+ import {
2
+ CORE_PACKAGE_VERSION,
3
+ canonicalJson,
4
+ fnv1a64,
5
+ getMetricDiagnosticsResultIdentity,
6
+ getPreparedDiagnosticDataIdentity,
7
+ runMetricDiagnostics,
8
+ verifyPreparedDiagnosticDataIntegrity,
9
+ type DiagnosticDeepReadonly,
10
+ type CompiledDiagnosticDefinition,
11
+ type JsonValue as CoreDiagnosticJsonValue,
12
+ type NormalizedDiagnosticDefinitionIdentity,
13
+ type NormalizedDiagnosticPreparationIdentity,
14
+ type NormalizedDiagnosticResultIdentity,
15
+ } from "@actuarial-ts/core";
16
+ import {
17
+ DATA_PACKAGE_VERSION,
18
+ assertCompletedValidatedMetricDiagnosticsRun,
19
+ reviewPreparedDiagnosticData,
20
+ type CompletedValidatedMetricDiagnosticsRun,
21
+ type DiagnosticReviewIdentityBody,
22
+ type DiagnosticReviewReceipt,
23
+ } from "@actuarial-ts/data";
24
+ import { ComplianceError } from "./errors.js";
25
+ import { COMPLIANCE_PACKAGE_VERSION } from "./version.js";
26
+
27
+ export interface DiagnosticArtifactDigestBase { readonly id: string; readonly scope: "input" | "preparation"; readonly algorithm: string; readonly value: string; readonly byteLength: number }
28
+ export type DiagnosticArtifactDigest = DiagnosticArtifactDigestBase & { readonly assurance: "sdk-computed" | "caller-declared" };
29
+ export type DiagnosticArtifactEvidence =
30
+ | { readonly id: string; readonly scope: "input" | "preparation"; readonly assurance: "sdk-computed"; readonly bytes: Uint8Array }
31
+ | { readonly id: string; readonly scope: "input" | "preparation"; readonly assurance: "caller-declared"; readonly algorithm: string; readonly value: string; readonly byteLength: number };
32
+ export interface DiagnosticPreparationLineage { readonly artifactId: string; readonly inputArtifactIds: readonly string[] }
33
+
34
+ export interface DiagnosticRunManifest {
35
+ readonly definitionIntegrity: string;
36
+ readonly runPresetId: string | null;
37
+ readonly datasetArtifactId: string | null;
38
+ readonly packageVersions: Readonly<Record<string, string>>;
39
+ readonly preparation: DiagnosticDeepReadonly<NormalizedDiagnosticPreparationIdentity>;
40
+ readonly preparationFingerprint: string;
41
+ readonly expectedGridFingerprint: string | null;
42
+ readonly executionPolicy: { readonly review: { readonly body: DiagnosticReviewIdentityBody; readonly reportFingerprint: string }; readonly gate: CompletedValidatedMetricDiagnosticsRun["gate"] };
43
+ readonly groupMap: Readonly<Record<string, string>>;
44
+ readonly groupDimensions: Readonly<Record<string, CoreDiagnosticJsonValue>>;
45
+ readonly artifacts: readonly DiagnosticArtifactDigest[];
46
+ readonly lineage: readonly DiagnosticPreparationLineage[];
47
+ }
48
+ export type NormalizedDiagnosticRunManifestIdentity = DiagnosticDeepReadonly<DiagnosticRunManifest>;
49
+
50
+ export interface DiagnosticRunIdentity {
51
+ readonly runFingerprint: string;
52
+ readonly resultFingerprint: string;
53
+ readonly runResultFingerprint: string;
54
+ }
55
+ export interface DiagnosticRunProvenance extends DiagnosticRunIdentity {
56
+ readonly definition: DiagnosticDeepReadonly<NormalizedDiagnosticDefinitionIdentity>;
57
+ readonly definitionIdentities: { readonly algorithm: "fnv1a64-jcs-v1"; readonly formulaById: Readonly<Record<string,string>>; readonly calculationByInstanceId: Readonly<Record<string,string>>; readonly definition: string };
58
+ readonly manifest: NormalizedDiagnosticRunManifestIdentity;
59
+ readonly review: DiagnosticReviewReceipt;
60
+ readonly result: DiagnosticDeepReadonly<NormalizedDiagnosticResultIdentity>;
61
+ }
62
+ declare const verifiedDiagnosticRunProvenanceBrand: unique symbol;
63
+ export interface VerifiedDiagnosticRunProvenance extends DiagnosticRunProvenance { readonly [verifiedDiagnosticRunProvenanceBrand]: true }
64
+ export interface CreateDiagnosticRunIdentityInput { readonly completedRun: CompletedValidatedMetricDiagnosticsRun; readonly artifacts?: readonly DiagnosticArtifactEvidence[]; readonly lineage?: readonly DiagnosticPreparationLineage[] }
65
+
66
+ const verified = new WeakMap<object, CompletedValidatedMetricDiagnosticsRun>();
67
+ const token = (value:string,path:string) => {
68
+ if (value.length === 0 || /^[\t-\r ]|[\t-\r ]$/.test(value) || value.includes("\0")) throw new ComplianceError("BAD_DIAGNOSTIC_RUN", `${path} must be a nonempty token`, path);
69
+ for(let index=0;index<value.length;index++){const unit=value.charCodeAt(index);if(unit>=0xd800&&unit<=0xdbff){const next=value.charCodeAt(index+1);if(!(next>=0xdc00&&next<=0xdfff))throw new ComplianceError("BAD_DIAGNOSTIC_RUN",`${path} contains malformed Unicode`,path);index++}else if(unit>=0xdc00&&unit<=0xdfff)throw new ComplianceError("BAD_DIAGNOSTIC_RUN",`${path} contains malformed Unicode`,path)}
70
+ };
71
+ function freeze<T>(value:T,seen=new WeakSet<object>()):DiagnosticDeepReadonly<T>{if(value===null||typeof value!=="object"||seen.has(value))return value as DiagnosticDeepReadonly<T>;seen.add(value);for(const child of Object.values(value as Record<string,unknown>))freeze(child,seen);return Object.freeze(value) as DiagnosticDeepReadonly<T>}
72
+ function tag(kind:string,key:string,value:unknown):string{return `fnv1a64-jcs-v1:${fnv1a64(canonicalJson({identityVersion:1,kind,[key]:value}))}`}
73
+ type SnapshottedArtifact=DiagnosticArtifactDigest|{readonly id:string;readonly scope:"input"|"preparation";readonly assurance:"sdk-computed";readonly bytes:Uint8Array};
74
+ function snapshotArtifacts(evidence:readonly DiagnosticArtifactEvidence[]):readonly SnapshottedArtifact[]{
75
+ const seen=new Set<string>();const result:SnapshottedArtifact[]=[];
76
+ for(const [index,item] of evidence.entries()){
77
+ token(item.id,`$.artifacts[${index}].id`);token(item.scope,`$.artifacts[${index}].scope`);if(seen.has(item.id))throw new ComplianceError("BAD_DIAGNOSTIC_RUN",`Duplicate artifact ID ${item.id}`,`$.artifacts[${index}].id`);seen.add(item.id);
78
+ if(item.assurance==="sdk-computed"){
79
+ if(!(item.bytes instanceof Uint8Array))throw new ComplianceError("BAD_DIAGNOSTIC_RUN","SDK-computed artifact evidence requires actual Uint8Array bytes",`$.artifacts[${index}].bytes`);
80
+ const bytes=new Uint8Array(item.bytes.byteLength);bytes.set(item.bytes);result.push({id:item.id,scope:item.scope,assurance:item.assurance,bytes});
81
+ }else{token(item.algorithm,`$.artifacts[${index}].algorithm`);token(item.value,`$.artifacts[${index}].value`);if(!Number.isSafeInteger(item.byteLength)||item.byteLength<0)throw new ComplianceError("BAD_DIAGNOSTIC_RUN","Artifact byteLength must be a nonnegative safe integer",`$.artifacts[${index}].byteLength`);result.push({...item})}
82
+ }
83
+ return result.sort((a,b)=>a.id<b.id?-1:a.id>b.id?1:0);
84
+ }
85
+ async function digestArtifacts(snapshot:readonly SnapshottedArtifact[]):Promise<readonly DiagnosticArtifactDigest[]>{
86
+ if(snapshot.some((item)=>"bytes" in item)&&globalThis.crypto?.subtle===undefined)throw new ComplianceError("CRYPTO_UNAVAILABLE","Web Crypto SHA-256 is unavailable","$.artifacts");
87
+ return Promise.all(snapshot.map(async(item)=>{if(!("bytes" in item))return item;const hash=await globalThis.crypto.subtle.digest("SHA-256",item.bytes);return {id:item.id,scope:item.scope,assurance:item.assurance,algorithm:"sha-256",value:[...new Uint8Array(hash)].map((b)=>b.toString(16).padStart(2,"0")).join(""),byteLength:item.bytes.byteLength}}));
88
+ }
89
+
90
+ function snapshotLineage(lineage:readonly DiagnosticPreparationLineage[]):readonly DiagnosticPreparationLineage[]{
91
+ return lineage.map((item,index)=>{token(item.artifactId,`$.lineage[${index}].artifactId`);if(!Array.isArray(item.inputArtifactIds))throw new ComplianceError("BAD_DIAGNOSTIC_RUN","Lineage inputs must be an array",`$.lineage[${index}].inputArtifactIds`);const inputs=item.inputArtifactIds.map((id,inputIndex)=>{token(id,`$.lineage[${index}].inputArtifactIds[${inputIndex}]`);return id});if(new Set(inputs).size!==inputs.length)throw new ComplianceError("BAD_DIAGNOSTIC_RUN","Lineage inputs must be unique",`$.lineage[${index}].inputArtifactIds`);return {artifactId:item.artifactId,inputArtifactIds:[...inputs].sort()}}).sort((a,b)=>a.artifactId<b.artifactId?-1:a.artifactId>b.artifactId?1:0);
92
+ }
93
+
94
+ function validateArtifactGraph(run:CompletedValidatedMetricDiagnosticsRun,artifacts:readonly DiagnosticArtifactDigest[],lineage:readonly DiagnosticPreparationLineage[]):void{
95
+ const byId=new Map(artifacts.map((item)=>[item.id,item]));
96
+ const referenced=new Set<string>();
97
+ const requireArtifact=(id:string,scope:"input"|"preparation",path:string)=>{const artifact=byId.get(id);if(!artifact)throw new ComplianceError("BAD_DIAGNOSTIC_RUN",`Artifact reference ${id} is unresolved`,path);if(artifact.scope!==scope)throw new ComplianceError("BAD_DIAGNOSTIC_RUN",`Artifact ${id} must have ${scope} scope`,path);referenced.add(id)};
98
+ let unsourced=false;
99
+ for(const [index,item] of run.prepared.inputAudit.entries()){const source=item.record.source;if(source)requireArtifact(source.artifactId,"input",`$.completedRun.prepared.inputAudit[${index}].record.source.artifactId`);else unsourced=true}
100
+ const evidence=run.review.evidence;
101
+ if(evidence){for(const [index,item] of evidence.groupingAssignments.entries()){if(item.source)requireArtifact(item.source.artifactId,"input",`$.completedRun.review.evidence.groupingAssignments[${index}].source.artifactId`);else unsourced=true}for(const [index,item] of evidence.cachedFormulas.entries()){if(item.source)requireArtifact(item.source.artifactId,"input",`$.completedRun.review.evidence.cachedFormulas[${index}].source.artifactId`);else unsourced=true}}
102
+ if(unsourced){if(run.datasetArtifactId===null)throw new ComplianceError("BAD_DIAGNOSTIC_RUN","Unsourced diagnostic input requires datasetArtifactId","$.completedRun.datasetArtifactId");const fallback=byId.get(run.datasetArtifactId);if(!fallback||fallback.scope!=="input"||fallback.assurance!=="sdk-computed")throw new ComplianceError("BAD_DIAGNOSTIC_RUN","datasetArtifactId must resolve to SDK-computed input evidence","$.completedRun.datasetArtifactId");referenced.add(run.datasetArtifactId)}
103
+ else if(run.datasetArtifactId!==null)requireArtifact(run.datasetArtifactId,"input","$.completedRun.datasetArtifactId");
104
+ for(const [basisIndex,basis] of run.prepared.definition.definition.amountBases.entries())for(const [componentIndex,component] of basis.components.entries())if(component.limitation.kind!=="unlimited"&&component.limitation.kind!=="unknown"&&component.limitation.derivation.kind==="external")requireArtifact(component.limitation.derivation.transformationRef,"preparation",`$.definition.amountBases[${basisIndex}].components[${componentIndex}].limitation.derivation.transformationRef`);
105
+ for(const [ruleIndex,rule] of run.prepared.definition.definition.reviewRules.entries())if(rule.kind==="layer-order"&&rule.comparability.kind==="caller-asserted")requireArtifact(rule.comparability.rationaleArtifactId,"preparation",`$.definition.reviewRules[${ruleIndex}].comparability.rationaleArtifactId`);
106
+ if(run.gate.rationaleRef!==null)requireArtifact(run.gate.rationaleRef,"preparation","$.completedRun.gate.rationaleRef");
107
+ const edges=new Map<string,readonly string[]>();for(const [index,edge] of lineage.entries()){if(edges.has(edge.artifactId))throw new ComplianceError("BAD_DIAGNOSTIC_RUN","An artifact may have only one producing lineage edge",`$.lineage[${index}].artifactId`);const downstream=byId.get(edge.artifactId);if(!downstream)throw new ComplianceError("BAD_DIAGNOSTIC_RUN",`Lineage artifact ${edge.artifactId} is unresolved`,`$.lineage[${index}].artifactId`);if(downstream.scope!=="input")throw new ComplianceError("BAD_DIAGNOSTIC_RUN","Lineage downstream artifact must have input scope",`$.lineage[${index}].artifactId`);for(const [inputIndex,id] of edge.inputArtifactIds.entries()){if(id===edge.artifactId)throw new ComplianceError("BAD_DIAGNOSTIC_RUN","Lineage may not reference itself",`$.lineage[${index}].inputArtifactIds[${inputIndex}]`);if(!byId.has(id))throw new ComplianceError("BAD_DIAGNOSTIC_RUN",`Lineage reference ${id} is unresolved`,`$.lineage[${index}].inputArtifactIds[${inputIndex}]`)}edges.set(edge.artifactId,edge.inputArtifactIds)}
108
+ const visiting=new Set<string>(),visited=new Set<string>();const walk=(id:string,path:string)=>{if(visiting.has(id))throw new ComplianceError("BAD_DIAGNOSTIC_RUN","Artifact lineage contains a cycle",path);if(visited.has(id))return;visiting.add(id);for(const upstream of edges.get(id)??[]){referenced.add(upstream);walk(upstream,path)}visiting.delete(id);visited.add(id)};for(const id of [...referenced])walk(id,"$.lineage");
109
+ for(const [index,artifact] of artifacts.entries())if(!referenced.has(artifact.id))throw new ComplianceError("BAD_DIAGNOSTIC_RUN",`Artifact ${artifact.id} is orphaned`, `$.artifacts[${index}].id`);
110
+ }
111
+
112
+ function rerunAndVerify(run:CompletedValidatedMetricDiagnosticsRun):{review:DiagnosticReviewReceipt;result:DiagnosticDeepReadonly<NormalizedDiagnosticResultIdentity>}{
113
+ assertCompletedValidatedMetricDiagnosticsRun(run);verifyPreparedDiagnosticDataIntegrity(run.prepared);
114
+ const review=reviewPreparedDiagnosticData({prepared:run.prepared,evidence:run.review.evidence});
115
+ if(review.reportFingerprint!==run.review.reportFingerprint||canonicalJson(review.identityBody)!==canonicalJson(run.review.identityBody))throw new ComplianceError("DIAGNOSTIC_MISMATCH","Stored diagnostic review does not match a regenerated review","$.review");
116
+ const rerun=runMetricDiagnostics({prepared:run.prepared,groupMap:run.groupMap,groupDimensions:run.groupDimensions});
117
+ if(canonicalJson(getMetricDiagnosticsResultIdentity(rerun))!==canonicalJson(getMetricDiagnosticsResultIdentity(run.result)))throw new ComplianceError("DIAGNOSTIC_MISMATCH","Stored diagnostic result does not match deterministic replay","$.result");
118
+ const reviewBlocked=review.report.checks.some((check)=>!run.gate.allowedReviewStatuses.includes(check.status));
119
+ const metricBlocked=rerun.findings.some((finding)=>finding.category!=="structural"&&!run.gate.allowedMetricFindingSeverities.includes(finding.severity));
120
+ if(reviewBlocked||metricBlocked||run.gate.reviewGate!=="passed"||run.gate.metricGate!=="passed")throw new ComplianceError("DIAGNOSTIC_MISMATCH","Diagnostic execution gates do not recompute as passed","$.gate");
121
+ return {review,result:getMetricDiagnosticsResultIdentity(run.result)};
122
+ }
123
+
124
+ export async function createDiagnosticRunIdentity(input:CreateDiagnosticRunIdentityInput):Promise<VerifiedDiagnosticRunProvenance>{
125
+ const run=input.completedRun;const authenticated=rerunAndVerify(run);const artifactSnapshot=snapshotArtifacts(input.artifacts??[]);const lineage=snapshotLineage(input.lineage??[]);const artifacts=await digestArtifacts(artifactSnapshot);validateArtifactGraph(run,artifacts,lineage);
126
+ const preparation=getPreparedDiagnosticDataIdentity(run.prepared);
127
+ const expectedGridFingerprint=preparation.expectedCellsProvided?tag("diagnostic-expected-grid","expectedCells",preparation.expectedCells):null;
128
+ const manifest=freeze({definitionIntegrity:run.prepared.definition.definitionIntegrity,runPresetId:run.runPresetId,datasetArtifactId:run.datasetArtifactId,packageVersions:{"@actuarial-ts/core":CORE_PACKAGE_VERSION,"@actuarial-ts/data":DATA_PACKAGE_VERSION,"@actuarial-ts/compliance":COMPLIANCE_PACKAGE_VERSION},preparation,preparationFingerprint:run.prepared.preparationFingerprint,expectedGridFingerprint,executionPolicy:{review:{body:authenticated.review.identityBody,reportFingerprint:authenticated.review.reportFingerprint},gate:run.gate},groupMap:{...run.groupMap},groupDimensions:{...run.groupDimensions},artifacts,lineage});
129
+ const runFingerprint=tag("diagnostic-run","run",manifest);const resultFingerprint=tag("diagnostic-result","result",authenticated.result);const runResultFingerprint=tag("diagnostic-run-result","binding",{runFingerprint,resultFingerprint});
130
+ const definition=run.prepared.definition;
131
+ const provenance=freeze({definition:definition.definition,definitionIdentities:{algorithm:"fnv1a64-jcs-v1" as const,formulaById:{...definition.formulaFingerprints},calculationByInstanceId:{...definition.calculationFingerprints},definition:definition.definitionIntegrity},manifest,review:authenticated.review,result:run.result,runFingerprint,resultFingerprint,runResultFingerprint}) as unknown as VerifiedDiagnosticRunProvenance;
132
+ verified.set(provenance,run);return provenance;
133
+ }
134
+
135
+ export function assertVerifiedDiagnosticRunProvenance(value:unknown):asserts value is VerifiedDiagnosticRunProvenance{if(value===null||typeof value!=="object"||!verified.has(value))throw new ComplianceError("BAD_DIAGNOSTIC_RUN","Value is not authentic verified diagnostic provenance","$")}
136
+
137
+ /** @internal Bundle authoring uses owner state rather than trusting the public snapshot. */
138
+ export function verifiedDefinitionForBundle(value:VerifiedDiagnosticRunProvenance):CompiledDiagnosticDefinition{assertVerifiedDiagnosticRunProvenance(value);return verified.get(value)!.prepared.definition}
139
+
140
+ export async function verifyDiagnosticRunIdentity(candidate:unknown,input:CreateDiagnosticRunIdentityInput):Promise<VerifiedDiagnosticRunProvenance>{
141
+ const regenerated=await createDiagnosticRunIdentity(input);
142
+ let left:string;try{left=canonicalJson(candidate)}catch{throw new ComplianceError("DIAGNOSTIC_MISMATCH","Stored diagnostic provenance is not canonical JSON","$")}
143
+ if(left!==canonicalJson(regenerated))throw new ComplianceError("DIAGNOSTIC_MISMATCH","Stored diagnostic provenance differs from regenerated provenance",firstDifference(candidate,regenerated,"$")??"$");
144
+ return regenerated;
145
+ }
146
+
147
+ function plain(value:unknown):value is Record<string,unknown>{if(value===null||typeof value!=="object"||Array.isArray(value))return false;const prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===null}
148
+ function firstDifference(left:unknown,right:unknown,path:string):string|null{if(plain(left)&&plain(right)){const keys=[...new Set([...Object.keys(left),...Object.keys(right)])].sort();for(const key of keys){if(!(key in left)||!(key in right))return `${path}.${key}`;const found=firstDifference(left[key],right[key],`${path}.${key}`);if(found)return found}return null}if(Array.isArray(left)&&Array.isArray(right)){const shared=Math.min(left.length,right.length);for(let index=0;index<shared;index++){const found=firstDifference(left[index],right[index],`${path}[${index}]`);if(found)return found}return left.length===right.length?null:`${path}[${shared}]`}try{return canonicalJson(left)===canonicalJson(right)?null:path}catch{return path}}
package/src/errors.ts ADDED
@@ -0,0 +1,12 @@
1
+ export const COMPLIANCE_ERROR_CODES = [
2
+ "BAD_BUNDLE", "BAD_CDF", "MISSING_RATIONALE", "BAD_DIAGNOSTIC_RUN",
3
+ "DIAGNOSTIC_MISMATCH", "CRYPTO_UNAVAILABLE",
4
+ ] as const;
5
+ export type ComplianceErrorCode = (typeof COMPLIANCE_ERROR_CODES)[number];
6
+ export class ComplianceError extends Error {
7
+ readonly code: ComplianceErrorCode;
8
+ readonly path?: string;
9
+ constructor(code: ComplianceErrorCode, message: string, path?: string) {
10
+ super(message);this.name="ComplianceError";this.code=code;if(path!==undefined)this.path=path;
11
+ }
12
+ }
package/src/index.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  export * from "./metadata.js";
2
+ export * from "./errors.js";
3
+ export * from "./version.js";
2
4
  export * from "./ledger.js";
3
5
  export * from "./modelCards.js";
4
6
  export * from "./disclosure.js";
5
- export * from "./diagnostics.js";
7
+ export * from "./diagnosticRun.js";
6
8
  export * from "./bundle.js";
7
9
  export * from "./ave.js";
package/src/version.ts ADDED
@@ -0,0 +1 @@
1
+ export const COMPLIANCE_PACKAGE_VERSION = "0.6.0";
@@ -1,75 +0,0 @@
1
- import type { AmountLayerDefinition, MeasureExpression, MetricDefinition, SparseValuePolicy } from "@actuarial-ts/core";
2
- /** Serializable metric metadata: executable warning callbacks are deliberately excluded. */
3
- export interface DiagnosticMetricProvenance {
4
- id: string;
5
- definitionVersion: string;
6
- displayName: string;
7
- unit: string;
8
- scale: number;
9
- numerator: MeasureExpression;
10
- denominator: MeasureExpression;
11
- numeratorLabel: string;
12
- denominatorLabel: string;
13
- basis: string;
14
- requiredComponents: string[];
15
- }
16
- export interface DiagnosticLayerProvenance {
17
- id: string;
18
- displayName: string;
19
- paidMeasure: string;
20
- incurredMeasure: string;
21
- paid: AmountLayerDefinition["paid"];
22
- incurred: AmountLayerDefinition["incurred"];
23
- basis: AmountLayerDefinition["basis"];
24
- }
25
- export interface CreateDiagnosticsProvenanceInput {
26
- packageVersions: Readonly<Record<string, string>>;
27
- formulaPack: {
28
- id: string;
29
- version: string;
30
- };
31
- metrics: readonly MetricDefinition[];
32
- layers?: readonly AmountLayerDefinition[];
33
- exposure: {
34
- basis: string;
35
- frequencyScale: number;
36
- };
37
- sparsePolicy: SparseValuePolicy;
38
- ageConvention: string;
39
- completePeriodCutoffs: Readonly<Record<string, string | number | boolean | null>>;
40
- appliedFilters?: Readonly<Record<string, unknown>>;
41
- groupingSelections?: Readonly<Record<string, unknown>>;
42
- inputReferences: readonly {
43
- id: string;
44
- hash?: string;
45
- }[];
46
- }
47
- /**
48
- * Audit metadata for a diagnostic run. This helper lives in compliance so
49
- * core numeric results stay deterministic and free of application filter or
50
- * persistence state. The returned record can be placed directly in
51
- * `createBundle(...).parameters` or an interchange `extensions` object.
52
- */
53
- export declare function createDiagnosticsProvenance(input: CreateDiagnosticsProvenanceInput): {
54
- packageVersions: Record<string, string>;
55
- formulaPack: {
56
- id: string;
57
- version: string;
58
- };
59
- metrics: DiagnosticMetricProvenance[];
60
- layers: DiagnosticLayerProvenance[];
61
- exposure: {
62
- basis: string;
63
- frequencyScale: number;
64
- };
65
- sparsePolicy: SparseValuePolicy;
66
- ageConvention: string;
67
- completePeriodCutoffs: Record<string, string | number | boolean | null>;
68
- appliedFilters?: Record<string, unknown>;
69
- groupingSelections?: Record<string, unknown>;
70
- inputReferences: {
71
- id: string;
72
- hash?: string;
73
- }[];
74
- };
75
- //# sourceMappingURL=diagnostics.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"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"}
@@ -1,44 +0,0 @@
1
- /**
2
- * Audit metadata for a diagnostic run. This helper lives in compliance so
3
- * core numeric results stay deterministic and free of application filter or
4
- * persistence state. The returned record can be placed directly in
5
- * `createBundle(...).parameters` or an interchange `extensions` object.
6
- */
7
- export function createDiagnosticsProvenance(input) {
8
- const metrics = input.metrics.map((metric) => ({
9
- id: metric.id,
10
- definitionVersion: metric.version,
11
- displayName: metric.displayName,
12
- unit: metric.unit,
13
- scale: metric.scale,
14
- numerator: metric.numerator,
15
- denominator: metric.denominator,
16
- numeratorLabel: metric.numeratorLabel,
17
- denominatorLabel: metric.denominatorLabel,
18
- basis: metric.basis,
19
- requiredComponents: [...metric.requiredComponents],
20
- }));
21
- const layers = (input.layers ?? []).map((layer) => ({
22
- id: layer.id,
23
- displayName: layer.displayName,
24
- paidMeasure: layer.paidMeasure,
25
- incurredMeasure: layer.incurredMeasure,
26
- paid: layer.paid,
27
- incurred: layer.incurred,
28
- basis: layer.basis,
29
- }));
30
- return {
31
- packageVersions: { ...input.packageVersions },
32
- formulaPack: { ...input.formulaPack },
33
- metrics,
34
- layers,
35
- exposure: { ...input.exposure },
36
- sparsePolicy: input.sparsePolicy,
37
- ageConvention: input.ageConvention,
38
- completePeriodCutoffs: { ...input.completePeriodCutoffs },
39
- ...(input.appliedFilters ? { appliedFilters: { ...input.appliedFilters } } : {}),
40
- ...(input.groupingSelections ? { groupingSelections: { ...input.groupingSelections } } : {}),
41
- inputReferences: input.inputReferences.map((reference) => ({ ...reference })),
42
- };
43
- }
44
- //# sourceMappingURL=diagnostics.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AA8CA;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B,CACzC,KAAuC;IAcvC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC7C,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,iBAAiB,EAAE,MAAM,CAAC,OAAO;QACjC,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,kBAAkB,EAAE,CAAC,GAAG,MAAM,CAAC,kBAAkB,CAAC;KACnD,CAAC,CAAC,CAAC;IACJ,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAClD,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,EAAE,KAAK,CAAC,KAAK;KACnB,CAAC,CAAC,CAAC;IACJ,OAAO;QACL,eAAe,EAAE,EAAE,GAAG,KAAK,CAAC,eAAe,EAAE;QAC7C,WAAW,EAAE,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE;QACrC,OAAO;QACP,MAAM;QACN,QAAQ,EAAE,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE;QAC/B,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,qBAAqB,EAAE,EAAE,GAAG,KAAK,CAAC,qBAAqB,EAAE;QACzD,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChF,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,EAAE,GAAG,KAAK,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5F,eAAe,EAAE,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,SAAS,EAAE,CAAC,CAAC;KAC9E,CAAC;AACJ,CAAC"}
@@ -1,104 +0,0 @@
1
- import type {
2
- AmountLayerDefinition,
3
- MeasureExpression,
4
- MetricDefinition,
5
- SparseValuePolicy,
6
- } from "@actuarial-ts/core";
7
-
8
- /** Serializable metric metadata: executable warning callbacks are deliberately excluded. */
9
- export interface DiagnosticMetricProvenance {
10
- id: string;
11
- definitionVersion: string;
12
- displayName: string;
13
- unit: string;
14
- scale: number;
15
- numerator: MeasureExpression;
16
- denominator: MeasureExpression;
17
- numeratorLabel: string;
18
- denominatorLabel: string;
19
- basis: string;
20
- requiredComponents: string[];
21
- }
22
-
23
- export interface DiagnosticLayerProvenance {
24
- id: string;
25
- displayName: string;
26
- paidMeasure: string;
27
- incurredMeasure: string;
28
- paid: AmountLayerDefinition["paid"];
29
- incurred: AmountLayerDefinition["incurred"];
30
- basis: AmountLayerDefinition["basis"];
31
- }
32
-
33
- export interface CreateDiagnosticsProvenanceInput {
34
- packageVersions: Readonly<Record<string, string>>;
35
- formulaPack: { id: string; version: string };
36
- metrics: readonly MetricDefinition[];
37
- layers?: readonly AmountLayerDefinition[];
38
- exposure: { basis: string; frequencyScale: number };
39
- sparsePolicy: SparseValuePolicy;
40
- ageConvention: string;
41
- completePeriodCutoffs: Readonly<Record<string, string | number | boolean | null>>;
42
- appliedFilters?: Readonly<Record<string, unknown>>;
43
- groupingSelections?: Readonly<Record<string, unknown>>;
44
- inputReferences: readonly { id: string; hash?: string }[];
45
- }
46
-
47
- /**
48
- * Audit metadata for a diagnostic run. This helper lives in compliance so
49
- * core numeric results stay deterministic and free of application filter or
50
- * persistence state. The returned record can be placed directly in
51
- * `createBundle(...).parameters` or an interchange `extensions` object.
52
- */
53
- export function createDiagnosticsProvenance(
54
- input: CreateDiagnosticsProvenanceInput,
55
- ): {
56
- packageVersions: Record<string, string>;
57
- formulaPack: { id: string; version: string };
58
- metrics: DiagnosticMetricProvenance[];
59
- layers: DiagnosticLayerProvenance[];
60
- exposure: { basis: string; frequencyScale: number };
61
- sparsePolicy: SparseValuePolicy;
62
- ageConvention: string;
63
- completePeriodCutoffs: Record<string, string | number | boolean | null>;
64
- appliedFilters?: Record<string, unknown>;
65
- groupingSelections?: Record<string, unknown>;
66
- inputReferences: { id: string; hash?: string }[];
67
- } {
68
- const metrics = input.metrics.map((metric) => ({
69
- id: metric.id,
70
- definitionVersion: metric.version,
71
- displayName: metric.displayName,
72
- unit: metric.unit,
73
- scale: metric.scale,
74
- numerator: metric.numerator,
75
- denominator: metric.denominator,
76
- numeratorLabel: metric.numeratorLabel,
77
- denominatorLabel: metric.denominatorLabel,
78
- basis: metric.basis,
79
- requiredComponents: [...metric.requiredComponents],
80
- }));
81
- const layers = (input.layers ?? []).map((layer) => ({
82
- id: layer.id,
83
- displayName: layer.displayName,
84
- paidMeasure: layer.paidMeasure,
85
- incurredMeasure: layer.incurredMeasure,
86
- paid: layer.paid,
87
- incurred: layer.incurred,
88
- basis: layer.basis,
89
- }));
90
- return {
91
- packageVersions: { ...input.packageVersions },
92
- formulaPack: { ...input.formulaPack },
93
- metrics,
94
- layers,
95
- exposure: { ...input.exposure },
96
- sparsePolicy: input.sparsePolicy,
97
- ageConvention: input.ageConvention,
98
- completePeriodCutoffs: { ...input.completePeriodCutoffs },
99
- ...(input.appliedFilters ? { appliedFilters: { ...input.appliedFilters } } : {}),
100
- ...(input.groupingSelections ? { groupingSelections: { ...input.groupingSelections } } : {}),
101
- inputReferences: input.inputReferences.map((reference) => ({ ...reference })),
102
- };
103
- }
104
-