@hardlydifficult/daemon 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # @hardlydifficult/teardown
2
+
3
+ Idempotent resource teardown with signal trapping. Register cleanup functions once at resource creation time — all exit paths call a single `run()`.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @hardlydifficult/teardown
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { createTeardown } from "@hardlydifficult/teardown";
15
+
16
+ const teardown = createTeardown();
17
+ teardown.add(() => server.stop());
18
+ teardown.add(() => db.close());
19
+ teardown.trapSignals();
20
+
21
+ // Any manual exit path:
22
+ await teardown.run();
23
+ ```
24
+
25
+ ## Creating a Teardown Registry
26
+
27
+ Use `createTeardown()` to create a new teardown registry that manages cleanup functions.
28
+
29
+ ```typescript
30
+ import { createTeardown } from "@hardlydifficult/teardown";
31
+
32
+ const teardown = createTeardown();
33
+ ```
34
+
35
+ ## Registering Cleanup Functions
36
+
37
+ Call `add()` to register a cleanup function. Functions run in LIFO order (last added runs first). The `add()` method returns an unregister function for selective cleanup.
38
+
39
+ ```typescript
40
+ const teardown = createTeardown();
41
+
42
+ // Register sync cleanup
43
+ teardown.add(() => {
44
+ console.log("Closing server");
45
+ server.stop();
46
+ });
47
+
48
+ // Register async cleanup
49
+ teardown.add(async () => {
50
+ console.log("Closing database");
51
+ await db.close();
52
+ });
53
+
54
+ // Unregister a specific function
55
+ const unregister = teardown.add(() => {
56
+ console.log("This won't run");
57
+ });
58
+ unregister();
59
+
60
+ await teardown.run();
61
+ // Output:
62
+ // Closing database
63
+ // Closing server
64
+ ```
65
+
66
+ ## Running Teardown
67
+
68
+ Call `run()` to execute all registered cleanup functions in LIFO order. The method is idempotent — subsequent calls are no-ops.
69
+
70
+ ```typescript
71
+ const teardown = createTeardown();
72
+ teardown.add(() => console.log("cleanup"));
73
+
74
+ await teardown.run();
75
+ // Output: cleanup
76
+
77
+ await teardown.run();
78
+ // No output (idempotent)
79
+ ```
80
+
81
+ ## Signal Trapping
82
+
83
+ Call `trapSignals()` to automatically run teardown when the process receives SIGTERM or SIGINT signals. Returns an untrap function to remove signal handlers.
84
+
85
+ ```typescript
86
+ const teardown = createTeardown();
87
+ teardown.add(() => server.stop());
88
+
89
+ // Wire SIGTERM/SIGINT to run() then process.exit(0)
90
+ const untrap = teardown.trapSignals();
91
+
92
+ // Later, if needed:
93
+ untrap();
94
+ ```
95
+
96
+ ## Error Resilience
97
+
98
+ Each teardown function is wrapped in try/catch. Errors don't block remaining teardowns — all functions run regardless of failures.
99
+
100
+ ```typescript
101
+ const teardown = createTeardown();
102
+
103
+ teardown.add(() => {
104
+ throw new Error("First cleanup fails");
105
+ });
106
+ teardown.add(() => {
107
+ console.log("Second cleanup still runs");
108
+ });
109
+
110
+ await teardown.run();
111
+ // Output: Second cleanup still runs
112
+ ```
113
+
114
+ ## Behavior Reference
115
+
116
+ | Behavior | Details |
117
+ |----------|---------|
118
+ | **LIFO order** | Teardowns run in reverse registration order (last added runs first) |
119
+ | **Idempotent** | `run()` executes once; subsequent calls are no-ops |
120
+ | **Error resilient** | Each function is wrapped in try/catch; failures don't block remaining teardowns |
121
+ | **Safe unregister** | `add()` returns an unregister function; safe to call multiple times |
122
+ | **Post-run add** | `add()` after `run()` is a silent no-op |
123
+ | **Duplicate safe** | Same function added twice runs twice; unregister only removes its own registration |
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Idempotent resource teardown with signal trapping
3
+ *
4
+ * Register cleanup functions once at resource creation time.
5
+ * All exit paths call a single `run()` — no duplication, no missed resources.
6
+ */
7
+ export interface Teardown {
8
+ /** Register a teardown function. Returns an unregister function. */
9
+ add(fn: () => void | Promise<void>): () => void;
10
+ /** Run all teardown functions in LIFO order. Idempotent: second call is a no-op. */
11
+ run(): Promise<void>;
12
+ /** Wire SIGTERM/SIGINT to run() then process.exit(0). Returns an untrap function. */
13
+ trapSignals(): () => void;
14
+ }
15
+ /**
16
+ * Create a teardown registry
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const teardown = createTeardown();
21
+ * teardown.add(() => server.stop());
22
+ * teardown.add(() => db.close());
23
+ * teardown.trapSignals();
24
+ *
25
+ * // Any manual exit path:
26
+ * await teardown.run();
27
+ * ```
28
+ */
29
+ export declare function createTeardown(): Teardown;
30
+ //# sourceMappingURL=createTeardown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createTeardown.d.ts","sourceRoot":"","sources":["../src/createTeardown.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,oEAAoE;IACpE,GAAG,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC;IAChD,oFAAoF;IACpF,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACrB,qFAAqF;IACrF,WAAW,IAAI,MAAM,IAAI,CAAC;CAC3B;AAOD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,IAAI,QAAQ,CAsDzC"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createTeardown = createTeardown;
4
+ /**
5
+ * Create a teardown registry
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const teardown = createTeardown();
10
+ * teardown.add(() => server.stop());
11
+ * teardown.add(() => db.close());
12
+ * teardown.trapSignals();
13
+ *
14
+ * // Any manual exit path:
15
+ * await teardown.run();
16
+ * ```
17
+ */
18
+ function createTeardown() {
19
+ const entries = [];
20
+ let hasRun = false;
21
+ const run = async () => {
22
+ if (hasRun) {
23
+ return;
24
+ }
25
+ hasRun = true;
26
+ for (let i = entries.length - 1; i >= 0; i--) {
27
+ const entry = entries[i];
28
+ if (entry.removed) {
29
+ continue;
30
+ }
31
+ try {
32
+ await entry.fn();
33
+ }
34
+ catch {
35
+ // Swallow errors per-fn so remaining teardowns still run
36
+ }
37
+ }
38
+ };
39
+ return {
40
+ add(fn) {
41
+ if (hasRun) {
42
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
43
+ return () => { };
44
+ }
45
+ const entry = { fn, removed: false };
46
+ entries.push(entry);
47
+ return () => {
48
+ entry.removed = true;
49
+ };
50
+ },
51
+ run,
52
+ trapSignals() {
53
+ const onSignal = () => {
54
+ void run().then(() => process.exit(0));
55
+ };
56
+ process.on("SIGTERM", onSignal);
57
+ process.on("SIGINT", onSignal);
58
+ return () => {
59
+ process.off("SIGTERM", onSignal);
60
+ process.off("SIGINT", onSignal);
61
+ };
62
+ },
63
+ };
64
+ }
65
+ //# sourceMappingURL=createTeardown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createTeardown.js","sourceRoot":"","sources":["../src/createTeardown.ts"],"names":[],"mappings":";;AAkCA,wCAsDC;AApED;;;;;;;;;;;;;GAaG;AACH,SAAgB,cAAc;IAC5B,MAAM,OAAO,GAAY,EAAE,CAAC;IAC5B,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,MAAM,GAAG,GAAG,KAAK,IAAmB,EAAE;QACpC,IAAI,MAAM,EAAE,CAAC;YACX,OAAO;QACT,CAAC;QACD,MAAM,GAAG,IAAI,CAAC;QAEd,KAAK,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACzB,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBAClB,SAAS;YACX,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,KAAK,CAAC,EAAE,EAAE,CAAC;YACnB,CAAC;YAAC,MAAM,CAAC;gBACP,yDAAyD;YAC3D,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;QACL,GAAG,CAAC,EAA8B;YAChC,IAAI,MAAM,EAAE,CAAC;gBACX,gEAAgE;gBAChE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;YAClB,CAAC;YAED,MAAM,KAAK,GAAU,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;YAC5C,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAEpB,OAAO,GAAG,EAAE;gBACV,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;YACvB,CAAC,CAAC;QACJ,CAAC;QAED,GAAG;QAEH,WAAW;YACT,MAAM,QAAQ,GAAG,GAAS,EAAE;gBAC1B,KAAK,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACzC,CAAC,CAAC;YAEF,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YAChC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAE/B,OAAO,GAAG,EAAE;gBACV,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;gBACjC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAClC,CAAC,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { createTeardown, type Teardown } from "./createTeardown.js";
2
+ export { runContinuousLoop, type ContinuousLoopOptions, } from "./runContinuousLoop.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runContinuousLoop = exports.createTeardown = void 0;
4
+ var createTeardown_js_1 = require("./createTeardown.js");
5
+ Object.defineProperty(exports, "createTeardown", { enumerable: true, get: function () { return createTeardown_js_1.createTeardown; } });
6
+ var runContinuousLoop_js_1 = require("./runContinuousLoop.js");
7
+ Object.defineProperty(exports, "runContinuousLoop", { enumerable: true, get: function () { return runContinuousLoop_js_1.runContinuousLoop; } });
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Shared utilities for graceful shutdown and continuous loop execution.
3
+ *
4
+ * This module provides interruptible sleep and continuous loop functionality with proper signal handling for
5
+ * SIGINT/SIGTERM.
6
+ */
7
+ /** Options for running a continuous loop */
8
+ export interface ContinuousLoopOptions {
9
+ /** Interval between cycles in seconds */
10
+ intervalSeconds: number;
11
+ /**
12
+ * Callback to run on each cycle.
13
+ *
14
+ * @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)
16
+ */
17
+ runCycle: (isShutdownRequested: () => boolean) => Promise<unknown>;
18
+ /** Optional callback for cleanup on shutdown */
19
+ onShutdown?: () => Promise<void>;
20
+ }
21
+ /**
22
+ * Run a function in a continuous loop with graceful shutdown support.
23
+ *
24
+ * Features:
25
+ *
26
+ * - Interruptible sleep that responds immediately to SIGINT/SIGTERM
27
+ * - Proper signal handler cleanup to prevent listener accumulation
28
+ * - Continues to next cycle even if current cycle fails
29
+ * - Passes shutdown check callback to runCycle for in-cycle interruption
30
+ *
31
+ * @param options - Configuration for the continuous loop
32
+ */
33
+ export declare function runContinuousLoop(options: ContinuousLoopOptions): Promise<void>;
34
+ //# sourceMappingURL=runContinuousLoop.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ /**
3
+ * Shared utilities for graceful shutdown and continuous loop execution.
4
+ *
5
+ * This module provides interruptible sleep and continuous loop functionality with proper signal handling for
6
+ * SIGINT/SIGTERM.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.runContinuousLoop = runContinuousLoop;
10
+ /**
11
+ * Creates an interruptible sleep that can be woken early by calling the returned cancel function.
12
+ *
13
+ * @returns An object with a promise that resolves after durationMs or when cancel() is called
14
+ */
15
+ function createInterruptibleSleep(durationMs) {
16
+ let resolve = null;
17
+ let timeout = null;
18
+ const promise = new Promise((r) => {
19
+ resolve = r;
20
+ timeout = setTimeout(() => {
21
+ timeout = null;
22
+ resolve = null;
23
+ r();
24
+ }, durationMs);
25
+ });
26
+ const cancel = () => {
27
+ if (timeout !== null) {
28
+ clearTimeout(timeout);
29
+ timeout = null;
30
+ }
31
+ if (resolve !== null) {
32
+ resolve();
33
+ resolve = null;
34
+ }
35
+ };
36
+ return { promise, cancel };
37
+ }
38
+ /**
39
+ * Run a function in a continuous loop with graceful shutdown support.
40
+ *
41
+ * Features:
42
+ *
43
+ * - Interruptible sleep that responds immediately to SIGINT/SIGTERM
44
+ * - Proper signal handler cleanup to prevent listener accumulation
45
+ * - Continues to next cycle even if current cycle fails
46
+ * - Passes shutdown check callback to runCycle for in-cycle interruption
47
+ *
48
+ * @param options - Configuration for the continuous loop
49
+ */
50
+ async function runContinuousLoop(options) {
51
+ const { intervalSeconds, runCycle, onShutdown } = options;
52
+ let shutdownRequested = false;
53
+ let cancelCurrentSleep = null;
54
+ const handleShutdown = (signal) => {
55
+ console.warn(`Received ${signal}, shutting down gracefully...`);
56
+ shutdownRequested = true;
57
+ if (cancelCurrentSleep !== null) {
58
+ cancelCurrentSleep();
59
+ cancelCurrentSleep = null;
60
+ }
61
+ };
62
+ const sigintHandler = () => {
63
+ handleShutdown("SIGINT");
64
+ };
65
+ const sigtermHandler = () => {
66
+ handleShutdown("SIGTERM");
67
+ };
68
+ process.on("SIGINT", sigintHandler);
69
+ process.on("SIGTERM", sigtermHandler);
70
+ const isShutdownRequested = () => shutdownRequested;
71
+ const shouldContinue = () => !shutdownRequested;
72
+ try {
73
+ while (shouldContinue()) {
74
+ try {
75
+ await runCycle(isShutdownRequested);
76
+ }
77
+ catch (error) {
78
+ const errorMessage = error instanceof Error ? error.message : String(error);
79
+ console.error(`Cycle error: ${errorMessage}`);
80
+ }
81
+ if (!shouldContinue()) {
82
+ break;
83
+ }
84
+ const sleep = createInterruptibleSleep(intervalSeconds * 1000);
85
+ cancelCurrentSleep = sleep.cancel;
86
+ await sleep.promise;
87
+ cancelCurrentSleep = null;
88
+ }
89
+ }
90
+ finally {
91
+ process.off("SIGINT", sigintHandler);
92
+ process.off("SIGTERM", sigtermHandler);
93
+ if (onShutdown !== undefined) {
94
+ await onShutdown();
95
+ }
96
+ }
97
+ }
98
+ //# sourceMappingURL=runContinuousLoop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runContinuousLoop.js","sourceRoot":"","sources":["../src/runContinuousLoop.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@hardlydifficult/daemon",
3
+ "version": "1.0.0",
4
+ "main": "./dist/index.js",
5
+ "types": "./dist/index.d.ts",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "test": "vitest run",
12
+ "test:watch": "vitest",
13
+ "test:coverage": "vitest run --coverage",
14
+ "lint": "tsc --noEmit",
15
+ "clean": "rm -rf dist"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "25.2.3",
19
+ "typescript": "5.9.3",
20
+ "vitest": "4.0.18"
21
+ },
22
+ "engines": {
23
+ "node": ">=18.0.0"
24
+ }
25
+ }