@patchstack/connect 0.5.7 → 0.5.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/dist/protect.cjs CHANGED
@@ -369,8 +369,8 @@ var init_hosting = __esm({
369
369
  RULES = [
370
370
  { platform: "netlify", any: ["NETLIFY", "NETLIFY_BUILD_BASE", "DEPLOY_PRIME_URL"] },
371
371
  { platform: "vercel", any: ["VERCEL", "VERCEL_ENV", "VERCEL_URL"] },
372
- { platform: "cloudflare", any: ["CF_PAGES", "CF_PAGES_URL", "CLOUDFLARE_ACCOUNT_ID"] },
373
- { platform: "aws", any: ["AWS_APP_ID", "AWS_LAMBDA_FUNCTION_NAME", "AWS_EXECUTION_ENV"] },
372
+ { platform: "cloudflare", any: ["CF_PAGES", "CF_PAGES_URL", "WORKERS_CI", "WORKERS_CI_BRANCH", "CLOUDFLARE_ACCOUNT_ID"] },
373
+ { platform: "aws", any: ["AWS_APP_ID", "AWS_BRANCH", "AWS_LAMBDA_FUNCTION_NAME", "AWS_EXECUTION_ENV"] },
374
374
  { platform: "render", any: ["RENDER", "RENDER_SERVICE_ID", "RENDER_EXTERNAL_URL"] },
375
375
  { platform: "railway", any: ["RAILWAY_ENVIRONMENT", "RAILWAY_ENVIRONMENT_NAME", "RAILWAY_PROJECT_ID"] },
376
376
  { platform: "fly", any: ["FLY_APP_NAME", "FLY_REGION", "FLY_ALLOC_ID"] },
@@ -407,11 +407,18 @@ function authFailureMessage(status, config) {
407
407
  }
408
408
  return null;
409
409
  }
410
- function buildManifestBody(config, payload, env = process.env) {
410
+ function buildManifestBody(config, payload, env = process.env, marker = null) {
411
411
  const hosting = config.environment === "local" ? null : detectHostingPlatform(env);
412
412
  return {
413
413
  ...payload,
414
414
  environment: config.environment,
415
+ // What the label rests on, sent with the label itself: a `production` claim decided by an
416
+ // assumption about the project is worth less than one the platform declared, and only the
417
+ // report can say which this was.
418
+ ...config.environmentSource != null ? { environment_source: config.environmentSource } : {},
419
+ // Present only on the report from a finished build, which is also what tells the two reports
420
+ // of one build apart.
421
+ ...marker !== null ? { marker } : {},
415
422
  ...typeof config.siteUrl === "string" && config.siteUrl !== "" ? { url: config.siteUrl } : {},
416
423
  ...typeof config.siteName === "string" && config.siteName !== "" ? { name: config.siteName } : {},
417
424
  ...hosting !== null && hosting.platform !== null ? { hosting } : {}
@@ -459,7 +466,7 @@ function manifestResponse(body, provisioning) {
459
466
  };
460
467
  return result;
461
468
  }
462
- async function postManifest(config, payload) {
469
+ async function postManifest(config, payload, marker = null) {
463
470
  const url = buildEndpointUrl(config.endpoint, config.siteUuid);
464
471
  const timeoutMs = config.timeoutMs;
465
472
  assertConnectableEndpoint(config, url);
@@ -473,7 +480,7 @@ async function postManifest(config, payload) {
473
480
  "User-Agent": "@patchstack/connect",
474
481
  ...claimTokenHeader(config)
475
482
  },
476
- body: JSON.stringify(buildManifestBody(config, payload)),
483
+ body: JSON.stringify(buildManifestBody(config, payload, process.env, marker)),
477
484
  signal: AbortSignal.timeout(timeoutMs)
478
485
  });
479
486
  } catch (cause) {
@@ -551,6 +558,9 @@ var init_client = __esm({
551
558
  });
552
559
 
553
560
  // src/environment.ts
561
+ function environmentFromBranch(variable, branch) {
562
+ return PRODUCTION_BRANCHES.has(branch.toLowerCase()) ? { environment: "production", evidence: `${variable}=${branch} (a production branch by name)` } : { environment: "sandbox", evidence: `${variable}=${branch} (not a production branch by name)` };
563
+ }
554
564
  function detectHostedBuilder(cwd) {
555
565
  let declared;
556
566
  try {
@@ -572,24 +582,37 @@ function inferEnvironment(env = process.env, builder = null) {
572
582
  for (const discriminator of DISCRIMINATORS) {
573
583
  const verdict = discriminator.read(env);
574
584
  if (verdict !== null) {
575
- return { environment: verdict.environment, evidence: [`${discriminator.platform}: ${verdict.evidence}`] };
585
+ return {
586
+ environment: verdict.environment,
587
+ evidence: [`${discriminator.platform}: ${verdict.evidence}`],
588
+ source: "platform"
589
+ };
576
590
  }
577
591
  }
578
592
  if (builder !== null) {
579
593
  return {
580
594
  environment: "production",
581
- evidence: [`${builder}: a build in a ${builder} project is its publish step`]
595
+ evidence: [`${builder}: a build in a ${builder} project is its publish step`],
596
+ source: "builder"
582
597
  };
583
598
  }
584
- return { environment: "local", evidence: [] };
599
+ return { environment: "local", evidence: [], source: null };
585
600
  }
586
- var import_node_fs, import_node_path, set, DISCRIMINATORS, HOSTED_BUILDER_PACKAGES;
601
+ var import_node_fs, import_node_path, set, PRODUCTION_BRANCHES, DISCRIMINATORS, HOSTED_BUILDER_PACKAGES;
587
602
  var init_environment = __esm({
588
603
  "src/environment.ts"() {
589
604
  "use strict";
590
605
  import_node_fs = require("fs");
591
606
  import_node_path = __toESM(require("path"), 1);
592
607
  set = (value) => value !== void 0 && value !== "";
608
+ PRODUCTION_BRANCHES = /* @__PURE__ */ new Set([
609
+ "main",
610
+ "master",
611
+ "production",
612
+ "prod",
613
+ "release",
614
+ "live"
615
+ ]);
593
616
  DISCRIMINATORS = [
594
617
  {
595
618
  platform: "vercel",
@@ -618,6 +641,79 @@ var init_environment = __esm({
618
641
  if (!set(env.RAILWAY_ENVIRONMENT_NAME)) return null;
619
642
  return env.RAILWAY_ENVIRONMENT_NAME === "production" ? { environment: "production", evidence: "RAILWAY_ENVIRONMENT_NAME=production" } : { environment: "sandbox", evidence: `RAILWAY_ENVIRONMENT_NAME=${env.RAILWAY_ENVIRONMENT_NAME}` };
620
643
  }
644
+ },
645
+ {
646
+ // Pages and Workers Builds both name the branch and neither names the tier.
647
+ platform: "cloudflare",
648
+ read: (env) => {
649
+ if (set(env.CF_PAGES) && set(env.CF_PAGES_BRANCH)) {
650
+ return environmentFromBranch("CF_PAGES_BRANCH", env.CF_PAGES_BRANCH);
651
+ }
652
+ if (set(env.WORKERS_CI) && set(env.WORKERS_CI_BRANCH)) {
653
+ return environmentFromBranch("WORKERS_CI_BRANCH", env.WORKERS_CI_BRANCH);
654
+ }
655
+ return null;
656
+ }
657
+ },
658
+ {
659
+ // Amplify names the branch, and separately whether the build is a pull-request preview.
660
+ platform: "aws",
661
+ read: (env) => {
662
+ if (!set(env.AWS_APP_ID) || !set(env.AWS_BRANCH)) return null;
663
+ return set(env.AWS_PULL_REQUEST_ID) ? { environment: "sandbox", evidence: "AWS_PULL_REQUEST_ID set (a pull-request preview)" } : environmentFromBranch("AWS_BRANCH", env.AWS_BRANCH);
664
+ }
665
+ },
666
+ {
667
+ // A Deployment build carries `REPLIT_DEPLOYMENT`; the workspace carries `REPL_ID` alone. Read here,
668
+ // before the hosted-builder rule, so a workspace build in a Replit project is not its publish step.
669
+ platform: "replit",
670
+ read: (env) => {
671
+ if (set(env.REPLIT_DEPLOYMENT)) {
672
+ return { environment: "production", evidence: "REPLIT_DEPLOYMENT set (a Replit Deployment build)" };
673
+ }
674
+ if (set(env.REPL_ID)) {
675
+ return { environment: "sandbox", evidence: "REPL_ID set without REPLIT_DEPLOYMENT (the workspace)" };
676
+ }
677
+ return null;
678
+ }
679
+ },
680
+ {
681
+ // GitHub Pages deploys build here. A pull request checks out a merge ref whose name says nothing, so
682
+ // the event is read first; a tag is a release; otherwise the branch name decides.
683
+ platform: "github-actions",
684
+ read: (env) => {
685
+ if (env.GITHUB_ACTIONS !== "true") return null;
686
+ if (set(env.GITHUB_EVENT_NAME) && env.GITHUB_EVENT_NAME.startsWith("pull_request")) {
687
+ return { environment: "sandbox", evidence: `GITHUB_EVENT_NAME=${env.GITHUB_EVENT_NAME} (a pull-request build)` };
688
+ }
689
+ if (env.GITHUB_REF_TYPE === "tag") {
690
+ return { environment: "production", evidence: "GITHUB_REF_TYPE=tag (a tag build)" };
691
+ }
692
+ if (!set(env.GITHUB_REF_NAME)) return null;
693
+ return environmentFromBranch("GITHUB_REF_NAME", env.GITHUB_REF_NAME);
694
+ }
695
+ },
696
+ {
697
+ // The tier, when a job declares one, is GitLab's own word for it. Without one: a merge request is a
698
+ // check, a tag is a release, the default branch goes live, and any other branch name decides.
699
+ platform: "gitlab-ci",
700
+ read: (env) => {
701
+ if (env.GITLAB_CI !== "true") return null;
702
+ if (set(env.CI_ENVIRONMENT_TIER)) {
703
+ return env.CI_ENVIRONMENT_TIER === "production" ? { environment: "production", evidence: "CI_ENVIRONMENT_TIER=production" } : { environment: "sandbox", evidence: `CI_ENVIRONMENT_TIER=${env.CI_ENVIRONMENT_TIER}` };
704
+ }
705
+ if (set(env.CI_MERGE_REQUEST_IID)) {
706
+ return { environment: "sandbox", evidence: "CI_MERGE_REQUEST_IID set (a merge-request build)" };
707
+ }
708
+ if (set(env.CI_COMMIT_TAG)) {
709
+ return { environment: "production", evidence: "CI_COMMIT_TAG set (a tag build)" };
710
+ }
711
+ if (!set(env.CI_COMMIT_BRANCH)) return null;
712
+ if (set(env.CI_DEFAULT_BRANCH) && env.CI_COMMIT_BRANCH === env.CI_DEFAULT_BRANCH) {
713
+ return { environment: "production", evidence: `CI_COMMIT_BRANCH=${env.CI_COMMIT_BRANCH} (the default branch)` };
714
+ }
715
+ return environmentFromBranch("CI_COMMIT_BRANCH", env.CI_COMMIT_BRANCH);
716
+ }
621
717
  }
622
718
  ];
623
719
  HOSTED_BUILDER_PACKAGES = [
@@ -855,6 +951,7 @@ async function resolveConfig(options) {
855
951
  const inferred = environmentRaw === void 0 ? inferEnvironment(process.env, detectHostedBuilder(options.cwd)) : null;
856
952
  const environment = environmentRaw ?? inferred.environment;
857
953
  const environmentEvidence = inferred?.evidence ?? [];
954
+ const environmentSource = environmentRaw !== void 0 ? "override" : inferred.source;
858
955
  if (siteUuid !== null && siteUuid.length > 0 && !isCanonicalUuid(siteUuid)) {
859
956
  throw new PatchstackError(
860
957
  `Site UUID "${siteUuid}" does not look like a valid UUID.`,
@@ -882,6 +979,7 @@ async function resolveConfig(options) {
882
979
  timeoutMs,
883
980
  environment,
884
981
  environmentEvidence,
982
+ environmentSource,
885
983
  widget: fromFile.widget !== false,
886
984
  claimToken
887
985
  };
@@ -4974,6 +5072,7 @@ var DEFAULT_BASE_URL2 = "https://api.patchstack.com/monitor/pulse";
4974
5072
  var DEFAULT_CACHE_TTL2 = 3e5;
4975
5073
  var BUILD_VERDICT_HEADER = "X-Patchstack-Build-Match";
4976
5074
  var BUILD_IDENTITY_HEADER = "X-Patchstack-Build-ID";
5075
+ var PER_RULE_DRY_RUN_HEADER = "X-Patchstack-Per-Rule-Dry-Run";
4977
5076
  var JITTER_FRACTION2 = 0.1;
4978
5077
  var PulseRuleClient = class {
4979
5078
  #siteUuid;
@@ -5018,6 +5117,9 @@ var PulseRuleClient = class {
5018
5117
  if (this.#buildId !== null && typeof auth.Authorization === "string") {
5019
5118
  headers["X-Patchstack-Build"] = this.#buildId;
5020
5119
  }
5120
+ if (typeof auth.Authorization === "string") {
5121
+ headers[PER_RULE_DRY_RUN_HEADER] = "1";
5122
+ }
5021
5123
  if (this.#etag) headers["If-None-Match"] = this.#etag;
5022
5124
  const response = await fetch(url, { method: "GET", headers, signal: AbortSignal.timeout(this.#timeoutMs) });
5023
5125
  if (response.status === 304) {