busroot-sdk 0.0.8-dev.2916117361 → 0.0.8-dev.2920950808

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.
Files changed (2) hide show
  1. package/build/index.js +111 -35
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -38,6 +38,7 @@ var require_time = __commonJS({
38
38
  exports2.Crons = exports2.TimeIntervals = exports2.TimeRange = void 0;
39
39
  exports2.getTimeIntervalFromString = getTimeIntervalFromString;
40
40
  exports2.getTimeIntervalKeyFromMs = getTimeIntervalKeyFromMs;
41
+ exports2.nominalDurationMs = nominalDurationMs;
41
42
  exports2.millisToSec = millisToSec;
42
43
  var zod_1 = require("zod");
43
44
  exports2.TimeRange = zod_1.z.object({
@@ -95,6 +96,15 @@ var require_time = __commonJS({
95
96
  }
96
97
  return void 0;
97
98
  }
99
+ var MAX_CLOCK_CHANGE_MS = exports2.TimeIntervals["1h"].ms;
100
+ function nominalDurationMs(durationMs) {
101
+ const dayMs = exports2.TimeIntervals["1d"].ms;
102
+ const wholeDays = Math.round(durationMs / dayMs);
103
+ if (wholeDays >= 1 && Math.abs(durationMs - wholeDays * dayMs) <= MAX_CLOCK_CHANGE_MS) {
104
+ return wholeDays * dayMs;
105
+ }
106
+ return durationMs;
107
+ }
98
108
  function millisToSec(millis) {
99
109
  return millis / 1e3;
100
110
  }
@@ -536,6 +546,8 @@ var require_times = __commonJS({
536
546
  })();
537
547
  Object.defineProperty(exports2, "__esModule", { value: true });
538
548
  exports2.getDurationAsHumanize = exports2.getDuration = exports2.hourToMoment = exports2.convertToUniversal = exports2.AcceptedDateTimeFormats = exports2.getOverlappedTimeRange = exports2.intersectTimeWindowsIso = exports2.intersectTimeWindows = exports2.intersectTimeRanges = exports2.getCoverageRanges = exports2.convertNumberToMomentTime = exports2.getTimesBetweenRangeTimes = exports2.formattedDurationShort = exports2.formattedDuration = exports2.RelativeRanges = exports2.SUPPORTED_DURATION = exports2.RangeType = void 0;
549
+ exports2.isoWeeksInIsoWeekYear = isoWeeksInIsoWeekYear;
550
+ exports2.isValidRangeCode = isValidRangeCode;
539
551
  exports2.getAllowedAggregationWindowsForDuration = getAllowedAggregationWindowsForDuration;
540
552
  exports2.getTimestampFromString = getTimestampFromString;
541
553
  exports2.getTimestampInSecond = getTimestampInSecond;
@@ -552,6 +564,7 @@ var require_times = __commonJS({
552
564
  exports2.fromSpecificHourString = fromSpecificHourString;
553
565
  exports2.plantWeekNumber = plantWeekNumber;
554
566
  exports2.plantWeekStart = plantWeekStart;
567
+ exports2.addPlantDays = addPlantDays;
555
568
  exports2.getTimeRangeFromRangeCode = getTimeRangeFromRangeCode;
556
569
  exports2.getCoverage = getCoverage;
557
570
  exports2.getWindowAtTime = getWindowAtTime;
@@ -566,6 +579,7 @@ var require_times = __commonJS({
566
579
  exports2.getDayRangeCode = getDayRangeCode;
567
580
  exports2.getCurrentWeekRangeCode = getCurrentWeekRangeCode;
568
581
  exports2.generateTimeWindows = generateTimeWindows;
582
+ exports2.generatePlantDayWindows = generatePlantDayWindows;
569
583
  exports2.BusrootDateTimeParse = BusrootDateTimeParse;
570
584
  exports2.decimalHoursToTime = decimalHoursToTime;
571
585
  var moment_timezone_1 = __importStar(require("moment-timezone"));
@@ -651,6 +665,49 @@ var require_times = __commonJS({
651
665
  months: 12
652
666
  }
653
667
  ];
668
+ var RANGE_CODE_MIN_YEAR = 2020;
669
+ var RANGE_CODE_MAX_YEAR = 2100;
670
+ function isoWeeksInIsoWeekYear(year) {
671
+ return moment_timezone_1.default.utc({ year }).isoWeeksInYear();
672
+ }
673
+ var DATED_RANGE_CODE_FORMATS = [
674
+ { pattern: /^DAY_(\d{4})_\d{2}_\d{2}$/, momentFormat: "YYYY_MM_DD" },
675
+ { pattern: /^MONTH_(\d{4})_\d{2}$/, momentFormat: "YYYY_MM" },
676
+ { pattern: /^HOUR_(\d{4})_\d{2}_\d{2}_\d{2}$/, momentFormat: "YYYY_MM_DD_HH" },
677
+ { pattern: /^MINUTE_(\d{4})_\d{2}_\d{2}_\d{2}_\d{2}$/, momentFormat: "YYYY_MM_DD_HH_mm" },
678
+ { pattern: /^QUARTER_(\d{4})_[1-4]$/ },
679
+ { pattern: /^YEAR_(\d{4})$/ }
680
+ ];
681
+ var WEEK_RANGE_CODE_PATTERN = /^WEEK_(\d{4})_(\d{1,2})$/;
682
+ var CUSTOM_RANGE_CODE_PATTERN = /^CUSTOM_\d+_\d+$/;
683
+ function isValidRangeCode(code) {
684
+ if (code === "TODAY" || code === "YESTERDAY") {
685
+ return true;
686
+ }
687
+ if (code.startsWith("RELATIVE_")) {
688
+ return exports2.RelativeRanges.some((relativeRange) => relativeRange.rangeCode === code);
689
+ }
690
+ if (CUSTOM_RANGE_CODE_PATTERN.test(code)) {
691
+ return true;
692
+ }
693
+ const isYearInBounds = (year) => year >= RANGE_CODE_MIN_YEAR && year <= RANGE_CODE_MAX_YEAR;
694
+ const weekMatch = WEEK_RANGE_CODE_PATTERN.exec(code);
695
+ if (weekMatch) {
696
+ const year = Number(weekMatch[1]);
697
+ const week = Number(weekMatch[2]);
698
+ return isYearInBounds(year) && week >= 1 && week <= isoWeeksInIsoWeekYear(year);
699
+ }
700
+ for (const { pattern, momentFormat } of DATED_RANGE_CODE_FORMATS) {
701
+ const match = pattern.exec(code);
702
+ if (match) {
703
+ const components = code.slice(code.indexOf("_") + 1);
704
+ const parsed = momentFormat == null ? null : moment_timezone_1.default.utc(components, momentFormat, true);
705
+ const isRealPeriod = parsed == null || parsed.isValid() && parsed.format(momentFormat) === components;
706
+ return isYearInBounds(Number(match[1])) && isRealPeriod;
707
+ }
708
+ }
709
+ return false;
710
+ }
654
711
  var AllowedAggregationWindows = [
655
712
  // 1 Hour
656
713
  {
@@ -699,7 +756,8 @@ var require_times = __commonJS({
699
756
  }
700
757
  ];
701
758
  function getAllowedAggregationWindowsForDuration(durationMs) {
702
- const allowedAggregationWindows = AllowedAggregationWindows.find((a) => durationMs <= a.durationMs)?.allowedAggregationWindows;
759
+ const nominalMs = (0, time_1.nominalDurationMs)(durationMs);
760
+ const allowedAggregationWindows = AllowedAggregationWindows.find((a) => nominalMs <= a.durationMs)?.allowedAggregationWindows;
703
761
  return allowedAggregationWindows;
704
762
  }
705
763
  function getTimestampFromString(timeString) {
@@ -933,6 +991,15 @@ var require_times = __commonJS({
933
991
  }
934
992
  return start;
935
993
  }
994
+ var DAYS_PER_WEEK = 7;
995
+ function plantDayRange(date, days, dayStartHour) {
996
+ const from = date.clone().add(dayStartHour, "hour").valueOf();
997
+ const to = date.clone().add(days, "days").add(dayStartHour, "hour").valueOf();
998
+ return timestampsToTimeRange({ from, to });
999
+ }
1000
+ function addPlantDays(instant, days, timezone) {
1001
+ return (0, moment_timezone_1.default)(instant).tz(timezone).add(days, "days").valueOf();
1002
+ }
936
1003
  function floorToPlantWeekStart(instant, plant) {
937
1004
  const weekStartDay = plant.weekStartDay ?? 0;
938
1005
  return (0, moment_timezone_1.default)(instant).tz(plant.timezone).subtract(weekStartDay, "days").subtract(plant.dayStartHour, "hours").startOf("isoWeek").add(weekStartDay, "days").add(plant.dayStartHour, "hours").valueOf();
@@ -992,36 +1059,33 @@ var require_times = __commonJS({
992
1059
  day.utc();
993
1060
  return timestampsToTimeRange({ from: day.valueOf(), to: day.add(1, "hours").valueOf() });
994
1061
  },
1062
+ // Every range below ends where the next one of its kind starts, built on the plant's local calendar
1063
+ // (add whole days/months, then dayStartHour) and only then read as UTC. Adding a fixed n×24h instead
1064
+ // would make a range that contains a daylight-saving change an hour too long or short, leaving gaps
1065
+ // and overlaps between consecutive ranges.
1066
+ //
995
1067
  // TODAY/YESTERDAY: subtract dayStartHour before startOf("day") to find the
996
1068
  // correct production day boundary, then re-add it. This handles the case where
997
1069
  // e.g. it's 02:00 but the production day started at 06:00 the previous calendar day.
998
1070
  today: () => {
999
- const day = (0, moment_timezone_1.default)(now).tz(plant.timezone).add(-plant.dayStartHour, "hour").startOf("day").add(plant.dayStartHour, "hour");
1000
- day.utc();
1001
- return timestampsToTimeRange({ from: day.valueOf(), to: day.add(24, "hours").valueOf() });
1071
+ const date = (0, moment_timezone_1.default)(now).tz(plant.timezone).add(-plant.dayStartHour, "hour").startOf("day");
1072
+ return plantDayRange(date, 1, plant.dayStartHour);
1002
1073
  },
1003
1074
  yesterday: () => {
1004
- const day = (0, moment_timezone_1.default)(now).tz(plant.timezone).add(-plant.dayStartHour, "hour").startOf("day").add(-1, "day").add(plant.dayStartHour, "hour");
1005
- day.utc();
1006
- return timestampsToTimeRange({ from: day.valueOf(), to: day.add(24, "hours").valueOf() });
1075
+ const date = (0, moment_timezone_1.default)(now).tz(plant.timezone).add(-plant.dayStartHour, "hour").startOf("day").add(-1, "day");
1076
+ return plantDayRange(date, 1, plant.dayStartHour);
1007
1077
  },
1008
1078
  day: () => {
1009
- const day = moment_timezone_1.default.tz(`${parseInt(parts[1])}_${parseInt(parts[2])}_${parseInt(parts[3])}`, "YYYY_MM_DD", plant.timezone);
1010
- day.add(plant.dayStartHour, "hour");
1011
- day.utc();
1012
- return timestampsToTimeRange({ from: day.valueOf(), to: day.add(24, "hours").valueOf() });
1079
+ const date = moment_timezone_1.default.tz(`${parseInt(parts[1])}_${parseInt(parts[2])}_${parseInt(parts[3])}`, "YYYY_MM_DD", plant.timezone);
1080
+ return plantDayRange(date, 1, plant.dayStartHour);
1013
1081
  },
1014
1082
  week: () => {
1015
1083
  const week = plantWeekStart(parseInt(parts[1]), parseInt(parts[2]), plant.weekStartDay ?? 0, plant.timezone);
1016
- week.add(plant.dayStartHour, "hour");
1017
- week.utc();
1018
- return timestampsToTimeRange({ from: week.valueOf(), to: week.add(24 * 7, "hours").valueOf() });
1084
+ return plantDayRange(week, DAYS_PER_WEEK, plant.dayStartHour);
1019
1085
  },
1020
1086
  month: () => {
1021
1087
  const month = moment_timezone_1.default.tz(`${parseInt(parts[1])}_${parseInt(parts[2])}}`, "YYYY_MM", plant.timezone).startOf("month");
1022
- month.add(plant.dayStartHour, "hour");
1023
- month.utc();
1024
- return timestampsToTimeRange({ from: month.valueOf(), to: month.add(month.daysInMonth() * 24, "hours").valueOf() });
1088
+ return plantDayRange(month, month.daysInMonth(), plant.dayStartHour);
1025
1089
  },
1026
1090
  // QUARTER: 13 ISO weeks (14 for Q4 in 53-week years). Start at the first ISO week of the quarter,
1027
1091
  // then apply the plant's weekStartDay and dayStartHour offsets exactly like the week handler.
@@ -1031,18 +1095,14 @@ var require_times = __commonJS({
1031
1095
  const weeks = getWeeksInQuarter(year, quarter);
1032
1096
  const firstWeek = weeks[0];
1033
1097
  const start = plantWeekStart(year, firstWeek, plant.weekStartDay ?? 0, plant.timezone);
1034
- start.add(plant.dayStartHour, "hour");
1035
- start.utc();
1036
- return timestampsToTimeRange({ from: start.valueOf(), to: start.add(weeks.length * 7 * 24, "hours").valueOf() });
1098
+ return plantDayRange(start, weeks.length * DAYS_PER_WEEK, plant.dayStartHour);
1037
1099
  },
1038
1100
  // YEAR: full ISO week year (52 or 53 weeks). Start at ISO week 1, apply plant offsets like the week handler.
1039
1101
  year: () => {
1040
1102
  const year = parseInt(parts[1]);
1041
- const weeksInYear = (0, moment_timezone_1.default)({ year }).isoWeeksInISOWeekYear();
1103
+ const weeksInYear = isoWeeksInIsoWeekYear(year);
1042
1104
  const start = plantWeekStart(year, 1, plant.weekStartDay ?? 0, plant.timezone);
1043
- start.add(plant.dayStartHour, "hour");
1044
- start.utc();
1045
- return timestampsToTimeRange({ from: start.valueOf(), to: start.add(weeksInYear * 7 * 24, "hours").valueOf() });
1105
+ return plantDayRange(start, weeksInYear * DAYS_PER_WEEK, plant.dayStartHour);
1046
1106
  },
1047
1107
  custom: () => timestampsToTimeRange({ from: 0, to: 0 }),
1048
1108
  // Already handled above
@@ -1151,7 +1211,7 @@ var require_times = __commonJS({
1151
1211
  };
1152
1212
  exports2.intersectTimeWindowsIso = intersectTimeWindowsIso;
1153
1213
  function getWeeksInQuarter(year, quarter) {
1154
- const weeksInYear = (0, moment_timezone_1.default)({ year }).isoWeeksInISOWeekYear();
1214
+ const weeksInYear = isoWeeksInIsoWeekYear(year);
1155
1215
  if (quarter === 1) {
1156
1216
  return new Array(13).fill("").map((v, i) => i + 1);
1157
1217
  }
@@ -1338,6 +1398,25 @@ var require_times = __commonJS({
1338
1398
  endAt: (0, moment_timezone_1.default)(endAtTimestamp).toISOString()
1339
1399
  };
1340
1400
  }
1401
+ function generatePlantDayWindows(startAt, days, until, timezone) {
1402
+ const windows = [];
1403
+ let endAtTimestamp = 0;
1404
+ for (let i = 0; ; i++) {
1405
+ const windowStartAtTimestamp = addPlantDays(startAt, days * i, timezone);
1406
+ const windowEndAtTimestamp = addPlantDays(startAt, days * (i + 1), timezone);
1407
+ if (windowEndAtTimestamp > until) {
1408
+ break;
1409
+ }
1410
+ endAtTimestamp = windowEndAtTimestamp;
1411
+ windows.push({
1412
+ windowStartAtTimestamp,
1413
+ windowEndAtTimestamp,
1414
+ windowStartAt: (0, moment_timezone_1.default)(windowStartAtTimestamp).toISOString(),
1415
+ windowEndAt: (0, moment_timezone_1.default)(windowEndAtTimestamp).toISOString()
1416
+ });
1417
+ }
1418
+ return { windows, endAtTimestamp };
1419
+ }
1341
1420
  function BusrootDateTimeParse(obj, now = moment_timezone_1.default.utc().valueOf()) {
1342
1421
  const fields = ["plannedStartAt", "plannedEndAt", "actualStartAt", "actualEndAt"];
1343
1422
  for (const field of fields) {
@@ -3511,10 +3590,7 @@ var require_common = __commonJS({
3511
3590
  var times_1 = require_times();
3512
3591
  var time_1 = require_time();
3513
3592
  exports2.UNKNOWN_CODE = "unknown";
3514
- exports2.RangeCodeSchema = zod_1.z.string().trim().refine((value) => {
3515
- const rangeType = value.split("_")[0].toLowerCase();
3516
- return zod_1.z.nativeEnum(times_1.RangeType).safeParse(rangeType).success;
3517
- }).optional().describe("Time range code (e.g. TODAY, YESTERDAY, DAY_2026_02_24, WEEK_2026_09, MONTH_2026_02, QUARTER_2026_2, YEAR_2026, HOUR_2026_02_24_14, MINUTE_2026_02_24_14_30, RELATIVE_HOURS_1, RELATIVE_HOURS_24, RELATIVE_DAYS_7, RELATIVE_DAYS_28).");
3593
+ exports2.RangeCodeSchema = zod_1.z.string().trim().refine(times_1.isValidRangeCode).optional().describe("Time range code (e.g. TODAY, YESTERDAY, DAY_2026_02_24, WEEK_2026_09, MONTH_2026_02, QUARTER_2026_2, YEAR_2026, HOUR_2026_02_24_14, MINUTE_2026_02_24_14_30, RELATIVE_HOURS_1, RELATIVE_HOURS_24, RELATIVE_DAYS_7, RELATIVE_DAYS_28).");
3518
3594
  exports2.StandardRequestParams = zod_1.z.object({
3519
3595
  plantcode: zod_1.z.string().trim().min(1).optional(),
3520
3596
  rangecode: exports2.RangeCodeSchema,
@@ -9865,11 +9941,11 @@ var require_station_windows = __commonJS({
9865
9941
  if (windows.length === 0) {
9866
9942
  throw new Error("Must be at least 1 window.");
9867
9943
  }
9868
- const windowDuration = windows[0].totalMs;
9944
+ const windowDuration = (0, time_1.nominalDurationMs)(windows[0].totalMs);
9869
9945
  const window2 = windows.reduce((a, b, i) => {
9870
9946
  const isFirstWindow = i === 0;
9871
9947
  const isConsecutive = a.windowEndAt === b.windowStartAt;
9872
- const isSameDuration = b.totalMs === windowDuration;
9948
+ const isSameDuration = (0, time_1.nominalDurationMs)(b.totalMs) === windowDuration;
9873
9949
  if (!isFirstWindow && !skipConsecutiveCheck) {
9874
9950
  if (!isConsecutive) {
9875
9951
  throw new Error("Windows are not consecutive.");
@@ -10148,7 +10224,7 @@ var require_station_windows = __commonJS({
10148
10224
  const batch = [];
10149
10225
  for (let i = 0; i < windows.length; i++) {
10150
10226
  const thisWindow = windows[i];
10151
- const thisWindowDuration = thisWindow.windowEndAtTimestamp - thisWindow.windowStartAtTimestamp;
10227
+ const thisWindowDuration = (0, time_1.nominalDurationMs)(thisWindow.windowEndAtTimestamp - thisWindow.windowStartAtTimestamp);
10152
10228
  if (thisWindowDuration > aggregateWindowInMs) {
10153
10229
  throw new Error("Windows being aggregated must be less than or equal to the aggregate window. " + windows[i].totalMs + " " + aggregateWindowInMinutes);
10154
10230
  }
@@ -10160,7 +10236,7 @@ var require_station_windows = __commonJS({
10160
10236
  }
10161
10237
  }
10162
10238
  batch.push(windows[i]);
10163
- const durationOfBatch = batch.reduce((a, b) => a + b.totalMs, 0);
10239
+ const durationOfBatch = batch.reduce((a, b) => a + (0, time_1.nominalDurationMs)(b.totalMs), 0);
10164
10240
  if (durationOfBatch >= aggregateWindowInMs) {
10165
10241
  aggregatedWindows.push(combineStationWindows(batch, inShiftAverages));
10166
10242
  batch.length = 0;
@@ -10171,8 +10247,8 @@ var require_station_windows = __commonJS({
10171
10247
  const lastAggregatedWindowEndAtTimestamp = aggregatedWindows[aggregatedWindows.length - 1]?.windowEndAtTimestamp;
10172
10248
  const lastWindowEndAtTimestamp = lastAggregatedWindowEndAtTimestamp ?? windows[windows.length - 1].windowEndAtTimestamp;
10173
10249
  const futureWindowsStartAt = lastAggregatedWindowEndAtTimestamp == null && aggregateWindowInMinutes < time_1.TimeIntervals["1h"].min ? lastWindowEndAtTimestamp - lastWindowEndAtTimestamp % windowDurationMs : lastWindowEndAtTimestamp;
10174
- const requiredFutureWindowsCount = Math.floor((includeFutureWindowsUntil - futureWindowsStartAt) / windowDurationMs);
10175
- const { windows: futureTimeWindows, endAtTimestamp } = (0, times_1.generateTimeWindows)(futureWindowsStartAt, windowDurationMs, requiredFutureWindowsCount, void 0, false);
10250
+ const windowDays = windowDurationMs / time_1.TimeIntervals["1d"].ms;
10251
+ const { windows: futureTimeWindows, endAtTimestamp } = Number.isInteger(windowDays) ? (0, times_1.generatePlantDayWindows)(futureWindowsStartAt, windowDays, includeFutureWindowsUntil, station.timezone) : (0, times_1.generateTimeWindows)(futureWindowsStartAt, windowDurationMs, Math.floor((includeFutureWindowsUntil - futureWindowsStartAt) / windowDurationMs), void 0, false);
10176
10252
  const futureWindows = futureTimeWindows.map((x) => {
10177
10253
  const isInShift = shiftUtils.isTimeInShifts({
10178
10254
  time: x.windowStartAtTimestamp,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "busroot-sdk",
3
- "version": "0.0.8-dev.2916117361",
3
+ "version": "0.0.8-dev.2920950808",
4
4
  "description": "An SDK for accessing Busroot from output.industries",
5
5
  "homepage": "https://www.output.industries",
6
6
  "main": "./build/index.js",