@reticlehq/init 3.3.0 → 3.4.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/detect/capabilities.d.ts +4 -1
- package/dist/detect/capabilities.js +45 -9
- package/dist/detect/detect.d.ts +45 -2
- package/dist/detect/detect.js +76 -5
- package/dist/detect/dev-script.d.ts +26 -2
- package/dist/detect/dev-script.js +80 -7
- package/dist/detect/non-js-project.d.ts +1 -1
- package/dist/detect/non-js-project.js +6 -1
- package/dist/detect/workspace-apps.d.ts +21 -0
- package/dist/detect/workspace-apps.js +111 -12
- package/dist/detect/workspace-redirect.js +28 -2
- package/dist/diagnose/containerised-dev-server.d.ts +12 -4
- package/dist/diagnose/containerised-dev-server.js +36 -4
- package/dist/diagnose/csp-check.d.ts +12 -6
- package/dist/diagnose/csp-check.js +61 -13
- package/dist/diagnose/csp-doctor.d.ts +13 -1
- package/dist/diagnose/csp-doctor.js +2 -2
- package/dist/diagnose/csp-step.d.ts +27 -0
- package/dist/diagnose/csp-step.js +86 -0
- package/dist/diagnose/desktop-doctor.js +8 -2
- package/dist/diagnose/install-hint.d.ts +9 -1
- package/dist/diagnose/install-hint.js +17 -7
- package/dist/diagnose/install-retries.d.ts +16 -1
- package/dist/diagnose/install-retries.js +50 -3
- package/dist/diagnose/terminal-wrap.d.ts +18 -0
- package/dist/diagnose/terminal-wrap.js +63 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +6 -2
- package/dist/no-package-json.d.ts +10 -0
- package/dist/no-package-json.js +121 -0
- package/dist/node-io.d.ts +9 -5
- package/dist/node-io.js +25 -7
- package/dist/patch/angular.d.ts +68 -0
- package/dist/patch/angular.js +216 -0
- package/dist/patch/astro-patch.d.ts +1 -1
- package/dist/patch/astro-patch.js +2 -2
- package/dist/patch/cra.d.ts +0 -2
- package/dist/patch/cra.js +0 -2
- package/dist/patch/electron-patch.d.ts +10 -1
- package/dist/patch/electron-patch.js +79 -27
- package/dist/patch/electron-vite-patch.js +1 -1
- package/dist/patch/nuxt-patch.d.ts +1 -1
- package/dist/patch/nuxt-patch.js +2 -2
- package/dist/patch/nuxt-snippets.js +6 -6
- package/dist/patch/remix.d.ts +24 -0
- package/dist/patch/remix.js +59 -0
- package/dist/patch/snippets.d.ts +57 -9
- package/dist/patch/snippets.js +119 -28
- package/dist/patch/static-page.d.ts +39 -0
- package/dist/patch/static-page.js +102 -0
- package/dist/patch/tanstack-start.d.ts +36 -8
- package/dist/patch/tanstack-start.js +118 -29
- package/dist/patch/vite-config.d.ts +5 -0
- package/dist/patch/vite-config.js +8 -0
- package/dist/patch/vite-owning-config.d.ts +34 -0
- package/dist/patch/vite-owning-config.js +63 -5
- package/dist/plan/connect-steps.d.ts +7 -1
- package/dist/plan/connect-steps.js +18 -1
- package/dist/plan/framework-adapter.d.ts +3 -1
- package/dist/plan/framework-adapter.js +99 -6
- package/dist/plan/notices.js +4 -3
- package/dist/plan/plan-angular.d.ts +6 -0
- package/dist/plan/plan-angular.js +89 -0
- package/dist/plan/plan-electron-vite.d.ts +6 -3
- package/dist/plan/plan-electron-vite.js +39 -8
- package/dist/plan/plan-framework.d.ts +8 -46
- package/dist/plan/plan-framework.js +81 -312
- package/dist/plan/plan-types.d.ts +29 -0
- package/dist/plan/plan-vite.d.ts +67 -0
- package/dist/plan/plan-vite.js +256 -0
- package/dist/plan/plan.js +38 -34
- package/dist/plan/port-steps.d.ts +45 -0
- package/dist/plan/port-steps.js +119 -0
- package/dist/plan/preflight.d.ts +17 -3
- package/dist/plan/preflight.js +53 -23
- package/dist/register/mcp.d.ts +18 -5
- package/dist/register/mcp.js +48 -8
- package/dist/run-types.d.ts +8 -0
- package/dist/run.js +111 -78
- package/package.json +3 -4
- package/NOTICE +0 -5
package/dist/patch/snippets.d.ts
CHANGED
|
@@ -2,8 +2,34 @@
|
|
|
2
2
|
* Generated file contents and copy-paste snippets for `reticle init`. Kept as named constants/builders
|
|
3
3
|
* so the runner never inlines free strings.
|
|
4
4
|
*/
|
|
5
|
-
import { UiLibrary } from '../detect/detect.js';
|
|
5
|
+
import { Framework, UiLibrary } from '../detect/detect.js';
|
|
6
6
|
import type { FoundStore } from '../detect/capabilities.js';
|
|
7
|
+
/**
|
|
8
|
+
* The SDK as one import a plain page can actually resolve.
|
|
9
|
+
*
|
|
10
|
+
* `init` used to tell static-HTML users that a page with no build step could not load the SDK, and
|
|
11
|
+
* to go and stand up a bundler. That is true of a BARE specifier and false of a URL, and the
|
|
12
|
+
* difference was the whole road for every server-rendered app we hear from: FastAPI, Flask, Django,
|
|
13
|
+
* Streamlit, Rails. Proven end to end before this shipped, on a page served by `python3 -m
|
|
14
|
+
* http.server`: a session connected, a snapshot returned, and two act_and_wait calls came back
|
|
15
|
+
* `verified: "yes"`.
|
|
16
|
+
*
|
|
17
|
+
* jsDelivr rather than esm.sh, measured: a third of the requests and a third of the bytes for the
|
|
18
|
+
* same result. `/+esm` is what makes it work, and the bare package URL does NOT: every file in
|
|
19
|
+
* `dist` still carries bare workspace specifiers such as `@reticlehq/core`, so an unbundled entry
|
|
20
|
+
* point dies on the first import. That is also why adding `unpkg`/`jsdelivr` fields to package.json
|
|
21
|
+
* would not help.
|
|
22
|
+
*
|
|
23
|
+
* PINNED to this server's version on purpose. A floating import upgrades the page SDK underneath a
|
|
24
|
+
* daemon that did not move, which is `version_skew` arriving by a route nothing checks.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Where a generated file sends someone to learn `registerStore`. A URL, not a path into
|
|
28
|
+
* `node_modules/@reticlehq/server`: the server is never installed into the app, so the path the
|
|
29
|
+
* generated files used to name did not exist in any project they were written into.
|
|
30
|
+
*/
|
|
31
|
+
export declare const STATE_DOCS_URL = "https://docs.reticle.sh/state-management";
|
|
32
|
+
export declare const CDN_SDK_URL: string;
|
|
7
33
|
/**
|
|
8
34
|
* The connect argument literal: a non-default port adds a `url`, and a projectId is always passed
|
|
9
35
|
* (so the app is identifiable across port changes). Empty string only when neither applies.
|
|
@@ -22,6 +48,7 @@ export declare function connectArg(port: number | undefined, projectId?: string)
|
|
|
22
48
|
* one that does hold a token.
|
|
23
49
|
*/
|
|
24
50
|
export declare function connectArgWithToken(port: number | undefined, projectId: string | undefined, pairingToken: string | undefined): string;
|
|
51
|
+
export declare function usesReactKit(uiLibrary: UiLibrary, framework?: Framework): boolean;
|
|
25
52
|
/**
|
|
26
53
|
* Which SDK package the GENERATED code should import, and whether `install()` applies.
|
|
27
54
|
*
|
|
@@ -33,7 +60,7 @@ export declare function connectArgWithToken(port: number | undefined, projectId:
|
|
|
33
60
|
* `install()` is the React adapter's, not the sensor's: `@reticlehq/browser` exports `reticle` and no
|
|
34
61
|
* `install`, so swapping the specifier alone would trade a missing module for a missing export.
|
|
35
62
|
*/
|
|
36
|
-
export declare function sdkImport(uiLibrary: UiLibrary): {
|
|
63
|
+
export declare function sdkImport(uiLibrary: UiLibrary, framework?: Framework): {
|
|
37
64
|
specifier: string;
|
|
38
65
|
usesInstall: boolean;
|
|
39
66
|
};
|
|
@@ -42,8 +69,11 @@ export declare function viteManual(port: number | undefined, uiLibrary?: UiLibra
|
|
|
42
69
|
/**
|
|
43
70
|
* The electron-vite recipe: the plugin belongs in `renderer`, never in `main` or `preload`.
|
|
44
71
|
*
|
|
45
|
-
* `desktop: true` is load-bearing
|
|
46
|
-
*
|
|
72
|
+
* `desktop: true` is load-bearing: it injects connect() into the entry module and passes
|
|
73
|
+
* `allowInProduction`, because a packaged renderer has no dev server and reports
|
|
74
|
+
* `NODE_ENV=production`, so without it the SDK's production backstop refuses to start. It does not
|
|
75
|
+
* instrument a release — a production-mode `electron-vite build` is stubbed like a web bundle — so
|
|
76
|
+
* a packaged renderer that should be driven is built with `--mode development`.
|
|
47
77
|
*/
|
|
48
78
|
export declare function electronViteManual(port: number | undefined, uiLibrary?: UiLibrary): string;
|
|
49
79
|
/** Next.js config wrap — always printed (we never auto-rewrite next.config). */
|
|
@@ -89,9 +119,14 @@ export declare function registerCapabilitiesCall(testids: readonly string[], ind
|
|
|
89
119
|
* breaks the module everything else hangs off. So the store lines are generated COMMENTED, naming
|
|
90
120
|
* the libraries actually found in package.json, with the exact call to uncomment.
|
|
91
121
|
*/
|
|
92
|
-
export declare function viteDevModuleFile(testids: readonly string[], stores: readonly string[], found?: readonly FoundStore[], uiLibrary?: UiLibrary): string;
|
|
122
|
+
export declare function viteDevModuleFile(testids: readonly string[], stores: readonly string[], found?: readonly FoundStore[], uiLibrary?: UiLibrary, framework?: Framework): string;
|
|
93
123
|
/** Where that module goes. Matches @reticlehq/vite-plugin's convention list. */
|
|
94
124
|
export declare const VITE_DEV_MODULE_PATH = "src/reticle-dev.ts";
|
|
125
|
+
/**
|
|
126
|
+
* The dev module as a relative import from `fromFile` — for the frameworks that connect themselves
|
|
127
|
+
* and so have to load it themselves (see svelteKitHooksFile, tanstackStartConnectFile).
|
|
128
|
+
*/
|
|
129
|
+
export declare function devModuleSpecifier(fromFile: string): string;
|
|
95
130
|
/**
|
|
96
131
|
* electron-vite's renderer Vite root is `src/renderer`, so the plugin looks for
|
|
97
132
|
* `src/renderer/src/reticle-dev.ts` — not the package-root path a plain Vite app uses.
|
|
@@ -137,7 +172,7 @@ export declare function streamlitPageSnippet(connectArgLiteral: string): string;
|
|
|
137
172
|
* `<head>`, so the SDK sees a parsed document.
|
|
138
173
|
*/
|
|
139
174
|
export declare function djangoMiddlewareSnippet(connectArgLiteral: string): string;
|
|
140
|
-
export declare function htmlManual(port: number | undefined, projectId?: string, pairingToken?: string): string;
|
|
175
|
+
export declare function htmlManual(port: number | undefined, projectId?: string, pairingToken?: string, uiLibrary?: UiLibrary): string;
|
|
141
176
|
export declare const NEXT_RETICLE_DEV_PATH = "app/reticle-dev.tsx";
|
|
142
177
|
export declare const SVELTEKIT_HOOKS_PATH = "src/hooks.client.ts";
|
|
143
178
|
/**
|
|
@@ -146,9 +181,9 @@ export declare const SVELTEKIT_HOOKS_PATH = "src/hooks.client.ts";
|
|
|
146
181
|
* component names and source mapping do not, and no CI gate covers this stack. Saying so beats
|
|
147
182
|
* reporting all-green.
|
|
148
183
|
*/
|
|
149
|
-
export declare function unverifiedUiLibraryNote(library: string): string;
|
|
150
|
-
export declare const
|
|
151
|
-
export declare const UNVERIFIED_TANSTACK_START_NOTE = "Reticle has no TanStack Start app and no CI gate for one, so this wiring is untested \u2014 it may work, but nothing proves it and nothing will tell us if it breaks.
|
|
184
|
+
export declare function unverifiedUiLibraryNote(library: string, installGated?: boolean): string;
|
|
185
|
+
export declare const SVELTEKIT_SETUP_GATED_NOTE = "The install gate scaffolds a SvelteKit app on every release and requires this hook to connect a session, so the wiring is proven; driving flows on SvelteKit to a verdict is not. If the hook does not register a session, please open an issue.";
|
|
186
|
+
export declare const UNVERIFIED_TANSTACK_START_NOTE = "Reticle has no TanStack Start app and no CI gate for one, so this wiring is untested \u2014 it may work, but nothing proves it and nothing will tell us if it breaks. Gated today: Vite (React, Vue), Next.js, Nuxt, React Router, Remix, Astro, SvelteKit, Create React App and Angular. If the client effect does not register a session, please open an issue.";
|
|
152
187
|
/**
|
|
153
188
|
* Printed when init detects react-three-fiber. Reticle's model is DOM + store + network; a WebGL
|
|
154
189
|
* canvas is none of those. Without this line, init succeeds, sessions connect, and surrounding UI
|
|
@@ -181,6 +216,19 @@ export declare const WEBPACK4_REACT_SCRIPTS_MAJOR = 5;
|
|
|
181
216
|
export declare function webpack4TranspileNote(reactScriptsMajor: number): string;
|
|
182
217
|
/** React Router's client-entry override point, in framework mode. */
|
|
183
218
|
export declare const REACT_ROUTER_ENTRY_PATH = "app/entry.client.tsx";
|
|
219
|
+
/**
|
|
220
|
+
* The one line that puts Reticle in a React Router client entry — and in a Remix one.
|
|
221
|
+
*
|
|
222
|
+
* The specifier is BUILT, not written as a literal. `import('/@reticle-connect')` is a module only
|
|
223
|
+
* the Vite dev server can answer, so `tsc` cannot resolve it: a fresh framework-mode scaffold went
|
|
224
|
+
* red on `npm run typecheck` (`TS2307: Cannot find module '/@reticle-connect'`) the moment `init`
|
|
225
|
+
* wrote it. A computed specifier is typed `Promise<any>` and never resolved, so no declaration, no
|
|
226
|
+
* suppression comment and no tsconfig edit is needed. The browser still asks the dev server for the
|
|
227
|
+
* same URL, which the plugin serves; `BASE_URL` keeps it right under a non-root `base`. `@vite-ignore`
|
|
228
|
+
* is Vite's own marker for a dynamic import it should not try to analyse, not a lint suppression. In
|
|
229
|
+
* a production build `import.meta.env.DEV` is false and the whole line is dropped.
|
|
230
|
+
*/
|
|
231
|
+
export declare const REACT_ROUTER_CONNECT_LINE = "if (import.meta.env.DEV) void import(/* @vite-ignore */ `${import.meta.env.BASE_URL}@reticle-connect`);";
|
|
184
232
|
/**
|
|
185
233
|
* The client entry `init` writes when React Router framework mode has none.
|
|
186
234
|
*
|
package/dist/patch/snippets.js
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* so the runner never inlines free strings.
|
|
4
4
|
*/
|
|
5
5
|
import { RETICLE_DEFAULT_PORT, bridgeWsUrl, RETICLE_CLIENT_HOST, RETICLE_WS_PATH, } from '@reticlehq/core';
|
|
6
|
-
import { UiLibrary } from '../detect/detect.js';
|
|
6
|
+
import { Framework, UiLibrary } from '../detect/detect.js';
|
|
7
7
|
import { RETICLE_VERSION } from '../version.js';
|
|
8
8
|
import { VITE_ENV_DEV_DECLARATION } from './vite-env-types.js';
|
|
9
|
+
import { posix } from 'node:path';
|
|
9
10
|
/**
|
|
10
11
|
* The SDK as one import a plain page can actually resolve.
|
|
11
12
|
*
|
|
@@ -25,7 +26,13 @@ import { VITE_ENV_DEV_DECLARATION } from './vite-env-types.js';
|
|
|
25
26
|
* PINNED to this server's version on purpose. A floating import upgrades the page SDK underneath a
|
|
26
27
|
* daemon that did not move, which is `version_skew` arriving by a route nothing checks.
|
|
27
28
|
*/
|
|
28
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Where a generated file sends someone to learn `registerStore`. A URL, not a path into
|
|
31
|
+
* `node_modules/@reticlehq/server`: the server is never installed into the app, so the path the
|
|
32
|
+
* generated files used to name did not exist in any project they were written into.
|
|
33
|
+
*/
|
|
34
|
+
export const STATE_DOCS_URL = 'https://docs.reticle.sh/state-management';
|
|
35
|
+
export const CDN_SDK_URL = `https://cdn.jsdelivr.net/npm/@reticlehq/browser@${RETICLE_VERSION}/+esm`;
|
|
29
36
|
/**
|
|
30
37
|
* The connect argument literal: a non-default port adds a `url`, and a projectId is always passed
|
|
31
38
|
* (so the app is identifiable across port changes). Empty string only when neither applies.
|
|
@@ -58,6 +65,35 @@ export function connectArgWithToken(port, projectId, pairingToken) {
|
|
|
58
65
|
const inner = base.length > 0 ? base.slice(1, -1).trim() : '';
|
|
59
66
|
return `{ ${[inner, `token: '${pairingToken}'`].filter((p) => p.length > 0).join(', ')} }`;
|
|
60
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Does this codebase want the React kit, or the framework-neutral sensor? The ONE answer, read by
|
|
70
|
+
* both `frameworkPackages` (what is installed) and `sdkImport` (what generated code imports).
|
|
71
|
+
*
|
|
72
|
+
* The kit is what adds component identity — component names and stacks — and it is worth having
|
|
73
|
+
* wherever React or Preact is rendering (the adapter reaches Preact through `preact/compat`).
|
|
74
|
+
* Everywhere else it is a package named `@reticlehq/react`, carrying `react` in its peer
|
|
75
|
+
* dependencies, being installed into a codebase that has no React in it.
|
|
76
|
+
*
|
|
77
|
+
* UNKNOWN keeps the kit deliberately. Absence of evidence is not evidence of Vue, and guessing
|
|
78
|
+
* "sensor" on no information silently drops component identity from apps that should have it.
|
|
79
|
+
*
|
|
80
|
+
* Except where the absence IS the evidence: on these stacks nothing renders React but a `react` the
|
|
81
|
+
* app depends on itself, so no renderer found is a vanilla, Lit, Solid or Angular app.
|
|
82
|
+
*/
|
|
83
|
+
const RENDERER_IS_DECLARED = new Set([
|
|
84
|
+
Framework.VITE,
|
|
85
|
+
// A package.json with no UI library and no bundler Reticle recognises. It was handed
|
|
86
|
+
// `@reticlehq/react` and `react` — a React install into a page with no React in it.
|
|
87
|
+
Framework.HTML,
|
|
88
|
+
// Forge's Vite template is vanilla TypeScript; a React renderer declares `react` like any app.
|
|
89
|
+
Framework.ELECTRON_FORGE,
|
|
90
|
+
Framework.ANGULAR,
|
|
91
|
+
]);
|
|
92
|
+
export function usesReactKit(uiLibrary, framework) {
|
|
93
|
+
if (UiLibrary.UNKNOWN === uiLibrary)
|
|
94
|
+
return !RENDERER_IS_DECLARED.has(framework);
|
|
95
|
+
return uiLibrary !== UiLibrary.VUE && uiLibrary !== UiLibrary.SVELTE;
|
|
96
|
+
}
|
|
61
97
|
/**
|
|
62
98
|
* Which SDK package the GENERATED code should import, and whether `install()` applies.
|
|
63
99
|
*
|
|
@@ -69,9 +105,8 @@ export function connectArgWithToken(port, projectId, pairingToken) {
|
|
|
69
105
|
* `install()` is the React adapter's, not the sensor's: `@reticlehq/browser` exports `reticle` and no
|
|
70
106
|
* `install`, so swapping the specifier alone would trade a missing module for a missing export.
|
|
71
107
|
*/
|
|
72
|
-
export function sdkImport(uiLibrary) {
|
|
73
|
-
|
|
74
|
-
return react
|
|
108
|
+
export function sdkImport(uiLibrary, framework) {
|
|
109
|
+
return usesReactKit(uiLibrary, framework)
|
|
75
110
|
? { specifier: '@reticlehq/react', usesInstall: true }
|
|
76
111
|
: { specifier: '@reticlehq/browser', usesInstall: false };
|
|
77
112
|
}
|
|
@@ -96,6 +131,15 @@ function frameworkPluginExample(uiLibrary) {
|
|
|
96
131
|
return '/* your existing plugins */';
|
|
97
132
|
}
|
|
98
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* The `defineConfig` import each printed config needs. The snippets call `defineConfig`, and one
|
|
136
|
+
* pasted as printed into an empty file used to die with `ReferenceError: defineConfig is not
|
|
137
|
+
* defined` because the import was left out.
|
|
138
|
+
*/
|
|
139
|
+
const DEFINE_CONFIG_IMPORT = {
|
|
140
|
+
VITE: "import { defineConfig } from 'vite';",
|
|
141
|
+
ELECTRON_VITE: "import { defineConfig } from 'electron-vite';",
|
|
142
|
+
};
|
|
99
143
|
/** The Vite-config snippet printed when we can't safely auto-patch the config. */
|
|
100
144
|
export function viteManual(port, uiLibrary = UiLibrary.UNKNOWN, inject = true, sourceMapping = true) {
|
|
101
145
|
const options = [
|
|
@@ -111,6 +155,7 @@ lowercase JSX tag is not an element. Stamping one crashes the app at commit time
|
|
|
111
155
|
pointers, not the app.`;
|
|
112
156
|
return `Add the Reticle plugin to your Vite config:
|
|
113
157
|
|
|
158
|
+
${DEFINE_CONFIG_IMPORT.VITE}
|
|
114
159
|
import { reticle } from '@reticlehq/vite-plugin';
|
|
115
160
|
|
|
116
161
|
export default defineConfig({
|
|
@@ -125,8 +170,11 @@ from \`vite build\`.${note}`;
|
|
|
125
170
|
/**
|
|
126
171
|
* The electron-vite recipe: the plugin belongs in `renderer`, never in `main` or `preload`.
|
|
127
172
|
*
|
|
128
|
-
* `desktop: true` is load-bearing
|
|
129
|
-
*
|
|
173
|
+
* `desktop: true` is load-bearing: it injects connect() into the entry module and passes
|
|
174
|
+
* `allowInProduction`, because a packaged renderer has no dev server and reports
|
|
175
|
+
* `NODE_ENV=production`, so without it the SDK's production backstop refuses to start. It does not
|
|
176
|
+
* instrument a release — a production-mode `electron-vite build` is stubbed like a web bundle — so
|
|
177
|
+
* a packaged renderer that should be driven is built with `--mode development`.
|
|
130
178
|
*/
|
|
131
179
|
export function electronViteManual(port, uiLibrary = UiLibrary.UNKNOWN) {
|
|
132
180
|
const extras = port === undefined
|
|
@@ -134,6 +182,7 @@ export function electronViteManual(port, uiLibrary = UiLibrary.UNKNOWN) {
|
|
|
134
182
|
: `desktop: true, port: ${String(port)}, captureNetworkBodies: true`;
|
|
135
183
|
return `Add the Reticle plugin to the \`renderer\` block of electron.vite.config, not \`main\`:
|
|
136
184
|
|
|
185
|
+
${DEFINE_CONFIG_IMPORT.ELECTRON_VITE}
|
|
137
186
|
import { reticle } from '@reticlehq/vite-plugin';
|
|
138
187
|
|
|
139
188
|
export default defineConfig({
|
|
@@ -144,8 +193,9 @@ export function electronViteManual(port, uiLibrary = UiLibrary.UNKNOWN) {
|
|
|
144
193
|
},
|
|
145
194
|
});
|
|
146
195
|
|
|
147
|
-
\`desktop: true\` is required: a packaged renderer
|
|
148
|
-
|
|
196
|
+
\`desktop: true\` is required: a packaged renderer has no dev server and reports production, and
|
|
197
|
+
without it the SDK refuses to start there. A production-mode build still ships no Reticle code; to drive a
|
|
198
|
+
packaged renderer with no dev server, build it with \`--mode development\`.
|
|
149
199
|
|
|
150
200
|
Also add \`import '@reticlehq/electron/preload'\` as the first line of your preload, and
|
|
151
201
|
\`installReticleCapture(win)\` in main after you construct the BrowserWindow.`;
|
|
@@ -183,7 +233,7 @@ export function nextReticleDevFile(port, projectId, testids = [], stores = [], f
|
|
|
183
233
|
const storeBlock = found.length > 0
|
|
184
234
|
? found.map((s) => ` registerStore('${s.key}', ${s.ident});`).join('\n')
|
|
185
235
|
: 0 === stores.length
|
|
186
|
-
?
|
|
236
|
+
? ` // No state library detected. If you add one, register it here — see ${STATE_DOCS_URL}`
|
|
187
237
|
: stores.map((h) => ` // import your store, then: ${h}`).join('\n');
|
|
188
238
|
const registerNames = found.length > 0 ? ', registerStore' : '';
|
|
189
239
|
// Multiline connect + single blank after imports (#684): the one-line connect and the double blank
|
|
@@ -398,7 +448,7 @@ ${indent}});`;
|
|
|
398
448
|
* breaks the module everything else hangs off. So the store lines are generated COMMENTED, naming
|
|
399
449
|
* the libraries actually found in package.json, with the exact call to uncomment.
|
|
400
450
|
*/
|
|
401
|
-
export function viteDevModuleFile(testids, stores, found = [], uiLibrary = UiLibrary.REACT) {
|
|
451
|
+
export function viteDevModuleFile(testids, stores, found = [], uiLibrary = UiLibrary.REACT, framework) {
|
|
402
452
|
// The specifier has to be the package `frameworkPackages` installed. This file is the Vite path —
|
|
403
453
|
// the commonest install there is — and it was hardcoded to `@reticlehq/react` while a Vue or
|
|
404
454
|
// Svelte app was being given `@reticlehq/browser`, so the generated file imported something that
|
|
@@ -406,7 +456,7 @@ export function viteDevModuleFile(testids, stores, found = [], uiLibrary = UiLib
|
|
|
406
456
|
//
|
|
407
457
|
// The sensor exports `registerCapabilities` and `registerStore` (and the store adapters) just as
|
|
408
458
|
// the React kit does; the only thing it lacks is `install()`, which this file never called.
|
|
409
|
-
const sdk = sdkImport(uiLibrary);
|
|
459
|
+
const sdk = sdkImport(uiLibrary, framework);
|
|
410
460
|
const ids = testids.map((t) => `'${t}'`).join(', ');
|
|
411
461
|
// A store we FOUND is imported and registered outright — the whole point of the file. The hints
|
|
412
462
|
// stay only for the libraries we can name but not wire (they need an argument we cannot infer).
|
|
@@ -414,10 +464,11 @@ export function viteDevModuleFile(testids, stores, found = [], uiLibrary = UiLib
|
|
|
414
464
|
const storeBlock = found.length > 0
|
|
415
465
|
? found.map((s) => ` registerStore('${s.key}', ${s.ident});`).join('\n')
|
|
416
466
|
: 0 === stores.length
|
|
417
|
-
?
|
|
467
|
+
? ` // No state library detected. If you add one, register it here — see ${STATE_DOCS_URL}`
|
|
418
468
|
: stores.map((h) => ` // import your store, then: ${h}`).join('\n');
|
|
419
469
|
const registerImport = found.length > 0 ? 'registerCapabilities, registerStore' : 'registerCapabilities';
|
|
420
|
-
return `// Dev-only.
|
|
470
|
+
return `// Dev-only. Loaded for you after connect() — by @reticlehq/vite-plugin's injected connect, or by
|
|
471
|
+
// the connect file init wrote for a framework that connects itself — so you do not import it.
|
|
421
472
|
// Self-guards on import.meta.env.DEV, so it is a no-op in a production build.
|
|
422
473
|
import { ${registerImport} } from '${sdk.specifier}';
|
|
423
474
|
${storeImports.length > 0 ? `${storeImports}\n` : ''}
|
|
@@ -444,6 +495,14 @@ ${VITE_ENV_DEV_DECLARATION}`;
|
|
|
444
495
|
}
|
|
445
496
|
/** Where that module goes. Matches @reticlehq/vite-plugin's convention list. */
|
|
446
497
|
export const VITE_DEV_MODULE_PATH = 'src/reticle-dev.ts';
|
|
498
|
+
/**
|
|
499
|
+
* The dev module as a relative import from `fromFile` — for the frameworks that connect themselves
|
|
500
|
+
* and so have to load it themselves (see svelteKitHooksFile, tanstackStartConnectFile).
|
|
501
|
+
*/
|
|
502
|
+
export function devModuleSpecifier(fromFile) {
|
|
503
|
+
const rel = posix.relative(posix.dirname(fromFile), VITE_DEV_MODULE_PATH.replace(/\.ts$/, ''));
|
|
504
|
+
return rel.startsWith('.') ? rel : `./${rel}`;
|
|
505
|
+
}
|
|
447
506
|
/**
|
|
448
507
|
* electron-vite's renderer Vite root is `src/renderer`, so the plugin looks for
|
|
449
508
|
* `src/renderer/src/reticle-dev.ts` — not the package-root path a plain Vite app uses.
|
|
@@ -556,8 +615,21 @@ class ReticleDevMiddleware:
|
|
|
556
615
|
return response
|
|
557
616
|
`;
|
|
558
617
|
}
|
|
559
|
-
export function htmlManual(port, projectId, pairingToken
|
|
618
|
+
export function htmlManual(port, projectId, pairingToken,
|
|
619
|
+
// Which SDK the bundled form imports must be the one init INSTALLED. A page with no UI library
|
|
620
|
+
// now gets the sensor, and a snippet still naming `@reticlehq/react` would import a package that
|
|
621
|
+
// is not there.
|
|
622
|
+
uiLibrary = UiLibrary.UNKNOWN) {
|
|
560
623
|
const withToken = connectArgWithToken(port, projectId, pairingToken);
|
|
624
|
+
const sdk = sdkImport(uiLibrary, Framework.HTML);
|
|
625
|
+
const bundledImport = sdk.usesInstall
|
|
626
|
+
? `void import('${sdk.specifier}').then(({ reticle, install }) => {
|
|
627
|
+
install();
|
|
628
|
+
reticle.connect(${withToken});
|
|
629
|
+
});`
|
|
630
|
+
: `void import('${sdk.specifier}').then(({ reticle }) => {
|
|
631
|
+
reticle.connect(${withToken});
|
|
632
|
+
});`;
|
|
561
633
|
const tokenNote = pairingToken === undefined || 0 === pairingToken.length
|
|
562
634
|
? ''
|
|
563
635
|
: `\n\n The \`token\` is this machine's pairing token, read from ~/.reticle/pairing-token. Keep it: the
|
|
@@ -566,13 +638,10 @@ export function htmlManual(port, projectId, pairingToken) {
|
|
|
566
638
|
return `No Vite/Next plugin detected — wire the dev-only connect by hand. Pick the form for your setup:
|
|
567
639
|
|
|
568
640
|
• Bundled app (Create React App, webpack, Parcel, Vue/Svelte CLI, etc.) — add to your ENTRY module
|
|
569
|
-
(e.g. src/index.js or src/main.js), where '
|
|
641
|
+
(e.g. src/index.js or src/main.js), where '${sdk.specifier}' resolves through your bundler:
|
|
570
642
|
|
|
571
643
|
if (process.env.NODE_ENV !== 'production') {
|
|
572
|
-
|
|
573
|
-
install();
|
|
574
|
-
reticle.connect(${withToken});
|
|
575
|
-
});
|
|
644
|
+
${bundledImport}
|
|
576
645
|
}${tokenNote}
|
|
577
646
|
|
|
578
647
|
• Plain HTML with NO build step (FastAPI, Flask, Django, Rails, Streamlit, a hand-written page) —
|
|
@@ -597,7 +666,7 @@ export const SVELTEKIT_HOOKS_PATH = 'src/hooks.client.ts';
|
|
|
597
666
|
* component names and source mapping do not, and no CI gate covers this stack. Saying so beats
|
|
598
667
|
* reporting all-green.
|
|
599
668
|
*/
|
|
600
|
-
export function unverifiedUiLibraryNote(library) {
|
|
669
|
+
export function unverifiedUiLibraryNote(library, installGated = false) {
|
|
601
670
|
// Preact is not in the same position as Vue or Svelte and must not be told it is. The React
|
|
602
671
|
// adapter reaches Preact through `preact/compat`, which is what `docs/frameworks.mdx` has always
|
|
603
672
|
// said, so telling a Preact reader they get no component identity contradicts our own docs and
|
|
@@ -606,10 +675,10 @@ export function unverifiedUiLibraryNote(library) {
|
|
|
606
675
|
const identity = 'preact' === library
|
|
607
676
|
? 'React component identity — component names and stacks — comes from `@reticlehq/react`, which reaches Preact through `preact/compat`. That path is not covered by a CI gate here, so treat it as expected-to-work rather than proven.'
|
|
608
677
|
: 'What `@reticlehq/react` adds and you will NOT get is React component identity: component names and component stacks.';
|
|
609
|
-
return `Detected a ${library} app. Reticle's DOM, network, console and state tools work here. ${'vue' === library ? 'Source `file:line` does NOT come through: the build plugin stamps JSX and, separately, Svelte components, and a Vue single-file component is neither — measured, a Svelte counter reports `src/lib/Counter.svelte:5` and the same drive on Vue reports no source at all.' : 'Source `file:line` does too — the build plugin stamps it for this library (measured on preact and svelte).'} ${identity} ${
|
|
678
|
+
return `Detected a ${library} app. Reticle's DOM, network, console and state tools work here. ${'vue' === library ? 'Source `file:line` does NOT come through: the build plugin stamps JSX and, separately, Svelte components, and a Vue single-file component is neither — measured, a Svelte counter reports `src/lib/Counter.svelte:5` and the same drive on Vue reports no source at all.' : 'Source `file:line` does too — the build plugin stamps it for this library (measured on preact and svelte).'} ${identity} ${installGated ? `The install gate scaffolds this ${library} stack from scratch on every change, so this SETUP is proven; no gate drives it to a verdict, so the drive is not.` : `No CI gate covers ${library}.`} Driven on every change: Vite + React, Next.js, Astro. If something doesn't work, please open an issue.`;
|
|
610
679
|
}
|
|
611
|
-
export const
|
|
612
|
-
export const UNVERIFIED_TANSTACK_START_NOTE = 'Reticle has no TanStack Start app and no CI gate for one, so this wiring is untested — it may work, but nothing proves it and nothing will tell us if it breaks.
|
|
680
|
+
export const SVELTEKIT_SETUP_GATED_NOTE = 'The install gate scaffolds a SvelteKit app on every release and requires this hook to connect a session, so the wiring is proven; driving flows on SvelteKit to a verdict is not. If the hook does not register a session, please open an issue.';
|
|
681
|
+
export const UNVERIFIED_TANSTACK_START_NOTE = 'Reticle has no TanStack Start app and no CI gate for one, so this wiring is untested — it may work, but nothing proves it and nothing will tell us if it breaks. Gated today: Vite (React, Vue), Next.js, Nuxt, React Router, Remix, Astro, SvelteKit, Create React App and Angular. If the client effect does not register a session, please open an issue.';
|
|
613
682
|
/**
|
|
614
683
|
* Printed when init detects react-three-fiber. Reticle's model is DOM + store + network; a WebGL
|
|
615
684
|
* canvas is none of those. Without this line, init succeeds, sessions connect, and surrounding UI
|
|
@@ -648,6 +717,9 @@ if (import.meta.env.DEV) {
|
|
|
648
717
|
...(sdkVersion.length > 0 ? { sdkVersion } : {}),
|
|
649
718
|
});
|
|
650
719
|
${registerCapabilitiesCall(testids, ' ')}
|
|
720
|
+
// Your stores and capabilities live in ${VITE_DEV_MODULE_PATH}. Only the plugin's injected
|
|
721
|
+
// connect imports that file, and SvelteKit never runs it, so this hook loads it instead.
|
|
722
|
+
void import('${devModuleSpecifier(SVELTEKIT_HOOKS_PATH)}');
|
|
651
723
|
});
|
|
652
724
|
}
|
|
653
725
|
|
|
@@ -697,10 +769,26 @@ export function webpack4TranspileNote(reactScriptsMajor) {
|
|
|
697
769
|
}
|
|
698
770
|
/** React Router's client-entry override point, in framework mode. */
|
|
699
771
|
export const REACT_ROUTER_ENTRY_PATH = 'app/entry.client.tsx';
|
|
700
|
-
/**
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
772
|
+
/**
|
|
773
|
+
* The dev-only module @reticlehq/vite-plugin serves: connect() with the token already in it. Named
|
|
774
|
+
* without its leading slash, because that is the part both the current line and the old one carry.
|
|
775
|
+
*/
|
|
776
|
+
const RETICLE_CONNECT_MODULE = '@reticle-connect';
|
|
777
|
+
/**
|
|
778
|
+
* The one line that puts Reticle in a React Router client entry — and in a Remix one.
|
|
779
|
+
*
|
|
780
|
+
* The specifier is BUILT, not written as a literal. `import('/@reticle-connect')` is a module only
|
|
781
|
+
* the Vite dev server can answer, so `tsc` cannot resolve it: a fresh framework-mode scaffold went
|
|
782
|
+
* red on `npm run typecheck` (`TS2307: Cannot find module '/@reticle-connect'`) the moment `init`
|
|
783
|
+
* wrote it. A computed specifier is typed `Promise<any>` and never resolved, so no declaration, no
|
|
784
|
+
* suppression comment and no tsconfig edit is needed. The browser still asks the dev server for the
|
|
785
|
+
* same URL, which the plugin serves; `BASE_URL` keeps it right under a non-root `base`. `@vite-ignore`
|
|
786
|
+
* is Vite's own marker for a dynamic import it should not try to analyse, not a lint suppression. In
|
|
787
|
+
* a production build `import.meta.env.DEV` is false and the whole line is dropped.
|
|
788
|
+
*/
|
|
789
|
+
export const REACT_ROUTER_CONNECT_LINE = 'if (import.meta.env.DEV) void import(/* @vite-ignore */ `${import.meta.env.BASE_URL}@reticle-connect`);';
|
|
790
|
+
/** The line `init` used to write, which fails `tsc`; replaced in place on a re-run. */
|
|
791
|
+
const REACT_ROUTER_CONNECT_LINE_UNTYPED = "if (import.meta.env.DEV) void import('/@reticle-connect');";
|
|
704
792
|
/**
|
|
705
793
|
* The client entry `init` writes when React Router framework mode has none.
|
|
706
794
|
*
|
|
@@ -742,6 +830,9 @@ startTransition(() => {
|
|
|
742
830
|
* still be evaluated first, and this is a side-effect import.
|
|
743
831
|
*/
|
|
744
832
|
export function reactRouterEntryPatch(source) {
|
|
833
|
+
if (source.includes(REACT_ROUTER_CONNECT_LINE_UNTYPED)) {
|
|
834
|
+
return source.replace(REACT_ROUTER_CONNECT_LINE_UNTYPED, REACT_ROUTER_CONNECT_LINE);
|
|
835
|
+
}
|
|
745
836
|
if (source.includes(RETICLE_CONNECT_MODULE))
|
|
746
837
|
return null;
|
|
747
838
|
const lines = source.split('\n');
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The connect snippet, written INTO a static page rather than printed beside it.
|
|
3
|
+
*
|
|
4
|
+
* A page with no package.json has no build, so the pasted `<script type="module">` is the whole
|
|
5
|
+
* install — proven end to end on a page served by `python3 -m http.server`. `init` used to print it
|
|
6
|
+
* and exit 1 with `no_package_json`, which made the one path that works read as a failure, and left
|
|
7
|
+
* the step to a human who then had to be told where to put it.
|
|
8
|
+
*
|
|
9
|
+
* Two things differ from the printed recipe, both because the file is the deployable artifact:
|
|
10
|
+
*
|
|
11
|
+
* - It runs only on a loopback host. A static file has no development build to strip it, so the
|
|
12
|
+
* host is the only line between this snippet and a published page; guarded, a published copy
|
|
13
|
+
* does not even fetch the SDK. The printed bundled-app recipe deliberately does NOT do this
|
|
14
|
+
* (a hosts-file dev alias defeats it, see html-no-build.test.ts) — that recipe has NODE_ENV.
|
|
15
|
+
* - It is marked, so a re-run recognises its own block and never writes a second one.
|
|
16
|
+
* - It carries no pairing token. The token used to be a literal in the page, so committing or
|
|
17
|
+
* publishing index.html published it; it now sits in a module beside the page that `init`
|
|
18
|
+
* gitignores, which the block imports and any static server serves. Absent — a teammate's
|
|
19
|
+
* checkout, a published copy — the connect goes without one, and the bridge says why it refused.
|
|
20
|
+
*/
|
|
21
|
+
/** The page a static site serves at its root, and the one file `init` will write the snippet into. */
|
|
22
|
+
export declare const HTML_INDEX_PATH = "index.html";
|
|
23
|
+
/** The gitignored module beside the page that holds this machine's pairing token. */
|
|
24
|
+
export declare const STATIC_TOKEN_MODULE = "reticle.local.js";
|
|
25
|
+
/** Opens the block `init` writes; a re-run looks for it. */
|
|
26
|
+
export declare const STATIC_SNIPPET_MARKER = "<!-- reticle:connect (dev only, written by reticle init) -->";
|
|
27
|
+
/** Whether this page already carries a Reticle connect. */
|
|
28
|
+
export declare function hasStaticSnippet(html: string): boolean;
|
|
29
|
+
/** The token module's content. Rewritten on every run, so a re-minted token is picked up. */
|
|
30
|
+
export declare function staticTokenModule(pairingToken: string): string;
|
|
31
|
+
/** The files to write so the token module exists and git ignores it; only what actually changes. */
|
|
32
|
+
export declare function staticTokenFiles(pairingToken: string | undefined, readFile: (path: string) => string | null): Record<string, string>;
|
|
33
|
+
/**
|
|
34
|
+
* The page with the snippet added, or null when it already connects.
|
|
35
|
+
*
|
|
36
|
+
* Before `</body>` so the SDK starts on a parsed document; before `</head>` for a page with no body
|
|
37
|
+
* tag, and at the end for a fragment with neither.
|
|
38
|
+
*/
|
|
39
|
+
export declare function withStaticSnippet(html: string, connectArgLiteral: string): string | null;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The connect snippet, written INTO a static page rather than printed beside it.
|
|
3
|
+
*
|
|
4
|
+
* A page with no package.json has no build, so the pasted `<script type="module">` is the whole
|
|
5
|
+
* install — proven end to end on a page served by `python3 -m http.server`. `init` used to print it
|
|
6
|
+
* and exit 1 with `no_package_json`, which made the one path that works read as a failure, and left
|
|
7
|
+
* the step to a human who then had to be told where to put it.
|
|
8
|
+
*
|
|
9
|
+
* Two things differ from the printed recipe, both because the file is the deployable artifact:
|
|
10
|
+
*
|
|
11
|
+
* - It runs only on a loopback host. A static file has no development build to strip it, so the
|
|
12
|
+
* host is the only line between this snippet and a published page; guarded, a published copy
|
|
13
|
+
* does not even fetch the SDK. The printed bundled-app recipe deliberately does NOT do this
|
|
14
|
+
* (a hosts-file dev alias defeats it, see html-no-build.test.ts) — that recipe has NODE_ENV.
|
|
15
|
+
* - It is marked, so a re-run recognises its own block and never writes a second one.
|
|
16
|
+
* - It carries no pairing token. The token used to be a literal in the page, so committing or
|
|
17
|
+
* publishing index.html published it; it now sits in a module beside the page that `init`
|
|
18
|
+
* gitignores, which the block imports and any static server serves. Absent — a teammate's
|
|
19
|
+
* checkout, a published copy — the connect goes without one, and the bridge says why it refused.
|
|
20
|
+
*/
|
|
21
|
+
import { CDN_SDK_URL } from './snippets.js';
|
|
22
|
+
/** The page a static site serves at its root, and the one file `init` will write the snippet into. */
|
|
23
|
+
export const HTML_INDEX_PATH = 'index.html';
|
|
24
|
+
/** The gitignored module beside the page that holds this machine's pairing token. */
|
|
25
|
+
export const STATIC_TOKEN_MODULE = 'reticle.local.js';
|
|
26
|
+
const GITIGNORE_PATH = '.gitignore';
|
|
27
|
+
/** Opens the block `init` writes; a re-run looks for it. */
|
|
28
|
+
export const STATIC_SNIPPET_MARKER = '<!-- reticle:connect (dev only, written by reticle init) -->';
|
|
29
|
+
const STATIC_SNIPPET_END = '<!-- /reticle:connect -->';
|
|
30
|
+
/** Hostnames a developer's own machine serves a page on. */
|
|
31
|
+
const LOOPBACK_HOSTS = ['localhost', '127.0.0.1', '[::1]'];
|
|
32
|
+
/**
|
|
33
|
+
* Signs that the page already connects, whoever put the snippet there — the printed recipe pasted
|
|
34
|
+
* by hand counts as much as our own block.
|
|
35
|
+
*/
|
|
36
|
+
const ALREADY_CONNECTS = [STATIC_SNIPPET_MARKER, '@reticlehq/browser'];
|
|
37
|
+
const BODY_CLOSE = /<\/body>/i;
|
|
38
|
+
const HEAD_CLOSE = /<\/head>/i;
|
|
39
|
+
/** Whether this page already carries a Reticle connect. */
|
|
40
|
+
export function hasStaticSnippet(html) {
|
|
41
|
+
return ALREADY_CONNECTS.some((sign) => html.includes(sign));
|
|
42
|
+
}
|
|
43
|
+
function block(connectArgLiteral) {
|
|
44
|
+
const hosts = LOOPBACK_HOSTS.map((h) => `'${h}'`).join(', ');
|
|
45
|
+
const arg = '' === connectArgLiteral
|
|
46
|
+
? '{ token: local.token }'
|
|
47
|
+
: `{ ...${connectArgLiteral}, token: local.token }`;
|
|
48
|
+
return ` ${STATIC_SNIPPET_MARKER}
|
|
49
|
+
<script type="module">
|
|
50
|
+
if ([${hosts}].includes(location.hostname)) {
|
|
51
|
+
Promise.all([
|
|
52
|
+
import('${CDN_SDK_URL}'),
|
|
53
|
+
import('./${STATIC_TOKEN_MODULE}').catch(() => ({})),
|
|
54
|
+
]).then(([{ reticle }, local]) => reticle.connect(${arg}));
|
|
55
|
+
}
|
|
56
|
+
</script>
|
|
57
|
+
${STATIC_SNIPPET_END}
|
|
58
|
+
`;
|
|
59
|
+
}
|
|
60
|
+
/** The token module's content. Rewritten on every run, so a re-minted token is picked up. */
|
|
61
|
+
export function staticTokenModule(pairingToken) {
|
|
62
|
+
return (`// This machine's Reticle pairing token, written by \`reticle init\`. Gitignored: never commit\n` +
|
|
63
|
+
`// or publish it — each machine runs \`reticle init\` for its own.\n` +
|
|
64
|
+
`export const token = '${pairingToken}';\n`);
|
|
65
|
+
}
|
|
66
|
+
/** The files to write so the token module exists and git ignores it; only what actually changes. */
|
|
67
|
+
export function staticTokenFiles(pairingToken, readFile) {
|
|
68
|
+
if (pairingToken === undefined || 0 === pairingToken.length)
|
|
69
|
+
return {};
|
|
70
|
+
const files = {};
|
|
71
|
+
const module = staticTokenModule(pairingToken);
|
|
72
|
+
if (readFile(STATIC_TOKEN_MODULE) !== module)
|
|
73
|
+
files[STATIC_TOKEN_MODULE] = module;
|
|
74
|
+
const ignore = readFile(GITIGNORE_PATH) ?? '';
|
|
75
|
+
const covered = ignore
|
|
76
|
+
.split('\n')
|
|
77
|
+
.some((l) => [STATIC_TOKEN_MODULE, `/${STATIC_TOKEN_MODULE}`].includes(l.trim()));
|
|
78
|
+
if (!covered) {
|
|
79
|
+
files[GITIGNORE_PATH] =
|
|
80
|
+
`${'' === ignore ? '' : ignore.replace(/\n*$/, '\n')}${STATIC_TOKEN_MODULE}\n`;
|
|
81
|
+
}
|
|
82
|
+
return files;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The page with the snippet added, or null when it already connects.
|
|
86
|
+
*
|
|
87
|
+
* Before `</body>` so the SDK starts on a parsed document; before `</head>` for a page with no body
|
|
88
|
+
* tag, and at the end for a fragment with neither.
|
|
89
|
+
*/
|
|
90
|
+
export function withStaticSnippet(html, connectArgLiteral) {
|
|
91
|
+
if (hasStaticSnippet(html))
|
|
92
|
+
return null;
|
|
93
|
+
const snippet = block(connectArgLiteral);
|
|
94
|
+
const anchor = BODY_CLOSE.exec(html) ?? HEAD_CLOSE.exec(html);
|
|
95
|
+
if (null === anchor)
|
|
96
|
+
return `${html.trimEnd()}\n${snippet}`;
|
|
97
|
+
// At the start of the closing tag's own line when it sits alone on one, so its indentation stays
|
|
98
|
+
// with it rather than moving onto the first line of the snippet.
|
|
99
|
+
const lineStart = html.lastIndexOf('\n', anchor.index - 1) + 1;
|
|
100
|
+
const at = '' === html.slice(lineStart, anchor.index).trim() ? lineStart : anchor.index;
|
|
101
|
+
return `${html.slice(0, at)}${snippet}${html.slice(at)}`;
|
|
102
|
+
}
|
|
@@ -1,17 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The TanStack Start connect
|
|
2
|
+
* The TanStack Start connect: a client-only component, rendered in the document the root route SSRs.
|
|
3
3
|
*
|
|
4
|
-
* Its own module because Start is the one framework whose document module
|
|
5
|
-
*
|
|
4
|
+
* Its own module because Start is the one framework whose document module is ALSO the server
|
|
5
|
+
* render, so what may be written into it — and what must stay out — is a unit.
|
|
6
6
|
*/
|
|
7
|
+
import { type SourcePatch } from './patch-kind.js';
|
|
7
8
|
/** TanStack Start's document module — the file that SSRs `<html>`. */
|
|
8
9
|
export declare const TANSTACK_START_ROOT_PATH = "src/routes/__root.tsx";
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
+
* Where the connect component goes: beside the `routes/` directory, never inside it — a file under
|
|
12
|
+
* `routes/` is a route to Start's file-based router.
|
|
13
|
+
*/
|
|
14
|
+
export declare function tanstackStartConnectPath(rootPath?: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* The connect component.
|
|
17
|
+
*
|
|
18
|
+
* A dynamic SDK import inside `useEffect`, because a static import on anything the root document
|
|
19
|
+
* imports is evaluated during the server render and 500s the page. Effects never run on the server.
|
|
20
|
+
* The pairing token is the one the Vite plugin inlines as `__RETICLE_TOKEN__`, declared here so the
|
|
21
|
+
* file passes the scaffold's strict `tsc` — the recipe this replaced used the name undeclared and
|
|
22
|
+
* failed with `TS2304: Cannot find name '__RETICLE_TOKEN__'`.
|
|
23
|
+
*
|
|
24
|
+
* It also loads the app's `src/reticle-dev.ts` after connecting. Start is written `inject: false`,
|
|
25
|
+
* and the plugin's injected connect is the only other thing that imports that file, so without this
|
|
26
|
+
* line every store and capability registered there silently never ran.
|
|
27
|
+
*/
|
|
28
|
+
export declare function tanstackStartConnectFile(port: number | undefined, projectId?: string, rootPath?: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Render the component in the root document, just before `<Scripts />`.
|
|
31
|
+
*
|
|
32
|
+
* `<Scripts />` is the anchor because every Start root has to render it — it is how the client
|
|
33
|
+
* bundle reaches the page — whatever the component around it is called (`RootDocument` as the
|
|
34
|
+
* route's `shellComponent` in the current template; `RootComponent` in older ones). A root without
|
|
35
|
+
* one is a shape we do not recognise, and a half-edited document is worse than a documented step.
|
|
36
|
+
*/
|
|
37
|
+
export declare function patchTanstackStartRoot(source: string, rootPath: string): SourcePatch;
|
|
38
|
+
/**
|
|
39
|
+
* The recipe, for a root `patchTanstackStartRoot` would not edit.
|
|
11
40
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* makes about `app/entry.client.tsx`: a half-written document is worse than a documented manual step.
|
|
41
|
+
* The same two edits it makes, stated as edits: create the component, render it before
|
|
42
|
+
* `<Scripts />`. It used to say "add this useEffect to the App component", naming a component the
|
|
43
|
+
* current template does not have, and its snippet used `__RETICLE_TOKEN__` undeclared.
|
|
16
44
|
*/
|
|
17
45
|
export declare function tanstackStartManual(port: number | undefined, projectId?: string, rootPath?: string): string;
|