{"version":3,"file":"dump/report-action-dump.mjs","sources":["../../../src/dump/report-action-dump.ts"],"sourcesContent":["import {\n  existsSync,\n  mkdirSync,\n  readFileSync,\n  rmSync,\n  writeFileSync,\n} from 'node:fs';\nimport { join } from 'node:path';\nimport { ScreenshotItem } from '../screenshot-item';\nimport type {\n  ExecutionTask,\n  IExecutionDump,\n  IReportActionDump,\n} from '../types';\nimport { restoreImageReferences } from './screenshot-restoration';\nimport { ScreenshotStore } from './screenshot-store';\n\n/**\n * Replacer function for JSON serialization that handles Page, Browser objects and ScreenshotItem\n */\nfunction replacerForDumpSerialization(_key: string, value: any): any {\n  if (value && value.constructor?.name === 'Page') {\n    return '[Page object]';\n  }\n  if (value && value.constructor?.name === 'Browser') {\n    return '[Browser object]';\n  }\n  // Handle ScreenshotItem serialization\n  if (value && typeof value.toSerializable === 'function') {\n    return value.toSerializable();\n  }\n  return value;\n}\n\n/**\n * Reviver function for JSON deserialization that keeps screenshot references\n * as plain objects. Resolution is handled lazily by restoreImageReferences.\n *\n * @param key - JSON key being processed\n * @param value - JSON value being processed\n * @returns Restored value\n */\nfunction reviverForDumpDeserialization(key: string, value: any): any {\n  // Only process screenshot fields\n  if (key !== 'screenshot' || typeof value !== 'object' || value === null) {\n    return value;\n  }\n\n  if (ScreenshotItem.isSerialized(value)) {\n    return value;\n  }\n\n  return value;\n}\n\n/**\n * ExecutionDump class for serializing and deserializing execution dumps\n */\nexport class ExecutionDump implements IExecutionDump {\n  id?: string;\n  logTime: number;\n  name: string;\n  description?: string;\n  tasks: ExecutionTask[];\n  aiActContext?: string;\n\n  constructor(data: IExecutionDump) {\n    this.id = data.id;\n    this.logTime = data.logTime;\n    this.name = data.name;\n    this.description = data.description;\n    this.tasks = data.tasks;\n    this.aiActContext = data.aiActContext;\n  }\n\n  /**\n   * Serialize the ExecutionDump to a JSON string\n   */\n  serialize(indents?: number): string {\n    return JSON.stringify(this.toJSON(), replacerForDumpSerialization, indents);\n  }\n\n  /**\n   * Convert to a plain object for JSON serialization\n   */\n  toJSON(): IExecutionDump {\n    return {\n      id: this.id,\n      logTime: this.logTime,\n      name: this.name,\n      description: this.description,\n      tasks: this.tasks.map((task) => ({\n        ...task,\n        recorder: task.recorder || [],\n      })),\n      aiActContext: this.aiActContext,\n    };\n  }\n\n  /**\n   * Create an ExecutionDump instance from a serialized JSON string\n   */\n  static fromSerializedString(serialized: string): ExecutionDump {\n    const parsed = JSON.parse(\n      serialized,\n      reviverForDumpDeserialization,\n    ) as IExecutionDump;\n    return new ExecutionDump(parsed);\n  }\n\n  /**\n   * Create an ExecutionDump instance from a plain object\n   */\n  static fromJSON(data: IExecutionDump): ExecutionDump {\n    return new ExecutionDump(data);\n  }\n\n  /**\n   * Collect all ScreenshotItem instances from tasks.\n   * Scans through uiContext and recorder items to find screenshots.\n   *\n   * @returns Array of ScreenshotItem instances\n   */\n  collectScreenshots(): ScreenshotItem[] {\n    const screenshots: ScreenshotItem[] = [];\n\n    for (const task of this.tasks) {\n      // Collect uiContext.screenshot if present\n      if (task.uiContext?.screenshot instanceof ScreenshotItem) {\n        screenshots.push(task.uiContext.screenshot);\n      }\n\n      // Collect recorder screenshots\n      if (task.recorder) {\n        for (const record of task.recorder) {\n          if (record.screenshot instanceof ScreenshotItem) {\n            screenshots.push(record.screenshot);\n          }\n        }\n      }\n    }\n\n    return screenshots;\n  }\n}\n\n/**\n * ReportActionDump class for serializing and deserializing report action dumps\n */\nexport class ReportActionDump implements IReportActionDump {\n  sdkVersion: string;\n  groupName: string;\n  groupDescription?: string;\n  modelBriefs: IReportActionDump['modelBriefs'];\n  executions: ExecutionDump[];\n  deviceType?: string;\n\n  constructor(data: IReportActionDump) {\n    this.sdkVersion = data.sdkVersion;\n    this.groupName = data.groupName;\n    this.groupDescription = data.groupDescription;\n    this.modelBriefs = data.modelBriefs;\n    this.executions = data.executions.map((exec) =>\n      exec instanceof ExecutionDump ? exec : ExecutionDump.fromJSON(exec),\n    );\n    this.deviceType = data.deviceType;\n  }\n\n  /**\n   * Serialize the ReportActionDump to a JSON string\n   * Uses compact { $screenshot: id } format\n   */\n  serialize(indents?: number): string {\n    return JSON.stringify(this.toJSON(), replacerForDumpSerialization, indents);\n  }\n\n  /**\n   * Serialize the ReportActionDump with inline screenshots to a JSON string.\n   * Each ScreenshotItem is replaced with { base64: \"...\", capturedAt }.\n   */\n  serializeWithInlineScreenshots(indents?: number): string {\n    const processValue = (obj: unknown): unknown => {\n      if (obj instanceof ScreenshotItem) {\n        return { base64: obj.base64, capturedAt: obj.capturedAt };\n      }\n      if (Array.isArray(obj)) {\n        return obj.map(processValue);\n      }\n      if (obj && typeof obj === 'object') {\n        const entries = Object.entries(obj).map(([key, value]) => [\n          key,\n          processValue(value),\n        ]);\n        return Object.fromEntries(entries);\n      }\n      return obj;\n    };\n\n    const data = processValue(this.toJSON());\n    return JSON.stringify(data, null, indents);\n  }\n\n  /**\n   * Convert to a plain object for JSON serialization\n   */\n  toJSON(): IReportActionDump {\n    return {\n      sdkVersion: this.sdkVersion,\n      groupName: this.groupName,\n      groupDescription: this.groupDescription,\n      modelBriefs: this.modelBriefs,\n      executions: this.executions.map((exec) => exec.toJSON()),\n      deviceType: this.deviceType,\n    };\n  }\n\n  /**\n   * Create a ReportActionDump instance from a serialized JSON string\n   */\n  static fromSerializedString(serialized: string): ReportActionDump {\n    const parsed = JSON.parse(\n      serialized,\n      reviverForDumpDeserialization,\n    ) as IReportActionDump;\n    return new ReportActionDump(parsed);\n  }\n\n  /**\n   * Create a ReportActionDump instance from a plain object\n   */\n  static fromJSON(data: IReportActionDump): ReportActionDump {\n    return new ReportActionDump(data);\n  }\n\n  /**\n   * Collect all ScreenshotItem instances from all executions.\n   *\n   * @returns Array of all ScreenshotItem instances across all executions\n   */\n  collectAllScreenshots(): ScreenshotItem[] {\n    const screenshots: ScreenshotItem[] = [];\n    for (const execution of this.executions) {\n      screenshots.push(...execution.collectScreenshots());\n    }\n    return screenshots;\n  }\n\n  /**\n   * Serialize the dump to files with screenshots as separate PNG files.\n   * Creates:\n   * - {basePath} - dump JSON with { $screenshot: id } references\n   * - {basePath}.screenshots/ - PNG files\n   *\n   * @param basePath - Base path for the dump file\n   */\n  serializeToFiles(basePath: string): void {\n    const screenshotsDir = `${basePath}.screenshots`;\n    if (!existsSync(screenshotsDir)) {\n      mkdirSync(screenshotsDir, { recursive: true });\n    }\n\n    const screenshots = this.collectAllScreenshots();\n\n    for (const screenshot of screenshots) {\n      const imagePath = join(\n        screenshotsDir,\n        `${screenshot.id}.${screenshot.extension}`,\n      );\n      if (existsSync(imagePath)) {\n        continue;\n      }\n\n      const rawBase64 = screenshot.rawBase64;\n      writeFileSync(imagePath, Buffer.from(rawBase64, 'base64'));\n    }\n\n    // Write dump JSON with references\n    writeFileSync(basePath, this.serialize(), 'utf-8');\n  }\n\n  /**\n   * Read dump from files and return JSON string with inline screenshots.\n   * Reads the dump JSON and screenshot files, then inlines the base64 data.\n   *\n   * @param basePath - Base path for the dump file\n   * @returns JSON string with inline screenshots ({ base64: \"...\" } format)\n   */\n  static fromFilesAsInlineJson(basePath: string): string {\n    const dumpString = readFileSync(basePath, 'utf-8');\n    const screenshotsDir = `${basePath}.screenshots`;\n\n    const loadFromExecutionScreenshotDir = (id: string, mimeType: string) => {\n      const ext = mimeType === 'image/jpeg' ? 'jpeg' : 'png';\n      const filePath = join(screenshotsDir, `${id}.${ext}`);\n      if (!existsSync(filePath)) {\n        return '';\n      }\n      const data = readFileSync(filePath);\n      return `data:image/${ext};base64,${data.toString('base64')}`;\n    };\n\n    // Restore image references\n    const dumpData = JSON.parse(dumpString);\n    const store = new ScreenshotStore({\n      mode: 'directory',\n      reportPath: basePath,\n    });\n    const processedData = restoreImageReferences(dumpData, (ref) => {\n      const executionFileImage = loadFromExecutionScreenshotDir(\n        ref.id,\n        ref.mimeType,\n      );\n      if (executionFileImage) {\n        return executionFileImage;\n      }\n\n      if (ref.storage === 'inline') {\n        return '';\n      }\n      return store.loadBase64(ref);\n    });\n    return JSON.stringify(processedData);\n  }\n\n  /**\n   * Clean up all files associated with a serialized dump.\n   *\n   * @param basePath - Base path for the dump file\n   */\n  static cleanupFiles(basePath: string): void {\n    const filesToClean = [basePath, `${basePath}.screenshots`];\n\n    for (const filePath of filesToClean) {\n      try {\n        rmSync(filePath, { force: true, recursive: true });\n      } catch {\n        // Ignore errors - file may already be deleted\n      }\n    }\n  }\n\n  /**\n   * Get all file paths associated with a serialized dump.\n   *\n   * @param basePath - Base path for the dump file\n   * @returns Array of all associated file paths\n   */\n  static getFilePaths(basePath: string): string[] {\n    return [basePath, `${basePath}.screenshots`];\n  }\n}\n\n// Backward-compatible aliases for existing external consumers.\nexport type GroupedActionDump = ReportActionDump;\nexport const GroupedActionDump = ReportActionDump;\n"],"names":["replacerForDumpSerialization","_key","value","reviverForDumpDeserialization","key","ScreenshotItem","ExecutionDump","indents","JSON","task","serialized","parsed","data","screenshots","record","ReportActionDump","processValue","obj","Array","entries","Object","exec","execution","basePath","screenshotsDir","existsSync","mkdirSync","screenshot","imagePath","join","rawBase64","writeFileSync","Buffer","dumpString","readFileSync","loadFromExecutionScreenshotDir","id","mimeType","ext","filePath","dumpData","store","ScreenshotStore","processedData","restoreImageReferences","ref","executionFileImage","filesToClean","rmSync","GroupedActionDump"],"mappings":"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"}