@checkstack/backend 0.4.9 → 0.4.11

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,45 @@
1
1
  # @checkstack/backend
2
2
 
3
+ ## 0.4.11
4
+
5
+ ### Patch Changes
6
+
7
+ - 48c2080: Migrate aggregation from batch to incremental (`mergeResult`)
8
+
9
+ ### Breaking Changes (Internal)
10
+
11
+ - Replaced `aggregateResult(runs[])` with `mergeResult(existing, run)` interface across all HealthCheckStrategy and CollectorStrategy implementations
12
+
13
+ ### New Features
14
+
15
+ - Added incremental aggregation utilities in `@checkstack/backend-api`:
16
+ - `mergeCounter()` - track occurrences
17
+ - `mergeAverage()` - track sum/count, compute avg
18
+ - `mergeRate()` - track success/total, compute %
19
+ - `mergeMinMax()` - track min/max values
20
+ - Exported Zod schemas for internal state: `averageStateSchema`, `rateStateSchema`, `minMaxStateSchema`, `counterStateSchema`
21
+
22
+ ### Improvements
23
+
24
+ - Enables O(1) storage overhead by maintaining incremental aggregation state
25
+ - Prepares for real-time hourly aggregation without batch accumulation
26
+
27
+ - Updated dependencies [f676e11]
28
+ - Updated dependencies [48c2080]
29
+ - @checkstack/common@0.6.2
30
+ - @checkstack/backend-api@0.6.0
31
+ - @checkstack/api-docs-common@0.1.6
32
+ - @checkstack/auth-common@0.5.5
33
+ - @checkstack/signal-backend@0.1.9
34
+ - @checkstack/signal-common@0.1.6
35
+ - @checkstack/queue-api@0.2.3
36
+
37
+ ## 0.4.10
38
+
39
+ ### Patch Changes
40
+
41
+ - f8ce585: Improved RPC error logging to include full stack traces for procedure errors. Previously, errors inside RPC handlers (such as database table not found errors) resulted in silent 500 responses. Now these errors are logged with detailed information to the backend console for easier debugging.
42
+
3
43
  ## 0.4.9
4
44
 
5
45
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/backend",
3
- "version": "0.4.9",
3
+ "version": "0.4.11",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "bun --env-file=../../.env --watch src/index.ts",
@@ -10,14 +10,14 @@
10
10
  "lint:code": "eslint . --max-warnings 0"
11
11
  },
12
12
  "dependencies": {
13
- "@checkstack/api-docs-common": "0.1.4",
14
- "@checkstack/auth-common": "0.5.3",
15
- "@checkstack/backend-api": "0.5.1",
16
- "@checkstack/common": "0.6.0",
17
- "@checkstack/drizzle-helper": "0.0.2",
18
- "@checkstack/queue-api": "0.2.1",
19
- "@checkstack/signal-backend": "0.1.7",
20
- "@checkstack/signal-common": "0.1.4",
13
+ "@checkstack/api-docs-common": "0.1.5",
14
+ "@checkstack/auth-common": "0.5.4",
15
+ "@checkstack/backend-api": "0.5.2",
16
+ "@checkstack/common": "0.6.1",
17
+ "@checkstack/drizzle-helper": "0.0.3",
18
+ "@checkstack/queue-api": "0.2.2",
19
+ "@checkstack/signal-backend": "0.1.8",
20
+ "@checkstack/signal-common": "0.1.5",
21
21
  "@hono/zod-validator": "^0.7.6",
22
22
  "@orpc/client": "^1.13.2",
23
23
  "@orpc/openapi": "^1.13.2",
@@ -35,8 +35,8 @@
35
35
  "drizzle-kit": "^0.31.8",
36
36
  "@types/pg": "^8.11.0",
37
37
  "@types/bun": "latest",
38
- "@checkstack/tsconfig": "0.0.2",
39
- "@checkstack/scripts": "0.1.0",
40
- "@checkstack/test-utils-backend": "0.1.7"
38
+ "@checkstack/tsconfig": "0.0.3",
39
+ "@checkstack/scripts": "0.1.1",
40
+ "@checkstack/test-utils-backend": "0.1.8"
41
41
  }
42
42
  }
@@ -55,7 +55,7 @@ describe("HealthCheck Plugin Integration", () => {
55
55
  schema: z.record(z.string(), z.unknown()),
56
56
  }),
57
57
  createClient: mockCreateClient,
58
- aggregateResult: mock(() => ({})),
58
+ mergeResult: mock(() => ({})),
59
59
  };
60
60
 
61
61
  // 2. Define a mock plugin that registers this strategy
@@ -56,21 +56,42 @@ export function createApiRouteHandler({
56
56
  rootRpcRouter[pluginId] = router;
57
57
  }
58
58
 
59
+ // Resolve logger first for use in interceptor
60
+ const logger = await getService(coreServices.logger);
61
+
59
62
  const rpcHandler = new RPCHandler(
60
- rootRpcRouter as ConstructorParameters<typeof RPCHandler>[0]
63
+ rootRpcRouter as ConstructorParameters<typeof RPCHandler>[0],
64
+ {
65
+ interceptors: [
66
+ async ({ next, ...rest }) => {
67
+ try {
68
+ return await next(rest);
69
+ } catch (error) {
70
+ if (logger) {
71
+ (logger as Logger).error(
72
+ `RPC procedure error: ${String(error)}`,
73
+ );
74
+ if (error instanceof Error && error.stack) {
75
+ (logger as Logger).error(`Stack trace: ${error.stack}`);
76
+ }
77
+ }
78
+ throw error;
79
+ }
80
+ },
81
+ ],
82
+ },
61
83
  );
62
84
 
63
85
  // Resolve core services for RPC context
64
86
  const auth = await getService(coreServices.auth);
65
- const logger = await getService(coreServices.logger);
66
87
  const db = await getService(coreServices.database);
67
88
  const fetch = await getService(coreServices.fetch);
68
89
  const healthCheckRegistry = await getService(
69
- coreServices.healthCheckRegistry
90
+ coreServices.healthCheckRegistry,
70
91
  );
71
92
  const collectorRegistry = await getService(coreServices.collectorRegistry);
72
93
  const queuePluginRegistry = await getService(
73
- coreServices.queuePluginRegistry
94
+ coreServices.queuePluginRegistry,
74
95
  );
75
96
  const queueManager = await getService(coreServices.queueManager);
76
97
  const eventBus = await getService(coreServices.eventBus);
@@ -119,29 +140,24 @@ export function createApiRouteHandler({
119
140
  };
120
141
 
121
142
  // 1. Try oRPC first
122
- try {
123
- const { matched, response } = await rpcHandler.handle(c.req.raw, {
124
- prefix: "/api",
125
- context,
126
- });
127
-
128
- if (matched) {
129
- return c.newResponse(response.body, response);
130
- }
143
+ const { matched, response } = await rpcHandler.handle(c.req.raw, {
144
+ prefix: "/api",
145
+ context,
146
+ });
131
147
 
132
- logger.debug(`RPC mismatch for: ${c.req.method} ${pathname}`);
133
- } catch (error) {
134
- logger.error(`RPC Handler error: ${String(error)}`);
148
+ if (matched) {
149
+ return c.newResponse(response.body, response);
135
150
  }
136
151
 
152
+ logger.debug(`RPC mismatch for: ${c.req.method} ${pathname}`);
153
+
137
154
  // 2. Try native handlers
138
155
  // Sort by path length (descending) to ensure more specific paths are tried first
139
- const sortedHandlers = [...pluginHttpHandlers.entries()].toSorted(function (
140
- a,
141
- b
142
- ) {
143
- return b[0].length - a[0].length;
144
- });
156
+ const sortedHandlers = [...pluginHttpHandlers.entries()].toSorted(
157
+ function (a, b) {
158
+ return b[0].length - a[0].length;
159
+ },
160
+ );
145
161
 
146
162
  for (const [path, handler] of sortedHandlers) {
147
163
  if (pathname.startsWith(path)) {
@@ -158,7 +174,7 @@ export function createApiRouteHandler({
158
174
  */
159
175
  export function registerApiRoute(
160
176
  rootRouter: Hono,
161
- handler: ReturnType<typeof createApiRouteHandler>
177
+ handler: ReturnType<typeof createApiRouteHandler>,
162
178
  ) {
163
179
  rootRouter.all("/api/:pluginId/*", handler);
164
180
  }
@@ -36,9 +36,9 @@ describe("CoreHealthCheckRegistry", () => {
36
36
  schema: z.record(z.string(), z.unknown()),
37
37
  }),
38
38
  createClient: mock(() =>
39
- Promise.resolve({ client: { exec: async () => ({}) }, close: () => {} })
39
+ Promise.resolve({ client: { exec: async () => ({}) }, close: () => {} }),
40
40
  ),
41
- aggregateResult: mock(() => ({})),
41
+ mergeResult: mock(() => ({})),
42
42
  };
43
43
 
44
44
  const mockStrategy2: HealthCheckStrategy = {
@@ -58,9 +58,9 @@ describe("CoreHealthCheckRegistry", () => {
58
58
  schema: z.record(z.string(), z.unknown()),
59
59
  }),
60
60
  createClient: mock(() =>
61
- Promise.resolve({ client: { exec: async () => ({}) }, close: () => {} })
61
+ Promise.resolve({ client: { exec: async () => ({}) }, close: () => {} }),
62
62
  ),
63
- aggregateResult: mock(() => ({})),
63
+ mergeResult: mock(() => ({})),
64
64
  };
65
65
 
66
66
  beforeEach(() => {