@ddd-ts-kit/scheduling 0.0.0-stage → 0.1.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 (106) hide show
  1. package/README.md +424 -2
  2. package/dist/errors/error-code-of.d.ts +4 -0
  3. package/dist/errors/error-code-of.d.ts.map +1 -0
  4. package/dist/errors/error-code-of.js +11 -0
  5. package/dist/errors/error-code-of.js.map +1 -0
  6. package/dist/errors/job-aborted-error.d.ts +6 -0
  7. package/dist/errors/job-aborted-error.d.ts.map +1 -0
  8. package/dist/errors/job-aborted-error.js +9 -0
  9. package/dist/errors/job-aborted-error.js.map +1 -0
  10. package/dist/errors/job-lock-unavailable-error.d.ts +6 -0
  11. package/dist/errors/job-lock-unavailable-error.d.ts.map +1 -0
  12. package/dist/errors/job-lock-unavailable-error.js +9 -0
  13. package/dist/errors/job-lock-unavailable-error.js.map +1 -0
  14. package/dist/errors/job-timeout-error.d.ts +6 -0
  15. package/dist/errors/job-timeout-error.d.ts.map +1 -0
  16. package/dist/errors/job-timeout-error.js +9 -0
  17. package/dist/errors/job-timeout-error.js.map +1 -0
  18. package/dist/in-process-job-scheduler.d.ts +66 -0
  19. package/dist/in-process-job-scheduler.d.ts.map +1 -0
  20. package/dist/in-process-job-scheduler.js +189 -0
  21. package/dist/in-process-job-scheduler.js.map +1 -0
  22. package/dist/index.d.ts +14 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +13 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/job-checkpoints.d.ts +11 -0
  27. package/dist/job-checkpoints.d.ts.map +1 -0
  28. package/dist/job-checkpoints.js +9 -0
  29. package/dist/job-checkpoints.js.map +1 -0
  30. package/dist/job-recorder.d.ts +28 -0
  31. package/dist/job-recorder.d.ts.map +1 -0
  32. package/dist/job-recorder.js +114 -0
  33. package/dist/job-recorder.js.map +1 -0
  34. package/dist/job-run-observer.d.ts +9 -0
  35. package/dist/job-run-observer.d.ts.map +1 -0
  36. package/dist/job-run-observer.js +2 -0
  37. package/dist/job-run-observer.js.map +1 -0
  38. package/dist/job-runner.d.ts +29 -0
  39. package/dist/job-runner.d.ts.map +1 -0
  40. package/dist/job-runner.js +96 -0
  41. package/dist/job-runner.js.map +1 -0
  42. package/dist/job-timers.d.ts +7 -0
  43. package/dist/job-timers.d.ts.map +1 -0
  44. package/dist/job-timers.js +5 -0
  45. package/dist/job-timers.js.map +1 -0
  46. package/dist/metric-names.d.ts +3 -0
  47. package/dist/metric-names.d.ts.map +1 -0
  48. package/dist/metric-names.js +3 -0
  49. package/dist/metric-names.js.map +1 -0
  50. package/dist/occurrence.d.ts +6 -0
  51. package/dist/occurrence.d.ts.map +1 -0
  52. package/dist/occurrence.js +2 -0
  53. package/dist/occurrence.js.map +1 -0
  54. package/dist/occurrences/instants-of-wall-time.d.ts +7 -0
  55. package/dist/occurrences/instants-of-wall-time.d.ts.map +1 -0
  56. package/dist/occurrences/instants-of-wall-time.js +15 -0
  57. package/dist/occurrences/instants-of-wall-time.js.map +1 -0
  58. package/dist/occurrences/latest-occurrence.d.ts +10 -0
  59. package/dist/occurrences/latest-occurrence.d.ts.map +1 -0
  60. package/dist/occurrences/latest-occurrence.js +22 -0
  61. package/dist/occurrences/latest-occurrence.js.map +1 -0
  62. package/dist/occurrences/local-day-of.d.ts +4 -0
  63. package/dist/occurrences/local-day-of.d.ts.map +1 -0
  64. package/dist/occurrences/local-day-of.js +7 -0
  65. package/dist/occurrences/local-day-of.js.map +1 -0
  66. package/dist/occurrences/next-occurrence.d.ts +4 -0
  67. package/dist/occurrences/next-occurrence.d.ts.map +1 -0
  68. package/dist/occurrences/next-occurrence.js +20 -0
  69. package/dist/occurrences/next-occurrence.js.map +1 -0
  70. package/dist/occurrences/occurrences-of-day.d.ts +4 -0
  71. package/dist/occurrences/occurrences-of-day.d.ts.map +1 -0
  72. package/dist/occurrences/occurrences-of-day.js +35 -0
  73. package/dist/occurrences/occurrences-of-day.js.map +1 -0
  74. package/dist/occurrences/offset-at.d.ts +3 -0
  75. package/dist/occurrences/offset-at.d.ts.map +1 -0
  76. package/dist/occurrences/offset-at.js +32 -0
  77. package/dist/occurrences/offset-at.js.map +1 -0
  78. package/dist/registered-job.d.ts +21 -0
  79. package/dist/registered-job.d.ts.map +1 -0
  80. package/dist/registered-job.js +27 -0
  81. package/dist/registered-job.js.map +1 -0
  82. package/dist/report-failed-runs.d.ts +11 -0
  83. package/dist/report-failed-runs.d.ts.map +1 -0
  84. package/dist/report-failed-runs.js +23 -0
  85. package/dist/report-failed-runs.js.map +1 -0
  86. package/dist/started-run.d.ts +7 -0
  87. package/dist/started-run.d.ts.map +1 -0
  88. package/dist/started-run.js +2 -0
  89. package/dist/started-run.js.map +1 -0
  90. package/dist/system-job-timers.d.ts +3 -0
  91. package/dist/system-job-timers.d.ts.map +1 -0
  92. package/dist/system-job-timers.js +9 -0
  93. package/dist/system-job-timers.js.map +1 -0
  94. package/dist/testing/manual-job-scheduler.d.ts +24 -0
  95. package/dist/testing/manual-job-scheduler.d.ts.map +1 -0
  96. package/dist/testing/manual-job-scheduler.js +42 -0
  97. package/dist/testing/manual-job-scheduler.js.map +1 -0
  98. package/dist/testing/manual-job-timers.d.ts +23 -0
  99. package/dist/testing/manual-job-timers.d.ts.map +1 -0
  100. package/dist/testing/manual-job-timers.js +54 -0
  101. package/dist/testing/manual-job-timers.js.map +1 -0
  102. package/dist/testing.d.ts +3 -0
  103. package/dist/testing.d.ts.map +1 -0
  104. package/dist/testing.js +3 -0
  105. package/dist/testing.js.map +1 -0
  106. package/package.json +36 -4
@@ -0,0 +1,6 @@
1
+ /** One occurrence to run: due by time, or asked for with `runNow`. */
2
+ export interface Occurrence {
3
+ readonly scheduledAt: Date;
4
+ readonly manual: boolean;
5
+ }
6
+ //# sourceMappingURL=occurrence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"occurrence.d.ts","sourceRoot":"","sources":["../src/occurrence.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=occurrence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"occurrence.js","sourceRoot":"","sources":["../src/occurrence.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The instants at which the clocks of the zone show a wall time, earliest first: none for a
3
+ * time skipped when clocks are set forward, two for a time repeated when they are set back. A
4
+ * wall time is written as the milliseconds it would be in UTC.
5
+ */
6
+ export declare function instantsOfWallTime(timeZone: string, wallTime: number): number[];
7
+ //# sourceMappingURL=instants-of-wall-time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instants-of-wall-time.d.ts","sourceRoot":"","sources":["../../src/occurrences/instants-of-wall-time.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAO/E"}
@@ -0,0 +1,15 @@
1
+ import { offsetAt } from './offset-at.js';
2
+ const DAY_MS = 86_400_000;
3
+ /**
4
+ * The instants at which the clocks of the zone show a wall time, earliest first: none for a
5
+ * time skipped when clocks are set forward, two for a time repeated when they are set back. A
6
+ * wall time is written as the milliseconds it would be in UTC.
7
+ */
8
+ export function instantsOfWallTime(timeZone, wallTime) {
9
+ const offsets = new Set([offsetAt(timeZone, wallTime - DAY_MS), offsetAt(timeZone, wallTime + DAY_MS)]);
10
+ return [...offsets]
11
+ .filter((offset) => offsetAt(timeZone, wallTime - offset) === offset)
12
+ .map((offset) => wallTime - offset)
13
+ .sort((left, right) => left - right);
14
+ }
15
+ //# sourceMappingURL=instants-of-wall-time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instants-of-wall-time.js","sourceRoot":"","sources":["../../src/occurrences/instants-of-wall-time.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,MAAM,GAAG,UAAU,CAAC;AAE1B;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,QAAgB;IACnE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAC,EAAE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAExG,OAAO,CAAC,GAAG,OAAO,CAAC;SAChB,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAC,KAAK,MAAM,CAAC;SACpE,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,GAAG,MAAM,CAAC;SAClC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ParsedJobSchedule } from '@ddd-ts-kit/kernel';
2
+ export interface OccurrenceRange {
3
+ /** Excluded. */
4
+ readonly after: Date;
5
+ /** Included. */
6
+ readonly notAfter: Date;
7
+ }
8
+ /** The last occurrence of the schedule in the range, `undefined` when it holds none. */
9
+ export declare function latestOccurrence(schedule: ParsedJobSchedule, range: OccurrenceRange): Date | undefined;
10
+ //# sourceMappingURL=latest-occurrence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"latest-occurrence.d.ts","sourceRoot":"","sources":["../../src/occurrences/latest-occurrence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAIxE,MAAM,WAAW,eAAe;IAC9B,gBAAgB;IAChB,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IACrB,gBAAgB;IAChB,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;CACzB;AAED,wFAAwF;AACxF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,CAUtG"}
@@ -0,0 +1,22 @@
1
+ import { localDayOf } from './local-day-of.js';
2
+ import { occurrencesOfDay } from './occurrences-of-day.js';
3
+ /** The last occurrence of the schedule in the range, `undefined` when it holds none. */
4
+ export function latestOccurrence(schedule, range) {
5
+ const from = range.after.getTime();
6
+ const until = range.notAfter.getTime();
7
+ const latest = schedule.kind === 'interval'
8
+ ? Math.floor(until / schedule.everyMs) * schedule.everyMs
9
+ : latestOfCron(schedule, until, from);
10
+ return latest === undefined || latest <= from ? undefined : new Date(latest);
11
+ }
12
+ function latestOfCron(cron, until, from) {
13
+ const lastDay = localDayOf(cron, from) - 1;
14
+ for (let day = localDayOf(cron, until) + 1; day >= lastDay; day -= 1) {
15
+ const found = occurrencesOfDay(cron, day).findLast((occurrence) => occurrence <= until);
16
+ if (found !== undefined) {
17
+ return found;
18
+ }
19
+ }
20
+ return undefined;
21
+ }
22
+ //# sourceMappingURL=latest-occurrence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"latest-occurrence.js","sourceRoot":"","sources":["../../src/occurrences/latest-occurrence.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAS3D,wFAAwF;AACxF,MAAM,UAAU,gBAAgB,CAAC,QAA2B,EAAE,KAAsB;IAClF,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;IAEvC,MAAM,MAAM,GACV,QAAQ,CAAC,IAAI,KAAK,UAAU;QAC1B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAC,OAAO;QACzD,CAAC,CAAC,YAAY,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAE1C,OAAO,MAAM,KAAK,SAAS,IAAI,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/E,CAAC;AAED,SAAS,YAAY,CAAC,IAAgB,EAAE,KAAa,EAAE,IAAY;IACjE,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IAE3C,KAAK,IAAI,GAAG,GAAG,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,OAAO,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QACrE,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,IAAI,KAAK,CAAC,CAAC;QAExF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ParsedCron } from '@ddd-ts-kit/kernel';
2
+ /** The day the clocks of the zone show at an instant, counted from the epoch. */
3
+ export declare function localDayOf(cron: ParsedCron, instant: number): number;
4
+ //# sourceMappingURL=local-day-of.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-day-of.d.ts","sourceRoot":"","sources":["../../src/occurrences/local-day-of.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAKrD,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpE"}
@@ -0,0 +1,7 @@
1
+ import { offsetAt } from './offset-at.js';
2
+ const DAY_MS = 86_400_000;
3
+ /** The day the clocks of the zone show at an instant, counted from the epoch. */
4
+ export function localDayOf(cron, instant) {
5
+ return Math.floor((instant + offsetAt(cron.timeZone, instant)) / DAY_MS);
6
+ }
7
+ //# sourceMappingURL=local-day-of.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-day-of.js","sourceRoot":"","sources":["../../src/occurrences/local-day-of.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,MAAM,GAAG,UAAU,CAAC;AAE1B,iFAAiF;AACjF,MAAM,UAAU,UAAU,CAAC,IAAgB,EAAE,OAAe;IAC1D,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ParsedJobSchedule } from '@ddd-ts-kit/kernel';
2
+ /** The first occurrence of the schedule strictly after an instant. */
3
+ export declare function nextOccurrence(schedule: ParsedJobSchedule, after: Date): Date;
4
+ //# sourceMappingURL=next-occurrence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next-occurrence.d.ts","sourceRoot":"","sources":["../../src/occurrences/next-occurrence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAO5D,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,iBAAiB,EAAE,KAAK,EAAE,IAAI,GAAG,IAAI,CAoB7E"}
@@ -0,0 +1,20 @@
1
+ import { localDayOf } from './local-day-of.js';
2
+ import { occurrencesOfDay } from './occurrences-of-day.js';
3
+ /** Two 29ths of February are at most eight years apart. */
4
+ const HORIZON_DAYS = 366 * 8 + 2;
5
+ /** The first occurrence of the schedule strictly after an instant. */
6
+ export function nextOccurrence(schedule, after) {
7
+ const instant = after.getTime();
8
+ if (schedule.kind === 'interval') {
9
+ return new Date((Math.floor(instant / schedule.everyMs) + 1) * schedule.everyMs);
10
+ }
11
+ const firstDay = localDayOf(schedule, instant) - 1;
12
+ for (let day = firstDay; day < firstDay + HORIZON_DAYS; day += 1) {
13
+ const found = occurrencesOfDay(schedule, day).find((occurrence) => occurrence > instant);
14
+ if (found !== undefined) {
15
+ return new Date(found);
16
+ }
17
+ }
18
+ throw new RangeError(`Cron "${schedule.expression}" has no occurrence in the eight years after ${after.toISOString()}.`);
19
+ }
20
+ //# sourceMappingURL=next-occurrence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next-occurrence.js","sourceRoot":"","sources":["../../src/occurrences/next-occurrence.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,2DAA2D;AAC3D,MAAM,YAAY,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;AAEjC,sEAAsE;AACtE,MAAM,UAAU,cAAc,CAAC,QAA2B,EAAE,KAAW;IACrE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;IAEhC,IAAI,QAAQ,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACjC,OAAO,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IACnF,CAAC;IAED,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IAEnD,KAAK,IAAI,GAAG,GAAG,QAAQ,EAAE,GAAG,GAAG,QAAQ,GAAG,YAAY,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QACjE,MAAM,KAAK,GAAG,gBAAgB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,GAAG,OAAO,CAAC,CAAC;QAEzF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,MAAM,IAAI,UAAU,CAClB,SAAS,QAAQ,CAAC,UAAU,gDAAgD,KAAK,CAAC,WAAW,EAAE,GAAG,CACnG,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ParsedCron } from '@ddd-ts-kit/kernel';
2
+ /** The instants of one local day at which the expression fires, in order. Most days hold no change of offset. */
3
+ export declare function occurrencesOfDay(cron: ParsedCron, day: number): number[];
4
+ //# sourceMappingURL=occurrences-of-day.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"occurrences-of-day.d.ts","sourceRoot":"","sources":["../../src/occurrences/occurrences-of-day.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAQrD,iHAAiH;AACjH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAgBxE"}
@@ -0,0 +1,35 @@
1
+ import { instantsOfWallTime } from './instants-of-wall-time.js';
2
+ import { offsetAt } from './offset-at.js';
3
+ const MINUTE_MS = 60_000;
4
+ const HOUR_MS = 3_600_000;
5
+ const DAY_MS = 86_400_000;
6
+ /** The instants of one local day at which the expression fires, in order. Most days hold no change of offset. */
7
+ export function occurrencesOfDay(cron, day) {
8
+ if (!firesOn(cron, day)) {
9
+ return [];
10
+ }
11
+ const wallTimes = cron.hours.flatMap((hour) => cron.minutes.map((minute) => day * DAY_MS + hour * HOUR_MS + minute * MINUTE_MS));
12
+ const before = offsetAt(cron.timeZone, (day - 1) * DAY_MS);
13
+ if (before === offsetAt(cron.timeZone, (day + 2) * DAY_MS)) {
14
+ return wallTimes.map((wallTime) => wallTime - before);
15
+ }
16
+ return wallTimes.flatMap((wallTime) => occurrencesAt(cron, wallTime)).sort((left, right) => left - right);
17
+ }
18
+ /** The day-of-month and day-of-week fields are alternatives when both are restricted, as in every cron. */
19
+ function firesOn(cron, day) {
20
+ const date = new Date(day * DAY_MS);
21
+ if (!cron.months.includes(date.getUTCMonth() + 1)) {
22
+ return false;
23
+ }
24
+ const onDayOfMonth = cron.daysOfMonth.includes(date.getUTCDate());
25
+ const onDayOfWeek = cron.daysOfWeek.includes(date.getUTCDay());
26
+ return cron.dayOfMonthRestricted && cron.dayOfWeekRestricted
27
+ ? onDayOfMonth || onDayOfWeek
28
+ : onDayOfMonth && onDayOfWeek;
29
+ }
30
+ /** A wall time that occurs twice runs once, unless the job follows elapsed time (`*` hours). */
31
+ function occurrencesAt(cron, wallTime) {
32
+ const instants = instantsOfWallTime(cron.timeZone, wallTime);
33
+ return cron.everyHour ? instants : instants.slice(0, 1);
34
+ }
35
+ //# sourceMappingURL=occurrences-of-day.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"occurrences-of-day.js","sourceRoot":"","sources":["../../src/occurrences/occurrences-of-day.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,SAAS,GAAG,MAAM,CAAC;AACzB,MAAM,OAAO,GAAG,SAAS,CAAC;AAC1B,MAAM,MAAM,GAAG,UAAU,CAAC;AAE1B,iHAAiH;AACjH,MAAM,UAAU,gBAAgB,CAAC,IAAgB,EAAE,GAAW;IAC5D,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAC5C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC,CACjF,CAAC;IAEF,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;IAE3D,IAAI,MAAM,KAAK,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC;QAC3D,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,GAAG,MAAM,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;AAC5G,CAAC;AAED,2GAA2G;AAC3G,SAAS,OAAO,CAAC,IAAgB,EAAE,GAAW;IAC5C,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC;IAEpC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;QAClD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IAClE,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IAE/D,OAAO,IAAI,CAAC,oBAAoB,IAAI,IAAI,CAAC,mBAAmB;QAC1D,CAAC,CAAC,YAAY,IAAI,WAAW;QAC7B,CAAC,CAAC,YAAY,IAAI,WAAW,CAAC;AAClC,CAAC;AAED,gGAAgG;AAChG,SAAS,aAAa,CAAC,IAAgB,EAAE,QAAgB;IACvD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAE7D,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** What the clocks of the zone show at an instant, minus the instant: positive east of Greenwich. */
2
+ export declare function offsetAt(timeZone: string, instant: number): number;
3
+ //# sourceMappingURL=offset-at.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offset-at.d.ts","sourceRoot":"","sources":["../../src/occurrences/offset-at.ts"],"names":[],"mappings":"AAEA,qGAAqG;AACrG,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAuBlE"}
@@ -0,0 +1,32 @@
1
+ const formatters = new Map();
2
+ /** What the clocks of the zone show at an instant, minus the instant: positive east of Greenwich. */
3
+ export function offsetAt(timeZone, instant) {
4
+ if (timeZone === 'UTC') {
5
+ return 0;
6
+ }
7
+ const shown = new Map(formatterOf(timeZone)
8
+ .formatToParts(instant)
9
+ .map((part) => [part.type, Number(part.value)]));
10
+ const field = (type) => shown.get(type) ?? 0;
11
+ const wallTime = Date.UTC(field('year'), field('month') - 1, field('day'), field('hour'), field('minute'), field('second'));
12
+ return wallTime - Math.floor(instant / 1_000) * 1_000;
13
+ }
14
+ function formatterOf(timeZone) {
15
+ const known = formatters.get(timeZone);
16
+ if (known !== undefined) {
17
+ return known;
18
+ }
19
+ const formatter = new Intl.DateTimeFormat('en-US', {
20
+ timeZone,
21
+ hourCycle: 'h23',
22
+ year: 'numeric',
23
+ month: 'numeric',
24
+ day: 'numeric',
25
+ hour: 'numeric',
26
+ minute: 'numeric',
27
+ second: 'numeric',
28
+ });
29
+ formatters.set(timeZone, formatter);
30
+ return formatter;
31
+ }
32
+ //# sourceMappingURL=offset-at.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offset-at.js","sourceRoot":"","sources":["../../src/occurrences/offset-at.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,GAAG,IAAI,GAAG,EAA+B,CAAC;AAE1D,qGAAqG;AACrG,MAAM,UAAU,QAAQ,CAAC,QAAgB,EAAE,OAAe;IACxD,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;QACvB,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,GAAG,CACnB,WAAW,CAAC,QAAQ,CAAC;SAClB,aAAa,CAAC,OAAO,CAAC;SACtB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAClD,CAAC;IAEF,MAAM,KAAK,GAAG,CAAC,IAAkC,EAAU,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEnF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CACvB,KAAK,CAAC,MAAM,CAAC,EACb,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAClB,KAAK,CAAC,KAAK,CAAC,EACZ,KAAK,CAAC,MAAM,CAAC,EACb,KAAK,CAAC,QAAQ,CAAC,EACf,KAAK,CAAC,QAAQ,CAAC,CAChB,CAAC;IAEF,OAAO,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC;AACxD,CAAC;AAED,SAAS,WAAW,CAAC,QAAgB;IACnC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEvC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;QACjD,QAAQ;QACR,SAAS,EAAE,KAAK;QAChB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,SAAS;QACd,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,SAAS;KAClB,CAAC,CAAC;IAEH,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAEpC,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { type JobDefinition, type JobPolicy, type JobRun, type LockHandle, type ParsedJobSchedule } from '@ddd-ts-kit/kernel';
2
+ /** A job and what this instance knows of it. Held by the scheduler alone. */
3
+ export declare class RegisteredJob {
4
+ readonly definition: JobDefinition;
5
+ readonly name: string;
6
+ readonly policy: JobPolicy;
7
+ readonly schedule: ParsedJobSchedule;
8
+ readonly scheduleText: string;
9
+ /** The last occurrence handled, or the instant the scheduler started following the job. */
10
+ cursor: Date | undefined;
11
+ cancelSleep: (() => void) | undefined;
12
+ /** The run in progress, or the one waiting behind it. */
13
+ active: Promise<JobRun> | undefined;
14
+ waiting: boolean;
15
+ lastRun: JobRun | undefined;
16
+ /** The lock of the last occurrence run, kept until the next one so that no instance runs it again. */
17
+ held: LockHandle | undefined;
18
+ abort: ((reason: Error) => void) | undefined;
19
+ constructor(definition: JobDefinition);
20
+ }
21
+ //# sourceMappingURL=registered-job.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registered-job.d.ts","sourceRoot":"","sources":["../src/registered-job.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,MAAM,EACX,KAAK,UAAU,EACf,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAE5B,6EAA6E;AAC7E,qBAAa,aAAa;IA0BZ,QAAQ,CAAC,UAAU,EAAE,aAAa;IAzB9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAE3B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IAErC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAE9B,2FAA2F;IAC3F,MAAM,EAAE,IAAI,GAAG,SAAS,CAAC;IAEzB,WAAW,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAEtC,yDAAyD;IACzD,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAEpC,OAAO,UAAS;IAEhB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAE5B,sGAAsG;IACtG,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;IAE7B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAE7C,YAAqB,UAAU,EAAE,aAAa,EAK7C;CACF"}
@@ -0,0 +1,27 @@
1
+ import { describeJobSchedule, jobPolicy, parseJobSchedule, } from '@ddd-ts-kit/kernel';
2
+ /** A job and what this instance knows of it. Held by the scheduler alone. */
3
+ export class RegisteredJob {
4
+ definition;
5
+ name;
6
+ policy;
7
+ schedule;
8
+ scheduleText;
9
+ /** The last occurrence handled, or the instant the scheduler started following the job. */
10
+ cursor;
11
+ cancelSleep;
12
+ /** The run in progress, or the one waiting behind it. */
13
+ active;
14
+ waiting = false;
15
+ lastRun;
16
+ /** The lock of the last occurrence run, kept until the next one so that no instance runs it again. */
17
+ held;
18
+ abort;
19
+ constructor(definition) {
20
+ this.definition = definition;
21
+ this.name = definition.name;
22
+ this.policy = jobPolicy(definition);
23
+ this.schedule = parseJobSchedule(definition.schedule);
24
+ this.scheduleText = describeJobSchedule(definition.schedule);
25
+ }
26
+ }
27
+ //# sourceMappingURL=registered-job.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registered-job.js","sourceRoot":"","sources":["../src/registered-job.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,SAAS,EACT,gBAAgB,GAMjB,MAAM,oBAAoB,CAAC;AAE5B,6EAA6E;AAC7E,MAAM,OAAO,aAAa;IA0BH,UAAU;IAzBtB,IAAI,CAAS;IAEb,MAAM,CAAY;IAElB,QAAQ,CAAoB;IAE5B,YAAY,CAAS;IAE9B,2FAA2F;IAC3F,MAAM,CAAmB;IAEzB,WAAW,CAA2B;IAEtC,yDAAyD;IACzD,MAAM,CAA8B;IAEpC,OAAO,GAAG,KAAK,CAAC;IAEhB,OAAO,CAAqB;IAE5B,sGAAsG;IACtG,IAAI,CAAyB;IAE7B,KAAK,CAAwC;IAE7C,YAAqB,UAAyB;0BAAzB,UAAU;QAC7B,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;QACpC,IAAI,CAAC,QAAQ,GAAG,gBAAgB,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACtD,IAAI,CAAC,YAAY,GAAG,mBAAmB,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC/D,CAAC;CACF"}
@@ -0,0 +1,11 @@
1
+ import { type ErrorReporter } from '@ddd-ts-kit/kernel';
2
+ import type { JobRunObserver } from './job-run-observer.js';
3
+ /**
4
+ * The observer that reports every run that failed with something else than a `DomainError`:
5
+ * a timeout, a lock that could not be asked, a fault of the job. A run that was skipped or
6
+ * missed is not a failure, and one whose command a bus middleware already reported is left alone.
7
+ *
8
+ * new InProcessJobScheduler({ ..., onRun: reportFailedRuns(errors) });
9
+ */
10
+ export declare function reportFailedRuns(reporter: ErrorReporter): JobRunObserver;
11
+ //# sourceMappingURL=report-failed-runs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-failed-runs.d.ts","sourceRoot":"","sources":["../src/report-failed-runs.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6D,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,aAAa,GAAG,cAAc,CAkBxE"}
@@ -0,0 +1,23 @@
1
+ import { errorReportOf, isUnexpectedError, reportOnce, systemActor } from '@ddd-ts-kit/kernel';
2
+ /**
3
+ * The observer that reports every run that failed with something else than a `DomainError`:
4
+ * a timeout, a lock that could not be asked, a fault of the job. A run that was skipped or
5
+ * missed is not a failure, and one whose command a bus middleware already reported is left alone.
6
+ *
7
+ * new InProcessJobScheduler({ ..., onRun: reportFailedRuns(errors) });
8
+ */
9
+ export function reportFailedRuns(reporter) {
10
+ return (run, error, detail) => {
11
+ if (run.outcome !== 'failed' || !isUnexpectedError(error)) {
12
+ return;
13
+ }
14
+ reportOnce(reporter, error, errorReportOf(error, {
15
+ origin: 'schedule',
16
+ operation: run.job,
17
+ context: run.job.split('.', 1).join(''),
18
+ actorId: systemActor(run.job).id,
19
+ ...(detail?.correlationId === undefined ? {} : { correlationId: detail.correlationId }),
20
+ }));
21
+ };
22
+ }
23
+ //# sourceMappingURL=report-failed-runs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-failed-runs.js","sourceRoot":"","sources":["../src/report-failed-runs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAsB,MAAM,oBAAoB,CAAC;AAGnH;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAuB;IACtD,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAC5B,IAAI,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1D,OAAO;QACT,CAAC;QAED,UAAU,CACR,QAAQ,EACR,KAAK,EACL,aAAa,CAAC,KAAK,EAAE;YACnB,MAAM,EAAE,UAAU;YAClB,SAAS,EAAE,GAAG,CAAC,GAAG;YAClB,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACvC,OAAO,EAAE,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE;YAChC,GAAG,CAAC,MAAM,EAAE,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC;SACxF,CAAC,CACH,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Occurrence } from './occurrence.js';
2
+ export interface StartedRun {
3
+ readonly occurrence: Occurrence;
4
+ readonly startedAt: Date;
5
+ readonly correlationId: string;
6
+ }
7
+ //# sourceMappingURL=started-run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"started-run.d.ts","sourceRoot":"","sources":["../src/started-run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=started-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"started-run.js","sourceRoot":"","sources":["../src/started-run.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ import type { JobTimers } from './job-timers.js';
2
+ export declare const systemJobTimers: JobTimers;
3
+ //# sourceMappingURL=system-job-timers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-job-timers.d.ts","sourceRoot":"","sources":["../src/system-job-timers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,eAAO,MAAM,eAAe,EAAE,SAQ5B,CAAC"}
@@ -0,0 +1,9 @@
1
+ export const systemJobTimers = Object.freeze({
2
+ schedule(delayMs, task) {
3
+ const timer = setTimeout(task, delayMs);
4
+ return () => {
5
+ clearTimeout(timer);
6
+ };
7
+ },
8
+ });
9
+ //# sourceMappingURL=system-job-timers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-job-timers.js","sourceRoot":"","sources":["../src/system-job-timers.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,eAAe,GAAc,MAAM,CAAC,MAAM,CAAC;IACtD,QAAQ,CAAC,OAAe,EAAE,IAAgB;QACxC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAExC,OAAO,GAAG,EAAE;YACV,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { FixedClock, JobRun } from '@ddd-ts-kit/kernel';
2
+ import { InProcessJobScheduler, type InProcessJobSchedulerOptions } from '../in-process-job-scheduler.js';
3
+ import { ManualJobTimers } from './manual-job-timers.js';
4
+ export interface ManualJobSchedulerOptions extends Omit<InProcessJobSchedulerOptions, 'timers' | 'clock' | 'random'> {
5
+ /** The clock `advance` moves. */
6
+ readonly clock: FixedClock;
7
+ }
8
+ /**
9
+ * The in-process scheduler on manual timers, without jitter: a test registers jobs, starts it,
10
+ * advances time and reads `runs`, or runs one job with `runNow`.
11
+ */
12
+ export declare class ManualJobScheduler extends InProcessJobScheduler {
13
+ /** Every occurrence handled, in order, whatever became of it. */
14
+ readonly runs: JobRun[];
15
+ /** What the failed runs threw, in order. */
16
+ readonly failures: unknown[];
17
+ readonly timers: ManualJobTimers;
18
+ constructor(options: ManualJobSchedulerOptions);
19
+ /** See `ManualJobTimers.advance`. */
20
+ advance(durationMs: number): Promise<void>;
21
+ /** The runs of one job, in order. */
22
+ runsOf(job: string): JobRun[];
23
+ }
24
+ //# sourceMappingURL=manual-job-scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manual-job-scheduler.d.ts","sourceRoot":"","sources":["../../src/testing/manual-job-scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,KAAK,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAC1G,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,WAAW,yBAA0B,SAAQ,IAAI,CAAC,4BAA4B,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;IAClH,iCAAiC;IACjC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B;AAED;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,qBAAqB;IAC3D,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAExB,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC;IAE7B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IAEjC,YAAY,OAAO,EAAE,yBAAyB,EAuB7C;IAED,qCAAqC;IACrC,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzC;IAED,qCAAqC;IACrC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAE5B;CACF"}
@@ -0,0 +1,42 @@
1
+ import { InProcessJobScheduler } from '../in-process-job-scheduler.js';
2
+ import { ManualJobTimers } from './manual-job-timers.js';
3
+ /**
4
+ * The in-process scheduler on manual timers, without jitter: a test registers jobs, starts it,
5
+ * advances time and reads `runs`, or runs one job with `runNow`.
6
+ */
7
+ export class ManualJobScheduler extends InProcessJobScheduler {
8
+ /** Every occurrence handled, in order, whatever became of it. */
9
+ runs;
10
+ /** What the failed runs threw, in order. */
11
+ failures;
12
+ timers;
13
+ constructor(options) {
14
+ const timers = new ManualJobTimers(options.clock);
15
+ const runs = [];
16
+ const failures = [];
17
+ super({
18
+ ...options,
19
+ timers,
20
+ random: () => 0,
21
+ onRun: (run, error, detail) => {
22
+ runs.push(run);
23
+ if (run.outcome === 'failed') {
24
+ failures.push(error);
25
+ }
26
+ options.onRun?.(run, error, detail);
27
+ },
28
+ });
29
+ this.timers = timers;
30
+ this.runs = runs;
31
+ this.failures = failures;
32
+ }
33
+ /** See `ManualJobTimers.advance`. */
34
+ advance(durationMs) {
35
+ return this.timers.advance(durationMs);
36
+ }
37
+ /** The runs of one job, in order. */
38
+ runsOf(job) {
39
+ return this.runs.filter((run) => run.job === job);
40
+ }
41
+ }
42
+ //# sourceMappingURL=manual-job-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manual-job-scheduler.js","sourceRoot":"","sources":["../../src/testing/manual-job-scheduler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAqC,MAAM,gCAAgC,CAAC;AAC1G,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAOzD;;;GAGG;AACH,MAAM,OAAO,kBAAmB,SAAQ,qBAAqB;IAC3D,iEAAiE;IACxD,IAAI,CAAW;IAExB,4CAA4C;IACnC,QAAQ,CAAY;IAEpB,MAAM,CAAkB;IAEjC,YAAY,OAAkC;QAC5C,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAClD,MAAM,IAAI,GAAa,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAc,EAAE,CAAC;QAE/B,KAAK,CAAC;YACJ,GAAG,OAAO;YACV,MAAM;YACN,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YACf,KAAK,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;gBAC5B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAEf,IAAI,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;oBAC7B,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACvB,CAAC;gBAED,OAAO,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;YACtC,CAAC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,qCAAqC;IACrC,OAAO,CAAC,UAAkB;QACxB,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACzC,CAAC;IAED,qCAAqC;IACrC,MAAM,CAAC,GAAW;QAChB,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;IACpD,CAAC;CACF"}
@@ -0,0 +1,23 @@
1
+ import type { FixedClock } from '@ddd-ts-kit/kernel';
2
+ import type { JobTimers } from '../job-timers.js';
3
+ /**
4
+ * Timers that only move when a test says so, on the clock the scheduler reads: `advance(ms)`
5
+ * fires what becomes due, in order, each at its instant.
6
+ */
7
+ export declare class ManualJobTimers implements JobTimers {
8
+ private readonly clock;
9
+ private sleeping;
10
+ constructor(clock: FixedClock);
11
+ /** Timers neither fired nor cancelled. */
12
+ get pending(): number;
13
+ schedule(delayMs: number, task: () => void): () => void;
14
+ /**
15
+ * Moves the clock forward, firing each timer at its instant and letting what it started go as
16
+ * far as promises alone take it: a job that waits on nothing else has ended when this resolves,
17
+ * a job blocked by the test is still running.
18
+ */
19
+ advance(durationMs: number): Promise<void>;
20
+ private nextDue;
21
+ private moveTo;
22
+ }
23
+ //# sourceMappingURL=manual-job-timers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manual-job-timers.d.ts","sourceRoot":"","sources":["../../src/testing/manual-job-timers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAOlD;;;GAGG;AACH,qBAAa,eAAgB,YAAW,SAAS;IAGnC,OAAO,CAAC,QAAQ,CAAC,KAAK;IAFlC,OAAO,CAAC,QAAQ,CAAkB;IAElC,YAA6B,KAAK,EAAE,UAAU,EAAI;IAElD,0CAA0C;IAC1C,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAQtD;IAED;;;;OAIG;IACG,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAa/C;IAED,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,MAAM;CAGf"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Timers that only move when a test says so, on the clock the scheduler reads: `advance(ms)`
3
+ * fires what becomes due, in order, each at its instant.
4
+ */
5
+ export class ManualJobTimers {
6
+ clock;
7
+ sleeping = [];
8
+ constructor(clock) {
9
+ this.clock = clock;
10
+ }
11
+ /** Timers neither fired nor cancelled. */
12
+ get pending() {
13
+ return this.sleeping.length;
14
+ }
15
+ schedule(delayMs, task) {
16
+ const sleeping = { dueAt: this.clock.now().getTime() + delayMs, task };
17
+ this.sleeping.push(sleeping);
18
+ return () => {
19
+ this.sleeping = this.sleeping.filter((other) => other !== sleeping);
20
+ };
21
+ }
22
+ /**
23
+ * Moves the clock forward, firing each timer at its instant and letting what it started go as
24
+ * far as promises alone take it: a job that waits on nothing else has ended when this resolves,
25
+ * a job blocked by the test is still running.
26
+ */
27
+ async advance(durationMs) {
28
+ const target = this.clock.now().getTime() + durationMs;
29
+ await settled();
30
+ for (let next = this.nextDue(target); next !== undefined; next = this.nextDue(target)) {
31
+ this.sleeping = this.sleeping.filter((other) => other !== next);
32
+ this.moveTo(next.dueAt);
33
+ next.task();
34
+ await settled();
35
+ }
36
+ this.moveTo(target);
37
+ }
38
+ nextDue(target) {
39
+ const [earliest] = this.sleeping
40
+ .filter(({ dueAt }) => dueAt <= target)
41
+ .sort((left, right) => left.dueAt - right.dueAt);
42
+ return earliest;
43
+ }
44
+ moveTo(instant) {
45
+ this.clock.advance(Math.max(0, instant - this.clock.now().getTime()));
46
+ }
47
+ }
48
+ /** One turn of the event loop: every promise that depends on promises alone has settled. Not a wait in time. */
49
+ function settled() {
50
+ return new Promise((resolve) => {
51
+ setImmediate(resolve);
52
+ });
53
+ }
54
+ //# sourceMappingURL=manual-job-timers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manual-job-timers.js","sourceRoot":"","sources":["../../src/testing/manual-job-timers.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,MAAM,OAAO,eAAe;IAGG,KAAK;IAF1B,QAAQ,GAAe,EAAE,CAAC;IAElC,YAA6B,KAAiB;qBAAjB,KAAK;IAAe,CAAC;IAElD,0CAA0C;IAC1C,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;IAC9B,CAAC;IAED,QAAQ,CAAC,OAAe,EAAE,IAAgB;QACxC,MAAM,QAAQ,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC;QAEvE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAE7B,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC;QACtE,CAAC,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAAC,UAAkB;QAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC;QAEvD,MAAM,OAAO,EAAE,CAAC;QAEhB,KAAK,IAAI,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,KAAK,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACtF,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC;YAChE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACxB,IAAI,CAAC,IAAI,EAAE,CAAC;YACZ,MAAM,OAAO,EAAE,CAAC;QAClB,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;IAEO,OAAO,CAAC,MAAc;QAC5B,MAAM,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,QAAQ;aAC7B,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,IAAI,MAAM,CAAC;aACtC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;QAEnD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,MAAM,CAAC,OAAe;QAC5B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACxE,CAAC;CACF;AAED,gHAAgH;AAChH,SAAS,OAAO;IACd,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,YAAY,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { ManualJobScheduler, type ManualJobSchedulerOptions } from './testing/manual-job-scheduler.js';
2
+ export { ManualJobTimers } from './testing/manual-job-timers.js';
3
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AACvG,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { ManualJobScheduler } from './testing/manual-job-scheduler.js';
2
+ export { ManualJobTimers } from './testing/manual-job-timers.js';
3
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAkC,MAAM,mCAAmC,CAAC;AACvG,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC"}