Release govoplan-files v0.1.26: speed archive workflows and unify file tools
Module Package Release / publish-packages (push) Successful in 12s

This commit is contained in:
2026-09-08 01:32:41 +02:00
parent 2baa8f2657
commit ff84812f7f
35 changed files with 4331 additions and 275 deletions
+302
View File
@@ -0,0 +1,302 @@
// Execute the actual Files client and Core HTTP transport. Only browser I/O
// (fetch, XHR, timers, document cookies) and temporal context are substituted.
// No source slicing, regex assertions, live credentials, or server is involved.
import assert from "node:assert/strict";
import { randomUUID } from "node:crypto";
import { readFileSync } from "node:fs";
import { createRequire } from "node:module";
import vm from "node:vm";
const coreRoot = new URL("../../../govoplan-core/webui/", import.meta.url);
const require = createRequire(new URL("package.json", coreRoot));
const { transformSync } = require("esbuild");
const compile = (url) => transformSync(readFileSync(url, "utf8"), {
loader: "ts", format: "cjs", target: "es2022", sourcefile: url.pathname,
define: { "import.meta.env": "{}" }
}).code;
const coreCode = compile(new URL("src/api/client.ts", coreRoot));
const filesCode = compile(new URL("../src/api/files.ts", import.meta.url));
const settle = () => new Promise((resolve) => setImmediate(resolve));
const plain = (value) => JSON.parse(JSON.stringify(value));
const settings = { apiBaseUrl: "https://fixture.invalid", accessToken: "", apiKey: "" };
const destination = { owner_type: "user", owner_id: "fixture-user", path: "imports" };
const previewResponse = (changes = {}) => ({
preview_token: "preview-token", staged_upload_id: "stage-one", expires_at: "2099-01-01T00:00:00Z",
entries: [], file_count: 1, directory_count: 0, archive_format: "zip",
requires_password: false, password_verified: true, compressed_size_bytes: 100,
expanded_size_bytes: 10, ...changes
});
const json = (body, status = 200) => new Response(JSON.stringify(body), {
status, headers: { "content-type": "application/json" }
});
const deferred = () => {
let resolve;
const promise = new Promise((complete) => { resolve = complete; });
return { promise, resolve };
};
function harness() {
const requests = [];
const xhrRequests = [];
const timers = new Map();
let timerId = 0;
let now = Date.parse("2026-09-07T10:00:00Z");
let onFetch = () => { throw new Error("Unexpected fetch"); };
let onXhr = () => { throw new Error("Unexpected XHR"); };
class ClockDate extends Date { static now() { return now; } }
class FakeXhr {
upload = {};
headers = new Headers();
open(method, url) { this.method = method; this.url = url; }
setRequestHeader(name, value) { this.headers.set(name, value); }
getResponseHeader(name) { return name.toLowerCase() === "content-type" ? "application/json" : null; }
send(body) { this.body = body; xhrRequests.push(this); onXhr(this); }
progress(loaded, total, lengthComputable = true) { this.upload.onprogress?.({ loaded, total, lengthComputable }); }
respond(body, status = 200) { this.status = status; this.statusText = status === 200 ? "OK" : "Error"; this.responseText = JSON.stringify(body); this.onload?.(); }
}
const context = vm.createContext({
File, Blob, FormData, Headers, Response, URL, URLSearchParams, AbortController,
Date: ClockDate, crypto: { randomUUID }, XMLHttpRequest: FakeXhr,
document: { cookie: "" }, console,
fetch: async (url, init) => {
const request = { url, path: new URL(url).pathname, method: init?.method ?? "GET", ...init };
requests.push(request);
return onFetch(request);
},
setTimeout: (callback, delay) => { const id = ++timerId; timers.set(id, { callback, at: now + delay }); return id; },
clearTimeout: (id) => timers.delete(id)
});
function load(code, dependencies) {
context.module = { exports: {} };
context.exports = context.module.exports;
context.require = (name) => {
assert.ok(name in dependencies, `Unexpected client import: ${name}`);
return dependencies[name];
};
vm.runInContext(`(function(module, exports, require) {\n${code}\n})(module, exports, require);`, context);
return context.module.exports;
}
const core = load(coreCode, { "../platform/temporal": { temporalRequestHeaders: () => ({}) } });
const api = load(filesCode, { "@govoplan/core-webui": core });
return {
api, core, requests, xhrRequests, timers,
fetch(handler) { onFetch = handler; },
xhr(handler) { onXhr = handler; },
async advance(milliseconds) {
const target = now + milliseconds;
while (true) {
const next = [...timers.entries()].filter(([, timer]) => timer.at <= target).sort((left, right) => left[1].at - right[1].at)[0];
if (!next) break;
now = next[1].at;
timers.delete(next[0]);
next[1].callback();
await settle();
}
now = target;
await settle();
}
};
}
let passed = 0;
async function test(name, run) {
await run();
passed += 1;
console.log(`PASS ${name}`);
}
await test("locally expired stage is released and preview reuploads the original file", async () => {
const env = harness();
const file = new File(["archive fixture"], "fixture.zip");
let previews = 0;
env.fetch((request) => {
if (request.method === "DELETE") return new Response(null, { status: 204 });
assert.equal(request.path, "/api/v1/files/archive-preview");
assert.equal(request.body.get("file"), file);
assert.equal(request.body.has("staged_upload_id"), false);
return json(previewResponse(++previews === 1 ? { expires_at: "2026-09-07T10:00:01Z" } : { staged_upload_id: "stage-two" }));
});
await env.api.previewArchiveUpload(settings, file, destination);
await env.advance(1100);
const result = await env.api.previewArchiveUpload(settings, file, destination);
assert.equal(previews, 2);
assert.equal(result.staged_upload_id, "stage-two");
assert.equal(env.requests.filter((request) => request.method === "DELETE").length, 1);
assert.equal(env.requests.filter((request) => request.path.endsWith("archive-confirm")).length, 0);
});
await test("server-expired cached preview retries only the read-only preview once with file bytes", async () => {
const env = harness();
const file = new File(["archive fixture"], "fixture.zip");
let calls = 0;
env.fetch((request) => {
assert.equal(request.path, "/api/v1/files/archive-preview");
calls += 1;
if (calls === 2) {
assert.equal(request.body.get("staged_upload_id"), "stage-one");
assert.equal(request.body.get("preview_token"), "preview-token");
assert.equal(request.body.has("file"), false);
return json({ detail: "Archive preview expired" }, 410);
}
assert.equal(request.body.get("file"), file);
assert.equal(request.body.has("staged_upload_id"), false);
return json(previewResponse({ staged_upload_id: calls === 1 ? "stage-one" : "stage-two" }));
});
await env.api.previewArchiveUpload(settings, file, destination);
assert.equal((await env.api.previewArchiveUpload(settings, file, destination)).staged_upload_id, "stage-two");
assert.equal(calls, 3, "one initial preview, one expired-stage attempt, one safe reupload");
});
await test("a failed fresh reupload cannot recurse into repeated preview attempts", async () => {
const env = harness();
const file = new File(["archive fixture"], "fixture.zip");
let calls = 0;
env.fetch(() => ++calls === 1 ? json(previewResponse()) : json({ detail: "Expired" }, 410));
await env.api.previewArchiveUpload(settings, file, destination);
await assert.rejects(env.api.previewArchiveUpload(settings, file, destination), (error) => error instanceof env.core.ApiError && error.status === 410);
assert.equal(calls, 3);
});
await test("failed confirmation never automatically retries or falls back to a file reupload", async () => {
for (const status of [400, 410, 500]) {
const env = harness();
const file = new File(["archive fixture"], "fixture.zip");
env.fetch((request) => request.path.endsWith("archive-preview") ? json(previewResponse()) : json({ detail: "Confirmation failed" }, status));
const preview = await env.api.previewArchiveUpload(settings, file, destination);
await assert.rejects(env.api.confirmArchiveUpload(settings, file, { ...destination, preview_token: preview.preview_token, selected_paths: ["one.txt"] }), (error) => error instanceof env.core.ApiError && error.status === status);
const confirmations = env.requests.filter((request) => request.path.endsWith("archive-confirm"));
assert.equal(confirmations.length, 1);
assert.equal(confirmations[0].body.get("staged_upload_id"), "stage-one");
assert.equal(confirmations[0].body.has("file"), false);
assert.equal(env.requests.length, 2);
}
});
await test("progress polling failures neither reject nor retry the authoritative confirmation POST", async () => {
for (const failure of [404, 500, "network"]) {
const env = harness();
const pending = deferred();
const events = [];
env.fetch((request) => {
if (request.method === "POST") return pending.promise;
if (failure === "network") throw new TypeError("Offline telemetry");
return json({ detail: "Telemetry unavailable" }, failure);
});
const confirmation = env.api.confirmManagedArchive(settings, "managed-file", {
...destination, source_version_id: "version-one", preview_token: "token", selected_paths: ["one.txt"],
onArchiveProgress: (event) => events.push(event)
});
let settled = false;
void confirmation.then(() => { settled = true; });
await env.advance(1200);
assert.equal(settled, false);
assert.equal(events.length, 0);
assert.equal(env.requests.filter((request) => request.method === "POST").length, 1);
const polls = env.requests.filter((request) => request.method === "GET");
assert.ok(polls.length >= 2);
assert.ok(polls.every((request) => request.cache === "no-store"));
pending.resolve(json({ files: [{ id: "one", size_bytes: 12345 }, { id: "two", size_bytes: 678 }] }));
const result = await confirmation;
assert.equal(result.files.length, 2);
assert.deepEqual(plain(events.at(-1)), { phase: "complete", status: "complete", completed_files: 2, total_files: 2, completed_bytes: 13023, total_bytes: 13023 });
assert.equal(env.timers.size, 0);
assert.ok(polls.every((request) => request.signal.aborted));
await env.advance(2000);
assert.equal(env.requests.filter((request) => request.method === "POST").length, 1);
assert.equal(env.requests.filter((request) => request.method === "GET").length, polls.length);
}
});
await test("confirmation errors stop polling without fabricating a successful completion", async () => {
const env = harness();
const pending = deferred();
const events = [];
env.fetch((request) => request.method === "POST" ? pending.promise : json({ detail: "Not yet available" }, 404));
const confirmation = env.api.confirmManagedArchive(settings, "managed-file", {
...destination, source_version_id: "version-one", preview_token: "token", selected_paths: ["one.txt"],
onArchiveProgress: (event) => events.push(event)
});
const rejected = assert.rejects(confirmation, (error) => error instanceof env.core.ApiError && error.status === 409);
await env.advance(200);
pending.resolve(json({ detail: "Destination conflict" }, 409));
await rejected;
assert.equal(events.length, 0);
assert.equal(env.timers.size, 0);
assert.equal(env.requests.filter((request) => request.method === "POST").length, 1);
});
await test("a late in-flight poll cannot replace the authoritative successful result", async () => {
const env = harness();
const post = deferred();
const poll = deferred();
const events = [];
env.fetch((request) => request.method === "POST" ? post.promise : poll.promise);
const confirmation = env.api.confirmManagedArchive(settings, "managed-file", {
...destination, source_version_id: "version-one", preview_token: "token", selected_paths: ["one.txt"],
onArchiveProgress: (event) => events.push(event)
});
await env.advance(200);
assert.equal(env.requests.filter((request) => request.method === "GET").length, 1);
post.resolve(json({ files: [{ id: "one", size_bytes: 12345 }] }));
await confirmation;
poll.resolve(json({ phase: "extracting", status: "running", completed_files: 0, total_files: 1, completed_bytes: 10, total_bytes: 12345 }));
await settle();
assert.equal(events.length, 1);
assert.equal(events[0].status, "complete");
assert.equal(events[0].completed_bytes, 12345);
assert.equal(env.timers.size, 0);
});
await test("XHR preview reports measured byte counters and preserves them at transfer completion", async () => {
const env = harness();
const file = new File(["archive fixture"], "fixture.zip");
const events = [];
env.xhr((xhr) => {
assert.equal(xhr.body.get("file"), file);
assert.equal(xhr.withCredentials, true);
xhr.progress(1578, 4096);
xhr.progress(4096, 4096);
xhr.respond(previewResponse());
});
await env.api.previewArchiveUpload(settings, file, { ...destination, onProgress: (event) => events.push(event) });
assert.deepEqual(plain(events.slice(1)), [
{ loaded: 1578, total: 4096, percentage: 39 },
{ loaded: 4096, total: 4096, percentage: 100 },
{ loaded: 4096, total: 4096, percentage: 100 }
]);
assert.equal(env.xhrRequests.length, 1);
assert.equal(env.requests.length, 0);
});
await test("unknown-length XHR uploads never fabricate total bytes", async () => {
const env = harness();
const events = [];
env.xhr((xhr) => { xhr.progress(3917, 0, false); xhr.respond(previewResponse()); });
await env.api.previewArchiveUpload(settings, new File(["fixture"], "fixture.zip"), { ...destination, onProgress: (event) => events.push(event) });
assert.equal(events.at(-2).percentage, null);
assert.equal(events.at(-1).loaded, 3917);
assert.equal(events.at(-1).total, undefined);
assert.equal(events.at(-1).percentage, 100);
});
await test("password repreview and confirmation reuse staging without another XHR transfer", async () => {
const env = harness();
const file = new File(["archive fixture"], "fixture.zip");
env.xhr((xhr) => { xhr.progress(1024, 1024); xhr.respond(previewResponse()); });
env.fetch((request) => {
assert.equal(request.body.get("staged_upload_id"), "stage-one");
assert.equal(request.body.has("file"), false);
assert.equal(request.body.get("password"), "fixture-only-password");
return request.path.endsWith("archive-preview") ? json(previewResponse({ preview_token: "verified-token" })) : json({ files: [] });
});
await env.api.previewArchiveUpload(settings, file, { ...destination, onProgress: () => {} });
const verified = await env.api.previewArchiveUpload(settings, file, { ...destination, password: "fixture-only-password", onProgress: () => assert.fail("Staged repreview must not claim another upload") });
await env.api.confirmArchiveUpload(settings, file, {
...destination, password: "fixture-only-password", preview_token: verified.preview_token, selected_paths: ["one.txt"],
onProgress: () => assert.fail("Staged confirmation must not claim another upload")
});
assert.equal(env.xhrRequests.length, 1);
assert.equal(env.requests.length, 2);
});
console.log(`Archive client behavior: ${passed} tests passed using real Files and Core transport code.`);
@@ -0,0 +1,80 @@
// Run the owning Files read helpers and real Core request cache, replacing only network I/O.
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { createRequire } from "node:module";
import vm from "node:vm";
const coreRoot = new URL("../../../govoplan-core/webui/", import.meta.url);
const require = createRequire(new URL("package.json", coreRoot));
const { transformSync } = require("esbuild");
const compile = (url) => transformSync(readFileSync(url, "utf8"), {
loader: "ts", format: "cjs", target: "es2022", define: { "import.meta.env": "{}" }
}).code;
const coreCode = compile(new URL("src/api/client.ts", coreRoot));
const filesCode = compile(new URL("../src/api/files.ts", import.meta.url));
const settings = { apiBaseUrl: "https://fixture.invalid", apiKey: "", accessToken: "" };
const owner = { owner_type: "user", owner_id: "fixture-user" };
const fresh = { cache: "no-store" };
function harness(respond) {
const requests = [];
const context = vm.createContext({
Headers, Response, FormData, URL, URLSearchParams, AbortController, console,
document: { cookie: "" },
fetch: async (url, init) => {
const request = { url: new URL(url), ...init };
requests.push(request);
assert.equal(init?.method ?? "GET", "GET", "Reload read helpers must never mutate");
return new Response(JSON.stringify(respond(request)), { status: 200, headers: { "content-type": "application/json" } });
}
});
function load(code, dependencies) {
context.module = { exports: {} };
context.exports = context.module.exports;
context.require = (name) => {
assert.ok(name in dependencies, `Unexpected module import: ${name}`);
return dependencies[name];
};
vm.runInContext(`(function(module, exports, require) {\n${code}\n})(module, exports, require);`, context);
return context.module.exports;
}
const core = load(coreCode, { "../platform/temporal": { temporalRequestHeaders: () => ({}) } });
return { api: load(filesCode, { "@govoplan/core-webui": core }), requests };
}
for (const kind of ["spaces", "connector"]) {
const { api, requests } = harness(() => kind === "spaces" ? { spaces: [] } : { items: [], path: "source/nested" });
const read = (options) => kind === "spaces"
? api.listFileSpaces(settings, options)
: api.browseFileConnectorProfile(settings, "fixture-profile", { path: "source/nested" }, options);
await read();
await read();
assert.equal(requests.length, 1, "normal reads retain short-lived request reuse");
await read(fresh);
await read(fresh);
assert.equal(requests.length, 3, "each explicit Reload bypasses the shared recent-response cache");
assert.equal(requests[2].cache, "no-store");
}
{
const { api, requests } = harness(({ url }) => {
const next = url.searchParams.has("cursor");
if (url.pathname.endsWith("/folders")) return { folders: [], total: 0, next_cursor: next ? null : "folders-2", watermark: "start" };
if (url.pathname.endsWith("/delta")) return { full: false, files: [], folders: [], deleted: [], has_more: url.searchParams.get("since") === "start", watermark: url.searchParams.get("since") === "start" ? "middle" : "end" };
return { files: [], total: 0, next_cursor: next ? null : "files-2" };
});
await api.listManagedFileSnapshot(settings, owner, fresh);
await api.listManagedFileSnapshot(settings, owner, fresh);
assert.equal(requests.length, 12, "two snapshots each re-read both folder/file pages and both reconciliation pages");
assert.ok(requests.every((request) => request.cache === "no-store"));
}
{
const { api, requests } = harness(({ url }) => ({ files: [], total: 0, next_cursor: url.searchParams.has("cursor") ? null : "filtered-2" }));
await api.listFilesByProperties(settings, { ...owner, campaign_usage: "linked", audit_relevant: true }, fresh);
await api.listFilesByProperties(settings, { ...owner, campaign_usage: "linked", audit_relevant: true }, fresh);
assert.equal(requests.length, 4, "each filtered Reload must re-read every server result page");
assert.ok(requests.every((request) => request.cache === "no-store" && request.url.searchParams.get("campaign_usage") === "linked" && request.url.searchParams.get("audit_relevant") === "true"));
}
console.log("Files Reload: 4 real-client cache, read-only, pagination and filter checks passed.");
@@ -38,7 +38,17 @@ assert.match(filesPage, /topicId: "files\.workflow\.organize-managed-files"/);
assert.match(filesPage, /disabledReason=\{uploadBlocker\}/);
assert.match(filesPage, /disabledReason=\{deleteBlocker\}/);
assert.match(filesPage, /<ConfirmDialog[\s\S]*tone="danger"/);
assert.match(filesPage, /className="workspace-data-page module-entry-page file-manager-page file-manager-fullscreen files-page"/);
assert.match(filesPage, /<WorkspaceFrame as="main" height="viewport" surface="plain"/);
assert.match(filesPage, /const toolbar = <WorkspaceActionBar\s+scope="workspace"\s+variant="collection"\s+refreshable/);
assert.match(filesPage, /\{toolbar\}[\s\S]*className=\{`file-manager-shell/);
assert.match(filesPage, /const selectionToolbar = <WorkspaceActionBar\s+scope="detail-pane"/);
assert.match(filesPage, /<Dialog\s+open=\{toolsPanel !== null\}/);
assert.match(filesPage, /destructiveActions=\{<Button variant="danger"/);
const reload = filesPage.slice(filesPage.indexOf(" async function reloadCurrentView()"), filesPage.indexOf(" function applyManagedSpaceDelta"));
for (const readOperation of ["listManagedFileSnapshot", "resolveFilePatterns", "listFilesByProperties", "browseFileConnectorProfile"]) assert.ok(reload.includes(readOperation));
assert.doesNotMatch(reload, /syncConnector|importFile|uploadFile|deleteFile|resetTransientState|setCurrentFolder|clearSelection|setSearchActive/);
assert.match(reload, /if \(!isCurrent\(\)\) return;/);
assert.doesNotMatch(reload, /setBusy\(/, "manual reload owns only its own busy state");
assert.doesNotMatch(filesPage, /window\.(?:alert|confirm)\(/);
assert.doesNotMatch(`${connector}\n${integrity}\n${filesPage}\n${moduleSource}`, /@govoplan\/(?:campaign|mail|docs)-webui|govoplan_(?:campaign|mail|docs)/);
@@ -0,0 +1,60 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import vm from "node:vm";
const read = (path) => readFileSync(new URL(path, import.meta.url), "utf8");
const page = read("../src/features/files/FilesPage.tsx");
const menu = read("../src/features/files/components/FileManagerComponents.tsx");
const api = read("../src/api/files.ts");
const translations = read("../src/i18n/generatedTranslations.ts");
assert.match(page, /selectedFiles\.length === 1 && selectedFolderPaths\.size === 0 && ARCHIVE_FILENAME_PATTERN/);
assert.match(page, /function openManagedArchive[\s\S]*?!canUpload \|\| !canDownload[\s\S]*?setManagedArchiveFile\(file\)/);
assert.match(page, /disabledReason=\{unpackBlocker\}/);
assert.match(menu, /onClick=\{onUnpackArchive\} disabled=\{!canUnpackArchive\}/);
assert.match(page, /file: File \| ManagedFile/);
assert.match(page, /previewManagedArchive\(settings, file\.id,[\s\S]*?source_version_id: file\.version_id/);
assert.match(page, /confirmManagedArchive\(settings, managedArchiveFile\.id,[\s\S]*?source_version_id: managedArchiveFile\.version_id/);
assert.match(page, /archivePreview && \(archiveFile \|\| managedArchiveFile\)/);
assert.match(page, /loadArchivePreview\(\(managedArchiveFile \|\| archiveFile\)!/);
assert.match(page, /if \(managedArchiveFile\) \{ setArchivePreview\(null\); setSelectedArchivePaths/);
assert.match(page, /onClose=\{\(\) => \{ if \(!busy\) closeDialog\(\); \}\}/);
assert.match(page, /<LoadingFrame loading=\{uploadActive\}[\s\S]*?indicator="none"[\s\S]*?progress=\{operationProgressValue\}/);
assert.match(page, /<div inert=\{uploadActive\}>/);
assert.match(page, /onArchiveProgress: \(progress: ArchiveOperationProgress\)/);
assert.match(page, /archiveProgress\?\.status === "complete" \? 100/);
assert.match(page, /archiveProgress.phase !== "finalizing"[\s\S]*?serverProgressRatio < 1/);
assert.match(menu, /closeDisabled=\{busy\}[\s\S]*?closeOnBackdrop=\{!busy\}/);
const feedback = page.slice(page.indexOf(" const selectedArchiveBytes ="), page.indexOf(" const connectorLocationLabel ="));
function operationFeedback(archiveProgress, uploadPhase = "unpacking", uploadProgress = null) {
const context = { archiveProgress, uploadPhase, uploadProgress, selectedArchivePaths: new Set(["one.txt", "empty.txt"]),
archivePreview: { entries: [{ path: "one.txt", kind: "file", size_bytes: 3 }, { path: "empty.txt", kind: "file", size_bytes: 0 }] },
formatBytes: (value) => `${value} B`, i18nMessage: (key, values) => ({ key, values }) };
vm.runInNewContext(`${feedback}\nglobalThis.result = { value: operationProgressValue, label: operationProgressLabel, phase: operationBusyLabel };`, context);
return context.result;
}
const measured = { phase: "extracting", status: "running", completed_files: 1, total_files: 2, completed_bytes: 3, total_bytes: 6 };
assert.equal(operationFeedback(measured).value, 50);
assert.equal(operationFeedback({ ...measured, phase: "finalizing", completed_files: 2, completed_bytes: 6 }).value, null, "finalization does not fabricate completion");
assert.equal(operationFeedback({ ...measured, completed_files: 2, completed_bytes: 6 }).value, null, "processed bytes alone do not mean commit success");
assert.equal(operationFeedback({ ...measured, phase: "complete", status: "complete" }).value, 100);
assert.equal(operationFeedback({ ...measured, total_bytes: 0, completed_bytes: 0 }).value, 50, "empty members use actual file counts");
assert.equal(operationFeedback(null).value, null, "no measured result remains indeterminate");
assert.equal(operationFeedback(null).label.values.total, 2, "selected totals remain available before polling responds");
assert.equal(operationFeedback(null).label.values.bytes, "3 B");
assert.equal(operationFeedback(null, "uploading", 24).value, 24);
assert.equal(operationFeedback(null, "uploading", 100).value, null, "completed transfer is not completed extraction");
assert.equal(page.split('event.dataTransfer.effectAllowed = "copyMove";').length - 1, 2);
assert.doesNotMatch(page, /effectAllowed = "i18n:/, "browser drag/drop enums must not be translated");
for (const name of ["previewManagedArchive", "confirmManagedArchive"]) {
const body = api.slice(api.indexOf(`export function ${name}`)).split("\n}\n", 1)[0];
assert.match(body, /encodeURIComponent\(fileId\)/);
assert.match(body, /body: JSON\.stringify\(/);
assert.doesNotMatch(body, /FormData|downloadFile|Blob|createObjectURL/);
}
assert.match(page, /function resetArchiveUploadState\(\) \{\s*void releaseArchivePreview\(settings, archiveFile\)/, "reset/cancel releases any temporary archive before forgetting it");
assert.match(page, /archiveFile && archiveFile !== file\) void releaseArchivePreview/, "replacing a selected archive releases its previous temporary upload");
for (const key of ["unpack", "preview", "select_one", "source", "expires", "extracting", "change_destination"]) {
assert.equal(translations.split(`"i18n:govoplan-files.managed_archive.${key}":`).length - 1, 2, `${key} must have EN and DE text`);
}
console.log("Managed archive actions reuse the preview dialog and send source-bound JSON without browser re-upload.");