@postman-cs/onboarding-repo-sync 2.10.8 → 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/README.md CHANGED
@@ -169,7 +169,7 @@ with:
169
169
  | `smoke-collection-id` | Smoke collection ID used for monitor creation. | no | |
170
170
  | `contract-collection-id` | Contract collection ID used for exported artifacts. | no | |
171
171
  | `onboarding-scope` | Onboarding scope. Use full for the complete pipeline or spec-only for repository linking and workspace/spec state only. | no | `full` |
172
- | `prebuilt-collections-json` | 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. | no | `""` |
172
+ | `prebuilt-collections-json` | 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. | no | `""` |
173
173
  | `collection-sync-mode` | Collection sync lifecycle mode (refresh or version). | no | `refresh` |
174
174
  | `spec-sync-mode` | Spec sync lifecycle mode (update or version). | no | `update` |
175
175
  | `release-label` | Optional release label used for versioned naming. | no | |
package/action.yml CHANGED
@@ -43,7 +43,7 @@ inputs:
43
43
  required: false
44
44
  default: full
45
45
  prebuilt-collections-json:
46
- 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.
46
+ 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.
47
47
  required: false
48
48
  default: ''
49
49
  collection-sync-mode:
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
  },
@@ -127837,8 +127848,8 @@ var PostmanGatewayAssetsClient = class {
127837
127848
  }
127838
127849
  /**
127839
127850
  * Validate and normalize a collection public UID for routes that require the
127840
- * full owner-prefixed UID (`<owner>-<uuid>`, 6 hyphen segments): collection
127841
- * 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.
127842
127853
  *
127843
127854
  * - Trims once.
127844
127855
  * - Rejects empty values.
@@ -128150,32 +128161,52 @@ var PostmanGatewayAssetsClient = class {
128150
128161
  );
128151
128162
  return match?.uid ? { uid: match.uid, name: match.name } : null;
128152
128163
  }
128153
- // --- collection read (service: collection, v3 export) ---
128164
+ // --- collection read (service: sync, populated v2.1 snapshot) ---
128154
128165
  //
128155
- // The gateway `collection` service does NOT serve spec-generated uids on the
128156
- // v2 `/collections/:uid` path (404 RESOURCE_NOT_FOUND, live-probed). It DOES
128157
- // serve `GET /v3/collections/:id/export`, which returns the canonical v3
128158
- // collection IR (`{ data: { collection: { ... } } }`). That v3 IR is fed
128159
- // straight to `convertAndSplitV3Collection` never round-tripped back to v2.
128160
- // The full owner-prefixed public UID must be sent verbatim; the bare model id
128161
- // is rejected on root mutation routes (403 FORBIDDEN) and must not be used
128162
- // 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.
128163
128172
  /**
128164
- * Fetch a collection's v3 IR through the gateway v3 export endpoint.
128165
- * Returns the `data.collection` object (canonical v3 shape with `$kind`
128166
- * discriminators, `items`, `variables`, `references`). Caller writes it to
128167
- * disk via `convertAndSplitV3Collection`. PMAK is never used for collection
128168
- * 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.
128169
128176
  */
128170
128177
  async getCollection(uid) {
128171
128178
  const id = this.requireCollectionPublicUid(uid);
128172
128179
  const response = await this.gateway.requestJson({
128173
- service: "collection",
128180
+ service: "sync",
128174
128181
  method: "get",
128175
- path: `/v3/collections/${id}/export`
128176
- });
128177
- const data2 = this.asRecord(response?.data);
128178
- 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;
128179
128210
  }
128180
128211
  // --- mocks (service: mock) ---
128181
128212
  async createMock(workspaceId, name, collectionUid, environmentUid, requestedVisibility = "private") {
@@ -131183,13 +131214,14 @@ function applyPrivateMockPrebuiltPlans(plans) {
131183
131214
  }
131184
131215
  }
131185
131216
  async function preparePrivateMockCloudCollection(role, collectionId, postman) {
131186
- const col = await postman.getCollection(collectionId);
131187
- const { collection } = applyPrivateMockExportCleanup(col, {
131217
+ const snapshot = await postman.getCollection(collectionId);
131218
+ const v3Collection = convertV2CollectionToV3Collection(snapshot);
131219
+ const { collection } = applyPrivateMockExportCleanup(v3Collection, {
131188
131220
  stripManagedBlocks: isPrivateMockLegacyExportCleanupEnabled()
131189
131221
  });
131190
131222
  if (!verifyPrivateMockRootHook(collection)) {
131191
131223
  throw new Error(
131192
- `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}`
131193
131225
  );
131194
131226
  }
131195
131227
  return collection;
@@ -131235,7 +131267,7 @@ async function acquireCollectionArtifact(options) {
131235
131267
  return { reusePrebuilt: true };
131236
131268
  }
131237
131269
  if (entry) {
131238
- core.info(privateMockAuth ? `Private mock has no locally reconciled exact prebuilt ${role} collection; 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`);
131239
131271
  }
131240
131272
  const cloud = privateMockAuth ? await preparePrivateMockCloudCollection(role, collectionId, postman) : await postman.getCollection(collectionId);
131241
131273
  return { reusePrebuilt: false, cloudCollection: cloud };
@@ -132482,9 +132514,9 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
132482
132514
  getEnvironment: gatewayAssets.getEnvironment.bind(gatewayAssets),
132483
132515
  updateEnvironment: gatewayAssets.updateEnvironment.bind(gatewayAssets),
132484
132516
  findEnvironmentByName: gatewayAssets.findEnvironmentByName.bind(gatewayAssets),
132485
- // Collection read via the v3 export endpoint — returns canonical v3 IR,
132486
- // written to disk by `convertAndSplitAnyCollection`. PMAK is never used for
132487
- // 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.
132488
132520
  getCollection: gatewayAssets.getCollection.bind(gatewayAssets),
132489
132521
  // Mocks via the `mock` service, collection-based monitors via the `monitors`
132490
132522
  // service (jobTemplates). Both reference the collection by its full public
package/dist/cli.cjs CHANGED
@@ -115824,6 +115824,7 @@ var path = __toESM(require("node:path"), 1);
115824
115824
  var V2 = __toESM(require_v2(), 1);
115825
115825
  var import_transforms = __toESM(require_transforms(), 1);
115826
115826
  var import_v3 = __toESM(require_node2(), 1);
115827
+ var V2_COLLECTION_MODEL = V2.Collection;
115827
115828
  function asArray(value) {
115828
115829
  return Array.isArray(value) ? value : [];
115829
115830
  }
@@ -115844,13 +115845,23 @@ function normalizeGraphqlRequests(node) {
115844
115845
  }
115845
115846
  }
115846
115847
  async function convertAndSplitCollection(v2Collection, outputDir) {
115847
- const model = V2.Collection;
115848
- const parsed = model.parse(v2Collection ?? {});
115849
- const v3 = (0, import_transforms.transform)(model, import_transforms.FormatVersion.V3, parsed);
115848
+ const v3 = convertV2CollectionToV3Collection(v2Collection);
115849
+ await writeSplitCollection(v3, outputDir);
115850
+ }
115851
+ function convertV2CollectionToV3Collection(v2Collection) {
115852
+ const parsed = V2_COLLECTION_MODEL.parse(v2Collection ?? {});
115853
+ const v3 = (0, import_transforms.transform)(
115854
+ V2_COLLECTION_MODEL,
115855
+ import_transforms.FormatVersion.V3,
115856
+ parsed
115857
+ );
115850
115858
  for (const item of asArray(v3.items)) {
115851
115859
  normalizeGraphqlRequests(item);
115852
115860
  }
115853
- await writeSplitCollection(v3, outputDir);
115861
+ return v3;
115862
+ }
115863
+ function assertV2CollectionModel(value) {
115864
+ V2_COLLECTION_MODEL.parse(value ?? {});
115854
115865
  }
115855
115866
  function normalizeV3ExportIds(node) {
115856
115867
  if (!node || typeof node !== "object") return;
@@ -119408,7 +119419,7 @@ var postmanRepoSyncActionContract = {
119408
119419
  allowedValues: ["full", "spec-only"]
119409
119420
  },
119410
119421
  "prebuilt-collections-json": {
119411
- 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.",
119422
+ 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.",
119412
119423
  required: false,
119413
119424
  default: ""
119414
119425
  },
@@ -125942,8 +125953,8 @@ var PostmanGatewayAssetsClient = class {
125942
125953
  }
125943
125954
  /**
125944
125955
  * Validate and normalize a collection public UID for routes that require the
125945
- * full owner-prefixed UID (`<owner>-<uuid>`, 6 hyphen segments): collection
125946
- * GET export, collection-root PATCH/read, and private-mock runtime auth.
125956
+ * full owner-prefixed UID (`<owner>-<uuid>`, 6 hyphen segments): populated
125957
+ * collection snapshot, collection-root PATCH/read, and private-mock runtime auth.
125947
125958
  *
125948
125959
  * - Trims once.
125949
125960
  * - Rejects empty values.
@@ -126255,32 +126266,52 @@ var PostmanGatewayAssetsClient = class {
126255
126266
  );
126256
126267
  return match?.uid ? { uid: match.uid, name: match.name } : null;
126257
126268
  }
126258
- // --- collection read (service: collection, v3 export) ---
126269
+ // --- collection read (service: sync, populated v2.1 snapshot) ---
126259
126270
  //
126260
- // The gateway `collection` service does NOT serve spec-generated uids on the
126261
- // v2 `/collections/:uid` path (404 RESOURCE_NOT_FOUND, live-probed). It DOES
126262
- // serve `GET /v3/collections/:id/export`, which returns the canonical v3
126263
- // collection IR (`{ data: { collection: { ... } } }`). That v3 IR is fed
126264
- // straight to `convertAndSplitV3Collection` never round-tripped back to v2.
126265
- // The full owner-prefixed public UID must be sent verbatim; the bare model id
126266
- // is rejected on root mutation routes (403 FORBIDDEN) and must not be used
126267
- // here either, so the caller is responsible for passing the public UID.
126271
+ // This is the canonical full collection read used by
126272
+ // api-specification-service before whole-tree Sync updates. Collection v3
126273
+ // `/export` performs an expensive projection and has returned 500s for fresh
126274
+ // roots under provider load. The populated Sync read returns the complete
126275
+ // v2.1 model directly and is transformed in memory through the official
126276
+ // runtime.models V2 -> V3 pipeline before repository materialization.
126268
126277
  /**
126269
- * Fetch a collection's v3 IR through the gateway v3 export endpoint.
126270
- * Returns the `data.collection` object (canonical v3 shape with `$kind`
126271
- * discriminators, `items`, `variables`, `references`). Caller writes it to
126272
- * disk via `convertAndSplitV3Collection`. PMAK is never used for collection
126273
- * reads.
126278
+ * Fetch one complete v2.1 collection through a single retry-free populated
126279
+ * Sync GET. The response must be a v2.1 model for the requested public UID;
126280
+ * malformed or foreign snapshots fail closed. PMAK is never used.
126274
126281
  */
126275
126282
  async getCollection(uid) {
126276
126283
  const id = this.requireCollectionPublicUid(uid);
126277
126284
  const response = await this.gateway.requestJson({
126278
- service: "collection",
126285
+ service: "sync",
126279
126286
  method: "get",
126280
- path: `/v3/collections/${id}/export`
126281
- });
126282
- const data2 = this.asRecord(response?.data);
126283
- return data2?.collection ?? null;
126287
+ path: `/collection/${encodeURIComponent(id)}`,
126288
+ query: { populate: "true", format: "2.1.0", uid: "false" },
126289
+ retry: "none",
126290
+ fallback: "none"
126291
+ });
126292
+ const collection = this.asRecord(response?.data);
126293
+ const info = this.asRecord(collection?.info);
126294
+ const schema = typeof info?.schema === "string" ? info.schema.trim() : "";
126295
+ if (!collection || !info || typeof info.name !== "string" || !info.name.trim() || !/\/collection\/v2\.1\.0\/collection\.json\/?$/i.test(schema) || !Array.isArray(collection.item)) {
126296
+ throw new Error(
126297
+ "COLLECTION_SNAPSHOT_INVALID: populated Sync read did not return a complete v2.1 collection"
126298
+ );
126299
+ }
126300
+ try {
126301
+ assertV2CollectionModel(collection);
126302
+ } catch (error) {
126303
+ throw new Error(
126304
+ "COLLECTION_SNAPSHOT_INVALID: populated Sync payload failed v2.1 schema validation",
126305
+ { cause: error }
126306
+ );
126307
+ }
126308
+ const observedId = String(info._postman_id ?? collection.id ?? "").trim();
126309
+ if (!observedId || this.toModelId(observedId).toLowerCase() !== this.toModelId(id).toLowerCase()) {
126310
+ throw new Error(
126311
+ "COLLECTION_SNAPSHOT_INVALID: populated Sync read returned a different collection identity"
126312
+ );
126313
+ }
126314
+ return collection;
126284
126315
  }
126285
126316
  // --- mocks (service: mock) ---
126286
126317
  async createMock(workspaceId, name, collectionUid, environmentUid, requestedVisibility = "private") {
@@ -129092,13 +129123,14 @@ function applyPrivateMockPrebuiltPlans(plans) {
129092
129123
  }
129093
129124
  }
129094
129125
  async function preparePrivateMockCloudCollection(role, collectionId, postman) {
129095
- const col = await postman.getCollection(collectionId);
129096
- const { collection } = applyPrivateMockExportCleanup(col, {
129126
+ const snapshot = await postman.getCollection(collectionId);
129127
+ const v3Collection = convertV2CollectionToV3Collection(snapshot);
129128
+ const { collection } = applyPrivateMockExportCleanup(v3Collection, {
129097
129129
  stripManagedBlocks: isPrivateMockLegacyExportCleanupEnabled()
129098
129130
  });
129099
129131
  if (!verifyPrivateMockRootHook(collection)) {
129100
129132
  throw new Error(
129101
- `PRIVATE_MOCK_AUTH_ROOT_UNVERIFIED: Managed root hook missing from exported ${role} collection ${collectionId}`
129133
+ `PRIVATE_MOCK_AUTH_ROOT_UNVERIFIED: Managed root hook missing from populated ${role} collection snapshot ${collectionId}`
129102
129134
  );
129103
129135
  }
129104
129136
  return collection;
@@ -129144,7 +129176,7 @@ async function acquireCollectionArtifact(options) {
129144
129176
  return { reusePrebuilt: true };
129145
129177
  }
129146
129178
  if (entry) {
129147
- core.info(privateMockAuth ? `Private mock has no locally reconciled exact prebuilt ${role} collection; exporting from cloud` : `Prebuilt ${role} collection entry present but did not exactly match; exporting from cloud`);
129179
+ 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`);
129148
129180
  }
129149
129181
  const cloud = privateMockAuth ? await preparePrivateMockCloudCollection(role, collectionId, postman) : await postman.getCollection(collectionId);
129150
129182
  return { reusePrebuilt: false, cloudCollection: cloud };
@@ -130391,9 +130423,9 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
130391
130423
  getEnvironment: gatewayAssets.getEnvironment.bind(gatewayAssets),
130392
130424
  updateEnvironment: gatewayAssets.updateEnvironment.bind(gatewayAssets),
130393
130425
  findEnvironmentByName: gatewayAssets.findEnvironmentByName.bind(gatewayAssets),
130394
- // Collection read via the v3 export endpoint — returns canonical v3 IR,
130395
- // written to disk by `convertAndSplitAnyCollection`. PMAK is never used for
130396
- // collection reads.
130426
+ // Collection read via one retry-free populated Sync GET — returns a full
130427
+ // v2.1 snapshot that the official runtime.models transform converts to v3
130428
+ // before repository materialization. PMAK is never used for reads.
130397
130429
  getCollection: gatewayAssets.getCollection.bind(gatewayAssets),
130398
130430
  // Mocks via the `mock` service, collection-based monitors via the `monitors`
130399
130431
  // service (jobTemplates). Both reference the collection by its full public
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
  },
@@ -127863,8 +127874,8 @@ var PostmanGatewayAssetsClient = class {
127863
127874
  }
127864
127875
  /**
127865
127876
  * Validate and normalize a collection public UID for routes that require the
127866
- * full owner-prefixed UID (`<owner>-<uuid>`, 6 hyphen segments): collection
127867
- * 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.
127868
127879
  *
127869
127880
  * - Trims once.
127870
127881
  * - Rejects empty values.
@@ -128176,32 +128187,52 @@ var PostmanGatewayAssetsClient = class {
128176
128187
  );
128177
128188
  return match?.uid ? { uid: match.uid, name: match.name } : null;
128178
128189
  }
128179
- // --- collection read (service: collection, v3 export) ---
128190
+ // --- collection read (service: sync, populated v2.1 snapshot) ---
128180
128191
  //
128181
- // The gateway `collection` service does NOT serve spec-generated uids on the
128182
- // v2 `/collections/:uid` path (404 RESOURCE_NOT_FOUND, live-probed). It DOES
128183
- // serve `GET /v3/collections/:id/export`, which returns the canonical v3
128184
- // collection IR (`{ data: { collection: { ... } } }`). That v3 IR is fed
128185
- // straight to `convertAndSplitV3Collection` never round-tripped back to v2.
128186
- // The full owner-prefixed public UID must be sent verbatim; the bare model id
128187
- // is rejected on root mutation routes (403 FORBIDDEN) and must not be used
128188
- // 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.
128189
128198
  /**
128190
- * Fetch a collection's v3 IR through the gateway v3 export endpoint.
128191
- * Returns the `data.collection` object (canonical v3 shape with `$kind`
128192
- * discriminators, `items`, `variables`, `references`). Caller writes it to
128193
- * disk via `convertAndSplitV3Collection`. PMAK is never used for collection
128194
- * 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.
128195
128202
  */
128196
128203
  async getCollection(uid) {
128197
128204
  const id = this.requireCollectionPublicUid(uid);
128198
128205
  const response = await this.gateway.requestJson({
128199
- service: "collection",
128206
+ service: "sync",
128200
128207
  method: "get",
128201
- path: `/v3/collections/${id}/export`
128202
- });
128203
- const data2 = this.asRecord(response?.data);
128204
- 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;
128205
128236
  }
128206
128237
  // --- mocks (service: mock) ---
128207
128238
  async createMock(workspaceId, name, collectionUid, environmentUid, requestedVisibility = "private") {
@@ -131209,13 +131240,14 @@ function applyPrivateMockPrebuiltPlans(plans) {
131209
131240
  }
131210
131241
  }
131211
131242
  async function preparePrivateMockCloudCollection(role, collectionId, postman) {
131212
- const col = await postman.getCollection(collectionId);
131213
- const { collection } = applyPrivateMockExportCleanup(col, {
131243
+ const snapshot = await postman.getCollection(collectionId);
131244
+ const v3Collection = convertV2CollectionToV3Collection(snapshot);
131245
+ const { collection } = applyPrivateMockExportCleanup(v3Collection, {
131214
131246
  stripManagedBlocks: isPrivateMockLegacyExportCleanupEnabled()
131215
131247
  });
131216
131248
  if (!verifyPrivateMockRootHook(collection)) {
131217
131249
  throw new Error(
131218
- `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}`
131219
131251
  );
131220
131252
  }
131221
131253
  return collection;
@@ -131261,7 +131293,7 @@ async function acquireCollectionArtifact(options) {
131261
131293
  return { reusePrebuilt: true };
131262
131294
  }
131263
131295
  if (entry) {
131264
- core.info(privateMockAuth ? `Private mock has no locally reconciled exact prebuilt ${role} collection; 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`);
131265
131297
  }
131266
131298
  const cloud = privateMockAuth ? await preparePrivateMockCloudCollection(role, collectionId, postman) : await postman.getCollection(collectionId);
131267
131299
  return { reusePrebuilt: false, cloudCollection: cloud };
@@ -132508,9 +132540,9 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
132508
132540
  getEnvironment: gatewayAssets.getEnvironment.bind(gatewayAssets),
132509
132541
  updateEnvironment: gatewayAssets.updateEnvironment.bind(gatewayAssets),
132510
132542
  findEnvironmentByName: gatewayAssets.findEnvironmentByName.bind(gatewayAssets),
132511
- // Collection read via the v3 export endpoint — returns canonical v3 IR,
132512
- // written to disk by `convertAndSplitAnyCollection`. PMAK is never used for
132513
- // 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.
132514
132546
  getCollection: gatewayAssets.getCollection.bind(gatewayAssets),
132515
132547
  // Mocks via the `mock` service, collection-based monitors via the `monitors`
132516
132548
  // service (jobTemplates). Both reference the collection by its full public
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postman-cs/onboarding-repo-sync",
3
- "version": "2.10.8",
3
+ "version": "2.10.9",
4
4
  "description": "Postman repo sync GitHub Action.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",