hyperframes 0.6.48 → 0.6.50

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/cli.js CHANGED
@@ -54,7 +54,7 @@ var VERSION;
54
54
  var init_version = __esm({
55
55
  "src/version.ts"() {
56
56
  "use strict";
57
- VERSION = true ? "0.6.48" : "0.0.0-dev";
57
+ VERSION = true ? "0.6.50" : "0.0.0-dev";
58
58
  }
59
59
  });
60
60
 
@@ -11820,6 +11820,23 @@ function trackInitTemplate(templateId, props) {
11820
11820
  function trackBrowserInstall() {
11821
11821
  trackEvent("browser_install", {});
11822
11822
  }
11823
+ function trackCliError(props) {
11824
+ trackEvent("cli_error", {
11825
+ error_name: props.error_name,
11826
+ error_message: props.error_message.slice(0, 1e3),
11827
+ stack_trace: props.stack_trace?.slice(0, 2e3),
11828
+ command: props.command,
11829
+ kind: props.kind
11830
+ });
11831
+ }
11832
+ function trackCommandResult(props) {
11833
+ trackEvent("cli_command_result", {
11834
+ command: props.command,
11835
+ success: props.success,
11836
+ exit_code: props.exitCode,
11837
+ duration_ms: props.durationMs
11838
+ });
11839
+ }
11823
11840
  var init_events = __esm({
11824
11841
  "src/telemetry/events.ts"() {
11825
11842
  "use strict";
@@ -26579,6 +26596,12 @@ function patchElementInHtml(source, target, operations) {
26579
26596
  }
26580
26597
  return wrappedFragment ? document2.body.innerHTML || "" : document2.toString();
26581
26598
  }
26599
+ function probeElementInSource(source, target) {
26600
+ if (!target.id && !target.selector) return false;
26601
+ const { document: document2 } = parseSourceDocument(source);
26602
+ const el = findTargetElement(document2, target);
26603
+ return el != null && isHTMLElement(el);
26604
+ }
26582
26605
  var ALLOWED_HTML_ATTRS, DANGEROUS_URI_SCHEMES, DANGEROUS_DATA_URI, URI_ATTRS;
26583
26606
  var init_sourceMutation = __esm({
26584
26607
  "../core/src/studio-api/helpers/sourceMutation.ts"() {
@@ -26670,13 +26693,13 @@ import {
26670
26693
  readdirSync as readdirSync4
26671
26694
  } from "fs";
26672
26695
  import { resolve as resolve9, dirname as dirname6, join as join13 } from "path";
26673
- async function resolveProjectFile(c3, adapter2, opts) {
26696
+ async function resolveProjectPath(c3, adapter2, pathPrefix, opts) {
26674
26697
  const id = c3.req.param("id");
26675
26698
  const project = await adapter2.resolveProject(id);
26676
26699
  if (!project) {
26677
26700
  return { error: c3.json({ error: "not found" }, 404) };
26678
26701
  }
26679
- const filePath = decodeURIComponent(c3.req.path.replace(`/projects/${project.id}/files/`, ""));
26702
+ const filePath = decodeURIComponent(c3.req.path.replace(pathPrefix(project.id), ""));
26680
26703
  if (filePath.includes("\0")) {
26681
26704
  return { error: c3.json({ error: "forbidden" }, 403) };
26682
26705
  }
@@ -26689,6 +26712,26 @@ async function resolveProjectFile(c3, adapter2, opts) {
26689
26712
  }
26690
26713
  return { project, filePath, absPath };
26691
26714
  }
26715
+ function resolveProjectFile(c3, adapter2, opts) {
26716
+ return resolveProjectPath(c3, adapter2, (id) => `/projects/${id}/files/`, opts);
26717
+ }
26718
+ function resolveFileMutationContext(c3, adapter2, operation) {
26719
+ return resolveProjectPath(c3, adapter2, (id) => `/projects/${id}/file-mutations/${operation}/`);
26720
+ }
26721
+ function writeIfChanged(c3, absPath, original, next) {
26722
+ if (next === original) {
26723
+ return c3.json({ ok: true, changed: false, content: original });
26724
+ }
26725
+ writeFileSync8(absPath, next, "utf-8");
26726
+ return c3.json({ ok: true, changed: true, content: next });
26727
+ }
26728
+ async function parseMutationBody(c3) {
26729
+ const body = await c3.req.json().catch(() => null);
26730
+ if (!body?.target) {
26731
+ return { error: c3.json({ error: "target required" }, 400) };
26732
+ }
26733
+ return { target: body.target, body };
26734
+ }
26692
26735
  function ensureDir(filePath) {
26693
26736
  const dir = dirname6(filePath);
26694
26737
  if (!existsSync11(dir)) mkdirSync6(dir, { recursive: true });
@@ -26781,64 +26824,55 @@ function registerFileRoutes(api, adapter2) {
26781
26824
  return c3.json({ ok: true });
26782
26825
  });
26783
26826
  api.post("/projects/:id/file-mutations/remove-element/*", async (c3) => {
26784
- const id = c3.req.param("id");
26785
- const project = await adapter2.resolveProject(id);
26786
- if (!project) return c3.json({ error: "not found" }, 404);
26787
- const filePath = decodeURIComponent(
26788
- c3.req.path.replace(`/projects/${project.id}/file-mutations/remove-element/`, "")
26789
- );
26790
- if (filePath.includes("\0")) {
26791
- return c3.json({ error: "forbidden" }, 403);
26792
- }
26793
- const absPath = resolve9(project.dir, filePath);
26794
- if (!isSafePath(project.dir, absPath)) {
26795
- return c3.json({ error: "forbidden" }, 403);
26796
- }
26797
- if (!existsSync11(absPath)) {
26827
+ const ctx = await resolveFileMutationContext(c3, adapter2, "remove-element");
26828
+ if ("error" in ctx) return ctx.error;
26829
+ if (!existsSync11(ctx.absPath)) {
26798
26830
  return c3.json({ error: "not found" }, 404);
26799
26831
  }
26800
- const body = await c3.req.json().catch(() => null);
26801
- if (!body?.target) {
26802
- return c3.json({ error: "target required" }, 400);
26803
- }
26804
- const originalContent = readFileSync12(absPath, "utf-8");
26805
- const patchedContent = removeElementFromHtml2(originalContent, body.target);
26806
- if (patchedContent === originalContent) {
26807
- return c3.json({ ok: true, changed: false, content: originalContent });
26808
- }
26809
- writeFileSync8(absPath, patchedContent, "utf-8");
26810
- return c3.json({ ok: true, changed: true, content: patchedContent });
26832
+ const parsed = await parseMutationBody(c3);
26833
+ if ("error" in parsed) return parsed.error;
26834
+ const originalContent = readFileSync12(ctx.absPath, "utf-8");
26835
+ return writeIfChanged(
26836
+ c3,
26837
+ ctx.absPath,
26838
+ originalContent,
26839
+ removeElementFromHtml2(originalContent, parsed.target)
26840
+ );
26811
26841
  });
26812
26842
  api.post("/projects/:id/file-mutations/patch-element/*", async (c3) => {
26813
- const id = c3.req.param("id");
26814
- const project = await adapter2.resolveProject(id);
26815
- if (!project) return c3.json({ error: "not found" }, 404);
26816
- const filePath = decodeURIComponent(
26817
- c3.req.path.replace(`/projects/${project.id}/file-mutations/patch-element/`, "")
26818
- );
26819
- if (filePath.includes("\0")) {
26820
- return c3.json({ error: "forbidden" }, 403);
26821
- }
26822
- const absPath = resolve9(project.dir, filePath);
26823
- if (!isSafePath(project.dir, absPath)) {
26824
- return c3.json({ error: "forbidden" }, 403);
26825
- }
26826
- const body = await c3.req.json().catch(() => null);
26827
- if (!body?.target || !Array.isArray(body.operations) || body.operations.length === 0) {
26843
+ const ctx = await resolveFileMutationContext(c3, adapter2, "patch-element");
26844
+ if ("error" in ctx) return ctx.error;
26845
+ const parsed = await parseMutationBody(c3);
26846
+ if ("error" in parsed) return parsed.error;
26847
+ if (!Array.isArray(parsed.body.operations) || parsed.body.operations.length === 0) {
26828
26848
  return c3.json({ error: "target and operations required" }, 400);
26829
26849
  }
26830
26850
  let originalContent;
26831
26851
  try {
26832
- originalContent = readFileSync12(absPath, "utf-8");
26852
+ originalContent = readFileSync12(ctx.absPath, "utf-8");
26833
26853
  } catch {
26834
26854
  return c3.json({ error: "not found" }, 404);
26835
26855
  }
26836
- const patchedContent = patchElementInHtml(originalContent, body.target, body.operations);
26837
- if (patchedContent === originalContent) {
26838
- return c3.json({ ok: true, changed: false, content: originalContent });
26856
+ return writeIfChanged(
26857
+ c3,
26858
+ ctx.absPath,
26859
+ originalContent,
26860
+ patchElementInHtml(originalContent, parsed.target, parsed.body.operations)
26861
+ );
26862
+ });
26863
+ api.post("/projects/:id/file-mutations/probe-element/*", async (c3) => {
26864
+ const ctx = await resolveFileMutationContext(c3, adapter2, "probe-element");
26865
+ if ("error" in ctx) return ctx.error;
26866
+ const parsed = await parseMutationBody(c3);
26867
+ if ("error" in parsed) return parsed.error;
26868
+ let content;
26869
+ try {
26870
+ content = readFileSync12(ctx.absPath, "utf-8");
26871
+ } catch {
26872
+ return c3.json({ exists: false });
26839
26873
  }
26840
- writeFileSync8(absPath, patchedContent, "utf-8");
26841
- return c3.json({ ok: true, changed: true, content: patchedContent });
26874
+ const exists = probeElementInSource(content, parsed.target);
26875
+ return c3.json({ exists });
26842
26876
  });
26843
26877
  api.patch("/projects/:id/files/*", async (c3) => {
26844
26878
  const res = await resolveProjectFile(c3, adapter2, { mustExist: true });
@@ -30086,7 +30120,11 @@ async function pageScreenshotCapture(page, options) {
30086
30120
  format: isPng ? "png" : "jpeg",
30087
30121
  quality: isPng ? void 0 : options.quality ?? 80,
30088
30122
  fromSurface: true,
30089
- captureBeyondViewport: false,
30123
+ // The explicit clip rect constrains output to exact composition
30124
+ // dimensions. The viewport-boundary pre-clip from captureBeyondViewport:
30125
+ // false is redundant, and Chrome's compositor rounds it inward under
30126
+ // multi-tab load — clipping the bottom/right edge of tall viewports.
30127
+ captureBeyondViewport: true,
30090
30128
  optimizeForSpeed: !isPng,
30091
30129
  clip
30092
30130
  });
@@ -30101,7 +30139,8 @@ async function captureScreenshotWithAlpha(page, width, height) {
30101
30139
  const result = await client.send("Page.captureScreenshot", {
30102
30140
  format: "png",
30103
30141
  fromSurface: true,
30104
- captureBeyondViewport: false,
30142
+ captureBeyondViewport: true,
30143
+ // see pageScreenshotCapture for rationale
30105
30144
  optimizeForSpeed: false,
30106
30145
  // `true` uses a zero-alpha-aware fast path that crushes real alpha values — observed empirically, CDP docs don't spell it out
30107
30146
  clip: { x: 0, y: 0, width, height, scale: 1 }
@@ -30130,7 +30169,8 @@ async function captureAlphaPng(page, width, height) {
30130
30169
  const result = await client.send("Page.captureScreenshot", {
30131
30170
  format: "png",
30132
30171
  fromSurface: true,
30133
- captureBeyondViewport: false,
30172
+ captureBeyondViewport: true,
30173
+ // see pageScreenshotCapture for rationale
30134
30174
  optimizeForSpeed: false,
30135
30175
  // must be false to preserve alpha
30136
30176
  clip: { x: 0, y: 0, width, height, scale: 1 }
@@ -99558,7 +99598,9 @@ __export(telemetry_exports2, {
99558
99598
  shouldTrack: () => shouldTrack,
99559
99599
  showTelemetryNotice: () => showTelemetryNotice,
99560
99600
  trackBrowserInstall: () => trackBrowserInstall,
99601
+ trackCliError: () => trackCliError,
99561
99602
  trackCommand: () => trackCommand,
99603
+ trackCommandResult: () => trackCommandResult,
99562
99604
  trackEvent: () => trackEvent,
99563
99605
  trackInitTemplate: () => trackInitTemplate,
99564
99606
  trackRenderComplete: () => trackRenderComplete,
@@ -100097,11 +100139,15 @@ var command = cliCommandArg && cliCommandArg in subCommands ? cliCommandArg : "u
100097
100139
  var hasJsonFlag = process.argv.includes("--json");
100098
100140
  var _flush;
100099
100141
  var _flushSync;
100142
+ var _trackCliError;
100143
+ var _trackCommandResult;
100100
100144
  var _printUpdateNotice;
100101
100145
  if (!isHelp && command !== "telemetry" && command !== "unknown") {
100102
100146
  Promise.resolve().then(() => (init_telemetry2(), telemetry_exports2)).then((mod) => {
100103
100147
  _flush = mod.flush;
100104
100148
  _flushSync = mod.flushSync;
100149
+ _trackCliError = mod.trackCliError;
100150
+ _trackCommandResult = mod.trackCommandResult;
100105
100151
  mod.showTelemetryNotice();
100106
100152
  mod.trackCommand(command);
100107
100153
  if (mod.shouldTrack()) mod.incrementCommandCount();
@@ -100119,13 +100165,44 @@ if (!isHelp && !hasJsonFlag && command !== "upgrade") {
100119
100165
  }
100120
100166
  });
100121
100167
  }
100168
+ var commandStart = Date.now();
100169
+ var commandFailed = false;
100122
100170
  process.on("beforeExit", () => {
100123
100171
  _flush?.().catch(() => {
100124
100172
  });
100125
100173
  if (!hasJsonFlag) _printUpdateNotice?.();
100126
100174
  });
100127
- process.on("exit", () => {
100175
+ process.on("exit", (code) => {
100176
+ _trackCommandResult?.({
100177
+ command,
100178
+ success: code === 0 && !commandFailed,
100179
+ exitCode: code,
100180
+ durationMs: Date.now() - commandStart
100181
+ });
100182
+ _flushSync?.();
100183
+ });
100184
+ process.on("uncaughtException", (error) => {
100185
+ commandFailed = true;
100186
+ _trackCliError?.({
100187
+ error_name: error.name,
100188
+ error_message: error.message,
100189
+ stack_trace: error.stack,
100190
+ command,
100191
+ kind: "uncaught_exception"
100192
+ });
100128
100193
  _flushSync?.();
100194
+ process.exit(1);
100195
+ });
100196
+ process.on("unhandledRejection", (reason) => {
100197
+ commandFailed = true;
100198
+ const error = reason instanceof Error ? reason : new Error(String(reason));
100199
+ _trackCliError?.({
100200
+ error_name: error.name,
100201
+ error_message: error.message,
100202
+ stack_trace: error.stack,
100203
+ command,
100204
+ kind: "unhandled_rejection"
100205
+ });
100129
100206
  });
100130
100207
  async function showUsage3(cmd, parent) {
100131
100208
  const { showUsage: impl } = await Promise.resolve().then(() => (init_help(), help_exports));