@openstatus/health-s3 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 ADDED
@@ -0,0 +1,73 @@
1
+ # @openstatus/health-s3
2
+
3
+ S3 probe for [`@openstatus/health`](https://jsr.io/@openstatus/health).
4
+ Sends [`HeadBucket`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadBucket.html)
5
+ through an [`@aws-sdk/client-s3`](https://github.com/aws/aws-sdk-js-v3)
6
+ client and fails the check when the bucket does not answer — on AWS S3,
7
+ Cloudflare R2, Tigris, Backblaze B2, MinIO or any other S3-compatible store.
8
+
9
+ ```sh
10
+ deno add jsr:@openstatus/health jsr:@openstatus/health-s3
11
+ npm install @openstatus/health @openstatus/health-s3 @aws-sdk/client-s3
12
+ ```
13
+
14
+ ```ts
15
+ import { S3Client } from "@aws-sdk/client-s3";
16
+ import { createHealthHandler } from "@openstatus/health";
17
+ import { s3Probe } from "@openstatus/health-s3";
18
+
19
+ const client = new S3Client({ region: env.AWS_REGION });
20
+
21
+ Deno.serve(
22
+ createHealthHandler({
23
+ probes: [s3Probe({ client, bucket: env.S3_BUCKET })],
24
+ }),
25
+ );
26
+ ```
27
+
28
+ `HeadBucket` is the cheapest request that proves the endpoint, the
29
+ credentials and the bucket at once: it transfers no body, and a `403` or
30
+ `404` — wrong credentials, wrong bucket, wrong region — fails the check
31
+ just like a network error. The probe's `AbortSignal` is passed as
32
+ `abortSignal`, so a `timeoutMs` cancels the request in flight.
33
+
34
+ ```ts
35
+ s3Probe({
36
+ client,
37
+ bucket: "uploads",
38
+ // optional overrides from the Probe contract
39
+ name: "uploads",
40
+ critical: true,
41
+ timeoutMs: 2000,
42
+ skip: () => env.S3_BUCKET == null,
43
+ });
44
+ ```
45
+
46
+ Non-critical by default: object storage usually backs uploads and assets
47
+ whose outage degrades the report instead of taking the service out of
48
+ rotation. Set `critical: true` when requests cannot proceed without it.
49
+
50
+ Unlike most probes here, this one imports `HeadBucketCommand` at runtime,
51
+ so `@aws-sdk/client-s3` is a required peer dependency. The client itself is
52
+ typed structurally as `{ send(command, options?) }`. The factory throws
53
+ `ProbeConfigError` at construction when the client has no `send()` or
54
+ `bucket` is empty. Inside a Cloudflare Worker, prefer
55
+ `@openstatus/health-cloudflare-r2` and its binding over the S3 API.
56
+
57
+ ## About openstatus
58
+
59
+ [openstatus](https://www.openstatus.dev/) is the open-source uptime monitoring
60
+ and status page platform. This package is part of
61
+ [`@openstatus/health`](https://github.com/openstatusHQ/health), the `/health`
62
+ endpoints behind openstatus's own services, extracted so any JavaScript server
63
+ can expose one. Point an
64
+ [openstatus monitor](https://www.openstatus.dev/docs/reference/http-monitor)
65
+ at the endpoint and assert on `status` in the body to be alerted on
66
+ `degraded` before it becomes `unhealthy`.
67
+
68
+ Source: [github.com/openstatusHQ/health](https://github.com/openstatusHQ/health).
69
+ Issues and PRs welcome.
70
+
71
+ ## License
72
+
73
+ [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,33 @@
1
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
+ const __aws_sdk_client_s3 = require_rolldown_runtime.__toESM(require("@aws-sdk/client-s3"));
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 s3DefaultName = "storage";
8
+ /** A probe that sends `HeadBucket`; non-critical by default. Throws `ProbeConfigError` without `send()` or with an empty `bucket`. */
9
+ function s3Probe(options) {
10
+ const client = options.client;
11
+ if (typeof client?.send !== "function") throw new __openstatus_health.ProbeConfigError("s3Probe", "client", `must expose send(), got ${describe(client)}`);
12
+ const bucket = options.bucket;
13
+ if (typeof bucket !== "string" || bucket.length === 0) throw new __openstatus_health.ProbeConfigError("s3Probe", "bucket", typeof bucket !== "string" ? `must be a string, got ${String(bucket)}` : "must not be empty");
14
+ return {
15
+ name: options.name ?? s3DefaultName,
16
+ critical: options.critical ?? false,
17
+ timeoutMs: options.timeoutMs,
18
+ skip: options.skip,
19
+ run: async (signal) => {
20
+ await client.send(new __aws_sdk_client_s3.HeadBucketCommand({ Bucket: bucket }), { abortSignal: signal });
21
+ }
22
+ };
23
+ }
24
+ function describe(client) {
25
+ if (client == null) return String(client);
26
+ if (typeof client !== "object") return typeof client;
27
+ const keys = Object.keys(client);
28
+ return keys.length === 0 ? "an object with no keys" : `an object with keys ${keys.slice(0, 8).join(", ")}`;
29
+ }
30
+
31
+ //#endregion
32
+ exports.s3DefaultName = s3DefaultName;
33
+ exports.s3Probe = s3Probe;
package/dist/mod.d.cts ADDED
@@ -0,0 +1,30 @@
1
+ import { HeadBucketCommand } from "@aws-sdk/client-s3";
2
+ import { Probe, ProbeOverrides, ProbeResult } from "@openstatus/health";
3
+
4
+ //#region src/mod.d.ts
5
+
6
+ /** Probe name when `name` is unset. */
7
+ declare const s3DefaultName = "storage";
8
+ /** What the probe passes to `send()` after the command. */
9
+ interface S3SendOptions {
10
+ /** Aborts when the probe times out. */
11
+ readonly abortSignal?: AbortSignal;
12
+ }
13
+ /** The subset of an `S3Client` the probe uses. */
14
+ interface S3LikeClient {
15
+ /** Send one command. */
16
+ send(command: HeadBucketCommand, options?: S3SendOptions): PromiseLike<ProbeResult>;
17
+ }
18
+ /** Options for `s3Probe()`. */
19
+ interface S3ProbeOptions extends ProbeOverrides {
20
+ /** An `S3Client`. */
21
+ readonly client: S3LikeClient;
22
+ /** Bucket to `HeadBucket`. */
23
+ readonly bucket: string;
24
+ }
25
+ /** A probe that sends `HeadBucket`; non-critical by default. Throws `ProbeConfigError` without `send()` or with an empty `bucket`. */
26
+ declare function s3Probe(options: S3ProbeOptions): Probe;
27
+ //# sourceMappingURL=mod.d.ts.map
28
+ //#endregion
29
+ export { S3LikeClient, S3ProbeOptions, S3SendOptions, s3DefaultName, s3Probe };
30
+ //# sourceMappingURL=mod.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.cts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;;AA0CsD,cAlBzC,aAAA,GAkByC,SAAA;AAQtD;AAAuB,UAvBN,aAAA,CAuBM;EAAA;EAAwB,SAAG,WAAA,CAAA,EArBzB,WAqByB;AAAK;;UAjBtC,YAAA;;gBAGJ,6BACC,gBACT,YAAY;;;UAIA,cAAA,SAAuB;;mBAErB;;;;;iBAMH,OAAA,UAAiB,iBAAiB"}
package/dist/mod.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ import { HeadBucketCommand } from "@aws-sdk/client-s3";
2
+ import { Probe, ProbeOverrides, ProbeResult } from "@openstatus/health";
3
+
4
+ //#region src/mod.d.ts
5
+
6
+ /** Probe name when `name` is unset. */
7
+ declare const s3DefaultName = "storage";
8
+ /** What the probe passes to `send()` after the command. */
9
+ interface S3SendOptions {
10
+ /** Aborts when the probe times out. */
11
+ readonly abortSignal?: AbortSignal;
12
+ }
13
+ /** The subset of an `S3Client` the probe uses. */
14
+ interface S3LikeClient {
15
+ /** Send one command. */
16
+ send(command: HeadBucketCommand, options?: S3SendOptions): PromiseLike<ProbeResult>;
17
+ }
18
+ /** Options for `s3Probe()`. */
19
+ interface S3ProbeOptions extends ProbeOverrides {
20
+ /** An `S3Client`. */
21
+ readonly client: S3LikeClient;
22
+ /** Bucket to `HeadBucket`. */
23
+ readonly bucket: string;
24
+ }
25
+ /** A probe that sends `HeadBucket`; non-critical by default. Throws `ProbeConfigError` without `send()` or with an empty `bucket`. */
26
+ declare function s3Probe(options: S3ProbeOptions): Probe;
27
+ //# sourceMappingURL=mod.d.ts.map
28
+ //#endregion
29
+ export { S3LikeClient, S3ProbeOptions, S3SendOptions, s3DefaultName, s3Probe };
30
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;;AA0CsD,cAlBzC,aAAA,GAkByC,SAAA;AAQtD;AAAuB,UAvBN,aAAA,CAuBM;EAAA;EAAwB,SAAG,WAAA,CAAA,EArBzB,WAqByB;AAAK;;UAjBtC,YAAA;;gBAGJ,6BACC,gBACT,YAAY;;;UAIA,cAAA,SAAuB;;mBAErB;;;;;iBAMH,OAAA,UAAiB,iBAAiB"}
package/dist/mod.js ADDED
@@ -0,0 +1,32 @@
1
+ import { HeadBucketCommand } from "@aws-sdk/client-s3";
2
+ import { ProbeConfigError } from "@openstatus/health";
3
+
4
+ //#region src/mod.ts
5
+ /** Probe name when `name` is unset. */
6
+ const s3DefaultName = "storage";
7
+ /** A probe that sends `HeadBucket`; non-critical by default. Throws `ProbeConfigError` without `send()` or with an empty `bucket`. */
8
+ function s3Probe(options) {
9
+ const client = options.client;
10
+ if (typeof client?.send !== "function") throw new ProbeConfigError("s3Probe", "client", `must expose send(), got ${describe(client)}`);
11
+ const bucket = options.bucket;
12
+ if (typeof bucket !== "string" || bucket.length === 0) throw new ProbeConfigError("s3Probe", "bucket", typeof bucket !== "string" ? `must be a string, got ${String(bucket)}` : "must not be empty");
13
+ return {
14
+ name: options.name ?? s3DefaultName,
15
+ critical: options.critical ?? false,
16
+ timeoutMs: options.timeoutMs,
17
+ skip: options.skip,
18
+ run: async (signal) => {
19
+ await client.send(new HeadBucketCommand({ Bucket: bucket }), { abortSignal: signal });
20
+ }
21
+ };
22
+ }
23
+ function describe(client) {
24
+ if (client == null) return String(client);
25
+ if (typeof client !== "object") return typeof client;
26
+ const keys = Object.keys(client);
27
+ return keys.length === 0 ? "an object with no keys" : `an object with keys ${keys.slice(0, 8).join(", ")}`;
28
+ }
29
+
30
+ //#endregion
31
+ export { s3DefaultName, s3Probe };
32
+ //# sourceMappingURL=mod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.js","names":["options: S3ProbeOptions","client: S3LikeClient"],"sources":["../src/mod.ts"],"sourcesContent":["/**\n * S3 probe for `@openstatus/health`: sends `HeadBucket` through an\n * `@aws-sdk/client-s3` client, so it covers AWS S3, Cloudflare R2, Tigris,\n * MinIO and every other S3-compatible store.\n *\n * ```ts\n * import { S3Client } from \"@aws-sdk/client-s3\";\n * import { s3Probe } from \"@openstatus/health-s3\";\n *\n * const probe = s3Probe({ client: new S3Client({}), bucket: \"uploads\" });\n * ```\n *\n * @module\n */\n\nimport { HeadBucketCommand } from \"@aws-sdk/client-s3\";\nimport {\n type Probe,\n ProbeConfigError,\n type ProbeOverrides,\n type ProbeResult,\n} from \"@openstatus/health\";\n\n/** Probe name when `name` is unset. */\nexport const s3DefaultName = \"storage\";\n\n/** What the probe passes to `send()` after the command. */\nexport interface S3SendOptions {\n /** Aborts when the probe times out. */\n readonly abortSignal?: AbortSignal;\n}\n\n/** The subset of an `S3Client` the probe uses. */\nexport interface S3LikeClient {\n /** Send one command. */\n send(\n command: HeadBucketCommand,\n options?: S3SendOptions,\n ): PromiseLike<ProbeResult>;\n}\n\n/** Options for `s3Probe()`. */\nexport interface S3ProbeOptions extends ProbeOverrides {\n /** An `S3Client`. */\n readonly client: S3LikeClient;\n /** Bucket to `HeadBucket`. */\n readonly bucket: string;\n}\n\n/** A probe that sends `HeadBucket`; non-critical by default. Throws `ProbeConfigError` without `send()` or with an empty `bucket`. */\nexport function s3Probe(options: S3ProbeOptions): Probe {\n const client = options.client;\n if (typeof client?.send !== \"function\") {\n throw new ProbeConfigError(\n \"s3Probe\",\n \"client\",\n `must expose send(), got ${describe(client)}`,\n );\n }\n const bucket = options.bucket;\n if (typeof bucket !== \"string\" || bucket.length === 0) {\n throw new ProbeConfigError(\n \"s3Probe\",\n \"bucket\",\n typeof bucket !== \"string\"\n ? `must be a string, got ${String(bucket)}`\n : \"must not be empty\",\n );\n }\n return {\n name: options.name ?? s3DefaultName,\n critical: options.critical ?? false,\n timeoutMs: options.timeoutMs,\n skip: options.skip,\n run: async (signal) => {\n await client.send(new HeadBucketCommand({ Bucket: bucket }), {\n abortSignal: signal,\n });\n },\n };\n}\n\nfunction describe(client: S3LikeClient): string {\n if (client == null) return String(client);\n if (typeof client !== \"object\") return typeof client;\n const keys = Object.keys(client);\n return keys.length === 0\n ? \"an object with no keys\"\n : `an object with keys ${keys.slice(0, 8).join(\", \")}`;\n}\n"],"mappings":";;;;;AAwBA,MAAa,gBAAgB;;AA0B7B,SAAgB,QAAQA,SAAgC;CACtD,MAAM,SAAS,QAAQ;AACvB,YAAW,QAAQ,SAAS,WAC1B,OAAM,IAAI,iBACR,WACA,WACC,0BAA0B,SAAS,OAAO,CAAC;CAGhD,MAAM,SAAS,QAAQ;AACvB,YAAW,WAAW,YAAY,OAAO,WAAW,EAClD,OAAM,IAAI,iBACR,WACA,iBACO,WAAW,YACb,wBAAwB,OAAO,OAAO,CAAC,IACxC;AAGR,QAAO;EACL,MAAM,QAAQ,QAAQ;EACtB,UAAU,QAAQ,YAAY;EAC9B,WAAW,QAAQ;EACnB,MAAM,QAAQ;EACd,KAAK,OAAO,WAAW;AACrB,SAAM,OAAO,KAAK,IAAI,kBAAkB,EAAE,QAAQ,OAAQ,IAAG,EAC3D,aAAa,OACd,EAAC;EACH;CACF;AACF;AAED,SAAS,SAASC,QAA8B;AAC9C,KAAI,UAAU,KAAM,QAAO,OAAO,OAAO;AACzC,YAAW,WAAW,SAAU,eAAc;CAC9C,MAAM,OAAO,OAAO,KAAK,OAAO;AAChC,QAAO,KAAK,WAAW,IACnB,4BACC,sBAAsB,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,KAAK,CAAC;AACxD"}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@openstatus/health-s3",
3
+ "version": "0.1.4-dev.0",
4
+ "description": "S3 HeadBucket probe for @openstatus/health",
5
+ "keywords": [
6
+ "openstatus",
7
+ "health",
8
+ "healthcheck",
9
+ "s3",
10
+ "aws",
11
+ "r2",
12
+ "minio",
13
+ "tigris",
14
+ "storage"
15
+ ],
16
+ "license": "MIT",
17
+ "author": {
18
+ "name": "openstatus",
19
+ "url": "https://www.openstatus.dev/"
20
+ },
21
+ "homepage": "https://github.com/openstatusHQ/health",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/openstatusHQ/health.git",
25
+ "directory": "packages/s3/"
26
+ },
27
+ "bugs": {
28
+ "url": "https://github.com/openstatusHQ/health/issues"
29
+ },
30
+ "type": "module",
31
+ "module": "./dist/mod.js",
32
+ "main": "./dist/mod.cjs",
33
+ "types": "./dist/mod.d.ts",
34
+ "exports": {
35
+ ".": {
36
+ "types": {
37
+ "import": "./dist/mod.d.ts",
38
+ "require": "./dist/mod.d.cts"
39
+ },
40
+ "import": "./dist/mod.js",
41
+ "require": "./dist/mod.cjs"
42
+ },
43
+ "./package.json": "./package.json"
44
+ },
45
+ "sideEffects": false,
46
+ "files": [
47
+ "dist/"
48
+ ],
49
+ "engines": {
50
+ "node": ">=22"
51
+ },
52
+ "peerDependencies": {
53
+ "@openstatus/health": "^0.1.4-dev.0",
54
+ "@aws-sdk/client-s3": ">=3.0.0"
55
+ },
56
+ "devDependencies": {
57
+ "@aws-sdk/client-s3": "^3.850.0",
58
+ "tsdown": "^0.12.7",
59
+ "typescript": "^5.8.3"
60
+ },
61
+ "scripts": {
62
+ "build": "tsdown",
63
+ "prepack": "tsdown",
64
+ "test": "node --experimental-transform-types --test"
65
+ }
66
+ }