@reticlehq/vite-plugin 3.7.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/index.cjs +307 -144
- package/dist/index.js +15 -6
- 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
|
+
}
|
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",
|
|
@@ -4369,8 +4250,148 @@ var ReticleEnv = {
|
|
|
4369
4250
|
/** The platform host. Read through `cloudUrlFrom()`, which also accepts `URL`. */
|
|
4370
4251
|
CLOUD_URL: "RETICLE_CLOUD_URL",
|
|
4371
4252
|
/** The short name for `CLOUD_URL`, which people type first. `CLOUD_URL` wins when both are set. */
|
|
4372
|
-
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"
|
|
4373
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
|
+
}
|
|
4374
4395
|
var SYNC_BATCH_LIMITS = {
|
|
4375
4396
|
MAX_RUNS: 50,
|
|
4376
4397
|
MAX_BYTES: 4 * 1024 * 1024
|
|
@@ -4465,6 +4486,8 @@ var ReticleDir = {
|
|
|
4465
4486
|
VISUAL_SUBDIR: "visual",
|
|
4466
4487
|
/** verification-run artifacts —.reticle/runs/<runId>.json (the OEM/CI-consumable verdict). */
|
|
4467
4488
|
RUNS_SUBDIR: "runs",
|
|
4489
|
+
/** Dev-server logs, only when the state home cannot be written. Machine-local, like `runs/`. */
|
|
4490
|
+
LOGS_SUBDIR: "logs",
|
|
4468
4491
|
/** fail-to-pass bug capsules —.reticle/capsules/<id>.json (a minimal failing flow + its evidence). */
|
|
4469
4492
|
CAPSULES_SUBDIR: "capsules",
|
|
4470
4493
|
/**
|
|
@@ -4479,6 +4502,11 @@ var ReticleDir = {
|
|
|
4479
4502
|
PLANS_SUBDIR: "plans",
|
|
4480
4503
|
/** durable causal journal —.reticle/sessions/<id>/{events,actions}.jsonl (the substrate). */
|
|
4481
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",
|
|
4482
4510
|
/** append-only event ledger inside a session dir (one ReticleEvent per line). */
|
|
4483
4511
|
JOURNAL_EVENTS_FILE: "events.jsonl",
|
|
4484
4512
|
/** append-only action ledger inside a session dir (one JournalAction per line). */
|
|
@@ -4502,8 +4530,12 @@ var ReticleDir = {
|
|
|
4502
4530
|
COVERAGE_FILE: "coverage.json",
|
|
4503
4531
|
/** Human review marks (HUD notes), pending and resolved — see server portal/session/human. */
|
|
4504
4532
|
NOTES_FILE: "notes.json",
|
|
4505
|
-
/** The user's latest request
|
|
4533
|
+
/** The user's latest request: the newest prompt the hook captured, or the one the agent relayed. Local. */
|
|
4506
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",
|
|
4507
4539
|
APP_NOTES_FILE: "app-notes.json",
|
|
4508
4540
|
// confirmed targeting hints for the next session; never evidence
|
|
4509
4541
|
PLATFORM_MOMENTS_FILE: "platform-moments.json",
|
|
@@ -4600,6 +4632,111 @@ function projectIdFrom(pkgName, rootPath, hash) {
|
|
|
4600
4632
|
return `${base}-${hash(rootPath)}`;
|
|
4601
4633
|
}
|
|
4602
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
|
+
|
|
4603
4740
|
// ../../../core/dist/registry/daemon-registry.js
|
|
4604
4741
|
var DAEMON_REGISTRY_PREFIX = "daemon-";
|
|
4605
4742
|
var DAEMON_REGISTRY_SUFFIX = ".json";
|
|
@@ -4690,6 +4827,32 @@ function readNearestField(startDir, basename, field, readFile, maxDepth, accept)
|
|
|
4690
4827
|
}
|
|
4691
4828
|
return void 0;
|
|
4692
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
|
+
}
|
|
4693
4856
|
function readNearestPackageName(startDir, readFile = readFileOrThrow) {
|
|
4694
4857
|
return readNearestField(
|
|
4695
4858
|
startDir,
|
|
@@ -4701,32 +4864,16 @@ function readNearestPackageName(startDir, readFile = readFileOrThrow) {
|
|
|
4701
4864
|
);
|
|
4702
4865
|
}
|
|
4703
4866
|
function readConfiguredProjectId(startDir, readFile = readFileOrThrow) {
|
|
4704
|
-
return
|
|
4705
|
-
startDir,
|
|
4706
|
-
RETICLE_CONFIG_BASENAME,
|
|
4707
|
-
"projectId",
|
|
4708
|
-
readFile,
|
|
4709
|
-
MAX_CONFIG_SEARCH_DEPTH,
|
|
4710
|
-
nonEmptyString
|
|
4711
|
-
);
|
|
4867
|
+
return readConfigField(startDir, "projectId", readFile, nonEmptyString);
|
|
4712
4868
|
}
|
|
4713
4869
|
function readConfiguredPort(startDir, readFile = readFileOrThrow) {
|
|
4714
|
-
return
|
|
4715
|
-
startDir,
|
|
4716
|
-
RETICLE_CONFIG_BASENAME,
|
|
4717
|
-
"port",
|
|
4718
|
-
readFile,
|
|
4719
|
-
MAX_CONFIG_SEARCH_DEPTH,
|
|
4720
|
-
tcpPort
|
|
4721
|
-
);
|
|
4870
|
+
return readConfigField(startDir, "port", readFile, tcpPort);
|
|
4722
4871
|
}
|
|
4723
4872
|
function readConfiguredTestIdAttribute(startDir, readFile = readFileOrThrow) {
|
|
4724
|
-
return
|
|
4873
|
+
return readConfigField(
|
|
4725
4874
|
startDir,
|
|
4726
|
-
RETICLE_CONFIG_BASENAME,
|
|
4727
4875
|
"testIdAttribute",
|
|
4728
4876
|
readFile,
|
|
4729
|
-
MAX_CONFIG_SEARCH_DEPTH,
|
|
4730
4877
|
(value) => "string" === typeof value && TESTID_ATTR_PATTERN.test(value) ? value : void 0
|
|
4731
4878
|
);
|
|
4732
4879
|
}
|
|
@@ -4835,6 +4982,12 @@ function announceDevServer(entry, home = stateHome(), io = nodeIo) {
|
|
|
4835
4982
|
}
|
|
4836
4983
|
};
|
|
4837
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
|
+
}
|
|
4838
4991
|
|
|
4839
4992
|
// src/svelte-source.ts
|
|
4840
4993
|
var import_node_module = require("node:module");
|
|
@@ -5384,8 +5537,18 @@ function isHtmlEntry(id, specifier, root) {
|
|
|
5384
5537
|
return candidate.endsWith(target.startsWith("/") ? target : `/${target}`);
|
|
5385
5538
|
}
|
|
5386
5539
|
function readPairingToken() {
|
|
5387
|
-
|
|
5388
|
-
|
|
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
|
+
}
|
|
5389
5552
|
return ensurePairingToken(dir);
|
|
5390
5553
|
}
|
|
5391
5554
|
var tokenWarned = false;
|
|
@@ -5661,7 +5824,7 @@ ${code}`;
|
|
|
5661
5824
|
port,
|
|
5662
5825
|
pid: process.pid,
|
|
5663
5826
|
root: opts.root ?? process.cwd(),
|
|
5664
|
-
url: server.resolvedUrls?.local[0]
|
|
5827
|
+
url: announcedUrl(process.env, server.resolvedUrls?.local[0], port),
|
|
5665
5828
|
...opts.sdkVersion === void 0 || 0 === opts.sdkVersion.length ? {} : { sdkVersion: opts.sdkVersion },
|
|
5666
5829
|
startedAt: Date.now(),
|
|
5667
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.
|
|
@@ -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"
|