@postman-cse/onboarding-repo-sync 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -1,12 +1,11 @@
1
- # Postman Repo Sync
1
+ # Postman Onboarding: Repo Sync
2
2
 
3
- [![CI](https://github.com/postman-cs/postman-repo-sync-action/actions/workflows/ci.yml/badge.svg)](https://github.com/postman-cs/postman-repo-sync-action/actions/workflows/ci.yml)
4
- [![Release](https://img.shields.io/github/v/release/postman-cs/postman-repo-sync-action?sort=semver)](https://github.com/postman-cs/postman-repo-sync-action/releases)
5
- [![npm](https://img.shields.io/npm/v/%40postman-cse%2Fonboarding-repo-sync)](https://www.npmjs.com/package/@postman-cse/onboarding-repo-sync)
6
- [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
3
+ [![CI](https://github.com/postman-cs/postman-repo-sync-action/actions/workflows/ci.yml/badge.svg)](https://github.com/postman-cs/postman-repo-sync-action/actions/workflows/ci.yml) [![Release](https://img.shields.io/github/v/release/postman-cs/postman-repo-sync-action?sort=semver)](https://github.com/postman-cs/postman-repo-sync-action/releases) [![npm](https://img.shields.io/npm/v/%40postman-cse%2Fonboarding-repo-sync)](https://www.npmjs.com/package/@postman-cse/onboarding-repo-sync) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
4
 
8
5
  Exports Postman collections and environments into your repository and wires CI, mocks, and monitors around them.
9
6
 
7
+ Part of the [Postman API Onboarding suite](https://github.com/postman-cs/postman-api-onboarding-action).
8
+
10
9
  ## Usage
11
10
 
12
11
  ```yaml
@@ -205,6 +204,18 @@ Deeper reference:
205
204
 
206
205
  ## Resources
207
206
 
207
+ ### The suite
208
+
209
+ | Action | Role |
210
+ | --- | --- |
211
+ | [Postman API Onboarding](https://github.com/postman-cs/postman-api-onboarding-action) | Entry point: chains workspace bootstrap, repo sync, and optional Insights linking |
212
+ | [Postman Onboarding: Service Token](https://github.com/postman-cs/postman-resolve-service-token-action) | Mints the service-account access token and team ID |
213
+ | [Postman Onboarding: AWS Spec Discovery](https://github.com/postman-cs/postman-aws-spec-discovery-action) | Discovers and exports API specs from AWS services |
214
+ | [Postman Onboarding: Workspace Bootstrap](https://github.com/postman-cs/postman-bootstrap-action) | Creates the workspace, uploads the spec, generates collections |
215
+ | [Postman Onboarding: Smoke Flow](https://github.com/postman-cs/postman-smoke-flow-action) | Applies a curated flow.yaml to the Smoke collection |
216
+ | [Postman Onboarding: Repo Sync](https://github.com/postman-cs/postman-repo-sync-action) | Exports artifacts into the repo and wires CI, mocks, and monitors |
217
+ | [Postman Onboarding: Insights Linking](https://github.com/postman-cs/postman-insights-onboarding-action) | Links Insights discovered services to the workspace |
218
+
208
219
  - [postman-resolve-service-token-action](https://github.com/postman-cs/postman-resolve-service-token-action): mints a service-account access token and team ID.
209
220
  - [postman-api-onboarding-action](https://github.com/postman-cs/postman-api-onboarding-action): composite action that orchestrates the onboarding pipeline.
210
221
  - [postman-bootstrap-action](https://github.com/postman-cs/postman-bootstrap-action): workspace provisioning, spec upload, and collection generation.
package/action.yml CHANGED
@@ -1,5 +1,5 @@
1
- name: Postman Repo Sync
2
- description: Export Postman collections and environments into your repo and wire CI, mocks, and monitors around them.
1
+ name: 'Postman Onboarding: Repo Sync'
2
+ description: Export Postman collections and environments into your repo with CI wiring. Part of the Postman API Onboarding suite.
3
3
  author: Postman
4
4
  branding:
5
5
  icon: refresh-cw
package/dist/action.cjs CHANGED
@@ -9182,14 +9182,14 @@ var require_retry_agent = __commonJS({
9182
9182
  this.#options = options;
9183
9183
  }
9184
9184
  dispatch(opts, handler) {
9185
- const retry = new RetryHandler({
9185
+ const retry2 = new RetryHandler({
9186
9186
  ...opts,
9187
9187
  retryOptions: this.#options
9188
9188
  }, {
9189
9189
  dispatch: this.#agent.dispatch.bind(this.#agent),
9190
9190
  handler
9191
9191
  });
9192
- return this.#agent.dispatch(opts, retry);
9192
+ return this.#agent.dispatch(opts, retry2);
9193
9193
  }
9194
9194
  close() {
9195
9195
  return this.#agent.close();
@@ -25282,12 +25282,60 @@ var postmanRepoSyncActionContract = {
25282
25282
  }
25283
25283
  };
25284
25284
 
25285
+ // src/lib/retry.ts
25286
+ function sleep(delayMs) {
25287
+ return new Promise((resolve3) => {
25288
+ setTimeout(resolve3, delayMs);
25289
+ });
25290
+ }
25291
+ function normalizeRetryOptions(options) {
25292
+ return {
25293
+ maxAttempts: Math.max(1, options.maxAttempts ?? 3),
25294
+ delayMs: Math.max(0, options.delayMs ?? 2e3),
25295
+ backoffMultiplier: Math.max(1, options.backoffMultiplier ?? 1),
25296
+ maxDelayMs: options.maxDelayMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, options.maxDelayMs),
25297
+ onRetry: options.onRetry ?? (async () => void 0),
25298
+ shouldRetry: options.shouldRetry ?? (() => true),
25299
+ sleep: options.sleep ?? sleep
25300
+ };
25301
+ }
25302
+ async function retry(operation, options = {}) {
25303
+ const normalized = normalizeRetryOptions(options);
25304
+ let nextDelayMs = normalized.delayMs;
25305
+ for (let attempt = 1; attempt <= normalized.maxAttempts; attempt += 1) {
25306
+ try {
25307
+ return await operation();
25308
+ } catch (error2) {
25309
+ const shouldRetry = attempt < normalized.maxAttempts && normalized.shouldRetry(error2, {
25310
+ attempt,
25311
+ maxAttempts: normalized.maxAttempts
25312
+ });
25313
+ if (!shouldRetry) {
25314
+ throw error2;
25315
+ }
25316
+ await normalized.onRetry({
25317
+ attempt,
25318
+ maxAttempts: normalized.maxAttempts,
25319
+ delayMs: nextDelayMs,
25320
+ error: error2
25321
+ });
25322
+ await normalized.sleep(nextDelayMs);
25323
+ nextDelayMs = Math.min(
25324
+ normalized.maxDelayMs,
25325
+ Math.round(nextDelayMs * normalized.backoffMultiplier)
25326
+ );
25327
+ }
25328
+ }
25329
+ throw new Error("Retry exhausted without returning or throwing");
25330
+ }
25331
+
25285
25332
  // src/lib/postman/postman-assets-client.ts
25286
25333
  var PostmanAssetsClient = class {
25287
25334
  apiKey;
25288
25335
  baseUrl;
25289
25336
  bifrostBaseUrl;
25290
25337
  fetchImpl;
25338
+ retrySleep;
25291
25339
  constructor(options) {
25292
25340
  this.apiKey = String(options.apiKey || "").trim();
25293
25341
  this.baseUrl = String(options.baseUrl || POSTMAN_ENDPOINT_PROFILES.prod.apiBaseUrl).replace(
@@ -25298,6 +25346,7 @@ var PostmanAssetsClient = class {
25298
25346
  options.bifrostBaseUrl || POSTMAN_ENDPOINT_PROFILES.prod.bifrostBaseUrl
25299
25347
  ).replace(/\/+$/, "");
25300
25348
  this.fetchImpl = options.fetchImpl ?? fetch;
25349
+ this.retrySleep = options.retrySleep;
25301
25350
  void (options.secretMasker ?? createSecretMasker([this.apiKey]));
25302
25351
  }
25303
25352
  getBaseUrl() {
@@ -25361,9 +25410,36 @@ var PostmanAssetsClient = class {
25361
25410
  })
25362
25411
  });
25363
25412
  }
25413
+ /**
25414
+ * Monitor and mock creation reference a collection that may have been
25415
+ * created moments earlier; the Postman backend is eventually consistent
25416
+ * and can reject the reference with a 400 "Unable to load collection"
25417
+ * until the collection becomes visible. Retry only that specific 400 and
25418
+ * 429 throttling: both guarantee nothing was created. A 5xx on these
25419
+ * non-idempotent creates is ambiguous (the asset may exist server-side),
25420
+ * so it is not retried to avoid duplicate mocks and monitors.
25421
+ */
25422
+ async requestWithCollectionRetry(path8, init) {
25423
+ return retry(() => this.request(path8, init), {
25424
+ maxAttempts: 5,
25425
+ delayMs: 2e3,
25426
+ backoffMultiplier: 2,
25427
+ maxDelayMs: 15e3,
25428
+ ...this.retrySleep ? { sleep: this.retrySleep } : {},
25429
+ shouldRetry: (error2) => {
25430
+ if (!(error2 instanceof HttpError)) {
25431
+ return false;
25432
+ }
25433
+ if (error2.status === 429) {
25434
+ return true;
25435
+ }
25436
+ return error2.status === 400 && /unable to load collection/i.test(error2.responseBody);
25437
+ }
25438
+ });
25439
+ }
25364
25440
  async createMonitor(workspaceId, name, collectionUid, environmentUid, cronSchedule) {
25365
25441
  const effectiveCron = cronSchedule && cronSchedule.trim() ? cronSchedule.trim() : "0 0 * * 0";
25366
- const response = await this.request(`/monitors?workspace=${workspaceId}`, {
25442
+ const response = await this.requestWithCollectionRetry(`/monitors?workspace=${workspaceId}`, {
25367
25443
  method: "POST",
25368
25444
  body: JSON.stringify({
25369
25445
  monitor: {
@@ -25393,7 +25469,7 @@ var PostmanAssetsClient = class {
25393
25469
  return uid;
25394
25470
  }
25395
25471
  async createMock(workspaceId, name, collectionUid, environmentUid) {
25396
- const response = await this.request(`/mocks?workspace=${workspaceId}`, {
25472
+ const response = await this.requestWithCollectionRetry(`/mocks?workspace=${workspaceId}`, {
25397
25473
  method: "POST",
25398
25474
  body: JSON.stringify({
25399
25475
  mock: {
package/dist/cli.cjs CHANGED
@@ -9183,14 +9183,14 @@ var require_retry_agent = __commonJS({
9183
9183
  this.#options = options;
9184
9184
  }
9185
9185
  dispatch(opts, handler) {
9186
- const retry = new RetryHandler({
9186
+ const retry2 = new RetryHandler({
9187
9187
  ...opts,
9188
9188
  retryOptions: this.#options
9189
9189
  }, {
9190
9190
  dispatch: this.#agent.dispatch.bind(this.#agent),
9191
9191
  handler
9192
9192
  });
9193
- return this.#agent.dispatch(opts, retry);
9193
+ return this.#agent.dispatch(opts, retry2);
9194
9194
  }
9195
9195
  close() {
9196
9196
  return this.#agent.close();
@@ -23385,12 +23385,60 @@ var postmanRepoSyncActionContract = {
23385
23385
  }
23386
23386
  };
23387
23387
 
23388
+ // src/lib/retry.ts
23389
+ function sleep(delayMs) {
23390
+ return new Promise((resolve2) => {
23391
+ setTimeout(resolve2, delayMs);
23392
+ });
23393
+ }
23394
+ function normalizeRetryOptions(options) {
23395
+ return {
23396
+ maxAttempts: Math.max(1, options.maxAttempts ?? 3),
23397
+ delayMs: Math.max(0, options.delayMs ?? 2e3),
23398
+ backoffMultiplier: Math.max(1, options.backoffMultiplier ?? 1),
23399
+ maxDelayMs: options.maxDelayMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, options.maxDelayMs),
23400
+ onRetry: options.onRetry ?? (async () => void 0),
23401
+ shouldRetry: options.shouldRetry ?? (() => true),
23402
+ sleep: options.sleep ?? sleep
23403
+ };
23404
+ }
23405
+ async function retry(operation, options = {}) {
23406
+ const normalized = normalizeRetryOptions(options);
23407
+ let nextDelayMs = normalized.delayMs;
23408
+ for (let attempt = 1; attempt <= normalized.maxAttempts; attempt += 1) {
23409
+ try {
23410
+ return await operation();
23411
+ } catch (error) {
23412
+ const shouldRetry = attempt < normalized.maxAttempts && normalized.shouldRetry(error, {
23413
+ attempt,
23414
+ maxAttempts: normalized.maxAttempts
23415
+ });
23416
+ if (!shouldRetry) {
23417
+ throw error;
23418
+ }
23419
+ await normalized.onRetry({
23420
+ attempt,
23421
+ maxAttempts: normalized.maxAttempts,
23422
+ delayMs: nextDelayMs,
23423
+ error
23424
+ });
23425
+ await normalized.sleep(nextDelayMs);
23426
+ nextDelayMs = Math.min(
23427
+ normalized.maxDelayMs,
23428
+ Math.round(nextDelayMs * normalized.backoffMultiplier)
23429
+ );
23430
+ }
23431
+ }
23432
+ throw new Error("Retry exhausted without returning or throwing");
23433
+ }
23434
+
23388
23435
  // src/lib/postman/postman-assets-client.ts
23389
23436
  var PostmanAssetsClient = class {
23390
23437
  apiKey;
23391
23438
  baseUrl;
23392
23439
  bifrostBaseUrl;
23393
23440
  fetchImpl;
23441
+ retrySleep;
23394
23442
  constructor(options) {
23395
23443
  this.apiKey = String(options.apiKey || "").trim();
23396
23444
  this.baseUrl = String(options.baseUrl || POSTMAN_ENDPOINT_PROFILES.prod.apiBaseUrl).replace(
@@ -23401,6 +23449,7 @@ var PostmanAssetsClient = class {
23401
23449
  options.bifrostBaseUrl || POSTMAN_ENDPOINT_PROFILES.prod.bifrostBaseUrl
23402
23450
  ).replace(/\/+$/, "");
23403
23451
  this.fetchImpl = options.fetchImpl ?? fetch;
23452
+ this.retrySleep = options.retrySleep;
23404
23453
  void (options.secretMasker ?? createSecretMasker([this.apiKey]));
23405
23454
  }
23406
23455
  getBaseUrl() {
@@ -23464,9 +23513,36 @@ var PostmanAssetsClient = class {
23464
23513
  })
23465
23514
  });
23466
23515
  }
23516
+ /**
23517
+ * Monitor and mock creation reference a collection that may have been
23518
+ * created moments earlier; the Postman backend is eventually consistent
23519
+ * and can reject the reference with a 400 "Unable to load collection"
23520
+ * until the collection becomes visible. Retry only that specific 400 and
23521
+ * 429 throttling: both guarantee nothing was created. A 5xx on these
23522
+ * non-idempotent creates is ambiguous (the asset may exist server-side),
23523
+ * so it is not retried to avoid duplicate mocks and monitors.
23524
+ */
23525
+ async requestWithCollectionRetry(path4, init) {
23526
+ return retry(() => this.request(path4, init), {
23527
+ maxAttempts: 5,
23528
+ delayMs: 2e3,
23529
+ backoffMultiplier: 2,
23530
+ maxDelayMs: 15e3,
23531
+ ...this.retrySleep ? { sleep: this.retrySleep } : {},
23532
+ shouldRetry: (error) => {
23533
+ if (!(error instanceof HttpError)) {
23534
+ return false;
23535
+ }
23536
+ if (error.status === 429) {
23537
+ return true;
23538
+ }
23539
+ return error.status === 400 && /unable to load collection/i.test(error.responseBody);
23540
+ }
23541
+ });
23542
+ }
23467
23543
  async createMonitor(workspaceId, name, collectionUid, environmentUid, cronSchedule) {
23468
23544
  const effectiveCron = cronSchedule && cronSchedule.trim() ? cronSchedule.trim() : "0 0 * * 0";
23469
- const response = await this.request(`/monitors?workspace=${workspaceId}`, {
23545
+ const response = await this.requestWithCollectionRetry(`/monitors?workspace=${workspaceId}`, {
23470
23546
  method: "POST",
23471
23547
  body: JSON.stringify({
23472
23548
  monitor: {
@@ -23496,7 +23572,7 @@ var PostmanAssetsClient = class {
23496
23572
  return uid;
23497
23573
  }
23498
23574
  async createMock(workspaceId, name, collectionUid, environmentUid) {
23499
- const response = await this.request(`/mocks?workspace=${workspaceId}`, {
23575
+ const response = await this.requestWithCollectionRetry(`/mocks?workspace=${workspaceId}`, {
23500
23576
  method: "POST",
23501
23577
  body: JSON.stringify({
23502
23578
  mock: {
package/dist/index.cjs CHANGED
@@ -9183,14 +9183,14 @@ var require_retry_agent = __commonJS({
9183
9183
  this.#options = options;
9184
9184
  }
9185
9185
  dispatch(opts, handler) {
9186
- const retry = new RetryHandler({
9186
+ const retry2 = new RetryHandler({
9187
9187
  ...opts,
9188
9188
  retryOptions: this.#options
9189
9189
  }, {
9190
9190
  dispatch: this.#agent.dispatch.bind(this.#agent),
9191
9191
  handler
9192
9192
  });
9193
- return this.#agent.dispatch(opts, retry);
9193
+ return this.#agent.dispatch(opts, retry2);
9194
9194
  }
9195
9195
  close() {
9196
9196
  return this.#agent.close();
@@ -25297,12 +25297,60 @@ var postmanRepoSyncActionContract = {
25297
25297
  }
25298
25298
  };
25299
25299
 
25300
+ // src/lib/retry.ts
25301
+ function sleep(delayMs) {
25302
+ return new Promise((resolve3) => {
25303
+ setTimeout(resolve3, delayMs);
25304
+ });
25305
+ }
25306
+ function normalizeRetryOptions(options) {
25307
+ return {
25308
+ maxAttempts: Math.max(1, options.maxAttempts ?? 3),
25309
+ delayMs: Math.max(0, options.delayMs ?? 2e3),
25310
+ backoffMultiplier: Math.max(1, options.backoffMultiplier ?? 1),
25311
+ maxDelayMs: options.maxDelayMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, options.maxDelayMs),
25312
+ onRetry: options.onRetry ?? (async () => void 0),
25313
+ shouldRetry: options.shouldRetry ?? (() => true),
25314
+ sleep: options.sleep ?? sleep
25315
+ };
25316
+ }
25317
+ async function retry(operation, options = {}) {
25318
+ const normalized = normalizeRetryOptions(options);
25319
+ let nextDelayMs = normalized.delayMs;
25320
+ for (let attempt = 1; attempt <= normalized.maxAttempts; attempt += 1) {
25321
+ try {
25322
+ return await operation();
25323
+ } catch (error2) {
25324
+ const shouldRetry = attempt < normalized.maxAttempts && normalized.shouldRetry(error2, {
25325
+ attempt,
25326
+ maxAttempts: normalized.maxAttempts
25327
+ });
25328
+ if (!shouldRetry) {
25329
+ throw error2;
25330
+ }
25331
+ await normalized.onRetry({
25332
+ attempt,
25333
+ maxAttempts: normalized.maxAttempts,
25334
+ delayMs: nextDelayMs,
25335
+ error: error2
25336
+ });
25337
+ await normalized.sleep(nextDelayMs);
25338
+ nextDelayMs = Math.min(
25339
+ normalized.maxDelayMs,
25340
+ Math.round(nextDelayMs * normalized.backoffMultiplier)
25341
+ );
25342
+ }
25343
+ }
25344
+ throw new Error("Retry exhausted without returning or throwing");
25345
+ }
25346
+
25300
25347
  // src/lib/postman/postman-assets-client.ts
25301
25348
  var PostmanAssetsClient = class {
25302
25349
  apiKey;
25303
25350
  baseUrl;
25304
25351
  bifrostBaseUrl;
25305
25352
  fetchImpl;
25353
+ retrySleep;
25306
25354
  constructor(options) {
25307
25355
  this.apiKey = String(options.apiKey || "").trim();
25308
25356
  this.baseUrl = String(options.baseUrl || POSTMAN_ENDPOINT_PROFILES.prod.apiBaseUrl).replace(
@@ -25313,6 +25361,7 @@ var PostmanAssetsClient = class {
25313
25361
  options.bifrostBaseUrl || POSTMAN_ENDPOINT_PROFILES.prod.bifrostBaseUrl
25314
25362
  ).replace(/\/+$/, "");
25315
25363
  this.fetchImpl = options.fetchImpl ?? fetch;
25364
+ this.retrySleep = options.retrySleep;
25316
25365
  void (options.secretMasker ?? createSecretMasker([this.apiKey]));
25317
25366
  }
25318
25367
  getBaseUrl() {
@@ -25376,9 +25425,36 @@ var PostmanAssetsClient = class {
25376
25425
  })
25377
25426
  });
25378
25427
  }
25428
+ /**
25429
+ * Monitor and mock creation reference a collection that may have been
25430
+ * created moments earlier; the Postman backend is eventually consistent
25431
+ * and can reject the reference with a 400 "Unable to load collection"
25432
+ * until the collection becomes visible. Retry only that specific 400 and
25433
+ * 429 throttling: both guarantee nothing was created. A 5xx on these
25434
+ * non-idempotent creates is ambiguous (the asset may exist server-side),
25435
+ * so it is not retried to avoid duplicate mocks and monitors.
25436
+ */
25437
+ async requestWithCollectionRetry(path8, init) {
25438
+ return retry(() => this.request(path8, init), {
25439
+ maxAttempts: 5,
25440
+ delayMs: 2e3,
25441
+ backoffMultiplier: 2,
25442
+ maxDelayMs: 15e3,
25443
+ ...this.retrySleep ? { sleep: this.retrySleep } : {},
25444
+ shouldRetry: (error2) => {
25445
+ if (!(error2 instanceof HttpError)) {
25446
+ return false;
25447
+ }
25448
+ if (error2.status === 429) {
25449
+ return true;
25450
+ }
25451
+ return error2.status === 400 && /unable to load collection/i.test(error2.responseBody);
25452
+ }
25453
+ });
25454
+ }
25379
25455
  async createMonitor(workspaceId, name, collectionUid, environmentUid, cronSchedule) {
25380
25456
  const effectiveCron = cronSchedule && cronSchedule.trim() ? cronSchedule.trim() : "0 0 * * 0";
25381
- const response = await this.request(`/monitors?workspace=${workspaceId}`, {
25457
+ const response = await this.requestWithCollectionRetry(`/monitors?workspace=${workspaceId}`, {
25382
25458
  method: "POST",
25383
25459
  body: JSON.stringify({
25384
25460
  monitor: {
@@ -25408,7 +25484,7 @@ var PostmanAssetsClient = class {
25408
25484
  return uid;
25409
25485
  }
25410
25486
  async createMock(workspaceId, name, collectionUid, environmentUid) {
25411
- const response = await this.request(`/mocks?workspace=${workspaceId}`, {
25487
+ const response = await this.requestWithCollectionRetry(`/mocks?workspace=${workspaceId}`, {
25412
25488
  method: "POST",
25413
25489
  body: JSON.stringify({
25414
25490
  mock: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postman-cse/onboarding-repo-sync",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Public customer preview Postman repo sync GitHub Action.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",