ezmedicationinput 0.1.1 → 0.1.2

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
@@ -86,7 +86,7 @@ Routes always include SNOMED CT codings. Every code from the SNOMED Route of Adm
86
86
 
87
87
  ### Next due dose generation
88
88
 
89
- `nextDueDoses` produces upcoming administration timestamps from an existing FHIR `Dosage`. Supply the order start, the reference window, and a clinic configuration that defines anchor times.
89
+ `nextDueDoses` produces upcoming administration timestamps from an existing FHIR `Dosage`. Supply the evaluation window (`from`), optionally the order start (`orderedAt`), and clinic clock details such as a time zone and event timing anchors.
90
90
 
91
91
  ```ts
92
92
  import { EventTiming, nextDueDoses, parseSig } from "ezmedicationinput";
@@ -97,24 +97,22 @@ const schedule = nextDueDoses(fhir, {
97
97
  orderedAt: "2024-01-01T08:15:00Z",
98
98
  from: "2024-01-01T09:00:00Z",
99
99
  limit: 5,
100
- config: {
101
- timeZone: "Asia/Bangkok",
102
- eventClock: {
103
- [EventTiming.Morning]: "08:00",
104
- [EventTiming.Noon]: "12:00",
105
- [EventTiming.Evening]: "18:00",
106
- [EventTiming["Before Sleep"]]: "22:00",
107
- [EventTiming.Breakfast]: "08:00",
108
- [EventTiming.Lunch]: "12:30",
109
- [EventTiming.Dinner]: "18:30"
110
- },
111
- mealOffsets: {
112
- [EventTiming["Before Meal"]]: -30,
113
- [EventTiming["After Meal"]]: 30
114
- },
115
- frequencyDefaults: {
116
- byCode: { BID: ["08:00", "20:00"] }
117
- }
100
+ timeZone: "Asia/Bangkok",
101
+ eventClock: {
102
+ [EventTiming.Morning]: "08:00",
103
+ [EventTiming.Noon]: "12:00",
104
+ [EventTiming.Evening]: "18:00",
105
+ [EventTiming["Before Sleep"]]: "22:00",
106
+ [EventTiming.Breakfast]: "08:00",
107
+ [EventTiming.Lunch]: "12:30",
108
+ [EventTiming.Dinner]: "18:30"
109
+ },
110
+ mealOffsets: {
111
+ [EventTiming["Before Meal"]]: -30,
112
+ [EventTiming["After Meal"]]: 30
113
+ },
114
+ frequencyDefaults: {
115
+ byCode: { BID: ["08:00", "20:00"] }
118
116
  }
119
117
  });
120
118
 
@@ -128,6 +126,8 @@ Key rules:
128
126
  - Pure frequency schedules (`BID`, `TID`, etc.) fall back to clinic-defined institution times.
129
127
  - All timestamps are emitted as ISO strings that include the clinic time-zone offset.
130
128
 
129
+ `from` is required and marks the evaluation window. `orderedAt` is optional—when supplied it acts as the baseline for interval calculations; otherwise the `from` timestamp is reused. The options bag also accepts `timeZone`, `eventClock`, `mealOffsets`, and `frequencyDefaults` at the top level (mirroring the legacy `config` object). `limit` defaults to 10 when omitted.
130
+
131
131
  ### Ocular & intravitreal shortcuts
132
132
 
133
133
  The parser recognizes ophthalmic shorthands such as `OD`, `OS`, `OU`, `LE`, `RE`, and `BE`, as well as intravitreal-specific tokens including `IVT`, `IVTOD`, `IVTOS`, `IVTLE`, `IVTBE`, `VOD`, and `VOS`. Intravitreal sigs require an eye side; the parser surfaces a warning if one is missing so downstream workflows can prompt the clinician for clarification.
package/dist/context.js CHANGED
@@ -1,12 +1,17 @@
1
- import { DEFAULT_UNIT_BY_NORMALIZED_FORM, KNOWN_DOSAGE_FORMS_TO_DOSE } from "./maps";
2
- export function normalizeDosageForm(form) {
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeDosageForm = normalizeDosageForm;
4
+ exports.inferUnitFromContext = inferUnitFromContext;
5
+ const maps_1 = require("./maps");
6
+ function normalizeDosageForm(form) {
7
+ var _a;
3
8
  if (!form) {
4
9
  return undefined;
5
10
  }
6
11
  const key = form.trim().toLowerCase();
7
- return KNOWN_DOSAGE_FORMS_TO_DOSE[key] ?? key;
12
+ return (_a = maps_1.KNOWN_DOSAGE_FORMS_TO_DOSE[key]) !== null && _a !== void 0 ? _a : key;
8
13
  }
9
- export function inferUnitFromContext(ctx) {
14
+ function inferUnitFromContext(ctx) {
10
15
  if (!ctx) {
11
16
  return undefined;
12
17
  }
@@ -16,7 +21,7 @@ export function inferUnitFromContext(ctx) {
16
21
  if (ctx.dosageForm) {
17
22
  const normalized = normalizeDosageForm(ctx.dosageForm);
18
23
  if (normalized) {
19
- const unit = DEFAULT_UNIT_BY_NORMALIZED_FORM[normalized];
24
+ const unit = maps_1.DEFAULT_UNIT_BY_NORMALIZED_FORM[normalized];
20
25
  if (unit) {
21
26
  return unit;
22
27
  }
package/dist/fhir.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ParsedSigInternal } from "./parser";
1
+ import { ParsedSigInternal } from "./internal-types";
2
2
  import { FhirDosage } from "./types";
3
3
  export declare function toFhir(internal: ParsedSigInternal): FhirDosage;
4
4
  export declare function internalFromFhir(dosage: FhirDosage): ParsedSigInternal;
package/dist/fhir.js CHANGED
@@ -1,8 +1,15 @@
1
- import { formatInternal } from "./format";
2
- import { ROUTE_BY_SNOMED, ROUTE_SNOMED, ROUTE_TEXT } from "./maps";
3
- import { EventTiming } from "./types";
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toFhir = toFhir;
4
+ exports.internalFromFhir = internalFromFhir;
5
+ const format_1 = require("./format");
6
+ const maps_1 = require("./maps");
7
+ const types_1 = require("./types");
8
+ const object_1 = require("./utils/object");
9
+ const array_1 = require("./utils/array");
4
10
  const SNOMED_SYSTEM = "http://snomed.info/sct";
5
- export function toFhir(internal) {
11
+ function toFhir(internal) {
12
+ var _a, _b;
6
13
  const dosage = {};
7
14
  const repeat = {};
8
15
  let hasRepeat = false;
@@ -38,7 +45,7 @@ export function toFhir(internal) {
38
45
  dosage.timing = {};
39
46
  }
40
47
  if (internal.timingCode) {
41
- dosage.timing = dosage.timing ?? {};
48
+ dosage.timing = (_a = dosage.timing) !== null && _a !== void 0 ? _a : {};
42
49
  dosage.timing.code = {
43
50
  coding: [{ code: internal.timingCode }],
44
51
  text: internal.timingCode
@@ -70,9 +77,8 @@ export function toFhir(internal) {
70
77
  }
71
78
  // Emit SNOMED-coded routes whenever we have parsed or inferred route data.
72
79
  if (internal.routeCode || internal.routeText) {
73
- const coding = internal.routeCode ? ROUTE_SNOMED[internal.routeCode] : undefined;
74
- const text = internal.routeText ??
75
- (internal.routeCode ? ROUTE_TEXT[internal.routeCode] : undefined);
80
+ const coding = internal.routeCode ? maps_1.ROUTE_SNOMED[internal.routeCode] : undefined;
81
+ const text = (_b = internal.routeText) !== null && _b !== void 0 ? _b : (internal.routeCode ? maps_1.ROUTE_TEXT[internal.routeCode] : undefined);
76
82
  if (coding) {
77
83
  // Provide both text and coding so human-readable and coded systems align.
78
84
  dosage.route = {
@@ -99,53 +105,55 @@ export function toFhir(internal) {
99
105
  dosage.asNeededFor = [{ text: internal.asNeededReason }];
100
106
  }
101
107
  }
102
- const longText = formatInternal(internal, "long");
108
+ const longText = (0, format_1.formatInternal)(internal, "long");
103
109
  if (longText) {
104
110
  dosage.text = longText;
105
111
  }
106
112
  return dosage;
107
113
  }
108
- export function internalFromFhir(dosage) {
114
+ function internalFromFhir(dosage) {
115
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3;
109
116
  const internal = {
110
- input: dosage.text ?? "",
117
+ input: (_a = dosage.text) !== null && _a !== void 0 ? _a : "",
111
118
  tokens: [],
112
119
  consumed: new Set(),
113
- dayOfWeek: dosage.timing?.repeat?.dayOfWeek
120
+ dayOfWeek: ((_c = (_b = dosage.timing) === null || _b === void 0 ? void 0 : _b.repeat) === null || _c === void 0 ? void 0 : _c.dayOfWeek)
114
121
  ? [...dosage.timing.repeat.dayOfWeek]
115
122
  : [],
116
- when: dosage.timing?.repeat?.when
117
- ? dosage.timing.repeat.when.filter((value) => Object.values(EventTiming).includes(value))
123
+ when: ((_e = (_d = dosage.timing) === null || _d === void 0 ? void 0 : _d.repeat) === null || _e === void 0 ? void 0 : _e.when)
124
+ ? dosage.timing.repeat.when.filter((value) => (0, array_1.arrayIncludes)((0, object_1.objectValues)(types_1.EventTiming), value))
118
125
  : [],
119
126
  warnings: [],
120
- timingCode: dosage.timing?.code?.coding?.[0]?.code,
121
- frequency: dosage.timing?.repeat?.frequency,
122
- frequencyMax: dosage.timing?.repeat?.frequencyMax,
123
- period: dosage.timing?.repeat?.period,
124
- periodMax: dosage.timing?.repeat?.periodMax,
125
- periodUnit: dosage.timing?.repeat?.periodUnit,
126
- routeText: dosage.route?.text,
127
- siteText: dosage.site?.text,
127
+ timingCode: (_j = (_h = (_g = (_f = dosage.timing) === null || _f === void 0 ? void 0 : _f.code) === null || _g === void 0 ? void 0 : _g.coding) === null || _h === void 0 ? void 0 : _h[0]) === null || _j === void 0 ? void 0 : _j.code,
128
+ frequency: (_l = (_k = dosage.timing) === null || _k === void 0 ? void 0 : _k.repeat) === null || _l === void 0 ? void 0 : _l.frequency,
129
+ frequencyMax: (_o = (_m = dosage.timing) === null || _m === void 0 ? void 0 : _m.repeat) === null || _o === void 0 ? void 0 : _o.frequencyMax,
130
+ period: (_q = (_p = dosage.timing) === null || _p === void 0 ? void 0 : _p.repeat) === null || _q === void 0 ? void 0 : _q.period,
131
+ periodMax: (_s = (_r = dosage.timing) === null || _r === void 0 ? void 0 : _r.repeat) === null || _s === void 0 ? void 0 : _s.periodMax,
132
+ periodUnit: (_u = (_t = dosage.timing) === null || _t === void 0 ? void 0 : _t.repeat) === null || _u === void 0 ? void 0 : _u.periodUnit,
133
+ routeText: (_v = dosage.route) === null || _v === void 0 ? void 0 : _v.text,
134
+ siteText: (_w = dosage.site) === null || _w === void 0 ? void 0 : _w.text,
128
135
  asNeeded: dosage.asNeededBoolean,
129
- asNeededReason: dosage.asNeededFor?.[0]?.text
136
+ asNeededReason: (_y = (_x = dosage.asNeededFor) === null || _x === void 0 ? void 0 : _x[0]) === null || _y === void 0 ? void 0 : _y.text,
137
+ siteTokenIndices: new Set()
130
138
  };
131
- const routeCoding = dosage.route?.coding?.find((code) => code.system === SNOMED_SYSTEM);
132
- if (routeCoding?.code) {
139
+ const routeCoding = (_0 = (_z = dosage.route) === null || _z === void 0 ? void 0 : _z.coding) === null || _0 === void 0 ? void 0 : _0.find((code) => code.system === SNOMED_SYSTEM);
140
+ if (routeCoding === null || routeCoding === void 0 ? void 0 : routeCoding.code) {
133
141
  // Translate SNOMED codings back into the simplified enum for round-trip fidelity.
134
- const mapped = ROUTE_BY_SNOMED[routeCoding.code];
142
+ const mapped = maps_1.ROUTE_BY_SNOMED[routeCoding.code];
135
143
  if (mapped) {
136
144
  internal.routeCode = mapped;
137
- internal.routeText = ROUTE_TEXT[mapped];
145
+ internal.routeText = maps_1.ROUTE_TEXT[mapped];
138
146
  }
139
147
  }
140
- const doseAndRate = dosage.doseAndRate?.[0];
141
- if (doseAndRate?.doseRange) {
148
+ const doseAndRate = (_1 = dosage.doseAndRate) === null || _1 === void 0 ? void 0 : _1[0];
149
+ if (doseAndRate === null || doseAndRate === void 0 ? void 0 : doseAndRate.doseRange) {
142
150
  const { low, high } = doseAndRate.doseRange;
143
- if (low?.value !== undefined && high?.value !== undefined) {
151
+ if ((low === null || low === void 0 ? void 0 : low.value) !== undefined && (high === null || high === void 0 ? void 0 : high.value) !== undefined) {
144
152
  internal.doseRange = { low: low.value, high: high.value };
145
153
  }
146
- internal.unit = low?.unit ?? high?.unit ?? internal.unit;
154
+ internal.unit = (_3 = (_2 = low === null || low === void 0 ? void 0 : low.unit) !== null && _2 !== void 0 ? _2 : high === null || high === void 0 ? void 0 : high.unit) !== null && _3 !== void 0 ? _3 : internal.unit;
147
155
  }
148
- else if (doseAndRate?.doseQuantity) {
156
+ else if (doseAndRate === null || doseAndRate === void 0 ? void 0 : doseAndRate.doseQuantity) {
149
157
  const dose = doseAndRate.doseQuantity;
150
158
  if (dose.value !== undefined) {
151
159
  internal.dose = dose.value;
package/dist/format.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- import { ParsedSigInternal } from "./parser";
2
- export declare function formatInternal(internal: ParsedSigInternal, style: "short" | "long"): string;
1
+ import { ParsedSigInternal } from "./internal-types";
2
+ import type { SigLocalization } from "./i18n";
3
+ export declare function formatInternal(internal: ParsedSigInternal, style: "short" | "long", localization?: SigLocalization): string;