@reticlehq/vite-plugin 3.6.0 → 3.8.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/announce.d.ts +2 -0
- package/dist/announce.js +13 -0
- package/dist/disabled-browser-stub.d.ts +1 -1
- package/dist/disabled-browser-stub.js +4 -1
- package/dist/index.cjs +321 -152
- package/dist/index.js +22 -13
- package/dist/project-id.js +25 -4
- package/package.json +4 -4
package/dist/announce.d.ts
CHANGED
|
@@ -14,3 +14,5 @@ export interface AnnounceIo {
|
|
|
14
14
|
* throw inside a signal handler does not degrade, it takes the shutdown with it.
|
|
15
15
|
*/
|
|
16
16
|
export declare function announceDevServer(entry: DevServerEntry, home?: string, io?: AnnounceIo): () => void;
|
|
17
|
+
/** The URL to announce: the dev proxy's when one runs this server, else the one Vite reports. */
|
|
18
|
+
export declare function announcedUrl(env: Readonly<Record<string, string | undefined>>, reported: string | undefined, port: number): string;
|
package/dist/announce.js
CHANGED
|
@@ -58,3 +58,16 @@ export function announceDevServer(entry, home = stateHome(), io = nodeIo) {
|
|
|
58
58
|
}
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* The environment variable a dev proxy sets to the URL the app is served on. portless runs each app,
|
|
63
|
+
* and each git worktree of it, at `https://<branch>.<app>.localhost`, while the dev server listens on
|
|
64
|
+
* a port of its own. The page's origin is the proxy URL, so that is what the daemon must match.
|
|
65
|
+
*/
|
|
66
|
+
const DEV_PROXY_URL_ENV = 'PORTLESS_URL';
|
|
67
|
+
/** The URL to announce: the dev proxy's when one runs this server, else the one Vite reports. */
|
|
68
|
+
export function announcedUrl(env, reported, port) {
|
|
69
|
+
const proxied = env[DEV_PROXY_URL_ENV];
|
|
70
|
+
if (proxied !== undefined && proxied.length > 0)
|
|
71
|
+
return proxied;
|
|
72
|
+
return reported ?? `http://localhost:${String(port)}/`;
|
|
73
|
+
}
|
|
@@ -12,4 +12,4 @@ export declare const RETICLE_DISABLED_STUB = "\0reticle-disabled-stub";
|
|
|
12
12
|
* production, because the cost of guessing wrong the other way is an instrumented release.
|
|
13
13
|
*/
|
|
14
14
|
export declare const isReticleDisabledBuild: (desktop: boolean, command: string | undefined, mode: string | undefined) => boolean;
|
|
15
|
-
export declare const RETICLE_DISABLED_STUB_CODE = "\nexport class Reticle {\n connect() {}\n signal() {}\n state() {}\n renderCommit() {}\n describe() {}\n endSession() {}\n observeHotUpdates() {}\n disconnect() {}\n\n get connected() {\n return false;\n }\n}\n\nexport const reticle = new Reticle();\n\nexport const SESSION_AUTO = 'auto';\n\nexport function setIgnoreSelectors() {}\n\nexport function registerAdapter() {}\nexport function identifyComponent() {\n return null;\n}\nexport function readComponentState() {\n return undefined;\n}\nexport function elementHasHoverHandlers() {\n return false;\n}\nexport function adapterNames() {\n return [];\n}\n\nexport function registerStore() {}\nexport function unregisterStore() {}\nexport function storeNames() {\n return [];\n}\nexport function readStores() {\n return {};\n}\nexport function readStoresWithTruncation() {\n return { stores: {} };\n}\nexport function sourceOwner() {\n return undefined;\n}\nexport function markAdapterSource() {}\n\nexport function tanstackQueryStore() {\n return undefined;\n}\nexport function jotaiStore() {\n return undefined;\n}\nexport function xstateStore() {\n return undefined;\n}\nexport function valtioStore() {\n return undefined;\n}\nexport function mobxStore() {\n return undefined;\n}\nexport function recoilStore() {\n return undefined;\n}\nexport function svelteStore() {\n return undefined;\n}\nexport function piniaStore() {\n return undefined;\n}\nexport function pushStore() {\n return undefined;\n}\n\nexport function registerCapabilities() {}\nexport function getCapabilities() {\n return {};\n}\nexport function hasCapabilities() {\n return false;\n}\nexport function setCapabilitiesListener() {}\n\nexport function createReticleEmitter() {\n return {\n signal() {},\n state() {},\n };\n}\n\nexport function commitAndSignal() {}\nexport function registerReticleDomain() {}\n\nexport function buildSnapshot() {\n return {};\n}\n\nexport function inspectChart() {\n return {};\n}\nexport function canvasChartData() {\n return {};\n}\n\nexport function matchQuery() {\n return false;\n}\nexport function runQuery() {\n return undefined;\n}\n\nexport function executeAction() {\n return undefined;\n}\nexport function executeSequence() {\n return undefined;\n}\n\nexport function describe() {\n return undefined;\n}\nexport function getRole() {\n return undefined;\n}\nexport function getAccessibleName() {\n return undefined;\n}\nexport function getStates() {\n return {};\n}\nexport function isVisible() {\n return false;\n}\n\nexport class RefRegistry {\n get size() {\n return 0;\n }\n\n markEdited() {}\n\n mintedBeforeLastEdit() {\n return false;\n }\n\n refFor() {\n return undefined;\n }\n\n resolve() {\n return null;\n }\n}\n\nexport const refs = new RefRegistry();\n\nexport class Annotator {\n constructor() {}\n\n get active() {\n return false;\n }\n\n get markCount() {\n return 0;\n }\n\n mount() {}\n unmount() {}\n}\n\nexport function installAnnotator() {}\n\nexport function resolveMarkAnchor() {\n return undefined;\n}\n";
|
|
15
|
+
export declare const RETICLE_DISABLED_STUB_CODE = "\nexport class Reticle {\n connect() {}\n signal() {}\n state() {}\n renderCommit() {}\n describe() {}\n endSession() {}\n observeHotUpdates() {}\n disconnect() {}\n\n get connected() {\n return false;\n }\n}\n\nexport const reticle = new Reticle();\n\nexport const SESSION_AUTO = 'auto';\n\nexport function setIgnoreSelectors() {}\n\nexport function registerAdapter() {}\nexport function identifyComponent() {\n return null;\n}\nexport function readComponentState() {\n return undefined;\n}\nexport function elementHasHoverHandlers() {\n return false;\n}\nexport function adapterNames() {\n return [];\n}\n\nexport function registerStore() {}\nexport function unregisterStore() {}\nexport function storeNames() {\n return [];\n}\nexport function readStores() {\n return {};\n}\nexport function readStoresWithTruncation() {\n return { stores: {} };\n}\nexport function sourceOwner() {\n return undefined;\n}\nexport function markAdapterSource() {}\n\nexport function tanstackQueryStore() {\n return undefined;\n}\nexport function jotaiStore() {\n return undefined;\n}\nexport function xstateStore() {\n return undefined;\n}\nexport function valtioStore() {\n return undefined;\n}\nexport function mobxStore() {\n return undefined;\n}\nexport function recoilStore() {\n return undefined;\n}\nexport function svelteStore() {\n return undefined;\n}\nexport function piniaStore() {\n return undefined;\n}\nexport function pushStore() {\n return undefined;\n}\n\nexport function registerCapabilities() {}\nexport function getCapabilities() {\n return {};\n}\nexport function hasCapabilities() {\n return false;\n}\nexport function setCapabilitiesListener() {}\n\nexport function createReticleEmitter() {\n return {\n signal() {},\n state() {},\n };\n}\n\n// It wraps the APP's write, so the write still runs: Reticle does nothing in production, the app does.\nexport function commitAndSignal(_emitter, mutate) {\n return mutate();\n}\nexport function registerReticleDomain() {}\n\nexport function buildSnapshot() {\n return {};\n}\n\nexport function inspectChart() {\n return {};\n}\nexport function canvasChartData() {\n return {};\n}\n\nexport function matchQuery() {\n return false;\n}\nexport function runQuery() {\n return undefined;\n}\n\nexport function executeAction() {\n return undefined;\n}\nexport function executeSequence() {\n return undefined;\n}\n\nexport function describe() {\n return undefined;\n}\nexport function getRole() {\n return undefined;\n}\nexport function getAccessibleName() {\n return undefined;\n}\nexport function getStates() {\n return {};\n}\nexport function isVisible() {\n return false;\n}\n\nexport class RefRegistry {\n get size() {\n return 0;\n }\n\n markEdited() {}\n\n mintedBeforeLastEdit() {\n return false;\n }\n\n refFor() {\n return undefined;\n }\n\n resolve() {\n return null;\n }\n}\n\nexport const refs = new RefRegistry();\n\nexport class Annotator {\n constructor() {}\n\n get active() {\n return false;\n }\n\n get markCount() {\n return 0;\n }\n\n mount() {}\n unmount() {}\n}\n\nexport function installAnnotator() {}\n\nexport function resolveMarkAnchor() {\n return undefined;\n}\n";
|
|
@@ -111,7 +111,10 @@ export function createReticleEmitter() {
|
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
// It wraps the APP's write, so the write still runs: Reticle does nothing in production, the app does.
|
|
115
|
+
export function commitAndSignal(_emitter, mutate) {
|
|
116
|
+
return mutate();
|
|
117
|
+
}
|
|
115
118
|
export function registerReticleDomain() {}
|
|
116
119
|
|
|
117
120
|
export function buildSnapshot() {
|
package/dist/index.cjs
CHANGED
|
@@ -4122,126 +4122,7 @@ var coerce = {
|
|
|
4122
4122
|
};
|
|
4123
4123
|
var NEVER = INVALID;
|
|
4124
4124
|
|
|
4125
|
-
// ../../../core/dist/wire/constants/
|
|
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
|
-
|
|
4238
|
-
// ../../../core/dist/wire/constants/constants.js
|
|
4239
|
-
var RETICLE_DEFAULT_PORT = 4400;
|
|
4240
|
-
var RETICLE_WS_PATH = "/reticle";
|
|
4241
|
-
var RETICLE_CLIENT_HOST = "localhost";
|
|
4242
|
-
function bridgeWsUrl(port = RETICLE_DEFAULT_PORT, host = RETICLE_CLIENT_HOST) {
|
|
4243
|
-
return `ws://${host}:${String(port)}${RETICLE_WS_PATH}`;
|
|
4244
|
-
}
|
|
4125
|
+
// ../../../core/dist/wire/constants/reticle-env.js
|
|
4245
4126
|
var ReticleEnv = {
|
|
4246
4127
|
/** "0"/"false"/"off" turns off telemetry and every optional outbound call; DO_NOT_TRACK too. */
|
|
4247
4128
|
TELEMETRY: "RETICLE_TELEMETRY",
|
|
@@ -4330,6 +4211,8 @@ var ReticleEnv = {
|
|
|
4330
4211
|
HARNESS_BASE_URL: "RETICLE_HARNESS_BASE_URL",
|
|
4331
4212
|
/** Hard ceiling on harness model turns in one drive. Bounds cost, not value. */
|
|
4332
4213
|
HARNESS_MAX_STEPS: "RETICLE_HARNESS_MAX_STEPS",
|
|
4214
|
+
/** The free drive `reticle try` was granted, sent on every Harness call it bills. */
|
|
4215
|
+
DRIVE_ID: "RETICLE_DRIVE_ID",
|
|
4333
4216
|
/**
|
|
4334
4217
|
* Which model backs the harness driver: `anthropic` (the default) or `jev`.
|
|
4335
4218
|
*
|
|
@@ -4367,8 +4250,148 @@ var ReticleEnv = {
|
|
|
4367
4250
|
/** The platform host. Read through `cloudUrlFrom()`, which also accepts `URL`. */
|
|
4368
4251
|
CLOUD_URL: "RETICLE_CLOUD_URL",
|
|
4369
4252
|
/** The short name for `CLOUD_URL`, which people type first. `CLOUD_URL` wins when both are set. */
|
|
4370
|
-
URL: "RETICLE_URL"
|
|
4253
|
+
URL: "RETICLE_URL",
|
|
4254
|
+
/** "0"/"false"/"off" stops `reticle hook prompt` writing prompts, on this whole machine. */
|
|
4255
|
+
PROMPT_CAPTURE: "RETICLE_PROMPT_CAPTURE",
|
|
4256
|
+
/** "0"/"false"/"off" stops the daemon's background sync, on this whole machine. */
|
|
4257
|
+
BACKGROUND_SYNC: "RETICLE_BACKGROUND_SYNC"
|
|
4371
4258
|
};
|
|
4259
|
+
|
|
4260
|
+
// ../../../core/dist/wire/constants/event-types.js
|
|
4261
|
+
var EventType = {
|
|
4262
|
+
DOM_ADDED: "dom.added",
|
|
4263
|
+
DOM_REMOVED: "dom.removed",
|
|
4264
|
+
DOM_ATTR: "dom.attr",
|
|
4265
|
+
DOM_TEXT: "dom.text",
|
|
4266
|
+
NET_REQUEST: "net.request",
|
|
4267
|
+
NET_PENDING: "net.pending",
|
|
4268
|
+
/** An SSE (EventSource) or WebSocket frame — a message on a long-lived streaming connection. */
|
|
4269
|
+
NET_STREAM: "net.stream",
|
|
4270
|
+
/** A web-perf metric a screenshot can't verify: LCP, cumulative layout shift, or a long task. */
|
|
4271
|
+
PERF: "perf",
|
|
4272
|
+
ROUTE_CHANGE: "route.change",
|
|
4273
|
+
CONSOLE_LOG: "console.log",
|
|
4274
|
+
CONSOLE_WARN: "console.warn",
|
|
4275
|
+
CONSOLE_ERROR: "console.error",
|
|
4276
|
+
CONSOLE_INFO: "console.info",
|
|
4277
|
+
CONSOLE_DEBUG: "console.debug",
|
|
4278
|
+
ERROR_UNCAUGHT: "error.uncaught",
|
|
4279
|
+
VISIBLE_SHOWN: "visible.shown",
|
|
4280
|
+
ANIM_START: "anim.start",
|
|
4281
|
+
ANIM_END: "anim.end",
|
|
4282
|
+
SCROLL_POSITION: "scroll.position",
|
|
4283
|
+
REVEAL_SHOWN: "reveal.shown",
|
|
4284
|
+
SIGNAL: "signal",
|
|
4285
|
+
STATE_CHANGE: "state.change",
|
|
4286
|
+
/** a write to localStorage/sessionStorage/cookies — `data: { area, key, old?, new? }` (values redacted). */
|
|
4287
|
+
STORAGE_CHANGE: "storage.change",
|
|
4288
|
+
/** page-level visibility/focus health (distinct from element-level VISIBLE_*). */
|
|
4289
|
+
PAGE_HEALTH: "page.health",
|
|
4290
|
+
/**
|
|
4291
|
+
* synthetic: the page called window.open, so the consequence of what was just clicked may live in
|
|
4292
|
+
* another browsing context this one cannot observe (an OAuth popup is the archetype).
|
|
4293
|
+
* `data: { href }` — the URL the page asked to open, when it named one.
|
|
4294
|
+
*/
|
|
4295
|
+
CONTEXT_OPENED: "context.opened",
|
|
4296
|
+
/**
|
|
4297
|
+
* The app opened a native `alert`/`confirm`/`prompt` while Reticle was driving it.
|
|
4298
|
+
*
|
|
4299
|
+
* Recorded because Reticle ANSWERS these rather than letting them block — a native dialog halts
|
|
4300
|
+
* the main thread, and the SDK's own message pump is on that thread, so one `confirm` behind a
|
|
4301
|
+
* driven click made the tab permanently unresponsive with no recovery from inside the session.
|
|
4302
|
+
* Answering silently would trade a wedge for an invisible one, so the question the app asked, and
|
|
4303
|
+
* the answer given, ride out as an event.
|
|
4304
|
+
*/
|
|
4305
|
+
DIALOG_OPENED: "dialog.opened",
|
|
4306
|
+
/** aggregated React commits over a throttle window (dev builds) — `data: { commits }`. Commit storms /
|
|
4307
|
+
* wasted re-renders show up here without a per-render flood. */
|
|
4308
|
+
RENDER_COMMIT: "render.commit",
|
|
4309
|
+
/** element focus moved — `data: { to, from, toBody }`. Focus dropping to body after an act is a regression. */
|
|
4310
|
+
FOCUS_CHANGE: "focus.change",
|
|
4311
|
+
/**
|
|
4312
|
+
* a form field's value moved — `data: { field, kind, value?, checked?, previous?, previousChecked?, redacted?, length }`.
|
|
4313
|
+
*
|
|
4314
|
+
* The DOM observer never saw this: `value` is in its attribute allowlist, but React and every
|
|
4315
|
+
* controlled input set the PROPERTY, so `MutationObserver` does not fire. `value` is omitted and
|
|
4316
|
+
* `redacted` set for a password, a sensitive name, or a payment autocomplete hint; `length` is
|
|
4317
|
+
* always present, because "it was wiped" is assertable without carrying anybody's data.
|
|
4318
|
+
* `checked` is the checkbox/radio property (`value` there is the HTML value, usually `"on"`).
|
|
4319
|
+
* `kind: "settled"` is a read after the action's handlers; `previous` / `previousChecked` are
|
|
4320
|
+
* the pre-action reading, so a restore is distinguishable from a render.
|
|
4321
|
+
*/
|
|
4322
|
+
FIELD_CHANGE: "field.change",
|
|
4323
|
+
/** browser → bridge: a human recording compiled in-page. */
|
|
4324
|
+
FLOW_RECORDED: "flow.recorded",
|
|
4325
|
+
/** synthetic: browser transport queue overflowed; events were dropped. `data: { dropped: number }`. */
|
|
4326
|
+
TRANSPORT_OVERFLOW: "transport.overflow",
|
|
4327
|
+
/**
|
|
4328
|
+
* synthetic: a per-channel cap truncated a batch (e.g. a DOM mutation flood). `data: { channel, dropped }`.
|
|
4329
|
+
* Marks downstream rollups/envelopes as built on incomplete data — a ledger that lies at scale is worse
|
|
4330
|
+
* than no ledger, so truncation is never silent.
|
|
4331
|
+
*/
|
|
4332
|
+
TRUNCATED: "truncated",
|
|
4333
|
+
/**
|
|
4334
|
+
* synthetic: the SDK detected a region it CANNOT observe (a cross-origin iframe, a closed shadow root).
|
|
4335
|
+
* `data: { kind: BlindSpotKind, count }`. Surfaced on results as `coverage: partial` so a green never
|
|
4336
|
+
* implies it saw everything.
|
|
4337
|
+
*/
|
|
4338
|
+
BLIND_SPOT: "blind-spot",
|
|
4339
|
+
/** synthetic: the SDK ITSELF failed (an observer threw). `data: { site, message, errorType }`.
|
|
4340
|
+
* Rides the existing bridge — no outbound request. See browser/observers/sdk-failure.ts. */
|
|
4341
|
+
SDK_FAILED: "sdk.failed",
|
|
4342
|
+
/**
|
|
4343
|
+
* synthetic (driven only): CDP/Playwright-authoritative network detail for a response the in-page
|
|
4344
|
+
* fetch/XHR wrapper also saw — full response headers + authoritative status/mimeType the page-side
|
|
4345
|
+
* wrapper can't reach. `data: { url, method?, status, headers, resourceType? }`. Merged onto the
|
|
4346
|
+
* matching in-page NET_REQUEST so the driven view never loses fidelity to an outside-in tool.
|
|
4347
|
+
*/
|
|
4348
|
+
NET_DETAIL: "net.detail",
|
|
4349
|
+
/**
|
|
4350
|
+
* Live-control: browser → bridge. A human acted on the presenter panel.
|
|
4351
|
+
* `data: { kind: HumanControlKind; text?: string }`. Rides the existing EventMessage.
|
|
4352
|
+
*/
|
|
4353
|
+
HUMAN_CONTROL: "human.control",
|
|
4354
|
+
/**
|
|
4355
|
+
* Human review: browser → bridge. A human pinned a mistake to an element on the running page
|
|
4356
|
+
* (the "annotate the bug where you see it" loop). `data` narrows to HumanMarkDataSchema — a note
|
|
4357
|
+
* plus a re-resolvable element anchor (and its source file:line when the framework stamped one) so
|
|
4358
|
+
* the agent that drains the mark knows exactly which element and which source to fix.
|
|
4359
|
+
*/
|
|
4360
|
+
HUMAN_MARK: "human.mark",
|
|
4361
|
+
/**
|
|
4362
|
+
* The app produced a FILE — a Blob handed to `URL.createObjectURL`, usually saved by clicking an
|
|
4363
|
+
* anchor with `download`. `data: { filename?, mimeType, bytes, lines?, preview? }`. The one artifact
|
|
4364
|
+
* class no outside-the-browser tool can inspect: it never crosses the network, so there is no
|
|
4365
|
+
* request to intercept. See `observers/download.ts` for the defect that motivated it.
|
|
4366
|
+
*/
|
|
4367
|
+
DOWNLOAD: "download",
|
|
4368
|
+
/**
|
|
4369
|
+
* Browser -> bridge: a person used the HUD — pressed a control, or the HUD changed how it sits.
|
|
4370
|
+
* `data` narrows to HudUseDataSchema: names only. Telemetry, not evidence: the session takes it
|
|
4371
|
+
* off the wire before the buffer, so no verdict, settle or agent read ever sees it.
|
|
4372
|
+
*/
|
|
4373
|
+
HUD_USED: "hud.used",
|
|
4374
|
+
/**
|
|
4375
|
+
* A `<video>` or `<audio>` changed playback state: `data: { event, tag, currentTime, duration?,
|
|
4376
|
+
* readyState, paused }`. The event's own `t` orders it against the network, which is what a media
|
|
4377
|
+
* defect ("Ready before the response arrived") is: an ordering in time no DOM count shows.
|
|
4378
|
+
*/
|
|
4379
|
+
MEDIA_STATE: "media.state",
|
|
4380
|
+
/**
|
|
4381
|
+
* A `<canvas>` drew something different: `data: { digest, width, height, mean }`. A hash of a
|
|
4382
|
+
* 16x16 sample of its pixels and their mean colour, emitted only when the digest changes. What a
|
|
4383
|
+
* canvas shows never reaches the DOM, so without this a canvas app's output is invisible.
|
|
4384
|
+
*/
|
|
4385
|
+
CANVAS_DIGEST: "canvas.digest"
|
|
4386
|
+
};
|
|
4387
|
+
|
|
4388
|
+
// ../../../core/dist/wire/constants/constants.js
|
|
4389
|
+
var RETICLE_DEFAULT_PORT = 4400;
|
|
4390
|
+
var RETICLE_WS_PATH = "/reticle";
|
|
4391
|
+
var RETICLE_CLIENT_HOST = "localhost";
|
|
4392
|
+
function bridgeWsUrl(port = RETICLE_DEFAULT_PORT, host = RETICLE_CLIENT_HOST) {
|
|
4393
|
+
return `ws://${host}:${String(port)}${RETICLE_WS_PATH}`;
|
|
4394
|
+
}
|
|
4372
4395
|
var SYNC_BATCH_LIMITS = {
|
|
4373
4396
|
MAX_RUNS: 50,
|
|
4374
4397
|
MAX_BYTES: 4 * 1024 * 1024
|
|
@@ -4463,6 +4486,8 @@ var ReticleDir = {
|
|
|
4463
4486
|
VISUAL_SUBDIR: "visual",
|
|
4464
4487
|
/** verification-run artifacts —.reticle/runs/<runId>.json (the OEM/CI-consumable verdict). */
|
|
4465
4488
|
RUNS_SUBDIR: "runs",
|
|
4489
|
+
/** Dev-server logs, only when the state home cannot be written. Machine-local, like `runs/`. */
|
|
4490
|
+
LOGS_SUBDIR: "logs",
|
|
4466
4491
|
/** fail-to-pass bug capsules —.reticle/capsules/<id>.json (a minimal failing flow + its evidence). */
|
|
4467
4492
|
CAPSULES_SUBDIR: "capsules",
|
|
4468
4493
|
/**
|
|
@@ -4477,6 +4502,11 @@ var ReticleDir = {
|
|
|
4477
4502
|
PLANS_SUBDIR: "plans",
|
|
4478
4503
|
/** durable causal journal —.reticle/sessions/<id>/{events,actions}.jsonl (the substrate). */
|
|
4479
4504
|
SESSIONS_SUBDIR: "sessions",
|
|
4505
|
+
/**
|
|
4506
|
+
* Every tool call an agent made, in full, with what it did to the page —
|
|
4507
|
+
* .reticle/episodes/<connection>.jsonl. Local evidence: never committed, never sent.
|
|
4508
|
+
*/
|
|
4509
|
+
EPISODES_SUBDIR: "episodes",
|
|
4480
4510
|
/** append-only event ledger inside a session dir (one ReticleEvent per line). */
|
|
4481
4511
|
JOURNAL_EVENTS_FILE: "events.jsonl",
|
|
4482
4512
|
/** append-only action ledger inside a session dir (one JournalAction per line). */
|
|
@@ -4492,20 +4522,26 @@ var ReticleDir = {
|
|
|
4492
4522
|
JOURNAL_EVENTS_CLOSED_FILE: "events.closed.json",
|
|
4493
4523
|
/** learned expected-envelopes per route, accumulated across runs (the deviation-report baseline). */
|
|
4494
4524
|
ENVELOPES_FILE: "envelopes.json",
|
|
4495
|
-
/** learned ambient (action-less churn) region map — excluded from settle/summaries/envelopes. */
|
|
4496
4525
|
AMBIENT_FILE: "ambient.json",
|
|
4526
|
+
// learned action-less churn regions, excluded from settle/summaries
|
|
4497
4527
|
/** per-flow flake ledger — replay outcomes that decide intermittent-failure quarantine. */
|
|
4498
4528
|
FLAKE_FILE: "flake.json",
|
|
4499
4529
|
/** The app-wide coverage ledger — see server features/exhaust/ledger.ts. */
|
|
4500
4530
|
COVERAGE_FILE: "coverage.json",
|
|
4501
4531
|
/** Human review marks (HUD notes), pending and resolved — see server portal/session/human. */
|
|
4502
4532
|
NOTES_FILE: "notes.json",
|
|
4503
|
-
/** The user's latest request
|
|
4533
|
+
/** The user's latest request: the newest prompt the hook captured, or the one the agent relayed. Local. */
|
|
4504
4534
|
REQUEST_FILE: "request.json",
|
|
4535
|
+
/** Every prompt the agent hook captured, one JSON line each, oldest first. `request.json` holds the newest. */
|
|
4536
|
+
REQUESTS_LOG_FILE: "requests.jsonl",
|
|
4537
|
+
/** The prompt history moved aside when `requests.jsonl` fills. The same words, so the same rule. */
|
|
4538
|
+
REQUESTS_LOG_ROTATED_FILE: "requests.1.jsonl",
|
|
4539
|
+
APP_NOTES_FILE: "app-notes.json",
|
|
4540
|
+
// confirmed targeting hints for the next session; never evidence
|
|
4505
4541
|
PLATFORM_MOMENTS_FILE: "platform-moments.json",
|
|
4506
4542
|
// moments already said, so each is said once
|
|
4507
4543
|
/**
|
|
4508
|
-
* the project's cloud binding — .reticle/cloud.json, written by `reticle
|
|
4544
|
+
* the project's cloud binding — .reticle/cloud.json, written by `reticle connect`. Git-checked and
|
|
4509
4545
|
* non-secret: the project id, the API origin, and where its dashboard lives. The KEY lives in
|
|
4510
4546
|
* ~/.reticle/credentials.json instead, because that one must never reach a repository.
|
|
4511
4547
|
*/
|
|
@@ -4596,6 +4632,111 @@ function projectIdFrom(pkgName, rootPath, hash) {
|
|
|
4596
4632
|
return `${base}-${hash(rootPath)}`;
|
|
4597
4633
|
}
|
|
4598
4634
|
|
|
4635
|
+
// ../../../core/dist/identity/checkout.js
|
|
4636
|
+
var MAX_UP_LEVELS = 50;
|
|
4637
|
+
var GITDIR_PREFIX = "gitdir:";
|
|
4638
|
+
var BRANCH_PREFIX = "ref: refs/heads/";
|
|
4639
|
+
var WORKTREES_SEGMENT = "worktrees";
|
|
4640
|
+
var GIT_DIR_NAME = ".git";
|
|
4641
|
+
function checkoutOf(dir, readFile) {
|
|
4642
|
+
const sep2 = dir.includes("\\") ? "\\" : "/";
|
|
4643
|
+
let current = trimEnd(dir, sep2);
|
|
4644
|
+
for (let level = 0; level <= MAX_UP_LEVELS; level += 1) {
|
|
4645
|
+
const head = readFile(`${current}${sep2}${GIT_DIR_NAME}${sep2}HEAD`);
|
|
4646
|
+
if (head !== void 0) {
|
|
4647
|
+
return withBranch({
|
|
4648
|
+
root: current,
|
|
4649
|
+
mainRoot: current,
|
|
4650
|
+
commonDir: `${current}${sep2}${GIT_DIR_NAME}`,
|
|
4651
|
+
linked: false
|
|
4652
|
+
}, head);
|
|
4653
|
+
}
|
|
4654
|
+
const pointer = readFile(`${current}${sep2}${GIT_DIR_NAME}`);
|
|
4655
|
+
if (pointer !== void 0 && pointer.trim().startsWith(GITDIR_PREFIX)) {
|
|
4656
|
+
return linkedCheckout(current, pointer.trim().slice(GITDIR_PREFIX.length).trim(), sep2, readFile);
|
|
4657
|
+
}
|
|
4658
|
+
const parent = parentOf(current, sep2);
|
|
4659
|
+
if (parent === void 0)
|
|
4660
|
+
return void 0;
|
|
4661
|
+
current = parent;
|
|
4662
|
+
}
|
|
4663
|
+
return void 0;
|
|
4664
|
+
}
|
|
4665
|
+
function mainCheckoutTwin(path, readFile) {
|
|
4666
|
+
const checkout = checkoutOf(path, readFile);
|
|
4667
|
+
if (checkout === void 0 || checkout.root === checkout.mainRoot)
|
|
4668
|
+
return void 0;
|
|
4669
|
+
return `${checkout.mainRoot}${path.slice(checkout.root.length)}`;
|
|
4670
|
+
}
|
|
4671
|
+
function linkedCheckout(root, gitdir, sep2, readFile) {
|
|
4672
|
+
const absolute = resolvePath(root, gitdir.split(/[\\/]/).join(sep2), sep2);
|
|
4673
|
+
const parts = absolute.split(sep2);
|
|
4674
|
+
const isWorktree = WORKTREES_SEGMENT === parts[parts.length - 2];
|
|
4675
|
+
const gitDirOfRepo = parts.slice(0, -2);
|
|
4676
|
+
const commonDir = isWorktree ? gitDirOfRepo.join(sep2) : absolute;
|
|
4677
|
+
const mainRoot = isWorktree && GIT_DIR_NAME === gitDirOfRepo[gitDirOfRepo.length - 1] ? gitDirOfRepo.slice(0, -1).join(sep2) : root;
|
|
4678
|
+
const head = readFile(`${absolute}${sep2}HEAD`);
|
|
4679
|
+
const checkout = { root, mainRoot, commonDir, linked: isWorktree };
|
|
4680
|
+
return head === void 0 ? checkout : withBranch(checkout, head);
|
|
4681
|
+
}
|
|
4682
|
+
function withBranch(checkout, head) {
|
|
4683
|
+
const ref = head.trim();
|
|
4684
|
+
return ref.startsWith(BRANCH_PREFIX) ? { ...checkout, branch: ref.slice(BRANCH_PREFIX.length) } : checkout;
|
|
4685
|
+
}
|
|
4686
|
+
function resolvePath(base, rel, sep2) {
|
|
4687
|
+
const absolute = rel.startsWith(sep2) || /^[A-Za-z]:/.test(rel);
|
|
4688
|
+
const parts = (absolute ? rel : `${base}${sep2}${rel}`).split(sep2);
|
|
4689
|
+
const out = [];
|
|
4690
|
+
for (const part of parts) {
|
|
4691
|
+
if ("." === part || "" === part && out.length > 0)
|
|
4692
|
+
continue;
|
|
4693
|
+
if (".." === part) {
|
|
4694
|
+
if (out.length > 1)
|
|
4695
|
+
out.pop();
|
|
4696
|
+
continue;
|
|
4697
|
+
}
|
|
4698
|
+
out.push(part);
|
|
4699
|
+
}
|
|
4700
|
+
return out.join(sep2);
|
|
4701
|
+
}
|
|
4702
|
+
function parentOf(path, sep2) {
|
|
4703
|
+
const at = path.lastIndexOf(sep2);
|
|
4704
|
+
if (at <= 0)
|
|
4705
|
+
return void 0;
|
|
4706
|
+
const parent = path.slice(0, at);
|
|
4707
|
+
return /^[A-Za-z]:$/.test(parent) ? void 0 : parent;
|
|
4708
|
+
}
|
|
4709
|
+
function trimEnd(path, sep2) {
|
|
4710
|
+
let end = path.length;
|
|
4711
|
+
while (end > 1 && sep2 === path[end - 1])
|
|
4712
|
+
end -= 1;
|
|
4713
|
+
return path.slice(0, end);
|
|
4714
|
+
}
|
|
4715
|
+
|
|
4716
|
+
// ../../../core/dist/identity/state-dir.js
|
|
4717
|
+
var stateDirNotAbsolute = (dir) => `The Reticle state folder "${dir}" is not an absolute path, so Reticle did not use it. A relative folder is made inside the folder this process runs in, which is often the project. Set ${ReticleEnv.PAIRING_TOKEN_DIR} and ${ReticleEnv.STATE_DIR} to absolute paths, or unset them.`;
|
|
4718
|
+
var stateDirResolvedFromCwd = (dir, resolved) => `The Reticle state folder "${dir}" is a relative path. Reticle uses "${resolved}", in the folder this process runs in. Set ${ReticleEnv.PAIRING_TOKEN_DIR} to an absolute path to remove this warning.`;
|
|
4719
|
+
var stateDirWindowsPathOnPosix = (dir, fallback) => `The Reticle state folder "${dir}" is a Windows path, and this system does not use Windows paths. Reticle did not use it, and uses "${fallback}". Set ${ReticleEnv.PAIRING_TOKEN_DIR} to a path for this system, or unset it.`;
|
|
4720
|
+
var WINDOWS_DRIVE = /^[A-Za-z]:/;
|
|
4721
|
+
var POSIX_SEPARATOR = "/";
|
|
4722
|
+
function resolveStateDir(override, fallback, rules, warn) {
|
|
4723
|
+
if (override !== void 0 && override.length > 0) {
|
|
4724
|
+
if (rules.isAbsolute(override)) {
|
|
4725
|
+
return override;
|
|
4726
|
+
}
|
|
4727
|
+
if (rules.sep !== POSIX_SEPARATOR || !WINDOWS_DRIVE.test(override)) {
|
|
4728
|
+
const resolved = rules.resolve(override);
|
|
4729
|
+
warn(stateDirResolvedFromCwd(override, resolved));
|
|
4730
|
+
return resolved;
|
|
4731
|
+
}
|
|
4732
|
+
warn(stateDirWindowsPathOnPosix(override, fallback));
|
|
4733
|
+
}
|
|
4734
|
+
if (!rules.isAbsolute(fallback)) {
|
|
4735
|
+
throw new Error(stateDirNotAbsolute(fallback));
|
|
4736
|
+
}
|
|
4737
|
+
return fallback;
|
|
4738
|
+
}
|
|
4739
|
+
|
|
4599
4740
|
// ../../../core/dist/registry/daemon-registry.js
|
|
4600
4741
|
var DAEMON_REGISTRY_PREFIX = "daemon-";
|
|
4601
4742
|
var DAEMON_REGISTRY_SUFFIX = ".json";
|
|
@@ -4686,6 +4827,32 @@ function readNearestField(startDir, basename, field, readFile, maxDepth, accept)
|
|
|
4686
4827
|
}
|
|
4687
4828
|
return void 0;
|
|
4688
4829
|
}
|
|
4830
|
+
function readConfigField(startDir, field, readFile, accept) {
|
|
4831
|
+
const own = readNearestField(
|
|
4832
|
+
startDir,
|
|
4833
|
+
RETICLE_CONFIG_BASENAME,
|
|
4834
|
+
field,
|
|
4835
|
+
readFile,
|
|
4836
|
+
MAX_CONFIG_SEARCH_DEPTH,
|
|
4837
|
+
accept
|
|
4838
|
+
);
|
|
4839
|
+
if (own !== void 0) return own;
|
|
4840
|
+
const twin = mainCheckoutTwin(startDir, (path) => {
|
|
4841
|
+
try {
|
|
4842
|
+
return readFile(path);
|
|
4843
|
+
} catch {
|
|
4844
|
+
return void 0;
|
|
4845
|
+
}
|
|
4846
|
+
});
|
|
4847
|
+
return twin === void 0 ? void 0 : readNearestField(
|
|
4848
|
+
twin,
|
|
4849
|
+
RETICLE_CONFIG_BASENAME,
|
|
4850
|
+
field,
|
|
4851
|
+
readFile,
|
|
4852
|
+
MAX_CONFIG_SEARCH_DEPTH,
|
|
4853
|
+
accept
|
|
4854
|
+
);
|
|
4855
|
+
}
|
|
4689
4856
|
function readNearestPackageName(startDir, readFile = readFileOrThrow) {
|
|
4690
4857
|
return readNearestField(
|
|
4691
4858
|
startDir,
|
|
@@ -4697,32 +4864,16 @@ function readNearestPackageName(startDir, readFile = readFileOrThrow) {
|
|
|
4697
4864
|
);
|
|
4698
4865
|
}
|
|
4699
4866
|
function readConfiguredProjectId(startDir, readFile = readFileOrThrow) {
|
|
4700
|
-
return
|
|
4701
|
-
startDir,
|
|
4702
|
-
RETICLE_CONFIG_BASENAME,
|
|
4703
|
-
"projectId",
|
|
4704
|
-
readFile,
|
|
4705
|
-
MAX_CONFIG_SEARCH_DEPTH,
|
|
4706
|
-
nonEmptyString
|
|
4707
|
-
);
|
|
4867
|
+
return readConfigField(startDir, "projectId", readFile, nonEmptyString);
|
|
4708
4868
|
}
|
|
4709
4869
|
function readConfiguredPort(startDir, readFile = readFileOrThrow) {
|
|
4710
|
-
return
|
|
4711
|
-
startDir,
|
|
4712
|
-
RETICLE_CONFIG_BASENAME,
|
|
4713
|
-
"port",
|
|
4714
|
-
readFile,
|
|
4715
|
-
MAX_CONFIG_SEARCH_DEPTH,
|
|
4716
|
-
tcpPort
|
|
4717
|
-
);
|
|
4870
|
+
return readConfigField(startDir, "port", readFile, tcpPort);
|
|
4718
4871
|
}
|
|
4719
4872
|
function readConfiguredTestIdAttribute(startDir, readFile = readFileOrThrow) {
|
|
4720
|
-
return
|
|
4873
|
+
return readConfigField(
|
|
4721
4874
|
startDir,
|
|
4722
|
-
RETICLE_CONFIG_BASENAME,
|
|
4723
4875
|
"testIdAttribute",
|
|
4724
4876
|
readFile,
|
|
4725
|
-
MAX_CONFIG_SEARCH_DEPTH,
|
|
4726
4877
|
(value) => "string" === typeof value && TESTID_ATTR_PATTERN.test(value) ? value : void 0
|
|
4727
4878
|
);
|
|
4728
4879
|
}
|
|
@@ -4831,6 +4982,12 @@ function announceDevServer(entry, home = stateHome(), io = nodeIo) {
|
|
|
4831
4982
|
}
|
|
4832
4983
|
};
|
|
4833
4984
|
}
|
|
4985
|
+
var DEV_PROXY_URL_ENV = "PORTLESS_URL";
|
|
4986
|
+
function announcedUrl(env, reported, port) {
|
|
4987
|
+
const proxied = env[DEV_PROXY_URL_ENV];
|
|
4988
|
+
if (proxied !== void 0 && proxied.length > 0) return proxied;
|
|
4989
|
+
return reported ?? `http://localhost:${String(port)}/`;
|
|
4990
|
+
}
|
|
4834
4991
|
|
|
4835
4992
|
// src/svelte-source.ts
|
|
4836
4993
|
var import_node_module = require("node:module");
|
|
@@ -5160,7 +5317,10 @@ export function createReticleEmitter() {
|
|
|
5160
5317
|
};
|
|
5161
5318
|
}
|
|
5162
5319
|
|
|
5163
|
-
|
|
5320
|
+
// It wraps the APP's write, so the write still runs: Reticle does nothing in production, the app does.
|
|
5321
|
+
export function commitAndSignal(_emitter, mutate) {
|
|
5322
|
+
return mutate();
|
|
5323
|
+
}
|
|
5164
5324
|
export function registerReticleDomain() {}
|
|
5165
5325
|
|
|
5166
5326
|
export function buildSnapshot() {
|
|
@@ -5377,8 +5537,18 @@ function isHtmlEntry(id, specifier, root) {
|
|
|
5377
5537
|
return candidate.endsWith(target.startsWith("/") ? target : `/${target}`);
|
|
5378
5538
|
}
|
|
5379
5539
|
function readPairingToken() {
|
|
5380
|
-
|
|
5381
|
-
|
|
5540
|
+
let dir;
|
|
5541
|
+
try {
|
|
5542
|
+
dir = resolveStateDir(
|
|
5543
|
+
process.env[ReticleEnv.PAIRING_TOKEN_DIR],
|
|
5544
|
+
(0, import_node_path8.join)((0, import_node_os2.homedir)(), ReticleDir.ROOT),
|
|
5545
|
+
{ isAbsolute: import_node_path8.isAbsolute, resolve: import_node_path8.resolve, sep: import_node_path8.sep },
|
|
5546
|
+
(warning) => console.warn(`[reticle] ${warning}`)
|
|
5547
|
+
);
|
|
5548
|
+
} catch (error) {
|
|
5549
|
+
console.warn(`[reticle] ${error instanceof Error ? error.message : String(error)}`);
|
|
5550
|
+
return void 0;
|
|
5551
|
+
}
|
|
5382
5552
|
return ensurePairingToken(dir);
|
|
5383
5553
|
}
|
|
5384
5554
|
var tokenWarned = false;
|
|
@@ -5441,10 +5611,7 @@ function reticle(options = {}) {
|
|
|
5441
5611
|
const sourceMapping = options.sourceMapping !== false;
|
|
5442
5612
|
const inject = options.inject !== false;
|
|
5443
5613
|
const desktop = true === options.desktop;
|
|
5444
|
-
const
|
|
5445
|
-
...options,
|
|
5446
|
-
projectId: resolveProjectId(options.projectId, process.cwd())
|
|
5447
|
-
};
|
|
5614
|
+
const projectIdIn = (dir) => resolveProjectId(options.projectId, dir);
|
|
5448
5615
|
let htmlEntrySpecifier;
|
|
5449
5616
|
let root;
|
|
5450
5617
|
let command;
|
|
@@ -5459,7 +5626,9 @@ function reticle(options = {}) {
|
|
|
5459
5626
|
let connectDelivered = false;
|
|
5460
5627
|
let lastPortWarning;
|
|
5461
5628
|
const resolveLazy = () => {
|
|
5462
|
-
const
|
|
5629
|
+
const appDir = root ?? process.cwd();
|
|
5630
|
+
const resolved = { ...options, projectId: projectIdIn(appDir) };
|
|
5631
|
+
const { port, warning } = resolveDaemonPort(resolved.port, resolved.projectId, appDir);
|
|
5463
5632
|
if (warning !== void 0 && warning !== lastPortWarning) {
|
|
5464
5633
|
lastPortWarning = warning;
|
|
5465
5634
|
warn(warning);
|
|
@@ -5655,7 +5824,7 @@ ${code}`;
|
|
|
5655
5824
|
port,
|
|
5656
5825
|
pid: process.pid,
|
|
5657
5826
|
root: opts.root ?? process.cwd(),
|
|
5658
|
-
url: server.resolvedUrls?.local[0]
|
|
5827
|
+
url: announcedUrl(process.env, server.resolvedUrls?.local[0], port),
|
|
5659
5828
|
...opts.sdkVersion === void 0 || 0 === opts.sdkVersion.length ? {} : { sdkVersion: opts.sdkVersion },
|
|
5660
5829
|
startedAt: Date.now(),
|
|
5661
5830
|
...opts.projectId === void 0 ? {} : { projectId: opts.projectId }
|
package/dist/index.js
CHANGED
|
@@ -2,11 +2,11 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
import { missingTokenWarning } from './token/missing-token.js';
|
|
3
3
|
import { ensurePairingToken } from './token/ensure-token.js';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
|
-
import { join } from 'node:path';
|
|
6
|
-
import { RETICLE_RENDER_PREHOOK, ReticleDir, ReticleEnv, RETICLE_ROOT_GLOBAL, RETICLE_SDK_VERSION_GLOBAL, } from '@reticlehq/core';
|
|
5
|
+
import { isAbsolute, join, resolve, sep } from 'node:path';
|
|
6
|
+
import { RETICLE_RENDER_PREHOOK, ReticleDir, ReticleEnv, RETICLE_ROOT_GLOBAL, RETICLE_SDK_VERSION_GLOBAL, resolveStateDir, } from '@reticlehq/core';
|
|
7
7
|
import { readConfiguredTestIdAttribute, resolveProjectId } from './project-id.js';
|
|
8
8
|
import { resolveDaemonPort } from './discover-port.js';
|
|
9
|
-
import { announceDevServer } from './announce.js';
|
|
9
|
+
import { announceDevServer, announcedUrl } from './announce.js';
|
|
10
10
|
import { stampSvelte } from './svelte-source.js';
|
|
11
11
|
import { NODE_MODULES, ignoredFileNotice, optsOutOfStamping, shouldStamp, shouldStampSvelte, stamp, } from './stamping.js';
|
|
12
12
|
import { resolvableChain, sdkPackageVersion, sdkBuildFingerprint, viteMajor, optimizerOptionsKey, optimizerOptions, } from './installed.js';
|
|
@@ -135,8 +135,17 @@ function isHtmlEntry(id, specifier, root) {
|
|
|
135
135
|
* undefined if the daemon hasn't started yet (the page reloads once it has). Exported for testing.
|
|
136
136
|
*/
|
|
137
137
|
export function readPairingToken() {
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
let dir;
|
|
139
|
+
try {
|
|
140
|
+
// The daemon's rule: a relative folder is resolved against the working folder, and a Windows
|
|
141
|
+
// path on POSIX is not used. Both warn, so the two sides agree on one token file.
|
|
142
|
+
dir = resolveStateDir(process.env[ReticleEnv.PAIRING_TOKEN_DIR], join(homedir(), ReticleDir.ROOT), { isAbsolute, resolve, sep }, (warning) => console.warn(`[reticle] ${warning}`));
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
// Only a home folder that is not absolute gets here. The dev server still starts.
|
|
146
|
+
console.warn(`[reticle] ${error instanceof Error ? error.message : String(error)}`);
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
140
149
|
// Read-or-CREATE, matching the daemon. Reading alone meant a dev server started before the daemon
|
|
141
150
|
// baked in an empty token and every page it served was refused — with the SDK loading and the
|
|
142
151
|
// socket opening, so nothing looked broken. See ensure-token for the bisect.
|
|
@@ -259,12 +268,10 @@ export function reticle(options = {}) {
|
|
|
259
268
|
const sourceMapping = options.sourceMapping !== false;
|
|
260
269
|
const inject = options.inject !== false;
|
|
261
270
|
const desktop = true === options.desktop;
|
|
262
|
-
//
|
|
263
|
-
// app is identifiable across port changes with zero config.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
projectId: resolveProjectId(options.projectId, process.cwd()),
|
|
267
|
-
};
|
|
271
|
+
// The stable projectId (explicit option, else `.reticle.json` / package.json in VITE'S root), so the
|
|
272
|
+
// app is identifiable across port changes with zero config. Read from the root, never the cwd:
|
|
273
|
+
// `vite apps/admin` from a monorepo root announced the root's app and saved flows under it.
|
|
274
|
+
const projectIdIn = (dir) => resolveProjectId(options.projectId, dir);
|
|
268
275
|
/**
|
|
269
276
|
* The specifier the HTML points at, e.g. `/src/main.tsx`.
|
|
270
277
|
*
|
|
@@ -315,7 +322,9 @@ export function reticle(options = {}) {
|
|
|
315
322
|
*/
|
|
316
323
|
const resolveLazy = () => {
|
|
317
324
|
// Wherever the daemon for this project actually is; the order is argued in discover-port.ts.
|
|
318
|
-
const
|
|
325
|
+
const appDir = root ?? process.cwd();
|
|
326
|
+
const resolved = { ...options, projectId: projectIdIn(appDir) };
|
|
327
|
+
const { port, warning } = resolveDaemonPort(resolved.port, resolved.projectId, appDir);
|
|
319
328
|
if (warning !== undefined && warning !== lastPortWarning) {
|
|
320
329
|
lastPortWarning = warning;
|
|
321
330
|
warn(warning);
|
|
@@ -568,7 +577,7 @@ export function reticle(options = {}) {
|
|
|
568
577
|
port,
|
|
569
578
|
pid: process.pid,
|
|
570
579
|
root: opts.root ?? process.cwd(),
|
|
571
|
-
url: server.resolvedUrls?.local[0]
|
|
580
|
+
url: announcedUrl(process.env, server.resolvedUrls?.local[0], port),
|
|
572
581
|
...(opts.sdkVersion === undefined || 0 === opts.sdkVersion.length
|
|
573
582
|
? {}
|
|
574
583
|
: { sdkVersion: opts.sdkVersion }),
|
package/dist/project-id.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { createHash } from 'node:crypto';
|
|
10
10
|
import { dirname, join } from 'node:path';
|
|
11
11
|
import { readFileSync } from 'node:fs';
|
|
12
|
-
import { PROJECT_ID_HASH_LENGTH, TESTID_ATTR_PATTERN, projectIdFrom } from '@reticlehq/core';
|
|
12
|
+
import { PROJECT_ID_HASH_LENGTH, TESTID_ATTR_PATTERN, mainCheckoutTwin, projectIdFrom, } from '@reticlehq/core';
|
|
13
13
|
/** The project config `reticle init` writes, and the id of record it carries. */
|
|
14
14
|
const RETICLE_CONFIG_BASENAME = '.reticle.json';
|
|
15
15
|
export { slugifyPackageName } from '@reticlehq/core';
|
|
@@ -76,6 +76,27 @@ function readNearestField(startDir, basename, field, readFile, maxDepth, accept)
|
|
|
76
76
|
}
|
|
77
77
|
return undefined;
|
|
78
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* A field of the nearest `.reticle.json`, else of the main checkout's when `startDir` is in a git
|
|
81
|
+
* worktree that has none (it is git-ignored, or not committed yet). Every worktree of a project is
|
|
82
|
+
* the same project: one id, one daemon port.
|
|
83
|
+
*/
|
|
84
|
+
function readConfigField(startDir, field, readFile, accept) {
|
|
85
|
+
const own = readNearestField(startDir, RETICLE_CONFIG_BASENAME, field, readFile, MAX_CONFIG_SEARCH_DEPTH, accept);
|
|
86
|
+
if (own !== undefined)
|
|
87
|
+
return own;
|
|
88
|
+
const twin = mainCheckoutTwin(startDir, (path) => {
|
|
89
|
+
try {
|
|
90
|
+
return readFile(path);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
return twin === undefined
|
|
97
|
+
? undefined
|
|
98
|
+
: readNearestField(twin, RETICLE_CONFIG_BASENAME, field, readFile, MAX_CONFIG_SEARCH_DEPTH, accept);
|
|
99
|
+
}
|
|
79
100
|
/** Read the `name` from the nearest package.json at or above `startDir`, or undefined if none. */
|
|
80
101
|
function readNearestPackageName(startDir, readFile = readFileOrThrow) {
|
|
81
102
|
return readNearestField(startDir, 'package.json', 'name', readFile, MAX_PACKAGE_SEARCH_DEPTH, nonEmptyString);
|
|
@@ -87,7 +108,7 @@ function readNearestPackageName(startDir, readFile = readFileOrThrow) {
|
|
|
87
108
|
* through `init` — it is not a second opinion, and where the two disagree the config wins.
|
|
88
109
|
*/
|
|
89
110
|
export function readConfiguredProjectId(startDir, readFile = readFileOrThrow) {
|
|
90
|
-
return
|
|
111
|
+
return readConfigField(startDir, 'projectId', readFile, nonEmptyString);
|
|
91
112
|
}
|
|
92
113
|
/**
|
|
93
114
|
* Read the daemon port `reticle init --port` recorded in the nearest `.reticle.json`, or undefined.
|
|
@@ -97,7 +118,7 @@ export function readConfiguredProjectId(startDir, readFile = readFileOrThrow) {
|
|
|
97
118
|
* project's daemon — and the bridge refused it.
|
|
98
119
|
*/
|
|
99
120
|
export function readConfiguredPort(startDir, readFile = readFileOrThrow) {
|
|
100
|
-
return
|
|
121
|
+
return readConfigField(startDir, 'port', readFile, tcpPort);
|
|
101
122
|
}
|
|
102
123
|
/**
|
|
103
124
|
* Read `testIdAttribute` from the nearest `.reticle.json`, or undefined: the attribute this project
|
|
@@ -106,7 +127,7 @@ export function readConfiguredPort(startDir, readFile = readFileOrThrow) {
|
|
|
106
127
|
* already agree on, so the plugin forwards it to `connect()` rather than asking for it twice.
|
|
107
128
|
*/
|
|
108
129
|
export function readConfiguredTestIdAttribute(startDir, readFile = readFileOrThrow) {
|
|
109
|
-
return
|
|
130
|
+
return readConfigField(startDir, 'testIdAttribute', readFile, (value) => 'string' === typeof value && TESTID_ATTR_PATTERN.test(value) ? value : undefined);
|
|
110
131
|
}
|
|
111
132
|
/**
|
|
112
133
|
* Resolve the projectId for a plugin instance, most-local statement of intent first: an explicit
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reticlehq/vite-plugin",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"description": "Vite plugin for Reticle: dev-only source-map stamping plus auto-injected reticle.connect(). apply:'serve' guarantees it never ships to production.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -41,15 +41,15 @@
|
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@babel/core": "^7.26.0",
|
|
44
|
-
"@reticlehq/
|
|
45
|
-
"@reticlehq/
|
|
44
|
+
"@reticlehq/core": "3.8.0",
|
|
45
|
+
"@reticlehq/babel-plugin": "3.8.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/babel__core": "^7.20.5",
|
|
49
49
|
"esbuild": "^0.28.2",
|
|
50
50
|
"svelte": "^5.57.1",
|
|
51
51
|
"vite": "^8",
|
|
52
|
-
"@reticlehq/react": "3.
|
|
52
|
+
"@reticlehq/react": "3.8.0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"vite": ">=4"
|