Files
action-topology-engine/src/metadata.ts
2026-06-16 19:07:20 +08:00

74 lines
2.0 KiB
TypeScript

import { randomUUID } from "node:crypto";
import { ENGINE_VERSION, SCHEMA_VERSION, type GraphMode, type RuntimeContext, type TopologyGraph } from "./types.js";
import { shortHash } from "./hash.js";
const DEFAULT_VIEWPORT = { width: 1440, height: 1000 } as const;
export function defaultViewport(): { width: number; height: number } {
return { ...DEFAULT_VIEWPORT };
}
export function buildRuntimeContext(storageState?: string): RuntimeContext {
return {
browserName: "chromium",
viewport: defaultViewport(),
locale: "en-US",
timezoneId: null,
authContext: storageState ? "storage_state" : "anonymous",
};
}
export function buildEmptyGraph(args: {
inputUrl: string;
entryUrl: string;
mode: GraphMode;
storageState?: string;
}): TopologyGraph {
const generatedAt = new Date().toISOString();
const crawlSessionId = `crawl_${randomUUID()}`;
const inputFingerprint = `input_${shortHash([
args.inputUrl,
args.mode,
"risk-policy-v0.2",
"normalization-v0.2",
"locator-scoring-v0.2",
])}`;
return {
schemaVersion: SCHEMA_VERSION,
metadata: {
artifactVersion: "topology-graph-v0.2",
inputUrl: args.inputUrl,
entryUrl: args.entryUrl,
generatedAt,
engineVersion: ENGINE_VERSION,
mode: args.mode,
crawlSessionId,
inputSeed: args.inputUrl,
inputFingerprint,
runtimeContext: buildRuntimeContext(args.storageState),
site: {
baseUrl: baseUrl(args.entryUrl),
entryUrl: args.entryUrl,
},
riskPolicyVersion: "risk-policy-v0.2",
normalizationRuleVersion: "normalization-v0.2",
locatorScoringVersion: "locator-scoring-v0.2",
},
states: [],
elements: [],
edges: [],
skippedActions: [],
failedObservations: [],
};
}
function baseUrl(value: string): string {
try {
const url = new URL(value);
if (url.protocol === "file:") return url.protocol;
return `${url.protocol}//${url.host}`;
} catch {
return "unknown";
}
}