@patchstack/connect 0.3.20 → 0.3.21

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/protect.cjs CHANGED
@@ -164,7 +164,7 @@ async function resolveConfig(options) {
164
164
  };
165
165
  }
166
166
  async function readConfigFile(cwd) {
167
- const target = import_node_path.default.join(cwd, CONFIG_FILENAME);
167
+ const target = import_node_path2.default.join(cwd, CONFIG_FILENAME);
168
168
  let raw;
169
169
  try {
170
170
  raw = await (0, import_promises.readFile)(target, "utf8");
@@ -215,12 +215,12 @@ function isUuid(value) {
215
215
  function isEnvironment(value) {
216
216
  return value === "production" || value === "sandbox";
217
217
  }
218
- var import_promises, import_node_path, CONFIG_FILENAME, DEFAULT_ENVIRONMENT;
218
+ var import_promises, import_node_path2, CONFIG_FILENAME, DEFAULT_ENVIRONMENT;
219
219
  var init_config = __esm({
220
220
  "src/config.ts"() {
221
221
  "use strict";
222
222
  import_promises = require("fs/promises");
223
- import_node_path = __toESM(require("path"), 1);
223
+ import_node_path2 = __toESM(require("path"), 1);
224
224
  init_types();
225
225
  init_client();
226
226
  CONFIG_FILENAME = ".patchstackrc.json";
@@ -232,7 +232,7 @@ var init_config = __esm({
232
232
  async function readDeclaredDependencyNames(cwd) {
233
233
  let raw;
234
234
  try {
235
- raw = await (0, import_promises2.readFile)(import_node_path2.default.join(cwd, "package.json"), "utf8");
235
+ raw = await (0, import_promises2.readFile)(import_node_path3.default.join(cwd, "package.json"), "utf8");
236
236
  } catch {
237
237
  return [];
238
238
  }
@@ -264,12 +264,12 @@ function missingDependencies(declared, packages) {
264
264
  const present = new Set(packages.map((entry) => entry.name));
265
265
  return declared.filter((name) => !present.has(name));
266
266
  }
267
- var import_promises2, import_node_path2;
267
+ var import_promises2, import_node_path3;
268
268
  var init_consistency = __esm({
269
269
  "src/parsers/consistency.ts"() {
270
270
  "use strict";
271
271
  import_promises2 = require("fs/promises");
272
- import_node_path2 = __toESM(require("path"), 1);
272
+ import_node_path3 = __toESM(require("path"), 1);
273
273
  }
274
274
  });
275
275
 
@@ -335,7 +335,7 @@ function extractFromV1(deps, acc = [], depth = 0) {
335
335
  return acc;
336
336
  }
337
337
  function extractNameFromPath(pkgPath) {
338
- const segments = pkgPath.split("node_modules" + import_node_path3.default.sep === pkgPath ? import_node_path3.default.sep : "/");
338
+ const segments = pkgPath.split("node_modules" + import_node_path4.default.sep === pkgPath ? import_node_path4.default.sep : "/");
339
339
  const parts = pkgPath.split("/");
340
340
  const nmIndex = parts.lastIndexOf("node_modules");
341
341
  if (nmIndex === -1 || nmIndex >= parts.length - 1) {
@@ -356,19 +356,19 @@ function isDirectV2(pkgPath) {
356
356
  const nmCount = parts.filter((p) => p === "node_modules").length;
357
357
  return nmCount === 1;
358
358
  }
359
- var import_promises3, import_node_path3;
359
+ var import_promises3, import_node_path4;
360
360
  var init_npm = __esm({
361
361
  "src/parsers/npm.ts"() {
362
362
  "use strict";
363
363
  import_promises3 = require("fs/promises");
364
- import_node_path3 = __toESM(require("path"), 1);
364
+ import_node_path4 = __toESM(require("path"), 1);
365
365
  init_types();
366
366
  }
367
367
  });
368
368
 
369
369
  // src/parsers/node_modules.ts
370
370
  async function walkNodeModules(cwd) {
371
- const root = import_node_path4.default.join(cwd, "node_modules");
371
+ const root = import_node_path5.default.join(cwd, "node_modules");
372
372
  try {
373
373
  const info = await (0, import_promises4.stat)(root);
374
374
  if (!info.isDirectory()) {
@@ -402,7 +402,7 @@ async function walk(dir, acc, depth) {
402
402
  if (name.startsWith(".")) {
403
403
  continue;
404
404
  }
405
- const fullPath = import_node_path4.default.join(dir, name);
405
+ const fullPath = import_node_path5.default.join(dir, name);
406
406
  if (!await isPlainDirectory(fullPath)) {
407
407
  continue;
408
408
  }
@@ -417,7 +417,7 @@ async function walk(dir, acc, depth) {
417
417
  if (sub.startsWith(".")) {
418
418
  continue;
419
419
  }
420
- const scopedDir = import_node_path4.default.join(fullPath, sub);
420
+ const scopedDir = import_node_path5.default.join(fullPath, sub);
421
421
  if (!await isPlainDirectory(scopedDir)) {
422
422
  continue;
423
423
  }
@@ -433,7 +433,7 @@ async function walk(dir, acc, depth) {
433
433
  async function readPackage(pkgDir, depth, acc) {
434
434
  let raw;
435
435
  try {
436
- raw = await (0, import_promises4.readFile)(import_node_path4.default.join(pkgDir, "package.json"), "utf8");
436
+ raw = await (0, import_promises4.readFile)(import_node_path5.default.join(pkgDir, "package.json"), "utf8");
437
437
  } catch {
438
438
  return;
439
439
  }
@@ -456,7 +456,7 @@ async function readPackage(pkgDir, depth, acc) {
456
456
  });
457
457
  }
458
458
  async function walkNested(pkgDir, acc, depth) {
459
- const nested = import_node_path4.default.join(pkgDir, "node_modules");
459
+ const nested = import_node_path5.default.join(pkgDir, "node_modules");
460
460
  if (!await isPlainDirectory(nested)) {
461
461
  return;
462
462
  }
@@ -470,12 +470,12 @@ async function isPlainDirectory(dir) {
470
470
  return false;
471
471
  }
472
472
  }
473
- var import_promises4, import_node_path4;
473
+ var import_promises4, import_node_path5;
474
474
  var init_node_modules = __esm({
475
475
  "src/parsers/node_modules.ts"() {
476
476
  "use strict";
477
477
  import_promises4 = require("fs/promises");
478
- import_node_path4 = __toESM(require("path"), 1);
478
+ import_node_path5 = __toESM(require("path"), 1);
479
479
  init_types();
480
480
  }
481
481
  });
@@ -758,7 +758,7 @@ async function parseYarnLockfile(lockfilePath) {
758
758
  "LOCKFILE_PARSE_ERROR"
759
759
  );
760
760
  }
761
- const directNames = await readDirectDepNames(import_node_path5.default.dirname(lockfilePath));
761
+ const directNames = await readDirectDepNames(import_node_path6.default.dirname(lockfilePath));
762
762
  const entries = [];
763
763
  const seen = /* @__PURE__ */ new Set();
764
764
  for (const block of blocks) {
@@ -902,7 +902,7 @@ async function readDirectDepNames(cwd) {
902
902
  const names = /* @__PURE__ */ new Set();
903
903
  let raw;
904
904
  try {
905
- raw = await (0, import_promises6.readFile)(import_node_path5.default.join(cwd, "package.json"), "utf8");
905
+ raw = await (0, import_promises6.readFile)(import_node_path6.default.join(cwd, "package.json"), "utf8");
906
906
  } catch {
907
907
  return names;
908
908
  }
@@ -927,12 +927,12 @@ async function readDirectDepNames(cwd) {
927
927
  }
928
928
  return names;
929
929
  }
930
- var import_promises6, import_node_path5;
930
+ var import_promises6, import_node_path6;
931
931
  var init_yarn = __esm({
932
932
  "src/parsers/yarn.ts"() {
933
933
  "use strict";
934
934
  import_promises6 = require("fs/promises");
935
- import_node_path5 = __toESM(require("path"), 1);
935
+ import_node_path6 = __toESM(require("path"), 1);
936
936
  init_types();
937
937
  }
938
938
  });
@@ -941,7 +941,7 @@ var init_yarn = __esm({
941
941
  async function detectLockfile(cwd) {
942
942
  const probed = await Promise.all(
943
943
  LOCKFILE_CANDIDATES.map(async (candidate) => {
944
- const filePath = import_node_path6.default.join(cwd, candidate.filename);
944
+ const filePath = import_node_path7.default.join(cwd, candidate.filename);
945
945
  return { ...candidate, filePath, present: await exists(filePath) };
946
946
  })
947
947
  );
@@ -1019,7 +1019,7 @@ function staleWarning(source, missing) {
1019
1019
  async function presentLockfiles(cwd) {
1020
1020
  const probed = await Promise.all(
1021
1021
  LOCKFILE_CANDIDATES.map(async (candidate) => {
1022
- const filePath = import_node_path6.default.join(cwd, candidate.filename);
1022
+ const filePath = import_node_path7.default.join(cwd, candidate.filename);
1023
1023
  return { ...candidate, filePath, present: await exists(filePath) };
1024
1024
  })
1025
1025
  );
@@ -1045,12 +1045,12 @@ async function exists(filePath) {
1045
1045
  return false;
1046
1046
  }
1047
1047
  }
1048
- var import_promises7, import_node_path6, LOCKFILE_CANDIDATES;
1048
+ var import_promises7, import_node_path7, LOCKFILE_CANDIDATES;
1049
1049
  var init_parsers = __esm({
1050
1050
  "src/parsers/index.ts"() {
1051
1051
  "use strict";
1052
1052
  import_promises7 = require("fs/promises");
1053
- import_node_path6 = __toESM(require("path"), 1);
1053
+ import_node_path7 = __toESM(require("path"), 1);
1054
1054
  init_types();
1055
1055
  init_consistency();
1056
1056
  init_npm();
@@ -2876,9 +2876,189 @@ function renderBlockPage({ url = "", code = "patchstack" } = {}) {
2876
2876
  </html>`;
2877
2877
  }
2878
2878
 
2879
- // src/protect/runtime.js
2879
+ // src/protect/rules/store.js
2880
2880
  var import_node_fs = require("fs");
2881
- var import_node_path7 = require("path");
2881
+ var import_node_path = require("path");
2882
+ function makeStore(options = {}) {
2883
+ let mem = null;
2884
+ const durable = durableTier(options);
2885
+ return {
2886
+ async read() {
2887
+ if (mem) return mem;
2888
+ const env = await durable.read();
2889
+ if (env) mem = env;
2890
+ return env;
2891
+ },
2892
+ async write(env) {
2893
+ if (env) mem = env;
2894
+ await durable.write(env);
2895
+ }
2896
+ };
2897
+ }
2898
+ function durableTier(options) {
2899
+ const adapter = options.ruleCache;
2900
+ if (adapter && typeof adapter.read === "function" && typeof adapter.write === "function") {
2901
+ return {
2902
+ read: async () => {
2903
+ try {
2904
+ return toEnvelope(await adapter.read());
2905
+ } catch {
2906
+ return null;
2907
+ }
2908
+ },
2909
+ write: async (env) => {
2910
+ try {
2911
+ await adapter.write(env);
2912
+ } catch {
2913
+ }
2914
+ }
2915
+ };
2916
+ }
2917
+ const dir = options.cacheDir;
2918
+ return {
2919
+ read: async () => cacheRead(dir),
2920
+ write: async (env) => cacheWrite(dir, env)
2921
+ };
2922
+ }
2923
+ function cachePath(dir) {
2924
+ return (0, import_node_path.join)(dir, "patchstack-rules.json");
2925
+ }
2926
+ function cacheWrite(dir, env) {
2927
+ if (!dir) return;
2928
+ try {
2929
+ (0, import_node_fs.mkdirSync)(dir, { recursive: true });
2930
+ (0, import_node_fs.writeFileSync)(cachePath(dir), JSON.stringify(env));
2931
+ } catch {
2932
+ }
2933
+ }
2934
+ function cacheRead(dir) {
2935
+ if (!dir) return null;
2936
+ try {
2937
+ return toEnvelope(JSON.parse((0, import_node_fs.readFileSync)(cachePath(dir), "utf8")));
2938
+ } catch {
2939
+ return null;
2940
+ }
2941
+ }
2942
+ function toEnvelope(value) {
2943
+ if (!value || typeof value !== "object") return null;
2944
+ if (value.bundle && typeof value.bundle === "object") {
2945
+ return { bundle: value.bundle, etag: value.etag ?? null };
2946
+ }
2947
+ if (Array.isArray(value.firewall) || Array.isArray(value.whitelists)) {
2948
+ return { bundle: value, etag: null };
2949
+ }
2950
+ return null;
2951
+ }
2952
+
2953
+ // src/protect/rules/source.js
2954
+ async function resolveRules(options, store) {
2955
+ if (options.siteUuid) {
2956
+ const prior = await store.read();
2957
+ const client = new PulseRuleClient({ siteUuid: options.siteUuid, baseUrl: options.pulseRulesUrl, etag: prior?.etag });
2958
+ const res = await client.getRules();
2959
+ if (res.success && res.notModified && prior?.bundle) return normalizeBundle(prior.bundle);
2960
+ if (res.success && !res.notModified) {
2961
+ const bundle = normalizeBundle(res);
2962
+ await store.write({ bundle, etag: res.etag ?? null });
2963
+ return bundle;
2964
+ }
2965
+ if (prior?.bundle) {
2966
+ options.onError?.(new Error(`pulse rule fetch failed (${res.error ?? "no usable response"}); using cached bundle`));
2967
+ return normalizeBundle(prior.bundle);
2968
+ }
2969
+ if (options.rules) {
2970
+ options.onError?.(new Error(`pulse rule fetch failed (${res.error ?? "no usable response"}); using bundled fallback`));
2971
+ return normalizeBundle(options.rules);
2972
+ }
2973
+ options.onError?.(new Error(`pulse rule fetch failed (${res.error ?? "no usable response"}); no cache \u2014 running with no rules`));
2974
+ return emptyBundle();
2975
+ }
2976
+ if (options.token) {
2977
+ const prior = await store.read();
2978
+ const client = new PatchstackRuleClient({ token: options.token, baseUrl: options.baseUrl, etag: prior?.etag });
2979
+ const res = await client.getRules();
2980
+ if (res.success && res.notModified && prior?.bundle) return normalizeBundle(prior.bundle);
2981
+ if (res.success && !res.notModified) {
2982
+ const bundle = normalizeBundle(res);
2983
+ await store.write({ bundle, etag: res.etag ?? null });
2984
+ return bundle;
2985
+ }
2986
+ if (prior?.bundle) {
2987
+ options.onError?.(new Error(`rule fetch failed (${res.error ?? "no usable response"}); using cached bundle`));
2988
+ return normalizeBundle(prior.bundle);
2989
+ }
2990
+ options.onError?.(new Error(`rule fetch failed (${res.error ?? "no usable response"}); no cache \u2014 running with no rules`));
2991
+ return emptyBundle();
2992
+ }
2993
+ if (options.rules) {
2994
+ return normalizeBundle(options.rules);
2995
+ }
2996
+ return emptyBundle();
2997
+ }
2998
+ function normalizeBundle(b) {
2999
+ return {
3000
+ firewall: Array.isArray(b.firewall) ? b.firewall : [],
3001
+ whitelists: Array.isArray(b.whitelists) ? b.whitelists : [],
3002
+ whitelist_keys: b.whitelist_keys ?? {}
3003
+ };
3004
+ }
3005
+ function emptyBundle() {
3006
+ return { firewall: [], whitelists: [], whitelist_keys: {} };
3007
+ }
3008
+
3009
+ // src/protect/rules/refresh.js
3010
+ var JITTER_FRACTION3 = 0.1;
3011
+ var MAX_BACKOFF_MULTIPLIER = 8;
3012
+ function startRefresh(tick, { refreshMs, onError } = {}) {
3013
+ let stopped = false;
3014
+ let failures = 0;
3015
+ let timer = null;
3016
+ const schedule = () => {
3017
+ if (stopped) return;
3018
+ const backoff = Math.min(2 ** failures, MAX_BACKOFF_MULTIPLIER);
3019
+ const jitter = 1 - Math.random() * JITTER_FRACTION3;
3020
+ const delay = refreshMs * backoff * jitter;
3021
+ timer = setTimeout(run, delay);
3022
+ timer?.unref?.();
3023
+ };
3024
+ const run = async () => {
3025
+ if (stopped) return;
3026
+ try {
3027
+ await tick();
3028
+ failures = 0;
3029
+ } catch (err) {
3030
+ failures++;
3031
+ onError?.(err);
3032
+ }
3033
+ schedule();
3034
+ };
3035
+ schedule();
3036
+ return {
3037
+ stop() {
3038
+ stopped = true;
3039
+ if (timer) clearTimeout(timer);
3040
+ }
3041
+ };
3042
+ }
3043
+ function makeRefreshHandler(tick, secret) {
3044
+ return async (request) => {
3045
+ if (!secret) return new Response("not found", { status: 404 });
3046
+ let provided = null;
3047
+ try {
3048
+ provided = request?.headers?.get?.("x-patchstack-refresh") ?? new URL(request.url).searchParams.get("token");
3049
+ } catch {
3050
+ provided = null;
3051
+ }
3052
+ if (provided !== secret) return new Response("forbidden", { status: 403 });
3053
+ let refreshed = true;
3054
+ try {
3055
+ await tick();
3056
+ } catch {
3057
+ refreshed = false;
3058
+ }
3059
+ return new Response(JSON.stringify({ refreshed }), { status: 200, headers: { "content-type": "application/json" } });
3060
+ };
3061
+ }
2882
3062
 
2883
3063
  // src/protect/supabase-guard.js
2884
3064
  var GUARD_PATH = "/_patchstack/guard";
@@ -2954,7 +3134,8 @@ async function createProtection(options = {}) {
2954
3134
  const mode = options.mode === "block" ? "block" : "dry-run";
2955
3135
  const onError = options.onError;
2956
3136
  const onDetect = options.onDetect ?? defaultOnDetect;
2957
- const bundle = await resolveRules(options);
3137
+ const store = makeStore(options);
3138
+ const bundle = await resolveRules(options, store);
2958
3139
  let requestRules;
2959
3140
  let responseRules;
2960
3141
  let egressRules;
@@ -3270,30 +3451,35 @@ async function createProtection(options = {}) {
3270
3451
  allowHosts: options.allowHosts
3271
3452
  });
3272
3453
  }
3273
- if (options.refreshMs > 0 && (options.siteUuid || options.token)) {
3274
- const cwd = options.cwd ?? (typeof process !== "undefined" ? process.cwd() : void 0);
3275
- let reportManifest2 = null;
3276
- if (options.siteUuid && options.reportManifest !== false && cwd) {
3454
+ const cwd = options.cwd ?? (typeof process !== "undefined" ? process.cwd() : void 0);
3455
+ const live = Boolean(options.siteUuid || options.token);
3456
+ const refreshSecret = options.refreshSecret ?? (typeof process !== "undefined" ? process.env.PATCHSTACK_REFRESH_SECRET : void 0);
3457
+ const refreshable = live && (options.refreshMs > 0 || Boolean(refreshSecret));
3458
+ let reporter = null;
3459
+ if (refreshable && options.siteUuid && options.reportManifest !== false && cwd) {
3460
+ try {
3461
+ ({ reportManifest: reporter } = await Promise.resolve().then(() => (init_refresh_manifest(), refresh_manifest_exports)));
3462
+ } catch (err) {
3463
+ onError?.(err);
3464
+ }
3465
+ }
3466
+ const runRefreshTick = async () => {
3467
+ if (reporter) {
3277
3468
  try {
3278
- ({ reportManifest: reportManifest2 } = await Promise.resolve().then(() => (init_refresh_manifest(), refresh_manifest_exports)));
3469
+ await reporter(cwd);
3279
3470
  } catch (err) {
3280
3471
  onError?.(err);
3281
3472
  }
3282
3473
  }
3283
- const timer = setInterval(() => {
3284
- (async () => {
3285
- if (reportManifest2) {
3286
- try {
3287
- await reportManifest2(cwd);
3288
- } catch (err) {
3289
- onError?.(err);
3290
- }
3291
- }
3292
- applyBundle(await resolveRules(options));
3293
- })().catch((err) => onError?.(err));
3294
- }, options.refreshMs);
3295
- timer.unref?.();
3296
- protection.stopRefresh = () => clearInterval(timer);
3474
+ applyBundle(await resolveRules(options, store));
3475
+ };
3476
+ if (live) {
3477
+ protection.refresh = () => runRefreshTick();
3478
+ protection.refreshHandler = () => makeRefreshHandler(runRefreshTick, refreshSecret);
3479
+ }
3480
+ if (options.refreshMs > 0 && live) {
3481
+ const loop = startRefresh(runRefreshTick, { refreshMs: options.refreshMs, onError });
3482
+ protection.stopRefresh = loop.stop;
3297
3483
  }
3298
3484
  return protection;
3299
3485
  }
@@ -3535,115 +3721,6 @@ function leakResponse() {
3535
3721
  headers: { "content-type": "application/json" }
3536
3722
  });
3537
3723
  }
3538
- async function resolveRules(options) {
3539
- const cache = makeCache(options);
3540
- if (options.siteUuid) {
3541
- const prior = await cache.read();
3542
- const client = new PulseRuleClient({ siteUuid: options.siteUuid, baseUrl: options.pulseRulesUrl, etag: prior?.etag });
3543
- const res = await client.getRules();
3544
- if (res.success && res.notModified && prior?.bundle) return normalizeBundle(prior.bundle);
3545
- if (res.success && !res.notModified) {
3546
- const bundle = normalizeBundle(res);
3547
- await cache.write({ bundle, etag: res.etag ?? null });
3548
- return bundle;
3549
- }
3550
- if (prior?.bundle) {
3551
- options.onError?.(new Error(`pulse rule fetch failed (${res.error ?? "no usable response"}); using cached bundle`));
3552
- return normalizeBundle(prior.bundle);
3553
- }
3554
- if (options.rules) {
3555
- options.onError?.(new Error(`pulse rule fetch failed (${res.error ?? "no usable response"}); using bundled fallback`));
3556
- return normalizeBundle(options.rules);
3557
- }
3558
- options.onError?.(new Error(`pulse rule fetch failed (${res.error ?? "no usable response"}); no cache \u2014 running with no rules`));
3559
- return emptyBundle();
3560
- }
3561
- if (options.token) {
3562
- const prior = await cache.read();
3563
- const client = new PatchstackRuleClient({ token: options.token, baseUrl: options.baseUrl, etag: prior?.etag });
3564
- const res = await client.getRules();
3565
- if (res.success && res.notModified && prior?.bundle) return normalizeBundle(prior.bundle);
3566
- if (res.success && !res.notModified) {
3567
- const bundle = normalizeBundle(res);
3568
- await cache.write({ bundle, etag: res.etag ?? null });
3569
- return bundle;
3570
- }
3571
- if (prior?.bundle) {
3572
- options.onError?.(new Error(`rule fetch failed (${res.error ?? "no usable response"}); using cached bundle`));
3573
- return normalizeBundle(prior.bundle);
3574
- }
3575
- options.onError?.(new Error(`rule fetch failed (${res.error ?? "no usable response"}); no cache \u2014 running with no rules`));
3576
- return emptyBundle();
3577
- }
3578
- if (options.rules) {
3579
- return normalizeBundle(options.rules);
3580
- }
3581
- return emptyBundle();
3582
- }
3583
- function normalizeBundle(b) {
3584
- return {
3585
- firewall: Array.isArray(b.firewall) ? b.firewall : [],
3586
- whitelists: Array.isArray(b.whitelists) ? b.whitelists : [],
3587
- whitelist_keys: b.whitelist_keys ?? {}
3588
- };
3589
- }
3590
- function emptyBundle() {
3591
- return { firewall: [], whitelists: [], whitelist_keys: {} };
3592
- }
3593
- function makeCache(options) {
3594
- const adapter = options.ruleCache;
3595
- if (adapter && typeof adapter.read === "function" && typeof adapter.write === "function") {
3596
- return {
3597
- read: async () => {
3598
- try {
3599
- return toEnvelope(await adapter.read());
3600
- } catch {
3601
- return null;
3602
- }
3603
- },
3604
- write: async (env) => {
3605
- try {
3606
- await adapter.write(env);
3607
- } catch {
3608
- }
3609
- }
3610
- };
3611
- }
3612
- const dir = options.cacheDir;
3613
- return {
3614
- read: async () => cacheRead(dir),
3615
- write: async (env) => cacheWrite(dir, env)
3616
- };
3617
- }
3618
- function cachePath(dir) {
3619
- return (0, import_node_path7.join)(dir, "patchstack-rules.json");
3620
- }
3621
- function cacheWrite(dir, env) {
3622
- if (!dir) return;
3623
- try {
3624
- (0, import_node_fs.mkdirSync)(dir, { recursive: true });
3625
- (0, import_node_fs.writeFileSync)(cachePath(dir), JSON.stringify(env));
3626
- } catch {
3627
- }
3628
- }
3629
- function cacheRead(dir) {
3630
- if (!dir) return null;
3631
- try {
3632
- return toEnvelope(JSON.parse((0, import_node_fs.readFileSync)(cachePath(dir), "utf8")));
3633
- } catch {
3634
- return null;
3635
- }
3636
- }
3637
- function toEnvelope(value) {
3638
- if (!value || typeof value !== "object") return null;
3639
- if (value.bundle && typeof value.bundle === "object") {
3640
- return { bundle: value.bundle, etag: value.etag ?? null };
3641
- }
3642
- if (Array.isArray(value.firewall) || Array.isArray(value.whitelists)) {
3643
- return { bundle: value, etag: null };
3644
- }
3645
- return null;
3646
- }
3647
3724
  var BLOCK_MESSAGE = "This request has been blocked by Patchstack.";
3648
3725
  function blockBody(result) {
3649
3726
  return { error: BLOCK_MESSAGE, message: BLOCK_MESSAGE, rule: result?.rule?.id };