feat: complete TASK-WP4-04 color and dynamic stickers

This commit is contained in:
suyx
2026-08-03 14:11:40 +08:00
parent dd7a23a281
commit 457e1147e4
29 changed files with 2234 additions and 61 deletions
+61
View File
@@ -0,0 +1,61 @@
import { createHash } from "node:crypto";
import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
import { homedir } from "node:os";
import { resolve } from "node:path";
const colorRoot = process.env.DADA_COLOR_ASSET_ROOT ?? resolve(homedir(), "Desktop", "sticker_colour", "单模板归档", "styles");
const dynamicRoot = process.env.DADA_DYNAMIC_ASSET_ROOT ?? resolve(homedir(), "Desktop", "sticker_interactive", "单模板归档", "templates");
const colorIds = ["COLOR001", "COLOR002", "COLOR008", "COLOR016"];
const dynamicIds = ["DYN001", "DYN002", "DYN003", "DYN004", "DYN007", "DYN008", "DYN011", "DYN012", "DYN015", "DYN016"];
const expectedColorStyles = { COLOR001: "style_01", COLOR002: "style_02", COLOR008: "style_08", COLOR016: "style_16" };
const expectedDynamicSources = {
DYN001: "l_POI01", DYN002: "l_POI02", DYN003: "l_POI03", DYN004: "l_POI04", DYN007: "diaoyu",
DYN008: "l_shijian2", DYN011: "l_shijian6", DYN012: "l_shijian7", DYN015: "0721userna", DYN016: "l_username00",
};
const requiredDynamicKeys = {
DYN001: ["title"], DYN002: ["city_en", "title"], DYN003: ["city", "title"], DYN004: ["city", "latitude", "longitude"],
DYN007: ["nickname"], DYN008: ["hour", "minute", "month"],
DYN011: ["day", "month", "year"], DYN012: ["hour", "minute"], DYN015: ["nickname"], DYN016: ["nickname"],
};
const paths = [
...colorIds.map((id) => resolve(colorRoot, id, "metadata.json")),
...dynamicIds.map((id) => resolve(dynamicRoot, id, "metadata.json")),
];
if (!paths.every(existsSync)) throw new Error("WP4-04 normative metadata is unavailable.");
const hash = (bytes) => createHash("sha256").update(bytes).digest("hex").toUpperCase();
const before = paths.map((path) => ({ bytes: readFileSync(path), mtime: statSync(path).mtimeMs }));
const metadata = before.map((entry) => JSON.parse(entry.bytes.toString("utf8")));
for (const id of colorIds) {
const item = metadata.find((entry) => entry.canonical_id === id);
if (!item || item.family !== "color_card" || item.resource_class !== "parameter_renderer"
|| item.runtime?.stable_style_id !== expectedColorStyles[id]) throw new Error(`Color metadata verification failed for ${id}.`);
}
for (const id of dynamicIds) {
const item = metadata.find((entry) => entry.canonical_id === id);
if (!item || item.family !== "interactive_sticker" || item.resource_class !== "dynamic_resource"
|| item.source_candidate_id !== expectedDynamicSources[id] || item.runtime?.has_dynamic_binding !== true
|| !requiredDynamicKeys[id].every((key) => item.dynamic_keys.includes(key))
|| ![...(item.files?.prefab ?? []), ...(item.files?.images ?? []), ...(item.files?.lua ?? [])]
.every((relativePath) => existsSync(resolve(dynamicRoot, id, relativePath)))) {
throw new Error(`Dynamic metadata verification failed for ${id}.`);
}
}
const dyn012 = metadata.find((entry) => entry.canonical_id === "DYN012");
const missingDin = dyn012.runtime.external_runtime_fonts?.some((entry) => entry.manifest_filename === "DIN_MediumAlternate.otf");
if (!missingDin) throw new Error("DYN012 missing-font evidence is unavailable.");
const unchanged = paths.every((path, index) => statSync(path).mtimeMs === before[index].mtime && hash(readFileSync(path)) === hash(before[index].bytes));
if (!unchanged) throw new Error("Normative source changed during read-only verification.");
const result = {
color_ids: colorIds,
color_metadata_sha256: hash(Buffer.concat(before.slice(0, colorIds.length).map((entry) => entry.bytes))),
dyn012_original_font: "DIN_MediumAlternate.otf",
dyn012_substitute_font: "FONT081 Lexend Deca",
dynamic_ids: dynamicIds,
dynamic_metadata_sha256: hash(Buffer.concat(before.slice(colorIds.length).map((entry) => entry.bytes))),
lua_prefab_execution: false,
source_access: "read_only_normative_metadata",
source_unchanged: unchanged,
status: "passed",
};
if (process.env.DADA_WP4_04_SOURCE_EVIDENCE) writeFileSync(process.env.DADA_WP4_04_SOURCE_EVIDENCE, `${JSON.stringify(result, null, 2)}\n`);
console.log(JSON.stringify(result, null, 2));