@push.rocks/smarttime 4.1.0 → 4.2.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.
@@ -1,8 +1,6 @@
1
1
  import * as plugins from './smarttime.plugins.js';
2
2
  import { CronManager } from './smarttime.classes.cronmanager.js';
3
3
 
4
- import { CronParser } from './smarttime.classes.cronparser.js';
5
-
6
4
  export type TJobFunction =
7
5
  | ((triggerTimeArg?: number) => void)
8
6
  | ((triggerTimeArg?: number) => Promise<any>);
@@ -24,6 +22,9 @@ export class CronJob {
24
22
  * checks wether the cronjob needs to be executed
25
23
  */
26
24
  public checkExecution(): number {
25
+ if (this.status === 'stopped') {
26
+ return this.nextExecutionUnix;
27
+ }
27
28
  if (this.nextExecutionUnix === 0) {
28
29
  this.getNextExecutionTime();
29
30
  }
@@ -1,6 +1,5 @@
1
1
  import * as plugins from './smarttime.plugins.js';
2
2
  import { CronJob, type TJobFunction } from './smarttime.classes.cronjob.js';
3
- import { getMilliSecondsAsHumanReadableString } from './smarttime.units.js';
4
3
 
5
4
  export class CronManager {
6
5
  public executionTimeout: plugins.smartdelay.Timeout<void>;
@@ -8,13 +7,26 @@ export class CronManager {
8
7
  public status: 'started' | 'stopped' = 'stopped';
9
8
  public cronjobs = new plugins.lik.ObjectMap<CronJob>();
10
9
 
10
+ private cycleWakeDeferred: plugins.smartpromise.Deferred<void> | null = null;
11
+
11
12
  constructor() {}
12
13
 
14
+ /**
15
+ * Resolves the current wake deferred, causing the sleeping cycle
16
+ * to immediately recalculate instead of waiting for its timeout.
17
+ */
18
+ private wakeCycle() {
19
+ if (this.cycleWakeDeferred && this.cycleWakeDeferred.status === 'pending') {
20
+ this.cycleWakeDeferred.resolve();
21
+ }
22
+ }
23
+
13
24
  public addCronjob(cronIdentifierArg: string, cronFunctionArg: TJobFunction) {
14
25
  const newCronJob = new CronJob(this, cronIdentifierArg, cronFunctionArg);
15
26
  this.cronjobs.add(newCronJob);
16
27
  if (this.status === 'started') {
17
28
  newCronJob.start();
29
+ this.wakeCycle();
18
30
  }
19
31
 
20
32
  return newCronJob;
@@ -23,6 +35,9 @@ export class CronManager {
23
35
  public removeCronjob(cronjobArg: CronJob) {
24
36
  cronjobArg.stop();
25
37
  this.cronjobs.remove(cronjobArg);
38
+ if (this.status === 'started') {
39
+ this.wakeCycle();
40
+ }
26
41
  }
27
42
 
28
43
  /**
@@ -39,35 +54,39 @@ export class CronManager {
39
54
  }
40
55
 
41
56
  private async runCronCycle() {
42
- this.executionTimeout = new plugins.smartdelay.Timeout(0);
43
- do {
44
- let nextRunningCronjob: CronJob;
57
+ while (this.status === 'started') {
58
+ // Create a fresh wake signal for this iteration
59
+ this.cycleWakeDeferred = new plugins.smartpromise.Deferred<void>();
60
+
61
+ // Check all cronjobs and find the soonest next execution
62
+ let soonestMs = Infinity;
45
63
  for (const cronJob of this.cronjobs.getArray()) {
46
64
  cronJob.checkExecution();
47
- if (
48
- !nextRunningCronjob ||
49
- cronJob.getTimeToNextExecution() < nextRunningCronjob.getTimeToNextExecution()
50
- ) {
51
- nextRunningCronjob = cronJob;
65
+ const msToNext = cronJob.getTimeToNextExecution();
66
+ if (msToNext < soonestMs) {
67
+ soonestMs = msToNext;
52
68
  }
53
69
  }
54
- if (nextRunningCronjob) {
55
- this.executionTimeout = new plugins.smartdelay.Timeout(
56
- nextRunningCronjob.getTimeToNextExecution()
57
- );
58
- console.log(
59
- `Next CronJob scheduled in ${getMilliSecondsAsHumanReadableString(
60
- this.executionTimeout.getTimeLeft()
61
- )}`
62
- );
70
+
71
+ // Sleep until the next job is due or until woken by a lifecycle event
72
+ if (soonestMs < Infinity && soonestMs > 0) {
73
+ this.executionTimeout = new plugins.smartdelay.Timeout(soonestMs);
74
+ await Promise.race([
75
+ this.executionTimeout.promise,
76
+ this.cycleWakeDeferred.promise,
77
+ ]);
78
+ // Cancel the timeout to avoid lingering timers
79
+ this.executionTimeout.cancel();
80
+ } else if (soonestMs <= 0) {
81
+ // Job is overdue, loop immediately to execute it
82
+ continue;
63
83
  } else {
64
- this.executionTimeout = new plugins.smartdelay.Timeout(1000);
65
- console.log('no cronjobs specified! Checking again in 1 second');
84
+ // No jobs — wait indefinitely until woken by addCronjob or stop
85
+ await this.cycleWakeDeferred.promise;
66
86
  }
67
-
68
- await this.executionTimeout.promise;
69
- } while (this.status === 'started');
70
- };
87
+ }
88
+ this.cycleWakeDeferred = null;
89
+ }
71
90
 
72
91
  /**
73
92
  * stops all cronjobs
@@ -75,9 +94,10 @@ export class CronManager {
75
94
  public stop() {
76
95
  if (this.status === 'started') {
77
96
  this.status = 'stopped';
78
- this.executionTimeout.cancel();
79
- } else {
80
- console.log(`You tried to stop a CronManager that was not actually started.`);
97
+ if (this.executionTimeout) {
98
+ this.executionTimeout.cancel();
99
+ }
100
+ this.wakeCycle();
81
101
  }
82
102
  for (const cron of this.cronjobs.getArray()) {
83
103
  cron.stop();
@@ -69,5 +69,5 @@ export const getMilliSecondsAsHumanReadableString = (milliSecondsArg: number): s
69
69
  };
70
70
 
71
71
  export const getMilliSecondsAsHumanReadableAgoTime = (timeStampArg: number): string => {
72
- return plugins.dateFns.formatDistanceToNow(timeStampArg);
72
+ return plugins.dateFns.formatDistanceToNow(new Date(timeStampArg));
73
73
  }