@openstatus/health-convex 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,93 @@
1
+ # @openstatus/health-convex
2
+
3
+ [Convex](https://www.convex.dev/) probe for
4
+ [`@openstatus/health`](https://jsr.io/@openstatus/health). Runs one query
5
+ function of your deployment over its
6
+ [HTTP API](https://docs.convex.dev/http-api/) — `POST {url}/api/query` —
7
+ and fails the check unless it answers `status: "success"`. With `fetch`
8
+ alone, on every runtime, and without the Convex client.
9
+
10
+ ```sh
11
+ deno add jsr:@openstatus/health jsr:@openstatus/health-convex
12
+ npm install @openstatus/health @openstatus/health-convex
13
+ ```
14
+
15
+ Add a trivial query to your `convex/` directory once:
16
+
17
+ ```ts
18
+ // convex/health.ts
19
+ import { query } from "./_generated/server";
20
+
21
+ export const ping = query({
22
+ args: {},
23
+ handler: () => "pong",
24
+ });
25
+ ```
26
+
27
+ Then point the probe at it:
28
+
29
+ ```ts
30
+ import { createHealthHandler, readEnv } from "@openstatus/health";
31
+ import { convexProbe } from "@openstatus/health-convex";
32
+
33
+ const url = readEnv("CONVEX_URL");
34
+
35
+ Deno.serve(
36
+ createHealthHandler({
37
+ probes: [
38
+ // the factory validates `url` at construction, so pass a placeholder
39
+ // and let `skip` keep the unconfigured check from running.
40
+ convexProbe({
41
+ url: url ?? "https://unconfigured.convex.cloud",
42
+ path: "health:ping",
43
+ skip: () => url == null,
44
+ }),
45
+ ],
46
+ }),
47
+ );
48
+ ```
49
+
50
+ `url` is the deployment URL (`CONVEX_URL` / `NEXT_PUBLIC_CONVEX_URL`, the
51
+ `.convex.cloud` host — not the `.convex.site` host of HTTP actions).
52
+ `path` names the function as `module:function`. A query is read-only and
53
+ runs inside the deployment, so a success proves the deployment, its
54
+ database and your code are all up. Pass `args` for a query that takes
55
+ them, and `token` — a deploy key or a user's JWT — when the query checks
56
+ authentication. Like every probe here, this one never reads the environment
57
+ itself — pass the values in, and use `skip` for environments where Convex
58
+ is not configured.
59
+
60
+ ```ts
61
+ convexProbe({
62
+ url,
63
+ path: "health:ping",
64
+ // optional overrides from the Probe contract
65
+ name: "convex",
66
+ critical: false,
67
+ timeoutMs: 2000,
68
+ skip: () => env.CONVEX_URL == null,
69
+ });
70
+ ```
71
+
72
+ Critical by default: Convex is usually the application's database, so a
73
+ deployment that does not answer turns the report `unhealthy` and takes the
74
+ instance out of rotation. Set `critical: false` when the service can serve
75
+ without it.
76
+
77
+ ## About openstatus
78
+
79
+ [openstatus](https://www.openstatus.dev/) is the open-source uptime monitoring
80
+ and status page platform. This package is part of
81
+ [`@openstatus/health`](https://github.com/openstatusHQ/health), the `/health`
82
+ endpoints behind openstatus's own services, extracted so any JavaScript server
83
+ can expose one. Point an
84
+ [openstatus monitor](https://www.openstatus.dev/docs/reference/http-monitor)
85
+ at the endpoint and assert on `status` in the body to be alerted on
86
+ `degraded` before it becomes `unhealthy`.
87
+
88
+ Source: [github.com/openstatusHQ/health](https://github.com/openstatusHQ/health).
89
+ Issues and PRs welcome.
90
+
91
+ ## License
92
+
93
+ [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,48 @@
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 convexDefaultName = "database";
7
+ /** A probe that runs `path` over the HTTP API and expects `status: "success"`; critical by default. Throws `ProbeConfigError` for an invalid `url`, an empty `path` or an empty or non-string `token`. */
8
+ function convexProbe(options) {
9
+ const doFetch = options.fetch ?? globalThis.fetch;
10
+ const url = (0, __openstatus_health.probeUrl)({
11
+ probe: "convexProbe",
12
+ field: "url",
13
+ value: options.url,
14
+ path: "/api/query"
15
+ });
16
+ if (typeof options.path !== "string" || options.path.length === 0) throw new __openstatus_health.ProbeConfigError("convexProbe", "path", typeof options.path !== "string" ? `must be a string, got ${String(options.path)}` : "must not be empty");
17
+ if (options.token != null && (typeof options.token !== "string" || options.token.length === 0)) throw new __openstatus_health.ProbeConfigError("convexProbe", "token", typeof options.token !== "string" ? `must be a string, got ${String(options.token)}` : "must not be empty");
18
+ const headers = { "content-type": "application/json" };
19
+ if (options.token != null) headers.authorization = `Bearer ${options.token}`;
20
+ const body = JSON.stringify({
21
+ path: options.path,
22
+ args: options.args ?? {},
23
+ format: "json"
24
+ });
25
+ return {
26
+ name: options.name ?? convexDefaultName,
27
+ critical: options.critical ?? true,
28
+ timeoutMs: options.timeoutMs,
29
+ skip: options.skip,
30
+ run: async (signal) => {
31
+ const res = await doFetch(url, {
32
+ method: "POST",
33
+ headers,
34
+ body,
35
+ signal
36
+ });
37
+ const result = await res.json().catch(() => null);
38
+ if (result?.status === "error") throw new Error(result.errorMessage ?? `query failed with status ${res.status}`);
39
+ if (!res.ok) throw new Error(`unexpected status ${res.status}`);
40
+ if (result?.status === "success") return;
41
+ throw new Error("unexpected response shape");
42
+ }
43
+ };
44
+ }
45
+
46
+ //#endregion
47
+ exports.convexDefaultName = convexDefaultName;
48
+ exports.convexProbe = convexProbe;
package/dist/mod.d.cts ADDED
@@ -0,0 +1,32 @@
1
+ import { JsonObject, Probe, ProbeOverrides } from "@openstatus/health";
2
+
3
+ //#region src/mod.d.ts
4
+
5
+ /** Probe name when `name` is unset. */
6
+ declare const convexDefaultName = "database";
7
+ /** Options for `convexProbe()`. */
8
+ interface ConvexProbeOptions extends ProbeOverrides {
9
+ /** The deployment URL, e.g. `https://happy-animal-123.convex.cloud`. */
10
+ readonly url: string | URL;
11
+ /** The query function to run, as `module:function`, e.g. `"health:ping"`. */
12
+ readonly path: string;
13
+ /** Arguments for the query. Default `{}`. */
14
+ readonly args?: JsonObject;
15
+ /** A deploy key or user token, sent as a bearer token when the query requires auth. */
16
+ readonly token?: string;
17
+ /** Replacement `fetch`, for tests. */
18
+ readonly fetch?: typeof fetch;
19
+ }
20
+ /** What `POST /api/query` answers. */
21
+ type ConvexQueryResponse = {
22
+ readonly status: "success";
23
+ } | {
24
+ readonly status: "error";
25
+ readonly errorMessage?: string;
26
+ };
27
+ /** A probe that runs `path` over the HTTP API and expects `status: "success"`; critical by default. Throws `ProbeConfigError` for an invalid `url`, an empty `path` or an empty or non-string `token`. */
28
+ declare function convexProbe(options: ConvexProbeOptions): Probe;
29
+ //# sourceMappingURL=mod.d.ts.map
30
+ //#endregion
31
+ export { ConvexProbeOptions, ConvexQueryResponse, convexDefaultName, convexProbe };
32
+ //# sourceMappingURL=mod.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.cts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;AA4CqC,cAtBxB,iBAAA,GAsBwB,UAAA;;AAA0B,UAnB9C,kBAAA,SAA2B,cAmBmB,CAAA;;yBAjBtC;;;;kBAIP;;;;0BAIQ;;;KAId,mBAAA;;;;;;;iBAKI,WAAA,UAAqB,qBAAqB"}
package/dist/mod.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ import { JsonObject, Probe, ProbeOverrides } from "@openstatus/health";
2
+
3
+ //#region src/mod.d.ts
4
+
5
+ /** Probe name when `name` is unset. */
6
+ declare const convexDefaultName = "database";
7
+ /** Options for `convexProbe()`. */
8
+ interface ConvexProbeOptions extends ProbeOverrides {
9
+ /** The deployment URL, e.g. `https://happy-animal-123.convex.cloud`. */
10
+ readonly url: string | URL;
11
+ /** The query function to run, as `module:function`, e.g. `"health:ping"`. */
12
+ readonly path: string;
13
+ /** Arguments for the query. Default `{}`. */
14
+ readonly args?: JsonObject;
15
+ /** A deploy key or user token, sent as a bearer token when the query requires auth. */
16
+ readonly token?: string;
17
+ /** Replacement `fetch`, for tests. */
18
+ readonly fetch?: typeof fetch;
19
+ }
20
+ /** What `POST /api/query` answers. */
21
+ type ConvexQueryResponse = {
22
+ readonly status: "success";
23
+ } | {
24
+ readonly status: "error";
25
+ readonly errorMessage?: string;
26
+ };
27
+ /** A probe that runs `path` over the HTTP API and expects `status: "success"`; critical by default. Throws `ProbeConfigError` for an invalid `url`, an empty `path` or an empty or non-string `token`. */
28
+ declare function convexProbe(options: ConvexProbeOptions): Probe;
29
+ //# sourceMappingURL=mod.d.ts.map
30
+ //#endregion
31
+ export { ConvexProbeOptions, ConvexQueryResponse, convexDefaultName, convexProbe };
32
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;AA4CqC,cAtBxB,iBAAA,GAsBwB,UAAA;;AAA0B,UAnB9C,kBAAA,SAA2B,cAmBmB,CAAA;;yBAjBtC;;;;kBAIP;;;;0BAIQ;;;KAId,mBAAA;;;;;;;iBAKI,WAAA,UAAqB,qBAAqB"}
package/dist/mod.js ADDED
@@ -0,0 +1,47 @@
1
+ import { ProbeConfigError, probeUrl } from "@openstatus/health";
2
+
3
+ //#region src/mod.ts
4
+ /** Probe name when `name` is unset. */
5
+ const convexDefaultName = "database";
6
+ /** A probe that runs `path` over the HTTP API and expects `status: "success"`; critical by default. Throws `ProbeConfigError` for an invalid `url`, an empty `path` or an empty or non-string `token`. */
7
+ function convexProbe(options) {
8
+ const doFetch = options.fetch ?? globalThis.fetch;
9
+ const url = probeUrl({
10
+ probe: "convexProbe",
11
+ field: "url",
12
+ value: options.url,
13
+ path: "/api/query"
14
+ });
15
+ if (typeof options.path !== "string" || options.path.length === 0) throw new ProbeConfigError("convexProbe", "path", typeof options.path !== "string" ? `must be a string, got ${String(options.path)}` : "must not be empty");
16
+ if (options.token != null && (typeof options.token !== "string" || options.token.length === 0)) throw new ProbeConfigError("convexProbe", "token", typeof options.token !== "string" ? `must be a string, got ${String(options.token)}` : "must not be empty");
17
+ const headers = { "content-type": "application/json" };
18
+ if (options.token != null) headers.authorization = `Bearer ${options.token}`;
19
+ const body = JSON.stringify({
20
+ path: options.path,
21
+ args: options.args ?? {},
22
+ format: "json"
23
+ });
24
+ return {
25
+ name: options.name ?? convexDefaultName,
26
+ critical: options.critical ?? true,
27
+ timeoutMs: options.timeoutMs,
28
+ skip: options.skip,
29
+ run: async (signal) => {
30
+ const res = await doFetch(url, {
31
+ method: "POST",
32
+ headers,
33
+ body,
34
+ signal
35
+ });
36
+ const result = await res.json().catch(() => null);
37
+ if (result?.status === "error") throw new Error(result.errorMessage ?? `query failed with status ${res.status}`);
38
+ if (!res.ok) throw new Error(`unexpected status ${res.status}`);
39
+ if (result?.status === "success") return;
40
+ throw new Error("unexpected response shape");
41
+ }
42
+ };
43
+ }
44
+
45
+ //#endregion
46
+ export { convexDefaultName, convexProbe };
47
+ //# sourceMappingURL=mod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.js","names":["options: ConvexProbeOptions","headers: Record<string, string>","result: ConvexQueryResponse | null"],"sources":["../src/mod.ts"],"sourcesContent":["/**\n * Convex probe for `@openstatus/health`: runs one query function over the\n * deployment's HTTP API (`POST /api/query`) and fails unless it succeeds.\n *\n * ```ts\n * import { convexProbe } from \"@openstatus/health-convex\";\n *\n * const probe = convexProbe({ url: env.CONVEX_URL, path: \"health:ping\" });\n * ```\n *\n * @module\n */\n\nimport {\n type JsonObject,\n type Probe,\n ProbeConfigError,\n type ProbeOverrides,\n probeUrl,\n} from \"@openstatus/health\";\n\n/** Probe name when `name` is unset. */\nexport const convexDefaultName = \"database\";\n\n/** Options for `convexProbe()`. */\nexport interface ConvexProbeOptions extends ProbeOverrides {\n /** The deployment URL, e.g. `https://happy-animal-123.convex.cloud`. */\n readonly url: string | URL;\n /** The query function to run, as `module:function`, e.g. `\"health:ping\"`. */\n readonly path: string;\n /** Arguments for the query. Default `{}`. */\n readonly args?: JsonObject;\n /** A deploy key or user token, sent as a bearer token when the query requires auth. */\n readonly token?: string;\n /** Replacement `fetch`, for tests. */\n readonly fetch?: typeof fetch;\n}\n\n/** What `POST /api/query` answers. */\nexport type ConvexQueryResponse =\n | { readonly status: \"success\" }\n | { readonly status: \"error\"; readonly errorMessage?: string };\n\n/** A probe that runs `path` over the HTTP API and expects `status: \"success\"`; critical by default. Throws `ProbeConfigError` for an invalid `url`, an empty `path` or an empty or non-string `token`. */\nexport function convexProbe(options: ConvexProbeOptions): Probe {\n const doFetch = options.fetch ?? globalThis.fetch;\n const url = probeUrl({\n probe: \"convexProbe\",\n field: \"url\",\n value: options.url,\n path: \"/api/query\",\n });\n if (typeof options.path !== \"string\" || options.path.length === 0) {\n throw new ProbeConfigError(\n \"convexProbe\",\n \"path\",\n typeof options.path !== \"string\"\n ? `must be a string, got ${String(options.path)}`\n : \"must not be empty\",\n );\n }\n if (\n options.token != null &&\n (typeof options.token !== \"string\" || options.token.length === 0)\n ) {\n throw new ProbeConfigError(\n \"convexProbe\",\n \"token\",\n typeof options.token !== \"string\"\n ? `must be a string, got ${String(options.token)}`\n : \"must not be empty\",\n );\n }\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n };\n if (options.token != null) headers.authorization = `Bearer ${options.token}`;\n const body = JSON.stringify({\n path: options.path,\n args: options.args ?? {},\n format: \"json\",\n });\n return {\n name: options.name ?? convexDefaultName,\n critical: options.critical ?? true,\n timeoutMs: options.timeoutMs,\n skip: options.skip,\n run: async (signal) => {\n const res = await doFetch(url, { method: \"POST\", headers, body, signal });\n const result: ConvexQueryResponse | null = await res.json()\n .catch(() => null);\n if (result?.status === \"error\") {\n throw new Error(\n result.errorMessage ?? `query failed with status ${res.status}`,\n );\n }\n if (!res.ok) throw new Error(`unexpected status ${res.status}`);\n if (result?.status === \"success\") return;\n throw new Error(\"unexpected response shape\");\n },\n };\n}\n"],"mappings":";;;;AAsBA,MAAa,oBAAoB;;AAsBjC,SAAgB,YAAYA,SAAoC;CAC9D,MAAM,UAAU,QAAQ,SAAS,WAAW;CAC5C,MAAM,MAAM,SAAS;EACnB,OAAO;EACP,OAAO;EACP,OAAO,QAAQ;EACf,MAAM;CACP,EAAC;AACF,YAAW,QAAQ,SAAS,YAAY,QAAQ,KAAK,WAAW,EAC9D,OAAM,IAAI,iBACR,eACA,eACO,QAAQ,SAAS,YACnB,wBAAwB,OAAO,QAAQ,KAAK,CAAC,IAC9C;AAGR,KACE,QAAQ,SAAS,gBACT,QAAQ,UAAU,YAAY,QAAQ,MAAM,WAAW,GAE/D,OAAM,IAAI,iBACR,eACA,gBACO,QAAQ,UAAU,YACpB,wBAAwB,OAAO,QAAQ,MAAM,CAAC,IAC/C;CAGR,MAAMC,UAAkC,EACtC,gBAAgB,mBACjB;AACD,KAAI,QAAQ,SAAS,KAAM,SAAQ,iBAAiB,SAAS,QAAQ,MAAM;CAC3E,MAAM,OAAO,KAAK,UAAU;EAC1B,MAAM,QAAQ;EACd,MAAM,QAAQ,QAAQ,CAAE;EACxB,QAAQ;CACT,EAAC;AACF,QAAO;EACL,MAAM,QAAQ,QAAQ;EACtB,UAAU,QAAQ,YAAY;EAC9B,WAAW,QAAQ;EACnB,MAAM,QAAQ;EACd,KAAK,OAAO,WAAW;GACrB,MAAM,MAAM,MAAM,QAAQ,KAAK;IAAE,QAAQ;IAAQ;IAAS;IAAM;GAAQ,EAAC;GACzE,MAAMC,SAAqC,MAAM,IAAI,MAAM,CACxD,MAAM,MAAM,KAAK;AACpB,OAAI,QAAQ,WAAW,QACrB,OAAM,IAAI,MACR,OAAO,iBAAiB,2BAA2B,IAAI,OAAO;AAGlE,QAAK,IAAI,GAAI,OAAM,IAAI,OAAO,oBAAoB,IAAI,OAAO;AAC7D,OAAI,QAAQ,WAAW,UAAW;AAClC,SAAM,IAAI,MAAM;EACjB;CACF;AACF"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@openstatus/health-convex",
3
+ "version": "0.1.4-dev.0",
4
+ "description": "Convex probe for @openstatus/health",
5
+ "keywords": [
6
+ "openstatus",
7
+ "health",
8
+ "healthcheck",
9
+ "convex",
10
+ "database",
11
+ "backend"
12
+ ],
13
+ "license": "MIT",
14
+ "author": {
15
+ "name": "openstatus",
16
+ "url": "https://www.openstatus.dev/"
17
+ },
18
+ "homepage": "https://github.com/openstatusHQ/health",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/openstatusHQ/health.git",
22
+ "directory": "packages/convex/"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/openstatusHQ/health/issues"
26
+ },
27
+ "type": "module",
28
+ "module": "./dist/mod.js",
29
+ "main": "./dist/mod.cjs",
30
+ "types": "./dist/mod.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": {
34
+ "import": "./dist/mod.d.ts",
35
+ "require": "./dist/mod.d.cts"
36
+ },
37
+ "import": "./dist/mod.js",
38
+ "require": "./dist/mod.cjs"
39
+ },
40
+ "./package.json": "./package.json"
41
+ },
42
+ "sideEffects": false,
43
+ "files": [
44
+ "dist/"
45
+ ],
46
+ "engines": {
47
+ "node": ">=22"
48
+ },
49
+ "peerDependencies": {
50
+ "@openstatus/health": "^0.1.4-dev.0"
51
+ },
52
+ "devDependencies": {
53
+ "tsdown": "^0.12.7",
54
+ "typescript": "^5.8.3"
55
+ },
56
+ "scripts": {
57
+ "build": "tsdown",
58
+ "prepack": "tsdown",
59
+ "test": "node --experimental-transform-types --test"
60
+ }
61
+ }