@patchstack/connect 0.2.7 → 0.2.9
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/README.md +23 -1
- package/dist/cli.js +297 -34
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +133 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +126 -27
- package/dist/index.js.map +1 -1
- package/dist/protect/templates/engine.d.ts +57 -0
- package/dist/protect/templates/engine.js +137 -0
- package/dist/protect/templates/guard.ts +100 -0
- package/dist/protect/templates/manifest.d.ts +1 -0
- package/dist/protect/templates/rules.json +22 -0
- package/package.json +4 -3
package/dist/index.cjs
CHANGED
|
@@ -47,8 +47,8 @@ __export(src_exports, {
|
|
|
47
47
|
module.exports = __toCommonJS(src_exports);
|
|
48
48
|
|
|
49
49
|
// src/parsers/index.ts
|
|
50
|
-
var
|
|
51
|
-
var
|
|
50
|
+
var import_promises6 = require("fs/promises");
|
|
51
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
52
52
|
|
|
53
53
|
// src/types.ts
|
|
54
54
|
var PatchstackError = class extends Error {
|
|
@@ -62,13 +62,52 @@ var PatchstackError = class extends Error {
|
|
|
62
62
|
cause;
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
-
// src/parsers/
|
|
65
|
+
// src/parsers/consistency.ts
|
|
66
66
|
var import_promises = require("fs/promises");
|
|
67
67
|
var import_node_path = __toESM(require("path"), 1);
|
|
68
|
+
async function readDeclaredDependencyNames(cwd) {
|
|
69
|
+
let raw;
|
|
70
|
+
try {
|
|
71
|
+
raw = await (0, import_promises.readFile)(import_node_path.default.join(cwd, "package.json"), "utf8");
|
|
72
|
+
} catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
let parsed;
|
|
76
|
+
try {
|
|
77
|
+
parsed = JSON.parse(raw);
|
|
78
|
+
} catch {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
const names = /* @__PURE__ */ new Set();
|
|
85
|
+
for (const group of ["dependencies", "devDependencies"]) {
|
|
86
|
+
const deps = parsed[group];
|
|
87
|
+
if (typeof deps !== "object" || deps === null) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
for (const [name, spec] of Object.entries(deps)) {
|
|
91
|
+
if (typeof spec === "string" && /^(file|link|workspace|portal):/.test(spec)) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
names.add(name);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return [...names];
|
|
98
|
+
}
|
|
99
|
+
function missingDependencies(declared, packages) {
|
|
100
|
+
const present = new Set(packages.map((entry) => entry.name));
|
|
101
|
+
return declared.filter((name) => !present.has(name));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/parsers/npm.ts
|
|
105
|
+
var import_promises2 = require("fs/promises");
|
|
106
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
68
107
|
async function parseNpmLockfile(lockfilePath) {
|
|
69
108
|
let raw;
|
|
70
109
|
try {
|
|
71
|
-
raw = await (0,
|
|
110
|
+
raw = await (0, import_promises2.readFile)(lockfilePath, "utf8");
|
|
72
111
|
} catch (cause) {
|
|
73
112
|
throw new PatchstackError(`Could not read lockfile at ${lockfilePath}`, "LOCKFILE_NOT_FOUND", cause);
|
|
74
113
|
}
|
|
@@ -126,7 +165,7 @@ function extractFromV1(deps, acc = [], depth = 0) {
|
|
|
126
165
|
return acc;
|
|
127
166
|
}
|
|
128
167
|
function extractNameFromPath(pkgPath) {
|
|
129
|
-
const segments = pkgPath.split("node_modules" +
|
|
168
|
+
const segments = pkgPath.split("node_modules" + import_node_path2.default.sep === pkgPath ? import_node_path2.default.sep : "/");
|
|
130
169
|
const parts = pkgPath.split("/");
|
|
131
170
|
const nmIndex = parts.lastIndexOf("node_modules");
|
|
132
171
|
if (nmIndex === -1 || nmIndex >= parts.length - 1) {
|
|
@@ -149,12 +188,12 @@ function isDirectV2(pkgPath) {
|
|
|
149
188
|
}
|
|
150
189
|
|
|
151
190
|
// src/parsers/node_modules.ts
|
|
152
|
-
var
|
|
153
|
-
var
|
|
191
|
+
var import_promises3 = require("fs/promises");
|
|
192
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
154
193
|
async function walkNodeModules(cwd) {
|
|
155
|
-
const root =
|
|
194
|
+
const root = import_node_path3.default.join(cwd, "node_modules");
|
|
156
195
|
try {
|
|
157
|
-
const info = await (0,
|
|
196
|
+
const info = await (0, import_promises3.stat)(root);
|
|
158
197
|
if (!info.isDirectory()) {
|
|
159
198
|
throw new PatchstackError(
|
|
160
199
|
`${root} exists but is not a directory.`,
|
|
@@ -178,7 +217,7 @@ async function walkNodeModules(cwd) {
|
|
|
178
217
|
async function walk(dir, acc, depth) {
|
|
179
218
|
let names;
|
|
180
219
|
try {
|
|
181
|
-
names = await (0,
|
|
220
|
+
names = await (0, import_promises3.readdir)(dir);
|
|
182
221
|
} catch {
|
|
183
222
|
return;
|
|
184
223
|
}
|
|
@@ -186,14 +225,14 @@ async function walk(dir, acc, depth) {
|
|
|
186
225
|
if (name.startsWith(".")) {
|
|
187
226
|
continue;
|
|
188
227
|
}
|
|
189
|
-
const fullPath =
|
|
228
|
+
const fullPath = import_node_path3.default.join(dir, name);
|
|
190
229
|
if (!await isPlainDirectory(fullPath)) {
|
|
191
230
|
continue;
|
|
192
231
|
}
|
|
193
232
|
if (name.startsWith("@")) {
|
|
194
233
|
let subNames;
|
|
195
234
|
try {
|
|
196
|
-
subNames = await (0,
|
|
235
|
+
subNames = await (0, import_promises3.readdir)(fullPath);
|
|
197
236
|
} catch {
|
|
198
237
|
continue;
|
|
199
238
|
}
|
|
@@ -201,7 +240,7 @@ async function walk(dir, acc, depth) {
|
|
|
201
240
|
if (sub.startsWith(".")) {
|
|
202
241
|
continue;
|
|
203
242
|
}
|
|
204
|
-
const scopedDir =
|
|
243
|
+
const scopedDir = import_node_path3.default.join(fullPath, sub);
|
|
205
244
|
if (!await isPlainDirectory(scopedDir)) {
|
|
206
245
|
continue;
|
|
207
246
|
}
|
|
@@ -217,7 +256,7 @@ async function walk(dir, acc, depth) {
|
|
|
217
256
|
async function readPackage(pkgDir, depth, acc) {
|
|
218
257
|
let raw;
|
|
219
258
|
try {
|
|
220
|
-
raw = await (0,
|
|
259
|
+
raw = await (0, import_promises3.readFile)(import_node_path3.default.join(pkgDir, "package.json"), "utf8");
|
|
221
260
|
} catch {
|
|
222
261
|
return;
|
|
223
262
|
}
|
|
@@ -240,7 +279,7 @@ async function readPackage(pkgDir, depth, acc) {
|
|
|
240
279
|
});
|
|
241
280
|
}
|
|
242
281
|
async function walkNested(pkgDir, acc, depth) {
|
|
243
|
-
const nested =
|
|
282
|
+
const nested = import_node_path3.default.join(pkgDir, "node_modules");
|
|
244
283
|
if (!await isPlainDirectory(nested)) {
|
|
245
284
|
return;
|
|
246
285
|
}
|
|
@@ -248,7 +287,7 @@ async function walkNested(pkgDir, acc, depth) {
|
|
|
248
287
|
}
|
|
249
288
|
async function isPlainDirectory(dir) {
|
|
250
289
|
try {
|
|
251
|
-
const info = await (0,
|
|
290
|
+
const info = await (0, import_promises3.lstat)(dir);
|
|
252
291
|
return info.isDirectory() && !info.isSymbolicLink();
|
|
253
292
|
} catch {
|
|
254
293
|
return false;
|
|
@@ -256,11 +295,11 @@ async function isPlainDirectory(dir) {
|
|
|
256
295
|
}
|
|
257
296
|
|
|
258
297
|
// src/parsers/pnpm.ts
|
|
259
|
-
var
|
|
298
|
+
var import_promises4 = require("fs/promises");
|
|
260
299
|
async function parsePnpmLockfile(lockfilePath) {
|
|
261
300
|
let raw;
|
|
262
301
|
try {
|
|
263
|
-
raw = await (0,
|
|
302
|
+
raw = await (0, import_promises4.readFile)(lockfilePath, "utf8");
|
|
264
303
|
} catch (cause) {
|
|
265
304
|
throw new PatchstackError(
|
|
266
305
|
`Could not read lockfile at ${lockfilePath}`,
|
|
@@ -508,12 +547,12 @@ function extractLeafName(trimmed) {
|
|
|
508
547
|
}
|
|
509
548
|
|
|
510
549
|
// src/parsers/yarn.ts
|
|
511
|
-
var
|
|
512
|
-
var
|
|
550
|
+
var import_promises5 = require("fs/promises");
|
|
551
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
513
552
|
async function parseYarnLockfile(lockfilePath) {
|
|
514
553
|
let raw;
|
|
515
554
|
try {
|
|
516
|
-
raw = await (0,
|
|
555
|
+
raw = await (0, import_promises5.readFile)(lockfilePath, "utf8");
|
|
517
556
|
} catch (cause) {
|
|
518
557
|
throw new PatchstackError(
|
|
519
558
|
`Could not read lockfile at ${lockfilePath}`,
|
|
@@ -528,7 +567,7 @@ async function parseYarnLockfile(lockfilePath) {
|
|
|
528
567
|
"LOCKFILE_PARSE_ERROR"
|
|
529
568
|
);
|
|
530
569
|
}
|
|
531
|
-
const directNames = await readDirectDepNames(
|
|
570
|
+
const directNames = await readDirectDepNames(import_node_path4.default.dirname(lockfilePath));
|
|
532
571
|
const entries = [];
|
|
533
572
|
const seen = /* @__PURE__ */ new Set();
|
|
534
573
|
for (const block of blocks) {
|
|
@@ -672,7 +711,7 @@ async function readDirectDepNames(cwd) {
|
|
|
672
711
|
const names = /* @__PURE__ */ new Set();
|
|
673
712
|
let raw;
|
|
674
713
|
try {
|
|
675
|
-
raw = await (0,
|
|
714
|
+
raw = await (0, import_promises5.readFile)(import_node_path4.default.join(cwd, "package.json"), "utf8");
|
|
676
715
|
} catch {
|
|
677
716
|
return names;
|
|
678
717
|
}
|
|
@@ -709,7 +748,7 @@ var LOCKFILE_CANDIDATES = [
|
|
|
709
748
|
async function detectLockfile(cwd) {
|
|
710
749
|
const probed = await Promise.all(
|
|
711
750
|
LOCKFILE_CANDIDATES.map(async (candidate) => {
|
|
712
|
-
const filePath =
|
|
751
|
+
const filePath = import_node_path5.default.join(cwd, candidate.filename);
|
|
713
752
|
return { ...candidate, filePath, present: await exists(filePath) };
|
|
714
753
|
})
|
|
715
754
|
);
|
|
@@ -729,9 +768,69 @@ async function detectLockfile(cwd) {
|
|
|
729
768
|
);
|
|
730
769
|
}
|
|
731
770
|
async function scanLockfile(cwd) {
|
|
732
|
-
const
|
|
733
|
-
|
|
734
|
-
|
|
771
|
+
const candidates = await presentLockfiles(cwd);
|
|
772
|
+
if (candidates.length === 0) {
|
|
773
|
+
await detectLockfile(cwd);
|
|
774
|
+
}
|
|
775
|
+
const declared = await readDeclaredDependencyNames(cwd);
|
|
776
|
+
const warnings = [];
|
|
777
|
+
let firstParsed = null;
|
|
778
|
+
let walkTried = false;
|
|
779
|
+
for (const candidate of candidates) {
|
|
780
|
+
let packages;
|
|
781
|
+
try {
|
|
782
|
+
packages = await runStrategy(candidate, cwd);
|
|
783
|
+
} catch {
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
walkTried ||= candidate.strategy === "node-modules-walk";
|
|
787
|
+
firstParsed ??= { packages, filename: candidate.filename };
|
|
788
|
+
const missing = missingDependencies(declared, packages);
|
|
789
|
+
if (missing.length === 0) {
|
|
790
|
+
return manifestWith(packages, warnings);
|
|
791
|
+
}
|
|
792
|
+
warnings.push(staleWarning(candidate.filename, missing));
|
|
793
|
+
}
|
|
794
|
+
if (!walkTried) {
|
|
795
|
+
try {
|
|
796
|
+
const packages = await walkNodeModules(cwd);
|
|
797
|
+
const missing = missingDependencies(declared, packages);
|
|
798
|
+
if (missing.length > 0) {
|
|
799
|
+
warnings.push(staleWarning("node_modules/", missing));
|
|
800
|
+
}
|
|
801
|
+
warnings.push("Scanned node_modules/ instead. Delete the stale lockfile to silence this warning.");
|
|
802
|
+
return manifestWith(packages, warnings);
|
|
803
|
+
} catch {
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
if (firstParsed === null) {
|
|
807
|
+
const expected = LOCKFILE_CANDIDATES.map((candidate) => candidate.filename).join(", ");
|
|
808
|
+
throw new PatchstackError(
|
|
809
|
+
`No readable lockfile found in ${cwd}. Expected one of: ${expected}.`,
|
|
810
|
+
"LOCKFILE_NOT_FOUND"
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
warnings.push(
|
|
814
|
+
`No fully-consistent source found; reporting ${firstParsed.filename}. The manifest may understate the real dependency set.`
|
|
815
|
+
);
|
|
816
|
+
return manifestWith(firstParsed.packages, warnings);
|
|
817
|
+
}
|
|
818
|
+
function manifestWith(packages, warnings) {
|
|
819
|
+
return warnings.length > 0 ? { ecosystem: "npm", packages, warnings } : { ecosystem: "npm", packages };
|
|
820
|
+
}
|
|
821
|
+
function staleWarning(source, missing) {
|
|
822
|
+
const sample = missing.slice(0, 3).join(", ");
|
|
823
|
+
const suffix = missing.length > 3 ? `, +${missing.length - 3} more` : "";
|
|
824
|
+
return `${source} looks stale: package.json declares ${missing.length} dependenc${missing.length === 1 ? "y" : "ies"} it does not contain (${sample}${suffix}).`;
|
|
825
|
+
}
|
|
826
|
+
async function presentLockfiles(cwd) {
|
|
827
|
+
const probed = await Promise.all(
|
|
828
|
+
LOCKFILE_CANDIDATES.map(async (candidate) => {
|
|
829
|
+
const filePath = import_node_path5.default.join(cwd, candidate.filename);
|
|
830
|
+
return { ...candidate, filePath, present: await exists(filePath) };
|
|
831
|
+
})
|
|
832
|
+
);
|
|
833
|
+
return probed.filter((candidate) => candidate.present).map(({ filename, strategy, filePath }) => ({ ecosystem: "npm", filename, strategy, filePath }));
|
|
735
834
|
}
|
|
736
835
|
async function runStrategy(detected, cwd) {
|
|
737
836
|
switch (detected.strategy) {
|
|
@@ -747,7 +846,7 @@ async function runStrategy(detected, cwd) {
|
|
|
747
846
|
}
|
|
748
847
|
async function exists(filePath) {
|
|
749
848
|
try {
|
|
750
|
-
await (0,
|
|
849
|
+
await (0, import_promises6.access)(filePath);
|
|
751
850
|
return true;
|
|
752
851
|
} catch {
|
|
753
852
|
return false;
|
|
@@ -934,8 +1033,8 @@ function isTimeoutError(cause) {
|
|
|
934
1033
|
}
|
|
935
1034
|
|
|
936
1035
|
// src/config.ts
|
|
937
|
-
var
|
|
938
|
-
var
|
|
1036
|
+
var import_promises7 = require("fs/promises");
|
|
1037
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
939
1038
|
var CONFIG_FILENAME = ".patchstackrc.json";
|
|
940
1039
|
var DEFAULT_ENVIRONMENT = "production";
|
|
941
1040
|
async function resolveConfig(options) {
|
|
@@ -972,9 +1071,9 @@ async function resolveConfig(options) {
|
|
|
972
1071
|
};
|
|
973
1072
|
}
|
|
974
1073
|
async function writeConfigFile(cwd, config) {
|
|
975
|
-
const target =
|
|
1074
|
+
const target = import_node_path6.default.join(cwd, CONFIG_FILENAME);
|
|
976
1075
|
const content = JSON.stringify(config, null, 2) + "\n";
|
|
977
|
-
await (0,
|
|
1076
|
+
await (0, import_promises7.writeFile)(target, content, "utf8");
|
|
978
1077
|
return target;
|
|
979
1078
|
}
|
|
980
1079
|
async function persistSiteUuid(cwd, siteUuid) {
|
|
@@ -982,10 +1081,10 @@ async function persistSiteUuid(cwd, siteUuid) {
|
|
|
982
1081
|
return writeConfigFile(cwd, { ...existing, siteUuid });
|
|
983
1082
|
}
|
|
984
1083
|
async function readConfigFile(cwd) {
|
|
985
|
-
const target =
|
|
1084
|
+
const target = import_node_path6.default.join(cwd, CONFIG_FILENAME);
|
|
986
1085
|
let raw;
|
|
987
1086
|
try {
|
|
988
|
-
raw = await (0,
|
|
1087
|
+
raw = await (0, import_promises7.readFile)(target, "utf8");
|
|
989
1088
|
} catch (err) {
|
|
990
1089
|
if (err.code === "ENOENT") {
|
|
991
1090
|
return {};
|