vet-data-utils-ts 0.3.0 → 0.4.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 CHANGED
@@ -8,7 +8,29 @@ Current surfaces cover the canonical animal-card DID, veterinary summary
8
8
  sections, animal-only emergency data, pseudonymous DigitalTwin search input and
9
9
  veterinary assistant intents.
10
10
 
11
+ The financial surface defines provider-neutral flat claims for FHIR R5
12
+ `Account`, `Invoice`, `PaymentNotice` and `PaymentReconciliation`. `Account`
13
+ tracks charges and balances; the responsible party is represented through
14
+ `Invoice.recipient`, `Account.guarantor-party` and, for an actual payment,
15
+ `PaymentReconciliation.payment-issuer`. Payment processors and wallets remain
16
+ adapters outside this package. `Communication` transports or references these
17
+ resources but does not duplicate their financial claims.
18
+
19
+ `vet-data-utils-ts/iso-jurisdictions` exports the assigned ISO 3166-1 alpha-2
20
+ country catalogue and every ISO 3166-2 subdivision level, including type and
21
+ parent relationships. Consumers persist codes such as `CA`, `CA-BC`, `ES-MD`
22
+ or its child province `ES-M`; localized labels remain presentation concerns.
23
+ Selecting a jurisdiction grants no authority. The generated catalogue comes
24
+ from Debian `iso-codes` 4.20.1; run `npm run update:iso-jurisdictions` to
25
+ refresh it deliberately.
26
+
11
27
  The catalogue retains longitudinal sections that also apply to animal care,
12
28
  with animal-facing labels where needed. Human advance directives
13
29
  (`LOINC|42348-3`) are deliberately excluded because they express the patient's
14
30
  own legal/autonomy decisions, not controller preferences.
31
+
32
+ Digital-twin search input contains one section, one or more resource types
33
+ allowed by that section, display text, `dateFrom` and optional `dateTo`.
34
+ `VeterinaryDigitalTwinSectionSearchProfiles` owns the corresponding
35
+ `code-display` and date claims so browser code never supplies claim names.
36
+ Identifying fields and caller-authored `claim`/`dateClaim` values are rejected.
@@ -0,0 +1,13 @@
1
+ # Third-party notices
2
+
3
+ The generated ISO jurisdiction catalogue is derived from Debian `iso-codes`
4
+ 4.20.1-1:
5
+
6
+ - Source: https://salsa.debian.org/iso-codes-team/iso-codes
7
+ - Copyright: 2001-2008 Alastair McKinstry; 2004-2016 Christian Perrier;
8
+ 2005-2026 Dr. Tobias Quathamer
9
+ - License: GNU Lesser General Public License 2.1 or later
10
+ - License text: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
11
+
12
+ The generated catalogue may be replaced or updated independently by running
13
+ `npm run update:iso-jurisdictions` against the version pinned in that script.
@@ -3,11 +3,24 @@ export declare const VeterinaryDigitalTwinFormats: Readonly<{
3
3
  readonly R4: "org.hl7.fhir.r4";
4
4
  }>;
5
5
  export type VeterinaryDigitalTwinFormat = typeof VeterinaryDigitalTwinFormats[keyof typeof VeterinaryDigitalTwinFormats];
6
+ export type VeterinaryDigitalTwinResourceSearchProfile = Readonly<{
7
+ resourceType: string;
8
+ codeDisplayClaim: `${string}.code-display`;
9
+ dateClaim: `${string}.${string}`;
10
+ }>;
11
+ /**
12
+ * Searchable flat-claim projections for each veterinary section.
13
+ *
14
+ * Native FHIR resources are converted by GW into these governed claims. The
15
+ * browser selects resource families only; it never supplies claim names.
16
+ */
17
+ export declare const VeterinaryDigitalTwinSectionSearchProfiles: Readonly<Record<string, readonly VeterinaryDigitalTwinResourceSearchProfile[]>>;
6
18
  export type VeterinaryDigitalTwinSearch = Readonly<{
7
19
  section: string;
8
- resourceType: string;
9
- claim: string;
10
- value: string;
20
+ resourceTypes: readonly string[];
21
+ text: string;
22
+ dateFrom: string;
23
+ dateTo?: string;
11
24
  }>;
12
- /** Bounds one section-first pseudonymous search and rejects direct identifiers. */
25
+ /** Bounds one section-first pseudonymous search and rejects direct identifiers and caller-authored claims. */
13
26
  export declare function parseVeterinaryDigitalTwinSearch(input: unknown): VeterinaryDigitalTwinSearch;
@@ -1,24 +1,61 @@
1
- import { isVeterinarySummarySection } from './veterinary-sections.js';
1
+ import { VeterinarySummarySections, isVeterinarySummarySection } from './veterinary-sections.js';
2
2
  export const VeterinaryDigitalTwinFormats = Object.freeze({ Api: 'org.hl7.fhir.api', R4: 'org.hl7.fhir.r4' });
3
- /** Bounds one section-first pseudonymous search and rejects direct identifiers. */
3
+ const profile = (resourceType, dateClaim) => Object.freeze({ resourceType, codeDisplayClaim: `${resourceType}.code-display`, dateClaim });
4
+ /**
5
+ * Searchable flat-claim projections for each veterinary section.
6
+ *
7
+ * Native FHIR resources are converted by GW into these governed claims. The
8
+ * browser selects resource families only; it never supplies claim names.
9
+ */
10
+ export const VeterinaryDigitalTwinSectionSearchProfiles = Object.freeze({
11
+ [VeterinarySummarySections.Problems.value]: Object.freeze([profile('Condition', 'Condition.onset-datetime')]),
12
+ [VeterinarySummarySections.Allergies.value]: Object.freeze([profile('AllergyIntolerance', 'AllergyIntolerance.onset-datetime')]),
13
+ [VeterinarySummarySections.Medications.value]: Object.freeze([profile('MedicationStatement', 'MedicationStatement.effective')]),
14
+ [VeterinarySummarySections.Immunizations.value]: Object.freeze([profile('Immunization', 'Immunization.date')]),
15
+ [VeterinarySummarySections.Results.value]: Object.freeze([
16
+ profile('Observation', 'Observation.date'),
17
+ profile('DiagnosticReport', 'DiagnosticReport.date'),
18
+ ]),
19
+ [VeterinarySummarySections.Procedures.value]: Object.freeze([profile('Procedure', 'Procedure.date')]),
20
+ [VeterinarySummarySections.VitalSigns.value]: Object.freeze([profile('Observation', 'Observation.date')]),
21
+ [VeterinarySummarySections.EnvironmentAndLifestyle.value]: Object.freeze([profile('Observation', 'Observation.date')]),
22
+ [VeterinarySummarySections.FunctionalStatus.value]: Object.freeze([profile('Condition', 'Condition.onset-datetime')]),
23
+ [VeterinarySummarySections.PastIllness.value]: Object.freeze([profile('Condition', 'Condition.onset-datetime')]),
24
+ [VeterinarySummarySections.ReproductiveHistory.value]: Object.freeze([profile('Observation', 'Observation.date')]),
25
+ });
26
+ /** Bounds one section-first pseudonymous search and rejects direct identifiers and caller-authored claims. */
4
27
  export function parseVeterinaryDigitalTwinSearch(input) {
5
28
  const value = (input || {});
6
29
  for (const forbidden of ['name', 'email', 'telephone', 'identifier', 'subjectDid', 'cardNumber']) {
7
30
  if (String(value[forbidden] || '').trim())
8
31
  throw new TypeError('veterinary_digital_twin_identifying_filter_forbidden');
9
32
  }
33
+ if (String(value.claim || '').trim() || String(value.dateClaim || '').trim()) {
34
+ throw new TypeError('veterinary_digital_twin_claim_forbidden');
35
+ }
10
36
  const section = String(value.section || '').trim();
11
- const resourceType = String(value.resourceType || '').trim();
12
- const claim = String(value.claim || '').trim();
13
- const filterValue = String(value.value || '').trim();
14
37
  if (!isVeterinarySummarySection(section))
15
38
  throw new TypeError('veterinary_digital_twin_section_invalid');
16
- if (!/^[A-Z][A-Za-z]{1,63}$/.test(resourceType))
39
+ const allowedProfiles = VeterinaryDigitalTwinSectionSearchProfiles[section] || [];
40
+ if (allowedProfiles.length === 0)
41
+ throw new TypeError('veterinary_digital_twin_section_not_searchable');
42
+ const rawResourceTypes = Array.isArray(value.resourceTypes) ? value.resourceTypes : [value.resourceType];
43
+ const resourceTypes = Array.from(new Set(rawResourceTypes.map(item => String(item || '').trim()).filter(Boolean)));
44
+ if (resourceTypes.length === 0 || resourceTypes.some(resourceType => !allowedProfiles.some(item => item.resourceType === resourceType))) {
17
45
  throw new TypeError('veterinary_digital_twin_resource_invalid');
18
- if (!new RegExp(`^${resourceType.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\.[A-Za-z0-9][A-Za-z0-9.-]{0,127}$`).test(claim)) {
19
- throw new TypeError('veterinary_digital_twin_claim_invalid');
20
46
  }
21
- if (!filterValue || filterValue.length > 256)
22
- throw new TypeError('veterinary_digital_twin_value_invalid');
23
- return Object.freeze({ section, resourceType, claim, value: filterValue });
47
+ const text = String(value.text || value.value || '').trim();
48
+ if (!text || text.length > 256)
49
+ throw new TypeError('veterinary_digital_twin_text_invalid');
50
+ const dateFrom = parseIsoDate(value.dateFrom, 'date_from');
51
+ const dateTo = String(value.dateTo || '').trim() ? parseIsoDate(value.dateTo, 'date_to') : undefined;
52
+ if (dateTo && Date.parse(dateTo) < Date.parse(dateFrom))
53
+ throw new TypeError('veterinary_digital_twin_date_range_invalid');
54
+ return Object.freeze({ section, resourceTypes: Object.freeze(resourceTypes), text, dateFrom, ...(dateTo ? { dateTo } : {}) });
55
+ }
56
+ function parseIsoDate(value, field) {
57
+ const normalized = String(value || '').trim();
58
+ if (!normalized || Number.isNaN(Date.parse(normalized)))
59
+ throw new TypeError(`veterinary_digital_twin_${field}_invalid`);
60
+ return normalized;
24
61
  }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Provider-neutral flat-claim names for veterinary financial resources.
3
+ *
4
+ * These names describe FHIR R5 Account, Invoice, PaymentNotice and
5
+ * PaymentReconciliation projections. They do not authorize payment, select a
6
+ * ledger channel or expose a payment-provider payload. Repeating FHIR elements
7
+ * remain repeated claim values; datatype serialization belongs to GW.
8
+ */
9
+ export declare const VeterinaryFinancialResourceTypes: Readonly<{
10
+ readonly Account: "Account";
11
+ readonly Invoice: "Invoice";
12
+ readonly PaymentNotice: "PaymentNotice";
13
+ readonly PaymentReconciliation: "PaymentReconciliation";
14
+ }>;
15
+ export type VeterinaryFinancialResourceType = typeof VeterinaryFinancialResourceTypes[keyof typeof VeterinaryFinancialResourceTypes];
16
+ /** Canonical claim allow-list used before a native FHIR resource is indexed. */
17
+ export declare const VeterinaryFinancialClaimCatalog: Readonly<{
18
+ Account: readonly ["Account.identifier", "Account.status", "Account.billing-status", "Account.type", "Account.name", "Account.subject", "Account.service-period", "Account.coverage-coverage", "Account.coverage-priority", "Account.owner", "Account.description", "Account.guarantor-party", "Account.guarantor-on-hold", "Account.guarantor-period", "Account.diagnosis-sequence", "Account.diagnosis-condition", "Account.diagnosis-date", "Account.diagnosis-type", "Account.diagnosis-on-admission", "Account.diagnosis-package-code", "Account.procedure-sequence", "Account.procedure-code", "Account.procedure-date-of-service", "Account.procedure-type", "Account.procedure-package-code", "Account.procedure-device", "Account.related-account-account", "Account.related-account-relationship", "Account.currency", "Account.balance-aggregate", "Account.balance-term", "Account.balance-estimate", "Account.balance-amount", "Account.calculated-at"];
19
+ Invoice: readonly ["Invoice.identifier", "Invoice.status", "Invoice.cancelled-reason", "Invoice.type", "Invoice.subject", "Invoice.recipient", "Invoice.creation", "Invoice.period", "Invoice.participant-role", "Invoice.participant-actor", "Invoice.issuer", "Invoice.account", "Invoice.line-item-sequence", "Invoice.line-item-serviced", "Invoice.line-item-charge-item", "Invoice.line-item-price-component-type", "Invoice.line-item-price-component-code", "Invoice.line-item-price-component-factor", "Invoice.line-item-price-component-amount", "Invoice.total-price-component-type", "Invoice.total-price-component-code", "Invoice.total-price-component-factor", "Invoice.total-price-component-amount", "Invoice.total-net", "Invoice.total-gross", "Invoice.payment-terms", "Invoice.note"];
20
+ PaymentNotice: readonly ["PaymentNotice.identifier", "PaymentNotice.status", "PaymentNotice.request", "PaymentNotice.response", "PaymentNotice.created", "PaymentNotice.reporter", "PaymentNotice.payment", "PaymentNotice.payment-date", "PaymentNotice.payee", "PaymentNotice.recipient", "PaymentNotice.amount", "PaymentNotice.payment-status"];
21
+ PaymentReconciliation: readonly ["PaymentReconciliation.identifier", "PaymentReconciliation.type", "PaymentReconciliation.status", "PaymentReconciliation.kind", "PaymentReconciliation.period", "PaymentReconciliation.created", "PaymentReconciliation.enterer", "PaymentReconciliation.issuer-type", "PaymentReconciliation.payment-issuer", "PaymentReconciliation.request", "PaymentReconciliation.requestor", "PaymentReconciliation.outcome", "PaymentReconciliation.disposition", "PaymentReconciliation.date", "PaymentReconciliation.location", "PaymentReconciliation.method", "PaymentReconciliation.card-brand", "PaymentReconciliation.account-number", "PaymentReconciliation.expiration-date", "PaymentReconciliation.processor", "PaymentReconciliation.reference-number", "PaymentReconciliation.authorization", "PaymentReconciliation.tendered-amount", "PaymentReconciliation.returned-amount", "PaymentReconciliation.amount", "PaymentReconciliation.payment-identifier", "PaymentReconciliation.allocation-identifier", "PaymentReconciliation.allocation-predecessor", "PaymentReconciliation.allocation-target", "PaymentReconciliation.allocation-target-item", "PaymentReconciliation.allocation-encounter", "PaymentReconciliation.allocation-account", "PaymentReconciliation.allocation-type", "PaymentReconciliation.allocation-submitter", "PaymentReconciliation.allocation-response", "PaymentReconciliation.allocation-date", "PaymentReconciliation.allocation-responsible", "PaymentReconciliation.allocation-payee", "PaymentReconciliation.allocation-amount", "PaymentReconciliation.form-code", "PaymentReconciliation.process-note-type", "PaymentReconciliation.process-note-text"];
22
+ }>;
23
+ /** Returns the immutable allow-list for one supported financial resource. */
24
+ export declare function getVeterinaryFinancialClaims(resourceType: unknown): readonly string[];
25
+ /** Checks both the resource family and its claim prefix/allow-list. */
26
+ export declare function isVeterinaryFinancialClaim(resourceType: unknown, claim: unknown): boolean;
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Provider-neutral flat-claim names for veterinary financial resources.
3
+ *
4
+ * These names describe FHIR R5 Account, Invoice, PaymentNotice and
5
+ * PaymentReconciliation projections. They do not authorize payment, select a
6
+ * ledger channel or expose a payment-provider payload. Repeating FHIR elements
7
+ * remain repeated claim values; datatype serialization belongs to GW.
8
+ */
9
+ export const VeterinaryFinancialResourceTypes = Object.freeze({
10
+ Account: 'Account',
11
+ Invoice: 'Invoice',
12
+ PaymentNotice: 'PaymentNotice',
13
+ PaymentReconciliation: 'PaymentReconciliation',
14
+ });
15
+ const accountClaims = Object.freeze([
16
+ 'Account.identifier',
17
+ 'Account.status',
18
+ 'Account.billing-status',
19
+ 'Account.type',
20
+ 'Account.name',
21
+ 'Account.subject',
22
+ 'Account.service-period',
23
+ 'Account.coverage-coverage',
24
+ 'Account.coverage-priority',
25
+ 'Account.owner',
26
+ 'Account.description',
27
+ 'Account.guarantor-party',
28
+ 'Account.guarantor-on-hold',
29
+ 'Account.guarantor-period',
30
+ 'Account.diagnosis-sequence',
31
+ 'Account.diagnosis-condition',
32
+ 'Account.diagnosis-date',
33
+ 'Account.diagnosis-type',
34
+ 'Account.diagnosis-on-admission',
35
+ 'Account.diagnosis-package-code',
36
+ 'Account.procedure-sequence',
37
+ 'Account.procedure-code',
38
+ 'Account.procedure-date-of-service',
39
+ 'Account.procedure-type',
40
+ 'Account.procedure-package-code',
41
+ 'Account.procedure-device',
42
+ 'Account.related-account-account',
43
+ 'Account.related-account-relationship',
44
+ 'Account.currency',
45
+ 'Account.balance-aggregate',
46
+ 'Account.balance-term',
47
+ 'Account.balance-estimate',
48
+ 'Account.balance-amount',
49
+ 'Account.calculated-at',
50
+ ]);
51
+ const invoiceClaims = Object.freeze([
52
+ 'Invoice.identifier',
53
+ 'Invoice.status',
54
+ 'Invoice.cancelled-reason',
55
+ 'Invoice.type',
56
+ 'Invoice.subject',
57
+ 'Invoice.recipient',
58
+ 'Invoice.creation',
59
+ 'Invoice.period',
60
+ 'Invoice.participant-role',
61
+ 'Invoice.participant-actor',
62
+ 'Invoice.issuer',
63
+ 'Invoice.account',
64
+ 'Invoice.line-item-sequence',
65
+ 'Invoice.line-item-serviced',
66
+ 'Invoice.line-item-charge-item',
67
+ 'Invoice.line-item-price-component-type',
68
+ 'Invoice.line-item-price-component-code',
69
+ 'Invoice.line-item-price-component-factor',
70
+ 'Invoice.line-item-price-component-amount',
71
+ 'Invoice.total-price-component-type',
72
+ 'Invoice.total-price-component-code',
73
+ 'Invoice.total-price-component-factor',
74
+ 'Invoice.total-price-component-amount',
75
+ 'Invoice.total-net',
76
+ 'Invoice.total-gross',
77
+ 'Invoice.payment-terms',
78
+ 'Invoice.note',
79
+ ]);
80
+ const paymentNoticeClaims = Object.freeze([
81
+ 'PaymentNotice.identifier',
82
+ 'PaymentNotice.status',
83
+ 'PaymentNotice.request',
84
+ 'PaymentNotice.response',
85
+ 'PaymentNotice.created',
86
+ 'PaymentNotice.reporter',
87
+ 'PaymentNotice.payment',
88
+ 'PaymentNotice.payment-date',
89
+ 'PaymentNotice.payee',
90
+ 'PaymentNotice.recipient',
91
+ 'PaymentNotice.amount',
92
+ 'PaymentNotice.payment-status',
93
+ ]);
94
+ const paymentReconciliationClaims = Object.freeze([
95
+ 'PaymentReconciliation.identifier',
96
+ 'PaymentReconciliation.type',
97
+ 'PaymentReconciliation.status',
98
+ 'PaymentReconciliation.kind',
99
+ 'PaymentReconciliation.period',
100
+ 'PaymentReconciliation.created',
101
+ 'PaymentReconciliation.enterer',
102
+ 'PaymentReconciliation.issuer-type',
103
+ 'PaymentReconciliation.payment-issuer',
104
+ 'PaymentReconciliation.request',
105
+ 'PaymentReconciliation.requestor',
106
+ 'PaymentReconciliation.outcome',
107
+ 'PaymentReconciliation.disposition',
108
+ 'PaymentReconciliation.date',
109
+ 'PaymentReconciliation.location',
110
+ 'PaymentReconciliation.method',
111
+ 'PaymentReconciliation.card-brand',
112
+ 'PaymentReconciliation.account-number',
113
+ 'PaymentReconciliation.expiration-date',
114
+ 'PaymentReconciliation.processor',
115
+ 'PaymentReconciliation.reference-number',
116
+ 'PaymentReconciliation.authorization',
117
+ 'PaymentReconciliation.tendered-amount',
118
+ 'PaymentReconciliation.returned-amount',
119
+ 'PaymentReconciliation.amount',
120
+ 'PaymentReconciliation.payment-identifier',
121
+ 'PaymentReconciliation.allocation-identifier',
122
+ 'PaymentReconciliation.allocation-predecessor',
123
+ 'PaymentReconciliation.allocation-target',
124
+ 'PaymentReconciliation.allocation-target-item',
125
+ 'PaymentReconciliation.allocation-encounter',
126
+ 'PaymentReconciliation.allocation-account',
127
+ 'PaymentReconciliation.allocation-type',
128
+ 'PaymentReconciliation.allocation-submitter',
129
+ 'PaymentReconciliation.allocation-response',
130
+ 'PaymentReconciliation.allocation-date',
131
+ 'PaymentReconciliation.allocation-responsible',
132
+ 'PaymentReconciliation.allocation-payee',
133
+ 'PaymentReconciliation.allocation-amount',
134
+ 'PaymentReconciliation.form-code',
135
+ 'PaymentReconciliation.process-note-type',
136
+ 'PaymentReconciliation.process-note-text',
137
+ ]);
138
+ /** Canonical claim allow-list used before a native FHIR resource is indexed. */
139
+ export const VeterinaryFinancialClaimCatalog = Object.freeze({
140
+ Account: accountClaims,
141
+ Invoice: invoiceClaims,
142
+ PaymentNotice: paymentNoticeClaims,
143
+ PaymentReconciliation: paymentReconciliationClaims,
144
+ });
145
+ /** Returns the immutable allow-list for one supported financial resource. */
146
+ export function getVeterinaryFinancialClaims(resourceType) {
147
+ const normalized = String(resourceType || '').trim();
148
+ if (!isVeterinaryFinancialResourceType(normalized)) {
149
+ throw new TypeError('veterinary_financial_resource_type_invalid');
150
+ }
151
+ return VeterinaryFinancialClaimCatalog[normalized];
152
+ }
153
+ /** Checks both the resource family and its claim prefix/allow-list. */
154
+ export function isVeterinaryFinancialClaim(resourceType, claim) {
155
+ const normalizedResourceType = String(resourceType || '').trim();
156
+ if (!isVeterinaryFinancialResourceType(normalizedResourceType))
157
+ return false;
158
+ return VeterinaryFinancialClaimCatalog[normalizedResourceType].includes(String(claim || '').trim());
159
+ }
160
+ function isVeterinaryFinancialResourceType(value) {
161
+ return Object.values(VeterinaryFinancialResourceTypes).includes(value);
162
+ }
package/dist/index.d.ts CHANGED
@@ -2,4 +2,6 @@ export * from './animal-card.js';
2
2
  export * from './assistant.js';
3
3
  export * from './digital-twin.js';
4
4
  export * from './emergency.js';
5
+ export * from './financial.js';
6
+ export * from './iso-jurisdictions.js';
5
7
  export * from './veterinary-sections.js';
package/dist/index.js CHANGED
@@ -2,4 +2,6 @@ export * from './animal-card.js';
2
2
  export * from './assistant.js';
3
3
  export * from './digital-twin.js';
4
4
  export * from './emergency.js';
5
+ export * from './financial.js';
6
+ export * from './iso-jurisdictions.js';
5
7
  export * from './veterinary-sections.js';
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Generated from Debian iso-codes 4.20.1-1. Do not edit manually.
3
+ * Source: https://sources.debian.org/data/main/i/iso-codes/4.20.1-1/data
4
+ */
5
+ export declare const Iso3166CountryData: readonly {
6
+ code: string;
7
+ name: string;
8
+ }[];
9
+ export declare const Iso3166SubdivisionData: readonly {
10
+ code: string;
11
+ countryCode: string;
12
+ name: string;
13
+ parentCode?: string;
14
+ type: string;
15
+ }[];