@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/cli.cjs CHANGED
@@ -125394,12 +125394,20 @@ async function retry(operation, options = {}) {
125394
125394
  }
125395
125395
 
125396
125396
  // src/lib/postman/postman-gateway-assets-client.ts
125397
+ var MAX_CREATE_FLIGHTS = 256;
125398
+ var createFlights = /* @__PURE__ */ new Map();
125397
125399
  var PostmanGatewayAssetsClient = class {
125398
125400
  gateway;
125399
125401
  workspaceId;
125402
+ sleep;
125403
+ reconcileAttempts;
125404
+ reconcileDelayMs;
125400
125405
  constructor(options) {
125401
125406
  this.gateway = options.gateway;
125402
125407
  this.workspaceId = String(options.workspaceId || "").trim();
125408
+ this.sleep = options.sleep ?? sleep;
125409
+ this.reconcileAttempts = Math.max(1, options.reconcileAttempts ?? 3);
125410
+ this.reconcileDelayMs = Math.max(0, options.reconcileDelayMs ?? 500);
125403
125411
  }
125404
125412
  asRecord(value) {
125405
125413
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
@@ -125429,8 +125437,64 @@ var PostmanGatewayAssetsClient = class {
125429
125437
  const parts = trimmed.split("-");
125430
125438
  return parts.length >= 6 ? parts.slice(1).join("-") : trimmed;
125431
125439
  }
125432
- isTransient(error) {
125433
- return error instanceof HttpError && (error.status === 429 || error.status >= 500);
125440
+ isAmbiguousCreateOutcome(error) {
125441
+ if (error instanceof HttpError) {
125442
+ return error.status === 408 || error.status === 429 || error.status >= 500;
125443
+ }
125444
+ return error instanceof Error;
125445
+ }
125446
+ isRetryableIdempotentWriteOutcome(error) {
125447
+ if (error instanceof HttpError) {
125448
+ return error.status === 408 || error.status === 429 || error.status >= 500;
125449
+ }
125450
+ return error instanceof Error;
125451
+ }
125452
+ selectExactMatch(kind, identity, matches) {
125453
+ if (matches.length > 1) {
125454
+ const ids = matches.map((match) => match.uid).join(", ");
125455
+ throw new Error(
125456
+ `Multiple ${kind}s match ${identity}: ${ids}. Refusing to choose one; remove duplicates or pass an explicit asset ID.`
125457
+ );
125458
+ }
125459
+ return matches[0] ?? null;
125460
+ }
125461
+ async singleFlight(key, fingerprint, kind, operation) {
125462
+ const existing = createFlights.get(key);
125463
+ if (existing) {
125464
+ if (existing.fingerprint !== fingerprint) {
125465
+ throw new Error(`Incompatible concurrent ${kind} create for ${key}`);
125466
+ }
125467
+ return existing.promise;
125468
+ }
125469
+ if (createFlights.size >= MAX_CREATE_FLIGHTS) {
125470
+ throw new Error(`Too many concurrent Postman asset creates (limit ${MAX_CREATE_FLIGHTS})`);
125471
+ }
125472
+ const pending = operation().finally(() => {
125473
+ if (createFlights.get(key)?.promise === pending) {
125474
+ createFlights.delete(key);
125475
+ }
125476
+ });
125477
+ createFlights.set(key, { fingerprint, promise: pending });
125478
+ return pending;
125479
+ }
125480
+ async discoverAfterAmbiguousCreate(discover, error) {
125481
+ if (!this.isAmbiguousCreateOutcome(error)) {
125482
+ return null;
125483
+ }
125484
+ for (let attempt = 0; attempt < this.reconcileAttempts; attempt += 1) {
125485
+ if (attempt > 0) {
125486
+ await this.sleep(this.reconcileDelayMs);
125487
+ }
125488
+ const found = await discover();
125489
+ if (found) {
125490
+ return found;
125491
+ }
125492
+ }
125493
+ return null;
125494
+ }
125495
+ publicEnvironmentUid(data, bareId) {
125496
+ const owner = String(data?.owner ?? "").trim();
125497
+ return owner && !bareId.startsWith(`${owner}-`) ? `${owner}-${bareId}` : bareId;
125434
125498
  }
125435
125499
  // --- environments (service: sync) ---
125436
125500
  //
@@ -125441,9 +125505,10 @@ var PostmanGatewayAssetsClient = class {
125441
125505
  /**
125442
125506
  * Create/upsert an environment through the sync service.
125443
125507
  * POST /environment/import?workspace=:ws { id:<uuid>, name, values } ->
125444
- * { data:{ id:<bare uuid>, owner } }. The id is generated once and reused
125445
- * across retries so the import is idempotent (a retry upserts the same
125446
- * environment instead of duplicating it).
125508
+ * { data:{ id:<bare uuid>, owner } }. The id is generated once for the
125509
+ * operation. Unsafe creates submit once (no blind retry); on an ambiguous
125510
+ * response the client discovers by exact workspace-scoped name before
125511
+ * failing.
125447
125512
  *
125448
125513
  * Returns the PUBLIC uid (`<owner>-<uuid>`), not the bare model id the sync
125449
125514
  * import echoes: mock/monitor create reference the environment by its public
@@ -125453,33 +125518,54 @@ var PostmanGatewayAssetsClient = class {
125453
125518
  */
125454
125519
  async createEnvironment(workspaceId, name, values) {
125455
125520
  const ws = workspaceId || this.workspaceId;
125456
- const id = crypto.randomUUID();
125457
- const body = {
125458
- id,
125459
- name,
125460
- values: values.map((v) => ({
125461
- key: v.key,
125462
- value: v.value,
125463
- type: v.type ?? "default",
125464
- enabled: v.enabled ?? true
125465
- }))
125466
- };
125467
- const response = await retry(
125468
- () => this.gateway.requestJson({
125469
- service: "sync",
125470
- method: "post",
125471
- path: `/environment/import?workspace=${ws}`,
125472
- body
125473
- }),
125474
- { maxAttempts: 5, delayMs: 2e3, backoffMultiplier: 2, maxDelayMs: 15e3, shouldRetry: (e) => this.isTransient(e) }
125475
- );
125476
- const data = this.dataOf(response);
125477
- const bareId = this.idOf(data);
125478
- if (!bareId) {
125479
- throw new Error("Environment import did not return a UID");
125480
- }
125481
- const owner = String(data?.owner ?? "").trim();
125482
- return owner && !bareId.startsWith(`${owner}-`) ? `${owner}-${bareId}` : bareId;
125521
+ const envName = String(name ?? "").trim();
125522
+ const flightKey = `environment:${ws}:${envName}`;
125523
+ const normalizedValues = values.map((v) => ({
125524
+ key: v.key,
125525
+ value: v.value,
125526
+ type: v.type ?? "default",
125527
+ enabled: v.enabled ?? true
125528
+ }));
125529
+ return this.singleFlight(flightKey, JSON.stringify(normalizedValues), "environment", async () => {
125530
+ const existing = await this.findEnvironmentByName(ws, envName);
125531
+ if (existing?.uid) {
125532
+ await this.updateEnvironment(existing.uid, envName, values);
125533
+ return existing.uid;
125534
+ }
125535
+ const id = crypto.randomUUID();
125536
+ const body = {
125537
+ id,
125538
+ name: envName,
125539
+ values: normalizedValues
125540
+ };
125541
+ try {
125542
+ const response = await this.gateway.requestJson(
125543
+ {
125544
+ service: "sync",
125545
+ method: "post",
125546
+ path: `/environment/import?workspace=${ws}`,
125547
+ body
125548
+ },
125549
+ { retryTransient: false }
125550
+ );
125551
+ const data = this.dataOf(response);
125552
+ const bareId = this.idOf(data);
125553
+ if (!bareId) {
125554
+ throw new Error("Environment import did not return a UID");
125555
+ }
125556
+ return this.publicEnvironmentUid(data, bareId);
125557
+ } catch (error) {
125558
+ const adopted = await this.discoverAfterAmbiguousCreate(async () => {
125559
+ const match = await this.findEnvironmentByName(ws, envName);
125560
+ return match?.uid ?? null;
125561
+ }, error);
125562
+ if (adopted) {
125563
+ await this.updateEnvironment(adopted, envName, values);
125564
+ return adopted;
125565
+ }
125566
+ throw error;
125567
+ }
125568
+ });
125483
125569
  }
125484
125570
  /**
125485
125571
  * Update an existing environment through the sync service.
@@ -125509,7 +125595,7 @@ var PostmanGatewayAssetsClient = class {
125509
125595
  path: `/environment/${id}`,
125510
125596
  body
125511
125597
  }),
125512
- { maxAttempts: 5, delayMs: 2e3, backoffMultiplier: 2, maxDelayMs: 15e3, shouldRetry: (e) => this.isTransient(e) }
125598
+ { maxAttempts: 5, delayMs: 2e3, backoffMultiplier: 2, maxDelayMs: 15e3, shouldRetry: (e) => this.isRetryableIdempotentWriteOutcome(e) }
125513
125599
  );
125514
125600
  }
125515
125601
  /**
@@ -125533,13 +125619,37 @@ var PostmanGatewayAssetsClient = class {
125533
125619
  */
125534
125620
  async listEnvironments(workspaceId) {
125535
125621
  const ws = workspaceId || this.workspaceId;
125536
- const response = await this.gateway.requestJson({
125537
- service: "sync",
125538
- method: "post",
125539
- path: `/list/environment?workspace=${ws}`
125540
- });
125622
+ const response = await this.gateway.requestJson(
125623
+ {
125624
+ service: "sync",
125625
+ method: "post",
125626
+ path: `/list/environment?workspace=${ws}`
125627
+ },
125628
+ { retryTransient: true }
125629
+ );
125541
125630
  const items = Array.isArray(response?.data) ? response.data : [];
125542
- return items.map((raw) => this.asRecord(raw)).filter((e) => e !== null).map((e) => ({ name: String(e.name ?? ""), uid: this.idOf(e) }));
125631
+ return items.map((raw) => this.asRecord(raw)).filter((e) => e !== null).map((e) => {
125632
+ const bareOrPublic = this.idOf(e);
125633
+ return {
125634
+ name: String(e.name ?? ""),
125635
+ uid: this.publicEnvironmentUid(e, bareOrPublic)
125636
+ };
125637
+ });
125638
+ }
125639
+ /** Exact name match within a workspace. Prefer tracked UIDs before calling. */
125640
+ async findEnvironmentByName(workspaceId, name) {
125641
+ const want = String(name ?? "").trim();
125642
+ if (!want) {
125643
+ return null;
125644
+ }
125645
+ const environments = await this.listEnvironments(workspaceId);
125646
+ const matches = environments.filter((entry) => entry.name === want);
125647
+ const match = this.selectExactMatch(
125648
+ "environment",
125649
+ `workspace ${workspaceId} and name "${want}"`,
125650
+ matches
125651
+ );
125652
+ return match?.uid ? { uid: match.uid, name: match.name } : null;
125543
125653
  }
125544
125654
  // --- collection read (service: collection, v3 export) ---
125545
125655
  //
@@ -125569,32 +125679,51 @@ var PostmanGatewayAssetsClient = class {
125569
125679
  // --- mocks (service: mock) ---
125570
125680
  async createMock(workspaceId, name, collectionUid, environmentUid) {
125571
125681
  const ws = workspaceId || this.workspaceId;
125682
+ const mockName = String(name ?? "").trim();
125572
125683
  const collection = String(collectionUid ?? "").trim();
125573
125684
  const environment = String(environmentUid ?? "").trim();
125574
- const body = {
125575
- name,
125576
- collection,
125577
- private: false,
125578
- ...environment ? { environment } : {}
125579
- };
125580
- const response = await retry(
125581
- () => this.gateway.requestJson({
125582
- service: "mock",
125583
- method: "post",
125584
- path: `/mocks?workspace=${ws}`,
125585
- body
125586
- }),
125587
- { maxAttempts: 5, delayMs: 2e3, backoffMultiplier: 2, maxDelayMs: 15e3, shouldRetry: (e) => this.isTransient(e) }
125588
- );
125589
- const record = this.dataOf(response);
125590
- const uid = this.idOf(record);
125591
- if (!uid) {
125592
- throw new Error("Mock create did not return a UID");
125593
- }
125594
- return {
125595
- uid,
125596
- url: String(record?.url ?? record?.mockUrl ?? "").trim()
125597
- };
125685
+ const flightKey = `mock:${ws}:${collection}:${environment}:${mockName}`;
125686
+ return this.singleFlight(flightKey, flightKey, "mock", async () => {
125687
+ const existing = await this.findMockByCollection(collection, environment, mockName);
125688
+ if (existing) {
125689
+ return { uid: existing.uid, url: existing.mockUrl };
125690
+ }
125691
+ const body = {
125692
+ name: mockName,
125693
+ collection,
125694
+ private: false,
125695
+ ...environment ? { environment } : {}
125696
+ };
125697
+ try {
125698
+ const response = await this.gateway.requestJson(
125699
+ {
125700
+ service: "mock",
125701
+ method: "post",
125702
+ path: `/mocks?workspace=${ws}`,
125703
+ body
125704
+ },
125705
+ { retryTransient: false }
125706
+ );
125707
+ const record = this.dataOf(response);
125708
+ const uid = this.idOf(record);
125709
+ if (!uid) {
125710
+ throw new Error("Mock create did not return a UID");
125711
+ }
125712
+ return {
125713
+ uid,
125714
+ url: String(record?.url ?? record?.mockUrl ?? "").trim()
125715
+ };
125716
+ } catch (error) {
125717
+ const adopted = await this.discoverAfterAmbiguousCreate(
125718
+ () => this.findMockByCollection(collection, environment, mockName),
125719
+ error
125720
+ );
125721
+ if (adopted) {
125722
+ return { uid: adopted.uid, url: adopted.mockUrl };
125723
+ }
125724
+ throw error;
125725
+ }
125726
+ });
125598
125727
  }
125599
125728
  async listMocks() {
125600
125729
  const response = await this.gateway.requestJson({
@@ -125623,10 +125752,19 @@ var PostmanGatewayAssetsClient = class {
125623
125752
  return false;
125624
125753
  }
125625
125754
  }
125626
- async findMockByCollection(collectionUid) {
125755
+ async findMockByCollection(collectionUid, environmentUid, name) {
125627
125756
  const mocks = await this.listMocks();
125628
125757
  const want = String(collectionUid ?? "").trim();
125629
- const match = mocks.find((m) => m.collection === want);
125758
+ const environment = String(environmentUid ?? "").trim();
125759
+ const mockName = String(name ?? "").trim();
125760
+ const matches = mocks.filter(
125761
+ (mock) => mock.collection === want && mock.environment === environment && mock.name === mockName
125762
+ );
125763
+ const match = this.selectExactMatch(
125764
+ "mock",
125765
+ `workspace ${this.workspaceId}, name "${mockName}", collection ${want}, and environment ${environment || "(none)"}`,
125766
+ matches
125767
+ );
125630
125768
  return match ? { uid: match.uid, mockUrl: match.mockUrl } : null;
125631
125769
  }
125632
125770
  // --- monitors (service: monitors; collection-based = jobTemplates) ---
@@ -125643,32 +125781,51 @@ var PostmanGatewayAssetsClient = class {
125643
125781
  async createMonitor(workspaceId, name, collectionUid, environmentUid, cronSchedule) {
125644
125782
  const ws = workspaceId || this.workspaceId;
125645
125783
  const effectiveCron = cronSchedule && cronSchedule.trim() ? cronSchedule.trim() : "0 0 * * 0";
125784
+ const monitorName = String(name ?? "").trim();
125646
125785
  const collection = String(collectionUid ?? "").trim();
125647
125786
  const environment = String(environmentUid ?? "").trim();
125648
- const body = {
125649
- name,
125650
- collection,
125651
- options: { strictSSL: false, followRedirects: true, requestTimeout: null, requestDelay: 0 },
125652
- notifications: { onFailure: [], onError: [] },
125653
- retry: {},
125654
- schedule: { cronPattern: effectiveCron, timeZone: "UTC" },
125655
- distribution: null,
125656
- ...environment ? { environment } : {}
125657
- };
125658
- const response = await retry(
125659
- () => this.gateway.requestJson({
125660
- service: "monitors",
125661
- method: "post",
125662
- path: `/jobTemplates?workspace=${ws}`,
125663
- body
125664
- }),
125665
- { maxAttempts: 5, delayMs: 2e3, backoffMultiplier: 2, maxDelayMs: 15e3, shouldRetry: (e) => this.isTransient(e) }
125666
- );
125667
- const uid = this.idOf(this.dataOf(response));
125668
- if (!uid) {
125669
- throw new Error("Monitor create did not return a UID");
125670
- }
125671
- return uid;
125787
+ const flightKey = `monitor:${ws}:${collection}:${environment}:${monitorName}`;
125788
+ return this.singleFlight(flightKey, effectiveCron, "monitor", async () => {
125789
+ const existing = await this.findMonitorByCollection(collection, environment, monitorName);
125790
+ if (existing?.uid) {
125791
+ return existing.uid;
125792
+ }
125793
+ const body = {
125794
+ name: monitorName,
125795
+ collection,
125796
+ options: { strictSSL: false, followRedirects: true, requestTimeout: null, requestDelay: 0 },
125797
+ notifications: { onFailure: [], onError: [] },
125798
+ retry: {},
125799
+ schedule: { cronPattern: effectiveCron, timeZone: "UTC" },
125800
+ distribution: null,
125801
+ ...environment ? { environment } : {}
125802
+ };
125803
+ try {
125804
+ const response = await this.gateway.requestJson(
125805
+ {
125806
+ service: "monitors",
125807
+ method: "post",
125808
+ path: `/jobTemplates?workspace=${ws}`,
125809
+ body
125810
+ },
125811
+ { retryTransient: false }
125812
+ );
125813
+ const uid = this.idOf(this.dataOf(response));
125814
+ if (!uid) {
125815
+ throw new Error("Monitor create did not return a UID");
125816
+ }
125817
+ return uid;
125818
+ } catch (error) {
125819
+ const adopted = await this.discoverAfterAmbiguousCreate(
125820
+ () => this.findMonitorByCollection(collection, environment, monitorName),
125821
+ error
125822
+ );
125823
+ if (adopted?.uid) {
125824
+ return adopted.uid;
125825
+ }
125826
+ throw error;
125827
+ }
125828
+ });
125672
125829
  }
125673
125830
  async listMonitors() {
125674
125831
  const response = await this.gateway.requestJson({
@@ -125691,24 +125848,30 @@ var PostmanGatewayAssetsClient = class {
125691
125848
  return false;
125692
125849
  }
125693
125850
  }
125694
- async findMonitorByCollection(collectionUid) {
125851
+ async findMonitorByCollection(collectionUid, environmentUid, name) {
125695
125852
  const want = String(collectionUid ?? "").trim();
125696
- const response = await this.gateway.requestJson({
125697
- service: "monitors",
125698
- method: "get",
125699
- path: `/collections/${want}/jobTemplates?_etc=true`
125700
- });
125701
- const data = response?.data;
125702
- const monitors = this.mapJobTemplates(Array.isArray(data) ? data : []);
125703
- const match = monitors.find((m) => m.collectionUid === want) ?? monitors[0];
125853
+ const environment = String(environmentUid ?? "").trim();
125854
+ const monitorName = String(name ?? "").trim();
125855
+ const monitors = await this.listMonitors();
125856
+ const matches = monitors.filter(
125857
+ (monitor) => monitor.collectionUid === want && monitor.environmentUid === environment && monitor.name === monitorName
125858
+ );
125859
+ const match = this.selectExactMatch(
125860
+ "monitor",
125861
+ `workspace ${this.workspaceId}, name "${monitorName}", collection ${want}, and environment ${environment || "(none)"}`,
125862
+ matches
125863
+ );
125704
125864
  return match?.uid ? { uid: match.uid, name: match.name } : null;
125705
125865
  }
125706
125866
  async runMonitor(uid) {
125707
- await this.gateway.requestJson({
125708
- service: "monitors",
125709
- method: "post",
125710
- path: `/jobTemplates/${uid}/jobs`
125711
- });
125867
+ await this.gateway.requestJson(
125868
+ {
125869
+ service: "monitors",
125870
+ method: "post",
125871
+ path: `/jobTemplates/${uid}/jobs`
125872
+ },
125873
+ { retryTransient: false }
125874
+ );
125712
125875
  }
125713
125876
  };
125714
125877
 
@@ -125874,12 +126037,8 @@ async function mintAccessTokenIfNeeded(inputs, log, setSecret2, fetchImpl = fetc
125874
126037
  function isExpiredAuthError(status, body) {
125875
126038
  return status === 401 || body.includes("UNAUTHENTICATED") || body.includes("authenticationError");
125876
126039
  }
125877
- function isTransientGatewayError(status, body) {
125878
- if (status === 502 || status === 503 || status === 504) return true;
125879
- if (status >= 500 && (body.includes("ESOCKETTIMEDOUT") || body.includes("ETIMEDOUT") || body.includes("ECONNRESET") || body.includes("serverError") || body.includes("downstream"))) {
125880
- return true;
125881
- }
125882
- return false;
126040
+ function isRetryableSafeReadResponse(status) {
126041
+ return status === 408 || status === 429 || status >= 500;
125883
126042
  }
125884
126043
  function defaultSleep(ms) {
125885
126044
  return new Promise((resolve2) => setTimeout(resolve2, ms));
@@ -125938,14 +126097,28 @@ var AccessTokenGatewayClient = class {
125938
126097
  }
125939
126098
  /**
125940
126099
  * Send a gateway request, refreshing the token once on an auth failure and
125941
- * retrying transient downstream failures (5xx / Bifrost read timeouts) with
125942
- * exponential backoff. The auth-refresh-once path is independent of the
125943
- * transient-retry budget.
126100
+ * optionally retrying transient downstream failures (5xx / Bifrost read
126101
+ * timeouts) with exponential backoff. Safe reads keep transient retries;
126102
+ * unsafe creates pass `{ retryTransient: false }` and reconcile after an
126103
+ * ambiguous response instead of re-POSTing. Auth refresh remains independent
126104
+ * of the transient-retry budget.
125944
126105
  */
125945
- async request(request) {
126106
+ async request(request, options = {}) {
126107
+ const retryTransient = options.retryTransient ?? request.method === "get";
125946
126108
  let attempt = 0;
125947
126109
  for (; ; ) {
125948
- let response = await this.send(request);
126110
+ let response;
126111
+ try {
126112
+ response = await this.send(request);
126113
+ } catch (error) {
126114
+ if (retryTransient && attempt < this.maxRetries) {
126115
+ const delay = this.retryBaseDelayMs * 2 ** attempt;
126116
+ attempt += 1;
126117
+ await this.sleepImpl(delay);
126118
+ continue;
126119
+ }
126120
+ throw error;
126121
+ }
125949
126122
  if (response.ok) {
125950
126123
  return response;
125951
126124
  }
@@ -125959,7 +126132,7 @@ var AccessTokenGatewayClient = class {
125959
126132
  const retryBody = await response.text().catch(() => "");
125960
126133
  throw this.toHttpError(request, response, retryBody);
125961
126134
  }
125962
- if (isTransientGatewayError(response.status, body) && attempt < this.maxRetries) {
126135
+ if (retryTransient && isRetryableSafeReadResponse(response.status) && attempt < this.maxRetries) {
125963
126136
  const delay = this.retryBaseDelayMs * 2 ** attempt;
125964
126137
  attempt += 1;
125965
126138
  await this.sleepImpl(delay);
@@ -125969,8 +126142,8 @@ var AccessTokenGatewayClient = class {
125969
126142
  }
125970
126143
  }
125971
126144
  /** Send a gateway request and parse the JSON body, or null when empty. */
125972
- async requestJson(request) {
125973
- const response = await this.request(request);
126145
+ async requestJson(request, options = {}) {
126146
+ const response = await this.request(request, options);
125974
126147
  const text = await response.text().catch(() => "");
125975
126148
  if (!text.trim()) {
125976
126149
  return null;
@@ -126373,18 +126546,28 @@ async function upsertEnvironments(inputs, dependencies, resourcesState) {
126373
126546
  for (const envName of inputs.environments) {
126374
126547
  const runtimeUrl = String(inputs.envRuntimeUrls[envName] || "").trim();
126375
126548
  const values = buildEnvironmentValues(envName, runtimeUrl);
126376
- const existingUid = envUids[envName];
126377
- if (existingUid) {
126378
- await dependencies.postman.updateEnvironment(
126379
- existingUid,
126380
- `${inputs.projectName} - ${envName}`,
126381
- values
126549
+ const displayName = `${inputs.projectName} - ${envName}`;
126550
+ let existingUid = String(envUids[envName] || "").trim();
126551
+ if (!existingUid) {
126552
+ const discovered = await dependencies.postman.findEnvironmentByName(
126553
+ inputs.workspaceId,
126554
+ displayName
126382
126555
  );
126556
+ if (discovered?.uid) {
126557
+ existingUid = discovered.uid;
126558
+ dependencies.core.info(
126559
+ `Discovered existing environment for ${displayName}: ${existingUid}`
126560
+ );
126561
+ }
126562
+ }
126563
+ if (existingUid) {
126564
+ await dependencies.postman.updateEnvironment(existingUid, displayName, values);
126565
+ envUids[envName] = existingUid;
126383
126566
  continue;
126384
126567
  }
126385
126568
  envUids[envName] = await dependencies.postman.createEnvironment(
126386
126569
  inputs.workspaceId,
126387
- `${inputs.projectName} - ${envName}`,
126570
+ displayName,
126388
126571
  values
126389
126572
  );
126390
126573
  }
@@ -126755,12 +126938,17 @@ async function runRepoSyncInner(inputs, dependencies) {
126755
126938
  const mockEnvUid = envUids.dev || envUids.prod || Object.values(envUids)[0];
126756
126939
  if (mockEnvUid) {
126757
126940
  let resolvedMockUrl = "";
126941
+ const mockName = `${assetProjectName} Mock`;
126758
126942
  if (inputs.mockUrl) {
126759
126943
  resolvedMockUrl = inputs.mockUrl;
126760
126944
  dependencies.core.info(`Reusing mock from explicit input: ${resolvedMockUrl}`);
126761
126945
  }
126762
126946
  if (!resolvedMockUrl && inputs.baselineCollectionId) {
126763
- const discovered = await dependencies.postman.findMockByCollection(inputs.baselineCollectionId);
126947
+ const discovered = await dependencies.postman.findMockByCollection(
126948
+ inputs.baselineCollectionId,
126949
+ mockEnvUid,
126950
+ mockName
126951
+ );
126764
126952
  if (discovered) {
126765
126953
  resolvedMockUrl = discovered.mockUrl;
126766
126954
  dependencies.core.info(`Discovered existing mock for collection ${inputs.baselineCollectionId}: ${resolvedMockUrl}`);
@@ -126769,7 +126957,7 @@ async function runRepoSyncInner(inputs, dependencies) {
126769
126957
  if (!resolvedMockUrl) {
126770
126958
  const mock = await dependencies.postman.createMock(
126771
126959
  inputs.workspaceId,
126772
- `${assetProjectName} Mock`,
126960
+ mockName,
126773
126961
  inputs.baselineCollectionId,
126774
126962
  mockEnvUid
126775
126963
  );
@@ -126784,6 +126972,7 @@ async function runRepoSyncInner(inputs, dependencies) {
126784
126972
  const effectiveCron = inputs.monitorCron && inputs.monitorCron.trim() ? inputs.monitorCron.trim() : "";
126785
126973
  if (monitorEnvUid && inputs.monitorType !== "cli") {
126786
126974
  let resolvedMonitorId = "";
126975
+ const monitorName = `${assetProjectName} - Smoke Monitor`;
126787
126976
  if (inputs.monitorId) {
126788
126977
  const valid = await dependencies.postman.monitorExists(inputs.monitorId);
126789
126978
  if (valid) {
@@ -126794,7 +126983,11 @@ async function runRepoSyncInner(inputs, dependencies) {
126794
126983
  }
126795
126984
  }
126796
126985
  if (!resolvedMonitorId && inputs.smokeCollectionId) {
126797
- const discovered = await dependencies.postman.findMonitorByCollection(inputs.smokeCollectionId);
126986
+ const discovered = await dependencies.postman.findMonitorByCollection(
126987
+ inputs.smokeCollectionId,
126988
+ monitorEnvUid,
126989
+ monitorName
126990
+ );
126798
126991
  if (discovered) {
126799
126992
  resolvedMonitorId = discovered.uid;
126800
126993
  dependencies.core.info(`Discovered existing monitor for collection ${inputs.smokeCollectionId}: ${resolvedMonitorId}`);
@@ -126803,7 +126996,7 @@ async function runRepoSyncInner(inputs, dependencies) {
126803
126996
  if (!resolvedMonitorId) {
126804
126997
  resolvedMonitorId = await dependencies.postman.createMonitor(
126805
126998
  inputs.workspaceId,
126806
- `${assetProjectName} - Smoke Monitor`,
126999
+ monitorName,
126807
127000
  inputs.smokeCollectionId,
126808
127001
  monitorEnvUid,
126809
127002
  effectiveCron || void 0
@@ -127016,6 +127209,7 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
127016
127209
  createEnvironment: gatewayAssets.createEnvironment.bind(gatewayAssets),
127017
127210
  getEnvironment: gatewayAssets.getEnvironment.bind(gatewayAssets),
127018
127211
  updateEnvironment: gatewayAssets.updateEnvironment.bind(gatewayAssets),
127212
+ findEnvironmentByName: gatewayAssets.findEnvironmentByName.bind(gatewayAssets),
127019
127213
  // Collection read via the v3 export endpoint — returns canonical v3 IR,
127020
127214
  // written to disk by `convertAndSplitAnyCollection`. PMAK is never used for
127021
127215
  // collection reads.