@postman-cse/onboarding-repo-sync 2.1.13 → 2.1.15

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/action.cjs CHANGED
@@ -26569,7 +26569,7 @@ var require_util7 = __commonJS({
26569
26569
  exports2.esc = esc;
26570
26570
  exports2.slugify = slugify;
26571
26571
  exports2.isObject = isObject;
26572
- exports2.isPlainObject = isPlainObject2;
26572
+ exports2.isPlainObject = isPlainObject3;
26573
26573
  exports2.shallowClone = shallowClone;
26574
26574
  exports2.numKeys = numKeys;
26575
26575
  exports2.escapeRegex = escapeRegex;
@@ -26752,7 +26752,7 @@ var require_util7 = __commonJS({
26752
26752
  return false;
26753
26753
  }
26754
26754
  });
26755
- function isPlainObject2(o) {
26755
+ function isPlainObject3(o) {
26756
26756
  if (isObject(o) === false)
26757
26757
  return false;
26758
26758
  const ctor = o.constructor;
@@ -26769,7 +26769,7 @@ var require_util7 = __commonJS({
26769
26769
  return true;
26770
26770
  }
26771
26771
  function shallowClone(o) {
26772
- if (isPlainObject2(o))
26772
+ if (isPlainObject3(o))
26773
26773
  return { ...o };
26774
26774
  if (Array.isArray(o))
26775
26775
  return [...o];
@@ -26974,7 +26974,7 @@ var require_util7 = __commonJS({
26974
26974
  return clone(schema, def);
26975
26975
  }
26976
26976
  function extend(schema, shape) {
26977
- if (!isPlainObject2(shape)) {
26977
+ if (!isPlainObject3(shape)) {
26978
26978
  throw new Error("Invalid input to extend: expected a plain object");
26979
26979
  }
26980
26980
  const checks = schema._zod.def.checks;
@@ -26997,7 +26997,7 @@ var require_util7 = __commonJS({
26997
26997
  return clone(schema, def);
26998
26998
  }
26999
26999
  function safeExtend(schema, shape) {
27000
- if (!isPlainObject2(shape)) {
27000
+ if (!isPlainObject3(shape)) {
27001
27001
  throw new Error("Invalid input to safeExtend: expected a plain object");
27002
27002
  }
27003
27003
  const def = mergeDefs(schema._zod.def, {
@@ -54816,7 +54816,7 @@ var require_lodash = __commonJS({
54816
54816
  } else {
54817
54817
  newValue = [];
54818
54818
  }
54819
- } else if (isPlainObject2(srcValue) || isArguments(srcValue)) {
54819
+ } else if (isPlainObject3(srcValue) || isArguments(srcValue)) {
54820
54820
  newValue = objValue;
54821
54821
  if (isArguments(objValue)) {
54822
54822
  newValue = toPlainObject(objValue);
@@ -55780,7 +55780,7 @@ var require_lodash = __commonJS({
55780
55780
  return objValue;
55781
55781
  }
55782
55782
  function customOmitClone(value) {
55783
- return isPlainObject2(value) ? undefined2 : value;
55783
+ return isPlainObject3(value) ? undefined2 : value;
55784
55784
  }
55785
55785
  function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
55786
55786
  var isPartial = bitmask & COMPARE_PARTIAL_FLAG, arrLength = array.length, othLength = other.length;
@@ -57281,7 +57281,7 @@ var require_lodash = __commonJS({
57281
57281
  var isBuffer = nativeIsBuffer || stubFalse;
57282
57282
  var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate;
57283
57283
  function isElement(value) {
57284
- return isObjectLike(value) && value.nodeType === 1 && !isPlainObject2(value);
57284
+ return isObjectLike(value) && value.nodeType === 1 && !isPlainObject3(value);
57285
57285
  }
57286
57286
  function isEmpty(value) {
57287
57287
  if (value == null) {
@@ -57317,7 +57317,7 @@ var require_lodash = __commonJS({
57317
57317
  return false;
57318
57318
  }
57319
57319
  var tag = baseGetTag(value);
57320
- return tag == errorTag || tag == domExcTag || typeof value.message == "string" && typeof value.name == "string" && !isPlainObject2(value);
57320
+ return tag == errorTag || tag == domExcTag || typeof value.message == "string" && typeof value.name == "string" && !isPlainObject3(value);
57321
57321
  }
57322
57322
  function isFinite2(value) {
57323
57323
  return typeof value == "number" && nativeIsFinite(value);
@@ -57368,7 +57368,7 @@ var require_lodash = __commonJS({
57368
57368
  function isNumber(value) {
57369
57369
  return typeof value == "number" || isObjectLike(value) && baseGetTag(value) == numberTag;
57370
57370
  }
57371
- function isPlainObject2(value) {
57371
+ function isPlainObject3(value) {
57372
57372
  if (!isObjectLike(value) || baseGetTag(value) != objectTag) {
57373
57373
  return false;
57374
57374
  }
@@ -58439,7 +58439,7 @@ var require_lodash = __commonJS({
58439
58439
  lodash.isNumber = isNumber;
58440
58440
  lodash.isObject = isObject;
58441
58441
  lodash.isObjectLike = isObjectLike;
58442
- lodash.isPlainObject = isPlainObject2;
58442
+ lodash.isPlainObject = isPlainObject3;
58443
58443
  lodash.isRegExp = isRegExp;
58444
58444
  lodash.isSafeInteger = isSafeInteger;
58445
58445
  lodash.isSet = isSet;
@@ -131082,7 +131082,8 @@ function getIDToken(aud) {
131082
131082
  }
131083
131083
 
131084
131084
  // src/index.ts
131085
- var import_node_fs4 = require("node:fs");
131085
+ var import_node_crypto4 = require("node:crypto");
131086
+ var import_node_fs5 = require("node:fs");
131086
131087
  var path8 = __toESM(require("node:path"), 1);
131087
131088
 
131088
131089
  // node_modules/js-yaml/dist/js-yaml.mjs
@@ -133998,6 +133999,7 @@ function dump(input, options = {}) {
133998
133999
  }
133999
134000
 
134000
134001
  // src/postman-v3/converter.ts
134002
+ var import_node_fs = require("node:fs");
134001
134003
  var fs3 = __toESM(require("node:fs/promises"), 1);
134002
134004
  var path6 = __toESM(require("node:path"), 1);
134003
134005
  var V2 = __toESM(require_v2(), 1);
@@ -134068,6 +134070,72 @@ function structuredCloneSafe(value) {
134068
134070
  if (typeof structuredClone === "function") return structuredClone(value);
134069
134071
  return JSON.parse(JSON.stringify(value));
134070
134072
  }
134073
+ var ArtifactDigestStreamError = class extends Error {
134074
+ code = "ARTIFACT_DIGEST_STREAM";
134075
+ constructor(message, options) {
134076
+ super(message, options);
134077
+ this.name = "ArtifactDigestStreamError";
134078
+ }
134079
+ };
134080
+ function sameFileIdentity(left, right) {
134081
+ return left.dev === right.dev && left.ino === right.ino && left.size === right.size;
134082
+ }
134083
+ async function appendArtifactDigestFileStreaming(hash, relative4, absolute, expected) {
134084
+ const flags = typeof import_node_fs.constants.O_NOFOLLOW === "number" ? import_node_fs.constants.O_RDONLY | import_node_fs.constants.O_NOFOLLOW : import_node_fs.constants.O_RDONLY;
134085
+ let handle;
134086
+ try {
134087
+ handle = await fs3.open(absolute, flags);
134088
+ } catch (error2) {
134089
+ const code = error2?.code;
134090
+ if (code === "ELOOP" || code === "EMLINK" || code === "EINVAL") {
134091
+ throw new ArtifactDigestStreamError(
134092
+ `prebuilt collection tree file became a symlink or unsupported node at ${relative4}`,
134093
+ { cause: error2 }
134094
+ );
134095
+ }
134096
+ throw new ArtifactDigestStreamError(
134097
+ `prebuilt collection tree file changed or became unsupported at ${relative4}`,
134098
+ { cause: error2 }
134099
+ );
134100
+ }
134101
+ try {
134102
+ const before = await handle.stat();
134103
+ if (!before.isFile() || !sameFileIdentity(before, expected)) {
134104
+ throw new ArtifactDigestStreamError(
134105
+ `prebuilt collection tree file changed or became unsupported at ${relative4}`
134106
+ );
134107
+ }
134108
+ hash.update(relative4);
134109
+ hash.update("\0");
134110
+ const content = Buffer.allocUnsafe(expected.size);
134111
+ let total = 0;
134112
+ const buffer = Buffer.alloc(64 * 1024);
134113
+ for (; ; ) {
134114
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
134115
+ if (bytesRead === 0) {
134116
+ break;
134117
+ }
134118
+ if (total + bytesRead > expected.size) {
134119
+ throw new ArtifactDigestStreamError(
134120
+ `prebuilt collection tree file changed while reading at ${relative4}`
134121
+ );
134122
+ }
134123
+ buffer.copy(content, total, 0, bytesRead);
134124
+ hash.update(buffer.subarray(0, bytesRead));
134125
+ total += bytesRead;
134126
+ }
134127
+ hash.update("\0");
134128
+ const after = await handle.stat();
134129
+ if (!after.isFile() || !sameFileIdentity(after, expected) || total !== expected.size) {
134130
+ throw new ArtifactDigestStreamError(
134131
+ `prebuilt collection tree file changed while reading at ${relative4}`
134132
+ );
134133
+ }
134134
+ return total === expected.size ? content : content.subarray(0, total);
134135
+ } finally {
134136
+ await handle.close();
134137
+ }
134138
+ }
134071
134139
  async function listFilesRelative(dir, base) {
134072
134140
  let entries;
134073
134141
  try {
@@ -134628,7 +134696,7 @@ function getCiWorkflowTemplate(provider, options = {}) {
134628
134696
  }
134629
134697
 
134630
134698
  // src/lib/github/repo-mutation.ts
134631
- var import_node_fs = require("node:fs");
134699
+ var import_node_fs2 = require("node:fs");
134632
134700
  var import_node_path = __toESM(require("node:path"), 1);
134633
134701
 
134634
134702
  // src/lib/secrets.ts
@@ -134918,7 +134986,7 @@ var RepoMutationService = class {
134918
134986
  throw new Error(this.secretMasker(changed.stderr || changed.stdout || "Failed to inspect generated changes"));
134919
134987
  }
134920
134988
  const hasPlannedRemoval = removePaths.some(
134921
- (removePath) => (0, import_node_fs.existsSync)(import_node_path.default.resolve(this.cwd, removePath))
134989
+ (removePath) => (0, import_node_fs2.existsSync)(import_node_path.default.resolve(this.cwd, removePath))
134922
134990
  );
134923
134991
  if (!changed.stdout.trim() && !hasPlannedRemoval) {
134924
134992
  return {
@@ -134942,7 +135010,7 @@ var RepoMutationService = class {
134942
135010
  await this.execute("git", ["config", "user.name", options.committerName]);
134943
135011
  await this.execute("git", ["config", "user.email", options.committerEmail]);
134944
135012
  for (const removePath of removePaths) {
134945
- (0, import_node_fs.rmSync)(import_node_path.default.resolve(this.cwd, removePath), { force: true });
135013
+ (0, import_node_fs2.rmSync)(import_node_path.default.resolve(this.cwd, removePath), { force: true });
134946
135014
  }
134947
135015
  await this.execute("git", ["add", "-A", "--", ...stagePaths]);
134948
135016
  const staged = await this.execute("git", ["diff", "--cached", "--quiet"]);
@@ -135485,14 +135553,14 @@ function createTelemetryContext(options) {
135485
135553
  }
135486
135554
 
135487
135555
  // src/action-version.ts
135488
- var import_node_fs2 = require("node:fs");
135556
+ var import_node_fs3 = require("node:fs");
135489
135557
  var import_node_path2 = require("node:path");
135490
135558
  function resolveActionVersion2() {
135491
135559
  if (false) {
135492
135560
  return void 0;
135493
135561
  }
135494
135562
  try {
135495
- const raw = (0, import_node_fs2.readFileSync)((0, import_node_path2.join)(__dirname, "..", "package.json"), "utf8");
135563
+ const raw = (0, import_node_fs3.readFileSync)((0, import_node_path2.join)(__dirname, "..", "package.json"), "utf8");
135496
135564
  return JSON.parse(raw).version ?? "unknown";
135497
135565
  } catch {
135498
135566
  return "unknown";
@@ -135731,7 +135799,8 @@ async function resolveSessionIdentity(opts) {
135731
135799
  opts.fetchImpl ?? fetch,
135732
135800
  Math.max(1, opts.maxAttempts ?? SESSION_MAX_ATTEMPTS),
135733
135801
  opts.sleepImpl ?? defaultSessionSleep,
135734
- opts.randomImpl ?? defaultRandom
135802
+ opts.randomImpl ?? defaultRandom,
135803
+ opts.onRetryEvent
135735
135804
  );
135736
135805
  sessionMemo.set(memoKey, pending);
135737
135806
  }
@@ -135765,7 +135834,7 @@ async function parseSessionResponse(response) {
135765
135834
  consumerType: coerceText(root.consumerType) ?? coerceText(data?.consumerType) ?? coerceText(user?.consumerType)
135766
135835
  };
135767
135836
  }
135768
- async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts, sleepImpl, random) {
135837
+ async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts, sleepImpl, random, onRetryEvent) {
135769
135838
  let failure = "unavailable";
135770
135839
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
135771
135840
  let response;
@@ -135777,7 +135846,9 @@ async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts
135777
135846
  } catch {
135778
135847
  failure = "unavailable";
135779
135848
  if (attempt < maxAttempts) {
135780
- await sleepImpl(computeSessionRetryDelayMs(void 0, attempt, random));
135849
+ const delay = computeSessionRetryDelayMs(void 0, attempt, random);
135850
+ onRetryEvent?.({ class: "transport", attempt, delay });
135851
+ await sleepImpl(delay);
135781
135852
  continue;
135782
135853
  }
135783
135854
  break;
@@ -135799,7 +135870,9 @@ async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts
135799
135870
  if (response.status === 429 || response.status >= 500) {
135800
135871
  failure = "unavailable";
135801
135872
  if (attempt < maxAttempts) {
135802
- await sleepImpl(computeSessionRetryDelayMs(response, attempt, random));
135873
+ const delay = computeSessionRetryDelayMs(response, attempt, random);
135874
+ onRetryEvent?.({ class: "http", status: response.status, attempt, delay });
135875
+ await sleepImpl(delay);
135803
135876
  continue;
135804
135877
  }
135805
135878
  break;
@@ -135901,7 +135974,8 @@ async function runCredentialPreflight(args) {
135901
135974
  accessToken,
135902
135975
  fetchImpl: args.fetchImpl,
135903
135976
  ...args.sleepImpl ? { sleepImpl: args.sleepImpl } : {},
135904
- ...args.randomImpl ? { randomImpl: args.randomImpl } : {}
135977
+ ...args.randomImpl ? { randomImpl: args.randomImpl } : {},
135978
+ onRetryEvent: args.retryEvent ?? args.log.retryEvent
135905
135979
  });
135906
135980
  } catch (error2) {
135907
135981
  args.log.warning(
@@ -136553,6 +136627,11 @@ var postmanRepoSyncActionContract = {
136553
136627
  description: "Contract collection ID used for exported artifacts.",
136554
136628
  required: false
136555
136629
  },
136630
+ "prebuilt-collections-json": {
136631
+ description: "Optional digest-bound JSON manifest of unique baseline, smoke, or contract roles with confined repo-relative path, SHA-256 artifact digest of the on-disk v3 collection tree (sorted relative-path + NUL + bytes + NUL), and canonical cloud ID. The optional payloadDigest field is the semantic v2 payload digest carried for provenance (format-validated only, not the reuse gate). Exact role, path, cloudId, and artifactDigest matches reuse the on-disk tree without cloud export.",
136632
+ required: false,
136633
+ default: ""
136634
+ },
136556
136635
  "collection-sync-mode": {
136557
136636
  description: "Collection sync lifecycle mode (refresh or version).",
136558
136637
  required: false,
@@ -136795,7 +136874,7 @@ var postmanRepoSyncActionContract = {
136795
136874
  "Create or update Postman environments from runtime URLs.",
136796
136875
  "Associate Postman environments to system environments through Postman integration APIs.",
136797
136876
  "Create mock servers and smoke monitors from generated collections.",
136798
- "Export Postman collections in the Collection v3 multi-file YAML directory structure under `postman/collections/` (e.g., `<name>/collection.yaml`, nested folder and request YAML files), and export environments plus `.postman/resources.yaml` into the repository.",
136877
+ "Export Postman collections in the Collection v3 multi-file YAML directory structure under `postman/collections/` (canonical `.resources/definition.yaml` plus request/resource YAML files), and export environments plus `.postman/resources.yaml` into the repository.",
136799
136878
  "Link the Postman workspace to the repository (GitHub or GitLab) through Postman integration APIs.",
136800
136879
  "Commit synced artifacts and push them back to the current checked out ref."
136801
136880
  ],
@@ -136906,13 +136985,36 @@ var PostmanGatewayAssetsClient = class {
136906
136985
  name: String(entry.name ?? entry.message ?? "").trim()
136907
136986
  })).filter((entry) => entry.id || entry.name);
136908
136987
  }
136988
+ /**
136989
+ * Delete a collection (GC path). DELETE is idempotent: transient 408/429/5xx
136990
+ * (including Bifrost ESOCKETTIMEDOUT envelopes) are retried with the same
136991
+ * bounded budget as other idempotent writes; 404 is success (already gone).
136992
+ * Ordinary 4xx are not retried.
136993
+ */
136909
136994
  async deleteCollection(collectionUid) {
136910
136995
  const bareId = String(collectionUid).split("-").slice(-5).join("-") || collectionUid;
136911
- await this.gateway.requestJson({
136912
- service: "collection",
136913
- method: "delete",
136914
- path: `/v3/collections/${bareId}`
136915
- });
136996
+ try {
136997
+ await retry(
136998
+ () => this.gateway.requestJson({
136999
+ service: "collection",
137000
+ method: "delete",
137001
+ path: `/v3/collections/${bareId}`
137002
+ }),
137003
+ {
137004
+ maxAttempts: 5,
137005
+ delayMs: 2e3,
137006
+ backoffMultiplier: 2,
137007
+ maxDelayMs: 15e3,
137008
+ sleep: this.sleep,
137009
+ shouldRetry: (e) => this.isRetryableIdempotentWriteOutcome(e)
137010
+ }
137011
+ );
137012
+ } catch (error2) {
137013
+ if (error2 instanceof HttpError && error2.status === 404) {
137014
+ return;
137015
+ }
137016
+ throw error2;
137017
+ }
136916
137018
  }
136917
137019
  async listSpecifications(workspaceId) {
136918
137020
  const response = await this.gateway.requestJson({
@@ -137580,6 +137682,7 @@ var AccessTokenProvider = class {
137580
137682
  maxAttempts;
137581
137683
  onToken;
137582
137684
  sleep;
137685
+ onRetryEvent;
137583
137686
  inflight;
137584
137687
  constructor(options) {
137585
137688
  this.token = String(options.accessToken || "").trim();
@@ -137591,6 +137694,7 @@ var AccessTokenProvider = class {
137591
137694
  this.maxAttempts = Math.max(1, options.maxAttempts ?? 2);
137592
137695
  this.onToken = options.onToken;
137593
137696
  this.sleep = options.sleep;
137697
+ this.onRetryEvent = options.onRetryEvent;
137594
137698
  }
137595
137699
  current() {
137596
137700
  return this.token;
@@ -137616,7 +137720,12 @@ var AccessTokenProvider = class {
137616
137720
  delayMs: 1e3,
137617
137721
  backoffMultiplier: 2,
137618
137722
  ...this.sleep ? { sleep: this.sleep } : {},
137619
- shouldRetry: (error2) => !(error2 instanceof MintError && error2.permanent)
137723
+ shouldRetry: (error2) => !(error2 instanceof MintError && error2.permanent),
137724
+ onRetry: ({ attempt, delayMs, error: error2 }) => {
137725
+ const status = error2 instanceof MintError ? error2.status : void 0;
137726
+ const retryClass = status === void 0 ? "transport" : "http";
137727
+ this.onRetryEvent?.({ class: retryClass, ...status === void 0 ? {} : { status }, attempt, delay: Math.max(0, delayMs) });
137728
+ }
137620
137729
  });
137621
137730
  this.token = token;
137622
137731
  this.onToken?.(token);
@@ -137647,7 +137756,7 @@ var AccessTokenProvider = class {
137647
137756
  true
137648
137757
  );
137649
137758
  }
137650
- throw new MintError(`postman: re-mint failed (service-account-tokens HTTP ${status}).`, false);
137759
+ throw new MintError(`postman: re-mint failed (service-account-tokens HTTP ${status}).`, false, status);
137651
137760
  }
137652
137761
  let parsed;
137653
137762
  try {
@@ -137678,7 +137787,8 @@ async function mintAccessTokenIfNeeded(inputs, log, setSecret2, fetchImpl = fetc
137678
137787
  apiKey: inputs.postmanApiKey,
137679
137788
  apiBaseUrl,
137680
137789
  fetchImpl,
137681
- onToken: (token) => setSecret2?.(token)
137790
+ onToken: (token) => setSecret2?.(token),
137791
+ onRetryEvent: (event) => log.retryEvent?.(event)
137682
137792
  });
137683
137793
  try {
137684
137794
  inputs.postmanAccessToken = await provider.refresh();
@@ -137718,6 +137828,7 @@ var AccessTokenGatewayClient = class {
137718
137828
  requestTimeoutMs;
137719
137829
  retryMaxDelayMs;
137720
137830
  randomImpl;
137831
+ onRetryEvent;
137721
137832
  constructor(options) {
137722
137833
  this.tokenProvider = options.tokenProvider;
137723
137834
  this.bifrostBaseUrl = String(
@@ -137736,6 +137847,7 @@ var AccessTokenGatewayClient = class {
137736
137847
  this.requestTimeoutMs = options.requestTimeoutMs ?? 3e4;
137737
137848
  this.retryMaxDelayMs = options.retryMaxDelayMs ?? 5e3;
137738
137849
  this.randomImpl = options.randomImpl ?? Math.random;
137850
+ this.onRetryEvent = options.onRetryEvent;
137739
137851
  }
137740
137852
  configureTeamContext(teamId, orgMode) {
137741
137853
  this.teamId = String(teamId || "").trim();
@@ -137804,8 +137916,9 @@ var AccessTokenGatewayClient = class {
137804
137916
  if (!this.fallbackBaseUrl) return false;
137805
137917
  return retryTransient || request.fallback === "auto";
137806
137918
  }
137807
- async attemptFallback(request, retryTransient) {
137919
+ async attemptFallback(request, retryTransient, status) {
137808
137920
  if (!this.fallbackEligible(request, retryTransient)) return null;
137921
+ this.emitRetryEvent("fallback", status, 1, 0);
137809
137922
  const response = await this.tryFallback(request);
137810
137923
  if (!response) return null;
137811
137924
  if (response.ok) return response;
@@ -137831,6 +137944,7 @@ var AccessTokenGatewayClient = class {
137831
137944
  } catch (error2) {
137832
137945
  if (retryTransient && attempt < this.maxRetries) {
137833
137946
  const delay = this.retryDelayMs(attempt);
137947
+ this.emitRetryEvent("transport", void 0, attempt + 1, delay);
137834
137948
  attempt += 1;
137835
137949
  await this.sleepImpl(delay);
137836
137950
  continue;
@@ -137844,7 +137958,9 @@ var AccessTokenGatewayClient = class {
137844
137958
  const inner = this.innerStatus(okBody);
137845
137959
  if (inner !== void 0) {
137846
137960
  if (retryTransient && this.isTransient(inner, okBody) && attempt < this.maxRetries) {
137847
- await this.sleepImpl(this.retryDelayMs(attempt));
137961
+ const delay = this.retryDelayMs(attempt);
137962
+ this.emitRetryEvent("inner", inner, attempt + 1, delay);
137963
+ await this.sleepImpl(delay);
137848
137964
  attempt += 1;
137849
137965
  continue;
137850
137966
  }
@@ -137854,6 +137970,7 @@ var AccessTokenGatewayClient = class {
137854
137970
  }
137855
137971
  const body = await response.text().catch(() => "");
137856
137972
  if (isExpiredAuthError(response.status, body) && this.tokenProvider.canRefresh()) {
137973
+ this.emitRetryEvent("auth", response.status, 1, 0);
137857
137974
  await this.tokenProvider.refresh();
137858
137975
  response = await this.send(request);
137859
137976
  if (response.ok) {
@@ -137864,11 +137981,12 @@ var AccessTokenGatewayClient = class {
137864
137981
  }
137865
137982
  if (retryTransient && this.isTransient(response.status, body) && attempt < this.maxRetries) {
137866
137983
  const delay = this.retryDelayMs(attempt, parseRetryAfterMs2(response.headers.get("retry-after")));
137984
+ this.emitRetryEvent("http", response.status, attempt + 1, delay);
137867
137985
  attempt += 1;
137868
137986
  await this.sleepImpl(delay);
137869
137987
  continue;
137870
137988
  }
137871
- const fallbackResponse = await this.attemptFallback(request, retryTransient);
137989
+ const fallbackResponse = await this.attemptFallback(request, retryTransient, response.status);
137872
137990
  if (fallbackResponse) return fallbackResponse;
137873
137991
  throw this.toHttpError(request, response, body);
137874
137992
  }
@@ -137876,6 +137994,14 @@ var AccessTokenGatewayClient = class {
137876
137994
  retryDelayMs(attempt, retryAfter) {
137877
137995
  return retryAfter === void 0 ? fullJitterDelayMs(attempt, this.retryBaseDelayMs, this.retryMaxDelayMs, this.randomImpl) : Math.min(this.retryMaxDelayMs, retryAfter);
137878
137996
  }
137997
+ emitRetryEvent(retryClass, status, attempt, delay) {
137998
+ this.onRetryEvent?.({
137999
+ class: retryClass,
138000
+ ...status === void 0 ? {} : { status },
138001
+ attempt: Math.max(1, attempt),
138002
+ delay: Math.max(0, delay)
138003
+ });
138004
+ }
137879
138005
  isTransient(status, body) {
137880
138006
  return status === 408 || status === 429 || status >= 500 || /ESOCKETTIMEDOUT|ETIMEDOUT|ECONNRESET|serverError|downstream/.test(body);
137881
138007
  }
@@ -138005,7 +138131,7 @@ function validateCertMaterial(certBase64, keyBase64, passphrase) {
138005
138131
  }
138006
138132
 
138007
138133
  // src/lib/repo/branch-decision.ts
138008
- var import_node_fs3 = require("node:fs");
138134
+ var import_node_fs4 = require("node:fs");
138009
138135
  var import_node_crypto3 = require("node:crypto");
138010
138136
  var ContractError = class extends Error {
138011
138137
  code;
@@ -138046,7 +138172,7 @@ function readGithubEvent(env) {
138046
138172
  const path9 = clean(env.GITHUB_EVENT_PATH);
138047
138173
  if (!path9) return void 0;
138048
138174
  try {
138049
- return JSON.parse((0, import_node_fs3.readFileSync)(path9, "utf8"));
138175
+ return JSON.parse((0, import_node_fs4.readFileSync)(path9, "utf8"));
138050
138176
  } catch {
138051
138177
  return void 0;
138052
138178
  }
@@ -138502,6 +138628,7 @@ function resolveInputs(env = process.env) {
138502
138628
  baselineCollectionId: getInput2("baseline-collection-id", env),
138503
138629
  smokeCollectionId: getInput2("smoke-collection-id", env),
138504
138630
  contractCollectionId: getInput2("contract-collection-id", env),
138631
+ prebuiltCollectionsJson: getInput2("prebuilt-collections-json", env),
138505
138632
  specId: getInput2("spec-id", env),
138506
138633
  specContentChanged: parseBooleanInput(getInput2("spec-content-changed", env), true),
138507
138634
  specPath: getInput2("spec-path", env),
@@ -138617,7 +138744,7 @@ var StateUnreadableError = class extends Error {
138617
138744
  function readResourcesState() {
138618
138745
  let raw;
138619
138746
  try {
138620
- raw = (0, import_node_fs4.readFileSync)(".postman/resources.yaml", "utf8");
138747
+ raw = (0, import_node_fs5.readFileSync)(".postman/resources.yaml", "utf8");
138621
138748
  } catch {
138622
138749
  return null;
138623
138750
  }
@@ -138676,12 +138803,15 @@ function getEnvironmentUidsFromResources(resourcesState) {
138676
138803
  function normalizeToPosix(filePath) {
138677
138804
  return filePath.split(path8.sep).join("/").replace(/\\/g, "/");
138678
138805
  }
138806
+ function canonicalizeRelativePath(value) {
138807
+ return normalizeToPosix(String(value ?? "").trim()).replace(/^\.\/+/, "").replace(/\/{2,}/g, "/").replace(/\/+$/g, "");
138808
+ }
138679
138809
  function isOpenApiSpecFile(filePath) {
138680
138810
  if (!(filePath.endsWith(".json") || filePath.endsWith(".yaml") || filePath.endsWith(".yml"))) {
138681
138811
  return false;
138682
138812
  }
138683
138813
  try {
138684
- const raw = (0, import_node_fs4.readFileSync)(filePath, "utf8");
138814
+ const raw = (0, import_node_fs5.readFileSync)(filePath, "utf8");
138685
138815
  const parsed = filePath.endsWith(".json") ? JSON.parse(raw) : load(raw);
138686
138816
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
138687
138817
  return false;
@@ -138710,7 +138840,7 @@ function scanLocalSpecReferences(baseDir = ".") {
138710
138840
  const found = /* @__PURE__ */ new Set();
138711
138841
  const refs = [];
138712
138842
  const visit2 = (currentDir) => {
138713
- for (const entry of (0, import_node_fs4.readdirSync)(currentDir, { withFileTypes: true })) {
138843
+ for (const entry of (0, import_node_fs5.readdirSync)(currentDir, { withFileTypes: true })) {
138714
138844
  if (ignoredDirs.has(entry.name)) {
138715
138845
  continue;
138716
138846
  }
@@ -138740,7 +138870,7 @@ function resolveMappedSpecReference(explicitSpecPath, discoveredSpecs) {
138740
138870
  const normalizedExplicitPath = normalizeToPosix(explicitSpecPath.trim());
138741
138871
  if (normalizedExplicitPath) {
138742
138872
  const explicitFullPath = path8.resolve(normalizedExplicitPath);
138743
- if ((0, import_node_fs4.existsSync)(explicitFullPath) && (0, import_node_fs4.statSync)(explicitFullPath).isFile()) {
138873
+ if ((0, import_node_fs5.existsSync)(explicitFullPath) && (0, import_node_fs5.statSync)(explicitFullPath).isFile()) {
138744
138874
  return {
138745
138875
  repoRelativePath: normalizedExplicitPath,
138746
138876
  configRelativePath: normalizeToPosix(path8.join("..", normalizedExplicitPath))
@@ -138792,6 +138922,7 @@ function readActionInputs(actionCore) {
138792
138922
  INPUT_BASELINE_COLLECTION_ID: readInput(actionCore, "baseline-collection-id"),
138793
138923
  INPUT_SMOKE_COLLECTION_ID: readInput(actionCore, "smoke-collection-id"),
138794
138924
  INPUT_CONTRACT_COLLECTION_ID: readInput(actionCore, "contract-collection-id"),
138925
+ INPUT_PREBUILT_COLLECTIONS_JSON: readInput(actionCore, "prebuilt-collections-json"),
138795
138926
  INPUT_SPEC_ID: readInput(actionCore, "spec-id"),
138796
138927
  INPUT_SPEC_PATH: readInput(actionCore, "spec-path"),
138797
138928
  INPUT_COLLECTION_SYNC_MODE: readInput(actionCore, "collection-sync-mode") || "refresh",
@@ -139005,6 +139136,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
139005
139136
  );
139006
139137
  }
139007
139138
  envUids[envName] = existingUid;
139139
+ dependencies.core.setOutput("environment-uids-json", JSON.stringify(envUids));
139008
139140
  continue;
139009
139141
  }
139010
139142
  const values = buildEnvironmentValues(envName, runtimeUrl);
@@ -139015,6 +139147,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
139015
139147
  displayName,
139016
139148
  values
139017
139149
  );
139150
+ dependencies.core.setOutput("environment-uids-json", JSON.stringify(envUids));
139018
139151
  } catch (error2) {
139019
139152
  throw new Error(
139020
139153
  formatOrchestrationIssue({
@@ -139031,7 +139164,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
139031
139164
  return envUids;
139032
139165
  }
139033
139166
  function ensureDir(path9) {
139034
- (0, import_node_fs4.mkdirSync)(path9, { recursive: true });
139167
+ (0, import_node_fs5.mkdirSync)(path9, { recursive: true });
139035
139168
  }
139036
139169
  function getCollectionDirectoryName(kind, projectName) {
139037
139170
  if (kind === "Baseline") {
@@ -139068,7 +139201,7 @@ function stripVolatileFields(obj) {
139068
139201
  }
139069
139202
  function writeJsonFile(path9, content, normalize4 = false) {
139070
139203
  const data = normalize4 ? stripVolatileFields(content) : content;
139071
- (0, import_node_fs4.writeFileSync)(path9, JSON.stringify(data, null, 2));
139204
+ (0, import_node_fs5.writeFileSync)(path9, JSON.stringify(data, null, 2));
139072
139205
  }
139073
139206
  function buildMappedSpecCloudKey(mappedSource, specSyncMode, releaseLabel) {
139074
139207
  if (specSyncMode !== "version") {
@@ -139132,22 +139265,422 @@ function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir,
139132
139265
  sortKeys: false
139133
139266
  });
139134
139267
  }
139135
- function buildSpecCollectionWorkflowManifest(specRef, collectionRefs) {
139136
- return dump(
139137
- {
139138
- workflows: {
139139
- syncSpecToCollection: collectionRefs.map((collectionRef) => ({
139140
- spec: specRef,
139141
- collection: collectionRef
139142
- }))
139268
+ function isPlainObject2(value) {
139269
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
139270
+ }
139271
+ function buildSpecCollectionWorkflowManifest(specRef, collectionRefs, existingRaw) {
139272
+ const pairs = collectionRefs.map((collectionRef) => ({
139273
+ spec: specRef,
139274
+ collection: collectionRef
139275
+ }));
139276
+ let root = {};
139277
+ if (typeof existingRaw === "string" && existingRaw.trim()) {
139278
+ let parsed;
139279
+ try {
139280
+ parsed = load(existingRaw);
139281
+ } catch (error2) {
139282
+ throw new Error(
139283
+ `CONTRACT_WORKFLOWS_UNREADABLE: .postman/workflows.yaml exists but is not parseable YAML (${error2 instanceof Error ? error2.message : String(error2)}). Fix or delete the file; refusing to overwrite unrelated workflow data.`,
139284
+ { cause: error2 }
139285
+ );
139286
+ }
139287
+ if (parsed === null || parsed === void 0) {
139288
+ root = {};
139289
+ } else if (!isPlainObject2(parsed)) {
139290
+ throw new Error(
139291
+ "CONTRACT_WORKFLOWS_UNREADABLE: .postman/workflows.yaml exists but does not contain a YAML mapping. Fix or delete the file; refusing to overwrite unrelated workflow data."
139292
+ );
139293
+ } else {
139294
+ root = { ...parsed };
139295
+ }
139296
+ }
139297
+ const workflows = isPlainObject2(root.workflows) ? { ...root.workflows } : {};
139298
+ const desiredCollections = new Set(collectionRefs);
139299
+ const currentPairs = (Array.isArray(workflows.syncSpecToCollection) ? workflows.syncSpecToCollection : []).map((entry) => isPlainObject2(entry) ? { ...entry } : null).filter((entry) => Boolean(entry)).filter(
139300
+ (entry) => String(entry.spec ?? "") !== specRef || desiredCollections.has(String(entry.collection ?? ""))
139301
+ );
139302
+ for (const pair of pairs) {
139303
+ const index = currentPairs.findIndex(
139304
+ (entry) => String(entry.spec ?? "") === pair.spec && String(entry.collection ?? "") === pair.collection
139305
+ );
139306
+ if (index >= 0) {
139307
+ const previous = currentPairs[index];
139308
+ currentPairs[index] = { ...previous, spec: pair.spec, collection: pair.collection };
139309
+ } else {
139310
+ currentPairs.push({ spec: pair.spec, collection: pair.collection });
139311
+ }
139312
+ }
139313
+ workflows.syncSpecToCollection = currentPairs;
139314
+ root.workflows = workflows;
139315
+ return dump(root, {
139316
+ lineWidth: -1,
139317
+ noRefs: true,
139318
+ sortKeys: false
139319
+ });
139320
+ }
139321
+ var PREBUILT_COLLECTION_ROLES = /* @__PURE__ */ new Set([
139322
+ "baseline",
139323
+ "smoke",
139324
+ "contract"
139325
+ ]);
139326
+ var SHA256_HEX = /^[a-f0-9]{64}$/;
139327
+ var PREBUILT_CLOUD_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
139328
+ function failPrebuiltCollections(detail) {
139329
+ throw new Error(`CONTRACT_PREBUILT_COLLECTIONS_INVALID: ${detail}`);
139330
+ }
139331
+ function parsePrebuiltCollectionsJson(raw) {
139332
+ const trimmed = String(raw ?? "").trim();
139333
+ if (!trimmed) {
139334
+ return [];
139335
+ }
139336
+ let parsed;
139337
+ try {
139338
+ parsed = JSON.parse(trimmed);
139339
+ } catch (error2) {
139340
+ throw new Error(
139341
+ `CONTRACT_PREBUILT_COLLECTIONS_INVALID: malformed JSON (${error2 instanceof Error ? error2.message : String(error2)})`,
139342
+ { cause: error2 }
139343
+ );
139344
+ }
139345
+ let entriesRaw;
139346
+ if (Array.isArray(parsed)) {
139347
+ entriesRaw = parsed;
139348
+ } else if (isPlainObject2(parsed)) {
139349
+ if (parsed.schemaVersion !== 1) {
139350
+ failPrebuiltCollections("schemaVersion must be 1");
139351
+ }
139352
+ if (!Array.isArray(parsed.collections)) {
139353
+ failPrebuiltCollections("collections must be an array when schemaVersion is set");
139354
+ }
139355
+ entriesRaw = parsed.collections;
139356
+ } else {
139357
+ failPrebuiltCollections("expected a JSON array or {schemaVersion:1,collections:[]}");
139358
+ }
139359
+ const seenRoles = /* @__PURE__ */ new Set();
139360
+ const entries = [];
139361
+ for (const [index, value] of entriesRaw.entries()) {
139362
+ if (!isPlainObject2(value)) {
139363
+ failPrebuiltCollections(`collections[${index}] must be an object`);
139364
+ }
139365
+ const roleRaw = String(value.role ?? "").trim();
139366
+ if (!PREBUILT_COLLECTION_ROLES.has(roleRaw)) {
139367
+ failPrebuiltCollections(
139368
+ `collections[${index}].role must be one of baseline|smoke|contract`
139369
+ );
139370
+ }
139371
+ const role = roleRaw;
139372
+ if (seenRoles.has(role)) {
139373
+ failPrebuiltCollections(`duplicate role ${role}`);
139374
+ }
139375
+ seenRoles.add(role);
139376
+ const collectionPath = String(
139377
+ value.collectionPath ?? value.path ?? ""
139378
+ ).trim();
139379
+ if (!collectionPath || hasControlCharacter2(collectionPath)) {
139380
+ failPrebuiltCollections(
139381
+ `collections[${index}].collectionPath must be a non-empty confined relative path`
139382
+ );
139383
+ }
139384
+ const cloudId = String(value.cloudId ?? "").trim();
139385
+ if (!cloudId || !PREBUILT_CLOUD_ID.test(cloudId)) {
139386
+ failPrebuiltCollections(
139387
+ `collections[${index}].cloudId must be a non-empty Postman collection id`
139388
+ );
139389
+ }
139390
+ let payloadDigest;
139391
+ if (Object.prototype.hasOwnProperty.call(value, "payloadDigest")) {
139392
+ const payloadDigestRaw = value.payloadDigest;
139393
+ if (payloadDigestRaw !== void 0 && payloadDigestRaw !== null) {
139394
+ payloadDigest = String(payloadDigestRaw).trim();
139395
+ if (!SHA256_HEX.test(payloadDigest)) {
139396
+ failPrebuiltCollections(
139397
+ `collections[${index}].payloadDigest must be lowercase 64-hex`
139398
+ );
139399
+ }
139143
139400
  }
139144
- },
139145
- {
139146
- lineWidth: -1,
139147
- noRefs: true,
139148
- sortKeys: false
139149
139401
  }
139402
+ const artifactDigest = String(value.artifactDigest ?? "").trim();
139403
+ if (!SHA256_HEX.test(artifactDigest)) {
139404
+ failPrebuiltCollections(
139405
+ `collections[${index}].artifactDigest must be lowercase 64-hex`
139406
+ );
139407
+ }
139408
+ entries.push({
139409
+ role,
139410
+ collectionPath,
139411
+ cloudId,
139412
+ ...payloadDigest !== void 0 ? { payloadDigest } : {},
139413
+ artifactDigest
139414
+ });
139415
+ }
139416
+ return entries;
139417
+ }
139418
+ function assertPathWithinArtifactRoot(targetPath, artifactDir, fieldName) {
139419
+ assertPathWithinCwd(targetPath, fieldName);
139420
+ assertPathWithinCwd(artifactDir, "artifact-dir");
139421
+ const cwd = (0, import_node_fs5.realpathSync)(process.cwd());
139422
+ const artifactRoot = path8.resolve(cwd, artifactDir.trim());
139423
+ const resolved = path8.resolve(cwd, targetPath.trim());
139424
+ const relativeToArtifact = path8.relative(artifactRoot, resolved);
139425
+ if (!relativeToArtifact || relativeToArtifact.startsWith("..") || path8.isAbsolute(relativeToArtifact)) {
139426
+ failPrebuiltCollections(
139427
+ `${fieldName} must stay under artifact-dir (${artifactDir}); received ${targetPath}`
139428
+ );
139429
+ }
139430
+ let existingPath = resolved;
139431
+ while (!(0, import_node_fs5.existsSync)(existingPath)) {
139432
+ const parent = path8.dirname(existingPath);
139433
+ if (parent === existingPath) {
139434
+ break;
139435
+ }
139436
+ existingPath = parent;
139437
+ }
139438
+ const realExisting = (0, import_node_fs5.realpathSync)(existingPath);
139439
+ const realRelative = path8.relative(artifactRoot, realExisting);
139440
+ if (realRelative.startsWith("..") || path8.isAbsolute(realRelative)) {
139441
+ failPrebuiltCollections(
139442
+ `${fieldName} resolves outside artifact-dir via symlink; received ${targetPath}`
139443
+ );
139444
+ }
139445
+ return normalizeToPosix(path8.relative(cwd, resolved));
139446
+ }
139447
+ function prebuiltDirectoryTraversalIdentity(absolutePath, stats, options = {}) {
139448
+ const platform2 = options.platform ?? process.platform;
139449
+ const inodeIsZero = stats.ino === 0 || stats.ino === 0n;
139450
+ if (platform2 !== "win32" && !inodeIsZero) {
139451
+ return `${stats.dev}:${stats.ino}`;
139452
+ }
139453
+ const resolveCanonicalPath = options.resolveCanonicalPath ?? ((candidate) => import_node_fs5.realpathSync.native(candidate));
139454
+ const canonical = resolveCanonicalPath(absolutePath);
139455
+ return platform2 === "win32" ? canonical.toLowerCase() : canonical;
139456
+ }
139457
+ function listPrebuiltCollectionTreeFiles(collectionPath, artifactDir) {
139458
+ const confined = assertPathWithinArtifactRoot(
139459
+ collectionPath,
139460
+ artifactDir,
139461
+ "prebuilt collection path"
139462
+ );
139463
+ const absRoot = path8.resolve(process.cwd(), confined);
139464
+ if (!(0, import_node_fs5.existsSync)(absRoot)) {
139465
+ return [];
139466
+ }
139467
+ let rootStat;
139468
+ try {
139469
+ rootStat = (0, import_node_fs5.lstatSync)(absRoot);
139470
+ } catch {
139471
+ return [];
139472
+ }
139473
+ if (rootStat.isSymbolicLink()) {
139474
+ failPrebuiltCollections(
139475
+ `prebuilt collection path must not be a symlink; received ${collectionPath}`
139476
+ );
139477
+ }
139478
+ if (!rootStat.isDirectory()) {
139479
+ return [];
139480
+ }
139481
+ const files = [];
139482
+ const pendingDirectories = [absRoot];
139483
+ const seenDirectories = /* @__PURE__ */ new Set();
139484
+ while (pendingDirectories.length > 0) {
139485
+ const currentAbsolute = pendingDirectories.pop();
139486
+ if (!currentAbsolute) {
139487
+ break;
139488
+ }
139489
+ const currentStat = (0, import_node_fs5.lstatSync)(currentAbsolute);
139490
+ if (currentStat.isSymbolicLink() || !currentStat.isDirectory()) {
139491
+ failPrebuiltCollections(
139492
+ `prebuilt collection tree directory changed or became unsupported at ${normalizeToPosix(path8.relative(absRoot, currentAbsolute)) || "."}`
139493
+ );
139494
+ }
139495
+ const currentKey = prebuiltDirectoryTraversalIdentity(currentAbsolute, currentStat);
139496
+ if (seenDirectories.has(currentKey)) {
139497
+ failPrebuiltCollections(
139498
+ `prebuilt collection tree directory cycle detected at ${normalizeToPosix(path8.relative(absRoot, currentAbsolute)) || "."}`
139499
+ );
139500
+ }
139501
+ seenDirectories.add(currentKey);
139502
+ const entries = (0, import_node_fs5.readdirSync)(currentAbsolute, { withFileTypes: true });
139503
+ for (const entry of entries) {
139504
+ const abs = path8.join(currentAbsolute, entry.name);
139505
+ if (entry.isSymbolicLink()) {
139506
+ failPrebuiltCollections(
139507
+ `prebuilt collection tree must not contain symlinks; received ${normalizeToPosix(path8.relative(absRoot, abs))}`
139508
+ );
139509
+ }
139510
+ if (entry.isDirectory()) {
139511
+ pendingDirectories.push(abs);
139512
+ continue;
139513
+ }
139514
+ if (!entry.isFile()) {
139515
+ failPrebuiltCollections(
139516
+ `prebuilt collection tree contains unsupported entry type at ${normalizeToPosix(path8.relative(absRoot, abs))}`
139517
+ );
139518
+ }
139519
+ const fileLstat = (0, import_node_fs5.lstatSync)(abs);
139520
+ if (fileLstat.isSymbolicLink() || !fileLstat.isFile()) {
139521
+ failPrebuiltCollections(
139522
+ `prebuilt collection tree file changed or became unsupported at ${normalizeToPosix(path8.relative(absRoot, abs))}`
139523
+ );
139524
+ }
139525
+ files.push({
139526
+ absolute: abs,
139527
+ relative: normalizeToPosix(path8.relative(absRoot, abs)),
139528
+ dev: fileLstat.dev,
139529
+ ino: fileLstat.ino,
139530
+ size: fileLstat.size
139531
+ });
139532
+ }
139533
+ }
139534
+ files.sort((a, b) => a.relative.localeCompare(b.relative));
139535
+ return files;
139536
+ }
139537
+ function validateCanonicalV3CollectionFile(relative4, bytes) {
139538
+ let expectedKind;
139539
+ if (/(^|\/)\.resources\/definition\.yaml$/.test(relative4)) {
139540
+ expectedKind = "collection";
139541
+ } else if (relative4.endsWith(".request.yaml")) {
139542
+ expectedKind = "request";
139543
+ } else if (relative4.endsWith(".example.yaml")) {
139544
+ expectedKind = "example";
139545
+ } else if (relative4.endsWith(".message.yaml")) {
139546
+ expectedKind = "message";
139547
+ } else {
139548
+ failPrebuiltCollections(`prebuilt collection tree contains unexpected file ${relative4}`);
139549
+ }
139550
+ let parsed;
139551
+ try {
139552
+ parsed = load(bytes.toString("utf8"));
139553
+ } catch (error2) {
139554
+ failPrebuiltCollections(
139555
+ `prebuilt collection tree contains malformed YAML at ${relative4} (${error2 instanceof Error ? error2.message : String(error2)})`
139556
+ );
139557
+ }
139558
+ if (!isPlainObject2(parsed)) {
139559
+ failPrebuiltCollections(`prebuilt collection tree file ${relative4} must contain a YAML mapping`);
139560
+ }
139561
+ const kind = typeof parsed.$kind === "string" ? parsed.$kind.trim() : "";
139562
+ if (!kind) {
139563
+ failPrebuiltCollections(`prebuilt collection tree file ${relative4} must contain a nonempty $kind`);
139564
+ }
139565
+ const agrees = expectedKind === "collection" ? kind === "collection" : kind.endsWith(`-${expectedKind}`);
139566
+ if (!agrees) {
139567
+ failPrebuiltCollections(
139568
+ `prebuilt collection tree file ${relative4} has $kind ${kind} inconsistent with its filename`
139569
+ );
139570
+ }
139571
+ }
139572
+ async function digestAndValidatePrebuiltCollectionTree(files) {
139573
+ if (!files.some((file) => file.relative === ".resources/definition.yaml")) {
139574
+ failPrebuiltCollections("prebuilt collection tree is missing .resources/definition.yaml");
139575
+ }
139576
+ const hash = (0, import_node_crypto4.createHash)("sha256");
139577
+ for (const file of files) {
139578
+ let bytes;
139579
+ try {
139580
+ bytes = await appendArtifactDigestFileStreaming(hash, file.relative, file.absolute, {
139581
+ dev: file.dev,
139582
+ ino: file.ino,
139583
+ size: file.size
139584
+ });
139585
+ } catch (error2) {
139586
+ if (error2 instanceof ArtifactDigestStreamError) {
139587
+ failPrebuiltCollections(error2.message);
139588
+ }
139589
+ const code = error2?.code;
139590
+ if (code === "ELOOP" || code === "EPERM") {
139591
+ failPrebuiltCollections(
139592
+ `prebuilt collection tree file became a symlink or unsupported node at ${file.relative}`
139593
+ );
139594
+ }
139595
+ failPrebuiltCollections(
139596
+ `prebuilt collection tree file changed or became unsupported at ${file.relative}`
139597
+ );
139598
+ }
139599
+ validateCanonicalV3CollectionFile(file.relative, bytes);
139600
+ }
139601
+ return hash.digest("hex");
139602
+ }
139603
+ async function preparePrebuiltCollections(inputs) {
139604
+ const prepared = /* @__PURE__ */ new Map();
139605
+ for (const entry of parsePrebuiltCollectionsJson(inputs.prebuiltCollectionsJson ?? "")) {
139606
+ const confinedPath = assertPathWithinArtifactRoot(
139607
+ entry.collectionPath,
139608
+ inputs.artifactDir,
139609
+ `prebuilt ${entry.role} collection path`
139610
+ );
139611
+ const absolute = path8.resolve(process.cwd(), confinedPath);
139612
+ if (!(0, import_node_fs5.existsSync)(absolute)) {
139613
+ prepared.set(entry.role, { entry, confinedPath });
139614
+ continue;
139615
+ }
139616
+ const files = listPrebuiltCollectionTreeFiles(confinedPath, inputs.artifactDir);
139617
+ const artifactDigest = await digestAndValidatePrebuiltCollectionTree(files);
139618
+ prepared.set(entry.role, {
139619
+ entry,
139620
+ confinedPath,
139621
+ artifactDigest
139622
+ });
139623
+ }
139624
+ return prepared;
139625
+ }
139626
+ function tryReusePrebuiltCollection(options) {
139627
+ const { prepared, expectedPath, expectedCloudId } = options;
139628
+ const { entry } = prepared;
139629
+ if (!expectedCloudId || entry.cloudId !== expectedCloudId) {
139630
+ return false;
139631
+ }
139632
+ const expectedNormalized = canonicalizeRelativePath(expectedPath);
139633
+ const entryNormalized = canonicalizeRelativePath(entry.collectionPath);
139634
+ if (entryNormalized !== expectedNormalized) {
139635
+ return false;
139636
+ }
139637
+ const confined = canonicalizeRelativePath(prepared.confinedPath);
139638
+ if (confined !== expectedNormalized) {
139639
+ return false;
139640
+ }
139641
+ const expectedName = path8.posix.basename(expectedNormalized);
139642
+ if (path8.posix.basename(confined) !== expectedName) {
139643
+ return false;
139644
+ }
139645
+ return prepared.artifactDigest === entry.artifactDigest;
139646
+ }
139647
+ async function exportCollectionArtifact(options) {
139648
+ const {
139649
+ role,
139650
+ collectionId,
139651
+ dirName,
139652
+ collectionsDir,
139653
+ prebuiltByRole,
139654
+ postman,
139655
+ core
139656
+ } = options;
139657
+ if (!collectionId) {
139658
+ return void 0;
139659
+ }
139660
+ const expectedPath = `${collectionsDir}/${dirName}`;
139661
+ const entry = prebuiltByRole.get(role);
139662
+ if (entry) {
139663
+ const reused = tryReusePrebuiltCollection({
139664
+ prepared: entry,
139665
+ expectedPath,
139666
+ expectedCloudId: collectionId
139667
+ });
139668
+ if (reused) {
139669
+ core.info(
139670
+ `Reusing prebuilt ${role} collection tree at ${expectedPath} (artifactDigest match)`
139671
+ );
139672
+ return `../${expectedPath}`;
139673
+ }
139674
+ core.info(
139675
+ `Prebuilt ${role} collection entry present but did not exactly match; exporting from cloud`
139676
+ );
139677
+ }
139678
+ const col = await postman.getCollection(collectionId);
139679
+ await convertAndSplitAnyCollection(
139680
+ col,
139681
+ expectedPath
139150
139682
  );
139683
+ return `../${expectedPath}`;
139151
139684
  }
139152
139685
  function hasControlCharacter2(value) {
139153
139686
  for (let index = 0; index < value.length; index += 1) {
@@ -139165,21 +139698,21 @@ function assertPathWithinCwd(targetPath, fieldName) {
139165
139698
  if (!rawPath || hasControlCharacter2(originalPath) || path8.isAbsolute(rawPath) || path8.win32.isAbsolute(rawPath) || segments.includes("..") || rawPath.startsWith(":") || hasControlCharacter2(rawPath)) {
139166
139699
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
139167
139700
  }
139168
- const base = (0, import_node_fs4.realpathSync)(process.cwd());
139701
+ const base = (0, import_node_fs5.realpathSync)(process.cwd());
139169
139702
  const resolved = path8.resolve(base, rawPath);
139170
139703
  const relative4 = path8.relative(base, resolved);
139171
139704
  if (relative4.startsWith("..") || path8.isAbsolute(relative4)) {
139172
139705
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
139173
139706
  }
139174
139707
  let existingPath = resolved;
139175
- while (!(0, import_node_fs4.existsSync)(existingPath)) {
139708
+ while (!(0, import_node_fs5.existsSync)(existingPath)) {
139176
139709
  const parent = path8.dirname(existingPath);
139177
139710
  if (parent === existingPath) {
139178
139711
  break;
139179
139712
  }
139180
139713
  existingPath = parent;
139181
139714
  }
139182
- const realExistingPath = (0, import_node_fs4.realpathSync)(existingPath);
139715
+ const realExistingPath = (0, import_node_fs5.realpathSync)(existingPath);
139183
139716
  const realRelative = path8.relative(base, realExistingPath);
139184
139717
  if (realRelative.startsWith("..") || path8.isAbsolute(realRelative)) {
139185
139718
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
@@ -139207,8 +139740,8 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
139207
139740
  ensureDir(specsDir);
139208
139741
  ensureDir(".postman");
139209
139742
  const globalsFilePath = `${globalsDir}/workspace.globals.yaml`;
139210
- if (!(0, import_node_fs4.existsSync)(globalsFilePath)) {
139211
- (0, import_node_fs4.writeFileSync)(globalsFilePath, "name: Globals\nvalues: []\n");
139743
+ if (!(0, import_node_fs5.existsSync)(globalsFilePath)) {
139744
+ (0, import_node_fs5.writeFileSync)(globalsFilePath, "name: Globals\nvalues: []\n");
139212
139745
  }
139213
139746
  if (inputs.generateCiWorkflow) {
139214
139747
  const ciDir = inputs.ciWorkflowPath.split("/").slice(0, -1).join("/");
@@ -139224,23 +139757,42 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
139224
139757
  inputs.specSyncMode,
139225
139758
  options.releaseLabel
139226
139759
  ) : void 0;
139227
- if (inputs.baselineCollectionId) {
139228
- const col = await dependencies.postman.getCollection(inputs.baselineCollectionId);
139229
- const dirName = getCollectionDirectoryName("Baseline", assetProjectName);
139230
- await convertAndSplitAnyCollection(col, `${collectionsDir}/${dirName}`);
139231
- manifestCollections[`../${collectionsDir}/${dirName}`] = inputs.baselineCollectionId;
139232
- }
139233
- if (inputs.smokeCollectionId) {
139234
- const col = await dependencies.postman.getCollection(inputs.smokeCollectionId);
139235
- const dirName = getCollectionDirectoryName("Smoke", assetProjectName);
139236
- await convertAndSplitAnyCollection(col, `${collectionsDir}/${dirName}`);
139237
- manifestCollections[`../${collectionsDir}/${dirName}`] = inputs.smokeCollectionId;
139238
- }
139239
- if (inputs.contractCollectionId) {
139240
- const col = await dependencies.postman.getCollection(inputs.contractCollectionId);
139241
- const dirName = getCollectionDirectoryName("Contract", assetProjectName);
139242
- await convertAndSplitAnyCollection(col, `${collectionsDir}/${dirName}`);
139243
- manifestCollections[`../${collectionsDir}/${dirName}`] = inputs.contractCollectionId;
139760
+ const prebuiltByRole = options.preparedPrebuiltCollections;
139761
+ const baselineRef = await exportCollectionArtifact({
139762
+ role: "baseline",
139763
+ collectionId: inputs.baselineCollectionId,
139764
+ dirName: getCollectionDirectoryName("Baseline", assetProjectName),
139765
+ collectionsDir,
139766
+ prebuiltByRole,
139767
+ postman: dependencies.postman,
139768
+ core: dependencies.core
139769
+ });
139770
+ if (baselineRef) {
139771
+ manifestCollections[baselineRef] = inputs.baselineCollectionId;
139772
+ }
139773
+ const smokeRef = await exportCollectionArtifact({
139774
+ role: "smoke",
139775
+ collectionId: inputs.smokeCollectionId,
139776
+ dirName: getCollectionDirectoryName("Smoke", assetProjectName),
139777
+ collectionsDir,
139778
+ prebuiltByRole,
139779
+ postman: dependencies.postman,
139780
+ core: dependencies.core
139781
+ });
139782
+ if (smokeRef) {
139783
+ manifestCollections[smokeRef] = inputs.smokeCollectionId;
139784
+ }
139785
+ const contractRef = await exportCollectionArtifact({
139786
+ role: "contract",
139787
+ collectionId: inputs.contractCollectionId,
139788
+ dirName: getCollectionDirectoryName("Contract", assetProjectName),
139789
+ collectionsDir,
139790
+ prebuiltByRole,
139791
+ postman: dependencies.postman,
139792
+ core: dependencies.core
139793
+ });
139794
+ if (contractRef) {
139795
+ manifestCollections[contractRef] = inputs.contractCollectionId;
139244
139796
  }
139245
139797
  for (const [envName, envUid] of Object.entries(envUids)) {
139246
139798
  writeJsonFile(
@@ -139255,7 +139807,7 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
139255
139807
  workspaceLinkEnabled: inputs.workspaceLinkEnabled,
139256
139808
  workspaceLinkStatus: options.workspaceLinkStatus
139257
139809
  });
139258
- (0, import_node_fs4.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
139810
+ (0, import_node_fs5.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
139259
139811
  durableWorkspaceId,
139260
139812
  manifestCollections,
139261
139813
  envUids,
@@ -139267,11 +139819,18 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
139267
139819
  options.priorState
139268
139820
  ));
139269
139821
  if (mappedSpec && Object.keys(manifestCollections).length > 0) {
139270
- (0, import_node_fs4.writeFileSync)(
139822
+ let existingWorkflows;
139823
+ try {
139824
+ existingWorkflows = (0, import_node_fs5.readFileSync)(".postman/workflows.yaml", "utf8");
139825
+ } catch {
139826
+ existingWorkflows = void 0;
139827
+ }
139828
+ (0, import_node_fs5.writeFileSync)(
139271
139829
  ".postman/workflows.yaml",
139272
139830
  buildSpecCollectionWorkflowManifest(
139273
139831
  mappedSpec.configRelativePath,
139274
- Object.keys(manifestCollections)
139832
+ Object.keys(manifestCollections),
139833
+ existingWorkflows
139275
139834
  )
139276
139835
  );
139277
139836
  }
@@ -139316,12 +139875,12 @@ async function commitAndPushGeneratedFiles(inputs, dependencies) {
139316
139875
  const dir = parts.slice(0, -1).join("/");
139317
139876
  ensureDir(dir);
139318
139877
  }
139319
- (0, import_node_fs4.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
139878
+ (0, import_node_fs5.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
139320
139879
  if (inputs.provider === "github" || inputs.provider === "unknown") {
139321
139880
  const gcPath = ".github/workflows/postman-preview-gc.yml";
139322
139881
  if (inputs.ciWorkflowPath.endsWith(".github/workflows/ci.yml") || inputs.ciWorkflowPath === ".github/workflows/ci.yml") {
139323
139882
  ensureDir(".github/workflows");
139324
- (0, import_node_fs4.writeFileSync)(gcPath, renderGcWorkflowTemplate());
139883
+ (0, import_node_fs5.writeFileSync)(gcPath, renderGcWorkflowTemplate());
139325
139884
  }
139326
139885
  }
139327
139886
  }
@@ -139329,16 +139888,16 @@ async function commitAndPushGeneratedFiles(inputs, dependencies) {
139329
139888
  return { commitSha: "", resolvedCurrentRef: "", pushed: false };
139330
139889
  }
139331
139890
  const provisionPath = ".github/workflows/provision.yml";
139332
- const provisionExists = inputs.provider === "github" && (0, import_node_fs4.existsSync)(provisionPath);
139891
+ const provisionExists = inputs.provider === "github" && (0, import_node_fs5.existsSync)(provisionPath);
139333
139892
  const gcWorkflowPath = ".github/workflows/postman-preview-gc.yml";
139334
- const gcExists = inputs.generateCiWorkflow && (0, import_node_fs4.existsSync)(gcWorkflowPath);
139893
+ const gcExists = inputs.generateCiWorkflow && (0, import_node_fs5.existsSync)(gcWorkflowPath);
139335
139894
  const stagePaths = [
139336
139895
  inputs.artifactDir,
139337
139896
  ".postman",
139338
139897
  inputs.generateCiWorkflow ? inputs.ciWorkflowPath : null,
139339
139898
  gcExists ? gcWorkflowPath : null,
139340
139899
  provisionExists ? provisionPath : null
139341
- ].filter((entry) => typeof entry === "string" && ((0, import_node_fs4.existsSync)(entry) || entry === provisionPath));
139900
+ ].filter((entry) => typeof entry === "string" && ((0, import_node_fs5.existsSync)(entry) || entry === provisionPath));
139342
139901
  if (stagePaths.length === 0) {
139343
139902
  dependencies.core.info("No generated repository paths were found; skipping repo mutation.");
139344
139903
  return {
@@ -139446,6 +140005,7 @@ async function runRepoSyncInner(inputs, dependencies) {
139446
140005
  }
139447
140006
  }
139448
140007
  }
140008
+ const preparedPrebuiltCollections = await preparePrebuiltCollections(inputs);
139449
140009
  let skipRepositoryLinkPost = false;
139450
140010
  let repositoryLinkPreflightWasFree = false;
139451
140011
  if (inputs.workspaceLinkEnabled && inputs.workspaceId && inputs.repoUrl && dependencies.internalIntegration?.findWorkspaceForRepo) {
@@ -139482,6 +140042,7 @@ async function runRepoSyncInner(inputs, dependencies) {
139482
140042
  const branchAssetMarker = buildBranchAssetMarker(branchDecision, inputs);
139483
140043
  const envUids = await upsertEnvironments(inputs, dependencies, resourcesState, branchAssetMarker);
139484
140044
  outputs["environment-uids-json"] = JSON.stringify(envUids);
140045
+ dependencies.core.setOutput("environment-uids-json", outputs["environment-uids-json"]);
139485
140046
  if (inputs.environmentSyncEnabled && inputs.workspaceId && dependencies.internalIntegration) {
139486
140047
  const associations = Object.entries(envUids).map(([envName, envUid]) => ({
139487
140048
  envUid,
@@ -139600,6 +140161,7 @@ async function runRepoSyncInner(inputs, dependencies) {
139600
140161
  }
139601
140162
  }
139602
140163
  outputs["mock-url"] = resolvedMockUrl;
140164
+ dependencies.core.setOutput("mock-url", resolvedMockUrl);
139603
140165
  }
139604
140166
  }
139605
140167
  if (inputs.workspaceId && inputs.smokeCollectionId && Object.keys(envUids).length > 0) {
@@ -139675,6 +140237,7 @@ async function runRepoSyncInner(inputs, dependencies) {
139675
140237
  }
139676
140238
  }
139677
140239
  outputs["monitor-id"] = resolvedMonitorId;
140240
+ dependencies.core.setOutput("monitor-id", resolvedMonitorId);
139678
140241
  if (!effectiveCron && resolvedMonitorId) {
139679
140242
  try {
139680
140243
  await dependencies.postman.runMonitor(resolvedMonitorId);
@@ -139732,7 +140295,8 @@ async function runRepoSyncInner(inputs, dependencies) {
139732
140295
  priorWorkspaceId: resourcesState?.workspace?.id,
139733
140296
  existingSpecs: resourcesState?.cloudResources?.specs,
139734
140297
  releaseLabel,
139735
- priorState: resourcesState
140298
+ priorState: resourcesState,
140299
+ preparedPrebuiltCollections
139736
140300
  });
139737
140301
  const commit = await commitAndPushGeneratedFiles(inputs, dependencies);
139738
140302
  outputs["commit-sha"] = commit.commitSha;
@@ -139958,7 +140522,12 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
139958
140522
  bifrostBaseUrl: inputs.postmanBifrostBase,
139959
140523
  // No fallback on the org-mode probe: the expected non-org 400 must
139960
140524
  // surface verbatim, not be re-fired against the /_api alias.
139961
- secretMasker: masker
140525
+ secretMasker: masker,
140526
+ onRetryEvent: (event) => {
140527
+ actionCore.info(
140528
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
140529
+ );
140530
+ }
139962
140531
  });
139963
140532
  const squads = await gateway.getSquads(teamId);
139964
140533
  if (squads.length > 0) {
@@ -140022,7 +140591,12 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
140022
140591
  fallbackBaseUrl: inputs.postmanFallbackBase,
140023
140592
  teamId: resolved.teamId,
140024
140593
  orgMode: inputs.orgMode,
140025
- secretMasker: masker
140594
+ secretMasker: masker,
140595
+ onRetryEvent: (event) => {
140596
+ factories.core.info(
140597
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
140598
+ );
140599
+ }
140026
140600
  });
140027
140601
  const gatewayAssets = new PostmanGatewayAssetsClient({
140028
140602
  gateway,
@@ -140141,7 +140715,10 @@ async function runAction(actionCore = core_exports, actionExec = exec_exports) {
140141
140715
  }
140142
140716
  await mintAccessTokenIfNeeded(inputs, {
140143
140717
  info: (message) => actionCore.info(message),
140144
- warning: (message) => actionCore.warning(message)
140718
+ warning: (message) => actionCore.warning(message),
140719
+ retryEvent: (event) => actionCore.info(
140720
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
140721
+ )
140145
140722
  }, (secret) => actionCore.setSecret(secret));
140146
140723
  const masker = createSecretMasker([
140147
140724
  inputs.postmanApiKey,
@@ -140162,7 +140739,12 @@ async function runAction(actionCore = core_exports, actionExec = exec_exports) {
140162
140739
  explicitTeamId: inputs.teamId || void 0,
140163
140740
  mode: inputs.credentialPreflight,
140164
140741
  mask: masker,
140165
- log: actionCore
140742
+ log: {
140743
+ ...actionCore,
140744
+ retryEvent: (event) => actionCore.info(
140745
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
140746
+ )
140747
+ }
140166
140748
  });
140167
140749
  const resolved = await resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, masker, {
140168
140750
  env: process.env