@reticlehq/vite-plugin 3.4.0 → 3.6.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/connect-args.js +7 -0
- package/dist/discover-port.d.ts +1 -1
- package/dist/discover-port.js +12 -3
- package/dist/index.cjs +49 -13
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +10 -2
- package/dist/project-id.d.ts +7 -0
- package/dist/project-id.js +10 -1
- package/package.json +4 -4
package/dist/connect-args.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* of the plugin's options the PAGE needs to be told about.
|
|
7
7
|
*/
|
|
8
8
|
import { RETICLE_DEFAULT_PORT, bridgeWsUrl } from '@reticlehq/core';
|
|
9
|
+
const PRESENT_OPTION = 'present';
|
|
9
10
|
/** Build the `reticle.connect` argument literal — only includes keys the user set. */
|
|
10
11
|
export function connectArgs(options) {
|
|
11
12
|
const args = {};
|
|
@@ -18,6 +19,9 @@ export function connectArgs(options) {
|
|
|
18
19
|
args['projectId'] = options.projectId;
|
|
19
20
|
if (options.token !== undefined)
|
|
20
21
|
args['token'] = options.token;
|
|
22
|
+
// Only the opt-out is needed; the SDK defaults to showing its presenter.
|
|
23
|
+
if (false === options.present)
|
|
24
|
+
args[PRESENT_OPTION] = false;
|
|
21
25
|
// Passed as connect ARGUMENTS, not as a `define`. A define substitutes a bare identifier in the
|
|
22
26
|
// source it transforms; the SDK reads these as `globalThis[NAME]`, a dynamic lookup no define can
|
|
23
27
|
// ever reach — so defining them looked right, shipped, and did nothing. Baking them into the
|
|
@@ -67,5 +71,8 @@ export function connectArgs(options) {
|
|
|
67
71
|
'1' === process.env['VITE_RETICLE_ALLOW_NON_LOCALHOST']) {
|
|
68
72
|
args['allowNonLocalhost'] = true;
|
|
69
73
|
}
|
|
74
|
+
if (options.testIdAttribute !== undefined && options.testIdAttribute.length > 0) {
|
|
75
|
+
args['testIdAttribute'] = options.testIdAttribute;
|
|
76
|
+
}
|
|
70
77
|
return Object.keys(args).length > 0 ? JSON.stringify(args) : '';
|
|
71
78
|
}
|
package/dist/discover-port.d.ts
CHANGED
|
@@ -34,4 +34,4 @@ export interface DaemonPortChoice {
|
|
|
34
34
|
*/
|
|
35
35
|
export declare function chooseDaemonPort(sources: DaemonPortSources): DaemonPortChoice;
|
|
36
36
|
/** {@link chooseDaemonPort} over the real registry and the real `.reticle.json` above `cwd`. */
|
|
37
|
-
export declare function resolveDaemonPort(explicit: number | undefined, projectId: string | undefined, cwd: string): DaemonPortChoice;
|
|
37
|
+
export declare function resolveDaemonPort(explicit: number | undefined, projectId: string | undefined, cwd: string, env?: NodeJS.ProcessEnv): DaemonPortChoice;
|
package/dist/discover-port.js
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { readdirSync, readFileSync } from 'node:fs';
|
|
9
9
|
import { join } from 'node:path';
|
|
10
|
-
import { daemonRegistryPort, DaemonRegistryEntrySchema, pickDaemonPort, } from '@reticlehq/core';
|
|
10
|
+
import { daemonRegistryPort, DaemonRegistryEntrySchema, pickDaemonPort, ReticleEnv, } from '@reticlehq/core';
|
|
11
11
|
import { stateHome } from './state-home.js';
|
|
12
12
|
import { readConfiguredPort } from './project-id.js';
|
|
13
13
|
import { RETICLE_VITE_PLUGIN_NAME } from './plugin-name.js';
|
|
14
|
+
const MAX_TCP_PORT = 65_535;
|
|
14
15
|
/** process.kill(pid, 0) throws iff the process is gone — the same liveness probe the daemon uses. */
|
|
15
16
|
function isAlive(pid) {
|
|
16
17
|
try {
|
|
@@ -77,10 +78,18 @@ export function chooseDaemonPort(sources) {
|
|
|
77
78
|
};
|
|
78
79
|
}
|
|
79
80
|
/** {@link chooseDaemonPort} over the real registry and the real `.reticle.json` above `cwd`. */
|
|
80
|
-
export function resolveDaemonPort(explicit, projectId, cwd) {
|
|
81
|
+
export function resolveDaemonPort(explicit, projectId, cwd, env = process.env) {
|
|
82
|
+
// The CLI treats RETICLE_PORT as an override across projects. A page must follow the same
|
|
83
|
+
// daemon even when an older instance is still registered or init wrote a different port.
|
|
84
|
+
const raw = env[ReticleEnv.PORT];
|
|
85
|
+
if (raw !== undefined && /^\d+$/.test(raw)) {
|
|
86
|
+
const port = Number(raw);
|
|
87
|
+
if (0 < port && port <= MAX_TCP_PORT)
|
|
88
|
+
return { port, warning: undefined };
|
|
89
|
+
}
|
|
81
90
|
const configured = readConfiguredPort(cwd);
|
|
82
91
|
return chooseDaemonPort({
|
|
83
|
-
discovered: discoverDaemonPort(projectId, stateHome(), isAlive, configured),
|
|
92
|
+
discovered: discoverDaemonPort(projectId, stateHome(env), isAlive, configured),
|
|
84
93
|
configured,
|
|
85
94
|
explicit,
|
|
86
95
|
});
|
package/dist/index.cjs
CHANGED
|
@@ -4243,6 +4243,9 @@ function bridgeWsUrl(port = RETICLE_DEFAULT_PORT, host = RETICLE_CLIENT_HOST) {
|
|
|
4243
4243
|
return `ws://${host}:${String(port)}${RETICLE_WS_PATH}`;
|
|
4244
4244
|
}
|
|
4245
4245
|
var ReticleEnv = {
|
|
4246
|
+
/** "0"/"false"/"off" turns off telemetry and every optional outbound call; DO_NOT_TRACK too. */
|
|
4247
|
+
TELEMETRY: "RETICLE_TELEMETRY",
|
|
4248
|
+
DO_NOT_TRACK: "DO_NOT_TRACK",
|
|
4246
4249
|
/** Shared-secret the browser SDK must present in HELLO; absent ⇒ loopback-trust only. */
|
|
4247
4250
|
TOKEN: "RETICLE_TOKEN",
|
|
4248
4251
|
/** Bridge bind host. Defaults to loopback; setting anything else is opt-in remote exposure. */
|
|
@@ -4257,6 +4260,8 @@ var ReticleEnv = {
|
|
|
4257
4260
|
STATE_DIR: "RETICLE_STATE_DIR",
|
|
4258
4261
|
/** Attach to an already-running browser over CDP instead of launching one. */
|
|
4259
4262
|
CDP_URL: "RETICLE_CDP_URL",
|
|
4263
|
+
/** The Chromium executable to launch, in place of Playwright's own build or an installed Chrome. */
|
|
4264
|
+
CHROMIUM_PATH: "RETICLE_CHROMIUM_PATH",
|
|
4260
4265
|
/** Max simultaneous leased headless contexts in the browser pool (resource cap). */
|
|
4261
4266
|
MAX_CONTEXTS: "RETICLE_MAX_CONTEXTS",
|
|
4262
4267
|
/**
|
|
@@ -4437,8 +4442,9 @@ var ReticleDir = {
|
|
|
4437
4442
|
/**
|
|
4438
4443
|
* the user's own record of what Reticle has done for them — .reticle/impact.json.
|
|
4439
4444
|
*
|
|
4440
|
-
*
|
|
4441
|
-
* about
|
|
4445
|
+
* NOT telemetry: telemetry answers questions about the product; this answers the user's question
|
|
4446
|
+
* about their own work. A linked project's copy syncs to the user's own dashboard (the sync cycle's
|
|
4447
|
+
* `impact` kind); the machine-wide copy in `~/.reticle` never leaves the machine.
|
|
4442
4448
|
*/
|
|
4443
4449
|
IMPACT_FILE: "impact.json",
|
|
4444
4450
|
/** what changes were SUPPOSED to make true —.reticle/intent.json (git-checked, reviewed) */
|
|
@@ -4453,14 +4459,6 @@ var ReticleDir = {
|
|
|
4453
4459
|
* classified nor ignored.
|
|
4454
4460
|
*/
|
|
4455
4461
|
INTENT_SUBDIR: "intent",
|
|
4456
|
-
/**
|
|
4457
|
-
* what a drive types into a field, keyed by the field's label —.reticle/fill-values.json.
|
|
4458
|
-
*
|
|
4459
|
-
* Git-checked on purpose. A generated value is paid for once and then belongs to the project: the
|
|
4460
|
-
* next drive reuses it for free, a replay sends exactly what the recording sent, and a human who
|
|
4461
|
-
* dislikes one can edit the file rather than argue with a model.
|
|
4462
|
-
*/
|
|
4463
|
-
FILL_VALUES_FILE: "fill-values.json",
|
|
4464
4462
|
/** opt-in pixel baselines —.reticle/visual/<name>.png + <name>.diff.png. */
|
|
4465
4463
|
VISUAL_SUBDIR: "visual",
|
|
4466
4464
|
/** verification-run artifacts —.reticle/runs/<runId>.json (the OEM/CI-consumable verdict). */
|
|
@@ -4475,6 +4473,8 @@ var ReticleDir = {
|
|
|
4475
4473
|
* in three separate files, so the guard could not see it and nobody was ever asked.
|
|
4476
4474
|
*/
|
|
4477
4475
|
FEEDBACK_SUBDIR: "feedback",
|
|
4476
|
+
/** Every Harness drive plan, and how each journey went — .reticle/plans/<when>-<planId>.json. */
|
|
4477
|
+
PLANS_SUBDIR: "plans",
|
|
4478
4478
|
/** durable causal journal —.reticle/sessions/<id>/{events,actions}.jsonl (the substrate). */
|
|
4479
4479
|
SESSIONS_SUBDIR: "sessions",
|
|
4480
4480
|
/** append-only event ledger inside a session dir (one ReticleEvent per line). */
|
|
@@ -4498,6 +4498,12 @@ var ReticleDir = {
|
|
|
4498
4498
|
FLAKE_FILE: "flake.json",
|
|
4499
4499
|
/** The app-wide coverage ledger — see server features/exhaust/ledger.ts. */
|
|
4500
4500
|
COVERAGE_FILE: "coverage.json",
|
|
4501
|
+
/** Human review marks (HUD notes), pending and resolved — see server portal/session/human. */
|
|
4502
|
+
NOTES_FILE: "notes.json",
|
|
4503
|
+
/** The user's latest request, as the agent relayed it. Local: it is the user's own words. */
|
|
4504
|
+
REQUEST_FILE: "request.json",
|
|
4505
|
+
PLATFORM_MOMENTS_FILE: "platform-moments.json",
|
|
4506
|
+
// moments already said, so each is said once
|
|
4501
4507
|
/**
|
|
4502
4508
|
* the project's cloud binding — .reticle/cloud.json, written by `reticle link`. Git-checked and
|
|
4503
4509
|
* non-secret: the project id, the API origin, and where its dashboard lives. The KEY lives in
|
|
@@ -4557,6 +4563,9 @@ var RETICLE_ROOT_GLOBAL = "__RETICLE_ROOT__";
|
|
|
4557
4563
|
var RETICLE_SDK_VERSION_GLOBAL = "__RETICLE_SDK_VERSION__";
|
|
4558
4564
|
var RETICLE_IGNORE_MARKER = "@reticle-ignore";
|
|
4559
4565
|
|
|
4566
|
+
// ../../../core/dist/identity/testid-attribute.js
|
|
4567
|
+
var TESTID_ATTR_PATTERN = /^[a-zA-Z_][\w-]*$/;
|
|
4568
|
+
|
|
4560
4569
|
// ../../../core/dist/identity/project-id.js
|
|
4561
4570
|
var PROJECT_ID_HASH_LENGTH = 8;
|
|
4562
4571
|
var FALLBACK_PROJECT_BASE = "app";
|
|
@@ -4707,6 +4716,16 @@ function readConfiguredPort(startDir, readFile = readFileOrThrow) {
|
|
|
4707
4716
|
tcpPort
|
|
4708
4717
|
);
|
|
4709
4718
|
}
|
|
4719
|
+
function readConfiguredTestIdAttribute(startDir, readFile = readFileOrThrow) {
|
|
4720
|
+
return readNearestField(
|
|
4721
|
+
startDir,
|
|
4722
|
+
RETICLE_CONFIG_BASENAME,
|
|
4723
|
+
"testIdAttribute",
|
|
4724
|
+
readFile,
|
|
4725
|
+
MAX_CONFIG_SEARCH_DEPTH,
|
|
4726
|
+
(value) => "string" === typeof value && TESTID_ATTR_PATTERN.test(value) ? value : void 0
|
|
4727
|
+
);
|
|
4728
|
+
}
|
|
4710
4729
|
function resolveProjectId(explicit, cwd, readPkgName = readNearestPackageName, readConfiguredId = readConfiguredProjectId) {
|
|
4711
4730
|
if (explicit !== void 0 && explicit.length > 0) return explicit;
|
|
4712
4731
|
const configured = readConfiguredId(cwd);
|
|
@@ -4730,6 +4749,7 @@ function stateHome(env = process.env) {
|
|
|
4730
4749
|
var RETICLE_VITE_PLUGIN_NAME = "reticle";
|
|
4731
4750
|
|
|
4732
4751
|
// src/discover-port.ts
|
|
4752
|
+
var MAX_TCP_PORT2 = 65535;
|
|
4733
4753
|
function isAlive(pid) {
|
|
4734
4754
|
try {
|
|
4735
4755
|
process.kill(pid, 0);
|
|
@@ -4770,10 +4790,15 @@ function chooseDaemonPort(sources) {
|
|
|
4770
4790
|
warning: explicit === configured ? void 0 : portConflictWarning(explicit, configured, port)
|
|
4771
4791
|
};
|
|
4772
4792
|
}
|
|
4773
|
-
function resolveDaemonPort(explicit, projectId, cwd) {
|
|
4793
|
+
function resolveDaemonPort(explicit, projectId, cwd, env = process.env) {
|
|
4794
|
+
const raw = env[ReticleEnv.PORT];
|
|
4795
|
+
if (raw !== void 0 && /^\d+$/.test(raw)) {
|
|
4796
|
+
const port = Number(raw);
|
|
4797
|
+
if (0 < port && port <= MAX_TCP_PORT2) return { port, warning: void 0 };
|
|
4798
|
+
}
|
|
4774
4799
|
const configured = readConfiguredPort(cwd);
|
|
4775
4800
|
return chooseDaemonPort({
|
|
4776
|
-
discovered: discoverDaemonPort(projectId, stateHome(), isAlive, configured),
|
|
4801
|
+
discovered: discoverDaemonPort(projectId, stateHome(env), isAlive, configured),
|
|
4777
4802
|
configured,
|
|
4778
4803
|
explicit
|
|
4779
4804
|
});
|
|
@@ -5224,6 +5249,7 @@ export function resolveMarkAnchor() {
|
|
|
5224
5249
|
`;
|
|
5225
5250
|
|
|
5226
5251
|
// src/connect-args.ts
|
|
5252
|
+
var PRESENT_OPTION = "present";
|
|
5227
5253
|
function connectArgs(options) {
|
|
5228
5254
|
const args = {};
|
|
5229
5255
|
const port = options.port ?? RETICLE_DEFAULT_PORT;
|
|
@@ -5231,6 +5257,7 @@ function connectArgs(options) {
|
|
|
5231
5257
|
if (options.session !== void 0) args["session"] = options.session;
|
|
5232
5258
|
if (options.projectId !== void 0) args["projectId"] = options.projectId;
|
|
5233
5259
|
if (options.token !== void 0) args["token"] = options.token;
|
|
5260
|
+
if (false === options.present) args[PRESENT_OPTION] = false;
|
|
5234
5261
|
if (options.root !== void 0 && options.root.length > 0) args["root"] = options.root;
|
|
5235
5262
|
if (options.sdkVersion !== void 0 && options.sdkVersion.length > 0) {
|
|
5236
5263
|
args["sdkVersion"] = options.sdkVersion;
|
|
@@ -5254,6 +5281,9 @@ function connectArgs(options) {
|
|
|
5254
5281
|
if (true === options.allowNonLocalhost || "1" === process.env["VITE_RETICLE_ALLOW_NON_LOCALHOST"]) {
|
|
5255
5282
|
args["allowNonLocalhost"] = true;
|
|
5256
5283
|
}
|
|
5284
|
+
if (options.testIdAttribute !== void 0 && options.testIdAttribute.length > 0) {
|
|
5285
|
+
args["testIdAttribute"] = options.testIdAttribute;
|
|
5286
|
+
}
|
|
5257
5287
|
return Object.keys(args).length > 0 ? JSON.stringify(args) : "";
|
|
5258
5288
|
}
|
|
5259
5289
|
|
|
@@ -5439,7 +5469,13 @@ function reticle(options = {}) {
|
|
|
5439
5469
|
const withToken = token !== void 0 ? { ...withPort, token } : withPort;
|
|
5440
5470
|
const appRoot = withToken.root ?? root ?? process.cwd();
|
|
5441
5471
|
const sdkVersion = withToken.sdkVersion ?? sdkPackageVersion(appRoot);
|
|
5442
|
-
|
|
5472
|
+
const testIdAttribute = withToken.testIdAttribute ?? readConfiguredTestIdAttribute(appRoot);
|
|
5473
|
+
return {
|
|
5474
|
+
...withToken,
|
|
5475
|
+
root: appRoot,
|
|
5476
|
+
sdkVersion,
|
|
5477
|
+
...testIdAttribute === void 0 ? {} : { testIdAttribute }
|
|
5478
|
+
};
|
|
5443
5479
|
};
|
|
5444
5480
|
const currentConnectSource = () => connectModuleSource(resolveLazy(), root === void 0 ? null : findDevModule(root, import_node_fs6.existsSync));
|
|
5445
5481
|
let lastServedConnectSource;
|
package/dist/index.d.cts
CHANGED
|
@@ -64,6 +64,8 @@ export interface ReticleVitePluginOptions {
|
|
|
64
64
|
projectId?: string;
|
|
65
65
|
/** Auth token forwarded to connect when the bridge requires one. */
|
|
66
66
|
token?: string;
|
|
67
|
+
/** Show Reticle's presenter. Defaults to true; set false to keep the agent connection without the panel. */
|
|
68
|
+
present?: boolean;
|
|
67
69
|
/** Stamp data-reticle-source for React 19 source mapping. Default true (harmless on React <=18). */
|
|
68
70
|
sourceMapping?: boolean;
|
|
69
71
|
/** Auto-inject the dev-gated reticle.connect call. Default true. */
|
|
@@ -109,6 +111,8 @@ export interface ReticleVitePluginOptions {
|
|
|
109
111
|
* vite.config.
|
|
110
112
|
*/
|
|
111
113
|
networkBodyMaxChars?: number;
|
|
114
|
+
/** Attribute this app marks controls with instead of `data-testid`. Defaults to `.reticle.json`'s `testIdAttribute`. */
|
|
115
|
+
testIdAttribute?: string;
|
|
112
116
|
/**
|
|
113
117
|
* Retain a FAILED request's response body even with `captureNetworkBodies` off. Default true.
|
|
114
118
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -64,6 +64,8 @@ export interface ReticleVitePluginOptions {
|
|
|
64
64
|
projectId?: string;
|
|
65
65
|
/** Auth token forwarded to connect when the bridge requires one. */
|
|
66
66
|
token?: string;
|
|
67
|
+
/** Show Reticle's presenter. Defaults to true; set false to keep the agent connection without the panel. */
|
|
68
|
+
present?: boolean;
|
|
67
69
|
/** Stamp data-reticle-source for React 19 source mapping. Default true (harmless on React <=18). */
|
|
68
70
|
sourceMapping?: boolean;
|
|
69
71
|
/** Auto-inject the dev-gated reticle.connect call. Default true. */
|
|
@@ -109,6 +111,8 @@ export interface ReticleVitePluginOptions {
|
|
|
109
111
|
* vite.config.
|
|
110
112
|
*/
|
|
111
113
|
networkBodyMaxChars?: number;
|
|
114
|
+
/** Attribute this app marks controls with instead of `data-testid`. Defaults to `.reticle.json`'s `testIdAttribute`. */
|
|
115
|
+
testIdAttribute?: string;
|
|
112
116
|
/**
|
|
113
117
|
* Retain a FAILED request's response body even with `captureNetworkBodies` off. Default true.
|
|
114
118
|
*
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { ensurePairingToken } from './token/ensure-token.js';
|
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { RETICLE_RENDER_PREHOOK, ReticleDir, ReticleEnv, RETICLE_ROOT_GLOBAL, RETICLE_SDK_VERSION_GLOBAL, } from '@reticlehq/core';
|
|
7
|
-
import { resolveProjectId } from './project-id.js';
|
|
7
|
+
import { readConfiguredTestIdAttribute, resolveProjectId } from './project-id.js';
|
|
8
8
|
import { resolveDaemonPort } from './discover-port.js';
|
|
9
9
|
import { announceDevServer } from './announce.js';
|
|
10
10
|
import { stampSvelte } from './svelte-source.js';
|
|
@@ -327,7 +327,15 @@ export function reticle(options = {}) {
|
|
|
327
327
|
// tree, and they travel in the generated connect call rather than through a `define`.
|
|
328
328
|
const appRoot = withToken.root ?? root ?? process.cwd();
|
|
329
329
|
const sdkVersion = withToken.sdkVersion ?? sdkPackageVersion(appRoot);
|
|
330
|
-
|
|
330
|
+
// `.reticle.json` is where a project names its test-id attribute, read per call like the port so
|
|
331
|
+
// an edit takes effect on the next page load; the option, when given, wins.
|
|
332
|
+
const testIdAttribute = withToken.testIdAttribute ?? readConfiguredTestIdAttribute(appRoot);
|
|
333
|
+
return {
|
|
334
|
+
...withToken,
|
|
335
|
+
root: appRoot,
|
|
336
|
+
sdkVersion,
|
|
337
|
+
...(testIdAttribute === undefined ? {} : { testIdAttribute }),
|
|
338
|
+
};
|
|
331
339
|
};
|
|
332
340
|
/**
|
|
333
341
|
* The connect module's source as it would be served RIGHT NOW. Recomputed rather than cached: the
|
package/dist/project-id.d.ts
CHANGED
|
@@ -34,6 +34,13 @@ export declare function readConfiguredProjectId(startDir: string, readFile?: Rea
|
|
|
34
34
|
* project's daemon — and the bridge refused it.
|
|
35
35
|
*/
|
|
36
36
|
export declare function readConfiguredPort(startDir: string, readFile?: ReadFile): number | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Read `testIdAttribute` from the nearest `.reticle.json`, or undefined: the attribute this project
|
|
39
|
+
* marks controls with when it is not `data-testid` (`data-test-subj`, `data-cy`, ...). The page needs
|
|
40
|
+
* it to resolve `{ testid }`, and the file is the one place the CLI, the daemon and the plugin
|
|
41
|
+
* already agree on, so the plugin forwards it to `connect()` rather than asking for it twice.
|
|
42
|
+
*/
|
|
43
|
+
export declare function readConfiguredTestIdAttribute(startDir: string, readFile?: ReadFile): string | undefined;
|
|
37
44
|
/**
|
|
38
45
|
* Resolve the projectId for a plugin instance, most-local statement of intent first: an explicit
|
|
39
46
|
* option, then the id `reticle init` recorded in `.reticle.json`, then derivation.
|
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, projectIdFrom } from '@reticlehq/core';
|
|
12
|
+
import { PROJECT_ID_HASH_LENGTH, TESTID_ATTR_PATTERN, 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';
|
|
@@ -99,6 +99,15 @@ export function readConfiguredProjectId(startDir, readFile = readFileOrThrow) {
|
|
|
99
99
|
export function readConfiguredPort(startDir, readFile = readFileOrThrow) {
|
|
100
100
|
return readNearestField(startDir, RETICLE_CONFIG_BASENAME, 'port', readFile, MAX_CONFIG_SEARCH_DEPTH, tcpPort);
|
|
101
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Read `testIdAttribute` from the nearest `.reticle.json`, or undefined: the attribute this project
|
|
104
|
+
* marks controls with when it is not `data-testid` (`data-test-subj`, `data-cy`, ...). The page needs
|
|
105
|
+
* it to resolve `{ testid }`, and the file is the one place the CLI, the daemon and the plugin
|
|
106
|
+
* already agree on, so the plugin forwards it to `connect()` rather than asking for it twice.
|
|
107
|
+
*/
|
|
108
|
+
export function readConfiguredTestIdAttribute(startDir, readFile = readFileOrThrow) {
|
|
109
|
+
return readNearestField(startDir, RETICLE_CONFIG_BASENAME, 'testIdAttribute', readFile, MAX_CONFIG_SEARCH_DEPTH, (value) => ('string' === typeof value && TESTID_ATTR_PATTERN.test(value) ? value : undefined));
|
|
110
|
+
}
|
|
102
111
|
/**
|
|
103
112
|
* Resolve the projectId for a plugin instance, most-local statement of intent first: an explicit
|
|
104
113
|
* option, then the id `reticle init` recorded in `.reticle.json`, then derivation.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reticlehq/vite-plugin",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.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/babel-plugin": "3.
|
|
45
|
-
"@reticlehq/core": "3.
|
|
44
|
+
"@reticlehq/babel-plugin": "3.6.0",
|
|
45
|
+
"@reticlehq/core": "3.6.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.6.0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"vite": ">=4"
|