@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/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
  ],
@@ -135010,13 +135090,36 @@ var PostmanGatewayAssetsClient = class {
135010
135090
  name: String(entry.name ?? entry.message ?? "").trim()
135011
135091
  })).filter((entry) => entry.id || entry.name);
135012
135092
  }
135093
+ /**
135094
+ * Delete a collection (GC path). DELETE is idempotent: transient 408/429/5xx
135095
+ * (including Bifrost ESOCKETTIMEDOUT envelopes) are retried with the same
135096
+ * bounded budget as other idempotent writes; 404 is success (already gone).
135097
+ * Ordinary 4xx are not retried.
135098
+ */
135013
135099
  async deleteCollection(collectionUid) {
135014
135100
  const bareId = String(collectionUid).split("-").slice(-5).join("-") || collectionUid;
135015
- await this.gateway.requestJson({
135016
- service: "collection",
135017
- method: "delete",
135018
- path: `/v3/collections/${bareId}`
135019
- });
135101
+ try {
135102
+ await retry(
135103
+ () => this.gateway.requestJson({
135104
+ service: "collection",
135105
+ method: "delete",
135106
+ path: `/v3/collections/${bareId}`
135107
+ }),
135108
+ {
135109
+ maxAttempts: 5,
135110
+ delayMs: 2e3,
135111
+ backoffMultiplier: 2,
135112
+ maxDelayMs: 15e3,
135113
+ sleep: this.sleep,
135114
+ shouldRetry: (e) => this.isRetryableIdempotentWriteOutcome(e)
135115
+ }
135116
+ );
135117
+ } catch (error) {
135118
+ if (error instanceof HttpError && error.status === 404) {
135119
+ return;
135120
+ }
135121
+ throw error;
135122
+ }
135020
135123
  }
135021
135124
  async listSpecifications(workspaceId) {
135022
135125
  const response = await this.gateway.requestJson({
@@ -135684,6 +135787,7 @@ var AccessTokenProvider = class {
135684
135787
  maxAttempts;
135685
135788
  onToken;
135686
135789
  sleep;
135790
+ onRetryEvent;
135687
135791
  inflight;
135688
135792
  constructor(options) {
135689
135793
  this.token = String(options.accessToken || "").trim();
@@ -135695,6 +135799,7 @@ var AccessTokenProvider = class {
135695
135799
  this.maxAttempts = Math.max(1, options.maxAttempts ?? 2);
135696
135800
  this.onToken = options.onToken;
135697
135801
  this.sleep = options.sleep;
135802
+ this.onRetryEvent = options.onRetryEvent;
135698
135803
  }
135699
135804
  current() {
135700
135805
  return this.token;
@@ -135720,7 +135825,12 @@ var AccessTokenProvider = class {
135720
135825
  delayMs: 1e3,
135721
135826
  backoffMultiplier: 2,
135722
135827
  ...this.sleep ? { sleep: this.sleep } : {},
135723
- shouldRetry: (error) => !(error instanceof MintError && error.permanent)
135828
+ shouldRetry: (error) => !(error instanceof MintError && error.permanent),
135829
+ onRetry: ({ attempt, delayMs, error }) => {
135830
+ const status = error instanceof MintError ? error.status : void 0;
135831
+ const retryClass = status === void 0 ? "transport" : "http";
135832
+ this.onRetryEvent?.({ class: retryClass, ...status === void 0 ? {} : { status }, attempt, delay: Math.max(0, delayMs) });
135833
+ }
135724
135834
  });
135725
135835
  this.token = token;
135726
135836
  this.onToken?.(token);
@@ -135751,7 +135861,7 @@ var AccessTokenProvider = class {
135751
135861
  true
135752
135862
  );
135753
135863
  }
135754
- throw new MintError(`postman: re-mint failed (service-account-tokens HTTP ${status}).`, false);
135864
+ throw new MintError(`postman: re-mint failed (service-account-tokens HTTP ${status}).`, false, status);
135755
135865
  }
135756
135866
  let parsed;
135757
135867
  try {
@@ -135782,7 +135892,8 @@ async function mintAccessTokenIfNeeded(inputs, log, setSecret2, fetchImpl = fetc
135782
135892
  apiKey: inputs.postmanApiKey,
135783
135893
  apiBaseUrl,
135784
135894
  fetchImpl,
135785
- onToken: (token) => setSecret2?.(token)
135895
+ onToken: (token) => setSecret2?.(token),
135896
+ onRetryEvent: (event) => log.retryEvent?.(event)
135786
135897
  });
135787
135898
  try {
135788
135899
  inputs.postmanAccessToken = await provider.refresh();
@@ -135822,6 +135933,7 @@ var AccessTokenGatewayClient = class {
135822
135933
  requestTimeoutMs;
135823
135934
  retryMaxDelayMs;
135824
135935
  randomImpl;
135936
+ onRetryEvent;
135825
135937
  constructor(options) {
135826
135938
  this.tokenProvider = options.tokenProvider;
135827
135939
  this.bifrostBaseUrl = String(
@@ -135840,6 +135952,7 @@ var AccessTokenGatewayClient = class {
135840
135952
  this.requestTimeoutMs = options.requestTimeoutMs ?? 3e4;
135841
135953
  this.retryMaxDelayMs = options.retryMaxDelayMs ?? 5e3;
135842
135954
  this.randomImpl = options.randomImpl ?? Math.random;
135955
+ this.onRetryEvent = options.onRetryEvent;
135843
135956
  }
135844
135957
  configureTeamContext(teamId, orgMode) {
135845
135958
  this.teamId = String(teamId || "").trim();
@@ -135908,8 +136021,9 @@ var AccessTokenGatewayClient = class {
135908
136021
  if (!this.fallbackBaseUrl) return false;
135909
136022
  return retryTransient || request.fallback === "auto";
135910
136023
  }
135911
- async attemptFallback(request, retryTransient) {
136024
+ async attemptFallback(request, retryTransient, status) {
135912
136025
  if (!this.fallbackEligible(request, retryTransient)) return null;
136026
+ this.emitRetryEvent("fallback", status, 1, 0);
135913
136027
  const response = await this.tryFallback(request);
135914
136028
  if (!response) return null;
135915
136029
  if (response.ok) return response;
@@ -135935,6 +136049,7 @@ var AccessTokenGatewayClient = class {
135935
136049
  } catch (error) {
135936
136050
  if (retryTransient && attempt < this.maxRetries) {
135937
136051
  const delay = this.retryDelayMs(attempt);
136052
+ this.emitRetryEvent("transport", void 0, attempt + 1, delay);
135938
136053
  attempt += 1;
135939
136054
  await this.sleepImpl(delay);
135940
136055
  continue;
@@ -135948,7 +136063,9 @@ var AccessTokenGatewayClient = class {
135948
136063
  const inner = this.innerStatus(okBody);
135949
136064
  if (inner !== void 0) {
135950
136065
  if (retryTransient && this.isTransient(inner, okBody) && attempt < this.maxRetries) {
135951
- await this.sleepImpl(this.retryDelayMs(attempt));
136066
+ const delay = this.retryDelayMs(attempt);
136067
+ this.emitRetryEvent("inner", inner, attempt + 1, delay);
136068
+ await this.sleepImpl(delay);
135952
136069
  attempt += 1;
135953
136070
  continue;
135954
136071
  }
@@ -135958,6 +136075,7 @@ var AccessTokenGatewayClient = class {
135958
136075
  }
135959
136076
  const body = await response.text().catch(() => "");
135960
136077
  if (isExpiredAuthError(response.status, body) && this.tokenProvider.canRefresh()) {
136078
+ this.emitRetryEvent("auth", response.status, 1, 0);
135961
136079
  await this.tokenProvider.refresh();
135962
136080
  response = await this.send(request);
135963
136081
  if (response.ok) {
@@ -135968,11 +136086,12 @@ var AccessTokenGatewayClient = class {
135968
136086
  }
135969
136087
  if (retryTransient && this.isTransient(response.status, body) && attempt < this.maxRetries) {
135970
136088
  const delay = this.retryDelayMs(attempt, parseRetryAfterMs2(response.headers.get("retry-after")));
136089
+ this.emitRetryEvent("http", response.status, attempt + 1, delay);
135971
136090
  attempt += 1;
135972
136091
  await this.sleepImpl(delay);
135973
136092
  continue;
135974
136093
  }
135975
- const fallbackResponse = await this.attemptFallback(request, retryTransient);
136094
+ const fallbackResponse = await this.attemptFallback(request, retryTransient, response.status);
135976
136095
  if (fallbackResponse) return fallbackResponse;
135977
136096
  throw this.toHttpError(request, response, body);
135978
136097
  }
@@ -135980,6 +136099,14 @@ var AccessTokenGatewayClient = class {
135980
136099
  retryDelayMs(attempt, retryAfter) {
135981
136100
  return retryAfter === void 0 ? fullJitterDelayMs(attempt, this.retryBaseDelayMs, this.retryMaxDelayMs, this.randomImpl) : Math.min(this.retryMaxDelayMs, retryAfter);
135982
136101
  }
136102
+ emitRetryEvent(retryClass, status, attempt, delay) {
136103
+ this.onRetryEvent?.({
136104
+ class: retryClass,
136105
+ ...status === void 0 ? {} : { status },
136106
+ attempt: Math.max(1, attempt),
136107
+ delay: Math.max(0, delay)
136108
+ });
136109
+ }
135983
136110
  isTransient(status, body) {
135984
136111
  return status === 408 || status === 429 || status >= 500 || /ESOCKETTIMEDOUT|ETIMEDOUT|ECONNRESET|serverError|downstream/.test(body);
135985
136112
  }
@@ -136057,7 +136184,7 @@ var AccessTokenGatewayClient = class {
136057
136184
  };
136058
136185
 
136059
136186
  // src/lib/repo/branch-decision.ts
136060
- var import_node_fs3 = require("node:fs");
136187
+ var import_node_fs4 = require("node:fs");
136061
136188
  var import_node_crypto2 = require("node:crypto");
136062
136189
  var ContractError = class extends Error {
136063
136190
  code;
@@ -136098,7 +136225,7 @@ function readGithubEvent(env) {
136098
136225
  const path5 = clean(env.GITHUB_EVENT_PATH);
136099
136226
  if (!path5) return void 0;
136100
136227
  try {
136101
- return JSON.parse((0, import_node_fs3.readFileSync)(path5, "utf8"));
136228
+ return JSON.parse((0, import_node_fs4.readFileSync)(path5, "utf8"));
136102
136229
  } catch {
136103
136230
  return void 0;
136104
136231
  }
@@ -136551,6 +136678,7 @@ function resolveInputs(env = process.env) {
136551
136678
  baselineCollectionId: getInput("baseline-collection-id", env),
136552
136679
  smokeCollectionId: getInput("smoke-collection-id", env),
136553
136680
  contractCollectionId: getInput("contract-collection-id", env),
136681
+ prebuiltCollectionsJson: getInput("prebuilt-collections-json", env),
136554
136682
  specId: getInput("spec-id", env),
136555
136683
  specContentChanged: parseBooleanInput(getInput("spec-content-changed", env), true),
136556
136684
  specPath: getInput("spec-path", env),
@@ -136666,7 +136794,7 @@ var StateUnreadableError = class extends Error {
136666
136794
  function readResourcesState() {
136667
136795
  let raw;
136668
136796
  try {
136669
- raw = (0, import_node_fs4.readFileSync)(".postman/resources.yaml", "utf8");
136797
+ raw = (0, import_node_fs5.readFileSync)(".postman/resources.yaml", "utf8");
136670
136798
  } catch {
136671
136799
  return null;
136672
136800
  }
@@ -136725,12 +136853,15 @@ function getEnvironmentUidsFromResources(resourcesState) {
136725
136853
  function normalizeToPosix(filePath) {
136726
136854
  return filePath.split(path3.sep).join("/").replace(/\\/g, "/");
136727
136855
  }
136856
+ function canonicalizeRelativePath(value) {
136857
+ return normalizeToPosix(String(value ?? "").trim()).replace(/^\.\/+/, "").replace(/\/{2,}/g, "/").replace(/\/+$/g, "");
136858
+ }
136728
136859
  function isOpenApiSpecFile(filePath) {
136729
136860
  if (!(filePath.endsWith(".json") || filePath.endsWith(".yaml") || filePath.endsWith(".yml"))) {
136730
136861
  return false;
136731
136862
  }
136732
136863
  try {
136733
- const raw = (0, import_node_fs4.readFileSync)(filePath, "utf8");
136864
+ const raw = (0, import_node_fs5.readFileSync)(filePath, "utf8");
136734
136865
  const parsed = filePath.endsWith(".json") ? JSON.parse(raw) : load(raw);
136735
136866
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
136736
136867
  return false;
@@ -136759,7 +136890,7 @@ function scanLocalSpecReferences(baseDir = ".") {
136759
136890
  const found = /* @__PURE__ */ new Set();
136760
136891
  const refs = [];
136761
136892
  const visit2 = (currentDir) => {
136762
- for (const entry of (0, import_node_fs4.readdirSync)(currentDir, { withFileTypes: true })) {
136893
+ for (const entry of (0, import_node_fs5.readdirSync)(currentDir, { withFileTypes: true })) {
136763
136894
  if (ignoredDirs.has(entry.name)) {
136764
136895
  continue;
136765
136896
  }
@@ -136789,7 +136920,7 @@ function resolveMappedSpecReference(explicitSpecPath, discoveredSpecs) {
136789
136920
  const normalizedExplicitPath = normalizeToPosix(explicitSpecPath.trim());
136790
136921
  if (normalizedExplicitPath) {
136791
136922
  const explicitFullPath = path3.resolve(normalizedExplicitPath);
136792
- if ((0, import_node_fs4.existsSync)(explicitFullPath) && (0, import_node_fs4.statSync)(explicitFullPath).isFile()) {
136923
+ if ((0, import_node_fs5.existsSync)(explicitFullPath) && (0, import_node_fs5.statSync)(explicitFullPath).isFile()) {
136793
136924
  return {
136794
136925
  repoRelativePath: normalizedExplicitPath,
136795
136926
  configRelativePath: normalizeToPosix(path3.join("..", normalizedExplicitPath))
@@ -136915,6 +137046,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
136915
137046
  );
136916
137047
  }
136917
137048
  envUids[envName] = existingUid;
137049
+ dependencies.core.setOutput("environment-uids-json", JSON.stringify(envUids));
136918
137050
  continue;
136919
137051
  }
136920
137052
  const values = buildEnvironmentValues(envName, runtimeUrl);
@@ -136925,6 +137057,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
136925
137057
  displayName,
136926
137058
  values
136927
137059
  );
137060
+ dependencies.core.setOutput("environment-uids-json", JSON.stringify(envUids));
136928
137061
  } catch (error) {
136929
137062
  throw new Error(
136930
137063
  formatOrchestrationIssue({
@@ -136941,7 +137074,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
136941
137074
  return envUids;
136942
137075
  }
136943
137076
  function ensureDir(path5) {
136944
- (0, import_node_fs4.mkdirSync)(path5, { recursive: true });
137077
+ (0, import_node_fs5.mkdirSync)(path5, { recursive: true });
136945
137078
  }
136946
137079
  function getCollectionDirectoryName(kind, projectName) {
136947
137080
  if (kind === "Baseline") {
@@ -136978,7 +137111,7 @@ function stripVolatileFields(obj) {
136978
137111
  }
136979
137112
  function writeJsonFile(path5, content, normalize4 = false) {
136980
137113
  const data = normalize4 ? stripVolatileFields(content) : content;
136981
- (0, import_node_fs4.writeFileSync)(path5, JSON.stringify(data, null, 2));
137114
+ (0, import_node_fs5.writeFileSync)(path5, JSON.stringify(data, null, 2));
136982
137115
  }
136983
137116
  function buildMappedSpecCloudKey(mappedSource, specSyncMode, releaseLabel) {
136984
137117
  if (specSyncMode !== "version") {
@@ -137042,22 +137175,422 @@ function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir,
137042
137175
  sortKeys: false
137043
137176
  });
137044
137177
  }
137045
- function buildSpecCollectionWorkflowManifest(specRef, collectionRefs) {
137046
- return dump(
137047
- {
137048
- workflows: {
137049
- syncSpecToCollection: collectionRefs.map((collectionRef) => ({
137050
- spec: specRef,
137051
- collection: collectionRef
137052
- }))
137178
+ function isPlainObject2(value) {
137179
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
137180
+ }
137181
+ function buildSpecCollectionWorkflowManifest(specRef, collectionRefs, existingRaw) {
137182
+ const pairs = collectionRefs.map((collectionRef) => ({
137183
+ spec: specRef,
137184
+ collection: collectionRef
137185
+ }));
137186
+ let root = {};
137187
+ if (typeof existingRaw === "string" && existingRaw.trim()) {
137188
+ let parsed;
137189
+ try {
137190
+ parsed = load(existingRaw);
137191
+ } catch (error) {
137192
+ throw new Error(
137193
+ `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.`,
137194
+ { cause: error }
137195
+ );
137196
+ }
137197
+ if (parsed === null || parsed === void 0) {
137198
+ root = {};
137199
+ } else if (!isPlainObject2(parsed)) {
137200
+ throw new Error(
137201
+ "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."
137202
+ );
137203
+ } else {
137204
+ root = { ...parsed };
137205
+ }
137206
+ }
137207
+ const workflows = isPlainObject2(root.workflows) ? { ...root.workflows } : {};
137208
+ const desiredCollections = new Set(collectionRefs);
137209
+ const currentPairs = (Array.isArray(workflows.syncSpecToCollection) ? workflows.syncSpecToCollection : []).map((entry) => isPlainObject2(entry) ? { ...entry } : null).filter((entry) => Boolean(entry)).filter(
137210
+ (entry) => String(entry.spec ?? "") !== specRef || desiredCollections.has(String(entry.collection ?? ""))
137211
+ );
137212
+ for (const pair of pairs) {
137213
+ const index = currentPairs.findIndex(
137214
+ (entry) => String(entry.spec ?? "") === pair.spec && String(entry.collection ?? "") === pair.collection
137215
+ );
137216
+ if (index >= 0) {
137217
+ const previous = currentPairs[index];
137218
+ currentPairs[index] = { ...previous, spec: pair.spec, collection: pair.collection };
137219
+ } else {
137220
+ currentPairs.push({ spec: pair.spec, collection: pair.collection });
137221
+ }
137222
+ }
137223
+ workflows.syncSpecToCollection = currentPairs;
137224
+ root.workflows = workflows;
137225
+ return dump(root, {
137226
+ lineWidth: -1,
137227
+ noRefs: true,
137228
+ sortKeys: false
137229
+ });
137230
+ }
137231
+ var PREBUILT_COLLECTION_ROLES = /* @__PURE__ */ new Set([
137232
+ "baseline",
137233
+ "smoke",
137234
+ "contract"
137235
+ ]);
137236
+ var SHA256_HEX = /^[a-f0-9]{64}$/;
137237
+ var PREBUILT_CLOUD_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
137238
+ function failPrebuiltCollections(detail) {
137239
+ throw new Error(`CONTRACT_PREBUILT_COLLECTIONS_INVALID: ${detail}`);
137240
+ }
137241
+ function parsePrebuiltCollectionsJson(raw) {
137242
+ const trimmed = String(raw ?? "").trim();
137243
+ if (!trimmed) {
137244
+ return [];
137245
+ }
137246
+ let parsed;
137247
+ try {
137248
+ parsed = JSON.parse(trimmed);
137249
+ } catch (error) {
137250
+ throw new Error(
137251
+ `CONTRACT_PREBUILT_COLLECTIONS_INVALID: malformed JSON (${error instanceof Error ? error.message : String(error)})`,
137252
+ { cause: error }
137253
+ );
137254
+ }
137255
+ let entriesRaw;
137256
+ if (Array.isArray(parsed)) {
137257
+ entriesRaw = parsed;
137258
+ } else if (isPlainObject2(parsed)) {
137259
+ if (parsed.schemaVersion !== 1) {
137260
+ failPrebuiltCollections("schemaVersion must be 1");
137261
+ }
137262
+ if (!Array.isArray(parsed.collections)) {
137263
+ failPrebuiltCollections("collections must be an array when schemaVersion is set");
137264
+ }
137265
+ entriesRaw = parsed.collections;
137266
+ } else {
137267
+ failPrebuiltCollections("expected a JSON array or {schemaVersion:1,collections:[]}");
137268
+ }
137269
+ const seenRoles = /* @__PURE__ */ new Set();
137270
+ const entries = [];
137271
+ for (const [index, value] of entriesRaw.entries()) {
137272
+ if (!isPlainObject2(value)) {
137273
+ failPrebuiltCollections(`collections[${index}] must be an object`);
137274
+ }
137275
+ const roleRaw = String(value.role ?? "").trim();
137276
+ if (!PREBUILT_COLLECTION_ROLES.has(roleRaw)) {
137277
+ failPrebuiltCollections(
137278
+ `collections[${index}].role must be one of baseline|smoke|contract`
137279
+ );
137280
+ }
137281
+ const role = roleRaw;
137282
+ if (seenRoles.has(role)) {
137283
+ failPrebuiltCollections(`duplicate role ${role}`);
137284
+ }
137285
+ seenRoles.add(role);
137286
+ const collectionPath = String(
137287
+ value.collectionPath ?? value.path ?? ""
137288
+ ).trim();
137289
+ if (!collectionPath || hasControlCharacter2(collectionPath)) {
137290
+ failPrebuiltCollections(
137291
+ `collections[${index}].collectionPath must be a non-empty confined relative path`
137292
+ );
137293
+ }
137294
+ const cloudId = String(value.cloudId ?? "").trim();
137295
+ if (!cloudId || !PREBUILT_CLOUD_ID.test(cloudId)) {
137296
+ failPrebuiltCollections(
137297
+ `collections[${index}].cloudId must be a non-empty Postman collection id`
137298
+ );
137299
+ }
137300
+ let payloadDigest;
137301
+ if (Object.prototype.hasOwnProperty.call(value, "payloadDigest")) {
137302
+ const payloadDigestRaw = value.payloadDigest;
137303
+ if (payloadDigestRaw !== void 0 && payloadDigestRaw !== null) {
137304
+ payloadDigest = String(payloadDigestRaw).trim();
137305
+ if (!SHA256_HEX.test(payloadDigest)) {
137306
+ failPrebuiltCollections(
137307
+ `collections[${index}].payloadDigest must be lowercase 64-hex`
137308
+ );
137309
+ }
137053
137310
  }
137054
- },
137055
- {
137056
- lineWidth: -1,
137057
- noRefs: true,
137058
- sortKeys: false
137059
137311
  }
137312
+ const artifactDigest = String(value.artifactDigest ?? "").trim();
137313
+ if (!SHA256_HEX.test(artifactDigest)) {
137314
+ failPrebuiltCollections(
137315
+ `collections[${index}].artifactDigest must be lowercase 64-hex`
137316
+ );
137317
+ }
137318
+ entries.push({
137319
+ role,
137320
+ collectionPath,
137321
+ cloudId,
137322
+ ...payloadDigest !== void 0 ? { payloadDigest } : {},
137323
+ artifactDigest
137324
+ });
137325
+ }
137326
+ return entries;
137327
+ }
137328
+ function assertPathWithinArtifactRoot(targetPath, artifactDir, fieldName) {
137329
+ assertPathWithinCwd(targetPath, fieldName);
137330
+ assertPathWithinCwd(artifactDir, "artifact-dir");
137331
+ const cwd = (0, import_node_fs5.realpathSync)(process.cwd());
137332
+ const artifactRoot = path3.resolve(cwd, artifactDir.trim());
137333
+ const resolved = path3.resolve(cwd, targetPath.trim());
137334
+ const relativeToArtifact = path3.relative(artifactRoot, resolved);
137335
+ if (!relativeToArtifact || relativeToArtifact.startsWith("..") || path3.isAbsolute(relativeToArtifact)) {
137336
+ failPrebuiltCollections(
137337
+ `${fieldName} must stay under artifact-dir (${artifactDir}); received ${targetPath}`
137338
+ );
137339
+ }
137340
+ let existingPath = resolved;
137341
+ while (!(0, import_node_fs5.existsSync)(existingPath)) {
137342
+ const parent = path3.dirname(existingPath);
137343
+ if (parent === existingPath) {
137344
+ break;
137345
+ }
137346
+ existingPath = parent;
137347
+ }
137348
+ const realExisting = (0, import_node_fs5.realpathSync)(existingPath);
137349
+ const realRelative = path3.relative(artifactRoot, realExisting);
137350
+ if (realRelative.startsWith("..") || path3.isAbsolute(realRelative)) {
137351
+ failPrebuiltCollections(
137352
+ `${fieldName} resolves outside artifact-dir via symlink; received ${targetPath}`
137353
+ );
137354
+ }
137355
+ return normalizeToPosix(path3.relative(cwd, resolved));
137356
+ }
137357
+ function prebuiltDirectoryTraversalIdentity(absolutePath, stats, options = {}) {
137358
+ const platform2 = options.platform ?? process.platform;
137359
+ const inodeIsZero = stats.ino === 0 || stats.ino === 0n;
137360
+ if (platform2 !== "win32" && !inodeIsZero) {
137361
+ return `${stats.dev}:${stats.ino}`;
137362
+ }
137363
+ const resolveCanonicalPath = options.resolveCanonicalPath ?? ((candidate) => import_node_fs5.realpathSync.native(candidate));
137364
+ const canonical = resolveCanonicalPath(absolutePath);
137365
+ return platform2 === "win32" ? canonical.toLowerCase() : canonical;
137366
+ }
137367
+ function listPrebuiltCollectionTreeFiles(collectionPath, artifactDir) {
137368
+ const confined = assertPathWithinArtifactRoot(
137369
+ collectionPath,
137370
+ artifactDir,
137371
+ "prebuilt collection path"
137372
+ );
137373
+ const absRoot = path3.resolve(process.cwd(), confined);
137374
+ if (!(0, import_node_fs5.existsSync)(absRoot)) {
137375
+ return [];
137376
+ }
137377
+ let rootStat;
137378
+ try {
137379
+ rootStat = (0, import_node_fs5.lstatSync)(absRoot);
137380
+ } catch {
137381
+ return [];
137382
+ }
137383
+ if (rootStat.isSymbolicLink()) {
137384
+ failPrebuiltCollections(
137385
+ `prebuilt collection path must not be a symlink; received ${collectionPath}`
137386
+ );
137387
+ }
137388
+ if (!rootStat.isDirectory()) {
137389
+ return [];
137390
+ }
137391
+ const files = [];
137392
+ const pendingDirectories = [absRoot];
137393
+ const seenDirectories = /* @__PURE__ */ new Set();
137394
+ while (pendingDirectories.length > 0) {
137395
+ const currentAbsolute = pendingDirectories.pop();
137396
+ if (!currentAbsolute) {
137397
+ break;
137398
+ }
137399
+ const currentStat = (0, import_node_fs5.lstatSync)(currentAbsolute);
137400
+ if (currentStat.isSymbolicLink() || !currentStat.isDirectory()) {
137401
+ failPrebuiltCollections(
137402
+ `prebuilt collection tree directory changed or became unsupported at ${normalizeToPosix(path3.relative(absRoot, currentAbsolute)) || "."}`
137403
+ );
137404
+ }
137405
+ const currentKey = prebuiltDirectoryTraversalIdentity(currentAbsolute, currentStat);
137406
+ if (seenDirectories.has(currentKey)) {
137407
+ failPrebuiltCollections(
137408
+ `prebuilt collection tree directory cycle detected at ${normalizeToPosix(path3.relative(absRoot, currentAbsolute)) || "."}`
137409
+ );
137410
+ }
137411
+ seenDirectories.add(currentKey);
137412
+ const entries = (0, import_node_fs5.readdirSync)(currentAbsolute, { withFileTypes: true });
137413
+ for (const entry of entries) {
137414
+ const abs = path3.join(currentAbsolute, entry.name);
137415
+ if (entry.isSymbolicLink()) {
137416
+ failPrebuiltCollections(
137417
+ `prebuilt collection tree must not contain symlinks; received ${normalizeToPosix(path3.relative(absRoot, abs))}`
137418
+ );
137419
+ }
137420
+ if (entry.isDirectory()) {
137421
+ pendingDirectories.push(abs);
137422
+ continue;
137423
+ }
137424
+ if (!entry.isFile()) {
137425
+ failPrebuiltCollections(
137426
+ `prebuilt collection tree contains unsupported entry type at ${normalizeToPosix(path3.relative(absRoot, abs))}`
137427
+ );
137428
+ }
137429
+ const fileLstat = (0, import_node_fs5.lstatSync)(abs);
137430
+ if (fileLstat.isSymbolicLink() || !fileLstat.isFile()) {
137431
+ failPrebuiltCollections(
137432
+ `prebuilt collection tree file changed or became unsupported at ${normalizeToPosix(path3.relative(absRoot, abs))}`
137433
+ );
137434
+ }
137435
+ files.push({
137436
+ absolute: abs,
137437
+ relative: normalizeToPosix(path3.relative(absRoot, abs)),
137438
+ dev: fileLstat.dev,
137439
+ ino: fileLstat.ino,
137440
+ size: fileLstat.size
137441
+ });
137442
+ }
137443
+ }
137444
+ files.sort((a, b) => a.relative.localeCompare(b.relative));
137445
+ return files;
137446
+ }
137447
+ function validateCanonicalV3CollectionFile(relative3, bytes) {
137448
+ let expectedKind;
137449
+ if (/(^|\/)\.resources\/definition\.yaml$/.test(relative3)) {
137450
+ expectedKind = "collection";
137451
+ } else if (relative3.endsWith(".request.yaml")) {
137452
+ expectedKind = "request";
137453
+ } else if (relative3.endsWith(".example.yaml")) {
137454
+ expectedKind = "example";
137455
+ } else if (relative3.endsWith(".message.yaml")) {
137456
+ expectedKind = "message";
137457
+ } else {
137458
+ failPrebuiltCollections(`prebuilt collection tree contains unexpected file ${relative3}`);
137459
+ }
137460
+ let parsed;
137461
+ try {
137462
+ parsed = load(bytes.toString("utf8"));
137463
+ } catch (error) {
137464
+ failPrebuiltCollections(
137465
+ `prebuilt collection tree contains malformed YAML at ${relative3} (${error instanceof Error ? error.message : String(error)})`
137466
+ );
137467
+ }
137468
+ if (!isPlainObject2(parsed)) {
137469
+ failPrebuiltCollections(`prebuilt collection tree file ${relative3} must contain a YAML mapping`);
137470
+ }
137471
+ const kind = typeof parsed.$kind === "string" ? parsed.$kind.trim() : "";
137472
+ if (!kind) {
137473
+ failPrebuiltCollections(`prebuilt collection tree file ${relative3} must contain a nonempty $kind`);
137474
+ }
137475
+ const agrees = expectedKind === "collection" ? kind === "collection" : kind.endsWith(`-${expectedKind}`);
137476
+ if (!agrees) {
137477
+ failPrebuiltCollections(
137478
+ `prebuilt collection tree file ${relative3} has $kind ${kind} inconsistent with its filename`
137479
+ );
137480
+ }
137481
+ }
137482
+ async function digestAndValidatePrebuiltCollectionTree(files) {
137483
+ if (!files.some((file) => file.relative === ".resources/definition.yaml")) {
137484
+ failPrebuiltCollections("prebuilt collection tree is missing .resources/definition.yaml");
137485
+ }
137486
+ const hash = (0, import_node_crypto3.createHash)("sha256");
137487
+ for (const file of files) {
137488
+ let bytes;
137489
+ try {
137490
+ bytes = await appendArtifactDigestFileStreaming(hash, file.relative, file.absolute, {
137491
+ dev: file.dev,
137492
+ ino: file.ino,
137493
+ size: file.size
137494
+ });
137495
+ } catch (error) {
137496
+ if (error instanceof ArtifactDigestStreamError) {
137497
+ failPrebuiltCollections(error.message);
137498
+ }
137499
+ const code = error?.code;
137500
+ if (code === "ELOOP" || code === "EPERM") {
137501
+ failPrebuiltCollections(
137502
+ `prebuilt collection tree file became a symlink or unsupported node at ${file.relative}`
137503
+ );
137504
+ }
137505
+ failPrebuiltCollections(
137506
+ `prebuilt collection tree file changed or became unsupported at ${file.relative}`
137507
+ );
137508
+ }
137509
+ validateCanonicalV3CollectionFile(file.relative, bytes);
137510
+ }
137511
+ return hash.digest("hex");
137512
+ }
137513
+ async function preparePrebuiltCollections(inputs) {
137514
+ const prepared = /* @__PURE__ */ new Map();
137515
+ for (const entry of parsePrebuiltCollectionsJson(inputs.prebuiltCollectionsJson ?? "")) {
137516
+ const confinedPath = assertPathWithinArtifactRoot(
137517
+ entry.collectionPath,
137518
+ inputs.artifactDir,
137519
+ `prebuilt ${entry.role} collection path`
137520
+ );
137521
+ const absolute = path3.resolve(process.cwd(), confinedPath);
137522
+ if (!(0, import_node_fs5.existsSync)(absolute)) {
137523
+ prepared.set(entry.role, { entry, confinedPath });
137524
+ continue;
137525
+ }
137526
+ const files = listPrebuiltCollectionTreeFiles(confinedPath, inputs.artifactDir);
137527
+ const artifactDigest = await digestAndValidatePrebuiltCollectionTree(files);
137528
+ prepared.set(entry.role, {
137529
+ entry,
137530
+ confinedPath,
137531
+ artifactDigest
137532
+ });
137533
+ }
137534
+ return prepared;
137535
+ }
137536
+ function tryReusePrebuiltCollection(options) {
137537
+ const { prepared, expectedPath, expectedCloudId } = options;
137538
+ const { entry } = prepared;
137539
+ if (!expectedCloudId || entry.cloudId !== expectedCloudId) {
137540
+ return false;
137541
+ }
137542
+ const expectedNormalized = canonicalizeRelativePath(expectedPath);
137543
+ const entryNormalized = canonicalizeRelativePath(entry.collectionPath);
137544
+ if (entryNormalized !== expectedNormalized) {
137545
+ return false;
137546
+ }
137547
+ const confined = canonicalizeRelativePath(prepared.confinedPath);
137548
+ if (confined !== expectedNormalized) {
137549
+ return false;
137550
+ }
137551
+ const expectedName = path3.posix.basename(expectedNormalized);
137552
+ if (path3.posix.basename(confined) !== expectedName) {
137553
+ return false;
137554
+ }
137555
+ return prepared.artifactDigest === entry.artifactDigest;
137556
+ }
137557
+ async function exportCollectionArtifact(options) {
137558
+ const {
137559
+ role,
137560
+ collectionId,
137561
+ dirName,
137562
+ collectionsDir,
137563
+ prebuiltByRole,
137564
+ postman,
137565
+ core
137566
+ } = options;
137567
+ if (!collectionId) {
137568
+ return void 0;
137569
+ }
137570
+ const expectedPath = `${collectionsDir}/${dirName}`;
137571
+ const entry = prebuiltByRole.get(role);
137572
+ if (entry) {
137573
+ const reused = tryReusePrebuiltCollection({
137574
+ prepared: entry,
137575
+ expectedPath,
137576
+ expectedCloudId: collectionId
137577
+ });
137578
+ if (reused) {
137579
+ core.info(
137580
+ `Reusing prebuilt ${role} collection tree at ${expectedPath} (artifactDigest match)`
137581
+ );
137582
+ return `../${expectedPath}`;
137583
+ }
137584
+ core.info(
137585
+ `Prebuilt ${role} collection entry present but did not exactly match; exporting from cloud`
137586
+ );
137587
+ }
137588
+ const col = await postman.getCollection(collectionId);
137589
+ await convertAndSplitAnyCollection(
137590
+ col,
137591
+ expectedPath
137060
137592
  );
137593
+ return `../${expectedPath}`;
137061
137594
  }
137062
137595
  function hasControlCharacter2(value) {
137063
137596
  for (let index = 0; index < value.length; index += 1) {
@@ -137075,21 +137608,21 @@ function assertPathWithinCwd(targetPath, fieldName) {
137075
137608
  if (!rawPath || hasControlCharacter2(originalPath) || path3.isAbsolute(rawPath) || path3.win32.isAbsolute(rawPath) || segments.includes("..") || rawPath.startsWith(":") || hasControlCharacter2(rawPath)) {
137076
137609
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
137077
137610
  }
137078
- const base = (0, import_node_fs4.realpathSync)(process.cwd());
137611
+ const base = (0, import_node_fs5.realpathSync)(process.cwd());
137079
137612
  const resolved = path3.resolve(base, rawPath);
137080
137613
  const relative3 = path3.relative(base, resolved);
137081
137614
  if (relative3.startsWith("..") || path3.isAbsolute(relative3)) {
137082
137615
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
137083
137616
  }
137084
137617
  let existingPath = resolved;
137085
- while (!(0, import_node_fs4.existsSync)(existingPath)) {
137618
+ while (!(0, import_node_fs5.existsSync)(existingPath)) {
137086
137619
  const parent = path3.dirname(existingPath);
137087
137620
  if (parent === existingPath) {
137088
137621
  break;
137089
137622
  }
137090
137623
  existingPath = parent;
137091
137624
  }
137092
- const realExistingPath = (0, import_node_fs4.realpathSync)(existingPath);
137625
+ const realExistingPath = (0, import_node_fs5.realpathSync)(existingPath);
137093
137626
  const realRelative = path3.relative(base, realExistingPath);
137094
137627
  if (realRelative.startsWith("..") || path3.isAbsolute(realRelative)) {
137095
137628
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
@@ -137117,8 +137650,8 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
137117
137650
  ensureDir(specsDir);
137118
137651
  ensureDir(".postman");
137119
137652
  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");
137653
+ if (!(0, import_node_fs5.existsSync)(globalsFilePath)) {
137654
+ (0, import_node_fs5.writeFileSync)(globalsFilePath, "name: Globals\nvalues: []\n");
137122
137655
  }
137123
137656
  if (inputs.generateCiWorkflow) {
137124
137657
  const ciDir = inputs.ciWorkflowPath.split("/").slice(0, -1).join("/");
@@ -137134,23 +137667,42 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
137134
137667
  inputs.specSyncMode,
137135
137668
  options.releaseLabel
137136
137669
  ) : 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;
137670
+ const prebuiltByRole = options.preparedPrebuiltCollections;
137671
+ const baselineRef = await exportCollectionArtifact({
137672
+ role: "baseline",
137673
+ collectionId: inputs.baselineCollectionId,
137674
+ dirName: getCollectionDirectoryName("Baseline", assetProjectName),
137675
+ collectionsDir,
137676
+ prebuiltByRole,
137677
+ postman: dependencies.postman,
137678
+ core: dependencies.core
137679
+ });
137680
+ if (baselineRef) {
137681
+ manifestCollections[baselineRef] = inputs.baselineCollectionId;
137682
+ }
137683
+ const smokeRef = await exportCollectionArtifact({
137684
+ role: "smoke",
137685
+ collectionId: inputs.smokeCollectionId,
137686
+ dirName: getCollectionDirectoryName("Smoke", assetProjectName),
137687
+ collectionsDir,
137688
+ prebuiltByRole,
137689
+ postman: dependencies.postman,
137690
+ core: dependencies.core
137691
+ });
137692
+ if (smokeRef) {
137693
+ manifestCollections[smokeRef] = inputs.smokeCollectionId;
137694
+ }
137695
+ const contractRef = await exportCollectionArtifact({
137696
+ role: "contract",
137697
+ collectionId: inputs.contractCollectionId,
137698
+ dirName: getCollectionDirectoryName("Contract", assetProjectName),
137699
+ collectionsDir,
137700
+ prebuiltByRole,
137701
+ postman: dependencies.postman,
137702
+ core: dependencies.core
137703
+ });
137704
+ if (contractRef) {
137705
+ manifestCollections[contractRef] = inputs.contractCollectionId;
137154
137706
  }
137155
137707
  for (const [envName, envUid] of Object.entries(envUids)) {
137156
137708
  writeJsonFile(
@@ -137165,7 +137717,7 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
137165
137717
  workspaceLinkEnabled: inputs.workspaceLinkEnabled,
137166
137718
  workspaceLinkStatus: options.workspaceLinkStatus
137167
137719
  });
137168
- (0, import_node_fs4.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
137720
+ (0, import_node_fs5.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
137169
137721
  durableWorkspaceId,
137170
137722
  manifestCollections,
137171
137723
  envUids,
@@ -137177,11 +137729,18 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
137177
137729
  options.priorState
137178
137730
  ));
137179
137731
  if (mappedSpec && Object.keys(manifestCollections).length > 0) {
137180
- (0, import_node_fs4.writeFileSync)(
137732
+ let existingWorkflows;
137733
+ try {
137734
+ existingWorkflows = (0, import_node_fs5.readFileSync)(".postman/workflows.yaml", "utf8");
137735
+ } catch {
137736
+ existingWorkflows = void 0;
137737
+ }
137738
+ (0, import_node_fs5.writeFileSync)(
137181
137739
  ".postman/workflows.yaml",
137182
137740
  buildSpecCollectionWorkflowManifest(
137183
137741
  mappedSpec.configRelativePath,
137184
- Object.keys(manifestCollections)
137742
+ Object.keys(manifestCollections),
137743
+ existingWorkflows
137185
137744
  )
137186
137745
  );
137187
137746
  }
@@ -137226,12 +137785,12 @@ async function commitAndPushGeneratedFiles(inputs, dependencies) {
137226
137785
  const dir = parts.slice(0, -1).join("/");
137227
137786
  ensureDir(dir);
137228
137787
  }
137229
- (0, import_node_fs4.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
137788
+ (0, import_node_fs5.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
137230
137789
  if (inputs.provider === "github" || inputs.provider === "unknown") {
137231
137790
  const gcPath = ".github/workflows/postman-preview-gc.yml";
137232
137791
  if (inputs.ciWorkflowPath.endsWith(".github/workflows/ci.yml") || inputs.ciWorkflowPath === ".github/workflows/ci.yml") {
137233
137792
  ensureDir(".github/workflows");
137234
- (0, import_node_fs4.writeFileSync)(gcPath, renderGcWorkflowTemplate());
137793
+ (0, import_node_fs5.writeFileSync)(gcPath, renderGcWorkflowTemplate());
137235
137794
  }
137236
137795
  }
137237
137796
  }
@@ -137239,16 +137798,16 @@ async function commitAndPushGeneratedFiles(inputs, dependencies) {
137239
137798
  return { commitSha: "", resolvedCurrentRef: "", pushed: false };
137240
137799
  }
137241
137800
  const provisionPath = ".github/workflows/provision.yml";
137242
- const provisionExists = inputs.provider === "github" && (0, import_node_fs4.existsSync)(provisionPath);
137801
+ const provisionExists = inputs.provider === "github" && (0, import_node_fs5.existsSync)(provisionPath);
137243
137802
  const gcWorkflowPath = ".github/workflows/postman-preview-gc.yml";
137244
- const gcExists = inputs.generateCiWorkflow && (0, import_node_fs4.existsSync)(gcWorkflowPath);
137803
+ const gcExists = inputs.generateCiWorkflow && (0, import_node_fs5.existsSync)(gcWorkflowPath);
137245
137804
  const stagePaths = [
137246
137805
  inputs.artifactDir,
137247
137806
  ".postman",
137248
137807
  inputs.generateCiWorkflow ? inputs.ciWorkflowPath : null,
137249
137808
  gcExists ? gcWorkflowPath : null,
137250
137809
  provisionExists ? provisionPath : null
137251
- ].filter((entry) => typeof entry === "string" && ((0, import_node_fs4.existsSync)(entry) || entry === provisionPath));
137810
+ ].filter((entry) => typeof entry === "string" && ((0, import_node_fs5.existsSync)(entry) || entry === provisionPath));
137252
137811
  if (stagePaths.length === 0) {
137253
137812
  dependencies.core.info("No generated repository paths were found; skipping repo mutation.");
137254
137813
  return {
@@ -137356,6 +137915,7 @@ async function runRepoSyncInner(inputs, dependencies) {
137356
137915
  }
137357
137916
  }
137358
137917
  }
137918
+ const preparedPrebuiltCollections = await preparePrebuiltCollections(inputs);
137359
137919
  let skipRepositoryLinkPost = false;
137360
137920
  let repositoryLinkPreflightWasFree = false;
137361
137921
  if (inputs.workspaceLinkEnabled && inputs.workspaceId && inputs.repoUrl && dependencies.internalIntegration?.findWorkspaceForRepo) {
@@ -137392,6 +137952,7 @@ async function runRepoSyncInner(inputs, dependencies) {
137392
137952
  const branchAssetMarker = buildBranchAssetMarker(branchDecision, inputs);
137393
137953
  const envUids = await upsertEnvironments(inputs, dependencies, resourcesState, branchAssetMarker);
137394
137954
  outputs["environment-uids-json"] = JSON.stringify(envUids);
137955
+ dependencies.core.setOutput("environment-uids-json", outputs["environment-uids-json"]);
137395
137956
  if (inputs.environmentSyncEnabled && inputs.workspaceId && dependencies.internalIntegration) {
137396
137957
  const associations = Object.entries(envUids).map(([envName, envUid]) => ({
137397
137958
  envUid,
@@ -137510,6 +138071,7 @@ async function runRepoSyncInner(inputs, dependencies) {
137510
138071
  }
137511
138072
  }
137512
138073
  outputs["mock-url"] = resolvedMockUrl;
138074
+ dependencies.core.setOutput("mock-url", resolvedMockUrl);
137513
138075
  }
137514
138076
  }
137515
138077
  if (inputs.workspaceId && inputs.smokeCollectionId && Object.keys(envUids).length > 0) {
@@ -137585,6 +138147,7 @@ async function runRepoSyncInner(inputs, dependencies) {
137585
138147
  }
137586
138148
  }
137587
138149
  outputs["monitor-id"] = resolvedMonitorId;
138150
+ dependencies.core.setOutput("monitor-id", resolvedMonitorId);
137588
138151
  if (!effectiveCron && resolvedMonitorId) {
137589
138152
  try {
137590
138153
  await dependencies.postman.runMonitor(resolvedMonitorId);
@@ -137642,7 +138205,8 @@ async function runRepoSyncInner(inputs, dependencies) {
137642
138205
  priorWorkspaceId: resourcesState?.workspace?.id,
137643
138206
  existingSpecs: resourcesState?.cloudResources?.specs,
137644
138207
  releaseLabel,
137645
- priorState: resourcesState
138208
+ priorState: resourcesState,
138209
+ preparedPrebuiltCollections
137646
138210
  });
137647
138211
  const commit = await commitAndPushGeneratedFiles(inputs, dependencies);
137648
138212
  outputs["commit-sha"] = commit.commitSha;
@@ -137868,7 +138432,12 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
137868
138432
  bifrostBaseUrl: inputs.postmanBifrostBase,
137869
138433
  // No fallback on the org-mode probe: the expected non-org 400 must
137870
138434
  // surface verbatim, not be re-fired against the /_api alias.
137871
- secretMasker: masker
138435
+ secretMasker: masker,
138436
+ onRetryEvent: (event) => {
138437
+ actionCore.info(
138438
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
138439
+ );
138440
+ }
137872
138441
  });
137873
138442
  const squads = await gateway.getSquads(teamId);
137874
138443
  if (squads.length > 0) {
@@ -137932,7 +138501,12 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
137932
138501
  fallbackBaseUrl: inputs.postmanFallbackBase,
137933
138502
  teamId: resolved.teamId,
137934
138503
  orgMode: inputs.orgMode,
137935
- secretMasker: masker
138504
+ secretMasker: masker,
138505
+ onRetryEvent: (event) => {
138506
+ factories.core.info(
138507
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
138508
+ );
138509
+ }
137936
138510
  });
137937
138511
  const gatewayAssets = new PostmanGatewayAssetsClient({
137938
138512
  gateway,
@@ -138415,6 +138989,7 @@ var CLI_INPUT_NAMES = [
138415
138989
  "baseline-collection-id",
138416
138990
  "smoke-collection-id",
138417
138991
  "contract-collection-id",
138992
+ "prebuilt-collections-json",
138418
138993
  "collection-sync-mode",
138419
138994
  "spec-sync-mode",
138420
138995
  "release-label",
@@ -138486,17 +139061,38 @@ Examples:
138486
139061
  postman-repo-sync --repo-write-mode none --workspace-id <id> ...
138487
139062
  `;
138488
139063
  var ConsoleReporter = class {
139064
+ constructor(onOutput) {
139065
+ this.onOutput = onOutput;
139066
+ }
139067
+ onOutput;
138489
139068
  info(message) {
138490
139069
  console.error(message);
138491
139070
  }
138492
139071
  warning(message) {
138493
139072
  console.error(`warning: ${message}`);
138494
139073
  }
138495
- setOutput() {
139074
+ retryEvent(event) {
139075
+ this.info(
139076
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
139077
+ );
139078
+ }
139079
+ setOutput(name, value) {
139080
+ this.onOutput?.(name, value);
138496
139081
  }
138497
139082
  setSecret() {
138498
139083
  }
138499
139084
  };
139085
+ async function timedStage(reporter, stage, operation) {
139086
+ const started = performance.now();
139087
+ try {
139088
+ const result = await operation();
139089
+ reporter.info(`[repo-sync timing] ${JSON.stringify({ stage, ms: Math.max(0, performance.now() - started), status: "success" })}`);
139090
+ return result;
139091
+ } catch (error) {
139092
+ reporter.info(`[repo-sync timing] ${JSON.stringify({ stage, ms: Math.max(0, performance.now() - started), status: "error" })}`);
139093
+ throw error;
139094
+ }
139095
+ }
138500
139096
  function normalizeCliFlag(name) {
138501
139097
  return `INPUT_${name.replace(/-/g, "_").toUpperCase()}`;
138502
139098
  }
@@ -138570,7 +139166,7 @@ function resolvePackageVersion() {
138570
139166
  candidates.push(import_node_path3.default.join(process.cwd(), "package.json"));
138571
139167
  for (const packageJsonPath of candidates) {
138572
139168
  try {
138573
- const packageJson = JSON.parse((0, import_node_fs5.readFileSync)(packageJsonPath, "utf8"));
139169
+ const packageJson = JSON.parse((0, import_node_fs6.readFileSync)(packageJsonPath, "utf8"));
138574
139170
  if (packageJson.name === "@postman-cse/onboarding-repo-sync" && packageJson.version) {
138575
139171
  return String(packageJson.version).trim();
138576
139172
  }
@@ -138681,20 +139277,107 @@ function toDotenv(outputs) {
138681
139277
  String(value ?? "")
138682
139278
  ]).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
138683
139279
  }
138684
- async function writeOptionalFile(filePath, content) {
138685
- if (!filePath) {
138686
- return;
138687
- }
138688
- const workspaceRoot = import_node_path3.default.resolve(process.cwd());
139280
+ function assertOutputFileAllowed(filePath) {
139281
+ const workspaceRoot = (0, import_node_fs6.realpathSync)(import_node_path3.default.resolve(process.cwd()));
138689
139282
  const resolved = import_node_path3.default.resolve(workspaceRoot, filePath);
138690
139283
  const relative3 = import_node_path3.default.relative(workspaceRoot, resolved);
138691
139284
  if (relative3.startsWith("..") || import_node_path3.default.isAbsolute(relative3)) {
138692
139285
  throw new Error(`Output path must stay within workspace: ${filePath}`);
138693
139286
  }
138694
- await (0, import_promises.mkdir)(import_node_path3.default.dirname(resolved), { recursive: true });
139287
+ let existingParent = import_node_path3.default.dirname(resolved);
139288
+ while (!(0, import_node_fs6.existsSync)(existingParent)) {
139289
+ const parent = import_node_path3.default.dirname(existingParent);
139290
+ if (parent === existingParent) break;
139291
+ existingParent = parent;
139292
+ }
139293
+ const parentReal = (0, import_node_fs6.realpathSync)(existingParent);
139294
+ const parentRelative = import_node_path3.default.relative(workspaceRoot, parentReal);
139295
+ if (parentRelative.startsWith("..") || import_node_path3.default.isAbsolute(parentRelative)) {
139296
+ throw new Error(`Output path must stay within workspace: ${filePath}`);
139297
+ }
139298
+ if ((0, import_node_fs6.existsSync)(resolved) || (() => {
139299
+ try {
139300
+ (0, import_node_fs6.lstatSync)(resolved);
139301
+ return true;
139302
+ } catch {
139303
+ return false;
139304
+ }
139305
+ })()) {
139306
+ const targetReal = (0, import_node_fs6.realpathSync)(resolved);
139307
+ const targetRelative = import_node_path3.default.relative(workspaceRoot, targetReal);
139308
+ if (targetRelative.startsWith("..") || import_node_path3.default.isAbsolute(targetRelative)) {
139309
+ throw new Error(`Output path must stay within workspace: ${filePath}`);
139310
+ }
139311
+ }
139312
+ return { resolved, directory: import_node_path3.default.dirname(resolved) };
139313
+ }
139314
+ function resolveSymlinkAwarePath(filePath) {
139315
+ const workspaceRoot = (0, import_node_fs6.realpathSync)(import_node_path3.default.resolve(process.cwd()));
139316
+ const resolved = import_node_path3.default.resolve(workspaceRoot, filePath);
139317
+ let existing = resolved;
139318
+ while (!(0, import_node_fs6.existsSync)(existing)) {
139319
+ const parent = import_node_path3.default.dirname(existing);
139320
+ if (parent === existing) break;
139321
+ existing = parent;
139322
+ }
139323
+ return import_node_path3.default.resolve((0, import_node_fs6.realpathSync)(existing), import_node_path3.default.relative(existing, resolved));
139324
+ }
139325
+ function assertResultPathOutsideGeneratedPaths(resultJsonPath, inputs) {
139326
+ const result = assertOutputFileAllowed(resultJsonPath).resolved;
139327
+ const resultReal = resolveSymlinkAwarePath(result);
139328
+ for (const root of [inputs.artifactDir, ".postman"].map(resolveSymlinkAwarePath)) {
139329
+ const relative3 = import_node_path3.default.relative(root, resultReal);
139330
+ if (!relative3 || !relative3.startsWith("..") && !import_node_path3.default.isAbsolute(relative3)) {
139331
+ throw new Error(
139332
+ `--result-json must not overlap a generated or staged repository path: ${resultJsonPath}`
139333
+ );
139334
+ }
139335
+ }
139336
+ const generatedFiles = [];
139337
+ if (inputs.generateCiWorkflow) {
139338
+ generatedFiles.push(inputs.ciWorkflowPath);
139339
+ if ((inputs.provider === "github" || inputs.provider === "unknown") && (inputs.ciWorkflowPath.endsWith(".github/workflows/ci.yml") || inputs.ciWorkflowPath === ".github/workflows/ci.yml")) {
139340
+ generatedFiles.push(".github/workflows/postman-preview-gc.yml");
139341
+ }
139342
+ }
139343
+ if (inputs.provider === "github" && (0, import_node_fs6.existsSync)(".github/workflows/provision.yml")) {
139344
+ generatedFiles.push(".github/workflows/provision.yml");
139345
+ }
139346
+ for (const generatedFile of generatedFiles) {
139347
+ if (resultReal === resolveSymlinkAwarePath(generatedFile)) {
139348
+ throw new Error(
139349
+ `--result-json must not overlap a generated or staged repository path: ${resultJsonPath}`
139350
+ );
139351
+ }
139352
+ }
139353
+ }
139354
+ async function writeOptionalFile(filePath, content) {
139355
+ if (!filePath) {
139356
+ return;
139357
+ }
139358
+ const { resolved, directory } = assertOutputFileAllowed(filePath);
139359
+ await (0, import_promises.mkdir)(directory, { recursive: true });
139360
+ const validated = assertOutputFileAllowed(filePath);
139361
+ if (validated.resolved !== resolved) throw new Error(`Output path changed during validation: ${filePath}`);
138695
139362
  await (0, import_promises.writeFile)(resolved, content, "utf8");
138696
139363
  }
138697
- function createCliDependencies(inputs, resolved) {
139364
+ function writeOptionalFileAtomic(filePath, content) {
139365
+ if (!filePath) return;
139366
+ const { resolved, directory } = assertOutputFileAllowed(filePath);
139367
+ (0, import_node_fs6.mkdirSync)(directory, { recursive: true });
139368
+ const validated = assertOutputFileAllowed(filePath);
139369
+ const temporaryDirectory = (0, import_node_fs6.mkdtempSync)(
139370
+ import_node_path3.default.join((0, import_node_fs6.realpathSync)(validated.directory), `.${import_node_path3.default.basename(resolved)}.`)
139371
+ );
139372
+ const temporary = import_node_path3.default.join(temporaryDirectory, import_node_path3.default.basename(resolved));
139373
+ try {
139374
+ (0, import_node_fs6.writeFileSync)(temporary, content, "utf8");
139375
+ (0, import_node_fs6.renameSync)(temporary, resolved);
139376
+ } finally {
139377
+ (0, import_node_fs6.rmSync)(temporaryDirectory, { recursive: true, force: true });
139378
+ }
139379
+ }
139380
+ function createCliDependencies(inputs, resolved, reporter = new ConsoleReporter()) {
138698
139381
  const secretMasker = createSecretMasker([
138699
139382
  resolved.apiKey,
138700
139383
  inputs.postmanAccessToken,
@@ -138711,7 +139394,7 @@ function createCliDependencies(inputs, resolved) {
138711
139394
  inputs,
138712
139395
  resolved,
138713
139396
  {
138714
- core: new ConsoleReporter(),
139397
+ core: reporter,
138715
139398
  exec: cliExec
138716
139399
  },
138717
139400
  {
@@ -138741,17 +139424,24 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
138741
139424
  }
138742
139425
  const config = parseCliArgs(argv, env);
138743
139426
  const inputs = resolveInputs(config.inputEnv);
138744
- const reporter = new ConsoleReporter();
138745
139427
  const branchDecision = decideBranchTier(inputs, config.inputEnv);
139428
+ if (branchDecision.tier !== "gated") {
139429
+ assertResultPathOutsideGeneratedPaths(config.resultJsonPath, inputs);
139430
+ }
139431
+ const partialOutputs = {};
139432
+ const reporter = new ConsoleReporter((name, value) => {
139433
+ partialOutputs[name] = String(value ?? "");
139434
+ writeOptionalFileAtomic(config.resultJsonPath, JSON.stringify(partialOutputs, null, 2));
139435
+ });
138746
139436
  if (branchDecision.tier === "gated") {
138747
139437
  const result2 = runGatedSkip(inputs, branchDecision, reporter);
138748
- await writeOptionalFile(config.resultJsonPath, JSON.stringify(result2, null, 2));
139438
+ writeOptionalFileAtomic(config.resultJsonPath, JSON.stringify(result2, null, 2));
138749
139439
  await writeOptionalFile(config.dotenvPath, toDotenv(result2));
138750
139440
  writeStdout(`${JSON.stringify(result2, null, 2)}
138751
139441
  `);
138752
139442
  return;
138753
139443
  }
138754
- await mintAccessTokenIfNeeded(inputs, reporter);
139444
+ await timedStage(reporter, "access-token mint", () => mintAccessTokenIfNeeded(inputs, reporter));
138755
139445
  const initialMasker = createSecretMasker([
138756
139446
  inputs.postmanApiKey,
138757
139447
  inputs.postmanAccessToken,
@@ -138763,7 +139453,7 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
138763
139453
  inputs.sslClientPassphrase,
138764
139454
  inputs.sslExtraCaCerts
138765
139455
  ]);
138766
- await runCredentialPreflight({
139456
+ await timedStage(reporter, "credential preflight", () => runCredentialPreflight({
138767
139457
  apiBaseUrl: inputs.postmanApiBase,
138768
139458
  iapubBaseUrl: inputs.postmanIapubBase,
138769
139459
  postmanApiKey: inputs.postmanApiKey,
@@ -138772,9 +139462,9 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
138772
139462
  mode: inputs.credentialPreflight,
138773
139463
  mask: initialMasker,
138774
139464
  log: reporter
138775
- });
139465
+ }));
138776
139466
  const resolvingExec = createCliExec(initialMasker);
138777
- const resolved = await resolvePostmanApiKeyAndTeamId(
139467
+ const resolved = await timedStage(reporter, "API-key/team resolution", () => resolvePostmanApiKeyAndTeamId(
138778
139468
  inputs,
138779
139469
  reporter,
138780
139470
  resolvingExec,
@@ -138783,8 +139473,8 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
138783
139473
  persistGeneratedApiKeySecret: false,
138784
139474
  env
138785
139475
  }
138786
- );
138787
- const dependencies = createCliDependencies(inputs, resolved);
139476
+ ));
139477
+ const dependencies = createCliDependencies(inputs, resolved, reporter);
138788
139478
  if (inputs.environmentSyncEnabled && !dependencies.internalIntegration) {
138789
139479
  dependencies.core.warning(
138790
139480
  "Skipping system environment association because postman-access-token is not configured"
@@ -138795,8 +139485,12 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
138795
139485
  "Skipping workspace linking because postman-access-token is not configured"
138796
139486
  );
138797
139487
  }
138798
- const result = await (runtime.executeRepoSync ?? runRepoSync)(inputs, dependencies);
138799
- await writeOptionalFile(config.resultJsonPath, JSON.stringify(result, null, 2));
139488
+ const result = await timedStage(
139489
+ reporter,
139490
+ "runRepoSync finalize",
139491
+ () => (runtime.executeRepoSync ?? runRepoSync)(inputs, dependencies)
139492
+ );
139493
+ writeOptionalFileAtomic(config.resultJsonPath, JSON.stringify(result, null, 2));
138800
139494
  await writeOptionalFile(config.dotenvPath, toDotenv(result));
138801
139495
  writeStdout(`${JSON.stringify(result, null, 2)}
138802
139496
  `);
@@ -138882,7 +139576,7 @@ function isEntrypoint(currentPath, entrypointPath) {
138882
139576
  return false;
138883
139577
  }
138884
139578
  try {
138885
- return (0, import_node_fs5.realpathSync)(currentPath) === (0, import_node_fs5.realpathSync)(entrypointPath);
139579
+ return (0, import_node_fs6.realpathSync)(currentPath) === (0, import_node_fs6.realpathSync)(entrypointPath);
138886
139580
  } catch {
138887
139581
  return import_node_path3.default.resolve(currentPath) === import_node_path3.default.resolve(entrypointPath);
138888
139582
  }
@@ -138903,7 +139597,8 @@ if (isEntrypoint(currentModulePath, entrypoint)) {
138903
139597
  normalizeCliFlag,
138904
139598
  parseCliArgs,
138905
139599
  runCli,
138906
- toDotenv
139600
+ toDotenv,
139601
+ writeOptionalFileAtomic
138907
139602
  });
138908
139603
  /*! Bundled license information:
138909
139604