{"version":3,"file":"ServerTiming.cjs","names":["runAsPromise","acc"],"sources":["../../src/helpers/ServerTiming.ts"],"sourcesContent":["import type { Logger } from \"../middleware/logger.ts\";\nimport { runAsPromise } from \"./promises.ts\";\n\ninterface Timing {\n  description: string;\n  timers: {\n    start?: number;\n    end?: number;\n  }[];\n}\n\n/**\n * A class to manage timing functions and arbitrary periods of time before\n * generating a `Server-Timing` header for use in HTTP responses.\n *\n * This is a very simple implementation that does not support nested timings or\n * fractions of a millisecond.\n */\nexport class ServerTiming {\n  private timings: Record<string, Timing> = {};\n  private readonly logger: Logger;\n\n  constructor(logger: Logger) {\n    this.logger = logger;\n  }\n\n  /**\n   * Start a timing. Returns a function that, when called, will stop the timing\n   * and add it to the header.\n   */\n  public start(name: string, description?: string): () => void {\n    if (!this.timings[name]) {\n      this.timings[name] = {\n        description: description ?? \"\",\n        timers: [],\n      };\n    }\n\n    const index = this.timings[name].timers.push({ start: Date.now() }) - 1;\n\n    return (): void => {\n      const target = this.timings[name];\n      if (!target) {\n        return this.logger.warn({ timing: name }, \"Timing does not exist\");\n      }\n\n      const timer = target.timers[index];\n      if (!timer) {\n        return this.logger.warn(\n          { timing: name, index },\n          \"Timer does not exist\",\n        );\n      }\n\n      timer.end = Date.now();\n    };\n  }\n\n  /**\n   * Add a piece of arbitrary, untimed information to the header. Common use\n   * cases would be cache misses.\n   *\n   * @example\n   * ```\n   * timer.append(\"cache\", \"miss\");\n   * ```\n   */\n  public append(key: string, value: string): void {\n    this.timings[key] = {\n      description: value,\n      timers: [],\n    };\n  }\n\n  /**\n   * Wrap a function in a timing. The timing will be stopped and added to the\n   * header when the function resolves or rejects.\n   *\n   * The return value of the function will be returned from this function.\n   */\n  public async wrap<T extends (...args: unknown[]) => unknown>(\n    name: string,\n    fn: T,\n    description?: string,\n  ): Promise<Awaited<ReturnType<T>>> {\n    const stop = this.start(name, description);\n\n    try {\n      return (await runAsPromise(fn)) as Awaited<ReturnType<T>>;\n    } finally {\n      stop();\n    }\n  }\n\n  /**\n   * Generate the `Server-Timing` header.\n   */\n  public getHeader(): string {\n    const entries = Object.entries(this.timings).reduce<string[]>(\n      (acc, [name, { description, timers }]) => {\n        /**\n         * Ignore timers that had no end.\n         */\n        const hasTimersWithEnd = timers.some((timer) => timer.end);\n        if (!hasTimersWithEnd) {\n          return acc;\n        }\n\n        const dur = timers.reduce((acc, { start, end }) => {\n          if (!start || !end) return acc;\n          return acc + (end - start);\n        }, 0);\n\n        const entry = [\n          name,\n          description ? `desc=\"${description}\"` : \"\",\n          dur ? `dur=${dur}` : \"\",\n        ]\n          .filter(Boolean)\n          .join(\";\");\n\n        return [...acc, entry];\n      },\n      [],\n    );\n\n    return entries.join(\", \");\n  }\n}\n"],"mappings":";;;;;;;;;;;;AAkBA,IAAa,eAAb,MAA0B;CACxB,AAAQ,UAAkC,EAAE;CAC5C,AAAiB;CAEjB,YAAY,QAAgB;AAC1B,OAAK,SAAS;;;;;;CAOhB,AAAO,MAAM,MAAc,aAAkC;AAC3D,MAAI,CAAC,KAAK,QAAQ,MAChB,MAAK,QAAQ,QAAQ;GACnB,aAAa,eAAe;GAC5B,QAAQ,EAAE;GACX;EAGH,MAAM,QAAQ,KAAK,QAAQ,MAAM,OAAO,KAAK,EAAE,OAAO,KAAK,KAAK,EAAE,CAAC,GAAG;AAEtE,eAAmB;GACjB,MAAM,SAAS,KAAK,QAAQ;AAC5B,OAAI,CAAC,OACH,QAAO,KAAK,OAAO,KAAK,EAAE,QAAQ,MAAM,EAAE,wBAAwB;GAGpE,MAAM,QAAQ,OAAO,OAAO;AAC5B,OAAI,CAAC,MACH,QAAO,KAAK,OAAO,KACjB;IAAE,QAAQ;IAAM;IAAO,EACvB,uBACD;AAGH,SAAM,MAAM,KAAK,KAAK;;;;;;;;;;;;CAa1B,AAAO,OAAO,KAAa,OAAqB;AAC9C,OAAK,QAAQ,OAAO;GAClB,aAAa;GACb,QAAQ,EAAE;GACX;;;;;;;;CASH,MAAa,KACX,MACA,IACA,aACiC;EACjC,MAAM,OAAO,KAAK,MAAM,MAAM,YAAY;AAE1C,MAAI;AACF,UAAQ,MAAMA,8BAAa,GAAG;YACtB;AACR,SAAM;;;;;;CAOV,AAAO,YAAoB;AA6BzB,SA5BgB,OAAO,QAAQ,KAAK,QAAQ,CAAC,QAC1C,KAAK,CAAC,MAAM,EAAE,aAAa,cAAc;AAKxC,OAAI,CADqB,OAAO,MAAM,UAAU,MAAM,IAAI,CAExD,QAAO;GAGT,MAAM,MAAM,OAAO,QAAQ,OAAK,EAAE,OAAO,UAAU;AACjD,QAAI,CAAC,SAAS,CAAC,IAAK,QAAOC;AAC3B,WAAOA,SAAO,MAAM;MACnB,EAAE;GAEL,MAAM,QAAQ;IACZ;IACA,cAAc,SAAS,YAAY,KAAK;IACxC,MAAM,OAAO,QAAQ;IACtB,CACE,OAAO,QAAQ,CACf,KAAK,IAAI;AAEZ,UAAO,CAAC,GAAG,KAAK,MAAM;KAExB,EAAE,CACH,CAEc,KAAK,KAAK"}