owox 0.9.0-next-20251006155344 → 0.9.0-next-20251007055312

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.
@@ -3,7 +3,7 @@ import { IdpProtocolMiddleware } from '@owox/idp-protocol';
3
3
  import express from 'express';
4
4
  import { IdpFactory } from '../idp/factory.js';
5
5
  import { getPackageInfo } from '../utils/package-info.js';
6
- import { registerPublicFlagsRoute, setupWebStaticAssets } from '../web/index.js';
6
+ import { registerHealthRoutes, registerPublicFlagsRoute, setupWebStaticAssets, } from '../web/index.js';
7
7
  import { BaseCommand } from './base.js';
8
8
  /**
9
9
  * Command class for starting the OWOX Data Marts application server.
@@ -138,14 +138,19 @@ export default class Serve extends BaseCommand {
138
138
  const port = process.env.PORT;
139
139
  const logFormat = process.env.LOG_FORMAT;
140
140
  this.log(`📦 Starting server on port ${port} with ${logFormat} logs...`);
141
- const { bootstrap } = await import('@owox/backend');
141
+ const { bootstrap, createHealthProbe } = await import('@owox/backend');
142
142
  const expressApp = express();
143
143
  expressApp.set('trust proxy', 1);
144
+ // Holders for late-bound dependencies used by early health routes
145
+ let currentIdp = null;
146
+ let currentBackendApp = null;
147
+ registerHealthRoutes(expressApp, () => currentIdp, () => currentBackendApp, () => this.isShuttingDown);
144
148
  const idpProvider = await IdpFactory.createFromEnvironment(this);
145
149
  await idpProvider.initialize();
146
150
  const idpProtocolMiddleware = new IdpProtocolMiddleware(idpProvider);
147
151
  idpProtocolMiddleware.register(expressApp);
148
152
  expressApp.set('idp', idpProvider);
153
+ currentIdp = idpProvider;
149
154
  // Register public route to expose whitelisted flags
150
155
  registerPublicFlagsRoute(expressApp);
151
156
  // Configure web static assets if web interface is enabled
@@ -163,6 +168,7 @@ export default class Serve extends BaseCommand {
163
168
  }
164
169
  try {
165
170
  this.app = await bootstrap({ express: expressApp });
171
+ currentBackendApp = createHealthProbe(this.app);
166
172
  this.log(`📝 Process ID: ${process.pid}`);
167
173
  this.log(`✅ Server started successfully. Open http://localhost:${port} in your browser.`);
168
174
  // Keep process alive until shutdown
@@ -0,0 +1,26 @@
1
+ import { checkReadiness } from './readiness.js';
2
+ /**
3
+ * Registers health check routes on the given Express app.
4
+ *
5
+ * Behavior:
6
+ * - /health/live returns 503 until BOTH IDP and backend probe are available; 200 otherwise.
7
+ * - /health/ready returns 503 if shutting down, or if dependencies are missing; otherwise uses checkReadiness.
8
+ */
9
+ export function registerHealthRoutes(app, getIdp, getBackend, isShuttingDown) {
10
+ app.get('/health/live', (_req, res) => res.status(200).json({}));
11
+ app.get('/health/ready', async (_req, res) => {
12
+ if (isShuttingDown()) {
13
+ return res.status(503).json({ reason: 'shutting down' });
14
+ }
15
+ const idp = getIdp();
16
+ if (!idp) {
17
+ return res.status(503).json({ reason: 'IDP not available' });
18
+ }
19
+ const backend = getBackend();
20
+ if (!backend) {
21
+ return res.status(503).json({ reason: 'backend not available' });
22
+ }
23
+ const ok = await checkReadiness(idp, backend);
24
+ return res.status(ok ? 200 : 503).json({ reason: ok ? 'ok' : 'dependencies not healthy' });
25
+ });
26
+ }
package/dist/web/index.js CHANGED
@@ -5,4 +5,6 @@
5
5
  * including static file serving, SPA routing configuration, and helper routes.
6
6
  */
7
7
  export { registerPublicFlagsRoute } from './flags-route.js';
8
+ export { registerHealthRoutes } from './health-route.js';
9
+ export { checkReadiness } from './readiness.js';
8
10
  export { setupWebStaticAssets } from './static-config.js';
@@ -0,0 +1,19 @@
1
+ import { withTimeout } from '@owox/internal-helpers';
2
+ const HEALTHCHECK_TIMEOUT_MS = 500;
3
+ /**
4
+ * Aggregate readiness check for the web layer.
5
+ *
6
+ * Returns true only if both the IDP and backend report healthy status.
7
+ * Errors and timeouts (> HEALTHCHECK_TIMEOUT_MS) are treated as unhealthy to keep the behavior conservative.
8
+ */
9
+ export async function checkReadiness(idp, backend) {
10
+ // Start both checks concurrently and enforce a strict timeout for each.
11
+ // If a check errors or exceeds the timeout, we treat it as unhealthy (false).
12
+ const idpHealthyPromise = withTimeout(idp.isHealthy(), HEALTHCHECK_TIMEOUT_MS, false).catch(() => false);
13
+ const backendHealthyPromise = withTimeout(backend.isHealthy(), HEALTHCHECK_TIMEOUT_MS, false).catch(() => false);
14
+ const [idpHealthy, backendHealthy] = await Promise.all([
15
+ idpHealthyPromise,
16
+ backendHealthyPromise,
17
+ ]);
18
+ return idpHealthy && backendHealthy;
19
+ }
@@ -253,5 +253,5 @@
253
253
  ]
254
254
  }
255
255
  },
256
- "version": "0.9.0-next-20251006155344"
256
+ "version": "0.9.0-next-20251007055312"
257
257
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "owox",
3
3
  "description": "OWOX Data Marts CLI: Simple command-line interface to start the OWOX Data Marts application with backend and frontend components.",
4
- "version": "0.9.0-next-20251006155344",
4
+ "version": "0.9.0-next-20251007055312",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -13,12 +13,12 @@
13
13
  "@oclif/core": "^4",
14
14
  "@oclif/plugin-help": "^6",
15
15
  "@oclif/plugin-plugins": "^5",
16
- "@owox/backend": "0.9.0-next-20251006155344",
17
- "@owox/idp-better-auth": "0.9.0-next-20251006155344",
18
- "@owox/idp-owox": "0.9.0-next-20251006155344",
19
- "@owox/idp-protocol": "0.9.0-next-20251006155344",
20
- "@owox/internal-helpers": "0.9.0-next-20251006155344",
21
- "@owox/web": "0.9.0-next-20251006155344",
16
+ "@owox/backend": "0.9.0-next-20251007055312",
17
+ "@owox/idp-better-auth": "0.9.0-next-20251007055312",
18
+ "@owox/idp-owox": "0.9.0-next-20251007055312",
19
+ "@owox/idp-protocol": "0.9.0-next-20251007055312",
20
+ "@owox/internal-helpers": "0.9.0-next-20251007055312",
21
+ "@owox/web": "0.9.0-next-20251007055312",
22
22
  "find-process": "^1.4.10"
23
23
  },
24
24
  "peerDependencies": {
@@ -88,6 +88,7 @@
88
88
  "build:internal-helpers": "npm run build -w @owox/internal-helpers --prefix ../..",
89
89
  "dev": "node ./bin/run.js serve -e ../../.env",
90
90
  "lint": "eslint . --config ./eslint.config.mjs",
91
+ "lint:fix": "eslint . --config ./eslint.config.mjs --fix",
91
92
  "lint:md": "markdownlint-cli2 --config ../../.markdownlint-cli2.mjs",
92
93
  "lint:md:fix": "markdownlint-cli2 --config ../../.markdownlint-cli2.mjs --fix",
93
94
  "format": "prettier --write \"**/*.{ts,js,json}\" --ignore-path ../../.prettierignore",