@postman-cse/onboarding-repo-sync 2.1.1 → 2.1.2

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/index.cjs CHANGED
@@ -127306,12 +127306,20 @@ async function retry(operation, options = {}) {
127306
127306
  }
127307
127307
 
127308
127308
  // src/lib/postman/postman-gateway-assets-client.ts
127309
+ var MAX_CREATE_FLIGHTS = 256;
127310
+ var createFlights = /* @__PURE__ */ new Map();
127309
127311
  var PostmanGatewayAssetsClient = class {
127310
127312
  gateway;
127311
127313
  workspaceId;
127314
+ sleep;
127315
+ reconcileAttempts;
127316
+ reconcileDelayMs;
127312
127317
  constructor(options) {
127313
127318
  this.gateway = options.gateway;
127314
127319
  this.workspaceId = String(options.workspaceId || "").trim();
127320
+ this.sleep = options.sleep ?? sleep;
127321
+ this.reconcileAttempts = Math.max(1, options.reconcileAttempts ?? 3);
127322
+ this.reconcileDelayMs = Math.max(0, options.reconcileDelayMs ?? 500);
127315
127323
  }
127316
127324
  asRecord(value) {
127317
127325
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
@@ -127341,8 +127349,64 @@ var PostmanGatewayAssetsClient = class {
127341
127349
  const parts = trimmed.split("-");
127342
127350
  return parts.length >= 6 ? parts.slice(1).join("-") : trimmed;
127343
127351
  }
127344
- isTransient(error2) {
127345
- return error2 instanceof HttpError && (error2.status === 429 || error2.status >= 500);
127352
+ isAmbiguousCreateOutcome(error2) {
127353
+ if (error2 instanceof HttpError) {
127354
+ return error2.status === 408 || error2.status === 429 || error2.status >= 500;
127355
+ }
127356
+ return error2 instanceof Error;
127357
+ }
127358
+ isRetryableIdempotentWriteOutcome(error2) {
127359
+ if (error2 instanceof HttpError) {
127360
+ return error2.status === 408 || error2.status === 429 || error2.status >= 500;
127361
+ }
127362
+ return error2 instanceof Error;
127363
+ }
127364
+ selectExactMatch(kind, identity, matches) {
127365
+ if (matches.length > 1) {
127366
+ const ids = matches.map((match) => match.uid).join(", ");
127367
+ throw new Error(
127368
+ `Multiple ${kind}s match ${identity}: ${ids}. Refusing to choose one; remove duplicates or pass an explicit asset ID.`
127369
+ );
127370
+ }
127371
+ return matches[0] ?? null;
127372
+ }
127373
+ async singleFlight(key, fingerprint, kind, operation) {
127374
+ const existing = createFlights.get(key);
127375
+ if (existing) {
127376
+ if (existing.fingerprint !== fingerprint) {
127377
+ throw new Error(`Incompatible concurrent ${kind} create for ${key}`);
127378
+ }
127379
+ return existing.promise;
127380
+ }
127381
+ if (createFlights.size >= MAX_CREATE_FLIGHTS) {
127382
+ throw new Error(`Too many concurrent Postman asset creates (limit ${MAX_CREATE_FLIGHTS})`);
127383
+ }
127384
+ const pending = operation().finally(() => {
127385
+ if (createFlights.get(key)?.promise === pending) {
127386
+ createFlights.delete(key);
127387
+ }
127388
+ });
127389
+ createFlights.set(key, { fingerprint, promise: pending });
127390
+ return pending;
127391
+ }
127392
+ async discoverAfterAmbiguousCreate(discover, error2) {
127393
+ if (!this.isAmbiguousCreateOutcome(error2)) {
127394
+ return null;
127395
+ }
127396
+ for (let attempt = 0; attempt < this.reconcileAttempts; attempt += 1) {
127397
+ if (attempt > 0) {
127398
+ await this.sleep(this.reconcileDelayMs);
127399
+ }
127400
+ const found = await discover();
127401
+ if (found) {
127402
+ return found;
127403
+ }
127404
+ }
127405
+ return null;
127406
+ }
127407
+ publicEnvironmentUid(data, bareId) {
127408
+ const owner = String(data?.owner ?? "").trim();
127409
+ return owner && !bareId.startsWith(`${owner}-`) ? `${owner}-${bareId}` : bareId;
127346
127410
  }
127347
127411
  // --- environments (service: sync) ---
127348
127412
  //
@@ -127353,9 +127417,10 @@ var PostmanGatewayAssetsClient = class {
127353
127417
  /**
127354
127418
  * Create/upsert an environment through the sync service.
127355
127419
  * POST /environment/import?workspace=:ws { id:<uuid>, name, values } ->
127356
- * { data:{ id:<bare uuid>, owner } }. The id is generated once and reused
127357
- * across retries so the import is idempotent (a retry upserts the same
127358
- * environment instead of duplicating it).
127420
+ * { data:{ id:<bare uuid>, owner } }. The id is generated once for the
127421
+ * operation. Unsafe creates submit once (no blind retry); on an ambiguous
127422
+ * response the client discovers by exact workspace-scoped name before
127423
+ * failing.
127359
127424
  *
127360
127425
  * Returns the PUBLIC uid (`<owner>-<uuid>`), not the bare model id the sync
127361
127426
  * import echoes: mock/monitor create reference the environment by its public
@@ -127365,33 +127430,54 @@ var PostmanGatewayAssetsClient = class {
127365
127430
  */
127366
127431
  async createEnvironment(workspaceId, name, values) {
127367
127432
  const ws = workspaceId || this.workspaceId;
127368
- const id = crypto.randomUUID();
127369
- const body = {
127370
- id,
127371
- name,
127372
- values: values.map((v) => ({
127373
- key: v.key,
127374
- value: v.value,
127375
- type: v.type ?? "default",
127376
- enabled: v.enabled ?? true
127377
- }))
127378
- };
127379
- const response = await retry(
127380
- () => this.gateway.requestJson({
127381
- service: "sync",
127382
- method: "post",
127383
- path: `/environment/import?workspace=${ws}`,
127384
- body
127385
- }),
127386
- { maxAttempts: 5, delayMs: 2e3, backoffMultiplier: 2, maxDelayMs: 15e3, shouldRetry: (e) => this.isTransient(e) }
127387
- );
127388
- const data = this.dataOf(response);
127389
- const bareId = this.idOf(data);
127390
- if (!bareId) {
127391
- throw new Error("Environment import did not return a UID");
127392
- }
127393
- const owner = String(data?.owner ?? "").trim();
127394
- return owner && !bareId.startsWith(`${owner}-`) ? `${owner}-${bareId}` : bareId;
127433
+ const envName = String(name ?? "").trim();
127434
+ const flightKey = `environment:${ws}:${envName}`;
127435
+ const normalizedValues = values.map((v) => ({
127436
+ key: v.key,
127437
+ value: v.value,
127438
+ type: v.type ?? "default",
127439
+ enabled: v.enabled ?? true
127440
+ }));
127441
+ return this.singleFlight(flightKey, JSON.stringify(normalizedValues), "environment", async () => {
127442
+ const existing = await this.findEnvironmentByName(ws, envName);
127443
+ if (existing?.uid) {
127444
+ await this.updateEnvironment(existing.uid, envName, values);
127445
+ return existing.uid;
127446
+ }
127447
+ const id = crypto.randomUUID();
127448
+ const body = {
127449
+ id,
127450
+ name: envName,
127451
+ values: normalizedValues
127452
+ };
127453
+ try {
127454
+ const response = await this.gateway.requestJson(
127455
+ {
127456
+ service: "sync",
127457
+ method: "post",
127458
+ path: `/environment/import?workspace=${ws}`,
127459
+ body
127460
+ },
127461
+ { retryTransient: false }
127462
+ );
127463
+ const data = this.dataOf(response);
127464
+ const bareId = this.idOf(data);
127465
+ if (!bareId) {
127466
+ throw new Error("Environment import did not return a UID");
127467
+ }
127468
+ return this.publicEnvironmentUid(data, bareId);
127469
+ } catch (error2) {
127470
+ const adopted = await this.discoverAfterAmbiguousCreate(async () => {
127471
+ const match = await this.findEnvironmentByName(ws, envName);
127472
+ return match?.uid ?? null;
127473
+ }, error2);
127474
+ if (adopted) {
127475
+ await this.updateEnvironment(adopted, envName, values);
127476
+ return adopted;
127477
+ }
127478
+ throw error2;
127479
+ }
127480
+ });
127395
127481
  }
127396
127482
  /**
127397
127483
  * Update an existing environment through the sync service.
@@ -127421,7 +127507,7 @@ var PostmanGatewayAssetsClient = class {
127421
127507
  path: `/environment/${id}`,
127422
127508
  body
127423
127509
  }),
127424
- { maxAttempts: 5, delayMs: 2e3, backoffMultiplier: 2, maxDelayMs: 15e3, shouldRetry: (e) => this.isTransient(e) }
127510
+ { maxAttempts: 5, delayMs: 2e3, backoffMultiplier: 2, maxDelayMs: 15e3, shouldRetry: (e) => this.isRetryableIdempotentWriteOutcome(e) }
127425
127511
  );
127426
127512
  }
127427
127513
  /**
@@ -127445,13 +127531,37 @@ var PostmanGatewayAssetsClient = class {
127445
127531
  */
127446
127532
  async listEnvironments(workspaceId) {
127447
127533
  const ws = workspaceId || this.workspaceId;
127448
- const response = await this.gateway.requestJson({
127449
- service: "sync",
127450
- method: "post",
127451
- path: `/list/environment?workspace=${ws}`
127452
- });
127534
+ const response = await this.gateway.requestJson(
127535
+ {
127536
+ service: "sync",
127537
+ method: "post",
127538
+ path: `/list/environment?workspace=${ws}`
127539
+ },
127540
+ { retryTransient: true }
127541
+ );
127453
127542
  const items = Array.isArray(response?.data) ? response.data : [];
127454
- return items.map((raw) => this.asRecord(raw)).filter((e) => e !== null).map((e) => ({ name: String(e.name ?? ""), uid: this.idOf(e) }));
127543
+ return items.map((raw) => this.asRecord(raw)).filter((e) => e !== null).map((e) => {
127544
+ const bareOrPublic = this.idOf(e);
127545
+ return {
127546
+ name: String(e.name ?? ""),
127547
+ uid: this.publicEnvironmentUid(e, bareOrPublic)
127548
+ };
127549
+ });
127550
+ }
127551
+ /** Exact name match within a workspace. Prefer tracked UIDs before calling. */
127552
+ async findEnvironmentByName(workspaceId, name) {
127553
+ const want = String(name ?? "").trim();
127554
+ if (!want) {
127555
+ return null;
127556
+ }
127557
+ const environments = await this.listEnvironments(workspaceId);
127558
+ const matches = environments.filter((entry) => entry.name === want);
127559
+ const match = this.selectExactMatch(
127560
+ "environment",
127561
+ `workspace ${workspaceId} and name "${want}"`,
127562
+ matches
127563
+ );
127564
+ return match?.uid ? { uid: match.uid, name: match.name } : null;
127455
127565
  }
127456
127566
  // --- collection read (service: collection, v3 export) ---
127457
127567
  //
@@ -127481,32 +127591,51 @@ var PostmanGatewayAssetsClient = class {
127481
127591
  // --- mocks (service: mock) ---
127482
127592
  async createMock(workspaceId, name, collectionUid, environmentUid) {
127483
127593
  const ws = workspaceId || this.workspaceId;
127594
+ const mockName = String(name ?? "").trim();
127484
127595
  const collection = String(collectionUid ?? "").trim();
127485
127596
  const environment = String(environmentUid ?? "").trim();
127486
- const body = {
127487
- name,
127488
- collection,
127489
- private: false,
127490
- ...environment ? { environment } : {}
127491
- };
127492
- const response = await retry(
127493
- () => this.gateway.requestJson({
127494
- service: "mock",
127495
- method: "post",
127496
- path: `/mocks?workspace=${ws}`,
127497
- body
127498
- }),
127499
- { maxAttempts: 5, delayMs: 2e3, backoffMultiplier: 2, maxDelayMs: 15e3, shouldRetry: (e) => this.isTransient(e) }
127500
- );
127501
- const record = this.dataOf(response);
127502
- const uid = this.idOf(record);
127503
- if (!uid) {
127504
- throw new Error("Mock create did not return a UID");
127505
- }
127506
- return {
127507
- uid,
127508
- url: String(record?.url ?? record?.mockUrl ?? "").trim()
127509
- };
127597
+ const flightKey = `mock:${ws}:${collection}:${environment}:${mockName}`;
127598
+ return this.singleFlight(flightKey, flightKey, "mock", async () => {
127599
+ const existing = await this.findMockByCollection(collection, environment, mockName);
127600
+ if (existing) {
127601
+ return { uid: existing.uid, url: existing.mockUrl };
127602
+ }
127603
+ const body = {
127604
+ name: mockName,
127605
+ collection,
127606
+ private: false,
127607
+ ...environment ? { environment } : {}
127608
+ };
127609
+ try {
127610
+ const response = await this.gateway.requestJson(
127611
+ {
127612
+ service: "mock",
127613
+ method: "post",
127614
+ path: `/mocks?workspace=${ws}`,
127615
+ body
127616
+ },
127617
+ { retryTransient: false }
127618
+ );
127619
+ const record = this.dataOf(response);
127620
+ const uid = this.idOf(record);
127621
+ if (!uid) {
127622
+ throw new Error("Mock create did not return a UID");
127623
+ }
127624
+ return {
127625
+ uid,
127626
+ url: String(record?.url ?? record?.mockUrl ?? "").trim()
127627
+ };
127628
+ } catch (error2) {
127629
+ const adopted = await this.discoverAfterAmbiguousCreate(
127630
+ () => this.findMockByCollection(collection, environment, mockName),
127631
+ error2
127632
+ );
127633
+ if (adopted) {
127634
+ return { uid: adopted.uid, url: adopted.mockUrl };
127635
+ }
127636
+ throw error2;
127637
+ }
127638
+ });
127510
127639
  }
127511
127640
  async listMocks() {
127512
127641
  const response = await this.gateway.requestJson({
@@ -127535,10 +127664,19 @@ var PostmanGatewayAssetsClient = class {
127535
127664
  return false;
127536
127665
  }
127537
127666
  }
127538
- async findMockByCollection(collectionUid) {
127667
+ async findMockByCollection(collectionUid, environmentUid, name) {
127539
127668
  const mocks = await this.listMocks();
127540
127669
  const want = String(collectionUid ?? "").trim();
127541
- const match = mocks.find((m) => m.collection === want);
127670
+ const environment = String(environmentUid ?? "").trim();
127671
+ const mockName = String(name ?? "").trim();
127672
+ const matches = mocks.filter(
127673
+ (mock) => mock.collection === want && mock.environment === environment && mock.name === mockName
127674
+ );
127675
+ const match = this.selectExactMatch(
127676
+ "mock",
127677
+ `workspace ${this.workspaceId}, name "${mockName}", collection ${want}, and environment ${environment || "(none)"}`,
127678
+ matches
127679
+ );
127542
127680
  return match ? { uid: match.uid, mockUrl: match.mockUrl } : null;
127543
127681
  }
127544
127682
  // --- monitors (service: monitors; collection-based = jobTemplates) ---
@@ -127555,32 +127693,51 @@ var PostmanGatewayAssetsClient = class {
127555
127693
  async createMonitor(workspaceId, name, collectionUid, environmentUid, cronSchedule) {
127556
127694
  const ws = workspaceId || this.workspaceId;
127557
127695
  const effectiveCron = cronSchedule && cronSchedule.trim() ? cronSchedule.trim() : "0 0 * * 0";
127696
+ const monitorName = String(name ?? "").trim();
127558
127697
  const collection = String(collectionUid ?? "").trim();
127559
127698
  const environment = String(environmentUid ?? "").trim();
127560
- const body = {
127561
- name,
127562
- collection,
127563
- options: { strictSSL: false, followRedirects: true, requestTimeout: null, requestDelay: 0 },
127564
- notifications: { onFailure: [], onError: [] },
127565
- retry: {},
127566
- schedule: { cronPattern: effectiveCron, timeZone: "UTC" },
127567
- distribution: null,
127568
- ...environment ? { environment } : {}
127569
- };
127570
- const response = await retry(
127571
- () => this.gateway.requestJson({
127572
- service: "monitors",
127573
- method: "post",
127574
- path: `/jobTemplates?workspace=${ws}`,
127575
- body
127576
- }),
127577
- { maxAttempts: 5, delayMs: 2e3, backoffMultiplier: 2, maxDelayMs: 15e3, shouldRetry: (e) => this.isTransient(e) }
127578
- );
127579
- const uid = this.idOf(this.dataOf(response));
127580
- if (!uid) {
127581
- throw new Error("Monitor create did not return a UID");
127582
- }
127583
- return uid;
127699
+ const flightKey = `monitor:${ws}:${collection}:${environment}:${monitorName}`;
127700
+ return this.singleFlight(flightKey, effectiveCron, "monitor", async () => {
127701
+ const existing = await this.findMonitorByCollection(collection, environment, monitorName);
127702
+ if (existing?.uid) {
127703
+ return existing.uid;
127704
+ }
127705
+ const body = {
127706
+ name: monitorName,
127707
+ collection,
127708
+ options: { strictSSL: false, followRedirects: true, requestTimeout: null, requestDelay: 0 },
127709
+ notifications: { onFailure: [], onError: [] },
127710
+ retry: {},
127711
+ schedule: { cronPattern: effectiveCron, timeZone: "UTC" },
127712
+ distribution: null,
127713
+ ...environment ? { environment } : {}
127714
+ };
127715
+ try {
127716
+ const response = await this.gateway.requestJson(
127717
+ {
127718
+ service: "monitors",
127719
+ method: "post",
127720
+ path: `/jobTemplates?workspace=${ws}`,
127721
+ body
127722
+ },
127723
+ { retryTransient: false }
127724
+ );
127725
+ const uid = this.idOf(this.dataOf(response));
127726
+ if (!uid) {
127727
+ throw new Error("Monitor create did not return a UID");
127728
+ }
127729
+ return uid;
127730
+ } catch (error2) {
127731
+ const adopted = await this.discoverAfterAmbiguousCreate(
127732
+ () => this.findMonitorByCollection(collection, environment, monitorName),
127733
+ error2
127734
+ );
127735
+ if (adopted?.uid) {
127736
+ return adopted.uid;
127737
+ }
127738
+ throw error2;
127739
+ }
127740
+ });
127584
127741
  }
127585
127742
  async listMonitors() {
127586
127743
  const response = await this.gateway.requestJson({
@@ -127603,24 +127760,30 @@ var PostmanGatewayAssetsClient = class {
127603
127760
  return false;
127604
127761
  }
127605
127762
  }
127606
- async findMonitorByCollection(collectionUid) {
127763
+ async findMonitorByCollection(collectionUid, environmentUid, name) {
127607
127764
  const want = String(collectionUid ?? "").trim();
127608
- const response = await this.gateway.requestJson({
127609
- service: "monitors",
127610
- method: "get",
127611
- path: `/collections/${want}/jobTemplates?_etc=true`
127612
- });
127613
- const data = response?.data;
127614
- const monitors = this.mapJobTemplates(Array.isArray(data) ? data : []);
127615
- const match = monitors.find((m) => m.collectionUid === want) ?? monitors[0];
127765
+ const environment = String(environmentUid ?? "").trim();
127766
+ const monitorName = String(name ?? "").trim();
127767
+ const monitors = await this.listMonitors();
127768
+ const matches = monitors.filter(
127769
+ (monitor) => monitor.collectionUid === want && monitor.environmentUid === environment && monitor.name === monitorName
127770
+ );
127771
+ const match = this.selectExactMatch(
127772
+ "monitor",
127773
+ `workspace ${this.workspaceId}, name "${monitorName}", collection ${want}, and environment ${environment || "(none)"}`,
127774
+ matches
127775
+ );
127616
127776
  return match?.uid ? { uid: match.uid, name: match.name } : null;
127617
127777
  }
127618
127778
  async runMonitor(uid) {
127619
- await this.gateway.requestJson({
127620
- service: "monitors",
127621
- method: "post",
127622
- path: `/jobTemplates/${uid}/jobs`
127623
- });
127779
+ await this.gateway.requestJson(
127780
+ {
127781
+ service: "monitors",
127782
+ method: "post",
127783
+ path: `/jobTemplates/${uid}/jobs`
127784
+ },
127785
+ { retryTransient: false }
127786
+ );
127624
127787
  }
127625
127788
  };
127626
127789
 
@@ -127786,12 +127949,8 @@ async function mintAccessTokenIfNeeded(inputs, log, setSecret2, fetchImpl = fetc
127786
127949
  function isExpiredAuthError(status, body) {
127787
127950
  return status === 401 || body.includes("UNAUTHENTICATED") || body.includes("authenticationError");
127788
127951
  }
127789
- function isTransientGatewayError(status, body) {
127790
- if (status === 502 || status === 503 || status === 504) return true;
127791
- if (status >= 500 && (body.includes("ESOCKETTIMEDOUT") || body.includes("ETIMEDOUT") || body.includes("ECONNRESET") || body.includes("serverError") || body.includes("downstream"))) {
127792
- return true;
127793
- }
127794
- return false;
127952
+ function isRetryableSafeReadResponse(status) {
127953
+ return status === 408 || status === 429 || status >= 500;
127795
127954
  }
127796
127955
  function defaultSleep(ms) {
127797
127956
  return new Promise((resolve3) => setTimeout(resolve3, ms));
@@ -127850,14 +128009,28 @@ var AccessTokenGatewayClient = class {
127850
128009
  }
127851
128010
  /**
127852
128011
  * Send a gateway request, refreshing the token once on an auth failure and
127853
- * retrying transient downstream failures (5xx / Bifrost read timeouts) with
127854
- * exponential backoff. The auth-refresh-once path is independent of the
127855
- * transient-retry budget.
128012
+ * optionally retrying transient downstream failures (5xx / Bifrost read
128013
+ * timeouts) with exponential backoff. Safe reads keep transient retries;
128014
+ * unsafe creates pass `{ retryTransient: false }` and reconcile after an
128015
+ * ambiguous response instead of re-POSTing. Auth refresh remains independent
128016
+ * of the transient-retry budget.
127856
128017
  */
127857
- async request(request) {
128018
+ async request(request, options = {}) {
128019
+ const retryTransient = options.retryTransient ?? request.method === "get";
127858
128020
  let attempt = 0;
127859
128021
  for (; ; ) {
127860
- let response = await this.send(request);
128022
+ let response;
128023
+ try {
128024
+ response = await this.send(request);
128025
+ } catch (error2) {
128026
+ if (retryTransient && attempt < this.maxRetries) {
128027
+ const delay = this.retryBaseDelayMs * 2 ** attempt;
128028
+ attempt += 1;
128029
+ await this.sleepImpl(delay);
128030
+ continue;
128031
+ }
128032
+ throw error2;
128033
+ }
127861
128034
  if (response.ok) {
127862
128035
  return response;
127863
128036
  }
@@ -127871,7 +128044,7 @@ var AccessTokenGatewayClient = class {
127871
128044
  const retryBody = await response.text().catch(() => "");
127872
128045
  throw this.toHttpError(request, response, retryBody);
127873
128046
  }
127874
- if (isTransientGatewayError(response.status, body) && attempt < this.maxRetries) {
128047
+ if (retryTransient && isRetryableSafeReadResponse(response.status) && attempt < this.maxRetries) {
127875
128048
  const delay = this.retryBaseDelayMs * 2 ** attempt;
127876
128049
  attempt += 1;
127877
128050
  await this.sleepImpl(delay);
@@ -127881,8 +128054,8 @@ var AccessTokenGatewayClient = class {
127881
128054
  }
127882
128055
  }
127883
128056
  /** Send a gateway request and parse the JSON body, or null when empty. */
127884
- async requestJson(request) {
127885
- const response = await this.request(request);
128057
+ async requestJson(request, options = {}) {
128058
+ const response = await this.request(request, options);
127886
128059
  const text = await response.text().catch(() => "");
127887
128060
  if (!text.trim()) {
127888
128061
  return null;
@@ -128474,18 +128647,28 @@ async function upsertEnvironments(inputs, dependencies, resourcesState) {
128474
128647
  for (const envName of inputs.environments) {
128475
128648
  const runtimeUrl = String(inputs.envRuntimeUrls[envName] || "").trim();
128476
128649
  const values = buildEnvironmentValues(envName, runtimeUrl);
128477
- const existingUid = envUids[envName];
128478
- if (existingUid) {
128479
- await dependencies.postman.updateEnvironment(
128480
- existingUid,
128481
- `${inputs.projectName} - ${envName}`,
128482
- values
128650
+ const displayName = `${inputs.projectName} - ${envName}`;
128651
+ let existingUid = String(envUids[envName] || "").trim();
128652
+ if (!existingUid) {
128653
+ const discovered = await dependencies.postman.findEnvironmentByName(
128654
+ inputs.workspaceId,
128655
+ displayName
128483
128656
  );
128657
+ if (discovered?.uid) {
128658
+ existingUid = discovered.uid;
128659
+ dependencies.core.info(
128660
+ `Discovered existing environment for ${displayName}: ${existingUid}`
128661
+ );
128662
+ }
128663
+ }
128664
+ if (existingUid) {
128665
+ await dependencies.postman.updateEnvironment(existingUid, displayName, values);
128666
+ envUids[envName] = existingUid;
128484
128667
  continue;
128485
128668
  }
128486
128669
  envUids[envName] = await dependencies.postman.createEnvironment(
128487
128670
  inputs.workspaceId,
128488
- `${inputs.projectName} - ${envName}`,
128671
+ displayName,
128489
128672
  values
128490
128673
  );
128491
128674
  }
@@ -128856,12 +129039,17 @@ async function runRepoSyncInner(inputs, dependencies) {
128856
129039
  const mockEnvUid = envUids.dev || envUids.prod || Object.values(envUids)[0];
128857
129040
  if (mockEnvUid) {
128858
129041
  let resolvedMockUrl = "";
129042
+ const mockName = `${assetProjectName} Mock`;
128859
129043
  if (inputs.mockUrl) {
128860
129044
  resolvedMockUrl = inputs.mockUrl;
128861
129045
  dependencies.core.info(`Reusing mock from explicit input: ${resolvedMockUrl}`);
128862
129046
  }
128863
129047
  if (!resolvedMockUrl && inputs.baselineCollectionId) {
128864
- const discovered = await dependencies.postman.findMockByCollection(inputs.baselineCollectionId);
129048
+ const discovered = await dependencies.postman.findMockByCollection(
129049
+ inputs.baselineCollectionId,
129050
+ mockEnvUid,
129051
+ mockName
129052
+ );
128865
129053
  if (discovered) {
128866
129054
  resolvedMockUrl = discovered.mockUrl;
128867
129055
  dependencies.core.info(`Discovered existing mock for collection ${inputs.baselineCollectionId}: ${resolvedMockUrl}`);
@@ -128870,7 +129058,7 @@ async function runRepoSyncInner(inputs, dependencies) {
128870
129058
  if (!resolvedMockUrl) {
128871
129059
  const mock = await dependencies.postman.createMock(
128872
129060
  inputs.workspaceId,
128873
- `${assetProjectName} Mock`,
129061
+ mockName,
128874
129062
  inputs.baselineCollectionId,
128875
129063
  mockEnvUid
128876
129064
  );
@@ -128885,6 +129073,7 @@ async function runRepoSyncInner(inputs, dependencies) {
128885
129073
  const effectiveCron = inputs.monitorCron && inputs.monitorCron.trim() ? inputs.monitorCron.trim() : "";
128886
129074
  if (monitorEnvUid && inputs.monitorType !== "cli") {
128887
129075
  let resolvedMonitorId = "";
129076
+ const monitorName = `${assetProjectName} - Smoke Monitor`;
128888
129077
  if (inputs.monitorId) {
128889
129078
  const valid = await dependencies.postman.monitorExists(inputs.monitorId);
128890
129079
  if (valid) {
@@ -128895,7 +129084,11 @@ async function runRepoSyncInner(inputs, dependencies) {
128895
129084
  }
128896
129085
  }
128897
129086
  if (!resolvedMonitorId && inputs.smokeCollectionId) {
128898
- const discovered = await dependencies.postman.findMonitorByCollection(inputs.smokeCollectionId);
129087
+ const discovered = await dependencies.postman.findMonitorByCollection(
129088
+ inputs.smokeCollectionId,
129089
+ monitorEnvUid,
129090
+ monitorName
129091
+ );
128899
129092
  if (discovered) {
128900
129093
  resolvedMonitorId = discovered.uid;
128901
129094
  dependencies.core.info(`Discovered existing monitor for collection ${inputs.smokeCollectionId}: ${resolvedMonitorId}`);
@@ -128904,7 +129097,7 @@ async function runRepoSyncInner(inputs, dependencies) {
128904
129097
  if (!resolvedMonitorId) {
128905
129098
  resolvedMonitorId = await dependencies.postman.createMonitor(
128906
129099
  inputs.workspaceId,
128907
- `${assetProjectName} - Smoke Monitor`,
129100
+ monitorName,
128908
129101
  inputs.smokeCollectionId,
128909
129102
  monitorEnvUid,
128910
129103
  effectiveCron || void 0
@@ -129117,6 +129310,7 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
129117
129310
  createEnvironment: gatewayAssets.createEnvironment.bind(gatewayAssets),
129118
129311
  getEnvironment: gatewayAssets.getEnvironment.bind(gatewayAssets),
129119
129312
  updateEnvironment: gatewayAssets.updateEnvironment.bind(gatewayAssets),
129313
+ findEnvironmentByName: gatewayAssets.findEnvironmentByName.bind(gatewayAssets),
129120
129314
  // Collection read via the v3 export endpoint — returns canonical v3 IR,
129121
129315
  // written to disk by `convertAndSplitAnyCollection`. PMAK is never used for
129122
129316
  // collection reads.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postman-cse/onboarding-repo-sync",
3
- "version": "2.1.1",
3
+ "version": "2.1.2",
4
4
  "description": "Postman repo sync GitHub Action.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",