{
  "version": 3,
  "sources": ["../../src/ConsoleSystemStatusPublisher.ts", "../../src/HttpSystemStatusPublisher.ts"],
  "sourcesContent": ["import type { Logger } from '@ariestools/sdk'\nimport type { SystemStatusPublisher, SystemStatusRunner } from '@xyo-network/xl1-protocol/protocol-lib'\nimport { SystemStatusPublisherMoniker } from '@xyo-network/xl1-protocol/protocol-lib'\n\n/** Options for the console system-status publisher. */\nexport interface ConsoleSystemStatusPublisherOptions {\n  /** Logger used by this instance and the providers it creates. */\n  logger?: Logger\n}\n\n/**\n * Publishes system status to the console by subscribing to the Runner's global\n * status transitions.\n *\n * It is a plain start/stop observer \u2014 NOT a CreatableProvider \u2014 so it never\n * reports its own lifecycle into the Runner. A self-reporting publisher would\n * report itself `started` before any real service is up, falsely flipping the\n * aggregate status to `started` (and any HTTP `/healthz` derived from it to\n * 200). Being a plain observer keeps the global status reflecting only the\n * actual services.\n */\nexport class ConsoleSystemStatusPublisher implements SystemStatusPublisher {\n  /** Moniker implemented by this provider instance. */\n  readonly moniker = SystemStatusPublisherMoniker\n\n  private _started = false\n  private readonly options: ConsoleSystemStatusPublisherOptions\n  private readonly status: SystemStatusRunner\n\n  /** Creates a console observer for global status transitions. */\n  constructor(status: SystemStatusRunner, options: ConsoleSystemStatusPublisherOptions = {}) {\n    this.status = status\n    this.options = options\n  }\n\n  /** Enables logging and subscribes to subsequent global status transitions. */\n  start(): void {\n    this._started = true\n    const { logger } = this.options\n    this.status.onGlobalTransition({}, (from, to) => {\n      if (!this._started) return\n      logger?.log(`System status: ${from} -> ${to}`)\n      if (to === 'started') logger?.log('All services started.')\n    })\n  }\n\n  /** Disables output from the registered transition callback. */\n  stop(): void {\n    this._started = false\n  }\n}\n", "import type {\n  IncomingMessage, Server, ServerResponse,\n} from 'node:http'\nimport http from 'node:http'\n\nimport type { Logger } from '@ariestools/sdk'\nimport type {\n  SystemStatusPublisher, SystemStatusRunner, SystemStatusViewerMethods,\n} from '@xyo-network/xl1-protocol/protocol-lib'\nimport { SystemStatusPublisherMoniker } from '@xyo-network/xl1-protocol/protocol-lib'\n\n/** Options for the HTTP system-status publisher. */\nexport interface HttpSystemStatusPublisherOptions {\n  /** Optional host or interface on which the probe server listens. */\n  host?: string\n  /** Logger used by this instance and the providers it creates. */\n  logger?: Logger\n  /** TCP port on which the probe server listens. */\n  port: number\n}\n\n/** A resolved probe response (HTTP status code + JSON body). */\nexport interface StatusProbeResponse {\n  /** JSON-serializable probe response body. */\n  body: unknown\n  /** HTTP status code returned for the probe route. */\n  statusCode: number\n}\n\n/**\n * Pure routing for the status probe endpoints. Extracted from the HTTP server\n * so the routing logic is testable without binding a socket.\n */\nexport function resolveStatusProbe(status: SystemStatusViewerMethods, url: string): StatusProbeResponse {\n  switch (url) {\n    case '/healthz': {\n      const global = status.getGlobalStatus()\n      return { statusCode: global === 'started' ? 200 : 500, body: { status: global } }\n    }\n    case '/livez': {\n      return { statusCode: 200, body: { status: 'live' } }\n    }\n    case '/readyz': {\n      if (status.isShuttingDown()) return { statusCode: 503, body: { status: 'shutting-down' } }\n      if (status.isReady()) return { statusCode: 200, body: { status: 'ready' } }\n      return { statusCode: 503, body: { status: 'pending' } }\n    }\n    case '/status': {\n      return { statusCode: 200, body: status.snapshot() }\n    }\n    default: {\n      return { statusCode: 404, body: { status: 'not found' } }\n    }\n  }\n}\n\nconst sendJson = (res: ServerResponse, statusCode: number, body: unknown): void => {\n  res.writeHead(statusCode, { 'Content-Type': 'application/json' })\n  res.end(JSON.stringify(body))\n}\n\n/**\n * Publishes system status over HTTP with Kubernetes-style probes plus a richer\n * `/status` snapshot:\n * - `/healthz` \u2014 200 once the global status is `started`, otherwise 500.\n * - `/livez` \u2014 200 unconditionally while the process is running.\n * - `/readyz` \u2014 503 while shutting down, 200 once `isReady()`, else 503.\n * - `/status` \u2014 200 with the full status snapshot as JSON.\n *\n * It publishes to two sinks: the HTTP endpoints above, and the logger (it logs\n * each global status transition via the Runner's subscription).\n *\n * It is a plain start/stop observer (not a CreatableProvider) so it does not\n * report its own lifecycle into the Runner \u2014 keeping the global status (and\n * thus `/healthz`) reflecting only the actual services.\n */\nexport class HttpSystemStatusPublisher implements SystemStatusPublisher {\n  /** Moniker implemented by this provider instance. */\n  readonly moniker = SystemStatusPublisherMoniker\n\n  private _server: Server | undefined\n  private _started = false\n  private readonly options: HttpSystemStatusPublisherOptions\n  private readonly status: SystemStatusRunner\n\n  /** Creates an HTTP probe publisher for the supplied status runner. */\n  constructor(status: SystemStatusRunner, options: HttpSystemStatusPublisherOptions) {\n    this.status = status\n    this.options = options\n  }\n\n  /** Starts the probe HTTP server and enables transition logging. */\n  async start(): Promise<void> {\n    this._started = true\n    const {\n      host, logger, port,\n    } = this.options\n    // Console sink: log every global status transition via the logger.\n    this.status.onGlobalTransition({}, (from, to) => {\n      if (!this._started) return\n      logger?.log(`System status: ${from} -> ${to}`)\n    })\n    const server = http.createServer((req, res) => this.handleRequest(req, res))\n    await new Promise<void>((resolve, reject) => {\n      server.once('error', reject)\n      server.listen(port, host, () => {\n        logger?.log(`System status HTTP server running on http://${host ?? 'localhost'}:${port}`)\n        logger?.log(' - /healthz')\n        logger?.log(' - /livez')\n        logger?.log(' - /readyz')\n        logger?.log(' - /status')\n        resolve()\n      })\n    })\n    this._server = server\n  }\n\n  /** Disables transition logging and closes the probe HTTP server. */\n  async stop(): Promise<void> {\n    this._started = false\n    const server = this._server\n    this._server = undefined\n    if (server !== undefined) {\n      await new Promise<void>((resolve) => {\n        server.close(() => resolve())\n      })\n    }\n  }\n\n  private handleRequest(req: IncomingMessage, res: ServerResponse): void {\n    const { statusCode, body } = resolveStatusProbe(this.status, req.url ?? '')\n    sendJson(res, statusCode, body)\n  }\n}\n"],
  "mappings": "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",
  "names": ["SystemStatusPublisherMoniker"]
}
