axusage 3.8.2 → 3.8.3

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 (57) hide show
  1. package/README.md +19 -8
  2. package/dist/cli.js +2 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/fetch-service-usage.d.ts +4 -3
  5. package/dist/commands/fetch-service-usage.d.ts.map +1 -1
  6. package/dist/commands/fetch-service-usage.js +13 -27
  7. package/dist/commands/fetch-service-usage.js.map +1 -1
  8. package/dist/commands/serve-command.d.ts +1 -19
  9. package/dist/commands/serve-command.d.ts.map +1 -1
  10. package/dist/commands/serve-command.js +42 -104
  11. package/dist/commands/serve-command.js.map +1 -1
  12. package/dist/commands/usage-command.d.ts +2 -1
  13. package/dist/commands/usage-command.d.ts.map +1 -1
  14. package/dist/commands/usage-command.js +4 -0
  15. package/dist/commands/usage-command.js.map +1 -1
  16. package/dist/config/serve-config.d.ts +15 -0
  17. package/dist/config/serve-config.d.ts.map +1 -1
  18. package/dist/config/serve-config.js +57 -18
  19. package/dist/config/serve-config.js.map +1 -1
  20. package/dist/server/routes.d.ts +14 -8
  21. package/dist/server/routes.d.ts.map +1 -1
  22. package/dist/server/routes.js +111 -60
  23. package/dist/server/routes.js.map +1 -1
  24. package/dist/server/serve-logger.d.ts +5 -0
  25. package/dist/server/serve-logger.d.ts.map +1 -0
  26. package/dist/server/serve-logger.js +88 -0
  27. package/dist/server/serve-logger.js.map +1 -0
  28. package/dist/server/server.d.ts +14 -7
  29. package/dist/server/server.d.ts.map +1 -1
  30. package/dist/server/server.js +90 -56
  31. package/dist/server/server.js.map +1 -1
  32. package/dist/server/usage-cache.d.ts +26 -0
  33. package/dist/server/usage-cache.d.ts.map +1 -0
  34. package/dist/server/usage-cache.js +77 -0
  35. package/dist/server/usage-cache.js.map +1 -0
  36. package/dist/services/get-instance-access-token.d.ts +2 -1
  37. package/dist/services/get-instance-access-token.d.ts.map +1 -1
  38. package/dist/services/get-instance-access-token.js.map +1 -1
  39. package/dist/services/service-adapter-registry.d.ts +3 -2
  40. package/dist/services/service-adapter-registry.d.ts.map +1 -1
  41. package/dist/services/service-adapter-registry.js +3 -3
  42. package/dist/services/service-adapter-registry.js.map +1 -1
  43. package/dist/services/supported-service.d.ts.map +1 -1
  44. package/dist/services/supported-service.js +5 -1
  45. package/dist/services/supported-service.js.map +1 -1
  46. package/dist/types/serve-api.d.ts +53 -0
  47. package/dist/types/serve-api.d.ts.map +1 -0
  48. package/dist/types/serve-api.js +34 -0
  49. package/dist/types/serve-api.js.map +1 -0
  50. package/dist/utils/format-prometheus-metrics.d.ts.map +1 -1
  51. package/dist/utils/format-prometheus-metrics.js +8 -2
  52. package/dist/utils/format-prometheus-metrics.js.map +1 -1
  53. package/dist/utils/format-service-usage.d.ts +2 -1
  54. package/dist/utils/format-service-usage.d.ts.map +1 -1
  55. package/dist/utils/format-service-usage.js +2 -2
  56. package/dist/utils/format-service-usage.js.map +1 -1
  57. package/package.json +6 -3
@@ -3,39 +3,78 @@
3
3
  *
4
4
  * Priority: CLI flags > environment variables > defaults.
5
5
  */
6
+ import { z } from "zod";
7
+ export const SERVE_LOG_LEVELS = [
8
+ "trace",
9
+ "debug",
10
+ "info",
11
+ "warn",
12
+ "error",
13
+ "fatal",
14
+ "silent",
15
+ ];
16
+ const ServeLogLevelSchema = z.enum(SERVE_LOG_LEVELS);
6
17
  const DEFAULT_PORT = 3848;
7
18
  const DEFAULT_HOST = "127.0.0.1";
8
19
  const DEFAULT_INTERVAL_SECONDS = 300; // 5 minutes
20
+ const DEFAULT_LOG_LEVEL = "info";
21
+ class ServeConfigError extends Error {
22
+ constructor(message) {
23
+ super(message);
24
+ this.name = "ServeConfigError";
25
+ }
26
+ }
9
27
  /** Parse serve config from environment and CLI overrides */
10
28
  export function getServeConfig(overrides = {}) {
11
- const port = parsePort(overrides.port ?? process.env.AXUSAGE_PORT);
12
- const host = overrides.host || process.env.AXUSAGE_HOST || DEFAULT_HOST;
13
- const intervalSeconds = parsePositiveInt(overrides.interval ?? process.env.AXUSAGE_INTERVAL, DEFAULT_INTERVAL_SECONDS, "AXUSAGE_INTERVAL");
14
- const service = (overrides.service ?? process.env.AXUSAGE_SERVICE) || undefined;
29
+ const port = parseIntegerOption(normalizeOptionalValue(overrides.port ?? process.env.AXUSAGE_PORT), {
30
+ label: "AXUSAGE_PORT",
31
+ defaultValue: DEFAULT_PORT,
32
+ min: 1,
33
+ max: 65_535,
34
+ });
35
+ const host = normalizeOptionalValue(overrides.host ?? process.env.AXUSAGE_HOST) ??
36
+ DEFAULT_HOST;
37
+ const intervalSeconds = parseIntegerOption(normalizeOptionalValue(overrides.interval ?? process.env.AXUSAGE_INTERVAL), {
38
+ label: "AXUSAGE_INTERVAL",
39
+ defaultValue: DEFAULT_INTERVAL_SECONDS,
40
+ min: 1,
41
+ });
42
+ const service = normalizeOptionalValue(overrides.service ?? process.env.AXUSAGE_SERVICE)?.toLowerCase();
43
+ const logLevel = parseLogLevel(normalizeOptionalValue(overrides.logLevel ?? process.env.AXUSAGE_LOG_LEVEL));
44
+ const trustProxy = normalizeOptionalValue(process.env.AXUSAGE_TRUST_PROXY) === "true";
15
45
  return {
16
46
  port,
17
47
  host,
18
48
  intervalMs: intervalSeconds * 1000,
19
49
  service,
50
+ logLevel,
51
+ trustProxy,
20
52
  };
21
53
  }
22
- function parsePort(value) {
23
- if (!value)
24
- return DEFAULT_PORT;
25
- const port = Number(value);
26
- if (!Number.isInteger(port) || port < 1 || port > 65_535) {
27
- throw new Error(`Invalid port: ${value}`);
28
- }
29
- return port;
54
+ function normalizeOptionalValue(value) {
55
+ const normalized = value?.trim();
56
+ return normalized || undefined;
30
57
  }
31
- function parsePositiveInt(value, defaultValue, name) {
32
- if (!value)
33
- return defaultValue;
58
+ function parseIntegerOption(value, options) {
59
+ if (value === undefined)
60
+ return options.defaultValue;
34
61
  const parsed = Number(value);
35
- if (!Number.isInteger(parsed) || parsed < 1) {
36
- console.error(`Invalid ${name} value "${value}", using default ${String(defaultValue)}`);
37
- return defaultValue;
62
+ if (!Number.isInteger(parsed) ||
63
+ parsed < options.min ||
64
+ (options.max !== undefined && parsed > options.max)) {
65
+ const bounds = options.max === undefined
66
+ ? `an integer >= ${String(options.min)}`
67
+ : `an integer between ${String(options.min)} and ${String(options.max)}`;
68
+ throw new ServeConfigError(`Invalid ${options.label} value "${value}". Expected ${bounds}.`);
38
69
  }
39
70
  return parsed;
40
71
  }
72
+ function parseLogLevel(value) {
73
+ if (value === undefined)
74
+ return DEFAULT_LOG_LEVEL;
75
+ const parsed = ServeLogLevelSchema.safeParse(value.toLowerCase());
76
+ if (parsed.success)
77
+ return parsed.data;
78
+ throw new ServeConfigError(`Invalid AXUSAGE_LOG_LEVEL value "${value}". Expected one of: ${SERVE_LOG_LEVELS.join(", ")}.`);
79
+ }
41
80
  //# sourceMappingURL=serve-config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"serve-config.js","sourceRoot":"","sources":["../../src/config/serve-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAgBH,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,MAAM,YAAY,GAAG,WAAW,CAAC;AACjC,MAAM,wBAAwB,GAAG,GAAG,CAAC,CAAC,YAAY;AAElD,4DAA4D;AAC5D,MAAM,UAAU,cAAc,CAC5B,YAAkC,EAAE;IAEpC,MAAM,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACnE,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,YAAY,CAAC;IACxE,MAAM,eAAe,GAAG,gBAAgB,CACtC,SAAS,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAClD,wBAAwB,EACxB,kBAAkB,CACnB,CAAC;IAEF,MAAM,OAAO,GACX,CAAC,SAAS,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,SAAS,CAAC;IAElE,OAAO;QACL,IAAI;QACJ,IAAI;QACJ,UAAU,EAAE,eAAe,GAAG,IAAI;QAClC,OAAO;KACR,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,KAAyB;IAC1C,IAAI,CAAC,KAAK;QAAE,OAAO,YAAY,CAAC;IAChC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,MAAM,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CAAC,iBAAiB,KAAK,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,gBAAgB,CACvB,KAAyB,EACzB,YAAoB,EACpB,IAAY;IAEZ,IAAI,CAAC,KAAK;QAAE,OAAO,YAAY,CAAC;IAChC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5C,OAAO,CAAC,KAAK,CACX,WAAW,IAAI,WAAW,KAAK,oBAAoB,MAAM,CAAC,YAAY,CAAC,EAAE,CAC1E,CAAC;QACF,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"serve-config.js","sourceRoot":"","sources":["../../src/config/serve-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,OAAO;IACP,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,OAAO;IACP,QAAQ;CACA,CAAC;AAEX,MAAM,mBAAmB,GAAG,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;AAqBrD,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,MAAM,YAAY,GAAG,WAAW,CAAC;AACjC,MAAM,wBAAwB,GAAG,GAAG,CAAC,CAAC,YAAY;AAClD,MAAM,iBAAiB,GAAkB,MAAM,CAAC;AAEhD,MAAM,gBAAiB,SAAQ,KAAK;IAClC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED,4DAA4D;AAC5D,MAAM,UAAU,cAAc,CAC5B,YAAkC,EAAE;IAEpC,MAAM,IAAI,GAAG,kBAAkB,CAC7B,sBAAsB,CAAC,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,EAClE;QACE,KAAK,EAAE,cAAc;QACrB,YAAY,EAAE,YAAY;QAC1B,GAAG,EAAE,CAAC;QACN,GAAG,EAAE,MAAM;KACZ,CACF,CAAC;IACF,MAAM,IAAI,GACR,sBAAsB,CAAC,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAClE,YAAY,CAAC;IACf,MAAM,eAAe,GAAG,kBAAkB,CACxC,sBAAsB,CAAC,SAAS,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAC1E;QACE,KAAK,EAAE,kBAAkB;QACzB,YAAY,EAAE,wBAAwB;QACtC,GAAG,EAAE,CAAC;KACP,CACF,CAAC;IACF,MAAM,OAAO,GAAG,sBAAsB,CACpC,SAAS,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,CACjD,EAAE,WAAW,EAAE,CAAC;IACjB,MAAM,QAAQ,GAAG,aAAa,CAC5B,sBAAsB,CAAC,SAAS,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAC5E,CAAC;IACF,MAAM,UAAU,GACd,sBAAsB,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,KAAK,MAAM,CAAC;IAErE,OAAO;QACL,IAAI;QACJ,IAAI;QACJ,UAAU,EAAE,eAAe,GAAG,IAAI;QAClC,OAAO;QACP,QAAQ;QACR,UAAU;KACX,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAyB;IACvD,MAAM,UAAU,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IACjC,OAAO,UAAU,IAAI,SAAS,CAAC;AACjC,CAAC;AAED,SAAS,kBAAkB,CACzB,KAAyB,EACzB,OAKC;IAED,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC,YAAY,CAAC;IACrD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7B,IACE,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QACzB,MAAM,GAAG,OAAO,CAAC,GAAG;QACpB,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,EACnD,CAAC;QACD,MAAM,MAAM,GACV,OAAO,CAAC,GAAG,KAAK,SAAS;YACvB,CAAC,CAAC,iBAAiB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YACxC,CAAC,CAAC,sBAAsB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7E,MAAM,IAAI,gBAAgB,CACxB,WAAW,OAAO,CAAC,KAAK,WAAW,KAAK,eAAe,MAAM,GAAG,CACjE,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,KAAyB;IAC9C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,iBAAiB,CAAC;IAClD,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;IAClE,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC;IACvC,MAAM,IAAI,gBAAgB,CACxB,oCAAoC,KAAK,uBAAuB,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC/F,CAAC;AACJ,CAAC"}
@@ -1,18 +1,24 @@
1
1
  /**
2
2
  * Route handlers for axusage serve mode.
3
3
  */
4
- import { Router } from "express";
4
+ import type { FastifyPluginCallbackZod } from "fastify-type-provider-zod";
5
5
  import type { ServiceUsageData } from "../types/domain.js";
6
+ /** Structured error from a failed service fetch. */
7
+ export type ServerError = {
8
+ readonly service: string;
9
+ readonly message: string;
10
+ readonly status?: number;
11
+ };
6
12
  /** Snapshot produced by each refresh cycle. */
7
13
  export type ServerState = {
8
14
  readonly usage: readonly ServiceUsageData[];
9
15
  readonly refreshedAt: Date;
10
- readonly errors: readonly string[];
16
+ readonly errors: readonly ServerError[];
11
17
  };
12
- /** Create router for GET /health */
13
- export declare function createHealthRouter(services: readonly string[], getState: () => ServerState | undefined): Router;
14
- /** Create router for GET /metrics (Prometheus text exposition) */
15
- export declare function createMetricsRouter(getState: () => Promise<ServerState | undefined>): Router;
16
- /** Create router for GET /usage (JSON) */
17
- export declare function createUsageRouter(getFreshState: () => Promise<ServerState | undefined>): Router;
18
+ /** Create plugin for GET /health */
19
+ export declare function createHealthPlugin(services: readonly string[], getState: () => ServerState | undefined, isShuttingDown: () => boolean): FastifyPluginCallbackZod;
20
+ /** Create plugin for GET /metrics (Prometheus text exposition) */
21
+ export declare function createMetricsPlugin(getState: () => Promise<ServerState | undefined>): FastifyPluginCallbackZod;
22
+ /** Create plugin for GET /usage (JSON) */
23
+ export declare function createUsagePlugin(getFreshState: () => Promise<ServerState | undefined>): FastifyPluginCallbackZod;
18
24
  //# sourceMappingURL=routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/server/routes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAI3D,+CAA+C;AAC/C,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC5C,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC,CAAC;AAEF,oCAAoC;AACpC,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,QAAQ,EAAE,MAAM,WAAW,GAAG,SAAS,GACtC,MAAM,CAgBR;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,GAC/C,MAAM,CAgCR;AAED,0CAA0C;AAC1C,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,GACpD,MAAM,CAkBR"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/server/routes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAG1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAS3D,oDAAoD;AACpD,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,+CAA+C;AAC/C,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC5C,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;CACzC,CAAC;AASF,oCAAoC;AACpC,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,QAAQ,EAAE,MAAM,WAAW,GAAG,SAAS,EACvC,cAAc,EAAE,MAAM,OAAO,GAC5B,wBAAwB,CAsC1B;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,GAC/C,wBAAwB,CA4C1B;AAED,0CAA0C;AAC1C,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,GACpD,wBAAwB,CA6B1B"}
@@ -1,71 +1,122 @@
1
1
  /**
2
2
  * Route handlers for axusage serve mode.
3
3
  */
4
- import { Router } from "express";
5
4
  import packageJson from "../../package.json" with { type: "json" };
5
+ import { ErrorResponseSchema, HealthResponseSchema, UsageResponseSchema, } from "../types/serve-api.js";
6
6
  import { formatPrometheusMetrics } from "../utils/format-prometheus-metrics.js";
7
7
  import { toJsonObject } from "../utils/format-service-usage.js";
8
- /** Create router for GET /health */
9
- export function createHealthRouter(services, getState) {
10
- const router = Router();
11
- router.get("/health", (_request, response) => {
12
- const state = getState();
13
- const healthy = state !== undefined && state.usage.length > 0;
14
- response.status(healthy ? 200 : 503).json({
15
- status: healthy ? "ok" : "degraded",
16
- version: packageJson.version,
17
- lastRefresh: state?.refreshedAt.toISOString(),
18
- services,
19
- errors: state?.errors ?? [],
8
+ function formatServerError(error) {
9
+ if (error.status === undefined) {
10
+ return `${error.service}: ${error.message}`;
11
+ }
12
+ return `${error.service}: ${error.message} (HTTP ${String(error.status)})`;
13
+ }
14
+ /** Create plugin for GET /health */
15
+ export function createHealthPlugin(services, getState, isShuttingDown) {
16
+ return function healthPlugin(fastify, _options, done) {
17
+ fastify.route({
18
+ method: "GET",
19
+ url: "/health",
20
+ config: { rateLimit: false },
21
+ schema: {
22
+ response: {
23
+ 200: HealthResponseSchema,
24
+ 503: HealthResponseSchema,
25
+ 500: ErrorResponseSchema,
26
+ },
27
+ },
28
+ handler: async (_request, reply) => {
29
+ if (isShuttingDown()) {
30
+ const state = getState();
31
+ return reply.code(503).send({
32
+ status: "shutting_down",
33
+ version: packageJson.version,
34
+ lastRefresh: state?.refreshedAt.toISOString(),
35
+ services: [...services],
36
+ errors: state?.errors.map((error) => formatServerError(error)) ?? [],
37
+ });
38
+ }
39
+ const state = getState();
40
+ const healthy = state !== undefined && state.usage.length > 0;
41
+ return reply.code(healthy ? 200 : 503).send({
42
+ status: healthy ? "ok" : "degraded",
43
+ version: packageJson.version,
44
+ lastRefresh: state?.refreshedAt.toISOString(),
45
+ services: [...services],
46
+ errors: state?.errors.map((error) => formatServerError(error)) ?? [],
47
+ });
48
+ },
20
49
  });
21
- });
22
- return router;
50
+ done();
51
+ };
23
52
  }
24
- /** Create router for GET /metrics (Prometheus text exposition) */
25
- export function createMetricsRouter(getState) {
26
- const router = Router();
27
- // Memoize the rendered Prometheus text by the state snapshot's refreshedAt
28
- // timestamp. Scrapes within the same cache window reuse the same Promise,
29
- // avoiding recreating prom-client Registry/Gauge objects on each request.
30
- // Rate is computed at refreshedAt so output is deterministic per snapshot,
31
- // keeping the gauge coherent with the usage data it describes.
32
- // Assignments happen synchronously (before any await) so require-atomic-updates
33
- // is satisfied and concurrent scrapes naturally coalesce onto one render.
34
- let memoFor;
35
- let memoPromise = Promise.resolve("");
36
- router.get("/metrics", async (_request, response) => {
37
- const state = await getState();
38
- const usage = state?.usage;
39
- if (!usage || usage.length === 0) {
40
- response.status(503).type("text/plain").send("No data yet\n");
41
- return;
42
- }
43
- if (memoFor !== state.refreshedAt) {
44
- memoFor = state.refreshedAt;
45
- memoPromise = formatPrometheusMetrics(usage, state.refreshedAt.getTime());
46
- }
47
- const text = await memoPromise;
48
- response
49
- .status(200)
50
- .type("text/plain; version=0.0.4; charset=utf-8")
51
- .send(text);
52
- });
53
- return router;
53
+ /** Create plugin for GET /metrics (Prometheus text exposition) */
54
+ export function createMetricsPlugin(getState) {
55
+ return function metricsPlugin(fastify, _options, done) {
56
+ // Memoize the rendered Prometheus text by the state snapshot's refreshedAt
57
+ // timestamp. Scrapes within the same cache window reuse the same Promise,
58
+ // avoiding recreating prom-client Registry/Gauge objects on each request.
59
+ // Rate is computed at refreshedAt so output is deterministic per snapshot,
60
+ // keeping the gauge coherent with the usage data it describes.
61
+ // Assignments happen synchronously (before any await) so require-atomic-updates
62
+ // is satisfied and concurrent scrapes naturally coalesce onto one render.
63
+ let memoFor;
64
+ let memoPromise = Promise.resolve("");
65
+ fastify.route({
66
+ method: "GET",
67
+ url: "/metrics",
68
+ config: { rateLimit: false },
69
+ // No response schema: /metrics returns text/plain Prometheus exposition
70
+ // format for 200, which cannot be validated by a Zod object schema.
71
+ // Adding only a 503 schema would prevent Fastify's type provider from
72
+ // accepting the 200 text response. The 503 error returns JSON
73
+ // { error: "No data yet" } consistent with other error responses.
74
+ handler: async (_request, reply) => {
75
+ const state = await getState();
76
+ const usage = state?.usage;
77
+ if (!usage || usage.length === 0) {
78
+ return reply.code(503).send({ error: "No data yet" });
79
+ }
80
+ const refreshedAtMs = state.refreshedAt.getTime();
81
+ if (memoFor !== refreshedAtMs) {
82
+ memoFor = refreshedAtMs;
83
+ memoPromise = formatPrometheusMetrics(usage, state.refreshedAt.getTime());
84
+ }
85
+ const text = await memoPromise;
86
+ return reply
87
+ .code(200)
88
+ .type("text/plain; version=0.0.4; charset=utf-8")
89
+ .send(text);
90
+ },
91
+ });
92
+ done();
93
+ };
54
94
  }
55
- /** Create router for GET /usage (JSON) */
56
- export function createUsageRouter(getFreshState) {
57
- const router = Router();
58
- router.get("/usage", async (_request, response) => {
59
- const state = await getFreshState();
60
- const usage = state?.usage;
61
- if (!usage || usage.length === 0) {
62
- response.status(503).json({ error: "No data yet" });
63
- return;
64
- }
65
- response
66
- .status(200)
67
- .json(usage.map((entry) => toJsonObject(entry, state.refreshedAt.getTime())));
68
- });
69
- return router;
95
+ /** Create plugin for GET /usage (JSON) */
96
+ export function createUsagePlugin(getFreshState) {
97
+ return function usagePlugin(fastify, _options, done) {
98
+ fastify.route({
99
+ method: "GET",
100
+ url: "/usage",
101
+ schema: {
102
+ response: {
103
+ 200: UsageResponseSchema,
104
+ 503: ErrorResponseSchema,
105
+ 500: ErrorResponseSchema,
106
+ },
107
+ },
108
+ handler: async (_request, reply) => {
109
+ const state = await getFreshState();
110
+ const usage = state?.usage;
111
+ if (!usage || usage.length === 0) {
112
+ return reply.code(503).send({ error: "No data yet" });
113
+ }
114
+ return reply
115
+ .code(200)
116
+ .send(usage.map((entry) => toJsonObject(entry, state.refreshedAt.getTime())));
117
+ },
118
+ });
119
+ done();
120
+ };
70
121
  }
71
122
  //# sourceMappingURL=routes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/server/routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/server/routes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,WAAW,MAAM,oBAAoB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAEnE,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAgBhE,SAAS,iBAAiB,CAAC,KAAkB;IAC3C,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,GAAG,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;IAC9C,CAAC;IACD,OAAO,GAAG,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,UAAU,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC;AAC7E,CAAC;AAED,oCAAoC;AACpC,MAAM,UAAU,kBAAkB,CAChC,QAA2B,EAC3B,QAAuC,EACvC,cAA6B;IAE7B,OAAO,SAAS,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI;QAClD,OAAO,CAAC,KAAK,CAAC;YACZ,MAAM,EAAE,KAAK;YACb,GAAG,EAAE,SAAS;YACd,MAAM,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;YAC5B,MAAM,EAAE;gBACN,QAAQ,EAAE;oBACR,GAAG,EAAE,oBAAoB;oBACzB,GAAG,EAAE,oBAAoB;oBACzB,GAAG,EAAE,mBAAmB;iBACzB;aACF;YACD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;gBACjC,IAAI,cAAc,EAAE,EAAE,CAAC;oBACrB,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;oBACzB,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;wBAC1B,MAAM,EAAE,eAAe;wBACvB,OAAO,EAAE,WAAW,CAAC,OAAO;wBAC5B,WAAW,EAAE,KAAK,EAAE,WAAW,CAAC,WAAW,EAAE;wBAC7C,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC;wBACvB,MAAM,EACJ,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE;qBAC/D,CAAC,CAAC;gBACL,CAAC;gBACD,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;gBACzB,MAAM,OAAO,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;gBAC9D,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBAC1C,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU;oBACnC,OAAO,EAAE,WAAW,CAAC,OAAO;oBAC5B,WAAW,EAAE,KAAK,EAAE,WAAW,CAAC,WAAW,EAAE;oBAC7C,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC;oBACvB,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE;iBACrE,CAAC,CAAC;YACL,CAAC;SACF,CAAC,CAAC;QACH,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,mBAAmB,CACjC,QAAgD;IAEhD,OAAO,SAAS,aAAa,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI;QACnD,2EAA2E;QAC3E,0EAA0E;QAC1E,0EAA0E;QAC1E,2EAA2E;QAC3E,+DAA+D;QAC/D,gFAAgF;QAChF,0EAA0E;QAC1E,IAAI,OAA2B,CAAC;QAChC,IAAI,WAAW,GAAoB,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAEvD,OAAO,CAAC,KAAK,CAAC;YACZ,MAAM,EAAE,KAAK;YACb,GAAG,EAAE,UAAU;YACf,MAAM,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;YAC5B,wEAAwE;YACxE,oEAAoE;YACpE,sEAAsE;YACtE,8DAA8D;YAC9D,kEAAkE;YAClE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;gBACjC,MAAM,KAAK,GAAG,MAAM,QAAQ,EAAE,CAAC;gBAC/B,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,CAAC;gBAC3B,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACjC,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;gBACxD,CAAC;gBACD,MAAM,aAAa,GAAG,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;gBAClD,IAAI,OAAO,KAAK,aAAa,EAAE,CAAC;oBAC9B,OAAO,GAAG,aAAa,CAAC;oBACxB,WAAW,GAAG,uBAAuB,CACnC,KAAK,EACL,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,CAC5B,CAAC;gBACJ,CAAC;gBACD,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC;gBAC/B,OAAO,KAAK;qBACT,IAAI,CAAC,GAAG,CAAC;qBACT,IAAI,CAAC,0CAA0C,CAAC;qBAChD,IAAI,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC;SACF,CAAC,CAAC;QACH,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,iBAAiB,CAC/B,aAAqD;IAErD,OAAO,SAAS,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI;QACjD,OAAO,CAAC,KAAK,CAAC;YACZ,MAAM,EAAE,KAAK;YACb,GAAG,EAAE,QAAQ;YACb,MAAM,EAAE;gBACN,QAAQ,EAAE;oBACR,GAAG,EAAE,mBAAmB;oBACxB,GAAG,EAAE,mBAAmB;oBACxB,GAAG,EAAE,mBAAmB;iBACzB;aACF;YACD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;gBACjC,MAAM,KAAK,GAAG,MAAM,aAAa,EAAE,CAAC;gBACpC,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,CAAC;gBAC3B,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACjC,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;gBACxD,CAAC;gBACD,OAAO,KAAK;qBACT,IAAI,CAAC,GAAG,CAAC;qBACT,IAAI,CACH,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAClB,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CACjD,CACF,CAAC;YACN,CAAC;SACF,CAAC,CAAC;QACH,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { FastifyBaseLogger } from "fastify";
2
+ export type ServeLogger = Pick<FastifyBaseLogger, "info" | "warn" | "error">;
3
+ export declare function createConsoleServeLogger(): ServeLogger;
4
+ export declare function createDeferredServeLogger(getLogger: () => FastifyBaseLogger | undefined): ServeLogger;
5
+ //# sourceMappingURL=serve-logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve-logger.d.ts","sourceRoot":"","sources":["../../src/server/serve-logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAE7E,wBAAgB,wBAAwB,IAAI,WAAW,CAwBtD;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,iBAAiB,GAAG,SAAS,GAC7C,WAAW,CAwDb"}
@@ -0,0 +1,88 @@
1
+ export function createConsoleServeLogger() {
2
+ return {
3
+ info(objectOrMessage, message) {
4
+ if (typeof objectOrMessage === "string") {
5
+ console.error(objectOrMessage);
6
+ return;
7
+ }
8
+ console.error(message ?? "", objectOrMessage);
9
+ },
10
+ warn(objectOrMessage, message) {
11
+ if (typeof objectOrMessage === "string") {
12
+ console.error(objectOrMessage);
13
+ return;
14
+ }
15
+ console.error(message ?? "", objectOrMessage);
16
+ },
17
+ error(objectOrMessage, message) {
18
+ if (typeof objectOrMessage === "string") {
19
+ console.error(objectOrMessage);
20
+ return;
21
+ }
22
+ console.error(message ?? "", objectOrMessage);
23
+ },
24
+ };
25
+ }
26
+ export function createDeferredServeLogger(getLogger) {
27
+ const fallback = createConsoleServeLogger();
28
+ return {
29
+ info(objectOrMessage, message) {
30
+ const logger = getLogger();
31
+ if (typeof objectOrMessage === "string") {
32
+ if (logger) {
33
+ logger.info(objectOrMessage);
34
+ }
35
+ else {
36
+ fallback.info(objectOrMessage);
37
+ }
38
+ return;
39
+ }
40
+ const resolvedMessage = message ?? "";
41
+ if (logger) {
42
+ logger.info(objectOrMessage, resolvedMessage);
43
+ }
44
+ else {
45
+ fallback.info(objectOrMessage, resolvedMessage);
46
+ }
47
+ },
48
+ warn(objectOrMessage, message) {
49
+ const logger = getLogger();
50
+ if (typeof objectOrMessage === "string") {
51
+ if (logger) {
52
+ logger.warn(objectOrMessage);
53
+ }
54
+ else {
55
+ fallback.warn(objectOrMessage);
56
+ }
57
+ return;
58
+ }
59
+ const resolvedMessage = message ?? "";
60
+ if (logger) {
61
+ logger.warn(objectOrMessage, resolvedMessage);
62
+ }
63
+ else {
64
+ fallback.warn(objectOrMessage, resolvedMessage);
65
+ }
66
+ },
67
+ error(objectOrMessage, message) {
68
+ const logger = getLogger();
69
+ if (typeof objectOrMessage === "string") {
70
+ if (logger) {
71
+ logger.error(objectOrMessage);
72
+ }
73
+ else {
74
+ fallback.error(objectOrMessage);
75
+ }
76
+ return;
77
+ }
78
+ const resolvedMessage = message ?? "";
79
+ if (logger) {
80
+ logger.error(objectOrMessage, resolvedMessage);
81
+ }
82
+ else {
83
+ fallback.error(objectOrMessage, resolvedMessage);
84
+ }
85
+ },
86
+ };
87
+ }
88
+ //# sourceMappingURL=serve-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve-logger.js","sourceRoot":"","sources":["../../src/server/serve-logger.ts"],"names":[],"mappings":"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"}
@@ -1,20 +1,27 @@
1
1
  /**
2
- * Express HTTP server for axusage metrics.
2
+ * Fastify HTTP server for axusage metrics.
3
3
  *
4
4
  * Designed for both CLI and library usage via factory pattern.
5
5
  */
6
- import { type Express, type Router } from "express";
6
+ import { type FastifyInstance } from "fastify";
7
+ import { type FastifyPluginCallbackZod } from "fastify-type-provider-zod";
7
8
  import type { ServeConfig } from "../config/serve-config.js";
8
- /** Server instance with lifecycle methods */
9
+ /**
10
+ * Server instance with lifecycle methods.
11
+ *
12
+ * Extend the server by passing plugins to {@link createServer}, not by
13
+ * mutating `app` directly — `start()` rebuilds the Fastify instance after
14
+ * a `stop()` call, so any ad-hoc additions would be lost.
15
+ */
9
16
  type AxusageServer = {
10
- /** The Express application instance */
11
- readonly app: Express;
12
- /** Start listening on the configured host:port */
17
+ /** The current Fastify application instance. Rebuilt after `stop()` + `start()`. */
18
+ readonly app: FastifyInstance;
19
+ /** Start listening on the configured host:port. Throws if already running. */
13
20
  start(): Promise<void>;
14
21
  /** Stop the server gracefully */
15
22
  stop(): Promise<void>;
16
23
  };
17
24
  /** Create an axusage server instance */
18
- export declare function createServer(config: Pick<ServeConfig, "port" | "host">, routers: Router[]): AxusageServer;
25
+ export declare function createServer(config: Pick<ServeConfig, "port" | "host" | "logLevel" | "trustProxy">, plugins: FastifyPluginCallbackZod[]): AxusageServer;
19
26
  export {};
20
27
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAgB,EAAE,KAAK,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D,6CAA6C;AAC7C,KAAK,aAAa,GAAG;IACnB,uCAAuC;IACvC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,kDAAkD;IAClD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,iCAAiC;IACjC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB,CAAC;AAsBF,wCAAwC;AACxC,wBAAgB,YAAY,CAC1B,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,EAC1C,OAAO,EAAE,MAAM,EAAE,GAChB,aAAa,CAmEf"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAgB,EAAqB,KAAK,eAAe,EAAE,MAAM,SAAS,CAAC;AAC3E,OAAO,EACL,KAAK,wBAAwB,EAM9B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D;;;;;;GAMG;AACH,KAAK,aAAa,GAAG;IACnB,oFAAoF;IACpF,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC;IAC9B,8EAA8E;IAC9E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,iCAAiC;IACjC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB,CAAC;AAgFF,wCAAwC;AACxC,wBAAgB,YAAY,CAC1B,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,YAAY,CAAC,EACtE,OAAO,EAAE,wBAAwB,EAAE,GAClC,aAAa,CA2Cf"}