@openstatus/health-cloudflare-r2 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,80 @@
1
+ # @openstatus/health-cloudflare-r2
2
+
3
+ [Cloudflare R2](https://developers.cloudflare.com/r2/) probe for
4
+ [`@openstatus/health`](https://jsr.io/@openstatus/health). Sends a `HEAD`
5
+ for one object through an `R2Bucket` binding and fails the check when the
6
+ request does — a missing object is a healthy answer, only an error is not.
7
+
8
+ ```sh
9
+ deno add jsr:@openstatus/health jsr:@openstatus/health-cloudflare-r2
10
+ npm install @openstatus/health @openstatus/health-cloudflare-r2
11
+ ```
12
+
13
+ ```ts
14
+ import { createLazyHealthHandler } from "@openstatus/health";
15
+ import { r2Probe } from "@openstatus/health-cloudflare-r2";
16
+
17
+ interface Env {
18
+ UPLOADS: R2Bucket;
19
+ }
20
+
21
+ export default {
22
+ fetch: createLazyHealthHandler<Request, Env>((env) => ({
23
+ path: "/health",
24
+ probes: [r2Probe({ bucket: env.UPLOADS })],
25
+ })),
26
+ };
27
+ ```
28
+
29
+ Bindings only exist inside `fetch(request, env)` in Workers, but the handler
30
+ must be built once or every request gets a fresh cache and the probe runs on
31
+ every poll. `createLazyHealthHandler` runs your callback on the first request
32
+ and reuses the handler it builds; with Hono, build the probe once inside a
33
+ route handler that receives `c.env` and memoise it the same way. The probe
34
+ calls `bucket.head("health")`, the cheapest R2 operation that still
35
+ reaches the bucket; pass `key` to check an object your Worker actually
36
+ serves. For an R2 bucket reached over its S3 API from outside Workers, use
37
+ `@openstatus/health-s3` instead.
38
+
39
+ ```ts
40
+ r2Probe({
41
+ bucket: env.UPLOADS,
42
+ key: "assets/logo.png",
43
+ // optional overrides from the Probe contract
44
+ name: "uploads",
45
+ critical: true,
46
+ timeoutMs: 1000,
47
+ skip: () => env.HEALTH_SKIP_R2 === "true",
48
+ });
49
+ ```
50
+
51
+ The factory validates `bucket` when it is called, before `skip` is ever
52
+ consulted, so `skip` cannot stand in for a missing binding; leave the probe
53
+ out of `probes` instead when the Worker may run without R2.
54
+
55
+ Non-critical by default: object storage usually backs uploads and assets
56
+ whose outage degrades the report instead of taking the Worker out of
57
+ rotation. Set `critical: true` when requests cannot proceed without it.
58
+
59
+ The binding is typed structurally as `{ head(key) }`, so the package has no
60
+ dependency on `@cloudflare/workers-types` and works with the types
61
+ `wrangler types` generates. The factory throws `ProbeConfigError` at
62
+ construction when the binding has no `head()` or `key` is empty.
63
+
64
+ ## About openstatus
65
+
66
+ [openstatus](https://www.openstatus.dev/) is the open-source uptime monitoring
67
+ and status page platform. This package is part of
68
+ [`@openstatus/health`](https://github.com/openstatusHQ/health), the `/health`
69
+ endpoints behind openstatus's own services, extracted so any JavaScript server
70
+ can expose one. Point an
71
+ [openstatus monitor](https://www.openstatus.dev/docs/reference/http-monitor)
72
+ at the endpoint and assert on `status` in the body to be alerted on
73
+ `degraded` before it becomes `unhealthy`.
74
+
75
+ Source: [github.com/openstatusHQ/health](https://github.com/openstatusHQ/health).
76
+ Issues and PRs welcome.
77
+
78
+ ## License
79
+
80
+ [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,35 @@
1
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
+ const __openstatus_health = require_rolldown_runtime.__toESM(require("@openstatus/health"));
3
+
4
+ //#region src/mod.ts
5
+ /** Probe name when `name` is unset. */
6
+ const r2DefaultName = "storage";
7
+ /** Object key checked when `key` is unset. */
8
+ const r2DefaultKey = "health";
9
+ /** A probe that heads `key`; non-critical by default. Throws `ProbeConfigError` without `head()` or with an empty `key`. */
10
+ function r2Probe(options) {
11
+ const bucket = options.bucket;
12
+ if (typeof bucket?.head !== "function") throw new __openstatus_health.ProbeConfigError("r2Probe", "bucket", `must expose head(), got ${describe(bucket)}`);
13
+ const key = options.key ?? r2DefaultKey;
14
+ if (typeof key !== "string" || key.length === 0) throw new __openstatus_health.ProbeConfigError("r2Probe", "key", typeof key !== "string" ? `must be a string, got ${String(key)}` : "must not be empty");
15
+ return {
16
+ name: options.name ?? r2DefaultName,
17
+ critical: options.critical ?? false,
18
+ timeoutMs: options.timeoutMs,
19
+ skip: options.skip,
20
+ run: async () => {
21
+ await bucket.head(key);
22
+ }
23
+ };
24
+ }
25
+ function describe(bucket) {
26
+ if (bucket == null) return String(bucket);
27
+ if (typeof bucket !== "object") return typeof bucket;
28
+ const keys = Object.keys(bucket);
29
+ return keys.length === 0 ? "an object with no keys" : `an object with keys ${keys.slice(0, 8).join(", ")}`;
30
+ }
31
+
32
+ //#endregion
33
+ exports.r2DefaultKey = r2DefaultKey;
34
+ exports.r2DefaultName = r2DefaultName;
35
+ exports.r2Probe = r2Probe;
package/dist/mod.d.cts ADDED
@@ -0,0 +1,26 @@
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 r2DefaultName = "storage";
7
+ /** Object key checked when `key` is unset. */
8
+ declare const r2DefaultKey = "health";
9
+ /** The subset of an `R2Bucket` binding the probe uses. */
10
+ interface R2LikeBucket {
11
+ /** Fetch one object's metadata; resolves with `null` when it does not exist. */
12
+ head(key: string): PromiseLike<object | null>;
13
+ }
14
+ /** Options for `r2Probe()`. */
15
+ interface R2ProbeOptions extends ProbeOverrides {
16
+ /** The `R2Bucket` binding from `env`. */
17
+ readonly bucket: R2LikeBucket;
18
+ /** Object key to `HEAD`. Default `r2DefaultKey`; it does not need to exist. */
19
+ readonly key?: string;
20
+ }
21
+ /** A probe that heads `key`; non-critical by default. Throws `ProbeConfigError` without `head()` or with an empty `key`. */
22
+ declare function r2Probe(options: R2ProbeOptions): Probe;
23
+ //# sourceMappingURL=mod.d.ts.map
24
+ //#endregion
25
+ export { R2LikeBucket, R2ProbeOptions, r2DefaultKey, r2DefaultName, r2Probe };
26
+ //# sourceMappingURL=mod.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.cts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;cAyBa,aAAA;;cAEA,YAAA;;UAGI,YAAA;;qBAEI;;;UAIJ,cAAA,SAAuB;;mBAErB;;;;;iBAMH,OAAA,UAAiB,iBAAiB"}
package/dist/mod.d.ts ADDED
@@ -0,0 +1,26 @@
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 r2DefaultName = "storage";
7
+ /** Object key checked when `key` is unset. */
8
+ declare const r2DefaultKey = "health";
9
+ /** The subset of an `R2Bucket` binding the probe uses. */
10
+ interface R2LikeBucket {
11
+ /** Fetch one object's metadata; resolves with `null` when it does not exist. */
12
+ head(key: string): PromiseLike<object | null>;
13
+ }
14
+ /** Options for `r2Probe()`. */
15
+ interface R2ProbeOptions extends ProbeOverrides {
16
+ /** The `R2Bucket` binding from `env`. */
17
+ readonly bucket: R2LikeBucket;
18
+ /** Object key to `HEAD`. Default `r2DefaultKey`; it does not need to exist. */
19
+ readonly key?: string;
20
+ }
21
+ /** A probe that heads `key`; non-critical by default. Throws `ProbeConfigError` without `head()` or with an empty `key`. */
22
+ declare function r2Probe(options: R2ProbeOptions): Probe;
23
+ //# sourceMappingURL=mod.d.ts.map
24
+ //#endregion
25
+ export { R2LikeBucket, R2ProbeOptions, r2DefaultKey, r2DefaultName, r2Probe };
26
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;cAyBa,aAAA;;cAEA,YAAA;;UAGI,YAAA;;qBAEI;;;UAIJ,cAAA,SAAuB;;mBAErB;;;;;iBAMH,OAAA,UAAiB,iBAAiB"}
package/dist/mod.js ADDED
@@ -0,0 +1,33 @@
1
+ import { ProbeConfigError } from "@openstatus/health";
2
+
3
+ //#region src/mod.ts
4
+ /** Probe name when `name` is unset. */
5
+ const r2DefaultName = "storage";
6
+ /** Object key checked when `key` is unset. */
7
+ const r2DefaultKey = "health";
8
+ /** A probe that heads `key`; non-critical by default. Throws `ProbeConfigError` without `head()` or with an empty `key`. */
9
+ function r2Probe(options) {
10
+ const bucket = options.bucket;
11
+ if (typeof bucket?.head !== "function") throw new ProbeConfigError("r2Probe", "bucket", `must expose head(), got ${describe(bucket)}`);
12
+ const key = options.key ?? r2DefaultKey;
13
+ if (typeof key !== "string" || key.length === 0) throw new ProbeConfigError("r2Probe", "key", typeof key !== "string" ? `must be a string, got ${String(key)}` : "must not be empty");
14
+ return {
15
+ name: options.name ?? r2DefaultName,
16
+ critical: options.critical ?? false,
17
+ timeoutMs: options.timeoutMs,
18
+ skip: options.skip,
19
+ run: async () => {
20
+ await bucket.head(key);
21
+ }
22
+ };
23
+ }
24
+ function describe(bucket) {
25
+ if (bucket == null) return String(bucket);
26
+ if (typeof bucket !== "object") return typeof bucket;
27
+ const keys = Object.keys(bucket);
28
+ return keys.length === 0 ? "an object with no keys" : `an object with keys ${keys.slice(0, 8).join(", ")}`;
29
+ }
30
+
31
+ //#endregion
32
+ export { r2DefaultKey, r2DefaultName, r2Probe };
33
+ //# sourceMappingURL=mod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.js","names":["options: R2ProbeOptions","bucket: R2LikeBucket"],"sources":["../src/mod.ts"],"sourcesContent":["/**\n * Cloudflare R2 probe for `@openstatus/health`: sends a `HEAD` for one\n * object through an `R2Bucket` binding.\n *\n * ```ts\n * import { createLazyHealthHandler } from \"@openstatus/health\";\n * import { r2Probe } from \"@openstatus/health-cloudflare-r2\";\n *\n * export default {\n * fetch: createLazyHealthHandler<Request, Env>((env) => ({\n * probes: [r2Probe({ bucket: env.UPLOADS })],\n * })),\n * };\n * ```\n *\n * @module\n */\n\nimport {\n type Probe,\n ProbeConfigError,\n type ProbeOverrides,\n} from \"@openstatus/health\";\n\n/** Probe name when `name` is unset. */\nexport const r2DefaultName = \"storage\";\n/** Object key checked when `key` is unset. */\nexport const r2DefaultKey = \"health\";\n\n/** The subset of an `R2Bucket` binding the probe uses. */\nexport interface R2LikeBucket {\n /** Fetch one object's metadata; resolves with `null` when it does not exist. */\n head(key: string): PromiseLike<object | null>;\n}\n\n/** Options for `r2Probe()`. */\nexport interface R2ProbeOptions extends ProbeOverrides {\n /** The `R2Bucket` binding from `env`. */\n readonly bucket: R2LikeBucket;\n /** Object key to `HEAD`. Default `r2DefaultKey`; it does not need to exist. */\n readonly key?: string;\n}\n\n/** A probe that heads `key`; non-critical by default. Throws `ProbeConfigError` without `head()` or with an empty `key`. */\nexport function r2Probe(options: R2ProbeOptions): Probe {\n const bucket = options.bucket;\n if (typeof bucket?.head !== \"function\") {\n throw new ProbeConfigError(\n \"r2Probe\",\n \"bucket\",\n `must expose head(), got ${describe(bucket)}`,\n );\n }\n const key = options.key ?? r2DefaultKey;\n if (typeof key !== \"string\" || key.length === 0) {\n throw new ProbeConfigError(\n \"r2Probe\",\n \"key\",\n typeof key !== \"string\"\n ? `must be a string, got ${String(key)}`\n : \"must not be empty\",\n );\n }\n return {\n name: options.name ?? r2DefaultName,\n critical: options.critical ?? false,\n timeoutMs: options.timeoutMs,\n skip: options.skip,\n run: async () => {\n await bucket.head(key);\n },\n };\n}\n\nfunction describe(bucket: R2LikeBucket): string {\n if (bucket == null) return String(bucket);\n if (typeof bucket !== \"object\") return typeof bucket;\n const keys = Object.keys(bucket);\n return keys.length === 0\n ? \"an object with no keys\"\n : `an object with keys ${keys.slice(0, 8).join(\", \")}`;\n}\n"],"mappings":";;;;AAyBA,MAAa,gBAAgB;;AAE7B,MAAa,eAAe;;AAiB5B,SAAgB,QAAQA,SAAgC;CACtD,MAAM,SAAS,QAAQ;AACvB,YAAW,QAAQ,SAAS,WAC1B,OAAM,IAAI,iBACR,WACA,WACC,0BAA0B,SAAS,OAAO,CAAC;CAGhD,MAAM,MAAM,QAAQ,OAAO;AAC3B,YAAW,QAAQ,YAAY,IAAI,WAAW,EAC5C,OAAM,IAAI,iBACR,WACA,cACO,QAAQ,YACV,wBAAwB,OAAO,IAAI,CAAC,IACrC;AAGR,QAAO;EACL,MAAM,QAAQ,QAAQ;EACtB,UAAU,QAAQ,YAAY;EAC9B,WAAW,QAAQ;EACnB,MAAM,QAAQ;EACd,KAAK,YAAY;AACf,SAAM,OAAO,KAAK,IAAI;EACvB;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,63 @@
1
+ {
2
+ "name": "@openstatus/health-cloudflare-r2",
3
+ "version": "0.1.4-dev.0",
4
+ "description": "Cloudflare R2 probe for @openstatus/health",
5
+ "keywords": [
6
+ "openstatus",
7
+ "health",
8
+ "healthcheck",
9
+ "cloudflare",
10
+ "r2",
11
+ "workers",
12
+ "storage",
13
+ "s3"
14
+ ],
15
+ "license": "MIT",
16
+ "author": {
17
+ "name": "openstatus",
18
+ "url": "https://www.openstatus.dev/"
19
+ },
20
+ "homepage": "https://github.com/openstatusHQ/health",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/openstatusHQ/health.git",
24
+ "directory": "packages/cloudflare-r2/"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/openstatusHQ/health/issues"
28
+ },
29
+ "type": "module",
30
+ "module": "./dist/mod.js",
31
+ "main": "./dist/mod.cjs",
32
+ "types": "./dist/mod.d.ts",
33
+ "exports": {
34
+ ".": {
35
+ "types": {
36
+ "import": "./dist/mod.d.ts",
37
+ "require": "./dist/mod.d.cts"
38
+ },
39
+ "import": "./dist/mod.js",
40
+ "require": "./dist/mod.cjs"
41
+ },
42
+ "./package.json": "./package.json"
43
+ },
44
+ "sideEffects": false,
45
+ "files": [
46
+ "dist/"
47
+ ],
48
+ "engines": {
49
+ "node": ">=22"
50
+ },
51
+ "peerDependencies": {
52
+ "@openstatus/health": "^0.1.4-dev.0"
53
+ },
54
+ "devDependencies": {
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
+ }