@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/action.cjs CHANGED
@@ -117719,6 +117719,7 @@ var path6 = __toESM(require("node:path"), 1);
117719
117719
  var V2 = __toESM(require_v2(), 1);
117720
117720
  var import_transforms = __toESM(require_transforms(), 1);
117721
117721
  var import_v3 = __toESM(require_node2(), 1);
117722
+ var V2_COLLECTION_MODEL = V2.Collection;
117722
117723
  function asArray(value) {
117723
117724
  return Array.isArray(value) ? value : [];
117724
117725
  }
@@ -117739,13 +117740,23 @@ function normalizeGraphqlRequests(node) {
117739
117740
  }
117740
117741
  }
117741
117742
  async function convertAndSplitCollection(v2Collection, outputDir) {
117742
- const model = V2.Collection;
117743
- const parsed = model.parse(v2Collection ?? {});
117744
- const v3 = (0, import_transforms.transform)(model, import_transforms.FormatVersion.V3, parsed);
117743
+ const v3 = convertV2CollectionToV3Collection(v2Collection);
117744
+ await writeSplitCollection(v3, outputDir);
117745
+ }
117746
+ function convertV2CollectionToV3Collection(v2Collection) {
117747
+ const parsed = V2_COLLECTION_MODEL.parse(v2Collection ?? {});
117748
+ const v3 = (0, import_transforms.transform)(
117749
+ V2_COLLECTION_MODEL,
117750
+ import_transforms.FormatVersion.V3,
117751
+ parsed
117752
+ );
117745
117753
  for (const item of asArray(v3.items)) {
117746
117754
  normalizeGraphqlRequests(item);
117747
117755
  }
117748
- await writeSplitCollection(v3, outputDir);
117756
+ return v3;
117757
+ }
117758
+ function assertV2CollectionModel(value) {
117759
+ V2_COLLECTION_MODEL.parse(value ?? {});
117749
117760
  }
117750
117761
  function normalizeV3ExportIds(node) {
117751
117762
  if (!node || typeof node !== "object") return;
@@ -121303,7 +121314,7 @@ var postmanRepoSyncActionContract = {
121303
121314
  allowedValues: ["full", "spec-only"]
121304
121315
  },
121305
121316
  "prebuilt-collections-json": {
121306
- 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.",
121317
+ 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.",
121307
121318
  required: false,
121308
121319
  default: ""
121309
121320
  },
@@ -121638,6 +121649,9 @@ var PostmanAssetsClient = class {
121638
121649
  }
121639
121650
  };
121640
121651
 
121652
+ // src/lib/postman/postman-gateway-assets-client.ts
121653
+ var import_node_util = require("node:util");
121654
+
121641
121655
  // node_modules/acorn/dist/acorn.mjs
121642
121656
  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];
121643
121657
  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];
@@ -127834,8 +127848,8 @@ var PostmanGatewayAssetsClient = class {
127834
127848
  }
127835
127849
  /**
127836
127850
  * Validate and normalize a collection public UID for routes that require the
127837
- * full owner-prefixed UID (`<owner>-<uuid>`, 6 hyphen segments): collection
127838
- * GET export, collection-root PATCH/read, and private-mock runtime auth.
127851
+ * full owner-prefixed UID (`<owner>-<uuid>`, 6 hyphen segments): populated
127852
+ * collection snapshot, collection-root PATCH/read, and private-mock runtime auth.
127839
127853
  *
127840
127854
  * - Trims once.
127841
127855
  * - Rejects empty values.
@@ -128147,32 +128161,52 @@ var PostmanGatewayAssetsClient = class {
128147
128161
  );
128148
128162
  return match?.uid ? { uid: match.uid, name: match.name } : null;
128149
128163
  }
128150
- // --- collection read (service: collection, v3 export) ---
128164
+ // --- collection read (service: sync, populated v2.1 snapshot) ---
128151
128165
  //
128152
- // The gateway `collection` service does NOT serve spec-generated uids on the
128153
- // v2 `/collections/:uid` path (404 RESOURCE_NOT_FOUND, live-probed). It DOES
128154
- // serve `GET /v3/collections/:id/export`, which returns the canonical v3
128155
- // collection IR (`{ data: { collection: { ... } } }`). That v3 IR is fed
128156
- // straight to `convertAndSplitV3Collection` — never round-tripped back to v2.
128157
- // The full owner-prefixed public UID must be sent verbatim; the bare model id
128158
- // is rejected on root mutation routes (403 FORBIDDEN) and must not be used
128159
- // here either, so the caller is responsible for passing the public UID.
128166
+ // This is the canonical full collection read used by
128167
+ // api-specification-service before whole-tree Sync updates. Collection v3
128168
+ // `/export` performs an expensive projection and has returned 500s for fresh
128169
+ // roots under provider load. The populated Sync read returns the complete
128170
+ // v2.1 model directly and is transformed in memory through the official
128171
+ // runtime.models V2 -> V3 pipeline before repository materialization.
128160
128172
  /**
128161
- * Fetch a collection's v3 IR through the gateway v3 export endpoint.
128162
- * Returns the `data.collection` object (canonical v3 shape with `$kind`
128163
- * discriminators, `items`, `variables`, `references`). Caller writes it to
128164
- * disk via `convertAndSplitV3Collection`. PMAK is never used for collection
128165
- * reads.
128173
+ * Fetch one complete v2.1 collection through a single retry-free populated
128174
+ * Sync GET. The response must be a v2.1 model for the requested public UID;
128175
+ * malformed or foreign snapshots fail closed. PMAK is never used.
128166
128176
  */
128167
128177
  async getCollection(uid) {
128168
128178
  const id = this.requireCollectionPublicUid(uid);
128169
128179
  const response = await this.gateway.requestJson({
128170
- service: "collection",
128180
+ service: "sync",
128171
128181
  method: "get",
128172
- path: `/v3/collections/${id}/export`
128173
- });
128174
- const data2 = this.asRecord(response?.data);
128175
- return data2?.collection ?? null;
128182
+ path: `/collection/${encodeURIComponent(id)}`,
128183
+ query: { populate: "true", format: "2.1.0", uid: "false" },
128184
+ retry: "none",
128185
+ fallback: "none"
128186
+ });
128187
+ const collection = this.asRecord(response?.data);
128188
+ const info2 = this.asRecord(collection?.info);
128189
+ const schema = typeof info2?.schema === "string" ? info2.schema.trim() : "";
128190
+ if (!collection || !info2 || typeof info2.name !== "string" || !info2.name.trim() || !/\/collection\/v2\.1\.0\/collection\.json\/?$/i.test(schema) || !Array.isArray(collection.item)) {
128191
+ throw new Error(
128192
+ "COLLECTION_SNAPSHOT_INVALID: populated Sync read did not return a complete v2.1 collection"
128193
+ );
128194
+ }
128195
+ try {
128196
+ assertV2CollectionModel(collection);
128197
+ } catch (error2) {
128198
+ throw new Error(
128199
+ "COLLECTION_SNAPSHOT_INVALID: populated Sync payload failed v2.1 schema validation",
128200
+ { cause: error2 }
128201
+ );
128202
+ }
128203
+ const observedId = String(info2._postman_id ?? collection.id ?? "").trim();
128204
+ if (!observedId || this.toModelId(observedId).toLowerCase() !== this.toModelId(id).toLowerCase()) {
128205
+ throw new Error(
128206
+ "COLLECTION_SNAPSHOT_INVALID: populated Sync read returned a different collection identity"
128207
+ );
128208
+ }
128209
+ return collection;
128176
128210
  }
128177
128211
  // --- mocks (service: mock) ---
128178
128212
  async createMock(workspaceId, name, collectionUid, environmentUid, requestedVisibility = "private") {
@@ -128240,79 +128274,160 @@ var PostmanGatewayAssetsClient = class {
128240
128274
  isAmbiguousTransportError(error2) {
128241
128275
  return this.isRetryableIdempotentWriteOutcome(error2);
128242
128276
  }
128243
- normalizeCollectionScripts(scripts) {
128244
- if (!Array.isArray(scripts)) return [];
128245
- return scripts.map((entry) => this.asRecord(entry)).filter((entry) => entry !== null);
128277
+ isRootPatchSnapshotConflict(error2) {
128278
+ return error2 instanceof HttpError && [400, 409, 412].includes(error2.status);
128279
+ }
128280
+ normalizeCollectionScripts(scripts, operation) {
128281
+ if (scripts === void 0 || scripts === null) return [];
128282
+ if (!Array.isArray(scripts)) {
128283
+ throw new Error(
128284
+ `PRIVATE_MOCK_AUTH_ROOT_INVALID: ${operation} returned a non-array scripts field.`
128285
+ );
128286
+ }
128287
+ const normalized = scripts.map((entry) => this.asRecord(entry));
128288
+ if (normalized.some((entry) => entry === null)) {
128289
+ throw new Error(
128290
+ `PRIVATE_MOCK_AUTH_ROOT_INVALID: ${operation} returned a non-object script entry.`
128291
+ );
128292
+ }
128293
+ return normalized;
128246
128294
  }
128247
128295
  rootScriptsIncludeManagedAuthHook(scripts) {
128248
128296
  return scripts.some((script) => isManagedPrivateMockAuthRootHook(script));
128249
128297
  }
128250
- buildPrivateMockRootScripts(existingScripts) {
128251
- const managedScript = {
128252
- type: PRIVATE_MOCK_AUTH_ROOT_TYPE,
128253
- code: PRIVATE_MOCK_AUTH_ROOT_SCRIPT,
128254
- language: "text/javascript"
128255
- };
128256
- return [...existingScripts, managedScript];
128298
+ expectedPrivateMockRootScripts(existingScripts) {
128299
+ return [
128300
+ ...existingScripts,
128301
+ {
128302
+ type: PRIVATE_MOCK_AUTH_ROOT_TYPE,
128303
+ code: PRIVATE_MOCK_AUTH_ROOT_SCRIPT,
128304
+ language: "text/javascript"
128305
+ }
128306
+ ];
128307
+ }
128308
+ assertAtMostOneManagedRootHook(scripts, operation) {
128309
+ const count = scripts.filter((script) => isManagedPrivateMockAuthRootHook(script)).length;
128310
+ if (count > 1) {
128311
+ throw new Error(
128312
+ `PRIVATE_MOCK_AUTH_ROOT_DUPLICATE: ${operation} contains ${count} exact managed hooks.`
128313
+ );
128314
+ }
128257
128315
  }
128258
128316
  async readCollectionRootScripts(collectionUid) {
128259
128317
  const id = this.requireCollectionPublicUid(collectionUid);
128260
- const response = await this.gateway.requestJson({
128261
- service: "collection",
128262
- method: "get",
128263
- path: `/v3/collections/${id}/export`
128264
- });
128318
+ const response = await this.gateway.requestJson(
128319
+ {
128320
+ service: "collection",
128321
+ method: "get",
128322
+ path: `/v3/collections/${id}`
128323
+ },
128324
+ // Root reconciliation is evidence, not a transient-success mechanism.
128325
+ // One response must prove the current state; never hide a 5xx by retrying.
128326
+ { retryTransient: false }
128327
+ );
128265
128328
  const data2 = this.asRecord(response?.data);
128266
- const collection = this.asRecord(data2?.collection);
128267
- if (!collection) {
128329
+ if (!data2) {
128268
128330
  throw new Error(
128269
- `PRIVATE_MOCK_AUTH_EXPORT_INVALID: Collection ${id} export did not return data.collection; refusing to configure private-mock root auth from an unexpected envelope.`
128331
+ `PRIVATE_MOCK_AUTH_ROOT_INVALID: Collection ${id} root read did not return data; refusing to configure private-mock root auth from an unexpected envelope.`
128270
128332
  );
128271
128333
  }
128272
- return this.normalizeCollectionScripts(collection.scripts);
128334
+ return this.normalizeCollectionScripts(data2.scripts, `Collection ${id} root read`);
128273
128335
  }
128274
- async patchCollectionRootScripts(collectionUid, scripts) {
128336
+ async patchCollectionRootScripts(collectionUid, existingScripts) {
128275
128337
  const id = this.requireCollectionPublicUid(collectionUid);
128276
- await this.gateway.requestJson(
128338
+ const expectedScripts = this.expectedPrivateMockRootScripts(existingScripts);
128339
+ const managedScript = expectedScripts.at(-1);
128340
+ const response = await this.gateway.requestJson(
128277
128341
  {
128278
128342
  service: "collection",
128279
128343
  method: "patch",
128280
128344
  path: `/v3/collections/${id}`,
128281
- body: [{ op: "add", path: "/scripts", value: scripts }]
128345
+ // Bootstrap creates these HTTP collections through Sync. Its current
128346
+ // collection-service wire projection materializes absent/null scripts
128347
+ // as `[]` before applying RFC 6902, so this pair is one atomic
128348
+ // compare-and-append. If that contract ever changes, `test` fails and
128349
+ // the append cannot overwrite or manufacture a scripts field.
128350
+ body: [
128351
+ { op: "test", path: "/scripts", value: existingScripts },
128352
+ { op: "add", path: "/scripts/-", value: managedScript }
128353
+ ]
128282
128354
  },
128283
128355
  { retryTransient: false }
128284
128356
  );
128357
+ const data2 = this.asRecord(response?.data);
128358
+ if (!data2) {
128359
+ throw new Error(
128360
+ `PRIVATE_MOCK_AUTH_ROOT_PATCH_INVALID: Collection ${id} root patch did not return data.`
128361
+ );
128362
+ }
128363
+ const scripts = this.normalizeCollectionScripts(data2.scripts, `Collection ${id} root patch`);
128364
+ if (!(0, import_node_util.isDeepStrictEqual)(scripts, expectedScripts)) {
128365
+ throw new Error(
128366
+ `PRIVATE_MOCK_AUTH_ROOT_PATCH_UNVERIFIED: Collection ${id} root patch response did not exactly preserve the prior scripts and append one managed hook.`
128367
+ );
128368
+ }
128369
+ return scripts;
128285
128370
  }
128286
128371
  /**
128287
128372
  * Add a secret-free runtime hook at the collection root. The PMAK value is
128288
128373
  * supplied only by the runner as a transient variable; this method persists
128289
128374
  * the variable name and header wiring, never the credential.
128290
128375
  */
128291
- async configurePrivateMockRuntimeAuth(collectionUid) {
128376
+ async configurePrivateMockRuntimeAuth(collectionUid, trustedRootScripts) {
128292
128377
  const cid = this.requireCollectionPublicUid(collectionUid);
128293
- const installFromFreshRoot = async (existingScripts) => {
128378
+ const installFromRootSnapshot = async (existingScripts) => {
128379
+ this.assertAtMostOneManagedRootHook(existingScripts, `Collection ${cid} root snapshot`);
128294
128380
  if (this.rootScriptsIncludeManagedAuthHook(existingScripts)) {
128295
128381
  return 0;
128296
128382
  }
128297
- const nextScripts = this.buildPrivateMockRootScripts(existingScripts);
128383
+ const expectedScripts = this.expectedPrivateMockRootScripts(existingScripts);
128298
128384
  try {
128299
- await this.patchCollectionRootScripts(cid, nextScripts);
128385
+ await this.patchCollectionRootScripts(cid, existingScripts);
128300
128386
  return 1;
128301
128387
  } catch (error2) {
128388
+ if (this.isRootPatchSnapshotConflict(error2)) {
128389
+ const currentScripts = await this.readCollectionRootScripts(cid);
128390
+ if ((0, import_node_util.isDeepStrictEqual)(currentScripts, expectedScripts)) {
128391
+ return 0;
128392
+ }
128393
+ throw error2;
128394
+ }
128302
128395
  if (!this.isAmbiguousTransportError(error2)) {
128303
128396
  throw error2;
128304
128397
  }
128305
128398
  const freshScripts = await this.readCollectionRootScripts(cid);
128306
- if (this.rootScriptsIncludeManagedAuthHook(freshScripts)) {
128399
+ if ((0, import_node_util.isDeepStrictEqual)(freshScripts, expectedScripts)) {
128307
128400
  return 1;
128308
128401
  }
128309
- const recomputed = this.buildPrivateMockRootScripts(freshScripts);
128310
- await this.patchCollectionRootScripts(cid, recomputed);
128311
- return 1;
128402
+ if (!(0, import_node_util.isDeepStrictEqual)(freshScripts, existingScripts)) {
128403
+ throw new Error(
128404
+ `PRIVATE_MOCK_AUTH_ROOT_RECONCILE_DIVERGED: Collection ${cid} root scripts changed while reconciling an ambiguous patch.`,
128405
+ { cause: error2 }
128406
+ );
128407
+ }
128408
+ try {
128409
+ await this.patchCollectionRootScripts(cid, existingScripts);
128410
+ return 1;
128411
+ } catch (resendError) {
128412
+ if (!this.isAmbiguousTransportError(resendError)) {
128413
+ throw resendError;
128414
+ }
128415
+ const finalScripts = await this.readCollectionRootScripts(cid);
128416
+ if ((0, import_node_util.isDeepStrictEqual)(finalScripts, expectedScripts)) {
128417
+ return 1;
128418
+ }
128419
+ if (!(0, import_node_util.isDeepStrictEqual)(finalScripts, existingScripts)) {
128420
+ throw new Error(
128421
+ `PRIVATE_MOCK_AUTH_ROOT_RECONCILE_DIVERGED: Collection ${cid} root scripts changed after the ambiguous patch resend.`,
128422
+ { cause: resendError }
128423
+ );
128424
+ }
128425
+ throw resendError;
128426
+ }
128312
128427
  }
128313
128428
  };
128314
- const scripts = await this.readCollectionRootScripts(cid);
128315
- return installFromFreshRoot(scripts);
128429
+ const scripts = trustedRootScripts === void 0 ? await this.readCollectionRootScripts(cid) : this.normalizeCollectionScripts(trustedRootScripts, `Trusted collection ${cid} root`);
128430
+ return installFromRootSnapshot(scripts);
128316
128431
  }
128317
128432
  async listMocks() {
128318
128433
  const response = await this.gateway.requestJson({
@@ -128650,6 +128765,45 @@ function cloneCollection(collection) {
128650
128765
  return cloneCollectionNodeIterative(collection);
128651
128766
  }
128652
128767
  }
128768
+ function cleanManagedItemAuthScriptsInPlace(item, stripManagedBlocks) {
128769
+ if (!stripManagedBlocks) {
128770
+ return 0;
128771
+ }
128772
+ const scripts = item.scripts;
128773
+ if (!Array.isArray(scripts)) {
128774
+ return 0;
128775
+ }
128776
+ let strippedBlocks = 0;
128777
+ const nextScripts = [];
128778
+ for (const script of scripts) {
128779
+ if (!script || typeof script !== "object" || Array.isArray(script)) {
128780
+ nextScripts.push(script);
128781
+ continue;
128782
+ }
128783
+ const record = script;
128784
+ if (String(record.type ?? "") !== "beforeRequest") {
128785
+ nextScripts.push(record);
128786
+ continue;
128787
+ }
128788
+ const originalCode = String(record.code ?? "");
128789
+ const cleaned = stripManagedItemAuthBlocks(originalCode);
128790
+ if (cleaned === originalCode) {
128791
+ nextScripts.push(record);
128792
+ continue;
128793
+ }
128794
+ strippedBlocks += countManagedItemAuthBlocks(originalCode);
128795
+ if (!cleaned) {
128796
+ continue;
128797
+ }
128798
+ nextScripts.push({ ...record, code: cleaned });
128799
+ }
128800
+ if (nextScripts.length === 0) {
128801
+ delete item.scripts;
128802
+ } else {
128803
+ item.scripts = nextScripts;
128804
+ }
128805
+ return strippedBlocks;
128806
+ }
128653
128807
  function isPrivateMockLegacyExportCleanupEnabled() {
128654
128808
  const value = String(process.env.POSTMAN_PRIVATE_MOCK_LEGACY_EXPORT_CLEANUP ?? "").trim().toLowerCase();
128655
128809
  return value !== "off";
@@ -128658,52 +128812,23 @@ function verifyPrivateMockRootHook(collection) {
128658
128812
  const scripts = asArray2(collection.scripts);
128659
128813
  return scripts.some((script) => isManagedPrivateMockAuthRootHook(script));
128660
128814
  }
128815
+ function applyPrivateMockArtifactNodeCleanup(node, options = {}) {
128816
+ const stripManagedBlocks = options.stripManagedBlocks ?? isPrivateMockLegacyExportCleanupEnabled();
128817
+ const cloned = cloneCollection(node);
128818
+ return {
128819
+ node: cloned,
128820
+ strippedBlocks: cleanManagedItemAuthScriptsInPlace(cloned, stripManagedBlocks)
128821
+ };
128822
+ }
128661
128823
  function applyPrivateMockExportCleanup(collection, options = {}) {
128662
128824
  const stripManagedBlocks = options.stripManagedBlocks ?? isPrivateMockLegacyExportCleanupEnabled();
128663
128825
  const cloned = cloneCollection(collection);
128664
128826
  let strippedBlocks = 0;
128665
- const visitItem = (item) => {
128666
- if (!stripManagedBlocks) {
128667
- return;
128668
- }
128669
- const scripts = item.scripts;
128670
- if (!Array.isArray(scripts)) {
128671
- return;
128672
- }
128673
- const nextScripts = [];
128674
- for (const script of scripts) {
128675
- if (!script || typeof script !== "object" || Array.isArray(script)) {
128676
- nextScripts.push(script);
128677
- continue;
128678
- }
128679
- const record = script;
128680
- if (String(record.type ?? "") !== "beforeRequest") {
128681
- nextScripts.push(record);
128682
- continue;
128683
- }
128684
- const originalCode = String(record.code ?? "");
128685
- const cleaned = stripManagedItemAuthBlocks(originalCode);
128686
- if (cleaned === originalCode) {
128687
- nextScripts.push(record);
128688
- continue;
128689
- }
128690
- strippedBlocks += countManagedItemAuthBlocks(originalCode);
128691
- if (!cleaned) {
128692
- continue;
128693
- }
128694
- nextScripts.push({ ...record, code: cleaned });
128695
- }
128696
- if (nextScripts.length === 0) {
128697
- delete item.scripts;
128698
- } else {
128699
- item.scripts = nextScripts;
128700
- }
128701
- };
128702
128827
  const walkItemsIteratively = (rootItems) => {
128703
128828
  const stack = [...rootItems].reverse();
128704
128829
  while (stack.length > 0) {
128705
128830
  const item = stack.pop();
128706
- visitItem(item);
128831
+ strippedBlocks += cleanManagedItemAuthScriptsInPlace(item, stripManagedBlocks);
128707
128832
  for (const key of ["items", "children"]) {
128708
128833
  const nested = asArray2(item[key]);
128709
128834
  for (let i3 = nested.length - 1; i3 >= 0; i3 -= 1) {
@@ -130871,7 +130996,7 @@ function validateCanonicalV3CollectionFile(relative4, bytes) {
130871
130996
  );
130872
130997
  }
130873
130998
  }
130874
- async function digestAndValidatePrebuiltCollectionTree(files) {
130999
+ async function digestAndValidatePrebuiltCollectionTree(files, onFile) {
130875
131000
  if (!files.some((file) => file.relative === ".resources/definition.yaml")) {
130876
131001
  failPrebuiltCollections("prebuilt collection tree is missing .resources/definition.yaml");
130877
131002
  }
@@ -130899,6 +131024,7 @@ async function digestAndValidatePrebuiltCollectionTree(files) {
130899
131024
  );
130900
131025
  }
130901
131026
  validateCanonicalV3CollectionFile(file.relative, bytes);
131027
+ onFile?.(file, bytes);
130902
131028
  }
130903
131029
  return hash.digest("hex");
130904
131030
  }
@@ -130946,14 +131072,156 @@ function tryReusePrebuiltCollection(options) {
130946
131072
  }
130947
131073
  return prepared.artifactDigest === entry.artifactDigest;
130948
131074
  }
131075
+ function normalizeTrustedPrivateMockRootScripts(value, role) {
131076
+ if (value === void 0 || value === null) {
131077
+ return [];
131078
+ }
131079
+ if (!Array.isArray(value) || value.some((entry) => !isPlainObject2(entry))) {
131080
+ failPrebuiltCollections(
131081
+ `prebuilt ${role} collection root scripts must be an array of mappings`
131082
+ );
131083
+ }
131084
+ return value.map((entry) => ({ ...entry }));
131085
+ }
131086
+ function serializePrivateMockArtifactNode(node) {
131087
+ return Buffer.from(dump(node, {
131088
+ lineWidth: -1,
131089
+ noRefs: true,
131090
+ sortKeys: false
131091
+ }));
131092
+ }
131093
+ async function planPrivateMockPrebuiltArtifact(options) {
131094
+ const { role, collectionId, expectedPath, artifactDir, prepared } = options;
131095
+ if (!prepared || !tryReusePrebuiltCollection({
131096
+ prepared,
131097
+ expectedPath,
131098
+ expectedCloudId: collectionId
131099
+ })) {
131100
+ return void 0;
131101
+ }
131102
+ const files = listPrebuiltCollectionTreeFiles(prepared.confinedPath, artifactDir);
131103
+ const updates = [];
131104
+ let rootScripts;
131105
+ const stripManagedBlocks = isPrivateMockLegacyExportCleanupEnabled();
131106
+ const currentDigest = await digestAndValidatePrebuiltCollectionTree(files, (file, original) => {
131107
+ let parsed;
131108
+ try {
131109
+ parsed = load(original.toString("utf8"));
131110
+ } catch (error2) {
131111
+ failPrebuiltCollections(
131112
+ `prebuilt ${role} collection changed to malformed YAML at ${file.relative} (${error2 instanceof Error ? error2.message : String(error2)})`
131113
+ );
131114
+ }
131115
+ if (!isPlainObject2(parsed)) {
131116
+ failPrebuiltCollections(
131117
+ `prebuilt ${role} collection file ${file.relative} must contain a YAML mapping`
131118
+ );
131119
+ }
131120
+ let nextNode;
131121
+ if (file.relative === ".resources/definition.yaml") {
131122
+ rootScripts = normalizeTrustedPrivateMockRootScripts(parsed.scripts, role);
131123
+ if (!rootScripts.some((script) => isManagedPrivateMockAuthRootHook(script))) {
131124
+ nextNode = {
131125
+ ...parsed,
131126
+ scripts: [
131127
+ ...rootScripts,
131128
+ {
131129
+ type: PRIVATE_MOCK_AUTH_ROOT_TYPE,
131130
+ code: PRIVATE_MOCK_AUTH_ROOT_SCRIPT,
131131
+ language: "text/javascript"
131132
+ }
131133
+ ]
131134
+ };
131135
+ }
131136
+ } else {
131137
+ const cleaned = applyPrivateMockArtifactNodeCleanup(parsed, { stripManagedBlocks });
131138
+ if (cleaned.strippedBlocks > 0) {
131139
+ nextNode = cleaned.node;
131140
+ }
131141
+ }
131142
+ if (nextNode) {
131143
+ const next = serializePrivateMockArtifactNode(nextNode);
131144
+ if (!next.equals(original)) {
131145
+ updates.push({
131146
+ absolute: file.absolute,
131147
+ relative: file.relative,
131148
+ original: Buffer.from(original),
131149
+ next
131150
+ });
131151
+ }
131152
+ }
131153
+ });
131154
+ if (currentDigest !== prepared.entry.artifactDigest) {
131155
+ failPrebuiltCollections(
131156
+ `prebuilt ${role} collection changed after initial validation; expected artifactDigest ${prepared.entry.artifactDigest}, observed ${currentDigest}`
131157
+ );
131158
+ }
131159
+ if (!rootScripts) {
131160
+ failPrebuiltCollections(`prebuilt ${role} collection root definition disappeared`);
131161
+ }
131162
+ return { prepared, rootScripts, updates };
131163
+ }
131164
+ function applyPrivateMockPrebuiltPlans(plans) {
131165
+ const updates = plans.flatMap((plan) => plan.updates);
131166
+ const seen = /* @__PURE__ */ new Set();
131167
+ for (const update of updates) {
131168
+ if (seen.has(update.absolute)) {
131169
+ failPrebuiltCollections(
131170
+ `private-mock artifact plan addressed ${update.relative} more than once`
131171
+ );
131172
+ }
131173
+ seen.add(update.absolute);
131174
+ let current2;
131175
+ try {
131176
+ const stat2 = (0, import_node_fs6.lstatSync)(update.absolute);
131177
+ if (stat2.isSymbolicLink() || !stat2.isFile()) {
131178
+ failPrebuiltCollections(
131179
+ `prebuilt collection tree file changed or became unsupported at ${update.relative}`
131180
+ );
131181
+ }
131182
+ current2 = (0, import_node_fs6.readFileSync)(update.absolute);
131183
+ } catch (error2) {
131184
+ if (error2 instanceof Error && error2.message.startsWith("CONTRACT_PREBUILT_COLLECTIONS_INVALID:")) {
131185
+ throw error2;
131186
+ }
131187
+ failPrebuiltCollections(
131188
+ `prebuilt collection tree file changed or became unreadable at ${update.relative}`
131189
+ );
131190
+ }
131191
+ if (!current2.equals(update.original)) {
131192
+ failPrebuiltCollections(
131193
+ `prebuilt collection tree file changed before private-mock reconciliation at ${update.relative}`
131194
+ );
131195
+ }
131196
+ }
131197
+ const written = [];
131198
+ try {
131199
+ for (const update of updates) {
131200
+ (0, import_node_fs6.writeFileSync)(update.absolute, update.next);
131201
+ written.push(update);
131202
+ }
131203
+ } catch (error2) {
131204
+ for (const update of written.reverse()) {
131205
+ try {
131206
+ (0, import_node_fs6.writeFileSync)(update.absolute, update.original);
131207
+ } catch {
131208
+ }
131209
+ }
131210
+ throw error2;
131211
+ }
131212
+ for (const plan of plans) {
131213
+ plan.prepared.privateMockArtifactReady = true;
131214
+ }
131215
+ }
130949
131216
  async function preparePrivateMockCloudCollection(role, collectionId, postman) {
130950
- const col = await postman.getCollection(collectionId);
130951
- const { collection } = applyPrivateMockExportCleanup(col, {
131217
+ const snapshot = await postman.getCollection(collectionId);
131218
+ const v3Collection = convertV2CollectionToV3Collection(snapshot);
131219
+ const { collection } = applyPrivateMockExportCleanup(v3Collection, {
130952
131220
  stripManagedBlocks: isPrivateMockLegacyExportCleanupEnabled()
130953
131221
  });
130954
131222
  if (!verifyPrivateMockRootHook(collection)) {
130955
131223
  throw new Error(
130956
- `PRIVATE_MOCK_AUTH_ROOT_UNVERIFIED: Managed root hook missing from exported ${role} collection ${collectionId}`
131224
+ `PRIVATE_MOCK_AUTH_ROOT_UNVERIFIED: Managed root hook missing from populated ${role} collection snapshot ${collectionId}`
130957
131225
  );
130958
131226
  }
130959
131227
  return collection;
@@ -130985,20 +131253,23 @@ async function runBoundedInOrder(items, width, worker) {
130985
131253
  async function acquireCollectionArtifact(options) {
130986
131254
  const { role, collectionId, dirName, collectionsDir, prebuiltByRole, postman, core, privateMockAuth = false } = options;
130987
131255
  const expectedPath = `${collectionsDir}/${dirName}`;
130988
- const forceCloudExport = privateMockAuth;
130989
131256
  const entry = prebuiltByRole.get(role);
130990
- if (entry && !forceCloudExport && tryReusePrebuiltCollection({
131257
+ const exactPrebuilt = Boolean(entry && tryReusePrebuiltCollection({
130991
131258
  prepared: entry,
130992
131259
  expectedPath,
130993
131260
  expectedCloudId: collectionId
130994
- })) {
130995
- core.info(`Reusing prebuilt ${role} collection tree at ${expectedPath} (artifactDigest match)`);
131261
+ }));
131262
+ const reusePrebuilt = exactPrebuilt && (!privateMockAuth || entry?.privateMockArtifactReady === true);
131263
+ if (reusePrebuilt) {
131264
+ core.info(
131265
+ privateMockAuth ? `Reusing locally reconciled private-mock ${role} collection tree at ${expectedPath} (source artifactDigest match)` : `Reusing prebuilt ${role} collection tree at ${expectedPath} (artifactDigest match)`
131266
+ );
130996
131267
  return { reusePrebuilt: true };
130997
131268
  }
130998
131269
  if (entry) {
130999
- 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`);
131270
+ 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`);
131000
131271
  }
131001
- const cloud = forceCloudExport ? await preparePrivateMockCloudCollection(role, collectionId, postman) : await postman.getCollection(collectionId);
131272
+ const cloud = privateMockAuth ? await preparePrivateMockCloudCollection(role, collectionId, postman) : await postman.getCollection(collectionId);
131002
131273
  return { reusePrebuilt: false, cloudCollection: cloud };
131003
131274
  }
131004
131275
  async function exportCollectionArtifact(options) {
@@ -131673,19 +131944,49 @@ async function runRepoSyncInner(inputs, dependencies, executionContext) {
131673
131944
  "PRIVATE_MOCK_RUNTIME_AUTH_UNAVAILABLE: The Postman client cannot configure runtime x-api-key injection."
131674
131945
  );
131675
131946
  }
131947
+ const collectionTargets = [
131948
+ { role: "baseline", collectionUid: inputs.baselineCollectionId, kind: "Baseline" },
131949
+ { role: "smoke", collectionUid: inputs.smokeCollectionId, kind: "Smoke" },
131950
+ { role: "contract", collectionUid: inputs.contractCollectionId, kind: "Contract" }
131951
+ ].filter((target) => Boolean(target.collectionUid));
131952
+ const prebuiltPlans = /* @__PURE__ */ new Map();
131953
+ for (const target of collectionTargets) {
131954
+ const plan = await planPrivateMockPrebuiltArtifact({
131955
+ role: target.role,
131956
+ collectionId: target.collectionUid,
131957
+ expectedPath: `${inputs.artifactDir}/collections/${getCollectionDirectoryName(target.kind, assetProjectName)}`,
131958
+ artifactDir: inputs.artifactDir,
131959
+ prepared: preparedPrebuiltCollections.get(target.role)
131960
+ });
131961
+ if (plan) {
131962
+ prebuiltPlans.set(target.role, plan);
131963
+ }
131964
+ }
131676
131965
  const configured = [];
131677
131966
  const configuredUids = /* @__PURE__ */ new Set();
131678
- for (const { role, collectionUid } of [
131679
- { role: "baseline", collectionUid: inputs.baselineCollectionId },
131680
- { role: "smoke", collectionUid: inputs.smokeCollectionId },
131681
- { role: "contract", collectionUid: inputs.contractCollectionId }
131682
- ]) {
131683
- if (!collectionUid || configuredUids.has(collectionUid)) {
131967
+ for (const { role, collectionUid } of collectionTargets) {
131968
+ if (configuredUids.has(collectionUid)) {
131684
131969
  continue;
131685
131970
  }
131686
131971
  configuredUids.add(collectionUid);
131687
131972
  try {
131688
- await dependencies.postman.configurePrivateMockRuntimeAuth(collectionUid);
131973
+ const plansForUid = collectionTargets.filter((target) => target.collectionUid === collectionUid).map((target) => prebuiltPlans.get(target.role)).filter((plan) => Boolean(plan));
131974
+ const trustedRootScripts = plansForUid[0]?.rootScripts;
131975
+ if (trustedRootScripts && plansForUid.some(
131976
+ (plan) => JSON.stringify(plan.rootScripts) !== JSON.stringify(trustedRootScripts)
131977
+ )) {
131978
+ failPrebuiltCollections(
131979
+ `roles sharing collection ${collectionUid} disagree on trusted root scripts`
131980
+ );
131981
+ }
131982
+ if (trustedRootScripts) {
131983
+ await dependencies.postman.configurePrivateMockRuntimeAuth(
131984
+ collectionUid,
131985
+ trustedRootScripts
131986
+ );
131987
+ } else {
131988
+ await dependencies.postman.configurePrivateMockRuntimeAuth(collectionUid);
131989
+ }
131689
131990
  configured.push(collectionUid);
131690
131991
  } catch (error2) {
131691
131992
  throw new Error(
@@ -131694,6 +131995,7 @@ async function runRepoSyncInner(inputs, dependencies, executionContext) {
131694
131995
  );
131695
131996
  }
131696
131997
  }
131998
+ applyPrivateMockPrebuiltPlans([...prebuiltPlans.values()]);
131697
131999
  if (configured.length > 0) {
131698
132000
  (dependencies.core.notice ?? dependencies.core.info)(
131699
132001
  `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.`
@@ -132212,9 +132514,9 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
132212
132514
  getEnvironment: gatewayAssets.getEnvironment.bind(gatewayAssets),
132213
132515
  updateEnvironment: gatewayAssets.updateEnvironment.bind(gatewayAssets),
132214
132516
  findEnvironmentByName: gatewayAssets.findEnvironmentByName.bind(gatewayAssets),
132215
- // Collection read via the v3 export endpoint — returns canonical v3 IR,
132216
- // written to disk by `convertAndSplitAnyCollection`. PMAK is never used for
132217
- // collection reads.
132517
+ // Collection read via one retry-free populated Sync GET — returns a full
132518
+ // v2.1 snapshot that the official runtime.models transform converts to v3
132519
+ // before repository materialization. PMAK is never used for reads.
132218
132520
  getCollection: gatewayAssets.getCollection.bind(gatewayAssets),
132219
132521
  // Mocks via the `mock` service, collection-based monitors via the `monitors`
132220
132522
  // service (jobTemplates). Both reference the collection by its full public