traceIntake

tcmts.cli.web.traceIntake · #atxlizvy · term · runs on the server

uses tcmts.cli.main.thrownIn, which uses node:fs

Take the spans a page sends (POST /__tcm/traces, from the browser runtime): its calls as it saw them, its view and its navigations, for the tracer to keep in the traces this server took part in (receiveSpans). Only the app's own pages may send (as for a server function call; a beacon's text is let through), 256 KB at most, checked by receivedSpans.

import type { Codebase } from "#codebase/tcmts/cli/codebase";
import { thrownIn } from "#codebase/tcmts/cli/main";
import { receivedSpans, serverApiHost } from "#codebase/tcmts/cli/web";
import type { Middleware } from "#codebase/tcmts/cli/web";

export function traceIntake(cb: Codebase): Middleware {
  const host = serverApiHost(cb);
  return (req, res, next) => {
    if ((req.url ?? "").split("?")[0] !== "/__tcm/traces") return next();
    const answer = (status: number) => {
      res.statusCode = status;
      res.end();
    };
    void (async () => {
      if (req.method !== "POST") return answer(405);
      const origin = req.headers.origin;
      let sameOrigin = true;
      try {
        sameOrigin = !origin || new URL(origin).host === req.headers.host;
      } catch {
        sameOrigin = false;
      }
      if (!sameOrigin || req.headers["sec-fetch-site"] === "cross-site") return answer(403);
      let text = "";
      for await (const chunk of req) {
        text += chunk;
        if (text.length > 256 * 1024) return answer(413);
      }
      let body: unknown;
      try {
        body = JSON.parse(text);
      } catch {
        return answer(400);
      }
      const spans = receivedSpans(body, (h) => !!cb.store.getDef(h));
      if (!spans) return answer(400);
      // a page's error says where it was thrown, as the server's do (where its stack names the module)
      for (const s of spans)
        if (s.kind === "error") {
          const at = thrownIn(cb, s.error);
          if (at) s.attrs["tcm.thrown_in"] = at;
          else delete s.attrs["tcm.thrown_in"];
        }
      (await host()).receiveSpans(spans);
      answer(204);
    })().catch(() => {
      if (!res.headersSent) answer(500);
    });
  };
}

uses

used by