@reticlehq/vite-plugin 2.13.1 → 3.1.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 +581 -463
- package/dist/index.d.cts +85 -44
- package/dist/index.d.ts +85 -44
- package/dist/index.js +129 -106
- package/dist/injection-postcondition.d.ts +81 -0
- package/dist/injection-postcondition.js +123 -0
- package/dist/installed.d.ts +2 -1
- package/dist/plugin-name.d.ts +7 -0
- package/dist/plugin-name.js +7 -0
- package/dist/project-id.d.ts +24 -6
- package/dist/project-id.js +67 -22
- package/dist/vitest-browser.d.ts +31 -0
- package/dist/vitest-browser.js +37 -0
- package/dist/watch-ignore.d.ts +18 -0
- package/dist/watch-ignore.js +26 -0
- package/package.json +7 -6
- /package/dist/{ensure-token.d.ts → token/ensure-token.d.ts} +0 -0
- /package/dist/{ensure-token.js → token/ensure-token.js} +0 -0
- /package/dist/{missing-token.d.ts → token/missing-token.d.ts} +0 -0
- /package/dist/{missing-token.js → token/missing-token.js} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -38,6 +38,7 @@ __export(index_exports, {
|
|
|
38
38
|
RETICLE_VITE_PLUGIN_NAME: () => RETICLE_VITE_PLUGIN_NAME,
|
|
39
39
|
connectChurnWarning: () => connectChurnWarning,
|
|
40
40
|
connectModuleSource: () => connectModuleSource,
|
|
41
|
+
connectModuleUrl: () => connectModuleUrl,
|
|
41
42
|
findDevModule: () => findDevModule,
|
|
42
43
|
installedSdk: () => installedSdk,
|
|
43
44
|
readPairingToken: () => readPairingToken,
|
|
@@ -46,13 +47,13 @@ __export(index_exports, {
|
|
|
46
47
|
module.exports = __toCommonJS(index_exports);
|
|
47
48
|
var import_node_fs6 = require("node:fs");
|
|
48
49
|
|
|
49
|
-
// src/missing-token.ts
|
|
50
|
+
// src/token/missing-token.ts
|
|
50
51
|
function missingTokenWarning(token) {
|
|
51
52
|
if (token !== void 0 && token.length > 0) return void 0;
|
|
52
53
|
return "[reticle] no pairing token was available when this dev server started, so the app will connect and be refused \u2014 you will see NO SESSION even though the SDK loads and the socket opens. The token is written by the Reticle daemon: start it (`reticle serve`, or let your agent start it) and then RESTART this dev server, because the value is inlined at config time.";
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
// src/ensure-token.ts
|
|
56
|
+
// src/token/ensure-token.ts
|
|
56
57
|
var import_node_fs = require("node:fs");
|
|
57
58
|
var import_node_crypto = require("node:crypto");
|
|
58
59
|
var import_node_path = require("node:path");
|
|
@@ -82,301 +83,7 @@ var import_node_path8 = require("node:path");
|
|
|
82
83
|
var import_core6 = require("@babel/core");
|
|
83
84
|
var import_babel_plugin = __toESM(require("@reticlehq/babel-plugin"), 1);
|
|
84
85
|
|
|
85
|
-
//
|
|
86
|
-
var RETICLE_DEFAULT_PORT = 4400;
|
|
87
|
-
var RETICLE_WS_PATH = "/reticle";
|
|
88
|
-
var RETICLE_CLIENT_HOST = "localhost";
|
|
89
|
-
function bridgeWsUrl(port = RETICLE_DEFAULT_PORT, host = RETICLE_CLIENT_HOST) {
|
|
90
|
-
return `ws://${host}:${String(port)}${RETICLE_WS_PATH}`;
|
|
91
|
-
}
|
|
92
|
-
var ReticleEnv = {
|
|
93
|
-
/** Shared-secret the browser SDK must present in HELLO; absent ⇒ loopback-trust only. */
|
|
94
|
-
TOKEN: "RETICLE_TOKEN",
|
|
95
|
-
/** Bridge bind host. Defaults to loopback; setting anything else is opt-in remote exposure. */
|
|
96
|
-
HOST: "RETICLE_HOST",
|
|
97
|
-
/** Comma-separated WS Origin allow-list for the bridge. */
|
|
98
|
-
ALLOWED_ORIGINS: "RETICLE_ALLOWED_ORIGINS",
|
|
99
|
-
/** Bridge/daemon WS port override. */
|
|
100
|
-
PORT: "RETICLE_PORT",
|
|
101
|
-
/** Daemon state — pidfiles, discovery registry, logs. Defaults to `~/.reticle`. Overridable
|
|
102
|
-
* because a read-only $HOME (sandboxed agent, locked-down Windows profile, container) otherwise
|
|
103
|
-
* makes the daemon unstartable with a raw EACCES naming nothing. Reported by a Windows user. */
|
|
104
|
-
STATE_DIR: "RETICLE_STATE_DIR",
|
|
105
|
-
/** Attach to an already-running browser over CDP instead of launching one. */
|
|
106
|
-
CDP_URL: "RETICLE_CDP_URL",
|
|
107
|
-
/** Max simultaneous leased headless contexts in the browser pool (resource cap). */
|
|
108
|
-
MAX_CONTEXTS: "RETICLE_MAX_CONTEXTS",
|
|
109
|
-
/**
|
|
110
|
-
* Inbound events per second before the bridge starts SAMPLING (never disconnecting).
|
|
111
|
-
*
|
|
112
|
-
* Raise it for a legitimately busy app — a streaming dashboard, a live grid — rather than accept
|
|
113
|
-
* partial coverage. Free and local: the daemon runs on the same machine, so a higher ceiling costs
|
|
114
|
-
* nothing to anyone.
|
|
115
|
-
*/
|
|
116
|
-
MAX_MESSAGES_PER_SECOND: "RETICLE_MAX_MESSAGES_PER_SECOND",
|
|
117
|
-
/** Bearer token required by the optional `reticle serve --http` verify endpoint. */
|
|
118
|
-
VERIFY_TOKEN: "RETICLE_VERIFY_TOKEN",
|
|
119
|
-
/** Ms of continuous idleness (no agent, no browser session, no lease) before the daemon self-exits;
|
|
120
|
-
* `0` disables. Keeps Reticle from lingering on a user's machine after the editor closes. */
|
|
121
|
-
IDLE_SHUTDOWN: "RETICLE_IDLE_SHUTDOWN_MS",
|
|
122
|
-
/** Idle re-check cadence (default 30s). Overridable so daemon-lifecycle-test can watch a full
|
|
123
|
-
* exit/wake cycle in seconds rather than minutes. */
|
|
124
|
-
IDLE_CHECK: "RETICLE_IDLE_CHECK_MS",
|
|
125
|
-
/**
|
|
126
|
-
* Grace for a daemon with an agent ATTACHED. Longer than the base on purpose: quiet with a client
|
|
127
|
-
* present means a slow install or a thinking human, not an unwanted daemon — a flat 5 minutes was
|
|
128
|
-
* killing live runs mid-install. Derived from the base when unset.
|
|
129
|
-
*/
|
|
130
|
-
IDLE_ATTACHED: "RETICLE_IDLE_ATTACHED_MS",
|
|
131
|
-
/**
|
|
132
|
-
* How often the daemon writes `reticle_daemon_alive`, so a GAP in its log is evidence it died.
|
|
133
|
-
*
|
|
134
|
-
* Overridable for the same reason the idle windows are: a spec that has to prove "a killed daemon
|
|
135
|
-
* is distinguishable from a tidy one" cannot wait 30 seconds per beat, and a spec that instead
|
|
136
|
-
* re-implements the cadence is insensitive to the thing it claims to guard.
|
|
137
|
-
*/
|
|
138
|
-
HEARTBEAT: "RETICLE_HEARTBEAT_MS",
|
|
139
|
-
/** Directory holding the auto-provisioned pairing token. Defaults to ~/.reticle; relocatable for CI. */
|
|
140
|
-
PAIRING_TOKEN_DIR: "RETICLE_PAIRING_TOKEN_DIR",
|
|
141
|
-
/** Force the durable causal journal off (`0`/`false`/`off`) or on (`1`/`true`/`on`); default on. */
|
|
142
|
-
JOURNAL: "RETICLE_JOURNAL",
|
|
143
|
-
/**
|
|
144
|
-
* Verbose internal flow tracing for people working ON Reticle (`1`/`true`/`on`); default off.
|
|
145
|
-
*
|
|
146
|
-
* Distinct from the journal, which records what the AGENT did to the app. This records what
|
|
147
|
-
* RETICLE did to answer it: one line per internal stage, with its duration and nesting, so a
|
|
148
|
-
* developer can see which code a tool call actually went through and where the time went.
|
|
149
|
-
* Off by default and free when off — a trace on every tool call is a cost on the hot path.
|
|
150
|
-
*/
|
|
151
|
-
TRACE: "RETICLE_TRACE",
|
|
152
|
-
/**
|
|
153
|
-
* How many consecutive reconnects the MCP proxy attempts before it stops retrying and goes
|
|
154
|
-
* dormant. Overridable for the same reason the idle windows are: the real budget takes MINUTES to
|
|
155
|
-
* exhaust, so the one spec that proves the proxy SURVIVES exhaustion could not run at all without
|
|
156
|
-
* shortening it. A budget nobody can reach in a test is a budget nobody tests.
|
|
157
|
-
*/
|
|
158
|
-
RECONNECT_ATTEMPTS: "RETICLE_RECONNECT_ATTEMPTS"
|
|
159
|
-
};
|
|
160
|
-
var TRANSPORT_LIMITS = {
|
|
161
|
-
MAX_MESSAGE_BYTES: 1024 * 1024,
|
|
162
|
-
/**
|
|
163
|
-
* Inbound events per second before the bridge SAMPLES rather than records everything.
|
|
164
|
-
*
|
|
165
|
-
* This was 1000, and an ordinary React app with an active query cache blew through it: the
|
|
166
|
-
* reporter's FIRST `act_and_wait` of the session came back `unknown` with `unclean_capture` and a
|
|
167
|
-
* four-figure drop count, and setting the env override to twenty times the default fixed it (#316).
|
|
168
|
-
* Reticle was right to refuse the verdict — a sampled window cannot support one, and the guard that
|
|
169
|
-
* catches false greens is blindest exactly there — but landing that on the first drive after an
|
|
170
|
-
* install, recoverable only by knowing an environment variable exists and inventing a number for
|
|
171
|
-
* it, is the worst possible place to spend the honesty.
|
|
172
|
-
*
|
|
173
|
-
* 20000 is the value that was measured to work on the page that reported it. The cap exists to stop
|
|
174
|
-
* a PATHOLOGICAL page (an animation loop firing DOM mutations every frame), not to throttle a busy
|
|
175
|
-
* one, and the ceiling it has to defend is cheap: the daemon is on the same machine and a typical
|
|
176
|
-
* event is a few hundred bytes, so this is single-digit MB/s over loopback.
|
|
177
|
-
*
|
|
178
|
-
* Raising it does not raise what a runaway page can make the bridge HOLD, and that separation is
|
|
179
|
-
* what makes the change safe. Memory is bounded independently by the ring buffer, which evicts on
|
|
180
|
-
* `RING_BUFFER_DEFAULTS.MAX_BYTES` (this same constant, reached through that alias) as well as on
|
|
181
|
-
* a count and an age. Grep for `MAX_BUFFER_BYTES` alone and it looks like a constant nobody reads,
|
|
182
|
-
* which is exactly the wrong conclusion to draw before touching this number: the rate cap defends
|
|
183
|
-
* parse cost, the ring buffer defends memory, and they are not substitutes.
|
|
184
|
-
*/
|
|
185
|
-
MAX_MESSAGES_PER_SECOND: 2e4,
|
|
186
|
-
MAX_SESSIONS: 32,
|
|
187
|
-
MAX_PENDING_CONNECTIONS: 16,
|
|
188
|
-
HELLO_TIMEOUT_MS: 5e3,
|
|
189
|
-
MAX_BUFFER_BYTES: 8 * 1024 * 1024,
|
|
190
|
-
MAX_SESSION_ID_LENGTH: 128,
|
|
191
|
-
MAX_URL_LENGTH: 4096,
|
|
192
|
-
MAX_TITLE_LENGTH: 512,
|
|
193
|
-
MAX_ADAPTERS: 32,
|
|
194
|
-
MAX_ADAPTER_NAME_LENGTH: 128,
|
|
195
|
-
MAX_TOKEN_LENGTH: 512,
|
|
196
|
-
MAX_COMMAND_ID_LENGTH: 128,
|
|
197
|
-
MAX_COMMAND_NAME_LENGTH: 128,
|
|
198
|
-
MAX_REF_LENGTH: 128,
|
|
199
|
-
MAX_ERROR_LENGTH: 4096,
|
|
200
|
-
/** Cap on a captured stack trace before it crosses the wire — the console observer and both React
|
|
201
|
-
* error hooks (error-boundary, hydration-error) all truncate to this, so it is one fact. */
|
|
202
|
-
MAX_STACK_LENGTH: 4e3,
|
|
203
|
-
MAX_SERIALIZE_DEPTH: 8,
|
|
204
|
-
MAX_COLLECTION_ITEMS: 200,
|
|
205
|
-
MAX_OBJECT_KEYS: 200,
|
|
206
|
-
MAX_STRING_LENGTH: 64 * 1024,
|
|
207
|
-
/** Human review marks: the note the human types when flagging a mistake on the page. */
|
|
208
|
-
MAX_MARK_NOTE_LENGTH: 2e3,
|
|
209
|
-
/** Human review marks: the legible element label that pins the mark (e.g. "Submit button"). */
|
|
210
|
-
MAX_MARK_LABEL_LENGTH: 256
|
|
211
|
-
};
|
|
212
|
-
var ReticleDir = {
|
|
213
|
-
ROOT: ".reticle",
|
|
214
|
-
CONTRACT_FILE: "contract.json",
|
|
215
|
-
FLOWS_SUBDIR: "flows",
|
|
216
|
-
BASELINES_SUBDIR: "baselines",
|
|
217
|
-
/** cross-run memory — outcomes of past runs (the "did it behave like last time?" file). */
|
|
218
|
-
PROJECT_FILE: "project.json",
|
|
219
|
-
/**
|
|
220
|
-
* the user's own record of what Reticle has done for them — .reticle/impact.json.
|
|
221
|
-
*
|
|
222
|
-
* Local only, never uploaded, and deliberately NOT part of telemetry: telemetry answers our
|
|
223
|
-
* questions about the product; this answers the user's question about their own work.
|
|
224
|
-
*/
|
|
225
|
-
IMPACT_FILE: "impact.json",
|
|
226
|
-
/** what changes were SUPPOSED to make true —.reticle/intent.json (git-checked, reviewed) */
|
|
227
|
-
INTENT_FILE: "intent.json",
|
|
228
|
-
/** opt-in pixel baselines —.reticle/visual/<name>.png + <name>.diff.png. */
|
|
229
|
-
VISUAL_SUBDIR: "visual",
|
|
230
|
-
/** verification-run artifacts —.reticle/runs/<runId>.json (the OEM/CI-consumable verdict). */
|
|
231
|
-
RUNS_SUBDIR: "runs",
|
|
232
|
-
/** fail-to-pass bug capsules —.reticle/capsules/<id>.json (a minimal failing flow + its evidence). */
|
|
233
|
-
CAPSULES_SUBDIR: "capsules",
|
|
234
|
-
/** durable causal journal —.reticle/sessions/<id>/{events,actions}.jsonl (the substrate). */
|
|
235
|
-
SESSIONS_SUBDIR: "sessions",
|
|
236
|
-
/** append-only event ledger inside a session dir (one ReticleEvent per line). */
|
|
237
|
-
JOURNAL_EVENTS_FILE: "events.jsonl",
|
|
238
|
-
/** append-only action ledger inside a session dir (one JournalAction per line). */
|
|
239
|
-
JOURNAL_ACTIONS_FILE: "actions.jsonl",
|
|
240
|
-
/** learned expected-envelopes per route, accumulated across runs (the deviation-report baseline). */
|
|
241
|
-
ENVELOPES_FILE: "envelopes.json",
|
|
242
|
-
/** learned ambient (action-less churn) region map — excluded from settle/summaries/envelopes. */
|
|
243
|
-
AMBIENT_FILE: "ambient.json",
|
|
244
|
-
/** per-flow flake ledger — replay outcomes that decide intermittent-failure quarantine. */
|
|
245
|
-
FLAKE_FILE: "flake.json",
|
|
246
|
-
/**
|
|
247
|
-
* the project's cloud binding — .reticle/cloud.json, written by `reticle link`. Git-checked and
|
|
248
|
-
* non-secret: the project id, the API origin, and where its dashboard lives. The KEY lives in
|
|
249
|
-
* ~/.reticle/credentials.json instead, because that one must never reach a repository.
|
|
250
|
-
*/
|
|
251
|
-
CLOUD_LINK_FILE: "cloud.json",
|
|
252
|
-
/**
|
|
253
|
-
* local sync bookkeeping — .reticle/cloud-state.json. The pull cursor, when each half last ran,
|
|
254
|
-
* and the last error. NOT git-checked: it describes this machine's conversation with the server,
|
|
255
|
-
* and committing one machine's cursor would make every other machine skip what it had not seen.
|
|
256
|
-
*/
|
|
257
|
-
CLOUD_STATE_FILE: "cloud-state.json",
|
|
258
|
-
/**
|
|
259
|
-
* triage decisions pulled BACK from the dashboard — .reticle/issues.json. What a human said about
|
|
260
|
-
* a defect ("resolved", "not a bug"), so the HUD stops showing it and the next run does not
|
|
261
|
-
* re-report it as though nobody had looked.
|
|
262
|
-
*/
|
|
263
|
-
ISSUES_FILE: "issues.json",
|
|
264
|
-
/** Per-flow assertion tiers recorded on each PASSING replay — the gate's anti-downgrade baseline. */
|
|
265
|
-
TIERS_FILE: "assertion-tiers.json",
|
|
266
|
-
/**
|
|
267
|
-
* Auto-provisioned bridge pairing token, stored at ~/.reticle/pairing-token (mode 0600). Written by
|
|
268
|
-
* the daemon, read Node-side by the build plugins to inject into connect. A browser sandbox cannot
|
|
269
|
-
* read it, so a rogue localhost app can't present it — that's what stops cross-app session hijack.
|
|
270
|
-
*/
|
|
271
|
-
PAIRING_TOKEN_FILE: "pairing-token"
|
|
272
|
-
};
|
|
273
|
-
var UpdateCheckIntervalMs = 24 * 60 * 60 * 1e3;
|
|
274
|
-
var RING_BUFFER_DEFAULTS = {
|
|
275
|
-
MAX_EVENTS: 2e3,
|
|
276
|
-
MAX_AGE_MS: 6e4,
|
|
277
|
-
MAX_BYTES: TRANSPORT_LIMITS.MAX_BUFFER_BYTES
|
|
278
|
-
};
|
|
279
|
-
var EventType = {
|
|
280
|
-
DOM_ADDED: "dom.added",
|
|
281
|
-
DOM_REMOVED: "dom.removed",
|
|
282
|
-
DOM_ATTR: "dom.attr",
|
|
283
|
-
DOM_TEXT: "dom.text",
|
|
284
|
-
NET_REQUEST: "net.request",
|
|
285
|
-
NET_PENDING: "net.pending",
|
|
286
|
-
/** An SSE (EventSource) or WebSocket frame — a message on a long-lived streaming connection. */
|
|
287
|
-
NET_STREAM: "net.stream",
|
|
288
|
-
/** A web-perf metric a screenshot can't verify: LCP, cumulative layout shift, or a long task. */
|
|
289
|
-
PERF: "perf",
|
|
290
|
-
ROUTE_CHANGE: "route.change",
|
|
291
|
-
CONSOLE_LOG: "console.log",
|
|
292
|
-
CONSOLE_WARN: "console.warn",
|
|
293
|
-
CONSOLE_ERROR: "console.error",
|
|
294
|
-
CONSOLE_INFO: "console.info",
|
|
295
|
-
CONSOLE_DEBUG: "console.debug",
|
|
296
|
-
ERROR_UNCAUGHT: "error.uncaught",
|
|
297
|
-
VISIBLE_SHOWN: "visible.shown",
|
|
298
|
-
ANIM_START: "anim.start",
|
|
299
|
-
ANIM_END: "anim.end",
|
|
300
|
-
SCROLL_POSITION: "scroll.position",
|
|
301
|
-
REVEAL_SHOWN: "reveal.shown",
|
|
302
|
-
SIGNAL: "signal",
|
|
303
|
-
STATE_CHANGE: "state.change",
|
|
304
|
-
/** a write to localStorage/sessionStorage/cookies — `data: { area, key, old?, new? }` (values redacted). */
|
|
305
|
-
STORAGE_CHANGE: "storage.change",
|
|
306
|
-
/** page-level visibility/focus health (distinct from element-level VISIBLE_*). */
|
|
307
|
-
PAGE_HEALTH: "page.health",
|
|
308
|
-
/**
|
|
309
|
-
* synthetic: the page called window.open, so the consequence of what was just clicked may live in
|
|
310
|
-
* another browsing context this one cannot observe (an OAuth popup is the archetype).
|
|
311
|
-
* `data: { href }` — the URL the page asked to open, when it named one.
|
|
312
|
-
*/
|
|
313
|
-
CONTEXT_OPENED: "context.opened",
|
|
314
|
-
/** aggregated React commits over a throttle window (dev builds) — `data: { commits }`. Commit storms /
|
|
315
|
-
* wasted re-renders show up here without a per-render flood. */
|
|
316
|
-
RENDER_COMMIT: "render.commit",
|
|
317
|
-
/** element focus moved — `data: { to, from, toBody }`. Focus dropping to body after an act is a regression. */
|
|
318
|
-
FOCUS_CHANGE: "focus.change",
|
|
319
|
-
/** browser → bridge: a human recording compiled in-page. */
|
|
320
|
-
FLOW_RECORDED: "flow.recorded",
|
|
321
|
-
/** synthetic: browser transport queue overflowed; events were dropped. `data: { dropped: number }`. */
|
|
322
|
-
TRANSPORT_OVERFLOW: "transport.overflow",
|
|
323
|
-
/**
|
|
324
|
-
* synthetic: a per-channel cap truncated a batch (e.g. a DOM mutation flood). `data: { channel, dropped }`.
|
|
325
|
-
* Marks downstream rollups/envelopes as built on incomplete data — a ledger that lies at scale is worse
|
|
326
|
-
* than no ledger, so truncation is never silent.
|
|
327
|
-
*/
|
|
328
|
-
TRUNCATED: "truncated",
|
|
329
|
-
/**
|
|
330
|
-
* synthetic: the SDK detected a region it CANNOT observe (a cross-origin iframe, a closed shadow root).
|
|
331
|
-
* `data: { kind: BlindSpotKind, count }`. Surfaced on results as `coverage: partial` so a green never
|
|
332
|
-
* implies it saw everything.
|
|
333
|
-
*/
|
|
334
|
-
BLIND_SPOT: "blind-spot",
|
|
335
|
-
/** synthetic: the SDK ITSELF failed (an observer threw). `data: { site, message, errorType }`.
|
|
336
|
-
* Rides the existing bridge — no outbound request. See browser/observers/sdk-failure.ts. */
|
|
337
|
-
SDK_FAILED: "sdk.failed",
|
|
338
|
-
/**
|
|
339
|
-
* synthetic (driven only): CDP/Playwright-authoritative network detail for a response the in-page
|
|
340
|
-
* fetch/XHR wrapper also saw — full response headers + authoritative status/mimeType the page-side
|
|
341
|
-
* wrapper can't reach. `data: { url, method?, status, headers, resourceType? }`. Merged onto the
|
|
342
|
-
* matching in-page NET_REQUEST so the driven view never loses fidelity to an outside-in tool.
|
|
343
|
-
*/
|
|
344
|
-
NET_DETAIL: "net.detail",
|
|
345
|
-
/**
|
|
346
|
-
* Live-control: browser → bridge. A human acted on the presenter panel.
|
|
347
|
-
* `data: { kind: HumanControlKind; text?: string }`. Rides the existing EventMessage.
|
|
348
|
-
*/
|
|
349
|
-
HUMAN_CONTROL: "human.control",
|
|
350
|
-
/**
|
|
351
|
-
* Human review: browser → bridge. A human pinned a mistake to an element on the running page
|
|
352
|
-
* (the "annotate the bug where you see it" loop). `data` narrows to HumanMarkDataSchema — a note
|
|
353
|
-
* plus a re-resolvable element anchor (and its source file:line when the framework stamped one) so
|
|
354
|
-
* the agent that drains the mark knows exactly which element and which source to fix.
|
|
355
|
-
*/
|
|
356
|
-
HUMAN_MARK: "human.mark",
|
|
357
|
-
/**
|
|
358
|
-
* The app produced a FILE — a Blob handed to `URL.createObjectURL`, usually saved by clicking an
|
|
359
|
-
* anchor with `download`. `data: { filename?, mimeType, bytes, lines?, preview? }`. The one artifact
|
|
360
|
-
* class no outside-the-browser tool can inspect: it never crosses the network, so there is no
|
|
361
|
-
* request to intercept. See `observers/download.ts` for the defect that motivated it.
|
|
362
|
-
*/
|
|
363
|
-
DOWNLOAD: "download"
|
|
364
|
-
};
|
|
365
|
-
var RETICLE_RENDER_PREHOOK = "__reticleRenderPreHook";
|
|
366
|
-
var CONSOLE_LEVEL_PREFIX = "console.";
|
|
367
|
-
var CONSOLE_LEVELS = [
|
|
368
|
-
EventType.CONSOLE_LOG,
|
|
369
|
-
EventType.CONSOLE_WARN,
|
|
370
|
-
EventType.CONSOLE_ERROR,
|
|
371
|
-
EventType.CONSOLE_INFO
|
|
372
|
-
].map((type) => type.slice(CONSOLE_LEVEL_PREFIX.length));
|
|
373
|
-
|
|
374
|
-
// ../core/dist/source-constants.js
|
|
375
|
-
var DATA_RETICLE_SOURCE_ATTR = "data-reticle-source";
|
|
376
|
-
var RETICLE_ROOT_GLOBAL = "__RETICLE_ROOT__";
|
|
377
|
-
var RETICLE_SDK_VERSION_GLOBAL = "__RETICLE_SDK_VERSION__";
|
|
378
|
-
|
|
379
|
-
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
86
|
+
// ../../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
380
87
|
var external_exports = {};
|
|
381
88
|
__export(external_exports, {
|
|
382
89
|
BRAND: () => BRAND,
|
|
@@ -488,7 +195,7 @@ __export(external_exports, {
|
|
|
488
195
|
void: () => voidType
|
|
489
196
|
});
|
|
490
197
|
|
|
491
|
-
//
|
|
198
|
+
// ../../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js
|
|
492
199
|
var util;
|
|
493
200
|
(function(util2) {
|
|
494
201
|
util2.assertEqual = (_) => {
|
|
@@ -622,7 +329,7 @@ var getParsedType = (data) => {
|
|
|
622
329
|
}
|
|
623
330
|
};
|
|
624
331
|
|
|
625
|
-
//
|
|
332
|
+
// ../../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/ZodError.js
|
|
626
333
|
var ZodIssueCode = util.arrayToEnum([
|
|
627
334
|
"invalid_type",
|
|
628
335
|
"invalid_literal",
|
|
@@ -740,7 +447,7 @@ ZodError.create = (issues) => {
|
|
|
740
447
|
return error;
|
|
741
448
|
};
|
|
742
449
|
|
|
743
|
-
//
|
|
450
|
+
// ../../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/locales/en.js
|
|
744
451
|
var errorMap = (issue, _ctx) => {
|
|
745
452
|
let message;
|
|
746
453
|
switch (issue.code) {
|
|
@@ -843,7 +550,7 @@ var errorMap = (issue, _ctx) => {
|
|
|
843
550
|
};
|
|
844
551
|
var en_default = errorMap;
|
|
845
552
|
|
|
846
|
-
//
|
|
553
|
+
// ../../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/errors.js
|
|
847
554
|
var overrideErrorMap = en_default;
|
|
848
555
|
function setErrorMap(map) {
|
|
849
556
|
overrideErrorMap = map;
|
|
@@ -852,7 +559,7 @@ function getErrorMap() {
|
|
|
852
559
|
return overrideErrorMap;
|
|
853
560
|
}
|
|
854
561
|
|
|
855
|
-
//
|
|
562
|
+
// ../../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
856
563
|
var makeIssue = (params) => {
|
|
857
564
|
const { data, path, errorMaps, issueData } = params;
|
|
858
565
|
const fullPath = [...path, ...issueData.path || []];
|
|
@@ -962,14 +669,14 @@ var isDirty = (x) => x.status === "dirty";
|
|
|
962
669
|
var isValid = (x) => x.status === "valid";
|
|
963
670
|
var isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;
|
|
964
671
|
|
|
965
|
-
//
|
|
672
|
+
// ../../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js
|
|
966
673
|
var errorUtil;
|
|
967
674
|
(function(errorUtil2) {
|
|
968
675
|
errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {};
|
|
969
676
|
errorUtil2.toString = (message) => typeof message === "string" ? message : message?.message;
|
|
970
677
|
})(errorUtil || (errorUtil = {}));
|
|
971
678
|
|
|
972
|
-
//
|
|
679
|
+
// ../../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
|
|
973
680
|
var ParseInputLazyPath = class {
|
|
974
681
|
constructor(parent, value, path, key) {
|
|
975
682
|
this._cachedPath = [];
|
|
@@ -4326,98 +4033,436 @@ function custom(check, _params = {}, fatal) {
|
|
|
4326
4033
|
var late = {
|
|
4327
4034
|
object: ZodObject.lazycreate
|
|
4328
4035
|
};
|
|
4329
|
-
var ZodFirstPartyTypeKind;
|
|
4330
|
-
(function(ZodFirstPartyTypeKind2) {
|
|
4331
|
-
ZodFirstPartyTypeKind2["ZodString"] = "ZodString";
|
|
4332
|
-
ZodFirstPartyTypeKind2["ZodNumber"] = "ZodNumber";
|
|
4333
|
-
ZodFirstPartyTypeKind2["ZodNaN"] = "ZodNaN";
|
|
4334
|
-
ZodFirstPartyTypeKind2["ZodBigInt"] = "ZodBigInt";
|
|
4335
|
-
ZodFirstPartyTypeKind2["ZodBoolean"] = "ZodBoolean";
|
|
4336
|
-
ZodFirstPartyTypeKind2["ZodDate"] = "ZodDate";
|
|
4337
|
-
ZodFirstPartyTypeKind2["ZodSymbol"] = "ZodSymbol";
|
|
4338
|
-
ZodFirstPartyTypeKind2["ZodUndefined"] = "ZodUndefined";
|
|
4339
|
-
ZodFirstPartyTypeKind2["ZodNull"] = "ZodNull";
|
|
4340
|
-
ZodFirstPartyTypeKind2["ZodAny"] = "ZodAny";
|
|
4341
|
-
ZodFirstPartyTypeKind2["ZodUnknown"] = "ZodUnknown";
|
|
4342
|
-
ZodFirstPartyTypeKind2["ZodNever"] = "ZodNever";
|
|
4343
|
-
ZodFirstPartyTypeKind2["ZodVoid"] = "ZodVoid";
|
|
4344
|
-
ZodFirstPartyTypeKind2["ZodArray"] = "ZodArray";
|
|
4345
|
-
ZodFirstPartyTypeKind2["ZodObject"] = "ZodObject";
|
|
4346
|
-
ZodFirstPartyTypeKind2["ZodUnion"] = "ZodUnion";
|
|
4347
|
-
ZodFirstPartyTypeKind2["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion";
|
|
4348
|
-
ZodFirstPartyTypeKind2["ZodIntersection"] = "ZodIntersection";
|
|
4349
|
-
ZodFirstPartyTypeKind2["ZodTuple"] = "ZodTuple";
|
|
4350
|
-
ZodFirstPartyTypeKind2["ZodRecord"] = "ZodRecord";
|
|
4351
|
-
ZodFirstPartyTypeKind2["ZodMap"] = "ZodMap";
|
|
4352
|
-
ZodFirstPartyTypeKind2["ZodSet"] = "ZodSet";
|
|
4353
|
-
ZodFirstPartyTypeKind2["ZodFunction"] = "ZodFunction";
|
|
4354
|
-
ZodFirstPartyTypeKind2["ZodLazy"] = "ZodLazy";
|
|
4355
|
-
ZodFirstPartyTypeKind2["ZodLiteral"] = "ZodLiteral";
|
|
4356
|
-
ZodFirstPartyTypeKind2["ZodEnum"] = "ZodEnum";
|
|
4357
|
-
ZodFirstPartyTypeKind2["ZodEffects"] = "ZodEffects";
|
|
4358
|
-
ZodFirstPartyTypeKind2["ZodNativeEnum"] = "ZodNativeEnum";
|
|
4359
|
-
ZodFirstPartyTypeKind2["ZodOptional"] = "ZodOptional";
|
|
4360
|
-
ZodFirstPartyTypeKind2["ZodNullable"] = "ZodNullable";
|
|
4361
|
-
ZodFirstPartyTypeKind2["ZodDefault"] = "ZodDefault";
|
|
4362
|
-
ZodFirstPartyTypeKind2["ZodCatch"] = "ZodCatch";
|
|
4363
|
-
ZodFirstPartyTypeKind2["ZodPromise"] = "ZodPromise";
|
|
4364
|
-
ZodFirstPartyTypeKind2["ZodBranded"] = "ZodBranded";
|
|
4365
|
-
ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
|
|
4366
|
-
ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
|
|
4367
|
-
})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
|
|
4368
|
-
var instanceOfType = (cls, params = {
|
|
4369
|
-
message: `Input not instance of ${cls.name}`
|
|
4370
|
-
}) => custom((data) => data instanceof cls, params);
|
|
4371
|
-
var stringType = ZodString.create;
|
|
4372
|
-
var numberType = ZodNumber.create;
|
|
4373
|
-
var nanType = ZodNaN.create;
|
|
4374
|
-
var bigIntType = ZodBigInt.create;
|
|
4375
|
-
var booleanType = ZodBoolean.create;
|
|
4376
|
-
var dateType = ZodDate.create;
|
|
4377
|
-
var symbolType = ZodSymbol.create;
|
|
4378
|
-
var undefinedType = ZodUndefined.create;
|
|
4379
|
-
var nullType = ZodNull.create;
|
|
4380
|
-
var anyType = ZodAny.create;
|
|
4381
|
-
var unknownType = ZodUnknown.create;
|
|
4382
|
-
var neverType = ZodNever.create;
|
|
4383
|
-
var voidType = ZodVoid.create;
|
|
4384
|
-
var arrayType = ZodArray.create;
|
|
4385
|
-
var objectType = ZodObject.create;
|
|
4386
|
-
var strictObjectType = ZodObject.strictCreate;
|
|
4387
|
-
var unionType = ZodUnion.create;
|
|
4388
|
-
var discriminatedUnionType = ZodDiscriminatedUnion.create;
|
|
4389
|
-
var intersectionType = ZodIntersection.create;
|
|
4390
|
-
var tupleType = ZodTuple.create;
|
|
4391
|
-
var recordType = ZodRecord.create;
|
|
4392
|
-
var mapType = ZodMap.create;
|
|
4393
|
-
var setType = ZodSet.create;
|
|
4394
|
-
var functionType = ZodFunction.create;
|
|
4395
|
-
var lazyType = ZodLazy.create;
|
|
4396
|
-
var literalType = ZodLiteral.create;
|
|
4397
|
-
var enumType = ZodEnum.create;
|
|
4398
|
-
var nativeEnumType = ZodNativeEnum.create;
|
|
4399
|
-
var promiseType = ZodPromise.create;
|
|
4400
|
-
var effectsType = ZodEffects.create;
|
|
4401
|
-
var optionalType = ZodOptional.create;
|
|
4402
|
-
var nullableType = ZodNullable.create;
|
|
4403
|
-
var preprocessType = ZodEffects.createWithPreprocess;
|
|
4404
|
-
var pipelineType = ZodPipeline.create;
|
|
4405
|
-
var ostring = () => stringType().optional();
|
|
4406
|
-
var onumber = () => numberType().optional();
|
|
4407
|
-
var oboolean = () => booleanType().optional();
|
|
4408
|
-
var coerce = {
|
|
4409
|
-
string: ((arg) => ZodString.create({ ...arg, coerce: true })),
|
|
4410
|
-
number: ((arg) => ZodNumber.create({ ...arg, coerce: true })),
|
|
4411
|
-
boolean: ((arg) => ZodBoolean.create({
|
|
4412
|
-
...arg,
|
|
4413
|
-
coerce: true
|
|
4414
|
-
})),
|
|
4415
|
-
bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })),
|
|
4416
|
-
date: ((arg) => ZodDate.create({ ...arg, coerce: true }))
|
|
4036
|
+
var ZodFirstPartyTypeKind;
|
|
4037
|
+
(function(ZodFirstPartyTypeKind2) {
|
|
4038
|
+
ZodFirstPartyTypeKind2["ZodString"] = "ZodString";
|
|
4039
|
+
ZodFirstPartyTypeKind2["ZodNumber"] = "ZodNumber";
|
|
4040
|
+
ZodFirstPartyTypeKind2["ZodNaN"] = "ZodNaN";
|
|
4041
|
+
ZodFirstPartyTypeKind2["ZodBigInt"] = "ZodBigInt";
|
|
4042
|
+
ZodFirstPartyTypeKind2["ZodBoolean"] = "ZodBoolean";
|
|
4043
|
+
ZodFirstPartyTypeKind2["ZodDate"] = "ZodDate";
|
|
4044
|
+
ZodFirstPartyTypeKind2["ZodSymbol"] = "ZodSymbol";
|
|
4045
|
+
ZodFirstPartyTypeKind2["ZodUndefined"] = "ZodUndefined";
|
|
4046
|
+
ZodFirstPartyTypeKind2["ZodNull"] = "ZodNull";
|
|
4047
|
+
ZodFirstPartyTypeKind2["ZodAny"] = "ZodAny";
|
|
4048
|
+
ZodFirstPartyTypeKind2["ZodUnknown"] = "ZodUnknown";
|
|
4049
|
+
ZodFirstPartyTypeKind2["ZodNever"] = "ZodNever";
|
|
4050
|
+
ZodFirstPartyTypeKind2["ZodVoid"] = "ZodVoid";
|
|
4051
|
+
ZodFirstPartyTypeKind2["ZodArray"] = "ZodArray";
|
|
4052
|
+
ZodFirstPartyTypeKind2["ZodObject"] = "ZodObject";
|
|
4053
|
+
ZodFirstPartyTypeKind2["ZodUnion"] = "ZodUnion";
|
|
4054
|
+
ZodFirstPartyTypeKind2["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion";
|
|
4055
|
+
ZodFirstPartyTypeKind2["ZodIntersection"] = "ZodIntersection";
|
|
4056
|
+
ZodFirstPartyTypeKind2["ZodTuple"] = "ZodTuple";
|
|
4057
|
+
ZodFirstPartyTypeKind2["ZodRecord"] = "ZodRecord";
|
|
4058
|
+
ZodFirstPartyTypeKind2["ZodMap"] = "ZodMap";
|
|
4059
|
+
ZodFirstPartyTypeKind2["ZodSet"] = "ZodSet";
|
|
4060
|
+
ZodFirstPartyTypeKind2["ZodFunction"] = "ZodFunction";
|
|
4061
|
+
ZodFirstPartyTypeKind2["ZodLazy"] = "ZodLazy";
|
|
4062
|
+
ZodFirstPartyTypeKind2["ZodLiteral"] = "ZodLiteral";
|
|
4063
|
+
ZodFirstPartyTypeKind2["ZodEnum"] = "ZodEnum";
|
|
4064
|
+
ZodFirstPartyTypeKind2["ZodEffects"] = "ZodEffects";
|
|
4065
|
+
ZodFirstPartyTypeKind2["ZodNativeEnum"] = "ZodNativeEnum";
|
|
4066
|
+
ZodFirstPartyTypeKind2["ZodOptional"] = "ZodOptional";
|
|
4067
|
+
ZodFirstPartyTypeKind2["ZodNullable"] = "ZodNullable";
|
|
4068
|
+
ZodFirstPartyTypeKind2["ZodDefault"] = "ZodDefault";
|
|
4069
|
+
ZodFirstPartyTypeKind2["ZodCatch"] = "ZodCatch";
|
|
4070
|
+
ZodFirstPartyTypeKind2["ZodPromise"] = "ZodPromise";
|
|
4071
|
+
ZodFirstPartyTypeKind2["ZodBranded"] = "ZodBranded";
|
|
4072
|
+
ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
|
|
4073
|
+
ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
|
|
4074
|
+
})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
|
|
4075
|
+
var instanceOfType = (cls, params = {
|
|
4076
|
+
message: `Input not instance of ${cls.name}`
|
|
4077
|
+
}) => custom((data) => data instanceof cls, params);
|
|
4078
|
+
var stringType = ZodString.create;
|
|
4079
|
+
var numberType = ZodNumber.create;
|
|
4080
|
+
var nanType = ZodNaN.create;
|
|
4081
|
+
var bigIntType = ZodBigInt.create;
|
|
4082
|
+
var booleanType = ZodBoolean.create;
|
|
4083
|
+
var dateType = ZodDate.create;
|
|
4084
|
+
var symbolType = ZodSymbol.create;
|
|
4085
|
+
var undefinedType = ZodUndefined.create;
|
|
4086
|
+
var nullType = ZodNull.create;
|
|
4087
|
+
var anyType = ZodAny.create;
|
|
4088
|
+
var unknownType = ZodUnknown.create;
|
|
4089
|
+
var neverType = ZodNever.create;
|
|
4090
|
+
var voidType = ZodVoid.create;
|
|
4091
|
+
var arrayType = ZodArray.create;
|
|
4092
|
+
var objectType = ZodObject.create;
|
|
4093
|
+
var strictObjectType = ZodObject.strictCreate;
|
|
4094
|
+
var unionType = ZodUnion.create;
|
|
4095
|
+
var discriminatedUnionType = ZodDiscriminatedUnion.create;
|
|
4096
|
+
var intersectionType = ZodIntersection.create;
|
|
4097
|
+
var tupleType = ZodTuple.create;
|
|
4098
|
+
var recordType = ZodRecord.create;
|
|
4099
|
+
var mapType = ZodMap.create;
|
|
4100
|
+
var setType = ZodSet.create;
|
|
4101
|
+
var functionType = ZodFunction.create;
|
|
4102
|
+
var lazyType = ZodLazy.create;
|
|
4103
|
+
var literalType = ZodLiteral.create;
|
|
4104
|
+
var enumType = ZodEnum.create;
|
|
4105
|
+
var nativeEnumType = ZodNativeEnum.create;
|
|
4106
|
+
var promiseType = ZodPromise.create;
|
|
4107
|
+
var effectsType = ZodEffects.create;
|
|
4108
|
+
var optionalType = ZodOptional.create;
|
|
4109
|
+
var nullableType = ZodNullable.create;
|
|
4110
|
+
var preprocessType = ZodEffects.createWithPreprocess;
|
|
4111
|
+
var pipelineType = ZodPipeline.create;
|
|
4112
|
+
var ostring = () => stringType().optional();
|
|
4113
|
+
var onumber = () => numberType().optional();
|
|
4114
|
+
var oboolean = () => booleanType().optional();
|
|
4115
|
+
var coerce = {
|
|
4116
|
+
string: ((arg) => ZodString.create({ ...arg, coerce: true })),
|
|
4117
|
+
number: ((arg) => ZodNumber.create({ ...arg, coerce: true })),
|
|
4118
|
+
boolean: ((arg) => ZodBoolean.create({
|
|
4119
|
+
...arg,
|
|
4120
|
+
coerce: true
|
|
4121
|
+
})),
|
|
4122
|
+
bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })),
|
|
4123
|
+
date: ((arg) => ZodDate.create({ ...arg, coerce: true }))
|
|
4124
|
+
};
|
|
4125
|
+
var NEVER = INVALID;
|
|
4126
|
+
|
|
4127
|
+
// ../../../core/dist/wire/constants/constants.js
|
|
4128
|
+
var RETICLE_DEFAULT_PORT = 4400;
|
|
4129
|
+
var RETICLE_WS_PATH = "/reticle";
|
|
4130
|
+
var RETICLE_CLIENT_HOST = "localhost";
|
|
4131
|
+
function bridgeWsUrl(port = RETICLE_DEFAULT_PORT, host = RETICLE_CLIENT_HOST) {
|
|
4132
|
+
return `ws://${host}:${String(port)}${RETICLE_WS_PATH}`;
|
|
4133
|
+
}
|
|
4134
|
+
var ReticleEnv = {
|
|
4135
|
+
/** Shared-secret the browser SDK must present in HELLO; absent ⇒ loopback-trust only. */
|
|
4136
|
+
TOKEN: "RETICLE_TOKEN",
|
|
4137
|
+
/** Bridge bind host. Defaults to loopback; setting anything else is opt-in remote exposure. */
|
|
4138
|
+
HOST: "RETICLE_HOST",
|
|
4139
|
+
/** Comma-separated WS Origin allow-list for the bridge. */
|
|
4140
|
+
ALLOWED_ORIGINS: "RETICLE_ALLOWED_ORIGINS",
|
|
4141
|
+
/** Bridge/daemon WS port override. */
|
|
4142
|
+
PORT: "RETICLE_PORT",
|
|
4143
|
+
/** Daemon state — pidfiles, discovery registry, logs. Defaults to `~/.reticle`. Overridable
|
|
4144
|
+
* because a read-only $HOME (sandboxed agent, locked-down Windows profile, container) otherwise
|
|
4145
|
+
* makes the daemon unstartable with a raw EACCES naming nothing. Reported by a Windows user. */
|
|
4146
|
+
STATE_DIR: "RETICLE_STATE_DIR",
|
|
4147
|
+
/** Attach to an already-running browser over CDP instead of launching one. */
|
|
4148
|
+
CDP_URL: "RETICLE_CDP_URL",
|
|
4149
|
+
/** Max simultaneous leased headless contexts in the browser pool (resource cap). */
|
|
4150
|
+
MAX_CONTEXTS: "RETICLE_MAX_CONTEXTS",
|
|
4151
|
+
/**
|
|
4152
|
+
* Inbound events per second before the bridge starts SAMPLING (never disconnecting).
|
|
4153
|
+
*
|
|
4154
|
+
* Raise it for a legitimately busy app — a streaming dashboard, a live grid — rather than accept
|
|
4155
|
+
* partial coverage. Free and local: the daemon runs on the same machine, so a higher ceiling costs
|
|
4156
|
+
* nothing to anyone.
|
|
4157
|
+
*/
|
|
4158
|
+
MAX_MESSAGES_PER_SECOND: "RETICLE_MAX_MESSAGES_PER_SECOND",
|
|
4159
|
+
/** Bearer token required by the optional `reticle serve --http` verify endpoint. */
|
|
4160
|
+
VERIFY_TOKEN: "RETICLE_VERIFY_TOKEN",
|
|
4161
|
+
/** Ms of continuous idleness (no agent, no browser session, no lease) before the daemon self-exits;
|
|
4162
|
+
* `0` disables. Keeps Reticle from lingering on a user's machine after the editor closes. */
|
|
4163
|
+
IDLE_SHUTDOWN: "RETICLE_IDLE_SHUTDOWN_MS",
|
|
4164
|
+
/** Idle re-check cadence (default 30s). Overridable so daemon-lifecycle-test can watch a full
|
|
4165
|
+
* exit/wake cycle in seconds rather than minutes. */
|
|
4166
|
+
IDLE_CHECK: "RETICLE_IDLE_CHECK_MS",
|
|
4167
|
+
/**
|
|
4168
|
+
* Grace for a daemon with an agent ATTACHED. Longer than the base on purpose: quiet with a client
|
|
4169
|
+
* present means a slow install or a thinking human, not an unwanted daemon — a flat 5 minutes was
|
|
4170
|
+
* killing live runs mid-install. Derived from the base when unset.
|
|
4171
|
+
*/
|
|
4172
|
+
IDLE_ATTACHED: "RETICLE_IDLE_ATTACHED_MS",
|
|
4173
|
+
/**
|
|
4174
|
+
* How often the daemon writes `reticle_daemon_alive`, so a GAP in its log is evidence it died.
|
|
4175
|
+
*
|
|
4176
|
+
* Overridable for the same reason the idle windows are: a spec that has to prove "a killed daemon
|
|
4177
|
+
* is distinguishable from a tidy one" cannot wait 30 seconds per beat, and a spec that instead
|
|
4178
|
+
* re-implements the cadence is insensitive to the thing it claims to guard.
|
|
4179
|
+
*/
|
|
4180
|
+
HEARTBEAT: "RETICLE_HEARTBEAT_MS",
|
|
4181
|
+
/** Directory holding the auto-provisioned pairing token. Defaults to ~/.reticle; relocatable for CI. */
|
|
4182
|
+
PAIRING_TOKEN_DIR: "RETICLE_PAIRING_TOKEN_DIR",
|
|
4183
|
+
/** Force the durable causal journal off (`0`/`false`/`off`) or on (`1`/`true`/`on`); default on. */
|
|
4184
|
+
JOURNAL: "RETICLE_JOURNAL",
|
|
4185
|
+
/**
|
|
4186
|
+
* Verbose internal flow tracing for people working ON Reticle (`1`/`true`/`on`); default off.
|
|
4187
|
+
*
|
|
4188
|
+
* Distinct from the journal, which records what the AGENT did to the app. This records what
|
|
4189
|
+
* RETICLE did to answer it: one line per internal stage, with its duration and nesting, so a
|
|
4190
|
+
* developer can see which code a tool call actually went through and where the time went.
|
|
4191
|
+
* Off by default and free when off — a trace on every tool call is a cost on the hot path.
|
|
4192
|
+
*/
|
|
4193
|
+
TRACE: "RETICLE_TRACE",
|
|
4194
|
+
/**
|
|
4195
|
+
* How many consecutive reconnects the MCP proxy attempts before it stops retrying and goes
|
|
4196
|
+
* dormant. Overridable for the same reason the idle windows are: the real budget takes MINUTES to
|
|
4197
|
+
* exhaust, so the one spec that proves the proxy SURVIVES exhaustion could not run at all without
|
|
4198
|
+
* shortening it. A budget nobody can reach in a test is a budget nobody tests.
|
|
4199
|
+
*/
|
|
4200
|
+
RECONNECT_ATTEMPTS: "RETICLE_RECONNECT_ATTEMPTS",
|
|
4201
|
+
/** Quiet window before an abandoned MCP stdio proxy exits; `0` disables the watcher. */
|
|
4202
|
+
MCP_PROXY_IDLE: "RETICLE_MCP_PROXY_IDLE_MS",
|
|
4203
|
+
/**
|
|
4204
|
+
* API key for the harness — the model that drives the app when no coding agent is in the loop.
|
|
4205
|
+
*
|
|
4206
|
+
* The standard Anthropic name, on purpose: a machine that can already run a coding agent can
|
|
4207
|
+
* already run the harness, with nothing to configure. Absent ⇒ the harness is simply unavailable
|
|
4208
|
+
* and every other part of Reticle is unaffected.
|
|
4209
|
+
*/
|
|
4210
|
+
HARNESS_KEY: "ANTHROPIC_API_KEY",
|
|
4211
|
+
/** Model the harness drives with. Defaults to a small one — see `DEFAULT_HARNESS_MODEL`. */
|
|
4212
|
+
HARNESS_MODEL: "RETICLE_HARNESS_MODEL",
|
|
4213
|
+
/** Base URL for the harness's model API, for a proxy or a gateway. */
|
|
4214
|
+
HARNESS_BASE_URL: "RETICLE_HARNESS_BASE_URL",
|
|
4215
|
+
/** Hard ceiling on harness model turns in one drive. Bounds cost, not value. */
|
|
4216
|
+
HARNESS_MAX_STEPS: "RETICLE_HARNESS_MAX_STEPS"
|
|
4217
|
+
};
|
|
4218
|
+
var TRANSPORT_LIMITS = {
|
|
4219
|
+
MAX_MESSAGE_BYTES: 1024 * 1024,
|
|
4220
|
+
/**
|
|
4221
|
+
* Inbound events per second before the bridge SAMPLES rather than records everything.
|
|
4222
|
+
*
|
|
4223
|
+
* The cap exists to stop a PATHOLOGICAL page (an animation loop firing DOM mutations every frame),
|
|
4224
|
+
* not to throttle a busy one: an ordinary React app with an active query cache passes 1000/s, and a
|
|
4225
|
+
* sampled window cannot support a verdict, so too low a cap answers `unknown` with
|
|
4226
|
+
* `unclean_capture` on the first `act_and_wait` after an install (#316).
|
|
4227
|
+
*
|
|
4228
|
+
* The ceiling it defends is cheap — the daemon is on the same machine and a typical event is a few
|
|
4229
|
+
* hundred bytes, so 20000/s is single-digit MB/s over loopback. Raising it does not raise what a
|
|
4230
|
+
* runaway page can make the bridge HOLD: memory is bounded independently by the ring buffer, which
|
|
4231
|
+
* evicts on `RING_BUFFER_DEFAULTS.MAX_BYTES` (this same constant through that alias) as well as on
|
|
4232
|
+
* a count and an age. The rate cap defends parse cost, the ring buffer defends memory, and they are
|
|
4233
|
+
* not substitutes.
|
|
4234
|
+
*/
|
|
4235
|
+
MAX_MESSAGES_PER_SECOND: 2e4,
|
|
4236
|
+
MAX_SESSIONS: 32,
|
|
4237
|
+
MAX_PENDING_CONNECTIONS: 16,
|
|
4238
|
+
HELLO_TIMEOUT_MS: 5e3,
|
|
4239
|
+
MAX_BUFFER_BYTES: 8 * 1024 * 1024,
|
|
4240
|
+
MAX_SESSION_ID_LENGTH: 128,
|
|
4241
|
+
MAX_URL_LENGTH: 4096,
|
|
4242
|
+
MAX_TITLE_LENGTH: 512,
|
|
4243
|
+
MAX_ADAPTERS: 32,
|
|
4244
|
+
/*
|
|
4245
|
+
* The cap on ONE vocabulary in `contractParts` — the commands, events or actions a peer says it
|
|
4246
|
+
* speaks.
|
|
4247
|
+
*
|
|
4248
|
+
* Its own number, and the reason is an outage this nearly shipped. `contractParts` reused
|
|
4249
|
+
* `MAX_ADAPTERS` (32), and this implementation's own event vocabulary is larger than that. The
|
|
4250
|
+
* field had never had a producer, so nothing had ever tried: the first build to actually send it
|
|
4251
|
+
* had every HELLO rejected by the schema and NO SESSION COULD CONNECT AT ALL. The unit gate is
|
|
4252
|
+
* blind to it — the schema accepts the shape in isolation, and only a real page talking to a real
|
|
4253
|
+
* daemon fails — so the e2e battery is what caught it.
|
|
4254
|
+
*
|
|
4255
|
+
* 128 leaves the vocabularies room to roughly triple. A cap here is still worth having: this
|
|
4256
|
+
* arrives on an unauthenticated HELLO, so it is an untrusted list and the bound is chosen here.
|
|
4257
|
+
*/
|
|
4258
|
+
MAX_CONTRACT_NAMES: 128,
|
|
4259
|
+
MAX_ADAPTER_NAME_LENGTH: 128,
|
|
4260
|
+
MAX_TOKEN_LENGTH: 512,
|
|
4261
|
+
MAX_COMMAND_ID_LENGTH: 128,
|
|
4262
|
+
MAX_COMMAND_NAME_LENGTH: 128,
|
|
4263
|
+
MAX_REF_LENGTH: 128,
|
|
4264
|
+
MAX_ERROR_LENGTH: 4096,
|
|
4265
|
+
/** Cap on a captured stack trace before it crosses the wire — the console observer and both React
|
|
4266
|
+
* error hooks (error-boundary, hydration-error) all truncate to this, so it is one fact. */
|
|
4267
|
+
MAX_STACK_LENGTH: 4e3,
|
|
4268
|
+
MAX_SERIALIZE_DEPTH: 8,
|
|
4269
|
+
MAX_COLLECTION_ITEMS: 200,
|
|
4270
|
+
MAX_OBJECT_KEYS: 200,
|
|
4271
|
+
MAX_STRING_LENGTH: 64 * 1024,
|
|
4272
|
+
/** Human review marks: the note the human types when flagging a mistake on the page. */
|
|
4273
|
+
MAX_MARK_NOTE_LENGTH: 2e3,
|
|
4274
|
+
/** Human review marks: the legible element label that pins the mark (e.g. "Submit button"). */
|
|
4275
|
+
MAX_MARK_LABEL_LENGTH: 256
|
|
4276
|
+
};
|
|
4277
|
+
var ReticleDir = {
|
|
4278
|
+
ROOT: ".reticle",
|
|
4279
|
+
CONTRACT_FILE: "contract.json",
|
|
4280
|
+
FLOWS_SUBDIR: "flows",
|
|
4281
|
+
BASELINES_SUBDIR: "baselines",
|
|
4282
|
+
/** cross-run memory — outcomes of past runs (the "did it behave like last time?" file). */
|
|
4283
|
+
PROJECT_FILE: "project.json",
|
|
4284
|
+
/**
|
|
4285
|
+
* the user's own record of what Reticle has done for them — .reticle/impact.json.
|
|
4286
|
+
*
|
|
4287
|
+
* Local only, never uploaded, and deliberately NOT part of telemetry: telemetry answers questions
|
|
4288
|
+
* about the product; this answers the user's question about their own work.
|
|
4289
|
+
*/
|
|
4290
|
+
IMPACT_FILE: "impact.json",
|
|
4291
|
+
/** what changes were SUPPOSED to make true —.reticle/intent.json (git-checked, reviewed) */
|
|
4292
|
+
INTENT_FILE: "intent.json",
|
|
4293
|
+
/** opt-in pixel baselines —.reticle/visual/<name>.png + <name>.diff.png. */
|
|
4294
|
+
VISUAL_SUBDIR: "visual",
|
|
4295
|
+
/** verification-run artifacts —.reticle/runs/<runId>.json (the OEM/CI-consumable verdict). */
|
|
4296
|
+
RUNS_SUBDIR: "runs",
|
|
4297
|
+
/** fail-to-pass bug capsules —.reticle/capsules/<id>.json (a minimal failing flow + its evidence). */
|
|
4298
|
+
CAPSULES_SUBDIR: "capsules",
|
|
4299
|
+
/** durable causal journal —.reticle/sessions/<id>/{events,actions}.jsonl (the substrate). */
|
|
4300
|
+
SESSIONS_SUBDIR: "sessions",
|
|
4301
|
+
/** append-only event ledger inside a session dir (one ReticleEvent per line). */
|
|
4302
|
+
JOURNAL_EVENTS_FILE: "events.jsonl",
|
|
4303
|
+
/** append-only action ledger inside a session dir (one JournalAction per line). */
|
|
4304
|
+
JOURNAL_ACTIONS_FILE: "actions.jsonl",
|
|
4305
|
+
/** learned expected-envelopes per route, accumulated across runs (the deviation-report baseline). */
|
|
4306
|
+
ENVELOPES_FILE: "envelopes.json",
|
|
4307
|
+
/** learned ambient (action-less churn) region map — excluded from settle/summaries/envelopes. */
|
|
4308
|
+
AMBIENT_FILE: "ambient.json",
|
|
4309
|
+
/** per-flow flake ledger — replay outcomes that decide intermittent-failure quarantine. */
|
|
4310
|
+
FLAKE_FILE: "flake.json",
|
|
4311
|
+
/**
|
|
4312
|
+
* the project's cloud binding — .reticle/cloud.json, written by `reticle link`. Git-checked and
|
|
4313
|
+
* non-secret: the project id, the API origin, and where its dashboard lives. The KEY lives in
|
|
4314
|
+
* ~/.reticle/credentials.json instead, because that one must never reach a repository.
|
|
4315
|
+
*/
|
|
4316
|
+
CLOUD_LINK_FILE: "cloud.json",
|
|
4317
|
+
/**
|
|
4318
|
+
* local sync bookkeeping — .reticle/cloud-state.json. The pull cursor, when each half last ran,
|
|
4319
|
+
* and the last error. NOT git-checked: it describes this machine's conversation with the server,
|
|
4320
|
+
* and committing one machine's cursor would make every other machine skip what it had not seen.
|
|
4321
|
+
*/
|
|
4322
|
+
CLOUD_STATE_FILE: "cloud-state.json",
|
|
4323
|
+
/**
|
|
4324
|
+
* triage decisions pulled BACK from the dashboard — .reticle/issues.json. What a human said about
|
|
4325
|
+
* a defect ("resolved", "not a bug"), so the HUD stops showing it and the next run does not
|
|
4326
|
+
* re-report it as though nobody had looked.
|
|
4327
|
+
*/
|
|
4328
|
+
ISSUES_FILE: "issues.json",
|
|
4329
|
+
/** Per-flow assertion tiers recorded on each PASSING replay — the gate's anti-downgrade baseline. */
|
|
4330
|
+
TIERS_FILE: "assertion-tiers.json",
|
|
4331
|
+
/**
|
|
4332
|
+
* what to run when something happens — .reticle/hooks.json. Git-checked on purpose: a hook is a
|
|
4333
|
+
* decision the whole team shares, the same way a git hook or a package script is, and one that
|
|
4334
|
+
* only exists on the machine that wrote it is a rule nobody else is following.
|
|
4335
|
+
*
|
|
4336
|
+
* It names COMMANDS, so it is exactly as trusted as `package.json` scripts in the same repository
|
|
4337
|
+
* and no more: opening a repo does not run them, and Reticle runs one only when the event it is
|
|
4338
|
+
* attached to actually happens. The event payload is handed over on stdin, never interpolated into
|
|
4339
|
+
* the command line, so a defect's own text can never become part of a command.
|
|
4340
|
+
*/
|
|
4341
|
+
HOOKS_FILE: "hooks.json",
|
|
4342
|
+
/**
|
|
4343
|
+
* Auto-provisioned bridge pairing token, stored at ~/.reticle/pairing-token (mode 0600). Written by
|
|
4344
|
+
* the daemon, read Node-side by the build plugins to inject into connect. A browser sandbox cannot
|
|
4345
|
+
* read it, so a rogue localhost app can't present it — that's what stops cross-app session hijack.
|
|
4346
|
+
*/
|
|
4347
|
+
PAIRING_TOKEN_FILE: "pairing-token"
|
|
4348
|
+
};
|
|
4349
|
+
var UpdateCheckIntervalMs = 24 * 60 * 60 * 1e3;
|
|
4350
|
+
var RING_BUFFER_DEFAULTS = {
|
|
4351
|
+
MAX_EVENTS: 2e3,
|
|
4352
|
+
MAX_AGE_MS: 6e4,
|
|
4353
|
+
MAX_BYTES: TRANSPORT_LIMITS.MAX_BUFFER_BYTES
|
|
4417
4354
|
};
|
|
4418
|
-
var
|
|
4355
|
+
var EventType = {
|
|
4356
|
+
DOM_ADDED: "dom.added",
|
|
4357
|
+
DOM_REMOVED: "dom.removed",
|
|
4358
|
+
DOM_ATTR: "dom.attr",
|
|
4359
|
+
DOM_TEXT: "dom.text",
|
|
4360
|
+
NET_REQUEST: "net.request",
|
|
4361
|
+
NET_PENDING: "net.pending",
|
|
4362
|
+
/** An SSE (EventSource) or WebSocket frame — a message on a long-lived streaming connection. */
|
|
4363
|
+
NET_STREAM: "net.stream",
|
|
4364
|
+
/** A web-perf metric a screenshot can't verify: LCP, cumulative layout shift, or a long task. */
|
|
4365
|
+
PERF: "perf",
|
|
4366
|
+
ROUTE_CHANGE: "route.change",
|
|
4367
|
+
CONSOLE_LOG: "console.log",
|
|
4368
|
+
CONSOLE_WARN: "console.warn",
|
|
4369
|
+
CONSOLE_ERROR: "console.error",
|
|
4370
|
+
CONSOLE_INFO: "console.info",
|
|
4371
|
+
CONSOLE_DEBUG: "console.debug",
|
|
4372
|
+
ERROR_UNCAUGHT: "error.uncaught",
|
|
4373
|
+
VISIBLE_SHOWN: "visible.shown",
|
|
4374
|
+
ANIM_START: "anim.start",
|
|
4375
|
+
ANIM_END: "anim.end",
|
|
4376
|
+
SCROLL_POSITION: "scroll.position",
|
|
4377
|
+
REVEAL_SHOWN: "reveal.shown",
|
|
4378
|
+
SIGNAL: "signal",
|
|
4379
|
+
STATE_CHANGE: "state.change",
|
|
4380
|
+
/** a write to localStorage/sessionStorage/cookies — `data: { area, key, old?, new? }` (values redacted). */
|
|
4381
|
+
STORAGE_CHANGE: "storage.change",
|
|
4382
|
+
/** page-level visibility/focus health (distinct from element-level VISIBLE_*). */
|
|
4383
|
+
PAGE_HEALTH: "page.health",
|
|
4384
|
+
/**
|
|
4385
|
+
* synthetic: the page called window.open, so the consequence of what was just clicked may live in
|
|
4386
|
+
* another browsing context this one cannot observe (an OAuth popup is the archetype).
|
|
4387
|
+
* `data: { href }` — the URL the page asked to open, when it named one.
|
|
4388
|
+
*/
|
|
4389
|
+
CONTEXT_OPENED: "context.opened",
|
|
4390
|
+
/**
|
|
4391
|
+
* The app opened a native `alert`/`confirm`/`prompt` while Reticle was driving it.
|
|
4392
|
+
*
|
|
4393
|
+
* Recorded because Reticle ANSWERS these rather than letting them block — a native dialog halts
|
|
4394
|
+
* the main thread, and the SDK's own message pump is on that thread, so one `confirm` behind a
|
|
4395
|
+
* driven click made the tab permanently unresponsive with no recovery from inside the session.
|
|
4396
|
+
* Answering silently would trade a wedge for an invisible one, so the question the app asked, and
|
|
4397
|
+
* the answer given, ride out as an event.
|
|
4398
|
+
*/
|
|
4399
|
+
DIALOG_OPENED: "dialog.opened",
|
|
4400
|
+
/** aggregated React commits over a throttle window (dev builds) — `data: { commits }`. Commit storms /
|
|
4401
|
+
* wasted re-renders show up here without a per-render flood. */
|
|
4402
|
+
RENDER_COMMIT: "render.commit",
|
|
4403
|
+
/** element focus moved — `data: { to, from, toBody }`. Focus dropping to body after an act is a regression. */
|
|
4404
|
+
FOCUS_CHANGE: "focus.change",
|
|
4405
|
+
/** browser → bridge: a human recording compiled in-page. */
|
|
4406
|
+
FLOW_RECORDED: "flow.recorded",
|
|
4407
|
+
/** synthetic: browser transport queue overflowed; events were dropped. `data: { dropped: number }`. */
|
|
4408
|
+
TRANSPORT_OVERFLOW: "transport.overflow",
|
|
4409
|
+
/**
|
|
4410
|
+
* synthetic: a per-channel cap truncated a batch (e.g. a DOM mutation flood). `data: { channel, dropped }`.
|
|
4411
|
+
* Marks downstream rollups/envelopes as built on incomplete data — a ledger that lies at scale is worse
|
|
4412
|
+
* than no ledger, so truncation is never silent.
|
|
4413
|
+
*/
|
|
4414
|
+
TRUNCATED: "truncated",
|
|
4415
|
+
/**
|
|
4416
|
+
* synthetic: the SDK detected a region it CANNOT observe (a cross-origin iframe, a closed shadow root).
|
|
4417
|
+
* `data: { kind: BlindSpotKind, count }`. Surfaced on results as `coverage: partial` so a green never
|
|
4418
|
+
* implies it saw everything.
|
|
4419
|
+
*/
|
|
4420
|
+
BLIND_SPOT: "blind-spot",
|
|
4421
|
+
/** synthetic: the SDK ITSELF failed (an observer threw). `data: { site, message, errorType }`.
|
|
4422
|
+
* Rides the existing bridge — no outbound request. See browser/observers/sdk-failure.ts. */
|
|
4423
|
+
SDK_FAILED: "sdk.failed",
|
|
4424
|
+
/**
|
|
4425
|
+
* synthetic (driven only): CDP/Playwright-authoritative network detail for a response the in-page
|
|
4426
|
+
* fetch/XHR wrapper also saw — full response headers + authoritative status/mimeType the page-side
|
|
4427
|
+
* wrapper can't reach. `data: { url, method?, status, headers, resourceType? }`. Merged onto the
|
|
4428
|
+
* matching in-page NET_REQUEST so the driven view never loses fidelity to an outside-in tool.
|
|
4429
|
+
*/
|
|
4430
|
+
NET_DETAIL: "net.detail",
|
|
4431
|
+
/**
|
|
4432
|
+
* Live-control: browser → bridge. A human acted on the presenter panel.
|
|
4433
|
+
* `data: { kind: HumanControlKind; text?: string }`. Rides the existing EventMessage.
|
|
4434
|
+
*/
|
|
4435
|
+
HUMAN_CONTROL: "human.control",
|
|
4436
|
+
/**
|
|
4437
|
+
* Human review: browser → bridge. A human pinned a mistake to an element on the running page
|
|
4438
|
+
* (the "annotate the bug where you see it" loop). `data` narrows to HumanMarkDataSchema — a note
|
|
4439
|
+
* plus a re-resolvable element anchor (and its source file:line when the framework stamped one) so
|
|
4440
|
+
* the agent that drains the mark knows exactly which element and which source to fix.
|
|
4441
|
+
*/
|
|
4442
|
+
HUMAN_MARK: "human.mark",
|
|
4443
|
+
/**
|
|
4444
|
+
* The app produced a FILE — a Blob handed to `URL.createObjectURL`, usually saved by clicking an
|
|
4445
|
+
* anchor with `download`. `data: { filename?, mimeType, bytes, lines?, preview? }`. The one artifact
|
|
4446
|
+
* class no outside-the-browser tool can inspect: it never crosses the network, so there is no
|
|
4447
|
+
* request to intercept. See `observers/download.ts` for the defect that motivated it.
|
|
4448
|
+
*/
|
|
4449
|
+
DOWNLOAD: "download"
|
|
4450
|
+
};
|
|
4451
|
+
var RETICLE_RENDER_PREHOOK = "__reticleRenderPreHook";
|
|
4452
|
+
var CONSOLE_LEVEL_PREFIX = "console.";
|
|
4453
|
+
var CONSOLE_LEVELS = [
|
|
4454
|
+
EventType.CONSOLE_LOG,
|
|
4455
|
+
EventType.CONSOLE_WARN,
|
|
4456
|
+
EventType.CONSOLE_ERROR,
|
|
4457
|
+
EventType.CONSOLE_INFO
|
|
4458
|
+
].map((type) => type.slice(CONSOLE_LEVEL_PREFIX.length));
|
|
4459
|
+
|
|
4460
|
+
// ../../../core/dist/identity/source-constants.js
|
|
4461
|
+
var DATA_RETICLE_SOURCE_ATTR = "data-reticle-source";
|
|
4462
|
+
var RETICLE_ROOT_GLOBAL = "__RETICLE_ROOT__";
|
|
4463
|
+
var RETICLE_SDK_VERSION_GLOBAL = "__RETICLE_SDK_VERSION__";
|
|
4419
4464
|
|
|
4420
|
-
//
|
|
4465
|
+
// ../../../core/dist/identity/project-id.js
|
|
4421
4466
|
var PROJECT_ID_HASH_LENGTH = 8;
|
|
4422
4467
|
var FALLBACK_PROJECT_BASE = "app";
|
|
4423
4468
|
function slugifyPackageName(name) {
|
|
@@ -4447,7 +4492,7 @@ function projectIdFrom(pkgName, rootPath, hash) {
|
|
|
4447
4492
|
return `${base}-${hash(rootPath)}`;
|
|
4448
4493
|
}
|
|
4449
4494
|
|
|
4450
|
-
//
|
|
4495
|
+
// ../../../core/dist/registry/daemon-registry.js
|
|
4451
4496
|
var DAEMON_REGISTRY_PREFIX = "daemon-";
|
|
4452
4497
|
var DAEMON_REGISTRY_SUFFIX = ".json";
|
|
4453
4498
|
function daemonRegistryPort(fileName) {
|
|
@@ -4471,7 +4516,7 @@ function pickDaemonPort(entries, projectId, isAlive2) {
|
|
|
4471
4516
|
return matches[0] ?? null;
|
|
4472
4517
|
}
|
|
4473
4518
|
|
|
4474
|
-
//
|
|
4519
|
+
// ../../../core/dist/registry/dev-server-registry.js
|
|
4475
4520
|
var DEV_SERVER_PREFIX = "devserver-";
|
|
4476
4521
|
var DEV_SERVER_SUFFIX = ".json";
|
|
4477
4522
|
function devServerRegistryFileName(port) {
|
|
@@ -4504,25 +4549,26 @@ var DevServerEntrySchema = external_exports.object({
|
|
|
4504
4549
|
var import_node_crypto2 = require("node:crypto");
|
|
4505
4550
|
var import_node_path2 = require("node:path");
|
|
4506
4551
|
var import_node_fs2 = require("node:fs");
|
|
4552
|
+
var RETICLE_CONFIG_BASENAME = ".reticle.json";
|
|
4507
4553
|
function shortHash(input) {
|
|
4508
4554
|
return (0, import_node_crypto2.createHash)("sha1").update(input).digest("hex").slice(0, PROJECT_ID_HASH_LENGTH);
|
|
4509
4555
|
}
|
|
4510
4556
|
function deriveProjectId(pkgName, rootPath) {
|
|
4511
4557
|
return projectIdFrom(pkgName, rootPath, shortHash);
|
|
4512
4558
|
}
|
|
4513
|
-
|
|
4559
|
+
var readFileOrThrow = (path) => (0, import_node_fs2.readFileSync)(path, "utf8");
|
|
4560
|
+
var MAX_CONFIG_SEARCH_DEPTH = 6;
|
|
4561
|
+
var MAX_PACKAGE_SEARCH_DEPTH = 50;
|
|
4562
|
+
function readNearestField(startDir, basename, field, readFile, maxDepth) {
|
|
4514
4563
|
let dir = startDir;
|
|
4515
|
-
for (let depth = 0; depth
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
const
|
|
4520
|
-
if ("
|
|
4521
|
-
const name = parsed["name"];
|
|
4522
|
-
if ("string" === typeof name && name.length > 0) return name;
|
|
4523
|
-
}
|
|
4524
|
-
} catch {
|
|
4564
|
+
for (let depth = 0; depth <= maxDepth; depth++) {
|
|
4565
|
+
try {
|
|
4566
|
+
const parsed = JSON.parse(readFile((0, import_node_path2.join)(dir, basename)));
|
|
4567
|
+
if ("object" === typeof parsed && parsed !== null) {
|
|
4568
|
+
const value = parsed[field];
|
|
4569
|
+
if ("string" === typeof value && value.length > 0) return value;
|
|
4525
4570
|
}
|
|
4571
|
+
} catch {
|
|
4526
4572
|
}
|
|
4527
4573
|
const parent = (0, import_node_path2.dirname)(dir);
|
|
4528
4574
|
if (parent === dir) break;
|
|
@@ -4530,8 +4576,22 @@ function readNearestPackageName(startDir) {
|
|
|
4530
4576
|
}
|
|
4531
4577
|
return void 0;
|
|
4532
4578
|
}
|
|
4533
|
-
function
|
|
4579
|
+
function readNearestPackageName(startDir, readFile = readFileOrThrow) {
|
|
4580
|
+
return readNearestField(startDir, "package.json", "name", readFile, MAX_PACKAGE_SEARCH_DEPTH);
|
|
4581
|
+
}
|
|
4582
|
+
function readConfiguredProjectId(startDir, readFile = readFileOrThrow) {
|
|
4583
|
+
return readNearestField(
|
|
4584
|
+
startDir,
|
|
4585
|
+
RETICLE_CONFIG_BASENAME,
|
|
4586
|
+
"projectId",
|
|
4587
|
+
readFile,
|
|
4588
|
+
MAX_CONFIG_SEARCH_DEPTH
|
|
4589
|
+
);
|
|
4590
|
+
}
|
|
4591
|
+
function resolveProjectId(explicit, cwd, readPkgName = readNearestPackageName, readConfiguredId = readConfiguredProjectId) {
|
|
4534
4592
|
if (explicit !== void 0 && explicit.length > 0) return explicit;
|
|
4593
|
+
const configured = readConfiguredId(cwd);
|
|
4594
|
+
if (configured !== void 0 && configured.length > 0) return configured;
|
|
4535
4595
|
return deriveProjectId(readPkgName(cwd), cwd);
|
|
4536
4596
|
}
|
|
4537
4597
|
|
|
@@ -4787,8 +4847,61 @@ function optimizerOptions(key, inherited, add) {
|
|
|
4787
4847
|
};
|
|
4788
4848
|
}
|
|
4789
4849
|
|
|
4790
|
-
// src/
|
|
4850
|
+
// src/plugin-name.ts
|
|
4791
4851
|
var RETICLE_VITE_PLUGIN_NAME = "reticle";
|
|
4852
|
+
|
|
4853
|
+
// src/injection-postcondition.ts
|
|
4854
|
+
var DEV_INJECTION_GRACE_MS = 1e4;
|
|
4855
|
+
var notInjectedMessage = () => `[${RETICLE_VITE_PLUGIN_NAME}] could not inject reticle.connect(): the HTML entry module was never matched, so this app carries no instrumentation and will never connect. Check that index.html references your entry with a <script type="module" src="...">, or pass \`inject: false\` and call reticle.connect({ token: __RETICLE_TOKEN__ }) yourself. The plugin still inlines that define; a connect without it is refused.`;
|
|
4856
|
+
var unconfirmedInjectionMessage = () => `[${RETICLE_VITE_PLUGIN_NAME}] could not confirm reticle.connect() was injected: the HTML entry module was not transformed this session. That is expected when Vite served it from its transform cache. If the app does not appear in \`reticle status\`, restart the dev server with \`--force\` to bypass the cache, then check that index.html references your entry with a <script type="module" src="...">.`;
|
|
4857
|
+
var htmlHookNeverRanMessage = () => `[${RETICLE_VITE_PLUGIN_NAME}] this app will never connect: the dev server never asked this plugin to transform any HTML, so reticle.connect() was never added to the page. That usually means your framework renders its own HTML instead of serving index.html \u2014 SvelteKit, Nuxt, Astro, React Router (framework mode) and TanStack Start all do. Fix: import '@reticlehq/browser' and call reticle.connect({ token: __RETICLE_TOKEN__ }) yourself from your app entry file, and pass \`inject: false\` to this plugin so the two do not both try.`;
|
|
4858
|
+
var defaultSchedule = (run, ms) => {
|
|
4859
|
+
const timer = setTimeout(run, ms);
|
|
4860
|
+
timer.unref?.();
|
|
4861
|
+
};
|
|
4862
|
+
function createInjectionWatch(deps) {
|
|
4863
|
+
let htmlRequested = false;
|
|
4864
|
+
const isDocumentRequest = (req) => true === req.headers?.accept?.includes("text/html");
|
|
4865
|
+
const checkHtmlHookRan = () => {
|
|
4866
|
+
if (deps.desktop || !deps.inject || deps.htmlTransformed()) return;
|
|
4867
|
+
if (!htmlRequested) return;
|
|
4868
|
+
deps.warn(htmlHookNeverRanMessage());
|
|
4869
|
+
};
|
|
4870
|
+
const noteHtmlRequest = () => {
|
|
4871
|
+
if (htmlRequested) return;
|
|
4872
|
+
htmlRequested = true;
|
|
4873
|
+
(deps.schedule ?? defaultSchedule)(checkHtmlHookRan, DEV_INJECTION_GRACE_MS);
|
|
4874
|
+
};
|
|
4875
|
+
const checkInjected = () => {
|
|
4876
|
+
if (!deps.desktop || !deps.inject || deps.injected()) return;
|
|
4877
|
+
deps.warn(unconfirmedInjectionMessage());
|
|
4878
|
+
};
|
|
4879
|
+
const armDesktopCheck = () => {
|
|
4880
|
+
(deps.schedule ?? defaultSchedule)(checkInjected, DEV_INJECTION_GRACE_MS);
|
|
4881
|
+
};
|
|
4882
|
+
return { noteHtmlRequest, isDocumentRequest, checkHtmlHookRan, checkInjected, armDesktopCheck };
|
|
4883
|
+
}
|
|
4884
|
+
|
|
4885
|
+
// src/watch-ignore.ts
|
|
4886
|
+
function mergeIgnored(existing, ours) {
|
|
4887
|
+
if (void 0 === existing || null === existing) return [ours];
|
|
4888
|
+
const listed = Array.isArray(existing) ? existing : [existing];
|
|
4889
|
+
return [...listed.filter(isWatchPattern), ours];
|
|
4890
|
+
}
|
|
4891
|
+
function isWatchPattern(value) {
|
|
4892
|
+
return "string" === typeof value || value instanceof RegExp || "function" === typeof value;
|
|
4893
|
+
}
|
|
4894
|
+
|
|
4895
|
+
// src/vitest-browser.ts
|
|
4896
|
+
function isVitestBrowserServer(config) {
|
|
4897
|
+
const test = config.test;
|
|
4898
|
+
if (null === test || "object" !== typeof test) return false;
|
|
4899
|
+
const browser = test.browser;
|
|
4900
|
+
if (null === browser || "object" !== typeof browser) return false;
|
|
4901
|
+
return true === browser.enabled;
|
|
4902
|
+
}
|
|
4903
|
+
|
|
4904
|
+
// src/index.ts
|
|
4792
4905
|
var RETICLE_PACKAGE2 = "@reticlehq/react";
|
|
4793
4906
|
var RETICLE_SENSOR = "@reticlehq/browser";
|
|
4794
4907
|
var RETICLE_TOKEN_GLOBAL = "__RETICLE_TOKEN__";
|
|
@@ -4796,6 +4909,13 @@ var JSX_FILE = /\.[jt]sx$/;
|
|
|
4796
4909
|
var VIRTUAL_PREFIX = "\0";
|
|
4797
4910
|
var NODE_MODULES = "node_modules";
|
|
4798
4911
|
var RETICLE_CONNECT_MODULE = "/@reticle-connect";
|
|
4912
|
+
function connectModuleUrl(base) {
|
|
4913
|
+
if (void 0 === base || !base.startsWith("/")) return RETICLE_CONNECT_MODULE;
|
|
4914
|
+
let end = base.length;
|
|
4915
|
+
while (0 < end && "/" === base[end - 1]) end -= 1;
|
|
4916
|
+
const trimmed = base.slice(0, end);
|
|
4917
|
+
return 0 === trimmed.length ? RETICLE_CONNECT_MODULE : `${trimmed}${RETICLE_CONNECT_MODULE}`;
|
|
4918
|
+
}
|
|
4799
4919
|
var RENDER_PREHOOK_SOURCE = `(function(){try{
|
|
4800
4920
|
var K='__REACT_DEVTOOLS_GLOBAL_HOOK__',P='${RETICLE_RENDER_PREHOOK}';
|
|
4801
4921
|
if(globalThis[P])return;
|
|
@@ -4809,7 +4929,6 @@ onScheduleFiberRoot:function(){},onCommitFiberRoot:fire,onPostCommitFiberRoot:fu
|
|
|
4809
4929
|
}else{var prev=h.onCommitFiberRoot;h.onCommitFiberRoot=function(){try{fire.apply(null,arguments);}catch(e){}
|
|
4810
4930
|
if(typeof prev==='function')return prev.apply(this,arguments);};}
|
|
4811
4931
|
}catch(e){}})();`;
|
|
4812
|
-
var DEV_INJECTION_GRACE_MS = 1e4;
|
|
4813
4932
|
var CONNECT_CHURN_LIMIT = 5;
|
|
4814
4933
|
var connectChurnWarning = () => `[${RETICLE_VITE_PLUGIN_NAME}] the injected connect module has changed ${String(CONNECT_CHURN_LIMIT)} times in one dev-server session. Something it depends on (the bridge port, the pairing token, or a reticle-dev module appearing and disappearing) is not settling, and that can make the page reload repeatedly. Reticle will keep serving the newest version. Please report this at https://github.com/ReticleHQ/reticle/issues with your vite.config and whether more than one daemon is running (\`npx @reticlehq/server status\`).`;
|
|
4815
4934
|
function isHtmlEntry(id, specifier, root) {
|
|
@@ -4880,6 +4999,10 @@ function connectArgs(options) {
|
|
|
4880
4999
|
if (true === options.captureNetworkBodies || "1" === process.env["VITE_RETICLE_CAPTURE_BODIES"]) {
|
|
4881
5000
|
args["captureNetworkBodies"] = true;
|
|
4882
5001
|
}
|
|
5002
|
+
if (false === options.sourceMapping) args["sourceMapping"] = false;
|
|
5003
|
+
if (false === options.captureErrorBodies || "1" === process.env["VITE_RETICLE_NO_ERROR_BODIES"]) {
|
|
5004
|
+
args["captureErrorBodies"] = false;
|
|
5005
|
+
}
|
|
4883
5006
|
if (true === options.exposePresenter || "1" === process.env["VITE_RETICLE_EXPOSE_PRESENTER"]) {
|
|
4884
5007
|
args["exposePresenter"] = true;
|
|
4885
5008
|
}
|
|
@@ -4946,8 +5069,11 @@ function reticle(options = {}) {
|
|
|
4946
5069
|
let htmlEntrySpecifier;
|
|
4947
5070
|
let root;
|
|
4948
5071
|
let command;
|
|
5072
|
+
let base;
|
|
5073
|
+
let vitestBrowser = false;
|
|
4949
5074
|
const warn = options.onWarn ?? ((message) => globalThis.console.warn(message));
|
|
4950
5075
|
let injected = false;
|
|
5076
|
+
let htmlTransformed = false;
|
|
4951
5077
|
const resolveLazy = () => {
|
|
4952
5078
|
const port = resolved.port ?? discoverDaemonPort(resolved.projectId);
|
|
4953
5079
|
const withPort = port !== void 0 ? { ...resolved, port } : resolved;
|
|
@@ -4960,12 +5086,13 @@ function reticle(options = {}) {
|
|
|
4960
5086
|
const currentConnectSource = () => connectModuleSource(resolveLazy(), root === void 0 ? null : findDevModule(root, import_node_fs6.existsSync));
|
|
4961
5087
|
let lastServedConnectSource;
|
|
4962
5088
|
let connectChanges = 0;
|
|
4963
|
-
const
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
5089
|
+
const watch = createInjectionWatch({
|
|
5090
|
+
desktop,
|
|
5091
|
+
inject,
|
|
5092
|
+
injected: () => injected,
|
|
5093
|
+
htmlTransformed: () => htmlTransformed,
|
|
5094
|
+
warn
|
|
5095
|
+
});
|
|
4969
5096
|
return {
|
|
4970
5097
|
name: RETICLE_VITE_PLUGIN_NAME,
|
|
4971
5098
|
// Web: serve-only, so a production bundle can never carry the SDK — gating is the tool's job.
|
|
@@ -4976,9 +5103,9 @@ function reticle(options = {}) {
|
|
|
4976
5103
|
/**
|
|
4977
5104
|
* Declare the SDK itself and the optimizer cache fingerprint.
|
|
4978
5105
|
*
|
|
4979
|
-
* The
|
|
4980
|
-
*
|
|
4981
|
-
*
|
|
5106
|
+
* The SDK itself only. It does not import a second accessibility engine, and naming CJS deps it
|
|
5107
|
+
* does not use would make Vite pre-bundle packages the app may not have, then blame Reticle for a
|
|
5108
|
+
* false `Failed to resolve dependency` warning.
|
|
4982
5109
|
*/
|
|
4983
5110
|
config(config) {
|
|
4984
5111
|
const appRoot = config.root ?? process.cwd();
|
|
@@ -4987,38 +5114,27 @@ function reticle(options = {}) {
|
|
|
4987
5114
|
// Keep the daemon's journal out of the dev server's watcher.
|
|
4988
5115
|
//
|
|
4989
5116
|
// The daemon writes `.reticle/` into the PROJECT root — session journals, and `ambient.json`
|
|
4990
|
-
// rewritten atomically as `ambient.json.tmp` + rename on a live session.
|
|
4991
|
-
//
|
|
4992
|
-
//
|
|
4993
|
-
//
|
|
4994
|
-
// That is a loop with no exit: page loads -> SDK connects and streams events -> daemon
|
|
4995
|
-
// journals them -> Vite reloads the page -> SDK reconnects -> more events. It ran several
|
|
4996
|
-
// times a second for as long as the dev server was up, and the damage was total but
|
|
4997
|
-
// misattributed: every ref went stale, every act_and_wait died mid-flight, and the log
|
|
4998
|
-
// filled with connect/disconnect pairs that looked like a flapping SDK rather than a
|
|
4999
|
-
// watcher chasing its own tail.
|
|
5117
|
+
// rewritten atomically as `ambient.json.tmp` + rename on a live session. Unignored, every
|
|
5118
|
+
// journal write reads as a project file changing and Vite answers with a full page reload,
|
|
5119
|
+
// which is a loop with no exit: page loads -> SDK connects and streams events -> daemon
|
|
5120
|
+
// journals them -> Vite reloads the page -> SDK reconnects.
|
|
5000
5121
|
//
|
|
5001
|
-
// A RegExp,
|
|
5002
|
-
//
|
|
5003
|
-
//
|
|
5004
|
-
// this repo resolves: with the glob, a write to `.reticle/ambient.json` still fires; with
|
|
5005
|
-
// this RegExp it does not, while a normal file still does. Vite's own defaults are globs and
|
|
5006
|
-
// have the same problem, which is why it is not safe to copy their shape here.
|
|
5122
|
+
// A RegExp, NOT a glob: chokidar dropped glob support in v4, and Vite 7+ ships v4/v5, where
|
|
5123
|
+
// `**/.reticle/**` is silently accepted and matches nothing. Vite's own defaults are globs
|
|
5124
|
+
// and have the same problem, so their shape is not safe to copy here.
|
|
5007
5125
|
//
|
|
5008
5126
|
// Anchored on `^` or a separator so it matches the directory and not a file that merely ends
|
|
5009
5127
|
// in those characters, and both separators are accepted because chokidar reports the path in
|
|
5010
|
-
// the platform's own form.
|
|
5011
|
-
//
|
|
5012
|
-
// Appends to the app's list rather than replacing it, so nothing it already excluded is lost.
|
|
5128
|
+
// the platform's own form. Appends to the app's list rather than replacing it.
|
|
5013
5129
|
server: {
|
|
5014
5130
|
watch: {
|
|
5015
|
-
ignored:
|
|
5131
|
+
ignored: mergeIgnored(config.server?.watch?.ignored, JOURNAL_IGNORE)
|
|
5016
5132
|
}
|
|
5017
5133
|
},
|
|
5018
5134
|
// Expose the daemon's pairing token to hand-written connects in the same Vite app. The
|
|
5019
5135
|
// plugin's own injected connect gets the token directly, but a connect the USER writes —
|
|
5020
|
-
// SvelteKit's client hook, a custom entry —
|
|
5021
|
-
//
|
|
5136
|
+
// SvelteKit's client hook, a custom entry — cannot reach a file only Node can read, so it
|
|
5137
|
+
// would call connect() with no credential and the bridge would answer "authentication
|
|
5022
5138
|
// failed". Empty until the daemon has provisioned one; the page reloads once it has.
|
|
5023
5139
|
define: {
|
|
5024
5140
|
...config.define ?? {},
|
|
@@ -5096,30 +5212,28 @@ ${code}`;
|
|
|
5096
5212
|
configResolved(config) {
|
|
5097
5213
|
root = config.root;
|
|
5098
5214
|
command = config.command;
|
|
5215
|
+
base = config.base;
|
|
5216
|
+
vitestBrowser = isVitestBrowserServer(config);
|
|
5099
5217
|
},
|
|
5100
5218
|
/**
|
|
5101
5219
|
* Serve the connect module fresh, every time.
|
|
5102
5220
|
*
|
|
5103
5221
|
* `load` reads the daemon's pairing token at serve time precisely because the daemon may start
|
|
5104
|
-
* after the dev server — but Vite caches the module it produced
|
|
5105
|
-
* from that cache, INCLUDING after a full page reload.
|
|
5106
|
-
* tokenless connect module
|
|
5107
|
-
*
|
|
5108
|
-
*
|
|
5109
|
-
*
|
|
5222
|
+
* after the dev server — but Vite caches the module it produced and answers every later request
|
|
5223
|
+
* from that cache, INCLUDING after a full page reload. A dev server started first therefore keeps
|
|
5224
|
+
* serving a tokenless connect module: the SDK gets a 1008 `authentication failed` and stops
|
|
5225
|
+
* retrying (correctly — a wrong token does not fix itself), so `reticle status` shows no session
|
|
5226
|
+
* while the page demonstrably contains `/@reticle-connect`, and only a dev-server restart clears
|
|
5227
|
+
* it.
|
|
5110
5228
|
*
|
|
5111
5229
|
* Dropping the cached module before it is served makes `load` re-read the token, so starting the
|
|
5112
5230
|
* daemon and reloading the page is enough.
|
|
5113
5231
|
*
|
|
5114
|
-
* Only when the source would ACTUALLY differ, though.
|
|
5115
|
-
*
|
|
5116
|
-
*
|
|
5117
|
-
*
|
|
5118
|
-
*
|
|
5119
|
-
* reloaded the whole page about once a second, `/@reticle-connect` was fetched in every cycle,
|
|
5120
|
-
* and removing the plugin stopped it instantly. Comparing the source first costs one string
|
|
5121
|
-
* compare, keeps the late-daemon fix intact (the token appearing IS a change), and makes the
|
|
5122
|
-
* module inert once it has settled.
|
|
5232
|
+
* Only when the source would ACTUALLY differ, though. A module force-invalidated on every
|
|
5233
|
+
* request is re-resolved against Vite's dep optimizer on every page load, which is a
|
|
5234
|
+
* self-sustaining reload loop: reload → request → invalidate → re-resolve → reload. Comparing
|
|
5235
|
+
* the source first costs one string compare, keeps the late-daemon fix intact (the token
|
|
5236
|
+
* appearing IS a change), and makes the module inert once it has settled.
|
|
5123
5237
|
*/
|
|
5124
5238
|
configureServer(server) {
|
|
5125
5239
|
if (!inject) return;
|
|
@@ -5148,7 +5262,9 @@ ${code}`;
|
|
|
5148
5262
|
announce();
|
|
5149
5263
|
}
|
|
5150
5264
|
server.middlewares.use((req, _res, next) => {
|
|
5151
|
-
if (
|
|
5265
|
+
if (!desktop && watch.isDocumentRequest(req)) watch.noteHtmlRequest();
|
|
5266
|
+
const requestPath = (req.url ?? "").split("?")[0];
|
|
5267
|
+
if (requestPath === RETICLE_CONNECT_MODULE || requestPath === connectModuleUrl(base)) {
|
|
5152
5268
|
if (currentConnectSource() !== lastServedConnectSource) {
|
|
5153
5269
|
connectChanges++;
|
|
5154
5270
|
if (CONNECT_CHURN_LIMIT === connectChanges) warn(connectChurnWarning());
|
|
@@ -5168,13 +5284,14 @@ ${code}`;
|
|
|
5168
5284
|
if (!desktop || !inject || injected) return;
|
|
5169
5285
|
throw new Error(notInjectedMessage());
|
|
5170
5286
|
},
|
|
5171
|
-
checkInjectedForTest: checkInjected,
|
|
5287
|
+
checkInjectedForTest: watch.checkInjected,
|
|
5288
|
+
checkHtmlHookForTest: watch.checkHtmlHookRan,
|
|
5289
|
+
injectionWatchForTest: watch,
|
|
5172
5290
|
transformIndexHtml() {
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
timer.unref?.();
|
|
5176
|
-
}
|
|
5291
|
+
htmlTransformed = true;
|
|
5292
|
+
if (desktop && inject && "serve" === command) watch.armDesktopCheck();
|
|
5177
5293
|
if (!inject || desktop) return [];
|
|
5294
|
+
if (true !== options.inject && vitestBrowser) return [];
|
|
5178
5295
|
return [
|
|
5179
5296
|
// A CLASSIC inline script in <head>, and it has to be both.
|
|
5180
5297
|
//
|
|
@@ -5185,7 +5302,7 @@ ${code}`;
|
|
|
5185
5302
|
// `renderers.size === 0`, so the render meter counted zero forever while the docs advertised
|
|
5186
5303
|
// commit counts. This runs during parse, before any module, and the meter adopts its buffer.
|
|
5187
5304
|
{ tag: "script", children: RENDER_PREHOOK_SOURCE, injectTo: "head-prepend" },
|
|
5188
|
-
{ tag: "script", attrs: { type: "module", src:
|
|
5305
|
+
{ tag: "script", attrs: { type: "module", src: connectModuleUrl(base) }, injectTo: "body" }
|
|
5189
5306
|
];
|
|
5190
5307
|
}
|
|
5191
5308
|
};
|
|
@@ -5200,6 +5317,7 @@ ${code}`;
|
|
|
5200
5317
|
RETICLE_VITE_PLUGIN_NAME,
|
|
5201
5318
|
connectChurnWarning,
|
|
5202
5319
|
connectModuleSource,
|
|
5320
|
+
connectModuleUrl,
|
|
5203
5321
|
findDevModule,
|
|
5204
5322
|
installedSdk,
|
|
5205
5323
|
readPairingToken,
|