@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/index.cjs CHANGED
@@ -26570,7 +26570,7 @@ var require_util7 = __commonJS({
26570
26570
  exports2.esc = esc;
26571
26571
  exports2.slugify = slugify;
26572
26572
  exports2.isObject = isObject;
26573
- exports2.isPlainObject = isPlainObject2;
26573
+ exports2.isPlainObject = isPlainObject3;
26574
26574
  exports2.shallowClone = shallowClone;
26575
26575
  exports2.numKeys = numKeys;
26576
26576
  exports2.escapeRegex = escapeRegex;
@@ -26753,7 +26753,7 @@ var require_util7 = __commonJS({
26753
26753
  return false;
26754
26754
  }
26755
26755
  });
26756
- function isPlainObject2(o) {
26756
+ function isPlainObject3(o) {
26757
26757
  if (isObject(o) === false)
26758
26758
  return false;
26759
26759
  const ctor = o.constructor;
@@ -26770,7 +26770,7 @@ var require_util7 = __commonJS({
26770
26770
  return true;
26771
26771
  }
26772
26772
  function shallowClone(o) {
26773
- if (isPlainObject2(o))
26773
+ if (isPlainObject3(o))
26774
26774
  return { ...o };
26775
26775
  if (Array.isArray(o))
26776
26776
  return [...o];
@@ -26975,7 +26975,7 @@ var require_util7 = __commonJS({
26975
26975
  return clone(schema, def);
26976
26976
  }
26977
26977
  function extend(schema, shape) {
26978
- if (!isPlainObject2(shape)) {
26978
+ if (!isPlainObject3(shape)) {
26979
26979
  throw new Error("Invalid input to extend: expected a plain object");
26980
26980
  }
26981
26981
  const checks = schema._zod.def.checks;
@@ -26998,7 +26998,7 @@ var require_util7 = __commonJS({
26998
26998
  return clone(schema, def);
26999
26999
  }
27000
27000
  function safeExtend(schema, shape) {
27001
- if (!isPlainObject2(shape)) {
27001
+ if (!isPlainObject3(shape)) {
27002
27002
  throw new Error("Invalid input to safeExtend: expected a plain object");
27003
27003
  }
27004
27004
  const def = mergeDefs(schema._zod.def, {
@@ -54817,7 +54817,7 @@ var require_lodash = __commonJS({
54817
54817
  } else {
54818
54818
  newValue = [];
54819
54819
  }
54820
- } else if (isPlainObject2(srcValue) || isArguments(srcValue)) {
54820
+ } else if (isPlainObject3(srcValue) || isArguments(srcValue)) {
54821
54821
  newValue = objValue;
54822
54822
  if (isArguments(objValue)) {
54823
54823
  newValue = toPlainObject(objValue);
@@ -55781,7 +55781,7 @@ var require_lodash = __commonJS({
55781
55781
  return objValue;
55782
55782
  }
55783
55783
  function customOmitClone(value) {
55784
- return isPlainObject2(value) ? undefined2 : value;
55784
+ return isPlainObject3(value) ? undefined2 : value;
55785
55785
  }
55786
55786
  function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
55787
55787
  var isPartial = bitmask & COMPARE_PARTIAL_FLAG, arrLength = array.length, othLength = other.length;
@@ -57282,7 +57282,7 @@ var require_lodash = __commonJS({
57282
57282
  var isBuffer = nativeIsBuffer || stubFalse;
57283
57283
  var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate;
57284
57284
  function isElement(value) {
57285
- return isObjectLike(value) && value.nodeType === 1 && !isPlainObject2(value);
57285
+ return isObjectLike(value) && value.nodeType === 1 && !isPlainObject3(value);
57286
57286
  }
57287
57287
  function isEmpty(value) {
57288
57288
  if (value == null) {
@@ -57318,7 +57318,7 @@ var require_lodash = __commonJS({
57318
57318
  return false;
57319
57319
  }
57320
57320
  var tag = baseGetTag(value);
57321
- return tag == errorTag || tag == domExcTag || typeof value.message == "string" && typeof value.name == "string" && !isPlainObject2(value);
57321
+ return tag == errorTag || tag == domExcTag || typeof value.message == "string" && typeof value.name == "string" && !isPlainObject3(value);
57322
57322
  }
57323
57323
  function isFinite2(value) {
57324
57324
  return typeof value == "number" && nativeIsFinite(value);
@@ -57369,7 +57369,7 @@ var require_lodash = __commonJS({
57369
57369
  function isNumber(value) {
57370
57370
  return typeof value == "number" || isObjectLike(value) && baseGetTag(value) == numberTag;
57371
57371
  }
57372
- function isPlainObject2(value) {
57372
+ function isPlainObject3(value) {
57373
57373
  if (!isObjectLike(value) || baseGetTag(value) != objectTag) {
57374
57374
  return false;
57375
57375
  }
@@ -58440,7 +58440,7 @@ var require_lodash = __commonJS({
58440
58440
  lodash.isNumber = isNumber;
58441
58441
  lodash.isObject = isObject;
58442
58442
  lodash.isObjectLike = isObjectLike;
58443
- lodash.isPlainObject = isPlainObject2;
58443
+ lodash.isPlainObject = isPlainObject3;
58444
58444
  lodash.isRegExp = isRegExp;
58445
58445
  lodash.isSafeInteger = isSafeInteger;
58446
58446
  lodash.isSet = isSet;
@@ -128815,6 +128815,7 @@ __export(index_exports, {
128815
128815
  decideBranchTier: () => decideBranchTier,
128816
128816
  getInput: () => getInput2,
128817
128817
  hasInput: () => hasInput,
128818
+ prebuiltDirectoryTraversalIdentity: () => prebuiltDirectoryTraversalIdentity,
128818
128819
  readActionInputs: () => readActionInputs,
128819
128820
  resolveInputs: () => resolveInputs,
128820
128821
  resolvePostmanApiKeyAndTeamId: () => resolvePostmanApiKeyAndTeamId,
@@ -131103,7 +131104,8 @@ function getIDToken(aud) {
131103
131104
  }
131104
131105
 
131105
131106
  // src/index.ts
131106
- var import_node_fs4 = require("node:fs");
131107
+ var import_node_crypto4 = require("node:crypto");
131108
+ var import_node_fs5 = require("node:fs");
131107
131109
  var path8 = __toESM(require("node:path"), 1);
131108
131110
 
131109
131111
  // node_modules/js-yaml/dist/js-yaml.mjs
@@ -134019,6 +134021,7 @@ function dump(input, options = {}) {
134019
134021
  }
134020
134022
 
134021
134023
  // src/postman-v3/converter.ts
134024
+ var import_node_fs = require("node:fs");
134022
134025
  var fs3 = __toESM(require("node:fs/promises"), 1);
134023
134026
  var path6 = __toESM(require("node:path"), 1);
134024
134027
  var V2 = __toESM(require_v2(), 1);
@@ -134089,6 +134092,72 @@ function structuredCloneSafe(value) {
134089
134092
  if (typeof structuredClone === "function") return structuredClone(value);
134090
134093
  return JSON.parse(JSON.stringify(value));
134091
134094
  }
134095
+ var ArtifactDigestStreamError = class extends Error {
134096
+ code = "ARTIFACT_DIGEST_STREAM";
134097
+ constructor(message, options) {
134098
+ super(message, options);
134099
+ this.name = "ArtifactDigestStreamError";
134100
+ }
134101
+ };
134102
+ function sameFileIdentity(left, right) {
134103
+ return left.dev === right.dev && left.ino === right.ino && left.size === right.size;
134104
+ }
134105
+ async function appendArtifactDigestFileStreaming(hash, relative4, absolute, expected) {
134106
+ 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;
134107
+ let handle;
134108
+ try {
134109
+ handle = await fs3.open(absolute, flags);
134110
+ } catch (error2) {
134111
+ const code = error2?.code;
134112
+ if (code === "ELOOP" || code === "EMLINK" || code === "EINVAL") {
134113
+ throw new ArtifactDigestStreamError(
134114
+ `prebuilt collection tree file became a symlink or unsupported node at ${relative4}`,
134115
+ { cause: error2 }
134116
+ );
134117
+ }
134118
+ throw new ArtifactDigestStreamError(
134119
+ `prebuilt collection tree file changed or became unsupported at ${relative4}`,
134120
+ { cause: error2 }
134121
+ );
134122
+ }
134123
+ try {
134124
+ const before = await handle.stat();
134125
+ if (!before.isFile() || !sameFileIdentity(before, expected)) {
134126
+ throw new ArtifactDigestStreamError(
134127
+ `prebuilt collection tree file changed or became unsupported at ${relative4}`
134128
+ );
134129
+ }
134130
+ hash.update(relative4);
134131
+ hash.update("\0");
134132
+ const content = Buffer.allocUnsafe(expected.size);
134133
+ let total = 0;
134134
+ const buffer = Buffer.alloc(64 * 1024);
134135
+ for (; ; ) {
134136
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
134137
+ if (bytesRead === 0) {
134138
+ break;
134139
+ }
134140
+ if (total + bytesRead > expected.size) {
134141
+ throw new ArtifactDigestStreamError(
134142
+ `prebuilt collection tree file changed while reading at ${relative4}`
134143
+ );
134144
+ }
134145
+ buffer.copy(content, total, 0, bytesRead);
134146
+ hash.update(buffer.subarray(0, bytesRead));
134147
+ total += bytesRead;
134148
+ }
134149
+ hash.update("\0");
134150
+ const after = await handle.stat();
134151
+ if (!after.isFile() || !sameFileIdentity(after, expected) || total !== expected.size) {
134152
+ throw new ArtifactDigestStreamError(
134153
+ `prebuilt collection tree file changed while reading at ${relative4}`
134154
+ );
134155
+ }
134156
+ return total === expected.size ? content : content.subarray(0, total);
134157
+ } finally {
134158
+ await handle.close();
134159
+ }
134160
+ }
134092
134161
  async function listFilesRelative(dir, base) {
134093
134162
  let entries;
134094
134163
  try {
@@ -134649,7 +134718,7 @@ function getCiWorkflowTemplate(provider, options = {}) {
134649
134718
  }
134650
134719
 
134651
134720
  // src/lib/github/repo-mutation.ts
134652
- var import_node_fs = require("node:fs");
134721
+ var import_node_fs2 = require("node:fs");
134653
134722
  var import_node_path = __toESM(require("node:path"), 1);
134654
134723
 
134655
134724
  // src/lib/secrets.ts
@@ -134939,7 +135008,7 @@ var RepoMutationService = class {
134939
135008
  throw new Error(this.secretMasker(changed.stderr || changed.stdout || "Failed to inspect generated changes"));
134940
135009
  }
134941
135010
  const hasPlannedRemoval = removePaths.some(
134942
- (removePath) => (0, import_node_fs.existsSync)(import_node_path.default.resolve(this.cwd, removePath))
135011
+ (removePath) => (0, import_node_fs2.existsSync)(import_node_path.default.resolve(this.cwd, removePath))
134943
135012
  );
134944
135013
  if (!changed.stdout.trim() && !hasPlannedRemoval) {
134945
135014
  return {
@@ -134963,7 +135032,7 @@ var RepoMutationService = class {
134963
135032
  await this.execute("git", ["config", "user.name", options.committerName]);
134964
135033
  await this.execute("git", ["config", "user.email", options.committerEmail]);
134965
135034
  for (const removePath of removePaths) {
134966
- (0, import_node_fs.rmSync)(import_node_path.default.resolve(this.cwd, removePath), { force: true });
135035
+ (0, import_node_fs2.rmSync)(import_node_path.default.resolve(this.cwd, removePath), { force: true });
134967
135036
  }
134968
135037
  await this.execute("git", ["add", "-A", "--", ...stagePaths]);
134969
135038
  const staged = await this.execute("git", ["diff", "--cached", "--quiet"]);
@@ -135506,14 +135575,14 @@ function createTelemetryContext(options) {
135506
135575
  }
135507
135576
 
135508
135577
  // src/action-version.ts
135509
- var import_node_fs2 = require("node:fs");
135578
+ var import_node_fs3 = require("node:fs");
135510
135579
  var import_node_path2 = require("node:path");
135511
135580
  function resolveActionVersion2() {
135512
135581
  if (false) {
135513
135582
  return void 0;
135514
135583
  }
135515
135584
  try {
135516
- const raw = (0, import_node_fs2.readFileSync)((0, import_node_path2.join)(__dirname, "..", "package.json"), "utf8");
135585
+ const raw = (0, import_node_fs3.readFileSync)((0, import_node_path2.join)(__dirname, "..", "package.json"), "utf8");
135517
135586
  return JSON.parse(raw).version ?? "unknown";
135518
135587
  } catch {
135519
135588
  return "unknown";
@@ -135752,7 +135821,8 @@ async function resolveSessionIdentity(opts) {
135752
135821
  opts.fetchImpl ?? fetch,
135753
135822
  Math.max(1, opts.maxAttempts ?? SESSION_MAX_ATTEMPTS),
135754
135823
  opts.sleepImpl ?? defaultSessionSleep,
135755
- opts.randomImpl ?? defaultRandom
135824
+ opts.randomImpl ?? defaultRandom,
135825
+ opts.onRetryEvent
135756
135826
  );
135757
135827
  sessionMemo.set(memoKey, pending);
135758
135828
  }
@@ -135786,7 +135856,7 @@ async function parseSessionResponse(response) {
135786
135856
  consumerType: coerceText(root.consumerType) ?? coerceText(data?.consumerType) ?? coerceText(user?.consumerType)
135787
135857
  };
135788
135858
  }
135789
- async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts, sleepImpl, random) {
135859
+ async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts, sleepImpl, random, onRetryEvent) {
135790
135860
  let failure = "unavailable";
135791
135861
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
135792
135862
  let response;
@@ -135798,7 +135868,9 @@ async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts
135798
135868
  } catch {
135799
135869
  failure = "unavailable";
135800
135870
  if (attempt < maxAttempts) {
135801
- await sleepImpl(computeSessionRetryDelayMs(void 0, attempt, random));
135871
+ const delay = computeSessionRetryDelayMs(void 0, attempt, random);
135872
+ onRetryEvent?.({ class: "transport", attempt, delay });
135873
+ await sleepImpl(delay);
135802
135874
  continue;
135803
135875
  }
135804
135876
  break;
@@ -135820,7 +135892,9 @@ async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts
135820
135892
  if (response.status === 429 || response.status >= 500) {
135821
135893
  failure = "unavailable";
135822
135894
  if (attempt < maxAttempts) {
135823
- await sleepImpl(computeSessionRetryDelayMs(response, attempt, random));
135895
+ const delay = computeSessionRetryDelayMs(response, attempt, random);
135896
+ onRetryEvent?.({ class: "http", status: response.status, attempt, delay });
135897
+ await sleepImpl(delay);
135824
135898
  continue;
135825
135899
  }
135826
135900
  break;
@@ -135922,7 +135996,8 @@ async function runCredentialPreflight(args) {
135922
135996
  accessToken,
135923
135997
  fetchImpl: args.fetchImpl,
135924
135998
  ...args.sleepImpl ? { sleepImpl: args.sleepImpl } : {},
135925
- ...args.randomImpl ? { randomImpl: args.randomImpl } : {}
135999
+ ...args.randomImpl ? { randomImpl: args.randomImpl } : {},
136000
+ onRetryEvent: args.retryEvent ?? args.log.retryEvent
135926
136001
  });
135927
136002
  } catch (error2) {
135928
136003
  args.log.warning(
@@ -136574,6 +136649,11 @@ var postmanRepoSyncActionContract = {
136574
136649
  description: "Contract collection ID used for exported artifacts.",
136575
136650
  required: false
136576
136651
  },
136652
+ "prebuilt-collections-json": {
136653
+ 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.",
136654
+ required: false,
136655
+ default: ""
136656
+ },
136577
136657
  "collection-sync-mode": {
136578
136658
  description: "Collection sync lifecycle mode (refresh or version).",
136579
136659
  required: false,
@@ -136816,7 +136896,7 @@ var postmanRepoSyncActionContract = {
136816
136896
  "Create or update Postman environments from runtime URLs.",
136817
136897
  "Associate Postman environments to system environments through Postman integration APIs.",
136818
136898
  "Create mock servers and smoke monitors from generated collections.",
136819
- "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.",
136899
+ "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.",
136820
136900
  "Link the Postman workspace to the repository (GitHub or GitLab) through Postman integration APIs.",
136821
136901
  "Commit synced artifacts and push them back to the current checked out ref."
136822
136902
  ],
@@ -136927,13 +137007,36 @@ var PostmanGatewayAssetsClient = class {
136927
137007
  name: String(entry.name ?? entry.message ?? "").trim()
136928
137008
  })).filter((entry) => entry.id || entry.name);
136929
137009
  }
137010
+ /**
137011
+ * Delete a collection (GC path). DELETE is idempotent: transient 408/429/5xx
137012
+ * (including Bifrost ESOCKETTIMEDOUT envelopes) are retried with the same
137013
+ * bounded budget as other idempotent writes; 404 is success (already gone).
137014
+ * Ordinary 4xx are not retried.
137015
+ */
136930
137016
  async deleteCollection(collectionUid) {
136931
137017
  const bareId = String(collectionUid).split("-").slice(-5).join("-") || collectionUid;
136932
- await this.gateway.requestJson({
136933
- service: "collection",
136934
- method: "delete",
136935
- path: `/v3/collections/${bareId}`
136936
- });
137018
+ try {
137019
+ await retry(
137020
+ () => this.gateway.requestJson({
137021
+ service: "collection",
137022
+ method: "delete",
137023
+ path: `/v3/collections/${bareId}`
137024
+ }),
137025
+ {
137026
+ maxAttempts: 5,
137027
+ delayMs: 2e3,
137028
+ backoffMultiplier: 2,
137029
+ maxDelayMs: 15e3,
137030
+ sleep: this.sleep,
137031
+ shouldRetry: (e) => this.isRetryableIdempotentWriteOutcome(e)
137032
+ }
137033
+ );
137034
+ } catch (error2) {
137035
+ if (error2 instanceof HttpError && error2.status === 404) {
137036
+ return;
137037
+ }
137038
+ throw error2;
137039
+ }
136937
137040
  }
136938
137041
  async listSpecifications(workspaceId) {
136939
137042
  const response = await this.gateway.requestJson({
@@ -137601,6 +137704,7 @@ var AccessTokenProvider = class {
137601
137704
  maxAttempts;
137602
137705
  onToken;
137603
137706
  sleep;
137707
+ onRetryEvent;
137604
137708
  inflight;
137605
137709
  constructor(options) {
137606
137710
  this.token = String(options.accessToken || "").trim();
@@ -137612,6 +137716,7 @@ var AccessTokenProvider = class {
137612
137716
  this.maxAttempts = Math.max(1, options.maxAttempts ?? 2);
137613
137717
  this.onToken = options.onToken;
137614
137718
  this.sleep = options.sleep;
137719
+ this.onRetryEvent = options.onRetryEvent;
137615
137720
  }
137616
137721
  current() {
137617
137722
  return this.token;
@@ -137637,7 +137742,12 @@ var AccessTokenProvider = class {
137637
137742
  delayMs: 1e3,
137638
137743
  backoffMultiplier: 2,
137639
137744
  ...this.sleep ? { sleep: this.sleep } : {},
137640
- shouldRetry: (error2) => !(error2 instanceof MintError && error2.permanent)
137745
+ shouldRetry: (error2) => !(error2 instanceof MintError && error2.permanent),
137746
+ onRetry: ({ attempt, delayMs, error: error2 }) => {
137747
+ const status = error2 instanceof MintError ? error2.status : void 0;
137748
+ const retryClass = status === void 0 ? "transport" : "http";
137749
+ this.onRetryEvent?.({ class: retryClass, ...status === void 0 ? {} : { status }, attempt, delay: Math.max(0, delayMs) });
137750
+ }
137641
137751
  });
137642
137752
  this.token = token;
137643
137753
  this.onToken?.(token);
@@ -137668,7 +137778,7 @@ var AccessTokenProvider = class {
137668
137778
  true
137669
137779
  );
137670
137780
  }
137671
- throw new MintError(`postman: re-mint failed (service-account-tokens HTTP ${status}).`, false);
137781
+ throw new MintError(`postman: re-mint failed (service-account-tokens HTTP ${status}).`, false, status);
137672
137782
  }
137673
137783
  let parsed;
137674
137784
  try {
@@ -137699,7 +137809,8 @@ async function mintAccessTokenIfNeeded(inputs, log, setSecret2, fetchImpl = fetc
137699
137809
  apiKey: inputs.postmanApiKey,
137700
137810
  apiBaseUrl,
137701
137811
  fetchImpl,
137702
- onToken: (token) => setSecret2?.(token)
137812
+ onToken: (token) => setSecret2?.(token),
137813
+ onRetryEvent: (event) => log.retryEvent?.(event)
137703
137814
  });
137704
137815
  try {
137705
137816
  inputs.postmanAccessToken = await provider.refresh();
@@ -137739,6 +137850,7 @@ var AccessTokenGatewayClient = class {
137739
137850
  requestTimeoutMs;
137740
137851
  retryMaxDelayMs;
137741
137852
  randomImpl;
137853
+ onRetryEvent;
137742
137854
  constructor(options) {
137743
137855
  this.tokenProvider = options.tokenProvider;
137744
137856
  this.bifrostBaseUrl = String(
@@ -137757,6 +137869,7 @@ var AccessTokenGatewayClient = class {
137757
137869
  this.requestTimeoutMs = options.requestTimeoutMs ?? 3e4;
137758
137870
  this.retryMaxDelayMs = options.retryMaxDelayMs ?? 5e3;
137759
137871
  this.randomImpl = options.randomImpl ?? Math.random;
137872
+ this.onRetryEvent = options.onRetryEvent;
137760
137873
  }
137761
137874
  configureTeamContext(teamId, orgMode) {
137762
137875
  this.teamId = String(teamId || "").trim();
@@ -137825,8 +137938,9 @@ var AccessTokenGatewayClient = class {
137825
137938
  if (!this.fallbackBaseUrl) return false;
137826
137939
  return retryTransient || request.fallback === "auto";
137827
137940
  }
137828
- async attemptFallback(request, retryTransient) {
137941
+ async attemptFallback(request, retryTransient, status) {
137829
137942
  if (!this.fallbackEligible(request, retryTransient)) return null;
137943
+ this.emitRetryEvent("fallback", status, 1, 0);
137830
137944
  const response = await this.tryFallback(request);
137831
137945
  if (!response) return null;
137832
137946
  if (response.ok) return response;
@@ -137852,6 +137966,7 @@ var AccessTokenGatewayClient = class {
137852
137966
  } catch (error2) {
137853
137967
  if (retryTransient && attempt < this.maxRetries) {
137854
137968
  const delay = this.retryDelayMs(attempt);
137969
+ this.emitRetryEvent("transport", void 0, attempt + 1, delay);
137855
137970
  attempt += 1;
137856
137971
  await this.sleepImpl(delay);
137857
137972
  continue;
@@ -137865,7 +137980,9 @@ var AccessTokenGatewayClient = class {
137865
137980
  const inner = this.innerStatus(okBody);
137866
137981
  if (inner !== void 0) {
137867
137982
  if (retryTransient && this.isTransient(inner, okBody) && attempt < this.maxRetries) {
137868
- await this.sleepImpl(this.retryDelayMs(attempt));
137983
+ const delay = this.retryDelayMs(attempt);
137984
+ this.emitRetryEvent("inner", inner, attempt + 1, delay);
137985
+ await this.sleepImpl(delay);
137869
137986
  attempt += 1;
137870
137987
  continue;
137871
137988
  }
@@ -137875,6 +137992,7 @@ var AccessTokenGatewayClient = class {
137875
137992
  }
137876
137993
  const body = await response.text().catch(() => "");
137877
137994
  if (isExpiredAuthError(response.status, body) && this.tokenProvider.canRefresh()) {
137995
+ this.emitRetryEvent("auth", response.status, 1, 0);
137878
137996
  await this.tokenProvider.refresh();
137879
137997
  response = await this.send(request);
137880
137998
  if (response.ok) {
@@ -137885,11 +138003,12 @@ var AccessTokenGatewayClient = class {
137885
138003
  }
137886
138004
  if (retryTransient && this.isTransient(response.status, body) && attempt < this.maxRetries) {
137887
138005
  const delay = this.retryDelayMs(attempt, parseRetryAfterMs2(response.headers.get("retry-after")));
138006
+ this.emitRetryEvent("http", response.status, attempt + 1, delay);
137888
138007
  attempt += 1;
137889
138008
  await this.sleepImpl(delay);
137890
138009
  continue;
137891
138010
  }
137892
- const fallbackResponse = await this.attemptFallback(request, retryTransient);
138011
+ const fallbackResponse = await this.attemptFallback(request, retryTransient, response.status);
137893
138012
  if (fallbackResponse) return fallbackResponse;
137894
138013
  throw this.toHttpError(request, response, body);
137895
138014
  }
@@ -137897,6 +138016,14 @@ var AccessTokenGatewayClient = class {
137897
138016
  retryDelayMs(attempt, retryAfter) {
137898
138017
  return retryAfter === void 0 ? fullJitterDelayMs(attempt, this.retryBaseDelayMs, this.retryMaxDelayMs, this.randomImpl) : Math.min(this.retryMaxDelayMs, retryAfter);
137899
138018
  }
138019
+ emitRetryEvent(retryClass, status, attempt, delay) {
138020
+ this.onRetryEvent?.({
138021
+ class: retryClass,
138022
+ ...status === void 0 ? {} : { status },
138023
+ attempt: Math.max(1, attempt),
138024
+ delay: Math.max(0, delay)
138025
+ });
138026
+ }
137900
138027
  isTransient(status, body) {
137901
138028
  return status === 408 || status === 429 || status >= 500 || /ESOCKETTIMEDOUT|ETIMEDOUT|ECONNRESET|serverError|downstream/.test(body);
137902
138029
  }
@@ -138026,7 +138153,7 @@ function validateCertMaterial(certBase64, keyBase64, passphrase) {
138026
138153
  }
138027
138154
 
138028
138155
  // src/lib/repo/branch-decision.ts
138029
- var import_node_fs3 = require("node:fs");
138156
+ var import_node_fs4 = require("node:fs");
138030
138157
  var import_node_crypto3 = require("node:crypto");
138031
138158
  var ContractError = class extends Error {
138032
138159
  code;
@@ -138067,7 +138194,7 @@ function readGithubEvent(env) {
138067
138194
  const path9 = clean(env.GITHUB_EVENT_PATH);
138068
138195
  if (!path9) return void 0;
138069
138196
  try {
138070
- return JSON.parse((0, import_node_fs3.readFileSync)(path9, "utf8"));
138197
+ return JSON.parse((0, import_node_fs4.readFileSync)(path9, "utf8"));
138071
138198
  } catch {
138072
138199
  return void 0;
138073
138200
  }
@@ -138523,6 +138650,7 @@ function resolveInputs(env = process.env) {
138523
138650
  baselineCollectionId: getInput2("baseline-collection-id", env),
138524
138651
  smokeCollectionId: getInput2("smoke-collection-id", env),
138525
138652
  contractCollectionId: getInput2("contract-collection-id", env),
138653
+ prebuiltCollectionsJson: getInput2("prebuilt-collections-json", env),
138526
138654
  specId: getInput2("spec-id", env),
138527
138655
  specContentChanged: parseBooleanInput(getInput2("spec-content-changed", env), true),
138528
138656
  specPath: getInput2("spec-path", env),
@@ -138638,7 +138766,7 @@ var StateUnreadableError = class extends Error {
138638
138766
  function readResourcesState() {
138639
138767
  let raw;
138640
138768
  try {
138641
- raw = (0, import_node_fs4.readFileSync)(".postman/resources.yaml", "utf8");
138769
+ raw = (0, import_node_fs5.readFileSync)(".postman/resources.yaml", "utf8");
138642
138770
  } catch {
138643
138771
  return null;
138644
138772
  }
@@ -138697,12 +138825,15 @@ function getEnvironmentUidsFromResources(resourcesState) {
138697
138825
  function normalizeToPosix(filePath) {
138698
138826
  return filePath.split(path8.sep).join("/").replace(/\\/g, "/");
138699
138827
  }
138828
+ function canonicalizeRelativePath(value) {
138829
+ return normalizeToPosix(String(value ?? "").trim()).replace(/^\.\/+/, "").replace(/\/{2,}/g, "/").replace(/\/+$/g, "");
138830
+ }
138700
138831
  function isOpenApiSpecFile(filePath) {
138701
138832
  if (!(filePath.endsWith(".json") || filePath.endsWith(".yaml") || filePath.endsWith(".yml"))) {
138702
138833
  return false;
138703
138834
  }
138704
138835
  try {
138705
- const raw = (0, import_node_fs4.readFileSync)(filePath, "utf8");
138836
+ const raw = (0, import_node_fs5.readFileSync)(filePath, "utf8");
138706
138837
  const parsed = filePath.endsWith(".json") ? JSON.parse(raw) : load(raw);
138707
138838
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
138708
138839
  return false;
@@ -138731,7 +138862,7 @@ function scanLocalSpecReferences(baseDir = ".") {
138731
138862
  const found = /* @__PURE__ */ new Set();
138732
138863
  const refs = [];
138733
138864
  const visit2 = (currentDir) => {
138734
- for (const entry of (0, import_node_fs4.readdirSync)(currentDir, { withFileTypes: true })) {
138865
+ for (const entry of (0, import_node_fs5.readdirSync)(currentDir, { withFileTypes: true })) {
138735
138866
  if (ignoredDirs.has(entry.name)) {
138736
138867
  continue;
138737
138868
  }
@@ -138761,7 +138892,7 @@ function resolveMappedSpecReference(explicitSpecPath, discoveredSpecs) {
138761
138892
  const normalizedExplicitPath = normalizeToPosix(explicitSpecPath.trim());
138762
138893
  if (normalizedExplicitPath) {
138763
138894
  const explicitFullPath = path8.resolve(normalizedExplicitPath);
138764
- if ((0, import_node_fs4.existsSync)(explicitFullPath) && (0, import_node_fs4.statSync)(explicitFullPath).isFile()) {
138895
+ if ((0, import_node_fs5.existsSync)(explicitFullPath) && (0, import_node_fs5.statSync)(explicitFullPath).isFile()) {
138765
138896
  return {
138766
138897
  repoRelativePath: normalizedExplicitPath,
138767
138898
  configRelativePath: normalizeToPosix(path8.join("..", normalizedExplicitPath))
@@ -138813,6 +138944,7 @@ function readActionInputs(actionCore) {
138813
138944
  INPUT_BASELINE_COLLECTION_ID: readInput(actionCore, "baseline-collection-id"),
138814
138945
  INPUT_SMOKE_COLLECTION_ID: readInput(actionCore, "smoke-collection-id"),
138815
138946
  INPUT_CONTRACT_COLLECTION_ID: readInput(actionCore, "contract-collection-id"),
138947
+ INPUT_PREBUILT_COLLECTIONS_JSON: readInput(actionCore, "prebuilt-collections-json"),
138816
138948
  INPUT_SPEC_ID: readInput(actionCore, "spec-id"),
138817
138949
  INPUT_SPEC_PATH: readInput(actionCore, "spec-path"),
138818
138950
  INPUT_COLLECTION_SYNC_MODE: readInput(actionCore, "collection-sync-mode") || "refresh",
@@ -139026,6 +139158,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
139026
139158
  );
139027
139159
  }
139028
139160
  envUids[envName] = existingUid;
139161
+ dependencies.core.setOutput("environment-uids-json", JSON.stringify(envUids));
139029
139162
  continue;
139030
139163
  }
139031
139164
  const values = buildEnvironmentValues(envName, runtimeUrl);
@@ -139036,6 +139169,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
139036
139169
  displayName,
139037
139170
  values
139038
139171
  );
139172
+ dependencies.core.setOutput("environment-uids-json", JSON.stringify(envUids));
139039
139173
  } catch (error2) {
139040
139174
  throw new Error(
139041
139175
  formatOrchestrationIssue({
@@ -139052,7 +139186,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
139052
139186
  return envUids;
139053
139187
  }
139054
139188
  function ensureDir(path9) {
139055
- (0, import_node_fs4.mkdirSync)(path9, { recursive: true });
139189
+ (0, import_node_fs5.mkdirSync)(path9, { recursive: true });
139056
139190
  }
139057
139191
  function getCollectionDirectoryName(kind, projectName) {
139058
139192
  if (kind === "Baseline") {
@@ -139089,7 +139223,7 @@ function stripVolatileFields(obj) {
139089
139223
  }
139090
139224
  function writeJsonFile(path9, content, normalize4 = false) {
139091
139225
  const data = normalize4 ? stripVolatileFields(content) : content;
139092
- (0, import_node_fs4.writeFileSync)(path9, JSON.stringify(data, null, 2));
139226
+ (0, import_node_fs5.writeFileSync)(path9, JSON.stringify(data, null, 2));
139093
139227
  }
139094
139228
  function buildMappedSpecCloudKey(mappedSource, specSyncMode, releaseLabel) {
139095
139229
  if (specSyncMode !== "version") {
@@ -139153,22 +139287,422 @@ function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir,
139153
139287
  sortKeys: false
139154
139288
  });
139155
139289
  }
139156
- function buildSpecCollectionWorkflowManifest(specRef, collectionRefs) {
139157
- return dump(
139158
- {
139159
- workflows: {
139160
- syncSpecToCollection: collectionRefs.map((collectionRef) => ({
139161
- spec: specRef,
139162
- collection: collectionRef
139163
- }))
139290
+ function isPlainObject2(value) {
139291
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
139292
+ }
139293
+ function buildSpecCollectionWorkflowManifest(specRef, collectionRefs, existingRaw) {
139294
+ const pairs = collectionRefs.map((collectionRef) => ({
139295
+ spec: specRef,
139296
+ collection: collectionRef
139297
+ }));
139298
+ let root = {};
139299
+ if (typeof existingRaw === "string" && existingRaw.trim()) {
139300
+ let parsed;
139301
+ try {
139302
+ parsed = load(existingRaw);
139303
+ } catch (error2) {
139304
+ throw new Error(
139305
+ `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.`,
139306
+ { cause: error2 }
139307
+ );
139308
+ }
139309
+ if (parsed === null || parsed === void 0) {
139310
+ root = {};
139311
+ } else if (!isPlainObject2(parsed)) {
139312
+ throw new Error(
139313
+ "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."
139314
+ );
139315
+ } else {
139316
+ root = { ...parsed };
139317
+ }
139318
+ }
139319
+ const workflows = isPlainObject2(root.workflows) ? { ...root.workflows } : {};
139320
+ const desiredCollections = new Set(collectionRefs);
139321
+ const currentPairs = (Array.isArray(workflows.syncSpecToCollection) ? workflows.syncSpecToCollection : []).map((entry) => isPlainObject2(entry) ? { ...entry } : null).filter((entry) => Boolean(entry)).filter(
139322
+ (entry) => String(entry.spec ?? "") !== specRef || desiredCollections.has(String(entry.collection ?? ""))
139323
+ );
139324
+ for (const pair of pairs) {
139325
+ const index = currentPairs.findIndex(
139326
+ (entry) => String(entry.spec ?? "") === pair.spec && String(entry.collection ?? "") === pair.collection
139327
+ );
139328
+ if (index >= 0) {
139329
+ const previous = currentPairs[index];
139330
+ currentPairs[index] = { ...previous, spec: pair.spec, collection: pair.collection };
139331
+ } else {
139332
+ currentPairs.push({ spec: pair.spec, collection: pair.collection });
139333
+ }
139334
+ }
139335
+ workflows.syncSpecToCollection = currentPairs;
139336
+ root.workflows = workflows;
139337
+ return dump(root, {
139338
+ lineWidth: -1,
139339
+ noRefs: true,
139340
+ sortKeys: false
139341
+ });
139342
+ }
139343
+ var PREBUILT_COLLECTION_ROLES = /* @__PURE__ */ new Set([
139344
+ "baseline",
139345
+ "smoke",
139346
+ "contract"
139347
+ ]);
139348
+ var SHA256_HEX = /^[a-f0-9]{64}$/;
139349
+ var PREBUILT_CLOUD_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
139350
+ function failPrebuiltCollections(detail) {
139351
+ throw new Error(`CONTRACT_PREBUILT_COLLECTIONS_INVALID: ${detail}`);
139352
+ }
139353
+ function parsePrebuiltCollectionsJson(raw) {
139354
+ const trimmed = String(raw ?? "").trim();
139355
+ if (!trimmed) {
139356
+ return [];
139357
+ }
139358
+ let parsed;
139359
+ try {
139360
+ parsed = JSON.parse(trimmed);
139361
+ } catch (error2) {
139362
+ throw new Error(
139363
+ `CONTRACT_PREBUILT_COLLECTIONS_INVALID: malformed JSON (${error2 instanceof Error ? error2.message : String(error2)})`,
139364
+ { cause: error2 }
139365
+ );
139366
+ }
139367
+ let entriesRaw;
139368
+ if (Array.isArray(parsed)) {
139369
+ entriesRaw = parsed;
139370
+ } else if (isPlainObject2(parsed)) {
139371
+ if (parsed.schemaVersion !== 1) {
139372
+ failPrebuiltCollections("schemaVersion must be 1");
139373
+ }
139374
+ if (!Array.isArray(parsed.collections)) {
139375
+ failPrebuiltCollections("collections must be an array when schemaVersion is set");
139376
+ }
139377
+ entriesRaw = parsed.collections;
139378
+ } else {
139379
+ failPrebuiltCollections("expected a JSON array or {schemaVersion:1,collections:[]}");
139380
+ }
139381
+ const seenRoles = /* @__PURE__ */ new Set();
139382
+ const entries = [];
139383
+ for (const [index, value] of entriesRaw.entries()) {
139384
+ if (!isPlainObject2(value)) {
139385
+ failPrebuiltCollections(`collections[${index}] must be an object`);
139386
+ }
139387
+ const roleRaw = String(value.role ?? "").trim();
139388
+ if (!PREBUILT_COLLECTION_ROLES.has(roleRaw)) {
139389
+ failPrebuiltCollections(
139390
+ `collections[${index}].role must be one of baseline|smoke|contract`
139391
+ );
139392
+ }
139393
+ const role = roleRaw;
139394
+ if (seenRoles.has(role)) {
139395
+ failPrebuiltCollections(`duplicate role ${role}`);
139396
+ }
139397
+ seenRoles.add(role);
139398
+ const collectionPath = String(
139399
+ value.collectionPath ?? value.path ?? ""
139400
+ ).trim();
139401
+ if (!collectionPath || hasControlCharacter2(collectionPath)) {
139402
+ failPrebuiltCollections(
139403
+ `collections[${index}].collectionPath must be a non-empty confined relative path`
139404
+ );
139405
+ }
139406
+ const cloudId = String(value.cloudId ?? "").trim();
139407
+ if (!cloudId || !PREBUILT_CLOUD_ID.test(cloudId)) {
139408
+ failPrebuiltCollections(
139409
+ `collections[${index}].cloudId must be a non-empty Postman collection id`
139410
+ );
139411
+ }
139412
+ let payloadDigest;
139413
+ if (Object.prototype.hasOwnProperty.call(value, "payloadDigest")) {
139414
+ const payloadDigestRaw = value.payloadDigest;
139415
+ if (payloadDigestRaw !== void 0 && payloadDigestRaw !== null) {
139416
+ payloadDigest = String(payloadDigestRaw).trim();
139417
+ if (!SHA256_HEX.test(payloadDigest)) {
139418
+ failPrebuiltCollections(
139419
+ `collections[${index}].payloadDigest must be lowercase 64-hex`
139420
+ );
139421
+ }
139164
139422
  }
139165
- },
139166
- {
139167
- lineWidth: -1,
139168
- noRefs: true,
139169
- sortKeys: false
139170
139423
  }
139424
+ const artifactDigest = String(value.artifactDigest ?? "").trim();
139425
+ if (!SHA256_HEX.test(artifactDigest)) {
139426
+ failPrebuiltCollections(
139427
+ `collections[${index}].artifactDigest must be lowercase 64-hex`
139428
+ );
139429
+ }
139430
+ entries.push({
139431
+ role,
139432
+ collectionPath,
139433
+ cloudId,
139434
+ ...payloadDigest !== void 0 ? { payloadDigest } : {},
139435
+ artifactDigest
139436
+ });
139437
+ }
139438
+ return entries;
139439
+ }
139440
+ function assertPathWithinArtifactRoot(targetPath, artifactDir, fieldName) {
139441
+ assertPathWithinCwd(targetPath, fieldName);
139442
+ assertPathWithinCwd(artifactDir, "artifact-dir");
139443
+ const cwd = (0, import_node_fs5.realpathSync)(process.cwd());
139444
+ const artifactRoot = path8.resolve(cwd, artifactDir.trim());
139445
+ const resolved = path8.resolve(cwd, targetPath.trim());
139446
+ const relativeToArtifact = path8.relative(artifactRoot, resolved);
139447
+ if (!relativeToArtifact || relativeToArtifact.startsWith("..") || path8.isAbsolute(relativeToArtifact)) {
139448
+ failPrebuiltCollections(
139449
+ `${fieldName} must stay under artifact-dir (${artifactDir}); received ${targetPath}`
139450
+ );
139451
+ }
139452
+ let existingPath = resolved;
139453
+ while (!(0, import_node_fs5.existsSync)(existingPath)) {
139454
+ const parent = path8.dirname(existingPath);
139455
+ if (parent === existingPath) {
139456
+ break;
139457
+ }
139458
+ existingPath = parent;
139459
+ }
139460
+ const realExisting = (0, import_node_fs5.realpathSync)(existingPath);
139461
+ const realRelative = path8.relative(artifactRoot, realExisting);
139462
+ if (realRelative.startsWith("..") || path8.isAbsolute(realRelative)) {
139463
+ failPrebuiltCollections(
139464
+ `${fieldName} resolves outside artifact-dir via symlink; received ${targetPath}`
139465
+ );
139466
+ }
139467
+ return normalizeToPosix(path8.relative(cwd, resolved));
139468
+ }
139469
+ function prebuiltDirectoryTraversalIdentity(absolutePath, stats, options = {}) {
139470
+ const platform2 = options.platform ?? process.platform;
139471
+ const inodeIsZero = stats.ino === 0 || stats.ino === 0n;
139472
+ if (platform2 !== "win32" && !inodeIsZero) {
139473
+ return `${stats.dev}:${stats.ino}`;
139474
+ }
139475
+ const resolveCanonicalPath = options.resolveCanonicalPath ?? ((candidate) => import_node_fs5.realpathSync.native(candidate));
139476
+ const canonical = resolveCanonicalPath(absolutePath);
139477
+ return platform2 === "win32" ? canonical.toLowerCase() : canonical;
139478
+ }
139479
+ function listPrebuiltCollectionTreeFiles(collectionPath, artifactDir) {
139480
+ const confined = assertPathWithinArtifactRoot(
139481
+ collectionPath,
139482
+ artifactDir,
139483
+ "prebuilt collection path"
139484
+ );
139485
+ const absRoot = path8.resolve(process.cwd(), confined);
139486
+ if (!(0, import_node_fs5.existsSync)(absRoot)) {
139487
+ return [];
139488
+ }
139489
+ let rootStat;
139490
+ try {
139491
+ rootStat = (0, import_node_fs5.lstatSync)(absRoot);
139492
+ } catch {
139493
+ return [];
139494
+ }
139495
+ if (rootStat.isSymbolicLink()) {
139496
+ failPrebuiltCollections(
139497
+ `prebuilt collection path must not be a symlink; received ${collectionPath}`
139498
+ );
139499
+ }
139500
+ if (!rootStat.isDirectory()) {
139501
+ return [];
139502
+ }
139503
+ const files = [];
139504
+ const pendingDirectories = [absRoot];
139505
+ const seenDirectories = /* @__PURE__ */ new Set();
139506
+ while (pendingDirectories.length > 0) {
139507
+ const currentAbsolute = pendingDirectories.pop();
139508
+ if (!currentAbsolute) {
139509
+ break;
139510
+ }
139511
+ const currentStat = (0, import_node_fs5.lstatSync)(currentAbsolute);
139512
+ if (currentStat.isSymbolicLink() || !currentStat.isDirectory()) {
139513
+ failPrebuiltCollections(
139514
+ `prebuilt collection tree directory changed or became unsupported at ${normalizeToPosix(path8.relative(absRoot, currentAbsolute)) || "."}`
139515
+ );
139516
+ }
139517
+ const currentKey = prebuiltDirectoryTraversalIdentity(currentAbsolute, currentStat);
139518
+ if (seenDirectories.has(currentKey)) {
139519
+ failPrebuiltCollections(
139520
+ `prebuilt collection tree directory cycle detected at ${normalizeToPosix(path8.relative(absRoot, currentAbsolute)) || "."}`
139521
+ );
139522
+ }
139523
+ seenDirectories.add(currentKey);
139524
+ const entries = (0, import_node_fs5.readdirSync)(currentAbsolute, { withFileTypes: true });
139525
+ for (const entry of entries) {
139526
+ const abs = path8.join(currentAbsolute, entry.name);
139527
+ if (entry.isSymbolicLink()) {
139528
+ failPrebuiltCollections(
139529
+ `prebuilt collection tree must not contain symlinks; received ${normalizeToPosix(path8.relative(absRoot, abs))}`
139530
+ );
139531
+ }
139532
+ if (entry.isDirectory()) {
139533
+ pendingDirectories.push(abs);
139534
+ continue;
139535
+ }
139536
+ if (!entry.isFile()) {
139537
+ failPrebuiltCollections(
139538
+ `prebuilt collection tree contains unsupported entry type at ${normalizeToPosix(path8.relative(absRoot, abs))}`
139539
+ );
139540
+ }
139541
+ const fileLstat = (0, import_node_fs5.lstatSync)(abs);
139542
+ if (fileLstat.isSymbolicLink() || !fileLstat.isFile()) {
139543
+ failPrebuiltCollections(
139544
+ `prebuilt collection tree file changed or became unsupported at ${normalizeToPosix(path8.relative(absRoot, abs))}`
139545
+ );
139546
+ }
139547
+ files.push({
139548
+ absolute: abs,
139549
+ relative: normalizeToPosix(path8.relative(absRoot, abs)),
139550
+ dev: fileLstat.dev,
139551
+ ino: fileLstat.ino,
139552
+ size: fileLstat.size
139553
+ });
139554
+ }
139555
+ }
139556
+ files.sort((a, b) => a.relative.localeCompare(b.relative));
139557
+ return files;
139558
+ }
139559
+ function validateCanonicalV3CollectionFile(relative4, bytes) {
139560
+ let expectedKind;
139561
+ if (/(^|\/)\.resources\/definition\.yaml$/.test(relative4)) {
139562
+ expectedKind = "collection";
139563
+ } else if (relative4.endsWith(".request.yaml")) {
139564
+ expectedKind = "request";
139565
+ } else if (relative4.endsWith(".example.yaml")) {
139566
+ expectedKind = "example";
139567
+ } else if (relative4.endsWith(".message.yaml")) {
139568
+ expectedKind = "message";
139569
+ } else {
139570
+ failPrebuiltCollections(`prebuilt collection tree contains unexpected file ${relative4}`);
139571
+ }
139572
+ let parsed;
139573
+ try {
139574
+ parsed = load(bytes.toString("utf8"));
139575
+ } catch (error2) {
139576
+ failPrebuiltCollections(
139577
+ `prebuilt collection tree contains malformed YAML at ${relative4} (${error2 instanceof Error ? error2.message : String(error2)})`
139578
+ );
139579
+ }
139580
+ if (!isPlainObject2(parsed)) {
139581
+ failPrebuiltCollections(`prebuilt collection tree file ${relative4} must contain a YAML mapping`);
139582
+ }
139583
+ const kind = typeof parsed.$kind === "string" ? parsed.$kind.trim() : "";
139584
+ if (!kind) {
139585
+ failPrebuiltCollections(`prebuilt collection tree file ${relative4} must contain a nonempty $kind`);
139586
+ }
139587
+ const agrees = expectedKind === "collection" ? kind === "collection" : kind.endsWith(`-${expectedKind}`);
139588
+ if (!agrees) {
139589
+ failPrebuiltCollections(
139590
+ `prebuilt collection tree file ${relative4} has $kind ${kind} inconsistent with its filename`
139591
+ );
139592
+ }
139593
+ }
139594
+ async function digestAndValidatePrebuiltCollectionTree(files) {
139595
+ if (!files.some((file) => file.relative === ".resources/definition.yaml")) {
139596
+ failPrebuiltCollections("prebuilt collection tree is missing .resources/definition.yaml");
139597
+ }
139598
+ const hash = (0, import_node_crypto4.createHash)("sha256");
139599
+ for (const file of files) {
139600
+ let bytes;
139601
+ try {
139602
+ bytes = await appendArtifactDigestFileStreaming(hash, file.relative, file.absolute, {
139603
+ dev: file.dev,
139604
+ ino: file.ino,
139605
+ size: file.size
139606
+ });
139607
+ } catch (error2) {
139608
+ if (error2 instanceof ArtifactDigestStreamError) {
139609
+ failPrebuiltCollections(error2.message);
139610
+ }
139611
+ const code = error2?.code;
139612
+ if (code === "ELOOP" || code === "EPERM") {
139613
+ failPrebuiltCollections(
139614
+ `prebuilt collection tree file became a symlink or unsupported node at ${file.relative}`
139615
+ );
139616
+ }
139617
+ failPrebuiltCollections(
139618
+ `prebuilt collection tree file changed or became unsupported at ${file.relative}`
139619
+ );
139620
+ }
139621
+ validateCanonicalV3CollectionFile(file.relative, bytes);
139622
+ }
139623
+ return hash.digest("hex");
139624
+ }
139625
+ async function preparePrebuiltCollections(inputs) {
139626
+ const prepared = /* @__PURE__ */ new Map();
139627
+ for (const entry of parsePrebuiltCollectionsJson(inputs.prebuiltCollectionsJson ?? "")) {
139628
+ const confinedPath = assertPathWithinArtifactRoot(
139629
+ entry.collectionPath,
139630
+ inputs.artifactDir,
139631
+ `prebuilt ${entry.role} collection path`
139632
+ );
139633
+ const absolute = path8.resolve(process.cwd(), confinedPath);
139634
+ if (!(0, import_node_fs5.existsSync)(absolute)) {
139635
+ prepared.set(entry.role, { entry, confinedPath });
139636
+ continue;
139637
+ }
139638
+ const files = listPrebuiltCollectionTreeFiles(confinedPath, inputs.artifactDir);
139639
+ const artifactDigest = await digestAndValidatePrebuiltCollectionTree(files);
139640
+ prepared.set(entry.role, {
139641
+ entry,
139642
+ confinedPath,
139643
+ artifactDigest
139644
+ });
139645
+ }
139646
+ return prepared;
139647
+ }
139648
+ function tryReusePrebuiltCollection(options) {
139649
+ const { prepared, expectedPath, expectedCloudId } = options;
139650
+ const { entry } = prepared;
139651
+ if (!expectedCloudId || entry.cloudId !== expectedCloudId) {
139652
+ return false;
139653
+ }
139654
+ const expectedNormalized = canonicalizeRelativePath(expectedPath);
139655
+ const entryNormalized = canonicalizeRelativePath(entry.collectionPath);
139656
+ if (entryNormalized !== expectedNormalized) {
139657
+ return false;
139658
+ }
139659
+ const confined = canonicalizeRelativePath(prepared.confinedPath);
139660
+ if (confined !== expectedNormalized) {
139661
+ return false;
139662
+ }
139663
+ const expectedName = path8.posix.basename(expectedNormalized);
139664
+ if (path8.posix.basename(confined) !== expectedName) {
139665
+ return false;
139666
+ }
139667
+ return prepared.artifactDigest === entry.artifactDigest;
139668
+ }
139669
+ async function exportCollectionArtifact(options) {
139670
+ const {
139671
+ role,
139672
+ collectionId,
139673
+ dirName,
139674
+ collectionsDir,
139675
+ prebuiltByRole,
139676
+ postman,
139677
+ core
139678
+ } = options;
139679
+ if (!collectionId) {
139680
+ return void 0;
139681
+ }
139682
+ const expectedPath = `${collectionsDir}/${dirName}`;
139683
+ const entry = prebuiltByRole.get(role);
139684
+ if (entry) {
139685
+ const reused = tryReusePrebuiltCollection({
139686
+ prepared: entry,
139687
+ expectedPath,
139688
+ expectedCloudId: collectionId
139689
+ });
139690
+ if (reused) {
139691
+ core.info(
139692
+ `Reusing prebuilt ${role} collection tree at ${expectedPath} (artifactDigest match)`
139693
+ );
139694
+ return `../${expectedPath}`;
139695
+ }
139696
+ core.info(
139697
+ `Prebuilt ${role} collection entry present but did not exactly match; exporting from cloud`
139698
+ );
139699
+ }
139700
+ const col = await postman.getCollection(collectionId);
139701
+ await convertAndSplitAnyCollection(
139702
+ col,
139703
+ expectedPath
139171
139704
  );
139705
+ return `../${expectedPath}`;
139172
139706
  }
139173
139707
  function hasControlCharacter2(value) {
139174
139708
  for (let index = 0; index < value.length; index += 1) {
@@ -139186,21 +139720,21 @@ function assertPathWithinCwd(targetPath, fieldName) {
139186
139720
  if (!rawPath || hasControlCharacter2(originalPath) || path8.isAbsolute(rawPath) || path8.win32.isAbsolute(rawPath) || segments.includes("..") || rawPath.startsWith(":") || hasControlCharacter2(rawPath)) {
139187
139721
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
139188
139722
  }
139189
- const base = (0, import_node_fs4.realpathSync)(process.cwd());
139723
+ const base = (0, import_node_fs5.realpathSync)(process.cwd());
139190
139724
  const resolved = path8.resolve(base, rawPath);
139191
139725
  const relative4 = path8.relative(base, resolved);
139192
139726
  if (relative4.startsWith("..") || path8.isAbsolute(relative4)) {
139193
139727
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
139194
139728
  }
139195
139729
  let existingPath = resolved;
139196
- while (!(0, import_node_fs4.existsSync)(existingPath)) {
139730
+ while (!(0, import_node_fs5.existsSync)(existingPath)) {
139197
139731
  const parent = path8.dirname(existingPath);
139198
139732
  if (parent === existingPath) {
139199
139733
  break;
139200
139734
  }
139201
139735
  existingPath = parent;
139202
139736
  }
139203
- const realExistingPath = (0, import_node_fs4.realpathSync)(existingPath);
139737
+ const realExistingPath = (0, import_node_fs5.realpathSync)(existingPath);
139204
139738
  const realRelative = path8.relative(base, realExistingPath);
139205
139739
  if (realRelative.startsWith("..") || path8.isAbsolute(realRelative)) {
139206
139740
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
@@ -139228,8 +139762,8 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
139228
139762
  ensureDir(specsDir);
139229
139763
  ensureDir(".postman");
139230
139764
  const globalsFilePath = `${globalsDir}/workspace.globals.yaml`;
139231
- if (!(0, import_node_fs4.existsSync)(globalsFilePath)) {
139232
- (0, import_node_fs4.writeFileSync)(globalsFilePath, "name: Globals\nvalues: []\n");
139765
+ if (!(0, import_node_fs5.existsSync)(globalsFilePath)) {
139766
+ (0, import_node_fs5.writeFileSync)(globalsFilePath, "name: Globals\nvalues: []\n");
139233
139767
  }
139234
139768
  if (inputs.generateCiWorkflow) {
139235
139769
  const ciDir = inputs.ciWorkflowPath.split("/").slice(0, -1).join("/");
@@ -139245,23 +139779,42 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
139245
139779
  inputs.specSyncMode,
139246
139780
  options.releaseLabel
139247
139781
  ) : void 0;
139248
- if (inputs.baselineCollectionId) {
139249
- const col = await dependencies.postman.getCollection(inputs.baselineCollectionId);
139250
- const dirName = getCollectionDirectoryName("Baseline", assetProjectName);
139251
- await convertAndSplitAnyCollection(col, `${collectionsDir}/${dirName}`);
139252
- manifestCollections[`../${collectionsDir}/${dirName}`] = inputs.baselineCollectionId;
139253
- }
139254
- if (inputs.smokeCollectionId) {
139255
- const col = await dependencies.postman.getCollection(inputs.smokeCollectionId);
139256
- const dirName = getCollectionDirectoryName("Smoke", assetProjectName);
139257
- await convertAndSplitAnyCollection(col, `${collectionsDir}/${dirName}`);
139258
- manifestCollections[`../${collectionsDir}/${dirName}`] = inputs.smokeCollectionId;
139259
- }
139260
- if (inputs.contractCollectionId) {
139261
- const col = await dependencies.postman.getCollection(inputs.contractCollectionId);
139262
- const dirName = getCollectionDirectoryName("Contract", assetProjectName);
139263
- await convertAndSplitAnyCollection(col, `${collectionsDir}/${dirName}`);
139264
- manifestCollections[`../${collectionsDir}/${dirName}`] = inputs.contractCollectionId;
139782
+ const prebuiltByRole = options.preparedPrebuiltCollections;
139783
+ const baselineRef = await exportCollectionArtifact({
139784
+ role: "baseline",
139785
+ collectionId: inputs.baselineCollectionId,
139786
+ dirName: getCollectionDirectoryName("Baseline", assetProjectName),
139787
+ collectionsDir,
139788
+ prebuiltByRole,
139789
+ postman: dependencies.postman,
139790
+ core: dependencies.core
139791
+ });
139792
+ if (baselineRef) {
139793
+ manifestCollections[baselineRef] = inputs.baselineCollectionId;
139794
+ }
139795
+ const smokeRef = await exportCollectionArtifact({
139796
+ role: "smoke",
139797
+ collectionId: inputs.smokeCollectionId,
139798
+ dirName: getCollectionDirectoryName("Smoke", assetProjectName),
139799
+ collectionsDir,
139800
+ prebuiltByRole,
139801
+ postman: dependencies.postman,
139802
+ core: dependencies.core
139803
+ });
139804
+ if (smokeRef) {
139805
+ manifestCollections[smokeRef] = inputs.smokeCollectionId;
139806
+ }
139807
+ const contractRef = await exportCollectionArtifact({
139808
+ role: "contract",
139809
+ collectionId: inputs.contractCollectionId,
139810
+ dirName: getCollectionDirectoryName("Contract", assetProjectName),
139811
+ collectionsDir,
139812
+ prebuiltByRole,
139813
+ postman: dependencies.postman,
139814
+ core: dependencies.core
139815
+ });
139816
+ if (contractRef) {
139817
+ manifestCollections[contractRef] = inputs.contractCollectionId;
139265
139818
  }
139266
139819
  for (const [envName, envUid] of Object.entries(envUids)) {
139267
139820
  writeJsonFile(
@@ -139276,7 +139829,7 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
139276
139829
  workspaceLinkEnabled: inputs.workspaceLinkEnabled,
139277
139830
  workspaceLinkStatus: options.workspaceLinkStatus
139278
139831
  });
139279
- (0, import_node_fs4.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
139832
+ (0, import_node_fs5.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
139280
139833
  durableWorkspaceId,
139281
139834
  manifestCollections,
139282
139835
  envUids,
@@ -139288,11 +139841,18 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
139288
139841
  options.priorState
139289
139842
  ));
139290
139843
  if (mappedSpec && Object.keys(manifestCollections).length > 0) {
139291
- (0, import_node_fs4.writeFileSync)(
139844
+ let existingWorkflows;
139845
+ try {
139846
+ existingWorkflows = (0, import_node_fs5.readFileSync)(".postman/workflows.yaml", "utf8");
139847
+ } catch {
139848
+ existingWorkflows = void 0;
139849
+ }
139850
+ (0, import_node_fs5.writeFileSync)(
139292
139851
  ".postman/workflows.yaml",
139293
139852
  buildSpecCollectionWorkflowManifest(
139294
139853
  mappedSpec.configRelativePath,
139295
- Object.keys(manifestCollections)
139854
+ Object.keys(manifestCollections),
139855
+ existingWorkflows
139296
139856
  )
139297
139857
  );
139298
139858
  }
@@ -139337,12 +139897,12 @@ async function commitAndPushGeneratedFiles(inputs, dependencies) {
139337
139897
  const dir = parts.slice(0, -1).join("/");
139338
139898
  ensureDir(dir);
139339
139899
  }
139340
- (0, import_node_fs4.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
139900
+ (0, import_node_fs5.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
139341
139901
  if (inputs.provider === "github" || inputs.provider === "unknown") {
139342
139902
  const gcPath = ".github/workflows/postman-preview-gc.yml";
139343
139903
  if (inputs.ciWorkflowPath.endsWith(".github/workflows/ci.yml") || inputs.ciWorkflowPath === ".github/workflows/ci.yml") {
139344
139904
  ensureDir(".github/workflows");
139345
- (0, import_node_fs4.writeFileSync)(gcPath, renderGcWorkflowTemplate());
139905
+ (0, import_node_fs5.writeFileSync)(gcPath, renderGcWorkflowTemplate());
139346
139906
  }
139347
139907
  }
139348
139908
  }
@@ -139350,16 +139910,16 @@ async function commitAndPushGeneratedFiles(inputs, dependencies) {
139350
139910
  return { commitSha: "", resolvedCurrentRef: "", pushed: false };
139351
139911
  }
139352
139912
  const provisionPath = ".github/workflows/provision.yml";
139353
- const provisionExists = inputs.provider === "github" && (0, import_node_fs4.existsSync)(provisionPath);
139913
+ const provisionExists = inputs.provider === "github" && (0, import_node_fs5.existsSync)(provisionPath);
139354
139914
  const gcWorkflowPath = ".github/workflows/postman-preview-gc.yml";
139355
- const gcExists = inputs.generateCiWorkflow && (0, import_node_fs4.existsSync)(gcWorkflowPath);
139915
+ const gcExists = inputs.generateCiWorkflow && (0, import_node_fs5.existsSync)(gcWorkflowPath);
139356
139916
  const stagePaths = [
139357
139917
  inputs.artifactDir,
139358
139918
  ".postman",
139359
139919
  inputs.generateCiWorkflow ? inputs.ciWorkflowPath : null,
139360
139920
  gcExists ? gcWorkflowPath : null,
139361
139921
  provisionExists ? provisionPath : null
139362
- ].filter((entry) => typeof entry === "string" && ((0, import_node_fs4.existsSync)(entry) || entry === provisionPath));
139922
+ ].filter((entry) => typeof entry === "string" && ((0, import_node_fs5.existsSync)(entry) || entry === provisionPath));
139363
139923
  if (stagePaths.length === 0) {
139364
139924
  dependencies.core.info("No generated repository paths were found; skipping repo mutation.");
139365
139925
  return {
@@ -139467,6 +140027,7 @@ async function runRepoSyncInner(inputs, dependencies) {
139467
140027
  }
139468
140028
  }
139469
140029
  }
140030
+ const preparedPrebuiltCollections = await preparePrebuiltCollections(inputs);
139470
140031
  let skipRepositoryLinkPost = false;
139471
140032
  let repositoryLinkPreflightWasFree = false;
139472
140033
  if (inputs.workspaceLinkEnabled && inputs.workspaceId && inputs.repoUrl && dependencies.internalIntegration?.findWorkspaceForRepo) {
@@ -139503,6 +140064,7 @@ async function runRepoSyncInner(inputs, dependencies) {
139503
140064
  const branchAssetMarker = buildBranchAssetMarker(branchDecision, inputs);
139504
140065
  const envUids = await upsertEnvironments(inputs, dependencies, resourcesState, branchAssetMarker);
139505
140066
  outputs["environment-uids-json"] = JSON.stringify(envUids);
140067
+ dependencies.core.setOutput("environment-uids-json", outputs["environment-uids-json"]);
139506
140068
  if (inputs.environmentSyncEnabled && inputs.workspaceId && dependencies.internalIntegration) {
139507
140069
  const associations = Object.entries(envUids).map(([envName, envUid]) => ({
139508
140070
  envUid,
@@ -139621,6 +140183,7 @@ async function runRepoSyncInner(inputs, dependencies) {
139621
140183
  }
139622
140184
  }
139623
140185
  outputs["mock-url"] = resolvedMockUrl;
140186
+ dependencies.core.setOutput("mock-url", resolvedMockUrl);
139624
140187
  }
139625
140188
  }
139626
140189
  if (inputs.workspaceId && inputs.smokeCollectionId && Object.keys(envUids).length > 0) {
@@ -139696,6 +140259,7 @@ async function runRepoSyncInner(inputs, dependencies) {
139696
140259
  }
139697
140260
  }
139698
140261
  outputs["monitor-id"] = resolvedMonitorId;
140262
+ dependencies.core.setOutput("monitor-id", resolvedMonitorId);
139699
140263
  if (!effectiveCron && resolvedMonitorId) {
139700
140264
  try {
139701
140265
  await dependencies.postman.runMonitor(resolvedMonitorId);
@@ -139753,7 +140317,8 @@ async function runRepoSyncInner(inputs, dependencies) {
139753
140317
  priorWorkspaceId: resourcesState?.workspace?.id,
139754
140318
  existingSpecs: resourcesState?.cloudResources?.specs,
139755
140319
  releaseLabel,
139756
- priorState: resourcesState
140320
+ priorState: resourcesState,
140321
+ preparedPrebuiltCollections
139757
140322
  });
139758
140323
  const commit = await commitAndPushGeneratedFiles(inputs, dependencies);
139759
140324
  outputs["commit-sha"] = commit.commitSha;
@@ -139979,7 +140544,12 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
139979
140544
  bifrostBaseUrl: inputs.postmanBifrostBase,
139980
140545
  // No fallback on the org-mode probe: the expected non-org 400 must
139981
140546
  // surface verbatim, not be re-fired against the /_api alias.
139982
- secretMasker: masker
140547
+ secretMasker: masker,
140548
+ onRetryEvent: (event) => {
140549
+ actionCore.info(
140550
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
140551
+ );
140552
+ }
139983
140553
  });
139984
140554
  const squads = await gateway.getSquads(teamId);
139985
140555
  if (squads.length > 0) {
@@ -140043,7 +140613,12 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
140043
140613
  fallbackBaseUrl: inputs.postmanFallbackBase,
140044
140614
  teamId: resolved.teamId,
140045
140615
  orgMode: inputs.orgMode,
140046
- secretMasker: masker
140616
+ secretMasker: masker,
140617
+ onRetryEvent: (event) => {
140618
+ factories.core.info(
140619
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
140620
+ );
140621
+ }
140047
140622
  });
140048
140623
  const gatewayAssets = new PostmanGatewayAssetsClient({
140049
140624
  gateway,
@@ -140162,7 +140737,10 @@ async function runAction(actionCore = core_exports, actionExec = exec_exports) {
140162
140737
  }
140163
140738
  await mintAccessTokenIfNeeded(inputs, {
140164
140739
  info: (message) => actionCore.info(message),
140165
- warning: (message) => actionCore.warning(message)
140740
+ warning: (message) => actionCore.warning(message),
140741
+ retryEvent: (event) => actionCore.info(
140742
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
140743
+ )
140166
140744
  }, (secret) => actionCore.setSecret(secret));
140167
140745
  const masker = createSecretMasker([
140168
140746
  inputs.postmanApiKey,
@@ -140183,7 +140761,12 @@ async function runAction(actionCore = core_exports, actionExec = exec_exports) {
140183
140761
  explicitTeamId: inputs.teamId || void 0,
140184
140762
  mode: inputs.credentialPreflight,
140185
140763
  mask: masker,
140186
- log: actionCore
140764
+ log: {
140765
+ ...actionCore,
140766
+ retryEvent: (event) => actionCore.info(
140767
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
140768
+ )
140769
+ }
140187
140770
  });
140188
140771
  const resolved = await resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, masker, {
140189
140772
  env: process.env
@@ -140224,6 +140807,7 @@ async function runAction(actionCore = core_exports, actionExec = exec_exports) {
140224
140807
  decideBranchTier,
140225
140808
  getInput,
140226
140809
  hasInput,
140810
+ prebuiltDirectoryTraversalIdentity,
140227
140811
  readActionInputs,
140228
140812
  resolveInputs,
140229
140813
  resolvePostmanApiKeyAndTeamId,