scschedule 2.1.1 → 3.0.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.
Files changed (34) hide show
  1. package/README.md +142 -41
  2. package/dist/cleanupExpiredOverridesFromSchedule.d.ts +4 -0
  3. package/dist/cleanupExpiredOverridesFromSchedule.js +4 -0
  4. package/dist/constants.d.ts +1 -3
  5. package/dist/constants.js +1 -3
  6. package/dist/getAvailableRangesFromSchedule.d.ts +10 -0
  7. package/dist/getAvailableRangesFromSchedule.js +19 -0
  8. package/dist/getNextAvailableFromSchedule.d.ts +9 -8
  9. package/dist/getNextAvailableFromSchedule.js +14 -8
  10. package/dist/getNextUnavailableFromSchedule.d.ts +19 -9
  11. package/dist/getNextUnavailableFromSchedule.js +124 -63
  12. package/dist/index.d.ts +3 -2
  13. package/dist/index.js +4 -2
  14. package/dist/internal/getApplicableRuleForDate.d.ts +11 -11
  15. package/dist/internal/getApplicableRuleForDate.js +11 -7
  16. package/dist/internal/index.d.ts +1 -3
  17. package/dist/internal/index.js +1 -3
  18. package/dist/internal/validateNoEmptyWeekdays.js +2 -1
  19. package/dist/internal/validateNoOverlappingRules.js +5 -4
  20. package/dist/internal/validateNoOverlappingTimesInRule.js +2 -1
  21. package/dist/internal/validateNoSpilloverConflictsAtOverrideBoundaries.d.ts +4 -2
  22. package/dist/internal/validateNoSpilloverConflictsAtOverrideBoundaries.js +71 -54
  23. package/dist/internal/validateNonEmptyTimes.js +2 -1
  24. package/dist/internal/validateScDateFormats.js +2 -1
  25. package/dist/isScheduleAvailable.d.ts +9 -0
  26. package/dist/isScheduleAvailable.js +19 -2
  27. package/dist/types.d.ts +12 -18
  28. package/dist/validateSchedule.d.ts +4 -2
  29. package/dist/validateSchedule.js +4 -4
  30. package/package.json +2 -2
  31. package/dist/internal/isValidTimezone.d.ts +0 -4
  32. package/dist/internal/isValidTimezone.js +0 -12
  33. package/dist/internal/validateTimezone.d.ts +0 -5
  34. package/dist/internal/validateTimezone.js +0 -16
@@ -2,5 +2,14 @@ import type { STimestamp } from 'scdate';
2
2
  import type { Schedule } from './types.js';
3
3
  /**
4
4
  * Checks if a schedule is available at the specified timestamp.
5
+ *
6
+ * When `weekly` is `true`, the schedule is always available (unless an
7
+ * override applies). Otherwise, checks whether the timestamp falls within any
8
+ * matching time range for the day, including cross-midnight spillover from the
9
+ * previous day.
10
+ *
11
+ * @param schedule The schedule to check availability against.
12
+ * @param timestamp The timestamp to check.
13
+ * @returns True if the schedule is available at the given timestamp.
5
14
  */
6
15
  export declare const isScheduleAvailable: (schedule: Schedule, timestamp: STimestamp | string) => boolean;
@@ -3,6 +3,15 @@ import { getApplicableRuleForDate } from './internal/getApplicableRuleForDate.js
3
3
  import { isTimeInTimeRange } from './internal/isTimeInTimeRange.js';
4
4
  /**
5
5
  * Checks if a schedule is available at the specified timestamp.
6
+ *
7
+ * When `weekly` is `true`, the schedule is always available (unless an
8
+ * override applies). Otherwise, checks whether the timestamp falls within any
9
+ * matching time range for the day, including cross-midnight spillover from the
10
+ * previous day.
11
+ *
12
+ * @param schedule The schedule to check availability against.
13
+ * @param timestamp The timestamp to check.
14
+ * @returns True if the schedule is available at the given timestamp.
6
15
  */
7
16
  export const isScheduleAvailable = (schedule, timestamp) => {
8
17
  const date = getDateFromTimestamp(timestamp);
@@ -10,6 +19,10 @@ export const isScheduleAvailable = (schedule, timestamp) => {
10
19
  const weekday = getWeekdayFromDate(date);
11
20
  // Get the applicable rules for this date
12
21
  const { rules } = getApplicableRuleForDate(schedule, date.date);
22
+ // If weekly is true, always available (unless overridden)
23
+ if (rules === true) {
24
+ return true;
25
+ }
13
26
  // Check if any rule's time ranges include this timestamp (same-day check)
14
27
  const matchesSameDay = rules.some((rule) => {
15
28
  // Check if this weekday is in the rule
@@ -25,8 +38,12 @@ export const isScheduleAvailable = (schedule, timestamp) => {
25
38
  // Also check previous day's rules for cross-midnight spillover
26
39
  const previousDate = addDaysToDate(date, -1);
27
40
  const previousWeekday = getWeekdayFromDate(previousDate);
28
- const { rules: previousRules } = getApplicableRuleForDate(schedule, previousDate.date);
29
- return previousRules.some((rule) => {
41
+ const previousResult = getApplicableRuleForDate(schedule, previousDate.date);
42
+ // If previous day was always available, no cross-midnight rules to check
43
+ if (previousResult.rules === true) {
44
+ return false;
45
+ }
46
+ return previousResult.rules.some((rule) => {
30
47
  // Check if previous day's weekday is in the rule
31
48
  if (!doesWeekdaysIncludeWeekday(rule.weekdays, previousWeekday)) {
32
49
  return false;
package/dist/types.d.ts CHANGED
@@ -60,20 +60,19 @@ export interface OverrideScheduleRule {
60
60
  rules: WeeklyScheduleRule[];
61
61
  }
62
62
  /**
63
- * Represents a complete availability schedule. A schedule consists of a
64
- * timezone for all date/time operations, base weekly recurring patterns, and
65
- * optional date-specific overrides. Priority order for determining
66
- * availability: 1. Specific override (with both from and to dates) 2.
67
- * Indefinite override (with only from date) 3. Weekly schedule
63
+ * Represents a complete availability schedule. A schedule consists of base
64
+ * weekly recurring patterns and optional date-specific overrides. Priority
65
+ * order for determining availability: 1. Specific override (with both from and
66
+ * to dates) 2. Indefinite override (with only from date) 3. Weekly schedule
68
67
  */
69
68
  export interface Schedule {
70
69
  /**
71
- * IANA timezone identifier (e.g., 'America/New_York', 'Europe/London'). Must
72
- * be in `Intl.supportedValuesOf('timeZone')`.
70
+ * Base recurring weekly schedule patterns.
71
+ * - `true`: available 24/7 (overrides can close windows)
72
+ * - `WeeklyScheduleRule[]`: available during defined time ranges
73
+ * - `[]`: never available (overrides can open windows)
73
74
  */
74
- timezone: string;
75
- /** Base recurring weekly schedule patterns */
76
- weekly: WeeklyScheduleRule[];
75
+ weekly: WeeklyScheduleRule[] | true;
77
76
  /**
78
77
  * Date-specific exceptions to the weekly schedule. Overrides take precedence
79
78
  * over weekly rules.
@@ -95,11 +94,6 @@ export interface AvailabilityRange {
95
94
  * property to narrow the type and access error-specific fields.
96
95
  */
97
96
  export type ValidationError = {
98
- /** The timezone string is not a valid IANA timezone identifier */
99
- issue: ValidationIssue.InvalidTimezone;
100
- /** The invalid timezone string that was provided */
101
- timezone: string;
102
- } | {
103
97
  /** Two or more specific overrides have identical date ranges */
104
98
  issue: ValidationIssue.DuplicateOverrides;
105
99
  /** Indexes of the two duplicate overrides */
@@ -225,7 +219,7 @@ export type ValidationError = {
225
219
  } | {
226
220
  /**
227
221
  * Cross-midnight spillover from override's last day conflicts with next
228
- * day's time ranges
222
+ * day's time ranges or weekly: true availability
229
223
  */
230
224
  issue: ValidationIssue.SpilloverConflictOverrideIntoNext;
231
225
  /** Index of the override whose last day causes spillover */
@@ -235,8 +229,8 @@ export type ValidationError = {
235
229
  /** The override rule index causing the spillover */
236
230
  overrideRuleIndex: number;
237
231
  /**
238
- * The next day's rule that conflicts (weekly or another override). If
239
- * nextDayOverrideIndex is undefined, it's a weekly rule.
232
+ * The next day's rule that conflicts. When all three fields are
233
+ * undefined, the next day is weekly: true (fully available).
240
234
  */
241
235
  nextDayWeeklyRuleIndex?: number;
242
236
  nextDayOverrideIndex?: number;
@@ -3,13 +3,15 @@ import type { Schedule, ValidationResult } from './types.js';
3
3
  * Validates a schedule configuration and returns all validation errors found.
4
4
  *
5
5
  * Validation is performed in two phases:
6
- * 1. Structural validation (timezone, formats, date order, empty weekdays,
7
- * non-empty times, weekday-date mismatch) - runs on original schedule
6
+ * 1. Structural validation (formats, date order, empty weekdays, non-empty
7
+ * times, weekday-date mismatch) - runs on original schedule
8
8
  * 2. Semantic validation (overlaps, conflicts) - runs on normalized schedule
9
9
  * after filtering weekdays to actual dates
10
10
  *
11
11
  * If structural errors are found, validation stops early and returns only
12
12
  * those errors. This provides better user experience and avoids crashes from
13
13
  * invalid data during normalization.
14
+ *
15
+ * @param schedule The schedule to validate.
14
16
  */
15
17
  export declare const validateSchedule: (schedule: Schedule) => ValidationResult;
@@ -8,26 +8,26 @@ import { validateNonEmptyTimes } from './internal/validateNonEmptyTimes.js';
8
8
  import { validateOverrideDateOrder } from './internal/validateOverrideDateOrder.js';
9
9
  import { validateOverrideWeekdaysMatchDates } from './internal/validateOverrideWeekdaysMatchDates.js';
10
10
  import { validateScDateFormats } from './internal/validateScDateFormats.js';
11
- import { validateTimezone } from './internal/validateTimezone.js';
12
11
  /**
13
12
  * Validates a schedule configuration and returns all validation errors found.
14
13
  *
15
14
  * Validation is performed in two phases:
16
- * 1. Structural validation (timezone, formats, date order, empty weekdays,
17
- * non-empty times, weekday-date mismatch) - runs on original schedule
15
+ * 1. Structural validation (formats, date order, empty weekdays, non-empty
16
+ * times, weekday-date mismatch) - runs on original schedule
18
17
  * 2. Semantic validation (overlaps, conflicts) - runs on normalized schedule
19
18
  * after filtering weekdays to actual dates
20
19
  *
21
20
  * If structural errors are found, validation stops early and returns only
22
21
  * those errors. This provides better user experience and avoids crashes from
23
22
  * invalid data during normalization.
23
+ *
24
+ * @param schedule The schedule to validate.
24
25
  */
25
26
  export const validateSchedule = (schedule) => {
26
27
  // Phase 1: Structural validation
27
28
  // Note: Order matters - date formats and order must be validated before
28
29
  // weekday matching (which calls filterWeekdaysForDates)
29
30
  const structuralErrors = [
30
- ...validateTimezone(schedule),
31
31
  ...validateScDateFormats(schedule),
32
32
  ...validateOverrideDateOrder(schedule),
33
33
  ...validateNoEmptyWeekdays(schedule),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scschedule",
3
- "version": "2.1.1",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dist/index.js"
@@ -20,7 +20,7 @@
20
20
  "test": "vitest run"
21
21
  },
22
22
  "dependencies": {
23
- "scdate": "2.1.1"
23
+ "scdate": "3.0.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@eslint/js": "^9.39.2",
@@ -1,4 +0,0 @@
1
- /**
2
- * Checks if a string is a valid IANA timezone identifier.
3
- */
4
- export declare const isValidTimezone: (timezone: string) => boolean;
@@ -1,12 +0,0 @@
1
- /**
2
- * Checks if a string is a valid IANA timezone identifier.
3
- */
4
- export const isValidTimezone = (timezone) => {
5
- try {
6
- const validTimezones = Intl.supportedValuesOf('timeZone');
7
- return validTimezones.includes(timezone);
8
- }
9
- catch {
10
- return false;
11
- }
12
- };
@@ -1,5 +0,0 @@
1
- import type { Schedule, ValidationError } from '../types.js';
2
- /**
3
- * Validates that a schedule's timezone is a valid IANA timezone identifier.
4
- */
5
- export declare const validateTimezone: (schedule: Schedule) => ValidationError[];
@@ -1,16 +0,0 @@
1
- import { ValidationIssue } from '../constants.js';
2
- import { isValidTimezone } from './isValidTimezone.js';
3
- /**
4
- * Validates that a schedule's timezone is a valid IANA timezone identifier.
5
- */
6
- export const validateTimezone = (schedule) => {
7
- if (!isValidTimezone(schedule.timezone)) {
8
- return [
9
- {
10
- issue: ValidationIssue.InvalidTimezone,
11
- timezone: schedule.timezone,
12
- },
13
- ];
14
- }
15
- return [];
16
- };