Implement AXIS parity UI and execution baseline
This commit is contained in:
@@ -190,21 +190,28 @@ export function applyRollbackPackage(rollback, { currentPackageHash, failureInje
|
||||
return clone(target);
|
||||
}
|
||||
|
||||
function normalizeResources(resources) {
|
||||
function normalizeResources(resources, { maxResources = 256, maxBytes = 64 * 1024 * 1024 } = {}) {
|
||||
if (!Array.isArray(resources) || resources.length === 0) throw new Error("offline_resources_empty");
|
||||
if (resources.length > maxResources) throw new Error("offline_resource_capacity");
|
||||
const seen = new Set();
|
||||
return resources.map((resource) => {
|
||||
let totalBytes = 0;
|
||||
const normalized = resources.map((resource) => {
|
||||
requireString(resource?.path, "offline_resource_path");
|
||||
if (resource.path.startsWith("/") || resource.path.includes("\\") || resource.path.split("/").includes("..")) throw new Error("offline_resource_path");
|
||||
if (seen.has(resource.path)) throw new Error("offline_resource_duplicate");
|
||||
seen.add(resource.path);
|
||||
if (typeof resource.data !== "string") throw new Error("offline_resource_data_invalid");
|
||||
return { path: resource.path, bytes: new TextEncoder().encode(resource.data).byteLength, data_hash: deterministicHash(resource.data) };
|
||||
const bytes = new TextEncoder().encode(resource.data).byteLength;
|
||||
totalBytes += bytes;
|
||||
if (totalBytes > maxBytes) throw new Error("offline_resource_quota");
|
||||
return { path: resource.path, bytes, data_hash: deterministicHash(resource.data) };
|
||||
}).sort((left, right) => left.path.localeCompare(right.path));
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export function createOfflinePackage({ workspacePackage, resources } = {}) {
|
||||
export function createOfflinePackage({ workspacePackage, resources, maxResources = 256, maxBytes = 64 * 1024 * 1024 } = {}) {
|
||||
const workspace = validatePackageShape(workspacePackage);
|
||||
const normalized = normalizeResources(resources);
|
||||
const normalized = normalizeResources(resources, { maxResources, maxBytes });
|
||||
const bundle = {
|
||||
schema: OFFLINE_SCHEMA,
|
||||
format_version: 1,
|
||||
@@ -221,7 +228,15 @@ export function verifyOfflinePackage(bundle, { contextId, workspacePackage = nul
|
||||
requireString(bundle.context_id, "offline_context");
|
||||
if (contextId !== undefined && bundle.context_id !== contextId) throw new Error("offline_context_mismatch");
|
||||
if (workspacePackage !== null && verifyWorkspacePackage(workspacePackage, { contextId: bundle.context_id }).package_hash !== bundle.workspace_package_hash) throw new Error("offline_workspace_hash_mismatch");
|
||||
if (!Array.isArray(bundle.resources) || !Array.isArray(bundle.required_paths) || canonicalJson(bundle.required_paths) !== canonicalJson(bundle.resources.map((resource) => resource.path))) throw new Error("offline_manifest_invalid");
|
||||
if (!Array.isArray(bundle.resources) || !Array.isArray(bundle.required_paths) || bundle.resources.length > 256 || canonicalJson(bundle.required_paths) !== canonicalJson(bundle.resources.map((resource) => resource.path))) throw new Error("offline_manifest_invalid");
|
||||
const resourcePaths = new Set();
|
||||
let resourceBytes = 0;
|
||||
for (const resource of bundle.resources) {
|
||||
requireString(resource?.path, "offline_resource_path");
|
||||
if (resource.path.startsWith("/") || resource.path.includes("\\") || resource.path.split("/").includes("..") || resourcePaths.has(resource.path) || !Number.isSafeInteger(resource.bytes) || resource.bytes < 0 || typeof resource.data_hash !== "string") throw new Error("offline_manifest_invalid");
|
||||
resourcePaths.add(resource.path); resourceBytes += resource.bytes;
|
||||
if (resourceBytes > 64 * 1024 * 1024) throw new Error("offline_manifest_quota");
|
||||
}
|
||||
if (typeof bundle.bundle_hash !== "string" || bundle.bundle_hash !== deterministicHash({ schema: bundle.schema, format_version: bundle.format_version, context_id: bundle.context_id, workspace_package_hash: bundle.workspace_package_hash, resources: bundle.resources, required_paths: bundle.required_paths })) throw new Error("offline_bundle_hash_mismatch");
|
||||
if (!Array.isArray(available)) throw new Error("offline_available_invalid");
|
||||
const availableMap = new Map(available.map((entry) => {
|
||||
|
||||
Reference in New Issue
Block a user