// Copyright Epic Games, Inc. All Rights Reserved. "use strict"; import { ZenPage } from "./page.js" import { Fetcher } from "../util/fetcher.js" import { Friendly } from "../util/friendly.js" import { Table, PropTable, Toolbar, ProgressBar } from "../util/widgets.js" import { create_indexer } from "../indexer/indexer.js" //////////////////////////////////////////////////////////////////////////////// export class Page extends ZenPage { main() { this.set_title("oplog entry"); const project = this.get_param("project"); const oplog = this.get_param("oplog"); const opkey = this.get_param("opkey"); this._entry = new Fetcher() .resource("prj", project, "oplog", oplog, "entries") .param("opkey", opkey) .cbo(); this._indexer = this.load_indexer(project, oplog); this._build_page(); } async load_indexer(project, oplog, loaded_cb) { const progress_bar = this.add_widget(ProgressBar); progress_bar.set_progress("indexing"); const indexer = await create_indexer(project, oplog, (...args) => { progress_bar.set_progress(...args); }); progress_bar.destroy(); return indexer; } async _build_deps(section, tree) { const indexer = await this._indexer; for (const dep_name in tree) { const dep_section = section.add_section(dep_name); const table = dep_section.add_widget(Table, ["name", "id"], Table.Flag_PackRight); for (const dep_id of tree[dep_name]) { const cell_values = ["", dep_id.toString(16).padStart(16, "0")]; const row = table.add_row(...cell_values); var opkey = indexer.lookup_id(dep_id); row.get_cell(0).text(opkey).on_click((k) => this.view_opkey(k), opkey); } } } _find_iohash_field(container, name) { const found_field = container.find(name); if (found_field != undefined) { var found_value = found_field.as_value(); if (found_value instanceof Uint8Array) { var ret = ""; for (var x of found_value) ret += x.toString(16).padStart(2, "0"); return ret; } } return null; } async _build_meta(section, entry) { var tree = {} for (const field of entry) { var visibleKey = undefined; const name = field.get_name(); if (name == "CookPackageArtifacts") { visibleKey = name; } else if (name.startsWith("meta.")) { visibleKey = name.slice(5); } if (visibleKey != undefined) { var found_value = field.as_value(); if (found_value instanceof Uint8Array) { var ret = ""; for (var x of found_value) ret += x.toString(16).padStart(2, "0"); tree[visibleKey] = ret; } } } if (Object.keys(tree).length == 0) return; const sub_section = section.add_section("meta"); const table = sub_section.add_widget( Table, ["name", "actions"], Table.Flag_PackRight ); for (const key in tree) { const row = table.add_row(key); const value = tree[key]; const project = this.get_param("project"); const oplog = this.get_param("oplog"); const link = row.get_cell(0).link( "/" + ["prj", project, "oplog", oplog, value+".json"].join("/") ); const action_tb = new Toolbar(row.get_cell(-1), true); action_tb.left().add("copy-hash").on_click(async (v) => { await navigator.clipboard.writeText(v); }, value); } } async _build_page() { var entry = await this._entry; entry = entry.as_object().find("entry").as_object(); const name = entry.find("key").as_value(); var section = this.add_section(name); // tree { var tree = entry.find("$tree"); if (tree == undefined) tree = this._convert_legacy_to_tree(entry); if (tree == undefined) return this._display_unsupported(section, entry); delete tree["$id"]; if (Object.keys(tree).length != 0) { const sub_section = section.add_section("deps"); this._build_deps(sub_section, tree); } } // meta { this._build_meta(section, entry); } // data { const sub_section = section.add_section("data"); const table = sub_section.add_widget( Table, ["name", "size", "rawsize", "actions"], Table.Flag_PackRight ); table.id("datatable"); for (const field_name of ["packagedata", "bulkdata"]) { var pkg_data = entry.find(field_name); if (pkg_data == undefined) continue; for (const item of pkg_data.as_array()) { var io_hash, size, raw_size, file_name; for (const field of item.as_object()) { if (field.is_named("data")) io_hash = field.as_value(); else if (field.is_named("filename")) file_name = field.as_value(); else if (field.is_named("size")) size = field.as_value(); else if (field.is_named("rawsize")) raw_size = field.as_value(); } if (io_hash instanceof Uint8Array) { var ret = ""; for (var x of io_hash) ret += x.toString(16).padStart(2, "0"); io_hash = ret; } size = (size !== undefined) ? Friendly.kib(size) : ""; raw_size = (raw_size !== undefined) ? Friendly.kib(raw_size) : ""; const row = table.add_row(file_name, size, raw_size); var base_name = file_name.split("/").pop().split("\\").pop(); const project = this.get_param("project"); const oplog = this.get_param("oplog"); const link = row.get_cell(0).link( "/" + ["prj", project, "oplog", oplog, io_hash].join("/") ); link.first_child().attr("download", `${io_hash}_${base_name}`); const action_tb = new Toolbar(row.get_cell(-1), true); action_tb.left().add("copy-hash").on_click(async (v) => { await navigator.clipboard.writeText(v); }, io_hash); } } } // props { const object = entry.to_js_object(); var sub_section = section.add_section("props"); sub_section.add_widget(PropTable).add_object(object); } } _display_unsupported(section, entry) { const replacer = (key, value) => typeof value === "bigint" ? { $bigint: value.toString() } : value; const object = entry.to_js_object(); const text = JSON.stringify(object, replacer, " "); section.tag("pre").text(text); } _convert_legacy_to_tree(entry) { const pkg_data = entry.find("packagedata"); if (pkg_data == undefined) return const tree = {}; var id = 0n; for (var item of pkg_data.as_array()) { var pkg_id = item.as_object().find("id"); if (pkg_id == undefined) continue; pkg_id = pkg_id.as_value().subarray(0, 8); for (var i = 7; i >= 0; --i) { id <<= 8n; id |= BigInt(pkg_id[i]); } break; } tree["$id"] = id; const pkgst_entry = entry.find("packagestoreentry").as_object(); for (const field of pkgst_entry) { const field_name = field.get_name(); if (!field_name.endsWith("importedpackageids")) continue; var dep_name = field_name.slice(0, -18); if (dep_name.length == 0) dep_name = "imported"; var out = tree[dep_name] = []; for (var item of field.as_array()) out.push(item.as_value(BigInt)); } return tree; } view_opkey(opkey) { const params = this._params; params.set("opkey", opkey); window.location.search = params; } }