← Files DataARCHIVED FILE

tests/data-app-sites-package-fixtures.mjs

12 KB · Sep 30, 2026 · 23:19 UTC

↓ Download file

import { execFileSync } from "node:child_process";
import { createHash } from "node:crypto";
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";

import { digestProtectedFile } from "../templates/data-app/base/scripts/protected-file-digest.mjs";

const helper = fileURLToPath(
  new URL("../skills/publish-artifact-to-sites/scripts/package-data-app-for-sites.mjs", import.meta.url),
);
const fixturePresentationSource = [
  "const allowedPresentationFields = new Set([",
  '  "theme", "appearance", "title", "description", "hiddenBlocks", "componentTitles", "textEdits",',
  '  "chartOverrides", "filters", "assumptions", "notes", "refreshSchedule", "tabs", "blockLayouts", "verification",',
  "]);",
  "export function validatePresentation(value) {",
  '  if (value.invalid) throw new Error("Presentation is invalid.");',
  "  const unexpected = Object.keys(value).filter((key) => !allowedPresentationFields.has(key));",
  '  if (unexpected.length) throw new Error(`Unsupported presentation fields: ${unexpected.join(", ")}.`);',
  "  return value;",
  "}",
].join("\n");
const fixtureOwnerSource = (emailHash = "") => `export const dataAppOwnerEmailSha256 = "${emailHash}";\n`;
const fixtureWorkerSource = [
  'import dataAppHtml from "../dist/index.html?raw";',
  'import seedSnapshot from "./data.json";',
  'import { createDataAppWorker } from "./data-app-worker.js";',
  "export default createDataAppWorker({",
  "  html: dataAppHtml, seedSnapshot,",
  "});",
].join("\n");
const fixtureRuntimeSource = [
  'import { createHash } from "node:crypto";',
  fixturePresentationSource,
  "export function createDataAppWorker(configuration) {",
  "  validatePresentation(configuration.initialPresentation);",
  "  return { configuration, async fetch(request, environment) {",
  '    if (new URL(request.url).pathname === "/api/presentation" && request.method === "PUT") {',
  '      const email = request.headers.get("oai-authenticated-user-email");',
  '      const hash = email && createHash("sha256").update(email).digest("hex");',
  '      return new Response(null, { status: hash && hash === environment.DATA_APP_OWNER_EMAIL_SHA256 ? 400 : 403 });',
  "    }",
  "    const asset = configuration.deploymentAssets?.html;",
  "    return new Response(asset ? (await environment.BUCKET.get(asset.key)).body : configuration.html);",
  "  } };",
  "}",
].join("\n");
const digest = (value) => createHash("sha256").update(value).digest("hex");
const fixtureAppRuntimeHash = "a".repeat(64);
const digestHelperUrl = new URL("../templates/data-app/base/scripts/protected-file-digest.mjs", import.meta.url);

function reviewedHtml(snapshot) {
  return (
    `<!doctype html><head><meta name="data-app-snapshot-sha256" content="${digest(snapshot)}">` +
    `<meta name="data-app-runtime-sha256" content="${fixtureAppRuntimeHash}"></head>` +
    "<main>Reviewed Data app</main>"
  );
}

function writeSnapshot({ project }, snapshot) {
  const content = typeof snapshot === "string" || Buffer.isBuffer(snapshot) ? snapshot : JSON.stringify(snapshot);
  writeFileSync(join(project, "src/data.json"), content);
  writeFileSync(join(project, "dist/index.html"), reviewedHtml(content));
}

const legacyFixtureProtectedPaths = [
  ".openai/hosting.json",
  "package.json",
  "scripts/verify-protected-runtime.mjs",
  "src/data-app-owner.js",
  "src/worker.js",
  "src/presentation-state.js",
  "src/data-app-worker.js",
];
const fixtureProtectedPaths = [
  ...legacyFixtureProtectedPaths,
  "scripts/protected-file-digest.mjs",
];

function updateFixtureProtectedManifest({ project }, paths) {
  const manifestPath = join(project, "protected-runtime.json");
  const normalized = existsSync(join(project, "scripts/protected-file-digest.mjs"));
  const manifest = existsSync(manifestPath)
    ? JSON.parse(readFileSync(manifestPath, "utf8"))
    : { version: 1, editablePaths: ["src/content/", "src/theme.css", "src/data.json"], files: {} };
  for (const path of paths ?? (normalized ? fixtureProtectedPaths : legacyFixtureProtectedPaths)) {
    const bytes = readFileSync(join(project, path));
    manifest.files[path] = normalized ? digestProtectedFile(path, bytes) : digest(bytes);
  }
  writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
}

function verifyFixtureProtectedManifest({ project }) {
  execFileSync(process.execPath, [join(project, "scripts/verify-protected-runtime.mjs")], {
    cwd: project,
    encoding: "utf8",
    stdio: ["ignore", "pipe", "pipe"],
  });
}

function fixtureRuntime() {
  const plugin = mkdtempSync(join(tmpdir(), "data-app-sites-runtime-"));
  const helperPath = join(plugin, "skills/publish-artifact-to-sites/scripts/package-data-app-for-sites.mjs");
  mkdirSync(dirname(helperPath), { recursive: true });
  copyFileSync(helper, helperPath);
  for (const name of ["package-data-app-for-sites-source.mjs", "publication-secrets.mjs", "publication-scan-streams.mjs", "publication-assets.mjs", "publication-snapshot-index.mjs"]) {
    copyFileSync(
      new URL(`../skills/publish-artifact-to-sites/scripts/${name}`, import.meta.url),
      join(dirname(helperPath), name),
    );
  }
  mkdirSync(join(plugin, "scripts"));
  copyFileSync(new URL("../scripts/data-app-separate.mjs", import.meta.url), join(plugin, "scripts/data-app-separate.mjs"));
  // Packaging must never invoke the authored-client builder.
  writeFileSync(
    join(plugin, "scripts/data-app-build.mjs"),
    'export async function buildPrebuiltDataApp() { throw new Error("Unexpected fixture client rebuild."); }\n',
  );
  copyFileSync(new URL("../scripts/data-url.mjs", import.meta.url), join(plugin, "scripts/data-url.mjs"));
  mkdirSync(join(plugin, "templates/data-app/base/scripts"), { recursive: true });
  copyFileSync(digestHelperUrl, join(plugin, "templates/data-app/base/scripts/protected-file-digest.mjs"));
  mkdirSync(join(plugin, "templates/data-app/base/src"));
  copyFileSync(
    new URL("../templates/data-app/base/src/owner-email.js", import.meta.url),
    join(plugin, "templates/data-app/base/src/owner-email.js"),
  );
  copyFileSync(
    new URL("../templates/data-app/base/src/streaming-json.js", import.meta.url),
    join(plugin, "templates/data-app/base/src/streaming-json.js"),
  );
  for (const path of ["charting/chart-theme.js", "charting/chart-extrema.js", "chrome-contrast.js", "dashboard-url-state.js", "source-provenance.js"]) {
    const destination = join(plugin, "templates/data-app/base/src", path);
    mkdirSync(dirname(destination), { recursive: true });
    copyFileSync(new URL(`../templates/data-app/base/src/${path}`, import.meta.url), destination);
  }
  mkdirSync(join(plugin, "assets/data-app-runtime"), { recursive: true });
  writeFileSync(join(plugin, "assets/data-app-runtime/worker.mjs"), fixtureRuntimeSource);
  return helperPath;
}

function fixture({ legacySource = false } = {}) {
  const project = realpathSync(mkdtempSync(join(tmpdir(), "data-app-sites-package-")));
  for (const directory of [".openai", "dist", "src", "scripts", "node_modules/vite/bin"]) {
    mkdirSync(join(project, directory), { recursive: true });
  }
  writeFileSync(
    join(project, "package.json"),
    JSON.stringify({
      type: "module",
      scripts: { build: "node scripts/verify-protected-runtime.mjs" },
    }),
  );
  if (!legacySource) copyFileSync(digestHelperUrl, join(project, "scripts/protected-file-digest.mjs"));
  writeFileSync(
    join(project, "scripts/verify-protected-runtime.mjs"),
    [
      'import { createHash } from "node:crypto";',
      'import { readFileSync, writeFileSync } from "node:fs";',
      ...(legacySource ? [] : ['import { digestProtectedFile } from "./protected-file-digest.mjs";']),
      'const manifest = JSON.parse(readFileSync("protected-runtime.json", "utf8"));',
      "for (const [path, expected] of Object.entries(manifest.files)) {",
      legacySource
        ? '  const actual = createHash("sha256").update(readFileSync(path)).digest("hex");'
        : "  const actual = digestProtectedFile(path, readFileSync(path));",
      "  if (actual !== expected) throw new Error(`Protected runtime mismatch: ${path}`);",
      "}",
      'writeFileSync(".test-source-verified", "yes");',
    ].join("\n"),
  );
  writeSnapshot(
    { project },
    {
      generatedAt: "2026-08-02T04:57:36.243Z",
      queries: { reviewed: { rows: [{ amount: 42 }] } },
    },
  );
  writeFileSync(
    join(project, "src/data-app-schedule.js"),
    "export const normalizeDataAppRefreshSchedule = (value) => value;\n",
  );
  writeFileSync(join(project, "src/presentation-state.js"), fixturePresentationSource);
  writeFileSync(join(project, "src/data-app-owner.js"), fixtureOwnerSource());
  writeFileSync(join(project, "src/worker.js"), fixtureWorkerSource);
  writeFileSync(join(project, "src/data-app-worker.js"), fixtureRuntimeSource);
  writeFileSync(
    join(project, "node_modules/vite/bin/vite.js"),
    [
      'import { mkdirSync, readFileSync, writeFileSync } from "node:fs";',
      'import { join } from "node:path";',
      'const entry = process.argv[process.argv.indexOf("--ssr") + 1];',
      'const output = process.argv[process.argv.indexOf("--outDir") + 1];',
      'writeFileSync(join(process.cwd(), ".test-vite-arguments.json"), JSON.stringify(process.argv.slice(2)));',
      'writeFileSync(join(process.cwd(), ".test-vite-node-arguments.json"), JSON.stringify(process.execArgv));',
      "mkdirSync(output, { recursive: true });",
      'const source = readFileSync(entry, "utf8");',
      'const html = readFileSync(join(process.cwd(), "dist/index.html"), "utf8");',
      'writeFileSync(join(output, "index.js"), source + "\\n// reviewed HTML " + JSON.stringify(html)' +
        ' + "\\n// inherited session " + JSON.stringify(process.env.CODEX_SESSION_ID ?? "")' +
        ' + "\\n// inherited task " + JSON.stringify(process.env.CODEX_THREAD_ID ?? "")' +
        ' + "\\n// inherited WASI mode " + JSON.stringify(process.env.NAPI_RS_FORCE_WASI ?? "")' +
        ' + "\\n// inherited native library " + JSON.stringify(process.env.NAPI_RS_NATIVE_LIBRARY_PATH ?? null));',
    ].join("\n"),
  );
  writeFileSync(
    join(project, ".openai/hosting.json"),
    legacySource ? JSON.stringify({ d1: null, r2: null }) : `${JSON.stringify({ d1: "DB", r2: null }, null, 2)}\n`,
  );
  writeFileSync(
    join(project, "presentation.json"),
    JSON.stringify({
      theme: "codex-classic",
      title: "Reviewed Data app",
      filters: { week: "all" },
      blockLayouts: {
        "dashboard:dashboard:canvas": {
          authoredRevision: 2,
          order: ["trend", "metric"],
          rows: [{ id: "overview", items: ["trend", "metric"] }],
          spans: { trend: 8, metric: 4 },
          preferredSpans: { trend: 8, metric: 4 },
        },
      },
    }),
  );
  updateFixtureProtectedManifest({ project });
  return { project, helperPath: fixtureRuntime(), legacySource };
}

function legacyFixture() {
  return fixture({ legacySource: true });
}

function packageDataApp(
  { project, helperPath: fixtureHelper, legacySource = false },
  {
    projectId = "appgprj_reviewed",
    ownerEmail = null,
    migratePackagingManifest = false,
    presentationPath = join(project, "presentation.json"),
    environment = process.env,
    helperPath = fixtureHelper,
    source = legacySource,
    extra = [],
  } = {},
) {
  return execFileSync(
    process.execPath,
    [
      helperPath,
      "--project-dir",
      project,
      `--project-id=${projectId}`,
      ...(ownerEmail === null ? [] : [`--owner-email=${ownerEmail}`]),
      ...(migratePackagingManifest ? ["--migrate-packaging-manifest"] : []),
      ...(presentationPath ? ["--presentation-file", presentationPath] : []),
      ...(source ? ["--source"] : []),
      ...extra,
    ],
    { encoding: "utf8", env: environment, stdio: ["ignore", "pipe", "pipe"] },
  );
}

export {
  digest,
  fixture,
  fixtureOwnerSource,
  fixtureWorkerSource,
  legacyFixture,
  packageDataApp,
  reviewedHtml,
  updateFixtureProtectedManifest,
  verifyFixtureProtectedManifest,
  writeSnapshot,
};

SHA-256: 02a4e8ed68c85a8b12d6963b3d7ac210b371801357012ed67d058ddb0cc1fba8