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

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/cli.cjs CHANGED
@@ -26571,7 +26571,7 @@ var require_util7 = __commonJS({
26571
26571
  exports2.esc = esc;
26572
26572
  exports2.slugify = slugify;
26573
26573
  exports2.isObject = isObject;
26574
- exports2.isPlainObject = isPlainObject2;
26574
+ exports2.isPlainObject = isPlainObject3;
26575
26575
  exports2.shallowClone = shallowClone;
26576
26576
  exports2.numKeys = numKeys;
26577
26577
  exports2.escapeRegex = escapeRegex;
@@ -26754,7 +26754,7 @@ var require_util7 = __commonJS({
26754
26754
  return false;
26755
26755
  }
26756
26756
  });
26757
- function isPlainObject2(o) {
26757
+ function isPlainObject3(o) {
26758
26758
  if (isObject(o) === false)
26759
26759
  return false;
26760
26760
  const ctor = o.constructor;
@@ -26771,7 +26771,7 @@ var require_util7 = __commonJS({
26771
26771
  return true;
26772
26772
  }
26773
26773
  function shallowClone(o) {
26774
- if (isPlainObject2(o))
26774
+ if (isPlainObject3(o))
26775
26775
  return { ...o };
26776
26776
  if (Array.isArray(o))
26777
26777
  return [...o];
@@ -26976,7 +26976,7 @@ var require_util7 = __commonJS({
26976
26976
  return clone(schema, def);
26977
26977
  }
26978
26978
  function extend(schema, shape) {
26979
- if (!isPlainObject2(shape)) {
26979
+ if (!isPlainObject3(shape)) {
26980
26980
  throw new Error("Invalid input to extend: expected a plain object");
26981
26981
  }
26982
26982
  const checks = schema._zod.def.checks;
@@ -26999,7 +26999,7 @@ var require_util7 = __commonJS({
26999
26999
  return clone(schema, def);
27000
27000
  }
27001
27001
  function safeExtend(schema, shape) {
27002
- if (!isPlainObject2(shape)) {
27002
+ if (!isPlainObject3(shape)) {
27003
27003
  throw new Error("Invalid input to safeExtend: expected a plain object");
27004
27004
  }
27005
27005
  const def = mergeDefs(schema._zod.def, {
@@ -54818,7 +54818,7 @@ var require_lodash = __commonJS({
54818
54818
  } else {
54819
54819
  newValue = [];
54820
54820
  }
54821
- } else if (isPlainObject2(srcValue) || isArguments(srcValue)) {
54821
+ } else if (isPlainObject3(srcValue) || isArguments(srcValue)) {
54822
54822
  newValue = objValue;
54823
54823
  if (isArguments(objValue)) {
54824
54824
  newValue = toPlainObject(objValue);
@@ -55782,7 +55782,7 @@ var require_lodash = __commonJS({
55782
55782
  return objValue;
55783
55783
  }
55784
55784
  function customOmitClone(value) {
55785
- return isPlainObject2(value) ? undefined2 : value;
55785
+ return isPlainObject3(value) ? undefined2 : value;
55786
55786
  }
55787
55787
  function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
55788
55788
  var isPartial = bitmask & COMPARE_PARTIAL_FLAG, arrLength = array.length, othLength = other.length;
@@ -57283,7 +57283,7 @@ var require_lodash = __commonJS({
57283
57283
  var isBuffer = nativeIsBuffer || stubFalse;
57284
57284
  var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate;
57285
57285
  function isElement(value) {
57286
- return isObjectLike(value) && value.nodeType === 1 && !isPlainObject2(value);
57286
+ return isObjectLike(value) && value.nodeType === 1 && !isPlainObject3(value);
57287
57287
  }
57288
57288
  function isEmpty(value) {
57289
57289
  if (value == null) {
@@ -57319,7 +57319,7 @@ var require_lodash = __commonJS({
57319
57319
  return false;
57320
57320
  }
57321
57321
  var tag = baseGetTag(value);
57322
- return tag == errorTag || tag == domExcTag || typeof value.message == "string" && typeof value.name == "string" && !isPlainObject2(value);
57322
+ return tag == errorTag || tag == domExcTag || typeof value.message == "string" && typeof value.name == "string" && !isPlainObject3(value);
57323
57323
  }
57324
57324
  function isFinite2(value) {
57325
57325
  return typeof value == "number" && nativeIsFinite(value);
@@ -57370,7 +57370,7 @@ var require_lodash = __commonJS({
57370
57370
  function isNumber(value) {
57371
57371
  return typeof value == "number" || isObjectLike(value) && baseGetTag(value) == numberTag;
57372
57372
  }
57373
- function isPlainObject2(value) {
57373
+ function isPlainObject3(value) {
57374
57374
  if (!isObjectLike(value) || baseGetTag(value) != objectTag) {
57375
57375
  return false;
57376
57376
  }
@@ -58441,7 +58441,7 @@ var require_lodash = __commonJS({
58441
58441
  lodash.isNumber = isNumber;
58442
58442
  lodash.isObject = isObject;
58443
58443
  lodash.isObjectLike = isObjectLike;
58444
- lodash.isPlainObject = isPlainObject2;
58444
+ lodash.isPlainObject = isPlainObject3;
58445
58445
  lodash.isRegExp = isRegExp;
58446
58446
  lodash.isSafeInteger = isSafeInteger;
58447
58447
  lodash.isSet = isSet;
@@ -128814,11 +128814,12 @@ __export(cli_exports, {
128814
128814
  normalizeCliFlag: () => normalizeCliFlag,
128815
128815
  parseCliArgs: () => parseCliArgs,
128816
128816
  runCli: () => runCli,
128817
- toDotenv: () => toDotenv
128817
+ toDotenv: () => toDotenv,
128818
+ writeOptionalFileAtomic: () => writeOptionalFileAtomic
128818
128819
  });
128819
128820
  module.exports = __toCommonJS(cli_exports);
128820
128821
  var import_node_child_process = require("node:child_process");
128821
- var import_node_fs5 = require("node:fs");
128822
+ var import_node_fs6 = require("node:fs");
128822
128823
  var import_promises = require("node:fs/promises");
128823
128824
  var import_node_path3 = __toESM(require("node:path"), 1);
128824
128825
  var import_node_util = require("node:util");
@@ -129186,7 +129187,8 @@ var ExitCode;
129186
129187
  })(ExitCode || (ExitCode = {}));
129187
129188
 
129188
129189
  // src/index.ts
129189
- var import_node_fs4 = require("node:fs");
129190
+ var import_node_crypto3 = require("node:crypto");
129191
+ var import_node_fs5 = require("node:fs");
129190
129192
  var path3 = __toESM(require("node:path"), 1);
129191
129193
 
129192
129194
  // node_modules/js-yaml/dist/js-yaml.mjs
@@ -132102,6 +132104,7 @@ function dump(input, options = {}) {
132102
132104
  }
132103
132105
 
132104
132106
  // src/postman-v3/converter.ts
132107
+ var import_node_fs = require("node:fs");
132105
132108
  var fs2 = __toESM(require("node:fs/promises"), 1);
132106
132109
  var path = __toESM(require("node:path"), 1);
132107
132110
  var V2 = __toESM(require_v2(), 1);
@@ -132172,6 +132175,72 @@ function structuredCloneSafe(value) {
132172
132175
  if (typeof structuredClone === "function") return structuredClone(value);
132173
132176
  return JSON.parse(JSON.stringify(value));
132174
132177
  }
132178
+ var ArtifactDigestStreamError = class extends Error {
132179
+ code = "ARTIFACT_DIGEST_STREAM";
132180
+ constructor(message, options) {
132181
+ super(message, options);
132182
+ this.name = "ArtifactDigestStreamError";
132183
+ }
132184
+ };
132185
+ function sameFileIdentity(left, right) {
132186
+ return left.dev === right.dev && left.ino === right.ino && left.size === right.size;
132187
+ }
132188
+ async function appendArtifactDigestFileStreaming(hash, relative3, absolute, expected) {
132189
+ 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;
132190
+ let handle;
132191
+ try {
132192
+ handle = await fs2.open(absolute, flags);
132193
+ } catch (error) {
132194
+ const code = error?.code;
132195
+ if (code === "ELOOP" || code === "EMLINK" || code === "EINVAL") {
132196
+ throw new ArtifactDigestStreamError(
132197
+ `prebuilt collection tree file became a symlink or unsupported node at ${relative3}`,
132198
+ { cause: error }
132199
+ );
132200
+ }
132201
+ throw new ArtifactDigestStreamError(
132202
+ `prebuilt collection tree file changed or became unsupported at ${relative3}`,
132203
+ { cause: error }
132204
+ );
132205
+ }
132206
+ try {
132207
+ const before = await handle.stat();
132208
+ if (!before.isFile() || !sameFileIdentity(before, expected)) {
132209
+ throw new ArtifactDigestStreamError(
132210
+ `prebuilt collection tree file changed or became unsupported at ${relative3}`
132211
+ );
132212
+ }
132213
+ hash.update(relative3);
132214
+ hash.update("\0");
132215
+ const content = Buffer.allocUnsafe(expected.size);
132216
+ let total = 0;
132217
+ const buffer = Buffer.alloc(64 * 1024);
132218
+ for (; ; ) {
132219
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
132220
+ if (bytesRead === 0) {
132221
+ break;
132222
+ }
132223
+ if (total + bytesRead > expected.size) {
132224
+ throw new ArtifactDigestStreamError(
132225
+ `prebuilt collection tree file changed while reading at ${relative3}`
132226
+ );
132227
+ }
132228
+ buffer.copy(content, total, 0, bytesRead);
132229
+ hash.update(buffer.subarray(0, bytesRead));
132230
+ total += bytesRead;
132231
+ }
132232
+ hash.update("\0");
132233
+ const after = await handle.stat();
132234
+ if (!after.isFile() || !sameFileIdentity(after, expected) || total !== expected.size) {
132235
+ throw new ArtifactDigestStreamError(
132236
+ `prebuilt collection tree file changed while reading at ${relative3}`
132237
+ );
132238
+ }
132239
+ return total === expected.size ? content : content.subarray(0, total);
132240
+ } finally {
132241
+ await handle.close();
132242
+ }
132243
+ }
132175
132244
  async function listFilesRelative(dir, base) {
132176
132245
  let entries;
132177
132246
  try {
@@ -132732,7 +132801,7 @@ function getCiWorkflowTemplate(provider, options = {}) {
132732
132801
  }
132733
132802
 
132734
132803
  // src/lib/github/repo-mutation.ts
132735
- var import_node_fs = require("node:fs");
132804
+ var import_node_fs2 = require("node:fs");
132736
132805
  var import_node_path = __toESM(require("node:path"), 1);
132737
132806
 
132738
132807
  // src/lib/secrets.ts
@@ -133022,7 +133091,7 @@ var RepoMutationService = class {
133022
133091
  throw new Error(this.secretMasker(changed.stderr || changed.stdout || "Failed to inspect generated changes"));
133023
133092
  }
133024
133093
  const hasPlannedRemoval = removePaths.some(
133025
- (removePath) => (0, import_node_fs.existsSync)(import_node_path.default.resolve(this.cwd, removePath))
133094
+ (removePath) => (0, import_node_fs2.existsSync)(import_node_path.default.resolve(this.cwd, removePath))
133026
133095
  );
133027
133096
  if (!changed.stdout.trim() && !hasPlannedRemoval) {
133028
133097
  return {
@@ -133046,7 +133115,7 @@ var RepoMutationService = class {
133046
133115
  await this.execute("git", ["config", "user.name", options.committerName]);
133047
133116
  await this.execute("git", ["config", "user.email", options.committerEmail]);
133048
133117
  for (const removePath of removePaths) {
133049
- (0, import_node_fs.rmSync)(import_node_path.default.resolve(this.cwd, removePath), { force: true });
133118
+ (0, import_node_fs2.rmSync)(import_node_path.default.resolve(this.cwd, removePath), { force: true });
133050
133119
  }
133051
133120
  await this.execute("git", ["add", "-A", "--", ...stagePaths]);
133052
133121
  const staged = await this.execute("git", ["diff", "--cached", "--quiet"]);
@@ -133589,14 +133658,14 @@ function createTelemetryContext(options) {
133589
133658
  }
133590
133659
 
133591
133660
  // src/action-version.ts
133592
- var import_node_fs2 = require("node:fs");
133661
+ var import_node_fs3 = require("node:fs");
133593
133662
  var import_node_path2 = require("node:path");
133594
133663
  function resolveActionVersion2() {
133595
133664
  if (false) {
133596
133665
  return void 0;
133597
133666
  }
133598
133667
  try {
133599
- const raw = (0, import_node_fs2.readFileSync)((0, import_node_path2.join)(__dirname, "..", "package.json"), "utf8");
133668
+ const raw = (0, import_node_fs3.readFileSync)((0, import_node_path2.join)(__dirname, "..", "package.json"), "utf8");
133600
133669
  return JSON.parse(raw).version ?? "unknown";
133601
133670
  } catch {
133602
133671
  return "unknown";
@@ -133835,7 +133904,8 @@ async function resolveSessionIdentity(opts) {
133835
133904
  opts.fetchImpl ?? fetch,
133836
133905
  Math.max(1, opts.maxAttempts ?? SESSION_MAX_ATTEMPTS),
133837
133906
  opts.sleepImpl ?? defaultSessionSleep,
133838
- opts.randomImpl ?? defaultRandom
133907
+ opts.randomImpl ?? defaultRandom,
133908
+ opts.onRetryEvent
133839
133909
  );
133840
133910
  sessionMemo.set(memoKey, pending);
133841
133911
  }
@@ -133869,7 +133939,7 @@ async function parseSessionResponse(response) {
133869
133939
  consumerType: coerceText(root.consumerType) ?? coerceText(data?.consumerType) ?? coerceText(user?.consumerType)
133870
133940
  };
133871
133941
  }
133872
- async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts, sleepImpl, random) {
133942
+ async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts, sleepImpl, random, onRetryEvent) {
133873
133943
  let failure = "unavailable";
133874
133944
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
133875
133945
  let response;
@@ -133881,7 +133951,9 @@ async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts
133881
133951
  } catch {
133882
133952
  failure = "unavailable";
133883
133953
  if (attempt < maxAttempts) {
133884
- await sleepImpl(computeSessionRetryDelayMs(void 0, attempt, random));
133954
+ const delay = computeSessionRetryDelayMs(void 0, attempt, random);
133955
+ onRetryEvent?.({ class: "transport", attempt, delay });
133956
+ await sleepImpl(delay);
133885
133957
  continue;
133886
133958
  }
133887
133959
  break;
@@ -133903,7 +133975,9 @@ async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts
133903
133975
  if (response.status === 429 || response.status >= 500) {
133904
133976
  failure = "unavailable";
133905
133977
  if (attempt < maxAttempts) {
133906
- await sleepImpl(computeSessionRetryDelayMs(response, attempt, random));
133978
+ const delay = computeSessionRetryDelayMs(response, attempt, random);
133979
+ onRetryEvent?.({ class: "http", status: response.status, attempt, delay });
133980
+ await sleepImpl(delay);
133907
133981
  continue;
133908
133982
  }
133909
133983
  break;
@@ -134005,7 +134079,8 @@ async function runCredentialPreflight(args) {
134005
134079
  accessToken,
134006
134080
  fetchImpl: args.fetchImpl,
134007
134081
  ...args.sleepImpl ? { sleepImpl: args.sleepImpl } : {},
134008
- ...args.randomImpl ? { randomImpl: args.randomImpl } : {}
134082
+ ...args.randomImpl ? { randomImpl: args.randomImpl } : {},
134083
+ onRetryEvent: args.retryEvent ?? args.log.retryEvent
134009
134084
  });
134010
134085
  } catch (error) {
134011
134086
  args.log.warning(
@@ -134657,6 +134732,11 @@ var postmanRepoSyncActionContract = {
134657
134732
  description: "Contract collection ID used for exported artifacts.",
134658
134733
  required: false
134659
134734
  },
134735
+ "prebuilt-collections-json": {
134736
+ 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.",
134737
+ required: false,
134738
+ default: ""
134739
+ },
134660
134740
  "collection-sync-mode": {
134661
134741
  description: "Collection sync lifecycle mode (refresh or version).",
134662
134742
  required: false,
@@ -134899,7 +134979,7 @@ var postmanRepoSyncActionContract = {
134899
134979
  "Create or update Postman environments from runtime URLs.",
134900
134980
  "Associate Postman environments to system environments through Postman integration APIs.",
134901
134981
  "Create mock servers and smoke monitors from generated collections.",
134902
- "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.",
134982
+ "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.",
134903
134983
  "Link the Postman workspace to the repository (GitHub or GitLab) through Postman integration APIs.",
134904
134984
  "Commit synced artifacts and push them back to the current checked out ref."
134905
134985
  ],
@@ -135684,6 +135764,7 @@ var AccessTokenProvider = class {
135684
135764
  maxAttempts;
135685
135765
  onToken;
135686
135766
  sleep;
135767
+ onRetryEvent;
135687
135768
  inflight;
135688
135769
  constructor(options) {
135689
135770
  this.token = String(options.accessToken || "").trim();
@@ -135695,6 +135776,7 @@ var AccessTokenProvider = class {
135695
135776
  this.maxAttempts = Math.max(1, options.maxAttempts ?? 2);
135696
135777
  this.onToken = options.onToken;
135697
135778
  this.sleep = options.sleep;
135779
+ this.onRetryEvent = options.onRetryEvent;
135698
135780
  }
135699
135781
  current() {
135700
135782
  return this.token;
@@ -135720,7 +135802,12 @@ var AccessTokenProvider = class {
135720
135802
  delayMs: 1e3,
135721
135803
  backoffMultiplier: 2,
135722
135804
  ...this.sleep ? { sleep: this.sleep } : {},
135723
- shouldRetry: (error) => !(error instanceof MintError && error.permanent)
135805
+ shouldRetry: (error) => !(error instanceof MintError && error.permanent),
135806
+ onRetry: ({ attempt, delayMs, error }) => {
135807
+ const status = error instanceof MintError ? error.status : void 0;
135808
+ const retryClass = status === void 0 ? "transport" : "http";
135809
+ this.onRetryEvent?.({ class: retryClass, ...status === void 0 ? {} : { status }, attempt, delay: Math.max(0, delayMs) });
135810
+ }
135724
135811
  });
135725
135812
  this.token = token;
135726
135813
  this.onToken?.(token);
@@ -135751,7 +135838,7 @@ var AccessTokenProvider = class {
135751
135838
  true
135752
135839
  );
135753
135840
  }
135754
- throw new MintError(`postman: re-mint failed (service-account-tokens HTTP ${status}).`, false);
135841
+ throw new MintError(`postman: re-mint failed (service-account-tokens HTTP ${status}).`, false, status);
135755
135842
  }
135756
135843
  let parsed;
135757
135844
  try {
@@ -135782,7 +135869,8 @@ async function mintAccessTokenIfNeeded(inputs, log, setSecret2, fetchImpl = fetc
135782
135869
  apiKey: inputs.postmanApiKey,
135783
135870
  apiBaseUrl,
135784
135871
  fetchImpl,
135785
- onToken: (token) => setSecret2?.(token)
135872
+ onToken: (token) => setSecret2?.(token),
135873
+ onRetryEvent: (event) => log.retryEvent?.(event)
135786
135874
  });
135787
135875
  try {
135788
135876
  inputs.postmanAccessToken = await provider.refresh();
@@ -135822,6 +135910,7 @@ var AccessTokenGatewayClient = class {
135822
135910
  requestTimeoutMs;
135823
135911
  retryMaxDelayMs;
135824
135912
  randomImpl;
135913
+ onRetryEvent;
135825
135914
  constructor(options) {
135826
135915
  this.tokenProvider = options.tokenProvider;
135827
135916
  this.bifrostBaseUrl = String(
@@ -135840,6 +135929,7 @@ var AccessTokenGatewayClient = class {
135840
135929
  this.requestTimeoutMs = options.requestTimeoutMs ?? 3e4;
135841
135930
  this.retryMaxDelayMs = options.retryMaxDelayMs ?? 5e3;
135842
135931
  this.randomImpl = options.randomImpl ?? Math.random;
135932
+ this.onRetryEvent = options.onRetryEvent;
135843
135933
  }
135844
135934
  configureTeamContext(teamId, orgMode) {
135845
135935
  this.teamId = String(teamId || "").trim();
@@ -135908,8 +135998,9 @@ var AccessTokenGatewayClient = class {
135908
135998
  if (!this.fallbackBaseUrl) return false;
135909
135999
  return retryTransient || request.fallback === "auto";
135910
136000
  }
135911
- async attemptFallback(request, retryTransient) {
136001
+ async attemptFallback(request, retryTransient, status) {
135912
136002
  if (!this.fallbackEligible(request, retryTransient)) return null;
136003
+ this.emitRetryEvent("fallback", status, 1, 0);
135913
136004
  const response = await this.tryFallback(request);
135914
136005
  if (!response) return null;
135915
136006
  if (response.ok) return response;
@@ -135935,6 +136026,7 @@ var AccessTokenGatewayClient = class {
135935
136026
  } catch (error) {
135936
136027
  if (retryTransient && attempt < this.maxRetries) {
135937
136028
  const delay = this.retryDelayMs(attempt);
136029
+ this.emitRetryEvent("transport", void 0, attempt + 1, delay);
135938
136030
  attempt += 1;
135939
136031
  await this.sleepImpl(delay);
135940
136032
  continue;
@@ -135948,7 +136040,9 @@ var AccessTokenGatewayClient = class {
135948
136040
  const inner = this.innerStatus(okBody);
135949
136041
  if (inner !== void 0) {
135950
136042
  if (retryTransient && this.isTransient(inner, okBody) && attempt < this.maxRetries) {
135951
- await this.sleepImpl(this.retryDelayMs(attempt));
136043
+ const delay = this.retryDelayMs(attempt);
136044
+ this.emitRetryEvent("inner", inner, attempt + 1, delay);
136045
+ await this.sleepImpl(delay);
135952
136046
  attempt += 1;
135953
136047
  continue;
135954
136048
  }
@@ -135958,6 +136052,7 @@ var AccessTokenGatewayClient = class {
135958
136052
  }
135959
136053
  const body = await response.text().catch(() => "");
135960
136054
  if (isExpiredAuthError(response.status, body) && this.tokenProvider.canRefresh()) {
136055
+ this.emitRetryEvent("auth", response.status, 1, 0);
135961
136056
  await this.tokenProvider.refresh();
135962
136057
  response = await this.send(request);
135963
136058
  if (response.ok) {
@@ -135968,11 +136063,12 @@ var AccessTokenGatewayClient = class {
135968
136063
  }
135969
136064
  if (retryTransient && this.isTransient(response.status, body) && attempt < this.maxRetries) {
135970
136065
  const delay = this.retryDelayMs(attempt, parseRetryAfterMs2(response.headers.get("retry-after")));
136066
+ this.emitRetryEvent("http", response.status, attempt + 1, delay);
135971
136067
  attempt += 1;
135972
136068
  await this.sleepImpl(delay);
135973
136069
  continue;
135974
136070
  }
135975
- const fallbackResponse = await this.attemptFallback(request, retryTransient);
136071
+ const fallbackResponse = await this.attemptFallback(request, retryTransient, response.status);
135976
136072
  if (fallbackResponse) return fallbackResponse;
135977
136073
  throw this.toHttpError(request, response, body);
135978
136074
  }
@@ -135980,6 +136076,14 @@ var AccessTokenGatewayClient = class {
135980
136076
  retryDelayMs(attempt, retryAfter) {
135981
136077
  return retryAfter === void 0 ? fullJitterDelayMs(attempt, this.retryBaseDelayMs, this.retryMaxDelayMs, this.randomImpl) : Math.min(this.retryMaxDelayMs, retryAfter);
135982
136078
  }
136079
+ emitRetryEvent(retryClass, status, attempt, delay) {
136080
+ this.onRetryEvent?.({
136081
+ class: retryClass,
136082
+ ...status === void 0 ? {} : { status },
136083
+ attempt: Math.max(1, attempt),
136084
+ delay: Math.max(0, delay)
136085
+ });
136086
+ }
135983
136087
  isTransient(status, body) {
135984
136088
  return status === 408 || status === 429 || status >= 500 || /ESOCKETTIMEDOUT|ETIMEDOUT|ECONNRESET|serverError|downstream/.test(body);
135985
136089
  }
@@ -136057,7 +136161,7 @@ var AccessTokenGatewayClient = class {
136057
136161
  };
136058
136162
 
136059
136163
  // src/lib/repo/branch-decision.ts
136060
- var import_node_fs3 = require("node:fs");
136164
+ var import_node_fs4 = require("node:fs");
136061
136165
  var import_node_crypto2 = require("node:crypto");
136062
136166
  var ContractError = class extends Error {
136063
136167
  code;
@@ -136098,7 +136202,7 @@ function readGithubEvent(env) {
136098
136202
  const path5 = clean(env.GITHUB_EVENT_PATH);
136099
136203
  if (!path5) return void 0;
136100
136204
  try {
136101
- return JSON.parse((0, import_node_fs3.readFileSync)(path5, "utf8"));
136205
+ return JSON.parse((0, import_node_fs4.readFileSync)(path5, "utf8"));
136102
136206
  } catch {
136103
136207
  return void 0;
136104
136208
  }
@@ -136551,6 +136655,7 @@ function resolveInputs(env = process.env) {
136551
136655
  baselineCollectionId: getInput("baseline-collection-id", env),
136552
136656
  smokeCollectionId: getInput("smoke-collection-id", env),
136553
136657
  contractCollectionId: getInput("contract-collection-id", env),
136658
+ prebuiltCollectionsJson: getInput("prebuilt-collections-json", env),
136554
136659
  specId: getInput("spec-id", env),
136555
136660
  specContentChanged: parseBooleanInput(getInput("spec-content-changed", env), true),
136556
136661
  specPath: getInput("spec-path", env),
@@ -136666,7 +136771,7 @@ var StateUnreadableError = class extends Error {
136666
136771
  function readResourcesState() {
136667
136772
  let raw;
136668
136773
  try {
136669
- raw = (0, import_node_fs4.readFileSync)(".postman/resources.yaml", "utf8");
136774
+ raw = (0, import_node_fs5.readFileSync)(".postman/resources.yaml", "utf8");
136670
136775
  } catch {
136671
136776
  return null;
136672
136777
  }
@@ -136725,12 +136830,15 @@ function getEnvironmentUidsFromResources(resourcesState) {
136725
136830
  function normalizeToPosix(filePath) {
136726
136831
  return filePath.split(path3.sep).join("/").replace(/\\/g, "/");
136727
136832
  }
136833
+ function canonicalizeRelativePath(value) {
136834
+ return normalizeToPosix(String(value ?? "").trim()).replace(/^\.\/+/, "").replace(/\/{2,}/g, "/").replace(/\/+$/g, "");
136835
+ }
136728
136836
  function isOpenApiSpecFile(filePath) {
136729
136837
  if (!(filePath.endsWith(".json") || filePath.endsWith(".yaml") || filePath.endsWith(".yml"))) {
136730
136838
  return false;
136731
136839
  }
136732
136840
  try {
136733
- const raw = (0, import_node_fs4.readFileSync)(filePath, "utf8");
136841
+ const raw = (0, import_node_fs5.readFileSync)(filePath, "utf8");
136734
136842
  const parsed = filePath.endsWith(".json") ? JSON.parse(raw) : load(raw);
136735
136843
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
136736
136844
  return false;
@@ -136759,7 +136867,7 @@ function scanLocalSpecReferences(baseDir = ".") {
136759
136867
  const found = /* @__PURE__ */ new Set();
136760
136868
  const refs = [];
136761
136869
  const visit2 = (currentDir) => {
136762
- for (const entry of (0, import_node_fs4.readdirSync)(currentDir, { withFileTypes: true })) {
136870
+ for (const entry of (0, import_node_fs5.readdirSync)(currentDir, { withFileTypes: true })) {
136763
136871
  if (ignoredDirs.has(entry.name)) {
136764
136872
  continue;
136765
136873
  }
@@ -136789,7 +136897,7 @@ function resolveMappedSpecReference(explicitSpecPath, discoveredSpecs) {
136789
136897
  const normalizedExplicitPath = normalizeToPosix(explicitSpecPath.trim());
136790
136898
  if (normalizedExplicitPath) {
136791
136899
  const explicitFullPath = path3.resolve(normalizedExplicitPath);
136792
- if ((0, import_node_fs4.existsSync)(explicitFullPath) && (0, import_node_fs4.statSync)(explicitFullPath).isFile()) {
136900
+ if ((0, import_node_fs5.existsSync)(explicitFullPath) && (0, import_node_fs5.statSync)(explicitFullPath).isFile()) {
136793
136901
  return {
136794
136902
  repoRelativePath: normalizedExplicitPath,
136795
136903
  configRelativePath: normalizeToPosix(path3.join("..", normalizedExplicitPath))
@@ -136915,6 +137023,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
136915
137023
  );
136916
137024
  }
136917
137025
  envUids[envName] = existingUid;
137026
+ dependencies.core.setOutput("environment-uids-json", JSON.stringify(envUids));
136918
137027
  continue;
136919
137028
  }
136920
137029
  const values = buildEnvironmentValues(envName, runtimeUrl);
@@ -136925,6 +137034,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
136925
137034
  displayName,
136926
137035
  values
136927
137036
  );
137037
+ dependencies.core.setOutput("environment-uids-json", JSON.stringify(envUids));
136928
137038
  } catch (error) {
136929
137039
  throw new Error(
136930
137040
  formatOrchestrationIssue({
@@ -136941,7 +137051,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
136941
137051
  return envUids;
136942
137052
  }
136943
137053
  function ensureDir(path5) {
136944
- (0, import_node_fs4.mkdirSync)(path5, { recursive: true });
137054
+ (0, import_node_fs5.mkdirSync)(path5, { recursive: true });
136945
137055
  }
136946
137056
  function getCollectionDirectoryName(kind, projectName) {
136947
137057
  if (kind === "Baseline") {
@@ -136978,7 +137088,7 @@ function stripVolatileFields(obj) {
136978
137088
  }
136979
137089
  function writeJsonFile(path5, content, normalize4 = false) {
136980
137090
  const data = normalize4 ? stripVolatileFields(content) : content;
136981
- (0, import_node_fs4.writeFileSync)(path5, JSON.stringify(data, null, 2));
137091
+ (0, import_node_fs5.writeFileSync)(path5, JSON.stringify(data, null, 2));
136982
137092
  }
136983
137093
  function buildMappedSpecCloudKey(mappedSource, specSyncMode, releaseLabel) {
136984
137094
  if (specSyncMode !== "version") {
@@ -137042,22 +137152,422 @@ function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir,
137042
137152
  sortKeys: false
137043
137153
  });
137044
137154
  }
137045
- function buildSpecCollectionWorkflowManifest(specRef, collectionRefs) {
137046
- return dump(
137047
- {
137048
- workflows: {
137049
- syncSpecToCollection: collectionRefs.map((collectionRef) => ({
137050
- spec: specRef,
137051
- collection: collectionRef
137052
- }))
137155
+ function isPlainObject2(value) {
137156
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
137157
+ }
137158
+ function buildSpecCollectionWorkflowManifest(specRef, collectionRefs, existingRaw) {
137159
+ const pairs = collectionRefs.map((collectionRef) => ({
137160
+ spec: specRef,
137161
+ collection: collectionRef
137162
+ }));
137163
+ let root = {};
137164
+ if (typeof existingRaw === "string" && existingRaw.trim()) {
137165
+ let parsed;
137166
+ try {
137167
+ parsed = load(existingRaw);
137168
+ } catch (error) {
137169
+ throw new Error(
137170
+ `CONTRACT_WORKFLOWS_UNREADABLE: .postman/workflows.yaml exists but is not parseable YAML (${error instanceof Error ? error.message : String(error)}). Fix or delete the file; refusing to overwrite unrelated workflow data.`,
137171
+ { cause: error }
137172
+ );
137173
+ }
137174
+ if (parsed === null || parsed === void 0) {
137175
+ root = {};
137176
+ } else if (!isPlainObject2(parsed)) {
137177
+ throw new Error(
137178
+ "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."
137179
+ );
137180
+ } else {
137181
+ root = { ...parsed };
137182
+ }
137183
+ }
137184
+ const workflows = isPlainObject2(root.workflows) ? { ...root.workflows } : {};
137185
+ const desiredCollections = new Set(collectionRefs);
137186
+ const currentPairs = (Array.isArray(workflows.syncSpecToCollection) ? workflows.syncSpecToCollection : []).map((entry) => isPlainObject2(entry) ? { ...entry } : null).filter((entry) => Boolean(entry)).filter(
137187
+ (entry) => String(entry.spec ?? "") !== specRef || desiredCollections.has(String(entry.collection ?? ""))
137188
+ );
137189
+ for (const pair of pairs) {
137190
+ const index = currentPairs.findIndex(
137191
+ (entry) => String(entry.spec ?? "") === pair.spec && String(entry.collection ?? "") === pair.collection
137192
+ );
137193
+ if (index >= 0) {
137194
+ const previous = currentPairs[index];
137195
+ currentPairs[index] = { ...previous, spec: pair.spec, collection: pair.collection };
137196
+ } else {
137197
+ currentPairs.push({ spec: pair.spec, collection: pair.collection });
137198
+ }
137199
+ }
137200
+ workflows.syncSpecToCollection = currentPairs;
137201
+ root.workflows = workflows;
137202
+ return dump(root, {
137203
+ lineWidth: -1,
137204
+ noRefs: true,
137205
+ sortKeys: false
137206
+ });
137207
+ }
137208
+ var PREBUILT_COLLECTION_ROLES = /* @__PURE__ */ new Set([
137209
+ "baseline",
137210
+ "smoke",
137211
+ "contract"
137212
+ ]);
137213
+ var SHA256_HEX = /^[a-f0-9]{64}$/;
137214
+ var PREBUILT_CLOUD_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
137215
+ function failPrebuiltCollections(detail) {
137216
+ throw new Error(`CONTRACT_PREBUILT_COLLECTIONS_INVALID: ${detail}`);
137217
+ }
137218
+ function parsePrebuiltCollectionsJson(raw) {
137219
+ const trimmed = String(raw ?? "").trim();
137220
+ if (!trimmed) {
137221
+ return [];
137222
+ }
137223
+ let parsed;
137224
+ try {
137225
+ parsed = JSON.parse(trimmed);
137226
+ } catch (error) {
137227
+ throw new Error(
137228
+ `CONTRACT_PREBUILT_COLLECTIONS_INVALID: malformed JSON (${error instanceof Error ? error.message : String(error)})`,
137229
+ { cause: error }
137230
+ );
137231
+ }
137232
+ let entriesRaw;
137233
+ if (Array.isArray(parsed)) {
137234
+ entriesRaw = parsed;
137235
+ } else if (isPlainObject2(parsed)) {
137236
+ if (parsed.schemaVersion !== 1) {
137237
+ failPrebuiltCollections("schemaVersion must be 1");
137238
+ }
137239
+ if (!Array.isArray(parsed.collections)) {
137240
+ failPrebuiltCollections("collections must be an array when schemaVersion is set");
137241
+ }
137242
+ entriesRaw = parsed.collections;
137243
+ } else {
137244
+ failPrebuiltCollections("expected a JSON array or {schemaVersion:1,collections:[]}");
137245
+ }
137246
+ const seenRoles = /* @__PURE__ */ new Set();
137247
+ const entries = [];
137248
+ for (const [index, value] of entriesRaw.entries()) {
137249
+ if (!isPlainObject2(value)) {
137250
+ failPrebuiltCollections(`collections[${index}] must be an object`);
137251
+ }
137252
+ const roleRaw = String(value.role ?? "").trim();
137253
+ if (!PREBUILT_COLLECTION_ROLES.has(roleRaw)) {
137254
+ failPrebuiltCollections(
137255
+ `collections[${index}].role must be one of baseline|smoke|contract`
137256
+ );
137257
+ }
137258
+ const role = roleRaw;
137259
+ if (seenRoles.has(role)) {
137260
+ failPrebuiltCollections(`duplicate role ${role}`);
137261
+ }
137262
+ seenRoles.add(role);
137263
+ const collectionPath = String(
137264
+ value.collectionPath ?? value.path ?? ""
137265
+ ).trim();
137266
+ if (!collectionPath || hasControlCharacter2(collectionPath)) {
137267
+ failPrebuiltCollections(
137268
+ `collections[${index}].collectionPath must be a non-empty confined relative path`
137269
+ );
137270
+ }
137271
+ const cloudId = String(value.cloudId ?? "").trim();
137272
+ if (!cloudId || !PREBUILT_CLOUD_ID.test(cloudId)) {
137273
+ failPrebuiltCollections(
137274
+ `collections[${index}].cloudId must be a non-empty Postman collection id`
137275
+ );
137276
+ }
137277
+ let payloadDigest;
137278
+ if (Object.prototype.hasOwnProperty.call(value, "payloadDigest")) {
137279
+ const payloadDigestRaw = value.payloadDigest;
137280
+ if (payloadDigestRaw !== void 0 && payloadDigestRaw !== null) {
137281
+ payloadDigest = String(payloadDigestRaw).trim();
137282
+ if (!SHA256_HEX.test(payloadDigest)) {
137283
+ failPrebuiltCollections(
137284
+ `collections[${index}].payloadDigest must be lowercase 64-hex`
137285
+ );
137286
+ }
137053
137287
  }
137054
- },
137055
- {
137056
- lineWidth: -1,
137057
- noRefs: true,
137058
- sortKeys: false
137059
137288
  }
137289
+ const artifactDigest = String(value.artifactDigest ?? "").trim();
137290
+ if (!SHA256_HEX.test(artifactDigest)) {
137291
+ failPrebuiltCollections(
137292
+ `collections[${index}].artifactDigest must be lowercase 64-hex`
137293
+ );
137294
+ }
137295
+ entries.push({
137296
+ role,
137297
+ collectionPath,
137298
+ cloudId,
137299
+ ...payloadDigest !== void 0 ? { payloadDigest } : {},
137300
+ artifactDigest
137301
+ });
137302
+ }
137303
+ return entries;
137304
+ }
137305
+ function assertPathWithinArtifactRoot(targetPath, artifactDir, fieldName) {
137306
+ assertPathWithinCwd(targetPath, fieldName);
137307
+ assertPathWithinCwd(artifactDir, "artifact-dir");
137308
+ const cwd = (0, import_node_fs5.realpathSync)(process.cwd());
137309
+ const artifactRoot = path3.resolve(cwd, artifactDir.trim());
137310
+ const resolved = path3.resolve(cwd, targetPath.trim());
137311
+ const relativeToArtifact = path3.relative(artifactRoot, resolved);
137312
+ if (!relativeToArtifact || relativeToArtifact.startsWith("..") || path3.isAbsolute(relativeToArtifact)) {
137313
+ failPrebuiltCollections(
137314
+ `${fieldName} must stay under artifact-dir (${artifactDir}); received ${targetPath}`
137315
+ );
137316
+ }
137317
+ let existingPath = resolved;
137318
+ while (!(0, import_node_fs5.existsSync)(existingPath)) {
137319
+ const parent = path3.dirname(existingPath);
137320
+ if (parent === existingPath) {
137321
+ break;
137322
+ }
137323
+ existingPath = parent;
137324
+ }
137325
+ const realExisting = (0, import_node_fs5.realpathSync)(existingPath);
137326
+ const realRelative = path3.relative(artifactRoot, realExisting);
137327
+ if (realRelative.startsWith("..") || path3.isAbsolute(realRelative)) {
137328
+ failPrebuiltCollections(
137329
+ `${fieldName} resolves outside artifact-dir via symlink; received ${targetPath}`
137330
+ );
137331
+ }
137332
+ return normalizeToPosix(path3.relative(cwd, resolved));
137333
+ }
137334
+ function prebuiltDirectoryTraversalIdentity(absolutePath, stats, options = {}) {
137335
+ const platform2 = options.platform ?? process.platform;
137336
+ const inodeIsZero = stats.ino === 0 || stats.ino === 0n;
137337
+ if (platform2 !== "win32" && !inodeIsZero) {
137338
+ return `${stats.dev}:${stats.ino}`;
137339
+ }
137340
+ const resolveCanonicalPath = options.resolveCanonicalPath ?? ((candidate) => import_node_fs5.realpathSync.native(candidate));
137341
+ const canonical = resolveCanonicalPath(absolutePath);
137342
+ return platform2 === "win32" ? canonical.toLowerCase() : canonical;
137343
+ }
137344
+ function listPrebuiltCollectionTreeFiles(collectionPath, artifactDir) {
137345
+ const confined = assertPathWithinArtifactRoot(
137346
+ collectionPath,
137347
+ artifactDir,
137348
+ "prebuilt collection path"
137060
137349
  );
137350
+ const absRoot = path3.resolve(process.cwd(), confined);
137351
+ if (!(0, import_node_fs5.existsSync)(absRoot)) {
137352
+ return [];
137353
+ }
137354
+ let rootStat;
137355
+ try {
137356
+ rootStat = (0, import_node_fs5.lstatSync)(absRoot);
137357
+ } catch {
137358
+ return [];
137359
+ }
137360
+ if (rootStat.isSymbolicLink()) {
137361
+ failPrebuiltCollections(
137362
+ `prebuilt collection path must not be a symlink; received ${collectionPath}`
137363
+ );
137364
+ }
137365
+ if (!rootStat.isDirectory()) {
137366
+ return [];
137367
+ }
137368
+ const files = [];
137369
+ const pendingDirectories = [absRoot];
137370
+ const seenDirectories = /* @__PURE__ */ new Set();
137371
+ while (pendingDirectories.length > 0) {
137372
+ const currentAbsolute = pendingDirectories.pop();
137373
+ if (!currentAbsolute) {
137374
+ break;
137375
+ }
137376
+ const currentStat = (0, import_node_fs5.lstatSync)(currentAbsolute);
137377
+ if (currentStat.isSymbolicLink() || !currentStat.isDirectory()) {
137378
+ failPrebuiltCollections(
137379
+ `prebuilt collection tree directory changed or became unsupported at ${normalizeToPosix(path3.relative(absRoot, currentAbsolute)) || "."}`
137380
+ );
137381
+ }
137382
+ const currentKey = prebuiltDirectoryTraversalIdentity(currentAbsolute, currentStat);
137383
+ if (seenDirectories.has(currentKey)) {
137384
+ failPrebuiltCollections(
137385
+ `prebuilt collection tree directory cycle detected at ${normalizeToPosix(path3.relative(absRoot, currentAbsolute)) || "."}`
137386
+ );
137387
+ }
137388
+ seenDirectories.add(currentKey);
137389
+ const entries = (0, import_node_fs5.readdirSync)(currentAbsolute, { withFileTypes: true });
137390
+ for (const entry of entries) {
137391
+ const abs = path3.join(currentAbsolute, entry.name);
137392
+ if (entry.isSymbolicLink()) {
137393
+ failPrebuiltCollections(
137394
+ `prebuilt collection tree must not contain symlinks; received ${normalizeToPosix(path3.relative(absRoot, abs))}`
137395
+ );
137396
+ }
137397
+ if (entry.isDirectory()) {
137398
+ pendingDirectories.push(abs);
137399
+ continue;
137400
+ }
137401
+ if (!entry.isFile()) {
137402
+ failPrebuiltCollections(
137403
+ `prebuilt collection tree contains unsupported entry type at ${normalizeToPosix(path3.relative(absRoot, abs))}`
137404
+ );
137405
+ }
137406
+ const fileLstat = (0, import_node_fs5.lstatSync)(abs);
137407
+ if (fileLstat.isSymbolicLink() || !fileLstat.isFile()) {
137408
+ failPrebuiltCollections(
137409
+ `prebuilt collection tree file changed or became unsupported at ${normalizeToPosix(path3.relative(absRoot, abs))}`
137410
+ );
137411
+ }
137412
+ files.push({
137413
+ absolute: abs,
137414
+ relative: normalizeToPosix(path3.relative(absRoot, abs)),
137415
+ dev: fileLstat.dev,
137416
+ ino: fileLstat.ino,
137417
+ size: fileLstat.size
137418
+ });
137419
+ }
137420
+ }
137421
+ files.sort((a, b) => a.relative.localeCompare(b.relative));
137422
+ return files;
137423
+ }
137424
+ function validateCanonicalV3CollectionFile(relative3, bytes) {
137425
+ let expectedKind;
137426
+ if (/(^|\/)\.resources\/definition\.yaml$/.test(relative3)) {
137427
+ expectedKind = "collection";
137428
+ } else if (relative3.endsWith(".request.yaml")) {
137429
+ expectedKind = "request";
137430
+ } else if (relative3.endsWith(".example.yaml")) {
137431
+ expectedKind = "example";
137432
+ } else if (relative3.endsWith(".message.yaml")) {
137433
+ expectedKind = "message";
137434
+ } else {
137435
+ failPrebuiltCollections(`prebuilt collection tree contains unexpected file ${relative3}`);
137436
+ }
137437
+ let parsed;
137438
+ try {
137439
+ parsed = load(bytes.toString("utf8"));
137440
+ } catch (error) {
137441
+ failPrebuiltCollections(
137442
+ `prebuilt collection tree contains malformed YAML at ${relative3} (${error instanceof Error ? error.message : String(error)})`
137443
+ );
137444
+ }
137445
+ if (!isPlainObject2(parsed)) {
137446
+ failPrebuiltCollections(`prebuilt collection tree file ${relative3} must contain a YAML mapping`);
137447
+ }
137448
+ const kind = typeof parsed.$kind === "string" ? parsed.$kind.trim() : "";
137449
+ if (!kind) {
137450
+ failPrebuiltCollections(`prebuilt collection tree file ${relative3} must contain a nonempty $kind`);
137451
+ }
137452
+ const agrees = expectedKind === "collection" ? kind === "collection" : kind.endsWith(`-${expectedKind}`);
137453
+ if (!agrees) {
137454
+ failPrebuiltCollections(
137455
+ `prebuilt collection tree file ${relative3} has $kind ${kind} inconsistent with its filename`
137456
+ );
137457
+ }
137458
+ }
137459
+ async function digestAndValidatePrebuiltCollectionTree(files) {
137460
+ if (!files.some((file) => file.relative === ".resources/definition.yaml")) {
137461
+ failPrebuiltCollections("prebuilt collection tree is missing .resources/definition.yaml");
137462
+ }
137463
+ const hash = (0, import_node_crypto3.createHash)("sha256");
137464
+ for (const file of files) {
137465
+ let bytes;
137466
+ try {
137467
+ bytes = await appendArtifactDigestFileStreaming(hash, file.relative, file.absolute, {
137468
+ dev: file.dev,
137469
+ ino: file.ino,
137470
+ size: file.size
137471
+ });
137472
+ } catch (error) {
137473
+ if (error instanceof ArtifactDigestStreamError) {
137474
+ failPrebuiltCollections(error.message);
137475
+ }
137476
+ const code = error?.code;
137477
+ if (code === "ELOOP" || code === "EPERM") {
137478
+ failPrebuiltCollections(
137479
+ `prebuilt collection tree file became a symlink or unsupported node at ${file.relative}`
137480
+ );
137481
+ }
137482
+ failPrebuiltCollections(
137483
+ `prebuilt collection tree file changed or became unsupported at ${file.relative}`
137484
+ );
137485
+ }
137486
+ validateCanonicalV3CollectionFile(file.relative, bytes);
137487
+ }
137488
+ return hash.digest("hex");
137489
+ }
137490
+ async function preparePrebuiltCollections(inputs) {
137491
+ const prepared = /* @__PURE__ */ new Map();
137492
+ for (const entry of parsePrebuiltCollectionsJson(inputs.prebuiltCollectionsJson ?? "")) {
137493
+ const confinedPath = assertPathWithinArtifactRoot(
137494
+ entry.collectionPath,
137495
+ inputs.artifactDir,
137496
+ `prebuilt ${entry.role} collection path`
137497
+ );
137498
+ const absolute = path3.resolve(process.cwd(), confinedPath);
137499
+ if (!(0, import_node_fs5.existsSync)(absolute)) {
137500
+ prepared.set(entry.role, { entry, confinedPath });
137501
+ continue;
137502
+ }
137503
+ const files = listPrebuiltCollectionTreeFiles(confinedPath, inputs.artifactDir);
137504
+ const artifactDigest = await digestAndValidatePrebuiltCollectionTree(files);
137505
+ prepared.set(entry.role, {
137506
+ entry,
137507
+ confinedPath,
137508
+ artifactDigest
137509
+ });
137510
+ }
137511
+ return prepared;
137512
+ }
137513
+ function tryReusePrebuiltCollection(options) {
137514
+ const { prepared, expectedPath, expectedCloudId } = options;
137515
+ const { entry } = prepared;
137516
+ if (!expectedCloudId || entry.cloudId !== expectedCloudId) {
137517
+ return false;
137518
+ }
137519
+ const expectedNormalized = canonicalizeRelativePath(expectedPath);
137520
+ const entryNormalized = canonicalizeRelativePath(entry.collectionPath);
137521
+ if (entryNormalized !== expectedNormalized) {
137522
+ return false;
137523
+ }
137524
+ const confined = canonicalizeRelativePath(prepared.confinedPath);
137525
+ if (confined !== expectedNormalized) {
137526
+ return false;
137527
+ }
137528
+ const expectedName = path3.posix.basename(expectedNormalized);
137529
+ if (path3.posix.basename(confined) !== expectedName) {
137530
+ return false;
137531
+ }
137532
+ return prepared.artifactDigest === entry.artifactDigest;
137533
+ }
137534
+ async function exportCollectionArtifact(options) {
137535
+ const {
137536
+ role,
137537
+ collectionId,
137538
+ dirName,
137539
+ collectionsDir,
137540
+ prebuiltByRole,
137541
+ postman,
137542
+ core
137543
+ } = options;
137544
+ if (!collectionId) {
137545
+ return void 0;
137546
+ }
137547
+ const expectedPath = `${collectionsDir}/${dirName}`;
137548
+ const entry = prebuiltByRole.get(role);
137549
+ if (entry) {
137550
+ const reused = tryReusePrebuiltCollection({
137551
+ prepared: entry,
137552
+ expectedPath,
137553
+ expectedCloudId: collectionId
137554
+ });
137555
+ if (reused) {
137556
+ core.info(
137557
+ `Reusing prebuilt ${role} collection tree at ${expectedPath} (artifactDigest match)`
137558
+ );
137559
+ return `../${expectedPath}`;
137560
+ }
137561
+ core.info(
137562
+ `Prebuilt ${role} collection entry present but did not exactly match; exporting from cloud`
137563
+ );
137564
+ }
137565
+ const col = await postman.getCollection(collectionId);
137566
+ await convertAndSplitAnyCollection(
137567
+ col,
137568
+ expectedPath
137569
+ );
137570
+ return `../${expectedPath}`;
137061
137571
  }
137062
137572
  function hasControlCharacter2(value) {
137063
137573
  for (let index = 0; index < value.length; index += 1) {
@@ -137075,21 +137585,21 @@ function assertPathWithinCwd(targetPath, fieldName) {
137075
137585
  if (!rawPath || hasControlCharacter2(originalPath) || path3.isAbsolute(rawPath) || path3.win32.isAbsolute(rawPath) || segments.includes("..") || rawPath.startsWith(":") || hasControlCharacter2(rawPath)) {
137076
137586
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
137077
137587
  }
137078
- const base = (0, import_node_fs4.realpathSync)(process.cwd());
137588
+ const base = (0, import_node_fs5.realpathSync)(process.cwd());
137079
137589
  const resolved = path3.resolve(base, rawPath);
137080
137590
  const relative3 = path3.relative(base, resolved);
137081
137591
  if (relative3.startsWith("..") || path3.isAbsolute(relative3)) {
137082
137592
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
137083
137593
  }
137084
137594
  let existingPath = resolved;
137085
- while (!(0, import_node_fs4.existsSync)(existingPath)) {
137595
+ while (!(0, import_node_fs5.existsSync)(existingPath)) {
137086
137596
  const parent = path3.dirname(existingPath);
137087
137597
  if (parent === existingPath) {
137088
137598
  break;
137089
137599
  }
137090
137600
  existingPath = parent;
137091
137601
  }
137092
- const realExistingPath = (0, import_node_fs4.realpathSync)(existingPath);
137602
+ const realExistingPath = (0, import_node_fs5.realpathSync)(existingPath);
137093
137603
  const realRelative = path3.relative(base, realExistingPath);
137094
137604
  if (realRelative.startsWith("..") || path3.isAbsolute(realRelative)) {
137095
137605
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
@@ -137117,8 +137627,8 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
137117
137627
  ensureDir(specsDir);
137118
137628
  ensureDir(".postman");
137119
137629
  const globalsFilePath = `${globalsDir}/workspace.globals.yaml`;
137120
- if (!(0, import_node_fs4.existsSync)(globalsFilePath)) {
137121
- (0, import_node_fs4.writeFileSync)(globalsFilePath, "name: Globals\nvalues: []\n");
137630
+ if (!(0, import_node_fs5.existsSync)(globalsFilePath)) {
137631
+ (0, import_node_fs5.writeFileSync)(globalsFilePath, "name: Globals\nvalues: []\n");
137122
137632
  }
137123
137633
  if (inputs.generateCiWorkflow) {
137124
137634
  const ciDir = inputs.ciWorkflowPath.split("/").slice(0, -1).join("/");
@@ -137134,23 +137644,42 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
137134
137644
  inputs.specSyncMode,
137135
137645
  options.releaseLabel
137136
137646
  ) : void 0;
137137
- if (inputs.baselineCollectionId) {
137138
- const col = await dependencies.postman.getCollection(inputs.baselineCollectionId);
137139
- const dirName = getCollectionDirectoryName("Baseline", assetProjectName);
137140
- await convertAndSplitAnyCollection(col, `${collectionsDir}/${dirName}`);
137141
- manifestCollections[`../${collectionsDir}/${dirName}`] = inputs.baselineCollectionId;
137142
- }
137143
- if (inputs.smokeCollectionId) {
137144
- const col = await dependencies.postman.getCollection(inputs.smokeCollectionId);
137145
- const dirName = getCollectionDirectoryName("Smoke", assetProjectName);
137146
- await convertAndSplitAnyCollection(col, `${collectionsDir}/${dirName}`);
137147
- manifestCollections[`../${collectionsDir}/${dirName}`] = inputs.smokeCollectionId;
137148
- }
137149
- if (inputs.contractCollectionId) {
137150
- const col = await dependencies.postman.getCollection(inputs.contractCollectionId);
137151
- const dirName = getCollectionDirectoryName("Contract", assetProjectName);
137152
- await convertAndSplitAnyCollection(col, `${collectionsDir}/${dirName}`);
137153
- manifestCollections[`../${collectionsDir}/${dirName}`] = inputs.contractCollectionId;
137647
+ const prebuiltByRole = options.preparedPrebuiltCollections;
137648
+ const baselineRef = await exportCollectionArtifact({
137649
+ role: "baseline",
137650
+ collectionId: inputs.baselineCollectionId,
137651
+ dirName: getCollectionDirectoryName("Baseline", assetProjectName),
137652
+ collectionsDir,
137653
+ prebuiltByRole,
137654
+ postman: dependencies.postman,
137655
+ core: dependencies.core
137656
+ });
137657
+ if (baselineRef) {
137658
+ manifestCollections[baselineRef] = inputs.baselineCollectionId;
137659
+ }
137660
+ const smokeRef = await exportCollectionArtifact({
137661
+ role: "smoke",
137662
+ collectionId: inputs.smokeCollectionId,
137663
+ dirName: getCollectionDirectoryName("Smoke", assetProjectName),
137664
+ collectionsDir,
137665
+ prebuiltByRole,
137666
+ postman: dependencies.postman,
137667
+ core: dependencies.core
137668
+ });
137669
+ if (smokeRef) {
137670
+ manifestCollections[smokeRef] = inputs.smokeCollectionId;
137671
+ }
137672
+ const contractRef = await exportCollectionArtifact({
137673
+ role: "contract",
137674
+ collectionId: inputs.contractCollectionId,
137675
+ dirName: getCollectionDirectoryName("Contract", assetProjectName),
137676
+ collectionsDir,
137677
+ prebuiltByRole,
137678
+ postman: dependencies.postman,
137679
+ core: dependencies.core
137680
+ });
137681
+ if (contractRef) {
137682
+ manifestCollections[contractRef] = inputs.contractCollectionId;
137154
137683
  }
137155
137684
  for (const [envName, envUid] of Object.entries(envUids)) {
137156
137685
  writeJsonFile(
@@ -137165,7 +137694,7 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
137165
137694
  workspaceLinkEnabled: inputs.workspaceLinkEnabled,
137166
137695
  workspaceLinkStatus: options.workspaceLinkStatus
137167
137696
  });
137168
- (0, import_node_fs4.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
137697
+ (0, import_node_fs5.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
137169
137698
  durableWorkspaceId,
137170
137699
  manifestCollections,
137171
137700
  envUids,
@@ -137177,11 +137706,18 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
137177
137706
  options.priorState
137178
137707
  ));
137179
137708
  if (mappedSpec && Object.keys(manifestCollections).length > 0) {
137180
- (0, import_node_fs4.writeFileSync)(
137709
+ let existingWorkflows;
137710
+ try {
137711
+ existingWorkflows = (0, import_node_fs5.readFileSync)(".postman/workflows.yaml", "utf8");
137712
+ } catch {
137713
+ existingWorkflows = void 0;
137714
+ }
137715
+ (0, import_node_fs5.writeFileSync)(
137181
137716
  ".postman/workflows.yaml",
137182
137717
  buildSpecCollectionWorkflowManifest(
137183
137718
  mappedSpec.configRelativePath,
137184
- Object.keys(manifestCollections)
137719
+ Object.keys(manifestCollections),
137720
+ existingWorkflows
137185
137721
  )
137186
137722
  );
137187
137723
  }
@@ -137226,12 +137762,12 @@ async function commitAndPushGeneratedFiles(inputs, dependencies) {
137226
137762
  const dir = parts.slice(0, -1).join("/");
137227
137763
  ensureDir(dir);
137228
137764
  }
137229
- (0, import_node_fs4.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
137765
+ (0, import_node_fs5.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
137230
137766
  if (inputs.provider === "github" || inputs.provider === "unknown") {
137231
137767
  const gcPath = ".github/workflows/postman-preview-gc.yml";
137232
137768
  if (inputs.ciWorkflowPath.endsWith(".github/workflows/ci.yml") || inputs.ciWorkflowPath === ".github/workflows/ci.yml") {
137233
137769
  ensureDir(".github/workflows");
137234
- (0, import_node_fs4.writeFileSync)(gcPath, renderGcWorkflowTemplate());
137770
+ (0, import_node_fs5.writeFileSync)(gcPath, renderGcWorkflowTemplate());
137235
137771
  }
137236
137772
  }
137237
137773
  }
@@ -137239,16 +137775,16 @@ async function commitAndPushGeneratedFiles(inputs, dependencies) {
137239
137775
  return { commitSha: "", resolvedCurrentRef: "", pushed: false };
137240
137776
  }
137241
137777
  const provisionPath = ".github/workflows/provision.yml";
137242
- const provisionExists = inputs.provider === "github" && (0, import_node_fs4.existsSync)(provisionPath);
137778
+ const provisionExists = inputs.provider === "github" && (0, import_node_fs5.existsSync)(provisionPath);
137243
137779
  const gcWorkflowPath = ".github/workflows/postman-preview-gc.yml";
137244
- const gcExists = inputs.generateCiWorkflow && (0, import_node_fs4.existsSync)(gcWorkflowPath);
137780
+ const gcExists = inputs.generateCiWorkflow && (0, import_node_fs5.existsSync)(gcWorkflowPath);
137245
137781
  const stagePaths = [
137246
137782
  inputs.artifactDir,
137247
137783
  ".postman",
137248
137784
  inputs.generateCiWorkflow ? inputs.ciWorkflowPath : null,
137249
137785
  gcExists ? gcWorkflowPath : null,
137250
137786
  provisionExists ? provisionPath : null
137251
- ].filter((entry) => typeof entry === "string" && ((0, import_node_fs4.existsSync)(entry) || entry === provisionPath));
137787
+ ].filter((entry) => typeof entry === "string" && ((0, import_node_fs5.existsSync)(entry) || entry === provisionPath));
137252
137788
  if (stagePaths.length === 0) {
137253
137789
  dependencies.core.info("No generated repository paths were found; skipping repo mutation.");
137254
137790
  return {
@@ -137356,6 +137892,7 @@ async function runRepoSyncInner(inputs, dependencies) {
137356
137892
  }
137357
137893
  }
137358
137894
  }
137895
+ const preparedPrebuiltCollections = await preparePrebuiltCollections(inputs);
137359
137896
  let skipRepositoryLinkPost = false;
137360
137897
  let repositoryLinkPreflightWasFree = false;
137361
137898
  if (inputs.workspaceLinkEnabled && inputs.workspaceId && inputs.repoUrl && dependencies.internalIntegration?.findWorkspaceForRepo) {
@@ -137392,6 +137929,7 @@ async function runRepoSyncInner(inputs, dependencies) {
137392
137929
  const branchAssetMarker = buildBranchAssetMarker(branchDecision, inputs);
137393
137930
  const envUids = await upsertEnvironments(inputs, dependencies, resourcesState, branchAssetMarker);
137394
137931
  outputs["environment-uids-json"] = JSON.stringify(envUids);
137932
+ dependencies.core.setOutput("environment-uids-json", outputs["environment-uids-json"]);
137395
137933
  if (inputs.environmentSyncEnabled && inputs.workspaceId && dependencies.internalIntegration) {
137396
137934
  const associations = Object.entries(envUids).map(([envName, envUid]) => ({
137397
137935
  envUid,
@@ -137510,6 +138048,7 @@ async function runRepoSyncInner(inputs, dependencies) {
137510
138048
  }
137511
138049
  }
137512
138050
  outputs["mock-url"] = resolvedMockUrl;
138051
+ dependencies.core.setOutput("mock-url", resolvedMockUrl);
137513
138052
  }
137514
138053
  }
137515
138054
  if (inputs.workspaceId && inputs.smokeCollectionId && Object.keys(envUids).length > 0) {
@@ -137585,6 +138124,7 @@ async function runRepoSyncInner(inputs, dependencies) {
137585
138124
  }
137586
138125
  }
137587
138126
  outputs["monitor-id"] = resolvedMonitorId;
138127
+ dependencies.core.setOutput("monitor-id", resolvedMonitorId);
137588
138128
  if (!effectiveCron && resolvedMonitorId) {
137589
138129
  try {
137590
138130
  await dependencies.postman.runMonitor(resolvedMonitorId);
@@ -137642,7 +138182,8 @@ async function runRepoSyncInner(inputs, dependencies) {
137642
138182
  priorWorkspaceId: resourcesState?.workspace?.id,
137643
138183
  existingSpecs: resourcesState?.cloudResources?.specs,
137644
138184
  releaseLabel,
137645
- priorState: resourcesState
138185
+ priorState: resourcesState,
138186
+ preparedPrebuiltCollections
137646
138187
  });
137647
138188
  const commit = await commitAndPushGeneratedFiles(inputs, dependencies);
137648
138189
  outputs["commit-sha"] = commit.commitSha;
@@ -137868,7 +138409,12 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
137868
138409
  bifrostBaseUrl: inputs.postmanBifrostBase,
137869
138410
  // No fallback on the org-mode probe: the expected non-org 400 must
137870
138411
  // surface verbatim, not be re-fired against the /_api alias.
137871
- secretMasker: masker
138412
+ secretMasker: masker,
138413
+ onRetryEvent: (event) => {
138414
+ actionCore.info(
138415
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
138416
+ );
138417
+ }
137872
138418
  });
137873
138419
  const squads = await gateway.getSquads(teamId);
137874
138420
  if (squads.length > 0) {
@@ -137932,7 +138478,12 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
137932
138478
  fallbackBaseUrl: inputs.postmanFallbackBase,
137933
138479
  teamId: resolved.teamId,
137934
138480
  orgMode: inputs.orgMode,
137935
- secretMasker: masker
138481
+ secretMasker: masker,
138482
+ onRetryEvent: (event) => {
138483
+ factories.core.info(
138484
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
138485
+ );
138486
+ }
137936
138487
  });
137937
138488
  const gatewayAssets = new PostmanGatewayAssetsClient({
137938
138489
  gateway,
@@ -138415,6 +138966,7 @@ var CLI_INPUT_NAMES = [
138415
138966
  "baseline-collection-id",
138416
138967
  "smoke-collection-id",
138417
138968
  "contract-collection-id",
138969
+ "prebuilt-collections-json",
138418
138970
  "collection-sync-mode",
138419
138971
  "spec-sync-mode",
138420
138972
  "release-label",
@@ -138486,17 +139038,38 @@ Examples:
138486
139038
  postman-repo-sync --repo-write-mode none --workspace-id <id> ...
138487
139039
  `;
138488
139040
  var ConsoleReporter = class {
139041
+ constructor(onOutput) {
139042
+ this.onOutput = onOutput;
139043
+ }
139044
+ onOutput;
138489
139045
  info(message) {
138490
139046
  console.error(message);
138491
139047
  }
138492
139048
  warning(message) {
138493
139049
  console.error(`warning: ${message}`);
138494
139050
  }
138495
- setOutput() {
139051
+ retryEvent(event) {
139052
+ this.info(
139053
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
139054
+ );
139055
+ }
139056
+ setOutput(name, value) {
139057
+ this.onOutput?.(name, value);
138496
139058
  }
138497
139059
  setSecret() {
138498
139060
  }
138499
139061
  };
139062
+ async function timedStage(reporter, stage, operation) {
139063
+ const started = performance.now();
139064
+ try {
139065
+ const result = await operation();
139066
+ reporter.info(`[repo-sync timing] ${JSON.stringify({ stage, ms: Math.max(0, performance.now() - started), status: "success" })}`);
139067
+ return result;
139068
+ } catch (error) {
139069
+ reporter.info(`[repo-sync timing] ${JSON.stringify({ stage, ms: Math.max(0, performance.now() - started), status: "error" })}`);
139070
+ throw error;
139071
+ }
139072
+ }
138500
139073
  function normalizeCliFlag(name) {
138501
139074
  return `INPUT_${name.replace(/-/g, "_").toUpperCase()}`;
138502
139075
  }
@@ -138570,7 +139143,7 @@ function resolvePackageVersion() {
138570
139143
  candidates.push(import_node_path3.default.join(process.cwd(), "package.json"));
138571
139144
  for (const packageJsonPath of candidates) {
138572
139145
  try {
138573
- const packageJson = JSON.parse((0, import_node_fs5.readFileSync)(packageJsonPath, "utf8"));
139146
+ const packageJson = JSON.parse((0, import_node_fs6.readFileSync)(packageJsonPath, "utf8"));
138574
139147
  if (packageJson.name === "@postman-cse/onboarding-repo-sync" && packageJson.version) {
138575
139148
  return String(packageJson.version).trim();
138576
139149
  }
@@ -138681,20 +139254,107 @@ function toDotenv(outputs) {
138681
139254
  String(value ?? "")
138682
139255
  ]).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
138683
139256
  }
138684
- async function writeOptionalFile(filePath, content) {
138685
- if (!filePath) {
138686
- return;
138687
- }
138688
- const workspaceRoot = import_node_path3.default.resolve(process.cwd());
139257
+ function assertOutputFileAllowed(filePath) {
139258
+ const workspaceRoot = (0, import_node_fs6.realpathSync)(import_node_path3.default.resolve(process.cwd()));
138689
139259
  const resolved = import_node_path3.default.resolve(workspaceRoot, filePath);
138690
139260
  const relative3 = import_node_path3.default.relative(workspaceRoot, resolved);
138691
139261
  if (relative3.startsWith("..") || import_node_path3.default.isAbsolute(relative3)) {
138692
139262
  throw new Error(`Output path must stay within workspace: ${filePath}`);
138693
139263
  }
138694
- await (0, import_promises.mkdir)(import_node_path3.default.dirname(resolved), { recursive: true });
139264
+ let existingParent = import_node_path3.default.dirname(resolved);
139265
+ while (!(0, import_node_fs6.existsSync)(existingParent)) {
139266
+ const parent = import_node_path3.default.dirname(existingParent);
139267
+ if (parent === existingParent) break;
139268
+ existingParent = parent;
139269
+ }
139270
+ const parentReal = (0, import_node_fs6.realpathSync)(existingParent);
139271
+ const parentRelative = import_node_path3.default.relative(workspaceRoot, parentReal);
139272
+ if (parentRelative.startsWith("..") || import_node_path3.default.isAbsolute(parentRelative)) {
139273
+ throw new Error(`Output path must stay within workspace: ${filePath}`);
139274
+ }
139275
+ if ((0, import_node_fs6.existsSync)(resolved) || (() => {
139276
+ try {
139277
+ (0, import_node_fs6.lstatSync)(resolved);
139278
+ return true;
139279
+ } catch {
139280
+ return false;
139281
+ }
139282
+ })()) {
139283
+ const targetReal = (0, import_node_fs6.realpathSync)(resolved);
139284
+ const targetRelative = import_node_path3.default.relative(workspaceRoot, targetReal);
139285
+ if (targetRelative.startsWith("..") || import_node_path3.default.isAbsolute(targetRelative)) {
139286
+ throw new Error(`Output path must stay within workspace: ${filePath}`);
139287
+ }
139288
+ }
139289
+ return { resolved, directory: import_node_path3.default.dirname(resolved) };
139290
+ }
139291
+ function resolveSymlinkAwarePath(filePath) {
139292
+ const workspaceRoot = (0, import_node_fs6.realpathSync)(import_node_path3.default.resolve(process.cwd()));
139293
+ const resolved = import_node_path3.default.resolve(workspaceRoot, filePath);
139294
+ let existing = resolved;
139295
+ while (!(0, import_node_fs6.existsSync)(existing)) {
139296
+ const parent = import_node_path3.default.dirname(existing);
139297
+ if (parent === existing) break;
139298
+ existing = parent;
139299
+ }
139300
+ return import_node_path3.default.resolve((0, import_node_fs6.realpathSync)(existing), import_node_path3.default.relative(existing, resolved));
139301
+ }
139302
+ function assertResultPathOutsideGeneratedPaths(resultJsonPath, inputs) {
139303
+ const result = assertOutputFileAllowed(resultJsonPath).resolved;
139304
+ const resultReal = resolveSymlinkAwarePath(result);
139305
+ for (const root of [inputs.artifactDir, ".postman"].map(resolveSymlinkAwarePath)) {
139306
+ const relative3 = import_node_path3.default.relative(root, resultReal);
139307
+ if (!relative3 || !relative3.startsWith("..") && !import_node_path3.default.isAbsolute(relative3)) {
139308
+ throw new Error(
139309
+ `--result-json must not overlap a generated or staged repository path: ${resultJsonPath}`
139310
+ );
139311
+ }
139312
+ }
139313
+ const generatedFiles = [];
139314
+ if (inputs.generateCiWorkflow) {
139315
+ generatedFiles.push(inputs.ciWorkflowPath);
139316
+ if ((inputs.provider === "github" || inputs.provider === "unknown") && (inputs.ciWorkflowPath.endsWith(".github/workflows/ci.yml") || inputs.ciWorkflowPath === ".github/workflows/ci.yml")) {
139317
+ generatedFiles.push(".github/workflows/postman-preview-gc.yml");
139318
+ }
139319
+ }
139320
+ if (inputs.provider === "github" && (0, import_node_fs6.existsSync)(".github/workflows/provision.yml")) {
139321
+ generatedFiles.push(".github/workflows/provision.yml");
139322
+ }
139323
+ for (const generatedFile of generatedFiles) {
139324
+ if (resultReal === resolveSymlinkAwarePath(generatedFile)) {
139325
+ throw new Error(
139326
+ `--result-json must not overlap a generated or staged repository path: ${resultJsonPath}`
139327
+ );
139328
+ }
139329
+ }
139330
+ }
139331
+ async function writeOptionalFile(filePath, content) {
139332
+ if (!filePath) {
139333
+ return;
139334
+ }
139335
+ const { resolved, directory } = assertOutputFileAllowed(filePath);
139336
+ await (0, import_promises.mkdir)(directory, { recursive: true });
139337
+ const validated = assertOutputFileAllowed(filePath);
139338
+ if (validated.resolved !== resolved) throw new Error(`Output path changed during validation: ${filePath}`);
138695
139339
  await (0, import_promises.writeFile)(resolved, content, "utf8");
138696
139340
  }
138697
- function createCliDependencies(inputs, resolved) {
139341
+ function writeOptionalFileAtomic(filePath, content) {
139342
+ if (!filePath) return;
139343
+ const { resolved, directory } = assertOutputFileAllowed(filePath);
139344
+ (0, import_node_fs6.mkdirSync)(directory, { recursive: true });
139345
+ const validated = assertOutputFileAllowed(filePath);
139346
+ const temporaryDirectory = (0, import_node_fs6.mkdtempSync)(
139347
+ import_node_path3.default.join((0, import_node_fs6.realpathSync)(validated.directory), `.${import_node_path3.default.basename(resolved)}.`)
139348
+ );
139349
+ const temporary = import_node_path3.default.join(temporaryDirectory, import_node_path3.default.basename(resolved));
139350
+ try {
139351
+ (0, import_node_fs6.writeFileSync)(temporary, content, "utf8");
139352
+ (0, import_node_fs6.renameSync)(temporary, resolved);
139353
+ } finally {
139354
+ (0, import_node_fs6.rmSync)(temporaryDirectory, { recursive: true, force: true });
139355
+ }
139356
+ }
139357
+ function createCliDependencies(inputs, resolved, reporter = new ConsoleReporter()) {
138698
139358
  const secretMasker = createSecretMasker([
138699
139359
  resolved.apiKey,
138700
139360
  inputs.postmanAccessToken,
@@ -138711,7 +139371,7 @@ function createCliDependencies(inputs, resolved) {
138711
139371
  inputs,
138712
139372
  resolved,
138713
139373
  {
138714
- core: new ConsoleReporter(),
139374
+ core: reporter,
138715
139375
  exec: cliExec
138716
139376
  },
138717
139377
  {
@@ -138741,17 +139401,24 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
138741
139401
  }
138742
139402
  const config = parseCliArgs(argv, env);
138743
139403
  const inputs = resolveInputs(config.inputEnv);
138744
- const reporter = new ConsoleReporter();
138745
139404
  const branchDecision = decideBranchTier(inputs, config.inputEnv);
139405
+ if (branchDecision.tier !== "gated") {
139406
+ assertResultPathOutsideGeneratedPaths(config.resultJsonPath, inputs);
139407
+ }
139408
+ const partialOutputs = {};
139409
+ const reporter = new ConsoleReporter((name, value) => {
139410
+ partialOutputs[name] = String(value ?? "");
139411
+ writeOptionalFileAtomic(config.resultJsonPath, JSON.stringify(partialOutputs, null, 2));
139412
+ });
138746
139413
  if (branchDecision.tier === "gated") {
138747
139414
  const result2 = runGatedSkip(inputs, branchDecision, reporter);
138748
- await writeOptionalFile(config.resultJsonPath, JSON.stringify(result2, null, 2));
139415
+ writeOptionalFileAtomic(config.resultJsonPath, JSON.stringify(result2, null, 2));
138749
139416
  await writeOptionalFile(config.dotenvPath, toDotenv(result2));
138750
139417
  writeStdout(`${JSON.stringify(result2, null, 2)}
138751
139418
  `);
138752
139419
  return;
138753
139420
  }
138754
- await mintAccessTokenIfNeeded(inputs, reporter);
139421
+ await timedStage(reporter, "access-token mint", () => mintAccessTokenIfNeeded(inputs, reporter));
138755
139422
  const initialMasker = createSecretMasker([
138756
139423
  inputs.postmanApiKey,
138757
139424
  inputs.postmanAccessToken,
@@ -138763,7 +139430,7 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
138763
139430
  inputs.sslClientPassphrase,
138764
139431
  inputs.sslExtraCaCerts
138765
139432
  ]);
138766
- await runCredentialPreflight({
139433
+ await timedStage(reporter, "credential preflight", () => runCredentialPreflight({
138767
139434
  apiBaseUrl: inputs.postmanApiBase,
138768
139435
  iapubBaseUrl: inputs.postmanIapubBase,
138769
139436
  postmanApiKey: inputs.postmanApiKey,
@@ -138772,9 +139439,9 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
138772
139439
  mode: inputs.credentialPreflight,
138773
139440
  mask: initialMasker,
138774
139441
  log: reporter
138775
- });
139442
+ }));
138776
139443
  const resolvingExec = createCliExec(initialMasker);
138777
- const resolved = await resolvePostmanApiKeyAndTeamId(
139444
+ const resolved = await timedStage(reporter, "API-key/team resolution", () => resolvePostmanApiKeyAndTeamId(
138778
139445
  inputs,
138779
139446
  reporter,
138780
139447
  resolvingExec,
@@ -138783,8 +139450,8 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
138783
139450
  persistGeneratedApiKeySecret: false,
138784
139451
  env
138785
139452
  }
138786
- );
138787
- const dependencies = createCliDependencies(inputs, resolved);
139453
+ ));
139454
+ const dependencies = createCliDependencies(inputs, resolved, reporter);
138788
139455
  if (inputs.environmentSyncEnabled && !dependencies.internalIntegration) {
138789
139456
  dependencies.core.warning(
138790
139457
  "Skipping system environment association because postman-access-token is not configured"
@@ -138795,8 +139462,12 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
138795
139462
  "Skipping workspace linking because postman-access-token is not configured"
138796
139463
  );
138797
139464
  }
138798
- const result = await (runtime.executeRepoSync ?? runRepoSync)(inputs, dependencies);
138799
- await writeOptionalFile(config.resultJsonPath, JSON.stringify(result, null, 2));
139465
+ const result = await timedStage(
139466
+ reporter,
139467
+ "runRepoSync finalize",
139468
+ () => (runtime.executeRepoSync ?? runRepoSync)(inputs, dependencies)
139469
+ );
139470
+ writeOptionalFileAtomic(config.resultJsonPath, JSON.stringify(result, null, 2));
138800
139471
  await writeOptionalFile(config.dotenvPath, toDotenv(result));
138801
139472
  writeStdout(`${JSON.stringify(result, null, 2)}
138802
139473
  `);
@@ -138882,7 +139553,7 @@ function isEntrypoint(currentPath, entrypointPath) {
138882
139553
  return false;
138883
139554
  }
138884
139555
  try {
138885
- return (0, import_node_fs5.realpathSync)(currentPath) === (0, import_node_fs5.realpathSync)(entrypointPath);
139556
+ return (0, import_node_fs6.realpathSync)(currentPath) === (0, import_node_fs6.realpathSync)(entrypointPath);
138886
139557
  } catch {
138887
139558
  return import_node_path3.default.resolve(currentPath) === import_node_path3.default.resolve(entrypointPath);
138888
139559
  }
@@ -138903,7 +139574,8 @@ if (isEntrypoint(currentModulePath, entrypoint)) {
138903
139574
  normalizeCliFlag,
138904
139575
  parseCliArgs,
138905
139576
  runCli,
138906
- toDotenv
139577
+ toDotenv,
139578
+ writeOptionalFileAtomic
138907
139579
  });
138908
139580
  /*! Bundled license information:
138909
139581