@agent-scope/cli 1.20.2 → 1.20.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -11,6 +11,7 @@ var module$1 = require('module');
11
11
  var promises = require('fs/promises');
12
12
  var readline = require('readline');
13
13
  var playwright = require('@agent-scope/playwright');
14
+ var core = require('@agent-scope/core');
14
15
  var playwright$1 = require('playwright');
15
16
  var os = require('os');
16
17
  var http = require('http');
@@ -2302,26 +2303,7 @@ function buildHookInstrumentationScript() {
2302
2303
  // Walk fiber tree and collect hook data per component
2303
2304
  var components = [];
2304
2305
 
2305
- function serializeValue(v) {
2306
- if (v === null) return null;
2307
- if (v === undefined) return undefined;
2308
- var t = typeof v;
2309
- if (t === "string" || t === "number" || t === "boolean") return v;
2310
- if (t === "function") return "[function " + (v.name || "anonymous") + "]";
2311
- if (Array.isArray(v)) {
2312
- try { return v.map(serializeValue); } catch(e) { return "[Array]"; }
2313
- }
2314
- if (t === "object") {
2315
- try {
2316
- var keys = Object.keys(v).slice(0, 5);
2317
- var out = {};
2318
- for (var k of keys) { out[k] = serializeValue(v[k]); }
2319
- if (Object.keys(v).length > 5) out["..."] = "(truncated)";
2320
- return out;
2321
- } catch(e) { return "[Object]"; }
2322
- }
2323
- return String(v);
2324
- }
2306
+ var serializeValue = __SCOPE_SERIALIZE_VALUE__;
2325
2307
 
2326
2308
  // Hook node classifiers (mirrors hooks-extractor.ts logic)
2327
2309
  var HookLayout = 0b00100;
@@ -2542,7 +2524,10 @@ async function runHooksProfiling(componentName, filePath, props) {
2542
2524
  if (renderError !== null) {
2543
2525
  throw new Error(`Component render error: ${renderError}`);
2544
2526
  }
2545
- const instrumentScript = buildHookInstrumentationScript();
2527
+ const instrumentScript = buildHookInstrumentationScript().replace(
2528
+ "__SCOPE_SERIALIZE_VALUE__",
2529
+ core.serialize.toString()
2530
+ );
2546
2531
  const raw = await page.evaluate(instrumentScript);
2547
2532
  const result = raw;
2548
2533
  if (result.error) {
@@ -2738,44 +2723,79 @@ function buildProfilingCollectScript() {
2738
2723
  `;
2739
2724
  }
2740
2725
  async function replayInteraction(page, steps) {
2741
- for (const step of steps) {
2726
+ let executed = 0;
2727
+ const skippedInteractions = [];
2728
+ const skip = (index, step, reason) => {
2729
+ skippedInteractions.push({ index, action: step.action, target: step.target, reason });
2730
+ };
2731
+ for (const [index, step] of steps.entries()) {
2742
2732
  switch (step.action) {
2743
2733
  case "click":
2744
- if (step.target) {
2745
- await page.click(step.target, { timeout: 5e3 }).catch(() => {
2746
- process.stderr.write(` \u26A0 click target "${step.target}" not found, skipping
2734
+ if (!step.target) {
2735
+ skip(index, step, "missing_target");
2736
+ break;
2737
+ }
2738
+ try {
2739
+ await page.click(step.target, { timeout: 5e3 });
2740
+ executed++;
2741
+ } catch {
2742
+ process.stderr.write(` \u26A0 click target "${step.target}" not found, skipping
2747
2743
  `);
2748
- });
2744
+ skip(index, step, "not_found");
2749
2745
  }
2750
2746
  break;
2751
2747
  case "fill":
2752
- if (step.target && step.value !== void 0) {
2753
- await page.fill(step.target, step.value, { timeout: 5e3 }).catch(() => {
2754
- process.stderr.write(` \u26A0 fill target "${step.target}" not found, skipping
2748
+ if (!step.target) {
2749
+ skip(index, step, "missing_target");
2750
+ break;
2751
+ }
2752
+ if (step.value === void 0) {
2753
+ skip(index, step, "missing_value");
2754
+ break;
2755
+ }
2756
+ try {
2757
+ await page.fill(step.target, step.value, { timeout: 5e3 });
2758
+ executed++;
2759
+ } catch {
2760
+ process.stderr.write(` \u26A0 fill target "${step.target}" not found, skipping
2755
2761
  `);
2756
- });
2762
+ skip(index, step, "not_found");
2757
2763
  }
2758
2764
  break;
2759
2765
  case "hover":
2760
- if (step.target) {
2761
- await page.hover(step.target, { timeout: 5e3 }).catch(() => {
2762
- process.stderr.write(` \u26A0 hover target "${step.target}" not found, skipping
2766
+ if (!step.target) {
2767
+ skip(index, step, "missing_target");
2768
+ break;
2769
+ }
2770
+ try {
2771
+ await page.hover(step.target, { timeout: 5e3 });
2772
+ executed++;
2773
+ } catch {
2774
+ process.stderr.write(` \u26A0 hover target "${step.target}" not found, skipping
2763
2775
  `);
2764
- });
2776
+ skip(index, step, "not_found");
2765
2777
  }
2766
2778
  break;
2767
2779
  case "press":
2768
- if (step.value) {
2769
- await page.keyboard.press(step.value);
2780
+ if (!step.value) {
2781
+ skip(index, step, "missing_value");
2782
+ break;
2770
2783
  }
2784
+ await page.keyboard.press(step.value);
2785
+ executed++;
2771
2786
  break;
2772
2787
  case "wait":
2773
2788
  await page.waitForTimeout(step.delay ?? 500);
2789
+ executed++;
2790
+ break;
2791
+ default:
2792
+ skip(index, step, "unsupported");
2774
2793
  break;
2775
2794
  }
2776
2795
  }
2796
+ return { executed, skippedInteractions };
2777
2797
  }
2778
- function analyzeProfileFlags(totalRenders, wastedRenders, timing, layoutShifts) {
2798
+ function analyzeProfileFlags(totalRenders, wastedRenders, timing, layoutShifts, profileValidity) {
2779
2799
  const flags = /* @__PURE__ */ new Set();
2780
2800
  if (wastedRenders !== null && wastedRenders > 0 && wastedRenders / Math.max(1, totalRenders) > 0.3) {
2781
2801
  flags.add("WASTED_RENDER");
@@ -2786,7 +2806,7 @@ function analyzeProfileFlags(totalRenders, wastedRenders, timing, layoutShifts)
2786
2806
  if (layoutShifts.cumulativeScore > 0.1) {
2787
2807
  flags.add("LAYOUT_SHIFT_DETECTED");
2788
2808
  }
2789
- if (timing.js > 100) {
2809
+ if (profileValidity === "interaction" && timing.js > 100) {
2790
2810
  flags.add("SLOW_INTERACTION");
2791
2811
  }
2792
2812
  return [...flags];
@@ -2825,13 +2845,18 @@ async function runInteractionProfile(componentName, filePath, props, interaction
2825
2845
  throw new Error(`Profiling setup failed: ${setupData.error}`);
2826
2846
  }
2827
2847
  const jsStart = Date.now();
2848
+ let executedInteractions = 0;
2849
+ let skippedInteractions = [];
2828
2850
  if (interaction.length > 0) {
2829
2851
  process.stderr.write(` Replaying ${interaction.length} interaction step(s)\u2026
2830
2852
  `);
2831
- await replayInteraction(page, interaction);
2853
+ const replayResult = await replayInteraction(page, interaction);
2854
+ executedInteractions = replayResult.executed;
2855
+ skippedInteractions = replayResult.skippedInteractions;
2832
2856
  await page.waitForTimeout(300);
2833
2857
  }
2834
2858
  const jsDuration = Date.now() - jsStart;
2859
+ const profileValidity = interaction.length === 0 ? "no_interaction_requested" : executedInteractions === 0 ? "setup_only" : "interaction";
2835
2860
  const collected = await page.evaluate(buildProfilingCollectScript());
2836
2861
  const profileData = collected;
2837
2862
  if (profileData.error) {
@@ -2849,7 +2874,13 @@ async function runInteractionProfile(componentName, filePath, props, interaction
2849
2874
  const totalRenders = profileData.commitCount ?? 0;
2850
2875
  const uniqueComponents = profileData.uniqueComponents ?? 0;
2851
2876
  const wastedRenders = profileData.wastedRenders ?? null;
2852
- const flags = analyzeProfileFlags(totalRenders, wastedRenders, timing, layoutShifts);
2877
+ const flags = analyzeProfileFlags(
2878
+ totalRenders,
2879
+ wastedRenders,
2880
+ timing,
2881
+ layoutShifts,
2882
+ profileValidity
2883
+ );
2853
2884
  return {
2854
2885
  component: componentName,
2855
2886
  totalRenders,
@@ -2863,7 +2894,17 @@ async function runInteractionProfile(componentName, filePath, props, interaction
2863
2894
  timing,
2864
2895
  layoutShifts,
2865
2896
  flags,
2866
- interaction
2897
+ interaction,
2898
+ interactionSteps: {
2899
+ attempted: interaction.length,
2900
+ executed: executedInteractions,
2901
+ skipped: skippedInteractions.length
2902
+ },
2903
+ interactionStepsAttempted: interaction.length,
2904
+ interactionStepsExecuted: executedInteractions,
2905
+ interactionStepsSkipped: skippedInteractions.length,
2906
+ skippedInteractions,
2907
+ profileValidity
2867
2908
  };
2868
2909
  } finally {
2869
2910
  await browser.close();
@@ -2963,6 +3004,7 @@ function flattenSerializedValue(sv) {
2963
3004
  case "string":
2964
3005
  case "number":
2965
3006
  case "boolean":
3007
+ case "date":
2966
3008
  return v.value;
2967
3009
  case "object": {
2968
3010
  if (!Array.isArray(v.entries)) return {};
@@ -4083,6 +4125,39 @@ function formatAggregateRenderFailureJson(componentName, failures, scenarioCount
4083
4125
  }
4084
4126
  var MANIFEST_PATH6 = ".reactscope/manifest.json";
4085
4127
  var DEFAULT_OUTPUT_DIR = ".reactscope/renders";
4128
+ function renderArtifactBaseName(componentName, scenarioName) {
4129
+ return scenarioName === void 0 ? componentName : `${componentName}-${scenarioName}`;
4130
+ }
4131
+ function removeStaleRenderError(outputDir, componentName, scenarioName) {
4132
+ const errorPath = path.resolve(
4133
+ outputDir,
4134
+ `${renderArtifactBaseName(componentName, scenarioName)}.error.json`
4135
+ );
4136
+ if (fs.existsSync(errorPath)) {
4137
+ fs.rmSync(errorPath, { force: true });
4138
+ }
4139
+ }
4140
+ function writeRenderErrorArtifact(outputDir, failure) {
4141
+ const errPath = path.resolve(
4142
+ outputDir,
4143
+ `${renderArtifactBaseName(failure.component, failure.scenario)}.error.json`
4144
+ );
4145
+ fs.writeFileSync(
4146
+ errPath,
4147
+ JSON.stringify(
4148
+ {
4149
+ component: failure.component,
4150
+ scenario: failure.scenario,
4151
+ errorMessage: failure.errorMessage,
4152
+ heuristicFlags: failure.heuristicFlags,
4153
+ propsAtCrash: failure.propsAtCrash
4154
+ },
4155
+ null,
4156
+ 2
4157
+ )
4158
+ );
4159
+ return errPath;
4160
+ }
4086
4161
  var _pool3 = null;
4087
4162
  async function getPool3(viewportWidth, viewportHeight) {
4088
4163
  if (_pool3 === null) {
@@ -4767,20 +4842,12 @@ function registerRenderAll(renderCmd) {
4767
4842
  success: false,
4768
4843
  errorMessage: outcome.error.message
4769
4844
  });
4770
- const errPath = path.resolve(outputDir, `${name}.error.json`);
4771
- fs.writeFileSync(
4772
- errPath,
4773
- JSON.stringify(
4774
- {
4775
- component: name,
4776
- errorMessage: outcome.error.message,
4777
- heuristicFlags: outcome.error.heuristicFlags,
4778
- propsAtCrash: outcome.error.propsAtCrash
4779
- },
4780
- null,
4781
- 2
4782
- )
4783
- );
4845
+ const errPath = writeRenderErrorArtifact(outputDir, {
4846
+ component: name,
4847
+ errorMessage: outcome.error.message,
4848
+ heuristicFlags: outcome.error.heuristicFlags,
4849
+ propsAtCrash: outcome.error.propsAtCrash
4850
+ });
4784
4851
  failures.push({
4785
4852
  component: name,
4786
4853
  stage: "render",
@@ -4793,13 +4860,14 @@ function registerRenderAll(renderCmd) {
4793
4860
  }
4794
4861
  const result = outcome.result;
4795
4862
  results.push({ name, renderTimeMs: result.renderTimeMs, success: true });
4863
+ removeStaleRenderError(outputDir, name);
4796
4864
  if (!isIcon) {
4797
4865
  const pngPath = path.resolve(outputDir, `${name}.png`);
4798
4866
  fs.writeFileSync(pngPath, result.screenshot);
4799
4867
  outputPaths.push(pngPath);
4800
4868
  }
4801
4869
  const jsonPath = path.resolve(outputDir, `${name}.json`);
4802
- const renderJson = formatRenderJson(name, {}, result);
4870
+ const renderJson = formatRenderJson(name, renderProps, result);
4803
4871
  const extResult = result;
4804
4872
  if (isIcon && extResult.svgContent) {
4805
4873
  renderJson.svgContent = extResult.svgContent;
@@ -4860,13 +4928,34 @@ function registerRenderAll(renderCmd) {
4860
4928
  concurrency: 2
4861
4929
  });
4862
4930
  const matrixResult = await matrix.render();
4863
- const matrixCells = matrixResult.cells.map((cell) => ({
4864
- axisValues: [scenarioEntries2[cell.axisIndices[0] ?? 0]?.[0] ?? ""],
4865
- screenshot: cell.result.screenshot.toString("base64"),
4866
- width: cell.result.width,
4867
- height: cell.result.height,
4868
- renderTimeMs: cell.result.renderTimeMs
4869
- }));
4931
+ const matrixCells = matrixResult.cells.map((cell) => {
4932
+ const scenarioName = scenarioEntries2[cell.axisIndices[0] ?? 0]?.[0] ?? "";
4933
+ const artifactBaseName = renderArtifactBaseName(name, scenarioName);
4934
+ const scenarioPngPath = path.resolve(outputDir, `${artifactBaseName}.png`);
4935
+ const scenarioJsonPath = path.resolve(outputDir, `${artifactBaseName}.json`);
4936
+ if (!isIcon) {
4937
+ fs.writeFileSync(scenarioPngPath, cell.result.screenshot);
4938
+ outputPaths.push(scenarioPngPath);
4939
+ }
4940
+ const scenarioJson = formatRenderJson(
4941
+ `${name}:${scenarioName}`,
4942
+ scenarioPropsMap[scenarioName] ?? {},
4943
+ cell.result
4944
+ );
4945
+ fs.writeFileSync(scenarioJsonPath, JSON.stringify(scenarioJson, null, 2));
4946
+ outputPaths.push(scenarioJsonPath);
4947
+ removeStaleRenderError(outputDir, name, scenarioName);
4948
+ return {
4949
+ axisValues: [scenarioName],
4950
+ scenario: scenarioName,
4951
+ outputPath: scenarioJsonPath,
4952
+ screenshotPath: isIcon ? void 0 : scenarioPngPath,
4953
+ screenshot: cell.result.screenshot.toString("base64"),
4954
+ width: cell.result.width,
4955
+ height: cell.result.height,
4956
+ renderTimeMs: cell.result.renderTimeMs
4957
+ };
4958
+ });
4870
4959
  const existingJson = JSON.parse(fs.readFileSync(jsonPath, "utf-8"));
4871
4960
  existingJson.cells = matrixCells;
4872
4961
  existingJson.axisLabels = [scenarioAxis.values];