@agent-scope/playwright 1.2.0 → 1.3.0

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.
@@ -848,6 +848,126 @@
848
848
  children
849
849
  };
850
850
  }
851
+ var HookLayout2 = 4;
852
+ function isLightweightEffectNode(node) {
853
+ const ms = node.memoizedState;
854
+ if (ms === null || typeof ms !== "object") return false;
855
+ const obj = ms;
856
+ return typeof obj.create === "function" && "deps" in obj && typeof obj.tag === "number";
857
+ }
858
+ function isLightweightRefNode(node) {
859
+ if (node.queue != null) return false;
860
+ const ms = node.memoizedState;
861
+ if (ms === null || typeof ms !== "object" || Array.isArray(ms)) return false;
862
+ const keys = Object.keys(ms);
863
+ return keys.length === 1 && keys[0] === "current";
864
+ }
865
+ function isLightweightMemoTuple(node) {
866
+ if (node.queue != null) return false;
867
+ const ms = node.memoizedState;
868
+ if (!Array.isArray(ms) || ms.length !== 2) return false;
869
+ return ms[1] === null || Array.isArray(ms[1]);
870
+ }
871
+ function isLightweightStateOrReducer(node) {
872
+ return node.queue != null && typeof node.queue === "object" && typeof node.queue.dispatch === "function";
873
+ }
874
+ function isLightweightReducer(node) {
875
+ if (!isLightweightStateOrReducer(node)) return false;
876
+ const q = node.queue;
877
+ if (typeof q.reducer === "function") return true;
878
+ const lrr = q.lastRenderedReducer;
879
+ if (typeof lrr !== "function") return false;
880
+ const name = lrr.name ?? "";
881
+ return name !== "basicStateReducer" && name !== "";
882
+ }
883
+ function classifyHookType(node) {
884
+ if (isLightweightEffectNode(node)) {
885
+ const ms = node.memoizedState;
886
+ return ms.tag & HookLayout2 ? "useLayoutEffect" : "useEffect";
887
+ }
888
+ if (isLightweightRefNode(node)) return "useRef";
889
+ if (isLightweightMemoTuple(node)) {
890
+ const [val] = node.memoizedState;
891
+ return typeof val === "function" ? "useCallback" : "useMemo";
892
+ }
893
+ if (isLightweightStateOrReducer(node)) {
894
+ return isLightweightReducer(node) ? "useReducer" : "useState";
895
+ }
896
+ return "custom";
897
+ }
898
+ function countAndClassifyHooks(fiber) {
899
+ const hookTypes = [];
900
+ let node = fiber.memoizedState ?? null;
901
+ if (node === null || typeof node !== "object" || !("next" in node)) {
902
+ return { hookCount: 0, hookTypes: [] };
903
+ }
904
+ while (node !== null) {
905
+ hookTypes.push(classifyHookType(node));
906
+ node = node.next ?? null;
907
+ }
908
+ return { hookCount: hookTypes.length, hookTypes };
909
+ }
910
+ function walkFiberLightweightInner(fiber, includeHost, visited, depth) {
911
+ if (fiber === null || fiber === void 0) return null;
912
+ if (visited.has(fiber)) return null;
913
+ if (shouldSkip(fiber, includeHost)) return null;
914
+ visited.add(fiber);
915
+ const id = typeof fiber._debugID === "number" ? fiber._debugID : nextId();
916
+ const { hookCount, hookTypes } = countAndClassifyHooks(fiber);
917
+ const children = collectLightweightChildren(fiber, includeHost, visited, depth + 1);
918
+ const node = {
919
+ id,
920
+ name: extractName(fiber),
921
+ type: classifyType(fiber),
922
+ hookCount,
923
+ hookTypes,
924
+ childCount: children.length,
925
+ depth,
926
+ children
927
+ };
928
+ return node;
929
+ }
930
+ function collectLightweightChildren(fiber, includeHost, visited, childDepth) {
931
+ const nodes = [];
932
+ let current = fiber.child ?? null;
933
+ while (current !== null) {
934
+ if (visited.has(current)) {
935
+ current = current.sibling ?? null;
936
+ continue;
937
+ }
938
+ if (shouldSkip(current, includeHost)) {
939
+ const promoted = collectLightweightChildren(current, includeHost, visited, childDepth);
940
+ nodes.push(...promoted);
941
+ } else {
942
+ const node = walkFiberLightweightInner(current, includeHost, visited, childDepth);
943
+ if (node !== null) {
944
+ nodes.push(node);
945
+ }
946
+ }
947
+ current = current.sibling ?? null;
948
+ }
949
+ return nodes;
950
+ }
951
+ function walkFiberRootLightweight(fiberRoot, options = {}) {
952
+ if (!fiberRoot) return null;
953
+ const hostRootFiber = fiberRoot.current ?? null;
954
+ if (!hostRootFiber) return null;
955
+ const includeHost = options.includeHostElements ?? false;
956
+ const visited = /* @__PURE__ */ new Set();
957
+ const children = collectLightweightChildren(hostRootFiber, includeHost, visited, 0);
958
+ if (children.length === 0) return null;
959
+ if (children.length === 1) return children[0] ?? null;
960
+ return {
961
+ id: nextId(),
962
+ name: "Root",
963
+ type: "function",
964
+ hookCount: 0,
965
+ hookTypes: [],
966
+ childCount: children.length,
967
+ depth: 0,
968
+ children
969
+ };
970
+ }
851
971
 
852
972
  // ../runtime/src/suspense-detector.ts
853
973
  var SuspenseComponent2 = 13;
@@ -939,18 +1059,35 @@
939
1059
  const walkOptions = {
940
1060
  includeHostElements: options.includeHostElements ?? false
941
1061
  };
942
- const tree = walkFiberRoot(fiberRoot, walkOptions);
943
- if (!tree) {
944
- throw new Error(
945
- "capture(): Fiber tree is empty. Make sure React has rendered at least one component."
946
- );
947
- }
948
1062
  const hostRootFiber = fiberRoot.current ?? null;
949
1063
  const rootChild = hostRootFiber?.child ?? null;
950
1064
  const errors = detectErrors(rootChild);
951
1065
  const suspenseBoundaries = detectSuspenseBoundaries(rootChild);
952
1066
  const capturedIn = Date.now() - startTime;
953
1067
  const consoleEntries = getConsoleEntries();
1068
+ if (options.lightweight) {
1069
+ const tree2 = walkFiberRootLightweight(fiberRoot, walkOptions);
1070
+ if (!tree2) {
1071
+ throw new Error(
1072
+ "capture(): Fiber tree is empty. Make sure React has rendered at least one component."
1073
+ );
1074
+ }
1075
+ return {
1076
+ url,
1077
+ timestamp: startTime,
1078
+ capturedIn,
1079
+ tree: tree2,
1080
+ consoleEntries,
1081
+ errors,
1082
+ suspenseBoundaries
1083
+ };
1084
+ }
1085
+ const tree = walkFiberRoot(fiberRoot, walkOptions);
1086
+ if (!tree) {
1087
+ throw new Error(
1088
+ "capture(): Fiber tree is empty. Make sure React has rendered at least one component."
1089
+ );
1090
+ }
954
1091
  return {
955
1092
  url,
956
1093
  timestamp: startTime,
@@ -989,14 +1126,16 @@
989
1126
  hasCommitted = true;
990
1127
  resolveFirstCommit();
991
1128
  };
992
- window.__SCOPE_CAPTURE__ = async () => {
1129
+ window.__SCOPE_CAPTURE__ = async (options) => {
993
1130
  if (!hasCommitted) {
994
1131
  await firstCommit;
995
1132
  }
996
- return capture();
1133
+ return capture({ lightweight: options?.lightweight });
997
1134
  };
998
1135
  window.__SCOPE_CAPTURE_JSON__ = async () => {
999
- await firstCommit;
1136
+ if (!hasCommitted) {
1137
+ await firstCommit;
1138
+ }
1000
1139
  const result = await capture();
1001
1140
  return JSON.stringify(result);
1002
1141
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-scope/playwright",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Playwright integration for Scope — replay traces and generate tests",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,8 +31,8 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@playwright/test": "^1.58.2",
34
- "@agent-scope/core": "1.2.0",
35
- "@agent-scope/runtime": "1.2.0"
34
+ "@agent-scope/core": "1.3.0",
35
+ "@agent-scope/runtime": "1.3.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "*",