diff --git a/AGENT-INSTALL.md b/AGENT-INSTALL.md index 36029fa..0c00c21 100644 --- a/AGENT-INSTALL.md +++ b/AGENT-INSTALL.md @@ -114,7 +114,7 @@ Runtime rule polling also has a request-triggered check, for hosts that freeze i - **Mapping is automatic in `setup`, in its prebuild hook, and in the explicitly opted-in `dev -- ` wrapper.** It parses server source and sends structure, not source text. A standalone `map` command prints locally and uploads only with `--upload`. `protect`, `scan`, `guide`, `status` and `mark-build` do not invoke mapping. - **`scan` makes up to three source edits:** the Patchstack Connector's `\n` + + `) : (\n` + + ` \n` + `)}` ); } - return ( - `{${gate} && (\n` + + const script = (body: string) => ` \n` + - `)}` - ); + ` dangerouslySetInnerHTML={{ __html: '${body}' }}\n` + + ` />\n`; + return `{${gate} ? (\n${script(statements.join(''))}) : (\n${script(sandbox)})}`; } /** Global the widget reads to decide it is running on a published build. */ diff --git a/tests/guide.test.ts b/tests/guide.test.ts index 890eeb8..280c505 100644 --- a/tests/guide.test.ts +++ b/tests/guide.test.ts @@ -658,7 +658,7 @@ describe('guide', () => { expect(renderGuideChecklist(state, false)).toContain('Your live app does not tell Patchstack it is live yet'); const output = renderGuideChecklist(state, false, {}, { verbose: true }); expect(output).toContain('Run: npx @patchstack/connect scan'); - expect(output).toContain('import.meta.env.PROD &&'); + expect(output).toContain('import.meta.env.PROD ?'); expect(output).toContain('window.__PATCHSTACK_PROD__=true;'); }); diff --git a/tests/mark-build.test.ts b/tests/mark-build.test.ts index 2cc37e6..ddfc977 100644 --- a/tests/mark-build.test.ts +++ b/tests/mark-build.test.ts @@ -317,10 +317,22 @@ describe('buildSourceMarkerSnippet', () => { it('sets the marker the widget reads, behind the framework production gate', () => { const snippet = buildSourceMarkerSnippet('tanstack-start'); expect(snippet).toContain('window.__PATCHSTACK_PROD__=true;'); - expect(snippet).toContain('import.meta.env.PROD &&'); + expect(snippet).toContain('import.meta.env.PROD ?'); expect(snippet).toContain(MARKER_ATTR); }); + it('stamps a development build as a sandbox, and never marks it as the published site', () => { + for (const framework of ['tanstack-start', 'next', 'remix', 'react-router', 'gatsby', 'astro']) { + const snippet = buildSourceMarkerSnippet(framework, 'abcdef123456'); + const otherwise = snippet.indexOf(') : ('); + expect(otherwise, framework).toBeGreaterThan(snippet.indexOf('__PATCHSTACK_PROD__')); + const development = snippet.slice(otherwise); + expect(development, framework).toContain('window.__PATCHSTACK_ENV__="sandbox";'); + expect(development, framework).not.toContain('__PATCHSTACK_PROD__'); + expect(development, framework).not.toContain('__PATCHSTACK_BUILD__'); + } + }); + it('never emits an ungated marker, which would also hide the claim flow in dev', () => { for (const framework of ['tanstack-start', 'next', 'remix', 'react-router', 'gatsby']) { const snippet = buildSourceMarkerSnippet(framework); @@ -380,7 +392,7 @@ describe('ensureMarkerInJsxShell', () => { const { source, action } = ensureMarkerInJsxShell(doc(widgetLine), 'tanstack-start'); expect(action).toBe('added'); expect(source.indexOf('__PATCHSTACK_PROD__')).toBeLessThan(source.indexOf('patchstack-widget')); - expect(source).toContain('import.meta.env.PROD &&'); + expect(source).toContain('import.meta.env.PROD ?'); }); it('falls back to when no widget tag is present yet', () => { @@ -619,8 +631,9 @@ describe('ensureMarkerInJsxShell on an Astro layout', () => { expect(action).toBe('added'); expect(source).toContain( - '{import.meta.env.PROD && (\n \n )}', + '{import.meta.env.PROD ? (\n \n ) : (\n' + + ' \n )}', ); expect(source.indexOf('__PATCHSTACK_PROD__')).toBeGreaterThan(source.indexOf(' ')); expect(source.indexOf('__PATCHSTACK_PROD__')).toBeLessThan(source.indexOf(''));