@checkstack/backend-api 0.14.0 → 0.14.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,77 @@
1
1
  # @checkstack/backend-api
2
2
 
3
+ ## 0.14.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 302cd3f: fix: resilient startup routing + /health and /ready endpoints
8
+
9
+ Three fixes that together eliminate startup-race errors during boot and
10
+ hot-reload, plus a new readiness API for plugins.
11
+
12
+ 1. **TrieRouter swap (root cause).** Hono's default `SmartRouter` freezes
13
+ its matcher on the first request — any later `app.add()` throws
14
+ `MESSAGE_MATCHER_IS_ALREADY_BUILT`. Plugins register routes during
15
+ `init()` (and at runtime via `loadSinglePlugin`), so an early request
16
+ during boot would silently lock the matcher with only the module-load
17
+ routes, and every later route registration would fail. The backend
18
+ now uses `TrieRouter`, which is incremental — routes can be added at
19
+ any time, including after thousands of requests have been served.
20
+ This also future-proofs runtime plugin install.
21
+
22
+ 2. **Init gating + fail-loud.** Non-bypass requests now `await` an
23
+ `initPromise` (with a 30s timeout that returns 503 + Retry-After) so
24
+ no traffic reaches Hono before plugins finish registering routes.
25
+ Init failures crash the process via `process.exit(1)` so docker/k8s
26
+ restart cleanly instead of silently serving a half-initialized
27
+ backend.
28
+
29
+ 3. **`/assets/*` fall-through.** The production frontend asset handler
30
+ now calls `next()` instead of `c.notFound()` on miss, so
31
+ plugin-asset routes registered later (`/assets/plugins/:pluginName/*`)
32
+ actually get a chance to match.
33
+
34
+ ### New: platform endpoints under `/.checkstack/*`
35
+
36
+ - `GET /.checkstack/health` — liveness, always 200 once the process is up.
37
+ - `GET /.checkstack/ready` — readiness, 503 until init completes and all
38
+ critical probes pass; 200 otherwise. Returns `{ ready, checks: [...] }`
39
+ with per-probe status, message/error and duration.
40
+
41
+ The leading `.checkstack/` prefix namespaces platform-level endpoints
42
+ away from plugin `/api/*`, runtime frontend assets, and the SPA wildcard,
43
+ leaving room for additional operator endpoints in the future.
44
+
45
+ ### New: plugin readiness API
46
+
47
+ Plugins can contribute readiness probes via the new
48
+ `coreServices.readinessRegistry` service:
49
+
50
+ ```ts
51
+ registerInit({
52
+ deps: { readiness: coreServices.readinessRegistry },
53
+ async init({ readiness }) {
54
+ readiness.register({
55
+ name: "queue.connected",
56
+ critical: true,
57
+ check: async () => ({
58
+ ok: pool.isConnected(),
59
+ message: pool.isConnected() ? undefined : "queue pool not connected",
60
+ }),
61
+ });
62
+ },
63
+ });
64
+ ```
65
+
66
+ Probes run in parallel, throwing probes are reported as `ok: false`,
67
+ and non-critical probes don't block readiness.
68
+
69
+ - @checkstack/cache-api@0.2.3
70
+ - @checkstack/queue-api@0.2.17
71
+ - @checkstack/common@0.7.0
72
+ - @checkstack/healthcheck-common@1.0.0
73
+ - @checkstack/signal-common@0.2.0
74
+
3
75
  ## 0.14.0
4
76
 
5
77
  ### Minor Changes
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@checkstack/backend-api",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "scripts": {
7
- "typecheck": "tsc --noEmit",
7
+ "typecheck": "tsgo -b",
8
8
  "lint": "bun run lint:code",
9
9
  "lint:code": "eslint . --max-warnings 0"
10
10
  },
11
11
  "dependencies": {
12
12
  "@checkstack/common": "0.7.0",
13
- "@checkstack/healthcheck-common": "0.13.0",
14
- "@checkstack/cache-api": "0.2.1",
15
- "@checkstack/queue-api": "0.2.15",
13
+ "@checkstack/healthcheck-common": "1.0.0",
14
+ "@checkstack/cache-api": "0.2.2",
15
+ "@checkstack/queue-api": "0.2.16",
16
16
  "@checkstack/signal-common": "0.2.0",
17
17
  "@orpc/client": "^1.13.14",
18
18
  "@orpc/contract": "^1.13.14",
@@ -19,6 +19,7 @@ import {
19
19
  } from "./types";
20
20
  import type { EventBus } from "./event-bus-types";
21
21
  import type { WebSocketRouteRegistry } from "./ws-registry";
22
+ import type { ReadinessRegistry } from "./readiness-registry";
22
23
 
23
24
  export * from "./types";
24
25
 
@@ -53,4 +54,7 @@ export const coreServices = {
53
54
  "core.cachePluginRegistry",
54
55
  ),
55
56
  cacheManager: createServiceRef<CacheManager>("core.cacheManager"),
57
+ readinessRegistry: createServiceRef<ReadinessRegistry>(
58
+ "core.readinessRegistry",
59
+ ),
56
60
  };
package/src/index.ts CHANGED
@@ -28,3 +28,4 @@ export * from "./collector-registry";
28
28
  export * from "./incremental-aggregation";
29
29
  export * from "./aggregated-result";
30
30
  export * from "./ws-registry";
31
+ export * from "./readiness-registry";
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Result of a single readiness probe.
3
+ */
4
+ export interface ReadinessCheckResult {
5
+ ok: boolean;
6
+ /** Optional human-readable detail (shown verbatim in the /ready response). */
7
+ message?: string;
8
+ }
9
+
10
+ /**
11
+ * Single readiness check. The platform calls these in parallel from `/ready`.
12
+ *
13
+ * Implementations should return quickly (target < 1s). Long-running probes
14
+ * should cache their last result and refresh in the background — the
15
+ * `/ready` endpoint is hit by orchestrators (k8s, docker-compose) on a tight
16
+ * loop and slow probes will produce false-negative restarts.
17
+ */
18
+ export interface ReadinessCheck {
19
+ /** Probe name (e.g. "database", "queue", "auth-strategy-loaded"). */
20
+ name: string;
21
+ /** True if this probe should block startup. False = informational only. */
22
+ critical?: boolean;
23
+ check: () => Promise<ReadinessCheckResult>;
24
+ }
25
+
26
+ /**
27
+ * Plugin-facing API for contributing readiness probes to the `/ready` endpoint.
28
+ *
29
+ * The core platform always contributes a "core.init" probe that flips ready
30
+ * once `init()` has resolved. Plugins may add their own (e.g. "queue.connected",
31
+ * "auth.strategy-registered") to gate the platform from accepting traffic until
32
+ * their preconditions are met.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * registerInit({
37
+ * deps: { readiness: coreServices.readinessRegistry },
38
+ * async init({ readiness }) {
39
+ * readiness.register({
40
+ * name: "queue.connected",
41
+ * critical: true,
42
+ * check: async () => ({
43
+ * ok: pool.isConnected(),
44
+ * message: pool.isConnected() ? undefined : "queue pool not connected",
45
+ * }),
46
+ * });
47
+ * },
48
+ * });
49
+ * ```
50
+ */
51
+ export interface ReadinessRegistry {
52
+ register(check: ReadinessCheck): void;
53
+ }
package/tsconfig.json CHANGED
@@ -2,5 +2,22 @@
2
2
  "extends": "@checkstack/tsconfig/backend.json",
3
3
  "include": [
4
4
  "src"
5
+ ],
6
+ "references": [
7
+ {
8
+ "path": "../cache-api"
9
+ },
10
+ {
11
+ "path": "../common"
12
+ },
13
+ {
14
+ "path": "../healthcheck-common"
15
+ },
16
+ {
17
+ "path": "../queue-api"
18
+ },
19
+ {
20
+ "path": "../signal-common"
21
+ }
5
22
  ]
6
- }
23
+ }