@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 +72 -0
- package/package.json +5 -5
- package/src/core-services.ts +4 -0
- package/src/index.ts +1 -0
- package/src/readiness-registry.ts +53 -0
- package/tsconfig.json +18 -1
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.
|
|
3
|
+
"version": "0.14.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"typecheck": "
|
|
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.
|
|
14
|
-
"@checkstack/cache-api": "0.2.
|
|
15
|
-
"@checkstack/queue-api": "0.2.
|
|
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",
|
package/src/core-services.ts
CHANGED
|
@@ -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
|
@@ -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
|
+
}
|