rumdl 0.2.63

A fast Markdown linter and formatter written in Rust
Documentation
const SNAPSHOT_DAYS = 28;

const EVENT_SCHEMA = Object.freeze({
  cta_select: {
    dimensions: [["open_quickstart", "compare_markdownlint", "open_playground", "install"], ["hero", "next"], [""]],
  },
  command_copy: {
    dimensions: [["uvx_check"], ["success", "failure"], [""]],
  },
  playground_ready: {
    dimensions: [["default", "shared"], [""], [""]],
  },
  playground_example: {
    dimensions: [["common", "headings", "links", "clean"], [""], [""]],
  },
  playground_fix: {
    dimensions: [["single", "all"], ["clean", "remaining", "unchanged"], [""]],
  },
  playground_config: {
    dimensions: [
      ["standard", "mkdocs", "mdx", "pandoc", "quarto", "obsidian", "kramdown", "azure_devops", "myst", "hugo", "mdg"],
      ["0", "1", "2_4", "5_plus"],
      ["under_80", "80", "81_120", "over_120"],
    ],
  },
  playground_share: {
    dimensions: [["success", "failure", "too_large"], [""], [""]],
  },
  playground_error: {
    dimensions: [["load", "lint", "config", "share"], [""], [""]],
  },
});

const SIGNALS = Object.freeze([
  {
    key: "recorded_actions",
    label: "Recorded actions",
    note: "All privacy-preserving product actions",
    matches: () => true,
  },
  {
    key: "playground_starts",
    label: "Playground starts",
    note: "Playground sessions that loaded successfully",
    matches: row => row.event === "playground_ready",
  },
  {
    key: "playground_depth",
    label: "Playground depth",
    note: "Examples, fixes, configuration, and sharing actions",
    matches: row => ["playground_example", "playground_fix", "playground_config", "playground_share"].includes(row.event),
  },
  {
    key: "quickstart_opens",
    label: "Quickstart opens",
    note: "Selections of the Quickstart path",
    matches: row => row.event === "cta_select" && row.dimension1 === "open_quickstart",
  },
  {
    key: "playground_errors",
    label: "Playground errors",
    note: "Load, lint, configuration, or sharing failures",
    matches: row => row.event === "playground_error",
  },
]);

const JSON_HEADERS = {
  "cache-control": "no-store",
  "content-type": "application/json; charset=utf-8",
};

function json(body, status = 200) {
  return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS });
}

function sameSecret(left, right) {
  if (left.length !== right.length) return false;
  let difference = 0;
  for (let index = 0; index < left.length; index += 1) {
    difference |= left.charCodeAt(index) ^ right.charCodeAt(index);
  }
  return difference === 0;
}

function utcDate(value) {
  return new Date(`${value}T00:00:00.000Z`);
}

function dateString(value) {
  return value.toISOString().slice(0, 10);
}

function shiftDate(value, days) {
  const shifted = new Date(value);
  shifted.setUTCDate(shifted.getUTCDate() + days);
  return shifted;
}

function validIdentifier(value) {
  return typeof value === "string" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
}

function normalizeRows(data, from, to) {
  if (!Array.isArray(data)) throw new Error("Analytics Engine returned an invalid result");
  return data.flatMap(row => {
    const schema = EVENT_SCHEMA[row?.event];
    const dimensions = [row?.dimension1 ?? "", row?.dimension2 ?? "", row?.dimension3 ?? ""];
    const count = Number(row?.events);
    if (
      !schema
      || !/^\d{4}-\d{2}-\d{2}$/.test(row?.day ?? "")
      || row.day < from
      || row.day > to
      || !Number.isSafeInteger(count)
      || count < 0
      || !schema.dimensions.every((allowed, index) => allowed.includes(dimensions[index]))
    ) {
      return [];
    }
    return [{
      day: row.day,
      event: row.event,
      dimension1: dimensions[0],
      dimension2: dimensions[1],
      dimension3: dimensions[2],
      count,
    }];
  });
}

function buildSnapshot(rows, today) {
  const to = dateString(today);
  const from = dateString(shiftDate(today, -(SNAPSHOT_DAYS - 1)));
  const currentFrom = dateString(shiftDate(today, -6));
  const previousFrom = dateString(shiftDate(today, -13));
  const daily = new Map();
  for (let offset = SNAPSHOT_DAYS - 1; offset >= 0; offset -= 1) {
    daily.set(dateString(shiftDate(today, -offset)), 0);
  }
  for (const row of rows) daily.set(row.day, (daily.get(row.day) || 0) + row.count);
  const totalActions = [...daily.values()].reduce((sum, count) => sum + count, 0);
  const signals = SIGNALS.map(signal => ({
    key: signal.key,
    label: signal.label,
    current: rows
      .filter(row => row.day >= currentFrom && signal.matches(row))
      .reduce((sum, row) => sum + row.count, 0),
    previous: rows
      .filter(row => row.day >= previousFrom && row.day < currentFrom && signal.matches(row))
      .reduce((sum, row) => sum + row.count, 0),
    note: signal.note,
  }));
  return {
    schema_version: 1,
    generated_at: today.toISOString(),
    period: { from, to },
    total_actions: totalActions,
    active_days: [...daily.values()].filter(count => count > 0).length,
    daily: [...daily.entries()].map(([date, count]) => ({ date, count })),
    signals,
  };
}

function aggregateSql(dataset) {
  return `SELECT
  formatDateTime(toStartOfDay(timestamp), '%Y-%m-%d', 'Etc/UTC') AS day,
  index1 AS event,
  blob2 AS dimension1,
  blob3 AS dimension2,
  blob4 AS dimension3,
  sum(_sample_interval) AS events
FROM ${dataset}
WHERE timestamp >= toStartOfDay(now()) - INTERVAL '27' DAY
  AND timestamp < toStartOfDay(now()) + INTERVAL '1' DAY
GROUP BY day, event, dimension1, dimension2, dimension3
ORDER BY day ASC, event ASC`;
}

export async function onRequestGet(context) {
  const snapshotToken = String(context.env.ADOPTION_SNAPSHOT_TOKEN || "").trim();
  const accountId = String(context.env.CLOUDFLARE_ACCOUNT_ID || "").trim();
  const token = String(context.env.CLOUDFLARE_ANALYTICS_TOKEN || "").trim();
  const dataset = String(context.env.RUMDL_ANALYTICS_DATASET || "rumdl_web_events").trim();
  const authorization = context.request.headers.get("authorization") || "";
  if (!snapshotToken || !sameSecret(authorization, `Bearer ${snapshotToken}`)) {
    return json({ error: "Unauthorized" }, 401);
  }
  if (!/^[A-Fa-f0-9]{32}$/.test(accountId) || !token || !validIdentifier(dataset)) {
    return json({ error: "Adoption snapshot is not configured" }, 503);
  }

  const today = context.today ? utcDate(context.today) : new Date();
  const request = context.fetch || fetch;
  try {
    const response = await request(
      `https://api.cloudflare.com/client/v4/accounts/${accountId}/analytics_engine/sql`,
      {
        method: "POST",
        headers: {
          authorization: `Bearer ${token}`,
          "content-type": "text/plain; charset=utf-8",
        },
        body: aggregateSql(dataset),
      },
    );
    if (!response.ok) return json({ error: "Adoption snapshot is temporarily unavailable" }, 503);
    const payload = await response.json();
    const to = dateString(today);
    const from = dateString(shiftDate(today, -(SNAPSHOT_DAYS - 1)));
    return json(buildSnapshot(normalizeRows(payload.data, from, to), today));
  } catch {
    return json({ error: "Adoption snapshot is temporarily unavailable" }, 503);
  }
}