service-keepalive 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/CHANGELOG.md ADDED
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2026-08-16
9
+
10
+ ### Added
11
+
12
+ - **Core Library (`KeepAlive`)**:
13
+ - Programmable keep-alive scheduler with explicit `.start()`, `.stop()`, and `.pingOnce()` lifecycle.
14
+ - Native `fetch` HTTP requests with zero heavy network dependencies.
15
+ - Accurate high-resolution response timing via `performance.now()`.
16
+ - Configurable timeouts using `AbortController`.
17
+ - Configurable HTTP methods, custom headers, and optional request bodies.
18
+ - Status matcher validation (default 2xx range, custom status code lists, or custom predicate functions).
19
+ - **Multi-Service Manager (`MultiKeepAlive`)**:
20
+ - Support for monitoring and pinging multiple endpoints simultaneously with individual or shared schedules.
21
+ - **Intelligent Retries**:
22
+ - Exponential, linear, and fixed backoff strategies with ±20% randomized jitter.
23
+ - Maximum delay boundaries and cancellable sleep timers.
24
+ - **Human-Readable Duration Parsing**:
25
+ - Supports `ms`, `s`, `m`, `h`, `d` (e.g. `10s`, `1m`, `5m`, `10m`, `1h`, `500ms`).
26
+ - Strict validation guarding against zero, negative numbers, and malformed strings.
27
+ - **CLI & NPX Support**:
28
+ - Executable `service-keepalive` binary.
29
+ - Flags for `--url`, `--interval`, `--timeout`, `--method`, `--retries`, `--retry-delay`, `--header`, `--config`, `--once`, `--quiet`, `--verbose`.
30
+ - Signal trapping for clean `SIGINT` (Ctrl+C) and `SIGTERM` shutdown.
31
+ - **Configuration & Environment Variables**:
32
+ - Automatic loading of `keepalive.config.json`, `.keepaliverc`, and `.js`/`.mjs`/`.cjs` config files.
33
+ - Environment variable overrides (`KEEPALIVE_URL`, `KEEPALIVE_INTERVAL`, `KEEPALIVE_TIMEOUT`, etc.).
34
+ - **Security & Secret Masking**:
35
+ - Automatic redaction of sensitive request headers (`Authorization`, `Cookie`, `X-Api-Key`, `Token`, `Secret`) and URL query parameters in logs.
36
+ - **Developer Experience & Tooling**:
37
+ - Dual ESM and CommonJS bundle via `tsup` with `.d.ts` and `.d.cts` declarations.
38
+ - Comprehensive deterministic test suite powered by Vitest and local mock HTTP servers.
39
+ - ESLint 9 flat config + Prettier.
40
+ - Production-ready Dockerfile and GitHub Actions workflows (`ci.yml`, `keepalive.yml`, `publish.yml`).
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 service-keepalive contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,420 @@
1
+ # service-keepalive
2
+
3
+ > Production-ready, lightweight HTTP keep-alive utility and CLI to prevent idle service spin-down on cloud platforms where inbound traffic maintains active status.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/service-keepalive.svg?style=flat-square)](https://www.npmjs.com/package/service-keepalive)
6
+ [![CI](https://github.com/EZDevanshu/service-keepalive/actions/workflows/ci.yml/badge.svg)](https://github.com/EZDevanshu/service-keepalive/actions/workflows/ci.yml)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](https://opensource.org/licenses/MIT)
8
+ [![Node.js Version](https://img.shields.io/node/v/service-keepalive.svg?style=flat-square)](https://nodejs.org)
9
+
10
+ ---
11
+
12
+ ## What is it?
13
+
14
+ Many cloud hosting platforms (such as **Render**, **Fly.io**, **Railway**, or **Koyeb**) offer free or hobby-tier web services that automatically spin down into an idle state after 15–30 minutes of inactivity. When a new user requests the service, it can suffer a cold-start delay of 30–60 seconds while the container wakes up.
15
+
16
+ `service-keepalive` periodically sends lightweight HTTP requests to your deployed web service so that inbound traffic keeps the service warm and responsive.
17
+
18
+ ### ⚠️ IMPORTANT: External Execution Model
19
+
20
+ ```
21
+ ┌─────────────────────────────────────────────────────────┐
22
+ │ EXTERNAL RUNNER (Where service-keepalive MUST run) │
23
+ │ │
24
+ │ • Your Local PC / Mac / Raspberry Pi │
25
+ │ • VPS (DigitalOcean, Hetzner, AWS EC2, Linode, etc.) │
26
+ │ • GitHub Actions Scheduled Workflow (Cron) │
27
+ │ • Docker Container on home server / NAS │
28
+ └────────────────────────────┬────────────────────────────┘
29
+ │
30
+ Periodic HTTP Pings (e.g. every 10m)
31
+ │
32
+ ▼
33
+ ┌─────────────────────────────────────────────────────────┐
34
+ │ TARGET CLOUD SERVICE (e.g. Render, Railway, Fly.io) │
35
+ │ │
36
+ │ • Web API / Backend Server │
37
+ │ • Woken up & kept active by inbound HTTP traffic │
38
+ └─────────────────────────────────────────────────────────┘
39
+ ```
40
+
41
+ > [!IMPORTANT]
42
+ > **This package is NOT hosted inside your target server.**
43
+ >
44
+ > It must run **externally** (on your local machine, a separate VPS, a Docker container, or a scheduled CI runner like GitHub Actions). If your server is asleep, internal background timers cannot wake it up. An **external** inbound request is required.
45
+
46
+ ---
47
+
48
+ ## Hosting Provider Policy & Terms of Service
49
+
50
+ > [!WARNING]
51
+ >
52
+ > - **Compliance**: Always check your hosting provider’s **Terms of Service (ToS)**, Acceptable Use Policy, and free-tier limits before setting up keep-alive pings.
53
+ > - **Free Tier Quotas**: Free-tier plans often have a monthly quota of active instance hours (e.g., Render provides 750 free instance hours per month shared across all your free web services). Keeping a service active 24/7 consumes 720–744 hours per month, which may deplete your free tier allowance for other services.
54
+ > - **No Guarantees**: This package does **not** bypass provider-level billing or platform restrictions, nor does it guarantee 100% uptime. Platforms can modify their sleep policies at any time.
55
+
56
+ ---
57
+
58
+ ## Features
59
+
60
+ - ⚡ **Zero Heavy Dependencies**: Uses Node.js native `fetch` and lightweight utilities.
61
+ - 🕒 **Human-Readable Intervals**: Accepts durations like `10s`, `1m`, `5m`, `10m`, `1h`, or numeric milliseconds.
62
+ - 🔄 **Configurable Retries & Backoff**: Exponential, linear, or fixed backoff with jitter to handle intermittent network hiccups gracefully.
63
+ - 🛡️ **Timeout & AbortController**: Every ping is guarded by a configurable timeout so requests never hang indefinitely.
64
+ - 🔒 **Security-First**: Automatically redacts authorization headers, API keys, cookies, and secret tokens in logs.
65
+ - 🚦 **Multiple Services**: Keep one or dozens of endpoints warm with individual or shared schedules.
66
+ - 💻 **CLI & NPX Ready**: Instant execution with zero setup using `npx service-keepalive`.
67
+ - 📦 **Dual ESM & CommonJS**: Full compatibility with modern ESM projects and legacy CommonJS runtimes.
68
+ - 🛑 **Graceful Shutdown**: Intercepts `SIGINT` (Ctrl+C) and `SIGTERM` to safely clean up in-flight requests and timers.
69
+ - 🤖 **GitHub Actions & Docker**: Ready-to-use scheduled workflow and multi-stage container image.
70
+
71
+ ---
72
+
73
+ ## Installation
74
+
75
+ ### Run directly without installation (CLI)
76
+
77
+ ```bash
78
+ npx service-keepalive --url https://example.onrender.com/health --interval 10m
79
+ ```
80
+
81
+ ### Install globally (CLI)
82
+
83
+ ```bash
84
+ npm install -g service-keepalive
85
+ service-keepalive --url https://example.onrender.com/health
86
+ ```
87
+
88
+ ### Install as project dependency (Library API)
89
+
90
+ ```bash
91
+ npm install service-keepalive
92
+ ```
93
+
94
+ ---
95
+
96
+ ## CLI Usage
97
+
98
+ ### Basic Command
99
+
100
+ ```bash
101
+ service-keepalive --url https://example.onrender.com/health --interval 10m
102
+ ```
103
+
104
+ ### CLI Flags & Options
105
+
106
+ | Option | Shorthand | Description | Default |
107
+ | :------------------------- | :-------- | :------------------------------------------------- | :---------------------- |
108
+ | `--url <url>` | `-u` | Target endpoint URL to ping | _Required_ |
109
+ | `--interval <duration>` | `-i` | Interval between pings (`10s`, `5m`, `10m`, `1h`) | `10m` |
110
+ | `--timeout <duration>` | `-t` | Request timeout before aborting (`10s`, `30s`) | `30s` |
111
+ | `--method <method>` | `-m` | HTTP method (`GET`, `POST`, `HEAD`, etc.) | `GET` |
112
+ | `--retries <number>` | `-r` | Number of retry attempts on failure | `3` |
113
+ | `--retry-delay <duration>` | | Base delay before retrying failed requests | `5s` |
114
+ | `--header <key:value>` | `-H` | Custom header (can be specified multiple times) | |
115
+ | `--config <path>` | `-c` | Path to JSON or JS configuration file | `keepalive.config.json` |
116
+ | `--once` | | Execute a single ping cycle and exit (for Cron/CI) | `false` |
117
+ | `--quiet` | `-q` | Suppress routine logs; only output errors | `false` |
118
+ | `--verbose` | `-v` | Enable detailed debug logs and response headers | `false` |
119
+ | `--help` | `-h` | Display help screen | |
120
+ | `--version` | `-V` | Output package version | |
121
+
122
+ ### CLI Examples
123
+
124
+ ```bash
125
+ # Ping every 5 minutes with a 15-second timeout
126
+ npx service-keepalive -u https://api.example.com/health -i 5m -t 15s
127
+
128
+ # Send custom headers (e.g. authentication or custom user-agent)
129
+ npx service-keepalive -u https://api.example.com/health -H "Authorization: Bearer mytoken" -H "X-Client: KeepAlive"
130
+
131
+ # Run a single ping check (exits with code 0 on success, 1 on failure)
132
+ npx service-keepalive -u https://api.example.com/health --once
133
+
134
+ # Run using a configuration file
135
+ npx service-keepalive --config keepalive.config.json
136
+ ```
137
+
138
+ ---
139
+
140
+ ## Library Usage
141
+
142
+ ### TypeScript Example
143
+
144
+ ```typescript
145
+ import { KeepAlive, PingResult } from 'service-keepalive';
146
+
147
+ const keepAlive = new KeepAlive({
148
+ url: 'https://example.onrender.com/health',
149
+ interval: '10m', // Ping every 10 minutes
150
+ timeout: '30s', // 30s timeout per request
151
+ method: 'GET',
152
+ retries: 3,
153
+ retryDelay: '5s',
154
+ headers: {
155
+ 'User-Agent': 'service-keepalive-bot/1.0',
156
+ },
157
+ });
158
+
159
+ // Listen to lifecycle and telemetry events
160
+ keepAlive.on('start', name => console.log(`Started keepalive for ${name}`));
161
+ keepAlive.on('ping', ({ url, attempt }) => console.log(`Pinging ${url} (Attempt ${attempt})`));
162
+ keepAlive.on('success', (result: PingResult) =>
163
+ console.log(`✓ ${result.status} in ${result.durationMs}ms`),
164
+ );
165
+ keepAlive.on('failure', (result: PingResult) => console.error(`✗ Failed: ${result.error}`));
166
+ keepAlive.on('retry', info =>
167
+ console.warn(`↻ Retrying in ${info.delayMs}ms due to: ${info.error}`),
168
+ );
169
+
170
+ // Explicitly start the keep-alive scheduler
171
+ keepAlive.start();
172
+
173
+ // Gracefully stop whenever needed
174
+ // await keepAlive.stop();
175
+ ```
176
+
177
+ ### JavaScript (ESM) Example
178
+
179
+ ```javascript
180
+ import { KeepAlive } from 'service-keepalive';
181
+
182
+ const keepAlive = new KeepAlive({
183
+ url: 'https://api.example.com/health',
184
+ interval: '5m',
185
+ });
186
+
187
+ keepAlive.start();
188
+ ```
189
+
190
+ ### JavaScript (CommonJS) Example
191
+
192
+ ```javascript
193
+ const { KeepAlive } = require('service-keepalive');
194
+
195
+ const keepAlive = new KeepAlive({
196
+ url: 'https://api.example.com/health',
197
+ interval: '5m',
198
+ });
199
+
200
+ keepAlive.start();
201
+ ```
202
+
203
+ ---
204
+
205
+ ## Multiple Services
206
+
207
+ Manage and ping multiple endpoints concurrently with unified or per-service schedules using `MultiKeepAlive`:
208
+
209
+ ```typescript
210
+ import { MultiKeepAlive } from 'service-keepalive';
211
+
212
+ const multi = new MultiKeepAlive({
213
+ // Global defaults applied to all services
214
+ defaults: {
215
+ timeout: '30s',
216
+ retries: 3,
217
+ retryDelay: '5s',
218
+ },
219
+ services: [
220
+ {
221
+ name: 'backend-api',
222
+ url: 'https://backend.example.onrender.com/health',
223
+ interval: '10m',
224
+ },
225
+ {
226
+ name: 'auth-service',
227
+ url: 'https://auth.example.onrender.com/status',
228
+ interval: '15m',
229
+ },
230
+ {
231
+ name: 'worker-node',
232
+ url: 'https://worker.example.onrender.com/ping',
233
+ interval: '5m',
234
+ },
235
+ ],
236
+ });
237
+
238
+ // Starts keep-alive for all services
239
+ multi.start();
240
+
241
+ // Stop all services when shutting down
242
+ process.on('SIGINT', async () => {
243
+ await multi.stop();
244
+ process.exit(0);
245
+ });
246
+ ```
247
+
248
+ ---
249
+
250
+ ## Configuration File
251
+
252
+ You can store your settings in a configuration file (`keepalive.config.json` or `keepalive.config.js`).
253
+
254
+ ### Example `keepalive.config.json`
255
+
256
+ ```json
257
+ {
258
+ "defaults": {
259
+ "timeout": "30s",
260
+ "retries": 3,
261
+ "retryDelay": "5s",
262
+ "retryStrategy": "exponential"
263
+ },
264
+ "services": [
265
+ {
266
+ "name": "backend-api",
267
+ "url": "https://example.onrender.com/health",
268
+ "interval": "10m"
269
+ },
270
+ {
271
+ "name": "auth-service",
272
+ "url": "https://auth.example.com/status",
273
+ "interval": "15m",
274
+ "headers": {
275
+ "User-Agent": "service-keepalive/custom"
276
+ }
277
+ }
278
+ ]
279
+ }
280
+ ```
281
+
282
+ Run with:
283
+
284
+ ```bash
285
+ npx service-keepalive
286
+ # Or with explicit config path:
287
+ npx service-keepalive --config ./path/to/keepalive.config.json
288
+ ```
289
+
290
+ ---
291
+
292
+ ## Environment Variables
293
+
294
+ All primary settings can be configured via environment variables:
295
+
296
+ | Variable | Description | Example |
297
+ | :---------------------- | :-------------------------------------- | :------------------------------------ |
298
+ | `KEEPALIVE_URL` | Target service URL | `https://example.onrender.com/health` |
299
+ | `KEEPALIVE_INTERVAL` | Ping interval | `10m` |
300
+ | `KEEPALIVE_TIMEOUT` | Request timeout | `30s` |
301
+ | `KEEPALIVE_METHOD` | HTTP method | `GET` |
302
+ | `KEEPALIVE_RETRIES` | Max retries | `3` |
303
+ | `KEEPALIVE_RETRY_DELAY` | Base retry delay | `5s` |
304
+ | `KEEPALIVE_HEADERS` | Headers (JSON or comma-separated pairs) | `{"Authorization":"Bearer ..."}` |
305
+ | `KEEPALIVE_CONFIG` | Path to configuration file | `./keepalive.config.json` |
306
+ | `KEEPALIVE_QUIET` | Suppress routine logs | `true` |
307
+ | `KEEPALIVE_VERBOSE` | Enable debug logs | `true` |
308
+
309
+ ---
310
+
311
+ ## GitHub Actions
312
+
313
+ You can use GitHub Actions to ping your service periodically for free without hosting a long-running daemon.
314
+
315
+ > [!NOTE]
316
+ > **Schedule Delays / Jitter**: GitHub Actions cron schedules run on a best-effort basis. During high GitHub infrastructure load, scheduled runs may occasionally be delayed by a few minutes.
317
+
318
+ Create `.github/workflows/keepalive.yml`:
319
+
320
+ ```yaml
321
+ name: Service Keep-Alive
322
+
323
+ on:
324
+ schedule:
325
+ # Run every 10 minutes
326
+ - cron: '*/10 * * * *'
327
+ workflow_dispatch:
328
+
329
+ jobs:
330
+ keepalive:
331
+ name: Ping Service
332
+ runs-on: ubuntu-latest
333
+ steps:
334
+ - name: Setup Node.js
335
+ uses: actions/setup-node@v4
336
+ with:
337
+ node-version: '20'
338
+
339
+ - name: Execute Ping
340
+ env:
341
+ TARGET_URL: ${{ secrets.KEEPALIVE_URL }}
342
+ run: |
343
+ npx service-keepalive "$TARGET_URL" --timeout 30s --retries 3 --once
344
+ ```
345
+
346
+ ---
347
+
348
+ ## Docker Support
349
+
350
+ A lightweight Dockerfile is included for running `service-keepalive` as a background container on your VPS, server, or Raspberry Pi.
351
+
352
+ ### Build the image
353
+
354
+ ```bash
355
+ docker build -t service-keepalive .
356
+ ```
357
+
358
+ ### Run the container
359
+
360
+ ```bash
361
+ docker run -d \
362
+ --name keepalive \
363
+ --restart unless-stopped \
364
+ -e KEEPALIVE_URL="https://example.onrender.com/health" \
365
+ -e KEEPALIVE_INTERVAL="10m" \
366
+ service-keepalive
367
+ ```
368
+
369
+ ---
370
+
371
+ ## Configuration Reference
372
+
373
+ | Property | Type | Default | Description |
374
+ | :-------------------- | :------------------------------------- | :-------------- | :--------------------------------------------------------- |
375
+ | `url` | `string` | _Required_ | Target URL to ping (http or https) |
376
+ | `name` | `string` | URL hostname | Friendly identifier for logs and events |
377
+ | `interval` | `string \| number` | `'10m'` | Interval between consecutive pings (`10s`, `5m`, `1h`, ms) |
378
+ | `timeout` | `string \| number` | `'30s'` | Request timeout duration before aborting |
379
+ | `method` | `string` | `'GET'` | HTTP request method (`GET`, `POST`, `HEAD`, etc.) |
380
+ | `headers` | `Record<string, string>` | `{}` | Custom request headers |
381
+ | `body` | `string \| null` | `null` | Optional request body for POST/PUT requests |
382
+ | `retries` | `number` | `3` | Max retry attempts upon failure |
383
+ | `retryDelay` | `string \| number` | `'5s'` | Initial base delay before retrying |
384
+ | `retryStrategy` | `'exponential' \| 'linear' \| 'fixed'` | `'exponential'` | Delay calculation algorithm |
385
+ | `retryJitter` | `boolean` | `true` | Applies ±20% randomization to prevent request collisions |
386
+ | `maxRetryDelay` | `string \| number` | `'60s'` | Maximum upper boundary for retry delays |
387
+ | `expectedStatusCodes` | `number[] \| Function` | `200..299` | Status codes considered successful |
388
+ | `logLevel` | `'quiet' \| 'normal' \| 'verbose'` | `'normal'` | Terminal logging verbosity |
389
+ | `logger` | `LoggerInterface \| false` | Built-in | Custom logger instance or `false` to disable |
390
+ | `unrefTimer` | `boolean` | `false` | Whether timers allow Node event loop to exit |
391
+
392
+ ---
393
+
394
+ ## Troubleshooting
395
+
396
+ ### 1. "Request timed out after 30000ms"
397
+
398
+ - **Cause**: The service was spun down and cold-starting, taking longer than the configured timeout to boot and respond.
399
+ - **Solution**: Increase the timeout setting to `60s` or `90s` (e.g. `--timeout 60s`).
400
+
401
+ ### 2. "Request failed - HTTP 404 / 500"
402
+
403
+ - **Cause**: The target health check URL path does not exist on your service or your server threw an unhandled error.
404
+ - **Solution**: Verify the endpoint URL in your browser or curl (e.g. ensure `/health` or `/` returns a 200 OK status).
405
+
406
+ ### 3. "My service still spun down despite keep-alive pings"
407
+
408
+ - **Cause 1**: The interval might be too long (e.g., your provider spins down after 15 minutes, but your interval was 20m). Set interval to `10m` or `5m`.
409
+ - **Cause 2**: You might have exhausted your monthly free-tier instance hours.
410
+ - **Cause 3**: Ensure `service-keepalive` is running **externally**, not deployed within the sleeping instance itself.
411
+
412
+ ---
413
+
414
+ ## Contributing
415
+
416
+ Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and testing guidelines.
417
+
418
+ ## License
419
+
420
+ This project is licensed under the [MIT License](LICENSE).