@reticlehq/vite-plugin 3.5.0 → 3.7.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/disabled-browser-stub.d.ts +1 -1
- package/dist/disabled-browser-stub.js +4 -1
- package/dist/index.cjs +55 -19
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +17 -9
- 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
|
}
|
|
@@ -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
|
@@ -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
|
/**
|
|
@@ -4325,6 +4330,8 @@ var ReticleEnv = {
|
|
|
4325
4330
|
HARNESS_BASE_URL: "RETICLE_HARNESS_BASE_URL",
|
|
4326
4331
|
/** Hard ceiling on harness model turns in one drive. Bounds cost, not value. */
|
|
4327
4332
|
HARNESS_MAX_STEPS: "RETICLE_HARNESS_MAX_STEPS",
|
|
4333
|
+
/** The free drive `reticle try` was granted, sent on every Harness call it bills. */
|
|
4334
|
+
DRIVE_ID: "RETICLE_DRIVE_ID",
|
|
4328
4335
|
/**
|
|
4329
4336
|
* Which model backs the harness driver: `anthropic` (the default) or `jev`.
|
|
4330
4337
|
*
|
|
@@ -4437,8 +4444,9 @@ var ReticleDir = {
|
|
|
4437
4444
|
/**
|
|
4438
4445
|
* the user's own record of what Reticle has done for them — .reticle/impact.json.
|
|
4439
4446
|
*
|
|
4440
|
-
*
|
|
4441
|
-
* about
|
|
4447
|
+
* NOT telemetry: telemetry answers questions about the product; this answers the user's question
|
|
4448
|
+
* about their own work. A linked project's copy syncs to the user's own dashboard (the sync cycle's
|
|
4449
|
+
* `impact` kind); the machine-wide copy in `~/.reticle` never leaves the machine.
|
|
4442
4450
|
*/
|
|
4443
4451
|
IMPACT_FILE: "impact.json",
|
|
4444
4452
|
/** what changes were SUPPOSED to make true —.reticle/intent.json (git-checked, reviewed) */
|
|
@@ -4453,14 +4461,6 @@ var ReticleDir = {
|
|
|
4453
4461
|
* classified nor ignored.
|
|
4454
4462
|
*/
|
|
4455
4463
|
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
4464
|
/** opt-in pixel baselines —.reticle/visual/<name>.png + <name>.diff.png. */
|
|
4465
4465
|
VISUAL_SUBDIR: "visual",
|
|
4466
4466
|
/** verification-run artifacts —.reticle/runs/<runId>.json (the OEM/CI-consumable verdict). */
|
|
@@ -4475,6 +4475,8 @@ var ReticleDir = {
|
|
|
4475
4475
|
* in three separate files, so the guard could not see it and nobody was ever asked.
|
|
4476
4476
|
*/
|
|
4477
4477
|
FEEDBACK_SUBDIR: "feedback",
|
|
4478
|
+
/** Every Harness drive plan, and how each journey went — .reticle/plans/<when>-<planId>.json. */
|
|
4479
|
+
PLANS_SUBDIR: "plans",
|
|
4478
4480
|
/** durable causal journal —.reticle/sessions/<id>/{events,actions}.jsonl (the substrate). */
|
|
4479
4481
|
SESSIONS_SUBDIR: "sessions",
|
|
4480
4482
|
/** append-only event ledger inside a session dir (one ReticleEvent per line). */
|
|
@@ -4492,14 +4494,22 @@ var ReticleDir = {
|
|
|
4492
4494
|
JOURNAL_EVENTS_CLOSED_FILE: "events.closed.json",
|
|
4493
4495
|
/** learned expected-envelopes per route, accumulated across runs (the deviation-report baseline). */
|
|
4494
4496
|
ENVELOPES_FILE: "envelopes.json",
|
|
4495
|
-
/** learned ambient (action-less churn) region map — excluded from settle/summaries/envelopes. */
|
|
4496
4497
|
AMBIENT_FILE: "ambient.json",
|
|
4498
|
+
// learned action-less churn regions, excluded from settle/summaries
|
|
4497
4499
|
/** per-flow flake ledger — replay outcomes that decide intermittent-failure quarantine. */
|
|
4498
4500
|
FLAKE_FILE: "flake.json",
|
|
4499
4501
|
/** The app-wide coverage ledger — see server features/exhaust/ledger.ts. */
|
|
4500
4502
|
COVERAGE_FILE: "coverage.json",
|
|
4503
|
+
/** Human review marks (HUD notes), pending and resolved — see server portal/session/human. */
|
|
4504
|
+
NOTES_FILE: "notes.json",
|
|
4505
|
+
/** The user's latest request, as the agent relayed it. Local: it is the user's own words. */
|
|
4506
|
+
REQUEST_FILE: "request.json",
|
|
4507
|
+
APP_NOTES_FILE: "app-notes.json",
|
|
4508
|
+
// confirmed targeting hints for the next session; never evidence
|
|
4509
|
+
PLATFORM_MOMENTS_FILE: "platform-moments.json",
|
|
4510
|
+
// moments already said, so each is said once
|
|
4501
4511
|
/**
|
|
4502
|
-
* the project's cloud binding — .reticle/cloud.json, written by `reticle
|
|
4512
|
+
* the project's cloud binding — .reticle/cloud.json, written by `reticle connect`. Git-checked and
|
|
4503
4513
|
* non-secret: the project id, the API origin, and where its dashboard lives. The KEY lives in
|
|
4504
4514
|
* ~/.reticle/credentials.json instead, because that one must never reach a repository.
|
|
4505
4515
|
*/
|
|
@@ -4557,6 +4567,9 @@ var RETICLE_ROOT_GLOBAL = "__RETICLE_ROOT__";
|
|
|
4557
4567
|
var RETICLE_SDK_VERSION_GLOBAL = "__RETICLE_SDK_VERSION__";
|
|
4558
4568
|
var RETICLE_IGNORE_MARKER = "@reticle-ignore";
|
|
4559
4569
|
|
|
4570
|
+
// ../../../core/dist/identity/testid-attribute.js
|
|
4571
|
+
var TESTID_ATTR_PATTERN = /^[a-zA-Z_][\w-]*$/;
|
|
4572
|
+
|
|
4560
4573
|
// ../../../core/dist/identity/project-id.js
|
|
4561
4574
|
var PROJECT_ID_HASH_LENGTH = 8;
|
|
4562
4575
|
var FALLBACK_PROJECT_BASE = "app";
|
|
@@ -4707,6 +4720,16 @@ function readConfiguredPort(startDir, readFile = readFileOrThrow) {
|
|
|
4707
4720
|
tcpPort
|
|
4708
4721
|
);
|
|
4709
4722
|
}
|
|
4723
|
+
function readConfiguredTestIdAttribute(startDir, readFile = readFileOrThrow) {
|
|
4724
|
+
return readNearestField(
|
|
4725
|
+
startDir,
|
|
4726
|
+
RETICLE_CONFIG_BASENAME,
|
|
4727
|
+
"testIdAttribute",
|
|
4728
|
+
readFile,
|
|
4729
|
+
MAX_CONFIG_SEARCH_DEPTH,
|
|
4730
|
+
(value) => "string" === typeof value && TESTID_ATTR_PATTERN.test(value) ? value : void 0
|
|
4731
|
+
);
|
|
4732
|
+
}
|
|
4710
4733
|
function resolveProjectId(explicit, cwd, readPkgName = readNearestPackageName, readConfiguredId = readConfiguredProjectId) {
|
|
4711
4734
|
if (explicit !== void 0 && explicit.length > 0) return explicit;
|
|
4712
4735
|
const configured = readConfiguredId(cwd);
|
|
@@ -5141,7 +5164,10 @@ export function createReticleEmitter() {
|
|
|
5141
5164
|
};
|
|
5142
5165
|
}
|
|
5143
5166
|
|
|
5144
|
-
|
|
5167
|
+
// It wraps the APP's write, so the write still runs: Reticle does nothing in production, the app does.
|
|
5168
|
+
export function commitAndSignal(_emitter, mutate) {
|
|
5169
|
+
return mutate();
|
|
5170
|
+
}
|
|
5145
5171
|
export function registerReticleDomain() {}
|
|
5146
5172
|
|
|
5147
5173
|
export function buildSnapshot() {
|
|
@@ -5230,6 +5256,7 @@ export function resolveMarkAnchor() {
|
|
|
5230
5256
|
`;
|
|
5231
5257
|
|
|
5232
5258
|
// src/connect-args.ts
|
|
5259
|
+
var PRESENT_OPTION = "present";
|
|
5233
5260
|
function connectArgs(options) {
|
|
5234
5261
|
const args = {};
|
|
5235
5262
|
const port = options.port ?? RETICLE_DEFAULT_PORT;
|
|
@@ -5237,6 +5264,7 @@ function connectArgs(options) {
|
|
|
5237
5264
|
if (options.session !== void 0) args["session"] = options.session;
|
|
5238
5265
|
if (options.projectId !== void 0) args["projectId"] = options.projectId;
|
|
5239
5266
|
if (options.token !== void 0) args["token"] = options.token;
|
|
5267
|
+
if (false === options.present) args[PRESENT_OPTION] = false;
|
|
5240
5268
|
if (options.root !== void 0 && options.root.length > 0) args["root"] = options.root;
|
|
5241
5269
|
if (options.sdkVersion !== void 0 && options.sdkVersion.length > 0) {
|
|
5242
5270
|
args["sdkVersion"] = options.sdkVersion;
|
|
@@ -5260,6 +5288,9 @@ function connectArgs(options) {
|
|
|
5260
5288
|
if (true === options.allowNonLocalhost || "1" === process.env["VITE_RETICLE_ALLOW_NON_LOCALHOST"]) {
|
|
5261
5289
|
args["allowNonLocalhost"] = true;
|
|
5262
5290
|
}
|
|
5291
|
+
if (options.testIdAttribute !== void 0 && options.testIdAttribute.length > 0) {
|
|
5292
|
+
args["testIdAttribute"] = options.testIdAttribute;
|
|
5293
|
+
}
|
|
5263
5294
|
return Object.keys(args).length > 0 ? JSON.stringify(args) : "";
|
|
5264
5295
|
}
|
|
5265
5296
|
|
|
@@ -5417,10 +5448,7 @@ function reticle(options = {}) {
|
|
|
5417
5448
|
const sourceMapping = options.sourceMapping !== false;
|
|
5418
5449
|
const inject = options.inject !== false;
|
|
5419
5450
|
const desktop = true === options.desktop;
|
|
5420
|
-
const
|
|
5421
|
-
...options,
|
|
5422
|
-
projectId: resolveProjectId(options.projectId, process.cwd())
|
|
5423
|
-
};
|
|
5451
|
+
const projectIdIn = (dir) => resolveProjectId(options.projectId, dir);
|
|
5424
5452
|
let htmlEntrySpecifier;
|
|
5425
5453
|
let root;
|
|
5426
5454
|
let command;
|
|
@@ -5435,7 +5463,9 @@ function reticle(options = {}) {
|
|
|
5435
5463
|
let connectDelivered = false;
|
|
5436
5464
|
let lastPortWarning;
|
|
5437
5465
|
const resolveLazy = () => {
|
|
5438
|
-
const
|
|
5466
|
+
const appDir = root ?? process.cwd();
|
|
5467
|
+
const resolved = { ...options, projectId: projectIdIn(appDir) };
|
|
5468
|
+
const { port, warning } = resolveDaemonPort(resolved.port, resolved.projectId, appDir);
|
|
5439
5469
|
if (warning !== void 0 && warning !== lastPortWarning) {
|
|
5440
5470
|
lastPortWarning = warning;
|
|
5441
5471
|
warn(warning);
|
|
@@ -5445,7 +5475,13 @@ function reticle(options = {}) {
|
|
|
5445
5475
|
const withToken = token !== void 0 ? { ...withPort, token } : withPort;
|
|
5446
5476
|
const appRoot = withToken.root ?? root ?? process.cwd();
|
|
5447
5477
|
const sdkVersion = withToken.sdkVersion ?? sdkPackageVersion(appRoot);
|
|
5448
|
-
|
|
5478
|
+
const testIdAttribute = withToken.testIdAttribute ?? readConfiguredTestIdAttribute(appRoot);
|
|
5479
|
+
return {
|
|
5480
|
+
...withToken,
|
|
5481
|
+
root: appRoot,
|
|
5482
|
+
sdkVersion,
|
|
5483
|
+
...testIdAttribute === void 0 ? {} : { testIdAttribute }
|
|
5484
|
+
};
|
|
5449
5485
|
};
|
|
5450
5486
|
const currentConnectSource = () => connectModuleSource(resolveLazy(), root === void 0 ? null : findDevModule(root, import_node_fs6.existsSync));
|
|
5451
5487
|
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';
|
|
@@ -259,12 +259,10 @@ export function reticle(options = {}) {
|
|
|
259
259
|
const sourceMapping = options.sourceMapping !== false;
|
|
260
260
|
const inject = options.inject !== false;
|
|
261
261
|
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
|
-
};
|
|
262
|
+
// The stable projectId (explicit option, else `.reticle.json` / package.json in VITE'S root), so the
|
|
263
|
+
// app is identifiable across port changes with zero config. Read from the root, never the cwd:
|
|
264
|
+
// `vite apps/admin` from a monorepo root announced the root's app and saved flows under it.
|
|
265
|
+
const projectIdIn = (dir) => resolveProjectId(options.projectId, dir);
|
|
268
266
|
/**
|
|
269
267
|
* The specifier the HTML points at, e.g. `/src/main.tsx`.
|
|
270
268
|
*
|
|
@@ -315,7 +313,9 @@ export function reticle(options = {}) {
|
|
|
315
313
|
*/
|
|
316
314
|
const resolveLazy = () => {
|
|
317
315
|
// Wherever the daemon for this project actually is; the order is argued in discover-port.ts.
|
|
318
|
-
const
|
|
316
|
+
const appDir = root ?? process.cwd();
|
|
317
|
+
const resolved = { ...options, projectId: projectIdIn(appDir) };
|
|
318
|
+
const { port, warning } = resolveDaemonPort(resolved.port, resolved.projectId, appDir);
|
|
319
319
|
if (warning !== undefined && warning !== lastPortWarning) {
|
|
320
320
|
lastPortWarning = warning;
|
|
321
321
|
warn(warning);
|
|
@@ -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.7.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.7.0",
|
|
45
|
+
"@reticlehq/core": "3.7.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.7.0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"vite": ">=4"
|