@j13b/scheduler 0.0.0-stage → 0.1.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.
Files changed (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -2
  3. package/dist/alterations.d.ts +6 -0
  4. package/dist/alterations.d.ts.map +1 -0
  5. package/dist/alterations.js +61 -0
  6. package/dist/alterations.js.map +1 -0
  7. package/dist/cjs/alterations.d.ts +5 -0
  8. package/dist/cjs/alterations.js +96 -0
  9. package/dist/cjs/dispatcher.d.ts +38 -0
  10. package/dist/cjs/dispatcher.js +165 -0
  11. package/dist/cjs/dispatcher_store.d.ts +9 -0
  12. package/dist/cjs/dispatcher_store.js +15 -0
  13. package/dist/cjs/generators/anchored.d.ts +3 -0
  14. package/dist/cjs/generators/anchored.js +57 -0
  15. package/dist/cjs/generators/days_of_month.d.ts +3 -0
  16. package/dist/cjs/generators/days_of_month.js +76 -0
  17. package/dist/cjs/generators/days_of_week.d.ts +5 -0
  18. package/dist/cjs/generators/days_of_week.js +65 -0
  19. package/dist/cjs/generators/days_of_year.d.ts +3 -0
  20. package/dist/cjs/generators/days_of_year.js +70 -0
  21. package/dist/cjs/generators/instant.d.ts +3 -0
  22. package/dist/cjs/generators/instant.js +15 -0
  23. package/dist/cjs/generators/local_date_time.d.ts +3 -0
  24. package/dist/cjs/generators/local_date_time.js +31 -0
  25. package/dist/cjs/generators/months_of_year.d.ts +3 -0
  26. package/dist/cjs/generators/months_of_year.js +109 -0
  27. package/dist/cjs/generators/types.d.ts +9 -0
  28. package/dist/cjs/generators/types.js +19 -0
  29. package/dist/cjs/generators/weeks_of_month.d.ts +3 -0
  30. package/dist/cjs/generators/weeks_of_month.js +99 -0
  31. package/dist/cjs/generators/yearly.d.ts +3 -0
  32. package/dist/cjs/generators/yearly.js +52 -0
  33. package/dist/cjs/in_memory_port.d.ts +18 -0
  34. package/dist/cjs/in_memory_port.js +59 -0
  35. package/dist/cjs/index.d.ts +7 -0
  36. package/dist/cjs/index.js +18 -0
  37. package/dist/cjs/occurrence_key.d.ts +5 -0
  38. package/dist/cjs/occurrence_key.js +21 -0
  39. package/dist/cjs/package.json +3 -0
  40. package/dist/cjs/port.d.ts +15 -0
  41. package/dist/cjs/port.js +2 -0
  42. package/dist/cjs/scheduler.d.ts +23 -0
  43. package/dist/cjs/scheduler.js +302 -0
  44. package/dist/cjs/types.d.ts +149 -0
  45. package/dist/cjs/types.js +58 -0
  46. package/dist/cjs/tz.d.ts +22 -0
  47. package/dist/cjs/tz.js +197 -0
  48. package/dist/cjs/validate_rule.d.ts +4 -0
  49. package/dist/cjs/validate_rule.js +267 -0
  50. package/dist/dispatcher.d.ts +39 -0
  51. package/dist/dispatcher.d.ts.map +1 -0
  52. package/dist/dispatcher.js +111 -0
  53. package/dist/dispatcher.js.map +1 -0
  54. package/dist/dispatcher_store.d.ts +10 -0
  55. package/dist/dispatcher_store.d.ts.map +1 -0
  56. package/dist/dispatcher_store.js +13 -0
  57. package/dist/dispatcher_store.js.map +1 -0
  58. package/dist/generators/anchored.d.ts +4 -0
  59. package/dist/generators/anchored.d.ts.map +1 -0
  60. package/dist/generators/anchored.js +42 -0
  61. package/dist/generators/anchored.js.map +1 -0
  62. package/dist/generators/days_of_month.d.ts +4 -0
  63. package/dist/generators/days_of_month.d.ts.map +1 -0
  64. package/dist/generators/days_of_month.js +53 -0
  65. package/dist/generators/days_of_month.js.map +1 -0
  66. package/dist/generators/days_of_week.d.ts +6 -0
  67. package/dist/generators/days_of_week.d.ts.map +1 -0
  68. package/dist/generators/days_of_week.js +43 -0
  69. package/dist/generators/days_of_week.js.map +1 -0
  70. package/dist/generators/days_of_year.d.ts +4 -0
  71. package/dist/generators/days_of_year.d.ts.map +1 -0
  72. package/dist/generators/days_of_year.js +50 -0
  73. package/dist/generators/days_of_year.js.map +1 -0
  74. package/dist/generators/instant.d.ts +4 -0
  75. package/dist/generators/instant.d.ts.map +1 -0
  76. package/dist/generators/instant.js +15 -0
  77. package/dist/generators/instant.js.map +1 -0
  78. package/dist/generators/local_date_time.d.ts +4 -0
  79. package/dist/generators/local_date_time.d.ts.map +1 -0
  80. package/dist/generators/local_date_time.js +20 -0
  81. package/dist/generators/local_date_time.js.map +1 -0
  82. package/dist/generators/months_of_year.d.ts +4 -0
  83. package/dist/generators/months_of_year.d.ts.map +1 -0
  84. package/dist/generators/months_of_year.js +64 -0
  85. package/dist/generators/months_of_year.js.map +1 -0
  86. package/dist/generators/types.d.ts +10 -0
  87. package/dist/generators/types.d.ts.map +1 -0
  88. package/dist/generators/types.js +14 -0
  89. package/dist/generators/types.js.map +1 -0
  90. package/dist/generators/weeks_of_month.d.ts +4 -0
  91. package/dist/generators/weeks_of_month.d.ts.map +1 -0
  92. package/dist/generators/weeks_of_month.js +61 -0
  93. package/dist/generators/weeks_of_month.js.map +1 -0
  94. package/dist/generators/yearly.d.ts +4 -0
  95. package/dist/generators/yearly.d.ts.map +1 -0
  96. package/dist/generators/yearly.js +37 -0
  97. package/dist/generators/yearly.js.map +1 -0
  98. package/dist/in_memory_port.d.ts +19 -0
  99. package/dist/in_memory_port.d.ts.map +1 -0
  100. package/dist/in_memory_port.js +45 -0
  101. package/dist/in_memory_port.js.map +1 -0
  102. package/dist/index.d.ts +8 -0
  103. package/dist/index.d.ts.map +1 -0
  104. package/dist/index.js +7 -0
  105. package/dist/occurrence_key.d.ts +6 -0
  106. package/dist/occurrence_key.d.ts.map +1 -0
  107. package/dist/occurrence_key.js +25 -0
  108. package/dist/occurrence_key.js.map +1 -0
  109. package/dist/port.d.ts +16 -0
  110. package/dist/port.d.ts.map +1 -0
  111. package/dist/scheduler.d.ts +24 -0
  112. package/dist/scheduler.d.ts.map +1 -0
  113. package/dist/scheduler.js +241 -0
  114. package/dist/scheduler.js.map +1 -0
  115. package/dist/types.d.ts +150 -0
  116. package/dist/types.d.ts.map +1 -0
  117. package/dist/types.js +29 -0
  118. package/dist/types.js.map +1 -0
  119. package/dist/tz.d.ts +23 -0
  120. package/dist/tz.d.ts.map +1 -0
  121. package/dist/tz.js +131 -0
  122. package/dist/tz.js.map +1 -0
  123. package/dist/validate_rule.d.ts +5 -0
  124. package/dist/validate_rule.d.ts.map +1 -0
  125. package/dist/validate_rule.js +280 -0
  126. package/dist/validate_rule.js.map +1 -0
  127. package/package.json +76 -4
  128. package/src/__tests__/alterations.test.ts +177 -0
  129. package/src/__tests__/anchored.test.ts +107 -0
  130. package/src/__tests__/days_of_month.test.ts +162 -0
  131. package/src/__tests__/days_of_week.test.ts +134 -0
  132. package/src/__tests__/days_of_year.test.ts +158 -0
  133. package/src/__tests__/dispatcher.test.ts +317 -0
  134. package/src/__tests__/instant.test.ts +50 -0
  135. package/src/__tests__/local_date_time.test.ts +139 -0
  136. package/src/__tests__/months_of_year.test.ts +229 -0
  137. package/src/__tests__/scheduler.test.ts +888 -0
  138. package/src/__tests__/tz.test.ts +201 -0
  139. package/src/__tests__/validate_rule.test.ts +190 -0
  140. package/src/__tests__/weeks_of_month.test.ts +195 -0
  141. package/src/__tests__/yearly.test.ts +108 -0
  142. package/src/alterations.ts +108 -0
  143. package/src/dispatcher.ts +184 -0
  144. package/src/dispatcher_store.ts +26 -0
  145. package/src/generators/anchored.ts +69 -0
  146. package/src/generators/days_of_month.ts +100 -0
  147. package/src/generators/days_of_week.ts +101 -0
  148. package/src/generators/days_of_year.ts +88 -0
  149. package/src/generators/instant.ts +18 -0
  150. package/src/generators/local_date_time.ts +38 -0
  151. package/src/generators/months_of_year.ts +143 -0
  152. package/src/generators/types.ts +26 -0
  153. package/src/generators/weeks_of_month.ts +133 -0
  154. package/src/generators/yearly.ts +73 -0
  155. package/src/in_memory_port.ts +70 -0
  156. package/src/index.ts +25 -0
  157. package/src/occurrence_key.ts +42 -0
  158. package/src/port.ts +31 -0
  159. package/src/scheduler.ts +353 -0
  160. package/src/types.ts +369 -0
  161. package/src/tz.ts +246 -0
  162. package/src/validate_rule.ts +311 -0
  163. package/tsconfig.json +25 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher_store.js","names":[],"sources":["../src/dispatcher_store.ts"],"sourcesContent":["// Persistence for the Dispatcher's tick cursor. The cursor is a single\n// epoch-ms value: the last instant for which the Dispatcher has fired all\n// due events. On restart, the Dispatcher catches up by listing events in\n// [cursor, now) and firing them — so a process crash followed by restart\n// won't lose events that were due during downtime.\n//\n// Same shape as ChatStore / MemoryStore in the agent module — implement\n// against whatever backend you want.\nexport interface DispatcherStore {\n loadCursor(): Promise<number | null>;\n saveCursor(cursor: number): Promise<void>;\n}\n\n// Reference in-memory implementation. Useful for tests and short-lived\n// processes that don't need crash recovery.\nexport class InMemoryDispatcherStore implements DispatcherStore {\n private _cursor: number | null = null;\n\n async loadCursor(): Promise<number | null> {\n return this._cursor;\n }\n\n async saveCursor(cursor: number): Promise<void> {\n this._cursor = cursor;\n }\n}\n"],"mappings":";AAeA,IAAa,0BAAb,MAAgE;CAC9D,UAAiC;CAEjC,MAAM,aAAqC;EACzC,OAAO,KAAK;CACd;CAEA,MAAM,WAAW,QAA+B;EAC9C,KAAK,UAAU;CACjB;AACF"}
@@ -0,0 +1,4 @@
1
+ import { AnchoredRule } from '../types.js';
2
+ import { Occurrence } from './types.js';
3
+ export declare function generateAnchored(rule: AnchoredRule, tz: string | undefined, windowStart: number, windowEnd: number): Occurrence[];
4
+ //# sourceMappingURL=anchored.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchored.d.ts","sourceRoot":"","sources":["../../src/generators/anchored.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAEL,KAAK,UAAU,EAGhB,MAAM,YAAY,CAAC;AAKpB,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,UAAU,EAAE,CAmDd"}
@@ -0,0 +1,42 @@
1
+ import { computeSpanEnd } from "../tz.js";
2
+ import { TooManyOccurrencesError, intersects } from "./types.js";
3
+ // src/generators/anchored.ts
4
+ function generateAnchored(rule, tz, windowStart, windowEnd) {
5
+ if (rule.intervalMs <= 0) throw new Error(`AnchoredRule.intervalMs must be > 0 (got ${rule.intervalMs})`);
6
+ if (rule.count != null && rule.count < 0) throw new Error(`AnchoredRule.count must be >= 0 (got ${rule.count})`);
7
+ const seriesEnd = rule.seriesEnd ?? Number.POSITIVE_INFINITY;
8
+ const effectiveEnd = Math.min(windowEnd, seriesEnd);
9
+ const effectiveStart = Math.max(windowStart, rule.seriesStart);
10
+ if (effectiveStart >= effectiveEnd) return [];
11
+ let index;
12
+ let occurrenceStart;
13
+ if (rule.count != null) {
14
+ index = 0;
15
+ occurrenceStart = rule.anchorDate;
16
+ } else if (effectiveStart <= rule.anchorDate) {
17
+ index = 0;
18
+ occurrenceStart = rule.anchorDate;
19
+ } else {
20
+ const stepsAhead = Math.ceil((effectiveStart - rule.anchorDate) / rule.intervalMs);
21
+ index = stepsAhead;
22
+ occurrenceStart = rule.anchorDate + stepsAhead * rule.intervalMs;
23
+ }
24
+ const out = [];
25
+ while (occurrenceStart < effectiveEnd) {
26
+ if (rule.count != null && index >= rule.count) break;
27
+ const occEnd = computeSpanEnd(occurrenceStart, rule.timeSpan, tz);
28
+ if (intersects(occurrenceStart, occEnd, windowStart, windowEnd) && occurrenceStart >= rule.seriesStart) {
29
+ out.push({
30
+ startTimestamp: occurrenceStart,
31
+ endTimestamp: occEnd
32
+ });
33
+ if (out.length > 1e4) throw new TooManyOccurrencesError("anchored");
34
+ }
35
+ index++;
36
+ occurrenceStart += rule.intervalMs;
37
+ }
38
+ return out;
39
+ }
40
+ export { generateAnchored };
41
+
42
+ //# sourceMappingURL=anchored.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchored.js","names":[],"sources":["../../src/generators/anchored.ts"],"sourcesContent":["import type { AnchoredRule } from \"../types.js\";\nimport { computeSpanEnd } from \"../tz.js\";\nimport {\n MAX_OCCURRENCES_PER_GENERATE,\n type Occurrence,\n TooManyOccurrencesError,\n intersects,\n} from \"./types.js\";\n\n// Anchored is tz-independent for STEPPING (every occurrence is exactly\n// intervalMs ms from the anchor), but tz can still affect the end-of-\n// occurrence when hourSpan is used. seriesStart/seriesEnd bound in UTC.\nexport function generateAnchored(\n rule: AnchoredRule,\n tz: string | undefined,\n windowStart: number,\n windowEnd: number\n): Occurrence[] {\n if (rule.intervalMs <= 0) {\n throw new Error(`AnchoredRule.intervalMs must be > 0 (got ${rule.intervalMs})`);\n }\n if (rule.count != null && rule.count < 0) {\n throw new Error(`AnchoredRule.count must be >= 0 (got ${rule.count})`);\n }\n\n const seriesEnd = rule.seriesEnd ?? Number.POSITIVE_INFINITY;\n const effectiveEnd = Math.min(windowEnd, seriesEnd);\n const effectiveStart = Math.max(windowStart, rule.seriesStart);\n\n if (effectiveStart >= effectiveEnd) return [];\n\n // If `count` is set we must start counting from the anchor; otherwise we\n // can fast-forward to the window start.\n let index: number;\n let occurrenceStart: number;\n if (rule.count != null) {\n index = 0;\n occurrenceStart = rule.anchorDate;\n } else {\n if (effectiveStart <= rule.anchorDate) {\n index = 0;\n occurrenceStart = rule.anchorDate;\n } else {\n // First occurrence at or after effectiveStart.\n const stepsAhead = Math.ceil((effectiveStart - rule.anchorDate) / rule.intervalMs);\n index = stepsAhead;\n occurrenceStart = rule.anchorDate + stepsAhead * rule.intervalMs;\n }\n }\n\n const out: Occurrence[] = [];\n while (occurrenceStart < effectiveEnd) {\n if (rule.count != null && index >= rule.count) break;\n\n const occEnd = computeSpanEnd(occurrenceStart, rule.timeSpan, tz);\n if (\n intersects(occurrenceStart, occEnd, windowStart, windowEnd) &&\n occurrenceStart >= rule.seriesStart\n ) {\n out.push({ startTimestamp: occurrenceStart, endTimestamp: occEnd });\n if (out.length > MAX_OCCURRENCES_PER_GENERATE) {\n throw new TooManyOccurrencesError(\"anchored\");\n }\n }\n index++;\n occurrenceStart += rule.intervalMs;\n }\n return out;\n}\n"],"mappings":";;;AAYA,SAAgB,iBACd,MACA,IACA,aACA,WACc;CACd,IAAI,KAAK,cAAc,GACrB,MAAM,IAAI,MAAM,4CAA4C,KAAK,WAAW,EAAE;CAEhF,IAAI,KAAK,SAAS,QAAQ,KAAK,QAAQ,GACrC,MAAM,IAAI,MAAM,wCAAwC,KAAK,MAAM,EAAE;CAGvE,MAAM,YAAY,KAAK,aAAa,OAAO;CAC3C,MAAM,eAAe,KAAK,IAAI,WAAW,SAAS;CAClD,MAAM,iBAAiB,KAAK,IAAI,aAAa,KAAK,WAAW;CAE7D,IAAI,kBAAkB,cAAc,OAAO,CAAC;CAI5C,IAAI;CACJ,IAAI;CACJ,IAAI,KAAK,SAAS,MAAM;EACtB,QAAQ;EACR,kBAAkB,KAAK;CACzB,OACE,IAAI,kBAAkB,KAAK,YAAY;EACrC,QAAQ;EACR,kBAAkB,KAAK;CACzB,OAAO;EAEL,MAAM,aAAa,KAAK,MAAM,iBAAiB,KAAK,cAAc,KAAK,UAAU;EACjF,QAAQ;EACR,kBAAkB,KAAK,aAAa,aAAa,KAAK;CACxD;CAGF,MAAM,MAAoB,CAAC;CAC3B,OAAO,kBAAkB,cAAc;EACrC,IAAI,KAAK,SAAS,QAAQ,SAAS,KAAK,OAAO;EAE/C,MAAM,SAAS,eAAe,iBAAiB,KAAK,UAAU,EAAE;EAChE,IACE,WAAW,iBAAiB,QAAQ,aAAa,SAAS,KAC1D,mBAAmB,KAAK,aACxB;GACA,IAAI,KAAK;IAAE,gBAAgB;IAAiB,cAAc;GAAO,CAAC;GAClE,IAAI,IAAI,SAAA,KACN,MAAM,IAAI,wBAAwB,UAAU;EAEhD;EACA;EACA,mBAAmB,KAAK;CAC1B;CACA,OAAO;AACT"}
@@ -0,0 +1,4 @@
1
+ import { DaysOfMonthRule, Disambiguation } from '../types.js';
2
+ import { Occurrence } from './types.js';
3
+ export declare function generateDaysOfMonth(rule: DaysOfMonthRule, tz: string | undefined, windowStart: number, windowEnd: number, disambiguation?: Disambiguation): Occurrence[];
4
+ //# sourceMappingURL=days_of_month.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"days_of_month.d.ts","sourceRoot":"","sources":["../../src/generators/days_of_month.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAQnE,OAAO,EAEL,KAAK,UAAU,EAGhB,MAAM,YAAY,CAAC;AAIpB,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,eAAe,EACrB,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,cAAc,GAAE,cAA6B,GAC5C,UAAU,EAAE,CA4Ed"}
@@ -0,0 +1,53 @@
1
+ import { computeSpanEnd, isNonexistentWallTime, monthLength, utcToWallParts, wallTimeToUtc } from "../tz.js";
2
+ import { TooManyOccurrencesError, intersects } from "./types.js";
3
+ // src/generators/days_of_month.ts
4
+ function generateDaysOfMonth(rule, tz, windowStart, windowEnd, disambiguation = "compatible") {
5
+ if (rule.yearInterval <= 0) throw new Error(`DaysOfMonthRule.yearInterval must be > 0 (got ${rule.yearInterval})`);
6
+ if (rule.months.length === 0) throw new Error("DaysOfMonthRule.months must be non-empty");
7
+ for (const m of rule.months) if (m < 0 || m > 11) throw new Error(`DaysOfMonthRule.months entries must be in 0..11 (got ${m})`);
8
+ if (rule.daysOfMonth.length === 0) throw new Error("DaysOfMonthRule.daysOfMonth must be non-empty");
9
+ for (const d of rule.daysOfMonth) if (d < 1 || d > 31) throw new Error(`DaysOfMonthRule.daysOfMonth entries must be in 1..31 (got ${d})`);
10
+ if (rule.count != null && rule.count < 0) throw new Error(`DaysOfMonthRule.count must be >= 0 (got ${rule.count})`);
11
+ const seriesEnd = rule.seriesEnd ?? Number.POSITIVE_INFINITY;
12
+ const effectiveEnd = Math.min(windowEnd, seriesEnd);
13
+ if (rule.seriesStart >= effectiveEnd) return [];
14
+ const months = [...new Set(rule.months)].sort((a, b) => a - b);
15
+ const daysOfMonth = [...new Set(rule.daysOfMonth)].sort((a, b) => a - b);
16
+ const startYear = utcToWallParts(rule.seriesStart, tz).year;
17
+ const endYear = utcToWallParts(Math.min(effectiveEnd, Number.MAX_SAFE_INTEGER), tz).year;
18
+ const out = [];
19
+ let count = 0;
20
+ for (let year = startYear; year <= endYear; year += rule.yearInterval) {
21
+ const yearEmits = [];
22
+ for (const month of months) {
23
+ const len = monthLength(year, month);
24
+ const clampedDays = [...new Set(daysOfMonth.map((d) => Math.min(d, len)))];
25
+ for (const day of clampedDays) {
26
+ if (disambiguation === "skip" && isNonexistentWallTime(year, month, day, rule.timeOfDay, tz)) continue;
27
+ const occStart = wallTimeToUtc(year, month, day, rule.timeOfDay, tz, disambiguation);
28
+ const occEnd = computeSpanEnd(occStart, rule.timeSpan, tz);
29
+ if (occStart < rule.seriesStart) continue;
30
+ if (occStart >= seriesEnd) continue;
31
+ if (rule.count != null && count >= rule.count) {
32
+ yearEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);
33
+ out.push(...yearEmits);
34
+ return out;
35
+ }
36
+ count++;
37
+ if (intersects(occStart, occEnd, windowStart, windowEnd)) {
38
+ yearEmits.push({
39
+ startTimestamp: occStart,
40
+ endTimestamp: occEnd
41
+ });
42
+ if (out.length + yearEmits.length > 1e4) throw new TooManyOccurrencesError("daysOfMonth");
43
+ }
44
+ }
45
+ }
46
+ yearEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);
47
+ out.push(...yearEmits);
48
+ }
49
+ return out;
50
+ }
51
+ export { generateDaysOfMonth };
52
+
53
+ //# sourceMappingURL=days_of_month.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"days_of_month.js","names":[],"sources":["../../src/generators/days_of_month.ts"],"sourcesContent":["import type { DaysOfMonthRule, Disambiguation } from \"../types.js\";\nimport {\n computeSpanEnd,\n isNonexistentWallTime,\n monthLength,\n utcToWallParts,\n wallTimeToUtc,\n} from \"../tz.js\";\nimport {\n MAX_OCCURRENCES_PER_GENERATE,\n type Occurrence,\n TooManyOccurrencesError,\n intersects,\n} from \"./types.js\";\n\n// Numerical date-based recurrence: months × daysOfMonth. Days clamp per\n// month (day 31 in April → Apr 30, day 29 in Feb non-leap → Feb 28).\nexport function generateDaysOfMonth(\n rule: DaysOfMonthRule,\n tz: string | undefined,\n windowStart: number,\n windowEnd: number,\n disambiguation: Disambiguation = \"compatible\"\n): Occurrence[] {\n if (rule.yearInterval <= 0) {\n throw new Error(`DaysOfMonthRule.yearInterval must be > 0 (got ${rule.yearInterval})`);\n }\n if (rule.months.length === 0) {\n throw new Error(\"DaysOfMonthRule.months must be non-empty\");\n }\n for (const m of rule.months) {\n if (m < 0 || m > 11) {\n throw new Error(`DaysOfMonthRule.months entries must be in 0..11 (got ${m})`);\n }\n }\n if (rule.daysOfMonth.length === 0) {\n throw new Error(\"DaysOfMonthRule.daysOfMonth must be non-empty\");\n }\n for (const d of rule.daysOfMonth) {\n if (d < 1 || d > 31) {\n throw new Error(`DaysOfMonthRule.daysOfMonth entries must be in 1..31 (got ${d})`);\n }\n }\n if (rule.count != null && rule.count < 0) {\n throw new Error(`DaysOfMonthRule.count must be >= 0 (got ${rule.count})`);\n }\n\n const seriesEnd = rule.seriesEnd ?? Number.POSITIVE_INFINITY;\n const effectiveEnd = Math.min(windowEnd, seriesEnd);\n if (rule.seriesStart >= effectiveEnd) return [];\n\n const months = [...new Set(rule.months)].sort((a, b) => a - b);\n const daysOfMonth = [...new Set(rule.daysOfMonth)].sort((a, b) => a - b);\n\n const startYear = utcToWallParts(rule.seriesStart, tz).year;\n const endYear = utcToWallParts(Math.min(effectiveEnd, Number.MAX_SAFE_INTEGER), tz).year;\n\n const out: Occurrence[] = [];\n let count = 0;\n\n for (let year = startYear; year <= endYear; year += rule.yearInterval) {\n const yearEmits: Occurrence[] = [];\n\n for (const month of months) {\n const len = monthLength(year, month);\n // Dedup clamped days so day-31 + day-30 in April don't both emit Apr 30.\n const clampedDays = [...new Set(daysOfMonth.map((d) => Math.min(d, len)))];\n for (const day of clampedDays) {\n if (\n disambiguation === \"skip\" &&\n isNonexistentWallTime(year, month, day, rule.timeOfDay, tz)\n ) {\n continue;\n }\n const occStart = wallTimeToUtc(year, month, day, rule.timeOfDay, tz, disambiguation);\n const occEnd = computeSpanEnd(occStart, rule.timeSpan, tz);\n\n if (occStart < rule.seriesStart) continue;\n if (occStart >= seriesEnd) continue;\n\n if (rule.count != null && count >= rule.count) {\n yearEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);\n out.push(...yearEmits);\n return out;\n }\n count++;\n\n if (intersects(occStart, occEnd, windowStart, windowEnd)) {\n yearEmits.push({ startTimestamp: occStart, endTimestamp: occEnd });\n if (out.length + yearEmits.length > MAX_OCCURRENCES_PER_GENERATE) {\n throw new TooManyOccurrencesError(\"daysOfMonth\");\n }\n }\n }\n }\n yearEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);\n out.push(...yearEmits);\n }\n return out;\n}\n"],"mappings":";;;AAiBA,SAAgB,oBACd,MACA,IACA,aACA,WACA,iBAAiC,cACnB;CACd,IAAI,KAAK,gBAAgB,GACvB,MAAM,IAAI,MAAM,iDAAiD,KAAK,aAAa,EAAE;CAEvF,IAAI,KAAK,OAAO,WAAW,GACzB,MAAM,IAAI,MAAM,0CAA0C;CAE5D,KAAK,MAAM,KAAK,KAAK,QACnB,IAAI,IAAI,KAAK,IAAI,IACf,MAAM,IAAI,MAAM,wDAAwD,EAAE,EAAE;CAGhF,IAAI,KAAK,YAAY,WAAW,GAC9B,MAAM,IAAI,MAAM,+CAA+C;CAEjE,KAAK,MAAM,KAAK,KAAK,aACnB,IAAI,IAAI,KAAK,IAAI,IACf,MAAM,IAAI,MAAM,6DAA6D,EAAE,EAAE;CAGrF,IAAI,KAAK,SAAS,QAAQ,KAAK,QAAQ,GACrC,MAAM,IAAI,MAAM,2CAA2C,KAAK,MAAM,EAAE;CAG1E,MAAM,YAAY,KAAK,aAAa,OAAO;CAC3C,MAAM,eAAe,KAAK,IAAI,WAAW,SAAS;CAClD,IAAI,KAAK,eAAe,cAAc,OAAO,CAAC;CAE9C,MAAM,SAAS,CAAC,GAAG,IAAI,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CAC7D,MAAM,cAAc,CAAC,GAAG,IAAI,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CAEvE,MAAM,YAAY,eAAe,KAAK,aAAa,EAAE,CAAC,CAAC;CACvD,MAAM,UAAU,eAAe,KAAK,IAAI,cAAc,OAAO,gBAAgB,GAAG,EAAE,CAAC,CAAC;CAEpF,MAAM,MAAoB,CAAC;CAC3B,IAAI,QAAQ;CAEZ,KAAK,IAAI,OAAO,WAAW,QAAQ,SAAS,QAAQ,KAAK,cAAc;EACrE,MAAM,YAA0B,CAAC;EAEjC,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,MAAM,YAAY,MAAM,KAAK;GAEnC,MAAM,cAAc,CAAC,GAAG,IAAI,IAAI,YAAY,KAAK,MAAM,KAAK,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC;GACzE,KAAK,MAAM,OAAO,aAAa;IAC7B,IACE,mBAAmB,UACnB,sBAAsB,MAAM,OAAO,KAAK,KAAK,WAAW,EAAE,GAE1D;IAEF,MAAM,WAAW,cAAc,MAAM,OAAO,KAAK,KAAK,WAAW,IAAI,cAAc;IACnF,MAAM,SAAS,eAAe,UAAU,KAAK,UAAU,EAAE;IAEzD,IAAI,WAAW,KAAK,aAAa;IACjC,IAAI,YAAY,WAAW;IAE3B,IAAI,KAAK,SAAS,QAAQ,SAAS,KAAK,OAAO;KAC7C,UAAU,MAAM,GAAG,MAAM,EAAE,iBAAiB,EAAE,cAAc;KAC5D,IAAI,KAAK,GAAG,SAAS;KACrB,OAAO;IACT;IACA;IAEA,IAAI,WAAW,UAAU,QAAQ,aAAa,SAAS,GAAG;KACxD,UAAU,KAAK;MAAE,gBAAgB;MAAU,cAAc;KAAO,CAAC;KACjE,IAAI,IAAI,SAAS,UAAU,SAAA,KACzB,MAAM,IAAI,wBAAwB,aAAa;IAEnD;GACF;EACF;EACA,UAAU,MAAM,GAAG,MAAM,EAAE,iBAAiB,EAAE,cAAc;EAC5D,IAAI,KAAK,GAAG,SAAS;CACvB;CACA,OAAO;AACT"}
@@ -0,0 +1,6 @@
1
+ import { DaysOfWeekRule, Disambiguation } from '../types.js';
2
+ import { dayOfWeekInTz } from '../tz.js';
3
+ import { Occurrence } from './types.js';
4
+ export declare function generateDaysOfWeek(rule: DaysOfWeekRule, tz: string | undefined, windowStart: number, windowEnd: number, disambiguation?: Disambiguation): Occurrence[];
5
+ export { dayOfWeekInTz };
6
+ //# sourceMappingURL=days_of_week.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"days_of_week.d.ts","sourceRoot":"","sources":["../../src/generators/days_of_week.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAGL,aAAa,EAId,MAAM,UAAU,CAAC;AAClB,OAAO,EAEL,KAAK,UAAU,EAGhB,MAAM,YAAY,CAAC;AAKpB,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,cAAc,GAAE,cAA6B,GAC5C,UAAU,EAAE,CAwEd;AAGD,OAAO,EAAE,aAAa,EAAE,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { addDaysInTz, computeSpanEnd, isNonexistentWallTime, utcToWallParts, wallTimeToUtc } from "../tz.js";
2
+ import { TooManyOccurrencesError, intersects } from "./types.js";
3
+ // src/generators/days_of_week.ts
4
+ function generateDaysOfWeek(rule, tz, windowStart, windowEnd, disambiguation = "compatible") {
5
+ if (rule.weekInterval <= 0) throw new Error(`DaysOfWeekRule.weekInterval must be > 0 (got ${rule.weekInterval})`);
6
+ if (rule.days.length === 0) throw new Error("DaysOfWeekRule.days must be non-empty");
7
+ if (rule.count != null && rule.count < 0) throw new Error(`DaysOfWeekRule.count must be >= 0 (got ${rule.count})`);
8
+ const seriesEnd = rule.seriesEnd ?? Number.POSITIVE_INFINITY;
9
+ const effectiveEnd = Math.min(windowEnd, seriesEnd);
10
+ if (rule.seriesStart >= effectiveEnd) return [];
11
+ const days = [...rule.days].sort((a, b) => a - b);
12
+ const seriesStartParts = utcToWallParts(rule.seriesStart, tz);
13
+ const daysSinceSunday = seriesStartParts.dayOfWeek;
14
+ const weekAnchor = wallTimeToUtc(seriesStartParts.year, seriesStartParts.month, seriesStartParts.day - daysSinceSunday, 0, tz);
15
+ const out = [];
16
+ let count = 0;
17
+ let weekStart = weekAnchor;
18
+ while (weekStart < effectiveEnd) {
19
+ for (const dow of days) {
20
+ const dayUtc = addDaysInTz(weekStart, dow, tz);
21
+ const dayParts = utcToWallParts(dayUtc, tz);
22
+ if (disambiguation === "skip" && isNonexistentWallTime(dayParts.year, dayParts.month, dayParts.day, rule.timeOfDay, tz)) continue;
23
+ const occStart = wallTimeToUtc(dayParts.year, dayParts.month, dayParts.day, rule.timeOfDay, tz, disambiguation);
24
+ const occEnd = computeSpanEnd(occStart, rule.timeSpan, tz);
25
+ if (occStart < rule.seriesStart) continue;
26
+ if (occStart >= seriesEnd) continue;
27
+ if (rule.count != null && count >= rule.count) return out;
28
+ count++;
29
+ if (intersects(occStart, occEnd, windowStart, windowEnd)) {
30
+ out.push({
31
+ startTimestamp: occStart,
32
+ endTimestamp: occEnd
33
+ });
34
+ if (out.length > 1e4) throw new TooManyOccurrencesError("daysOfWeek");
35
+ }
36
+ }
37
+ weekStart = addDaysInTz(weekStart, 7 * rule.weekInterval, tz);
38
+ }
39
+ return out;
40
+ }
41
+ export { generateDaysOfWeek };
42
+
43
+ //# sourceMappingURL=days_of_week.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"days_of_week.js","names":[],"sources":["../../src/generators/days_of_week.ts"],"sourcesContent":["import type { DaysOfWeekRule, Disambiguation } from \"../types.js\";\nimport {\n addDaysInTz,\n computeSpanEnd,\n dayOfWeekInTz,\n isNonexistentWallTime,\n utcToWallParts,\n wallTimeToUtc,\n} from \"../tz.js\";\nimport {\n MAX_OCCURRENCES_PER_GENERATE,\n type Occurrence,\n TooManyOccurrencesError,\n intersects,\n} from \"./types.js\";\n\n// Iterate week-by-week (in wall time, in tz) starting from seriesStart's\n// week. For each scheduled week, emit the occurrences that fall on the\n// rule's days.\nexport function generateDaysOfWeek(\n rule: DaysOfWeekRule,\n tz: string | undefined,\n windowStart: number,\n windowEnd: number,\n disambiguation: Disambiguation = \"compatible\"\n): Occurrence[] {\n if (rule.weekInterval <= 0) {\n throw new Error(`DaysOfWeekRule.weekInterval must be > 0 (got ${rule.weekInterval})`);\n }\n if (rule.days.length === 0) {\n throw new Error(\"DaysOfWeekRule.days must be non-empty\");\n }\n if (rule.count != null && rule.count < 0) {\n throw new Error(`DaysOfWeekRule.count must be >= 0 (got ${rule.count})`);\n }\n\n const seriesEnd = rule.seriesEnd ?? Number.POSITIVE_INFINITY;\n const effectiveEnd = Math.min(windowEnd, seriesEnd);\n if (rule.seriesStart >= effectiveEnd) return [];\n\n // Sort days for deterministic emit order within a week.\n const days = [...rule.days].sort((a, b) => a - b);\n\n // Anchor: snap seriesStart to the start of its week (Sunday) in tz.\n const seriesStartParts = utcToWallParts(rule.seriesStart, tz);\n const daysSinceSunday = seriesStartParts.dayOfWeek;\n const weekAnchor = wallTimeToUtc(\n seriesStartParts.year,\n seriesStartParts.month,\n seriesStartParts.day - daysSinceSunday,\n 0,\n tz\n );\n\n const out: Occurrence[] = [];\n let count = 0;\n // Step by `weekInterval` weeks at a time. Use addDaysInTz to handle DST\n // correctly (a week is 7 calendar days, not 7*86400000 ms).\n let weekStart = weekAnchor;\n while (weekStart < effectiveEnd) {\n for (const dow of days) {\n // Compute the day-of-week within this week in tz wall time.\n const dayUtc = addDaysInTz(weekStart, dow, tz);\n const dayParts = utcToWallParts(dayUtc, tz);\n if (\n disambiguation === \"skip\" &&\n isNonexistentWallTime(dayParts.year, dayParts.month, dayParts.day, rule.timeOfDay, tz)\n ) {\n continue;\n }\n const occStart = wallTimeToUtc(\n dayParts.year,\n dayParts.month,\n dayParts.day,\n rule.timeOfDay,\n tz,\n disambiguation\n );\n const occEnd = computeSpanEnd(occStart, rule.timeSpan, tz);\n\n // Must be on/after seriesStart and before seriesEnd.\n if (occStart < rule.seriesStart) continue;\n if (occStart >= seriesEnd) continue;\n\n if (rule.count != null && count >= rule.count) return out;\n count++;\n\n if (intersects(occStart, occEnd, windowStart, windowEnd)) {\n out.push({ startTimestamp: occStart, endTimestamp: occEnd });\n if (out.length > MAX_OCCURRENCES_PER_GENERATE) {\n throw new TooManyOccurrencesError(\"daysOfWeek\");\n }\n }\n }\n weekStart = addDaysInTz(weekStart, 7 * rule.weekInterval, tz);\n }\n return out;\n}\n\n// Re-export so consumers can sanity-check the rule's first day in tz.\nexport { dayOfWeekInTz };\n"],"mappings":";;;AAmBA,SAAgB,mBACd,MACA,IACA,aACA,WACA,iBAAiC,cACnB;CACd,IAAI,KAAK,gBAAgB,GACvB,MAAM,IAAI,MAAM,gDAAgD,KAAK,aAAa,EAAE;CAEtF,IAAI,KAAK,KAAK,WAAW,GACvB,MAAM,IAAI,MAAM,uCAAuC;CAEzD,IAAI,KAAK,SAAS,QAAQ,KAAK,QAAQ,GACrC,MAAM,IAAI,MAAM,0CAA0C,KAAK,MAAM,EAAE;CAGzE,MAAM,YAAY,KAAK,aAAa,OAAO;CAC3C,MAAM,eAAe,KAAK,IAAI,WAAW,SAAS;CAClD,IAAI,KAAK,eAAe,cAAc,OAAO,CAAC;CAG9C,MAAM,OAAO,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CAGhD,MAAM,mBAAmB,eAAe,KAAK,aAAa,EAAE;CAC5D,MAAM,kBAAkB,iBAAiB;CACzC,MAAM,aAAa,cACjB,iBAAiB,MACjB,iBAAiB,OACjB,iBAAiB,MAAM,iBACvB,GACA,EACF;CAEA,MAAM,MAAoB,CAAC;CAC3B,IAAI,QAAQ;CAGZ,IAAI,YAAY;CAChB,OAAO,YAAY,cAAc;EAC/B,KAAK,MAAM,OAAO,MAAM;GAEtB,MAAM,SAAS,YAAY,WAAW,KAAK,EAAE;GAC7C,MAAM,WAAW,eAAe,QAAQ,EAAE;GAC1C,IACE,mBAAmB,UACnB,sBAAsB,SAAS,MAAM,SAAS,OAAO,SAAS,KAAK,KAAK,WAAW,EAAE,GAErF;GAEF,MAAM,WAAW,cACf,SAAS,MACT,SAAS,OACT,SAAS,KACT,KAAK,WACL,IACA,cACF;GACA,MAAM,SAAS,eAAe,UAAU,KAAK,UAAU,EAAE;GAGzD,IAAI,WAAW,KAAK,aAAa;GACjC,IAAI,YAAY,WAAW;GAE3B,IAAI,KAAK,SAAS,QAAQ,SAAS,KAAK,OAAO,OAAO;GACtD;GAEA,IAAI,WAAW,UAAU,QAAQ,aAAa,SAAS,GAAG;IACxD,IAAI,KAAK;KAAE,gBAAgB;KAAU,cAAc;IAAO,CAAC;IAC3D,IAAI,IAAI,SAAA,KACN,MAAM,IAAI,wBAAwB,YAAY;GAElD;EACF;EACA,YAAY,YAAY,WAAW,IAAI,KAAK,cAAc,EAAE;CAC9D;CACA,OAAO;AACT"}
@@ -0,0 +1,4 @@
1
+ import { DaysOfYearRule, Disambiguation } from '../types.js';
2
+ import { Occurrence } from './types.js';
3
+ export declare function generateDaysOfYear(rule: DaysOfYearRule, tz: string | undefined, windowStart: number, windowEnd: number, disambiguation?: Disambiguation): Occurrence[];
4
+ //# sourceMappingURL=days_of_year.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"days_of_year.d.ts","sourceRoot":"","sources":["../../src/generators/days_of_year.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElE,OAAO,EAEL,KAAK,UAAU,EAGhB,MAAM,YAAY,CAAC;AAKpB,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,cAAc,GAAE,cAA6B,GAC5C,UAAU,EAAE,CAqEd"}
@@ -0,0 +1,50 @@
1
+ import { computeSpanEnd, isNonexistentWallTime, utcToWallParts, wallTimeToUtc } from "../tz.js";
2
+ import { TooManyOccurrencesError, intersects } from "./types.js";
3
+ // src/generators/days_of_year.ts
4
+ function generateDaysOfYear(rule, tz, windowStart, windowEnd, disambiguation = "compatible") {
5
+ if (rule.yearInterval <= 0) throw new Error(`DaysOfYearRule.yearInterval must be > 0 (got ${rule.yearInterval})`);
6
+ if (rule.daysOfYear.length === 0) throw new Error("DaysOfYearRule.daysOfYear must be non-empty");
7
+ for (const d of rule.daysOfYear) if (d < 1 || d > 366) throw new Error(`DaysOfYearRule.daysOfYear entries must be in 1..366 (got ${d})`);
8
+ if (rule.count != null && rule.count < 0) throw new Error(`DaysOfYearRule.count must be >= 0 (got ${rule.count})`);
9
+ const seriesEnd = rule.seriesEnd ?? Number.POSITIVE_INFINITY;
10
+ const effectiveEnd = Math.min(windowEnd, seriesEnd);
11
+ if (rule.seriesStart >= effectiveEnd) return [];
12
+ const days = [...new Set(rule.daysOfYear)].sort((a, b) => a - b);
13
+ const startYear = utcToWallParts(rule.seriesStart, tz).year;
14
+ const endYear = utcToWallParts(Math.min(effectiveEnd, Number.MAX_SAFE_INTEGER), tz).year;
15
+ const out = [];
16
+ let count = 0;
17
+ for (let year = startYear; year <= endYear; year += rule.yearInterval) {
18
+ const yearEmits = [];
19
+ for (const dayOfYear of days) {
20
+ const probe = new Date(Date.UTC(year, 0, dayOfYear));
21
+ if (probe.getUTCFullYear() !== year) continue;
22
+ const month = probe.getUTCMonth();
23
+ const day = probe.getUTCDate();
24
+ if (disambiguation === "skip" && isNonexistentWallTime(year, month, day, rule.timeOfDay, tz)) continue;
25
+ const occStart = wallTimeToUtc(year, month, day, rule.timeOfDay, tz, disambiguation);
26
+ const occEnd = computeSpanEnd(occStart, rule.timeSpan, tz);
27
+ if (occStart < rule.seriesStart) continue;
28
+ if (occStart >= seriesEnd) continue;
29
+ if (rule.count != null && count >= rule.count) {
30
+ yearEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);
31
+ out.push(...yearEmits);
32
+ return out;
33
+ }
34
+ count++;
35
+ if (intersects(occStart, occEnd, windowStart, windowEnd)) {
36
+ yearEmits.push({
37
+ startTimestamp: occStart,
38
+ endTimestamp: occEnd
39
+ });
40
+ if (out.length + yearEmits.length > 1e4) throw new TooManyOccurrencesError("daysOfYear");
41
+ }
42
+ }
43
+ yearEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);
44
+ out.push(...yearEmits);
45
+ }
46
+ return out;
47
+ }
48
+ export { generateDaysOfYear };
49
+
50
+ //# sourceMappingURL=days_of_year.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"days_of_year.js","names":[],"sources":["../../src/generators/days_of_year.ts"],"sourcesContent":["import type { DaysOfYearRule, Disambiguation } from \"../types.js\";\nimport { computeSpanEnd, isNonexistentWallTime, utcToWallParts, wallTimeToUtc } from \"../tz.js\";\nimport {\n MAX_OCCURRENCES_PER_GENERATE,\n type Occurrence,\n TooManyOccurrencesError,\n intersects,\n} from \"./types.js\";\n\n// Day-of-year recurrence. Day 1 = Jan 1. Day 366 only exists in leap\n// years and is SKIPPED in non-leap years (no clamp — \"366th day\" means\n// literally that).\nexport function generateDaysOfYear(\n rule: DaysOfYearRule,\n tz: string | undefined,\n windowStart: number,\n windowEnd: number,\n disambiguation: Disambiguation = \"compatible\"\n): Occurrence[] {\n if (rule.yearInterval <= 0) {\n throw new Error(`DaysOfYearRule.yearInterval must be > 0 (got ${rule.yearInterval})`);\n }\n if (rule.daysOfYear.length === 0) {\n throw new Error(\"DaysOfYearRule.daysOfYear must be non-empty\");\n }\n for (const d of rule.daysOfYear) {\n if (d < 1 || d > 366) {\n throw new Error(`DaysOfYearRule.daysOfYear entries must be in 1..366 (got ${d})`);\n }\n }\n if (rule.count != null && rule.count < 0) {\n throw new Error(`DaysOfYearRule.count must be >= 0 (got ${rule.count})`);\n }\n\n const seriesEnd = rule.seriesEnd ?? Number.POSITIVE_INFINITY;\n const effectiveEnd = Math.min(windowEnd, seriesEnd);\n if (rule.seriesStart >= effectiveEnd) return [];\n\n const days = [...new Set(rule.daysOfYear)].sort((a, b) => a - b);\n\n const startYear = utcToWallParts(rule.seriesStart, tz).year;\n const endYear = utcToWallParts(Math.min(effectiveEnd, Number.MAX_SAFE_INTEGER), tz).year;\n\n const out: Occurrence[] = [];\n let count = 0;\n\n for (let year = startYear; year <= endYear; year += rule.yearInterval) {\n const yearEmits: Occurrence[] = [];\n\n for (const dayOfYear of days) {\n // Date.UTC(year, 0, N) treats N as \"Nth day from Jan 1\". If N is 366\n // in a non-leap year it rolls into year+1; detect and skip.\n const probe = new Date(Date.UTC(year, 0, dayOfYear));\n if (probe.getUTCFullYear() !== year) continue; // day 366 in non-leap year\n\n const month = probe.getUTCMonth();\n const day = probe.getUTCDate();\n if (\n disambiguation === \"skip\" &&\n isNonexistentWallTime(year, month, day, rule.timeOfDay, tz)\n ) {\n continue;\n }\n const occStart = wallTimeToUtc(year, month, day, rule.timeOfDay, tz, disambiguation);\n const occEnd = computeSpanEnd(occStart, rule.timeSpan, tz);\n\n if (occStart < rule.seriesStart) continue;\n if (occStart >= seriesEnd) continue;\n\n if (rule.count != null && count >= rule.count) {\n yearEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);\n out.push(...yearEmits);\n return out;\n }\n count++;\n\n if (intersects(occStart, occEnd, windowStart, windowEnd)) {\n yearEmits.push({ startTimestamp: occStart, endTimestamp: occEnd });\n if (out.length + yearEmits.length > MAX_OCCURRENCES_PER_GENERATE) {\n throw new TooManyOccurrencesError(\"daysOfYear\");\n }\n }\n }\n yearEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);\n out.push(...yearEmits);\n }\n return out;\n}\n"],"mappings":";;;AAYA,SAAgB,mBACd,MACA,IACA,aACA,WACA,iBAAiC,cACnB;CACd,IAAI,KAAK,gBAAgB,GACvB,MAAM,IAAI,MAAM,gDAAgD,KAAK,aAAa,EAAE;CAEtF,IAAI,KAAK,WAAW,WAAW,GAC7B,MAAM,IAAI,MAAM,6CAA6C;CAE/D,KAAK,MAAM,KAAK,KAAK,YACnB,IAAI,IAAI,KAAK,IAAI,KACf,MAAM,IAAI,MAAM,4DAA4D,EAAE,EAAE;CAGpF,IAAI,KAAK,SAAS,QAAQ,KAAK,QAAQ,GACrC,MAAM,IAAI,MAAM,0CAA0C,KAAK,MAAM,EAAE;CAGzE,MAAM,YAAY,KAAK,aAAa,OAAO;CAC3C,MAAM,eAAe,KAAK,IAAI,WAAW,SAAS;CAClD,IAAI,KAAK,eAAe,cAAc,OAAO,CAAC;CAE9C,MAAM,OAAO,CAAC,GAAG,IAAI,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CAE/D,MAAM,YAAY,eAAe,KAAK,aAAa,EAAE,CAAC,CAAC;CACvD,MAAM,UAAU,eAAe,KAAK,IAAI,cAAc,OAAO,gBAAgB,GAAG,EAAE,CAAC,CAAC;CAEpF,MAAM,MAAoB,CAAC;CAC3B,IAAI,QAAQ;CAEZ,KAAK,IAAI,OAAO,WAAW,QAAQ,SAAS,QAAQ,KAAK,cAAc;EACrE,MAAM,YAA0B,CAAC;EAEjC,KAAK,MAAM,aAAa,MAAM;GAG5B,MAAM,QAAQ,IAAI,KAAK,KAAK,IAAI,MAAM,GAAG,SAAS,CAAC;GACnD,IAAI,MAAM,eAAe,MAAM,MAAM;GAErC,MAAM,QAAQ,MAAM,YAAY;GAChC,MAAM,MAAM,MAAM,WAAW;GAC7B,IACE,mBAAmB,UACnB,sBAAsB,MAAM,OAAO,KAAK,KAAK,WAAW,EAAE,GAE1D;GAEF,MAAM,WAAW,cAAc,MAAM,OAAO,KAAK,KAAK,WAAW,IAAI,cAAc;GACnF,MAAM,SAAS,eAAe,UAAU,KAAK,UAAU,EAAE;GAEzD,IAAI,WAAW,KAAK,aAAa;GACjC,IAAI,YAAY,WAAW;GAE3B,IAAI,KAAK,SAAS,QAAQ,SAAS,KAAK,OAAO;IAC7C,UAAU,MAAM,GAAG,MAAM,EAAE,iBAAiB,EAAE,cAAc;IAC5D,IAAI,KAAK,GAAG,SAAS;IACrB,OAAO;GACT;GACA;GAEA,IAAI,WAAW,UAAU,QAAQ,aAAa,SAAS,GAAG;IACxD,UAAU,KAAK;KAAE,gBAAgB;KAAU,cAAc;IAAO,CAAC;IACjE,IAAI,IAAI,SAAS,UAAU,SAAA,KACzB,MAAM,IAAI,wBAAwB,YAAY;GAElD;EACF;EACA,UAAU,MAAM,GAAG,MAAM,EAAE,iBAAiB,EAAE,cAAc;EAC5D,IAAI,KAAK,GAAG,SAAS;CACvB;CACA,OAAO;AACT"}
@@ -0,0 +1,4 @@
1
+ import { InstantRule } from '../types.js';
2
+ import { Occurrence } from './types.js';
3
+ export declare function generateInstant(rule: InstantRule, tz: string | undefined, windowStart: number, windowEnd: number): Occurrence[];
4
+ //# sourceMappingURL=instant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instant.d.ts","sourceRoot":"","sources":["../../src/generators/instant.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,KAAK,UAAU,EAAc,MAAM,YAAY,CAAC;AAKzD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,WAAW,EACjB,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,UAAU,EAAE,CAKd"}
@@ -0,0 +1,15 @@
1
+ import { computeSpanEnd } from "../tz.js";
2
+ import { intersects } from "./types.js";
3
+ // src/generators/instant.ts
4
+ function generateInstant(rule, tz, windowStart, windowEnd) {
5
+ const start = rule.startOn;
6
+ const end = computeSpanEnd(start, rule.timeSpan, tz);
7
+ if (!intersects(start, end, windowStart, windowEnd)) return [];
8
+ return [{
9
+ startTimestamp: start,
10
+ endTimestamp: end
11
+ }];
12
+ }
13
+ export { generateInstant };
14
+
15
+ //# sourceMappingURL=instant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instant.js","names":[],"sources":["../../src/generators/instant.ts"],"sourcesContent":["import type { InstantRule } from \"../types.js\";\nimport { computeSpanEnd } from \"../tz.js\";\nimport { type Occurrence, intersects } from \"./types.js\";\n\n// A single occurrence at an absolute UTC instant. The template timezone does\n// not affect the start (the instant is already a fixed point on the timeline);\n// it only flexes an hourSpan end, via computeSpanEnd.\nexport function generateInstant(\n rule: InstantRule,\n tz: string | undefined,\n windowStart: number,\n windowEnd: number\n): Occurrence[] {\n const start = rule.startOn;\n const end = computeSpanEnd(start, rule.timeSpan, tz);\n if (!intersects(start, end, windowStart, windowEnd)) return [];\n return [{ startTimestamp: start, endTimestamp: end }];\n}\n"],"mappings":";;;AAOA,SAAgB,gBACd,MACA,IACA,aACA,WACc;CACd,MAAM,QAAQ,KAAK;CACnB,MAAM,MAAM,eAAe,OAAO,KAAK,UAAU,EAAE;CACnD,IAAI,CAAC,WAAW,OAAO,KAAK,aAAa,SAAS,GAAG,OAAO,CAAC;CAC7D,OAAO,CAAC;EAAE,gBAAgB;EAAO,cAAc;CAAI,CAAC;AACtD"}
@@ -0,0 +1,4 @@
1
+ import { Disambiguation, LocalDateTimeRule } from '../types.js';
2
+ import { Occurrence } from './types.js';
3
+ export declare function generateLocalDateTime(rule: LocalDateTimeRule, tz: string | undefined, windowStart: number, windowEnd: number, disambiguation?: Disambiguation): Occurrence[];
4
+ //# sourceMappingURL=local_date_time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local_date_time.d.ts","sourceRoot":"","sources":["../../src/generators/local_date_time.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErE,OAAO,EAAE,KAAK,UAAU,EAAc,MAAM,YAAY,CAAC;AAOzD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,iBAAiB,EACvB,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,cAAc,GAAE,cAA6B,GAC5C,UAAU,EAAE,CAsBd"}
@@ -0,0 +1,20 @@
1
+ import { computeSpanEnd, isNonexistentWallTime, monthLength, wallTimeToUtc } from "../tz.js";
2
+ import { intersects } from "./types.js";
3
+ // src/generators/local_date_time.ts
4
+ function generateLocalDateTime(rule, tz, windowStart, windowEnd, disambiguation = "compatible") {
5
+ if (!Number.isInteger(rule.year)) throw new Error(`LocalDateTimeRule.year must be an integer (got ${rule.year})`);
6
+ if (rule.month < 0 || rule.month > 11) throw new Error(`LocalDateTimeRule.month must be in 0..11 (got ${rule.month})`);
7
+ if (rule.day < 1 || rule.day > 31) throw new Error(`LocalDateTimeRule.day must be in 1..31 (got ${rule.day})`);
8
+ const day = Math.min(rule.day, monthLength(rule.year, rule.month));
9
+ if (disambiguation === "skip" && isNonexistentWallTime(rule.year, rule.month, day, rule.timeOfDay, tz)) return [];
10
+ const start = wallTimeToUtc(rule.year, rule.month, day, rule.timeOfDay, tz, disambiguation);
11
+ const end = computeSpanEnd(start, rule.timeSpan, tz);
12
+ if (!intersects(start, end, windowStart, windowEnd)) return [];
13
+ return [{
14
+ startTimestamp: start,
15
+ endTimestamp: end
16
+ }];
17
+ }
18
+ export { generateLocalDateTime };
19
+
20
+ //# sourceMappingURL=local_date_time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local_date_time.js","names":[],"sources":["../../src/generators/local_date_time.ts"],"sourcesContent":["import type { Disambiguation, LocalDateTimeRule } from \"../types.js\";\nimport { computeSpanEnd, isNonexistentWallTime, monthLength, wallTimeToUtc } from \"../tz.js\";\nimport { type Occurrence, intersects } from \"./types.js\";\n\n// A single occurrence expressed in wall-clock terms, resolved through the\n// template timezone (DST-aware). `day` clamps to the month length. When tz is\n// undefined the wall-clock fields are interpreted as UTC. A spring-forward gap\n// or fall-back overlap on the start is resolved per `disambiguation` (see\n// tz.ts) — \"skip\" drops the occurrence entirely if its wall time doesn't exist.\nexport function generateLocalDateTime(\n rule: LocalDateTimeRule,\n tz: string | undefined,\n windowStart: number,\n windowEnd: number,\n disambiguation: Disambiguation = \"compatible\"\n): Occurrence[] {\n if (!Number.isInteger(rule.year)) {\n throw new Error(`LocalDateTimeRule.year must be an integer (got ${rule.year})`);\n }\n if (rule.month < 0 || rule.month > 11) {\n throw new Error(`LocalDateTimeRule.month must be in 0..11 (got ${rule.month})`);\n }\n if (rule.day < 1 || rule.day > 31) {\n throw new Error(`LocalDateTimeRule.day must be in 1..31 (got ${rule.day})`);\n }\n\n const day = Math.min(rule.day, monthLength(rule.year, rule.month));\n if (\n disambiguation === \"skip\" &&\n isNonexistentWallTime(rule.year, rule.month, day, rule.timeOfDay, tz)\n ) {\n return [];\n }\n const start = wallTimeToUtc(rule.year, rule.month, day, rule.timeOfDay, tz, disambiguation);\n const end = computeSpanEnd(start, rule.timeSpan, tz);\n if (!intersects(start, end, windowStart, windowEnd)) return [];\n return [{ startTimestamp: start, endTimestamp: end }];\n}\n"],"mappings":";;;AASA,SAAgB,sBACd,MACA,IACA,aACA,WACA,iBAAiC,cACnB;CACd,IAAI,CAAC,OAAO,UAAU,KAAK,IAAI,GAC7B,MAAM,IAAI,MAAM,kDAAkD,KAAK,KAAK,EAAE;CAEhF,IAAI,KAAK,QAAQ,KAAK,KAAK,QAAQ,IACjC,MAAM,IAAI,MAAM,iDAAiD,KAAK,MAAM,EAAE;CAEhF,IAAI,KAAK,MAAM,KAAK,KAAK,MAAM,IAC7B,MAAM,IAAI,MAAM,+CAA+C,KAAK,IAAI,EAAE;CAG5E,MAAM,MAAM,KAAK,IAAI,KAAK,KAAK,YAAY,KAAK,MAAM,KAAK,KAAK,CAAC;CACjE,IACE,mBAAmB,UACnB,sBAAsB,KAAK,MAAM,KAAK,OAAO,KAAK,KAAK,WAAW,EAAE,GAEpE,OAAO,CAAC;CAEV,MAAM,QAAQ,cAAc,KAAK,MAAM,KAAK,OAAO,KAAK,KAAK,WAAW,IAAI,cAAc;CAC1F,MAAM,MAAM,eAAe,OAAO,KAAK,UAAU,EAAE;CACnD,IAAI,CAAC,WAAW,OAAO,KAAK,aAAa,SAAS,GAAG,OAAO,CAAC;CAC7D,OAAO,CAAC;EAAE,gBAAgB;EAAO,cAAc;CAAI,CAAC;AACtD"}
@@ -0,0 +1,4 @@
1
+ import { Disambiguation, MonthsOfYearRule } from '../types.js';
2
+ import { Occurrence } from './types.js';
3
+ export declare function generateMonthsOfYear(rule: MonthsOfYearRule, tz: string | undefined, windowStart: number, windowEnd: number, disambiguation?: Disambiguation): Occurrence[];
4
+ //# sourceMappingURL=months_of_year.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"months_of_year.d.ts","sourceRoot":"","sources":["../../src/generators/months_of_year.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AASpE,OAAO,EAEL,KAAK,UAAU,EAGhB,MAAM,YAAY,CAAC;AASpB,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,gBAAgB,EACtB,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,cAAc,GAAE,cAA6B,GAC5C,UAAU,EAAE,CAgGd"}
@@ -0,0 +1,64 @@
1
+ import { addMonthsInTz, computeSpanEnd, isNonexistentWallTime, monthLength, utcToWallParts, wallTimeToUtc } from "../tz.js";
2
+ import { TooManyOccurrencesError, intersects } from "./types.js";
3
+ // src/generators/months_of_year.ts
4
+ function generateMonthsOfYear(rule, tz, windowStart, windowEnd, disambiguation = "compatible") {
5
+ if (rule.yearInterval <= 0) throw new Error(`MonthsOfYearRule.yearInterval must be > 0 (got ${rule.yearInterval})`);
6
+ if (rule.months.length === 0) throw new Error("MonthsOfYearRule.months must be non-empty");
7
+ for (const m of rule.months) if (m < 0 || m > 11) throw new Error(`MonthsOfYearRule.months entries must be in 0..11 (got ${m})`);
8
+ if (rule.weeks.length === 0) throw new Error("MonthsOfYearRule.weeks must be non-empty");
9
+ for (const w of rule.weeks) if (w === 0 || w < -5 || w > 5) throw new Error(`MonthsOfYearRule.weeks entries must be in [-5..-1] or [1..5] (got ${w})`);
10
+ if (rule.days.length === 0) throw new Error("MonthsOfYearRule.days must be non-empty");
11
+ if (rule.count != null && rule.count < 0) throw new Error(`MonthsOfYearRule.count must be >= 0 (got ${rule.count})`);
12
+ const seriesEnd = rule.seriesEnd ?? Number.POSITIVE_INFINITY;
13
+ const effectiveEnd = Math.min(windowEnd, seriesEnd);
14
+ if (rule.seriesStart >= effectiveEnd) return [];
15
+ const months = [...new Set(rule.months)].sort((a, b) => a - b);
16
+ const weeks = [...new Set(rule.weeks)];
17
+ const weekdays = [...new Set(rule.days)].sort((a, b) => a - b);
18
+ const seriesStartParts = utcToWallParts(rule.seriesStart, tz);
19
+ let yearAnchor = wallTimeToUtc(seriesStartParts.year, 0, 1, 0, tz);
20
+ const out = [];
21
+ let count = 0;
22
+ while (yearAnchor < effectiveEnd) {
23
+ const yp = utcToWallParts(yearAnchor, tz);
24
+ const yearEmits = [];
25
+ for (const month of months) for (const week of weeks) for (const dow of weekdays) {
26
+ const targetDay = _nthWeekdayOfMonth(yp.year, month, dow, week);
27
+ if (targetDay == null) continue;
28
+ if (disambiguation === "skip" && isNonexistentWallTime(yp.year, month, targetDay, rule.timeOfDay, tz)) continue;
29
+ const occStart = wallTimeToUtc(yp.year, month, targetDay, rule.timeOfDay, tz, disambiguation);
30
+ const occEnd = computeSpanEnd(occStart, rule.timeSpan, tz);
31
+ if (occStart < rule.seriesStart) continue;
32
+ if (occStart >= seriesEnd) continue;
33
+ if (rule.count != null && count >= rule.count) {
34
+ yearEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);
35
+ out.push(...yearEmits);
36
+ return out;
37
+ }
38
+ count++;
39
+ if (intersects(occStart, occEnd, windowStart, windowEnd)) {
40
+ yearEmits.push({
41
+ startTimestamp: occStart,
42
+ endTimestamp: occEnd
43
+ });
44
+ if (out.length + yearEmits.length > 1e4) throw new TooManyOccurrencesError("monthsOfYear");
45
+ }
46
+ }
47
+ yearEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);
48
+ out.push(...yearEmits);
49
+ yearAnchor = addMonthsInTz(yearAnchor, 12 * rule.yearInterval, tz);
50
+ }
51
+ return out;
52
+ }
53
+ function _nthWeekdayOfMonth(year, month, dow, n) {
54
+ const len = monthLength(year, month);
55
+ if (n > 0) {
56
+ const day = 1 + (dow - new Date(Date.UTC(year, month, 1)).getUTCDay() + 7) % 7 + (n - 1) * 7;
57
+ return day <= len ? day : null;
58
+ }
59
+ const day = len - (new Date(Date.UTC(year, month, len)).getUTCDay() - dow + 7) % 7 + (n + 1) * 7;
60
+ return day >= 1 ? day : null;
61
+ }
62
+ export { generateMonthsOfYear };
63
+
64
+ //# sourceMappingURL=months_of_year.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"months_of_year.js","names":[],"sources":["../../src/generators/months_of_year.ts"],"sourcesContent":["import type { Disambiguation, MonthsOfYearRule } from \"../types.js\";\nimport {\n addMonthsInTz,\n computeSpanEnd,\n isNonexistentWallTime,\n monthLength,\n utcToWallParts,\n wallTimeToUtc,\n} from \"../tz.js\";\nimport {\n MAX_OCCURRENCES_PER_GENERATE,\n type Occurrence,\n TooManyOccurrencesError,\n intersects,\n} from \"./types.js\";\n\n// Positional yearly recurrence — top of the positional cascade.\n// (months × weeks-of-month × weekdays, with yearInterval as cadence.)\n//\n// For each year in cadence, walk the selected months; in each month, find\n// the Nth weekday matches (e.g. \"2nd Tuesday\") for each (week, day) pair.\n// If a position doesn't exist that month (e.g. 5th Sunday in a 4-Sunday\n// month), silently skip — same policy as WeeksOfMonthRule.\nexport function generateMonthsOfYear(\n rule: MonthsOfYearRule,\n tz: string | undefined,\n windowStart: number,\n windowEnd: number,\n disambiguation: Disambiguation = \"compatible\"\n): Occurrence[] {\n if (rule.yearInterval <= 0) {\n throw new Error(`MonthsOfYearRule.yearInterval must be > 0 (got ${rule.yearInterval})`);\n }\n if (rule.months.length === 0) {\n throw new Error(\"MonthsOfYearRule.months must be non-empty\");\n }\n for (const m of rule.months) {\n if (m < 0 || m > 11) {\n throw new Error(`MonthsOfYearRule.months entries must be in 0..11 (got ${m})`);\n }\n }\n if (rule.weeks.length === 0) {\n throw new Error(\"MonthsOfYearRule.weeks must be non-empty\");\n }\n for (const w of rule.weeks) {\n if (w === 0 || w < -5 || w > 5) {\n throw new Error(`MonthsOfYearRule.weeks entries must be in [-5..-1] or [1..5] (got ${w})`);\n }\n }\n if (rule.days.length === 0) {\n throw new Error(\"MonthsOfYearRule.days must be non-empty\");\n }\n if (rule.count != null && rule.count < 0) {\n throw new Error(`MonthsOfYearRule.count must be >= 0 (got ${rule.count})`);\n }\n\n const seriesEnd = rule.seriesEnd ?? Number.POSITIVE_INFINITY;\n const effectiveEnd = Math.min(windowEnd, seriesEnd);\n if (rule.seriesStart >= effectiveEnd) return [];\n\n const months = [...new Set(rule.months)].sort((a, b) => a - b);\n const weeks = [...new Set(rule.weeks)];\n const weekdays = [...new Set(rule.days)].sort((a, b) => a - b);\n\n // Anchor at the start of seriesStart's year (in tz), but actually anchor\n // at its first selected month within that year. We iterate year-by-year.\n const seriesStartParts = utcToWallParts(rule.seriesStart, tz);\n let yearAnchor = wallTimeToUtc(seriesStartParts.year, 0, 1, 0, tz);\n\n const out: Occurrence[] = [];\n let count = 0;\n\n while (yearAnchor < effectiveEnd) {\n const yp = utcToWallParts(yearAnchor, tz);\n const yearEmits: Occurrence[] = [];\n\n for (const month of months) {\n for (const week of weeks) {\n for (const dow of weekdays) {\n const targetDay = _nthWeekdayOfMonth(yp.year, month, dow, week);\n if (targetDay == null) continue; // Nth doesn't exist this month\n\n if (\n disambiguation === \"skip\" &&\n isNonexistentWallTime(yp.year, month, targetDay, rule.timeOfDay, tz)\n ) {\n continue;\n }\n const occStart = wallTimeToUtc(\n yp.year,\n month,\n targetDay,\n rule.timeOfDay,\n tz,\n disambiguation\n );\n const occEnd = computeSpanEnd(occStart, rule.timeSpan, tz);\n\n if (occStart < rule.seriesStart) continue;\n if (occStart >= seriesEnd) continue;\n\n if (rule.count != null && count >= rule.count) {\n yearEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);\n out.push(...yearEmits);\n return out;\n }\n count++;\n\n if (intersects(occStart, occEnd, windowStart, windowEnd)) {\n yearEmits.push({ startTimestamp: occStart, endTimestamp: occEnd });\n if (out.length + yearEmits.length > MAX_OCCURRENCES_PER_GENERATE) {\n throw new TooManyOccurrencesError(\"monthsOfYear\");\n }\n }\n }\n }\n }\n yearEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);\n out.push(...yearEmits);\n // Step yearInterval years. Use addMonthsInTz(12) so leap-year wall-clock\n // arithmetic is correct (it isn't strictly necessary at year granularity,\n // but matches the rest of the codebase).\n yearAnchor = addMonthsInTz(yearAnchor, 12 * rule.yearInterval, tz);\n }\n return out;\n}\n\n// Same helper as WeeksOfMonth uses. Returns day-of-month (1..31) for the\n// Nth occurrence of `dow` in the given month, or null if it doesn't exist.\nfunction _nthWeekdayOfMonth(year: number, month: number, dow: number, n: number): number | null {\n const len = monthLength(year, month);\n\n if (n > 0) {\n const firstDow = new Date(Date.UTC(year, month, 1)).getUTCDay();\n const offset = (dow - firstDow + 7) % 7;\n const day = 1 + offset + (n - 1) * 7;\n return day <= len ? day : null;\n }\n const lastDow = new Date(Date.UTC(year, month, len)).getUTCDay();\n const offset = (lastDow - dow + 7) % 7;\n const day = len - offset + (n + 1) * 7;\n return day >= 1 ? day : null;\n}\n"],"mappings":";;;AAuBA,SAAgB,qBACd,MACA,IACA,aACA,WACA,iBAAiC,cACnB;CACd,IAAI,KAAK,gBAAgB,GACvB,MAAM,IAAI,MAAM,kDAAkD,KAAK,aAAa,EAAE;CAExF,IAAI,KAAK,OAAO,WAAW,GACzB,MAAM,IAAI,MAAM,2CAA2C;CAE7D,KAAK,MAAM,KAAK,KAAK,QACnB,IAAI,IAAI,KAAK,IAAI,IACf,MAAM,IAAI,MAAM,yDAAyD,EAAE,EAAE;CAGjF,IAAI,KAAK,MAAM,WAAW,GACxB,MAAM,IAAI,MAAM,0CAA0C;CAE5D,KAAK,MAAM,KAAK,KAAK,OACnB,IAAI,MAAM,KAAK,IAAI,MAAM,IAAI,GAC3B,MAAM,IAAI,MAAM,qEAAqE,EAAE,EAAE;CAG7F,IAAI,KAAK,KAAK,WAAW,GACvB,MAAM,IAAI,MAAM,yCAAyC;CAE3D,IAAI,KAAK,SAAS,QAAQ,KAAK,QAAQ,GACrC,MAAM,IAAI,MAAM,4CAA4C,KAAK,MAAM,EAAE;CAG3E,MAAM,YAAY,KAAK,aAAa,OAAO;CAC3C,MAAM,eAAe,KAAK,IAAI,WAAW,SAAS;CAClD,IAAI,KAAK,eAAe,cAAc,OAAO,CAAC;CAE9C,MAAM,SAAS,CAAC,GAAG,IAAI,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CAC7D,MAAM,QAAQ,CAAC,GAAG,IAAI,IAAI,KAAK,KAAK,CAAC;CACrC,MAAM,WAAW,CAAC,GAAG,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CAI7D,MAAM,mBAAmB,eAAe,KAAK,aAAa,EAAE;CAC5D,IAAI,aAAa,cAAc,iBAAiB,MAAM,GAAG,GAAG,GAAG,EAAE;CAEjE,MAAM,MAAoB,CAAC;CAC3B,IAAI,QAAQ;CAEZ,OAAO,aAAa,cAAc;EAChC,MAAM,KAAK,eAAe,YAAY,EAAE;EACxC,MAAM,YAA0B,CAAC;EAEjC,KAAK,MAAM,SAAS,QAClB,KAAK,MAAM,QAAQ,OACjB,KAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,YAAY,mBAAmB,GAAG,MAAM,OAAO,KAAK,IAAI;GAC9D,IAAI,aAAa,MAAM;GAEvB,IACE,mBAAmB,UACnB,sBAAsB,GAAG,MAAM,OAAO,WAAW,KAAK,WAAW,EAAE,GAEnE;GAEF,MAAM,WAAW,cACf,GAAG,MACH,OACA,WACA,KAAK,WACL,IACA,cACF;GACA,MAAM,SAAS,eAAe,UAAU,KAAK,UAAU,EAAE;GAEzD,IAAI,WAAW,KAAK,aAAa;GACjC,IAAI,YAAY,WAAW;GAE3B,IAAI,KAAK,SAAS,QAAQ,SAAS,KAAK,OAAO;IAC7C,UAAU,MAAM,GAAG,MAAM,EAAE,iBAAiB,EAAE,cAAc;IAC5D,IAAI,KAAK,GAAG,SAAS;IACrB,OAAO;GACT;GACA;GAEA,IAAI,WAAW,UAAU,QAAQ,aAAa,SAAS,GAAG;IACxD,UAAU,KAAK;KAAE,gBAAgB;KAAU,cAAc;IAAO,CAAC;IACjE,IAAI,IAAI,SAAS,UAAU,SAAA,KACzB,MAAM,IAAI,wBAAwB,cAAc;GAEpD;EACF;EAGJ,UAAU,MAAM,GAAG,MAAM,EAAE,iBAAiB,EAAE,cAAc;EAC5D,IAAI,KAAK,GAAG,SAAS;EAIrB,aAAa,cAAc,YAAY,KAAK,KAAK,cAAc,EAAE;CACnE;CACA,OAAO;AACT;AAIA,SAAS,mBAAmB,MAAc,OAAe,KAAa,GAA0B;CAC9F,MAAM,MAAM,YAAY,MAAM,KAAK;CAEnC,IAAI,IAAI,GAAG;EAGT,MAAM,MAAM,KADI,MADC,IAAI,KAAK,KAAK,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,UAC9B,IAAW,KAAK,KACZ,IAAI,KAAK;EACnC,OAAO,OAAO,MAAM,MAAM;CAC5B;CAGA,MAAM,MAAM,OAFI,IAAI,KAAK,KAAK,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC,CAAC,UACrC,IAAU,MAAM,KAAK,KACT,IAAI,KAAK;CACrC,OAAO,OAAO,IAAI,MAAM;AAC1B"}
@@ -0,0 +1,10 @@
1
+ export interface Occurrence {
2
+ startTimestamp: number;
3
+ endTimestamp: number;
4
+ }
5
+ export declare const MAX_OCCURRENCES_PER_GENERATE = 10000;
6
+ export declare class TooManyOccurrencesError extends Error {
7
+ constructor(kind: string);
8
+ }
9
+ export declare function intersects(startA: number, endA: number, startB: number, endB: number): boolean;
10
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/generators/types.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,UAAU;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB;AAKD,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,IAAI,EAAE,MAAM;CAMzB;AAGD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9F"}
@@ -0,0 +1,14 @@
1
+ // src/generators/types.ts
2
+ var MAX_OCCURRENCES_PER_GENERATE = 1e4;
3
+ var TooManyOccurrencesError = class extends Error {
4
+ constructor(kind) {
5
+ super(`Generator '${kind}' would emit more than ${MAX_OCCURRENCES_PER_GENERATE} occurrences for the requested window. Narrow the window, increase the interval, or set 'count' on the rule.`);
6
+ this.name = "TooManyOccurrencesError";
7
+ }
8
+ };
9
+ function intersects(startA, endA, startB, endB) {
10
+ return Math.max(startA, startB) < Math.min(endA, endB);
11
+ }
12
+ export { MAX_OCCURRENCES_PER_GENERATE, TooManyOccurrencesError, intersects };
13
+
14
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../src/generators/types.ts"],"sourcesContent":["// Generators emit raw start/end pairs sorted ascending by startTimestamp.\n// Stable per-occurrence keys are assigned downstream (occurrence_key.ts) so\n// keys can be derived consistently across rule kinds.\nexport interface Occurrence {\n startTimestamp: number;\n endTimestamp: number;\n}\n\n// Hard ceiling per generate() call. A misconfigured rule (e.g. 1ms anchored\n// interval over a year-long window) would otherwise produce billions of\n// occurrences; we'd rather throw a clear error than blow heap.\nexport const MAX_OCCURRENCES_PER_GENERATE = 10_000;\n\nexport class TooManyOccurrencesError extends Error {\n constructor(kind: string) {\n super(\n `Generator '${kind}' would emit more than ${MAX_OCCURRENCES_PER_GENERATE} occurrences for the requested window. Narrow the window, increase the interval, or set 'count' on the rule.`\n );\n this.name = \"TooManyOccurrencesError\";\n }\n}\n\n// Half-open intersection: [startA, endA) overlaps [startB, endB)?\nexport function intersects(startA: number, endA: number, startB: number, endB: number): boolean {\n return Math.max(startA, startB) < Math.min(endA, endB);\n}\n"],"mappings":";AAWA,IAAa,+BAA+B;AAE5C,IAAa,0BAAb,cAA6C,MAAM;CACjD,YAAY,MAAc;EACxB,MACE,cAAc,KAAK,yBAAyB,6BAA6B,6GAC3E;EACA,KAAK,OAAO;CACd;AACF;AAGA,SAAgB,WAAW,QAAgB,MAAc,QAAgB,MAAuB;CAC9F,OAAO,KAAK,IAAI,QAAQ,MAAM,IAAI,KAAK,IAAI,MAAM,IAAI;AACvD"}
@@ -0,0 +1,4 @@
1
+ import { Disambiguation, WeeksOfMonthRule } from '../types.js';
2
+ import { Occurrence } from './types.js';
3
+ export declare function generateWeeksOfMonth(rule: WeeksOfMonthRule, tz: string | undefined, windowStart: number, windowEnd: number, disambiguation?: Disambiguation): Occurrence[];
4
+ //# sourceMappingURL=weeks_of_month.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weeks_of_month.d.ts","sourceRoot":"","sources":["../../src/generators/weeks_of_month.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AASpE,OAAO,EAEL,KAAK,UAAU,EAGhB,MAAM,YAAY,CAAC;AAKpB,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,gBAAgB,EACtB,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,cAAc,GAAE,cAA6B,GAC5C,UAAU,EAAE,CAuFd"}
@@ -0,0 +1,61 @@
1
+ import { addMonthsInTz, computeSpanEnd, isNonexistentWallTime, monthLength, utcToWallParts, wallTimeToUtc } from "../tz.js";
2
+ import { TooManyOccurrencesError, intersects } from "./types.js";
3
+ // src/generators/weeks_of_month.ts
4
+ function generateWeeksOfMonth(rule, tz, windowStart, windowEnd, disambiguation = "compatible") {
5
+ if (rule.monthInterval <= 0) throw new Error(`WeeksOfMonthRule.monthInterval must be > 0 (got ${rule.monthInterval})`);
6
+ if (rule.weeks.length === 0) throw new Error("WeeksOfMonthRule.weeks must be non-empty");
7
+ for (const w of rule.weeks) if (w === 0 || w < -5 || w > 5) throw new Error(`WeeksOfMonthRule.weeks entries must be in [-5..-1] or [1..5] (got ${w})`);
8
+ if (rule.days.length === 0) throw new Error("WeeksOfMonthRule.days must be non-empty");
9
+ if (rule.count != null && rule.count < 0) throw new Error(`WeeksOfMonthRule.count must be >= 0 (got ${rule.count})`);
10
+ const seriesEnd = rule.seriesEnd ?? Number.POSITIVE_INFINITY;
11
+ const effectiveEnd = Math.min(windowEnd, seriesEnd);
12
+ if (rule.seriesStart >= effectiveEnd) return [];
13
+ const days = [...rule.days].sort((a, b) => a - b);
14
+ const weeks = [...new Set(rule.weeks)];
15
+ const seriesStartParts = utcToWallParts(rule.seriesStart, tz);
16
+ let monthAnchor = wallTimeToUtc(seriesStartParts.year, seriesStartParts.month, 1, 0, tz);
17
+ const out = [];
18
+ let count = 0;
19
+ while (monthAnchor < effectiveEnd) {
20
+ const mp = utcToWallParts(monthAnchor, tz);
21
+ const monthEmits = [];
22
+ for (const week of weeks) for (const dow of days) {
23
+ const targetDay = _nthWeekdayOfMonth(mp.year, mp.month, dow, week);
24
+ if (targetDay == null) continue;
25
+ if (disambiguation === "skip" && isNonexistentWallTime(mp.year, mp.month, targetDay, rule.timeOfDay, tz)) continue;
26
+ const occStart = wallTimeToUtc(mp.year, mp.month, targetDay, rule.timeOfDay, tz, disambiguation);
27
+ const occEnd = computeSpanEnd(occStart, rule.timeSpan, tz);
28
+ if (occStart < rule.seriesStart) continue;
29
+ if (occStart >= seriesEnd) continue;
30
+ if (rule.count != null && count >= rule.count) {
31
+ monthEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);
32
+ out.push(...monthEmits);
33
+ return out;
34
+ }
35
+ count++;
36
+ if (intersects(occStart, occEnd, windowStart, windowEnd)) {
37
+ monthEmits.push({
38
+ startTimestamp: occStart,
39
+ endTimestamp: occEnd
40
+ });
41
+ if (out.length + monthEmits.length > 1e4) throw new TooManyOccurrencesError("weeksOfMonth");
42
+ }
43
+ }
44
+ monthEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);
45
+ out.push(...monthEmits);
46
+ monthAnchor = addMonthsInTz(monthAnchor, rule.monthInterval, tz);
47
+ }
48
+ return out;
49
+ }
50
+ function _nthWeekdayOfMonth(year, month, dow, n) {
51
+ const len = monthLength(year, month);
52
+ if (n > 0) {
53
+ const day = 1 + (dow - new Date(Date.UTC(year, month, 1)).getUTCDay() + 7) % 7 + (n - 1) * 7;
54
+ return day <= len ? day : null;
55
+ }
56
+ const day = len - (new Date(Date.UTC(year, month, len)).getUTCDay() - dow + 7) % 7 + (n + 1) * 7;
57
+ return day >= 1 ? day : null;
58
+ }
59
+ export { generateWeeksOfMonth };
60
+
61
+ //# sourceMappingURL=weeks_of_month.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weeks_of_month.js","names":[],"sources":["../../src/generators/weeks_of_month.ts"],"sourcesContent":["import type { Disambiguation, WeeksOfMonthRule } from \"../types.js\";\nimport {\n addMonthsInTz,\n computeSpanEnd,\n isNonexistentWallTime,\n monthLength,\n utcToWallParts,\n wallTimeToUtc,\n} from \"../tz.js\";\nimport {\n MAX_OCCURRENCES_PER_GENERATE,\n type Occurrence,\n TooManyOccurrencesError,\n intersects,\n} from \"./types.js\";\n\n// \"Nth Tuesday of every month\", \"last Friday of every other month\", etc.\n// Iterate month-by-month at monthInterval cadence; for each repeatable day,\n// resolve the target wall date.\nexport function generateWeeksOfMonth(\n rule: WeeksOfMonthRule,\n tz: string | undefined,\n windowStart: number,\n windowEnd: number,\n disambiguation: Disambiguation = \"compatible\"\n): Occurrence[] {\n if (rule.monthInterval <= 0) {\n throw new Error(`WeeksOfMonthRule.monthInterval must be > 0 (got ${rule.monthInterval})`);\n }\n if (rule.weeks.length === 0) {\n throw new Error(\"WeeksOfMonthRule.weeks must be non-empty\");\n }\n for (const w of rule.weeks) {\n if (w === 0 || w < -5 || w > 5) {\n throw new Error(`WeeksOfMonthRule.weeks entries must be in [-5..-1] or [1..5] (got ${w})`);\n }\n }\n if (rule.days.length === 0) {\n throw new Error(\"WeeksOfMonthRule.days must be non-empty\");\n }\n if (rule.count != null && rule.count < 0) {\n throw new Error(`WeeksOfMonthRule.count must be >= 0 (got ${rule.count})`);\n }\n\n const seriesEnd = rule.seriesEnd ?? Number.POSITIVE_INFINITY;\n const effectiveEnd = Math.min(windowEnd, seriesEnd);\n if (rule.seriesStart >= effectiveEnd) return [];\n\n const days = [...rule.days].sort((a, b) => a - b);\n // Dedup `weeks` so a duplicated entry doesn't double-emit. Don't sort —\n // we sort the per-month emits at the end of each month iteration anyway.\n const weeks = [...new Set(rule.weeks)];\n\n // Anchor at seriesStart's month (in tz).\n const seriesStartParts = utcToWallParts(rule.seriesStart, tz);\n let monthAnchor = wallTimeToUtc(seriesStartParts.year, seriesStartParts.month, 1, 0, tz);\n\n const out: Occurrence[] = [];\n let count = 0;\n\n while (monthAnchor < effectiveEnd) {\n const mp = utcToWallParts(monthAnchor, tz);\n const monthEmits: Occurrence[] = [];\n\n for (const week of weeks) {\n for (const dow of days) {\n const targetDay = _nthWeekdayOfMonth(mp.year, mp.month, dow, week);\n if (targetDay == null) continue; // e.g. 5th Friday in a 4-Friday month\n\n if (\n disambiguation === \"skip\" &&\n isNonexistentWallTime(mp.year, mp.month, targetDay, rule.timeOfDay, tz)\n ) {\n continue;\n }\n const occStart = wallTimeToUtc(\n mp.year,\n mp.month,\n targetDay,\n rule.timeOfDay,\n tz,\n disambiguation\n );\n const occEnd = computeSpanEnd(occStart, rule.timeSpan, tz);\n\n if (occStart < rule.seriesStart) continue;\n if (occStart >= seriesEnd) continue;\n\n if (rule.count != null && count >= rule.count) {\n // Sort and emit what we collected this month before returning.\n monthEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);\n out.push(...monthEmits);\n return out;\n }\n count++;\n\n if (intersects(occStart, occEnd, windowStart, windowEnd)) {\n monthEmits.push({ startTimestamp: occStart, endTimestamp: occEnd });\n if (out.length + monthEmits.length > MAX_OCCURRENCES_PER_GENERATE) {\n throw new TooManyOccurrencesError(\"weeksOfMonth\");\n }\n }\n }\n }\n // Sort within-month emits before appending. With multiple weeks (and\n // negative weeks mixed with positive), per-week iteration order doesn't\n // produce ascending startTimestamps.\n monthEmits.sort((a, b) => a.startTimestamp - b.startTimestamp);\n out.push(...monthEmits);\n monthAnchor = addMonthsInTz(monthAnchor, rule.monthInterval, tz);\n }\n return out;\n}\n\n// Returns the day-of-month (1..31) for the Nth weekday of the given month,\n// or null if it doesn't exist (e.g. asking for the 5th Tuesday of a 4-Tuesday\n// month). Negative N counts from the end: -1 = last, -2 = second-to-last.\nfunction _nthWeekdayOfMonth(year: number, month: number, dow: number, n: number): number | null {\n const len = monthLength(year, month);\n\n if (n > 0) {\n // Find the first occurrence of `dow` in the month, then step by 7s.\n const firstDow = new Date(Date.UTC(year, month, 1)).getUTCDay();\n const offset = (dow - firstDow + 7) % 7;\n const day = 1 + offset + (n - 1) * 7;\n return day <= len ? day : null;\n }\n // n < 0: count from end of month.\n const lastDow = new Date(Date.UTC(year, month, len)).getUTCDay();\n const offset = (lastDow - dow + 7) % 7;\n const day = len - offset + (n + 1) * 7;\n return day >= 1 ? day : null;\n}\n"],"mappings":";;;AAmBA,SAAgB,qBACd,MACA,IACA,aACA,WACA,iBAAiC,cACnB;CACd,IAAI,KAAK,iBAAiB,GACxB,MAAM,IAAI,MAAM,mDAAmD,KAAK,cAAc,EAAE;CAE1F,IAAI,KAAK,MAAM,WAAW,GACxB,MAAM,IAAI,MAAM,0CAA0C;CAE5D,KAAK,MAAM,KAAK,KAAK,OACnB,IAAI,MAAM,KAAK,IAAI,MAAM,IAAI,GAC3B,MAAM,IAAI,MAAM,qEAAqE,EAAE,EAAE;CAG7F,IAAI,KAAK,KAAK,WAAW,GACvB,MAAM,IAAI,MAAM,yCAAyC;CAE3D,IAAI,KAAK,SAAS,QAAQ,KAAK,QAAQ,GACrC,MAAM,IAAI,MAAM,4CAA4C,KAAK,MAAM,EAAE;CAG3E,MAAM,YAAY,KAAK,aAAa,OAAO;CAC3C,MAAM,eAAe,KAAK,IAAI,WAAW,SAAS;CAClD,IAAI,KAAK,eAAe,cAAc,OAAO,CAAC;CAE9C,MAAM,OAAO,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CAGhD,MAAM,QAAQ,CAAC,GAAG,IAAI,IAAI,KAAK,KAAK,CAAC;CAGrC,MAAM,mBAAmB,eAAe,KAAK,aAAa,EAAE;CAC5D,IAAI,cAAc,cAAc,iBAAiB,MAAM,iBAAiB,OAAO,GAAG,GAAG,EAAE;CAEvF,MAAM,MAAoB,CAAC;CAC3B,IAAI,QAAQ;CAEZ,OAAO,cAAc,cAAc;EACjC,MAAM,KAAK,eAAe,aAAa,EAAE;EACzC,MAAM,aAA2B,CAAC;EAElC,KAAK,MAAM,QAAQ,OACjB,KAAK,MAAM,OAAO,MAAM;GACtB,MAAM,YAAY,mBAAmB,GAAG,MAAM,GAAG,OAAO,KAAK,IAAI;GACjE,IAAI,aAAa,MAAM;GAEvB,IACE,mBAAmB,UACnB,sBAAsB,GAAG,MAAM,GAAG,OAAO,WAAW,KAAK,WAAW,EAAE,GAEtE;GAEF,MAAM,WAAW,cACf,GAAG,MACH,GAAG,OACH,WACA,KAAK,WACL,IACA,cACF;GACA,MAAM,SAAS,eAAe,UAAU,KAAK,UAAU,EAAE;GAEzD,IAAI,WAAW,KAAK,aAAa;GACjC,IAAI,YAAY,WAAW;GAE3B,IAAI,KAAK,SAAS,QAAQ,SAAS,KAAK,OAAO;IAE7C,WAAW,MAAM,GAAG,MAAM,EAAE,iBAAiB,EAAE,cAAc;IAC7D,IAAI,KAAK,GAAG,UAAU;IACtB,OAAO;GACT;GACA;GAEA,IAAI,WAAW,UAAU,QAAQ,aAAa,SAAS,GAAG;IACxD,WAAW,KAAK;KAAE,gBAAgB;KAAU,cAAc;IAAO,CAAC;IAClE,IAAI,IAAI,SAAS,WAAW,SAAA,KAC1B,MAAM,IAAI,wBAAwB,cAAc;GAEpD;EACF;EAKF,WAAW,MAAM,GAAG,MAAM,EAAE,iBAAiB,EAAE,cAAc;EAC7D,IAAI,KAAK,GAAG,UAAU;EACtB,cAAc,cAAc,aAAa,KAAK,eAAe,EAAE;CACjE;CACA,OAAO;AACT;AAKA,SAAS,mBAAmB,MAAc,OAAe,KAAa,GAA0B;CAC9F,MAAM,MAAM,YAAY,MAAM,KAAK;CAEnC,IAAI,IAAI,GAAG;EAIT,MAAM,MAAM,KADI,MADC,IAAI,KAAK,KAAK,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,UAC9B,IAAW,KAAK,KACZ,IAAI,KAAK;EACnC,OAAO,OAAO,MAAM,MAAM;CAC5B;CAIA,MAAM,MAAM,OAFI,IAAI,KAAK,KAAK,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC,CAAC,UACrC,IAAU,MAAM,KAAK,KACT,IAAI,KAAK;CACrC,OAAO,OAAO,IAAI,MAAM;AAC1B"}
@@ -0,0 +1,4 @@
1
+ import { Disambiguation, YearlyRule } from '../types.js';
2
+ import { Occurrence } from './types.js';
3
+ export declare function generateYearly(rule: YearlyRule, tz: string | undefined, windowStart: number, windowEnd: number, disambiguation?: Disambiguation): Occurrence[];
4
+ //# sourceMappingURL=yearly.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yearly.d.ts","sourceRoot":"","sources":["../../src/generators/yearly.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQ9D,OAAO,EAEL,KAAK,UAAU,EAGhB,MAAM,YAAY,CAAC;AAIpB,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,cAAc,GAAE,cAA6B,GAC5C,UAAU,EAAE,CAiDd"}