// src/health/causality.ts
var BAGGAGE_KEY_CAUSE = "sylphx.health.cause";
var BAGGAGE_KEY_CHAIN = "sylphx.health.cause_chain";
var DEFAULT_CAUSE_THRESHOLD = 0.5;
var MAX_CHAIN_LENGTH = 5;
var cachedApi;
async function tryLoadCausalityApi() {
  if (cachedApi !== void 0) return cachedApi;
  try {
    const api = await import("@opentelemetry/api");
    if (!api?.context || !api?.propagation) {
      cachedApi = null;
      return cachedApi;
    }
    cachedApi = { context: api.context, propagation: api.propagation };
  } catch {
    cachedApi = null;
  }
  return cachedApi;
}
function defaultServiceName() {
  if (typeof process !== "undefined" && process.env) {
    if (process.env.OTEL_SERVICE_NAME !== void 0) return process.env.OTEL_SERVICE_NAME;
    if (process.env.SERVICE_NAME !== void 0) return process.env.SERVICE_NAME;
  }
  return "unknown";
}
function isSafeIdentifier(part) {
  return part.length > 0 && !/[,@\s]/.test(part);
}
function encodeCauseFromScore(score, threshold, serviceName) {
  const factors = score.signalFactors;
  if (!factors) return null;
  const entries = [];
  for (const [signalName, factor] of Object.entries(factors)) {
    if (typeof factor !== "number" || !Number.isFinite(factor)) continue;
    if (factor >= threshold) continue;
    if (!isSafeIdentifier(signalName) || !isSafeIdentifier(serviceName)) continue;
    entries.push(`${signalName}@${serviceName}`);
  }
  if (entries.length === 0) return null;
  return entries.join(",");
}
function parseCauseValue(value) {
  const out = [];
  for (const raw of value.split(",")) {
    const piece = raw.trim();
    if (piece.length === 0) continue;
    const atIdx = piece.indexOf("@");
    if (atIdx <= 0 || atIdx === piece.length - 1) continue;
    const signal = piece.slice(0, atIdx);
    const service = piece.slice(atIdx + 1);
    if (!isSafeIdentifier(signal) || !isSafeIdentifier(service)) continue;
    out.push({ signal, service });
  }
  return out;
}
function parseChainValue(value) {
  return value.split(",").map((s) => s.trim()).filter((s) => s.length > 0 && isSafeIdentifier(s));
}
function appendChain(prev, serviceName, maxLen) {
  if (!isSafeIdentifier(serviceName)) return prev;
  if (prev[prev.length - 1] === serviceName) return prev;
  const next = [...prev, serviceName];
  if (next.length <= maxLen) return next;
  return next.slice(next.length - maxLen);
}
var CausalityHandleImpl = class {
  _state = "loading";
  apiRef = null;
  threshold;
  serviceName;
  propagateChain;
  constructor(opts) {
    const threshold = opts.threshold ?? DEFAULT_CAUSE_THRESHOLD;
    if (!Number.isFinite(threshold) || threshold <= 0 || threshold > 1) {
      throw new Error(`createCausalityHandle: threshold must be in (0, 1], got ${threshold}`);
    }
    this.threshold = threshold;
    this.serviceName = opts.serviceName ?? defaultServiceName();
    this.propagateChain = opts.propagateChain !== false;
    void this.initialize();
  }
  get state() {
    return this._state;
  }
  async initialize() {
    const api = await tryLoadCausalityApi();
    if (this._state === "disposed") return;
    if (api === null) {
      this._state = "unavailable";
      return;
    }
    this.apiRef = api;
    this._state = "ready";
  }
  readCause() {
    if (this._state !== "ready" || !this.apiRef) return null;
    try {
      const baggage = this.apiRef.propagation.getActiveBaggage();
      if (!baggage) return null;
      const causeEntry = baggage.getEntry(BAGGAGE_KEY_CAUSE);
      if (!causeEntry || typeof causeEntry.value !== "string") return null;
      const causes = parseCauseValue(causeEntry.value);
      if (causes.length === 0) return null;
      const chainEntry = baggage.getEntry(BAGGAGE_KEY_CHAIN);
      const chain = chainEntry && typeof chainEntry.value === "string" ? parseChainValue(chainEntry.value) : [];
      return { causes, chain };
    } catch {
      return null;
    }
  }
  encodeCause(score) {
    try {
      return encodeCauseFromScore(score, this.threshold, this.serviceName);
    } catch {
      return null;
    }
  }
  async runWithCause(fn, score) {
    if (this._state !== "ready" || !this.apiRef) {
      return await fn();
    }
    try {
      const localCause = this.encodeCause(score);
      const existing = this.apiRef.propagation.getActiveBaggage();
      const existingChain = existing?.getEntry(BAGGAGE_KEY_CHAIN)?.value ? parseChainValue(existing.getEntry(BAGGAGE_KEY_CHAIN).value) : [];
      if (!localCause && !existing) {
        return await fn();
      }
      const baseBaggage = existing ?? this.apiRef.propagation.createBaggage();
      let nextBaggage = baseBaggage;
      if (localCause) {
        const newEntry = { value: localCause };
        const upstream = existing?.getEntry(BAGGAGE_KEY_CAUSE)?.value;
        const mergedValue = upstream ? mergeCauseValues(upstream, localCause) : localCause;
        nextBaggage = nextBaggage.setEntry(BAGGAGE_KEY_CAUSE, {
          ...newEntry,
          value: mergedValue
        });
      }
      if (this.propagateChain && (localCause || existingChain.length > 0)) {
        const nextChain = localCause ? appendChain(existingChain, this.serviceName, MAX_CHAIN_LENGTH) : existingChain;
        if (nextChain.length > 0) {
          nextBaggage = nextBaggage.setEntry(BAGGAGE_KEY_CHAIN, {
            value: nextChain.join(",")
          });
        }
      }
      const ctxWithBaggage = this.apiRef.propagation.setBaggage(
        this.apiRef.context.active(),
        nextBaggage
      );
      return await this.apiRef.context.with(ctxWithBaggage, fn);
    } catch {
      return await fn();
    }
  }
  dispose() {
    this._state = "disposed";
    this.apiRef = null;
  }
};
function mergeCauseValues(existing, local) {
  const seen = /* @__PURE__ */ new Set();
  const out = [];
  for (const piece of [...existing.split(","), ...local.split(",")]) {
    const trimmed = piece.trim();
    if (trimmed.length === 0 || seen.has(trimmed)) continue;
    seen.add(trimmed);
    out.push(trimmed);
  }
  return out.join(",");
}
function createCausalityHandle(opts = {}) {
  return new CausalityHandleImpl(opts);
}

// src/health/effects.ts
import { Effect } from "effect";

// src/health/types.ts
var HealthError = class extends Error {
  _tag = "HealthError";
  cause;
  constructor(message, cause) {
    super(message);
    this.name = "HealthError";
    this.cause = cause;
  }
};

// src/health/effects.ts
function evaluateEffect(evaluator) {
  return Effect.tryPromise({
    try: () => evaluator(),
    catch: (err) => new HealthError("health evaluation failed", err)
  });
}

// src/health/handler.ts
var DIRECT_PROBE_LIVENESS_FLOOR = 0.5;
function statusForScore(score) {
  return Number.isFinite(score) && score >= DIRECT_PROBE_LIVENESS_FLOOR ? 200 : 503;
}
function createWebHandler(source) {
  return async (_req) => {
    try {
      const score = await source.evaluate();
      return new Response(JSON.stringify(score), {
        status: statusForScore(score.score),
        headers: {
          "Content-Type": "application/json",
          // Cache-Control: never cache. Even 1-second cache on a
          // CDN edge would mask a fast-moving health collapse.
          "Cache-Control": "no-store, no-cache, must-revalidate"
        }
      });
    } catch (err) {
      const message = err instanceof Error ? err.message : String(err);
      return new Response(
        JSON.stringify({
          error: "health_evaluator_failed",
          message
        }),
        {
          status: 500,
          headers: {
            "Content-Type": "application/json",
            "Cache-Control": "no-store, no-cache, must-revalidate"
          }
        }
      );
    }
  };
}
function createNodeHandler(source) {
  return async (_req, res) => {
    try {
      const score = await source.evaluate();
      res.statusCode = statusForScore(score.score);
      res.setHeader("Content-Type", "application/json");
      res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
      res.end(JSON.stringify(score));
    } catch (err) {
      const message = err instanceof Error ? err.message : String(err);
      res.statusCode = 500;
      res.setHeader("Content-Type", "application/json");
      res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
      res.end(JSON.stringify({ error: "health_evaluator_failed", message }));
    }
  };
}

// src/health/history.ts
var GENESIS_PREV_HASH = "0".repeat(64);
var DEFAULT_HISTORY_CAPACITY = 1e3;
var HASH_TEXT_ENCODER = new TextEncoder();
var HEX_BYTE = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
function canonicalizeSignals(signals) {
  const keys = Object.keys(signals).sort();
  const pairs = keys.map((k) => {
    const value = signals[k];
    return [k, value];
  });
  return JSON.stringify(Object.fromEntries(pairs));
}
function encodeScore(score) {
  return score.toFixed(6);
}
async function sha256Hex(input) {
  const subtle = globalThis.crypto?.subtle;
  if (!subtle || typeof subtle.digest !== "function") {
    return "";
  }
  const buf = HASH_TEXT_ENCODER.encode(input);
  const digest = await subtle.digest("SHA-256", buf);
  const bytes = new Uint8Array(digest);
  let out = "";
  for (let i = 0; i < bytes.length; i++) {
    out += HEX_BYTE[bytes[i]];
  }
  return out;
}
async function computeEntryHash(prehash) {
  const canonical = [
    prehash.sequence.toString(10),
    prehash.evaluatedAt,
    encodeScore(prehash.score),
    prehash.signalsDigest,
    prehash.prevHash
  ].join("|");
  return await sha256Hex(canonical);
}
var InMemoryHistoryStoreImpl = class {
  capacity;
  buf = [];
  constructor(opts = {}) {
    const cap = opts.capacity ?? DEFAULT_HISTORY_CAPACITY;
    if (!Number.isFinite(cap) || cap <= 0 || cap > 1e7) {
      throw new Error(`InMemoryHistoryStore: capacity must be in (0, 10000000], got ${cap}`);
    }
    this.capacity = cap;
  }
  append(entry) {
    this.buf.push(entry);
    if (this.buf.length > this.capacity) {
      this.buf.shift();
    }
  }
  range(query) {
    const out = [];
    for (const e of this.buf) {
      if (query.fromSequence !== void 0 && e.sequence < query.fromSequence) continue;
      if (query.toSequence !== void 0 && e.sequence > query.toSequence) continue;
      if (query.fromTimestamp !== void 0 && e.evaluatedAt < query.fromTimestamp) continue;
      if (query.toTimestamp !== void 0 && e.evaluatedAt > query.toTimestamp) continue;
      out.push(e);
      if (query.limit !== void 0 && out.length >= query.limit) break;
    }
    return out;
  }
  latest() {
    return this.buf[this.buf.length - 1] ?? null;
  }
};
function createInMemoryHistoryStore(opts = {}) {
  return new InMemoryHistoryStoreImpl(opts);
}
var HistoryRecorderImpl = class {
  store;
  nextSequence = 0;
  prevHash = GENESIS_PREV_HASH;
  disposed = false;
  initPromise;
  /**
   * Serializes record() calls so concurrent `evaluate()` invocations
   * don't race on `nextSequence` / `prevHash`. Each new call chains off
   * the previous; the chain itself never propagates errors (catch in the
   * tail so one failed record doesn't poison subsequent ones).
   */
  inFlight = Promise.resolve();
  constructor(opts) {
    this.store = opts.store ?? createInMemoryHistoryStore({ capacity: opts.capacity });
    this.initPromise = this.warmStart();
  }
  async warmStart() {
    try {
      const latest = await this.store.latest();
      if (latest && Number.isFinite(latest.sequence)) {
        this.nextSequence = latest.sequence + 1;
        this.prevHash = latest.entryHash || GENESIS_PREV_HASH;
      }
    } catch {
    }
  }
  async recordInternal(score) {
    if (this.disposed) return null;
    await this.initPromise;
    try {
      const signalsDigest = await sha256Hex(canonicalizeSignals(score.signals));
      const prehash = {
        sequence: this.nextSequence,
        evaluatedAt: score.lastTickAt,
        score: score.score,
        signalsDigest,
        prevHash: this.prevHash
      };
      const entryHash = await computeEntryHash(prehash);
      const entry = { ...prehash, entryHash };
      await this.store.append(entry);
      this.nextSequence += 1;
      this.prevHash = entryHash || GENESIS_PREV_HASH;
      return entry;
    } catch {
      return null;
    }
  }
  async record(score) {
    const job = this.inFlight.then(() => this.recordInternal(score));
    this.inFlight = job.catch(() => void 0);
    return await job;
  }
  async drainPendingRecords() {
    try {
      await this.inFlight;
    } catch {
    }
  }
  async range(query) {
    await this.drainPendingRecords();
    try {
      return await this.store.range(query);
    } catch {
      return [];
    }
  }
  async latest() {
    await this.drainPendingRecords();
    try {
      return await this.store.latest();
    } catch {
      return null;
    }
  }
  async verify(query) {
    const entries = await this.range(query);
    return await verifyChain(entries);
  }
  dispose() {
    this.disposed = true;
  }
};
function createHistoryRecorder(opts = {}) {
  return new HistoryRecorderImpl(opts);
}
async function verifyChain(entries) {
  if (entries.length === 0) {
    return { verified: true, entriesChecked: 0, firstBreakAtSequence: null, reason: null };
  }
  let expectedPrev = null;
  let checked = 0;
  for (let i = 0; i < entries.length; i++) {
    const entry = entries[i];
    if (i === 0 && entry.sequence === 0 && entry.prevHash !== GENESIS_PREV_HASH) {
      return {
        verified: false,
        entriesChecked: checked,
        firstBreakAtSequence: entry.sequence,
        reason: `genesis entry must have prevHash="${GENESIS_PREV_HASH}", got ${entry.prevHash}`
      };
    }
    if (i > 0 && expectedPrev !== null && entry.prevHash !== expectedPrev) {
      return {
        verified: false,
        entriesChecked: checked,
        firstBreakAtSequence: entry.sequence,
        reason: `prevHash mismatch at sequence ${entry.sequence}: expected ${expectedPrev}, got ${entry.prevHash}`
      };
    }
    const recomputedHash = await computeEntryHash({
      sequence: entry.sequence,
      evaluatedAt: entry.evaluatedAt,
      score: entry.score,
      signalsDigest: entry.signalsDigest,
      prevHash: entry.prevHash
    });
    if (entry.entryHash && recomputedHash && recomputedHash !== entry.entryHash) {
      return {
        verified: false,
        entriesChecked: checked,
        firstBreakAtSequence: entry.sequence,
        reason: `entryHash mismatch at sequence ${entry.sequence}: stored=${entry.entryHash} recomputed=${recomputedHash}`
      };
    }
    expectedPrev = entry.entryHash || null;
    checked += 1;
  }
  return {
    verified: true,
    entriesChecked: checked,
    firstBreakAtSequence: null,
    reason: null
  };
}

// src/health/otel.ts
var SDK_NAME = "@sylphx/sdk/health";
var SDK_VERSION = "0.11.0";
var METRIC_NAMES = {
  score: "health.score",
  signalFactor: "health.signal.factor",
  signalValue: "health.signal.value",
  signalValueState: "health.signal.value_state",
  /** ADR-143 §3.3 — increments per evaluation when an upstream cause is observed. */
  downstreamCause: "health.downstream_cause"
};
var SPAN_ATTRIBUTES = {
  score: "service.health.score",
  signalFactor: (name) => `service.health.signal.${name}.factor`,
  /** ADR-143 §3.2 — the upstream service that originated the failure chain. */
  causeRoot: "service.health.cause_root",
  /** ADR-143 §3.2 — the signal at the root that failed. */
  causeSignal: "service.health.cause_signal",
  /** ADR-143 §3.2 — comma-delimited hop chain (oldest to newest). */
  causeChain: "service.health.cause_chain"
};
var EVENT_NAMES = {
  evaluated: "health.evaluated"
};
function createOtelEmitter(opts = {}) {
  return new OtelEmitterImpl(opts);
}
var OtelEmitterImpl = class {
  constructor(opts) {
    this.opts = opts;
    this.staticAttrs = opts.attributes ?? {};
    this.events = opts.events === true;
    if (opts.meter) {
      this.instruments = buildInstruments(opts.meter);
      this._state = "ready";
      return;
    }
    void this.initialize();
  }
  _state = "loading";
  instruments = null;
  traceApi = null;
  staticAttrs;
  events;
  get state() {
    return this._state;
  }
  async initialize() {
    const api = await tryLoadOtelApi();
    if (api === null) {
      this._state = "unavailable";
      return;
    }
    if (this._state === "disposed") return;
    try {
      const meter = api.metrics.getMeter(SDK_NAME, SDK_VERSION);
      this.instruments = buildInstruments(meter);
      this.traceApi = api.trace;
      this._state = "ready";
    } catch {
      this._state = "unavailable";
    }
  }
  emit(score, upstreamCause) {
    if (this._state !== "ready" || !this.instruments) return;
    const baseAttrs = { ...this.staticAttrs };
    try {
      this.instruments.scoreGauge.record(score.score, baseAttrs);
      for (const [signalName, rawValue] of Object.entries(score.signals)) {
        const signalAttrs = { ...baseAttrs, "signal.name": signalName };
        const factor = score.signalFactors?.[signalName];
        if (typeof factor === "number" && Number.isFinite(factor)) {
          this.instruments.factorGauge.record(factor, signalAttrs);
        }
        if (typeof rawValue === "number" && Number.isFinite(rawValue)) {
          this.instruments.valueGauge.record(rawValue, signalAttrs);
        } else {
          const stateAttrs = {
            ...signalAttrs,
            state: String(rawValue)
          };
          this.instruments.valueStateCounter.add(1, stateAttrs);
        }
      }
      if (upstreamCause && upstreamCause.causes.length > 0) {
        for (const cause of upstreamCause.causes) {
          this.instruments.downstreamCauseCounter.add(1, {
            ...baseAttrs,
            cause_root: cause.service,
            cause_signal: cause.signal
          });
        }
      }
      if (this.traceApi) {
        const span = this.traceApi.getActiveSpan?.();
        maybeAttachToSpan(span, score, this.events, upstreamCause);
      }
    } catch {
    }
  }
  dispose() {
    this._state = "disposed";
    this.instruments = null;
    this.traceApi = null;
  }
};
var cachedApi2;
async function tryLoadOtelApi() {
  if (cachedApi2 !== void 0) return cachedApi2;
  try {
    cachedApi2 = await import("@opentelemetry/api");
  } catch {
    cachedApi2 = null;
  }
  return cachedApi2;
}
function buildInstruments(meter) {
  return {
    scoreGauge: meter.createGauge(METRIC_NAMES.score, {
      description: "Multi-signal health score per ADR-141 \xA74. Range [0, 1]. Higher = healthier.",
      unit: "1"
    }),
    factorGauge: meter.createGauge(METRIC_NAMES.signalFactor, {
      description: "Per-signal health factor; folded into health.score by the SDK scoring strategy (default: weightedProduct, ADR-111 \xA74.3).",
      unit: "1"
    }),
    valueGauge: meter.createGauge(METRIC_NAMES.signalValue, {
      description: "Raw observation behind a health signal (ms, count, rate). Numeric values only; string-valued signals emit health.signal.value_state instead."
    }),
    valueStateCounter: meter.createCounter(METRIC_NAMES.signalValueState, {
      description: 'Per-evaluation counter for non-numeric signal readings (e.g. "ok" / "timeout"). state attribute carries the string label.'
    }),
    downstreamCauseCounter: meter.createCounter(METRIC_NAMES.downstreamCause, {
      description: "Per-evaluation counter incremented when this service observes an upstream cause via OTel baggage (ADR-143). Operators read rate() to size the blast radius of an incident."
    })
  };
}
function maybeAttachToSpan(span, score, events, upstreamCause) {
  if (!span || typeof span !== "object") return;
  const setAttribute = span.setAttribute;
  if (typeof setAttribute === "function") {
    setAttribute.call(span, SPAN_ATTRIBUTES.score, score.score);
    for (const [signalName, factor] of Object.entries(score.signalFactors ?? {})) {
      if (typeof factor === "number" && Number.isFinite(factor)) {
        setAttribute.call(span, SPAN_ATTRIBUTES.signalFactor(signalName), factor);
      }
    }
    if (upstreamCause && upstreamCause.causes.length > 0) {
      const primary = upstreamCause.causes[0];
      if (primary) {
        setAttribute.call(span, SPAN_ATTRIBUTES.causeRoot, primary.service);
        setAttribute.call(span, SPAN_ATTRIBUTES.causeSignal, primary.signal);
      }
      if (upstreamCause.chain.length > 0) {
        setAttribute.call(span, SPAN_ATTRIBUTES.causeChain, upstreamCause.chain.join(","));
      }
    }
  }
  if (events) {
    const addEvent = span.addEvent;
    if (typeof addEvent === "function") {
      addEvent.call(span, EVENT_NAMES.evaluated, {
        [SPAN_ATTRIBUTES.score]: score.score,
        "service.health.lastTickAt": score.lastTickAt
      });
    }
  }
}

// src/health/scoring.ts
function clamp01(x) {
  if (!Number.isFinite(x)) return 0;
  if (x < 0) return 0;
  if (x > 1) return 1;
  return x;
}
var weightedProduct = (readings) => {
  if (readings.length === 0) return 1;
  const active = [];
  for (const { signal, reading } of readings) {
    const w = signal.weight;
    if (!Number.isFinite(w) || w <= 0) continue;
    if (reading.unknown === true) continue;
    active.push({
      factor: clamp01(reading.healthFactor),
      weight: w
    });
  }
  if (active.length === 0) return 1;
  const totalWeight = active.reduce((sum, a) => sum + a.weight, 0);
  if (totalWeight <= 0 || !Number.isFinite(totalWeight)) return 1;
  let logSum = 0;
  for (const { factor, weight } of active) {
    const normalisedWeight = weight / totalWeight;
    if (factor <= 0) {
      return 0;
    }
    logSum += normalisedWeight * Math.log(factor);
  }
  const score = Math.exp(logSum);
  return clamp01(score);
};
function defaultScoringStrategy() {
  return weightedProduct;
}

// src/health/signals/event-loop-lag.ts
import { monitorEventLoopDelay } from "perf_hooks";
function eventLoopLagSignal(opts = {}) {
  const degradedMs = opts.degradedMs ?? 5e3;
  const deadMs = opts.deadMs ?? 3e4;
  const resolutionMs = opts.resolutionMs ?? 10;
  const weight = opts.weight ?? 0.4;
  if (!Number.isFinite(degradedMs) || degradedMs < 0) {
    throw new Error(`eventLoopLagSignal: degradedMs must be >= 0, got ${degradedMs}`);
  }
  if (!Number.isFinite(deadMs) || deadMs <= degradedMs) {
    throw new Error(
      `eventLoopLagSignal: deadMs must be > degradedMs (${degradedMs}), got ${deadMs}`
    );
  }
  if (!Number.isFinite(resolutionMs) || resolutionMs < 1) {
    throw new Error(`eventLoopLagSignal: resolutionMs must be >= 1, got ${resolutionMs}`);
  }
  const monitor = opts.monitor ?? monitorEventLoopDelay({ resolution: resolutionMs });
  monitor.enable();
  return {
    name: "eventLoopLagMs",
    weight,
    read() {
      const maxNs = monitor.max;
      if (!Number.isFinite(maxNs) || maxNs <= 0 || maxNs >= Number.MAX_SAFE_INTEGER) {
        monitor.reset();
        return { value: 0, healthFactor: 1, unknown: true };
      }
      const observedMs = maxNs / 1e6;
      monitor.reset();
      let factor;
      if (observedMs <= degradedMs) factor = 1;
      else if (observedMs >= deadMs) factor = 0;
      else {
        const span = deadMs - degradedMs;
        factor = 1 - (observedMs - degradedMs) / span;
      }
      return {
        value: Math.round(observedMs),
        healthFactor: factor
      };
    },
    dispose() {
      monitor.disable();
    }
  };
}

// src/health/unix-socket-server.ts
import { unlinkSync } from "fs";
function startUnixSocketServer(source, opts = {}) {
  const bun = opts.bunRuntime !== void 0 ? opts.bunRuntime : globalThis.Bun;
  if (bun === null || bun === void 0 || typeof bun.serve !== "function") {
    throw new Error(
      "startUnixSocketServer: Bun.serve is unavailable \u2014 Unix-socket transport requires a Bun runtime"
    );
  }
  const path = opts.path ?? "/var/run/sylphx/health.sock";
  const unlinkOnShutdown = opts.unlinkOnShutdown ?? true;
  try {
    unlinkSync(path);
  } catch {
  }
  const handler = createWebHandler(source);
  const server = bun.serve({
    unix: path,
    fetch: async (req) => handler(req)
  });
  const handle = {
    path,
    server,
    async shutdown() {
      try {
        server.stop(true);
      } catch {
      }
      if (unlinkOnShutdown) {
        try {
          unlinkSync(path);
        } catch {
        }
      }
    }
  };
  return handle;
}

// src/health/middleware.ts
var DEFAULT_PATH = "/healthz";
var DEFAULT_SOCKET_PATH = "/var/run/sylphx/health.sock";
var AUTO_SOCKET_HANDLES = /* @__PURE__ */ new WeakMap();
function resolveInstance(opts) {
  if (opts.instance) return opts.instance;
  const { path: _path, instance: _instance, unixSocket: _unixSocket, ...sylphxOpts } = opts;
  return sylphxHealth(sylphxOpts);
}
function normalizePath(input) {
  if (!input) return DEFAULT_PATH;
  return input.startsWith("/") ? input : `/${input}`;
}
function getEnvSocketPath() {
  const proc = globalThis.process;
  const value = proc?.env?.SYLPHX_HEALTH_APP_SOCKET;
  return value && value.trim().length > 0 ? value.trim() : void 0;
}
function resolveUnixSocketPath(inputPath, envPath) {
  if (inputPath !== void 0) return inputPath;
  if (envPath !== void 0) return envPath;
  return DEFAULT_SOCKET_PATH;
}
function resolveUnixSocketOption(input) {
  if (input === false) return null;
  const envPath = getEnvSocketPath();
  if (input === void 0) {
    return envPath ? { path: envPath, required: false } : null;
  }
  if (input === true) {
    return { path: envPath ?? DEFAULT_SOCKET_PATH, required: false };
  }
  return {
    path: resolveUnixSocketPath(input.path, envPath),
    required: input.required ?? false,
    ...input.unlinkOnShutdown !== void 0 ? { unlinkOnShutdown: input.unlinkOnShutdown } : {}
  };
}
function bindUnixSocketIfRequested(health, opts) {
  const socket = resolveUnixSocketOption(opts.unixSocket);
  if (!socket) return () => void 0;
  const existing = AUTO_SOCKET_HANDLES.get(health);
  if (existing) return () => void 0;
  try {
    const handle = health.serveUnixSocket({
      path: socket.path,
      ...socket.unlinkOnShutdown !== void 0 ? { unlinkOnShutdown: socket.unlinkOnShutdown } : {}
    });
    AUTO_SOCKET_HANDLES.set(health, handle);
    return () => {
      if (AUTO_SOCKET_HANDLES.get(health) === handle) {
        AUTO_SOCKET_HANDLES.delete(health);
      }
      void handle.shutdown();
    };
  } catch (err) {
    if (socket.required) throw err;
    return () => void 0;
  }
}
function honoHelper(opts = {}) {
  const path = normalizePath(opts.path);
  const health = resolveInstance(opts);
  const disposeSocket = bindUnixSocketIfRequested(health, opts);
  const respond = health.handler();
  const middleware = (async (c, next) => {
    if (c.req.path === path) return respond();
    await health.runWithCause(() => next());
    return void 0;
  });
  Object.defineProperty(middleware, "health", { value: health, enumerable: true });
  Object.defineProperty(middleware, "dispose", {
    value: () => {
      disposeSocket();
      health.dispose();
    },
    enumerable: false
  });
  return middleware;
}
function pathOf(url) {
  if (!url) return "/";
  const q = url.indexOf("?");
  return q === -1 ? url : url.slice(0, q);
}
function expressHelper(opts = {}) {
  const path = normalizePath(opts.path);
  const health = resolveInstance(opts);
  const disposeSocket = bindUnixSocketIfRequested(health, opts);
  const respond = health.nodeHandler();
  const middleware = ((req, res, next) => {
    if (pathOf(req.url) === path) {
      void respond(req, res);
      return;
    }
    void health.runWithCause(async () => next());
  });
  Object.defineProperty(middleware, "health", { value: health, enumerable: true });
  Object.defineProperty(middleware, "dispose", {
    value: () => {
      disposeSocket();
      health.dispose();
    },
    enumerable: false
  });
  return middleware;
}
function fastifyHelper(opts = {}) {
  const path = normalizePath(opts.path);
  const health = resolveInstance(opts);
  const disposeSocket = bindUnixSocketIfRequested(health, opts);
  const respond = health.handler();
  const plugin = (async (instance) => {
    instance.get(path, async (_req, reply) => {
      const response = await respond();
      const body = await response.text();
      reply.code(response.status);
      reply.header("Content-Type", "application/json");
      reply.header("Cache-Control", "no-store, no-cache, must-revalidate");
      reply.send(body);
    });
  });
  Object.defineProperty(plugin, "health", { value: health, enumerable: true });
  Object.defineProperty(plugin, "dispose", {
    value: () => {
      disposeSocket();
      health.dispose();
    },
    enumerable: false
  });
  return plugin;
}
function fetchHelper(opts = {}) {
  const health = resolveInstance(opts);
  const disposeSocket = bindUnixSocketIfRequested(health, opts);
  const respond = health.handler();
  const handler = ((req) => respond(req));
  Object.defineProperty(handler, "health", { value: health, enumerable: true });
  Object.defineProperty(handler, "dispose", {
    value: () => {
      disposeSocket();
      health.dispose();
    },
    enumerable: false
  });
  return handler;
}
var withHealth = {
  /** Hono middleware. Mount with `app.use('*', withHealth.hono())`. */
  hono: honoHelper,
  /** Express / Connect / Polka middleware. Mount with `app.use(withHealth.express())`. */
  express: expressHelper,
  /** Fastify plugin. Register with `await fastify.register(withHealth.fastify())`. */
  fastify: fastifyHelper,
  /**
   * Pure Web Fetch handler. Works with Bun.serve, Next.js route
   * handlers, Deno, Cloudflare Workers, and any other `Request →
   * Response` runtime.
   */
  fetch: fetchHelper
};

// src/health/signals/error-rate.ts
function parseWindow(w) {
  if (typeof w === "number") {
    if (!Number.isFinite(w) || w <= 0) {
      throw new Error(`errorRateSignal: window must be > 0, got ${w}`);
    }
    return w;
  }
  const m = /^(\d+)(s|m)$/.exec(w);
  if (m === null) {
    throw new Error(`errorRateSignal: invalid window '${w}', expected '5s' / '1m' / number`);
  }
  const n = Number.parseInt(m[1], 10);
  const unit = m[2];
  const ms = unit === "s" ? n * 1e3 : n * 6e4;
  if (ms <= 0) {
    throw new Error(`errorRateSignal: parsed window ${ms}ms must be > 0`);
  }
  return ms;
}
function errorRateSignal(opts) {
  const windowMs = parseWindow(opts.window);
  const degradedRate = opts.degradedRate ?? 0.05;
  const deadRate = opts.deadRate ?? 0.5;
  const minSamples = opts.minSamples ?? 10;
  const weight = opts.weight ?? 0.2;
  const now = opts.now ?? Date.now;
  if (degradedRate < 0 || degradedRate > 1) {
    throw new Error(`errorRateSignal: degradedRate must be in [0, 1], got ${degradedRate}`);
  }
  if (deadRate <= degradedRate || deadRate > 1) {
    throw new Error(`errorRateSignal: deadRate must be in (degradedRate, 1], got ${deadRate}`);
  }
  if (!Number.isFinite(minSamples) || minSamples < 1) {
    throw new Error(`errorRateSignal: minSamples must be >= 1, got ${minSamples}`);
  }
  const samples = [];
  function pruneExpired(t) {
    const cutoff = t - windowMs;
    while (samples.length > 0 && samples[0].t < cutoff) {
      samples.shift();
    }
  }
  return {
    name: `recent${Math.round(windowMs / 1e3)}sErrorRate`,
    weight,
    recordSuccess() {
      samples.push({ t: now(), e: false });
    },
    recordError() {
      samples.push({ t: now(), e: true });
    },
    reset() {
      samples.length = 0;
    },
    read() {
      const t = now();
      pruneExpired(t);
      if (samples.length === 0 || samples.length < minSamples) {
        return { value: 0, healthFactor: 1 };
      }
      let errors = 0;
      for (const s of samples) {
        if (s.e) errors++;
      }
      const rate = errors / samples.length;
      let factor;
      if (rate <= degradedRate) factor = 1;
      else if (rate >= deadRate) factor = 0;
      else {
        const span = deadRate - degradedRate;
        factor = 1 - (rate - degradedRate) / span;
      }
      const valueRounded = Math.round(rate * 1e4) / 1e4;
      return {
        value: valueRounded,
        healthFactor: factor
      };
    }
  };
}

// src/health/signals/memory-pressure.ts
import { readFileSync } from "fs";
var CGROUP_V1_UNLIMITED_FLOOR = 1e18;
function tryRead(reader, path) {
  try {
    return reader(path);
  } catch {
    return null;
  }
}
function readContainerMemoryLimit(reader, v2Path, v1Path) {
  const v2 = tryRead(reader, v2Path);
  if (v2 !== null) {
    const trimmed2 = v2.trim();
    if (trimmed2 === "max" || trimmed2 === "") return null;
    const n2 = Number.parseInt(trimmed2, 10);
    if (!Number.isFinite(n2) || n2 <= 0) return null;
    if (n2 > CGROUP_V1_UNLIMITED_FLOOR) return null;
    return n2;
  }
  const v1 = tryRead(reader, v1Path);
  if (v1 === null) return null;
  const trimmed = v1.trim();
  const n = Number.parseInt(trimmed, 10);
  if (!Number.isFinite(n) || n <= 0) return null;
  if (n > CGROUP_V1_UNLIMITED_FLOOR) return null;
  return n;
}
function memoryPressureSignal(opts = {}) {
  const degradedRatio = opts.degradedRatio ?? 0.85;
  const deadRatio = opts.deadRatio ?? 0.95;
  const v2Path = opts.cgroupV2Path ?? "/sys/fs/cgroup/memory.max";
  const v1Path = opts.cgroupV1Path ?? "/sys/fs/cgroup/memory/memory.limit_in_bytes";
  const weight = opts.weight ?? 0.2;
  const memoryUsage = opts.memoryUsage ?? (() => {
    const m = process.memoryUsage();
    return { rss: m.rss };
  });
  const readFile = opts.readFile ?? ((p) => readFileSync(p, "utf8"));
  if (degradedRatio <= 0 || degradedRatio >= 1) {
    throw new Error(`memoryPressureSignal: degradedRatio must be in (0, 1), got ${degradedRatio}`);
  }
  if (deadRatio <= degradedRatio || deadRatio > 1) {
    throw new Error(
      `memoryPressureSignal: deadRatio must be in (degradedRatio, 1], got ${deadRatio}`
    );
  }
  let cachedLimit;
  function getLimit() {
    if (cachedLimit === void 0) {
      cachedLimit = readContainerMemoryLimit(readFile, v2Path, v1Path);
    }
    return cachedLimit;
  }
  return {
    name: "memoryPressure",
    weight,
    read() {
      const limit = getLimit();
      if (limit === null) {
        return { value: 0, healthFactor: 1, unknown: true };
      }
      let rss;
      try {
        rss = memoryUsage().rss;
      } catch {
        return { value: 0, healthFactor: 1, unknown: true };
      }
      if (!Number.isFinite(rss) || rss < 0) {
        return { value: 0, healthFactor: 1, unknown: true };
      }
      const ratio = rss / limit;
      let factor;
      if (ratio <= degradedRatio) factor = 1;
      else if (ratio >= deadRatio) factor = 0;
      else {
        const span = deadRatio - degradedRatio;
        factor = 1 - (ratio - degradedRatio) / span;
      }
      const valueRounded = Math.round(ratio * 1e3) / 1e3;
      return { value: valueRounded, healthFactor: factor };
    }
  };
}

// src/health/signals/ping.ts
var DEFAULT_TIMEOUT_MS = 500;
function pingSignal(opts) {
  if (typeof opts.ping !== "function") {
    throw new Error(`pingSignal[${opts.name}]: ping must be a function`);
  }
  if (typeof opts.name !== "string" || opts.name.length === 0) {
    throw new Error("pingSignal: name must be a non-empty string");
  }
  const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
    throw new Error(`pingSignal[${opts.name}]: timeoutMs must be > 0, got ${timeoutMs}`);
  }
  const weight = opts.weight ?? 1;
  return {
    name: opts.name,
    weight,
    async read() {
      let timer;
      const timeoutPromise = new Promise((resolve) => {
        timer = setTimeout(() => resolve("timeout"), timeoutMs);
      });
      try {
        const result = await Promise.race([opts.ping().then(() => "ok"), timeoutPromise]);
        if (result === "timeout") {
          return { value: "timeout", healthFactor: 0 };
        }
        return { value: "ok", healthFactor: 1 };
      } catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        return { value: message.slice(0, 120), healthFactor: 0 };
      } finally {
        if (timer) clearTimeout(timer);
      }
    }
  };
}
function databaseSignal(opts) {
  return pingSignal({
    name: opts.name ?? "database",
    ping: opts.ping,
    ...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
    ...opts.weight !== void 0 ? { weight: opts.weight } : {}
  });
}
function redisSignal(opts) {
  return pingSignal({
    name: opts.name ?? "redis",
    ping: opts.ping,
    ...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
    ...opts.weight !== void 0 ? { weight: opts.weight } : {}
  });
}

// src/health/signals/queue-depth.ts
function queueDepthSignal(opts) {
  if (typeof opts.getter !== "function") {
    throw new Error("queueDepthSignal: getter must be a function");
  }
  if (!Number.isFinite(opts.fullThreshold) || opts.fullThreshold <= 0) {
    throw new Error(`queueDepthSignal: fullThreshold must be > 0, got ${opts.fullThreshold}`);
  }
  const healthyBelow = opts.healthyBelow ?? 0;
  if (!Number.isFinite(healthyBelow) || healthyBelow < 0 || healthyBelow >= opts.fullThreshold) {
    throw new Error(
      `queueDepthSignal: healthyBelow must be in [0, fullThreshold), got ${healthyBelow}`
    );
  }
  const weight = opts.weight ?? 0.2;
  return {
    name: opts.name ?? "queueDepth",
    weight,
    async read() {
      let raw;
      try {
        raw = await opts.getter();
      } catch {
        return { value: 0, healthFactor: 1, unknown: true };
      }
      if (typeof raw !== "number" || !Number.isFinite(raw) || raw < 0) {
        return { value: 0, healthFactor: 1, unknown: true };
      }
      const depth = raw;
      let factor;
      if (depth <= healthyBelow) factor = 1;
      else if (depth >= opts.fullThreshold) factor = 0;
      else {
        const span = opts.fullThreshold - healthyBelow;
        factor = 1 - (depth - healthyBelow) / span;
      }
      return {
        value: depth,
        healthFactor: factor
      };
    }
  };
}

// src/health/index.ts
function resolveOtelEmitter(otelOpt) {
  if (otelOpt === false) return null;
  const emitterOpts = otelOpt && typeof otelOpt === "object" ? otelOpt : {};
  return createOtelEmitter(emitterOpts);
}
function resolveCausalityHandle(opt) {
  if (opt === false) return null;
  const causalityOpts = opt && typeof opt === "object" ? opt : {};
  return createCausalityHandle(causalityOpts);
}
function resolveHistoryRecorder(opt) {
  if (opt === false) return null;
  const recorderOpts = opt && typeof opt === "object" ? opt : {};
  return createHistoryRecorder(recorderOpts);
}
function clampHealthFactor(value) {
  if (!Number.isFinite(value)) return 0;
  if (value < 0) return 0;
  if (value > 1) return 1;
  return value;
}
var HEALTH_RELEASE_KEYS = [
  "version",
  "commitSha",
  "imageDigest",
  "imageTag",
  "deploymentId",
  "buildId",
  "replicaId"
];
function resolveHealthRelease(input) {
  if (!input) return void 0;
  try {
    const value = typeof input === "function" ? input() : input;
    if (!value) return void 0;
    const release = {};
    for (const key of HEALTH_RELEASE_KEYS) {
      const raw = value[key];
      if (raw === null) {
        release[key] = null;
      } else if (typeof raw === "string") {
        const trimmed = raw.trim();
        if (trimmed.length > 0) release[key] = trimmed;
      }
    }
    return Object.keys(release).length > 0 ? release : void 0;
  } catch {
    return void 0;
  }
}
function sylphxHealth(opts = {}) {
  const signals = opts.signals && opts.signals.length > 0 ? [...opts.signals] : [eventLoopLagSignal({ degradedMs: 5e3, deadMs: 3e4, weight: 1 })];
  const scoringStrategy = opts.scoringStrategy ?? defaultScoringStrategy();
  const now = opts.now ?? (() => /* @__PURE__ */ new Date());
  const otelEmitter = resolveOtelEmitter(opts.otel);
  const causality = resolveCausalityHandle(opts.causality);
  const historyRecorder = resolveHistoryRecorder(opts.history);
  let disposed = false;
  let lastScore = null;
  const evaluate = async () => {
    if (disposed) {
      throw new Error("sylphxHealth: instance disposed");
    }
    const upstreamCause = causality ? causality.readCause() : null;
    const readings = await Promise.all(
      signals.map(async (signal) => ({
        signal,
        reading: await signal.read()
      }))
    );
    const score = scoringStrategy(readings);
    const signalsMap = {};
    const signalFactors = {};
    for (const { signal, reading } of readings) {
      signalsMap[signal.name] = reading.value;
      signalFactors[signal.name] = reading.unknown === true ? 1 : clampHealthFactor(reading.healthFactor);
    }
    const release = resolveHealthRelease(opts.release);
    const result = {
      score,
      signals: signalsMap,
      signalFactors,
      ...release ? { release } : {},
      lastTickAt: now().toISOString()
    };
    lastScore = result;
    if (otelEmitter) {
      otelEmitter.emit(result, upstreamCause);
    }
    if (historyRecorder) {
      void historyRecorder.record(result);
    }
    return result;
  };
  const evaluator = { evaluate };
  const evaluateEffect2 = evaluateEffect(evaluate);
  const runWithCause = async (fn) => {
    if (!causality || !lastScore) return await fn();
    return await causality.runWithCause(fn, lastScore);
  };
  const getHistory = async (query = {}) => {
    if (!historyRecorder) return [];
    return await historyRecorder.range(query);
  };
  const verifyHistory = async (query = {}) => {
    if (!historyRecorder) {
      return {
        verified: false,
        entriesChecked: 0,
        firstBreakAtSequence: null,
        reason: "history recording is disabled on this instance"
      };
    }
    return await historyRecorder.verify(query);
  };
  return {
    signals,
    evaluate,
    evaluateEffect: evaluateEffect2,
    handler() {
      return createWebHandler(evaluator);
    },
    nodeHandler() {
      return createNodeHandler(evaluator);
    },
    serveUnixSocket(unixOpts) {
      return startUnixSocketServer(evaluator, unixOpts);
    },
    getLastScore() {
      return lastScore;
    },
    runWithCause,
    getHistory,
    verifyHistory,
    dispose() {
      if (disposed) return;
      disposed = true;
      if (otelEmitter) otelEmitter.dispose();
      if (causality) causality.dispose();
      if (historyRecorder) historyRecorder.dispose();
      for (const s of signals) {
        try {
          s.dispose?.();
        } catch {
        }
      }
    }
  };
}
export {
  BAGGAGE_KEY_CAUSE,
  BAGGAGE_KEY_CHAIN,
  DEFAULT_CAUSE_THRESHOLD,
  DEFAULT_HISTORY_CAPACITY,
  GENESIS_PREV_HASH,
  HealthError,
  MAX_CHAIN_LENGTH,
  EVENT_NAMES as OTEL_EVENT_NAMES,
  METRIC_NAMES as OTEL_METRIC_NAMES,
  SPAN_ATTRIBUTES as OTEL_SPAN_ATTRIBUTES,
  canonicalizeSignals,
  createCausalityHandle,
  createHistoryRecorder,
  createInMemoryHistoryStore,
  createNodeHandler,
  createOtelEmitter,
  createWebHandler,
  databaseSignal,
  defaultScoringStrategy,
  errorRateSignal,
  eventLoopLagSignal,
  memoryPressureSignal,
  pingSignal,
  queueDepthSignal,
  redisSignal,
  sylphxHealth,
  verifyChain,
  weightedProduct,
  withHealth
};
//# sourceMappingURL=index.mjs.map