ezmedicationinput 0.1.0 → 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 +19 -19
- package/dist/context.js +10 -5
- package/dist/fhir.d.ts +1 -1
- package/dist/fhir.js +40 -32
- package/dist/format.d.ts +3 -2
- package/dist/format.js +371 -86
- package/dist/i18n.d.ts +31 -0
- package/dist/i18n.js +664 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +53 -18
- package/dist/internal-types.d.ts +33 -0
- package/dist/internal-types.js +2 -0
- package/dist/maps.d.ts +1 -0
- package/dist/maps.js +434 -345
- package/dist/package.json +3 -0
- package/dist/parser.d.ts +2 -31
- package/dist/parser.js +690 -143
- package/dist/safety.js +7 -4
- package/dist/schedule.js +148 -76
- package/dist/suggest.d.ts +12 -0
- package/dist/suggest.js +228 -0
- package/dist/types.d.ts +17 -8
- package/dist/types.js +13 -9
- package/dist/utils/array.d.ts +1 -0
- package/dist/utils/array.js +11 -0
- package/dist/utils/enum.d.ts +2 -0
- package/dist/utils/enum.js +7 -0
- package/dist/utils/object.d.ts +7 -0
- package/dist/utils/object.js +34 -0
- package/package.json +2 -2
package/dist/safety.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkDiscouraged = checkDiscouraged;
|
|
4
|
+
const maps_1 = require("./maps");
|
|
5
|
+
function checkDiscouraged(token, options) {
|
|
3
6
|
const lower = token.toLowerCase();
|
|
4
|
-
if (!(lower in DISCOURAGED_TOKENS)) {
|
|
7
|
+
if (!(lower in maps_1.DISCOURAGED_TOKENS)) {
|
|
5
8
|
return { allowed: true };
|
|
6
9
|
}
|
|
7
|
-
const code = DISCOURAGED_TOKENS[lower];
|
|
10
|
+
const code = maps_1.DISCOURAGED_TOKENS[lower];
|
|
8
11
|
if (options && options.allowDiscouraged === false) {
|
|
9
12
|
throw new Error(`Discouraged token '${token}' is not allowed`);
|
|
10
13
|
}
|
package/dist/schedule.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.nextDueDoses = nextDueDoses;
|
|
4
|
+
const types_1 = require("./types");
|
|
5
|
+
const array_1 = require("./utils/array");
|
|
2
6
|
/**
|
|
3
7
|
* Default institution times used when a dosage only specifies frequency without
|
|
4
8
|
* explicit EventTiming anchors. Clinics can override these through the
|
|
@@ -29,12 +33,33 @@ const dateTimeFormatCache = new Map();
|
|
|
29
33
|
const weekdayFormatCache = new Map();
|
|
30
34
|
/** Simple zero-padding helper for numeric components. */
|
|
31
35
|
function pad(value, length = 2) {
|
|
32
|
-
|
|
36
|
+
const absolute = Math.abs(value);
|
|
37
|
+
let output = absolute.toString();
|
|
38
|
+
while (output.length < length) {
|
|
39
|
+
output = `0${output}`;
|
|
40
|
+
}
|
|
41
|
+
return value < 0 ? `-${output}` : output;
|
|
42
|
+
}
|
|
43
|
+
function formatToParts(formatter, date) {
|
|
44
|
+
const withParts = formatter;
|
|
45
|
+
if (typeof withParts.formatToParts === "function") {
|
|
46
|
+
return withParts.formatToParts(date);
|
|
47
|
+
}
|
|
48
|
+
const iso = date.toISOString();
|
|
49
|
+
return [
|
|
50
|
+
{ type: "year", value: iso.slice(0, 4) },
|
|
51
|
+
{ type: "month", value: iso.slice(5, 7) },
|
|
52
|
+
{ type: "day", value: iso.slice(8, 10) },
|
|
53
|
+
{ type: "hour", value: iso.slice(11, 13) },
|
|
54
|
+
{ type: "minute", value: iso.slice(14, 16) },
|
|
55
|
+
{ type: "second", value: iso.slice(17, 19) }
|
|
56
|
+
];
|
|
33
57
|
}
|
|
34
58
|
/**
|
|
35
59
|
* Normalizes HH:mm or HH:mm:ss clocks into a consistent HH:mm:ss string.
|
|
36
60
|
*/
|
|
37
61
|
function normalizeClock(clock) {
|
|
62
|
+
var _a;
|
|
38
63
|
const parts = clock.split(":");
|
|
39
64
|
if (parts.length < 2 || parts.length > 3) {
|
|
40
65
|
throw new Error(`Invalid clock value: ${clock}`);
|
|
@@ -42,7 +67,7 @@ function normalizeClock(clock) {
|
|
|
42
67
|
const [hourPart, minutePart, secondPart] = [
|
|
43
68
|
parts[0],
|
|
44
69
|
parts[1],
|
|
45
|
-
parts[2]
|
|
70
|
+
(_a = parts[2]) !== null && _a !== void 0 ? _a : "00"
|
|
46
71
|
];
|
|
47
72
|
const hour = Number(hourPart);
|
|
48
73
|
const minute = Number(minutePart);
|
|
@@ -64,7 +89,7 @@ function normalizeClock(clock) {
|
|
|
64
89
|
function getDateTimeFormat(timeZone) {
|
|
65
90
|
let formatter = dateTimeFormatCache.get(timeZone);
|
|
66
91
|
if (!formatter) {
|
|
67
|
-
|
|
92
|
+
const options = {
|
|
68
93
|
timeZone,
|
|
69
94
|
calendar: "iso8601",
|
|
70
95
|
numberingSystem: "latn",
|
|
@@ -75,7 +100,8 @@ function getDateTimeFormat(timeZone) {
|
|
|
75
100
|
hour: "2-digit",
|
|
76
101
|
minute: "2-digit",
|
|
77
102
|
second: "2-digit"
|
|
78
|
-
}
|
|
103
|
+
};
|
|
104
|
+
formatter = new Intl.DateTimeFormat("en-CA", options);
|
|
79
105
|
dateTimeFormatCache.set(timeZone, formatter);
|
|
80
106
|
}
|
|
81
107
|
return formatter;
|
|
@@ -97,9 +123,10 @@ function getWeekdayFormat(timeZone) {
|
|
|
97
123
|
* zone.
|
|
98
124
|
*/
|
|
99
125
|
function getTimeParts(date, timeZone) {
|
|
126
|
+
var _a, _b;
|
|
100
127
|
const formatter = getDateTimeFormat(timeZone);
|
|
101
128
|
const parts = {};
|
|
102
|
-
const rawParts =
|
|
129
|
+
const rawParts = formatToParts(formatter, date);
|
|
103
130
|
for (const part of rawParts) {
|
|
104
131
|
if (part.type === "literal") {
|
|
105
132
|
continue;
|
|
@@ -128,7 +155,7 @@ function getTimeParts(date, timeZone) {
|
|
|
128
155
|
// instant forward slightly so we can capture the correct calendar date and
|
|
129
156
|
// reset the hour component back to zero.
|
|
130
157
|
const forward = new Date(date.getTime() + 60 * 1000);
|
|
131
|
-
const forwardParts =
|
|
158
|
+
const forwardParts = formatToParts(formatter, forward);
|
|
132
159
|
for (const part of forwardParts) {
|
|
133
160
|
if (part.type === "literal") {
|
|
134
161
|
continue;
|
|
@@ -144,8 +171,8 @@ function getTimeParts(date, timeZone) {
|
|
|
144
171
|
}
|
|
145
172
|
}
|
|
146
173
|
parts.hour = 0;
|
|
147
|
-
parts.minute = parts.minute
|
|
148
|
-
parts.second = parts.second
|
|
174
|
+
parts.minute = (_a = parts.minute) !== null && _a !== void 0 ? _a : 0;
|
|
175
|
+
parts.second = (_b = parts.second) !== null && _b !== void 0 ? _b : 0;
|
|
149
176
|
}
|
|
150
177
|
if (parts.year === undefined ||
|
|
151
178
|
parts.month === undefined ||
|
|
@@ -278,49 +305,51 @@ function applyOffset(clock, offsetMinutes) {
|
|
|
278
305
|
}
|
|
279
306
|
/** Provides the default meal pairing used for AC/PC expansions. */
|
|
280
307
|
function getDefaultMealPairs(config) {
|
|
281
|
-
return [EventTiming.Breakfast, EventTiming.Lunch, EventTiming.Dinner];
|
|
308
|
+
return [types_1.EventTiming.Breakfast, types_1.EventTiming.Lunch, types_1.EventTiming.Dinner];
|
|
282
309
|
}
|
|
283
310
|
const SPECIFIC_BEFORE_MEALS = {
|
|
284
|
-
[EventTiming["Before Breakfast"]]: EventTiming.Breakfast,
|
|
285
|
-
[EventTiming["Before Lunch"]]: EventTiming.Lunch,
|
|
286
|
-
[EventTiming["Before Dinner"]]: EventTiming.Dinner
|
|
311
|
+
[types_1.EventTiming["Before Breakfast"]]: types_1.EventTiming.Breakfast,
|
|
312
|
+
[types_1.EventTiming["Before Lunch"]]: types_1.EventTiming.Lunch,
|
|
313
|
+
[types_1.EventTiming["Before Dinner"]]: types_1.EventTiming.Dinner
|
|
287
314
|
};
|
|
288
315
|
const SPECIFIC_AFTER_MEALS = {
|
|
289
|
-
[EventTiming["After Breakfast"]]: EventTiming.Breakfast,
|
|
290
|
-
[EventTiming["After Lunch"]]: EventTiming.Lunch,
|
|
291
|
-
[EventTiming["After Dinner"]]: EventTiming.Dinner
|
|
316
|
+
[types_1.EventTiming["After Breakfast"]]: types_1.EventTiming.Breakfast,
|
|
317
|
+
[types_1.EventTiming["After Lunch"]]: types_1.EventTiming.Lunch,
|
|
318
|
+
[types_1.EventTiming["After Dinner"]]: types_1.EventTiming.Dinner
|
|
292
319
|
};
|
|
293
320
|
/**
|
|
294
321
|
* Expands a single EventTiming code into concrete wall-clock entries.
|
|
295
322
|
*/
|
|
296
323
|
function expandTiming(code, config, repeat) {
|
|
297
|
-
|
|
324
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
325
|
+
const mealOffsets = (_a = config.mealOffsets) !== null && _a !== void 0 ? _a : {};
|
|
326
|
+
const eventClock = (_b = config.eventClock) !== null && _b !== void 0 ? _b : {};
|
|
298
327
|
const normalized = [];
|
|
299
|
-
const clockValue =
|
|
328
|
+
const clockValue = eventClock[code];
|
|
300
329
|
if (clockValue) {
|
|
301
330
|
normalized.push({ time: normalizeClock(clockValue), dayShift: 0 });
|
|
302
331
|
}
|
|
303
|
-
else if (code === EventTiming["Before Meal"]) {
|
|
332
|
+
else if (code === types_1.EventTiming["Before Meal"]) {
|
|
304
333
|
for (const meal of getDefaultMealPairs(config)) {
|
|
305
|
-
const base =
|
|
334
|
+
const base = eventClock[meal];
|
|
306
335
|
if (!base) {
|
|
307
336
|
continue;
|
|
308
337
|
}
|
|
309
|
-
normalized.push(applyOffset(normalizeClock(base), mealOffsets[code]
|
|
338
|
+
normalized.push(applyOffset(normalizeClock(base), (_c = mealOffsets[code]) !== null && _c !== void 0 ? _c : 0));
|
|
310
339
|
}
|
|
311
340
|
}
|
|
312
|
-
else if (code === EventTiming["After Meal"]) {
|
|
341
|
+
else if (code === types_1.EventTiming["After Meal"]) {
|
|
313
342
|
for (const meal of getDefaultMealPairs(config)) {
|
|
314
|
-
const base =
|
|
343
|
+
const base = eventClock[meal];
|
|
315
344
|
if (!base) {
|
|
316
345
|
continue;
|
|
317
346
|
}
|
|
318
|
-
normalized.push(applyOffset(normalizeClock(base), mealOffsets[code]
|
|
347
|
+
normalized.push(applyOffset(normalizeClock(base), (_d = mealOffsets[code]) !== null && _d !== void 0 ? _d : 0));
|
|
319
348
|
}
|
|
320
349
|
}
|
|
321
|
-
else if (code === EventTiming.Meal) {
|
|
350
|
+
else if (code === types_1.EventTiming.Meal) {
|
|
322
351
|
for (const meal of getDefaultMealPairs(config)) {
|
|
323
|
-
const base =
|
|
352
|
+
const base = eventClock[meal];
|
|
324
353
|
if (!base) {
|
|
325
354
|
continue;
|
|
326
355
|
}
|
|
@@ -329,25 +358,26 @@ function expandTiming(code, config, repeat) {
|
|
|
329
358
|
}
|
|
330
359
|
else if (code in SPECIFIC_BEFORE_MEALS) {
|
|
331
360
|
const mealCode = SPECIFIC_BEFORE_MEALS[code];
|
|
332
|
-
const base =
|
|
361
|
+
const base = eventClock[mealCode];
|
|
333
362
|
if (base) {
|
|
334
363
|
const baseClock = normalizeClock(base);
|
|
335
|
-
const offset = mealOffsets[code]
|
|
364
|
+
const offset = (_f = (_e = mealOffsets[code]) !== null && _e !== void 0 ? _e : mealOffsets[types_1.EventTiming["Before Meal"]]) !== null && _f !== void 0 ? _f : 0;
|
|
336
365
|
normalized.push(offset ? applyOffset(baseClock, offset) : { time: baseClock, dayShift: 0 });
|
|
337
366
|
}
|
|
338
367
|
}
|
|
339
368
|
else if (code in SPECIFIC_AFTER_MEALS) {
|
|
340
369
|
const mealCode = SPECIFIC_AFTER_MEALS[code];
|
|
341
|
-
const base =
|
|
370
|
+
const base = eventClock[mealCode];
|
|
342
371
|
if (base) {
|
|
343
372
|
const baseClock = normalizeClock(base);
|
|
344
|
-
const offset = mealOffsets[code]
|
|
373
|
+
const offset = (_h = (_g = mealOffsets[code]) !== null && _g !== void 0 ? _g : mealOffsets[types_1.EventTiming["After Meal"]]) !== null && _h !== void 0 ? _h : 0;
|
|
345
374
|
normalized.push(offset ? applyOffset(baseClock, offset) : { time: baseClock, dayShift: 0 });
|
|
346
375
|
}
|
|
347
376
|
}
|
|
348
377
|
if (repeat.offset && normalized.length) {
|
|
349
378
|
return normalized.map((entry) => {
|
|
350
|
-
|
|
379
|
+
var _a;
|
|
380
|
+
const adjusted = applyOffset(entry.time, (_a = repeat.offset) !== null && _a !== void 0 ? _a : 0);
|
|
351
381
|
return {
|
|
352
382
|
time: adjusted.time,
|
|
353
383
|
dayShift: entry.dayShift + adjusted.dayShift
|
|
@@ -361,7 +391,7 @@ function expandWhenCodes(whenCodes, config, repeat) {
|
|
|
361
391
|
const entries = [];
|
|
362
392
|
const seen = new Set();
|
|
363
393
|
for (const code of whenCodes) {
|
|
364
|
-
if (code === EventTiming.Immediate) {
|
|
394
|
+
if (code === types_1.EventTiming.Immediate) {
|
|
365
395
|
continue;
|
|
366
396
|
}
|
|
367
397
|
const expansions = expandTiming(code, config, repeat);
|
|
@@ -381,36 +411,45 @@ function expandWhenCodes(whenCodes, config, repeat) {
|
|
|
381
411
|
return a.time.localeCompare(b.time);
|
|
382
412
|
});
|
|
383
413
|
}
|
|
414
|
+
function mergeFrequencyDefaults(base, override) {
|
|
415
|
+
var _a, _b, _c, _d;
|
|
416
|
+
if (!base && !override) {
|
|
417
|
+
return undefined;
|
|
418
|
+
}
|
|
419
|
+
const merged = {};
|
|
420
|
+
if ((base === null || base === void 0 ? void 0 : base.byCode) || (override === null || override === void 0 ? void 0 : override.byCode)) {
|
|
421
|
+
merged.byCode = Object.assign(Object.assign({}, ((_a = base === null || base === void 0 ? void 0 : base.byCode) !== null && _a !== void 0 ? _a : {})), ((_b = override === null || override === void 0 ? void 0 : override.byCode) !== null && _b !== void 0 ? _b : {}));
|
|
422
|
+
}
|
|
423
|
+
if ((base === null || base === void 0 ? void 0 : base.byFrequency) || (override === null || override === void 0 ? void 0 : override.byFrequency)) {
|
|
424
|
+
merged.byFrequency = Object.assign(Object.assign({}, ((_c = base === null || base === void 0 ? void 0 : base.byFrequency) !== null && _c !== void 0 ? _c : {})), ((_d = override === null || override === void 0 ? void 0 : override.byFrequency) !== null && _d !== void 0 ? _d : {}));
|
|
425
|
+
}
|
|
426
|
+
return merged;
|
|
427
|
+
}
|
|
384
428
|
/** Resolves fallback clock arrays for frequency-only schedules. */
|
|
385
429
|
function resolveFrequencyClocks(timing, config) {
|
|
430
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
386
431
|
const defaults = {
|
|
387
|
-
byCode: {
|
|
388
|
-
|
|
389
|
-
...(config.frequencyDefaults?.byCode ?? {})
|
|
390
|
-
},
|
|
391
|
-
byFrequency: {
|
|
392
|
-
...DEFAULT_FREQUENCY_DEFAULTS.byFrequency,
|
|
393
|
-
...(config.frequencyDefaults?.byFrequency ?? {})
|
|
394
|
-
}
|
|
432
|
+
byCode: Object.assign(Object.assign({}, DEFAULT_FREQUENCY_DEFAULTS.byCode), ((_b = (_a = config.frequencyDefaults) === null || _a === void 0 ? void 0 : _a.byCode) !== null && _b !== void 0 ? _b : {})),
|
|
433
|
+
byFrequency: Object.assign(Object.assign({}, DEFAULT_FREQUENCY_DEFAULTS.byFrequency), ((_d = (_c = config.frequencyDefaults) === null || _c === void 0 ? void 0 : _c.byFrequency) !== null && _d !== void 0 ? _d : {}))
|
|
395
434
|
};
|
|
396
435
|
const collected = new Set();
|
|
397
|
-
const code = timing.code
|
|
398
|
-
const normalizedCode = code
|
|
399
|
-
if (normalizedCode && defaults.byCode
|
|
436
|
+
const code = (_g = (_f = (_e = timing.code) === null || _e === void 0 ? void 0 : _e.coding) === null || _f === void 0 ? void 0 : _f.find((coding) => coding.code)) === null || _g === void 0 ? void 0 : _g.code;
|
|
437
|
+
const normalizedCode = code === null || code === void 0 ? void 0 : code.toUpperCase();
|
|
438
|
+
if (normalizedCode && ((_h = defaults.byCode) === null || _h === void 0 ? void 0 : _h[normalizedCode])) {
|
|
400
439
|
for (const clock of defaults.byCode[normalizedCode]) {
|
|
401
440
|
collected.add(normalizeClock(clock));
|
|
402
441
|
}
|
|
403
442
|
}
|
|
404
443
|
const repeat = timing.repeat;
|
|
405
|
-
if (repeat
|
|
444
|
+
if ((repeat === null || repeat === void 0 ? void 0 : repeat.frequency) && repeat.period && repeat.periodUnit) {
|
|
406
445
|
const key = `freq:${repeat.frequency}/${repeat.periodUnit}`;
|
|
407
|
-
if (defaults.byFrequency
|
|
446
|
+
if ((_j = defaults.byFrequency) === null || _j === void 0 ? void 0 : _j[key]) {
|
|
408
447
|
for (const clock of defaults.byFrequency[key]) {
|
|
409
448
|
collected.add(normalizeClock(clock));
|
|
410
449
|
}
|
|
411
450
|
}
|
|
412
451
|
const perPeriodKey = `freq:${repeat.frequency}/per:${repeat.period}${repeat.periodUnit}`;
|
|
413
|
-
if (defaults.byFrequency
|
|
452
|
+
if ((_k = defaults.byFrequency) === null || _k === void 0 ? void 0 : _k[perPeriodKey]) {
|
|
414
453
|
for (const clock of defaults.byFrequency[perPeriodKey]) {
|
|
415
454
|
collected.add(normalizeClock(clock));
|
|
416
455
|
}
|
|
@@ -418,41 +457,50 @@ function resolveFrequencyClocks(timing, config) {
|
|
|
418
457
|
}
|
|
419
458
|
return Array.from(collected).sort();
|
|
420
459
|
}
|
|
421
|
-
/** Determines the greater of orderedAt/from for baseline comparisons. */
|
|
422
|
-
function computeBaseline(orderedAt, from) {
|
|
423
|
-
return orderedAt > from ? orderedAt : from;
|
|
424
|
-
}
|
|
425
460
|
/**
|
|
426
461
|
* Produces the next dose timestamps in ascending order according to the
|
|
427
462
|
* provided configuration and dosage metadata.
|
|
428
463
|
*/
|
|
429
|
-
|
|
464
|
+
function nextDueDoses(dosage, options) {
|
|
465
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
430
466
|
if (!options || typeof options !== "object") {
|
|
431
467
|
throw new Error("Options argument is required for nextDueDoses");
|
|
432
468
|
}
|
|
433
|
-
|
|
469
|
+
if (options.from === undefined) {
|
|
470
|
+
throw new Error("The 'from' option is required for nextDueDoses");
|
|
471
|
+
}
|
|
472
|
+
const limit = (_a = options.limit) !== null && _a !== void 0 ? _a : 10;
|
|
434
473
|
if (!Number.isFinite(limit) || limit <= 0) {
|
|
435
474
|
return [];
|
|
436
475
|
}
|
|
437
|
-
const orderedAt = coerceDate(options.orderedAt, "orderedAt");
|
|
438
476
|
const from = coerceDate(options.from, "from");
|
|
439
|
-
const
|
|
440
|
-
|
|
477
|
+
const orderedAt = options.orderedAt === undefined ? null : coerceDate(options.orderedAt, "orderedAt");
|
|
478
|
+
const baseTime = orderedAt !== null && orderedAt !== void 0 ? orderedAt : from;
|
|
479
|
+
const providedConfig = options.config;
|
|
480
|
+
const timeZone = (_b = options.timeZone) !== null && _b !== void 0 ? _b : providedConfig === null || providedConfig === void 0 ? void 0 : providedConfig.timeZone;
|
|
481
|
+
if (!timeZone) {
|
|
441
482
|
throw new Error("Configuration with a valid timeZone is required");
|
|
442
483
|
}
|
|
443
|
-
const
|
|
484
|
+
const eventClock = Object.assign(Object.assign({}, ((_c = providedConfig === null || providedConfig === void 0 ? void 0 : providedConfig.eventClock) !== null && _c !== void 0 ? _c : {})), ((_d = options.eventClock) !== null && _d !== void 0 ? _d : {}));
|
|
485
|
+
const mealOffsets = Object.assign(Object.assign({}, ((_e = providedConfig === null || providedConfig === void 0 ? void 0 : providedConfig.mealOffsets) !== null && _e !== void 0 ? _e : {})), ((_f = options.mealOffsets) !== null && _f !== void 0 ? _f : {}));
|
|
486
|
+
const frequencyDefaults = mergeFrequencyDefaults(providedConfig === null || providedConfig === void 0 ? void 0 : providedConfig.frequencyDefaults, options.frequencyDefaults);
|
|
487
|
+
const config = {
|
|
488
|
+
timeZone,
|
|
489
|
+
eventClock,
|
|
490
|
+
mealOffsets,
|
|
491
|
+
frequencyDefaults
|
|
492
|
+
};
|
|
444
493
|
const timing = dosage.timing;
|
|
445
|
-
const repeat = timing
|
|
494
|
+
const repeat = timing === null || timing === void 0 ? void 0 : timing.repeat;
|
|
446
495
|
if (!timing || !repeat) {
|
|
447
496
|
return [];
|
|
448
497
|
}
|
|
449
|
-
const baseline = computeBaseline(orderedAt, from);
|
|
450
498
|
const results = [];
|
|
451
499
|
const seen = new Set();
|
|
452
|
-
const dayFilter = new Set((repeat.dayOfWeek
|
|
500
|
+
const dayFilter = new Set(((_g = repeat.dayOfWeek) !== null && _g !== void 0 ? _g : []).map((day) => day.toLowerCase()));
|
|
453
501
|
const enforceDayFilter = dayFilter.size > 0;
|
|
454
|
-
const whenCodes = repeat.when
|
|
455
|
-
const timeOfDayEntries = repeat.timeOfDay
|
|
502
|
+
const whenCodes = (_h = repeat.when) !== null && _h !== void 0 ? _h : [];
|
|
503
|
+
const timeOfDayEntries = (_j = repeat.timeOfDay) !== null && _j !== void 0 ? _j : [];
|
|
456
504
|
if (whenCodes.length > 0 || timeOfDayEntries.length > 0) {
|
|
457
505
|
const expanded = expandWhenCodes(whenCodes, config, repeat);
|
|
458
506
|
if (timeOfDayEntries.length > 0) {
|
|
@@ -466,11 +514,14 @@ export function nextDueDoses(dosage, options) {
|
|
|
466
514
|
return a.time.localeCompare(b.time);
|
|
467
515
|
});
|
|
468
516
|
}
|
|
469
|
-
const includesImmediate = whenCodes.
|
|
470
|
-
if (includesImmediate
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
517
|
+
const includesImmediate = (0, array_1.arrayIncludes)(whenCodes, types_1.EventTiming.Immediate);
|
|
518
|
+
if (includesImmediate) {
|
|
519
|
+
const immediateSource = orderedAt !== null && orderedAt !== void 0 ? orderedAt : from;
|
|
520
|
+
if (!orderedAt || orderedAt >= from) {
|
|
521
|
+
const instantIso = formatZonedIso(immediateSource, timeZone);
|
|
522
|
+
results.push(instantIso);
|
|
523
|
+
seen.add(instantIso);
|
|
524
|
+
}
|
|
474
525
|
}
|
|
475
526
|
if (expanded.length === 0) {
|
|
476
527
|
return results.slice(0, limit);
|
|
@@ -489,10 +540,10 @@ export function nextDueDoses(dosage, options) {
|
|
|
489
540
|
if (!zoned) {
|
|
490
541
|
continue;
|
|
491
542
|
}
|
|
492
|
-
if (zoned <
|
|
543
|
+
if (zoned < from) {
|
|
493
544
|
continue;
|
|
494
545
|
}
|
|
495
|
-
if (zoned < orderedAt) {
|
|
546
|
+
if (orderedAt && zoned < orderedAt) {
|
|
496
547
|
continue;
|
|
497
548
|
}
|
|
498
549
|
const iso = formatZonedIso(zoned, timeZone);
|
|
@@ -518,7 +569,7 @@ export function nextDueDoses(dosage, options) {
|
|
|
518
569
|
if (treatAsInterval) {
|
|
519
570
|
// True interval schedules advance from the order start in fixed units. The
|
|
520
571
|
// timing.code remains advisory so we only rely on the period/unit fields.
|
|
521
|
-
const candidates = generateIntervalSeries(
|
|
572
|
+
const candidates = generateIntervalSeries(baseTime, from, limit, repeat, timeZone, dayFilter, enforceDayFilter, orderedAt);
|
|
522
573
|
return candidates;
|
|
523
574
|
}
|
|
524
575
|
if (repeat.frequency && repeat.period && repeat.periodUnit) {
|
|
@@ -540,7 +591,10 @@ export function nextDueDoses(dosage, options) {
|
|
|
540
591
|
if (!zoned) {
|
|
541
592
|
continue;
|
|
542
593
|
}
|
|
543
|
-
if (zoned <
|
|
594
|
+
if (zoned < from) {
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
if (orderedAt && zoned < orderedAt) {
|
|
544
598
|
continue;
|
|
545
599
|
}
|
|
546
600
|
const iso = formatZonedIso(zoned, timeZone);
|
|
@@ -561,21 +615,20 @@ export function nextDueDoses(dosage, options) {
|
|
|
561
615
|
return [];
|
|
562
616
|
}
|
|
563
617
|
/**
|
|
564
|
-
* Generates an interval-based series by stepping forward from
|
|
565
|
-
* the requested number of timestamps have been produced.
|
|
618
|
+
* Generates an interval-based series by stepping forward from the base time
|
|
619
|
+
* until the requested number of timestamps have been produced.
|
|
566
620
|
*/
|
|
567
|
-
function generateIntervalSeries(
|
|
621
|
+
function generateIntervalSeries(baseTime, from, limit, repeat, timeZone, dayFilter, enforceDayFilter, orderedAt) {
|
|
568
622
|
const increment = createIntervalStepper(repeat, timeZone);
|
|
569
623
|
if (!increment) {
|
|
570
624
|
return [];
|
|
571
625
|
}
|
|
572
626
|
const results = [];
|
|
573
627
|
const seen = new Set();
|
|
574
|
-
let current =
|
|
575
|
-
const baseline = computeBaseline(orderedAt, from);
|
|
628
|
+
let current = baseTime;
|
|
576
629
|
let guard = 0;
|
|
577
630
|
const maxIterations = limit * 1000;
|
|
578
|
-
while (current <
|
|
631
|
+
while (current < from && guard < maxIterations) {
|
|
579
632
|
const next = increment(current);
|
|
580
633
|
if (!next || next.getTime() === current.getTime()) {
|
|
581
634
|
break;
|
|
@@ -586,6 +639,25 @@ function generateIntervalSeries(orderedAt, from, limit, repeat, timeZone, dayFil
|
|
|
586
639
|
while (results.length < limit && guard < maxIterations) {
|
|
587
640
|
const weekday = getLocalWeekday(current, timeZone);
|
|
588
641
|
if (!enforceDayFilter || dayFilter.has(weekday)) {
|
|
642
|
+
if (current < from) {
|
|
643
|
+
// Ensure the current candidate respects the evaluation window.
|
|
644
|
+
guard += 1;
|
|
645
|
+
const next = increment(current);
|
|
646
|
+
if (!next || next.getTime() === current.getTime()) {
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
current = next;
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
if (orderedAt && current < orderedAt) {
|
|
653
|
+
guard += 1;
|
|
654
|
+
const next = increment(current);
|
|
655
|
+
if (!next || next.getTime() === current.getTime()) {
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
658
|
+
current = next;
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
589
661
|
const iso = formatZonedIso(current, timeZone);
|
|
590
662
|
if (!seen.has(iso)) {
|
|
591
663
|
seen.add(iso);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ParseOptions } from "./types";
|
|
2
|
+
export interface SuggestSigOptions extends ParseOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Maximum number of suggestions to return. Defaults to 10 when not supplied.
|
|
5
|
+
*/
|
|
6
|
+
limit?: number;
|
|
7
|
+
/**
|
|
8
|
+
* Optional custom PRN reasons to use when generating suggestions.
|
|
9
|
+
*/
|
|
10
|
+
prnReasons?: readonly string[];
|
|
11
|
+
}
|
|
12
|
+
export declare function suggestSig(input: string, options?: SuggestSigOptions): string[];
|