@postman-cse/onboarding-bootstrap 2.9.6 → 2.9.8

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
@@ -258195,6 +258195,7 @@ var POSTMAN_ENDPOINT_PROFILES = {
258195
258195
  prod: {
258196
258196
  apiBaseUrl: "https://api.getpostman.com",
258197
258197
  bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
258198
+ fallbackBaseUrl: "https://go.postman.co/_api",
258198
258199
  cliInstallUrl: "https://dl-cli.pstmn.io/install/unix.sh",
258199
258200
  gatewayBaseUrl: "https://gateway.postman.com",
258200
258201
  iapubBaseUrl: "https://iapub.postman.co"
@@ -258202,6 +258203,7 @@ var POSTMAN_ENDPOINT_PROFILES = {
258202
258203
  beta: {
258203
258204
  apiBaseUrl: "https://api.getpostman-beta.com",
258204
258205
  bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
258206
+ fallbackBaseUrl: "https://go.postman-beta.co/_api",
258205
258207
  cliInstallUrl: "https://dl-cli.pstmn-beta.io/install/unix.sh",
258206
258208
  gatewayBaseUrl: "https://gateway.postman-beta.com",
258207
258209
  iapubBaseUrl: "https://iapub.postman.co"
@@ -265363,31 +265365,42 @@ ${error2.responseBody ?? ""}`
265363
265365
  method: "post",
265364
265366
  path: `/v3/collections/${cid}/items/`,
265365
265367
  retry: "none",
265368
+ // The transport's cold /_api fallback only fires after the primary
265369
+ // budget is exhausted; by then this loop has reconciled the item as
265370
+ // absent (match == null), so a fallback resend cannot duplicate a
265371
+ // committed create.
265372
+ fallback: "auto",
265366
265373
  headers: { "X-Entity-Type": kind },
265367
265374
  body: this.buildItemCreateBody(item, parentId)
265368
265375
  });
265369
265376
  } catch (error2) {
265370
265377
  if (!isAmbiguousTransportError(error2)) throw error2;
265371
265378
  const name = String(item.name ?? "Untitled");
265372
- const matches = (await this.listCollectionItems(cid)).filter((candidate) => {
265373
- if (String(candidate.name ?? candidate.title ?? "") !== name) return false;
265374
- if (String(candidate.$kind ?? candidate.type ?? "http-request") !== kind) return false;
265375
- const position = asRecord8(candidate.position);
265376
- const parent = asRecord8(position?.parent);
265377
- const candidateParent = String(parent?.id ?? position?.parent ?? candidate.parent ?? "").trim();
265378
- return Boolean(candidateParent) && this.bareModelId(candidateParent) === this.bareModelId(parentId);
265379
- });
265380
- const match = adoptExactMatch(
265381
- `collection-item:${cid}:${parentId}:${kind}:${name}`,
265382
- matches,
265383
- (candidate) => String(candidate.id ?? "")
265384
- );
265379
+ const findCommittedItem = async () => {
265380
+ const matches = (await this.listCollectionItems(cid)).filter((candidate) => {
265381
+ if (String(candidate.name ?? candidate.title ?? "") !== name) return false;
265382
+ if (String(candidate.$kind ?? candidate.type ?? "http-request") !== kind) return false;
265383
+ const position = asRecord8(candidate.position);
265384
+ const parent = asRecord8(position?.parent);
265385
+ const candidateParent = String(parent?.id ?? position?.parent ?? candidate.parent ?? "").trim();
265386
+ return Boolean(candidateParent) && this.bareModelId(candidateParent) === this.bareModelId(parentId);
265387
+ });
265388
+ return adoptExactMatch(
265389
+ `collection-item:${cid}:${parentId}:${kind}:${name}`,
265390
+ matches,
265391
+ (candidate) => String(candidate.id ?? "")
265392
+ );
265393
+ };
265394
+ let match = await findCommittedItem();
265395
+ if (!match) {
265396
+ await this.sleep(fullJitterDelayMs(attempt - 1, 300, 2e3, this.random));
265397
+ match = await findCommittedItem();
265398
+ }
265385
265399
  if (match) {
265386
265400
  created = { data: { id: match.id } };
265387
265401
  } else {
265388
265402
  const retriable = error2 instanceof HttpError && error2.status >= 500;
265389
265403
  if (!retriable || attempt === maxAttempts) throw error2;
265390
- await this.sleep(fullJitterDelayMs(attempt - 1, 300, 2e3, this.random));
265391
265404
  continue;
265392
265405
  }
265393
265406
  }
@@ -265734,6 +265747,7 @@ var AccessTokenGatewayClient = class {
265734
265747
  orgMode;
265735
265748
  fetchImpl;
265736
265749
  secretMasker;
265750
+ fallbackBaseUrl;
265737
265751
  maxRetries;
265738
265752
  retryBaseDelayMs;
265739
265753
  retryMaxDelayMs;
@@ -265749,6 +265763,8 @@ var AccessTokenGatewayClient = class {
265749
265763
  this.orgMode = options.orgMode ?? false;
265750
265764
  this.fetchImpl = options.fetchImpl ?? fetch;
265751
265765
  this.secretMasker = options.secretMasker ?? createSecretMasker([this.tokenProvider.current()]);
265766
+ const fallbackEnv = typeof process !== "undefined" ? process.env?.POSTMAN_ITEM_CREATE_FALLBACK : void 0;
265767
+ this.fallbackBaseUrl = fallbackEnv === "off" ? void 0 : options.fallbackBaseUrl?.replace(/\/+$/, "");
265752
265768
  this.maxRetries = options.maxRetries ?? 3;
265753
265769
  this.retryBaseDelayMs = options.retryBaseDelayMs ?? 400;
265754
265770
  this.retryMaxDelayMs = options.retryMaxDelayMs ?? 5e3;
@@ -265771,8 +265787,8 @@ var AccessTokenGatewayClient = class {
265771
265787
  }
265772
265788
  return headers;
265773
265789
  }
265774
- async send(request) {
265775
- const url = `${this.bifrostBaseUrl}/ws/proxy`;
265790
+ async send(request, baseUrl) {
265791
+ const url = `${baseUrl ?? this.bifrostBaseUrl}/ws/proxy`;
265776
265792
  const controller = new AbortController();
265777
265793
  const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);
265778
265794
  try {
@@ -265802,6 +265818,50 @@ var AccessTokenGatewayClient = class {
265802
265818
  * 200 envelope carrying an inner collection-service error is treated as that
265803
265819
  * inner status. The auth-refresh-once path is independent of the retry budget.
265804
265820
  */
265821
+ /**
265822
+ * One cold, serial attempt against the fallback base URL after the primary
265823
+ * budget is exhausted on a transient failure. Never hedged in parallel with
265824
+ * the primary; only fires when the request would otherwise throw. Callers
265825
+ * with `retry: 'none'` still reconcile first — the fallback attempt here is
265826
+ * the resend, so it is only used for requests whose mutation is known
265827
+ * idempotent or already reconciled by the caller's adopt-on-ambiguous loop.
265828
+ */
265829
+ async tryFallback(request) {
265830
+ if (!this.fallbackBaseUrl) return null;
265831
+ try {
265832
+ return await this.send(request, this.fallbackBaseUrl);
265833
+ } catch {
265834
+ return null;
265835
+ }
265836
+ }
265837
+ /**
265838
+ * Run the fallback attempt and classify its response the same way the
265839
+ * primary path would. Returns the rebuilt success response, or null when the
265840
+ * fallback also failed transiently (caller then throws the original error).
265841
+ * Non-transient fallback failures (4xx, inner errors) surface as their own
265842
+ * HttpError since they are the freshest authoritative answer.
265843
+ */
265844
+ fallbackEligible(request) {
265845
+ if (!this.fallbackBaseUrl) return false;
265846
+ const retryMode = request.retry ?? (request.method === "get" ? "safe" : "none");
265847
+ return retryMode === "safe" || request.fallback === "auto";
265848
+ }
265849
+ async attemptFallback(request) {
265850
+ if (!this.fallbackEligible(request)) return null;
265851
+ const response = await this.tryFallback(request);
265852
+ if (!response) return null;
265853
+ const body2 = await response.text().catch(() => "");
265854
+ if (response.ok) {
265855
+ const innerStatus = detectInnerError(body2);
265856
+ if (innerStatus !== null) {
265857
+ if (isTransientGatewayError(innerStatus, body2)) return null;
265858
+ throw this.toInnerHttpError(request, innerStatus, body2);
265859
+ }
265860
+ return this.rebuildResponse(response, body2);
265861
+ }
265862
+ if (isTransientGatewayError(response.status, body2)) return null;
265863
+ throw this.toHttpError(request, response, body2);
265864
+ }
265805
265865
  async request(request) {
265806
265866
  if (!this.tokenProvider.current() && this.tokenProvider.canRefresh()) {
265807
265867
  await this.tokenProvider.refresh();
@@ -265819,6 +265879,8 @@ var AccessTokenGatewayClient = class {
265819
265879
  await this.sleepImpl(delay);
265820
265880
  continue;
265821
265881
  }
265882
+ const fallbackResponse2 = await this.attemptFallback(request);
265883
+ if (fallbackResponse2) return fallbackResponse2;
265822
265884
  throw error2;
265823
265885
  }
265824
265886
  if (response.ok) {
@@ -265831,6 +265893,8 @@ var AccessTokenGatewayClient = class {
265831
265893
  await this.sleepImpl(delay);
265832
265894
  continue;
265833
265895
  }
265896
+ const fallbackResponse2 = await this.attemptFallback(request);
265897
+ if (fallbackResponse2) return fallbackResponse2;
265834
265898
  throw this.toInnerHttpError(request, innerStatus, okBody);
265835
265899
  }
265836
265900
  return this.rebuildResponse(response, okBody);
@@ -265859,6 +265923,8 @@ var AccessTokenGatewayClient = class {
265859
265923
  await this.sleepImpl(delay);
265860
265924
  continue;
265861
265925
  }
265926
+ const fallbackResponse = await this.attemptFallback(request);
265927
+ if (fallbackResponse) return fallbackResponse;
265862
265928
  throw this.toHttpError(request, response, body2);
265863
265929
  }
265864
265930
  }
@@ -311984,6 +312050,7 @@ function resolveInputs(env = process.env) {
311984
312050
  postmanStack,
311985
312051
  postmanApiBase: endpointProfile.apiBaseUrl,
311986
312052
  postmanBifrostBase: endpointProfile.bifrostBaseUrl,
312053
+ postmanFallbackBase: endpointProfile.fallbackBaseUrl,
311987
312054
  postmanGatewayBase: endpointProfile.gatewayBaseUrl,
311988
312055
  postmanCliInstallUrl: endpointProfile.cliInstallUrl,
311989
312056
  postmanIapubBase: endpointProfile.iapubBaseUrl,
@@ -313738,6 +313805,7 @@ async function runAction(actionCore = core_exports, actionExec = exec_exports, a
313738
313805
  gateway: new AccessTokenGatewayClient({
313739
313806
  tokenProvider: probeProvider,
313740
313807
  bifrostBaseUrl: inputs.postmanBifrostBase,
313808
+ fallbackBaseUrl: inputs.postmanFallbackBase,
313741
313809
  teamId: inputs.teamId || "",
313742
313810
  orgMode: false,
313743
313811
  secretMasker: createSecretMasker([inputs.postmanApiKey, inputs.postmanAccessToken])
@@ -313860,6 +313928,7 @@ function createBootstrapDependencies(inputs, factories, orgMode = false) {
313860
313928
  const gatewayClient = inputs.postmanAccessToken || inputs.postmanApiKey ? new AccessTokenGatewayClient({
313861
313929
  tokenProvider,
313862
313930
  bifrostBaseUrl: inputs.postmanBifrostBase,
313931
+ fallbackBaseUrl: inputs.postmanFallbackBase,
313863
313932
  teamId: inputs.teamId || "",
313864
313933
  orgMode,
313865
313934
  secretMasker
package/dist/cli.cjs CHANGED
@@ -256513,6 +256513,7 @@ var POSTMAN_ENDPOINT_PROFILES = {
256513
256513
  prod: {
256514
256514
  apiBaseUrl: "https://api.getpostman.com",
256515
256515
  bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
256516
+ fallbackBaseUrl: "https://go.postman.co/_api",
256516
256517
  cliInstallUrl: "https://dl-cli.pstmn.io/install/unix.sh",
256517
256518
  gatewayBaseUrl: "https://gateway.postman.com",
256518
256519
  iapubBaseUrl: "https://iapub.postman.co"
@@ -256520,6 +256521,7 @@ var POSTMAN_ENDPOINT_PROFILES = {
256520
256521
  beta: {
256521
256522
  apiBaseUrl: "https://api.getpostman-beta.com",
256522
256523
  bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
256524
+ fallbackBaseUrl: "https://go.postman-beta.co/_api",
256523
256525
  cliInstallUrl: "https://dl-cli.pstmn-beta.io/install/unix.sh",
256524
256526
  gatewayBaseUrl: "https://gateway.postman-beta.com",
256525
256527
  iapubBaseUrl: "https://iapub.postman.co"
@@ -263681,31 +263683,42 @@ ${error.responseBody ?? ""}`
263681
263683
  method: "post",
263682
263684
  path: `/v3/collections/${cid}/items/`,
263683
263685
  retry: "none",
263686
+ // The transport's cold /_api fallback only fires after the primary
263687
+ // budget is exhausted; by then this loop has reconciled the item as
263688
+ // absent (match == null), so a fallback resend cannot duplicate a
263689
+ // committed create.
263690
+ fallback: "auto",
263684
263691
  headers: { "X-Entity-Type": kind },
263685
263692
  body: this.buildItemCreateBody(item, parentId)
263686
263693
  });
263687
263694
  } catch (error) {
263688
263695
  if (!isAmbiguousTransportError(error)) throw error;
263689
263696
  const name = String(item.name ?? "Untitled");
263690
- const matches = (await this.listCollectionItems(cid)).filter((candidate) => {
263691
- if (String(candidate.name ?? candidate.title ?? "") !== name) return false;
263692
- if (String(candidate.$kind ?? candidate.type ?? "http-request") !== kind) return false;
263693
- const position = asRecord8(candidate.position);
263694
- const parent = asRecord8(position?.parent);
263695
- const candidateParent = String(parent?.id ?? position?.parent ?? candidate.parent ?? "").trim();
263696
- return Boolean(candidateParent) && this.bareModelId(candidateParent) === this.bareModelId(parentId);
263697
- });
263698
- const match = adoptExactMatch(
263699
- `collection-item:${cid}:${parentId}:${kind}:${name}`,
263700
- matches,
263701
- (candidate) => String(candidate.id ?? "")
263702
- );
263697
+ const findCommittedItem = async () => {
263698
+ const matches = (await this.listCollectionItems(cid)).filter((candidate) => {
263699
+ if (String(candidate.name ?? candidate.title ?? "") !== name) return false;
263700
+ if (String(candidate.$kind ?? candidate.type ?? "http-request") !== kind) return false;
263701
+ const position = asRecord8(candidate.position);
263702
+ const parent = asRecord8(position?.parent);
263703
+ const candidateParent = String(parent?.id ?? position?.parent ?? candidate.parent ?? "").trim();
263704
+ return Boolean(candidateParent) && this.bareModelId(candidateParent) === this.bareModelId(parentId);
263705
+ });
263706
+ return adoptExactMatch(
263707
+ `collection-item:${cid}:${parentId}:${kind}:${name}`,
263708
+ matches,
263709
+ (candidate) => String(candidate.id ?? "")
263710
+ );
263711
+ };
263712
+ let match = await findCommittedItem();
263713
+ if (!match) {
263714
+ await this.sleep(fullJitterDelayMs(attempt - 1, 300, 2e3, this.random));
263715
+ match = await findCommittedItem();
263716
+ }
263703
263717
  if (match) {
263704
263718
  created = { data: { id: match.id } };
263705
263719
  } else {
263706
263720
  const retriable = error instanceof HttpError && error.status >= 500;
263707
263721
  if (!retriable || attempt === maxAttempts) throw error;
263708
- await this.sleep(fullJitterDelayMs(attempt - 1, 300, 2e3, this.random));
263709
263722
  continue;
263710
263723
  }
263711
263724
  }
@@ -264052,6 +264065,7 @@ var AccessTokenGatewayClient = class {
264052
264065
  orgMode;
264053
264066
  fetchImpl;
264054
264067
  secretMasker;
264068
+ fallbackBaseUrl;
264055
264069
  maxRetries;
264056
264070
  retryBaseDelayMs;
264057
264071
  retryMaxDelayMs;
@@ -264067,6 +264081,8 @@ var AccessTokenGatewayClient = class {
264067
264081
  this.orgMode = options.orgMode ?? false;
264068
264082
  this.fetchImpl = options.fetchImpl ?? fetch;
264069
264083
  this.secretMasker = options.secretMasker ?? createSecretMasker([this.tokenProvider.current()]);
264084
+ const fallbackEnv = typeof process !== "undefined" ? process.env?.POSTMAN_ITEM_CREATE_FALLBACK : void 0;
264085
+ this.fallbackBaseUrl = fallbackEnv === "off" ? void 0 : options.fallbackBaseUrl?.replace(/\/+$/, "");
264070
264086
  this.maxRetries = options.maxRetries ?? 3;
264071
264087
  this.retryBaseDelayMs = options.retryBaseDelayMs ?? 400;
264072
264088
  this.retryMaxDelayMs = options.retryMaxDelayMs ?? 5e3;
@@ -264089,8 +264105,8 @@ var AccessTokenGatewayClient = class {
264089
264105
  }
264090
264106
  return headers;
264091
264107
  }
264092
- async send(request) {
264093
- const url = `${this.bifrostBaseUrl}/ws/proxy`;
264108
+ async send(request, baseUrl) {
264109
+ const url = `${baseUrl ?? this.bifrostBaseUrl}/ws/proxy`;
264094
264110
  const controller = new AbortController();
264095
264111
  const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);
264096
264112
  try {
@@ -264120,6 +264136,50 @@ var AccessTokenGatewayClient = class {
264120
264136
  * 200 envelope carrying an inner collection-service error is treated as that
264121
264137
  * inner status. The auth-refresh-once path is independent of the retry budget.
264122
264138
  */
264139
+ /**
264140
+ * One cold, serial attempt against the fallback base URL after the primary
264141
+ * budget is exhausted on a transient failure. Never hedged in parallel with
264142
+ * the primary; only fires when the request would otherwise throw. Callers
264143
+ * with `retry: 'none'` still reconcile first — the fallback attempt here is
264144
+ * the resend, so it is only used for requests whose mutation is known
264145
+ * idempotent or already reconciled by the caller's adopt-on-ambiguous loop.
264146
+ */
264147
+ async tryFallback(request) {
264148
+ if (!this.fallbackBaseUrl) return null;
264149
+ try {
264150
+ return await this.send(request, this.fallbackBaseUrl);
264151
+ } catch {
264152
+ return null;
264153
+ }
264154
+ }
264155
+ /**
264156
+ * Run the fallback attempt and classify its response the same way the
264157
+ * primary path would. Returns the rebuilt success response, or null when the
264158
+ * fallback also failed transiently (caller then throws the original error).
264159
+ * Non-transient fallback failures (4xx, inner errors) surface as their own
264160
+ * HttpError since they are the freshest authoritative answer.
264161
+ */
264162
+ fallbackEligible(request) {
264163
+ if (!this.fallbackBaseUrl) return false;
264164
+ const retryMode = request.retry ?? (request.method === "get" ? "safe" : "none");
264165
+ return retryMode === "safe" || request.fallback === "auto";
264166
+ }
264167
+ async attemptFallback(request) {
264168
+ if (!this.fallbackEligible(request)) return null;
264169
+ const response = await this.tryFallback(request);
264170
+ if (!response) return null;
264171
+ const body2 = await response.text().catch(() => "");
264172
+ if (response.ok) {
264173
+ const innerStatus = detectInnerError(body2);
264174
+ if (innerStatus !== null) {
264175
+ if (isTransientGatewayError(innerStatus, body2)) return null;
264176
+ throw this.toInnerHttpError(request, innerStatus, body2);
264177
+ }
264178
+ return this.rebuildResponse(response, body2);
264179
+ }
264180
+ if (isTransientGatewayError(response.status, body2)) return null;
264181
+ throw this.toHttpError(request, response, body2);
264182
+ }
264123
264183
  async request(request) {
264124
264184
  if (!this.tokenProvider.current() && this.tokenProvider.canRefresh()) {
264125
264185
  await this.tokenProvider.refresh();
@@ -264137,6 +264197,8 @@ var AccessTokenGatewayClient = class {
264137
264197
  await this.sleepImpl(delay);
264138
264198
  continue;
264139
264199
  }
264200
+ const fallbackResponse2 = await this.attemptFallback(request);
264201
+ if (fallbackResponse2) return fallbackResponse2;
264140
264202
  throw error;
264141
264203
  }
264142
264204
  if (response.ok) {
@@ -264149,6 +264211,8 @@ var AccessTokenGatewayClient = class {
264149
264211
  await this.sleepImpl(delay);
264150
264212
  continue;
264151
264213
  }
264214
+ const fallbackResponse2 = await this.attemptFallback(request);
264215
+ if (fallbackResponse2) return fallbackResponse2;
264152
264216
  throw this.toInnerHttpError(request, innerStatus, okBody);
264153
264217
  }
264154
264218
  return this.rebuildResponse(response, okBody);
@@ -264177,6 +264241,8 @@ var AccessTokenGatewayClient = class {
264177
264241
  await this.sleepImpl(delay);
264178
264242
  continue;
264179
264243
  }
264244
+ const fallbackResponse = await this.attemptFallback(request);
264245
+ if (fallbackResponse) return fallbackResponse;
264180
264246
  throw this.toHttpError(request, response, body2);
264181
264247
  }
264182
264248
  }
@@ -310296,6 +310362,7 @@ function resolveInputs(env = process.env) {
310296
310362
  postmanStack,
310297
310363
  postmanApiBase: endpointProfile.apiBaseUrl,
310298
310364
  postmanBifrostBase: endpointProfile.bifrostBaseUrl,
310365
+ postmanFallbackBase: endpointProfile.fallbackBaseUrl,
310299
310366
  postmanGatewayBase: endpointProfile.gatewayBaseUrl,
310300
310367
  postmanCliInstallUrl: endpointProfile.cliInstallUrl,
310301
310368
  postmanIapubBase: endpointProfile.iapubBaseUrl,
@@ -312014,6 +312081,7 @@ function createBootstrapDependencies(inputs, factories, orgMode = false) {
312014
312081
  const gatewayClient = inputs.postmanAccessToken || inputs.postmanApiKey ? new AccessTokenGatewayClient({
312015
312082
  tokenProvider,
312016
312083
  bifrostBaseUrl: inputs.postmanBifrostBase,
312084
+ fallbackBaseUrl: inputs.postmanFallbackBase,
312017
312085
  teamId: inputs.teamId || "",
312018
312086
  orgMode,
312019
312087
  secretMasker
package/dist/index.cjs CHANGED
@@ -258220,6 +258220,7 @@ var POSTMAN_ENDPOINT_PROFILES = {
258220
258220
  prod: {
258221
258221
  apiBaseUrl: "https://api.getpostman.com",
258222
258222
  bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
258223
+ fallbackBaseUrl: "https://go.postman.co/_api",
258223
258224
  cliInstallUrl: "https://dl-cli.pstmn.io/install/unix.sh",
258224
258225
  gatewayBaseUrl: "https://gateway.postman.com",
258225
258226
  iapubBaseUrl: "https://iapub.postman.co"
@@ -258227,6 +258228,7 @@ var POSTMAN_ENDPOINT_PROFILES = {
258227
258228
  beta: {
258228
258229
  apiBaseUrl: "https://api.getpostman-beta.com",
258229
258230
  bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
258231
+ fallbackBaseUrl: "https://go.postman-beta.co/_api",
258230
258232
  cliInstallUrl: "https://dl-cli.pstmn-beta.io/install/unix.sh",
258231
258233
  gatewayBaseUrl: "https://gateway.postman-beta.com",
258232
258234
  iapubBaseUrl: "https://iapub.postman.co"
@@ -265388,31 +265390,42 @@ ${error2.responseBody ?? ""}`
265388
265390
  method: "post",
265389
265391
  path: `/v3/collections/${cid}/items/`,
265390
265392
  retry: "none",
265393
+ // The transport's cold /_api fallback only fires after the primary
265394
+ // budget is exhausted; by then this loop has reconciled the item as
265395
+ // absent (match == null), so a fallback resend cannot duplicate a
265396
+ // committed create.
265397
+ fallback: "auto",
265391
265398
  headers: { "X-Entity-Type": kind },
265392
265399
  body: this.buildItemCreateBody(item, parentId)
265393
265400
  });
265394
265401
  } catch (error2) {
265395
265402
  if (!isAmbiguousTransportError(error2)) throw error2;
265396
265403
  const name = String(item.name ?? "Untitled");
265397
- const matches = (await this.listCollectionItems(cid)).filter((candidate) => {
265398
- if (String(candidate.name ?? candidate.title ?? "") !== name) return false;
265399
- if (String(candidate.$kind ?? candidate.type ?? "http-request") !== kind) return false;
265400
- const position = asRecord8(candidate.position);
265401
- const parent = asRecord8(position?.parent);
265402
- const candidateParent = String(parent?.id ?? position?.parent ?? candidate.parent ?? "").trim();
265403
- return Boolean(candidateParent) && this.bareModelId(candidateParent) === this.bareModelId(parentId);
265404
- });
265405
- const match = adoptExactMatch(
265406
- `collection-item:${cid}:${parentId}:${kind}:${name}`,
265407
- matches,
265408
- (candidate) => String(candidate.id ?? "")
265409
- );
265404
+ const findCommittedItem = async () => {
265405
+ const matches = (await this.listCollectionItems(cid)).filter((candidate) => {
265406
+ if (String(candidate.name ?? candidate.title ?? "") !== name) return false;
265407
+ if (String(candidate.$kind ?? candidate.type ?? "http-request") !== kind) return false;
265408
+ const position = asRecord8(candidate.position);
265409
+ const parent = asRecord8(position?.parent);
265410
+ const candidateParent = String(parent?.id ?? position?.parent ?? candidate.parent ?? "").trim();
265411
+ return Boolean(candidateParent) && this.bareModelId(candidateParent) === this.bareModelId(parentId);
265412
+ });
265413
+ return adoptExactMatch(
265414
+ `collection-item:${cid}:${parentId}:${kind}:${name}`,
265415
+ matches,
265416
+ (candidate) => String(candidate.id ?? "")
265417
+ );
265418
+ };
265419
+ let match = await findCommittedItem();
265420
+ if (!match) {
265421
+ await this.sleep(fullJitterDelayMs(attempt - 1, 300, 2e3, this.random));
265422
+ match = await findCommittedItem();
265423
+ }
265410
265424
  if (match) {
265411
265425
  created = { data: { id: match.id } };
265412
265426
  } else {
265413
265427
  const retriable = error2 instanceof HttpError && error2.status >= 500;
265414
265428
  if (!retriable || attempt === maxAttempts) throw error2;
265415
- await this.sleep(fullJitterDelayMs(attempt - 1, 300, 2e3, this.random));
265416
265429
  continue;
265417
265430
  }
265418
265431
  }
@@ -265759,6 +265772,7 @@ var AccessTokenGatewayClient = class {
265759
265772
  orgMode;
265760
265773
  fetchImpl;
265761
265774
  secretMasker;
265775
+ fallbackBaseUrl;
265762
265776
  maxRetries;
265763
265777
  retryBaseDelayMs;
265764
265778
  retryMaxDelayMs;
@@ -265774,6 +265788,8 @@ var AccessTokenGatewayClient = class {
265774
265788
  this.orgMode = options.orgMode ?? false;
265775
265789
  this.fetchImpl = options.fetchImpl ?? fetch;
265776
265790
  this.secretMasker = options.secretMasker ?? createSecretMasker([this.tokenProvider.current()]);
265791
+ const fallbackEnv = typeof process !== "undefined" ? process.env?.POSTMAN_ITEM_CREATE_FALLBACK : void 0;
265792
+ this.fallbackBaseUrl = fallbackEnv === "off" ? void 0 : options.fallbackBaseUrl?.replace(/\/+$/, "");
265777
265793
  this.maxRetries = options.maxRetries ?? 3;
265778
265794
  this.retryBaseDelayMs = options.retryBaseDelayMs ?? 400;
265779
265795
  this.retryMaxDelayMs = options.retryMaxDelayMs ?? 5e3;
@@ -265796,8 +265812,8 @@ var AccessTokenGatewayClient = class {
265796
265812
  }
265797
265813
  return headers;
265798
265814
  }
265799
- async send(request) {
265800
- const url = `${this.bifrostBaseUrl}/ws/proxy`;
265815
+ async send(request, baseUrl) {
265816
+ const url = `${baseUrl ?? this.bifrostBaseUrl}/ws/proxy`;
265801
265817
  const controller = new AbortController();
265802
265818
  const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);
265803
265819
  try {
@@ -265827,6 +265843,50 @@ var AccessTokenGatewayClient = class {
265827
265843
  * 200 envelope carrying an inner collection-service error is treated as that
265828
265844
  * inner status. The auth-refresh-once path is independent of the retry budget.
265829
265845
  */
265846
+ /**
265847
+ * One cold, serial attempt against the fallback base URL after the primary
265848
+ * budget is exhausted on a transient failure. Never hedged in parallel with
265849
+ * the primary; only fires when the request would otherwise throw. Callers
265850
+ * with `retry: 'none'` still reconcile first — the fallback attempt here is
265851
+ * the resend, so it is only used for requests whose mutation is known
265852
+ * idempotent or already reconciled by the caller's adopt-on-ambiguous loop.
265853
+ */
265854
+ async tryFallback(request) {
265855
+ if (!this.fallbackBaseUrl) return null;
265856
+ try {
265857
+ return await this.send(request, this.fallbackBaseUrl);
265858
+ } catch {
265859
+ return null;
265860
+ }
265861
+ }
265862
+ /**
265863
+ * Run the fallback attempt and classify its response the same way the
265864
+ * primary path would. Returns the rebuilt success response, or null when the
265865
+ * fallback also failed transiently (caller then throws the original error).
265866
+ * Non-transient fallback failures (4xx, inner errors) surface as their own
265867
+ * HttpError since they are the freshest authoritative answer.
265868
+ */
265869
+ fallbackEligible(request) {
265870
+ if (!this.fallbackBaseUrl) return false;
265871
+ const retryMode = request.retry ?? (request.method === "get" ? "safe" : "none");
265872
+ return retryMode === "safe" || request.fallback === "auto";
265873
+ }
265874
+ async attemptFallback(request) {
265875
+ if (!this.fallbackEligible(request)) return null;
265876
+ const response = await this.tryFallback(request);
265877
+ if (!response) return null;
265878
+ const body2 = await response.text().catch(() => "");
265879
+ if (response.ok) {
265880
+ const innerStatus = detectInnerError(body2);
265881
+ if (innerStatus !== null) {
265882
+ if (isTransientGatewayError(innerStatus, body2)) return null;
265883
+ throw this.toInnerHttpError(request, innerStatus, body2);
265884
+ }
265885
+ return this.rebuildResponse(response, body2);
265886
+ }
265887
+ if (isTransientGatewayError(response.status, body2)) return null;
265888
+ throw this.toHttpError(request, response, body2);
265889
+ }
265830
265890
  async request(request) {
265831
265891
  if (!this.tokenProvider.current() && this.tokenProvider.canRefresh()) {
265832
265892
  await this.tokenProvider.refresh();
@@ -265844,6 +265904,8 @@ var AccessTokenGatewayClient = class {
265844
265904
  await this.sleepImpl(delay);
265845
265905
  continue;
265846
265906
  }
265907
+ const fallbackResponse2 = await this.attemptFallback(request);
265908
+ if (fallbackResponse2) return fallbackResponse2;
265847
265909
  throw error2;
265848
265910
  }
265849
265911
  if (response.ok) {
@@ -265856,6 +265918,8 @@ var AccessTokenGatewayClient = class {
265856
265918
  await this.sleepImpl(delay);
265857
265919
  continue;
265858
265920
  }
265921
+ const fallbackResponse2 = await this.attemptFallback(request);
265922
+ if (fallbackResponse2) return fallbackResponse2;
265859
265923
  throw this.toInnerHttpError(request, innerStatus, okBody);
265860
265924
  }
265861
265925
  return this.rebuildResponse(response, okBody);
@@ -265884,6 +265948,8 @@ var AccessTokenGatewayClient = class {
265884
265948
  await this.sleepImpl(delay);
265885
265949
  continue;
265886
265950
  }
265951
+ const fallbackResponse = await this.attemptFallback(request);
265952
+ if (fallbackResponse) return fallbackResponse;
265887
265953
  throw this.toHttpError(request, response, body2);
265888
265954
  }
265889
265955
  }
@@ -312009,6 +312075,7 @@ function resolveInputs(env = process.env) {
312009
312075
  postmanStack,
312010
312076
  postmanApiBase: endpointProfile.apiBaseUrl,
312011
312077
  postmanBifrostBase: endpointProfile.bifrostBaseUrl,
312078
+ postmanFallbackBase: endpointProfile.fallbackBaseUrl,
312012
312079
  postmanGatewayBase: endpointProfile.gatewayBaseUrl,
312013
312080
  postmanCliInstallUrl: endpointProfile.cliInstallUrl,
312014
312081
  postmanIapubBase: endpointProfile.iapubBaseUrl,
@@ -313766,6 +313833,7 @@ async function runAction(actionCore = core_exports, actionExec = exec_exports, a
313766
313833
  gateway: new AccessTokenGatewayClient({
313767
313834
  tokenProvider: probeProvider,
313768
313835
  bifrostBaseUrl: inputs.postmanBifrostBase,
313836
+ fallbackBaseUrl: inputs.postmanFallbackBase,
313769
313837
  teamId: inputs.teamId || "",
313770
313838
  orgMode: false,
313771
313839
  secretMasker: createSecretMasker([inputs.postmanApiKey, inputs.postmanAccessToken])
@@ -313888,6 +313956,7 @@ function createBootstrapDependencies(inputs, factories, orgMode = false) {
313888
313956
  const gatewayClient = inputs.postmanAccessToken || inputs.postmanApiKey ? new AccessTokenGatewayClient({
313889
313957
  tokenProvider,
313890
313958
  bifrostBaseUrl: inputs.postmanBifrostBase,
313959
+ fallbackBaseUrl: inputs.postmanFallbackBase,
313891
313960
  teamId: inputs.teamId || "",
313892
313961
  orgMode,
313893
313962
  secretMasker
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postman-cse/onboarding-bootstrap",
3
- "version": "2.9.6",
3
+ "version": "2.9.8",
4
4
  "description": "Bootstrap Postman workspaces, specs, and collections from OpenAPI.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -79,4 +79,4 @@
79
79
  "type": "git",
80
80
  "url": "https://github.com/postman-cs/postman-bootstrap-action"
81
81
  }
82
- }
82
+ }