← Files DataARCHIVED FILE
tests/data-app-sites-package.test.mjs
43.5 KB · Sep 30, 2026 · 23:19 UTC
import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import {
copyFileSync,
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join, relative } from "node:path";
import test from "node:test";
import { documentSnapshot, packageDataAppForSites } from "../skills/publish-artifact-to-sites/scripts/package-data-app-for-sites.mjs";
import { createDataAppWorker } from "../templates/data-app/base/src/data-app-worker.js";
import {
fixture as packageFixture,
packageDataApp,
writeSnapshot,
} from "./data-app-sites-package-fixtures.mjs";
const siteId = "appgprj_reviewed";
const ownerEmail = "owner@example.com";
const digest = (value) => createHash("sha256").update(value).digest("hex");
const publicationPaths = [
"dist/index.html",
"src/data.json",
"src/data-app-owner.js",
".openai/hosting.json",
"protected-runtime.json",
"dist/.openai/hosting.json",
"dist/server/index.js",
".data-app-assets/manifest.json", ".data-app-assets/html.html", ".data-app-assets/snapshot.json", ".data-app-offline/index.html",
];
function fixture(t) {
const state = packageFixture();
t.after(() => {
rmSync(state.project, { recursive: true, force: true });
rmSync(join(dirname(state.helperPath), "../../.."), { recursive: true, force: true });
});
return state;
}
function publicationBytes({ project }) {
return publicationPaths.map((path) => (existsSync(join(project, path)) ? readFileSync(join(project, path)) : null));
}
function packageOutputBytes({ project }, includeHosting = true) {
const paths = ["dist/server/index.js", "dist/.openai/hosting.json"];
if (includeHosting) paths.push(".openai/hosting.json");
return paths.map((path) => readFileSync(join(project, path)));
}
function packagedConfiguration({ project }) {
const worker = readFileSync(join(project, "dist/server/index.js"), "utf8");
const prefix = "\nexport default createDataAppWorker(JSON.parse(";
const suffix = "));\n";
assert.ok(worker.endsWith(suffix));
const offset = worker.lastIndexOf(prefix);
assert.ok(offset >= 0);
return JSON.parse(JSON.parse(worker.slice(offset + prefix.length, -suffix.length)));
}
function packagedHtml(state) { return readFileSync(join(state.project, ".data-app-assets/html.html"), "utf8"); }
function packagedSnapshot(state) { return JSON.parse(readFileSync(join(state.project, ".data-app-assets/snapshot.json"), "utf8")); }
function assetEnvironment(state) {
const manifest = JSON.parse(readFileSync(join(state.project, ".data-app-assets/manifest.json"), "utf8"));
return { BUCKET: { async get(key) {
const asset = Object.values(manifest.assets).find(asset => asset.key === key);
if (!asset) return null;
const bytes = readFileSync(join(state.project, ".data-app-assets", asset.path));
return { body: new Response(bytes).body, size: bytes.length, customMetadata: { sha256: asset.sha256 }, async json() { return JSON.parse(bytes); } };
} } };
}
function packageWorker(state, options) {
const result = JSON.parse(packageDataApp(state, options));
const configuration = packagedConfiguration(state);
// Exercise the real editing backend with the package's serialized inputs.
// Its focused authorization/presentation suite also tests every request path.
const worker = createDataAppWorker(configuration), assets = assetEnvironment(state);
return { result, configuration, worker: { fetch: (request, environment) => worker.fetch(request, { ...assets, ...environment }) } };
}
function database() {
let presentation = null;
return {
DATA_APP_OWNER_EMAIL_SHA256: digest(ownerEmail),
DB: {
prepare(sql) {
let values = [];
return {
bind(...args) {
values = args;
return this;
},
async run() {
if (sql.startsWith("INSERT INTO data_app_presentation_v1") && !presentation) {
const [, presentation_json, revision, updated_at] = values;
presentation = { presentation_json, revision, updated_at };
}
if (sql.startsWith("UPDATE data_app_presentation_v1")) {
const [presentation_json, updated_at, , revision] = values;
if (presentation.revision !== revision) return { meta: { changes: 0 } };
presentation = { presentation_json, revision: revision + 1, updated_at };
return { meta: { changes: 1 } };
}
return { meta: { changes: 0 } };
},
async first() {
return presentation;
},
};
},
},
};
}
function request(path, email, init = {}) {
return new Request(`https://dashboard.chatgpt.site${path}`, {
...init,
headers: { ...(email ? { "oai-authenticated-user-email": email } : {}), ...init.headers },
});
}
const mutation = (presentation, revision = 0) => ({
method: "PUT",
body: JSON.stringify({ presentation, revision }),
});
function embeddedPage(snapshot) {
return (
'<!doctype html><html><head><meta name="data-app-runtime-sha256" content="previous-runtime">' +
'<meta name="data-app-snapshot-sha256" content="stale-marker">' +
'<meta name="data-app-local-thread" content="local-preview-reference"></head><body>' +
`<script type="application/json" id="data-app-reviewed-snapshot">${JSON.stringify(snapshot)}</script>` +
"<main>Exact café — reviewed page</main></body></html>"
);
}
const hostedHtml = (html) => html.replace('<meta name="data-app-local-thread" content="local-preview-reference">', "")
.replace(/(<head[^>]*>)/iu, `$1<meta name="data-app-sites-project" content="${siteId}">`);
test("retired capture inputs fail before replacing publication outputs", (t) => {
const state = fixture(t);
packageDataApp(state);
const before = publicationBytes(state);
assert.throws(() => packageDataApp(state, { extra: ["--capture-file", "manifest.json"] }), /Unknown option '--capture-file'/u);
assert.throws(() => packageDataAppForSites({
"project-dir": state.project, "project-id": siteId, "capture-file": "manifest.json",
}), /Unsupported publication option: --capture-file/u);
assert.deepEqual(publicationBytes(state), before);
});
test("linear document inspection handles large embedded rows and ignores inert marker examples", () => {
const snapshot = { queries: { reviewed: { rows: Array.from({ length: 30_000 }, (_, index) => ({ index, title: "İstanbul — reviewed", note: "x".repeat(220) })) } } };
const page = embeddedPage(snapshot);
const noise = `<script>${"/* compiled runtime */".repeat(800_000)}</script>`;
const html = page.replace("</head>", `${noise}</head>`);
assert.ok(html.length > 20_000_000);
assert.deepEqual(JSON.parse(documentSnapshot(html).embedded), snapshot);
const marker = `<meta content='${"A".repeat(64)}' name='data-app-snapshot-sha256'>`;
const ignored = `<!--${marker}--><script>const sample='${marker}';</script><template><template>${marker}</template></template>`;
const source = `<!doctype html><html><head data-title="İstanbul >">${ignored}${marker}</head><body>${marker}${ignored}</body></html>`;
assert.deepEqual(documentSnapshot(source), { embedded: undefined, hashes: ["a".repeat(64)] });
assert.deepEqual(documentSnapshot(`<head>${marker}${marker}</head>`).hashes, ["a".repeat(64), "a".repeat(64)]);
assert.deepEqual(documentSnapshot(`<head>${marker.replace(" name=", " name='other' name=")}</head>`).hashes, [null]);
assert.deepEqual(documentSnapshot(`<head>${ignored}</head><body>${marker}</body>`).hashes, []);
});
test("publishes the exact compiled page and its embedded data without rebuilding or running authoring checks", async (t) => {
const state = fixture(t);
const snapshot = {
id: "reviewed-app",
generatedAt: "2026-08-31T12:00:00Z",
queries: {
reviewed: {
rows: [{ week: "2026-08-31", value: 12 }],
source: {
sql: "SELECT * FROM sessions s JOIN events e ON s.session_id = e.session_id",
reviewNote: "Reviewed query metadata",
localPath: "/Users/test/example.csv",
},
},
},
};
const html = embeddedPage(snapshot);
writeFileSync(join(state.project, "dist/index.html"), html);
writeFileSync(join(state.project, "src/data.json"), "Unfinished source edit; not the compiled snapshot");
writeFileSync(
join(state.project, "src/presentation-state.js"),
'throw new Error("Do not execute copied validators");',
);
rmSync(join(state.project, "node_modules"), { recursive: true, force: true });
const sourceBefore = publicationBytes(state).slice(0, 5);
const { result, configuration, worker } = packageWorker(state, { environment: { ...process.env, PATH: "" } });
assert.equal(result.buildMode, "existing-page");
assert.equal(result.snapshotSource, "compiled-html");
assert.equal(result.htmlSha256, digest(html));
assert.equal(result.scanStats.complete, true);
assert.ok(result.scanStats.inspectedTextUnits >= html.length);
assert.ok(result.scanStats.fieldsVisited > 0);
assert.ok(result.scanStats.elapsedMs >= 0);
assert.ok(Object.values(result.scanStats).every((value) =>
typeof value === "boolean" || (typeof value === "number" && Number.isFinite(value))));
assert.equal(configuration.projectId, siteId);
assert.equal(packagedHtml(state), hostedHtml(html));
assert.deepEqual(packagedSnapshot(state), snapshot);
assert.equal(await (await worker.fetch(request("/"), {})).text(), hostedHtml(html));
// Source, reviewed HTML, owner module, and protected-runtime manifest stay untouched.
const after = publicationBytes(state);
for (const index of [0, 1, 2, 4]) assert.deepEqual(after[index], sourceBefore[index]);
assert.equal(existsSync(join(state.project, ".test-source-verified")), false);
assert.equal(existsSync(join(state.project, ".test-runtime-verified")), false);
assert.equal(existsSync(join(state.project, "node_modules")), false);
assert.deepEqual(JSON.parse(readFileSync(join(state.project, "dist/.openai/hosting.json"))), {
d1: "DB",
r2: "BUCKET",
project_id: siteId,
artifact_metadata: { surface: "dashboard", producer: "data-analytics" },
});
});
test("uses the compiled report surface for deployed attribution without adding it to source hosting", (t) => {
const state = fixture(t);
writeFileSync(join(state.project, "dist/index.html"), embeddedPage({ surface: "report", queries: {} }));
packageDataApp(state);
assert.equal(JSON.parse(readFileSync(join(state.project, ".openai/hosting.json"))).artifact_metadata, undefined);
assert.deepEqual(JSON.parse(readFileSync(join(state.project, "dist/.openai/hosting.json"))).artifact_metadata, {
surface: "report", producer: "data-analytics",
});
});
test("rejects unapproved compiled artifact surfaces before changing publication files", async (t) => {
for (const surface of [null, "customer-authored-value", { customer_content: "secret" }]) {
await t.test(JSON.stringify(surface), (t) => {
const state = fixture(t);
writeFileSync(join(state.project, "dist/index.html"), embeddedPage({ surface, queries: {} }));
const before = publicationBytes(state);
assert.throws(() => packageDataApp(state), /surface must be dashboard or report/u);
assert.deepEqual(publicationBytes(state), before);
});
}
});
test("legacy HTML falls back to existing source data and never alters the compiled page", (t) => {
const state = fixture(t);
const html = readFileSync(join(state.project, "dist/index.html"), "utf8");
const source = JSON.parse(readFileSync(join(state.project, "src/data.json"), "utf8"));
const { result, configuration } = packageWorker(state);
assert.equal(result.snapshotSource, "src/data.json");
assert.deepEqual(packagedSnapshot(state), source);
assert.equal(packagedHtml(state), hostedHtml(html));
assert.equal(readFileSync(join(state.project, "dist/index.html"), "utf8"), html);
});
test("legacy source replacement decoding preserves canonical bytes and the original source fingerprint", t => {
const state = fixture(t);
const bytes = Buffer.concat([Buffer.from('{"queries":{"q":{"rows":[{"label":"'), Buffer.from([0xc3]), Buffer.from('"}]}},"title":"Reviewed"}')]);
writeSnapshot(state, bytes);
const before = readFileSync(join(state.project, "src/data.json"));
const result = JSON.parse(packageDataApp(state));
assert.deepEqual(packagedSnapshot(state), JSON.parse(bytes.toString()));
assert.equal(result.snapshotSha256, digest(JSON.stringify(JSON.parse(bytes.toString()))));
assert.deepEqual(readFileSync(join(state.project, "src/data.json")), before);
});
test("legacy snapshot markers accept HTML attribute order, whitespace, and case variations", (t) => {
const state = fixture(t);
const snapshotBytes = Buffer.from('{\n "queries": {}, "title": "Café — reviewed"\n}\n');
writeSnapshot(state, snapshotBytes);
const hash = digest(snapshotBytes).toUpperCase();
const html = `<!doctype html><HTML><HEAD>\n<META\nCONTENT = '${hash}' NAME = DATA-APP-SNAPSHOT-SHA256>\n</HEAD><BODY>Reviewed</BODY></HTML>`;
writeFileSync(join(state.project, "dist/index.html"), html);
const { configuration } = packageWorker(state);
assert.equal(packagedHtml(state), hostedHtml(html));
assert.deepEqual(packagedSnapshot(state), JSON.parse(snapshotBytes));
});
test("legacy publication rejects missing, mismatched, duplicate, and spoofed snapshot markers before writing", (t) => {
const cases = [
["missing", () => "<html><head></head><body>Reviewed</body></html>"],
[
"mismatched",
() => '<html><head><meta name="data-app-snapshot-sha256" content="' + "0".repeat(64) + '"></head></html>',
],
["duplicate", (marker) => `<html><head>${marker}${marker}</head></html>`],
["comment", (marker) => `<html><head><!-- ${marker} --></head></html>`],
["script", (marker) => `<html><head><script>const marker = '${marker}';</script></head></html>`],
["style", (marker) => `<html><head><style>/* ${marker} */</style></head></html>`],
["template", (marker) => `<html><head><template>${marker}</template></head></html>`],
["body", (marker) => `<html><head></head><body>${marker}</body></html>`],
["implicit body element", (marker) => `<html><head><div>Reviewed</div>${marker}</html>`],
["implicit body text", (marker) => `<html><head>Reviewed${marker}</html>`],
["reopened head", (marker) => `<html><head></head><body><head>${marker}</head></body></html>`],
[
"fake embedded snapshot",
() =>
'<html><head></head><body><!-- <script id="data-app-reviewed-snapshot">{"queries":{}}</script> --></body></html>',
],
["quoted attribute", (marker) => `<html><head><meta title='${marker}'></head></html>`],
["lookalike attribute", (marker) => `<html><head>${marker.replace(" name=", " data-name=")}</head></html>`],
[
"duplicate name attribute",
(marker) => `<html><head>${marker.replace(" name=", ' name="other" name=')}</head></html>`,
],
];
for (const [label, page] of cases) {
const state = fixture(t);
packageDataApp(state);
const hash = digest(readFileSync(join(state.project, "src/data.json")));
const marker = `<meta name="data-app-snapshot-sha256" content="${hash}">`;
writeFileSync(join(state.project, "dist/index.html"), page(marker));
const before = packageOutputBytes(state);
assert.throws(() => packageDataApp(state), /snapshot|reviewed data/iu, label);
assert.deepEqual(packageOutputBytes(state), before, label);
}
});
test("legacy consistency compares raw source bytes rather than parsed JSON equivalence", (t) => {
const state = fixture(t);
packageDataApp(state);
const snapshotPath = join(state.project, "src/data.json");
writeFileSync(snapshotPath, `${readFileSync(snapshotPath, "utf8")}\n`);
const before = packageOutputBytes(state);
assert.throws(() => packageDataApp(state), /snapshot|reviewed data/iu);
assert.deepEqual(packageOutputBytes(state), before);
});
test("embedded snapshots publish without a source snapshot or a matching legacy marker", (t) => {
const state = fixture(t);
const snapshot = { title: "Reviewed embedded page", queries: {} };
const html = embeddedPage(snapshot);
writeFileSync(join(state.project, "dist/index.html"), html);
rmSync(join(state.project, "src/data.json"));
const { result, configuration } = packageWorker(state);
assert.equal(result.snapshotSource, "compiled-html");
assert.deepEqual(packagedSnapshot(state), snapshot);
assert.equal(packagedHtml(state), hostedHtml(html));
});
test("invalid snapshot metadata fails before replacing existing publication outputs", (t) => {
const state = fixture(t);
packageDataApp(state);
writeFileSync(join(state.project, "dist/index.html"), embeddedPage(null));
const before = packageOutputBytes(state);
assert.throws(() => packageDataApp(state));
assert.deepEqual(packageOutputBytes(state), before);
});
test("an explicitly selected compiled HTML file is packaged without replacing the default page", (t) => {
const state = fixture(t);
const before = readFileSync(join(state.project, "dist/index.html"));
const selected = embeddedPage({ queries: {}, title: "Selected reviewed page" });
writeFileSync(join(state.project, "dist/selected.html"), selected);
const { configuration } = packageWorker(state, { extra: ["--html-file", "dist/selected.html"] });
assert.equal(packagedHtml(state), hostedHtml(selected));
assert.equal(packagedSnapshot(state).title, "Selected reviewed page");
assert.deepEqual(readFileSync(join(state.project, "dist/index.html")), before);
});
test("HTML and presentation inputs cannot select unrelated files by absolute or parent-relative paths", (t) => {
for (const selected of ["html", "presentation"]) {
for (const absolute of [true, false]) {
const state = fixture(t);
packageDataApp(state);
const outside = mkdtempSync(join(tmpdir(), "data-publish-input-"));
t.after(() => rmSync(outside, { recursive: true, force: true }));
const file = join(outside, selected === "html" ? "unrelated.html" : "unrelated.json");
copyFileSync(join(state.project, selected === "html" ? "dist/index.html" : "presentation.json"), file);
const selectedPath = absolute ? file : relative(state.project, file);
const options =
selected === "html" ? { extra: ["--html-file", selectedPath] } : { presentationPath: selectedPath };
const before = packageOutputBytes(state);
assert.throws(() => packageDataApp(state, options), /inside|within|outside|contain/iu);
assert.deepEqual(packageOutputBytes(state), before);
}
}
});
test("publication inputs reject outward symlinks without changing existing output", (t) => {
for (const input of ["dist/index.html", "src/data.json", "presentation.json", ".openai/hosting.json"]) {
const state = fixture(t);
packageDataApp(state);
const outside = mkdtempSync(join(tmpdir(), "data-publish-linked-input-"));
t.after(() => rmSync(outside, { recursive: true, force: true }));
const target = join(outside, "input");
copyFileSync(join(state.project, input), target);
rmSync(join(state.project, input));
symlinkSync(target, join(state.project, input));
const before = packageOutputBytes(state);
const unrelated = readFileSync(target);
assert.throws(() => packageDataApp(state), /inside|within|outside|contain|symlink/iu, input);
assert.deepEqual(packageOutputBytes(state), before, input);
assert.deepEqual(readFileSync(target), unrelated, input);
}
});
test("publication inputs must be regular files", (t) => {
for (const input of ["dist/index.html", "src/data.json", "presentation.json", ".openai/hosting.json"]) {
const state = fixture(t);
packageDataApp(state);
rmSync(join(state.project, input));
mkdirSync(join(state.project, input));
const before = packageOutputBytes(state, input !== ".openai/hosting.json");
assert.throws(() => packageDataApp(state), /regular file/iu, input);
assert.deepEqual(packageOutputBytes(state, input !== ".openai/hosting.json"), before, input);
}
});
test("input symlinks contained in the project preserve the selected page and snapshot", (t) => {
const state = fixture(t);
const html = readFileSync(join(state.project, "dist/index.html"), "utf8");
const snapshot = JSON.parse(readFileSync(join(state.project, "src/data.json"), "utf8"));
mkdirSync(join(state.project, "reviewed"));
for (const [input, target] of [
["dist/index.html", "reviewed/page.html"],
["src/data.json", "reviewed/snapshot.json"],
["presentation.json", "reviewed/presentation.json"],
]) {
copyFileSync(join(state.project, input), join(state.project, target));
rmSync(join(state.project, input));
symlinkSync(join(state.project, target), join(state.project, input));
}
const { configuration } = packageWorker(state);
assert.equal(packagedHtml(state), hostedHtml(html));
assert.deepEqual(packagedSnapshot(state), snapshot);
assert.equal(configuration.initialPresentation.title, "Reviewed Data app");
});
test("the selected project directory may itself be a symlink", (t) => {
const state = fixture(t);
const links = mkdtempSync(join(tmpdir(), "data-publish-project-link-"));
t.after(() => rmSync(links, { recursive: true, force: true }));
const projectLink = join(links, "project");
symlinkSync(state.project, projectLink);
const linked = { ...state, project: projectLink };
const { result, configuration } = packageWorker(linked);
assert.equal(result.projectRoot, state.project);
assert.equal(configuration.initialPresentation.title, "Reviewed Data app");
});
test("detected credentials fail before replacing output and are not copied into the error", (t) => {
const state = fixture(t);
packageDataApp(state);
const secret = "synthetic-publication-credential-never-echo";
for (const query of [
{ rows: [{ api_key: secret }] },
{ source: { authToken: secret }, rows: [] },
{ source: { url: `https://example.test/data#access_token=${secret}` }, rows: [] },
{ source: { url: `https://example.test/data?key=${secret}` }, rows: [] },
{ source: { url: `https://example.test/token/${secret}` }, rows: [] },
]) {
const html = embeddedPage({ queries: { reviewed: query } });
writeFileSync(join(state.project, "dist/index.html"), html);
const before = publicationBytes(state);
assert.throws(
() => packageDataApp(state),
(error) => {
const report = `${error.message}\n${error.stdout ?? ""}\n${error.stderr ?? ""}`;
assert.match(report, /secret|credential/iu);
assert.equal(report.includes(secret), false);
return true;
},
);
assert.deepEqual(publicationBytes(state), before);
}
});
test("inline CSS and SVG preserve clean assets and reject credentials before replacing output", async (t) => {
const state = fixture(t);
copyFileSync(
new URL("../assets/data-app-runtime/worker.mjs", import.meta.url),
join(dirname(state.helperPath), "../../../assets/data-app-runtime/worker.mjs"),
);
const base64 = (value) => Buffer.from(value).toString("base64");
const svg = (content) => `<svg xmlns="http://www.w3.org/2000/svg"><g id="icon">${content}</g></svg>`;
const cleanCss = ".fixture { color: blue; }";
const cleanSvg = svg('<rect width="8" height="8" fill="blue"/>');
const page = (mime, content, fragment = "") => embeddedPage({ queries: {} }).replace(
"</head>",
`<style>.fixture{background-image:url(data:image/svg+xml;base64,${base64(cleanSvg)});` +
`--asset:url(data:${mime};base64,${base64(content)}${fragment})}</style></head>`,
);
const cleanHtml = page("text/css", cleanCss);
writeFileSync(join(state.project, "dist/index.html"), cleanHtml);
packageDataApp(state);
const emitted = readFileSync(join(state.project, "dist/server/index.js"), "utf8");
const { default: worker } = await import(`data:text/javascript;base64,${base64(emitted)}`);
const response = await worker.fetch(request("/", "viewer@example.com"), assetEnvironment(state));
assert.equal(response.status, 200);
assert.equal(await response.text(), hostedHtml(cleanHtml));
const secret = "INERT_TEST_BEARER_VALUE_1234567890";
const credentialUrl = `https://example.test/image?token=${secret}`;
const cases = [
["CSS literal", "text/css", `.fixture { --note: "Bearer ${secret}"; }`],
["CSS URL parameter", "text/css", `.fixture { background-image: url("${credentialUrl}"); }`],
["SVG literal", "image/svg+xml", svg(`<metadata>Bearer ${secret}</metadata>`), "#icon"],
["SVG URL parameter", "image/svg+xml", svg(`<image href="${credentialUrl}"/>`), "#icon"],
];
for (const [label, mime, content, fragment] of cases) {
await t.test(label, () => {
writeFileSync(join(state.project, "dist/index.html"), page(mime, content, fragment));
const before = publicationBytes(state);
assert.throws(
() => packageDataApp(state),
(error) => {
const report = `${error.message}\n${error.stdout ?? ""}\n${error.stderr ?? ""}`;
assert.match(report, /secret|credential/iu);
for (const payload of [secret, content, base64(content)]) assert.equal(report.includes(payload), false);
return true;
},
);
assert.deepEqual(publicationBytes(state), before);
});
}
});
test("configuration serialization preserves special property names and script-like strings as data", (t) => {
const state = fixture(t);
const snapshot = JSON.parse(
'{"queries":{"__proto__":{"rows":[{"constructor":"</script><script>fixture</script>","value":"
"}]}}}',
);
// These values are in the source snapshot, not inside an HTML raw-text element.
writeSnapshot(state, snapshot);
const { configuration } = packageWorker(state);
assert.deepEqual(packagedSnapshot(state), snapshot);
assert.equal(Object.hasOwn(packagedSnapshot(state).queries, "__proto__"), true);
});
test("direct publication needs no copied owner module, protected manifest, verifier, or local dependencies", (t) => {
const state = fixture(t);
for (const path of ["src/data-app-owner.js", "protected-runtime.json", "scripts", "node_modules"]) {
rmSync(join(state.project, path), { recursive: true, force: true });
}
const { result } = packageWorker(state, { environment: { ...process.env, PATH: "" } });
assert.equal(result.ownerEnvironmentVariable, "DATA_APP_OWNER_EMAIL_SHA256");
for (const path of ["src/data-app-owner.js", "protected-runtime.json", "scripts", "node_modules"]) {
assert.equal(existsSync(join(state.project, path)), false);
}
});
test("packaged presentation handoff keeps shared editing, revision conflicts, and viewer isolation", async (t) => {
const state = fixture(t);
const presentation = {
theme: "original",
appearance: "system",
title: "Reviewed title",
filters: { lane: "All non-VIP" },
blockLayouts: {
canvas: {
order: ["trend", "metric"],
rows: [{ id: "overview", items: ["trend", "metric"] }],
spans: { trend: 8, metric: 4 },
},
},
};
const file = join(state.project, "presentation.json");
const transport = { ...presentation, filterDefinitions: [{ id: "transport-only" }] };
writeFileSync(file, JSON.stringify(transport));
const { configuration, worker } = packageWorker(state);
const expectedPresentation = {
hiddenBlocks: [], componentTitles: {}, textEdits: {}, chartOverrides: {}, ...presentation,
};
assert.deepEqual(configuration.initialPresentation, expectedPresentation);
assert.deepEqual(JSON.parse(readFileSync(file)), transport);
const env = database();
const initial = await (await worker.fetch(request("/api/presentation", ownerEmail), env)).json();
assert.equal(initial.canEdit, true);
assert.deepEqual(initial.presentation, expectedPresentation);
for (const viewer of [undefined, "another-viewer@example.com"]) {
assert.equal((await (await worker.fetch(request("/api/presentation", viewer), env)).json()).canEdit, false);
assert.equal(
(await worker.fetch(request("/api/presentation", viewer, mutation({ title: "viewer edit" })), env)).status,
403,
);
assert.equal(
(
await worker.fetch(
request("/api/queries/reviewed", viewer, { method: "PUT", body: JSON.stringify({ rows: [] }) }),
env,
)
).status,
403,
);
}
assert.equal(
(await worker.fetch(request("/api/presentation", ownerEmail, mutation({ title: "Owner edit" })), env)).status,
200,
);
assert.equal(
(await worker.fetch(request("/api/presentation", ownerEmail, mutation({ title: "Stale edit" })), env)).status,
409,
);
});
test("full and compact presentation handoffs produce identical seeds and fresh hosted state", async (t) => {
t.mock.timers.enable({ apis: ["Date"], now: Date.parse("2026-09-05T12:00:00Z") });
const state = fixture(t);
const shared = {
theme: "original", appearance: "system", title: "Reviewed compact handoff",
filters: {}, assumptions: { activationLift: 0 },
tabs: [{ id: "dashboard", label: "Dashboard" }],
};
const emptyCollections = { hiddenBlocks: [], componentTitles: {}, textEdits: {}, chartOverrides: {} };
const variants = [
{ full: { ...shared, ...emptyCollections }, compact: shared },
{
full: { ...shared, ...emptyCollections, componentTitles: { trend: "Reviewed trend" } },
compact: { ...shared, componentTitles: { trend: "Reviewed trend" } },
},
{
full: { ...shared, ...emptyCollections, hiddenBlocks: ["internal-note"], textEdits: { caption: "" },
chartOverrides: { trend: { type: "bar" } } },
compact: { ...shared, hiddenBlocks: ["internal-note"], textEdits: { caption: "" },
chartOverrides: { trend: { type: "bar" } } },
},
];
const file = join(state.project, "presentation.json");
for (const surface of ["dashboard", "report"]) {
writeFileSync(join(state.project, "dist/index.html"), embeddedPage({ surface, queries: {} }));
const reviewedBefore = publicationBytes(state).slice(0, 3);
for (const { full, compact } of variants) {
writeFileSync(file, JSON.stringify(full));
const original = packageWorker(state);
const originalRecord = await (await original.worker.fetch(request("/api/presentation", ownerEmail), database())).json();
writeFileSync(file, JSON.stringify(compact));
const cleaned = packageWorker(state);
const cleanedRecord = await (await cleaned.worker.fetch(request("/api/presentation", ownerEmail), database())).json();
assert.deepEqual(cleaned.configuration, original.configuration);
assert.deepEqual(cleaned.configuration.initialPresentation, full);
assert.deepEqual(cleanedRecord, originalRecord);
assert.deepEqual(cleanedRecord, { presentation: full, revision: 0,
updatedAt: "2026-09-05T12:00:00.000Z", canEdit: true, ownerEnvironmentConfigured: true });
assert.deepEqual(JSON.parse(readFileSync(file)), compact, "Packaging must not rewrite handoff inputs");
assert.deepEqual(publicationBytes(state).slice(0, 3), reviewedBefore);
}
}
});
test("omitting the presentation file keeps the existing empty seed contract", async (t) => {
const state = fixture(t);
const { configuration, worker } = packageWorker(state, { presentationPath: null });
assert.deepEqual(configuration.initialPresentation, {});
const record = await (await worker.fetch(request("/api/presentation", ownerEmail), database())).json();
assert.deepEqual(record.presentation, {});
assert.equal(record.revision, 0);
assert.equal(record.canEdit, true);
});
test("compact seed restoration preserves malformed explicit values for the editing validator", (t) => {
const state = fixture(t);
const file = join(state.project, "presentation.json");
for (const [key, invalidValues] of [
["hiddenBlocks", [null, {}, [7]]],
["componentTitles", [null, [], { trend: "" }]],
["textEdits", [null, [], { caption: false }]],
["chartOverrides", [null, [], { trend: { rows: [] } }]],
]) {
for (const value of invalidValues) {
writeFileSync(file, JSON.stringify({ [key]: value }));
packageDataApp(state);
const configuration = packagedConfiguration(state);
assert.deepEqual(configuration.initialPresentation[key], value);
assert.throws(() => createDataAppWorker(configuration), /Hidden blocks|bounded|reviewed data/u);
assert.deepEqual(JSON.parse(readFileSync(file)), { [key]: value });
}
}
for (const value of [[], "invalid", 7, false]) {
writeFileSync(file, JSON.stringify(value));
packageDataApp(state);
const configuration = packagedConfiguration(state);
assert.deepEqual(configuration.initialPresentation, value);
assert.throws(() => createDataAppWorker(configuration), /Presentation must be a JSON object/u);
}
});
test("the emitted shipped Worker preserves owner editing and saved presentation across republication", async (t) => {
const state = fixture(t);
copyFileSync(
new URL("../assets/data-app-runtime/worker.mjs", import.meta.url),
join(dirname(state.helperPath), "../../../assets/data-app-runtime/worker.mjs"),
);
const publish = async () => {
packageDataApp(state);
const emitted = readFileSync(join(state.project, "dist/server/index.js"), "utf8");
const { default: worker } = await import(`data:text/javascript;base64,${Buffer.from(emitted).toString("base64")}`);
return worker;
};
const env = database();
const worker = await publish();
const initial = await (await worker.fetch(request("/api/presentation", ownerEmail), env)).json();
assert.equal(initial.canEdit, true);
assert.equal(initial.presentation.title, "Reviewed Data app");
const saved = {
title: "Saved by owner", hiddenBlocks: ["private-note"], componentTitles: { trend: "Saved trend" },
textEdits: { caption: "" }, chartOverrides: { trend: { type: "bar" } }, filters: { week: "current" },
};
const updated = await worker.fetch(request("/api/presentation", ownerEmail, mutation(saved)), env);
assert.equal(updated.status, 200);
const savedRecord = await updated.json();
assert.deepEqual(savedRecord.presentation, saved);
assert.equal(savedRecord.revision, 1);
assert.equal(
(
await worker.fetch(
request("/api/presentation", "workspace-viewer@example.com", mutation({ title: "Unauthorized edit" }, 1)),
env,
)
).status,
403,
);
writeFileSync(join(state.project, "presentation.json"), JSON.stringify({ title: "New deployment seed" }));
const redeployed = await publish();
const retained = await (await redeployed.fetch(request("/api/presentation", ownerEmail), env)).json();
assert.deepEqual(retained, { ...savedRecord, canEdit: true, ownerEnvironmentConfigured: true });
});
test("collaborator republication preserves the owner's saves before and after reload", async (t) => {
const state = fixture(t);
const env = database();
const { worker } = packageWorker(state);
await worker.fetch(request("/api/presentation", ownerEmail, mutation({ title: "Already saved online" })), env);
writeFileSync(join(state.project, "presentation.json"), JSON.stringify({ title: "New local seed" }));
const editor = "editor@example.com";
const { worker: next, configuration } = packageWorker(state, {
environment: { ...process.env, DATA_APP_OWNER_EMAIL_SHA256: digest(editor) },
});
assert.equal(Object.hasOwn(configuration, "ownerEmailSha256"), false);
const retained = await (await next.fetch(request("/api/presentation", ownerEmail), env)).json();
assert.equal(retained.presentation.title, "Already saved online");
assert.equal(retained.revision, 1);
assert.equal(retained.ownerEnvironmentConfigured, true);
// An already-open owner tab can still save against the new deployment.
const saved = await next.fetch(request("/api/presentation", ownerEmail, mutation({ title: "Saved after republish" }, 1)), env);
assert.equal(saved.status, 200);
const record = await (await next.fetch(request("/api/presentation", ownerEmail), env)).json();
assert.equal(record.presentation.title, "Saved after republish");
assert.equal(record.revision, 2);
assert.equal(record.canEdit, true);
assert.equal((await (await next.fetch(request("/api/presentation", editor), env)).json()).canEdit, false);
assert.equal((await next.fetch(request("/api/presentation", editor, mutation({ title: "Editor save" }, 2)), env)).status, 403);
// Reloaded owner controls receive the current revision and can save again.
assert.equal((await next.fetch(request("/api/presentation", ownerEmail, mutation({ title: "Saved after reload" }, record.revision)), env)).status, 200);
});
test("publication passes presentation to the editing runtime without duplicating validation or verification authority", async (t) => {
const state = fixture(t);
writeFileSync(
join(state.project, "presentation.json"),
JSON.stringify({
title: "Reviewed",
verification: { verifiedBy: "fabricated@example.com", verifiedAt: "2026-08-31T12:00:00.000Z" },
}),
);
const { worker } = packageWorker(state);
const record = await (await worker.fetch(request("/api/presentation", ownerEmail), database())).json();
assert.equal(Object.hasOwn(record.presentation, "verification"), false);
writeFileSync(join(state.project, "presentation.json"), JSON.stringify({ unsupported: true }));
packageDataApp(state);
assert.throws(() => createDataAppWorker(packagedConfiguration(state)), /Unsupported presentation fields/u);
});
test("Site environment is the sole edit authority without packaging owner identity", async (t) => {
const state = fixture(t);
const historicalUserIdHash = digest("historical-site-user-id");
const historicalEmailHash = digest("previous-owner@example.com");
const ownerPath = join(state.project, "src/data-app-owner.js");
const historicalOwner =
`export const dataAppOwnerUserIdSha256 = "${historicalUserIdHash}";\n` +
`export const dataAppOwnerEmailSha256 = "${historicalEmailHash}";\n`;
writeFileSync(ownerPath, historicalOwner);
const { result, configuration, worker } = packageWorker(state);
assert.equal(result.ownerAuthorization, "environment");
assert.equal(result.ownerEnvironmentVariable, "DATA_APP_OWNER_EMAIL_SHA256");
assert.equal(Object.hasOwn(result, "ownerSeeded"), false);
assert.equal(Object.hasOwn(result, "ownerSetupRequired"), false);
assert.equal(Object.hasOwn(configuration, "ownerEmailSha256"), false);
assert.equal(Object.hasOwn(configuration, "ownerUserIdSha256"), false);
assert.equal(readFileSync(ownerPath, "utf8"), historicalOwner);
const emitted = readFileSync(join(state.project, "dist/server/index.js"), "utf8");
assert.equal(emitted.includes(historicalUserIdHash), false);
assert.equal(emitted.includes(historicalEmailHash), false);
assert.equal(emitted.includes(digest(ownerEmail)), false);
for (const artifact of [JSON.stringify(result), emitted, ...publicationBytes(state)]) {
if (artifact !== null) assert.doesNotMatch(artifact.toString(), /owner@example\.com/iu);
}
const env = database();
const normalizedOwner = " OWNER@EXAMPLE.com ";
assert.equal(
(await (await worker.fetch(request("/api/presentation", normalizedOwner), env)).json()).canEdit,
true,
);
assert.equal(
(await worker.fetch(request("/api/presentation", normalizedOwner, mutation({ title: "Owner edit" })), env)).status,
200,
);
for (const headers of [
{ "oai-authenticated-user-id": ownerEmail },
{ "x-user-email": ownerEmail },
{ "oai-authenticated-user-id": ownerEmail, "oai-authenticated-user-email": "viewer@example.com" },
]) {
assert.equal(
(await (await worker.fetch(request("/api/presentation", undefined, { headers }), env)).json()).canEdit,
false,
);
assert.equal(
(
await worker.fetch(
request("/api/presentation", undefined, { ...mutation({ title: "Unauthorized edit" }, 1), headers }),
env,
)
).status,
403,
);
}
});
test("environment ownership handles normalized email punctuation without embedding identity", async (t) => {
const state = fixture(t);
const literalEmail = "-Owner+`tag`$bucket'&@Example.COM";
const { result, configuration, worker } = packageWorker(state);
assert.equal(result.ownerAuthorization, "environment");
assert.equal(Object.hasOwn(configuration, "ownerEmailSha256"), false);
const env = { ...database(), DATA_APP_OWNER_EMAIL_SHA256: digest(literalEmail.toLowerCase()) };
assert.equal(
(await (await worker.fetch(request("/api/presentation", literalEmail), env)).json()).canEdit,
true,
);
for (const artifact of [JSON.stringify(result), readFileSync(join(state.project, "dist/server/index.js"), "utf8")]) {
assert.equal(artifact.toLowerCase().includes(literalEmail.toLowerCase()), false);
}
});
test("retired owner options fail before writes without exposing email", (t) => {
const cases = [
[{ ownerEmail }, /Unknown option '--owner-email'/u],
[{ ownerEmail: "" }, /Unknown option '--owner-email'/u],
...["--read-only", "--owner-user-id", "--owner-user-id-sha256", "--legacy-owner-email-site-file", "--bootstrap-owner"].map(
(flag) => [{ extra: [flag] }, /Unknown option/u],
),
];
for (const source of [false, true]) {
const state = fixture(t);
packageDataApp(state, { source });
const before = publicationBytes(state);
for (const [options, expected] of cases) {
assert.throws(
() => packageDataApp(state, { ...options, source }),
(error) => {
// execFileSync's wrapper message repeats its command; inspect the CLI's own diagnostics.
const report = `${error.stdout ?? ""}\n${error.stderr ?? ""}`;
assert.match(report, expected);
for (const identity of [ownerEmail, options.ownerEmail]) {
if (identity) assert.equal(report.includes(identity), false);
}
return true;
},
);
assert.deepEqual(publicationBytes(state), before);
}
}
const state = fixture(t);
const before = publicationBytes(state);
assert.throws(() => packageDataAppForSites({
"project-dir": state.project, "project-id": siteId, "owner-email": ownerEmail,
}), /no longer supported.*DATA_APP_OWNER_EMAIL_SHA256/u);
assert.deepEqual(publicationBytes(state), before);
});
test("publication cannot transfer a project to a different Site", (t) => {
const state = fixture(t);
packageDataApp(state);
const before = publicationBytes(state);
assert.throws(() => packageDataApp(state, { projectId: "different-site" }), /existing Site ID/u);
assert.deepEqual(publicationBytes(state), before);
});
test("a failed package write restores existing publication outputs", (t) => {
const state = fixture(t);
packageDataApp(state);
const before = publicationBytes(state);
rmSync(join(state.project, "dist/.openai"), { recursive: true, force: true });
writeFileSync(join(state.project, "dist/.openai"), "blocks the hosting output directory");
assert.throws(() => packageDataApp(state));
const after = publicationBytes(state);
for (const index of [0, 1, 2, 3, 4, 6]) assert.deepEqual(after[index], before[index]);
});
test("output symlinks cannot redirect publication writes outside the project", (t) => {
for (const target of ["dist/server", "dist/server/index.js", "dist/.openai", ".openai/hosting.json"]) {
const state = fixture(t);
const outside = mkdtempSync(join(tmpdir(), "data-publish-outside-"));
t.after(() => rmSync(outside, { recursive: true, force: true }));
const isFile = target.endsWith(".js") || target.endsWith(".json");
const unrelated = join(outside, isFile ? "outside-file" : "index.js");
writeFileSync(unrelated, isFile && target.endsWith(".json") ? "{}" : "unrelated file");
const before = readFileSync(unrelated);
const destination = join(state.project, target);
rmSync(destination, { recursive: true, force: true });
mkdirSync(dirname(destination), { recursive: true });
symlinkSync(isFile ? unrelated : outside, destination);
assert.throws(() => packageDataApp(state), /symlinks|inside the project/u);
assert.deepEqual(readFileSync(unrelated), before);
}
});
SHA-256: e39d18f78b008b05c62859aba8e53a1d930b645b4fc8713032d67b7a535eaa60