@patchstack/connect 0.3.28 → 0.3.29
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/cli.js +215 -45
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { readFileSync as
|
|
4
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "fs";
|
|
5
5
|
|
|
6
6
|
// src/parsers/index.ts
|
|
7
7
|
import { access } from "fs/promises";
|
|
@@ -1544,7 +1544,7 @@ function isEnvironment(value) {
|
|
|
1544
1544
|
}
|
|
1545
1545
|
|
|
1546
1546
|
// src/mark-build.ts
|
|
1547
|
-
import { existsSync, readdirSync, statSync } from "fs";
|
|
1547
|
+
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
1548
1548
|
import path7 from "path";
|
|
1549
1549
|
|
|
1550
1550
|
// src/stack.ts
|
|
@@ -1687,16 +1687,94 @@ function injectMarker(html, snippet) {
|
|
|
1687
1687
|
}
|
|
1688
1688
|
return stripped + snippet;
|
|
1689
1689
|
}
|
|
1690
|
+
var JSX_SHELL_FRAMEWORKS = /* @__PURE__ */ new Set([
|
|
1691
|
+
"next",
|
|
1692
|
+
"remix",
|
|
1693
|
+
"react-router",
|
|
1694
|
+
"tanstack-start",
|
|
1695
|
+
"gatsby"
|
|
1696
|
+
]);
|
|
1697
|
+
var PRODUCTION_GATES = {
|
|
1698
|
+
next: "process.env.NODE_ENV === 'production'",
|
|
1699
|
+
gatsby: "process.env.NODE_ENV === 'production'",
|
|
1700
|
+
nuxt: "!import.meta.dev"
|
|
1701
|
+
};
|
|
1702
|
+
var DEFAULT_PRODUCTION_GATE = "import.meta.env.PROD";
|
|
1703
|
+
function productionGate(framework) {
|
|
1704
|
+
const mapped = framework !== null ? PRODUCTION_GATES[framework] : void 0;
|
|
1705
|
+
return mapped ?? DEFAULT_PRODUCTION_GATE;
|
|
1706
|
+
}
|
|
1707
|
+
function hasJsxShell(framework) {
|
|
1708
|
+
return framework !== null && JSX_SHELL_FRAMEWORKS.has(framework);
|
|
1709
|
+
}
|
|
1710
|
+
function buildSourceMarkerSnippet(framework) {
|
|
1711
|
+
const gate = productionGate(framework);
|
|
1712
|
+
return `{${gate} && (
|
|
1713
|
+
<script
|
|
1714
|
+
${MARKER_ATTR}="true"
|
|
1715
|
+
dangerouslySetInnerHTML={{ __html: 'window.__PATCHSTACK_PROD__=true;' }}
|
|
1716
|
+
/>
|
|
1717
|
+
)}`;
|
|
1718
|
+
}
|
|
1719
|
+
var PROD_MARKER_GLOBAL = "__PATCHSTACK_PROD__";
|
|
1720
|
+
var REGION_OPEN = "{/* #region patchstack (managed by patchstack-connect \u2014 do not edit) */}";
|
|
1721
|
+
var REGION_CLOSE = "{/* #endregion patchstack */}";
|
|
1722
|
+
var REGION_RE = /[ \t]*\{\/\* #region patchstack[\s\S]*?#endregion patchstack \*\/\}\n?/g;
|
|
1723
|
+
function ensureMarkerInJsxShell(source, framework) {
|
|
1724
|
+
if (!hasJsxShell(framework)) {
|
|
1725
|
+
return { source, action: "unsupported" };
|
|
1726
|
+
}
|
|
1727
|
+
const stripped = source.replace(REGION_RE, "");
|
|
1728
|
+
if (stripped.includes(PROD_MARKER_GLOBAL)) {
|
|
1729
|
+
return { source, action: "manual" };
|
|
1730
|
+
}
|
|
1731
|
+
const block = (indent) => [REGION_OPEN, ...buildSourceMarkerSnippet(framework).split("\n"), REGION_CLOSE].map((line) => `${indent}${line}`).join("\n");
|
|
1732
|
+
const widget = stripped.match(/^([ \t]*).*patchstack-widget.*$/m);
|
|
1733
|
+
if (widget?.index !== void 0) {
|
|
1734
|
+
const indent = widget[1] ?? "";
|
|
1735
|
+
return {
|
|
1736
|
+
source: `${stripped.slice(0, widget.index)}${block(indent)}
|
|
1737
|
+
${stripped.slice(widget.index)}`,
|
|
1738
|
+
action: "added"
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
for (const anchor of [/^([ \t]*)<head>[ \t]*$/m, /^([ \t]*)<body>[ \t]*$/m]) {
|
|
1742
|
+
const match = stripped.match(anchor);
|
|
1743
|
+
if (match?.index === void 0) {
|
|
1744
|
+
continue;
|
|
1745
|
+
}
|
|
1746
|
+
const end = match.index + match[0].length;
|
|
1747
|
+
const indent = `${match[1] ?? ""} `;
|
|
1748
|
+
return {
|
|
1749
|
+
source: `${stripped.slice(0, end)}
|
|
1750
|
+
${block(indent)}${stripped.slice(end)}`,
|
|
1751
|
+
action: "added"
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
return { source, action: "no-anchor" };
|
|
1755
|
+
}
|
|
1756
|
+
function ensureSourceMarker(cwd, shell, framework) {
|
|
1757
|
+
if (shell === null) {
|
|
1758
|
+
return { shell: null, source: "", action: "no-anchor" };
|
|
1759
|
+
}
|
|
1760
|
+
const file = path7.resolve(cwd, shell);
|
|
1761
|
+
const before = readFileSync(file, "utf8");
|
|
1762
|
+
const result = ensureMarkerInJsxShell(before, framework);
|
|
1763
|
+
if (result.source !== before && result.action === "added") {
|
|
1764
|
+
writeFileSync(file, result.source);
|
|
1765
|
+
}
|
|
1766
|
+
return { ...result, shell };
|
|
1767
|
+
}
|
|
1690
1768
|
|
|
1691
1769
|
// src/guide.ts
|
|
1692
|
-
import { existsSync as existsSync10, readFileSync as
|
|
1770
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
1693
1771
|
import path9 from "path";
|
|
1694
1772
|
|
|
1695
1773
|
// src/protect/install/util.ts
|
|
1696
|
-
import { readFileSync, existsSync as existsSync2, writeFileSync } from "fs";
|
|
1774
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1697
1775
|
import { join as join2, dirname } from "path";
|
|
1698
1776
|
import { fileURLToPath } from "url";
|
|
1699
|
-
var read = (p) =>
|
|
1777
|
+
var read = (p) => readFileSync2(p, "utf8");
|
|
1700
1778
|
var log = (msg) => console.log(`patchstack protect: ${msg}`);
|
|
1701
1779
|
function hasDependency(cwd, name) {
|
|
1702
1780
|
try {
|
|
@@ -1732,7 +1810,7 @@ function bakeSiteUuid(cwd, guardRelPath) {
|
|
|
1732
1810
|
log(`${guardRelPath} site UUID already baked`);
|
|
1733
1811
|
return false;
|
|
1734
1812
|
}
|
|
1735
|
-
|
|
1813
|
+
writeFileSync2(guardPath, source.replace(SITE_UUID_PLACEHOLDER, uuid));
|
|
1736
1814
|
log(`baked site UUID into ${guardRelPath} \u2014 live rules from the Patchstack API`);
|
|
1737
1815
|
return true;
|
|
1738
1816
|
}
|
|
@@ -1749,7 +1827,7 @@ function templatesDir() {
|
|
|
1749
1827
|
}
|
|
1750
1828
|
|
|
1751
1829
|
// src/protect/install/adapters/tanstack-supabase.ts
|
|
1752
|
-
import { writeFileSync as
|
|
1830
|
+
import { writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync, copyFileSync } from "fs";
|
|
1753
1831
|
import { join as join3 } from "path";
|
|
1754
1832
|
var CLIENT_TUNNEL = [
|
|
1755
1833
|
"",
|
|
@@ -1863,7 +1941,7 @@ function patchClient(cwd) {
|
|
|
1863
1941
|
log("client.ts anchor not found \u2014 skipping (template changed?)");
|
|
1864
1942
|
return false;
|
|
1865
1943
|
}
|
|
1866
|
-
|
|
1944
|
+
writeFileSync3(p, s.replace(anchor, anchor + "\n" + CLIENT_TUNNEL));
|
|
1867
1945
|
log("patched client.ts (tunnel Supabase through the guard)");
|
|
1868
1946
|
return true;
|
|
1869
1947
|
}
|
|
@@ -1900,7 +1978,7 @@ function patchStart(cwd) {
|
|
|
1900
1978
|
log("start.ts already wired");
|
|
1901
1979
|
return false;
|
|
1902
1980
|
}
|
|
1903
|
-
|
|
1981
|
+
writeFileSync3(p, s);
|
|
1904
1982
|
log("patched start.ts (guard registered as request + function middleware)");
|
|
1905
1983
|
return true;
|
|
1906
1984
|
}
|
|
@@ -2151,7 +2229,7 @@ var nuxtAdapter = {
|
|
|
2151
2229
|
};
|
|
2152
2230
|
|
|
2153
2231
|
// src/protect/install/find-app.ts
|
|
2154
|
-
import { readFileSync as
|
|
2232
|
+
import { readFileSync as readFileSync3, existsSync as existsSync6, readdirSync as readdirSync2, lstatSync } from "fs";
|
|
2155
2233
|
import { join as join8, relative, dirname as dirname3 } from "path";
|
|
2156
2234
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2157
2235
|
"node_modules",
|
|
@@ -2198,7 +2276,7 @@ function findAppInstance(cwd, re) {
|
|
|
2198
2276
|
else if (/\.(?:ts|js|mjs|cjs)$/.test(name)) {
|
|
2199
2277
|
let src;
|
|
2200
2278
|
try {
|
|
2201
|
-
src =
|
|
2279
|
+
src = readFileSync3(p, "utf8");
|
|
2202
2280
|
} catch {
|
|
2203
2281
|
continue;
|
|
2204
2282
|
}
|
|
@@ -2218,11 +2296,11 @@ function importSpecifier(fromRel, toRel, preserveExtension = false) {
|
|
|
2218
2296
|
}
|
|
2219
2297
|
|
|
2220
2298
|
// src/protect/install/register.ts
|
|
2221
|
-
import { writeFileSync as
|
|
2299
|
+
import { writeFileSync as writeFileSync5, existsSync as existsSync8 } from "fs";
|
|
2222
2300
|
import { join as join10 } from "path";
|
|
2223
2301
|
|
|
2224
2302
|
// src/protect/install/generic.ts
|
|
2225
|
-
import { readFileSync as
|
|
2303
|
+
import { readFileSync as readFileSync4, existsSync as existsSync7, mkdirSync as mkdirSync4, copyFileSync as copyFileSync4, readdirSync as readdirSync3, lstatSync as lstatSync2 } from "fs";
|
|
2226
2304
|
import { join as join9 } from "path";
|
|
2227
2305
|
var GUARD_MARKER = "patchstack/guard";
|
|
2228
2306
|
function genericDir(cwd) {
|
|
@@ -2330,7 +2408,7 @@ function guardIsImported(cwd, guardPath) {
|
|
|
2330
2408
|
if (st.isDirectory()) walk2(p, depth + 1);
|
|
2331
2409
|
else if (/\.(?:ts|tsx|js|jsx|mjs|cjs)$/.test(name) && p !== guardPath) {
|
|
2332
2410
|
try {
|
|
2333
|
-
if (
|
|
2411
|
+
if (readFileSync4(p, "utf8").includes(GUARD_MARKER)) found = true;
|
|
2334
2412
|
} catch {
|
|
2335
2413
|
}
|
|
2336
2414
|
}
|
|
@@ -2413,7 +2491,7 @@ function wireRegister(cwd, opts, spec) {
|
|
|
2413
2491
|
const callIdx = preferredIdx === -1 ? appIdx : preferredIdx;
|
|
2414
2492
|
lines.splice(callIdx + 1, 0, REGION, spec.call(entry.appVar), "// #endregion patchstack");
|
|
2415
2493
|
}
|
|
2416
|
-
|
|
2494
|
+
writeFileSync5(p, lines.join("\n"));
|
|
2417
2495
|
changed.push(entry.relPath);
|
|
2418
2496
|
log(`patched ${entry.relPath} (${spec.call(entry.appVar).trim()})`);
|
|
2419
2497
|
return { ok: true, changed: [...new Set(changed)] };
|
|
@@ -2542,7 +2620,7 @@ function runVerify(cwd) {
|
|
|
2542
2620
|
}
|
|
2543
2621
|
|
|
2544
2622
|
// src/widget.ts
|
|
2545
|
-
import { existsSync as existsSync9, readFileSync as
|
|
2623
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
2546
2624
|
import path8 from "path";
|
|
2547
2625
|
var WIDGET_SCRIPT_URL = "https://cdn.patchstack.com/patchstack-widget.js";
|
|
2548
2626
|
var WIDGET_MARKER_ATTR = "data-patchstack-connect-widget";
|
|
@@ -2593,15 +2671,16 @@ function ensureSourceWidget(cwd, siteUuid) {
|
|
|
2593
2671
|
return { shell: null, action: "no-shell" };
|
|
2594
2672
|
}
|
|
2595
2673
|
const file = path8.join(cwd, shell);
|
|
2596
|
-
const before =
|
|
2674
|
+
const before = readFileSync5(file, "utf8");
|
|
2597
2675
|
const { html, action } = ensureWidgetInHtml(before, siteUuid);
|
|
2598
2676
|
if (html !== before) {
|
|
2599
|
-
|
|
2677
|
+
writeFileSync6(file, html);
|
|
2600
2678
|
}
|
|
2601
2679
|
return { shell, action };
|
|
2602
2680
|
}
|
|
2603
2681
|
|
|
2604
2682
|
// src/guide.ts
|
|
2683
|
+
var PROD_MARKER_NEEDLE = "__PATCHSTACK_PROD__";
|
|
2605
2684
|
var WIDGET_NEEDLE2 = "patchstack-widget";
|
|
2606
2685
|
var INSTALL_COMMANDS = {
|
|
2607
2686
|
npm: "npm install --save @patchstack/connect",
|
|
@@ -2685,7 +2764,7 @@ function installCommand(pm) {
|
|
|
2685
2764
|
}
|
|
2686
2765
|
function readPackageJson(cwd) {
|
|
2687
2766
|
try {
|
|
2688
|
-
return JSON.parse(
|
|
2767
|
+
return JSON.parse(readFileSync6(path9.join(cwd, "package.json"), "utf8"));
|
|
2689
2768
|
} catch {
|
|
2690
2769
|
return null;
|
|
2691
2770
|
}
|
|
@@ -2693,7 +2772,7 @@ function readPackageJson(cwd) {
|
|
|
2693
2772
|
function installedVersion(cwd, declaredRange) {
|
|
2694
2773
|
try {
|
|
2695
2774
|
const pkg = JSON.parse(
|
|
2696
|
-
|
|
2775
|
+
readFileSync6(path9.join(cwd, "node_modules", "@patchstack", "connect", "package.json"), "utf8")
|
|
2697
2776
|
);
|
|
2698
2777
|
if (typeof pkg.version === "string" && pkg.version.length > 0) {
|
|
2699
2778
|
return pkg.version;
|
|
@@ -2737,7 +2816,7 @@ function findWidgetMarker(cwd, siteUuid) {
|
|
|
2737
2816
|
if (statSync2(full).size > WIDGET_SCAN_MAX_BYTES) {
|
|
2738
2817
|
continue;
|
|
2739
2818
|
}
|
|
2740
|
-
const content =
|
|
2819
|
+
const content = readFileSync6(full, "utf8");
|
|
2741
2820
|
if (!content.includes(WIDGET_NEEDLE2)) {
|
|
2742
2821
|
continue;
|
|
2743
2822
|
}
|
|
@@ -2755,6 +2834,16 @@ function findWidgetMarker(cwd, siteUuid) {
|
|
|
2755
2834
|
uuidMatches: sawWidget && siteUuid != null ? sawTokenMatch : null
|
|
2756
2835
|
};
|
|
2757
2836
|
}
|
|
2837
|
+
function findProductionMarker(cwd, widgetFileHint) {
|
|
2838
|
+
if (widgetFileHint === null) {
|
|
2839
|
+
return false;
|
|
2840
|
+
}
|
|
2841
|
+
try {
|
|
2842
|
+
return readFileSync6(path9.join(cwd, widgetFileHint), "utf8").includes(PROD_MARKER_NEEDLE);
|
|
2843
|
+
} catch {
|
|
2844
|
+
return false;
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2758
2847
|
function resolveWidgetFileHint(cwd, framework) {
|
|
2759
2848
|
const candidates = [
|
|
2760
2849
|
...framework !== null ? WIDGET_FILE_CANDIDATES[framework] ?? [] : [],
|
|
@@ -2804,6 +2893,7 @@ async function collectGuideState(cwd) {
|
|
|
2804
2893
|
{}
|
|
2805
2894
|
);
|
|
2806
2895
|
const widget = findWidgetMarker(cwd, siteUuid);
|
|
2896
|
+
const widgetFileHint = resolveWidgetFileHint(cwd, stack.framework);
|
|
2807
2897
|
const protection = runVerify(cwd);
|
|
2808
2898
|
return {
|
|
2809
2899
|
projectName: pkg?.name ?? null,
|
|
@@ -2823,7 +2913,8 @@ async function collectGuideState(cwd) {
|
|
|
2823
2913
|
widgetTokenMatches: widget.uuidMatches,
|
|
2824
2914
|
widgetOptOut,
|
|
2825
2915
|
framework: stack.framework,
|
|
2826
|
-
widgetFileHint
|
|
2916
|
+
widgetFileHint,
|
|
2917
|
+
productionMarkerWired: findProductionMarker(cwd, widgetFileHint),
|
|
2827
2918
|
protectionWired: protection.wired,
|
|
2828
2919
|
protectionStack: protection.stack,
|
|
2829
2920
|
protectionChecks: protection.checks
|
|
@@ -2837,6 +2928,12 @@ var ANSI = {
|
|
|
2837
2928
|
yellow: "\x1B[33m",
|
|
2838
2929
|
cyan: "\x1B[36m"
|
|
2839
2930
|
};
|
|
2931
|
+
function needsSourceProductionMarker(state) {
|
|
2932
|
+
if (state.siteUuid === null || state.widgetOptOut || state.widgetFileHint === null) {
|
|
2933
|
+
return false;
|
|
2934
|
+
}
|
|
2935
|
+
return !state.widgetFileHint.toLowerCase().endsWith(".html");
|
|
2936
|
+
}
|
|
2840
2937
|
function countRemainingSteps(state) {
|
|
2841
2938
|
return [
|
|
2842
2939
|
state.installed?.section === "dependencies",
|
|
@@ -2844,6 +2941,7 @@ function countRemainingSteps(state) {
|
|
|
2844
2941
|
state.installScanWired,
|
|
2845
2942
|
!state.hasBuildScript || state.prebuildWired && state.postbuildWired,
|
|
2846
2943
|
state.widgetOptOut || state.widgetInstalled && state.widgetTokenMatches !== false,
|
|
2944
|
+
!needsSourceProductionMarker(state) || state.productionMarkerWired,
|
|
2847
2945
|
state.protectionWired
|
|
2848
2946
|
].filter((step) => !step).length;
|
|
2849
2947
|
}
|
|
@@ -2934,6 +3032,34 @@ function renderGuideChecklist(state, useColor) {
|
|
|
2934
3032
|
lines.push(detail(` ${buildWidgetTag(state.siteUuid)}`));
|
|
2935
3033
|
lines.push(detail("The site UUID is public by design \u2014 it ships in client-side HTML."));
|
|
2936
3034
|
}
|
|
3035
|
+
if (needsSourceProductionMarker(state)) {
|
|
3036
|
+
if (state.productionMarkerWired) {
|
|
3037
|
+
lines.push(done("Production marker wired (widget switches to report mode on the published site)"));
|
|
3038
|
+
} else {
|
|
3039
|
+
lines.push(todo("Add the production marker \u2014 this root is server-rendered, so mark-build cannot stamp it"));
|
|
3040
|
+
lines.push(
|
|
3041
|
+
detail(
|
|
3042
|
+
"Without it the widget treats the published site as build mode and shows the claim flow to visitors."
|
|
3043
|
+
)
|
|
3044
|
+
);
|
|
3045
|
+
const gate = productionGate(state.framework);
|
|
3046
|
+
if (hasJsxShell(state.framework)) {
|
|
3047
|
+
lines.push(detail(`Run \u2192 npx @patchstack/connect scan (adds it to ${state.widgetFileHint} automatically)`));
|
|
3048
|
+
lines.push(detail("Or add it by hand, in <head> above the widget tag:"));
|
|
3049
|
+
for (const snippetLine of buildSourceMarkerSnippet(state.framework).split("\n")) {
|
|
3050
|
+
lines.push(detail(` ${snippetLine}`));
|
|
3051
|
+
}
|
|
3052
|
+
} else {
|
|
3053
|
+
lines.push(
|
|
3054
|
+
detail(
|
|
3055
|
+
`Edit ${state.widgetFileHint} \u2192 using the framework's head mechanism, emit an inline`
|
|
3056
|
+
)
|
|
3057
|
+
);
|
|
3058
|
+
lines.push(detail(` <script>window.__PATCHSTACK_PROD__=true;</script> only when ${gate}.`));
|
|
3059
|
+
}
|
|
3060
|
+
lines.push(detail(`The ${gate} guard is required \u2014 an ungated marker also hides the claim flow in preview.`));
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
2937
3063
|
if (state.protectionWired) {
|
|
2938
3064
|
lines.push(done(`Runtime protection wired (${state.protectionStack})`));
|
|
2939
3065
|
} else {
|
|
@@ -2977,7 +3103,7 @@ function renderGuideChecklist(state, useColor) {
|
|
|
2977
3103
|
}
|
|
2978
3104
|
|
|
2979
3105
|
// src/login.ts
|
|
2980
|
-
import { mkdtempSync, readFileSync as
|
|
3106
|
+
import { mkdtempSync, readFileSync as readFileSync7, rmSync, writeFileSync as writeFileSync7 } from "fs";
|
|
2981
3107
|
import { tmpdir } from "os";
|
|
2982
3108
|
import path10 from "path";
|
|
2983
3109
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -2994,11 +3120,11 @@ function pendingPath(siteUuid) {
|
|
|
2994
3120
|
return path10.join(tmpdir(), `patchstack-login-${key}.json`);
|
|
2995
3121
|
}
|
|
2996
3122
|
function savePendingLogin(siteUuid, pending) {
|
|
2997
|
-
|
|
3123
|
+
writeFileSync7(pendingPath(siteUuid), JSON.stringify(pending), { encoding: "utf8", mode: 384 });
|
|
2998
3124
|
}
|
|
2999
3125
|
function readPendingLogin(siteUuid) {
|
|
3000
3126
|
try {
|
|
3001
|
-
return JSON.parse(
|
|
3127
|
+
return JSON.parse(readFileSync7(pendingPath(siteUuid), "utf8"));
|
|
3002
3128
|
} catch {
|
|
3003
3129
|
return null;
|
|
3004
3130
|
}
|
|
@@ -3122,7 +3248,7 @@ async function loadTypeScript(cwd) {
|
|
|
3122
3248
|
}
|
|
3123
3249
|
|
|
3124
3250
|
// src/map/extract.ts
|
|
3125
|
-
import { readFileSync as
|
|
3251
|
+
import { readFileSync as readFileSync12, realpathSync as realpathSync3 } from "fs";
|
|
3126
3252
|
import { createHash as createHash4 } from "crypto";
|
|
3127
3253
|
import { relative as relative4 } from "path";
|
|
3128
3254
|
|
|
@@ -3378,7 +3504,7 @@ function npmPackageOf(spec) {
|
|
|
3378
3504
|
}
|
|
3379
3505
|
|
|
3380
3506
|
// src/map/sources.ts
|
|
3381
|
-
import { readFileSync as
|
|
3507
|
+
import { readFileSync as readFileSync8, readdirSync as readdirSync5, realpathSync, statSync as statSync3 } from "fs";
|
|
3382
3508
|
import { join as join12, relative as relative2, isAbsolute } from "path";
|
|
3383
3509
|
|
|
3384
3510
|
// src/map/routes.ts
|
|
@@ -3463,7 +3589,7 @@ function hasEntrySignal(text) {
|
|
|
3463
3589
|
}
|
|
3464
3590
|
function detectFramework(cwd) {
|
|
3465
3591
|
try {
|
|
3466
|
-
const pkg = JSON.parse(
|
|
3592
|
+
const pkg = JSON.parse(readFileSync8(join12(cwd, "package.json"), "utf8"));
|
|
3467
3593
|
const d = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
3468
3594
|
if (d["@tanstack/react-start"] || d["@tanstack/start"] || d["@tanstack/solid-start"]) return "tanstack-start";
|
|
3469
3595
|
if (d["next"]) return "next";
|
|
@@ -3660,7 +3786,7 @@ function holdsSourceFile(dir) {
|
|
|
3660
3786
|
}
|
|
3661
3787
|
|
|
3662
3788
|
// src/map/surface.ts
|
|
3663
|
-
import { readFileSync as
|
|
3789
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
3664
3790
|
import { join as join13 } from "path";
|
|
3665
3791
|
var STATIC_GENERATORS = [
|
|
3666
3792
|
{ dep: "vite", label: "vite" },
|
|
@@ -3699,7 +3825,7 @@ var SHAPE_SIGNAL = {
|
|
|
3699
3825
|
};
|
|
3700
3826
|
function readManifest(cwd) {
|
|
3701
3827
|
try {
|
|
3702
|
-
const parsed = JSON.parse(
|
|
3828
|
+
const parsed = JSON.parse(readFileSync9(join13(cwd, "package.json"), "utf8"));
|
|
3703
3829
|
return typeof parsed === "object" && parsed !== null ? parsed : null;
|
|
3704
3830
|
} catch {
|
|
3705
3831
|
return null;
|
|
@@ -3712,7 +3838,7 @@ function nextExportsStatically(cwd, manifest, ts) {
|
|
|
3712
3838
|
for (const file of ["next.config.js", "next.config.mjs", "next.config.ts", "next.config.cjs"]) {
|
|
3713
3839
|
let text;
|
|
3714
3840
|
try {
|
|
3715
|
-
text =
|
|
3841
|
+
text = readFileSync9(join13(cwd, file), "utf8");
|
|
3716
3842
|
} catch {
|
|
3717
3843
|
continue;
|
|
3718
3844
|
}
|
|
@@ -4112,7 +4238,7 @@ function dedupeSinks(sinks, owner) {
|
|
|
4112
4238
|
}
|
|
4113
4239
|
|
|
4114
4240
|
// src/map/module-graph.ts
|
|
4115
|
-
import { readFileSync as
|
|
4241
|
+
import { readFileSync as readFileSync10, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
|
|
4116
4242
|
import { dirname as dirname4, join as join14, relative as relative3, resolve as resolvePath } from "path";
|
|
4117
4243
|
var RESOLVE_EXTS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
4118
4244
|
function createModuleGraph(ts, opts) {
|
|
@@ -4121,7 +4247,7 @@ function createModuleGraph(ts, opts) {
|
|
|
4121
4247
|
if (cache.has(file)) return cache.get(file) ?? null;
|
|
4122
4248
|
let entry = null;
|
|
4123
4249
|
try {
|
|
4124
|
-
const text =
|
|
4250
|
+
const text = readFileSync10(file, "utf8");
|
|
4125
4251
|
const sf = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, guessScriptKind(ts, file));
|
|
4126
4252
|
const bindings = buildModuleBindings(sf, ts);
|
|
4127
4253
|
entry = { fnSinks: collectLocalSinks(sf, ts, bindings), calleesOf: collectCallees(sf, ts), bindings };
|
|
@@ -4863,7 +4989,7 @@ function handlerEntry(name, kindLabel, params, body, ts, localSinks, bindings, c
|
|
|
4863
4989
|
}
|
|
4864
4990
|
|
|
4865
4991
|
// src/map/imports.ts
|
|
4866
|
-
import { readFileSync as
|
|
4992
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
4867
4993
|
import { join as join15 } from "path";
|
|
4868
4994
|
var MAX_SITES = 5;
|
|
4869
4995
|
var NPM_NAME = /^(?:@[^/@\s~][^/@\s]*\/)?[^/@\s.~][^/@\s]*$/;
|
|
@@ -4875,7 +5001,7 @@ function readPathAliases(cwd) {
|
|
|
4875
5001
|
for (const name of ["tsconfig.json", "jsconfig.json"]) {
|
|
4876
5002
|
let paths;
|
|
4877
5003
|
try {
|
|
4878
|
-
const raw =
|
|
5004
|
+
const raw = readFileSync11(join15(cwd, name), "utf8");
|
|
4879
5005
|
const parsed = JSON.parse(stripJsonComments(raw));
|
|
4880
5006
|
paths = parsed.compilerOptions?.paths;
|
|
4881
5007
|
} catch {
|
|
@@ -5254,7 +5380,7 @@ async function extractInputMap(cwd, ts, options = {}) {
|
|
|
5254
5380
|
const hopTargets = /* @__PURE__ */ new Set();
|
|
5255
5381
|
for (const file of files) {
|
|
5256
5382
|
try {
|
|
5257
|
-
const text =
|
|
5383
|
+
const text = readFileSync12(file, "utf8");
|
|
5258
5384
|
const relFile = relative4(cwd, file);
|
|
5259
5385
|
sourceBytes += text.length;
|
|
5260
5386
|
if (!hasEntrySignal(text)) {
|
|
@@ -5309,7 +5435,7 @@ async function extractInputMap(cwd, ts, options = {}) {
|
|
|
5309
5435
|
for (const target of hopTargets) {
|
|
5310
5436
|
if (entryFiles.has(target)) continue;
|
|
5311
5437
|
try {
|
|
5312
|
-
const text =
|
|
5438
|
+
const text = readFileSync12(target, "utf8");
|
|
5313
5439
|
const sf = ts.createSourceFile(target, text, ts.ScriptTarget.Latest, true, guessScriptKind(ts, target));
|
|
5314
5440
|
const bindings = buildModuleBindings(sf, ts);
|
|
5315
5441
|
const called = collectInvocations(sf, ts, bindings, { file: target, owner: relative4(cwd, target), graph });
|
|
@@ -5440,7 +5566,7 @@ async function buildInputMap(cwd, options = {}) {
|
|
|
5440
5566
|
}
|
|
5441
5567
|
|
|
5442
5568
|
// src/setup.ts
|
|
5443
|
-
import { readFileSync as
|
|
5569
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "fs";
|
|
5444
5570
|
import path11 from "path";
|
|
5445
5571
|
var SCAN_COMMAND = "patchstack-connect scan";
|
|
5446
5572
|
var MARK_BUILD_COMMAND = "patchstack-connect mark-build";
|
|
@@ -5460,7 +5586,7 @@ function appendHook(existing, command) {
|
|
|
5460
5586
|
}
|
|
5461
5587
|
function wireBuildScripts(cwd, packageManager) {
|
|
5462
5588
|
const target = path11.join(cwd, "package.json");
|
|
5463
|
-
const raw =
|
|
5589
|
+
const raw = readFileSync13(target, "utf8");
|
|
5464
5590
|
const pkg = JSON.parse(raw);
|
|
5465
5591
|
const scripts = pkg.scripts ?? {};
|
|
5466
5592
|
const build = scripts.build;
|
|
@@ -5509,7 +5635,7 @@ function wireBuildScripts(cwd, packageManager) {
|
|
|
5509
5635
|
const indentMatch = raw.match(/^[\t ]+(?=")/m)?.[0];
|
|
5510
5636
|
const indent = indentMatch?.includes(" ") ? " " : indentMatch?.length ?? 2;
|
|
5511
5637
|
const trailingNewline = raw.endsWith("\n") ? "\n" : "";
|
|
5512
|
-
|
|
5638
|
+
writeFileSync8(target, `${JSON.stringify(pkg, null, indent)}${trailingNewline}`, "utf8");
|
|
5513
5639
|
if (build === void 0 || build.trim().length === 0) {
|
|
5514
5640
|
return {
|
|
5515
5641
|
changed: true,
|
|
@@ -5810,7 +5936,7 @@ async function runMap(args) {
|
|
|
5810
5936
|
const json = JSON.stringify(map, null, 2);
|
|
5811
5937
|
const out = getStringFlag(args.flags, "out");
|
|
5812
5938
|
if (out) {
|
|
5813
|
-
|
|
5939
|
+
writeFileSync9(out, json);
|
|
5814
5940
|
console.error(`patchstack: wrote ${out}`);
|
|
5815
5941
|
} else if (args.flags.get("upload") !== true) {
|
|
5816
5942
|
console.log(json);
|
|
@@ -5883,6 +6009,9 @@ async function runScan(args, options = {}) {
|
|
|
5883
6009
|
}
|
|
5884
6010
|
return 0;
|
|
5885
6011
|
}
|
|
6012
|
+
if (config.widget) {
|
|
6013
|
+
reportSourceMarker(detectStack(payload.packages).framework);
|
|
6014
|
+
}
|
|
5886
6015
|
const provisioning = config.siteUuid === null;
|
|
5887
6016
|
if (provisioning) {
|
|
5888
6017
|
console.log("No site UUID configured \u2014 provisioning a new Patchstack site from this manifest\u2026");
|
|
@@ -5965,6 +6094,41 @@ function reportSourceWidget(siteUuid) {
|
|
|
5965
6094
|
console.warn(`Widget: skipped (${err.message}).`);
|
|
5966
6095
|
}
|
|
5967
6096
|
}
|
|
6097
|
+
function reportSourceMarker(framework) {
|
|
6098
|
+
try {
|
|
6099
|
+
const shell = resolveWidgetFileHint(process.cwd(), framework);
|
|
6100
|
+
if (shell === null || shell.toLowerCase().endsWith(".html")) {
|
|
6101
|
+
return;
|
|
6102
|
+
}
|
|
6103
|
+
const result = ensureSourceMarker(process.cwd(), shell, framework);
|
|
6104
|
+
switch (result.action) {
|
|
6105
|
+
case "added":
|
|
6106
|
+
console.log(`Production marker: added to ${shell} (guarded by ${productionGate(framework)}).`);
|
|
6107
|
+
console.log(" This root is server-rendered, so the marker ships in source rather than built HTML.");
|
|
6108
|
+
break;
|
|
6109
|
+
case "manual":
|
|
6110
|
+
console.log(`Production marker: already set in ${shell} \u2014 left untouched.`);
|
|
6111
|
+
break;
|
|
6112
|
+
case "no-anchor":
|
|
6113
|
+
case "unsupported":
|
|
6114
|
+
console.log(
|
|
6115
|
+
`Production marker: ${shell} needs it by hand \u2014 without it the widget reads the published site as build mode.`
|
|
6116
|
+
);
|
|
6117
|
+
if (hasJsxShell(framework)) {
|
|
6118
|
+
for (const line of buildSourceMarkerSnippet(framework).split("\n")) {
|
|
6119
|
+
console.log(` ${line}`);
|
|
6120
|
+
}
|
|
6121
|
+
} else {
|
|
6122
|
+
console.log(
|
|
6123
|
+
` Emit <script>window.__PATCHSTACK_PROD__=true;</script> only when ${productionGate(framework)}.`
|
|
6124
|
+
);
|
|
6125
|
+
}
|
|
6126
|
+
break;
|
|
6127
|
+
}
|
|
6128
|
+
} catch (err) {
|
|
6129
|
+
console.warn(`Production marker: skipped (${err.message}).`);
|
|
6130
|
+
}
|
|
6131
|
+
}
|
|
5968
6132
|
async function runProtectCommand(args) {
|
|
5969
6133
|
if (args.flags.get("check") === true) {
|
|
5970
6134
|
const report = runVerify(process.cwd());
|
|
@@ -6096,7 +6260,7 @@ async function runGuide(args) {
|
|
|
6096
6260
|
console.log("\u2014\u2014\u2014\u2014 Full reference guide (ships as AGENT-INSTALL.md) \u2014\u2014\u2014\u2014");
|
|
6097
6261
|
console.log("");
|
|
6098
6262
|
const guidePath = new URL("../AGENT-INSTALL.md", import.meta.url);
|
|
6099
|
-
console.log(
|
|
6263
|
+
console.log(readFileSync14(guidePath, "utf8"));
|
|
6100
6264
|
return 0;
|
|
6101
6265
|
}
|
|
6102
6266
|
async function runSetup(args) {
|
|
@@ -6270,14 +6434,20 @@ async function runMarkBuild(args) {
|
|
|
6270
6434
|
}
|
|
6271
6435
|
const files = findHtmlFiles(dir);
|
|
6272
6436
|
if (files.length === 0) {
|
|
6273
|
-
console.warn(`mark-build:
|
|
6437
|
+
console.warn(`mark-build: found ${dir} but no HTML files in it.`);
|
|
6438
|
+
console.warn(
|
|
6439
|
+
"mark-build: this build looks server-rendered, so the production flag has nothing to stamp."
|
|
6440
|
+
);
|
|
6441
|
+
console.warn(
|
|
6442
|
+
"mark-build: add the marker to your root shell instead \u2014 run `patchstack-connect guide` for the snippet."
|
|
6443
|
+
);
|
|
6274
6444
|
return 0;
|
|
6275
6445
|
}
|
|
6276
6446
|
const snippet = buildInjectionSnippet(checksum, stack);
|
|
6277
6447
|
let marked = 0;
|
|
6278
6448
|
let widgetTouched = 0;
|
|
6279
6449
|
for (const file of files) {
|
|
6280
|
-
const before =
|
|
6450
|
+
const before = readFileSync14(file, "utf8");
|
|
6281
6451
|
let after = injectMarker(before, snippet);
|
|
6282
6452
|
if (widgetUuid !== null) {
|
|
6283
6453
|
const ensured = ensureWidgetInHtml(after, widgetUuid);
|
|
@@ -6287,7 +6457,7 @@ async function runMarkBuild(args) {
|
|
|
6287
6457
|
after = ensured.html;
|
|
6288
6458
|
}
|
|
6289
6459
|
if (after !== before) {
|
|
6290
|
-
|
|
6460
|
+
writeFileSync9(file, after);
|
|
6291
6461
|
marked += 1;
|
|
6292
6462
|
}
|
|
6293
6463
|
}
|