@openstatus/health-disk 0.1.4-dev.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 +70 -0
- package/dist/_virtual/rolldown_runtime.cjs +30 -0
- package/dist/mod.cjs +66 -0
- package/dist/mod.d.cts +54 -0
- package/dist/mod.d.cts.map +1 -0
- package/dist/mod.d.ts +54 -0
- package/dist/mod.d.ts.map +1 -0
- package/dist/mod.js +62 -0
- package/dist/mod.js.map +1 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# @openstatus/health-disk
|
|
2
|
+
|
|
3
|
+
Disk space probe for [`@openstatus/health`](https://jsr.io/@openstatus/health).
|
|
4
|
+
Reads the free space of the filesystem holding `path` and fails the check
|
|
5
|
+
when it drops below a threshold — the input a readiness probe needs on
|
|
6
|
+
anything that writes to local disk: SQLite, uploads, logs, a build cache.
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
deno add jsr:@openstatus/health jsr:@openstatus/health-disk
|
|
10
|
+
npm install @openstatus/health @openstatus/health-disk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createHealthHandler } from "@openstatus/health";
|
|
15
|
+
import { diskProbe } from "@openstatus/health-disk";
|
|
16
|
+
|
|
17
|
+
Deno.serve(
|
|
18
|
+
createHealthHandler({
|
|
19
|
+
probes: [diskProbe({ path: "/data", minFreePercent: 10 })],
|
|
20
|
+
}),
|
|
21
|
+
);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`path` can be any file or directory; the probe reports on the filesystem
|
|
25
|
+
that holds it, so `/data` and `/data/app.db` answer the same. Free space is
|
|
26
|
+
what an unprivileged process can still use (`bavail`), which is what your
|
|
27
|
+
writes see once root's reserved blocks are excluded. The check fails below
|
|
28
|
+
`minFreePercent` (10% by default) or below `minFreeBytes` when that is set
|
|
29
|
+
instead; pass both to enforce both. A threshold failure throws
|
|
30
|
+
`DiskSpaceError`, which carries the `usage`; a `statfs` error or an
|
|
31
|
+
unreadable result fails the check with that error instead.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
diskProbe({
|
|
35
|
+
path: "/var/lib/app",
|
|
36
|
+
minFreeBytes: 2 * 1024 ** 3,
|
|
37
|
+
// optional overrides from the Probe contract
|
|
38
|
+
name: "data-volume",
|
|
39
|
+
critical: true,
|
|
40
|
+
timeoutMs: 500,
|
|
41
|
+
skip: () => env.DATA_DIR == null,
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Non-critical by default: a disk filling up degrades the report early enough
|
|
46
|
+
to act on before writes start failing. Set `critical: true` when the
|
|
47
|
+
instance should stop taking traffic instead — a database volume, for
|
|
48
|
+
instance.
|
|
49
|
+
|
|
50
|
+
This probe uses `fs.promises.statfs` from `node:fs`, so it runs on Node.js,
|
|
51
|
+
Deno and Bun but not on edge runtimes. `statfs` is typed structurally and
|
|
52
|
+
can be replaced for tests.
|
|
53
|
+
|
|
54
|
+
## About openstatus
|
|
55
|
+
|
|
56
|
+
[openstatus](https://www.openstatus.dev/) is the open-source uptime monitoring
|
|
57
|
+
and status page platform. This package is part of
|
|
58
|
+
[`@openstatus/health`](https://github.com/openstatusHQ/health), the `/health`
|
|
59
|
+
endpoints behind openstatus's own services, extracted so any JavaScript server
|
|
60
|
+
can expose one. Point an
|
|
61
|
+
[openstatus monitor](https://www.openstatus.dev/docs/reference/http-monitor)
|
|
62
|
+
at the endpoint and assert on `status` in the body to be alerted on
|
|
63
|
+
`degraded` before it becomes `unhealthy`.
|
|
64
|
+
|
|
65
|
+
Source: [github.com/openstatusHQ/health](https://github.com/openstatusHQ/health).
|
|
66
|
+
Issues and PRs welcome.
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
[MIT](https://github.com/openstatusHQ/health/blob/main/LICENSE)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
|
|
25
|
+
Object.defineProperty(exports, '__toESM', {
|
|
26
|
+
enumerable: true,
|
|
27
|
+
get: function () {
|
|
28
|
+
return __toESM;
|
|
29
|
+
}
|
|
30
|
+
});
|
package/dist/mod.cjs
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
2
|
+
const node_fs_promises = require_rolldown_runtime.__toESM(require("node:fs/promises"));
|
|
3
|
+
const __openstatus_health = require_rolldown_runtime.__toESM(require("@openstatus/health"));
|
|
4
|
+
|
|
5
|
+
//#region src/mod.ts
|
|
6
|
+
/** Probe name when `name` is unset. */
|
|
7
|
+
const diskDefaultName = "disk";
|
|
8
|
+
/** Path when `path` is unset. */
|
|
9
|
+
const diskDefaultPath = ".";
|
|
10
|
+
/** Threshold when neither `minFreePercent` nor `minFreeBytes` is set. */
|
|
11
|
+
const diskDefaultMinFreePercent = 10;
|
|
12
|
+
/** Thrown by the probe when free space is below the threshold. */
|
|
13
|
+
var DiskSpaceError = class extends Error {
|
|
14
|
+
/** The usage that crossed the threshold. */
|
|
15
|
+
usage;
|
|
16
|
+
/** Build the error for `usage` against the threshold described by `reason`. */
|
|
17
|
+
constructor(usage, reason) {
|
|
18
|
+
super(reason);
|
|
19
|
+
this.name = "DiskSpaceError";
|
|
20
|
+
this.usage = usage;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
/** A probe that fails when free space is below `minFreePercent` or `minFreeBytes`; non-critical by default. Throws `ProbeConfigError` for an empty `path` or a negative threshold. */
|
|
24
|
+
function diskProbe(options = {}) {
|
|
25
|
+
const path = options.path ?? diskDefaultPath;
|
|
26
|
+
if (typeof path !== "string" || path.length === 0) throw new __openstatus_health.ProbeConfigError("diskProbe", "path", typeof path !== "string" ? `must be a string, got ${String(path)}` : "must not be empty");
|
|
27
|
+
const minFreeBytes = options.minFreeBytes;
|
|
28
|
+
const minFreePercent = options.minFreePercent ?? (minFreeBytes == null ? diskDefaultMinFreePercent : void 0);
|
|
29
|
+
for (const [field, value] of [["minFreePercent", minFreePercent], ["minFreeBytes", minFreeBytes]]) if (value != null && (!Number.isFinite(value) || value < 0)) throw new __openstatus_health.ProbeConfigError("diskProbe", field, `must be a non-negative number, got ${String(value)}`);
|
|
30
|
+
const statfs = options.statfs ?? node_fs_promises.statfs;
|
|
31
|
+
return {
|
|
32
|
+
name: options.name ?? diskDefaultName,
|
|
33
|
+
critical: options.critical ?? false,
|
|
34
|
+
timeoutMs: options.timeoutMs,
|
|
35
|
+
skip: options.skip,
|
|
36
|
+
run: async () => {
|
|
37
|
+
const stats = await statfs(path);
|
|
38
|
+
const usage = toUsage(stats);
|
|
39
|
+
if (minFreeBytes != null && usage.freeBytes < minFreeBytes) throw new DiskSpaceError(usage, `${usage.freeBytes} bytes free, fewer than ${minFreeBytes}`);
|
|
40
|
+
if (minFreePercent != null && usage.freeBytes / usage.totalBytes * 100 < minFreePercent) throw new DiskSpaceError(usage, `${usage.freePercent}% free, less than ${minFreePercent}%`);
|
|
41
|
+
return usage;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function toUsage(stats) {
|
|
46
|
+
const { bsize, blocks, bavail } = stats;
|
|
47
|
+
if (![
|
|
48
|
+
bsize,
|
|
49
|
+
blocks,
|
|
50
|
+
bavail
|
|
51
|
+
].every((n) => Number.isFinite(n) && n >= 0) || bsize === 0 || blocks === 0) throw new Error("unexpected statfs result");
|
|
52
|
+
const totalBytes = blocks * bsize;
|
|
53
|
+
const freeBytes = bavail * bsize;
|
|
54
|
+
return {
|
|
55
|
+
freeBytes,
|
|
56
|
+
totalBytes,
|
|
57
|
+
freePercent: Math.round(freeBytes / totalBytes * 1e3) / 10
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
exports.DiskSpaceError = DiskSpaceError;
|
|
63
|
+
exports.diskDefaultMinFreePercent = diskDefaultMinFreePercent;
|
|
64
|
+
exports.diskDefaultName = diskDefaultName;
|
|
65
|
+
exports.diskDefaultPath = diskDefaultPath;
|
|
66
|
+
exports.diskProbe = diskProbe;
|
package/dist/mod.d.cts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Probe, ProbeOverrides } from "@openstatus/health";
|
|
2
|
+
|
|
3
|
+
//#region src/mod.d.ts
|
|
4
|
+
|
|
5
|
+
/** Probe name when `name` is unset. */
|
|
6
|
+
declare const diskDefaultName = "disk";
|
|
7
|
+
/** Path when `path` is unset. */
|
|
8
|
+
declare const diskDefaultPath = ".";
|
|
9
|
+
/** Threshold when neither `minFreePercent` nor `minFreeBytes` is set. */
|
|
10
|
+
declare const diskDefaultMinFreePercent = 10;
|
|
11
|
+
/** The subset of `fs.StatsFs` the probe reads. */
|
|
12
|
+
interface DiskStats {
|
|
13
|
+
/** Block size in bytes. */
|
|
14
|
+
readonly bsize: number;
|
|
15
|
+
/** Total blocks. */
|
|
16
|
+
readonly blocks: number;
|
|
17
|
+
/** Blocks available to unprivileged users. */
|
|
18
|
+
readonly bavail: number;
|
|
19
|
+
}
|
|
20
|
+
/** What the probe reads the filesystem with; `fs.promises.statfs` by default. */
|
|
21
|
+
type DiskStatfs = (path: string) => PromiseLike<DiskStats>;
|
|
22
|
+
/** Options for `diskProbe()`. */
|
|
23
|
+
interface DiskProbeOptions extends ProbeOverrides {
|
|
24
|
+
/** Any path on the filesystem to check. Default `diskDefaultPath`. */
|
|
25
|
+
readonly path?: string;
|
|
26
|
+
/** Fail when free space drops below this percentage of the total. Default `diskDefaultMinFreePercent` unless `minFreeBytes` is set. */
|
|
27
|
+
readonly minFreePercent?: number;
|
|
28
|
+
/** Fail when fewer bytes than this are free. */
|
|
29
|
+
readonly minFreeBytes?: number;
|
|
30
|
+
/** Replacement `statfs`, for tests. */
|
|
31
|
+
readonly statfs?: DiskStatfs;
|
|
32
|
+
}
|
|
33
|
+
/** What a healthy check resolves with. */
|
|
34
|
+
interface DiskUsage {
|
|
35
|
+
/** Bytes available. */
|
|
36
|
+
readonly freeBytes: number;
|
|
37
|
+
/** Bytes in total. */
|
|
38
|
+
readonly totalBytes: number;
|
|
39
|
+
/** `freeBytes` as a percentage of `totalBytes`, rounded to one decimal. */
|
|
40
|
+
readonly freePercent: number;
|
|
41
|
+
}
|
|
42
|
+
/** Thrown by the probe when free space is below the threshold. */
|
|
43
|
+
declare class DiskSpaceError extends Error {
|
|
44
|
+
/** The usage that crossed the threshold. */
|
|
45
|
+
readonly usage: DiskUsage;
|
|
46
|
+
/** Build the error for `usage` against the threshold described by `reason`. */
|
|
47
|
+
constructor(usage: DiskUsage, reason: string);
|
|
48
|
+
}
|
|
49
|
+
/** A probe that fails when free space is below `minFreePercent` or `minFreeBytes`; non-critical by default. Throws `ProbeConfigError` for an empty `path` or a negative threshold. */
|
|
50
|
+
declare function diskProbe(options?: DiskProbeOptions): Probe;
|
|
51
|
+
//# sourceMappingURL=mod.d.ts.map
|
|
52
|
+
//#endregion
|
|
53
|
+
export { DiskProbeOptions, DiskSpaceError, DiskStatfs, DiskStats, DiskUsage, diskDefaultMinFreePercent, diskDefaultName, diskDefaultPath, diskProbe };
|
|
54
|
+
//# sourceMappingURL=mod.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.d.cts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;AA2CwD,cApB3C,eAAA,GAoB2C,MAAA;AAYxD;AAUa,cAxCA,eAAA,GAwCe,GAAA;;AAEV,cAxCL,yBAAA,GAwCK,EAAA;;AAFkB,UAnCnB,SAAA,CAmCmB;EAAK;EAazB,SAAA,KAAS,EAAA,MAAA;EAAA;EAAA,SAAU,MAAA,EAAA,MAAA;EAAqB;EAAQ,SAAA,MAAA,EAAA,MAAA;;;KAtCpD,UAAA,qBAA+B,YAAY;;UAGtC,gBAAA,SAAyB;;;;;;;;oBAQtB;;;UAIH,SAAA;;;;;;;;;cAUJ,cAAA,SAAuB,KAAA;;kBAElB;;qBAGG;;;iBAQL,SAAA,WAAmB,mBAAwB"}
|
package/dist/mod.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Probe, ProbeOverrides } from "@openstatus/health";
|
|
2
|
+
|
|
3
|
+
//#region src/mod.d.ts
|
|
4
|
+
|
|
5
|
+
/** Probe name when `name` is unset. */
|
|
6
|
+
declare const diskDefaultName = "disk";
|
|
7
|
+
/** Path when `path` is unset. */
|
|
8
|
+
declare const diskDefaultPath = ".";
|
|
9
|
+
/** Threshold when neither `minFreePercent` nor `minFreeBytes` is set. */
|
|
10
|
+
declare const diskDefaultMinFreePercent = 10;
|
|
11
|
+
/** The subset of `fs.StatsFs` the probe reads. */
|
|
12
|
+
interface DiskStats {
|
|
13
|
+
/** Block size in bytes. */
|
|
14
|
+
readonly bsize: number;
|
|
15
|
+
/** Total blocks. */
|
|
16
|
+
readonly blocks: number;
|
|
17
|
+
/** Blocks available to unprivileged users. */
|
|
18
|
+
readonly bavail: number;
|
|
19
|
+
}
|
|
20
|
+
/** What the probe reads the filesystem with; `fs.promises.statfs` by default. */
|
|
21
|
+
type DiskStatfs = (path: string) => PromiseLike<DiskStats>;
|
|
22
|
+
/** Options for `diskProbe()`. */
|
|
23
|
+
interface DiskProbeOptions extends ProbeOverrides {
|
|
24
|
+
/** Any path on the filesystem to check. Default `diskDefaultPath`. */
|
|
25
|
+
readonly path?: string;
|
|
26
|
+
/** Fail when free space drops below this percentage of the total. Default `diskDefaultMinFreePercent` unless `minFreeBytes` is set. */
|
|
27
|
+
readonly minFreePercent?: number;
|
|
28
|
+
/** Fail when fewer bytes than this are free. */
|
|
29
|
+
readonly minFreeBytes?: number;
|
|
30
|
+
/** Replacement `statfs`, for tests. */
|
|
31
|
+
readonly statfs?: DiskStatfs;
|
|
32
|
+
}
|
|
33
|
+
/** What a healthy check resolves with. */
|
|
34
|
+
interface DiskUsage {
|
|
35
|
+
/** Bytes available. */
|
|
36
|
+
readonly freeBytes: number;
|
|
37
|
+
/** Bytes in total. */
|
|
38
|
+
readonly totalBytes: number;
|
|
39
|
+
/** `freeBytes` as a percentage of `totalBytes`, rounded to one decimal. */
|
|
40
|
+
readonly freePercent: number;
|
|
41
|
+
}
|
|
42
|
+
/** Thrown by the probe when free space is below the threshold. */
|
|
43
|
+
declare class DiskSpaceError extends Error {
|
|
44
|
+
/** The usage that crossed the threshold. */
|
|
45
|
+
readonly usage: DiskUsage;
|
|
46
|
+
/** Build the error for `usage` against the threshold described by `reason`. */
|
|
47
|
+
constructor(usage: DiskUsage, reason: string);
|
|
48
|
+
}
|
|
49
|
+
/** A probe that fails when free space is below `minFreePercent` or `minFreeBytes`; non-critical by default. Throws `ProbeConfigError` for an empty `path` or a negative threshold. */
|
|
50
|
+
declare function diskProbe(options?: DiskProbeOptions): Probe;
|
|
51
|
+
//# sourceMappingURL=mod.d.ts.map
|
|
52
|
+
//#endregion
|
|
53
|
+
export { DiskProbeOptions, DiskSpaceError, DiskStatfs, DiskStats, DiskUsage, diskDefaultMinFreePercent, diskDefaultName, diskDefaultPath, diskProbe };
|
|
54
|
+
//# sourceMappingURL=mod.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;AA2CwD,cApB3C,eAAA,GAoB2C,MAAA;AAYxD;AAUa,cAxCA,eAAA,GAwCe,GAAA;;AAEV,cAxCL,yBAAA,GAwCK,EAAA;;AAFkB,UAnCnB,SAAA,CAmCmB;EAAK;EAazB,SAAA,KAAS,EAAA,MAAA;EAAA;EAAA,SAAU,MAAA,EAAA,MAAA;EAAqB;EAAQ,SAAA,MAAA,EAAA,MAAA;;;KAtCpD,UAAA,qBAA+B,YAAY;;UAGtC,gBAAA,SAAyB;;;;;;;;oBAQtB;;;UAIH,SAAA;;;;;;;;;cAUJ,cAAA,SAAuB,KAAA;;kBAElB;;qBAGG;;;iBAQL,SAAA,WAAmB,mBAAwB"}
|
package/dist/mod.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { statfs } from "node:fs/promises";
|
|
2
|
+
import { ProbeConfigError } from "@openstatus/health";
|
|
3
|
+
|
|
4
|
+
//#region src/mod.ts
|
|
5
|
+
/** Probe name when `name` is unset. */
|
|
6
|
+
const diskDefaultName = "disk";
|
|
7
|
+
/** Path when `path` is unset. */
|
|
8
|
+
const diskDefaultPath = ".";
|
|
9
|
+
/** Threshold when neither `minFreePercent` nor `minFreeBytes` is set. */
|
|
10
|
+
const diskDefaultMinFreePercent = 10;
|
|
11
|
+
/** Thrown by the probe when free space is below the threshold. */
|
|
12
|
+
var DiskSpaceError = class extends Error {
|
|
13
|
+
/** The usage that crossed the threshold. */
|
|
14
|
+
usage;
|
|
15
|
+
/** Build the error for `usage` against the threshold described by `reason`. */
|
|
16
|
+
constructor(usage, reason) {
|
|
17
|
+
super(reason);
|
|
18
|
+
this.name = "DiskSpaceError";
|
|
19
|
+
this.usage = usage;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
/** A probe that fails when free space is below `minFreePercent` or `minFreeBytes`; non-critical by default. Throws `ProbeConfigError` for an empty `path` or a negative threshold. */
|
|
23
|
+
function diskProbe(options = {}) {
|
|
24
|
+
const path = options.path ?? diskDefaultPath;
|
|
25
|
+
if (typeof path !== "string" || path.length === 0) throw new ProbeConfigError("diskProbe", "path", typeof path !== "string" ? `must be a string, got ${String(path)}` : "must not be empty");
|
|
26
|
+
const minFreeBytes = options.minFreeBytes;
|
|
27
|
+
const minFreePercent = options.minFreePercent ?? (minFreeBytes == null ? diskDefaultMinFreePercent : void 0);
|
|
28
|
+
for (const [field, value] of [["minFreePercent", minFreePercent], ["minFreeBytes", minFreeBytes]]) if (value != null && (!Number.isFinite(value) || value < 0)) throw new ProbeConfigError("diskProbe", field, `must be a non-negative number, got ${String(value)}`);
|
|
29
|
+
const statfs$1 = options.statfs ?? statfs;
|
|
30
|
+
return {
|
|
31
|
+
name: options.name ?? diskDefaultName,
|
|
32
|
+
critical: options.critical ?? false,
|
|
33
|
+
timeoutMs: options.timeoutMs,
|
|
34
|
+
skip: options.skip,
|
|
35
|
+
run: async () => {
|
|
36
|
+
const stats = await statfs$1(path);
|
|
37
|
+
const usage = toUsage(stats);
|
|
38
|
+
if (minFreeBytes != null && usage.freeBytes < minFreeBytes) throw new DiskSpaceError(usage, `${usage.freeBytes} bytes free, fewer than ${minFreeBytes}`);
|
|
39
|
+
if (minFreePercent != null && usage.freeBytes / usage.totalBytes * 100 < minFreePercent) throw new DiskSpaceError(usage, `${usage.freePercent}% free, less than ${minFreePercent}%`);
|
|
40
|
+
return usage;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function toUsage(stats) {
|
|
45
|
+
const { bsize, blocks, bavail } = stats;
|
|
46
|
+
if (![
|
|
47
|
+
bsize,
|
|
48
|
+
blocks,
|
|
49
|
+
bavail
|
|
50
|
+
].every((n) => Number.isFinite(n) && n >= 0) || bsize === 0 || blocks === 0) throw new Error("unexpected statfs result");
|
|
51
|
+
const totalBytes = blocks * bsize;
|
|
52
|
+
const freeBytes = bavail * bsize;
|
|
53
|
+
return {
|
|
54
|
+
freeBytes,
|
|
55
|
+
totalBytes,
|
|
56
|
+
freePercent: Math.round(freeBytes / totalBytes * 1e3) / 10
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
export { DiskSpaceError, diskDefaultMinFreePercent, diskDefaultName, diskDefaultPath, diskProbe };
|
|
62
|
+
//# sourceMappingURL=mod.js.map
|
package/dist/mod.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.js","names":["usage: DiskUsage","reason: string","options: DiskProbeOptions","statfs","fsStatfs","stats: DiskStats"],"sources":["../src/mod.ts"],"sourcesContent":["/**\n * Disk space probe for `@openstatus/health`: reads the free space of the\n * filesystem holding `path` and fails below a threshold.\n *\n * ```ts\n * import { diskProbe } from \"@openstatus/health-disk\";\n *\n * const probe = diskProbe({ path: \"/data\", minFreePercent: 10 });\n * ```\n *\n * Node.js, Deno and Bun only: it uses `node:fs`.\n *\n * @module\n */\n\nimport { statfs as fsStatfs } from \"node:fs/promises\";\nimport {\n type Probe,\n ProbeConfigError,\n type ProbeOverrides,\n} from \"@openstatus/health\";\n\n/** Probe name when `name` is unset. */\nexport const diskDefaultName = \"disk\";\n/** Path when `path` is unset. */\nexport const diskDefaultPath = \".\";\n/** Threshold when neither `minFreePercent` nor `minFreeBytes` is set. */\nexport const diskDefaultMinFreePercent = 10;\n\n/** The subset of `fs.StatsFs` the probe reads. */\nexport interface DiskStats {\n /** Block size in bytes. */\n readonly bsize: number;\n /** Total blocks. */\n readonly blocks: number;\n /** Blocks available to unprivileged users. */\n readonly bavail: number;\n}\n\n/** What the probe reads the filesystem with; `fs.promises.statfs` by default. */\nexport type DiskStatfs = (path: string) => PromiseLike<DiskStats>;\n\n/** Options for `diskProbe()`. */\nexport interface DiskProbeOptions extends ProbeOverrides {\n /** Any path on the filesystem to check. Default `diskDefaultPath`. */\n readonly path?: string;\n /** Fail when free space drops below this percentage of the total. Default `diskDefaultMinFreePercent` unless `minFreeBytes` is set. */\n readonly minFreePercent?: number;\n /** Fail when fewer bytes than this are free. */\n readonly minFreeBytes?: number;\n /** Replacement `statfs`, for tests. */\n readonly statfs?: DiskStatfs;\n}\n\n/** What a healthy check resolves with. */\nexport interface DiskUsage {\n /** Bytes available. */\n readonly freeBytes: number;\n /** Bytes in total. */\n readonly totalBytes: number;\n /** `freeBytes` as a percentage of `totalBytes`, rounded to one decimal. */\n readonly freePercent: number;\n}\n\n/** Thrown by the probe when free space is below the threshold. */\nexport class DiskSpaceError extends Error {\n /** The usage that crossed the threshold. */\n readonly usage: DiskUsage;\n\n /** Build the error for `usage` against the threshold described by `reason`. */\n constructor(usage: DiskUsage, reason: string) {\n super(reason);\n this.name = \"DiskSpaceError\";\n this.usage = usage;\n }\n}\n\n/** A probe that fails when free space is below `minFreePercent` or `minFreeBytes`; non-critical by default. Throws `ProbeConfigError` for an empty `path` or a negative threshold. */\nexport function diskProbe(options: DiskProbeOptions = {}): Probe {\n const path = options.path ?? diskDefaultPath;\n if (typeof path !== \"string\" || path.length === 0) {\n throw new ProbeConfigError(\n \"diskProbe\",\n \"path\",\n typeof path !== \"string\"\n ? `must be a string, got ${String(path)}`\n : \"must not be empty\",\n );\n }\n const minFreeBytes = options.minFreeBytes;\n const minFreePercent = options.minFreePercent ??\n (minFreeBytes == null ? diskDefaultMinFreePercent : undefined);\n for (\n const [field, value] of [\n [\"minFreePercent\", minFreePercent],\n [\"minFreeBytes\", minFreeBytes],\n ] as const\n ) {\n if (value != null && (!Number.isFinite(value) || value < 0)) {\n throw new ProbeConfigError(\n \"diskProbe\",\n field,\n `must be a non-negative number, got ${String(value)}`,\n );\n }\n }\n const statfs = options.statfs ?? fsStatfs;\n return {\n name: options.name ?? diskDefaultName,\n critical: options.critical ?? false,\n timeoutMs: options.timeoutMs,\n skip: options.skip,\n run: async () => {\n const stats = await statfs(path);\n const usage = toUsage(stats);\n if (minFreeBytes != null && usage.freeBytes < minFreeBytes) {\n throw new DiskSpaceError(\n usage,\n `${usage.freeBytes} bytes free, fewer than ${minFreeBytes}`,\n );\n }\n if (\n minFreePercent != null &&\n (usage.freeBytes / usage.totalBytes) * 100 < minFreePercent\n ) {\n throw new DiskSpaceError(\n usage,\n `${usage.freePercent}% free, less than ${minFreePercent}%`,\n );\n }\n return usage;\n },\n };\n}\n\nfunction toUsage(stats: DiskStats): DiskUsage {\n const { bsize, blocks, bavail } = stats;\n if (\n ![bsize, blocks, bavail].every((n) => Number.isFinite(n) && n >= 0) ||\n bsize === 0 || blocks === 0\n ) {\n throw new Error(\"unexpected statfs result\");\n }\n const totalBytes = blocks * bsize;\n const freeBytes = bavail * bsize;\n return {\n freeBytes,\n totalBytes,\n freePercent: Math.round((freeBytes / totalBytes) * 1000) / 10,\n };\n}\n"],"mappings":";;;;;AAuBA,MAAa,kBAAkB;;AAE/B,MAAa,kBAAkB;;AAE/B,MAAa,4BAA4B;;AAsCzC,IAAa,iBAAb,cAAoC,MAAM;;CAExC,AAAS;;CAGT,YAAYA,OAAkBC,QAAgB;AAC5C,QAAM,OAAO;AACb,OAAK,OAAO;AACZ,OAAK,QAAQ;CACd;AACF;;AAGD,SAAgB,UAAUC,UAA4B,CAAE,GAAS;CAC/D,MAAM,OAAO,QAAQ,QAAQ;AAC7B,YAAW,SAAS,YAAY,KAAK,WAAW,EAC9C,OAAM,IAAI,iBACR,aACA,eACO,SAAS,YACX,wBAAwB,OAAO,KAAK,CAAC,IACtC;CAGR,MAAM,eAAe,QAAQ;CAC7B,MAAM,iBAAiB,QAAQ,mBAC5B,gBAAgB,OAAO;AAC1B,MACE,MAAM,CAAC,OAAO,MAAM,IAAI,CACtB,CAAC,kBAAkB,cAAe,GAClC,CAAC,gBAAgB,YAAa,CAC/B,EAED,KAAI,SAAS,UAAU,OAAO,SAAS,MAAM,IAAI,QAAQ,GACvD,OAAM,IAAI,iBACR,aACA,QACC,qCAAqC,OAAO,MAAM,CAAC;CAI1D,MAAMC,WAAS,QAAQ,UAAUC;AACjC,QAAO;EACL,MAAM,QAAQ,QAAQ;EACtB,UAAU,QAAQ,YAAY;EAC9B,WAAW,QAAQ;EACnB,MAAM,QAAQ;EACd,KAAK,YAAY;GACf,MAAM,QAAQ,MAAM,SAAO,KAAK;GAChC,MAAM,QAAQ,QAAQ,MAAM;AAC5B,OAAI,gBAAgB,QAAQ,MAAM,YAAY,aAC5C,OAAM,IAAI,eACR,QACC,EAAE,MAAM,UAAU,0BAA0B,aAAa;AAG9D,OACE,kBAAkB,QACjB,MAAM,YAAY,MAAM,aAAc,MAAM,eAE7C,OAAM,IAAI,eACR,QACC,EAAE,MAAM,YAAY,oBAAoB,eAAe;AAG5D,UAAO;EACR;CACF;AACF;AAED,SAAS,QAAQC,OAA6B;CAC5C,MAAM,EAAE,OAAO,QAAQ,QAAQ,GAAG;AAClC,MACG;EAAC;EAAO;EAAQ;CAAO,EAAC,MAAM,CAAC,MAAM,OAAO,SAAS,EAAE,IAAI,KAAK,EAAE,IACnE,UAAU,KAAK,WAAW,EAE1B,OAAM,IAAI,MAAM;CAElB,MAAM,aAAa,SAAS;CAC5B,MAAM,YAAY,SAAS;AAC3B,QAAO;EACL;EACA;EACA,aAAa,KAAK,MAAO,YAAY,aAAc,IAAK,GAAG;CAC5D;AACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openstatus/health-disk",
|
|
3
|
+
"version": "0.1.4-dev.0",
|
|
4
|
+
"description": "Disk space probe for @openstatus/health",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"openstatus",
|
|
7
|
+
"health",
|
|
8
|
+
"healthcheck",
|
|
9
|
+
"disk",
|
|
10
|
+
"storage",
|
|
11
|
+
"filesystem",
|
|
12
|
+
"system"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "openstatus",
|
|
17
|
+
"url": "https://www.openstatus.dev/"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/openstatusHQ/health",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/openstatusHQ/health.git",
|
|
23
|
+
"directory": "packages/disk/"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/openstatusHQ/health/issues"
|
|
27
|
+
},
|
|
28
|
+
"type": "module",
|
|
29
|
+
"module": "./dist/mod.js",
|
|
30
|
+
"main": "./dist/mod.cjs",
|
|
31
|
+
"types": "./dist/mod.d.ts",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": {
|
|
35
|
+
"import": "./dist/mod.d.ts",
|
|
36
|
+
"require": "./dist/mod.d.cts"
|
|
37
|
+
},
|
|
38
|
+
"import": "./dist/mod.js",
|
|
39
|
+
"require": "./dist/mod.cjs"
|
|
40
|
+
},
|
|
41
|
+
"./package.json": "./package.json"
|
|
42
|
+
},
|
|
43
|
+
"sideEffects": false,
|
|
44
|
+
"files": [
|
|
45
|
+
"dist/"
|
|
46
|
+
],
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=22"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@openstatus/health": "^0.1.4-dev.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": ">=22",
|
|
55
|
+
"tsdown": "^0.12.7",
|
|
56
|
+
"typescript": "^5.8.3"
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "tsdown",
|
|
60
|
+
"prepack": "tsdown",
|
|
61
|
+
"test": "node --experimental-transform-types --test"
|
|
62
|
+
}
|
|
63
|
+
}
|