@atlassian/chippy 0.0.1-beta.1 → 0.0.1-beta.2

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.
Files changed (54) hide show
  1. package/README.md +142 -54
  2. package/bin/chippy.mjs +53 -95
  3. package/bin/pack.mjs +20 -0
  4. package/bin/skill-dir.mjs +15 -0
  5. package/lib/avalanche.js +54 -23
  6. package/lib/avalanche.js.map +2 -2
  7. package/lib/bootstrap.js +20 -1
  8. package/lib/bootstrap.js.map +3 -3
  9. package/lib/extension/avalanche.es.js +173 -34
  10. package/lib/extension/avalanche.es.js.map +1 -1
  11. package/lib/extension/chippy-page-bridge.js +164 -44
  12. package/lib/mcp-skill/README.md +2 -4
  13. package/lib/mcp-skill/SKILL.md +21 -16
  14. package/lib/mcp-skill/extension/armed-reload-startup.js +83 -0
  15. package/lib/mcp-skill/extension/avalanche.es.js +173 -34
  16. package/lib/mcp-skill/extension/avalanche.es.js.map +1 -1
  17. package/lib/mcp-skill/extension/avalanche.iife.js +173 -34
  18. package/lib/mcp-skill/extension/avalanche.iife.js.map +1 -1
  19. package/lib/mcp-skill/extension/background.js +2 -0
  20. package/lib/mcp-skill/extension/chippy-page-bridge.js +164 -44
  21. package/lib/mcp-skill/extension/content-script.js +23 -0
  22. package/lib/mcp-skill/extension/devtools-hook-shim.js +18 -1
  23. package/lib/mcp-skill/extension/early-bootstrap.js +3 -1
  24. package/lib/mcp-skill/extension/manifest.json +1 -1
  25. package/lib/mcp-skill/extension/panel-settings.js +2 -20
  26. package/lib/mcp-skill/extension/startup-handoff.js +30 -0
  27. package/lib/mcp-skill/references/README.md +2 -2
  28. package/lib/mcp-skill/references/avalanche-detector.md +3 -3
  29. package/lib/mcp-skill/references/browser-runtime-api.md +4 -4
  30. package/lib/mcp-skill/references/command-output-formats.md +12 -2
  31. package/lib/mcp-skill/references/command-surface.md +6 -6
  32. package/lib/mcp-skill/references/mode-a-injection.md +43 -9
  33. package/lib/mcp-skill/references/playwright-cli-setup.md +5 -5
  34. package/lib/mcp-skill/references/playwright-probes.md +1 -1
  35. package/lib/mcp-skill/references/recipes.txt +1 -1
  36. package/lib/mcp-skill/references/stability-render.md +4 -4
  37. package/lib/mcp-skill/references/test-mode-playbook.md +2 -1
  38. package/lib/mcp-skill/references/troubleshooting.md +6 -1
  39. package/lib/mcp-skill/scripts/chippy-helper-server.js +116 -58
  40. package/lib/mcp-skill/scripts/chippy.sh +41 -7
  41. package/lib/mcp-skill/scripts/helper-access.js +20 -6
  42. package/lib/mcp-skill/scripts/helper-proxy.js +87 -0
  43. package/lib/mcp-skill/scripts/page-socket-trust.js +81 -0
  44. package/lib/plugin.js.map +2 -2
  45. package/lib/storybook/connection-fallback.jsx +1 -1
  46. package/lib/storybook/manager-owner.js +42 -0
  47. package/lib/storybook/manager.jsx +22 -0
  48. package/lib/storybook/preview.js +53 -18
  49. package/lib/storybook/setup.js +15 -4
  50. package/lib/types/chippy-bridge/fiber-source.d.ts +14 -3
  51. package/lib/types/chippy-bridge/react-renderer.d.ts +30 -5
  52. package/lib/types/chippy-bridge/recorded-source.d.ts +44 -0
  53. package/lib/types/debug-render/core.d.ts +6 -0
  54. package/package.json +3 -1
@@ -159,6 +159,72 @@ function inlineSourceMapOf(url) {
159
159
  return null;
160
160
  }
161
161
  }
162
+ function recordedLiteralPosition(code, source) {
163
+ const column = source.columnNumber === null ? "" : `,\\s*columnNumber:\\s*${source.columnNumber}`;
164
+ const literal = new RegExp(`lineNumber:\\s*${source.lineNumber}${column}(?!\\d)`, "g");
165
+ const first = literal.exec(code);
166
+ if (first === null || literal.exec(code) !== null) return null;
167
+ const before = code.slice(0, first.index);
168
+ const lineStart = before.lastIndexOf("\n");
169
+ return { line: before.split("\n").length, column: first.index - lineStart };
170
+ }
171
+ function decodedPath(pathname) {
172
+ try {
173
+ return decodeURIComponent(pathname);
174
+ } catch {
175
+ return pathname;
176
+ }
177
+ }
178
+ function slashPath(fileName) {
179
+ return fileName.replace(/\\/g, "/");
180
+ }
181
+ function servedModuleUrls(fileName, page) {
182
+ const file = slashPath(fileName);
183
+ const matches = /* @__PURE__ */ new Map();
184
+ for (const raw of page.resourceUrls) {
185
+ let url;
186
+ try {
187
+ url = new URL(raw, page.origin);
188
+ } catch {
189
+ continue;
190
+ }
191
+ if (url.origin !== page.origin) continue;
192
+ const path = decodedPath(url.pathname).replace(/^\/@fs(?=\/)/, "");
193
+ const tail = /^\/[A-Za-z]:\//.test(path) && /^[A-Za-z]:\//.test(file) ? path.slice(1) : path;
194
+ if (tail.length < 2 || !file.endsWith(tail)) continue;
195
+ matches.set(tail, { url: url.href, length: tail.length });
196
+ }
197
+ const ranked = [...matches.values()].sort((a, b) => b.length - a.length).map((entry) => entry.url);
198
+ if (page.vite && (file.startsWith("/") || /^[A-Za-z]:\//.test(file))) {
199
+ const fsUrl = new URL(`/@fs${file.startsWith("/") ? "" : "/"}${file}`, page.origin).href;
200
+ if (!ranked.includes(fsUrl)) ranked.push(fsUrl);
201
+ }
202
+ return ranked;
203
+ }
204
+ function readPageModules() {
205
+ var _a2, _b2, _c2, _d2;
206
+ const scope = globalThis;
207
+ const origin = (_a2 = scope.location) == null ? void 0 : _a2.origin;
208
+ if (!origin || origin === "null") return null;
209
+ const resourceUrls = [];
210
+ for (const script of Array.from(((_b2 = scope.document) == null ? void 0 : _b2.scripts) ?? [])) {
211
+ if (script.src) resourceUrls.push(script.src);
212
+ }
213
+ try {
214
+ for (const entry of ((_d2 = (_c2 = scope.performance) == null ? void 0 : _c2.getEntriesByType) == null ? void 0 : _d2.call(_c2, "resource")) ?? []) resourceUrls.push(entry.name);
215
+ } catch {
216
+ }
217
+ const vite = resourceUrls.some((url) => url.includes("/@vite/client"));
218
+ return { origin, resourceUrls, vite };
219
+ }
220
+ function pageModuleUrls(fileName) {
221
+ const page = readPageModules();
222
+ return page ? servedModuleUrls(fileName, page) : [];
223
+ }
224
+ function baseName(path) {
225
+ const clean = slashPath(path).replace(/[?#].*$/, "");
226
+ return clean.slice(clean.lastIndexOf("/") + 1);
227
+ }
162
228
  const V8_FRAME = /^\s*at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?$/;
163
229
  const AT_FRAME = /^\s*(?:(.*?)@)(.+?):(\d+):(\d+)$/;
164
230
  const MAX_CALL_SITE_FRAMES = 4;
@@ -221,9 +287,10 @@ function sourcePath(source, moduleUrl) {
221
287
  const parsed = parseUrl(resolved);
222
288
  return parsed ? parsed.pathname.replace(/^\/+/, "") : resolved;
223
289
  }
224
- function createCallSiteResolver(fetchModule) {
290
+ function createCallSiteResolver(fetchModule, moduleUrlsFor = () => []) {
225
291
  const maps = /* @__PURE__ */ new Map();
226
292
  const sites = /* @__PURE__ */ new Map();
293
+ const recordedSites = /* @__PURE__ */ new Map();
227
294
  const stats = { modules: 0, sites: 0, located: 0 };
228
295
  async function mapsFor(url) {
229
296
  const cached = maps.get(url);
@@ -236,10 +303,10 @@ function createCallSiteResolver(fetchModule) {
236
303
  const annotation = sourceMappingUrlOf(code);
237
304
  if (annotation === null) return null;
238
305
  const inline = inlineSourceMapOf(annotation);
239
- if (inline !== null) return { maps: parseSourceMap(inline), sourceBaseUrl: url };
306
+ if (inline !== null) return { code, maps: parseSourceMap(inline), sourceBaseUrl: url };
240
307
  const mapUrl = ((_a2 = parseUrl(annotation, url)) == null ? void 0 : _a2.href) ?? null;
241
308
  const text = mapUrl === null ? null : await fetchModule(mapUrl);
242
- return text === null || mapUrl === null ? null : { maps: parseSourceMap(text), sourceBaseUrl: mapUrl };
309
+ return text === null || mapUrl === null ? null : { code, maps: parseSourceMap(text), sourceBaseUrl: mapUrl };
243
310
  })();
244
311
  maps.set(url, pending);
245
312
  return pending;
@@ -274,7 +341,28 @@ function createCallSiteResolver(fetchModule) {
274
341
  }
275
342
  return null;
276
343
  }
277
- return { locate, stats };
344
+ async function correctRecorded(source) {
345
+ for (const url of moduleUrlsFor(source.fileName)) {
346
+ const parsed = await mapsFor(url);
347
+ if (!parsed || parsed.maps.length === 0) continue;
348
+ const served = recordedLiteralPosition(parsed.code, source);
349
+ if (served === null) continue;
350
+ const original = originalPositionFor(parsed.maps, served.line, served.column);
351
+ if (!original || baseName(original.source) !== baseName(source.fileName)) continue;
352
+ return { fileName: source.fileName, lineNumber: original.line, columnNumber: original.column };
353
+ }
354
+ return source;
355
+ }
356
+ function locateRecorded(source) {
357
+ const key = `${source.fileName}:${source.lineNumber}:${source.columnNumber}`;
358
+ let answer = recordedSites.get(key);
359
+ if (!answer) {
360
+ answer = correctRecorded(source).catch(() => source);
361
+ recordedSites.set(key, answer);
362
+ }
363
+ return answer;
364
+ }
365
+ return { locate, locateRecorded, stats };
278
366
  }
279
367
  function debugSourceOf(value) {
280
368
  if (!value || typeof value !== "object") return null;
@@ -312,8 +400,6 @@ function getFiberSourceCandidate(fiber) {
312
400
  }
313
401
  function getFiberSource$2(fiber) {
314
402
  if (!fiber) return null;
315
- const recorded = recordedSource(fiber);
316
- if (recorded) return recorded;
317
403
  const cached = resolvedFiberSources.get(fiber);
318
404
  if (cached !== void 0) return cached;
319
405
  return getFiberSourceCandidate(fiber);
@@ -336,15 +422,22 @@ async function fetchModuleFromPage(url) {
336
422
  }
337
423
  }
338
424
  function pageResolver() {
339
- if (!defaultResolver) defaultResolver = createCallSiteResolver(fetchModuleFromPage);
425
+ if (!defaultResolver) defaultResolver = createCallSiteResolver(fetchModuleFromPage, pageModuleUrls);
340
426
  return defaultResolver;
341
427
  }
342
428
  function resolveFiberSource$1(fiber, resolver = pageResolver()) {
343
429
  if (!fiber) return Promise.resolve(null);
344
- const recorded = recordedSource(fiber);
345
- if (recorded) return Promise.resolve(recorded);
346
430
  const cached = pendingFiberSources.get(fiber);
347
431
  if (cached) return cached;
432
+ const recorded = recordedSource(fiber);
433
+ if (recorded) {
434
+ const pending2 = resolver.locateRecorded(recorded).then((located) => {
435
+ resolvedFiberSources.set(fiber, located);
436
+ return located;
437
+ });
438
+ pendingFiberSources.set(fiber, pending2);
439
+ return pending2;
440
+ }
348
441
  const frames = getFiberStackFrames(fiber);
349
442
  const fallback = writerLocationOf(frames, () => null);
350
443
  const pending = resolver.locate(frames).catch(() => null).then((source) => {
@@ -908,13 +1001,42 @@ function getReactRenderers(scope = globalThis) {
908
1001
  }
909
1002
  return out;
910
1003
  }
911
- function getDispatcherRef(scope = globalThis) {
1004
+ function dispatcherSlotOf(injected) {
1005
+ if (!injected || typeof injected !== "object") return null;
1006
+ const owner = injected;
1007
+ if (typeof owner.H !== "undefined") return { owner, key: "H" };
1008
+ if ("current" in owner) return { owner, key: "current" };
1009
+ return null;
1010
+ }
1011
+ function getDispatcherSlot(scope = globalThis) {
912
1012
  for (const renderer of getReactRenderers(scope)) {
913
- const ref = renderer.currentDispatcherRef;
914
- if (ref && typeof ref === "object") return ref;
1013
+ const slot = dispatcherSlotOf(renderer.currentDispatcherRef);
1014
+ if (slot) return slot;
915
1015
  }
916
1016
  return null;
917
1017
  }
1018
+ const modernRefs = /* @__PURE__ */ new WeakMap();
1019
+ function dispatcherRefOf(slot) {
1020
+ if (slot.key === "current") return slot.owner;
1021
+ let ref = modernRefs.get(slot.owner);
1022
+ if (!ref) {
1023
+ const { owner } = slot;
1024
+ ref = {
1025
+ get current() {
1026
+ return owner.H;
1027
+ },
1028
+ set current(value) {
1029
+ owner.H = value;
1030
+ }
1031
+ };
1032
+ modernRefs.set(owner, ref);
1033
+ }
1034
+ return ref;
1035
+ }
1036
+ function getDispatcherRef(scope = globalThis) {
1037
+ const slot = getDispatcherSlot(scope);
1038
+ return slot ? dispatcherRefOf(slot) : null;
1039
+ }
918
1040
  function getScheduleUpdate(scope = globalThis) {
919
1041
  for (const renderer of getReactRenderers(scope)) {
920
1042
  if (typeof renderer.scheduleUpdate === "function") return renderer.scheduleUpdate;
@@ -973,11 +1095,14 @@ const HOOK_PRIMITIVE = {
973
1095
  function isReactInternalFrameName(name) {
974
1096
  return name === "atlDispatcherHook" || name === "record" || name.startsWith("Object.") || name === "renderWithHooks" || name === "updateFunctionComponent" || name === "mountIndeterminateComponent" || name === "beginWork" || /^beginWork\$/.test(name) || name === "performUnitOfWork" || name === "workLoopSync" || name === "renderRootSync" || name === "performConcurrentWorkOnRoot" || name === "workLoop" || name === "flushWork";
975
1097
  }
1098
+ function isHookFrameName(name) {
1099
+ return /^use[A-Z0-9_]/.test(name.slice(name.lastIndexOf(".") + 1));
1100
+ }
976
1101
  function deriveComponentName(frames) {
977
1102
  for (const { name } of frames) {
978
1103
  if (!name) continue;
979
1104
  if (isReactInternalFrameName(name)) continue;
980
- if (/^use[A-Z0-9_]/.test(name)) continue;
1105
+ if (isHookFrameName(name)) continue;
981
1106
  return name;
982
1107
  }
983
1108
  return "Unknown";
@@ -988,7 +1113,7 @@ function createDebugRender(options = {}) {
988
1113
  const patchedDispatchers = /* @__PURE__ */ new WeakMap();
989
1114
  let installed2 = false;
990
1115
  let originalDescriptor = void 0;
991
- let dispatcherRef = null;
1116
+ let dispatcherSlot = null;
992
1117
  let lastSeenDispatcher = null;
993
1118
  function record(primitive, args) {
994
1119
  const stack = new Error().stack || "";
@@ -1040,15 +1165,16 @@ function createDebugRender(options = {}) {
1040
1165
  }
1041
1166
  function install() {
1042
1167
  if (installed2) return { ok: true };
1043
- const ref = getDispatcherRef();
1044
- if (!ref) return { ok: false, reason: "ReactCurrentDispatcher not found" };
1045
- dispatcherRef = ref;
1046
- patchDispatcher(ref.current);
1047
- lastSeenDispatcher = ref.current;
1048
- originalDescriptor = Object.getOwnPropertyDescriptor(ref, "current") || null;
1049
- let stored = ref.current;
1168
+ const slot = getDispatcherSlot();
1169
+ if (!slot) return { ok: false, reason: "ReactCurrentDispatcher not found" };
1170
+ dispatcherSlot = slot;
1171
+ const { owner, key } = slot;
1172
+ patchDispatcher(owner[key]);
1173
+ lastSeenDispatcher = owner[key];
1174
+ originalDescriptor = Object.getOwnPropertyDescriptor(owner, key) || null;
1175
+ let stored = owner[key];
1050
1176
  try {
1051
- Object.defineProperty(ref, "current", {
1177
+ Object.defineProperty(owner, key, {
1052
1178
  configurable: true,
1053
1179
  get() {
1054
1180
  return stored;
@@ -1062,7 +1188,7 @@ function createDebugRender(options = {}) {
1062
1188
  }
1063
1189
  });
1064
1190
  } catch (e) {
1065
- dispatcherRef = null;
1191
+ dispatcherSlot = null;
1066
1192
  return { ok: false, reason: "cannot define property on ReactCurrentDispatcher" };
1067
1193
  }
1068
1194
  installed2 = true;
@@ -1070,12 +1196,13 @@ function createDebugRender(options = {}) {
1070
1196
  }
1071
1197
  function uninstall() {
1072
1198
  if (!installed2) return;
1073
- if (dispatcherRef) {
1199
+ if (dispatcherSlot) {
1200
+ const { owner, key } = dispatcherSlot;
1074
1201
  try {
1075
1202
  if (originalDescriptor) {
1076
- Object.defineProperty(dispatcherRef, "current", originalDescriptor);
1203
+ Object.defineProperty(owner, key, originalDescriptor);
1077
1204
  } else {
1078
- Object.defineProperty(dispatcherRef, "current", {
1205
+ Object.defineProperty(owner, key, {
1079
1206
  configurable: true,
1080
1207
  writable: true,
1081
1208
  enumerable: true,
@@ -1087,7 +1214,7 @@ function createDebugRender(options = {}) {
1087
1214
  }
1088
1215
  unpatchDispatcher(lastSeenDispatcher);
1089
1216
  installed2 = false;
1090
- dispatcherRef = null;
1217
+ dispatcherSlot = null;
1091
1218
  originalDescriptor = void 0;
1092
1219
  lastSeenDispatcher = null;
1093
1220
  }
@@ -1104,8 +1231,7 @@ function createDebugRender(options = {}) {
1104
1231
  const g = globalThis;
1105
1232
  const hasDevtoolsHook = !!g.__REACT_DEVTOOLS_GLOBAL_HOOK__;
1106
1233
  const rendererCount = getReactRenderers().length;
1107
- const ref = dispatcherRef || getDispatcherRef();
1108
- const hasDispatcherRef = !!ref;
1234
+ const hasDispatcherRef = !!(dispatcherSlot || getDispatcherSlot());
1109
1235
  let totalCapturedEvents = 0;
1110
1236
  for (const list of eventsByComponent.values()) totalCapturedEvents += list.length;
1111
1237
  let reason;
@@ -1144,7 +1270,7 @@ function createDebugRender(options = {}) {
1144
1270
  uninstall,
1145
1271
  reset,
1146
1272
  isInstalled: () => installed2,
1147
- dispatcherRef: () => dispatcherRef || getDispatcherRef(),
1273
+ dispatcherRef: () => dispatcherSlot ? dispatcherRefOf(dispatcherSlot) : getDispatcherRef(),
1148
1274
  rawEvents,
1149
1275
  allComponents,
1150
1276
  status,
@@ -2953,7 +3079,7 @@ function customHookPathFromFrames(frames, componentName) {
2953
3079
  const out = [];
2954
3080
  for (let i = relevant.length - 1; i >= 0; i--) {
2955
3081
  const { name, loc } = relevant[i];
2956
- if (/^use[A-Z0-9_]/.test(name) && !PRIMITIVE_HOOK_NAMES.has(name)) {
3082
+ if (isHookFrameName(name) && !PRIMITIVE_HOOK_NAMES.has(name.slice(name.lastIndexOf(".") + 1))) {
2957
3083
  const parent = i + 1 < parentLookup.length ? parentLookup[i + 1] : null;
2958
3084
  out.push({ name, loc: parent ? parent.loc : loc });
2959
3085
  }
@@ -4371,6 +4497,17 @@ function evalFindFibers(filters, helpers, rememberFiber) {
4371
4497
  }, { limit: 2e5 });
4372
4498
  return { count: matches.length, items: matches };
4373
4499
  }
4500
+ async function resolveFindFiberSources(items, getFiber2, resolve) {
4501
+ await Promise.all(items.map(async (item) => {
4502
+ const fiber = item.fiberId == null ? null : getFiber2(String(item.fiberId));
4503
+ if (!fiber) return;
4504
+ try {
4505
+ const source = await resolve(fiber);
4506
+ if (source) item.source = source;
4507
+ } catch {
4508
+ }
4509
+ }));
4510
+ }
4374
4511
  function safeJson(value, depth = 6, seen = /* @__PURE__ */ new WeakSet()) {
4375
4512
  if (value == null) return value;
4376
4513
  const t = typeof value;
@@ -5184,6 +5321,7 @@ function inspectPageDeclarativeBoundaries(max, deps) {
5184
5321
  };
5185
5322
  }
5186
5323
  const runtime = globalThis;
5324
+ const publishedApi = runtime.__AVALANCHE__;
5187
5325
  registerBuiltinPlugins();
5188
5326
  {
5189
5327
  const fromConfig = (_a = runtime.__CHIPPY_STARTUP_CONFIG) == null ? void 0 : _a.plugins;
@@ -5358,6 +5496,7 @@ const api = {
5358
5496
  evalFiberProps,
5359
5497
  evalFiberPropValue,
5360
5498
  evalFindFibers,
5499
+ resolveFindFiberSources,
5361
5500
  safeJson,
5362
5501
  resolveOwnerFiber,
5363
5502
  findFiberByName,
@@ -5377,8 +5516,7 @@ const api = {
5377
5516
  if (__auto) {
5378
5517
  try {
5379
5518
  const requested2 = ((_c = startupConfig == null ? void 0 : startupConfig.avalanche) == null ? void 0 : _c.options) || {};
5380
- const existingApi = runtime.__AVALANCHE__;
5381
- const targetApi = existingApi && typeof existingApi.install === "function" ? existingApi : api;
5519
+ const targetApi = publishedApi && typeof publishedApi.install === "function" ? publishedApi : api;
5382
5520
  const alreadyInstalled = !!(targetApi.status && targetApi.status().installed);
5383
5521
  if (!alreadyInstalled) {
5384
5522
  targetApi.install({
@@ -5390,7 +5528,8 @@ if (__auto) {
5390
5528
  } catch (e) {
5391
5529
  }
5392
5530
  }
5531
+ const iife = publishedApi ?? api;
5393
5532
  export {
5394
- api as default
5533
+ iife as default
5395
5534
  };
5396
5535
  //# sourceMappingURL=avalanche.es.js.map