@postman-cs/onboarding-repo-sync 2.10.7 → 2.10.9

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
@@ -117745,6 +117745,7 @@ var path6 = __toESM(require("node:path"), 1);
117745
117745
  var V2 = __toESM(require_v2(), 1);
117746
117746
  var import_transforms = __toESM(require_transforms(), 1);
117747
117747
  var import_v3 = __toESM(require_node2(), 1);
117748
+ var V2_COLLECTION_MODEL = V2.Collection;
117748
117749
  function asArray(value) {
117749
117750
  return Array.isArray(value) ? value : [];
117750
117751
  }
@@ -117765,13 +117766,23 @@ function normalizeGraphqlRequests(node) {
117765
117766
  }
117766
117767
  }
117767
117768
  async function convertAndSplitCollection(v2Collection, outputDir) {
117768
- const model = V2.Collection;
117769
- const parsed = model.parse(v2Collection ?? {});
117770
- const v3 = (0, import_transforms.transform)(model, import_transforms.FormatVersion.V3, parsed);
117769
+ const v3 = convertV2CollectionToV3Collection(v2Collection);
117770
+ await writeSplitCollection(v3, outputDir);
117771
+ }
117772
+ function convertV2CollectionToV3Collection(v2Collection) {
117773
+ const parsed = V2_COLLECTION_MODEL.parse(v2Collection ?? {});
117774
+ const v3 = (0, import_transforms.transform)(
117775
+ V2_COLLECTION_MODEL,
117776
+ import_transforms.FormatVersion.V3,
117777
+ parsed
117778
+ );
117771
117779
  for (const item of asArray(v3.items)) {
117772
117780
  normalizeGraphqlRequests(item);
117773
117781
  }
117774
- await writeSplitCollection(v3, outputDir);
117782
+ return v3;
117783
+ }
117784
+ function assertV2CollectionModel(value) {
117785
+ V2_COLLECTION_MODEL.parse(value ?? {});
117775
117786
  }
117776
117787
  function normalizeV3ExportIds(node) {
117777
117788
  if (!node || typeof node !== "object") return;
@@ -121329,7 +121340,7 @@ var postmanRepoSyncActionContract = {
121329
121340
  allowedValues: ["full", "spec-only"]
121330
121341
  },
121331
121342
  "prebuilt-collections-json": {
121332
- description: "Optional digest-bound JSON manifest of unique baseline, smoke, or contract roles with confined repo-relative path, SHA-256 artifact digest of the on-disk v3 collection tree (sorted relative-path + NUL + bytes + NUL), and canonical cloud ID. The optional payloadDigest field is the semantic v2 payload digest carried for provenance (format-validated only, not the reuse gate). Exact role, path, cloudId, and artifactDigest matches reuse the on-disk tree without cloud export.",
121343
+ description: "Optional digest-bound JSON manifest of unique baseline, smoke, or contract roles with confined repo-relative path, SHA-256 artifact digest of the on-disk v3 collection tree (sorted relative-path + NUL + bytes + NUL), and canonical cloud ID. The optional payloadDigest field is the semantic v2 payload digest carried for provenance (format-validated only, not the reuse gate). Exact role, path, cloudId, and artifactDigest matches reuse the on-disk tree without a cloud snapshot read.",
121333
121344
  required: false,
121334
121345
  default: ""
121335
121346
  },
@@ -121664,6 +121675,9 @@ var PostmanAssetsClient = class {
121664
121675
  }
121665
121676
  };
121666
121677
 
121678
+ // src/lib/postman/postman-gateway-assets-client.ts
121679
+ var import_node_util = require("node:util");
121680
+
121667
121681
  // node_modules/acorn/dist/acorn.mjs
121668
121682
  var astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 574, 3, 9, 9, 7, 9, 32, 4, 318, 1, 78, 5, 71, 10, 50, 3, 123, 2, 54, 14, 32, 10, 3, 1, 11, 3, 46, 10, 8, 0, 46, 9, 7, 2, 37, 13, 2, 9, 6, 1, 45, 0, 13, 2, 49, 13, 9, 3, 2, 11, 83, 11, 7, 0, 3, 0, 158, 11, 6, 9, 7, 3, 56, 1, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 68, 8, 2, 0, 3, 0, 2, 3, 2, 4, 2, 0, 15, 1, 83, 17, 10, 9, 5, 0, 82, 19, 13, 9, 214, 6, 3, 8, 28, 1, 83, 16, 16, 9, 82, 12, 9, 9, 7, 19, 58, 14, 5, 9, 243, 14, 166, 9, 71, 5, 2, 1, 3, 3, 2, 0, 2, 1, 13, 9, 120, 6, 3, 6, 4, 0, 29, 9, 41, 6, 2, 3, 9, 0, 10, 10, 47, 15, 199, 7, 137, 9, 54, 7, 2, 7, 17, 9, 57, 21, 2, 13, 123, 5, 4, 0, 2, 1, 2, 6, 2, 0, 9, 9, 49, 4, 2, 1, 2, 4, 9, 9, 55, 9, 266, 3, 10, 1, 2, 0, 49, 6, 4, 4, 14, 10, 5350, 0, 7, 14, 11465, 27, 2343, 9, 87, 9, 39, 4, 60, 6, 26, 9, 535, 9, 470, 0, 2, 54, 8, 3, 82, 0, 12, 1, 19628, 1, 4178, 9, 519, 45, 3, 22, 543, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 513, 54, 5, 49, 9, 0, 15, 0, 23, 4, 2, 14, 1361, 6, 2, 16, 3, 6, 2, 1, 2, 4, 101, 0, 161, 6, 10, 9, 357, 0, 62, 13, 499, 13, 245, 1, 2, 9, 233, 0, 3, 0, 8, 1, 6, 0, 475, 6, 110, 6, 6, 9, 4759, 9, 787719, 239];
121669
121683
  var astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 14, 29, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 19, 35, 5, 35, 5, 39, 9, 51, 13, 10, 2, 14, 2, 6, 2, 1, 2, 10, 2, 14, 2, 6, 2, 1, 4, 51, 13, 310, 10, 21, 11, 7, 25, 5, 2, 41, 2, 8, 70, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 66, 18, 2, 1, 11, 21, 11, 25, 7, 25, 39, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 28, 43, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 56, 50, 14, 50, 14, 35, 39, 27, 10, 22, 251, 41, 7, 1, 17, 5, 57, 28, 11, 0, 9, 21, 43, 17, 47, 20, 28, 22, 13, 52, 58, 1, 3, 0, 14, 44, 33, 24, 27, 35, 30, 0, 3, 0, 9, 34, 4, 0, 13, 47, 15, 3, 22, 0, 2, 0, 36, 17, 2, 24, 20, 1, 64, 6, 2, 0, 2, 3, 2, 14, 2, 9, 8, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 19, 0, 13, 4, 31, 9, 2, 0, 3, 0, 2, 37, 2, 0, 26, 0, 2, 0, 45, 52, 19, 3, 21, 2, 31, 47, 21, 1, 2, 0, 185, 46, 42, 3, 37, 47, 21, 0, 60, 42, 14, 0, 72, 26, 38, 6, 186, 43, 117, 63, 32, 7, 3, 0, 3, 7, 2, 1, 2, 23, 16, 0, 2, 0, 95, 7, 3, 38, 17, 0, 2, 0, 29, 0, 11, 39, 8, 0, 22, 0, 12, 45, 20, 0, 19, 72, 200, 32, 32, 8, 2, 36, 18, 0, 50, 29, 113, 6, 2, 1, 2, 37, 22, 0, 26, 5, 2, 1, 2, 31, 15, 0, 24, 43, 261, 18, 16, 0, 2, 12, 2, 33, 125, 0, 80, 921, 103, 110, 18, 195, 2637, 96, 16, 1071, 18, 5, 26, 3994, 6, 582, 6842, 29, 1763, 568, 8, 30, 18, 78, 18, 29, 19, 47, 17, 3, 32, 20, 6, 18, 433, 44, 212, 63, 33, 24, 3, 24, 45, 74, 6, 0, 67, 12, 65, 1, 2, 0, 15, 4, 10, 7381, 42, 31, 98, 114, 8702, 3, 2, 6, 2, 1, 2, 290, 16, 0, 30, 2, 3, 0, 15, 3, 9, 395, 2309, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 1845, 30, 7, 5, 262, 61, 147, 44, 11, 6, 17, 0, 322, 29, 19, 43, 485, 27, 229, 29, 3, 0, 208, 30, 2, 2, 2, 1, 2, 6, 3, 4, 10, 1, 225, 6, 2, 3, 2, 1, 2, 14, 2, 196, 60, 67, 8, 0, 1205, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42719, 33, 4381, 3, 5773, 3, 7472, 16, 621, 2467, 541, 1507, 4938, 6, 8489];
@@ -127860,8 +127874,8 @@ var PostmanGatewayAssetsClient = class {
127860
127874
  }
127861
127875
  /**
127862
127876
  * Validate and normalize a collection public UID for routes that require the
127863
- * full owner-prefixed UID (`<owner>-<uuid>`, 6 hyphen segments): collection
127864
- * GET export, collection-root PATCH/read, and private-mock runtime auth.
127877
+ * full owner-prefixed UID (`<owner>-<uuid>`, 6 hyphen segments): populated
127878
+ * collection snapshot, collection-root PATCH/read, and private-mock runtime auth.
127865
127879
  *
127866
127880
  * - Trims once.
127867
127881
  * - Rejects empty values.
@@ -128173,32 +128187,52 @@ var PostmanGatewayAssetsClient = class {
128173
128187
  );
128174
128188
  return match?.uid ? { uid: match.uid, name: match.name } : null;
128175
128189
  }
128176
- // --- collection read (service: collection, v3 export) ---
128190
+ // --- collection read (service: sync, populated v2.1 snapshot) ---
128177
128191
  //
128178
- // The gateway `collection` service does NOT serve spec-generated uids on the
128179
- // v2 `/collections/:uid` path (404 RESOURCE_NOT_FOUND, live-probed). It DOES
128180
- // serve `GET /v3/collections/:id/export`, which returns the canonical v3
128181
- // collection IR (`{ data: { collection: { ... } } }`). That v3 IR is fed
128182
- // straight to `convertAndSplitV3Collection` — never round-tripped back to v2.
128183
- // The full owner-prefixed public UID must be sent verbatim; the bare model id
128184
- // is rejected on root mutation routes (403 FORBIDDEN) and must not be used
128185
- // here either, so the caller is responsible for passing the public UID.
128192
+ // This is the canonical full collection read used by
128193
+ // api-specification-service before whole-tree Sync updates. Collection v3
128194
+ // `/export` performs an expensive projection and has returned 500s for fresh
128195
+ // roots under provider load. The populated Sync read returns the complete
128196
+ // v2.1 model directly and is transformed in memory through the official
128197
+ // runtime.models V2 -> V3 pipeline before repository materialization.
128186
128198
  /**
128187
- * Fetch a collection's v3 IR through the gateway v3 export endpoint.
128188
- * Returns the `data.collection` object (canonical v3 shape with `$kind`
128189
- * discriminators, `items`, `variables`, `references`). Caller writes it to
128190
- * disk via `convertAndSplitV3Collection`. PMAK is never used for collection
128191
- * reads.
128199
+ * Fetch one complete v2.1 collection through a single retry-free populated
128200
+ * Sync GET. The response must be a v2.1 model for the requested public UID;
128201
+ * malformed or foreign snapshots fail closed. PMAK is never used.
128192
128202
  */
128193
128203
  async getCollection(uid) {
128194
128204
  const id = this.requireCollectionPublicUid(uid);
128195
128205
  const response = await this.gateway.requestJson({
128196
- service: "collection",
128206
+ service: "sync",
128197
128207
  method: "get",
128198
- path: `/v3/collections/${id}/export`
128199
- });
128200
- const data2 = this.asRecord(response?.data);
128201
- return data2?.collection ?? null;
128208
+ path: `/collection/${encodeURIComponent(id)}`,
128209
+ query: { populate: "true", format: "2.1.0", uid: "false" },
128210
+ retry: "none",
128211
+ fallback: "none"
128212
+ });
128213
+ const collection = this.asRecord(response?.data);
128214
+ const info2 = this.asRecord(collection?.info);
128215
+ const schema = typeof info2?.schema === "string" ? info2.schema.trim() : "";
128216
+ if (!collection || !info2 || typeof info2.name !== "string" || !info2.name.trim() || !/\/collection\/v2\.1\.0\/collection\.json\/?$/i.test(schema) || !Array.isArray(collection.item)) {
128217
+ throw new Error(
128218
+ "COLLECTION_SNAPSHOT_INVALID: populated Sync read did not return a complete v2.1 collection"
128219
+ );
128220
+ }
128221
+ try {
128222
+ assertV2CollectionModel(collection);
128223
+ } catch (error2) {
128224
+ throw new Error(
128225
+ "COLLECTION_SNAPSHOT_INVALID: populated Sync payload failed v2.1 schema validation",
128226
+ { cause: error2 }
128227
+ );
128228
+ }
128229
+ const observedId = String(info2._postman_id ?? collection.id ?? "").trim();
128230
+ if (!observedId || this.toModelId(observedId).toLowerCase() !== this.toModelId(id).toLowerCase()) {
128231
+ throw new Error(
128232
+ "COLLECTION_SNAPSHOT_INVALID: populated Sync read returned a different collection identity"
128233
+ );
128234
+ }
128235
+ return collection;
128202
128236
  }
128203
128237
  // --- mocks (service: mock) ---
128204
128238
  async createMock(workspaceId, name, collectionUid, environmentUid, requestedVisibility = "private") {
@@ -128266,79 +128300,160 @@ var PostmanGatewayAssetsClient = class {
128266
128300
  isAmbiguousTransportError(error2) {
128267
128301
  return this.isRetryableIdempotentWriteOutcome(error2);
128268
128302
  }
128269
- normalizeCollectionScripts(scripts) {
128270
- if (!Array.isArray(scripts)) return [];
128271
- return scripts.map((entry) => this.asRecord(entry)).filter((entry) => entry !== null);
128303
+ isRootPatchSnapshotConflict(error2) {
128304
+ return error2 instanceof HttpError && [400, 409, 412].includes(error2.status);
128305
+ }
128306
+ normalizeCollectionScripts(scripts, operation) {
128307
+ if (scripts === void 0 || scripts === null) return [];
128308
+ if (!Array.isArray(scripts)) {
128309
+ throw new Error(
128310
+ `PRIVATE_MOCK_AUTH_ROOT_INVALID: ${operation} returned a non-array scripts field.`
128311
+ );
128312
+ }
128313
+ const normalized = scripts.map((entry) => this.asRecord(entry));
128314
+ if (normalized.some((entry) => entry === null)) {
128315
+ throw new Error(
128316
+ `PRIVATE_MOCK_AUTH_ROOT_INVALID: ${operation} returned a non-object script entry.`
128317
+ );
128318
+ }
128319
+ return normalized;
128272
128320
  }
128273
128321
  rootScriptsIncludeManagedAuthHook(scripts) {
128274
128322
  return scripts.some((script) => isManagedPrivateMockAuthRootHook(script));
128275
128323
  }
128276
- buildPrivateMockRootScripts(existingScripts) {
128277
- const managedScript = {
128278
- type: PRIVATE_MOCK_AUTH_ROOT_TYPE,
128279
- code: PRIVATE_MOCK_AUTH_ROOT_SCRIPT,
128280
- language: "text/javascript"
128281
- };
128282
- return [...existingScripts, managedScript];
128324
+ expectedPrivateMockRootScripts(existingScripts) {
128325
+ return [
128326
+ ...existingScripts,
128327
+ {
128328
+ type: PRIVATE_MOCK_AUTH_ROOT_TYPE,
128329
+ code: PRIVATE_MOCK_AUTH_ROOT_SCRIPT,
128330
+ language: "text/javascript"
128331
+ }
128332
+ ];
128333
+ }
128334
+ assertAtMostOneManagedRootHook(scripts, operation) {
128335
+ const count = scripts.filter((script) => isManagedPrivateMockAuthRootHook(script)).length;
128336
+ if (count > 1) {
128337
+ throw new Error(
128338
+ `PRIVATE_MOCK_AUTH_ROOT_DUPLICATE: ${operation} contains ${count} exact managed hooks.`
128339
+ );
128340
+ }
128283
128341
  }
128284
128342
  async readCollectionRootScripts(collectionUid) {
128285
128343
  const id = this.requireCollectionPublicUid(collectionUid);
128286
- const response = await this.gateway.requestJson({
128287
- service: "collection",
128288
- method: "get",
128289
- path: `/v3/collections/${id}/export`
128290
- });
128344
+ const response = await this.gateway.requestJson(
128345
+ {
128346
+ service: "collection",
128347
+ method: "get",
128348
+ path: `/v3/collections/${id}`
128349
+ },
128350
+ // Root reconciliation is evidence, not a transient-success mechanism.
128351
+ // One response must prove the current state; never hide a 5xx by retrying.
128352
+ { retryTransient: false }
128353
+ );
128291
128354
  const data2 = this.asRecord(response?.data);
128292
- const collection = this.asRecord(data2?.collection);
128293
- if (!collection) {
128355
+ if (!data2) {
128294
128356
  throw new Error(
128295
- `PRIVATE_MOCK_AUTH_EXPORT_INVALID: Collection ${id} export did not return data.collection; refusing to configure private-mock root auth from an unexpected envelope.`
128357
+ `PRIVATE_MOCK_AUTH_ROOT_INVALID: Collection ${id} root read did not return data; refusing to configure private-mock root auth from an unexpected envelope.`
128296
128358
  );
128297
128359
  }
128298
- return this.normalizeCollectionScripts(collection.scripts);
128360
+ return this.normalizeCollectionScripts(data2.scripts, `Collection ${id} root read`);
128299
128361
  }
128300
- async patchCollectionRootScripts(collectionUid, scripts) {
128362
+ async patchCollectionRootScripts(collectionUid, existingScripts) {
128301
128363
  const id = this.requireCollectionPublicUid(collectionUid);
128302
- await this.gateway.requestJson(
128364
+ const expectedScripts = this.expectedPrivateMockRootScripts(existingScripts);
128365
+ const managedScript = expectedScripts.at(-1);
128366
+ const response = await this.gateway.requestJson(
128303
128367
  {
128304
128368
  service: "collection",
128305
128369
  method: "patch",
128306
128370
  path: `/v3/collections/${id}`,
128307
- body: [{ op: "add", path: "/scripts", value: scripts }]
128371
+ // Bootstrap creates these HTTP collections through Sync. Its current
128372
+ // collection-service wire projection materializes absent/null scripts
128373
+ // as `[]` before applying RFC 6902, so this pair is one atomic
128374
+ // compare-and-append. If that contract ever changes, `test` fails and
128375
+ // the append cannot overwrite or manufacture a scripts field.
128376
+ body: [
128377
+ { op: "test", path: "/scripts", value: existingScripts },
128378
+ { op: "add", path: "/scripts/-", value: managedScript }
128379
+ ]
128308
128380
  },
128309
128381
  { retryTransient: false }
128310
128382
  );
128383
+ const data2 = this.asRecord(response?.data);
128384
+ if (!data2) {
128385
+ throw new Error(
128386
+ `PRIVATE_MOCK_AUTH_ROOT_PATCH_INVALID: Collection ${id} root patch did not return data.`
128387
+ );
128388
+ }
128389
+ const scripts = this.normalizeCollectionScripts(data2.scripts, `Collection ${id} root patch`);
128390
+ if (!(0, import_node_util.isDeepStrictEqual)(scripts, expectedScripts)) {
128391
+ throw new Error(
128392
+ `PRIVATE_MOCK_AUTH_ROOT_PATCH_UNVERIFIED: Collection ${id} root patch response did not exactly preserve the prior scripts and append one managed hook.`
128393
+ );
128394
+ }
128395
+ return scripts;
128311
128396
  }
128312
128397
  /**
128313
128398
  * Add a secret-free runtime hook at the collection root. The PMAK value is
128314
128399
  * supplied only by the runner as a transient variable; this method persists
128315
128400
  * the variable name and header wiring, never the credential.
128316
128401
  */
128317
- async configurePrivateMockRuntimeAuth(collectionUid) {
128402
+ async configurePrivateMockRuntimeAuth(collectionUid, trustedRootScripts) {
128318
128403
  const cid = this.requireCollectionPublicUid(collectionUid);
128319
- const installFromFreshRoot = async (existingScripts) => {
128404
+ const installFromRootSnapshot = async (existingScripts) => {
128405
+ this.assertAtMostOneManagedRootHook(existingScripts, `Collection ${cid} root snapshot`);
128320
128406
  if (this.rootScriptsIncludeManagedAuthHook(existingScripts)) {
128321
128407
  return 0;
128322
128408
  }
128323
- const nextScripts = this.buildPrivateMockRootScripts(existingScripts);
128409
+ const expectedScripts = this.expectedPrivateMockRootScripts(existingScripts);
128324
128410
  try {
128325
- await this.patchCollectionRootScripts(cid, nextScripts);
128411
+ await this.patchCollectionRootScripts(cid, existingScripts);
128326
128412
  return 1;
128327
128413
  } catch (error2) {
128414
+ if (this.isRootPatchSnapshotConflict(error2)) {
128415
+ const currentScripts = await this.readCollectionRootScripts(cid);
128416
+ if ((0, import_node_util.isDeepStrictEqual)(currentScripts, expectedScripts)) {
128417
+ return 0;
128418
+ }
128419
+ throw error2;
128420
+ }
128328
128421
  if (!this.isAmbiguousTransportError(error2)) {
128329
128422
  throw error2;
128330
128423
  }
128331
128424
  const freshScripts = await this.readCollectionRootScripts(cid);
128332
- if (this.rootScriptsIncludeManagedAuthHook(freshScripts)) {
128425
+ if ((0, import_node_util.isDeepStrictEqual)(freshScripts, expectedScripts)) {
128333
128426
  return 1;
128334
128427
  }
128335
- const recomputed = this.buildPrivateMockRootScripts(freshScripts);
128336
- await this.patchCollectionRootScripts(cid, recomputed);
128337
- return 1;
128428
+ if (!(0, import_node_util.isDeepStrictEqual)(freshScripts, existingScripts)) {
128429
+ throw new Error(
128430
+ `PRIVATE_MOCK_AUTH_ROOT_RECONCILE_DIVERGED: Collection ${cid} root scripts changed while reconciling an ambiguous patch.`,
128431
+ { cause: error2 }
128432
+ );
128433
+ }
128434
+ try {
128435
+ await this.patchCollectionRootScripts(cid, existingScripts);
128436
+ return 1;
128437
+ } catch (resendError) {
128438
+ if (!this.isAmbiguousTransportError(resendError)) {
128439
+ throw resendError;
128440
+ }
128441
+ const finalScripts = await this.readCollectionRootScripts(cid);
128442
+ if ((0, import_node_util.isDeepStrictEqual)(finalScripts, expectedScripts)) {
128443
+ return 1;
128444
+ }
128445
+ if (!(0, import_node_util.isDeepStrictEqual)(finalScripts, existingScripts)) {
128446
+ throw new Error(
128447
+ `PRIVATE_MOCK_AUTH_ROOT_RECONCILE_DIVERGED: Collection ${cid} root scripts changed after the ambiguous patch resend.`,
128448
+ { cause: resendError }
128449
+ );
128450
+ }
128451
+ throw resendError;
128452
+ }
128338
128453
  }
128339
128454
  };
128340
- const scripts = await this.readCollectionRootScripts(cid);
128341
- return installFromFreshRoot(scripts);
128455
+ const scripts = trustedRootScripts === void 0 ? await this.readCollectionRootScripts(cid) : this.normalizeCollectionScripts(trustedRootScripts, `Trusted collection ${cid} root`);
128456
+ return installFromRootSnapshot(scripts);
128342
128457
  }
128343
128458
  async listMocks() {
128344
128459
  const response = await this.gateway.requestJson({
@@ -128676,6 +128791,45 @@ function cloneCollection(collection) {
128676
128791
  return cloneCollectionNodeIterative(collection);
128677
128792
  }
128678
128793
  }
128794
+ function cleanManagedItemAuthScriptsInPlace(item, stripManagedBlocks) {
128795
+ if (!stripManagedBlocks) {
128796
+ return 0;
128797
+ }
128798
+ const scripts = item.scripts;
128799
+ if (!Array.isArray(scripts)) {
128800
+ return 0;
128801
+ }
128802
+ let strippedBlocks = 0;
128803
+ const nextScripts = [];
128804
+ for (const script of scripts) {
128805
+ if (!script || typeof script !== "object" || Array.isArray(script)) {
128806
+ nextScripts.push(script);
128807
+ continue;
128808
+ }
128809
+ const record = script;
128810
+ if (String(record.type ?? "") !== "beforeRequest") {
128811
+ nextScripts.push(record);
128812
+ continue;
128813
+ }
128814
+ const originalCode = String(record.code ?? "");
128815
+ const cleaned = stripManagedItemAuthBlocks(originalCode);
128816
+ if (cleaned === originalCode) {
128817
+ nextScripts.push(record);
128818
+ continue;
128819
+ }
128820
+ strippedBlocks += countManagedItemAuthBlocks(originalCode);
128821
+ if (!cleaned) {
128822
+ continue;
128823
+ }
128824
+ nextScripts.push({ ...record, code: cleaned });
128825
+ }
128826
+ if (nextScripts.length === 0) {
128827
+ delete item.scripts;
128828
+ } else {
128829
+ item.scripts = nextScripts;
128830
+ }
128831
+ return strippedBlocks;
128832
+ }
128679
128833
  function isPrivateMockLegacyExportCleanupEnabled() {
128680
128834
  const value = String(process.env.POSTMAN_PRIVATE_MOCK_LEGACY_EXPORT_CLEANUP ?? "").trim().toLowerCase();
128681
128835
  return value !== "off";
@@ -128684,52 +128838,23 @@ function verifyPrivateMockRootHook(collection) {
128684
128838
  const scripts = asArray2(collection.scripts);
128685
128839
  return scripts.some((script) => isManagedPrivateMockAuthRootHook(script));
128686
128840
  }
128841
+ function applyPrivateMockArtifactNodeCleanup(node, options = {}) {
128842
+ const stripManagedBlocks = options.stripManagedBlocks ?? isPrivateMockLegacyExportCleanupEnabled();
128843
+ const cloned = cloneCollection(node);
128844
+ return {
128845
+ node: cloned,
128846
+ strippedBlocks: cleanManagedItemAuthScriptsInPlace(cloned, stripManagedBlocks)
128847
+ };
128848
+ }
128687
128849
  function applyPrivateMockExportCleanup(collection, options = {}) {
128688
128850
  const stripManagedBlocks = options.stripManagedBlocks ?? isPrivateMockLegacyExportCleanupEnabled();
128689
128851
  const cloned = cloneCollection(collection);
128690
128852
  let strippedBlocks = 0;
128691
- const visitItem = (item) => {
128692
- if (!stripManagedBlocks) {
128693
- return;
128694
- }
128695
- const scripts = item.scripts;
128696
- if (!Array.isArray(scripts)) {
128697
- return;
128698
- }
128699
- const nextScripts = [];
128700
- for (const script of scripts) {
128701
- if (!script || typeof script !== "object" || Array.isArray(script)) {
128702
- nextScripts.push(script);
128703
- continue;
128704
- }
128705
- const record = script;
128706
- if (String(record.type ?? "") !== "beforeRequest") {
128707
- nextScripts.push(record);
128708
- continue;
128709
- }
128710
- const originalCode = String(record.code ?? "");
128711
- const cleaned = stripManagedItemAuthBlocks(originalCode);
128712
- if (cleaned === originalCode) {
128713
- nextScripts.push(record);
128714
- continue;
128715
- }
128716
- strippedBlocks += countManagedItemAuthBlocks(originalCode);
128717
- if (!cleaned) {
128718
- continue;
128719
- }
128720
- nextScripts.push({ ...record, code: cleaned });
128721
- }
128722
- if (nextScripts.length === 0) {
128723
- delete item.scripts;
128724
- } else {
128725
- item.scripts = nextScripts;
128726
- }
128727
- };
128728
128853
  const walkItemsIteratively = (rootItems) => {
128729
128854
  const stack = [...rootItems].reverse();
128730
128855
  while (stack.length > 0) {
128731
128856
  const item = stack.pop();
128732
- visitItem(item);
128857
+ strippedBlocks += cleanManagedItemAuthScriptsInPlace(item, stripManagedBlocks);
128733
128858
  for (const key of ["items", "children"]) {
128734
128859
  const nested = asArray2(item[key]);
128735
128860
  for (let i3 = nested.length - 1; i3 >= 0; i3 -= 1) {
@@ -130897,7 +131022,7 @@ function validateCanonicalV3CollectionFile(relative4, bytes) {
130897
131022
  );
130898
131023
  }
130899
131024
  }
130900
- async function digestAndValidatePrebuiltCollectionTree(files) {
131025
+ async function digestAndValidatePrebuiltCollectionTree(files, onFile) {
130901
131026
  if (!files.some((file) => file.relative === ".resources/definition.yaml")) {
130902
131027
  failPrebuiltCollections("prebuilt collection tree is missing .resources/definition.yaml");
130903
131028
  }
@@ -130925,6 +131050,7 @@ async function digestAndValidatePrebuiltCollectionTree(files) {
130925
131050
  );
130926
131051
  }
130927
131052
  validateCanonicalV3CollectionFile(file.relative, bytes);
131053
+ onFile?.(file, bytes);
130928
131054
  }
130929
131055
  return hash.digest("hex");
130930
131056
  }
@@ -130972,14 +131098,156 @@ function tryReusePrebuiltCollection(options) {
130972
131098
  }
130973
131099
  return prepared.artifactDigest === entry.artifactDigest;
130974
131100
  }
131101
+ function normalizeTrustedPrivateMockRootScripts(value, role) {
131102
+ if (value === void 0 || value === null) {
131103
+ return [];
131104
+ }
131105
+ if (!Array.isArray(value) || value.some((entry) => !isPlainObject2(entry))) {
131106
+ failPrebuiltCollections(
131107
+ `prebuilt ${role} collection root scripts must be an array of mappings`
131108
+ );
131109
+ }
131110
+ return value.map((entry) => ({ ...entry }));
131111
+ }
131112
+ function serializePrivateMockArtifactNode(node) {
131113
+ return Buffer.from(dump(node, {
131114
+ lineWidth: -1,
131115
+ noRefs: true,
131116
+ sortKeys: false
131117
+ }));
131118
+ }
131119
+ async function planPrivateMockPrebuiltArtifact(options) {
131120
+ const { role, collectionId, expectedPath, artifactDir, prepared } = options;
131121
+ if (!prepared || !tryReusePrebuiltCollection({
131122
+ prepared,
131123
+ expectedPath,
131124
+ expectedCloudId: collectionId
131125
+ })) {
131126
+ return void 0;
131127
+ }
131128
+ const files = listPrebuiltCollectionTreeFiles(prepared.confinedPath, artifactDir);
131129
+ const updates = [];
131130
+ let rootScripts;
131131
+ const stripManagedBlocks = isPrivateMockLegacyExportCleanupEnabled();
131132
+ const currentDigest = await digestAndValidatePrebuiltCollectionTree(files, (file, original) => {
131133
+ let parsed;
131134
+ try {
131135
+ parsed = load(original.toString("utf8"));
131136
+ } catch (error2) {
131137
+ failPrebuiltCollections(
131138
+ `prebuilt ${role} collection changed to malformed YAML at ${file.relative} (${error2 instanceof Error ? error2.message : String(error2)})`
131139
+ );
131140
+ }
131141
+ if (!isPlainObject2(parsed)) {
131142
+ failPrebuiltCollections(
131143
+ `prebuilt ${role} collection file ${file.relative} must contain a YAML mapping`
131144
+ );
131145
+ }
131146
+ let nextNode;
131147
+ if (file.relative === ".resources/definition.yaml") {
131148
+ rootScripts = normalizeTrustedPrivateMockRootScripts(parsed.scripts, role);
131149
+ if (!rootScripts.some((script) => isManagedPrivateMockAuthRootHook(script))) {
131150
+ nextNode = {
131151
+ ...parsed,
131152
+ scripts: [
131153
+ ...rootScripts,
131154
+ {
131155
+ type: PRIVATE_MOCK_AUTH_ROOT_TYPE,
131156
+ code: PRIVATE_MOCK_AUTH_ROOT_SCRIPT,
131157
+ language: "text/javascript"
131158
+ }
131159
+ ]
131160
+ };
131161
+ }
131162
+ } else {
131163
+ const cleaned = applyPrivateMockArtifactNodeCleanup(parsed, { stripManagedBlocks });
131164
+ if (cleaned.strippedBlocks > 0) {
131165
+ nextNode = cleaned.node;
131166
+ }
131167
+ }
131168
+ if (nextNode) {
131169
+ const next = serializePrivateMockArtifactNode(nextNode);
131170
+ if (!next.equals(original)) {
131171
+ updates.push({
131172
+ absolute: file.absolute,
131173
+ relative: file.relative,
131174
+ original: Buffer.from(original),
131175
+ next
131176
+ });
131177
+ }
131178
+ }
131179
+ });
131180
+ if (currentDigest !== prepared.entry.artifactDigest) {
131181
+ failPrebuiltCollections(
131182
+ `prebuilt ${role} collection changed after initial validation; expected artifactDigest ${prepared.entry.artifactDigest}, observed ${currentDigest}`
131183
+ );
131184
+ }
131185
+ if (!rootScripts) {
131186
+ failPrebuiltCollections(`prebuilt ${role} collection root definition disappeared`);
131187
+ }
131188
+ return { prepared, rootScripts, updates };
131189
+ }
131190
+ function applyPrivateMockPrebuiltPlans(plans) {
131191
+ const updates = plans.flatMap((plan) => plan.updates);
131192
+ const seen = /* @__PURE__ */ new Set();
131193
+ for (const update of updates) {
131194
+ if (seen.has(update.absolute)) {
131195
+ failPrebuiltCollections(
131196
+ `private-mock artifact plan addressed ${update.relative} more than once`
131197
+ );
131198
+ }
131199
+ seen.add(update.absolute);
131200
+ let current2;
131201
+ try {
131202
+ const stat2 = (0, import_node_fs6.lstatSync)(update.absolute);
131203
+ if (stat2.isSymbolicLink() || !stat2.isFile()) {
131204
+ failPrebuiltCollections(
131205
+ `prebuilt collection tree file changed or became unsupported at ${update.relative}`
131206
+ );
131207
+ }
131208
+ current2 = (0, import_node_fs6.readFileSync)(update.absolute);
131209
+ } catch (error2) {
131210
+ if (error2 instanceof Error && error2.message.startsWith("CONTRACT_PREBUILT_COLLECTIONS_INVALID:")) {
131211
+ throw error2;
131212
+ }
131213
+ failPrebuiltCollections(
131214
+ `prebuilt collection tree file changed or became unreadable at ${update.relative}`
131215
+ );
131216
+ }
131217
+ if (!current2.equals(update.original)) {
131218
+ failPrebuiltCollections(
131219
+ `prebuilt collection tree file changed before private-mock reconciliation at ${update.relative}`
131220
+ );
131221
+ }
131222
+ }
131223
+ const written = [];
131224
+ try {
131225
+ for (const update of updates) {
131226
+ (0, import_node_fs6.writeFileSync)(update.absolute, update.next);
131227
+ written.push(update);
131228
+ }
131229
+ } catch (error2) {
131230
+ for (const update of written.reverse()) {
131231
+ try {
131232
+ (0, import_node_fs6.writeFileSync)(update.absolute, update.original);
131233
+ } catch {
131234
+ }
131235
+ }
131236
+ throw error2;
131237
+ }
131238
+ for (const plan of plans) {
131239
+ plan.prepared.privateMockArtifactReady = true;
131240
+ }
131241
+ }
130975
131242
  async function preparePrivateMockCloudCollection(role, collectionId, postman) {
130976
- const col = await postman.getCollection(collectionId);
130977
- const { collection } = applyPrivateMockExportCleanup(col, {
131243
+ const snapshot = await postman.getCollection(collectionId);
131244
+ const v3Collection = convertV2CollectionToV3Collection(snapshot);
131245
+ const { collection } = applyPrivateMockExportCleanup(v3Collection, {
130978
131246
  stripManagedBlocks: isPrivateMockLegacyExportCleanupEnabled()
130979
131247
  });
130980
131248
  if (!verifyPrivateMockRootHook(collection)) {
130981
131249
  throw new Error(
130982
- `PRIVATE_MOCK_AUTH_ROOT_UNVERIFIED: Managed root hook missing from exported ${role} collection ${collectionId}`
131250
+ `PRIVATE_MOCK_AUTH_ROOT_UNVERIFIED: Managed root hook missing from populated ${role} collection snapshot ${collectionId}`
130983
131251
  );
130984
131252
  }
130985
131253
  return collection;
@@ -131011,20 +131279,23 @@ async function runBoundedInOrder(items, width, worker) {
131011
131279
  async function acquireCollectionArtifact(options) {
131012
131280
  const { role, collectionId, dirName, collectionsDir, prebuiltByRole, postman, core, privateMockAuth = false } = options;
131013
131281
  const expectedPath = `${collectionsDir}/${dirName}`;
131014
- const forceCloudExport = privateMockAuth;
131015
131282
  const entry = prebuiltByRole.get(role);
131016
- if (entry && !forceCloudExport && tryReusePrebuiltCollection({
131283
+ const exactPrebuilt = Boolean(entry && tryReusePrebuiltCollection({
131017
131284
  prepared: entry,
131018
131285
  expectedPath,
131019
131286
  expectedCloudId: collectionId
131020
- })) {
131021
- core.info(`Reusing prebuilt ${role} collection tree at ${expectedPath} (artifactDigest match)`);
131287
+ }));
131288
+ const reusePrebuilt = exactPrebuilt && (!privateMockAuth || entry?.privateMockArtifactReady === true);
131289
+ if (reusePrebuilt) {
131290
+ core.info(
131291
+ privateMockAuth ? `Reusing locally reconciled private-mock ${role} collection tree at ${expectedPath} (source artifactDigest match)` : `Reusing prebuilt ${role} collection tree at ${expectedPath} (artifactDigest match)`
131292
+ );
131022
131293
  return { reusePrebuilt: true };
131023
131294
  }
131024
131295
  if (entry) {
131025
- core.info(forceCloudExport ? `Private mock requires cloud export for ${role} collection to reconcile managed root hook; exporting from cloud` : `Prebuilt ${role} collection entry present but did not exactly match; exporting from cloud`);
131296
+ core.info(privateMockAuth ? `Private mock has no locally reconciled exact prebuilt ${role} collection; reading populated cloud snapshot` : `Prebuilt ${role} collection entry present but did not exactly match; reading populated cloud snapshot`);
131026
131297
  }
131027
- const cloud = forceCloudExport ? await preparePrivateMockCloudCollection(role, collectionId, postman) : await postman.getCollection(collectionId);
131298
+ const cloud = privateMockAuth ? await preparePrivateMockCloudCollection(role, collectionId, postman) : await postman.getCollection(collectionId);
131028
131299
  return { reusePrebuilt: false, cloudCollection: cloud };
131029
131300
  }
131030
131301
  async function exportCollectionArtifact(options) {
@@ -131699,19 +131970,49 @@ async function runRepoSyncInner(inputs, dependencies, executionContext) {
131699
131970
  "PRIVATE_MOCK_RUNTIME_AUTH_UNAVAILABLE: The Postman client cannot configure runtime x-api-key injection."
131700
131971
  );
131701
131972
  }
131973
+ const collectionTargets = [
131974
+ { role: "baseline", collectionUid: inputs.baselineCollectionId, kind: "Baseline" },
131975
+ { role: "smoke", collectionUid: inputs.smokeCollectionId, kind: "Smoke" },
131976
+ { role: "contract", collectionUid: inputs.contractCollectionId, kind: "Contract" }
131977
+ ].filter((target) => Boolean(target.collectionUid));
131978
+ const prebuiltPlans = /* @__PURE__ */ new Map();
131979
+ for (const target of collectionTargets) {
131980
+ const plan = await planPrivateMockPrebuiltArtifact({
131981
+ role: target.role,
131982
+ collectionId: target.collectionUid,
131983
+ expectedPath: `${inputs.artifactDir}/collections/${getCollectionDirectoryName(target.kind, assetProjectName)}`,
131984
+ artifactDir: inputs.artifactDir,
131985
+ prepared: preparedPrebuiltCollections.get(target.role)
131986
+ });
131987
+ if (plan) {
131988
+ prebuiltPlans.set(target.role, plan);
131989
+ }
131990
+ }
131702
131991
  const configured = [];
131703
131992
  const configuredUids = /* @__PURE__ */ new Set();
131704
- for (const { role, collectionUid } of [
131705
- { role: "baseline", collectionUid: inputs.baselineCollectionId },
131706
- { role: "smoke", collectionUid: inputs.smokeCollectionId },
131707
- { role: "contract", collectionUid: inputs.contractCollectionId }
131708
- ]) {
131709
- if (!collectionUid || configuredUids.has(collectionUid)) {
131993
+ for (const { role, collectionUid } of collectionTargets) {
131994
+ if (configuredUids.has(collectionUid)) {
131710
131995
  continue;
131711
131996
  }
131712
131997
  configuredUids.add(collectionUid);
131713
131998
  try {
131714
- await dependencies.postman.configurePrivateMockRuntimeAuth(collectionUid);
131999
+ const plansForUid = collectionTargets.filter((target) => target.collectionUid === collectionUid).map((target) => prebuiltPlans.get(target.role)).filter((plan) => Boolean(plan));
132000
+ const trustedRootScripts = plansForUid[0]?.rootScripts;
132001
+ if (trustedRootScripts && plansForUid.some(
132002
+ (plan) => JSON.stringify(plan.rootScripts) !== JSON.stringify(trustedRootScripts)
132003
+ )) {
132004
+ failPrebuiltCollections(
132005
+ `roles sharing collection ${collectionUid} disagree on trusted root scripts`
132006
+ );
132007
+ }
132008
+ if (trustedRootScripts) {
132009
+ await dependencies.postman.configurePrivateMockRuntimeAuth(
132010
+ collectionUid,
132011
+ trustedRootScripts
132012
+ );
132013
+ } else {
132014
+ await dependencies.postman.configurePrivateMockRuntimeAuth(collectionUid);
132015
+ }
131715
132016
  configured.push(collectionUid);
131716
132017
  } catch (error2) {
131717
132018
  throw new Error(
@@ -131720,6 +132021,7 @@ async function runRepoSyncInner(inputs, dependencies, executionContext) {
131720
132021
  );
131721
132022
  }
131722
132023
  }
132024
+ applyPrivateMockPrebuiltPlans([...prebuiltPlans.values()]);
131723
132025
  if (configured.length > 0) {
131724
132026
  (dependencies.core.notice ?? dependencies.core.info)(
131725
132027
  `Private mock: installed a request hook on ${configured.length} collection(s) that sends the ${PRIVATE_MOCK_AUTH_VARIABLE2} variable as x-api-key, and only to *.mock.pstmn.io hosts. The generated CI workflow supplies it from the POSTMAN_API_KEY secret. For manual runs in the Postman app, set that variable to a key with access to the mock. No key is stored in the collection, environment, outputs, or repository.`
@@ -132238,9 +132540,9 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
132238
132540
  getEnvironment: gatewayAssets.getEnvironment.bind(gatewayAssets),
132239
132541
  updateEnvironment: gatewayAssets.updateEnvironment.bind(gatewayAssets),
132240
132542
  findEnvironmentByName: gatewayAssets.findEnvironmentByName.bind(gatewayAssets),
132241
- // Collection read via the v3 export endpoint — returns canonical v3 IR,
132242
- // written to disk by `convertAndSplitAnyCollection`. PMAK is never used for
132243
- // collection reads.
132543
+ // Collection read via one retry-free populated Sync GET — returns a full
132544
+ // v2.1 snapshot that the official runtime.models transform converts to v3
132545
+ // before repository materialization. PMAK is never used for reads.
132244
132546
  getCollection: gatewayAssets.getCollection.bind(gatewayAssets),
132245
132547
  // Mocks via the `mock` service, collection-based monitors via the `monitors`
132246
132548
  // service (jobTemplates). Both reference the collection by its full public