@patchstack/connect 0.5.3 → 0.5.4
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 +2 -2
- package/README.md +3 -3
- package/dist/cli.js +3697 -3290
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +102 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -55
- package/dist/index.d.ts +68 -55
- package/dist/index.js +102 -11
- package/dist/index.js.map +1 -1
- package/dist/protect.cjs +116 -14
- package/dist/protect.cjs.map +1 -1
- package/dist/protect.js +1 -1
- package/dist/{refresh-manifest-3QGLYXV5.js → refresh-manifest-K3RT4XNH.js} +98 -10
- package/dist/refresh-manifest-K3RT4XNH.js.map +1 -0
- package/package.json +1 -1
- package/dist/refresh-manifest-3QGLYXV5.js.map +0 -1
package/dist/protect.cjs
CHANGED
|
@@ -158,6 +158,46 @@ var init_types = __esm({
|
|
|
158
158
|
}
|
|
159
159
|
code;
|
|
160
160
|
cause;
|
|
161
|
+
/**
|
|
162
|
+
* For a `VALIDATION_ERROR`, the request fields the server refused, as it named them. Read from the
|
|
163
|
+
* structured `errors` object a validation response carries, so a caller deciding what to do about a
|
|
164
|
+
* refusal keys on the field rather than on the wording of a sentence.
|
|
165
|
+
*/
|
|
166
|
+
fields = [];
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// src/hosting.ts
|
|
172
|
+
function detectHostingPlatform(env = process.env) {
|
|
173
|
+
for (const rule of RULES) {
|
|
174
|
+
const evidence = rule.any.filter((name) => present(env, name));
|
|
175
|
+
if (evidence.length > 0) {
|
|
176
|
+
return { platform: rule.platform, evidence };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return { platform: null, evidence: [] };
|
|
180
|
+
}
|
|
181
|
+
var RULES, present;
|
|
182
|
+
var init_hosting = __esm({
|
|
183
|
+
"src/hosting.ts"() {
|
|
184
|
+
"use strict";
|
|
185
|
+
RULES = [
|
|
186
|
+
{ platform: "netlify", any: ["NETLIFY", "NETLIFY_BUILD_BASE", "DEPLOY_PRIME_URL"] },
|
|
187
|
+
{ platform: "vercel", any: ["VERCEL", "VERCEL_ENV", "VERCEL_URL"] },
|
|
188
|
+
{ platform: "cloudflare", any: ["CF_PAGES", "CF_PAGES_URL", "CLOUDFLARE_ACCOUNT_ID"] },
|
|
189
|
+
{ platform: "aws", any: ["AWS_APP_ID", "AWS_LAMBDA_FUNCTION_NAME", "AWS_EXECUTION_ENV"] },
|
|
190
|
+
{ platform: "render", any: ["RENDER", "RENDER_SERVICE_ID", "RENDER_EXTERNAL_URL"] },
|
|
191
|
+
{ platform: "railway", any: ["RAILWAY_ENVIRONMENT", "RAILWAY_ENVIRONMENT_NAME", "RAILWAY_PROJECT_ID"] },
|
|
192
|
+
{ platform: "fly", any: ["FLY_APP_NAME", "FLY_REGION", "FLY_ALLOC_ID"] },
|
|
193
|
+
{ platform: "heroku", any: ["DYNO", "HEROKU_APP_NAME"] },
|
|
194
|
+
{ platform: "google-cloud", any: ["K_SERVICE", "GAE_APPLICATION", "GAE_SERVICE"] },
|
|
195
|
+
{ platform: "deno-deploy", any: ["DENO_DEPLOYMENT_ID"] },
|
|
196
|
+
{ platform: "azure", any: ["WEBSITE_SITE_NAME", "WEBSITE_INSTANCE_ID"] }
|
|
197
|
+
];
|
|
198
|
+
present = (env, name) => {
|
|
199
|
+
const value = env[name];
|
|
200
|
+
return value !== void 0 && value !== "";
|
|
161
201
|
};
|
|
162
202
|
}
|
|
163
203
|
});
|
|
@@ -183,14 +223,22 @@ function authFailureMessage(status, config) {
|
|
|
183
223
|
}
|
|
184
224
|
return null;
|
|
185
225
|
}
|
|
186
|
-
function buildManifestBody(config, payload) {
|
|
226
|
+
function buildManifestBody(config, payload, env = process.env) {
|
|
227
|
+
const hosting = config.environment === "local" ? null : detectHostingPlatform(env);
|
|
187
228
|
return {
|
|
188
229
|
...payload,
|
|
189
230
|
environment: config.environment,
|
|
190
231
|
...typeof config.siteUrl === "string" && config.siteUrl !== "" ? { url: config.siteUrl } : {},
|
|
191
|
-
...typeof config.siteName === "string" && config.siteName !== "" ? { name: config.siteName } : {}
|
|
232
|
+
...typeof config.siteName === "string" && config.siteName !== "" ? { name: config.siteName } : {},
|
|
233
|
+
...hosting !== null && hosting.platform !== null ? { hosting } : {}
|
|
192
234
|
};
|
|
193
235
|
}
|
|
236
|
+
function validationFields(body) {
|
|
237
|
+
if (typeof body !== "object" || body === null) return [];
|
|
238
|
+
const errors = body.errors;
|
|
239
|
+
if (typeof errors !== "object" || errors === null || Array.isArray(errors)) return [];
|
|
240
|
+
return Object.keys(errors);
|
|
241
|
+
}
|
|
194
242
|
async function postManifest(config, payload) {
|
|
195
243
|
const url = buildEndpointUrl(config.endpoint, config.siteUuid);
|
|
196
244
|
const timeoutMs = config.timeoutMs;
|
|
@@ -235,10 +283,12 @@ async function postManifest(config, payload) {
|
|
|
235
283
|
);
|
|
236
284
|
}
|
|
237
285
|
if (response.status === 422) {
|
|
238
|
-
|
|
286
|
+
const refused2 = new PatchstackError(
|
|
239
287
|
body?.message ?? "Patchstack rejected the manifest payload (validation failed).",
|
|
240
288
|
"VALIDATION_ERROR"
|
|
241
289
|
);
|
|
290
|
+
refused2.fields = validationFields(body);
|
|
291
|
+
throw refused2;
|
|
242
292
|
}
|
|
243
293
|
const refused = authFailureMessage(response.status, config);
|
|
244
294
|
if (refused !== null) {
|
|
@@ -266,6 +316,7 @@ var init_client = __esm({
|
|
|
266
316
|
"src/client.ts"() {
|
|
267
317
|
"use strict";
|
|
268
318
|
init_types();
|
|
319
|
+
init_hosting();
|
|
269
320
|
init_pulse_token();
|
|
270
321
|
init_build_id();
|
|
271
322
|
DEFAULT_ENDPOINT = "https://api.patchstack.com/monitor/pulse/manifest";
|
|
@@ -274,6 +325,54 @@ var init_client = __esm({
|
|
|
274
325
|
}
|
|
275
326
|
});
|
|
276
327
|
|
|
328
|
+
// src/environment.ts
|
|
329
|
+
function inferEnvironment(env = process.env) {
|
|
330
|
+
for (const discriminator of DISCRIMINATORS) {
|
|
331
|
+
const verdict = discriminator.read(env);
|
|
332
|
+
if (verdict !== null) {
|
|
333
|
+
return { environment: verdict.environment, evidence: [`${discriminator.platform}: ${verdict.evidence}`] };
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return { environment: "local", evidence: [] };
|
|
337
|
+
}
|
|
338
|
+
var set, DISCRIMINATORS;
|
|
339
|
+
var init_environment = __esm({
|
|
340
|
+
"src/environment.ts"() {
|
|
341
|
+
"use strict";
|
|
342
|
+
set = (value) => value !== void 0 && value !== "";
|
|
343
|
+
DISCRIMINATORS = [
|
|
344
|
+
{
|
|
345
|
+
platform: "vercel",
|
|
346
|
+
read: (env) => {
|
|
347
|
+
if (!set(env.VERCEL) || !set(env.VERCEL_ENV)) return null;
|
|
348
|
+
return env.VERCEL_ENV === "production" ? { environment: "production", evidence: "VERCEL_ENV=production" } : { environment: "sandbox", evidence: `VERCEL_ENV=${env.VERCEL_ENV}` };
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
platform: "netlify",
|
|
353
|
+
read: (env) => {
|
|
354
|
+
if (env.NETLIFY !== "true" || !set(env.CONTEXT)) return null;
|
|
355
|
+
return env.CONTEXT === "production" ? { environment: "production", evidence: "CONTEXT=production" } : { environment: "sandbox", evidence: `CONTEXT=${env.CONTEXT}` };
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
platform: "render",
|
|
360
|
+
read: (env) => {
|
|
361
|
+
if (env.RENDER !== "true") return null;
|
|
362
|
+
return env.IS_PULL_REQUEST === "true" ? { environment: "sandbox", evidence: "IS_PULL_REQUEST=true" } : { environment: "production", evidence: "RENDER=true, not a pull request" };
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
platform: "railway",
|
|
367
|
+
read: (env) => {
|
|
368
|
+
if (!set(env.RAILWAY_ENVIRONMENT_NAME)) return null;
|
|
369
|
+
return env.RAILWAY_ENVIRONMENT_NAME === "production" ? { environment: "production", evidence: "RAILWAY_ENVIRONMENT_NAME=production" } : { environment: "sandbox", evidence: `RAILWAY_ENVIRONMENT_NAME=${env.RAILWAY_ENVIRONMENT_NAME}` };
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
];
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
|
|
277
376
|
// src/site-url.ts
|
|
278
377
|
function isUnpublishableHost(hostname) {
|
|
279
378
|
const host = hostname.toLowerCase().replace(/\.+$/, "");
|
|
@@ -485,11 +584,13 @@ async function resolveConfig(options) {
|
|
|
485
584
|
const environmentRaw = fromEnv.environment ?? fromFile.environment;
|
|
486
585
|
if (environmentRaw !== void 0 && !isEnvironment(environmentRaw)) {
|
|
487
586
|
throw new PatchstackError(
|
|
488
|
-
`Environment must be "production" or "
|
|
587
|
+
`Environment must be "production", "sandbox" or "local"; got "${environmentRaw}".`,
|
|
489
588
|
"CONFIG_INVALID"
|
|
490
589
|
);
|
|
491
590
|
}
|
|
492
|
-
const
|
|
591
|
+
const inferred = environmentRaw === void 0 ? inferEnvironment(process.env) : null;
|
|
592
|
+
const environment = environmentRaw ?? inferred.environment;
|
|
593
|
+
const environmentEvidence = inferred?.evidence ?? [];
|
|
493
594
|
if (siteUuid !== null && siteUuid.length > 0 && !isUuid(siteUuid)) {
|
|
494
595
|
throw new PatchstackError(
|
|
495
596
|
`Site UUID "${siteUuid}" does not look like a valid UUID.`,
|
|
@@ -515,6 +616,7 @@ async function resolveConfig(options) {
|
|
|
515
616
|
endpoint,
|
|
516
617
|
timeoutMs,
|
|
517
618
|
environment,
|
|
619
|
+
environmentEvidence,
|
|
518
620
|
widget: fromFile.widget !== false,
|
|
519
621
|
claimToken
|
|
520
622
|
};
|
|
@@ -580,9 +682,9 @@ function isUuid(value) {
|
|
|
580
682
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
581
683
|
}
|
|
582
684
|
function isEnvironment(value) {
|
|
583
|
-
return value === "production" || value === "sandbox";
|
|
685
|
+
return value === "production" || value === "sandbox" || value === "local";
|
|
584
686
|
}
|
|
585
|
-
var import_promises2, import_node_path2, CONFIG_FILENAME, SECRET_FILENAME
|
|
687
|
+
var import_promises2, import_node_path2, CONFIG_FILENAME, SECRET_FILENAME;
|
|
586
688
|
var init_config = __esm({
|
|
587
689
|
"src/config.ts"() {
|
|
588
690
|
"use strict";
|
|
@@ -590,11 +692,11 @@ var init_config = __esm({
|
|
|
590
692
|
import_node_path2 = __toESM(require("path"), 1);
|
|
591
693
|
init_types();
|
|
592
694
|
init_client();
|
|
695
|
+
init_environment();
|
|
593
696
|
init_site_url();
|
|
594
697
|
init_site_name();
|
|
595
698
|
CONFIG_FILENAME = ".patchstackrc.json";
|
|
596
699
|
SECRET_FILENAME = ".patchstackrc.local.json";
|
|
597
|
-
DEFAULT_ENVIRONMENT = "production";
|
|
598
700
|
}
|
|
599
701
|
});
|
|
600
702
|
|
|
@@ -631,8 +733,8 @@ async function readDeclaredDependencyNames(cwd) {
|
|
|
631
733
|
return [...names];
|
|
632
734
|
}
|
|
633
735
|
function missingDependencies(declared, packages) {
|
|
634
|
-
const
|
|
635
|
-
return declared.filter((name) => !
|
|
736
|
+
const present2 = new Set(packages.map((entry) => entry.name));
|
|
737
|
+
return declared.filter((name) => !present2.has(name));
|
|
636
738
|
}
|
|
637
739
|
function disagreements(packages, other) {
|
|
638
740
|
const otherVersions = /* @__PURE__ */ new Map();
|
|
@@ -6985,8 +7087,8 @@ async function createProtection(options = {}) {
|
|
|
6985
7087
|
hardened = true;
|
|
6986
7088
|
try {
|
|
6987
7089
|
if (res.headersSent || typeof res.setHeader !== "function") return null;
|
|
6988
|
-
const
|
|
6989
|
-
const headers = effective && effective.headers ? { ...
|
|
7090
|
+
const set2 = typeof res.getHeaders === "function" ? res.getHeaders() : {};
|
|
7091
|
+
const headers = effective && effective.headers ? { ...set2, ...effective.headers } : set2;
|
|
6990
7092
|
const status = effective && effective.status !== void 0 ? effective.status : res.statusCode;
|
|
6991
7093
|
const r = screenText("", { status, headers }, reqCtx, hardensWithoutBody);
|
|
6992
7094
|
answeredWithoutBody = true;
|
|
@@ -7698,8 +7800,8 @@ function applyHeaderMutation(headers, rule) {
|
|
|
7698
7800
|
const ensure = rule.ensure === true;
|
|
7699
7801
|
for (const [name, value] of Object.entries(rule.set_headers ?? {})) {
|
|
7700
7802
|
const key = String(name).toLowerCase();
|
|
7701
|
-
const
|
|
7702
|
-
if (ensure &&
|
|
7803
|
+
const present2 = headers[key] != null && headers[key] !== "";
|
|
7804
|
+
if (ensure && present2) continue;
|
|
7703
7805
|
headers[key] = String(value);
|
|
7704
7806
|
}
|
|
7705
7807
|
return;
|