@patchstack/connect 0.5.23 → 0.5.25
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/AGENT-INSTALL.md +119 -9
- package/README.md +30 -0
- package/dist/cli.js +522 -153
- package/dist/cli.js.map +1 -1
- package/dist/protect/templates/next-guard.ts +54 -0
- package/dist/protect.cjs +11 -3
- package/dist/protect.cjs.map +1 -1
- package/dist/protect.edge.js +12 -4
- package/dist/protect.edge.js.map +1 -1
- package/dist/protect.js +11 -3
- package/dist/protect.js.map +1 -1
- package/package.json +9 -1
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { readFileSync as readFileSync19 } from "fs";
|
|
5
5
|
import path16 from "path";
|
|
6
|
-
import { createRequire as
|
|
6
|
+
import { createRequire as createRequire3 } from "module";
|
|
7
7
|
|
|
8
8
|
// src/safe-file.ts
|
|
9
9
|
import {
|
|
@@ -3179,7 +3179,7 @@ function ensureSourceMarker(cwd, shell, framework, checksum = null) {
|
|
|
3179
3179
|
}
|
|
3180
3180
|
|
|
3181
3181
|
// src/guide.ts
|
|
3182
|
-
import { existsSync as existsSync12, readFileSync as readFileSync11, readdirSync as
|
|
3182
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
|
|
3183
3183
|
import path12 from "path";
|
|
3184
3184
|
|
|
3185
3185
|
// src/architecture.ts
|
|
@@ -3815,8 +3815,8 @@ function staticSignals(cwd, ts) {
|
|
|
3815
3815
|
signals.push({ signal: "static-generator", source: "astro (no SSR adapter)" });
|
|
3816
3816
|
}
|
|
3817
3817
|
if (deps["next"] !== void 0) {
|
|
3818
|
-
const
|
|
3819
|
-
if (
|
|
3818
|
+
const exported2 = nextExportsStatically(cwd, manifest, ts);
|
|
3819
|
+
if (exported2 !== null) signals.push({ signal: "static-generator", source: `next: ${exported2}` });
|
|
3820
3820
|
}
|
|
3821
3821
|
return signals;
|
|
3822
3822
|
}
|
|
@@ -3937,12 +3937,12 @@ function serverEntries(cwd, manifest) {
|
|
|
3937
3937
|
if (!main2.startsWith("..")) candidates.add(main2);
|
|
3938
3938
|
}
|
|
3939
3939
|
const found = [];
|
|
3940
|
-
for (const
|
|
3941
|
-
const file = path10.join(cwd,
|
|
3940
|
+
for (const relative9 of candidates) {
|
|
3941
|
+
const file = path10.join(cwd, relative9);
|
|
3942
3942
|
try {
|
|
3943
3943
|
if (!existsSync2(file) || !statSync4(file).isFile()) continue;
|
|
3944
3944
|
const text = readFileSync6(file, "utf8").slice(0, MAX_ENTRY_BYTES);
|
|
3945
|
-
if (SERVING_CALL.test(text)) found.push(
|
|
3945
|
+
if (SERVING_CALL.test(text)) found.push(relative9);
|
|
3946
3946
|
} catch {
|
|
3947
3947
|
}
|
|
3948
3948
|
}
|
|
@@ -4272,90 +4272,459 @@ var tanstackSupabaseAdapter = {
|
|
|
4272
4272
|
};
|
|
4273
4273
|
|
|
4274
4274
|
// src/protect/install/adapters/next.ts
|
|
4275
|
-
import { existsSync as existsSync5 } from "fs";
|
|
4275
|
+
import { existsSync as existsSync5, lstatSync as lstatSync2, readdirSync as readdirSync3 } from "fs";
|
|
4276
|
+
import { dirname as dirname3, join as join8, relative as relative3 } from "path";
|
|
4277
|
+
|
|
4278
|
+
// src/protect/install/adapters/next-source.ts
|
|
4279
|
+
import { createRequire } from "module";
|
|
4276
4280
|
import { join as join7 } from "path";
|
|
4281
|
+
var NEXT_MARKER = "// #region patchstack-next-composed";
|
|
4282
|
+
var ROUTE_MARKER = "// #region patchstack-next-route";
|
|
4283
|
+
var METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
4284
|
+
function nextCompiler(cwd) {
|
|
4285
|
+
for (const base of [join7(cwd, "package.json"), import.meta.url]) {
|
|
4286
|
+
try {
|
|
4287
|
+
return createRequire(base)("typescript");
|
|
4288
|
+
} catch {
|
|
4289
|
+
}
|
|
4290
|
+
}
|
|
4291
|
+
return null;
|
|
4292
|
+
}
|
|
4293
|
+
function parse(ts, file, source) {
|
|
4294
|
+
const sf = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true);
|
|
4295
|
+
return sf.parseDiagnostics.length ? null : sf;
|
|
4296
|
+
}
|
|
4297
|
+
function exported(ts, node) {
|
|
4298
|
+
return ts.canHaveModifiers(node) && !!ts.getModifiers(node)?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
|
|
4299
|
+
}
|
|
4300
|
+
function apply(source, edits) {
|
|
4301
|
+
for (const edit of edits.sort((a, b) => b.start - a.start)) {
|
|
4302
|
+
source = source.slice(0, edit.start) + edit.text + source.slice(edit.end);
|
|
4303
|
+
}
|
|
4304
|
+
return source;
|
|
4305
|
+
}
|
|
4306
|
+
function handlers(ts, sf, names) {
|
|
4307
|
+
const out = [];
|
|
4308
|
+
for (const s of sf.statements) {
|
|
4309
|
+
if (!exported(ts, s)) continue;
|
|
4310
|
+
if (ts.isFunctionDeclaration(s) && s.name && names.has(s.name.text)) out.push(s);
|
|
4311
|
+
if (ts.isVariableStatement(s)) {
|
|
4312
|
+
for (const d of s.declarationList.declarations) {
|
|
4313
|
+
if (ts.isIdentifier(d.name) && names.has(d.name.text) && d.initializer && (ts.isArrowFunction(d.initializer) || ts.isFunctionExpression(d.initializer))) out.push(d.initializer);
|
|
4314
|
+
}
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
return out;
|
|
4318
|
+
}
|
|
4319
|
+
function methodExportCount(ts, sf) {
|
|
4320
|
+
return sf.statements.filter((s) => exported(ts, s)).flatMap((s) => {
|
|
4321
|
+
if (ts.isFunctionDeclaration(s)) return s.name ? [s.name.text] : [];
|
|
4322
|
+
if (ts.isVariableStatement(s)) return s.declarationList.declarations.map((d) => d.name.getText(sf));
|
|
4323
|
+
return [];
|
|
4324
|
+
}).filter((n) => METHODS.has(n)).length;
|
|
4325
|
+
}
|
|
4326
|
+
function standardNextRouting(ts, file, source) {
|
|
4327
|
+
const sf = parse(ts, file, source);
|
|
4328
|
+
if (!sf) return false;
|
|
4329
|
+
const objects = /* @__PURE__ */ new Map();
|
|
4330
|
+
let target;
|
|
4331
|
+
for (const s of sf.statements) {
|
|
4332
|
+
if (ts.isImportDeclaration(s) && s.importClause?.isTypeOnly) continue;
|
|
4333
|
+
if (ts.isVariableStatement(s) && s.declarationList.flags & ts.NodeFlags.Const) {
|
|
4334
|
+
for (const d of s.declarationList.declarations) {
|
|
4335
|
+
if (!ts.isIdentifier(d.name) || !d.initializer) return false;
|
|
4336
|
+
objects.set(d.name.text, d.initializer);
|
|
4337
|
+
}
|
|
4338
|
+
} else if (ts.isExportAssignment(s) && !s.isExportEquals && !target) target = s.expression;
|
|
4339
|
+
else return false;
|
|
4340
|
+
}
|
|
4341
|
+
const unwrap = (expr) => {
|
|
4342
|
+
while (ts.isAsExpression(expr) || ts.isSatisfiesExpression(expr) || ts.isParenthesizedExpression(expr)) expr = expr.expression;
|
|
4343
|
+
return expr;
|
|
4344
|
+
};
|
|
4345
|
+
if (!target) return false;
|
|
4346
|
+
target = unwrap(target);
|
|
4347
|
+
if (ts.isIdentifier(target)) target = objects.get(target.text);
|
|
4348
|
+
if (!target) return false;
|
|
4349
|
+
target = unwrap(target);
|
|
4350
|
+
if (!ts.isObjectLiteralExpression(target)) return false;
|
|
4351
|
+
const literal = (node) => {
|
|
4352
|
+
const expr = unwrap(node);
|
|
4353
|
+
if (ts.isStringLiteral(expr) || ts.isNumericLiteral(expr) || [ts.SyntaxKind.TrueKeyword, ts.SyntaxKind.FalseKeyword, ts.SyntaxKind.NullKeyword].includes(expr.kind)) return true;
|
|
4354
|
+
if (ts.isArrayLiteralExpression(expr)) return expr.elements.every(literal);
|
|
4355
|
+
if (ts.isObjectLiteralExpression(expr)) return expr.properties.every((p) => ts.isPropertyAssignment(p) && (ts.isIdentifier(p.name) || ts.isStringLiteral(p.name)) && literal(p.initializer));
|
|
4356
|
+
return false;
|
|
4357
|
+
};
|
|
4358
|
+
if (!literal(target) || ![...objects.values()].every(literal)) return false;
|
|
4359
|
+
return target.properties.every((p) => p.name && !["basePath", "i18n", "skipMiddlewareUrlNormalize", "pageExtensions", "__proto__"].includes(
|
|
4360
|
+
ts.isIdentifier(p.name) || ts.isStringLiteral(p.name) ? p.name.text : ""
|
|
4361
|
+
));
|
|
4362
|
+
}
|
|
4363
|
+
function matcherRegex(value) {
|
|
4364
|
+
if (value === "/:path*") return "^/";
|
|
4365
|
+
if (!/^\/[\w-]+(?:\/[\w-]+)*(?:\/:\w+\*)?$/.test(value)) return null;
|
|
4366
|
+
const catchAll = /\/:\w+\*$/.test(value);
|
|
4367
|
+
const prefix = value.replace(/\/:\w+\*$/, "");
|
|
4368
|
+
return `^(?:/_next/data/[^/]+)?${prefix}${catchAll ? "(?:/.*)?" : ""}(?:\\.json)?/?$`;
|
|
4369
|
+
}
|
|
4370
|
+
function middlewareScope(ts, sf) {
|
|
4371
|
+
const configs = sf.statements.filter((s) => ts.isVariableStatement(s) && exported(ts, s)).flatMap((s) => [...s.declarationList.declarations]).filter((d) => ts.isIdentifier(d.name) && d.name.text === "config");
|
|
4372
|
+
if (!configs.length) {
|
|
4373
|
+
if (sf.statements.some((s) => ts.isExportDeclaration(s))) return null;
|
|
4374
|
+
return { tests: [], edits: [] };
|
|
4375
|
+
}
|
|
4376
|
+
const config = configs[0]?.initializer;
|
|
4377
|
+
if (configs.length !== 1 || !config || !ts.isObjectLiteralExpression(config)) return null;
|
|
4378
|
+
if (config.properties.some((p) => !ts.isPropertyAssignment(p) || !ts.isIdentifier(p.name))) return null;
|
|
4379
|
+
const matcher = config.properties.find((p) => p.name?.getText(sf) === "matcher");
|
|
4380
|
+
if (!matcher) return { tests: [], edits: [] };
|
|
4381
|
+
const values = ts.isArrayLiteralExpression(matcher.initializer) ? [...matcher.initializer.elements] : [matcher.initializer];
|
|
4382
|
+
if (!values.length || !values.every((v) => ts.isStringLiteral(v))) return null;
|
|
4383
|
+
const tests = values.map((v) => matcherRegex(v.text));
|
|
4384
|
+
if (tests.some((v) => v === null)) return null;
|
|
4385
|
+
return {
|
|
4386
|
+
tests,
|
|
4387
|
+
edits: [{ start: matcher.initializer.getStart(sf), end: matcher.initializer.end, text: '"/:path*"' }]
|
|
4388
|
+
};
|
|
4389
|
+
}
|
|
4390
|
+
function requestName(ts, fn) {
|
|
4391
|
+
const param = fn.parameters[0];
|
|
4392
|
+
if (!param) return "psRequest";
|
|
4393
|
+
if (!ts.isIdentifier(param.name) || param.dotDotDotToken || param.initializer || param.questionToken) return null;
|
|
4394
|
+
return param.name.text;
|
|
4395
|
+
}
|
|
4396
|
+
function requestParameter(fn, file) {
|
|
4397
|
+
return fn.parameters.length ? [] : [{
|
|
4398
|
+
start: fn.parameters.pos,
|
|
4399
|
+
end: fn.parameters.pos,
|
|
4400
|
+
text: file.endsWith(".ts") ? "psRequest: Request" : "psRequest"
|
|
4401
|
+
}];
|
|
4402
|
+
}
|
|
4403
|
+
function importPosition(ts, sf) {
|
|
4404
|
+
let position = 0;
|
|
4405
|
+
for (const statement of sf.statements) {
|
|
4406
|
+
if (!ts.isExpressionStatement(statement) || !ts.isStringLiteral(statement.expression)) break;
|
|
4407
|
+
position = statement.end;
|
|
4408
|
+
}
|
|
4409
|
+
return position;
|
|
4410
|
+
}
|
|
4411
|
+
function prelude(request, marker) {
|
|
4412
|
+
return `
|
|
4413
|
+
${marker}
|
|
4414
|
+
const psProtection = await getPatchstackProtection();
|
|
4415
|
+
const psBlocked = psProtection && await psProtection.fetchGuard()(${request});
|
|
4416
|
+
if (psBlocked) return psBlocked;
|
|
4417
|
+
// #endregion
|
|
4418
|
+
`;
|
|
4419
|
+
}
|
|
4420
|
+
function composeNextMiddleware(ts, file, source, guardImport) {
|
|
4421
|
+
const sf = parse(ts, file, source);
|
|
4422
|
+
if (!sf || /\b(?:psProtection|psBlocked|psRequest|getPatchstackProtection)\b/.test(source)) return null;
|
|
4423
|
+
const found = handlers(ts, sf, /* @__PURE__ */ new Set(["middleware"]));
|
|
4424
|
+
if (found.length !== 1 || sf.statements.some((s) => ts.isExportAssignment(s) || ts.isExportDeclaration(s))) return null;
|
|
4425
|
+
const fn = found[0];
|
|
4426
|
+
const request = requestName(ts, fn);
|
|
4427
|
+
const scope = middlewareScope(ts, sf);
|
|
4428
|
+
if (!request || !scope || !fn.body || !ts.isBlock(fn.body) || fn.asteriskToken || fn.type) return null;
|
|
4429
|
+
const edits = [...scope.edits, ...requestParameter(fn, file)];
|
|
4430
|
+
const isAsync = fn.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword);
|
|
4431
|
+
if (!isAsync) {
|
|
4432
|
+
const start = ts.isFunctionDeclaration(fn) ? fn.getChildren(sf).find((n) => n.kind === ts.SyntaxKind.FunctionKeyword).getStart(sf) : fn.getStart(sf);
|
|
4433
|
+
edits.push({ start, end: start, text: "async " });
|
|
4434
|
+
}
|
|
4435
|
+
const scopeCheck = scope.tests.length ? ` if (!${JSON.stringify(scope.tests)}.some(pattern => new RegExp(pattern).test(new URL(${request}.url).pathname))) return;
|
|
4436
|
+
` : "";
|
|
4437
|
+
edits.push({ start: fn.body.getStart(sf) + 1, end: fn.body.getStart(sf) + 1, text: prelude(request, NEXT_MARKER) + scopeCheck });
|
|
4438
|
+
const position = importPosition(ts, sf);
|
|
4439
|
+
edits.push({ start: position, end: position, text: `
|
|
4440
|
+
import { getPatchstackProtection } from ${JSON.stringify(guardImport)};
|
|
4441
|
+
` });
|
|
4442
|
+
return apply(source, edits);
|
|
4443
|
+
}
|
|
4444
|
+
function composeNextRoute(ts, file, source, guardImport) {
|
|
4445
|
+
const sf = parse(ts, file, source);
|
|
4446
|
+
if (!sf || /\b(?:psProtection|psBlocked|psRequest|getPatchstackProtection|screenPatchstackResponse)\b/.test(source)) return null;
|
|
4447
|
+
const found = handlers(ts, sf, METHODS);
|
|
4448
|
+
if (!found.length || sf.statements.some((s) => ts.isExportDeclaration(s) || ts.isExportAssignment(s))) return null;
|
|
4449
|
+
if (methodExportCount(ts, sf) !== found.length) return null;
|
|
4450
|
+
const edits = [];
|
|
4451
|
+
for (const fn of found) {
|
|
4452
|
+
const request = requestName(ts, fn);
|
|
4453
|
+
if (!request || !fn.body || !ts.isBlock(fn.body) || fn.asteriskToken || fn.type || !fn.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword)) return null;
|
|
4454
|
+
const returns = [];
|
|
4455
|
+
const visit = (node) => {
|
|
4456
|
+
if (ts.isFunctionLike(node)) return;
|
|
4457
|
+
if (ts.isReturnStatement(node)) returns.push(node);
|
|
4458
|
+
ts.forEachChild(node, visit);
|
|
4459
|
+
};
|
|
4460
|
+
visit(fn.body);
|
|
4461
|
+
if (!returns.length || returns.some((r) => !r.expression)) return null;
|
|
4462
|
+
for (const r of returns) {
|
|
4463
|
+
const expr = r.expression;
|
|
4464
|
+
edits.push({
|
|
4465
|
+
start: expr.getStart(sf),
|
|
4466
|
+
end: expr.end,
|
|
4467
|
+
text: `screenPatchstackResponse((${expr.getText(sf)}), ${request}, psProtection)`
|
|
4468
|
+
});
|
|
4469
|
+
}
|
|
4470
|
+
edits.push(...requestParameter(fn, file));
|
|
4471
|
+
edits.push({ start: fn.body.getStart(sf) + 1, end: fn.body.getStart(sf) + 1, text: prelude(request, ROUTE_MARKER) });
|
|
4472
|
+
}
|
|
4473
|
+
const position = importPosition(ts, sf);
|
|
4474
|
+
edits.push({ start: position, end: position, text: `
|
|
4475
|
+
import { getPatchstackProtection, screenPatchstackResponse } from ${JSON.stringify(guardImport)};
|
|
4476
|
+
` });
|
|
4477
|
+
return apply(source, edits);
|
|
4478
|
+
}
|
|
4479
|
+
function nextSourceWired(ts, file, source, guardImport, route = false) {
|
|
4480
|
+
const sf = parse(ts, file, source);
|
|
4481
|
+
if (!sf) return false;
|
|
4482
|
+
const imported = sf.statements.some((s) => ts.isImportDeclaration(s) && ts.isStringLiteral(s.moduleSpecifier) && s.moduleSpecifier.text === guardImport && s.importClause?.namedBindings && ts.isNamedImports(s.importClause.namedBindings) && s.importClause.namedBindings.elements.some((e) => !e.propertyName && e.name.text === "getPatchstackProtection"));
|
|
4483
|
+
if (!imported) return false;
|
|
4484
|
+
if (route && !sf.statements.some((s) => ts.isImportDeclaration(s) && ts.isStringLiteral(s.moduleSpecifier) && s.moduleSpecifier.text === guardImport && s.importClause?.namedBindings && ts.isNamedImports(s.importClause.namedBindings) && s.importClause.namedBindings.elements.some((e) => !e.propertyName && e.name.text === "screenPatchstackResponse"))) return false;
|
|
4485
|
+
const found = handlers(ts, sf, route ? METHODS : /* @__PURE__ */ new Set(["middleware"]));
|
|
4486
|
+
if (!found.length || sf.statements.some((s) => ts.isExportDeclaration(s) || ts.isExportAssignment(s))) return false;
|
|
4487
|
+
if (route && methodExportCount(ts, sf) !== found.length) return false;
|
|
4488
|
+
const compact = (text) => text.replace(/\s+/g, "");
|
|
4489
|
+
for (const fn of found) {
|
|
4490
|
+
const request = requestName(ts, fn);
|
|
4491
|
+
if (!request || !fn.body || !ts.isBlock(fn.body)) return false;
|
|
4492
|
+
const head = fn.body.statements.slice(0, 3).map((s) => s.getText(sf)).join("");
|
|
4493
|
+
const expected = `const psProtection = await getPatchstackProtection();
|
|
4494
|
+
const psBlocked = psProtection && await psProtection.fetchGuard()(${request});
|
|
4495
|
+
if (psBlocked) return psBlocked;`;
|
|
4496
|
+
if (compact(head) !== compact(expected)) return false;
|
|
4497
|
+
if (route) {
|
|
4498
|
+
let covered = true;
|
|
4499
|
+
const visit = (node) => {
|
|
4500
|
+
if (ts.isFunctionLike(node)) return;
|
|
4501
|
+
if (ts.isReturnStatement(node) && node.expression?.getText(sf) !== "psBlocked") {
|
|
4502
|
+
const expr = node.expression;
|
|
4503
|
+
if (!expr || !ts.isCallExpression(expr) || expr.expression.getText(sf) !== "screenPatchstackResponse" || expr.arguments[1]?.getText(sf) !== request || expr.arguments[2]?.getText(sf) !== "psProtection") covered = false;
|
|
4504
|
+
}
|
|
4505
|
+
ts.forEachChild(node, visit);
|
|
4506
|
+
};
|
|
4507
|
+
visit(fn.body);
|
|
4508
|
+
if (!covered) return false;
|
|
4509
|
+
}
|
|
4510
|
+
}
|
|
4511
|
+
if (!route) {
|
|
4512
|
+
const scope = middlewareScope(ts, sf);
|
|
4513
|
+
if (!scope || scope.tests.some((test) => test !== "^/")) return false;
|
|
4514
|
+
}
|
|
4515
|
+
return true;
|
|
4516
|
+
}
|
|
4517
|
+
|
|
4518
|
+
// src/protect/install/adapters/next.ts
|
|
4277
4519
|
function middlewareInfo(cwd) {
|
|
4278
4520
|
const candidates = ["middleware.ts", "middleware.js", "src/middleware.ts", "src/middleware.js"];
|
|
4279
|
-
|
|
4280
|
-
|
|
4521
|
+
const relFile = candidates.find((rel) => existsSync5(join8(cwd, rel))) ?? (existsSync5(join8(cwd, "src")) ? "src/middleware.ts" : "middleware.ts");
|
|
4522
|
+
return { relFile, relDir: dirname3(relFile), exists: existsSync5(join8(cwd, relFile)) };
|
|
4523
|
+
}
|
|
4524
|
+
function proxyFiles(cwd) {
|
|
4525
|
+
return ["proxy.ts", "proxy.js", "src/proxy.ts", "src/proxy.js"].filter((file) => existsSync5(join8(cwd, file)));
|
|
4526
|
+
}
|
|
4527
|
+
function paths(cwd) {
|
|
4528
|
+
const mw = middlewareInfo(cwd);
|
|
4529
|
+
const ext = mw.relFile.endsWith(".js") ? "js" : "ts";
|
|
4530
|
+
return { ...mw, rules: join8(mw.relDir, "patchstack.rules.json"), guard: join8(mw.relDir, `patchstack.next.${ext}`) };
|
|
4531
|
+
}
|
|
4532
|
+
function importFrom(file, guard) {
|
|
4533
|
+
const rel = relative3(dirname3(file), guard).replace(/\\/g, "/").replace(/\.ts$/, "");
|
|
4534
|
+
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
4535
|
+
}
|
|
4536
|
+
function routes(cwd) {
|
|
4537
|
+
const files = [];
|
|
4538
|
+
let incomplete = false;
|
|
4539
|
+
let visited = 0;
|
|
4540
|
+
const walk2 = (rel) => {
|
|
4541
|
+
if (++visited > 5e3) {
|
|
4542
|
+
incomplete = true;
|
|
4543
|
+
return;
|
|
4544
|
+
}
|
|
4545
|
+
const full = join8(cwd, rel);
|
|
4546
|
+
if (lstatSync2(full).isSymbolicLink()) {
|
|
4547
|
+
incomplete = true;
|
|
4548
|
+
return;
|
|
4549
|
+
}
|
|
4550
|
+
for (const entry of readdirSync3(full, { withFileTypes: true })) {
|
|
4551
|
+
if (entry.name.startsWith("_") || entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
4552
|
+
if (entry.isSymbolicLink()) {
|
|
4553
|
+
incomplete = true;
|
|
4554
|
+
continue;
|
|
4555
|
+
}
|
|
4556
|
+
const child = join8(rel, entry.name);
|
|
4557
|
+
if (entry.isDirectory()) walk2(child);
|
|
4558
|
+
else if (entry.isFile() && /^route\.(?:tsx?|jsx?)$/.test(entry.name)) files.push(child);
|
|
4559
|
+
}
|
|
4560
|
+
};
|
|
4561
|
+
for (const root of ["app", "src/app"]) {
|
|
4562
|
+
if (!existsSync5(join8(cwd, root))) continue;
|
|
4563
|
+
if (root.startsWith("src/") && lstatSync2(join8(cwd, "src")).isSymbolicLink()) {
|
|
4564
|
+
incomplete = true;
|
|
4565
|
+
continue;
|
|
4566
|
+
}
|
|
4567
|
+
try {
|
|
4568
|
+
walk2(root);
|
|
4569
|
+
} catch {
|
|
4570
|
+
incomplete = true;
|
|
4571
|
+
}
|
|
4281
4572
|
}
|
|
4282
|
-
|
|
4283
|
-
return { relDir: useSrc ? "src" : ".", relFile: useSrc ? "src/middleware.ts" : "middleware.ts", exists: false };
|
|
4573
|
+
return { files: files.sort(), incomplete };
|
|
4284
4574
|
}
|
|
4285
|
-
function
|
|
4286
|
-
return
|
|
4575
|
+
function customRouting(cwd, ts) {
|
|
4576
|
+
return ["js", "mjs", "ts", "cjs"].some((ext) => {
|
|
4577
|
+
const file = join8(cwd, `next.config.${ext}`);
|
|
4578
|
+
return existsSync5(file) && !standardNextRouting(ts, file, read(file));
|
|
4579
|
+
});
|
|
4287
4580
|
}
|
|
4288
|
-
function
|
|
4289
|
-
|
|
4581
|
+
function sharedGuardPresent(file) {
|
|
4582
|
+
if (!existsSync5(file)) return false;
|
|
4583
|
+
const source = read(file);
|
|
4584
|
+
return source.includes("export async function getPatchstackProtection") && source.includes("export async function screenPatchstackResponse") && source.includes('import { createProtection } from "@patchstack/connect/protect"');
|
|
4290
4585
|
}
|
|
4291
4586
|
function wire2(cwd, opts) {
|
|
4587
|
+
const proxies = proxyFiles(cwd);
|
|
4588
|
+
if (proxies.length) {
|
|
4589
|
+
log(`left ${proxies.join(", ")} untouched: Next.js proxy integration requires manual wiring. Do not add middleware alongside a proxy; protect --check reports this gap.`);
|
|
4590
|
+
return { ok: false, changed: [] };
|
|
4591
|
+
}
|
|
4292
4592
|
const templates = templatesDir();
|
|
4293
|
-
const mw =
|
|
4294
|
-
|
|
4295
|
-
ensureProjectDirectorySync(cwd, dir);
|
|
4296
|
-
const rulesDst = join7(dir, "patchstack.rules.json");
|
|
4593
|
+
const mw = paths(cwd);
|
|
4594
|
+
ensureProjectDirectorySync(cwd, join8(cwd, mw.relDir));
|
|
4297
4595
|
const changed = [];
|
|
4298
|
-
if (opts.demo || !existsSync5(
|
|
4299
|
-
copyProjectFileSync(cwd,
|
|
4300
|
-
changed.push(
|
|
4301
|
-
}
|
|
4302
|
-
const
|
|
4303
|
-
const
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4596
|
+
if (opts.demo || !existsSync5(join8(cwd, mw.rules))) {
|
|
4597
|
+
copyProjectFileSync(cwd, join8(templates, opts.demo ? "demo-rules.json" : "rules.json"), join8(cwd, mw.rules));
|
|
4598
|
+
changed.push(mw.rules);
|
|
4599
|
+
}
|
|
4600
|
+
const ts = nextCompiler(cwd);
|
|
4601
|
+
const guardPath = join8(cwd, mw.guard);
|
|
4602
|
+
const guardConflict = existsSync5(guardPath) && !sharedGuardPresent(guardPath);
|
|
4603
|
+
const ensureGuard = () => {
|
|
4604
|
+
if (existsSync5(guardPath)) return;
|
|
4605
|
+
const source = read(join8(templates, "next-guard.ts"));
|
|
4606
|
+
writeProjectFileSync(cwd, guardPath, mw.guard.endsWith(".js") ? ts.transpileModule(source, { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext } }).outputText : source);
|
|
4607
|
+
if (!opts.demo) bakeSiteUuid(cwd, mw.guard);
|
|
4608
|
+
changed.push(mw.guard);
|
|
4609
|
+
};
|
|
4610
|
+
const existing = mw.exists ? read(join8(cwd, mw.relFile)) : "";
|
|
4611
|
+
if (!mw.exists) {
|
|
4612
|
+
copyProjectFileSync(cwd, join8(templates, "next-middleware.ts"), join8(cwd, mw.relFile));
|
|
4613
|
+
if (!opts.demo) bakeSiteUuid(cwd, mw.relFile);
|
|
4614
|
+
changed.push(mw.relFile);
|
|
4615
|
+
log(`scaffolded ${mw.relFile} (request-phase guard)`);
|
|
4616
|
+
} else if (existing.includes(NEXT_MARKER) || existing.includes("#region patchstack-next (")) {
|
|
4617
|
+
log(`${mw.relFile} already has a Patchstack guard \u2014 left as-is`);
|
|
4618
|
+
if (ts && existing.includes(NEXT_MARKER)) ensureGuard();
|
|
4619
|
+
} else {
|
|
4620
|
+
const composed = ts && !guardConflict && !customRouting(cwd, ts) && composeNextMiddleware(ts, mw.relFile, existing, importFrom(mw.relFile, mw.guard));
|
|
4621
|
+
if (composed) {
|
|
4622
|
+
ensureGuard();
|
|
4623
|
+
writeProjectFileSync(cwd, join8(cwd, mw.relFile), composed);
|
|
4624
|
+
changed.push(mw.relFile);
|
|
4625
|
+
log(`composed ${mw.relFile}: request guard first; existing middleware keeps its original matcher scope`);
|
|
4626
|
+
} else {
|
|
4627
|
+
log(`left ${mw.relFile} untouched: middleware/export/matcher shape needs manual integration${ts ? "" : " (install typescript to enable source-aware composition)"}. Add a guard before application middleware and preserve its routing scope; protect --check reports this gap.`);
|
|
4628
|
+
}
|
|
4307
4629
|
}
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
)
|
|
4312
|
-
|
|
4630
|
+
const inventory = routes(cwd);
|
|
4631
|
+
for (const file of inventory.files) {
|
|
4632
|
+
const source = read(join8(cwd, file));
|
|
4633
|
+
if (source.includes(ROUTE_MARKER)) {
|
|
4634
|
+
if (ts) ensureGuard();
|
|
4635
|
+
continue;
|
|
4636
|
+
}
|
|
4637
|
+
const composed = ts && !guardConflict && /\.(?:ts|js)$/.test(file) && composeNextRoute(ts, file, source, importFrom(file, mw.guard));
|
|
4638
|
+
if (composed) {
|
|
4639
|
+
ensureGuard();
|
|
4640
|
+
writeProjectFileSync(cwd, join8(cwd, file), composed);
|
|
4641
|
+
changed.push(file);
|
|
4642
|
+
log(`wired ${file} (request checks + response filtering)`);
|
|
4643
|
+
} else {
|
|
4644
|
+
log(`left ${file} untouched: wrap its request and final Response manually; protect --check reports this response-filtering gap.`);
|
|
4645
|
+
}
|
|
4313
4646
|
}
|
|
4314
|
-
|
|
4315
|
-
if (
|
|
4316
|
-
|
|
4317
|
-
log(`scaffolded ${mw.relFile} (Patchstack middleware)`);
|
|
4647
|
+
if (guardConflict) log(`${mw.guard} already exists but is not a recognized helper \u2014 left untouched; resolve the filename conflict before integrating handlers.`);
|
|
4648
|
+
if (inventory.incomplete) log("App Router inventory was incomplete \u2014 review skipped paths manually.");
|
|
4649
|
+
log("Next middleware cannot inspect downstream response bodies. Route-handler filtering does not cover rendered pages, Server Actions, or Pages API routes. Keep Next.js patched: a middleware bypass also bypasses a middleware guard.");
|
|
4318
4650
|
return { ok: true, changed: [...new Set(changed)] };
|
|
4319
4651
|
}
|
|
4320
4652
|
function verify2(cwd) {
|
|
4321
|
-
const
|
|
4322
|
-
|
|
4323
|
-
|
|
4653
|
+
const proxies = proxyFiles(cwd);
|
|
4654
|
+
if (proxies.length) return {
|
|
4655
|
+
wired: false,
|
|
4656
|
+
checks: [{
|
|
4657
|
+
label: "Next.js proxy wiring requires manual verification",
|
|
4658
|
+
ok: false,
|
|
4659
|
+
hint: `review ${proxies.join(", ")}; automatic proxy integration is not supported. Next.js cannot use middleware and proxy together.`
|
|
4660
|
+
}]
|
|
4661
|
+
};
|
|
4662
|
+
const mw = paths(cwd);
|
|
4663
|
+
const ts = nextCompiler(cwd);
|
|
4664
|
+
const source = mw.exists ? read(join8(cwd, mw.relFile)) : "";
|
|
4665
|
+
const shared = sharedGuardPresent(join8(cwd, mw.guard));
|
|
4666
|
+
const legacy = source.includes("#region patchstack-next (") && source.includes("await protection.fetchGuard()(request)") && source.includes("if (blocked) return blocked") && source.includes('matcher: "/:path*"');
|
|
4667
|
+
const composed = ts && shared && !customRouting(cwd, ts) && nextSourceWired(ts, mw.relFile, source, importFrom(mw.relFile, mw.guard));
|
|
4668
|
+
const present2 = !!(legacy || composed);
|
|
4669
|
+
const rulesPresent = existsSync5(join8(cwd, mw.rules));
|
|
4670
|
+
const inventory = routes(cwd);
|
|
4671
|
+
const missing = inventory.files.filter((file) => !(ts && shared && nextSourceWired(ts, file, read(join8(cwd, file)), importFrom(file, mw.guard), true)));
|
|
4324
4672
|
return {
|
|
4325
|
-
wired: present2 && rulesPresent,
|
|
4673
|
+
wired: present2 && rulesPresent && missing.length === 0 && !inventory.incomplete,
|
|
4326
4674
|
checks: [
|
|
4327
|
-
{
|
|
4328
|
-
|
|
4675
|
+
{
|
|
4676
|
+
label: "Next middleware request guard has a catch-all matcher",
|
|
4677
|
+
ok: present2,
|
|
4678
|
+
hint: `run \`patchstack-connect protect\`; review any unsupported middleware shape in ${mw.relFile}`
|
|
4679
|
+
},
|
|
4680
|
+
{ label: "fallback rules co-located with the guard", ok: rulesPresent, hint: "run `patchstack-connect protect`" },
|
|
4681
|
+
{
|
|
4682
|
+
label: `App Router request/response wiring: ${inventory.files.length - missing.length}/${inventory.files.length} route files`,
|
|
4683
|
+
ok: missing.length === 0 && !inventory.incomplete,
|
|
4684
|
+
hint: `run protect again after adding routes; manually integrate unsupported handlers: ${missing.join(", ") || "inventory incomplete"}`
|
|
4685
|
+
},
|
|
4686
|
+
{
|
|
4687
|
+
label: "rendered pages, Server Actions and Pages API response filtering are not verified",
|
|
4688
|
+
ok: true,
|
|
4689
|
+
unverifiable: true,
|
|
4690
|
+
hint: "middleware cannot screen the downstream response; use a supported server boundary or add response filtering to those handlers"
|
|
4691
|
+
},
|
|
4692
|
+
{
|
|
4693
|
+
label: "live rule delivery and enforcement are not established by source inspection",
|
|
4694
|
+
ok: true,
|
|
4695
|
+
unverifiable: true,
|
|
4696
|
+
hint: "inspect the running guard\u2019s ruleSource and mode; a request reaching middleware does not prove rules were fetched, accepted, or enforced. Framework middleware bypasses require a patched Next.js or upstream protection."
|
|
4697
|
+
}
|
|
4329
4698
|
]
|
|
4330
4699
|
};
|
|
4331
4700
|
}
|
|
4332
4701
|
var nextAdapter = {
|
|
4333
4702
|
name: "nextjs",
|
|
4334
4703
|
label: "Next.js",
|
|
4335
|
-
detect:
|
|
4704
|
+
detect: (cwd) => hasDependency(cwd, "next"),
|
|
4336
4705
|
wire: wire2,
|
|
4337
4706
|
verify: verify2
|
|
4338
4707
|
};
|
|
4339
4708
|
|
|
4340
4709
|
// src/protect/install/seam.ts
|
|
4341
4710
|
import { existsSync as existsSync6 } from "fs";
|
|
4342
|
-
import { join as
|
|
4711
|
+
import { join as join9, dirname as dirname4 } from "path";
|
|
4343
4712
|
function rulesRel(seamRel) {
|
|
4344
|
-
const d =
|
|
4713
|
+
const d = dirname4(seamRel);
|
|
4345
4714
|
return (d === "." ? "patchstack.rules.json" : `${d}/patchstack.rules.json`).replace(/\\/g, "/");
|
|
4346
4715
|
}
|
|
4347
4716
|
function wireSeam(cwd, opts, spec) {
|
|
4348
4717
|
const templates = templatesDir();
|
|
4349
|
-
const existing = spec.candidates.find((c) => existsSync6(
|
|
4718
|
+
const existing = spec.candidates.find((c) => existsSync6(join9(cwd, c)));
|
|
4350
4719
|
const seamRel = existing ?? spec.target;
|
|
4351
|
-
ensureProjectDirectorySync(cwd,
|
|
4352
|
-
const rulesDst =
|
|
4720
|
+
ensureProjectDirectorySync(cwd, dirname4(join9(cwd, seamRel)));
|
|
4721
|
+
const rulesDst = join9(cwd, rulesRel(seamRel));
|
|
4353
4722
|
const changed = [];
|
|
4354
4723
|
if (opts.demo || !existsSync6(rulesDst)) {
|
|
4355
|
-
copyProjectFileSync(cwd,
|
|
4724
|
+
copyProjectFileSync(cwd, join9(templates, opts.demo ? "demo-rules.json" : "rules.json"), rulesDst);
|
|
4356
4725
|
changed.push(rulesRel(seamRel));
|
|
4357
4726
|
}
|
|
4358
|
-
const current = existing ? read(
|
|
4727
|
+
const current = existing ? read(join9(cwd, existing)) : "";
|
|
4359
4728
|
if (existing && current.includes(spec.marker)) {
|
|
4360
4729
|
log(`${existing} already has the Patchstack guard \u2014 left as-is`);
|
|
4361
4730
|
return { ok: true, changed };
|
|
@@ -4364,17 +4733,17 @@ function wireSeam(cwd, opts, spec) {
|
|
|
4364
4733
|
log(`existing ${existing} left untouched \u2014 scaffolded ${rulesRel(seamRel)}; ${spec.planHint}`);
|
|
4365
4734
|
return { ok: true, changed };
|
|
4366
4735
|
}
|
|
4367
|
-
copyProjectFileSync(cwd,
|
|
4736
|
+
copyProjectFileSync(cwd, join9(templates, spec.templateName), join9(cwd, seamRel));
|
|
4368
4737
|
if (!opts.demo) bakeSiteUuid(cwd, seamRel);
|
|
4369
4738
|
changed.push(seamRel);
|
|
4370
4739
|
log(`scaffolded ${seamRel}`);
|
|
4371
4740
|
return { ok: true, changed: [...new Set(changed)] };
|
|
4372
4741
|
}
|
|
4373
4742
|
function verifySeam(cwd, spec) {
|
|
4374
|
-
const existing = spec.candidates.find((c) => existsSync6(
|
|
4743
|
+
const existing = spec.candidates.find((c) => existsSync6(join9(cwd, c)));
|
|
4375
4744
|
const seamRel = existing ?? spec.target;
|
|
4376
|
-
const present2 = existing ? read(
|
|
4377
|
-
const rulesPresent = existsSync6(
|
|
4745
|
+
const present2 = existing ? read(join9(cwd, existing)).includes(spec.marker) : false;
|
|
4746
|
+
const rulesPresent = existsSync6(join9(cwd, rulesRel(seamRel)));
|
|
4378
4747
|
return {
|
|
4379
4748
|
wired: present2 && rulesPresent,
|
|
4380
4749
|
checks: [
|
|
@@ -4393,7 +4762,7 @@ var SPEC = {
|
|
|
4393
4762
|
planHint: 'add the guard to your `handle` (compose with `sequence()` from "@sveltejs/kit/hooks"), then: npx patchstack-connect protect --check',
|
|
4394
4763
|
seamLabel: "SvelteKit server hook"
|
|
4395
4764
|
};
|
|
4396
|
-
function
|
|
4765
|
+
function detect2(cwd) {
|
|
4397
4766
|
return hasDependency(cwd, "@sveltejs/kit");
|
|
4398
4767
|
}
|
|
4399
4768
|
function wire3(cwd, opts) {
|
|
@@ -4405,7 +4774,7 @@ function verify3(cwd) {
|
|
|
4405
4774
|
var sveltekitAdapter = {
|
|
4406
4775
|
name: "sveltekit",
|
|
4407
4776
|
label: "SvelteKit",
|
|
4408
|
-
detect:
|
|
4777
|
+
detect: detect2,
|
|
4409
4778
|
wire: wire3,
|
|
4410
4779
|
verify: verify3
|
|
4411
4780
|
};
|
|
@@ -4419,7 +4788,7 @@ var SPEC2 = {
|
|
|
4419
4788
|
planHint: 'add the guard to your `onRequest` (compose with `sequence()` from "astro:middleware"), then: npx patchstack-connect protect --check',
|
|
4420
4789
|
seamLabel: "Astro middleware"
|
|
4421
4790
|
};
|
|
4422
|
-
function
|
|
4791
|
+
function detect3(cwd) {
|
|
4423
4792
|
return hasDependency(cwd, "astro");
|
|
4424
4793
|
}
|
|
4425
4794
|
function wire4(cwd, opts) {
|
|
@@ -4431,7 +4800,7 @@ function verify4(cwd) {
|
|
|
4431
4800
|
var astroAdapter = {
|
|
4432
4801
|
name: "astro",
|
|
4433
4802
|
label: "Astro",
|
|
4434
|
-
detect:
|
|
4803
|
+
detect: detect3,
|
|
4435
4804
|
wire: wire4,
|
|
4436
4805
|
verify: verify4
|
|
4437
4806
|
};
|
|
@@ -4445,7 +4814,7 @@ var SPEC3 = {
|
|
|
4445
4814
|
planHint: "add a `server/middleware/` handler that runs the guard, then: npx patchstack-connect protect --check",
|
|
4446
4815
|
seamLabel: "Nuxt server middleware"
|
|
4447
4816
|
};
|
|
4448
|
-
function
|
|
4817
|
+
function detect4(cwd) {
|
|
4449
4818
|
return hasDependency(cwd, "nuxt");
|
|
4450
4819
|
}
|
|
4451
4820
|
function wire5(cwd, opts) {
|
|
@@ -4457,14 +4826,14 @@ function verify5(cwd) {
|
|
|
4457
4826
|
var nuxtAdapter = {
|
|
4458
4827
|
name: "nuxt",
|
|
4459
4828
|
label: "Nuxt",
|
|
4460
|
-
detect:
|
|
4829
|
+
detect: detect4,
|
|
4461
4830
|
wire: wire5,
|
|
4462
4831
|
verify: verify5
|
|
4463
4832
|
};
|
|
4464
4833
|
|
|
4465
4834
|
// src/protect/install/find-app.ts
|
|
4466
|
-
import { readFileSync as readFileSync8, readdirSync as
|
|
4467
|
-
import { join as
|
|
4835
|
+
import { readFileSync as readFileSync8, readdirSync as readdirSync4, lstatSync as lstatSync3 } from "fs";
|
|
4836
|
+
import { join as join10, relative as relative4, dirname as dirname5 } from "path";
|
|
4468
4837
|
var SKIP_DIRS2 = /* @__PURE__ */ new Set([
|
|
4469
4838
|
"node_modules",
|
|
4470
4839
|
"patchstack",
|
|
@@ -4490,16 +4859,16 @@ function findAppInstance(cwd, re) {
|
|
|
4490
4859
|
if (depth > MAX_DEPTH) return;
|
|
4491
4860
|
let entries;
|
|
4492
4861
|
try {
|
|
4493
|
-
entries =
|
|
4862
|
+
entries = readdirSync4(dir).sort();
|
|
4494
4863
|
} catch {
|
|
4495
4864
|
return;
|
|
4496
4865
|
}
|
|
4497
4866
|
for (const name of entries) {
|
|
4498
4867
|
if (SKIP_DIRS2.has(name) || name.startsWith(".")) continue;
|
|
4499
|
-
const p =
|
|
4868
|
+
const p = join10(dir, name);
|
|
4500
4869
|
let st;
|
|
4501
4870
|
try {
|
|
4502
|
-
st =
|
|
4871
|
+
st = lstatSync3(p);
|
|
4503
4872
|
} catch {
|
|
4504
4873
|
continue;
|
|
4505
4874
|
}
|
|
@@ -4516,7 +4885,7 @@ function findAppInstance(cwd, re) {
|
|
|
4516
4885
|
if (!m) continue;
|
|
4517
4886
|
const appVar = m[1];
|
|
4518
4887
|
found.push({
|
|
4519
|
-
relPath:
|
|
4888
|
+
relPath: relative4(cwd, p).replace(/\\/g, "/"),
|
|
4520
4889
|
appVar,
|
|
4521
4890
|
listens: listensOn(src, appVar)
|
|
4522
4891
|
});
|
|
@@ -4552,17 +4921,17 @@ function entryRank(relPath) {
|
|
|
4552
4921
|
return index === -1 ? ENTRY_NAMES.length : index;
|
|
4553
4922
|
}
|
|
4554
4923
|
function declaredEntries(cwd) {
|
|
4555
|
-
const
|
|
4924
|
+
const paths2 = /* @__PURE__ */ new Set();
|
|
4556
4925
|
let pkg;
|
|
4557
4926
|
try {
|
|
4558
|
-
pkg = JSON.parse(readFileSync8(
|
|
4927
|
+
pkg = JSON.parse(readFileSync8(join10(cwd, "package.json"), "utf8"));
|
|
4559
4928
|
} catch {
|
|
4560
|
-
return
|
|
4929
|
+
return paths2;
|
|
4561
4930
|
}
|
|
4562
4931
|
const add = (value) => {
|
|
4563
4932
|
if (typeof value !== "string") return;
|
|
4564
4933
|
for (const match of value.matchAll(/[\w./@-]+\.(?:ts|js|mjs|cjs)/g)) {
|
|
4565
|
-
|
|
4934
|
+
paths2.add(match[0].replace(/^\.\//, ""));
|
|
4566
4935
|
}
|
|
4567
4936
|
};
|
|
4568
4937
|
add(pkg.main);
|
|
@@ -4571,10 +4940,10 @@ function declaredEntries(cwd) {
|
|
|
4571
4940
|
if (typeof scripts === "object" && scripts !== null) {
|
|
4572
4941
|
for (const name of ["start", "serve", "dev"]) add(scripts[name]);
|
|
4573
4942
|
}
|
|
4574
|
-
return
|
|
4943
|
+
return paths2;
|
|
4575
4944
|
}
|
|
4576
4945
|
function importSpecifier(fromRel, toRel, preserveExtension = false) {
|
|
4577
|
-
let spec =
|
|
4946
|
+
let spec = relative4(dirname5(fromRel), toRel).replace(/\\/g, "/");
|
|
4578
4947
|
if (!preserveExtension) spec = spec.replace(/\.(?:ts|js)$/, "");
|
|
4579
4948
|
if (!spec.startsWith(".")) spec = `./${spec}`;
|
|
4580
4949
|
return spec;
|
|
@@ -4582,16 +4951,16 @@ function importSpecifier(fromRel, toRel, preserveExtension = false) {
|
|
|
4582
4951
|
|
|
4583
4952
|
// src/protect/install/register.ts
|
|
4584
4953
|
import { existsSync as existsSync9 } from "fs";
|
|
4585
|
-
import { join as
|
|
4954
|
+
import { join as join13, resolve as resolve3 } from "path";
|
|
4586
4955
|
|
|
4587
4956
|
// src/protect/install/generic.ts
|
|
4588
|
-
import { readFileSync as readFileSync9, existsSync as existsSync8, readdirSync as
|
|
4589
|
-
import { join as
|
|
4957
|
+
import { readFileSync as readFileSync9, existsSync as existsSync8, readdirSync as readdirSync5, lstatSync as lstatSync4 } from "fs";
|
|
4958
|
+
import { join as join12, dirname as dirname7 } from "path";
|
|
4590
4959
|
|
|
4591
4960
|
// src/protect/install/source-scope.ts
|
|
4592
4961
|
import { execFileSync } from "child_process";
|
|
4593
4962
|
import { existsSync as existsSync7 } from "fs";
|
|
4594
|
-
import { dirname as
|
|
4963
|
+
import { dirname as dirname6, join as join11, resolve as resolve2 } from "path";
|
|
4595
4964
|
|
|
4596
4965
|
// src/protect/install/node-flags.ts
|
|
4597
4966
|
var flagName = (token) => token.split("=")[0];
|
|
@@ -4880,9 +5249,9 @@ function moduleKindOf(filePath, packageIsModule) {
|
|
|
4880
5249
|
return packageIsModule ? "esm" : "cjs";
|
|
4881
5250
|
}
|
|
4882
5251
|
function resolvesToGuardModule(specifier, query) {
|
|
4883
|
-
const resolved = resolve2(
|
|
4884
|
-
if (withoutModuleExtension(resolved) !==
|
|
4885
|
-
const existing = GUARD_FILENAMES.filter((name) => existsSync7(
|
|
5252
|
+
const resolved = resolve2(dirname6(query.fromFile), specifier);
|
|
5253
|
+
if (withoutModuleExtension(resolved) !== join11(query.guardDir, "guard")) return false;
|
|
5254
|
+
const existing = GUARD_FILENAMES.filter((name) => existsSync7(join11(query.guardDir, name)));
|
|
4886
5255
|
if (existing.length === 0) return false;
|
|
4887
5256
|
const extension = /(\.[A-Za-z0-9]+)$/.exec(specifier)?.[1] ?? "";
|
|
4888
5257
|
if (!query.strictExtensions) return true;
|
|
@@ -4901,16 +5270,16 @@ function withoutModuleExtension(filePath) {
|
|
|
4901
5270
|
// src/protect/install/generic.ts
|
|
4902
5271
|
function packageIsEsm(cwd) {
|
|
4903
5272
|
try {
|
|
4904
|
-
return JSON.parse(read(
|
|
5273
|
+
return JSON.parse(read(join12(cwd, "package.json"))).type === "module";
|
|
4905
5274
|
} catch {
|
|
4906
5275
|
return false;
|
|
4907
5276
|
}
|
|
4908
5277
|
}
|
|
4909
5278
|
function genericDir(cwd) {
|
|
4910
|
-
return existsSync8(
|
|
5279
|
+
return existsSync8(join12(cwd, "src")) ? "src/patchstack" : "patchstack";
|
|
4911
5280
|
}
|
|
4912
5281
|
function hasTypeScript(cwd) {
|
|
4913
|
-
return existsSync8(
|
|
5282
|
+
return existsSync8(join12(cwd, "tsconfig.json")) || candidateEntries(cwd).some((entry) => /\.tsx?$/.test(entry));
|
|
4914
5283
|
}
|
|
4915
5284
|
function genericGuardTarget(cwd) {
|
|
4916
5285
|
if (hasTypeScript(cwd)) {
|
|
@@ -4924,7 +5293,7 @@ function genericGuardTarget(cwd) {
|
|
|
4924
5293
|
function genericScaffoldFiles(cwd) {
|
|
4925
5294
|
const dir = genericDir(cwd);
|
|
4926
5295
|
const candidates = ["guard.ts", "guard.js", "guard.cjs", "rules.json"];
|
|
4927
|
-
return candidates.map((file) => `${dir}/${file}`).filter((
|
|
5296
|
+
return candidates.map((file) => `${dir}/${file}`).filter((relative9) => existsSync8(join12(cwd, relative9)));
|
|
4928
5297
|
}
|
|
4929
5298
|
function scaffoldGeneric(cwd, opts, guardTemplate, guardFile) {
|
|
4930
5299
|
const target = genericGuardTarget(cwd);
|
|
@@ -4932,15 +5301,15 @@ function scaffoldGeneric(cwd, opts, guardTemplate, guardFile) {
|
|
|
4932
5301
|
guardFile ??= target.file;
|
|
4933
5302
|
const templates = templatesDir();
|
|
4934
5303
|
const dir = genericDir(cwd);
|
|
4935
|
-
const dst =
|
|
5304
|
+
const dst = join12(cwd, dir);
|
|
4936
5305
|
ensureProjectDirectorySync(cwd, dst);
|
|
4937
|
-
copyProjectFileSync(cwd,
|
|
5306
|
+
copyProjectFileSync(cwd, join12(templates, guardTemplate), join12(dst, guardFile));
|
|
4938
5307
|
const guardRel = `${dir}/${guardFile}`;
|
|
4939
5308
|
const changed = [guardRel];
|
|
4940
5309
|
if (!opts.demo) bakeSiteUuid(cwd, guardRel);
|
|
4941
|
-
const rulesDst =
|
|
5310
|
+
const rulesDst = join12(dst, "rules.json");
|
|
4942
5311
|
if (opts.demo || !existsSync8(rulesDst)) {
|
|
4943
|
-
copyProjectFileSync(cwd,
|
|
5312
|
+
copyProjectFileSync(cwd, join12(templates, opts.demo ? "demo-rules.json" : "rules.json"), rulesDst);
|
|
4944
5313
|
changed.push(`${dir}/rules.json`);
|
|
4945
5314
|
}
|
|
4946
5315
|
return { changed, dir };
|
|
@@ -4961,10 +5330,10 @@ function candidateEntries(cwd) {
|
|
|
4961
5330
|
"app.ts",
|
|
4962
5331
|
"app.js"
|
|
4963
5332
|
];
|
|
4964
|
-
const hits = names.filter((n) => existsSync8(
|
|
5333
|
+
const hits = names.filter((n) => existsSync8(join12(cwd, n)));
|
|
4965
5334
|
try {
|
|
4966
|
-
const pkg = JSON.parse(read(
|
|
4967
|
-
if (typeof pkg.main === "string" && existsSync8(
|
|
5335
|
+
const pkg = JSON.parse(read(join12(cwd, "package.json")));
|
|
5336
|
+
if (typeof pkg.main === "string" && existsSync8(join12(cwd, pkg.main))) hits.push(pkg.main);
|
|
4968
5337
|
} catch {
|
|
4969
5338
|
}
|
|
4970
5339
|
return [...new Set(hits)];
|
|
@@ -5038,8 +5407,8 @@ function boundNames(declaration) {
|
|
|
5038
5407
|
const braced = /\{([^}]*)\}/.exec(clause)?.[1];
|
|
5039
5408
|
if (braced !== void 0) {
|
|
5040
5409
|
for (const part of braced.split(",")) {
|
|
5041
|
-
const [
|
|
5042
|
-
if (!isGuardExport(
|
|
5410
|
+
const [exported2, local] = splitBinding(part);
|
|
5411
|
+
if (!isGuardExport(exported2)) continue;
|
|
5043
5412
|
if (/^[A-Za-z_$][\w$]*$/.test(local)) direct.push(local);
|
|
5044
5413
|
}
|
|
5045
5414
|
}
|
|
@@ -5055,8 +5424,8 @@ function splitBinding(part) {
|
|
|
5055
5424
|
const name = part.trim();
|
|
5056
5425
|
return [name, name];
|
|
5057
5426
|
}
|
|
5058
|
-
const [
|
|
5059
|
-
return [(
|
|
5427
|
+
const [exported2, ...rest] = part.split(separator);
|
|
5428
|
+
return [(exported2 ?? "").trim(), (rest.join(separator) ?? "").trim()];
|
|
5060
5429
|
}
|
|
5061
5430
|
function isGuardExport(name) {
|
|
5062
5431
|
return GUARD_EXPORTS.includes(name);
|
|
@@ -5067,9 +5436,9 @@ function escapeForRegExp(value) {
|
|
|
5067
5436
|
function genericVerify(cwd) {
|
|
5068
5437
|
const dir = genericDir(cwd);
|
|
5069
5438
|
const target = genericGuardTarget(cwd);
|
|
5070
|
-
const present2 = GUARD_FILENAMES.find((name) => existsSync8(
|
|
5439
|
+
const present2 = GUARD_FILENAMES.find((name) => existsSync8(join12(cwd, dir, name)));
|
|
5071
5440
|
const scaffolded = present2 !== void 0;
|
|
5072
|
-
const imported = scaffolded && guardIsImported(cwd,
|
|
5441
|
+
const imported = scaffolded && guardIsImported(cwd, join12(cwd, dir, present2));
|
|
5073
5442
|
return {
|
|
5074
5443
|
wired: scaffolded && imported,
|
|
5075
5444
|
checks: [
|
|
@@ -5103,25 +5472,25 @@ var NON_RUNTIME_DIRS = /* @__PURE__ */ new Set([
|
|
|
5103
5472
|
"__mocks__"
|
|
5104
5473
|
]);
|
|
5105
5474
|
function guardIsImported(cwd, guardPath) {
|
|
5106
|
-
const guardDir =
|
|
5475
|
+
const guardDir = dirname7(guardPath);
|
|
5107
5476
|
const packageIsModule = packageIsEsm(cwd);
|
|
5108
|
-
const root = existsSync8(
|
|
5477
|
+
const root = existsSync8(join12(cwd, "src")) ? join12(cwd, "src") : cwd;
|
|
5109
5478
|
let found = false;
|
|
5110
5479
|
const walk2 = (d, depth) => {
|
|
5111
5480
|
if (found || depth > 8) return;
|
|
5112
5481
|
let entries;
|
|
5113
5482
|
try {
|
|
5114
|
-
entries =
|
|
5483
|
+
entries = readdirSync5(d);
|
|
5115
5484
|
} catch {
|
|
5116
5485
|
return;
|
|
5117
5486
|
}
|
|
5118
5487
|
for (const name of entries) {
|
|
5119
5488
|
if (found) return;
|
|
5120
5489
|
if (NON_RUNTIME_DIRS.has(name) || name.startsWith(".")) continue;
|
|
5121
|
-
const p =
|
|
5490
|
+
const p = join12(d, name);
|
|
5122
5491
|
let st;
|
|
5123
5492
|
try {
|
|
5124
|
-
st =
|
|
5493
|
+
st = lstatSync4(p);
|
|
5125
5494
|
} catch {
|
|
5126
5495
|
continue;
|
|
5127
5496
|
}
|
|
@@ -5177,7 +5546,7 @@ function guardTarget(cwd, entryRel, spec) {
|
|
|
5177
5546
|
}
|
|
5178
5547
|
function packageType(cwd) {
|
|
5179
5548
|
try {
|
|
5180
|
-
return JSON.parse(read(
|
|
5549
|
+
return JSON.parse(read(join13(cwd, "package.json"))).type;
|
|
5181
5550
|
} catch {
|
|
5182
5551
|
return void 0;
|
|
5183
5552
|
}
|
|
@@ -5213,8 +5582,8 @@ function bindsGuard(line, maskedLine, importName, call, fileRel, guard) {
|
|
|
5213
5582
|
}
|
|
5214
5583
|
function namesGuardModule(specifier, fileRel, guard) {
|
|
5215
5584
|
return resolvesToGuardModule(specifier, {
|
|
5216
|
-
fromFile:
|
|
5217
|
-
guardDir: resolve3(
|
|
5585
|
+
fromFile: join13(guard.cwd, fileRel),
|
|
5586
|
+
guardDir: resolve3(join13(guard.cwd, guard.guardDir)),
|
|
5218
5587
|
kind: moduleKindOf(fileRel, packageType(guard.cwd) === "module"),
|
|
5219
5588
|
strictExtensions: true
|
|
5220
5589
|
});
|
|
@@ -5233,7 +5602,7 @@ function wireRegister(cwd, opts, spec) {
|
|
|
5233
5602
|
}
|
|
5234
5603
|
const target = guardTarget(cwd, entry.relPath, spec);
|
|
5235
5604
|
const { changed, dir } = scaffoldGeneric(cwd, opts, target.template, target.file);
|
|
5236
|
-
const p =
|
|
5605
|
+
const p = join13(cwd, entry.relPath);
|
|
5237
5606
|
const s = read(p);
|
|
5238
5607
|
const state = wiringState(s, spec, entry.appVar, entry.relPath, { cwd, guardDir: dir });
|
|
5239
5608
|
if (state.importAtTopLevel && state.callInAppScope) {
|
|
@@ -5303,17 +5672,17 @@ function firstStatementLine(lines) {
|
|
|
5303
5672
|
return i;
|
|
5304
5673
|
}
|
|
5305
5674
|
function serverIsGuarded(cwd, spec, server, guardDir) {
|
|
5306
|
-
const source = read(
|
|
5675
|
+
const source = read(join13(cwd, server.relPath));
|
|
5307
5676
|
const state = wiringState(source, spec, server.appVar, server.relPath, { cwd, guardDir });
|
|
5308
5677
|
if (!state.importAtTopLevel || !state.callInAppScope || !state.ordered) return false;
|
|
5309
5678
|
return !spec.callAfter || routesBefore(source, server.appVar, state.callIndex).length === 0;
|
|
5310
5679
|
}
|
|
5311
5680
|
function verifyRegister(cwd, spec) {
|
|
5312
|
-
const dir = existsSync9(
|
|
5681
|
+
const dir = existsSync9(join13(cwd, "src")) ? "src/patchstack" : "patchstack";
|
|
5313
5682
|
const entry = findAppInstance(cwd, spec.appRe);
|
|
5314
5683
|
const target = entry ? guardTarget(cwd, entry.relPath, spec) : null;
|
|
5315
|
-
const scaffolded = target ? existsSync9(
|
|
5316
|
-
const entrySource = entry ? read(
|
|
5684
|
+
const scaffolded = target ? existsSync9(join13(cwd, dir, target.file)) : false;
|
|
5685
|
+
const entrySource = entry ? read(join13(cwd, entry.relPath)) : "";
|
|
5317
5686
|
const state = entry ? wiringState(entrySource, spec, entry.appVar, entry.relPath, { cwd, guardDir: dir }) : { importAtTopLevel: false, callInAppScope: false, ordered: false, callIndex: -1, appIndex: -1 };
|
|
5318
5687
|
const wired = state.importAtTopLevel && state.callInAppScope && state.ordered;
|
|
5319
5688
|
const early = spec.callAfter && entry ? routesBefore(entrySource, entry.appVar, state.callIndex) : [];
|
|
@@ -5404,7 +5773,7 @@ var expressAdapter = {
|
|
|
5404
5773
|
|
|
5405
5774
|
// src/protect/install/reporting.ts
|
|
5406
5775
|
import { existsSync as existsSync10 } from "fs";
|
|
5407
|
-
import { join as
|
|
5776
|
+
import { join as join14 } from "path";
|
|
5408
5777
|
|
|
5409
5778
|
// src/protect/reporting-state.js
|
|
5410
5779
|
var REPORTING_STATES = Object.freeze([
|
|
@@ -5462,7 +5831,7 @@ function explainReportingState(state) {
|
|
|
5462
5831
|
|
|
5463
5832
|
// src/protect/install/reporting.ts
|
|
5464
5833
|
function siteIdentity(cwd, env) {
|
|
5465
|
-
const rc =
|
|
5834
|
+
const rc = join14(cwd, ".patchstackrc.json");
|
|
5466
5835
|
let recorded;
|
|
5467
5836
|
if (existsSync10(rc)) {
|
|
5468
5837
|
try {
|
|
@@ -5559,7 +5928,7 @@ function runProtect(cwd, opts = {}) {
|
|
|
5559
5928
|
return { status: "scaffolded", adapter: "generic", changed: [], plan: "" };
|
|
5560
5929
|
}
|
|
5561
5930
|
}
|
|
5562
|
-
var RUNTIMES_WITHOUT_CONFIG_FILE = /* @__PURE__ */ new Set(["
|
|
5931
|
+
var RUNTIMES_WITHOUT_CONFIG_FILE = /* @__PURE__ */ new Set(["nextjs", "sveltekit", "astro", "nuxt", "generic"]);
|
|
5563
5932
|
function credentialNote(cwd, adapterName) {
|
|
5564
5933
|
if (!RUNTIMES_WITHOUT_CONFIG_FILE.has(adapterName)) return [];
|
|
5565
5934
|
const localHint = hasResolvableCredential(cwd) ? "a credential is configured here; set the same one in your deployment" : "no credential found here either \u2014 run `npx @patchstack/connect scan` to provision one";
|
|
@@ -5929,7 +6298,7 @@ function findWidgetMarker(cwd, siteUuid) {
|
|
|
5929
6298
|
}
|
|
5930
6299
|
let entries;
|
|
5931
6300
|
try {
|
|
5932
|
-
entries =
|
|
6301
|
+
entries = readdirSync6(dir, { withFileTypes: true });
|
|
5933
6302
|
} catch {
|
|
5934
6303
|
return;
|
|
5935
6304
|
}
|
|
@@ -5997,7 +6366,7 @@ function findAstroDocumentLayout(cwd) {
|
|
|
5997
6366
|
const dir = path12.join("src", "layouts");
|
|
5998
6367
|
let names;
|
|
5999
6368
|
try {
|
|
6000
|
-
names =
|
|
6369
|
+
names = readdirSync6(path12.join(cwd, dir)).filter((name) => name.endsWith(".astro")).sort();
|
|
6001
6370
|
} catch {
|
|
6002
6371
|
return null;
|
|
6003
6372
|
}
|
|
@@ -6543,11 +6912,11 @@ async function claim2(config, onPrompt, deps = {}) {
|
|
|
6543
6912
|
}
|
|
6544
6913
|
|
|
6545
6914
|
// src/protect/install/runtime/check.ts
|
|
6546
|
-
import { relative as
|
|
6915
|
+
import { relative as relative5 } from "path";
|
|
6547
6916
|
|
|
6548
6917
|
// src/protect/install/runtime/entry.ts
|
|
6549
6918
|
import { existsSync as existsSync13, readFileSync as readFileSync13, realpathSync as realpathSync4, statSync as statSync6 } from "fs";
|
|
6550
|
-
import { join as
|
|
6919
|
+
import { join as join15, resolve as resolve4, sep as sep2 } from "path";
|
|
6551
6920
|
var CONVENTIONAL = [
|
|
6552
6921
|
"server.js",
|
|
6553
6922
|
"server.mjs",
|
|
@@ -6573,7 +6942,7 @@ var CONVENTIONAL = [
|
|
|
6573
6942
|
var LOADABLE = /\.(?:js|mjs|cjs)$/;
|
|
6574
6943
|
function readPackage2(cwd) {
|
|
6575
6944
|
try {
|
|
6576
|
-
const parsed = JSON.parse(readFileSync13(
|
|
6945
|
+
const parsed = JSON.parse(readFileSync13(join15(cwd, "package.json"), "utf8"));
|
|
6577
6946
|
return parsed !== null && typeof parsed === "object" ? parsed : null;
|
|
6578
6947
|
} catch {
|
|
6579
6948
|
return null;
|
|
@@ -6643,7 +7012,7 @@ function resolveEntry(cwd) {
|
|
|
6643
7012
|
}
|
|
6644
7013
|
return {
|
|
6645
7014
|
kind: "unavailable",
|
|
6646
|
-
reason: existsSync13(
|
|
7015
|
+
reason: existsSync13(join15(cwd, "package.json")) ? 'no directly loadable entry was found \u2014 declare one as a "start" script of the form `node <file>`, or run the check against a built output' : "there is no package.json here"
|
|
6647
7016
|
};
|
|
6648
7017
|
}
|
|
6649
7018
|
|
|
@@ -6994,7 +7363,7 @@ async function runRuntimeCheck(cwd, options = {}) {
|
|
|
6994
7363
|
settleMs: options.settleMs,
|
|
6995
7364
|
platform: options.platform
|
|
6996
7365
|
});
|
|
6997
|
-
return { ...result, entry: { file:
|
|
7366
|
+
return { ...result, entry: { file: relative5(cwd, entry.file).replace(/\\/g, "/"), from: entry.from } };
|
|
6998
7367
|
}
|
|
6999
7368
|
function runtimeExitCode(report) {
|
|
7000
7369
|
if (report.outcome === "proven") return 0;
|
|
@@ -7028,12 +7397,12 @@ function formatRuntimeCheck(report) {
|
|
|
7028
7397
|
import path14 from "path";
|
|
7029
7398
|
|
|
7030
7399
|
// src/map/ts-loader.ts
|
|
7031
|
-
import { createRequire } from "module";
|
|
7400
|
+
import { createRequire as createRequire2 } from "module";
|
|
7032
7401
|
import { pathToFileURL } from "url";
|
|
7033
|
-
import { join as
|
|
7402
|
+
import { join as join16 } from "path";
|
|
7034
7403
|
async function loadTypeScript(cwd) {
|
|
7035
7404
|
try {
|
|
7036
|
-
const req =
|
|
7405
|
+
const req = createRequire2(pathToFileURL(join16(cwd, "package.json")));
|
|
7037
7406
|
const resolved = req.resolve("typescript");
|
|
7038
7407
|
const mod = await import(pathToFileURL(resolved).href);
|
|
7039
7408
|
return mod.default ?? mod;
|
|
@@ -7050,7 +7419,7 @@ async function loadTypeScript(cwd) {
|
|
|
7050
7419
|
// src/map/extract.ts
|
|
7051
7420
|
import { readFileSync as readFileSync16, realpathSync as realpathSync6 } from "fs";
|
|
7052
7421
|
import { createHash as createHash5 } from "crypto";
|
|
7053
|
-
import { relative as
|
|
7422
|
+
import { relative as relative7 } from "path";
|
|
7054
7423
|
|
|
7055
7424
|
// src/map/bindings.ts
|
|
7056
7425
|
import { builtinModules } from "module";
|
|
@@ -7407,14 +7776,14 @@ function directSinks(node, ts, bindings, ctx) {
|
|
|
7407
7776
|
const root = base ? rootIdentifier(base, ts) : void 0;
|
|
7408
7777
|
if (!root) return {};
|
|
7409
7778
|
const spec = bindings.resolve(root);
|
|
7410
|
-
const
|
|
7779
|
+
const relative9 = spec !== void 0 && (spec.startsWith(".") || spec.startsWith("/"));
|
|
7411
7780
|
const pkg = npmPackageOf(spec);
|
|
7412
7781
|
if (pkg) return { pkg, root, spec };
|
|
7413
|
-
if (
|
|
7782
|
+
if (relative9 && ctx && spec) {
|
|
7414
7783
|
const viaModule = ctx.graph.importedBinding(ctx.file, spec, bindings.exportNameOf(root) ?? root);
|
|
7415
7784
|
if (viaModule) return { pkg: viaModule.package, root, spec };
|
|
7416
7785
|
}
|
|
7417
|
-
return { local: bindings.locals.has(root), root, spec, relative:
|
|
7786
|
+
return { local: bindings.locals.has(root), root, spec, relative: relative9 };
|
|
7418
7787
|
};
|
|
7419
7788
|
const attributionOf = (b, pkg) => b.pkg ? "import" : pkg ? "inferred" : void 0;
|
|
7420
7789
|
const dbApi = (pkg, attribution) => {
|
|
@@ -8704,7 +9073,7 @@ function withNote(doc) {
|
|
|
8704
9073
|
|
|
8705
9074
|
// src/map/module-graph.ts
|
|
8706
9075
|
import { readFileSync as readFileSync14, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
|
|
8707
|
-
import { dirname as
|
|
9076
|
+
import { dirname as dirname9, join as join17, relative as relative6, resolve as resolvePath } from "path";
|
|
8708
9077
|
var RESOLVE_EXTS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
8709
9078
|
function createModuleGraph(ts, opts) {
|
|
8710
9079
|
const cache = /* @__PURE__ */ new Map();
|
|
@@ -8746,7 +9115,7 @@ function createModuleGraph(ts, opts) {
|
|
|
8746
9115
|
for (const callee of mod.calleesOf.get(exportName) ?? []) {
|
|
8747
9116
|
for (const s of mod.fnSinks.get(callee) ?? []) collected.push(s);
|
|
8748
9117
|
}
|
|
8749
|
-
const rel =
|
|
9118
|
+
const rel = relative6(opts.cwd, target);
|
|
8750
9119
|
return collected.map((s) => ({ ...s, file: rel }));
|
|
8751
9120
|
},
|
|
8752
9121
|
// A different question about the same module: not "what sinks are in there" but "what does this
|
|
@@ -8772,7 +9141,7 @@ function createModuleGraph(ts, opts) {
|
|
|
8772
9141
|
}
|
|
8773
9142
|
function resolveRelativeModule(fromFile, spec) {
|
|
8774
9143
|
if (!spec.startsWith(".")) return void 0;
|
|
8775
|
-
const dir =
|
|
9144
|
+
const dir = dirname9(fromFile);
|
|
8776
9145
|
const base = resolvePath(dir, spec);
|
|
8777
9146
|
const candidates = [];
|
|
8778
9147
|
const jsLike = /\.(js|jsx|mjs|cjs)$/.exec(base);
|
|
@@ -8782,7 +9151,7 @@ function resolveRelativeModule(fromFile, spec) {
|
|
|
8782
9151
|
}
|
|
8783
9152
|
candidates.push(base);
|
|
8784
9153
|
for (const e of RESOLVE_EXTS) candidates.push(base + e);
|
|
8785
|
-
for (const e of RESOLVE_EXTS) candidates.push(
|
|
9154
|
+
for (const e of RESOLVE_EXTS) candidates.push(join17(base, "index" + e));
|
|
8786
9155
|
for (const c of candidates) {
|
|
8787
9156
|
try {
|
|
8788
9157
|
if (statSync7(c).isFile()) return c;
|
|
@@ -9072,7 +9441,7 @@ function handlerEntry(name, kindLabel, params, body, ts, localSinks, bindings, c
|
|
|
9072
9441
|
|
|
9073
9442
|
// src/map/imports.ts
|
|
9074
9443
|
import { readFileSync as readFileSync15 } from "fs";
|
|
9075
|
-
import { join as
|
|
9444
|
+
import { join as join18 } from "path";
|
|
9076
9445
|
var MAX_SITES2 = 5;
|
|
9077
9446
|
var NPM_NAME = /^(?:@[^/@\s~][^/@\s]*\/)?[^/@\s.~][^/@\s]*$/;
|
|
9078
9447
|
function isNpmPackageName(pkg) {
|
|
@@ -9081,15 +9450,15 @@ function isNpmPackageName(pkg) {
|
|
|
9081
9450
|
function readPathAliases(cwd) {
|
|
9082
9451
|
const aliases = [];
|
|
9083
9452
|
for (const name of ["tsconfig.json", "jsconfig.json"]) {
|
|
9084
|
-
let
|
|
9453
|
+
let paths2;
|
|
9085
9454
|
try {
|
|
9086
|
-
const raw = readFileSync15(
|
|
9455
|
+
const raw = readFileSync15(join18(cwd, name), "utf8");
|
|
9087
9456
|
const parsed = JSON.parse(stripJsonComments(raw));
|
|
9088
|
-
|
|
9457
|
+
paths2 = parsed.compilerOptions?.paths;
|
|
9089
9458
|
} catch {
|
|
9090
9459
|
continue;
|
|
9091
9460
|
}
|
|
9092
|
-
for (const key of Object.keys(
|
|
9461
|
+
for (const key of Object.keys(paths2 ?? {})) {
|
|
9093
9462
|
if (key.startsWith(".")) continue;
|
|
9094
9463
|
if (key.endsWith("*")) aliases.push({ prefix: key.slice(0, -1), wildcard: true });
|
|
9095
9464
|
else aliases.push({ prefix: key, wildcard: false });
|
|
@@ -9355,7 +9724,7 @@ async function extractInputMap(cwd, ts, options = {}) {
|
|
|
9355
9724
|
for (const file of files) {
|
|
9356
9725
|
try {
|
|
9357
9726
|
const text = readFileSync16(file, "utf8");
|
|
9358
|
-
const relFile =
|
|
9727
|
+
const relFile = relative7(cwd, file);
|
|
9359
9728
|
sourceBytes += text.length;
|
|
9360
9729
|
if (isComponentFile(file)) {
|
|
9361
9730
|
componentFiles++;
|
|
@@ -9424,7 +9793,7 @@ async function extractInputMap(cwd, ts, options = {}) {
|
|
|
9424
9793
|
}
|
|
9425
9794
|
} catch (e) {
|
|
9426
9795
|
if (typeof process !== "undefined" && process.env?.PS_MAP_DEBUG) console.error("[patchstack] map error", file, e);
|
|
9427
|
-
failed.push(
|
|
9796
|
+
failed.push(relative7(cwd, file));
|
|
9428
9797
|
}
|
|
9429
9798
|
}
|
|
9430
9799
|
let hopParsed = 0;
|
|
@@ -9435,7 +9804,7 @@ async function extractInputMap(cwd, ts, options = {}) {
|
|
|
9435
9804
|
const text = readFileSync16(target, "utf8");
|
|
9436
9805
|
const sf = ts.createSourceFile(target, text, ts.ScriptTarget.Latest, true, guessScriptKind(ts, target));
|
|
9437
9806
|
const bindings = buildModuleBindings(sf, ts);
|
|
9438
|
-
const called = collectInvocations(sf, ts, bindings, { file: target, owner:
|
|
9807
|
+
const called = collectInvocations(sf, ts, bindings, { file: target, owner: relative7(cwd, target), graph });
|
|
9439
9808
|
invocations.add(called.invocations);
|
|
9440
9809
|
calls.total += called.counts.total;
|
|
9441
9810
|
calls.dependency += called.counts.dependency;
|
|
@@ -9481,7 +9850,7 @@ async function extractInputMap(cwd, ts, options = {}) {
|
|
|
9481
9850
|
if (endpoints.length === 0) notes.push("No recognized server-side entry points found under the analyzed roots.");
|
|
9482
9851
|
const importList = imports.list();
|
|
9483
9852
|
const unmodelled = importList.filter((d) => d.recognizedSinkKinds.length === 0).length;
|
|
9484
|
-
const skippedDirsWithSource = stats.skippedWithSource.map((d) =>
|
|
9853
|
+
const skippedDirsWithSource = stats.skippedWithSource.map((d) => relative7(cwd, d));
|
|
9485
9854
|
const importCoverageGaps = {
|
|
9486
9855
|
unreadableFiles: failed.length,
|
|
9487
9856
|
unscannableFiles: importScanFailures,
|
|
@@ -9595,8 +9964,8 @@ function getStringFlag(flags, name) {
|
|
|
9595
9964
|
}
|
|
9596
9965
|
|
|
9597
9966
|
// src/build-stamp.ts
|
|
9598
|
-
import { lstatSync as
|
|
9599
|
-
import { join as
|
|
9967
|
+
import { lstatSync as lstatSync5, readFileSync as readFileSync17, readdirSync as readdirSync7, realpathSync as realpathSync7 } from "fs";
|
|
9968
|
+
import { join as join19, relative as relative8, sep as sep3 } from "path";
|
|
9600
9969
|
var RULES_FILE_NAMES = /* @__PURE__ */ new Set(["rules.json", "patchstack.rules.json"]);
|
|
9601
9970
|
var SKIP_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "out", "coverage", "tmp", "vendor"]);
|
|
9602
9971
|
var MAX_DEPTH2 = 6;
|
|
@@ -9608,15 +9977,15 @@ function guardImportsRulesFile(dir, rulesName) {
|
|
|
9608
9977
|
const importsRules = new RegExp(`(?:\\bfrom\\s*|\\brequire\\s*\\(\\s*|\\bnew\\s+URL\\s*\\(\\s*)${quotedRules}`);
|
|
9609
9978
|
let siblings;
|
|
9610
9979
|
try {
|
|
9611
|
-
siblings =
|
|
9980
|
+
siblings = readdirSync7(dir);
|
|
9612
9981
|
} catch {
|
|
9613
9982
|
return false;
|
|
9614
9983
|
}
|
|
9615
9984
|
for (const name of siblings) {
|
|
9616
9985
|
if (!GUARD_SOURCE.test(name)) continue;
|
|
9617
|
-
const path17 =
|
|
9986
|
+
const path17 = join19(dir, name);
|
|
9618
9987
|
try {
|
|
9619
|
-
const entry =
|
|
9988
|
+
const entry = lstatSync5(path17);
|
|
9620
9989
|
if (!entry.isFile() || entry.isSymbolicLink() || entry.size > MAX_GUARD_SOURCE_BYTES) continue;
|
|
9621
9990
|
const source = stripComments(readFileSync17(path17, "utf8"));
|
|
9622
9991
|
if (source.includes("@patchstack/connect/protect") && importsRules.test(source)) return true;
|
|
@@ -9651,16 +10020,16 @@ function findRulesFile(cwd) {
|
|
|
9651
10020
|
if (depth > MAX_DEPTH2 || found.length > 8) return;
|
|
9652
10021
|
let entries;
|
|
9653
10022
|
try {
|
|
9654
|
-
entries =
|
|
10023
|
+
entries = readdirSync7(dir);
|
|
9655
10024
|
} catch {
|
|
9656
10025
|
return;
|
|
9657
10026
|
}
|
|
9658
10027
|
for (const name of entries) {
|
|
9659
10028
|
if (name.startsWith(".")) continue;
|
|
9660
|
-
const path17 =
|
|
10029
|
+
const path17 = join19(dir, name);
|
|
9661
10030
|
let entry;
|
|
9662
10031
|
try {
|
|
9663
|
-
entry =
|
|
10032
|
+
entry = lstatSync5(path17);
|
|
9664
10033
|
} catch {
|
|
9665
10034
|
continue;
|
|
9666
10035
|
}
|
|
@@ -9680,9 +10049,9 @@ function findRulesFile(cwd) {
|
|
|
9680
10049
|
}
|
|
9681
10050
|
};
|
|
9682
10051
|
walk2(cwd, 0);
|
|
9683
|
-
if (found.length > 1) return { kind: "ambiguous", paths: found.map((p) =>
|
|
10052
|
+
if (found.length > 1) return { kind: "ambiguous", paths: found.map((p) => relative8(cwd, p)) };
|
|
9684
10053
|
if (found.length === 1) return { kind: "one", path: found[0] };
|
|
9685
|
-
if (unreadable.length > 0) return { kind: "unreadable", paths: unreadable.map((p) =>
|
|
10054
|
+
if (unreadable.length > 0) return { kind: "unreadable", paths: unreadable.map((p) => relative8(cwd, p)) };
|
|
9686
10055
|
return { kind: "none" };
|
|
9687
10056
|
}
|
|
9688
10057
|
function detectIndent(text) {
|
|
@@ -9703,7 +10072,7 @@ function applyBuildStamp(cwd, id) {
|
|
|
9703
10072
|
reason: `more than one candidate guard rules file (${location.paths.join(", ")}) \u2014 none was stamped`
|
|
9704
10073
|
};
|
|
9705
10074
|
}
|
|
9706
|
-
const file =
|
|
10075
|
+
const file = relative8(cwd, location.path);
|
|
9707
10076
|
let text;
|
|
9708
10077
|
try {
|
|
9709
10078
|
text = readFileSync17(location.path, "utf8");
|
|
@@ -11142,7 +11511,7 @@ async function runMarkBuild(args) {
|
|
|
11142
11511
|
}
|
|
11143
11512
|
function packageVersion() {
|
|
11144
11513
|
try {
|
|
11145
|
-
const require2 =
|
|
11514
|
+
const require2 = createRequire3(import.meta.url);
|
|
11146
11515
|
const manifest = require2("../package.json");
|
|
11147
11516
|
return typeof manifest.version === "string" && manifest.version.length > 0 ? manifest.version : "unknown";
|
|
11148
11517
|
} catch {
|