@patchstack/connect 0.2.11 → 0.3.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/cli.js CHANGED
@@ -1108,94 +1108,6 @@ function isEnvironment(value) {
1108
1108
  // src/mark-build.ts
1109
1109
  import { existsSync, readdirSync, statSync } from "fs";
1110
1110
  import path7 from "path";
1111
-
1112
- // src/stack.ts
1113
- var STACK_RULES = [
1114
- // Meta-frameworks (most specific first).
1115
- { category: "framework", pkg: "@tanstack/react-start", label: "tanstack-start" },
1116
- { category: "framework", pkg: "@tanstack/start", label: "tanstack-start" },
1117
- { category: "framework", pkg: "next", label: "next" },
1118
- { category: "framework", pkg: "nuxt", label: "nuxt" },
1119
- { category: "framework", pkg: "@remix-run/react", label: "remix" },
1120
- { category: "framework", pkg: "@remix-run/node", label: "remix" },
1121
- { category: "framework", pkg: "react-router", label: "react-router" },
1122
- { category: "framework", pkg: "astro", label: "astro" },
1123
- { category: "framework", pkg: "@sveltejs/kit", label: "sveltekit" },
1124
- { category: "framework", pkg: "@builder.io/qwik-city", label: "qwik-city" },
1125
- { category: "framework", pkg: "gatsby", label: "gatsby" },
1126
- { category: "framework", pkg: "express", label: "express" },
1127
- { category: "framework", pkg: "fastify", label: "fastify" },
1128
- // UI runtimes.
1129
- { category: "ui", pkg: "@angular/core", label: "angular" },
1130
- { category: "ui", pkg: "react-dom", label: "react" },
1131
- { category: "ui", pkg: "react", label: "react" },
1132
- { category: "ui", pkg: "vue", label: "vue" },
1133
- { category: "ui", pkg: "svelte", label: "svelte" },
1134
- { category: "ui", pkg: "solid-js", label: "solid" },
1135
- { category: "ui", pkg: "preact", label: "preact" },
1136
- // Bundlers / build tools.
1137
- { category: "bundler", pkg: "vite", label: "vite" },
1138
- { category: "bundler", pkg: "@rspack/core", label: "rspack" },
1139
- { category: "bundler", pkg: "webpack", label: "webpack" },
1140
- { category: "bundler", pkg: "parcel", label: "parcel" },
1141
- { category: "bundler", pkg: "rollup", label: "rollup" },
1142
- { category: "bundler", pkg: "esbuild", label: "esbuild" },
1143
- // Deployment-runtime hints from build deps.
1144
- { category: "runtime", pkg: "wrangler", label: "cloudflare-workers" },
1145
- { category: "runtime", pkg: "@cloudflare/workers-types", label: "cloudflare-workers" },
1146
- { category: "runtime", pkg: "@cloudflare/vite-plugin", label: "cloudflare-workers" },
1147
- { category: "runtime", pkg: "@vercel/node", label: "vercel" },
1148
- { category: "runtime", pkg: "@netlify/functions", label: "netlify" },
1149
- { category: "runtime", pkg: "@netlify/blobs", label: "netlify" },
1150
- // Vibe / builder platforms — the "learn from each platform" signal.
1151
- { category: "builder", pkg: "lovable-tagger", label: "lovable" },
1152
- { category: "builder", pkg: "@replit/vite-plugin-runtime-error-modal", label: "replit" },
1153
- { category: "builder", pkg: "@replit/vite-plugin-cartographer", label: "replit" }
1154
- ];
1155
- var HOSTING_ENV_PATTERNS = [
1156
- /^CF_/,
1157
- /^CLOUDFLARE_/,
1158
- /^VERCEL/,
1159
- /^NETLIFY/,
1160
- /^AWS_(LAMBDA|REGION|EXECUTION)/,
1161
- /^FLY_/,
1162
- /^RENDER/,
1163
- /^RAILWAY_/,
1164
- /^DENO_/,
1165
- /^EDGE_RUNTIME/,
1166
- /^DYNO$/,
1167
- /^K_SERVICE$/,
1168
- /^GAE_/,
1169
- /^FUNCTION_/
1170
- ];
1171
- function collectHostingEnvKeys(env = process.env) {
1172
- return Object.keys(env).filter((key) => HOSTING_ENV_PATTERNS.some((pattern) => pattern.test(key))).sort();
1173
- }
1174
- function detectStack(packages, env = process.env) {
1175
- const present = new Set(packages.map((pkg) => pkg.name));
1176
- const firstMatch = (category) => {
1177
- for (const rule of STACK_RULES) {
1178
- if (rule.category === category && present.has(rule.pkg)) {
1179
- return rule.label;
1180
- }
1181
- }
1182
- return null;
1183
- };
1184
- return {
1185
- framework: firstMatch("framework"),
1186
- ui: firstMatch("ui"),
1187
- bundler: firstMatch("bundler"),
1188
- runtime: firstMatch("runtime"),
1189
- builder: firstMatch("builder"),
1190
- ecosystem: "npm",
1191
- hostingEnvKeys: collectHostingEnvKeys(env)
1192
- };
1193
- }
1194
- function isEmptyStack(stack) {
1195
- return stack.framework === null && stack.ui === null && stack.bundler === null && stack.runtime === null && stack.builder === null && stack.hostingEnvKeys.length === 0;
1196
- }
1197
-
1198
- // src/mark-build.ts
1199
1111
  var MARKER_ATTR = "data-patchstack-build";
1200
1112
  var BUILD_DIR_CANDIDATES = ["dist", "build", "out", ".output/public"];
1201
1113
  function resolveBuildDir(cwd, override) {
@@ -1226,14 +1138,11 @@ function findHtmlFiles(dir) {
1226
1138
  walk2(dir);
1227
1139
  return out;
1228
1140
  }
1229
- function buildInjectionSnippet(checksum, stack) {
1141
+ function buildInjectionSnippet(checksum) {
1230
1142
  const statements = ["window.__PATCHSTACK_PROD__=true;"];
1231
1143
  if (checksum !== null && checksum !== "") {
1232
1144
  statements.push(`window.__PATCHSTACK_BUILD__=${JSON.stringify(checksum)};`);
1233
1145
  }
1234
- if (stack != null && !isEmptyStack(stack)) {
1235
- statements.push(`window.__PATCHSTACK_STACK__=${JSON.stringify(stack)};`);
1236
- }
1237
1146
  return `<script ${MARKER_ATTR}>${statements.join("")}</script>`;
1238
1147
  }
1239
1148
  function injectMarker(html, snippet) {
@@ -1252,13 +1161,17 @@ function injectMarker(html, snippet) {
1252
1161
 
1253
1162
  // src/protect/install.ts
1254
1163
  import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync, copyFileSync } from "fs";
1255
- import { join } from "path";
1164
+ import { join, dirname } from "path";
1256
1165
  import { fileURLToPath } from "url";
1257
- var TEMPLATES = fileURLToPath(new URL("./protect/templates/", import.meta.url));
1166
+ var TEMPLATES = join(dirname(fileURLToPath(import.meta.url)), "protect", "templates");
1167
+ var APP = process.cwd();
1168
+ var PS_DIR = join(APP, "src/integrations/patchstack");
1169
+ var read = (p) => readFileSync(p, "utf8");
1170
+ var log = (msg) => console.log(`patchstack protect: ${msg}`);
1258
1171
  var CLIENT_TUNNEL = [
1259
1172
  "",
1260
- " // PATCHSTACK auto-guard: in the browser, tunnel Supabase traffic through the app's own",
1261
- " // server guard (same-origin) so payloads are inspected before they reach Supabase.",
1173
+ " // PATCHSTACK: in the browser, tunnel Supabase traffic through the app's own server guard",
1174
+ " // (same-origin) so payloads are inspected before they reach Supabase.",
1262
1175
  " if (typeof window !== 'undefined') {",
1263
1176
  " const target = typeof input === 'string' ? input : input instanceof Request ? input.url : String(input);",
1264
1177
  " const method = init?.method ?? (input instanceof Request ? input.method : 'GET');",
@@ -1274,7 +1187,7 @@ var START_IMPORTS = [
1274
1187
  ].join("\n");
1275
1188
  var START_MIDDLEWARE = [
1276
1189
  "",
1277
- "// Patchstack auto-guard: intercept the tunneled data traffic before anything else runs.",
1190
+ "// Patchstack guard: intercept the tunneled Supabase traffic before anything else runs.",
1278
1191
  "const patchstackGuard = createMiddleware().server(async ({ next }) => {",
1279
1192
  " const request = getRequest();",
1280
1193
  " if (request) {",
@@ -1285,76 +1198,38 @@ var START_MIDDLEWARE = [
1285
1198
  "});",
1286
1199
  ""
1287
1200
  ].join("\n");
1288
- var PS_DIR_REL = "src/integrations/patchstack";
1289
- function log(msg) {
1290
- console.log(`patchstack protect: ${msg}`);
1291
- }
1292
1201
  function detectSupportedStack(cwd) {
1293
1202
  const pkgPath = join(cwd, "package.json");
1294
1203
  if (!existsSync2(pkgPath)) return false;
1295
- const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
1204
+ const pkg = JSON.parse(read(pkgPath));
1296
1205
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
1297
1206
  return Boolean(deps["@tanstack/react-start"]) && existsSync2(join(cwd, "src/start.ts")) && existsSync2(join(cwd, "src/integrations/supabase/client.ts"));
1298
1207
  }
1299
- function generateManifest(cwd) {
1300
- const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
1301
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
1302
- const packages = {};
1303
- for (const name of Object.keys(deps)) {
1304
- let version = String(deps[name]).replace(/^[^\d]*/, "");
1305
- const installed = join(cwd, "node_modules", name, "package.json");
1306
- if (existsSync2(installed)) {
1307
- try {
1308
- version = JSON.parse(readFileSync(installed, "utf8")).version;
1309
- } catch {
1310
- }
1311
- }
1312
- packages[name] = version;
1313
- }
1314
- mkdirSync(join(cwd, PS_DIR_REL), { recursive: true });
1315
- const body = "// Patchstack auto-guard manifest \u2014 generated from the lockfile.\n// Do not edit; regenerated by `patchstack-connect protect --manifest` (wired into prebuild).\nexport const manifest = " + JSON.stringify({ packages }, null, 2) + ";\n";
1316
- writeFileSync(join(cwd, PS_DIR_REL, "manifest.js"), body);
1317
- log(`wrote manifest.js (${Object.keys(packages).length} packages)`);
1318
- }
1319
1208
  function scaffold(cwd) {
1320
- const dst = join(cwd, PS_DIR_REL);
1209
+ const dst = join(cwd, "src/integrations/patchstack");
1321
1210
  mkdirSync(dst, { recursive: true });
1322
- copyFileSync(join(TEMPLATES, "engine.js"), join(dst, "engine.js"));
1323
- copyFileSync(join(TEMPLATES, "engine.d.ts"), join(dst, "engine.d.ts"));
1324
- copyFileSync(join(TEMPLATES, "manifest.d.ts"), join(dst, "manifest.d.ts"));
1325
- copyFileSync(join(TEMPLATES, "rules.json"), join(dst, "rules.json"));
1326
1211
  copyFileSync(join(TEMPLATES, "guard.ts"), join(dst, "guard.ts"));
1327
- log("scaffolded engine.js (+types), rules.json, guard.ts");
1212
+ copyFileSync(join(TEMPLATES, "rules.json"), join(dst, "rules.json"));
1213
+ log("scaffolded guard.ts + rules.json");
1328
1214
  }
1329
1215
  function patchClient(cwd) {
1330
1216
  const p = join(cwd, "src/integrations/supabase/client.ts");
1331
- let s = readFileSync(p, "utf8");
1332
- if (s.includes("x-ps-target")) {
1333
- log("client.ts already wired");
1334
- return;
1335
- }
1217
+ let s = read(p);
1218
+ if (s.includes("x-ps-target")) return log("client.ts already wired");
1336
1219
  const anchor = "headers.set('apikey', supabaseKey);";
1337
- if (!s.includes(anchor)) {
1338
- log("client.ts anchor not found (template changed?) \u2014 skipping client patch");
1339
- return;
1340
- }
1341
- s = s.replace(anchor, anchor + "\n" + CLIENT_TUNNEL);
1342
- writeFileSync(p, s);
1220
+ if (!s.includes(anchor)) return log("client.ts anchor not found \u2014 skipping (template changed?)");
1221
+ writeFileSync(p, s.replace(anchor, anchor + "\n" + CLIENT_TUNNEL));
1343
1222
  log("patched client.ts (tunnel Supabase through the guard)");
1344
1223
  }
1345
1224
  function patchStart(cwd) {
1346
1225
  const p = join(cwd, "src/start.ts");
1347
- let s = readFileSync(p, "utf8");
1348
- if (s.includes("patchstackGuard")) {
1349
- log("start.ts already wired");
1350
- return;
1351
- }
1226
+ let s = read(p);
1227
+ if (s.includes("patchstackGuard")) return log("start.ts already wired");
1352
1228
  const importAnchor = 'import { createStart, createMiddleware } from "@tanstack/react-start";';
1353
1229
  const exportAnchor = "export const startInstance";
1354
1230
  const rmAnchor = "requestMiddleware: [";
1355
1231
  if (!s.includes(importAnchor) || !s.includes(exportAnchor) || !s.includes(rmAnchor)) {
1356
- log("start.ts anchors not found (template changed?) \u2014 skipping start patch");
1357
- return;
1232
+ return log("start.ts anchors not found \u2014 skipping (template changed?)");
1358
1233
  }
1359
1234
  s = s.replace(importAnchor, importAnchor + "\n" + START_IMPORTS);
1360
1235
  s = s.replace(exportAnchor, START_MIDDLEWARE + "\n" + exportAnchor);
@@ -1362,36 +1237,14 @@ function patchStart(cwd) {
1362
1237
  writeFileSync(p, s);
1363
1238
  log("patched start.ts (registered the guard as request middleware)");
1364
1239
  }
1365
- function wirePrebuild(cwd) {
1366
- const p = join(cwd, "package.json");
1367
- const pkg = JSON.parse(readFileSync(p, "utf8"));
1368
- pkg.scripts = pkg.scripts || {};
1369
- const step = "patchstack-connect protect --manifest";
1370
- const prebuild = pkg.scripts.prebuild || "";
1371
- if (prebuild.includes(step)) {
1372
- log("prebuild already refreshes the manifest");
1373
- return;
1374
- }
1375
- pkg.scripts.prebuild = prebuild ? prebuild + " && " + step : step;
1376
- writeFileSync(p, JSON.stringify(pkg, null, 2) + "\n");
1377
- log("wired manifest refresh into prebuild");
1378
- }
1379
- function runProtect(cwd, opts = {}) {
1380
- if (opts.manifestOnly) {
1381
- generateManifest(cwd);
1382
- return;
1383
- }
1240
+ function runProtect(cwd) {
1384
1241
  if (!detectSupportedStack(cwd)) {
1385
- log(
1386
- "runtime protection currently supports TanStack Start + Supabase apps; stack not detected \u2014 skipping."
1387
- );
1242
+ log("runtime protection currently supports TanStack Start + Supabase apps; stack not detected \u2014 skipping.");
1388
1243
  return;
1389
1244
  }
1390
1245
  scaffold(cwd);
1391
1246
  patchClient(cwd);
1392
1247
  patchStart(cwd);
1393
- generateManifest(cwd);
1394
- wirePrebuild(cwd);
1395
1248
  log("done \u2014 guard wired and always-on (blocks by default). Set PATCHSTACK_MODE=dry-run for log-only.");
1396
1249
  }
1397
1250
 
@@ -1407,9 +1260,8 @@ Usage:
1407
1260
  patchstack-connect status [options] Show current configuration
1408
1261
  patchstack-connect mark-build [options] Stamp built HTML with a production flag +
1409
1262
  build fingerprint (run as a postbuild step)
1410
- patchstack-connect protect [--manifest] Install runtime protection (the guard) into
1411
- a server-side JS app. --manifest only
1412
- regenerates the package manifest (prebuild).
1263
+ patchstack-connect protect Install always-on runtime protection (the
1264
+ guard) into a TanStack Start + Supabase app.
1413
1265
  patchstack-connect help Print this message
1414
1266
 
1415
1267
  Options (for scan and status):
@@ -1547,9 +1399,9 @@ async function runScan(args) {
1547
1399
  }
1548
1400
  return 0;
1549
1401
  }
1550
- async function runProtectCommand(args) {
1402
+ async function runProtectCommand(_args) {
1551
1403
  try {
1552
- runProtect(process.cwd(), { manifestOnly: args.flags.get("manifest") === true });
1404
+ runProtect(process.cwd());
1553
1405
  } catch (err) {
1554
1406
  console.warn(`patchstack protect: skipped (${err.message}).`);
1555
1407
  }
@@ -1570,19 +1422,9 @@ async function runStatus(args) {
1570
1422
  }
1571
1423
  return 0;
1572
1424
  }
1573
- function describeStack(stack) {
1574
- const parts = [stack.builder, stack.framework, stack.ui, stack.runtime].filter(
1575
- (part) => part !== null
1576
- );
1577
- if (stack.hostingEnvKeys.length > 0) {
1578
- parts.push(`${stack.hostingEnvKeys.length} hosting env key(s)`);
1579
- }
1580
- return parts.length > 0 ? parts.join(" \xB7 ") : null;
1581
- }
1582
1425
  async function runMarkBuild(args) {
1583
1426
  const cwd = process.cwd();
1584
1427
  let checksum = null;
1585
- let stack = null;
1586
1428
  try {
1587
1429
  const manifest = await scanLockfile(cwd);
1588
1430
  for (const warning of manifest.warnings ?? []) {
@@ -1590,7 +1432,6 @@ async function runMarkBuild(args) {
1590
1432
  }
1591
1433
  const { payload } = buildWirePayload(manifest);
1592
1434
  checksum = computeManifestChecksum(payload.packages);
1593
- stack = detectStack(payload.packages);
1594
1435
  } catch (err) {
1595
1436
  console.warn(
1596
1437
  `mark-build: could not compute the build fingerprint (${err.message}). Stamping the production flag only.`
@@ -1608,7 +1449,7 @@ async function runMarkBuild(args) {
1608
1449
  console.warn(`mark-build: no HTML files found under ${dir}. Nothing to mark.`);
1609
1450
  return 0;
1610
1451
  }
1611
- const snippet = buildInjectionSnippet(checksum, stack);
1452
+ const snippet = buildInjectionSnippet(checksum);
1612
1453
  let marked = 0;
1613
1454
  for (const file of files) {
1614
1455
  const before = readFileSync2(file, "utf8");
@@ -1618,9 +1459,8 @@ async function runMarkBuild(args) {
1618
1459
  marked += 1;
1619
1460
  }
1620
1461
  }
1621
- const stackSummary = stack !== null ? describeStack(stack) : null;
1622
1462
  console.log(
1623
- `mark-build: marked ${marked} HTML file(s) in ${dir}${checksum !== null ? ` (build ${checksum})` : ""}${stackSummary !== null ? ` [${stackSummary}]` : ""}.`
1463
+ `mark-build: marked ${marked} HTML file(s) in ${dir}${checksum !== null ? ` (build ${checksum})` : ""}.`
1624
1464
  );
1625
1465
  return 0;
1626
1466
  }