@reticlehq/vite-plugin 2.13.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,4 +1,5 @@
1
- export declare const RETICLE_VITE_PLUGIN_NAME = "reticle";
1
+ import { type InjectionWatch } from './injection-postcondition.js';
2
+ export { RETICLE_VITE_PLUGIN_NAME } from './plugin-name.js';
2
3
  /**
3
4
  * Compile-time global carrying the daemon's pairing token, for connects the plugin does not write
4
5
  * itself. The bridge requires the token even on localhost, and nothing in a browser can read the
@@ -12,6 +13,21 @@ export declare const RETICLE_TOKEN_GLOBAL = "__RETICLE_TOKEN__";
12
13
  * by the injected <script src> and served by the load hook below.
13
14
  */
14
15
  export declare const RETICLE_CONNECT_MODULE = "/@reticle-connect";
16
+ /**
17
+ * The URL the injected `<script src>` must actually point at: `base` + the module id.
18
+ *
19
+ * {@link RETICLE_CONNECT_MODULE} is a SERVER-ROOT path, and emitting it verbatim is only correct
20
+ * when Vite is serving from the root. Under `base: '/playground/'` the browser asked for
21
+ * `/@reticle-connect`, Vite answered 404 with its own "did you mean /playground/@reticle-connect"
22
+ * hint, and the page rendered perfectly while never connecting (#676) — the exact failure shape
23
+ * Reticle exists to catch, in Reticle's own setup path.
24
+ *
25
+ * Vite does not prefix tags returned from `transformIndexHtml`, so the prefix has to be applied
26
+ * here. Only a path base is joined: Vite serves the dev app from the root when `base` is an
27
+ * external URL, so prefixing a CDN origin onto a dev-server module would point the tag off-host.
28
+ */
29
+ import { type WatchPattern } from './watch-ignore.js';
30
+ export declare function connectModuleUrl(base: string | undefined): string;
15
31
  /**
16
32
  * The pre-hook, as source for an inline <head> script.
17
33
  *
@@ -73,22 +89,21 @@ export interface ReticleVitePluginOptions {
73
89
  * Off by default because a body is the one part of a request that routinely carries a card
74
90
  * number, a token or a customer's address, and the daemon journals what it is told.
75
91
  *
76
- * It matters that this is reachable at all. The SDK has supported `captureNetworkBodies` on
77
- * `connect()` since bodies existed, but the plugin the documented one-line integration, and the
78
- * only `connect()` most apps ever have — had no way to pass it, and calling `connect()` a second
79
- * time is a no-op. So for every app wired the recommended way, a payload was unreachable: on a
80
- * real payments dashboard, a refund POSTing `amount: 1187.01` into a paise field (a 100x
81
- * under-refund) was visible to Playwright's request inspector and invisible here.
92
+ * Exposed HERE because the plugin is the only `connect()` most apps have and a second `connect()`
93
+ * is a no-op, so an SDK option the plugin cannot pass is an option that does not exist.
82
94
  *
83
- * Also settable as `VITE_RETICLE_CAPTURE_BODIES=1`, so it can be turned on for one debugging
84
- * session without editing vite.config.
95
+ * Also settable as `VITE_RETICLE_CAPTURE_BODIES=1`.
85
96
  */
86
97
  captureNetworkBodies?: boolean;
87
98
  /**
88
- * Make Reticle's OWN presenter visible to snapshots and queries. CONTRIBUTORS ONLY.
99
+ * Retain a FAILED request's response body even with `captureNetworkBodies` off. Default true.
89
100
  *
90
- * Reachable here for the same reason `captureNetworkBodies` is: the plugin is the only `connect()`
91
- * most apps ever have, so an SDK option the plugin cannot pass is an option that does not exist.
101
+ * Also settable as `VITE_RETICLE_NO_ERROR_BODIES=1`, which turns it OFF -- the inverse of the
102
+ * other env vars, because this is the one that defaults on (#800).
103
+ */
104
+ captureErrorBodies?: boolean;
105
+ /**
106
+ * Make Reticle's OWN presenter visible to snapshots and queries. CONTRIBUTORS ONLY.
92
107
  *
93
108
  * The presenter is hidden from every tool by design — an agent that can drive Reticle's own
94
109
  * interface can fabricate its own impact report. The cost is that a HUD change is the only kind of
@@ -104,9 +119,7 @@ export interface ReticleVitePluginOptions {
104
119
  * Off by default: the SDK refuses outside localhost so a page on the open internet cannot be
105
120
  * instrumented by a bridge it happened to reach. Turn it on for a dev server that CANNOT be served
106
121
  * on localhost — a host-based multi-tenant frontend, a white-label app resolving the tenant from
107
- * the `Host` header, anything with cookie-scoped auth on a custom dev hostname. Without it those
108
- * apps cannot use Reticle at all, because the plugin is the only `connect()` they have and a
109
- * second, hand-written one is a no-op.
122
+ * the `Host` header, anything with cookie-scoped auth on a custom dev hostname.
110
123
  *
111
124
  * NOT SUFFICIENT ON ITS OWN — a pairing token is also required. `connectionPolicy` in
112
125
  * `@reticlehq/browser` refuses a non-localhost connect with "a pairing token is required outside
@@ -115,8 +128,7 @@ export interface ReticleVitePluginOptions {
115
128
  * started daemon is normally all it takes; pass `token` yourself only when the daemon's file is
116
129
  * unreachable. A non-loopback BRIDGE additionally has to be `wss://`.
117
130
  *
118
- * Also settable as `VITE_RETICLE_ALLOW_NON_LOCALHOST=1`, so it can be turned on for one session
119
- * without editing vite.config.
131
+ * Also settable as `VITE_RETICLE_ALLOW_NON_LOCALHOST=1`.
120
132
  */
121
133
  allowNonLocalhost?: boolean;
122
134
  /**
@@ -125,28 +137,53 @@ export interface ReticleVitePluginOptions {
125
137
  */
126
138
  onWarn?: (message: string) => void;
127
139
  }
140
+ /**
141
+ * The slice of Vite's `UserConfig` the `config` hook reads. Everything is optional AND nullable
142
+ * because that is what Vite declares — a stand-in that is narrower than the real value is not a
143
+ * looser type, it is a stricter one, and it makes the whole plugin unassignable to `Plugin`.
144
+ */
145
+ export interface ViteUserConfigLike {
146
+ optimizeDeps?: {
147
+ include?: string[] | undefined;
148
+ /** Whichever key the app used — the plugin reads both and writes the one this Vite wants. */
149
+ esbuildOptions?: Record<string, unknown> | undefined;
150
+ rolldownOptions?: Record<string, unknown> | undefined;
151
+ } | undefined;
152
+ define?: Record<string, unknown> | undefined;
153
+ root?: string | undefined;
154
+ /**
155
+ * The app's own watcher config. `watch` is nullable because `null` is how a config switches the
156
+ * watcher off, and `ignored` is `unknown` because Vite's `AnymatchMatcher` is not an array — it is
157
+ * a string, a RegExp, a predicate function, or an array of those. Typing it as an array here is
158
+ * the narrowing that made the whole plugin unassignable to Vite's `Plugin`, and it also invited a
159
+ * runtime defect: see `mergeIgnored`.
160
+ */
161
+ server?: {
162
+ watch?: {
163
+ ignored?: unknown;
164
+ } | null | undefined;
165
+ } | undefined;
166
+ /**
167
+ * Vitest's block, when this config belongs to a Vitest run. Read ONLY to spot browser mode — see
168
+ * `isVitestBrowserServer`. Typed as `unknown` because it is Vitest's shape, not Vite's, and this
169
+ * plugin has no business asserting anything about the rest of it.
170
+ */
171
+ test?: unknown;
172
+ }
128
173
  /** Structural Vite plugin shape — avoids a hard dependency on `vite` while staying assignable to its `Plugin`. */
129
174
  export interface ReticleVitePlugin {
130
175
  name: string;
131
176
  /**
132
177
  * Vite's `config` hook. Used to declare the SDK's CJS runtime deps for pre-bundling — see the
133
178
  * implementation for why omitting them makes the whole SDK fail to load on linked setups.
179
+ *
180
+ * METHOD syntax, not a property, and every field it reads is optional-and-nullable. Both halves
181
+ * are load-bearing, and both are the contravariance trap this file's sibling test documents:
182
+ * a property's parameter is checked strictly, so a narrow stand-in REJECTS the wider `UserConfig`
183
+ * Vite actually passes. `server.watch` is where it bit — Vite types it `WatchOptions | null`,
184
+ * `null` being how a config turns the watcher off, and SvelteKit's template does exactly that.
134
185
  */
135
- config?: (config: {
136
- optimizeDeps?: {
137
- include?: string[];
138
- /** Whichever key the app used — the plugin reads both and writes the one this Vite wants. */
139
- esbuildOptions?: Record<string, unknown>;
140
- rolldownOptions?: Record<string, unknown>;
141
- };
142
- define?: Record<string, string>;
143
- root?: string;
144
- server?: {
145
- watch?: {
146
- ignored?: (string | RegExp)[];
147
- };
148
- };
149
- }) => {
186
+ config?(config: ViteUserConfigLike): {
150
187
  optimizeDeps: {
151
188
  include: string[];
152
189
  [optionsKey: string]: unknown;
@@ -154,7 +191,7 @@ export interface ReticleVitePlugin {
154
191
  define: Record<string, string>;
155
192
  server: {
156
193
  watch: {
157
- ignored: (string | RegExp)[];
194
+ ignored: WatchPattern[];
158
195
  };
159
196
  };
160
197
  };
@@ -172,6 +209,9 @@ export interface ReticleVitePlugin {
172
209
  configResolved?: (config: {
173
210
  root?: string;
174
211
  command?: string;
212
+ base?: string;
213
+ /** Vitest's block, read only to spot browser mode. See isVitestBrowserServer. */
214
+ test?: unknown;
175
215
  }) => void;
176
216
  /** Dev-server hook: keeps the served connect module from outliving the token it was built without. */
177
217
  configureServer?: (server: ViteDevServerLike) => void;
@@ -179,6 +219,9 @@ export interface ReticleVitePlugin {
179
219
  buildEnd?: () => void;
180
220
  /** Runs the dev-mode injection check immediately. Test seam for the deferred timer. */
181
221
  checkInjectedForTest?: () => void;
222
+ checkHtmlHookForTest?: () => void;
223
+ /** The post-condition watch itself, so a test drives the real predicate and not a copy of it. */
224
+ injectionWatchForTest?: InjectionWatch;
182
225
  }
183
226
  /**
184
227
  * The slice of Vite's dev server this plugin touches, structurally — so `vite` stays a peer the
@@ -208,6 +251,9 @@ export interface ViteDevServerLike {
208
251
  middlewares: {
209
252
  use(handler: (req: {
210
253
  url?: string | undefined;
254
+ headers?: {
255
+ accept?: string | undefined;
256
+ } | undefined;
211
257
  }, res: unknown, next: () => void) => void): void;
212
258
  };
213
259
  moduleGraph: {
@@ -235,10 +281,9 @@ export declare function readPairingToken(): string | undefined;
235
281
  /**
236
282
  * The conventional app-side dev module: `registerStore` / `registerCapabilities` live here.
237
283
  *
238
- * It is imported by CONVENTION rather than by patching the app's entry file. The connect is injected
239
- * into a virtual module, so there is nowhere for a user to add these calls without `init` editing
240
- * `src/main.tsx` — an edit to the file people actually own, for something that is opt-in enrichment.
241
- * Convention costs one `existsSync` and leaves their entry untouched.
284
+ * Imported by CONVENTION rather than by patching the app's entry file: the connect is injected into
285
+ * a virtual module, so the alternative is `init` editing `src/main.tsx` a file the user owns for
286
+ * something that is opt-in enrichment. Convention costs one `existsSync`.
242
287
  */
243
288
  export declare const RETICLE_DEV_MODULE_CANDIDATES: readonly ["src/reticle-dev.ts", "src/reticle-dev.js", "src/reticle-dev.tsx", "src/reticle-dev.jsx"];
244
289
  /** The app's dev module, as an importable path — or null when the app has none. */
@@ -246,14 +291,11 @@ export declare function findDevModule(root: string, exists: (p: string) => boole
246
291
  /**
247
292
  * Which SDK package this app actually has, and whether `install()` applies.
248
293
  *
249
- * The injected connect used to name `@reticlehq/react` unconditionally. That is right for a React
294
+ * The injected connect must NOT name `@reticlehq/react` unconditionally. That is right for a React
250
295
  * app and fatal for any other: `reticle init` gives a Vue or Svelte codebase the framework-neutral
251
296
  * `@reticlehq/browser` — deliberately, because a package named `@reticlehq/react` with `react` in
252
- * its peers has no business in a Vue app — and the injected import then names a package that is not
253
- * installed, so nothing connects and the page reports no session with no obvious cause.
254
- *
255
- * Measured end to end on a pristine `npm create vite --template vue` app: init wrote every file
256
- * correctly and the tab never dialled the daemon, because of this one specifier.
297
+ * its peers has no business in a Vue app — and the injected import would then name a package that is
298
+ * not installed, so nothing connects and the page reports no session with no obvious cause.
257
299
  *
258
300
  * The React kit WINS when both resolve: it is a superset (it re-exports the sensor and adds the
259
301
  * adapter), so an app that has it wants component identity. `install()` is the adapter's alone and
@@ -288,5 +330,4 @@ export declare function connectModuleSource(options: ReticleVitePluginOptions, d
288
330
  */
289
331
  export declare const JOURNAL_IGNORE: RegExp;
290
332
  export declare function reticle(options?: ReticleVitePluginOptions): ReticleVitePlugin;
291
- export {};
292
333
  //# sourceMappingURL=index.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- export declare const RETICLE_VITE_PLUGIN_NAME = "reticle";
1
+ import { type InjectionWatch } from './injection-postcondition.js';
2
+ export { RETICLE_VITE_PLUGIN_NAME } from './plugin-name.js';
2
3
  /**
3
4
  * Compile-time global carrying the daemon's pairing token, for connects the plugin does not write
4
5
  * itself. The bridge requires the token even on localhost, and nothing in a browser can read the
@@ -12,6 +13,21 @@ export declare const RETICLE_TOKEN_GLOBAL = "__RETICLE_TOKEN__";
12
13
  * by the injected <script src> and served by the load hook below.
13
14
  */
14
15
  export declare const RETICLE_CONNECT_MODULE = "/@reticle-connect";
16
+ /**
17
+ * The URL the injected `<script src>` must actually point at: `base` + the module id.
18
+ *
19
+ * {@link RETICLE_CONNECT_MODULE} is a SERVER-ROOT path, and emitting it verbatim is only correct
20
+ * when Vite is serving from the root. Under `base: '/playground/'` the browser asked for
21
+ * `/@reticle-connect`, Vite answered 404 with its own "did you mean /playground/@reticle-connect"
22
+ * hint, and the page rendered perfectly while never connecting (#676) — the exact failure shape
23
+ * Reticle exists to catch, in Reticle's own setup path.
24
+ *
25
+ * Vite does not prefix tags returned from `transformIndexHtml`, so the prefix has to be applied
26
+ * here. Only a path base is joined: Vite serves the dev app from the root when `base` is an
27
+ * external URL, so prefixing a CDN origin onto a dev-server module would point the tag off-host.
28
+ */
29
+ import { type WatchPattern } from './watch-ignore.js';
30
+ export declare function connectModuleUrl(base: string | undefined): string;
15
31
  /**
16
32
  * The pre-hook, as source for an inline <head> script.
17
33
  *
@@ -73,22 +89,21 @@ export interface ReticleVitePluginOptions {
73
89
  * Off by default because a body is the one part of a request that routinely carries a card
74
90
  * number, a token or a customer's address, and the daemon journals what it is told.
75
91
  *
76
- * It matters that this is reachable at all. The SDK has supported `captureNetworkBodies` on
77
- * `connect()` since bodies existed, but the plugin the documented one-line integration, and the
78
- * only `connect()` most apps ever have — had no way to pass it, and calling `connect()` a second
79
- * time is a no-op. So for every app wired the recommended way, a payload was unreachable: on a
80
- * real payments dashboard, a refund POSTing `amount: 1187.01` into a paise field (a 100x
81
- * under-refund) was visible to Playwright's request inspector and invisible here.
92
+ * Exposed HERE because the plugin is the only `connect()` most apps have and a second `connect()`
93
+ * is a no-op, so an SDK option the plugin cannot pass is an option that does not exist.
82
94
  *
83
- * Also settable as `VITE_RETICLE_CAPTURE_BODIES=1`, so it can be turned on for one debugging
84
- * session without editing vite.config.
95
+ * Also settable as `VITE_RETICLE_CAPTURE_BODIES=1`.
85
96
  */
86
97
  captureNetworkBodies?: boolean;
87
98
  /**
88
- * Make Reticle's OWN presenter visible to snapshots and queries. CONTRIBUTORS ONLY.
99
+ * Retain a FAILED request's response body even with `captureNetworkBodies` off. Default true.
89
100
  *
90
- * Reachable here for the same reason `captureNetworkBodies` is: the plugin is the only `connect()`
91
- * most apps ever have, so an SDK option the plugin cannot pass is an option that does not exist.
101
+ * Also settable as `VITE_RETICLE_NO_ERROR_BODIES=1`, which turns it OFF -- the inverse of the
102
+ * other env vars, because this is the one that defaults on (#800).
103
+ */
104
+ captureErrorBodies?: boolean;
105
+ /**
106
+ * Make Reticle's OWN presenter visible to snapshots and queries. CONTRIBUTORS ONLY.
92
107
  *
93
108
  * The presenter is hidden from every tool by design — an agent that can drive Reticle's own
94
109
  * interface can fabricate its own impact report. The cost is that a HUD change is the only kind of
@@ -104,9 +119,7 @@ export interface ReticleVitePluginOptions {
104
119
  * Off by default: the SDK refuses outside localhost so a page on the open internet cannot be
105
120
  * instrumented by a bridge it happened to reach. Turn it on for a dev server that CANNOT be served
106
121
  * on localhost — a host-based multi-tenant frontend, a white-label app resolving the tenant from
107
- * the `Host` header, anything with cookie-scoped auth on a custom dev hostname. Without it those
108
- * apps cannot use Reticle at all, because the plugin is the only `connect()` they have and a
109
- * second, hand-written one is a no-op.
122
+ * the `Host` header, anything with cookie-scoped auth on a custom dev hostname.
110
123
  *
111
124
  * NOT SUFFICIENT ON ITS OWN — a pairing token is also required. `connectionPolicy` in
112
125
  * `@reticlehq/browser` refuses a non-localhost connect with "a pairing token is required outside
@@ -115,8 +128,7 @@ export interface ReticleVitePluginOptions {
115
128
  * started daemon is normally all it takes; pass `token` yourself only when the daemon's file is
116
129
  * unreachable. A non-loopback BRIDGE additionally has to be `wss://`.
117
130
  *
118
- * Also settable as `VITE_RETICLE_ALLOW_NON_LOCALHOST=1`, so it can be turned on for one session
119
- * without editing vite.config.
131
+ * Also settable as `VITE_RETICLE_ALLOW_NON_LOCALHOST=1`.
120
132
  */
121
133
  allowNonLocalhost?: boolean;
122
134
  /**
@@ -125,28 +137,53 @@ export interface ReticleVitePluginOptions {
125
137
  */
126
138
  onWarn?: (message: string) => void;
127
139
  }
140
+ /**
141
+ * The slice of Vite's `UserConfig` the `config` hook reads. Everything is optional AND nullable
142
+ * because that is what Vite declares — a stand-in that is narrower than the real value is not a
143
+ * looser type, it is a stricter one, and it makes the whole plugin unassignable to `Plugin`.
144
+ */
145
+ export interface ViteUserConfigLike {
146
+ optimizeDeps?: {
147
+ include?: string[] | undefined;
148
+ /** Whichever key the app used — the plugin reads both and writes the one this Vite wants. */
149
+ esbuildOptions?: Record<string, unknown> | undefined;
150
+ rolldownOptions?: Record<string, unknown> | undefined;
151
+ } | undefined;
152
+ define?: Record<string, unknown> | undefined;
153
+ root?: string | undefined;
154
+ /**
155
+ * The app's own watcher config. `watch` is nullable because `null` is how a config switches the
156
+ * watcher off, and `ignored` is `unknown` because Vite's `AnymatchMatcher` is not an array — it is
157
+ * a string, a RegExp, a predicate function, or an array of those. Typing it as an array here is
158
+ * the narrowing that made the whole plugin unassignable to Vite's `Plugin`, and it also invited a
159
+ * runtime defect: see `mergeIgnored`.
160
+ */
161
+ server?: {
162
+ watch?: {
163
+ ignored?: unknown;
164
+ } | null | undefined;
165
+ } | undefined;
166
+ /**
167
+ * Vitest's block, when this config belongs to a Vitest run. Read ONLY to spot browser mode — see
168
+ * `isVitestBrowserServer`. Typed as `unknown` because it is Vitest's shape, not Vite's, and this
169
+ * plugin has no business asserting anything about the rest of it.
170
+ */
171
+ test?: unknown;
172
+ }
128
173
  /** Structural Vite plugin shape — avoids a hard dependency on `vite` while staying assignable to its `Plugin`. */
129
174
  export interface ReticleVitePlugin {
130
175
  name: string;
131
176
  /**
132
177
  * Vite's `config` hook. Used to declare the SDK's CJS runtime deps for pre-bundling — see the
133
178
  * implementation for why omitting them makes the whole SDK fail to load on linked setups.
179
+ *
180
+ * METHOD syntax, not a property, and every field it reads is optional-and-nullable. Both halves
181
+ * are load-bearing, and both are the contravariance trap this file's sibling test documents:
182
+ * a property's parameter is checked strictly, so a narrow stand-in REJECTS the wider `UserConfig`
183
+ * Vite actually passes. `server.watch` is where it bit — Vite types it `WatchOptions | null`,
184
+ * `null` being how a config turns the watcher off, and SvelteKit's template does exactly that.
134
185
  */
135
- config?: (config: {
136
- optimizeDeps?: {
137
- include?: string[];
138
- /** Whichever key the app used — the plugin reads both and writes the one this Vite wants. */
139
- esbuildOptions?: Record<string, unknown>;
140
- rolldownOptions?: Record<string, unknown>;
141
- };
142
- define?: Record<string, string>;
143
- root?: string;
144
- server?: {
145
- watch?: {
146
- ignored?: (string | RegExp)[];
147
- };
148
- };
149
- }) => {
186
+ config?(config: ViteUserConfigLike): {
150
187
  optimizeDeps: {
151
188
  include: string[];
152
189
  [optionsKey: string]: unknown;
@@ -154,7 +191,7 @@ export interface ReticleVitePlugin {
154
191
  define: Record<string, string>;
155
192
  server: {
156
193
  watch: {
157
- ignored: (string | RegExp)[];
194
+ ignored: WatchPattern[];
158
195
  };
159
196
  };
160
197
  };
@@ -172,6 +209,9 @@ export interface ReticleVitePlugin {
172
209
  configResolved?: (config: {
173
210
  root?: string;
174
211
  command?: string;
212
+ base?: string;
213
+ /** Vitest's block, read only to spot browser mode. See isVitestBrowserServer. */
214
+ test?: unknown;
175
215
  }) => void;
176
216
  /** Dev-server hook: keeps the served connect module from outliving the token it was built without. */
177
217
  configureServer?: (server: ViteDevServerLike) => void;
@@ -179,6 +219,9 @@ export interface ReticleVitePlugin {
179
219
  buildEnd?: () => void;
180
220
  /** Runs the dev-mode injection check immediately. Test seam for the deferred timer. */
181
221
  checkInjectedForTest?: () => void;
222
+ checkHtmlHookForTest?: () => void;
223
+ /** The post-condition watch itself, so a test drives the real predicate and not a copy of it. */
224
+ injectionWatchForTest?: InjectionWatch;
182
225
  }
183
226
  /**
184
227
  * The slice of Vite's dev server this plugin touches, structurally — so `vite` stays a peer the
@@ -208,6 +251,9 @@ export interface ViteDevServerLike {
208
251
  middlewares: {
209
252
  use(handler: (req: {
210
253
  url?: string | undefined;
254
+ headers?: {
255
+ accept?: string | undefined;
256
+ } | undefined;
211
257
  }, res: unknown, next: () => void) => void): void;
212
258
  };
213
259
  moduleGraph: {
@@ -235,10 +281,9 @@ export declare function readPairingToken(): string | undefined;
235
281
  /**
236
282
  * The conventional app-side dev module: `registerStore` / `registerCapabilities` live here.
237
283
  *
238
- * It is imported by CONVENTION rather than by patching the app's entry file. The connect is injected
239
- * into a virtual module, so there is nowhere for a user to add these calls without `init` editing
240
- * `src/main.tsx` — an edit to the file people actually own, for something that is opt-in enrichment.
241
- * Convention costs one `existsSync` and leaves their entry untouched.
284
+ * Imported by CONVENTION rather than by patching the app's entry file: the connect is injected into
285
+ * a virtual module, so the alternative is `init` editing `src/main.tsx` a file the user owns for
286
+ * something that is opt-in enrichment. Convention costs one `existsSync`.
242
287
  */
243
288
  export declare const RETICLE_DEV_MODULE_CANDIDATES: readonly ["src/reticle-dev.ts", "src/reticle-dev.js", "src/reticle-dev.tsx", "src/reticle-dev.jsx"];
244
289
  /** The app's dev module, as an importable path — or null when the app has none. */
@@ -246,14 +291,11 @@ export declare function findDevModule(root: string, exists: (p: string) => boole
246
291
  /**
247
292
  * Which SDK package this app actually has, and whether `install()` applies.
248
293
  *
249
- * The injected connect used to name `@reticlehq/react` unconditionally. That is right for a React
294
+ * The injected connect must NOT name `@reticlehq/react` unconditionally. That is right for a React
250
295
  * app and fatal for any other: `reticle init` gives a Vue or Svelte codebase the framework-neutral
251
296
  * `@reticlehq/browser` — deliberately, because a package named `@reticlehq/react` with `react` in
252
- * its peers has no business in a Vue app — and the injected import then names a package that is not
253
- * installed, so nothing connects and the page reports no session with no obvious cause.
254
- *
255
- * Measured end to end on a pristine `npm create vite --template vue` app: init wrote every file
256
- * correctly and the tab never dialled the daemon, because of this one specifier.
297
+ * its peers has no business in a Vue app — and the injected import would then name a package that is
298
+ * not installed, so nothing connects and the page reports no session with no obvious cause.
257
299
  *
258
300
  * The React kit WINS when both resolve: it is a superset (it re-exports the sensor and adds the
259
301
  * adapter), so an app that has it wants component identity. `install()` is the adapter's alone and
@@ -288,4 +330,3 @@ export declare function connectModuleSource(options: ReticleVitePluginOptions, d
288
330
  */
289
331
  export declare const JOURNAL_IGNORE: RegExp;
290
332
  export declare function reticle(options?: ReticleVitePluginOptions): ReticleVitePlugin;
291
- export {};