@empyria/restate 0.4.18 → 0.4.20

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.
Files changed (2) hide show
  1. package/lib/Admin.js +45 -17
  2. package/package.json +4 -4
package/lib/Admin.js CHANGED
@@ -578,6 +578,34 @@ export function defineObject(def) {
578
578
  return restate.object(def)
579
579
  }
580
580
 
581
+ /**
582
+ * The health check `setupRestate` uses unless given a `healthCheckFn`: `GET /health`, `/health/liveness` and
583
+ * `/health/readiness` answer 200 once the deployment is registered and 503 before, anything else 404.
584
+ * @param {import('node:http').IncomingMessage} req
585
+ * @param {import('node:http').ServerResponse} res
586
+ * @param {{isHealthy: boolean, deploymentId?: string, services?: string[]}} state
587
+ */
588
+ export function defaultHealthCheck(req, res, { isHealthy, deploymentId, services }) {
589
+ if (
590
+ (req.url === '/health' ||
591
+ req.url === '/health/liveness' ||
592
+ req.url === '/health/readiness') &&
593
+ req.method === 'GET'
594
+ ) {
595
+ res.writeHead(isHealthy ? 200 : 503, { 'Content-Type': 'application/json' })
596
+ res.end(
597
+ JSON.stringify({
598
+ status: isHealthy ? 'ok' : 'unavailable',
599
+ deployment: deploymentId,
600
+ services,
601
+ }),
602
+ )
603
+ } else {
604
+ res.writeHead(404)
605
+ res.end()
606
+ }
607
+ }
608
+
581
609
  /**
582
610
  * Starts the HTTP/2 Restate endpoint, a health-check HTTP server, registers
583
611
  * the deployment with the admin API, and wires SIGTERM/SIGINT for graceful
@@ -599,6 +627,10 @@ export function defineObject(def) {
599
627
  * @param {Parameters<typeof restate.createEndpointHandler>[0]['services']} [params.services]
600
628
  * @param {() => void} [params.shutdownCallbackFn]
601
629
  * @param {(deploymentId: string) => void} [params.registrationCallbackFn]
630
+ * @param {(req: import('node:http').IncomingMessage, res: import('node:http').ServerResponse, state: {isHealthy: boolean, deploymentId?: string, services?: string[]}) => void | Promise<void>} [params.healthCheckFn]
631
+ * Takes over every request to the health port: routing, status code, headers and body are the function's to set and
632
+ * it must end the response. `state.isHealthy` is true once the deployment is registered. Without it, `/health`,
633
+ * `/health/liveness` and `/health/readiness` answer 200 or 503 with a small JSON body, healthy once registered.
602
634
  * @param {boolean} [params.keepDeploymentOnShutdown=false] Leave the deployment registered on SIGINT/SIGTERM.
603
635
  * @param {boolean} [params.forceRegistration=false] Register with `force: true`, updating an existing deployment at
604
636
  * the same address in place.
@@ -611,6 +643,7 @@ export async function setupRestate({
611
643
  services = [],
612
644
  shutdownCallbackFn,
613
645
  registrationCallbackFn,
646
+ healthCheckFn,
614
647
  keepDeploymentOnShutdown = false,
615
648
  forceRegistration = false,
616
649
  }) {
@@ -624,25 +657,20 @@ export async function setupRestate({
624
657
  // --- Health server ---
625
658
  let isHealthy = false
626
659
 
660
+ const checkFn = healthCheckFn ?? defaultHealthCheck
627
661
  const healthServer = createHttpServer((req, res) => {
628
- if (
629
- (req.url === '/health' ||
630
- req.url === '/health/liveness' ||
631
- req.url === '/health/readiness') &&
632
- req.method === 'GET'
633
- ) {
634
- const status = isHealthy ? 200 : 503
635
- const body = JSON.stringify({
636
- status: isHealthy ? 'ok' : 'unavailable',
637
- deployment: server.deploymentId,
638
- services: server.services,
639
- })
640
- res.writeHead(status, { 'Content-Type': 'application/json' })
641
- res.end(body)
642
- } else {
643
- res.writeHead(404)
644
- res.end()
662
+ const state = {
663
+ isHealthy,
664
+ deploymentId: server.deploymentId,
665
+ services: server.services,
645
666
  }
667
+ Promise.resolve()
668
+ .then(() => checkFn(req, res, state))
669
+ .catch((err) => {
670
+ console.error('healthCheckFn failed', err)
671
+ if (!res.headersSent) res.writeHead(500)
672
+ res.end()
673
+ })
646
674
  })
647
675
 
648
676
  healthServer.listen(healthPort, () => console.log(`Health endpoint on :${healthPort}/health`))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empyria/restate",
3
- "version": "0.4.18",
3
+ "version": "0.4.20",
4
4
  "description": "Restate.dev helpers for the Empyria nanoservice framework",
5
5
  "license": "MIT",
6
6
  "author": "Imre Fazekas <imre.fazekas@icloud.com>",
@@ -36,10 +36,10 @@
36
36
  "croner": "10.0.1"
37
37
  },
38
38
  "devDependencies": {
39
- "@ai-sdk/openai-compatible": "3.0.63",
40
- "@ai-sdk/provider": "4.0.22",
39
+ "@ai-sdk/openai-compatible": "3.0.65",
40
+ "@ai-sdk/provider": "4.0.24",
41
41
  "@restatedev/restate-server": "1.7.13",
42
- "ai": "7.0.128",
42
+ "ai": "7.0.130",
43
43
  "oxfmt": "0.72.0",
44
44
  "oxlint": "1.87.0"
45
45
  },