@postman-cse/onboarding-repo-sync 2.1.6 → 2.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/action.cjs CHANGED
@@ -125114,12 +125114,14 @@ var POSTMAN_ENDPOINT_PROFILES = {
125114
125114
  prod: {
125115
125115
  apiBaseUrl: "https://api.getpostman.com",
125116
125116
  bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
125117
+ fallbackBaseUrl: "https://go.postman.co/_api",
125117
125118
  cliInstallUrl: "https://dl-cli.pstmn.io/install/unix.sh",
125118
125119
  iapubBaseUrl: "https://iapub.postman.co"
125119
125120
  },
125120
125121
  beta: {
125121
125122
  apiBaseUrl: "https://api.getpostman-beta.com",
125122
125123
  bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
125124
+ fallbackBaseUrl: "https://go.postman-beta.co/_api",
125123
125125
  cliInstallUrl: "https://dl-cli.pstmn-beta.io/install/unix.sh",
125124
125126
  iapubBaseUrl: "https://iapub.postman.co"
125125
125127
  }
@@ -127587,6 +127589,14 @@ var PostmanGatewayAssetsClient = class {
127587
127589
  createFlights.set(key, { fingerprint, promise: pending });
127588
127590
  return pending;
127589
127591
  }
127592
+ /**
127593
+ * Reconcile an ambiguous create. Returns the adopted match, `undefined`
127594
+ * when every discovery read succeeded and found nothing (the create is
127595
+ * conclusively absent, so a fallback resend cannot duplicate), or `null`
127596
+ * when the outcome is inconclusive (non-ambiguous error, or a discovery
127597
+ * read itself failed — in that case a resend could duplicate a committed
127598
+ * twin and must not fire).
127599
+ */
127590
127600
  async discoverAfterAmbiguousCreate(discover, error2) {
127591
127601
  if (!this.isAmbiguousCreateOutcome(error2)) {
127592
127602
  return null;
@@ -127595,12 +127605,31 @@ var PostmanGatewayAssetsClient = class {
127595
127605
  if (attempt > 0) {
127596
127606
  await this.sleep(this.reconcileDelayMs);
127597
127607
  }
127598
- const found = await discover();
127608
+ let found;
127609
+ try {
127610
+ found = await discover();
127611
+ } catch {
127612
+ return null;
127613
+ }
127599
127614
  if (found) {
127600
127615
  return found;
127601
127616
  }
127602
127617
  }
127603
- return null;
127618
+ return void 0;
127619
+ }
127620
+ /**
127621
+ * After an ambiguous create failed AND discovery proved the asset absent,
127622
+ * re-attempt the create once with the cold `/_api` fallback enabled: the
127623
+ * reconcile above guarantees no committed twin, so the resend cannot
127624
+ * duplicate. Returns null when the resend also fails (caller rethrows the
127625
+ * original error).
127626
+ */
127627
+ async resendAbsentCreate(operation) {
127628
+ try {
127629
+ return await operation();
127630
+ } catch {
127631
+ return null;
127632
+ }
127604
127633
  }
127605
127634
  publicEnvironmentUid(data, bareId) {
127606
127635
  const owner = String(data?.owner ?? "").trim();
@@ -127648,17 +127677,18 @@ var PostmanGatewayAssetsClient = class {
127648
127677
  name: envName,
127649
127678
  values: normalizedValues
127650
127679
  };
127680
+ const send2 = (fallback) => this.gateway.requestJson(
127681
+ {
127682
+ service: "sync",
127683
+ method: "post",
127684
+ path: `/environment/import?workspace=${ws}`,
127685
+ body,
127686
+ ...fallback ? { fallback } : {}
127687
+ },
127688
+ { retryTransient: false }
127689
+ );
127651
127690
  try {
127652
- const response = await this.gateway.requestJson(
127653
- {
127654
- service: "sync",
127655
- method: "post",
127656
- path: `/environment/import?workspace=${ws}`,
127657
- body
127658
- },
127659
- { retryTransient: false }
127660
- );
127661
- const data = this.dataOf(response);
127691
+ const data = this.dataOf(await send2());
127662
127692
  const bareId = this.idOf(data);
127663
127693
  if (!bareId) {
127664
127694
  throw new Error("Environment import did not return a UID");
@@ -127673,6 +127703,15 @@ var PostmanGatewayAssetsClient = class {
127673
127703
  await this.updateEnvironment(adopted, envName, values);
127674
127704
  return adopted;
127675
127705
  }
127706
+ if (adopted === void 0) {
127707
+ const retried = await this.resendAbsentCreate(async () => {
127708
+ const data = this.dataOf(await send2("auto"));
127709
+ const bareId = this.idOf(data);
127710
+ if (!bareId) throw new Error("Environment import did not return a UID");
127711
+ return this.publicEnvironmentUid(data, bareId);
127712
+ });
127713
+ if (retried) return retried;
127714
+ }
127676
127715
  throw error2;
127677
127716
  }
127678
127717
  });
@@ -127804,19 +127843,20 @@ var PostmanGatewayAssetsClient = class {
127804
127843
  private: false,
127805
127844
  ...environment ? { environment } : {}
127806
127845
  };
127807
- try {
127808
- const response = await this.gateway.requestJson(
127809
- {
127810
- service: "mock",
127811
- method: "post",
127812
- path: `/mocks?workspace=${ws}`,
127813
- body
127814
- },
127815
- // Unsafe create: never blind re-POST. An ESOCKETTIMEDOUT after accept
127816
- // may still have created the mock; reconcile via discovery below and
127817
- // let the orchestrator retry the whole create-or-adopt cycle.
127818
- { retryTransient: false }
127819
- );
127846
+ const send2 = (fallback) => this.gateway.requestJson(
127847
+ {
127848
+ service: "mock",
127849
+ method: "post",
127850
+ path: `/mocks?workspace=${ws}`,
127851
+ body,
127852
+ ...fallback ? { fallback } : {}
127853
+ },
127854
+ // Unsafe create: never blind re-POST. An ESOCKETTIMEDOUT after accept
127855
+ // may still have created the mock; reconcile via discovery below and
127856
+ // let the orchestrator retry the whole create-or-adopt cycle.
127857
+ { retryTransient: false }
127858
+ );
127859
+ const parseMock = (response) => {
127820
127860
  const record = this.dataOf(response);
127821
127861
  const uid = this.idOf(record);
127822
127862
  if (!uid) {
@@ -127826,6 +127866,9 @@ var PostmanGatewayAssetsClient = class {
127826
127866
  uid,
127827
127867
  url: String(record?.url ?? record?.mockUrl ?? "").trim()
127828
127868
  };
127869
+ };
127870
+ try {
127871
+ return parseMock(await send2());
127829
127872
  } catch (error2) {
127830
127873
  const adopted = await this.discoverAfterAmbiguousCreate(
127831
127874
  () => this.findMockByCollection(collection, environment, mockName),
@@ -127834,6 +127877,10 @@ var PostmanGatewayAssetsClient = class {
127834
127877
  if (adopted) {
127835
127878
  return { uid: adopted.uid, url: adopted.mockUrl };
127836
127879
  }
127880
+ if (adopted === void 0) {
127881
+ const retried = await this.resendAbsentCreate(async () => parseMock(await send2("auto")));
127882
+ if (retried) return retried;
127883
+ }
127837
127884
  throw error2;
127838
127885
  }
127839
127886
  });
@@ -127940,21 +127987,25 @@ var PostmanGatewayAssetsClient = class {
127940
127987
  distribution: null,
127941
127988
  ...environment ? { environment } : {}
127942
127989
  };
127943
- try {
127944
- const response = await this.gateway.requestJson(
127945
- {
127946
- service: "monitors",
127947
- method: "post",
127948
- path: `/jobTemplates?workspace=${ws}`,
127949
- body
127950
- },
127951
- { retryTransient: false }
127952
- );
127990
+ const send2 = (fallback) => this.gateway.requestJson(
127991
+ {
127992
+ service: "monitors",
127993
+ method: "post",
127994
+ path: `/jobTemplates?workspace=${ws}`,
127995
+ body,
127996
+ ...fallback ? { fallback } : {}
127997
+ },
127998
+ { retryTransient: false }
127999
+ );
128000
+ const parseMonitor = (response) => {
127953
128001
  const uid = this.idOf(this.dataOf(response));
127954
128002
  if (!uid) {
127955
128003
  throw new Error("Monitor create did not return a UID");
127956
128004
  }
127957
128005
  return uid;
128006
+ };
128007
+ try {
128008
+ return parseMonitor(await send2());
127958
128009
  } catch (error2) {
127959
128010
  const adopted = await this.discoverAfterAmbiguousCreate(
127960
128011
  () => this.findMonitorByCollection(collection, environment, monitorName),
@@ -127963,6 +128014,10 @@ var PostmanGatewayAssetsClient = class {
127963
128014
  if (adopted?.uid) {
127964
128015
  return adopted.uid;
127965
128016
  }
128017
+ if (adopted === void 0) {
128018
+ const retried = await this.resendAbsentCreate(async () => parseMonitor(await send2("auto")));
128019
+ if (retried) return retried;
128020
+ }
127966
128021
  throw error2;
127967
128022
  }
127968
128023
  });
@@ -128191,6 +128246,7 @@ var AccessTokenGatewayClient = class {
128191
128246
  fetchImpl;
128192
128247
  secretMasker;
128193
128248
  maxRetries;
128249
+ fallbackBaseUrl;
128194
128250
  retryBaseDelayMs;
128195
128251
  sleepImpl;
128196
128252
  constructor(options) {
@@ -128203,6 +128259,8 @@ var AccessTokenGatewayClient = class {
128203
128259
  this.fetchImpl = options.fetchImpl ?? fetch;
128204
128260
  this.secretMasker = options.secretMasker ?? createSecretMasker([this.tokenProvider.current()]);
128205
128261
  this.maxRetries = options.maxRetries ?? 3;
128262
+ const fallbackEnv = typeof process !== "undefined" ? process.env?.POSTMAN_ITEM_CREATE_FALLBACK : void 0;
128263
+ this.fallbackBaseUrl = fallbackEnv === "off" ? void 0 : options.fallbackBaseUrl?.replace(/\/+$/, "");
128206
128264
  this.retryBaseDelayMs = options.retryBaseDelayMs ?? 400;
128207
128265
  this.sleepImpl = options.sleepImpl ?? defaultSleep;
128208
128266
  }
@@ -128221,8 +128279,8 @@ var AccessTokenGatewayClient = class {
128221
128279
  }
128222
128280
  return headers;
128223
128281
  }
128224
- async send(request) {
128225
- const url = `${this.bifrostBaseUrl}/ws/proxy`;
128282
+ async send(request, baseUrl) {
128283
+ const url = `${baseUrl ?? this.bifrostBaseUrl}/ws/proxy`;
128226
128284
  return this.fetchImpl(url, {
128227
128285
  method: "POST",
128228
128286
  headers: this.buildHeaders(request.headers),
@@ -128235,6 +128293,43 @@ var AccessTokenGatewayClient = class {
128235
128293
  })
128236
128294
  });
128237
128295
  }
128296
+ /**
128297
+ * One cold, serial attempt against the fallback base URL after the primary
128298
+ * budget is exhausted on a transient failure. Never hedged in parallel with
128299
+ * the primary; only fires when the request would otherwise throw. Callers
128300
+ * with `retryTransient: false` still reconcile first — the fallback attempt
128301
+ * here is the resend, so it is only used for requests whose mutation is
128302
+ * known idempotent or already reconciled by the caller's adopt-on-ambiguous
128303
+ * loop.
128304
+ */
128305
+ async tryFallback(request) {
128306
+ if (!this.fallbackBaseUrl) return null;
128307
+ try {
128308
+ return await this.send(request, this.fallbackBaseUrl);
128309
+ } catch {
128310
+ return null;
128311
+ }
128312
+ }
128313
+ /**
128314
+ * Run the fallback attempt and classify its response the same way the
128315
+ * primary path would. Returns the success response, or null when the
128316
+ * fallback also failed transiently (caller then throws the original error).
128317
+ * Non-transient fallback failures (4xx) surface as their own HttpError since
128318
+ * they are the freshest authoritative answer.
128319
+ */
128320
+ fallbackEligible(request, retryTransient) {
128321
+ if (!this.fallbackBaseUrl) return false;
128322
+ return retryTransient || request.fallback === "auto";
128323
+ }
128324
+ async attemptFallback(request, retryTransient) {
128325
+ if (!this.fallbackEligible(request, retryTransient)) return null;
128326
+ const response = await this.tryFallback(request);
128327
+ if (!response) return null;
128328
+ if (response.ok) return response;
128329
+ const body = await response.text().catch(() => "");
128330
+ if (isRetryableSafeReadResponse(response.status)) return null;
128331
+ throw this.toHttpError(request, response, body);
128332
+ }
128238
128333
  /**
128239
128334
  * Send a gateway request, refreshing the token once on an auth failure and
128240
128335
  * optionally retrying transient downstream failures (5xx / Bifrost read
@@ -128257,6 +128352,8 @@ var AccessTokenGatewayClient = class {
128257
128352
  await this.sleepImpl(delay);
128258
128353
  continue;
128259
128354
  }
128355
+ const fallbackResponse2 = await this.attemptFallback(request, retryTransient);
128356
+ if (fallbackResponse2) return fallbackResponse2;
128260
128357
  throw error2;
128261
128358
  }
128262
128359
  if (response.ok) {
@@ -128278,6 +128375,8 @@ var AccessTokenGatewayClient = class {
128278
128375
  await this.sleepImpl(delay);
128279
128376
  continue;
128280
128377
  }
128378
+ const fallbackResponse = await this.attemptFallback(request, retryTransient);
128379
+ if (fallbackResponse) return fallbackResponse;
128281
128380
  throw this.toHttpError(request, response, body);
128282
128381
  }
128283
128382
  }
@@ -128904,6 +129003,7 @@ function resolveInputs(env = process.env) {
128904
129003
  postmanStack,
128905
129004
  postmanApiBase: endpointProfile.apiBaseUrl,
128906
129005
  postmanBifrostBase: endpointProfile.bifrostBaseUrl,
129006
+ postmanFallbackBase: endpointProfile.fallbackBaseUrl,
128907
129007
  postmanCliInstallUrl: endpointProfile.cliInstallUrl,
128908
129008
  postmanIapubBase: endpointProfile.iapubBaseUrl
128909
129009
  };
@@ -130045,6 +130145,8 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130045
130145
  const gateway = new AccessTokenGatewayClient({
130046
130146
  tokenProvider,
130047
130147
  bifrostBaseUrl: inputs.postmanBifrostBase,
130148
+ // No fallback on the org-mode probe: the expected non-org 400 must
130149
+ // surface verbatim, not be re-fired against the /_api alias.
130048
130150
  secretMasker: masker
130049
130151
  });
130050
130152
  const squads = await gateway.getSquads(teamId);
@@ -130100,6 +130202,7 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
130100
130202
  const gateway = new AccessTokenGatewayClient({
130101
130203
  tokenProvider,
130102
130204
  bifrostBaseUrl: inputs.postmanBifrostBase,
130205
+ fallbackBaseUrl: inputs.postmanFallbackBase,
130103
130206
  teamId: resolved.teamId,
130104
130207
  orgMode: inputs.orgMode,
130105
130208
  secretMasker: masker
package/dist/cli.cjs CHANGED
@@ -123217,12 +123217,14 @@ var POSTMAN_ENDPOINT_PROFILES = {
123217
123217
  prod: {
123218
123218
  apiBaseUrl: "https://api.getpostman.com",
123219
123219
  bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
123220
+ fallbackBaseUrl: "https://go.postman.co/_api",
123220
123221
  cliInstallUrl: "https://dl-cli.pstmn.io/install/unix.sh",
123221
123222
  iapubBaseUrl: "https://iapub.postman.co"
123222
123223
  },
123223
123224
  beta: {
123224
123225
  apiBaseUrl: "https://api.getpostman-beta.com",
123225
123226
  bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
123227
+ fallbackBaseUrl: "https://go.postman-beta.co/_api",
123226
123228
  cliInstallUrl: "https://dl-cli.pstmn-beta.io/install/unix.sh",
123227
123229
  iapubBaseUrl: "https://iapub.postman.co"
123228
123230
  }
@@ -125690,6 +125692,14 @@ var PostmanGatewayAssetsClient = class {
125690
125692
  createFlights.set(key, { fingerprint, promise: pending });
125691
125693
  return pending;
125692
125694
  }
125695
+ /**
125696
+ * Reconcile an ambiguous create. Returns the adopted match, `undefined`
125697
+ * when every discovery read succeeded and found nothing (the create is
125698
+ * conclusively absent, so a fallback resend cannot duplicate), or `null`
125699
+ * when the outcome is inconclusive (non-ambiguous error, or a discovery
125700
+ * read itself failed — in that case a resend could duplicate a committed
125701
+ * twin and must not fire).
125702
+ */
125693
125703
  async discoverAfterAmbiguousCreate(discover, error) {
125694
125704
  if (!this.isAmbiguousCreateOutcome(error)) {
125695
125705
  return null;
@@ -125698,12 +125708,31 @@ var PostmanGatewayAssetsClient = class {
125698
125708
  if (attempt > 0) {
125699
125709
  await this.sleep(this.reconcileDelayMs);
125700
125710
  }
125701
- const found = await discover();
125711
+ let found;
125712
+ try {
125713
+ found = await discover();
125714
+ } catch {
125715
+ return null;
125716
+ }
125702
125717
  if (found) {
125703
125718
  return found;
125704
125719
  }
125705
125720
  }
125706
- return null;
125721
+ return void 0;
125722
+ }
125723
+ /**
125724
+ * After an ambiguous create failed AND discovery proved the asset absent,
125725
+ * re-attempt the create once with the cold `/_api` fallback enabled: the
125726
+ * reconcile above guarantees no committed twin, so the resend cannot
125727
+ * duplicate. Returns null when the resend also fails (caller rethrows the
125728
+ * original error).
125729
+ */
125730
+ async resendAbsentCreate(operation) {
125731
+ try {
125732
+ return await operation();
125733
+ } catch {
125734
+ return null;
125735
+ }
125707
125736
  }
125708
125737
  publicEnvironmentUid(data, bareId) {
125709
125738
  const owner = String(data?.owner ?? "").trim();
@@ -125751,17 +125780,18 @@ var PostmanGatewayAssetsClient = class {
125751
125780
  name: envName,
125752
125781
  values: normalizedValues
125753
125782
  };
125783
+ const send2 = (fallback) => this.gateway.requestJson(
125784
+ {
125785
+ service: "sync",
125786
+ method: "post",
125787
+ path: `/environment/import?workspace=${ws}`,
125788
+ body,
125789
+ ...fallback ? { fallback } : {}
125790
+ },
125791
+ { retryTransient: false }
125792
+ );
125754
125793
  try {
125755
- const response = await this.gateway.requestJson(
125756
- {
125757
- service: "sync",
125758
- method: "post",
125759
- path: `/environment/import?workspace=${ws}`,
125760
- body
125761
- },
125762
- { retryTransient: false }
125763
- );
125764
- const data = this.dataOf(response);
125794
+ const data = this.dataOf(await send2());
125765
125795
  const bareId = this.idOf(data);
125766
125796
  if (!bareId) {
125767
125797
  throw new Error("Environment import did not return a UID");
@@ -125776,6 +125806,15 @@ var PostmanGatewayAssetsClient = class {
125776
125806
  await this.updateEnvironment(adopted, envName, values);
125777
125807
  return adopted;
125778
125808
  }
125809
+ if (adopted === void 0) {
125810
+ const retried = await this.resendAbsentCreate(async () => {
125811
+ const data = this.dataOf(await send2("auto"));
125812
+ const bareId = this.idOf(data);
125813
+ if (!bareId) throw new Error("Environment import did not return a UID");
125814
+ return this.publicEnvironmentUid(data, bareId);
125815
+ });
125816
+ if (retried) return retried;
125817
+ }
125779
125818
  throw error;
125780
125819
  }
125781
125820
  });
@@ -125907,19 +125946,20 @@ var PostmanGatewayAssetsClient = class {
125907
125946
  private: false,
125908
125947
  ...environment ? { environment } : {}
125909
125948
  };
125910
- try {
125911
- const response = await this.gateway.requestJson(
125912
- {
125913
- service: "mock",
125914
- method: "post",
125915
- path: `/mocks?workspace=${ws}`,
125916
- body
125917
- },
125918
- // Unsafe create: never blind re-POST. An ESOCKETTIMEDOUT after accept
125919
- // may still have created the mock; reconcile via discovery below and
125920
- // let the orchestrator retry the whole create-or-adopt cycle.
125921
- { retryTransient: false }
125922
- );
125949
+ const send2 = (fallback) => this.gateway.requestJson(
125950
+ {
125951
+ service: "mock",
125952
+ method: "post",
125953
+ path: `/mocks?workspace=${ws}`,
125954
+ body,
125955
+ ...fallback ? { fallback } : {}
125956
+ },
125957
+ // Unsafe create: never blind re-POST. An ESOCKETTIMEDOUT after accept
125958
+ // may still have created the mock; reconcile via discovery below and
125959
+ // let the orchestrator retry the whole create-or-adopt cycle.
125960
+ { retryTransient: false }
125961
+ );
125962
+ const parseMock = (response) => {
125923
125963
  const record = this.dataOf(response);
125924
125964
  const uid = this.idOf(record);
125925
125965
  if (!uid) {
@@ -125929,6 +125969,9 @@ var PostmanGatewayAssetsClient = class {
125929
125969
  uid,
125930
125970
  url: String(record?.url ?? record?.mockUrl ?? "").trim()
125931
125971
  };
125972
+ };
125973
+ try {
125974
+ return parseMock(await send2());
125932
125975
  } catch (error) {
125933
125976
  const adopted = await this.discoverAfterAmbiguousCreate(
125934
125977
  () => this.findMockByCollection(collection, environment, mockName),
@@ -125937,6 +125980,10 @@ var PostmanGatewayAssetsClient = class {
125937
125980
  if (adopted) {
125938
125981
  return { uid: adopted.uid, url: adopted.mockUrl };
125939
125982
  }
125983
+ if (adopted === void 0) {
125984
+ const retried = await this.resendAbsentCreate(async () => parseMock(await send2("auto")));
125985
+ if (retried) return retried;
125986
+ }
125940
125987
  throw error;
125941
125988
  }
125942
125989
  });
@@ -126043,21 +126090,25 @@ var PostmanGatewayAssetsClient = class {
126043
126090
  distribution: null,
126044
126091
  ...environment ? { environment } : {}
126045
126092
  };
126046
- try {
126047
- const response = await this.gateway.requestJson(
126048
- {
126049
- service: "monitors",
126050
- method: "post",
126051
- path: `/jobTemplates?workspace=${ws}`,
126052
- body
126053
- },
126054
- { retryTransient: false }
126055
- );
126093
+ const send2 = (fallback) => this.gateway.requestJson(
126094
+ {
126095
+ service: "monitors",
126096
+ method: "post",
126097
+ path: `/jobTemplates?workspace=${ws}`,
126098
+ body,
126099
+ ...fallback ? { fallback } : {}
126100
+ },
126101
+ { retryTransient: false }
126102
+ );
126103
+ const parseMonitor = (response) => {
126056
126104
  const uid = this.idOf(this.dataOf(response));
126057
126105
  if (!uid) {
126058
126106
  throw new Error("Monitor create did not return a UID");
126059
126107
  }
126060
126108
  return uid;
126109
+ };
126110
+ try {
126111
+ return parseMonitor(await send2());
126061
126112
  } catch (error) {
126062
126113
  const adopted = await this.discoverAfterAmbiguousCreate(
126063
126114
  () => this.findMonitorByCollection(collection, environment, monitorName),
@@ -126066,6 +126117,10 @@ var PostmanGatewayAssetsClient = class {
126066
126117
  if (adopted?.uid) {
126067
126118
  return adopted.uid;
126068
126119
  }
126120
+ if (adopted === void 0) {
126121
+ const retried = await this.resendAbsentCreate(async () => parseMonitor(await send2("auto")));
126122
+ if (retried) return retried;
126123
+ }
126069
126124
  throw error;
126070
126125
  }
126071
126126
  });
@@ -126294,6 +126349,7 @@ var AccessTokenGatewayClient = class {
126294
126349
  fetchImpl;
126295
126350
  secretMasker;
126296
126351
  maxRetries;
126352
+ fallbackBaseUrl;
126297
126353
  retryBaseDelayMs;
126298
126354
  sleepImpl;
126299
126355
  constructor(options) {
@@ -126306,6 +126362,8 @@ var AccessTokenGatewayClient = class {
126306
126362
  this.fetchImpl = options.fetchImpl ?? fetch;
126307
126363
  this.secretMasker = options.secretMasker ?? createSecretMasker([this.tokenProvider.current()]);
126308
126364
  this.maxRetries = options.maxRetries ?? 3;
126365
+ const fallbackEnv = typeof process !== "undefined" ? process.env?.POSTMAN_ITEM_CREATE_FALLBACK : void 0;
126366
+ this.fallbackBaseUrl = fallbackEnv === "off" ? void 0 : options.fallbackBaseUrl?.replace(/\/+$/, "");
126309
126367
  this.retryBaseDelayMs = options.retryBaseDelayMs ?? 400;
126310
126368
  this.sleepImpl = options.sleepImpl ?? defaultSleep;
126311
126369
  }
@@ -126324,8 +126382,8 @@ var AccessTokenGatewayClient = class {
126324
126382
  }
126325
126383
  return headers;
126326
126384
  }
126327
- async send(request) {
126328
- const url = `${this.bifrostBaseUrl}/ws/proxy`;
126385
+ async send(request, baseUrl) {
126386
+ const url = `${baseUrl ?? this.bifrostBaseUrl}/ws/proxy`;
126329
126387
  return this.fetchImpl(url, {
126330
126388
  method: "POST",
126331
126389
  headers: this.buildHeaders(request.headers),
@@ -126338,6 +126396,43 @@ var AccessTokenGatewayClient = class {
126338
126396
  })
126339
126397
  });
126340
126398
  }
126399
+ /**
126400
+ * One cold, serial attempt against the fallback base URL after the primary
126401
+ * budget is exhausted on a transient failure. Never hedged in parallel with
126402
+ * the primary; only fires when the request would otherwise throw. Callers
126403
+ * with `retryTransient: false` still reconcile first — the fallback attempt
126404
+ * here is the resend, so it is only used for requests whose mutation is
126405
+ * known idempotent or already reconciled by the caller's adopt-on-ambiguous
126406
+ * loop.
126407
+ */
126408
+ async tryFallback(request) {
126409
+ if (!this.fallbackBaseUrl) return null;
126410
+ try {
126411
+ return await this.send(request, this.fallbackBaseUrl);
126412
+ } catch {
126413
+ return null;
126414
+ }
126415
+ }
126416
+ /**
126417
+ * Run the fallback attempt and classify its response the same way the
126418
+ * primary path would. Returns the success response, or null when the
126419
+ * fallback also failed transiently (caller then throws the original error).
126420
+ * Non-transient fallback failures (4xx) surface as their own HttpError since
126421
+ * they are the freshest authoritative answer.
126422
+ */
126423
+ fallbackEligible(request, retryTransient) {
126424
+ if (!this.fallbackBaseUrl) return false;
126425
+ return retryTransient || request.fallback === "auto";
126426
+ }
126427
+ async attemptFallback(request, retryTransient) {
126428
+ if (!this.fallbackEligible(request, retryTransient)) return null;
126429
+ const response = await this.tryFallback(request);
126430
+ if (!response) return null;
126431
+ if (response.ok) return response;
126432
+ const body = await response.text().catch(() => "");
126433
+ if (isRetryableSafeReadResponse(response.status)) return null;
126434
+ throw this.toHttpError(request, response, body);
126435
+ }
126341
126436
  /**
126342
126437
  * Send a gateway request, refreshing the token once on an auth failure and
126343
126438
  * optionally retrying transient downstream failures (5xx / Bifrost read
@@ -126360,6 +126455,8 @@ var AccessTokenGatewayClient = class {
126360
126455
  await this.sleepImpl(delay);
126361
126456
  continue;
126362
126457
  }
126458
+ const fallbackResponse2 = await this.attemptFallback(request, retryTransient);
126459
+ if (fallbackResponse2) return fallbackResponse2;
126363
126460
  throw error;
126364
126461
  }
126365
126462
  if (response.ok) {
@@ -126381,6 +126478,8 @@ var AccessTokenGatewayClient = class {
126381
126478
  await this.sleepImpl(delay);
126382
126479
  continue;
126383
126480
  }
126481
+ const fallbackResponse = await this.attemptFallback(request, retryTransient);
126482
+ if (fallbackResponse) return fallbackResponse;
126384
126483
  throw this.toHttpError(request, response, body);
126385
126484
  }
126386
126485
  }
@@ -126952,6 +127051,7 @@ function resolveInputs(env = process.env) {
126952
127051
  postmanStack,
126953
127052
  postmanApiBase: endpointProfile.apiBaseUrl,
126954
127053
  postmanBifrostBase: endpointProfile.bifrostBaseUrl,
127054
+ postmanFallbackBase: endpointProfile.fallbackBaseUrl,
126955
127055
  postmanCliInstallUrl: endpointProfile.cliInstallUrl,
126956
127056
  postmanIapubBase: endpointProfile.iapubBaseUrl
126957
127057
  };
@@ -127955,6 +128055,8 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
127955
128055
  const gateway = new AccessTokenGatewayClient({
127956
128056
  tokenProvider,
127957
128057
  bifrostBaseUrl: inputs.postmanBifrostBase,
128058
+ // No fallback on the org-mode probe: the expected non-org 400 must
128059
+ // surface verbatim, not be re-fired against the /_api alias.
127958
128060
  secretMasker: masker
127959
128061
  });
127960
128062
  const squads = await gateway.getSquads(teamId);
@@ -128010,6 +128112,7 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
128010
128112
  const gateway = new AccessTokenGatewayClient({
128011
128113
  tokenProvider,
128012
128114
  bifrostBaseUrl: inputs.postmanBifrostBase,
128115
+ fallbackBaseUrl: inputs.postmanFallbackBase,
128013
128116
  teamId: resolved.teamId,
128014
128117
  orgMode: inputs.orgMode,
128015
128118
  secretMasker: masker
package/dist/index.cjs CHANGED
@@ -125134,12 +125134,14 @@ var POSTMAN_ENDPOINT_PROFILES = {
125134
125134
  prod: {
125135
125135
  apiBaseUrl: "https://api.getpostman.com",
125136
125136
  bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
125137
+ fallbackBaseUrl: "https://go.postman.co/_api",
125137
125138
  cliInstallUrl: "https://dl-cli.pstmn.io/install/unix.sh",
125138
125139
  iapubBaseUrl: "https://iapub.postman.co"
125139
125140
  },
125140
125141
  beta: {
125141
125142
  apiBaseUrl: "https://api.getpostman-beta.com",
125142
125143
  bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
125144
+ fallbackBaseUrl: "https://go.postman-beta.co/_api",
125143
125145
  cliInstallUrl: "https://dl-cli.pstmn-beta.io/install/unix.sh",
125144
125146
  iapubBaseUrl: "https://iapub.postman.co"
125145
125147
  }
@@ -127607,6 +127609,14 @@ var PostmanGatewayAssetsClient = class {
127607
127609
  createFlights.set(key, { fingerprint, promise: pending });
127608
127610
  return pending;
127609
127611
  }
127612
+ /**
127613
+ * Reconcile an ambiguous create. Returns the adopted match, `undefined`
127614
+ * when every discovery read succeeded and found nothing (the create is
127615
+ * conclusively absent, so a fallback resend cannot duplicate), or `null`
127616
+ * when the outcome is inconclusive (non-ambiguous error, or a discovery
127617
+ * read itself failed — in that case a resend could duplicate a committed
127618
+ * twin and must not fire).
127619
+ */
127610
127620
  async discoverAfterAmbiguousCreate(discover, error2) {
127611
127621
  if (!this.isAmbiguousCreateOutcome(error2)) {
127612
127622
  return null;
@@ -127615,12 +127625,31 @@ var PostmanGatewayAssetsClient = class {
127615
127625
  if (attempt > 0) {
127616
127626
  await this.sleep(this.reconcileDelayMs);
127617
127627
  }
127618
- const found = await discover();
127628
+ let found;
127629
+ try {
127630
+ found = await discover();
127631
+ } catch {
127632
+ return null;
127633
+ }
127619
127634
  if (found) {
127620
127635
  return found;
127621
127636
  }
127622
127637
  }
127623
- return null;
127638
+ return void 0;
127639
+ }
127640
+ /**
127641
+ * After an ambiguous create failed AND discovery proved the asset absent,
127642
+ * re-attempt the create once with the cold `/_api` fallback enabled: the
127643
+ * reconcile above guarantees no committed twin, so the resend cannot
127644
+ * duplicate. Returns null when the resend also fails (caller rethrows the
127645
+ * original error).
127646
+ */
127647
+ async resendAbsentCreate(operation) {
127648
+ try {
127649
+ return await operation();
127650
+ } catch {
127651
+ return null;
127652
+ }
127624
127653
  }
127625
127654
  publicEnvironmentUid(data, bareId) {
127626
127655
  const owner = String(data?.owner ?? "").trim();
@@ -127668,17 +127697,18 @@ var PostmanGatewayAssetsClient = class {
127668
127697
  name: envName,
127669
127698
  values: normalizedValues
127670
127699
  };
127700
+ const send2 = (fallback) => this.gateway.requestJson(
127701
+ {
127702
+ service: "sync",
127703
+ method: "post",
127704
+ path: `/environment/import?workspace=${ws}`,
127705
+ body,
127706
+ ...fallback ? { fallback } : {}
127707
+ },
127708
+ { retryTransient: false }
127709
+ );
127671
127710
  try {
127672
- const response = await this.gateway.requestJson(
127673
- {
127674
- service: "sync",
127675
- method: "post",
127676
- path: `/environment/import?workspace=${ws}`,
127677
- body
127678
- },
127679
- { retryTransient: false }
127680
- );
127681
- const data = this.dataOf(response);
127711
+ const data = this.dataOf(await send2());
127682
127712
  const bareId = this.idOf(data);
127683
127713
  if (!bareId) {
127684
127714
  throw new Error("Environment import did not return a UID");
@@ -127693,6 +127723,15 @@ var PostmanGatewayAssetsClient = class {
127693
127723
  await this.updateEnvironment(adopted, envName, values);
127694
127724
  return adopted;
127695
127725
  }
127726
+ if (adopted === void 0) {
127727
+ const retried = await this.resendAbsentCreate(async () => {
127728
+ const data = this.dataOf(await send2("auto"));
127729
+ const bareId = this.idOf(data);
127730
+ if (!bareId) throw new Error("Environment import did not return a UID");
127731
+ return this.publicEnvironmentUid(data, bareId);
127732
+ });
127733
+ if (retried) return retried;
127734
+ }
127696
127735
  throw error2;
127697
127736
  }
127698
127737
  });
@@ -127824,19 +127863,20 @@ var PostmanGatewayAssetsClient = class {
127824
127863
  private: false,
127825
127864
  ...environment ? { environment } : {}
127826
127865
  };
127827
- try {
127828
- const response = await this.gateway.requestJson(
127829
- {
127830
- service: "mock",
127831
- method: "post",
127832
- path: `/mocks?workspace=${ws}`,
127833
- body
127834
- },
127835
- // Unsafe create: never blind re-POST. An ESOCKETTIMEDOUT after accept
127836
- // may still have created the mock; reconcile via discovery below and
127837
- // let the orchestrator retry the whole create-or-adopt cycle.
127838
- { retryTransient: false }
127839
- );
127866
+ const send2 = (fallback) => this.gateway.requestJson(
127867
+ {
127868
+ service: "mock",
127869
+ method: "post",
127870
+ path: `/mocks?workspace=${ws}`,
127871
+ body,
127872
+ ...fallback ? { fallback } : {}
127873
+ },
127874
+ // Unsafe create: never blind re-POST. An ESOCKETTIMEDOUT after accept
127875
+ // may still have created the mock; reconcile via discovery below and
127876
+ // let the orchestrator retry the whole create-or-adopt cycle.
127877
+ { retryTransient: false }
127878
+ );
127879
+ const parseMock = (response) => {
127840
127880
  const record = this.dataOf(response);
127841
127881
  const uid = this.idOf(record);
127842
127882
  if (!uid) {
@@ -127846,6 +127886,9 @@ var PostmanGatewayAssetsClient = class {
127846
127886
  uid,
127847
127887
  url: String(record?.url ?? record?.mockUrl ?? "").trim()
127848
127888
  };
127889
+ };
127890
+ try {
127891
+ return parseMock(await send2());
127849
127892
  } catch (error2) {
127850
127893
  const adopted = await this.discoverAfterAmbiguousCreate(
127851
127894
  () => this.findMockByCollection(collection, environment, mockName),
@@ -127854,6 +127897,10 @@ var PostmanGatewayAssetsClient = class {
127854
127897
  if (adopted) {
127855
127898
  return { uid: adopted.uid, url: adopted.mockUrl };
127856
127899
  }
127900
+ if (adopted === void 0) {
127901
+ const retried = await this.resendAbsentCreate(async () => parseMock(await send2("auto")));
127902
+ if (retried) return retried;
127903
+ }
127857
127904
  throw error2;
127858
127905
  }
127859
127906
  });
@@ -127960,21 +128007,25 @@ var PostmanGatewayAssetsClient = class {
127960
128007
  distribution: null,
127961
128008
  ...environment ? { environment } : {}
127962
128009
  };
127963
- try {
127964
- const response = await this.gateway.requestJson(
127965
- {
127966
- service: "monitors",
127967
- method: "post",
127968
- path: `/jobTemplates?workspace=${ws}`,
127969
- body
127970
- },
127971
- { retryTransient: false }
127972
- );
128010
+ const send2 = (fallback) => this.gateway.requestJson(
128011
+ {
128012
+ service: "monitors",
128013
+ method: "post",
128014
+ path: `/jobTemplates?workspace=${ws}`,
128015
+ body,
128016
+ ...fallback ? { fallback } : {}
128017
+ },
128018
+ { retryTransient: false }
128019
+ );
128020
+ const parseMonitor = (response) => {
127973
128021
  const uid = this.idOf(this.dataOf(response));
127974
128022
  if (!uid) {
127975
128023
  throw new Error("Monitor create did not return a UID");
127976
128024
  }
127977
128025
  return uid;
128026
+ };
128027
+ try {
128028
+ return parseMonitor(await send2());
127978
128029
  } catch (error2) {
127979
128030
  const adopted = await this.discoverAfterAmbiguousCreate(
127980
128031
  () => this.findMonitorByCollection(collection, environment, monitorName),
@@ -127983,6 +128034,10 @@ var PostmanGatewayAssetsClient = class {
127983
128034
  if (adopted?.uid) {
127984
128035
  return adopted.uid;
127985
128036
  }
128037
+ if (adopted === void 0) {
128038
+ const retried = await this.resendAbsentCreate(async () => parseMonitor(await send2("auto")));
128039
+ if (retried) return retried;
128040
+ }
127986
128041
  throw error2;
127987
128042
  }
127988
128043
  });
@@ -128211,6 +128266,7 @@ var AccessTokenGatewayClient = class {
128211
128266
  fetchImpl;
128212
128267
  secretMasker;
128213
128268
  maxRetries;
128269
+ fallbackBaseUrl;
128214
128270
  retryBaseDelayMs;
128215
128271
  sleepImpl;
128216
128272
  constructor(options) {
@@ -128223,6 +128279,8 @@ var AccessTokenGatewayClient = class {
128223
128279
  this.fetchImpl = options.fetchImpl ?? fetch;
128224
128280
  this.secretMasker = options.secretMasker ?? createSecretMasker([this.tokenProvider.current()]);
128225
128281
  this.maxRetries = options.maxRetries ?? 3;
128282
+ const fallbackEnv = typeof process !== "undefined" ? process.env?.POSTMAN_ITEM_CREATE_FALLBACK : void 0;
128283
+ this.fallbackBaseUrl = fallbackEnv === "off" ? void 0 : options.fallbackBaseUrl?.replace(/\/+$/, "");
128226
128284
  this.retryBaseDelayMs = options.retryBaseDelayMs ?? 400;
128227
128285
  this.sleepImpl = options.sleepImpl ?? defaultSleep;
128228
128286
  }
@@ -128241,8 +128299,8 @@ var AccessTokenGatewayClient = class {
128241
128299
  }
128242
128300
  return headers;
128243
128301
  }
128244
- async send(request) {
128245
- const url = `${this.bifrostBaseUrl}/ws/proxy`;
128302
+ async send(request, baseUrl) {
128303
+ const url = `${baseUrl ?? this.bifrostBaseUrl}/ws/proxy`;
128246
128304
  return this.fetchImpl(url, {
128247
128305
  method: "POST",
128248
128306
  headers: this.buildHeaders(request.headers),
@@ -128255,6 +128313,43 @@ var AccessTokenGatewayClient = class {
128255
128313
  })
128256
128314
  });
128257
128315
  }
128316
+ /**
128317
+ * One cold, serial attempt against the fallback base URL after the primary
128318
+ * budget is exhausted on a transient failure. Never hedged in parallel with
128319
+ * the primary; only fires when the request would otherwise throw. Callers
128320
+ * with `retryTransient: false` still reconcile first — the fallback attempt
128321
+ * here is the resend, so it is only used for requests whose mutation is
128322
+ * known idempotent or already reconciled by the caller's adopt-on-ambiguous
128323
+ * loop.
128324
+ */
128325
+ async tryFallback(request) {
128326
+ if (!this.fallbackBaseUrl) return null;
128327
+ try {
128328
+ return await this.send(request, this.fallbackBaseUrl);
128329
+ } catch {
128330
+ return null;
128331
+ }
128332
+ }
128333
+ /**
128334
+ * Run the fallback attempt and classify its response the same way the
128335
+ * primary path would. Returns the success response, or null when the
128336
+ * fallback also failed transiently (caller then throws the original error).
128337
+ * Non-transient fallback failures (4xx) surface as their own HttpError since
128338
+ * they are the freshest authoritative answer.
128339
+ */
128340
+ fallbackEligible(request, retryTransient) {
128341
+ if (!this.fallbackBaseUrl) return false;
128342
+ return retryTransient || request.fallback === "auto";
128343
+ }
128344
+ async attemptFallback(request, retryTransient) {
128345
+ if (!this.fallbackEligible(request, retryTransient)) return null;
128346
+ const response = await this.tryFallback(request);
128347
+ if (!response) return null;
128348
+ if (response.ok) return response;
128349
+ const body = await response.text().catch(() => "");
128350
+ if (isRetryableSafeReadResponse(response.status)) return null;
128351
+ throw this.toHttpError(request, response, body);
128352
+ }
128258
128353
  /**
128259
128354
  * Send a gateway request, refreshing the token once on an auth failure and
128260
128355
  * optionally retrying transient downstream failures (5xx / Bifrost read
@@ -128277,6 +128372,8 @@ var AccessTokenGatewayClient = class {
128277
128372
  await this.sleepImpl(delay);
128278
128373
  continue;
128279
128374
  }
128375
+ const fallbackResponse2 = await this.attemptFallback(request, retryTransient);
128376
+ if (fallbackResponse2) return fallbackResponse2;
128280
128377
  throw error2;
128281
128378
  }
128282
128379
  if (response.ok) {
@@ -128298,6 +128395,8 @@ var AccessTokenGatewayClient = class {
128298
128395
  await this.sleepImpl(delay);
128299
128396
  continue;
128300
128397
  }
128398
+ const fallbackResponse = await this.attemptFallback(request, retryTransient);
128399
+ if (fallbackResponse) return fallbackResponse;
128301
128400
  throw this.toHttpError(request, response, body);
128302
128401
  }
128303
128402
  }
@@ -128924,6 +129023,7 @@ function resolveInputs(env = process.env) {
128924
129023
  postmanStack,
128925
129024
  postmanApiBase: endpointProfile.apiBaseUrl,
128926
129025
  postmanBifrostBase: endpointProfile.bifrostBaseUrl,
129026
+ postmanFallbackBase: endpointProfile.fallbackBaseUrl,
128927
129027
  postmanCliInstallUrl: endpointProfile.cliInstallUrl,
128928
129028
  postmanIapubBase: endpointProfile.iapubBaseUrl
128929
129029
  };
@@ -130065,6 +130165,8 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130065
130165
  const gateway = new AccessTokenGatewayClient({
130066
130166
  tokenProvider,
130067
130167
  bifrostBaseUrl: inputs.postmanBifrostBase,
130168
+ // No fallback on the org-mode probe: the expected non-org 400 must
130169
+ // surface verbatim, not be re-fired against the /_api alias.
130068
130170
  secretMasker: masker
130069
130171
  });
130070
130172
  const squads = await gateway.getSquads(teamId);
@@ -130120,6 +130222,7 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
130120
130222
  const gateway = new AccessTokenGatewayClient({
130121
130223
  tokenProvider,
130122
130224
  bifrostBaseUrl: inputs.postmanBifrostBase,
130225
+ fallbackBaseUrl: inputs.postmanFallbackBase,
130123
130226
  teamId: resolved.teamId,
130124
130227
  orgMode: inputs.orgMode,
130125
130228
  secretMasker: masker
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postman-cse/onboarding-repo-sync",
3
- "version": "2.1.6",
3
+ "version": "2.1.7",
4
4
  "description": "Postman repo sync GitHub Action.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",