@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 +80 -0
- package/dist/_virtual/rolldown_runtime.cjs +30 -0
- package/dist/mod.cjs +35 -0
- package/dist/mod.d.cts +26 -0
- package/dist/mod.d.cts.map +1 -0
- package/dist/mod.d.ts +26 -0
- package/dist/mod.d.ts.map +1 -0
- package/dist/mod.js +33 -0
- package/dist/mod.js.map +1 -0
- package/package.json +63 -0
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
|
package/dist/mod.js.map
ADDED
|
@@ -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
|
+
}
|