@hardlydifficult/daemon 1.0.1 → 1.0.2

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.
package/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # @hardlydifficult/daemon
2
2
 
3
- Idempotent resource teardown with signal trapping and LIFO cleanup ordering.
3
+ Opinionated utilities for long-running Node.js services:
4
+
5
+ - `createTeardown()` for idempotent cleanup with signal trapping
6
+ - `runContinuousLoop()` for daemon-style cycle execution
4
7
 
5
8
  ## Installation
6
9
 
@@ -8,107 +11,89 @@ Idempotent resource teardown with signal trapping and LIFO cleanup ordering.
8
11
  npm install @hardlydifficult/daemon
9
12
  ```
10
13
 
11
- ## Quick Start
14
+ ## Teardown management
15
+
16
+ Use `createTeardown()` to register cleanup functions once and execute them from
17
+ every exit path.
12
18
 
13
19
  ```typescript
14
- import { createTeardown, runContinuousLoop } from "@hardlydifficult/daemon";
20
+ import { createTeardown } from "@hardlydifficult/daemon";
15
21
 
16
- // Register cleanup functions
17
22
  const teardown = createTeardown();
18
- teardown.add(() => console.log("Closing server"));
19
- teardown.add(() => console.log("Closing database"));
23
+ teardown.add(() => server.stop());
24
+ teardown.add(async () => {
25
+ await db.close();
26
+ });
20
27
  teardown.trapSignals();
21
28
 
22
- // Run teardown when ready
23
29
  await teardown.run();
24
- // Logs:
25
- // Closing database
26
- // Closing server
27
30
  ```
28
31
 
29
- ## Teardown Management
32
+ Behavior:
30
33
 
31
- Resource cleanup registry with idempotent execution and LIFO ordering.
34
+ - LIFO execution (last added, first run)
35
+ - Idempotent `run()` (safe to call multiple times)
36
+ - Per-function error isolation (one failing teardown does not block others)
37
+ - `add()` returns an unregister function
32
38
 
33
- ### `createTeardown()`
39
+ ## Continuous loop execution
34
40
 
35
- Creates a teardown registry for managing resource cleanup.
41
+ Use `runContinuousLoop()` to run work cycles with graceful shutdown, dynamic
42
+ delay control, and configurable error policy.
36
43
 
37
44
  ```typescript
38
- const teardown = createTeardown();
39
- ```
45
+ import { runContinuousLoop } from "@hardlydifficult/daemon";
40
46
 
41
- #### `.add(fn: () => void | Promise<void>): () => void`
47
+ await runContinuousLoop({
48
+ intervalSeconds: 30,
49
+ async runCycle(isShutdownRequested) {
50
+ if (isShutdownRequested()) {
51
+ return { stop: true };
52
+ }
42
53
 
43
- Registers a teardown function. Returns an unregister function.
54
+ const didWork = await syncQueue();
55
+ if (!didWork) {
56
+ return 60_000; // ms
57
+ }
44
58
 
45
- ```typescript
46
- const teardown = createTeardown();
47
- const unregister = teardown.add(async () => {
48
- await server.close();
59
+ return "immediate";
60
+ },
61
+ onCycleError(error, context) {
62
+ notifyOps(error, { cycleNumber: context.cycleNumber });
63
+ return "continue"; // or "stop"
64
+ },
49
65
  });
50
-
51
- // Unregister before teardown if needed
52
- unregister();
53
66
  ```
54
67
 
55
- #### `.run(): Promise<void>`
56
-
57
- Runs all teardown functions in LIFO order. Idempotent — subsequent calls are no-ops.
58
-
59
- ```typescript
60
- const teardown = createTeardown();
61
- teardown.add(() => console.log("First"));
62
- teardown.add(() => console.log("Second"));
68
+ ### Cycle return contract
63
69
 
64
- await teardown.run();
65
- // Logs:
66
- // Second
67
- // First
68
- ```
70
+ `runCycle()` can return:
69
71
 
70
- #### `.trapSignals(): () => void`
72
+ - any value/`undefined`: use default `intervalSeconds`
73
+ - `number`: use that delay in milliseconds
74
+ - `"immediate"`: run the next cycle without sleeping
75
+ - `{ stop: true }`: stop gracefully after current cycle
76
+ - `{ nextDelayMs: number | "immediate", stop?: true }`: explicit control object
71
77
 
72
- Wires SIGTERM/SIGINT handlers to run teardown and exit. Returns an untrap function to remove handlers.
78
+ ### Optional delay resolver
73
79
 
74
- ```typescript
75
- const teardown = createTeardown();
76
- const untrap = teardown.trapSignals();
77
-
78
- // Later, to stop trapping signals
79
- untrap();
80
- ```
80
+ If your cycle returns domain data, derive schedule policy with
81
+ `getNextDelayMs(result, context)`.
81
82
 
82
- ## Continuous Loop Execution
83
+ ### Error handling
83
84
 
84
- Runs cyclic tasks with graceful shutdown support.
85
+ Use `onCycleError(error, context)` to route to Slack/Sentry and decide whether
86
+ to `"continue"` or `"stop"`. Without this hook, cycle errors are logged and the
87
+ loop continues.
85
88
 
86
- ### `runContinuousLoop(options: ContinuousLoopOptions): Promise<void>`
89
+ ### Logger injection
87
90
 
88
- Executes a cycle function repeatedly with interruptible sleep and signal handling.
91
+ By default, warnings and errors use `console.warn` and `console.error`. Pass
92
+ `logger` to integrate your own logging implementation:
89
93
 
90
94
  ```typescript
91
- import { runContinuousLoop } from "@hardlydifficult/daemon";
92
-
93
- await runContinuousLoop({
94
- intervalSeconds: 5,
95
- runCycle: async (isShutdownRequested) => {
96
- if (isShutdownRequested()) {
97
- return;
98
- }
99
- // Perform work here
100
- console.log("Running cycle...");
101
- },
102
- onShutdown: async () => {
103
- console.log("Shutdown complete");
104
- }
105
- });
95
+ const logger = {
96
+ warn: (message, context) => myLogger.warn(message, context),
97
+ error: (message, context) => myLogger.error(message, context),
98
+ };
106
99
  ```
107
-
108
- #### Options
109
-
110
- | Name | Type | Description |
111
- |-------------------|---------------------------------|------------------------------------------------|
112
- | `intervalSeconds` | `number` | Interval between cycles in seconds |
113
- | `runCycle` | `(isShutdownRequested: () => boolean) => Promise<unknown>` | Callback to run each cycle (shutdown check provided) |
114
- | `onShutdown?` | `() => Promise<void>` | Optional cleanup callback after shutdown |
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { createTeardown, type Teardown } from "./createTeardown.js";
2
- export { runContinuousLoop, type ContinuousLoopOptions, } from "./runContinuousLoop.js";
2
+ export { runContinuousLoop, type ContinuousLoopCycleContext, type ContinuousLoopCycleControl, type ContinuousLoopDelay, type ContinuousLoopErrorAction, type ContinuousLoopErrorHandler, type ContinuousLoopErrorContext, type ContinuousLoopLogger, type ContinuousLoopOptions, type ContinuousLoopRunCycleResult, } from "./runContinuousLoop.js";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EACL,iBAAiB,EACjB,KAAK,qBAAqB,GAC3B,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EACL,iBAAiB,EACjB,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,EAC/B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,4BAA4B,GAClC,MAAM,wBAAwB,CAAC"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yDAAoE;AAA3D,mHAAA,cAAc,OAAA;AACvB,+DAGgC;AAF9B,yHAAA,iBAAiB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yDAAoE;AAA3D,mHAAA,cAAc,OAAA;AACvB,+DAWgC;AAV9B,yHAAA,iBAAiB,OAAA"}
@@ -4,19 +4,62 @@
4
4
  * This module provides interruptible sleep and continuous loop functionality with proper signal handling for
5
5
  * SIGINT/SIGTERM.
6
6
  */
7
+ /** Delay directive returned by runCycle/getNextDelayMs. */
8
+ export type ContinuousLoopDelay = number | "immediate";
9
+ /** Context provided to each cycle and delay resolver. */
10
+ export interface ContinuousLoopCycleContext {
11
+ /** 1-based cycle number for the current loop iteration */
12
+ cycleNumber: number;
13
+ /** Function to check if shutdown has been requested */
14
+ isShutdownRequested: () => boolean;
15
+ }
16
+ /** Context provided to cycle error handling callbacks. */
17
+ export type ContinuousLoopErrorContext = ContinuousLoopCycleContext;
18
+ /** Action returned by onCycleError to control loop behavior. */
19
+ export type ContinuousLoopErrorAction = "continue" | "stop";
20
+ type ContinuousLoopErrorDecision = ContinuousLoopErrorAction | undefined;
21
+ /** Callback used to process cycle errors and decide loop policy. */
22
+ export type ContinuousLoopErrorHandler = (error: unknown, context: ContinuousLoopErrorContext) => ContinuousLoopErrorDecision | Promise<ContinuousLoopErrorDecision>;
23
+ /** Optional control directives that can be returned from runCycle. */
24
+ export interface ContinuousLoopCycleControl {
25
+ /** Stop the loop after this cycle completes. */
26
+ stop?: boolean;
27
+ /** Override delay before the next cycle. */
28
+ nextDelayMs?: ContinuousLoopDelay;
29
+ }
30
+ /** Minimal logger interface compatible with @hardlydifficult/logger. */
31
+ export interface ContinuousLoopLogger {
32
+ warn(message: string, context?: Readonly<Record<string, unknown>>): void;
33
+ error(message: string, context?: Readonly<Record<string, unknown>>): void;
34
+ }
35
+ /** Supported return shape from runCycle. */
36
+ export type ContinuousLoopRunCycleResult<TResult = unknown> = TResult | ContinuousLoopDelay | ContinuousLoopCycleControl;
7
37
  /** Options for running a continuous loop */
8
- export interface ContinuousLoopOptions {
38
+ export interface ContinuousLoopOptions<TResult = unknown> {
9
39
  /** Interval between cycles in seconds */
10
40
  intervalSeconds: number;
11
41
  /**
12
42
  * Callback to run on each cycle.
13
43
  *
14
44
  * @param isShutdownRequested - Function to check if shutdown has been requested during the cycle
15
- * @returns Promise that resolves when the cycle is complete (return value is ignored)
45
+ * @returns Promise resolving to cycle result and optional control directives
46
+ */
47
+ runCycle: (isShutdownRequested: () => boolean) => Promise<ContinuousLoopRunCycleResult<TResult>>;
48
+ /**
49
+ * Optional hook to derive the next delay from a runCycle result.
50
+ * Used only when runCycle does not directly return a delay directive.
51
+ */
52
+ getNextDelayMs?: (result: ContinuousLoopRunCycleResult<TResult>, context: ContinuousLoopCycleContext) => ContinuousLoopDelay | undefined;
53
+ /**
54
+ * Optional error callback for cycle failures.
55
+ *
56
+ * Return "stop" to end the loop, otherwise it will continue.
16
57
  */
17
- runCycle: (isShutdownRequested: () => boolean) => Promise<unknown>;
58
+ onCycleError?: ContinuousLoopErrorHandler;
18
59
  /** Optional callback for cleanup on shutdown */
19
- onShutdown?: () => Promise<void>;
60
+ onShutdown?: () => void | Promise<void>;
61
+ /** Optional logger (defaults to console warn/error) */
62
+ logger?: ContinuousLoopLogger;
20
63
  }
21
64
  /**
22
65
  * Run a function in a continuous loop with graceful shutdown support.
@@ -25,10 +68,13 @@ export interface ContinuousLoopOptions {
25
68
  *
26
69
  * - Interruptible sleep that responds immediately to SIGINT/SIGTERM
27
70
  * - Proper signal handler cleanup to prevent listener accumulation
28
- * - Continues to next cycle even if current cycle fails
71
+ * - Per-cycle delay control via return value or getNextDelayMs
72
+ * - Graceful stop signaling from runCycle ({ stop: true })
73
+ * - Configurable error policy via onCycleError
29
74
  * - Passes shutdown check callback to runCycle for in-cycle interruption
30
75
  *
31
76
  * @param options - Configuration for the continuous loop
32
77
  */
33
- export declare function runContinuousLoop(options: ContinuousLoopOptions): Promise<void>;
78
+ export declare function runContinuousLoop<TResult = unknown>(options: ContinuousLoopOptions<TResult>): Promise<void>;
79
+ export {};
34
80
  //# sourceMappingURL=runContinuousLoop.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runContinuousLoop.d.ts","sourceRoot":"","sources":["../src/runContinuousLoop.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,4CAA4C;AAC5C,MAAM,WAAW,qBAAqB;IACpC,yCAAyC;IACzC,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,EAAE,CAAC,mBAAmB,EAAE,MAAM,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACnE,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAqCD;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAuDf"}
1
+ {"version":3,"file":"runContinuousLoop.d.ts","sourceRoot":"","sources":["../src/runContinuousLoop.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,2DAA2D;AAC3D,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,WAAW,CAAC;AAEvD,yDAAyD;AACzD,MAAM,WAAW,0BAA0B;IACzC,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,mBAAmB,EAAE,MAAM,OAAO,CAAC;CACpC;AAED,0DAA0D;AAC1D,MAAM,MAAM,0BAA0B,GAAG,0BAA0B,CAAC;AAEpE,gEAAgE;AAChE,MAAM,MAAM,yBAAyB,GAAG,UAAU,GAAG,MAAM,CAAC;AAE5D,KAAK,2BAA2B,GAAG,yBAAyB,GAAG,SAAS,CAAC;AAEzE,oEAAoE;AACpE,MAAM,MAAM,0BAA0B,GAAG,CACvC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,0BAA0B,KAChC,2BAA2B,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;AAExE,sEAAsE;AACtE,MAAM,WAAW,0BAA0B;IACzC,gDAAgD;IAChD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,4CAA4C;IAC5C,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC;AAED,wEAAwE;AACxE,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IACzE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;CAC3E;AAED,4CAA4C;AAC5C,MAAM,MAAM,4BAA4B,CAAC,OAAO,GAAG,OAAO,IACtD,OAAO,GACP,mBAAmB,GACnB,0BAA0B,CAAC;AAE/B,4CAA4C;AAC5C,MAAM,WAAW,qBAAqB,CAAC,OAAO,GAAG,OAAO;IACtD,yCAAyC;IACzC,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,EAAE,CACR,mBAAmB,EAAE,MAAM,OAAO,KAC/B,OAAO,CAAC,4BAA4B,CAAC,OAAO,CAAC,CAAC,CAAC;IACpD;;;OAGG;IACH,cAAc,CAAC,EAAE,CACf,MAAM,EAAE,4BAA4B,CAAC,OAAO,CAAC,EAC7C,OAAO,EAAE,0BAA0B,KAChC,mBAAmB,GAAG,SAAS,CAAC;IACrC;;;;OAIG;IACH,YAAY,CAAC,EAAE,0BAA0B,CAAC;IAC1C,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,uDAAuD;IACvD,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AA2HD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,GAAG,OAAO,EACvD,OAAO,EAAE,qBAAqB,CAAC,OAAO,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAwGf"}
@@ -7,6 +7,22 @@
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.runContinuousLoop = runContinuousLoop;
10
+ const defaultLogger = {
11
+ warn(message, context) {
12
+ if (context === undefined) {
13
+ console.warn(message);
14
+ return;
15
+ }
16
+ console.warn(message, context);
17
+ },
18
+ error(message, context) {
19
+ if (context === undefined) {
20
+ console.error(message);
21
+ return;
22
+ }
23
+ console.error(message, context);
24
+ },
25
+ };
10
26
  /**
11
27
  * Creates an interruptible sleep that can be woken early by calling the returned cancel function.
12
28
  *
@@ -35,6 +51,57 @@ function createInterruptibleSleep(durationMs) {
35
51
  };
36
52
  return { promise, cancel };
37
53
  }
54
+ function normalizeDelayMs(delayMs, source) {
55
+ if (delayMs === "immediate") {
56
+ return delayMs;
57
+ }
58
+ if (!Number.isFinite(delayMs) || delayMs < 0) {
59
+ if (source === "intervalSeconds") {
60
+ throw new Error("intervalSeconds must be a non-negative finite number");
61
+ }
62
+ throw new Error(`${source} must return a non-negative finite number or "immediate"`);
63
+ }
64
+ return delayMs;
65
+ }
66
+ function getControlFromCycleResult(result) {
67
+ if (typeof result === "number" || result === "immediate") {
68
+ return { nextDelayMs: result };
69
+ }
70
+ if (typeof result !== "object" || result === null) {
71
+ return {};
72
+ }
73
+ const value = result;
74
+ const { nextDelayMs } = value;
75
+ return {
76
+ stop: value.stop === true,
77
+ nextDelayMs: typeof nextDelayMs === "number" || nextDelayMs === "immediate"
78
+ ? nextDelayMs
79
+ : undefined,
80
+ };
81
+ }
82
+ async function handleCycleError(error, context, onCycleError, logger) {
83
+ if (onCycleError !== undefined) {
84
+ try {
85
+ const action = await onCycleError(error, context);
86
+ return action === "stop" ? "stop" : "continue";
87
+ }
88
+ catch (handlerError) {
89
+ logger.error("onCycleError handler failed", {
90
+ cycleNumber: context.cycleNumber,
91
+ cycleError: error instanceof Error ? error.message : String(error),
92
+ handlerError: handlerError instanceof Error
93
+ ? handlerError.message
94
+ : String(handlerError),
95
+ });
96
+ return "continue";
97
+ }
98
+ }
99
+ logger.error("Cycle error", {
100
+ cycleNumber: context.cycleNumber,
101
+ error: error instanceof Error ? error.message : String(error),
102
+ });
103
+ return "continue";
104
+ }
38
105
  /**
39
106
  * Run a function in a continuous loop with graceful shutdown support.
40
107
  *
@@ -42,17 +109,21 @@ function createInterruptibleSleep(durationMs) {
42
109
  *
43
110
  * - Interruptible sleep that responds immediately to SIGINT/SIGTERM
44
111
  * - Proper signal handler cleanup to prevent listener accumulation
45
- * - Continues to next cycle even if current cycle fails
112
+ * - Per-cycle delay control via return value or getNextDelayMs
113
+ * - Graceful stop signaling from runCycle ({ stop: true })
114
+ * - Configurable error policy via onCycleError
46
115
  * - Passes shutdown check callback to runCycle for in-cycle interruption
47
116
  *
48
117
  * @param options - Configuration for the continuous loop
49
118
  */
50
119
  async function runContinuousLoop(options) {
51
- const { intervalSeconds, runCycle, onShutdown } = options;
120
+ const { intervalSeconds, runCycle, getNextDelayMs, onCycleError, onShutdown, logger = defaultLogger, } = options;
121
+ const defaultDelayMs = normalizeDelayMs(intervalSeconds * 1000, "intervalSeconds");
52
122
  let shutdownRequested = false;
53
123
  let cancelCurrentSleep = null;
124
+ let cycleNumber = 0;
54
125
  const handleShutdown = (signal) => {
55
- console.warn(`Received ${signal}, shutting down gracefully...`);
126
+ logger.warn(`Received ${signal}, shutting down gracefully...`);
56
127
  shutdownRequested = true;
57
128
  if (cancelCurrentSleep !== null) {
58
129
  cancelCurrentSleep();
@@ -71,17 +142,46 @@ async function runContinuousLoop(options) {
71
142
  const shouldContinue = () => !shutdownRequested;
72
143
  try {
73
144
  while (shouldContinue()) {
145
+ cycleNumber += 1;
146
+ const cycleContext = {
147
+ cycleNumber,
148
+ isShutdownRequested,
149
+ };
150
+ let nextDelayMs = defaultDelayMs;
74
151
  try {
75
- await runCycle(isShutdownRequested);
152
+ const cycleResult = await runCycle(isShutdownRequested);
153
+ if (!shouldContinue()) {
154
+ continue;
155
+ }
156
+ const cycleControl = getControlFromCycleResult(cycleResult);
157
+ if (cycleControl.stop === true) {
158
+ shutdownRequested = true;
159
+ continue;
160
+ }
161
+ if (cycleControl.nextDelayMs !== undefined) {
162
+ nextDelayMs = normalizeDelayMs(cycleControl.nextDelayMs, "runCycle");
163
+ }
164
+ else if (getNextDelayMs !== undefined) {
165
+ const derivedDelay = getNextDelayMs(cycleResult, cycleContext);
166
+ if (derivedDelay !== undefined) {
167
+ nextDelayMs = normalizeDelayMs(derivedDelay, "getNextDelayMs");
168
+ }
169
+ }
76
170
  }
77
171
  catch (error) {
78
- const errorMessage = error instanceof Error ? error.message : String(error);
79
- console.error(`Cycle error: ${errorMessage}`);
172
+ const action = await handleCycleError(error, cycleContext, onCycleError, logger);
173
+ if (action === "stop") {
174
+ shutdownRequested = true;
175
+ continue;
176
+ }
80
177
  }
81
178
  if (!shouldContinue()) {
82
179
  break;
83
180
  }
84
- const sleep = createInterruptibleSleep(intervalSeconds * 1000);
181
+ if (nextDelayMs === "immediate") {
182
+ continue;
183
+ }
184
+ const sleep = createInterruptibleSleep(nextDelayMs);
85
185
  cancelCurrentSleep = sleep.cancel;
86
186
  await sleep.promise;
87
187
  cancelCurrentSleep = null;
@@ -1 +1 @@
1
- {"version":3,"file":"runContinuousLoop.js","sourceRoot":"","sources":["../src/runContinuousLoop.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runContinuousLoop.js","sourceRoot":"","sources":["../src/runContinuousLoop.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardlydifficult/daemon",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [