{"version":3,"file":"report.mjs","sources":["../../src/report.ts"],"sourcesContent":["import {\n  appendFileSync,\n  copyFileSync,\n  existsSync,\n  mkdirSync,\n  readdirSync,\n  rmSync,\n  unlinkSync,\n  writeFileSync,\n} from 'node:fs';\nimport * as path from 'node:path';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport { antiEscapeScriptTag, logMsg } from '@midscene/shared/utils';\nimport { getReportFileName } from './agent';\nimport {\n  DATA_SCREENSHOT_MODE_ATTR,\n  extractAllDumpScriptsSync,\n  extractLastDumpScriptSync,\n  generateAgentReportComment,\n  getBaseUrlFixScript,\n  streamDumpScriptsSync,\n  streamImageScriptsToFile,\n} from './dump/html-utils';\nimport {\n  normalizeScreenshotRef,\n  resolveScreenshotSource,\n} from './dump/screenshot-store';\nimport {\n  type ExecutionDump,\n  type IExecutionDump,\n  ReportActionDump,\n  type ScreenshotMode,\n} from './types';\nimport type { ReportFileWithAttributes } from './types';\nimport { getReportTpl, getVersion, reportHTMLContent } from './utils';\n\n/**\n * Read the screenshot storage mode a report declared at generation time.\n *\n * Reports written by current versions stamp `data-screenshot-mode` onto every\n * dump script tag, so we only need to peek at the first real dump script (the\n * template's bundled JS also references the dump type string, hence the\n * `data-group-id` filter) and can stop streaming immediately.\n *\n * Returns undefined for legacy reports that predate the attribute or for\n * unreadable files, letting the caller fall back to a filesystem heuristic.\n */\nconst screenshotModeAttrRegExp = new RegExp(\n  `${DATA_SCREENSHOT_MODE_ATTR}=\"(inline|directory)\"`,\n);\n\nfunction readDeclaredScreenshotMode(\n  reportFilePath: string,\n): ScreenshotMode | undefined {\n  let mode: ScreenshotMode | undefined;\n  try {\n    streamDumpScriptsSync(reportFilePath, ({ openTag }) => {\n      // Skip false matches from the template's bundled JS code.\n      if (!openTag.includes('data-group-id')) return false;\n      const match = openTag.match(screenshotModeAttrRegExp);\n      mode = match?.[1] as ScreenshotMode | undefined;\n      return true; // the first real dump script decides the mode\n    });\n  } catch {\n    // Unreadable / non-existent file — let the caller fall back.\n  }\n  return mode;\n}\n\n/**\n * Check if a report is in directory mode (html-and-external-assets).\n * Directory mode reports: {name}/index.html + {name}/screenshots/\n *\n * The mode is read from the report's own `data-screenshot-mode` metadata, which\n * is authoritative regardless of whether the run happened to capture any\n * screenshots. For legacy reports without the attribute we fall back to the old\n * filesystem heuristic (an `index.html` that has a sibling `screenshots/` dir).\n */\nexport function isDirectoryModeReport(reportFilePath: string): boolean {\n  // Directory-mode reports are always written as `{name}/index.html`, so any\n  // other filename is inline. Short-circuit before touching the file so the\n  // common inline case (`{name}.html`) costs nothing.\n  if (path.basename(reportFilePath) !== 'index.html') return false;\n\n  const declared = readDeclaredScreenshotMode(reportFilePath);\n  if (declared) return declared === 'directory';\n\n  // Legacy fallback for reports generated before screenshotMode was embedded.\n  return existsSync(path.join(path.dirname(reportFilePath), 'screenshots'));\n}\n\n/**\n * Whether a report lives in its own dedicated directory (`{name}/index.html`)\n * rather than being a single standalone file (`{name}.html`).\n *\n * This is a structural fact about *where the report file sits*, independent of\n * how it stores screenshots: a directory report can keep screenshots external\n * (a `screenshots/` sibling) OR inline them into `index.html`. Deletion needs\n * this — not the screenshot mode — to decide whether to remove the whole\n * directory or just unlink a file, so that an inline-screenshot report nested in\n * its own folder still has the folder removed instead of being orphaned.\n */\nfunction isDirectoryBasedReport(reportFilePath: string): boolean {\n  return path.basename(reportFilePath) === 'index.html';\n}\n\n/**\n * Deduplicate executions by stable id, keeping only the last occurrence.\n * Old-format executions without id are always preserved.\n */\nexport function dedupeExecutionsKeepLatest<T extends Pick<ExecutionDump, 'id'>>(\n  executions: T[],\n): T[] {\n  let noIdCounter = 0;\n  const deduped = new Map<string, T>();\n  for (const exec of executions) {\n    const key = exec.id || `__no_id_${noIdCounter++}`;\n    deduped.set(key, exec);\n  }\n  return Array.from(deduped.values());\n}\n/**\n * Peek at the first `sdkVersion` field embedded in a midscene_web_dump\n * script tag inside the given report file. Returns undefined if no\n * recognizable tag or sdkVersion is present.\n */\nfunction peekReportSdkVersion(reportFilePath: string): string | undefined {\n  try {\n    const dump = extractLastDumpScriptSync(reportFilePath);\n    if (!dump) return undefined;\n    const match = dump.match(/\"sdkVersion\"\\s*:\\s*\"([^\"]+)\"/);\n    return match?.[1];\n  } catch {\n    return undefined;\n  }\n}\n\nconst warnedMismatchedVersions = new Set<string>();\n\nfunction tryParseAgentReportDump(dumpString: string): ReportActionDump | null {\n  const trimmed = dumpString.trimStart();\n  if (!trimmed.startsWith('{') || !trimmed.includes('\"executions\"')) {\n    return null;\n  }\n\n  try {\n    return ReportActionDump.fromSerializedString(trimmed);\n  } catch {\n    return null;\n  }\n}\n\nfunction mergedAgentReportComment(reports: ReportActionDump[]): string {\n  if (reports.length === 0) {\n    return '';\n  }\n  if (reports.length === 1) {\n    return generateAgentReportComment(reports[0]);\n  }\n\n  const deviceTypes = Array.from(\n    new Set(reports.map((report) => report.deviceType).filter(Boolean)),\n  );\n  const mergedReport = new ReportActionDump({\n    sdkVersion: reports[0].sdkVersion || getVersion(),\n    groupName: 'Merged Midscene Report',\n    groupDescription: 'Agent-readable summary for merged report HTML',\n    modelBriefs: reports.flatMap((report) => report.modelBriefs ?? []),\n    deviceType: deviceTypes.length === 1 ? deviceTypes[0] : 'mixed',\n    executions: reports.flatMap((report) => report.executions ?? []),\n  });\n  return generateAgentReportComment(mergedReport);\n}\n\nexport class ReportMergingTool {\n  private reportInfos: ReportFileWithAttributes[] = [];\n\n  private createEmptyDumpString(groupName: string, groupDescription?: string) {\n    return new ReportActionDump({\n      sdkVersion: '',\n      groupName,\n      groupDescription,\n      modelBriefs: [],\n      executions: [],\n    }).serialize();\n  }\n\n  public append(reportInfo: ReportFileWithAttributes) {\n    if (reportInfo.reportFilePath) {\n      const sourceVersion = peekReportSdkVersion(reportInfo.reportFilePath);\n      const currentVersion = getVersion();\n      if (\n        sourceVersion &&\n        currentVersion &&\n        sourceVersion !== currentVersion &&\n        !warnedMismatchedVersions.has(sourceVersion)\n      ) {\n        warnedMismatchedVersions.add(sourceVersion);\n        logMsg(\n          `[@midscene/core] ReportMergingTool version mismatch: source report was written by @midscene/core@${sourceVersion} but the merger is @midscene/core@${currentVersion}. This commonly means @midscene/core and the device package (e.g. @midscene/android) resolve to different versions in node_modules. Merged output may silently drop intermediate steps. Align the versions and reinstall (rm -rf node_modules package-lock.json && npm install).`,\n        );\n      }\n    }\n    this.reportInfos.push(reportInfo);\n  }\n  public clear() {\n    this.reportInfos = [];\n  }\n\n  /**\n   * Merge multiple dump script contents (from the same source report)\n   * into a single serialized ReportActionDump string.\n   * If there's only one dump, return it as-is. If multiple, merge\n   * all executions into the first dump's group structure.\n   */\n  private mergeDumpScripts(contents: string[]): string {\n    const unescaped = contents\n      .map((c) => antiEscapeScriptTag(c))\n      .filter((c) => c.length > 0);\n    if (unescaped.length === 0) return '';\n    if (unescaped.length === 1) return unescaped[0];\n\n    // Parse all dumps and collect executions, deduplicating by id (keep last).\n    // Only executions with a stable id are deduped; old-format entries without\n    // id are always kept (they may be distinct despite sharing the same name).\n    const base = ReportActionDump.fromSerializedString(unescaped[0]);\n    const allExecutions = [...base.executions];\n    for (let i = 1; i < unescaped.length; i++) {\n      const other = ReportActionDump.fromSerializedString(unescaped[i]);\n      allExecutions.push(...other.executions);\n    }\n    base.executions = dedupeExecutionsKeepLatest(allExecutions);\n    return base.serialize();\n  }\n\n  public mergeReports(\n    reportFileName: 'AUTO' | string = 'AUTO',\n    opts?: {\n      rmOriginalReports?: boolean;\n      overwrite?: boolean;\n      outputDir?: string;\n    },\n  ): string | null {\n    const {\n      rmOriginalReports = false,\n      overwrite = false,\n      outputDir,\n    } = opts ?? {};\n\n    if (this.reportInfos.length === 0) {\n      logMsg('No reports to merge');\n      return null;\n    }\n\n    const targetDir = outputDir\n      ? path.resolve(outputDir)\n      : getMidsceneRunSubDir('report');\n    if (outputDir) {\n      mkdirSync(targetDir, { recursive: true });\n    }\n\n    // Resolve each report's screenshot mode exactly once. isDirectoryModeReport\n    // reads the file to find the authoritative metadata, so recomputing it for\n    // both the output-path decision and the per-report merge loop would re-scan\n    // every report twice.\n    const isDirModeByIndex = this.reportInfos.map((info) =>\n      Boolean(\n        info.reportFilePath && isDirectoryModeReport(info.reportFilePath),\n      ),\n    );\n    const hasDirectoryModeReport = isDirModeByIndex.some(Boolean);\n\n    const resolvedName =\n      reportFileName === 'AUTO'\n        ? getReportFileName('merged-report')\n        : reportFileName;\n\n    // Directory mode: output as {name}/index.html to keep relative paths working\n    // Inline mode: output as {name}.html (single file)\n    const outputFilePath = hasDirectoryModeReport\n      ? path.resolve(targetDir, resolvedName, 'index.html')\n      : path.resolve(targetDir, `${resolvedName}.html`);\n\n    if (reportFileName !== 'AUTO' && existsSync(outputFilePath)) {\n      if (!overwrite) {\n        throw new Error(\n          `Report file already exists: ${outputFilePath}\\nSet overwrite to true to overwrite this file.`,\n        );\n      }\n      if (hasDirectoryModeReport) {\n        rmSync(path.dirname(outputFilePath), { recursive: true, force: true });\n      } else {\n        unlinkSync(outputFilePath);\n      }\n    }\n\n    if (hasDirectoryModeReport) {\n      mkdirSync(path.dirname(outputFilePath), { recursive: true });\n    }\n\n    logMsg(\n      `Start merging ${this.reportInfos.length} reports...\\nCreating template file...`,\n    );\n\n    try {\n      // Write template without closing </html> tag so we can append\n      // dump scripts before it. The closing tag is added at the end.\n      const htmlEndTag = '</html>';\n      const tpl = getReportTpl();\n      const htmlEndIdx = tpl.lastIndexOf(htmlEndTag);\n      const tplWithoutClose =\n        htmlEndIdx !== -1 ? tpl.slice(0, htmlEndIdx) : tpl;\n      appendFileSync(outputFilePath, tplWithoutClose);\n\n      // For directory-mode output, inject base URL fix script\n      if (hasDirectoryModeReport) {\n        appendFileSync(outputFilePath, getBaseUrlFixScript());\n      }\n\n      const agentReports: ReportActionDump[] = [];\n\n      // Process all reports one by one\n      for (let i = 0; i < this.reportInfos.length; i++) {\n        const reportInfo = this.reportInfos[i];\n        logMsg(`Processing report ${i + 1}/${this.reportInfos.length}`);\n\n        const { reportAttributes } = reportInfo;\n        let dumpString = this.createEmptyDumpString(\n          reportAttributes.testTitle,\n          reportAttributes.testDescription,\n        );\n        let mergedGroupId = `merged-group-${i}`;\n\n        if (reportInfo.reportFilePath) {\n          if (isDirModeByIndex[i]) {\n            // Directory mode: copy external screenshot files. A directory-mode\n            // report can legitimately have no screenshots/ dir (a run that\n            // captured nothing), so only copy when the source dir exists.\n            const reportDir = path.dirname(reportInfo.reportFilePath);\n            const screenshotsDir = path.join(reportDir, 'screenshots');\n            if (existsSync(screenshotsDir)) {\n              const mergedScreenshotsDir = path.join(\n                path.dirname(outputFilePath),\n                'screenshots',\n              );\n              mkdirSync(mergedScreenshotsDir, { recursive: true });\n              for (const file of readdirSync(screenshotsDir)) {\n                const src = path.join(screenshotsDir, file);\n                const dest = path.join(mergedScreenshotsDir, file);\n                copyFileSync(src, dest);\n              }\n            }\n          } else {\n            // Inline mode: stream image scripts to output file\n            streamImageScriptsToFile(reportInfo.reportFilePath, outputFilePath);\n          }\n\n          // Extract all dump scripts from the source report.\n          // After the per-execution append refactor, a single source report\n          // may contain multiple <script type=\"midscene_web_dump\"> tags\n          // (one per execution). We merge them into a single ReportActionDump.\n          // Filter by data-group-id to exclude false matches from the template's\n          // bundled JS code, which also references the midscene_web_dump type string.\n          const allDumps = extractAllDumpScriptsSync(\n            reportInfo.reportFilePath,\n          ).filter((d) => d.openTag.includes('data-group-id'));\n          const groupIdMatch = allDumps[0]?.openTag.match(\n            /data-group-id=\"([^\"]+)\"/,\n          );\n          if (groupIdMatch) {\n            mergedGroupId = decodeURIComponent(groupIdMatch[1]);\n          }\n          const extractedDumpString =\n            allDumps.length > 0\n              ? this.mergeDumpScripts(allDumps.map((d) => d.content))\n              : extractLastDumpScriptSync(reportInfo.reportFilePath);\n          if (extractedDumpString) {\n            dumpString = extractedDumpString;\n          }\n        }\n\n        const agentReport = tryParseAgentReportDump(dumpString);\n        if (agentReport) {\n          agentReports.push(agentReport);\n        }\n\n        const reportHtmlStr = `${reportHTMLContent(\n          {\n            dumpString,\n            attributes: {\n              'data-group-id': mergedGroupId,\n              [DATA_SCREENSHOT_MODE_ATTR]: hasDirectoryModeReport\n                ? 'directory'\n                : 'inline',\n              playwright_test_duration: reportAttributes.testDuration,\n              playwright_test_status: reportAttributes.testStatus,\n              playwright_test_title: reportAttributes.testTitle,\n              playwright_test_id: reportAttributes.testId,\n              playwright_test_description: reportAttributes.testDescription,\n              is_merged: true,\n            },\n          },\n          undefined,\n          undefined,\n          false,\n        )}\\n`;\n\n        appendFileSync(outputFilePath, reportHtmlStr);\n      }\n\n      const agentComment = mergedAgentReportComment(agentReports);\n      if (agentComment) {\n        appendFileSync(outputFilePath, agentComment);\n      }\n\n      // Close the HTML document\n      appendFileSync(outputFilePath, `${htmlEndTag}\\n`);\n\n      logMsg(`Successfully merged new report: ${outputFilePath}`);\n\n      // Remove original reports if needed\n      if (rmOriginalReports) {\n        for (const info of this.reportInfos) {\n          if (!info.reportFilePath) continue;\n          try {\n            if (isDirectoryBasedReport(info.reportFilePath)) {\n              // The report owns its directory (`{name}/index.html`) — remove the\n              // whole folder, whether screenshots are external or inlined.\n              const reportDir = path.dirname(info.reportFilePath);\n              rmSync(reportDir, { recursive: true, force: true });\n            } else {\n              unlinkSync(info.reportFilePath);\n            }\n          } catch (error) {\n            logMsg(`Error deleting report ${info.reportFilePath}: ${error}`);\n          }\n        }\n        logMsg(`Removed ${this.reportInfos.length} original reports`);\n      }\n      return outputFilePath;\n    } catch (error) {\n      logMsg(`Error in mergeReports: ${error}`);\n      throw error;\n    }\n  }\n}\n\nexport interface SplitReportHtmlOptions {\n  htmlPath: string;\n  outputDir: string;\n}\n\nexport interface SplitReportHtmlResult {\n  executionJsonFiles: string[];\n  screenshotFiles: string[];\n}\n\nexport interface CollectedReportExecutions {\n  baseDump: ReportActionDump;\n  executions: IExecutionDump[];\n}\n\n/**\n * Collect executions from a report HTML, deduplicating by stable id while\n * keeping only the latest occurrence. Old-format executions without id are\n * always preserved.\n */\nexport function collectDedupedExecutions(\n  htmlPath: string,\n): CollectedReportExecutions {\n  let baseDump: ReportActionDump | null = null;\n  let executionSerial = 0;\n  const latestSerialByExecutionId = new Map<string, number>();\n\n  streamDumpScriptsSync(htmlPath, (dumpScript) => {\n    if (!dumpScript.openTag.includes('data-group-id')) {\n      return false;\n    }\n    const groupedDump = ReportActionDump.fromSerializedString(\n      antiEscapeScriptTag(dumpScript.content),\n    );\n    for (const execution of groupedDump.executions) {\n      executionSerial += 1;\n      if (execution.id) {\n        latestSerialByExecutionId.set(execution.id, executionSerial);\n      }\n    }\n    return false;\n  });\n\n  const executions: IExecutionDump[] = [];\n  executionSerial = 0;\n  streamDumpScriptsSync(htmlPath, (dumpScript) => {\n    if (!dumpScript.openTag.includes('data-group-id')) {\n      return false;\n    }\n\n    const groupedDump = ReportActionDump.fromSerializedString(\n      antiEscapeScriptTag(dumpScript.content),\n    );\n    if (!baseDump) {\n      baseDump = groupedDump;\n    }\n\n    for (const execution of groupedDump.executions) {\n      executionSerial += 1;\n      if (\n        execution.id &&\n        latestSerialByExecutionId.get(execution.id) !== executionSerial\n      ) {\n        continue;\n      }\n      executions.push(execution);\n    }\n\n    return false;\n  });\n\n  if (!baseDump) {\n    throw new Error(`No report dump scripts found in ${htmlPath}`);\n  }\n\n  return {\n    baseDump,\n    executions,\n  };\n}\n\nfunction extensionByMimeType(mimeType: string): 'png' | 'jpeg' {\n  if (mimeType === 'image/png') return 'png';\n  if (mimeType === 'image/jpeg') return 'jpeg';\n  throw new Error(`Unsupported screenshot mime type: ${mimeType}`);\n}\n\nfunction externalizeScreenshotsInExecution(\n  execution: IExecutionDump,\n  opts: {\n    htmlPath: string;\n    screenshotsDir: string;\n    writtenFiles: Set<string>;\n  },\n): void {\n  const visit = (node: unknown): void => {\n    if (Array.isArray(node)) {\n      for (const item of node) {\n        visit(item);\n      }\n      return;\n    }\n\n    if (typeof node !== 'object' || node === null) return;\n\n    const ref = normalizeScreenshotRef(node);\n    if (ref) {\n      const ext = extensionByMimeType(ref.mimeType);\n      const fileName = `${ref.id}.${ext}`;\n      const relativePath = `./screenshots/${fileName}`;\n      const absolutePath = path.join(opts.screenshotsDir, fileName);\n\n      if (!opts.writtenFiles.has(fileName)) {\n        const resolved = resolveScreenshotSource(ref, {\n          reportPath: opts.htmlPath,\n        });\n        if (resolved.type === 'data-uri') {\n          const rawBase64 = resolved.dataUri.replace(\n            /^data:image\\/[a-zA-Z+]+;base64,/,\n            '',\n          );\n          writeFileSync(absolutePath, Buffer.from(rawBase64, 'base64'));\n        } else {\n          copyFileSync(resolved.filePath, absolutePath);\n        }\n        opts.writtenFiles.add(fileName);\n      }\n\n      ref.storage = 'file';\n      ref.path = relativePath;\n      return;\n    }\n\n    for (const value of Object.values(node)) {\n      visit(value);\n    }\n  };\n\n  visit(execution);\n}\n\n/**\n * Reverse parse a Midscene report HTML into per-execution JSON files and\n * externalized screenshots.\n */\nexport function splitReportHtmlByExecution(\n  options: SplitReportHtmlOptions,\n): SplitReportHtmlResult {\n  const { htmlPath, outputDir } = options;\n  const screenshotsDir = path.join(outputDir, 'screenshots');\n\n  mkdirSync(outputDir, { recursive: true });\n  mkdirSync(screenshotsDir, { recursive: true });\n\n  const executionJsonFiles: string[] = [];\n  const writtenScreenshotFiles = new Set<string>();\n  const { baseDump, executions } = collectDedupedExecutions(htmlPath);\n\n  let fileIndex = 0;\n  for (const execution of executions) {\n    fileIndex += 1;\n    externalizeScreenshotsInExecution(execution, {\n      htmlPath,\n      screenshotsDir,\n      writtenFiles: writtenScreenshotFiles,\n    });\n    const singleExecutionDump = new ReportActionDump({\n      sdkVersion: baseDump.sdkVersion,\n      groupName: baseDump.groupName,\n      groupDescription: baseDump.groupDescription,\n      modelBriefs: baseDump.modelBriefs,\n      deviceType: baseDump.deviceType,\n      executions: [execution],\n    });\n\n    const jsonFilePath = path.join(outputDir, `${fileIndex}.execution.json`);\n    writeFileSync(jsonFilePath, singleExecutionDump.serialize(2), 'utf-8');\n    executionJsonFiles.push(jsonFilePath);\n  }\n\n  return {\n    executionJsonFiles,\n    screenshotFiles: Array.from(writtenScreenshotFiles)\n      .sort()\n      .map((fileName) => path.join(screenshotsDir, fileName)),\n  };\n}\n"],"names":["screenshotModeAttrRegExp","RegExp","DATA_SCREENSHOT_MODE_ATTR","readDeclaredScreenshotMode","reportFilePath","mode","streamDumpScriptsSync","openTag","match","isDirectoryModeReport","path","declared","existsSync","isDirectoryBasedReport","dedupeExecutionsKeepLatest","executions","noIdCounter","deduped","Map","exec","key","Array","peekReportSdkVersion","dump","extractLastDumpScriptSync","warnedMismatchedVersions","Set","tryParseAgentReportDump","dumpString","trimmed","ReportActionDump","mergedAgentReportComment","reports","generateAgentReportComment","deviceTypes","report","Boolean","mergedReport","getVersion","ReportMergingTool","groupName","groupDescription","reportInfo","sourceVersion","currentVersion","logMsg","contents","unescaped","c","antiEscapeScriptTag","base","allExecutions","i","other","reportFileName","opts","rmOriginalReports","overwrite","outputDir","targetDir","getMidsceneRunSubDir","mkdirSync","isDirModeByIndex","info","hasDirectoryModeReport","resolvedName","getReportFileName","outputFilePath","Error","rmSync","unlinkSync","htmlEndTag","tpl","getReportTpl","htmlEndIdx","tplWithoutClose","appendFileSync","getBaseUrlFixScript","agentReports","reportAttributes","mergedGroupId","reportDir","screenshotsDir","mergedScreenshotsDir","file","readdirSync","src","dest","copyFileSync","streamImageScriptsToFile","allDumps","extractAllDumpScriptsSync","d","groupIdMatch","decodeURIComponent","extractedDumpString","agentReport","reportHtmlStr","reportHTMLContent","undefined","agentComment","error","collectDedupedExecutions","htmlPath","baseDump","executionSerial","latestSerialByExecutionId","dumpScript","groupedDump","execution","extensionByMimeType","mimeType","externalizeScreenshotsInExecution","visit","node","item","ref","normalizeScreenshotRef","ext","fileName","relativePath","absolutePath","resolved","resolveScreenshotSource","rawBase64","writeFileSync","Buffer","value","Object","splitReportHtmlByExecution","options","executionJsonFiles","writtenScreenshotFiles","fileIndex","singleExecutionDump","jsonFilePath"],"mappings":"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"}