@reticlehq/vite-plugin 3.2.0 → 3.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.
package/dist/index.cjs CHANGED
@@ -80,8 +80,6 @@ function ensurePairingToken(dir) {
80
80
  // src/index.ts
81
81
  var import_node_os2 = require("node:os");
82
82
  var import_node_path8 = require("node:path");
83
- var import_core6 = require("@babel/core");
84
- var import_babel_plugin = __toESM(require("@reticlehq/babel-plugin"), 1);
85
83
 
86
84
  // ../../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
87
85
  var external_exports = {};
@@ -4124,6 +4122,119 @@ var coerce = {
4124
4122
  };
4125
4123
  var NEVER = INVALID;
4126
4124
 
4125
+ // ../../../core/dist/wire/constants/event-types.js
4126
+ var EventType = {
4127
+ DOM_ADDED: "dom.added",
4128
+ DOM_REMOVED: "dom.removed",
4129
+ DOM_ATTR: "dom.attr",
4130
+ DOM_TEXT: "dom.text",
4131
+ NET_REQUEST: "net.request",
4132
+ NET_PENDING: "net.pending",
4133
+ /** An SSE (EventSource) or WebSocket frame — a message on a long-lived streaming connection. */
4134
+ NET_STREAM: "net.stream",
4135
+ /** A web-perf metric a screenshot can't verify: LCP, cumulative layout shift, or a long task. */
4136
+ PERF: "perf",
4137
+ ROUTE_CHANGE: "route.change",
4138
+ CONSOLE_LOG: "console.log",
4139
+ CONSOLE_WARN: "console.warn",
4140
+ CONSOLE_ERROR: "console.error",
4141
+ CONSOLE_INFO: "console.info",
4142
+ CONSOLE_DEBUG: "console.debug",
4143
+ ERROR_UNCAUGHT: "error.uncaught",
4144
+ VISIBLE_SHOWN: "visible.shown",
4145
+ ANIM_START: "anim.start",
4146
+ ANIM_END: "anim.end",
4147
+ SCROLL_POSITION: "scroll.position",
4148
+ REVEAL_SHOWN: "reveal.shown",
4149
+ SIGNAL: "signal",
4150
+ STATE_CHANGE: "state.change",
4151
+ /** a write to localStorage/sessionStorage/cookies — `data: { area, key, old?, new? }` (values redacted). */
4152
+ STORAGE_CHANGE: "storage.change",
4153
+ /** page-level visibility/focus health (distinct from element-level VISIBLE_*). */
4154
+ PAGE_HEALTH: "page.health",
4155
+ /**
4156
+ * synthetic: the page called window.open, so the consequence of what was just clicked may live in
4157
+ * another browsing context this one cannot observe (an OAuth popup is the archetype).
4158
+ * `data: { href }` — the URL the page asked to open, when it named one.
4159
+ */
4160
+ CONTEXT_OPENED: "context.opened",
4161
+ /**
4162
+ * The app opened a native `alert`/`confirm`/`prompt` while Reticle was driving it.
4163
+ *
4164
+ * Recorded because Reticle ANSWERS these rather than letting them block — a native dialog halts
4165
+ * the main thread, and the SDK's own message pump is on that thread, so one `confirm` behind a
4166
+ * driven click made the tab permanently unresponsive with no recovery from inside the session.
4167
+ * Answering silently would trade a wedge for an invisible one, so the question the app asked, and
4168
+ * the answer given, ride out as an event.
4169
+ */
4170
+ DIALOG_OPENED: "dialog.opened",
4171
+ /** aggregated React commits over a throttle window (dev builds) — `data: { commits }`. Commit storms /
4172
+ * wasted re-renders show up here without a per-render flood. */
4173
+ RENDER_COMMIT: "render.commit",
4174
+ /** element focus moved — `data: { to, from, toBody }`. Focus dropping to body after an act is a regression. */
4175
+ FOCUS_CHANGE: "focus.change",
4176
+ /**
4177
+ * a form field's value moved — `data: { field, kind, value?, redacted?, length }`.
4178
+ *
4179
+ * The DOM observer never saw this: `value` is in its attribute allowlist, but React and every
4180
+ * controlled input set the PROPERTY, so `MutationObserver` does not fire. `value` is omitted and
4181
+ * `redacted` set for a password, a sensitive name, or a payment autocomplete hint; `length` is
4182
+ * always present, because "it was wiped" is assertable without carrying anybody's data.
4183
+ */
4184
+ FIELD_CHANGE: "field.change",
4185
+ /** browser → bridge: a human recording compiled in-page. */
4186
+ FLOW_RECORDED: "flow.recorded",
4187
+ /** synthetic: browser transport queue overflowed; events were dropped. `data: { dropped: number }`. */
4188
+ TRANSPORT_OVERFLOW: "transport.overflow",
4189
+ /**
4190
+ * synthetic: a per-channel cap truncated a batch (e.g. a DOM mutation flood). `data: { channel, dropped }`.
4191
+ * Marks downstream rollups/envelopes as built on incomplete data — a ledger that lies at scale is worse
4192
+ * than no ledger, so truncation is never silent.
4193
+ */
4194
+ TRUNCATED: "truncated",
4195
+ /**
4196
+ * synthetic: the SDK detected a region it CANNOT observe (a cross-origin iframe, a closed shadow root).
4197
+ * `data: { kind: BlindSpotKind, count }`. Surfaced on results as `coverage: partial` so a green never
4198
+ * implies it saw everything.
4199
+ */
4200
+ BLIND_SPOT: "blind-spot",
4201
+ /** synthetic: the SDK ITSELF failed (an observer threw). `data: { site, message, errorType }`.
4202
+ * Rides the existing bridge — no outbound request. See browser/observers/sdk-failure.ts. */
4203
+ SDK_FAILED: "sdk.failed",
4204
+ /**
4205
+ * synthetic (driven only): CDP/Playwright-authoritative network detail for a response the in-page
4206
+ * fetch/XHR wrapper also saw — full response headers + authoritative status/mimeType the page-side
4207
+ * wrapper can't reach. `data: { url, method?, status, headers, resourceType? }`. Merged onto the
4208
+ * matching in-page NET_REQUEST so the driven view never loses fidelity to an outside-in tool.
4209
+ */
4210
+ NET_DETAIL: "net.detail",
4211
+ /**
4212
+ * Live-control: browser → bridge. A human acted on the presenter panel.
4213
+ * `data: { kind: HumanControlKind; text?: string }`. Rides the existing EventMessage.
4214
+ */
4215
+ HUMAN_CONTROL: "human.control",
4216
+ /**
4217
+ * Human review: browser → bridge. A human pinned a mistake to an element on the running page
4218
+ * (the "annotate the bug where you see it" loop). `data` narrows to HumanMarkDataSchema — a note
4219
+ * plus a re-resolvable element anchor (and its source file:line when the framework stamped one) so
4220
+ * the agent that drains the mark knows exactly which element and which source to fix.
4221
+ */
4222
+ HUMAN_MARK: "human.mark",
4223
+ /**
4224
+ * The app produced a FILE — a Blob handed to `URL.createObjectURL`, usually saved by clicking an
4225
+ * anchor with `download`. `data: { filename?, mimeType, bytes, lines?, preview? }`. The one artifact
4226
+ * class no outside-the-browser tool can inspect: it never crosses the network, so there is no
4227
+ * request to intercept. See `observers/download.ts` for the defect that motivated it.
4228
+ */
4229
+ DOWNLOAD: "download",
4230
+ /**
4231
+ * Browser -> bridge: a person used the HUD — pressed a control, or the HUD changed how it sits.
4232
+ * `data` narrows to HudUseDataSchema: names only. Telemetry, not evidence: the session takes it
4233
+ * off the wire before the buffer, so no verdict, settle or agent read ever sees it.
4234
+ */
4235
+ HUD_USED: "hud.used"
4236
+ };
4237
+
4127
4238
  // ../../../core/dist/wire/constants/constants.js
4128
4239
  var RETICLE_DEFAULT_PORT = 4400;
4129
4240
  var RETICLE_WS_PATH = "/reticle";
@@ -4349,6 +4460,14 @@ var ReticleDir = {
4349
4460
  RUNS_SUBDIR: "runs",
4350
4461
  /** fail-to-pass bug capsules —.reticle/capsules/<id>.json (a minimal failing flow + its evidence). */
4351
4462
  CAPSULES_SUBDIR: "capsules",
4463
+ /**
4464
+ * Where a feedback report refused delivery is copied — .reticle/feedback/.
4465
+ *
4466
+ * Named here rather than at the writer because the partition over this object is what decides
4467
+ * whether a name Reticle creates is committed, ignored, or swept. It was spelled as a free string
4468
+ * in three separate files, so the guard could not see it and nobody was ever asked.
4469
+ */
4470
+ FEEDBACK_SUBDIR: "feedback",
4352
4471
  /** durable causal journal —.reticle/sessions/<id>/{events,actions}.jsonl (the substrate). */
4353
4472
  SESSIONS_SUBDIR: "sessions",
4354
4473
  /** append-only event ledger inside a session dir (one ReticleEvent per line). */
@@ -4414,102 +4533,6 @@ var RING_BUFFER_DEFAULTS = {
4414
4533
  MAX_AGE_MS: 6e4,
4415
4534
  MAX_BYTES: TRANSPORT_LIMITS.MAX_BUFFER_BYTES
4416
4535
  };
4417
- var EventType = {
4418
- DOM_ADDED: "dom.added",
4419
- DOM_REMOVED: "dom.removed",
4420
- DOM_ATTR: "dom.attr",
4421
- DOM_TEXT: "dom.text",
4422
- NET_REQUEST: "net.request",
4423
- NET_PENDING: "net.pending",
4424
- /** An SSE (EventSource) or WebSocket frame — a message on a long-lived streaming connection. */
4425
- NET_STREAM: "net.stream",
4426
- /** A web-perf metric a screenshot can't verify: LCP, cumulative layout shift, or a long task. */
4427
- PERF: "perf",
4428
- ROUTE_CHANGE: "route.change",
4429
- CONSOLE_LOG: "console.log",
4430
- CONSOLE_WARN: "console.warn",
4431
- CONSOLE_ERROR: "console.error",
4432
- CONSOLE_INFO: "console.info",
4433
- CONSOLE_DEBUG: "console.debug",
4434
- ERROR_UNCAUGHT: "error.uncaught",
4435
- VISIBLE_SHOWN: "visible.shown",
4436
- ANIM_START: "anim.start",
4437
- ANIM_END: "anim.end",
4438
- SCROLL_POSITION: "scroll.position",
4439
- REVEAL_SHOWN: "reveal.shown",
4440
- SIGNAL: "signal",
4441
- STATE_CHANGE: "state.change",
4442
- /** a write to localStorage/sessionStorage/cookies — `data: { area, key, old?, new? }` (values redacted). */
4443
- STORAGE_CHANGE: "storage.change",
4444
- /** page-level visibility/focus health (distinct from element-level VISIBLE_*). */
4445
- PAGE_HEALTH: "page.health",
4446
- /**
4447
- * synthetic: the page called window.open, so the consequence of what was just clicked may live in
4448
- * another browsing context this one cannot observe (an OAuth popup is the archetype).
4449
- * `data: { href }` — the URL the page asked to open, when it named one.
4450
- */
4451
- CONTEXT_OPENED: "context.opened",
4452
- /**
4453
- * The app opened a native `alert`/`confirm`/`prompt` while Reticle was driving it.
4454
- *
4455
- * Recorded because Reticle ANSWERS these rather than letting them block — a native dialog halts
4456
- * the main thread, and the SDK's own message pump is on that thread, so one `confirm` behind a
4457
- * driven click made the tab permanently unresponsive with no recovery from inside the session.
4458
- * Answering silently would trade a wedge for an invisible one, so the question the app asked, and
4459
- * the answer given, ride out as an event.
4460
- */
4461
- DIALOG_OPENED: "dialog.opened",
4462
- /** aggregated React commits over a throttle window (dev builds) — `data: { commits }`. Commit storms /
4463
- * wasted re-renders show up here without a per-render flood. */
4464
- RENDER_COMMIT: "render.commit",
4465
- /** element focus moved — `data: { to, from, toBody }`. Focus dropping to body after an act is a regression. */
4466
- FOCUS_CHANGE: "focus.change",
4467
- /** browser → bridge: a human recording compiled in-page. */
4468
- FLOW_RECORDED: "flow.recorded",
4469
- /** synthetic: browser transport queue overflowed; events were dropped. `data: { dropped: number }`. */
4470
- TRANSPORT_OVERFLOW: "transport.overflow",
4471
- /**
4472
- * synthetic: a per-channel cap truncated a batch (e.g. a DOM mutation flood). `data: { channel, dropped }`.
4473
- * Marks downstream rollups/envelopes as built on incomplete data — a ledger that lies at scale is worse
4474
- * than no ledger, so truncation is never silent.
4475
- */
4476
- TRUNCATED: "truncated",
4477
- /**
4478
- * synthetic: the SDK detected a region it CANNOT observe (a cross-origin iframe, a closed shadow root).
4479
- * `data: { kind: BlindSpotKind, count }`. Surfaced on results as `coverage: partial` so a green never
4480
- * implies it saw everything.
4481
- */
4482
- BLIND_SPOT: "blind-spot",
4483
- /** synthetic: the SDK ITSELF failed (an observer threw). `data: { site, message, errorType }`.
4484
- * Rides the existing bridge — no outbound request. See browser/observers/sdk-failure.ts. */
4485
- SDK_FAILED: "sdk.failed",
4486
- /**
4487
- * synthetic (driven only): CDP/Playwright-authoritative network detail for a response the in-page
4488
- * fetch/XHR wrapper also saw — full response headers + authoritative status/mimeType the page-side
4489
- * wrapper can't reach. `data: { url, method?, status, headers, resourceType? }`. Merged onto the
4490
- * matching in-page NET_REQUEST so the driven view never loses fidelity to an outside-in tool.
4491
- */
4492
- NET_DETAIL: "net.detail",
4493
- /**
4494
- * Live-control: browser → bridge. A human acted on the presenter panel.
4495
- * `data: { kind: HumanControlKind; text?: string }`. Rides the existing EventMessage.
4496
- */
4497
- HUMAN_CONTROL: "human.control",
4498
- /**
4499
- * Human review: browser → bridge. A human pinned a mistake to an element on the running page
4500
- * (the "annotate the bug where you see it" loop). `data` narrows to HumanMarkDataSchema — a note
4501
- * plus a re-resolvable element anchor (and its source file:line when the framework stamped one) so
4502
- * the agent that drains the mark knows exactly which element and which source to fix.
4503
- */
4504
- HUMAN_MARK: "human.mark",
4505
- /**
4506
- * The app produced a FILE — a Blob handed to `URL.createObjectURL`, usually saved by clicking an
4507
- * anchor with `download`. `data: { filename?, mimeType, bytes, lines?, preview? }`. The one artifact
4508
- * class no outside-the-browser tool can inspect: it never crosses the network, so there is no
4509
- * request to intercept. See `observers/download.ts` for the defect that motivated it.
4510
- */
4511
- DOWNLOAD: "download"
4512
- };
4513
4536
  var RETICLE_RENDER_PREHOOK = "__reticleRenderPreHook";
4514
4537
  var CONSOLE_LEVEL_PREFIX = "console.";
4515
4538
  var CONSOLE_LEVELS = [
@@ -4523,6 +4546,7 @@ var CONSOLE_LEVELS = [
4523
4546
  var DATA_RETICLE_SOURCE_ATTR = "data-reticle-source";
4524
4547
  var RETICLE_ROOT_GLOBAL = "__RETICLE_ROOT__";
4525
4548
  var RETICLE_SDK_VERSION_GLOBAL = "__RETICLE_SDK_VERSION__";
4549
+ var RETICLE_IGNORE_MARKER = "@reticle-ignore";
4526
4550
 
4527
4551
  // ../../../core/dist/identity/project-id.js
4528
4552
  var PROJECT_ID_HASH_LENGTH = 8;
@@ -4811,6 +4835,51 @@ function stampSvelte(code, id, load = defaultLoadCompiler) {
4811
4835
  return out;
4812
4836
  }
4813
4837
 
4838
+ // src/stamping.ts
4839
+ var import_core6 = require("@babel/core");
4840
+ var import_babel_plugin = __toESM(require("@reticlehq/babel-plugin"), 1);
4841
+ var import_ignore = require("@reticlehq/babel-plugin/ignore");
4842
+ var JSX_FILE = /\.[jt]sx$/;
4843
+ var VIRTUAL_PREFIX = "\0";
4844
+ var NODE_MODULES = "node_modules";
4845
+ function stampableId(id) {
4846
+ if (id.startsWith(VIRTUAL_PREFIX)) return null;
4847
+ if (id.includes(NODE_MODULES)) return null;
4848
+ return id.split("?")[0] ?? id;
4849
+ }
4850
+ var TEST_FILE = /(?:\.(?:test|spec)\.[^./]+$)|(?:^|\/)__tests__\//;
4851
+ function shouldStamp(id) {
4852
+ const clean = stampableId(id);
4853
+ if (null === clean) return false;
4854
+ if (TEST_FILE.test(clean)) return false;
4855
+ return JSX_FILE.test(clean);
4856
+ }
4857
+ function shouldStampSvelte(id) {
4858
+ const clean = stampableId(id);
4859
+ return clean !== null && SVELTE_FILE.test(clean);
4860
+ }
4861
+ function stamp(code, id) {
4862
+ const out = (0, import_core6.transformSync)(code, {
4863
+ filename: id,
4864
+ plugins: [import_babel_plugin.default],
4865
+ parserOpts: { plugins: ["jsx", "typescript"] },
4866
+ sourceMaps: true,
4867
+ configFile: false,
4868
+ babelrc: false
4869
+ });
4870
+ if (out?.code === void 0 || null === out.code) return null;
4871
+ return {
4872
+ code: out.code,
4873
+ map: out.map === void 0 || null === out.map ? null : JSON.stringify(out.map)
4874
+ };
4875
+ }
4876
+ function optsOutOfStamping(code, id) {
4877
+ return (shouldStamp(id) || shouldStampSvelte(id)) && (0, import_ignore.isSourceStampingIgnored)(code);
4878
+ }
4879
+ function ignoredFileNotice(id) {
4880
+ return `[reticle] ${id} opts out of source stamping (${RETICLE_IGNORE_MARKER} on its first line) \u2014 elements in it will have no file:line.`;
4881
+ }
4882
+
4814
4883
  // src/installed.ts
4815
4884
  var import_node_fs5 = require("node:fs");
4816
4885
  var import_node_path7 = require("node:path");
@@ -4909,6 +4978,227 @@ function optimizerOptions(key, inherited, add) {
4909
4978
  };
4910
4979
  }
4911
4980
 
4981
+ // src/disabled-browser-stub.ts
4982
+ var RETICLE_DISABLED_STUB = "\0reticle-disabled-stub";
4983
+ var isReticleDisabledWebBuild = (desktop, command) => !desktop && "build" === command;
4984
+ var RETICLE_DISABLED_STUB_CODE = `
4985
+ export class Reticle {
4986
+ connect() {}
4987
+ signal() {}
4988
+ state() {}
4989
+ renderCommit() {}
4990
+ describe() {}
4991
+ endSession() {}
4992
+ observeHotUpdates() {}
4993
+ disconnect() {}
4994
+
4995
+ get connected() {
4996
+ return false;
4997
+ }
4998
+ }
4999
+
5000
+ export const reticle = new Reticle();
5001
+
5002
+ export const SESSION_AUTO = 'auto';
5003
+
5004
+ export function setIgnoreSelectors() {}
5005
+
5006
+ export function registerAdapter() {}
5007
+ export function identifyComponent() {
5008
+ return null;
5009
+ }
5010
+ export function readComponentState() {
5011
+ return undefined;
5012
+ }
5013
+ export function elementHasHoverHandlers() {
5014
+ return false;
5015
+ }
5016
+ export function adapterNames() {
5017
+ return [];
5018
+ }
5019
+
5020
+ export function registerStore() {}
5021
+ export function unregisterStore() {}
5022
+ export function storeNames() {
5023
+ return [];
5024
+ }
5025
+ export function readStores() {
5026
+ return {};
5027
+ }
5028
+ export function readStoresWithTruncation() {
5029
+ return { stores: {} };
5030
+ }
5031
+ export function sourceOwner() {
5032
+ return undefined;
5033
+ }
5034
+ export function markAdapterSource() {}
5035
+
5036
+ export function tanstackQueryStore() {
5037
+ return undefined;
5038
+ }
5039
+ export function jotaiStore() {
5040
+ return undefined;
5041
+ }
5042
+ export function xstateStore() {
5043
+ return undefined;
5044
+ }
5045
+ export function valtioStore() {
5046
+ return undefined;
5047
+ }
5048
+ export function mobxStore() {
5049
+ return undefined;
5050
+ }
5051
+ export function recoilStore() {
5052
+ return undefined;
5053
+ }
5054
+ export function svelteStore() {
5055
+ return undefined;
5056
+ }
5057
+ export function piniaStore() {
5058
+ return undefined;
5059
+ }
5060
+ export function pushStore() {
5061
+ return undefined;
5062
+ }
5063
+
5064
+ export function registerCapabilities() {}
5065
+ export function getCapabilities() {
5066
+ return {};
5067
+ }
5068
+ export function hasCapabilities() {
5069
+ return false;
5070
+ }
5071
+ export function setCapabilitiesListener() {}
5072
+
5073
+ export function createReticleEmitter() {
5074
+ return {
5075
+ signal() {},
5076
+ state() {},
5077
+ };
5078
+ }
5079
+
5080
+ export function commitAndSignal() {}
5081
+ export function registerReticleDomain() {}
5082
+
5083
+ export function buildSnapshot() {
5084
+ return {};
5085
+ }
5086
+
5087
+ export function inspectChart() {
5088
+ return {};
5089
+ }
5090
+ export function canvasChartData() {
5091
+ return {};
5092
+ }
5093
+
5094
+ export function matchQuery() {
5095
+ return false;
5096
+ }
5097
+ export function runQuery() {
5098
+ return undefined;
5099
+ }
5100
+
5101
+ export function executeAction() {
5102
+ return undefined;
5103
+ }
5104
+ export function executeSequence() {
5105
+ return undefined;
5106
+ }
5107
+
5108
+ export function describe() {
5109
+ return undefined;
5110
+ }
5111
+ export function getRole() {
5112
+ return undefined;
5113
+ }
5114
+ export function getAccessibleName() {
5115
+ return undefined;
5116
+ }
5117
+ export function getStates() {
5118
+ return {};
5119
+ }
5120
+ export function isVisible() {
5121
+ return false;
5122
+ }
5123
+
5124
+ export class RefRegistry {
5125
+ get size() {
5126
+ return 0;
5127
+ }
5128
+
5129
+ markEdited() {}
5130
+
5131
+ mintedBeforeLastEdit() {
5132
+ return false;
5133
+ }
5134
+
5135
+ refFor() {
5136
+ return undefined;
5137
+ }
5138
+
5139
+ resolve() {
5140
+ return null;
5141
+ }
5142
+ }
5143
+
5144
+ export const refs = new RefRegistry();
5145
+
5146
+ export class Annotator {
5147
+ constructor() {}
5148
+
5149
+ get active() {
5150
+ return false;
5151
+ }
5152
+
5153
+ get markCount() {
5154
+ return 0;
5155
+ }
5156
+
5157
+ mount() {}
5158
+ unmount() {}
5159
+ }
5160
+
5161
+ export function installAnnotator() {}
5162
+
5163
+ export function resolveMarkAnchor() {
5164
+ return undefined;
5165
+ }
5166
+ `;
5167
+
5168
+ // src/connect-args.ts
5169
+ function connectArgs(options) {
5170
+ const args = {};
5171
+ const port = options.port ?? RETICLE_DEFAULT_PORT;
5172
+ if (port !== RETICLE_DEFAULT_PORT) args["url"] = bridgeWsUrl(port);
5173
+ if (options.session !== void 0) args["session"] = options.session;
5174
+ if (options.projectId !== void 0) args["projectId"] = options.projectId;
5175
+ if (options.token !== void 0) args["token"] = options.token;
5176
+ if (options.root !== void 0 && options.root.length > 0) args["root"] = options.root;
5177
+ if (options.sdkVersion !== void 0 && options.sdkVersion.length > 0) {
5178
+ args["sdkVersion"] = options.sdkVersion;
5179
+ }
5180
+ if (true === options.desktop) args["allowInProduction"] = true;
5181
+ if (true === options.captureNetworkBodies || "1" === process.env["VITE_RETICLE_CAPTURE_BODIES"]) {
5182
+ args["captureNetworkBodies"] = true;
5183
+ }
5184
+ if (false === options.sourceMapping) args["sourceMapping"] = false;
5185
+ if (false === options.captureErrorBodies || "1" === process.env["VITE_RETICLE_NO_ERROR_BODIES"]) {
5186
+ args["captureErrorBodies"] = false;
5187
+ }
5188
+ const envBodyChars = Number(process.env["VITE_RETICLE_BODY_MAX_CHARS"]);
5189
+ const bodyChars = options.networkBodyMaxChars ?? (Number.isFinite(envBodyChars) && envBodyChars > 0 ? envBodyChars : void 0);
5190
+ if (bodyChars !== void 0) {
5191
+ args["networkBodyMaxChars"] = bodyChars;
5192
+ }
5193
+ if (true === options.exposePresenter || "1" === process.env["VITE_RETICLE_EXPOSE_PRESENTER"]) {
5194
+ args["exposePresenter"] = true;
5195
+ }
5196
+ if (true === options.allowNonLocalhost || "1" === process.env["VITE_RETICLE_ALLOW_NON_LOCALHOST"]) {
5197
+ args["allowNonLocalhost"] = true;
5198
+ }
5199
+ return Object.keys(args).length > 0 ? JSON.stringify(args) : "";
5200
+ }
5201
+
4912
5202
  // src/plugin-name.ts
4913
5203
  var RETICLE_VITE_PLUGIN_NAME = "reticle";
4914
5204
 
@@ -4967,9 +5257,6 @@ function isVitestBrowserServer(config) {
4967
5257
  var RETICLE_PACKAGE2 = "@reticlehq/react";
4968
5258
  var RETICLE_SENSOR = "@reticlehq/browser";
4969
5259
  var RETICLE_TOKEN_GLOBAL = "__RETICLE_TOKEN__";
4970
- var JSX_FILE = /\.[jt]sx$/;
4971
- var VIRTUAL_PREFIX = "\0";
4972
- var NODE_MODULES = "node_modules";
4973
5260
  var RETICLE_CONNECT_MODULE = "/@reticle-connect";
4974
5261
  function connectModuleUrl(base) {
4975
5262
  if (void 0 === base || !base.startsWith("/")) return RETICLE_CONNECT_MODULE;
@@ -5004,34 +5291,6 @@ function isHtmlEntry(id, specifier, root) {
5004
5291
  }
5005
5292
  return candidate.endsWith(target.startsWith("/") ? target : `/${target}`);
5006
5293
  }
5007
- function stampableId(id) {
5008
- if (id.startsWith(VIRTUAL_PREFIX)) return null;
5009
- if (id.includes(NODE_MODULES)) return null;
5010
- return id.split("?")[0] ?? id;
5011
- }
5012
- function shouldStamp(id) {
5013
- const clean = stampableId(id);
5014
- return clean !== null && JSX_FILE.test(clean);
5015
- }
5016
- function shouldStampSvelte(id) {
5017
- const clean = stampableId(id);
5018
- return clean !== null && SVELTE_FILE.test(clean);
5019
- }
5020
- function stamp(code, id) {
5021
- const out = (0, import_core6.transformSync)(code, {
5022
- filename: id,
5023
- plugins: [import_babel_plugin.default],
5024
- parserOpts: { plugins: ["jsx", "typescript"] },
5025
- sourceMaps: true,
5026
- configFile: false,
5027
- babelrc: false
5028
- });
5029
- if (out?.code === void 0 || null === out.code) return null;
5030
- return {
5031
- code: out.code,
5032
- map: out.map === void 0 || null === out.map ? null : JSON.stringify(out.map)
5033
- };
5034
- }
5035
5294
  function readPairingToken() {
5036
5295
  const override = process.env[ReticleEnv.PAIRING_TOKEN_DIR];
5037
5296
  const dir = override !== void 0 && override.length > 0 ? override : (0, import_node_path8.join)((0, import_node_os2.homedir)(), ReticleDir.ROOT);
@@ -5046,33 +5305,6 @@ function warnIfTokenMissing(token) {
5046
5305
  }
5047
5306
  return token;
5048
5307
  }
5049
- function connectArgs(options) {
5050
- const args = {};
5051
- const port = options.port ?? RETICLE_DEFAULT_PORT;
5052
- if (port !== RETICLE_DEFAULT_PORT) args["url"] = bridgeWsUrl(port);
5053
- if (options.session !== void 0) args["session"] = options.session;
5054
- if (options.projectId !== void 0) args["projectId"] = options.projectId;
5055
- if (options.token !== void 0) args["token"] = options.token;
5056
- if (options.root !== void 0 && options.root.length > 0) args["root"] = options.root;
5057
- if (options.sdkVersion !== void 0 && options.sdkVersion.length > 0) {
5058
- args["sdkVersion"] = options.sdkVersion;
5059
- }
5060
- if (true === options.desktop) args["allowInProduction"] = true;
5061
- if (true === options.captureNetworkBodies || "1" === process.env["VITE_RETICLE_CAPTURE_BODIES"]) {
5062
- args["captureNetworkBodies"] = true;
5063
- }
5064
- if (false === options.sourceMapping) args["sourceMapping"] = false;
5065
- if (false === options.captureErrorBodies || "1" === process.env["VITE_RETICLE_NO_ERROR_BODIES"]) {
5066
- args["captureErrorBodies"] = false;
5067
- }
5068
- if (true === options.exposePresenter || "1" === process.env["VITE_RETICLE_EXPOSE_PRESENTER"]) {
5069
- args["exposePresenter"] = true;
5070
- }
5071
- if (true === options.allowNonLocalhost || "1" === process.env["VITE_RETICLE_ALLOW_NON_LOCALHOST"]) {
5072
- args["allowNonLocalhost"] = true;
5073
- }
5074
- return Object.keys(args).length > 0 ? JSON.stringify(args) : "";
5075
- }
5076
5308
  var RETICLE_DEV_MODULE_CANDIDATES = [
5077
5309
  "src/reticle-dev.ts",
5078
5310
  "src/reticle-dev.js",
@@ -5135,6 +5367,7 @@ function reticle(options = {}) {
5135
5367
  let vitestBrowser = false;
5136
5368
  const warn = options.onWarn ?? ((message) => globalThis.console.warn(message));
5137
5369
  let injected = false;
5370
+ const announcedIgnored = /* @__PURE__ */ new Set();
5138
5371
  let htmlTransformed = false;
5139
5372
  const resolveLazy = () => {
5140
5373
  const port = resolved.port ?? discoverDaemonPort(resolved.projectId);
@@ -5157,10 +5390,6 @@ function reticle(options = {}) {
5157
5390
  });
5158
5391
  return {
5159
5392
  name: RETICLE_VITE_PLUGIN_NAME,
5160
- // Web: serve-only, so a production bundle can never carry the SDK — gating is the tool's job.
5161
- // Desktop: a packaged renderer IS a production build with no dev server, so the plugin must also
5162
- // run for `vite build` or the shipped app has no connect() at all.
5163
- ...true === options.desktop ? {} : { apply: "serve" },
5164
5393
  enforce: "pre",
5165
5394
  /**
5166
5395
  * Declare the SDK itself and the optimizer cache fingerprint.
@@ -5244,6 +5473,7 @@ function reticle(options = {}) {
5244
5473
  };
5245
5474
  },
5246
5475
  transform(code, id) {
5476
+ if (isReticleDisabledWebBuild(desktop, command)) return null;
5247
5477
  if (desktop && inject && isHtmlEntry(id, htmlEntrySpecifier, root)) {
5248
5478
  injected = true;
5249
5479
  const withConnect = `${connectModuleSource(resolveLazy())}
@@ -5252,6 +5482,13 @@ ${code}`;
5252
5482
  return stamped ?? { code: withConnect, map: null };
5253
5483
  }
5254
5484
  if (!sourceMapping) return null;
5485
+ if (optsOutOfStamping(code, id)) {
5486
+ if (!announcedIgnored.has(id)) {
5487
+ announcedIgnored.add(id);
5488
+ warn(ignoredFileNotice(id));
5489
+ }
5490
+ return null;
5491
+ }
5255
5492
  if (shouldStampSvelte(id)) {
5256
5493
  const stamped = stampSvelte(code, id);
5257
5494
  return null === stamped ? null : { code: stamped, map: null };
@@ -5260,12 +5497,18 @@ ${code}`;
5260
5497
  return stamp(code, id);
5261
5498
  },
5262
5499
  resolveId(id, importer) {
5500
+ if (isReticleDisabledWebBuild(desktop, command) && id === RETICLE_SENSOR) {
5501
+ return RETICLE_DISABLED_STUB;
5502
+ }
5263
5503
  if (desktop && inject && importer !== void 0 && importer.includes(".html")) {
5264
5504
  htmlEntrySpecifier = id;
5265
5505
  }
5266
5506
  return inject && id === RETICLE_CONNECT_MODULE ? RETICLE_CONNECT_MODULE : null;
5267
5507
  },
5268
5508
  load(id) {
5509
+ if (isReticleDisabledWebBuild(desktop, command) && id === RETICLE_DISABLED_STUB) {
5510
+ return RETICLE_DISABLED_STUB_CODE;
5511
+ }
5269
5512
  if (!inject || id !== RETICLE_CONNECT_MODULE) return null;
5270
5513
  const source = currentConnectSource();
5271
5514
  lastServedConnectSource = source;
@@ -5350,6 +5593,7 @@ ${code}`;
5350
5593
  checkHtmlHookForTest: watch.checkHtmlHookRan,
5351
5594
  injectionWatchForTest: watch,
5352
5595
  transformIndexHtml() {
5596
+ if (isReticleDisabledWebBuild(desktop, command)) return [];
5353
5597
  htmlTransformed = true;
5354
5598
  if (desktop && inject && "serve" === command) watch.armDesktopCheck();
5355
5599
  if (!inject || desktop) return [];