@topolo/sdk 0.9.7 → 0.9.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.js CHANGED
@@ -28,19 +28,54 @@ var TopoloSdkError = class extends Error {
28
28
  }
29
29
  };
30
30
  var TopoloAuthError = class extends TopoloSdkError {
31
- constructor(message, code = "auth_error") {
31
+ status;
32
+ body;
33
+ service;
34
+ path;
35
+ requestId;
36
+ retryAfter;
37
+ constructor(message, code = "auth_error", options = {}) {
32
38
  super(code, message);
33
39
  this.name = "TopoloAuthError";
40
+ this.status = options.status ?? 401;
41
+ this.body = options.body;
42
+ this.service = options.service;
43
+ this.path = options.path;
44
+ this.requestId = options.requestId;
45
+ this.retryAfter = options.retryAfter;
34
46
  }
35
47
  };
36
48
  var TopoloPermissionError = class extends TopoloSdkError {
37
49
  required;
38
- constructor(message, required) {
50
+ status;
51
+ body;
52
+ service;
53
+ path;
54
+ requestId;
55
+ retryAfter;
56
+ constructor(message, required, options = {}) {
39
57
  super("permission_denied", message);
40
58
  this.name = "TopoloPermissionError";
41
59
  this.required = required;
60
+ this.status = options.status ?? 403;
61
+ this.body = options.body;
62
+ this.service = options.service;
63
+ this.path = options.path;
64
+ this.requestId = options.requestId;
65
+ this.retryAfter = options.retryAfter;
42
66
  }
43
67
  };
68
+ function extractHttpErrorCode(body) {
69
+ if (!body || typeof body !== "object" || Array.isArray(body)) return "http_error";
70
+ const record = body;
71
+ if (typeof record.code === "string" && record.code.trim()) return record.code.trim();
72
+ const nested = record.error;
73
+ if (nested && typeof nested === "object" && !Array.isArray(nested)) {
74
+ const code = nested.code;
75
+ if (typeof code === "string" && code.trim()) return code.trim();
76
+ }
77
+ return "http_error";
78
+ }
44
79
  var TopoloConfirmationRequiredError = class extends TopoloSdkError {
45
80
  constructor(message) {
46
81
  super("confirmation_required", message);
@@ -52,13 +87,17 @@ var TopoloHttpError = class extends TopoloSdkError {
52
87
  body;
53
88
  service;
54
89
  path;
55
- constructor(service, path, status, body, message) {
56
- super("http_error", message ?? `HTTP ${status} from ${service}${path}`);
90
+ requestId;
91
+ retryAfter;
92
+ constructor(service, path, status, body, message, options = {}) {
93
+ super(extractHttpErrorCode(body), message ?? `HTTP ${status} from ${service}${path}`);
57
94
  this.name = "TopoloHttpError";
58
95
  this.status = status;
59
96
  this.body = body;
60
97
  this.service = service;
61
98
  this.path = path;
99
+ this.requestId = options.requestId;
100
+ this.retryAfter = options.retryAfter;
62
101
  }
63
102
  };
64
103
 
@@ -67,6 +106,7 @@ var APP_API_URLS = {
67
106
  agent: "https://agent.topolo.app",
68
107
  auth: "https://auth.topolo.app",
69
108
  backup: "https://backup.topolo.app",
109
+ blog: "https://blog.topolo.app",
70
110
  books: "https://books.topolo.app",
71
111
  brand: "https://brand.topolo.app",
72
112
  bugfix: "https://bugfix.topolo.app",
@@ -79,6 +119,7 @@ var APP_API_URLS = {
79
119
  compose: "https://compose.topolo.app",
80
120
  consent: "https://consent.topolo.app",
81
121
  crm: "https://crm.topolo.app",
122
+ design: "https://design.topolo.app",
82
123
  developers: "https://developers.topolo.app",
83
124
  director: "https://director.topolo.app",
84
125
  feed: "https://feed.topolo.app",
@@ -164,7 +205,9 @@ var APPLICATIONS = {
164
205
  "domains": [
165
206
  "agent.topolo.app"
166
207
  ],
167
- "routes": [],
208
+ "routes": [
209
+ "agent.topolo.app"
210
+ ],
168
211
  "appId": "agent"
169
212
  },
170
213
  {
@@ -225,6 +268,38 @@ var APPLICATIONS = {
225
268
  }
226
269
  ]
227
270
  },
271
+ "blog": {
272
+ "id": "blog",
273
+ "appDir": "TopoloBlog",
274
+ "name": "topolo-blog",
275
+ "packageName": "topolo-blog",
276
+ "productionUrl": "https://blog.topolo.app",
277
+ "services": [
278
+ "blog"
279
+ ],
280
+ "deployTargets": [
281
+ {
282
+ "name": "topolo-blog",
283
+ "kind": "worker",
284
+ "apiUrl": "https://blog.topolo.app",
285
+ "domains": [
286
+ "blog.topolo.app"
287
+ ],
288
+ "routes": [
289
+ "blog.topolo.app"
290
+ ],
291
+ "appId": "blog"
292
+ },
293
+ {
294
+ "name": "topolo-blog-api",
295
+ "kind": "worker",
296
+ "apiUrl": null,
297
+ "domains": [],
298
+ "routes": [],
299
+ "appId": null
300
+ }
301
+ ]
302
+ },
228
303
  "books": {
229
304
  "id": "books",
230
305
  "appDir": "TopoloBooks",
@@ -242,7 +317,9 @@ var APPLICATIONS = {
242
317
  "domains": [
243
318
  "books.topolo.app"
244
319
  ],
245
- "routes": [],
320
+ "routes": [
321
+ "books.topolo.app"
322
+ ],
246
323
  "appId": "books"
247
324
  },
248
325
  {
@@ -272,7 +349,9 @@ var APPLICATIONS = {
272
349
  "domains": [
273
350
  "brand.topolo.app"
274
351
  ],
275
- "routes": [],
352
+ "routes": [
353
+ "brand.topolo.app"
354
+ ],
276
355
  "appId": "brand"
277
356
  },
278
357
  {
@@ -390,7 +469,9 @@ var APPLICATIONS = {
390
469
  "domains": [
391
470
  "campaigns.topolo.app"
392
471
  ],
393
- "routes": [],
472
+ "routes": [
473
+ "campaigns.topolo.app"
474
+ ],
394
475
  "appId": "campaigns"
395
476
  },
396
477
  {
@@ -444,7 +525,9 @@ var APPLICATIONS = {
444
525
  "domains": [
445
526
  "chat.topolo.app"
446
527
  ],
447
- "routes": [],
528
+ "routes": [
529
+ "chat.topolo.app"
530
+ ],
448
531
  "appId": "chat"
449
532
  },
450
533
  {
@@ -530,7 +613,9 @@ var APPLICATIONS = {
530
613
  "domains": [
531
614
  "compose.topolo.app"
532
615
  ],
533
- "routes": [],
616
+ "routes": [
617
+ "compose.topolo.app"
618
+ ],
534
619
  "appId": "compose"
535
620
  },
536
621
  {
@@ -560,7 +645,9 @@ var APPLICATIONS = {
560
645
  "domains": [
561
646
  "consent.topolo.app"
562
647
  ],
563
- "routes": [],
648
+ "routes": [
649
+ "consent.topolo.app"
650
+ ],
564
651
  "appId": "consent"
565
652
  },
566
653
  {
@@ -598,11 +685,45 @@ var APPLICATIONS = {
598
685
  "domains": [
599
686
  "crm.topolo.app"
600
687
  ],
601
- "routes": [],
688
+ "routes": [
689
+ "crm.topolo.app"
690
+ ],
602
691
  "appId": "crm"
603
692
  }
604
693
  ]
605
694
  },
695
+ "design": {
696
+ "id": "design",
697
+ "appDir": "TopoloDesign",
698
+ "name": "topolo-design",
699
+ "packageName": "@topolo-io/design",
700
+ "productionUrl": "https://design.topolo.app",
701
+ "services": [
702
+ "design"
703
+ ],
704
+ "deployTargets": [
705
+ {
706
+ "name": "topolo-design",
707
+ "kind": "worker",
708
+ "apiUrl": "https://design.topolo.app",
709
+ "domains": [
710
+ "design.topolo.app"
711
+ ],
712
+ "routes": [
713
+ "design.topolo.app"
714
+ ],
715
+ "appId": "design"
716
+ },
717
+ {
718
+ "name": "topolo-design-api",
719
+ "kind": "worker",
720
+ "apiUrl": null,
721
+ "domains": [],
722
+ "routes": [],
723
+ "appId": null
724
+ }
725
+ ]
726
+ },
606
727
  "developers": {
607
728
  "id": "developers",
608
729
  "appDir": "TopoloDevelopers",
@@ -628,7 +749,9 @@ var APPLICATIONS = {
628
749
  "domains": [
629
750
  "developers.topolo.app"
630
751
  ],
631
- "routes": [],
752
+ "routes": [
753
+ "developers.topolo.app"
754
+ ],
632
755
  "appId": "developers"
633
756
  },
634
757
  {
@@ -658,7 +781,9 @@ var APPLICATIONS = {
658
781
  "domains": [
659
782
  "director.topolo.app"
660
783
  ],
661
- "routes": [],
784
+ "routes": [
785
+ "director.topolo.app"
786
+ ],
662
787
  "appId": "director"
663
788
  },
664
789
  {
@@ -766,7 +891,9 @@ var APPLICATIONS = {
766
891
  "domains": [
767
892
  "flow.topolo.app"
768
893
  ],
769
- "routes": [],
894
+ "routes": [
895
+ "flow.topolo.app"
896
+ ],
770
897
  "appId": "flow"
771
898
  },
772
899
  {
@@ -796,7 +923,9 @@ var APPLICATIONS = {
796
923
  "domains": [
797
924
  "forecast.topolo.app"
798
925
  ],
799
- "routes": [],
926
+ "routes": [
927
+ "forecast.topolo.app"
928
+ ],
800
929
  "appId": "forecast"
801
930
  },
802
931
  {
@@ -826,7 +955,9 @@ var APPLICATIONS = {
826
955
  "domains": [
827
956
  "forms.topolo.app"
828
957
  ],
829
- "routes": [],
958
+ "routes": [
959
+ "forms.topolo.app"
960
+ ],
830
961
  "appId": "forms"
831
962
  },
832
963
  {
@@ -888,7 +1019,9 @@ var APPLICATIONS = {
888
1019
  "domains": [
889
1020
  "insights.topolo.app"
890
1021
  ],
891
- "routes": [],
1022
+ "routes": [
1023
+ "insights.topolo.app"
1024
+ ],
892
1025
  "appId": "insights"
893
1026
  },
894
1027
  {
@@ -918,7 +1051,9 @@ var APPLICATIONS = {
918
1051
  "domains": [
919
1052
  "inventory.topolo.app"
920
1053
  ],
921
- "routes": [],
1054
+ "routes": [
1055
+ "inventory.topolo.app"
1056
+ ],
922
1057
  "appId": "inventory"
923
1058
  },
924
1059
  {
@@ -1004,7 +1139,9 @@ var APPLICATIONS = {
1004
1139
  "domains": [
1005
1140
  "mail.topolo.app"
1006
1141
  ],
1007
- "routes": [],
1142
+ "routes": [
1143
+ "mail.topolo.app"
1144
+ ],
1008
1145
  "appId": "mail"
1009
1146
  },
1010
1147
  {
@@ -1270,7 +1407,9 @@ var APPLICATIONS = {
1270
1407
  "domains": [
1271
1408
  "pay.topolo.app"
1272
1409
  ],
1273
- "routes": [],
1410
+ "routes": [
1411
+ "pay.topolo.app"
1412
+ ],
1274
1413
  "appId": "pay"
1275
1414
  },
1276
1415
  {
@@ -1300,7 +1439,9 @@ var APPLICATIONS = {
1300
1439
  "domains": [
1301
1440
  "people.topolo.app"
1302
1441
  ],
1303
- "routes": [],
1442
+ "routes": [
1443
+ "people.topolo.app"
1444
+ ],
1304
1445
  "appId": "people"
1305
1446
  },
1306
1447
  {
@@ -1355,7 +1496,9 @@ var APPLICATIONS = {
1355
1496
  "domains": [
1356
1497
  "quro.topolo.app"
1357
1498
  ],
1358
- "routes": [],
1499
+ "routes": [
1500
+ "quro.topolo.app"
1501
+ ],
1359
1502
  "appId": null
1360
1503
  }
1361
1504
  ]
@@ -1377,7 +1520,9 @@ var APPLICATIONS = {
1377
1520
  "domains": [
1378
1521
  "roadmapper.topolo.app"
1379
1522
  ],
1380
- "routes": [],
1523
+ "routes": [
1524
+ "roadmapper.topolo.app"
1525
+ ],
1381
1526
  "appId": "roadmapper"
1382
1527
  },
1383
1528
  {
@@ -1407,7 +1552,9 @@ var APPLICATIONS = {
1407
1552
  "domains": [
1408
1553
  "sign.topolo.app"
1409
1554
  ],
1410
- "routes": [],
1555
+ "routes": [
1556
+ "sign.topolo.app"
1557
+ ],
1411
1558
  "appId": "sign"
1412
1559
  },
1413
1560
  {
@@ -1521,7 +1668,9 @@ var APPLICATIONS = {
1521
1668
  "domains": [
1522
1669
  "spaces.topolo.app"
1523
1670
  ],
1524
- "routes": [],
1671
+ "routes": [
1672
+ "spaces.topolo.app"
1673
+ ],
1525
1674
  "appId": "spaces"
1526
1675
  },
1527
1676
  {
@@ -1639,7 +1788,9 @@ var APPLICATIONS = {
1639
1788
  "domains": [
1640
1789
  "survey.topolo.app"
1641
1790
  ],
1642
- "routes": [],
1791
+ "routes": [
1792
+ "survey.topolo.app"
1793
+ ],
1643
1794
  "appId": "survey"
1644
1795
  },
1645
1796
  {
@@ -2437,6 +2588,168 @@ function isMultipleOf(value, divisor) {
2437
2588
  return Math.abs(quotient - Math.round(quotient)) < Number.EPSILON * Math.max(1, Math.abs(quotient)) * 4;
2438
2589
  }
2439
2590
 
2591
+ // src/action-upload.ts
2592
+ function parseTopoloActionUploadContract(value) {
2593
+ if (!isRecord2(value) || value.profile !== "inline-base64" && value.profile !== "url-session") {
2594
+ throw new TopoloSdkError("upload_contract_unavailable", "This action does not publish a supported upload contract.");
2595
+ }
2596
+ const contentTypeField = requiredField(value.contentTypeField, "contentTypeField");
2597
+ const fileNameField = requiredField(value.fileNameField, "fileNameField");
2598
+ const maxBytes = Number(value.maxBytes);
2599
+ const acceptedContentTypes = Array.isArray(value.acceptedContentTypes) ? value.acceptedContentTypes.map((item) => String(item).trim().toLowerCase()).filter(Boolean) : [];
2600
+ if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0 || acceptedContentTypes.length === 0) {
2601
+ throw new TopoloSdkError("invalid_upload_contract", "The action upload contract must declare maxBytes and acceptedContentTypes.");
2602
+ }
2603
+ if (value.profile === "inline-base64") {
2604
+ return {
2605
+ profile: "inline-base64",
2606
+ contentField: requiredField(value.contentField, "contentField"),
2607
+ contentTypeField,
2608
+ fileNameField,
2609
+ maxBytes,
2610
+ acceptedContentTypes
2611
+ };
2612
+ }
2613
+ const uploadMethod = value.uploadMethod === "PUT" || value.uploadMethod === "POST" ? value.uploadMethod : null;
2614
+ const uploadUrlPath = requiredPath(value.uploadUrlPath, "uploadUrlPath");
2615
+ const complete = value.complete === void 0 ? void 0 : parseCompleteContract(value.complete);
2616
+ if (!uploadMethod) throw new TopoloSdkError("invalid_upload_contract", "The action upload contract has an invalid uploadMethod.");
2617
+ return {
2618
+ profile: "url-session",
2619
+ fileNameField,
2620
+ contentTypeField,
2621
+ sizeField: requiredField(value.sizeField, "sizeField"),
2622
+ maxBytes,
2623
+ acceptedContentTypes,
2624
+ uploadUrlPath,
2625
+ uploadMethod,
2626
+ ...complete ? { complete } : {}
2627
+ };
2628
+ }
2629
+ function prepareTopoloActionUploadInput(contractValue, content, input, options) {
2630
+ const contract = parseTopoloActionUploadContract(contractValue);
2631
+ if (contract.profile !== "inline-base64") throw unsupportedInlineProfile();
2632
+ const bytes = content instanceof Uint8Array ? content : new Uint8Array(content);
2633
+ if (bytes.byteLength === 0) {
2634
+ throw new TopoloSdkError("invalid_upload_content", "Upload content must not be empty.");
2635
+ }
2636
+ if (bytes.byteLength > contract.maxBytes) {
2637
+ throw new TopoloSdkError("upload_too_large", `Upload content exceeds the ${contract.maxBytes}-byte action limit.`);
2638
+ }
2639
+ return mapUploadInput(contract, bytesToBase64(bytes), input, options);
2640
+ }
2641
+ function prepareTopoloActionBase64UploadInput(contractValue, contentBase64, input, options) {
2642
+ const contract = parseTopoloActionUploadContract(contractValue);
2643
+ if (contract.profile !== "inline-base64") throw unsupportedInlineProfile();
2644
+ if (!isCanonicalBase64(contentBase64)) {
2645
+ throw new TopoloSdkError("invalid_upload_content", "Upload content must be canonical base64.");
2646
+ }
2647
+ const padding = contentBase64.endsWith("==") ? 2 : contentBase64.endsWith("=") ? 1 : 0;
2648
+ const byteLength = contentBase64.length / 4 * 3 - padding;
2649
+ if (byteLength === 0) throw new TopoloSdkError("invalid_upload_content", "Upload content must not be empty.");
2650
+ if (byteLength > contract.maxBytes) {
2651
+ throw new TopoloSdkError("upload_too_large", `Upload content exceeds the ${contract.maxBytes}-byte action limit.`);
2652
+ }
2653
+ return mapUploadInput(contract, contentBase64, input, options);
2654
+ }
2655
+ function prepareTopoloUrlSessionUploadInput(contractValue, byteLength, input, options) {
2656
+ const contract = parseTopoloActionUploadContract(contractValue);
2657
+ if (contract.profile !== "url-session") {
2658
+ throw new TopoloSdkError("invalid_upload_contract", "This action does not publish a URL-session upload contract.");
2659
+ }
2660
+ validateUploadMetadata(contract, byteLength, options);
2661
+ return {
2662
+ contract,
2663
+ input: {
2664
+ ...input,
2665
+ [contract.fileNameField]: options.fileName.trim(),
2666
+ [contract.contentTypeField]: options.contentType.trim().toLowerCase(),
2667
+ [contract.sizeField]: byteLength
2668
+ }
2669
+ };
2670
+ }
2671
+ function topoloUploadResponseValue(response, path) {
2672
+ return path.split(".").reduce((value, segment) => isRecord2(value) ? value[segment] : void 0, response);
2673
+ }
2674
+ function decodeTopoloActionBase64(contentBase64) {
2675
+ if (!isCanonicalBase64(contentBase64)) {
2676
+ throw new TopoloSdkError("invalid_upload_content", "Upload content must be canonical base64.");
2677
+ }
2678
+ const binary = atob(contentBase64);
2679
+ return Uint8Array.from(binary, (character) => character.charCodeAt(0));
2680
+ }
2681
+ function mapUploadInput(contract, contentBase64, input, options) {
2682
+ validateUploadMetadata(contract, Math.max(1, decodedBase64Size(contentBase64)), options);
2683
+ const fileName = options.fileName.trim();
2684
+ const contentType = options.contentType.trim().toLowerCase();
2685
+ return {
2686
+ ...input,
2687
+ [contract.contentField]: contentBase64,
2688
+ [contract.contentTypeField]: contentType,
2689
+ [contract.fileNameField]: fileName
2690
+ };
2691
+ }
2692
+ function validateUploadMetadata(contract, byteLength, options) {
2693
+ const fileName = options.fileName.trim();
2694
+ const contentType = options.contentType.trim().toLowerCase();
2695
+ if (!fileName) throw new TopoloSdkError("invalid_upload_file_name", "Upload fileName must not be empty.");
2696
+ if (!Number.isSafeInteger(byteLength) || byteLength <= 0) {
2697
+ throw new TopoloSdkError("invalid_upload_content", "Upload content must not be empty.");
2698
+ }
2699
+ if (byteLength > contract.maxBytes) {
2700
+ throw new TopoloSdkError("upload_too_large", `Upload content exceeds the ${contract.maxBytes}-byte action limit.`);
2701
+ }
2702
+ if (!matchesContentType(contentType, contract.acceptedContentTypes)) {
2703
+ throw new TopoloSdkError("unsupported_upload_content_type", `Content type "${contentType}" is not accepted by this action.`);
2704
+ }
2705
+ }
2706
+ function decodedBase64Size(contentBase64) {
2707
+ const padding = contentBase64.endsWith("==") ? 2 : contentBase64.endsWith("=") ? 1 : 0;
2708
+ return contentBase64.length / 4 * 3 - padding;
2709
+ }
2710
+ function parseCompleteContract(value) {
2711
+ if (!isRecord2(value)) throw new TopoloSdkError("invalid_upload_contract", "The action upload completion contract is invalid.");
2712
+ return {
2713
+ actionId: requiredPath(value.actionId, "complete.actionId"),
2714
+ inputField: requiredField(value.inputField, "complete.inputField"),
2715
+ responsePath: requiredPath(value.responsePath, "complete.responsePath")
2716
+ };
2717
+ }
2718
+ function requiredPath(value, name) {
2719
+ const path = typeof value === "string" ? value.trim() : "";
2720
+ if (!/^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(path)) {
2721
+ throw new TopoloSdkError("invalid_upload_contract", `The action upload contract has an invalid ${name}.`);
2722
+ }
2723
+ return path;
2724
+ }
2725
+ function unsupportedInlineProfile() {
2726
+ return new TopoloSdkError("invalid_upload_contract", "This action does not publish an inline-base64 upload contract.");
2727
+ }
2728
+ function isCanonicalBase64(value) {
2729
+ return value.length > 0 && value.length % 4 === 0 && /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value);
2730
+ }
2731
+ function requiredField(value, name) {
2732
+ const field = typeof value === "string" ? value.trim() : "";
2733
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(field)) {
2734
+ throw new TopoloSdkError("invalid_upload_contract", `The action upload contract has an invalid ${name}.`);
2735
+ }
2736
+ return field;
2737
+ }
2738
+ function matchesContentType(contentType, accepted) {
2739
+ return accepted.some((candidate) => candidate === "*/*" || candidate === contentType || candidate.endsWith("/*") && contentType.startsWith(candidate.slice(0, -1)));
2740
+ }
2741
+ function bytesToBase64(bytes) {
2742
+ let binary = "";
2743
+ const chunkSize = 32768;
2744
+ for (let offset = 0; offset < bytes.length; offset += chunkSize) {
2745
+ binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
2746
+ }
2747
+ return btoa(binary);
2748
+ }
2749
+ function isRecord2(value) {
2750
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2751
+ }
2752
+
2440
2753
  // src/client.ts
2441
2754
  var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
2442
2755
  var TOPOLO_ACTION_ID_HEADER = "X-Topolo-Action-Id";
@@ -2514,6 +2827,9 @@ var TopoloClient = class _TopoloClient {
2514
2827
  if (v !== void 0) url.searchParams.set(k, String(v));
2515
2828
  }
2516
2829
  }
2830
+ if (opts.body !== void 0 && opts.rawBody !== void 0) {
2831
+ throw new TopoloSdkError("invalid_request_body", "body and rawBody are mutually exclusive.");
2832
+ }
2517
2833
  const headers = new Headers();
2518
2834
  headers.set("Accept", "application/json");
2519
2835
  if (opts.body !== void 0) headers.set("Content-Type", "application/json");
@@ -2548,7 +2864,7 @@ var TopoloClient = class _TopoloClient {
2548
2864
  return await this.fetchImpl(urlStr, {
2549
2865
  method,
2550
2866
  headers: new Headers(headers),
2551
- body: opts.body !== void 0 ? JSON.stringify(opts.body) : null,
2867
+ body: opts.rawBody ?? (opts.body !== void 0 ? JSON.stringify(opts.body) : null),
2552
2868
  signal
2553
2869
  });
2554
2870
  } finally {
@@ -2596,14 +2912,32 @@ var TopoloClient = class _TopoloClient {
2596
2912
  error: message,
2597
2913
  status: res.status
2598
2914
  });
2599
- if (res.status === 401) throw new TopoloAuthError(describeError(parsed, "Unauthorized"));
2915
+ const errorContext = {
2916
+ status: res.status,
2917
+ body: parsed,
2918
+ service: resolvedService.serviceKey,
2919
+ path: opts.path,
2920
+ requestId: res.headers.get("X-Topolo-Request-Id") || requestId,
2921
+ retryAfter: res.headers.get("Retry-After") || void 0
2922
+ };
2923
+ if (res.status === 401) {
2924
+ throw new TopoloAuthError(describeError(parsed, "Unauthorized"), "auth_error", errorContext);
2925
+ }
2600
2926
  if (res.status === 403) {
2601
2927
  throw new TopoloPermissionError(
2602
2928
  describeError(parsed, "Permission denied"),
2603
- extractRequired(parsed)
2929
+ extractRequired(parsed),
2930
+ errorContext
2604
2931
  );
2605
2932
  }
2606
- throw new TopoloHttpError(resolvedService.serviceKey, opts.path, res.status, parsed);
2933
+ throw new TopoloHttpError(
2934
+ resolvedService.serviceKey,
2935
+ opts.path,
2936
+ res.status,
2937
+ parsed,
2938
+ message,
2939
+ errorContext
2940
+ );
2607
2941
  }
2608
2942
  this.emit({
2609
2943
  phase: "response",
@@ -2762,7 +3096,7 @@ var TopoloClient = class _TopoloClient {
2762
3096
  }
2763
3097
  const entries = [];
2764
3098
  for (const item of raw) {
2765
- if (!isRecord2(item)) continue;
3099
+ if (!isRecord3(item)) continue;
2766
3100
  const s = item;
2767
3101
  const appId = typeof s.app_id === "string" ? s.app_id : typeof s.id === "string" ? s.id : null;
2768
3102
  if (!appId) continue;
@@ -2837,8 +3171,8 @@ var TopoloClient = class _TopoloClient {
2837
3171
  "app_topolo_auth.service_api_key_resources.list",
2838
3172
  { appId: service.appId }
2839
3173
  );
2840
- const payload = isRecord2(response) && isRecord2(response.data) ? response.data : response;
2841
- if (!isRecord2(payload) || !Array.isArray(payload.resources)) {
3174
+ const payload = isRecord3(response) && isRecord3(response.data) ? response.data : response;
3175
+ if (!isRecord3(payload) || !Array.isArray(payload.resources)) {
2842
3176
  throw new TopoloSdkError("resource_catalog_unavailable", "TopoloAuth did not return bindable resources.");
2843
3177
  }
2844
3178
  return {
@@ -2944,6 +3278,71 @@ var TopoloClient = class _TopoloClient {
2944
3278
  const entry = await this.getAction(action);
2945
3279
  return this.executeResolvedAction(entry, input, options);
2946
3280
  }
3281
+ async uploadAction(action, content, input, options) {
3282
+ const entry = await this.getAction(action);
3283
+ const contract = parseTopoloActionUploadContract(entry.agentContract?.upload);
3284
+ if (contract.profile === "url-session") {
3285
+ const bytes = content instanceof Uint8Array ? content : new Uint8Array(content);
3286
+ return this.executeUrlSessionUpload(entry, contract, bytes, input, options);
3287
+ }
3288
+ const uploadInput = prepareTopoloActionUploadInput(entry.agentContract?.upload, content, input, options);
3289
+ return this.executeResolvedAction(entry, uploadInput, options);
3290
+ }
3291
+ async uploadActionBase64(action, contentBase64, input, options) {
3292
+ const entry = await this.getAction(action);
3293
+ const contract = parseTopoloActionUploadContract(entry.agentContract?.upload);
3294
+ if (contract.profile === "url-session") {
3295
+ return this.executeUrlSessionUpload(entry, contract, decodeTopoloActionBase64(contentBase64), input, options);
3296
+ }
3297
+ const uploadInput = prepareTopoloActionBase64UploadInput(entry.agentContract?.upload, contentBase64, input, options);
3298
+ return this.executeResolvedAction(entry, uploadInput, options);
3299
+ }
3300
+ async executeUrlSessionUpload(entry, contract, bytes, input, options) {
3301
+ const prepared = prepareTopoloUrlSessionUploadInput(contract, bytes.byteLength, input, options);
3302
+ const initiation = await this.executeResolvedAction(entry, prepared.input, options);
3303
+ const uploadUrl = topoloUploadResponseValue(initiation, contract.uploadUrlPath);
3304
+ if (typeof uploadUrl !== "string" || !uploadUrl.trim()) {
3305
+ throw new TopoloSdkError("invalid_upload_response", `Upload action response is missing ${contract.uploadUrlPath}.`);
3306
+ }
3307
+ await this.putUrlSessionContent(entry, uploadUrl, contract.uploadMethod, bytes, options);
3308
+ if (!contract.complete) return initiation;
3309
+ const completionValue = topoloUploadResponseValue(initiation, contract.complete.responsePath);
3310
+ if (typeof completionValue !== "string" || !completionValue.trim()) {
3311
+ throw new TopoloSdkError("invalid_upload_response", `Upload action response is missing ${contract.complete.responsePath}.`);
3312
+ }
3313
+ const completion = await this.getAction(contract.complete.actionId);
3314
+ return this.executeResolvedAction(completion, {
3315
+ ...options.completionInput ?? {},
3316
+ [contract.complete.inputField]: completionValue
3317
+ }, options);
3318
+ }
3319
+ async putUrlSessionContent(entry, uploadUrl, method, bytes, options) {
3320
+ const service = entry.appSlug || entry.appId;
3321
+ const resolved = entry.apiBaseUrl ? { serviceKey: normalizeLookupKey(service), baseUrl: requireHttpOrigin(entry.apiBaseUrl) } : await this.resolveRequestService(service);
3322
+ const target = new URL(uploadUrl, resolved.baseUrl.endsWith("/") ? resolved.baseUrl : `${resolved.baseUrl}/`);
3323
+ const body = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
3324
+ if (target.origin === new URL(resolved.baseUrl).origin) {
3325
+ await this.request({
3326
+ service,
3327
+ baseUrl: resolved.baseUrl,
3328
+ path: target.toString(),
3329
+ method,
3330
+ rawBody: body,
3331
+ headers: { "Content-Type": options.contentType.trim().toLowerCase() },
3332
+ resource: options.resource,
3333
+ confirm: options.confirm
3334
+ });
3335
+ return;
3336
+ }
3337
+ const response = await this.fetchImpl(target, {
3338
+ method,
3339
+ headers: { "Content-Type": options.contentType.trim().toLowerCase() },
3340
+ body
3341
+ });
3342
+ if (!response.ok) {
3343
+ throw new TopoloHttpError(service, target.pathname, response.status, await response.text(), `Upload transport failed with HTTP ${response.status}.`);
3344
+ }
3345
+ }
2947
3346
  async validateActionInput(action, input = {}) {
2948
3347
  const entry = await this.getAction(action);
2949
3348
  return { action: entry, validation: validateTopoloActionInput(entry.inputSchema, input) };
@@ -3102,7 +3501,7 @@ function resolveConfiguredAppId(slug, appIds) {
3102
3501
  return null;
3103
3502
  }
3104
3503
  function normalizeApiKeyResourceMetadata(input) {
3105
- if (!isRecord2(input)) return null;
3504
+ if (!isRecord3(input)) return null;
3106
3505
  const resourceType = readString(input.resourceType ?? input.resource_type);
3107
3506
  const label = readString(input.label);
3108
3507
  const emptyLabel = readString(input.emptyLabel ?? input.empty_label);
@@ -3136,7 +3535,7 @@ function normalizeApiKeyResourceMetadataList(input) {
3136
3535
  return resources;
3137
3536
  }
3138
3537
  function normalizeBindableResource(input) {
3139
- if (!isRecord2(input)) return null;
3538
+ if (!isRecord3(input)) return null;
3140
3539
  const resourceType = readString(input.resourceType ?? input.resource_type);
3141
3540
  const resourceId = readString(input.resourceId ?? input.resource_id);
3142
3541
  if (!resourceType || !resourceId) return null;
@@ -3147,13 +3546,13 @@ function normalizeBindableResource(input) {
3147
3546
  slug: readOptionalString(input.slug),
3148
3547
  status: readOptionalString(input.status),
3149
3548
  organizationId: readOptionalString(input.organizationId ?? input.organization_id),
3150
- metadata: isRecord2(input.metadata) ? input.metadata : {}
3549
+ metadata: isRecord3(input.metadata) ? input.metadata : {}
3151
3550
  };
3152
3551
  }
3153
3552
  function normalizeResourceBindings(input) {
3154
3553
  if (!Array.isArray(input)) return [];
3155
3554
  return input.flatMap((binding) => {
3156
- if (!isRecord2(binding)) return [];
3555
+ if (!isRecord3(binding)) return [];
3157
3556
  const resourceType = readString(binding.resourceType ?? binding.resource_type);
3158
3557
  const resourceId = readString(binding.resourceId ?? binding.resource_id);
3159
3558
  return resourceType && resourceId ? [{ resourceType, resourceId }] : [];
@@ -3166,7 +3565,7 @@ function readOptionalString(value) {
3166
3565
  const normalized = readString(value);
3167
3566
  return normalized || null;
3168
3567
  }
3169
- function isRecord2(value) {
3568
+ function isRecord3(value) {
3170
3569
  return typeof value === "object" && value !== null && !Array.isArray(value);
3171
3570
  }
3172
3571
  function resolveConfiguredServiceUrl(slug, serviceUrls) {
@@ -3238,14 +3637,14 @@ function catalogSearchQuery(options) {
3238
3637
  return query;
3239
3638
  }
3240
3639
  function normalizeCatalogPage(value) {
3241
- if (!isRecord2(value)) return { nextCursor: null, hasMore: false };
3640
+ if (!isRecord3(value)) return { nextCursor: null, hasMore: false };
3242
3641
  return {
3243
3642
  nextCursor: typeof value.nextCursor === "string" && value.nextCursor ? value.nextCursor : null,
3244
3643
  hasMore: value.hasMore === true
3245
3644
  };
3246
3645
  }
3247
3646
  function normalizeServiceCatalogEntry(item) {
3248
- if (!isRecord2(item)) return null;
3647
+ if (!isRecord3(item)) return null;
3249
3648
  const appId = typeof item.app_id === "string" ? item.app_id : typeof item.id === "string" ? item.id : null;
3250
3649
  if (!appId) return null;
3251
3650
  const permissions = Array.isArray(item.granted_permissions) ? item.granted_permissions.filter(
@@ -3341,10 +3740,19 @@ function mapActionInput(action, input) {
3341
3740
  delete remaining[key];
3342
3741
  return encodeURIComponent(String(value));
3343
3742
  });
3344
- const explicitQuery = plainObject(remaining.query);
3345
- delete remaining.query;
3346
- const explicitBody = Object.prototype.hasOwnProperty.call(remaining, "body") ? remaining.body : void 0;
3347
- delete remaining.body;
3743
+ const schemaProperties = plainObject(plainObject(action.inputSchema).properties);
3744
+ const schemaOwnsQuery = Object.prototype.hasOwnProperty.call(schemaProperties, "query");
3745
+ const schemaOwnsBody = Object.prototype.hasOwnProperty.call(schemaProperties, "body");
3746
+ let explicitQuery = {};
3747
+ if (!schemaOwnsQuery) {
3748
+ explicitQuery = plainObject(remaining.query);
3749
+ delete remaining.query;
3750
+ }
3751
+ let explicitBody;
3752
+ if (!schemaOwnsBody) {
3753
+ explicitBody = Object.prototype.hasOwnProperty.call(remaining, "body") ? remaining.body : void 0;
3754
+ delete remaining.body;
3755
+ }
3348
3756
  if (action.method === "GET") {
3349
3757
  return {
3350
3758
  path,
@@ -3503,7 +3911,7 @@ var INVERSE_OPERATIONS = /* @__PURE__ */ new Map([
3503
3911
  ["restore", "archive"],
3504
3912
  ["unpublish", "publish"]
3505
3913
  ]);
3506
- function isRecord3(value) {
3914
+ function isRecord4(value) {
3507
3915
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
3508
3916
  }
3509
3917
  function nonEmptyStrings(value) {
@@ -3541,10 +3949,10 @@ function collectionFor(action, resource) {
3541
3949
  return resource.endsWith("s") ? resource : `${resource}s`;
3542
3950
  }
3543
3951
  function isConcreteOutputSchema(schema) {
3544
- if (!isRecord3(schema)) return false;
3952
+ if (!isRecord4(schema)) return false;
3545
3953
  if (typeof schema.$ref === "string" && schema.$ref.trim()) return true;
3546
- if (schema.type === "array" && isRecord3(schema.items) && Object.keys(schema.items).length > 0) return true;
3547
- if (isRecord3(schema.properties) && Object.keys(schema.properties).length > 0) return true;
3954
+ if (schema.type === "array" && isRecord4(schema.items) && Object.keys(schema.items).length > 0) return true;
3955
+ if (isRecord4(schema.properties) && Object.keys(schema.properties).length > 0) return true;
3548
3956
  return ["oneOf", "anyOf", "allOf"].some((key) => Array.isArray(schema[key]) && schema[key].some(isConcreteOutputSchema));
3549
3957
  }
3550
3958
  function resourceSchema(resource) {
@@ -3625,7 +4033,7 @@ function derivedOutputSchema(action) {
3625
4033
  };
3626
4034
  }
3627
4035
  function sampleForSchema(schema, depth = 0) {
3628
- if (!isRecord3(schema) || depth > 5) return null;
4036
+ if (!isRecord4(schema) || depth > 5) return null;
3629
4037
  if (schema.const !== void 0) return schema.const;
3630
4038
  if (Array.isArray(schema.enum) && schema.enum.length > 0) return schema.enum[0];
3631
4039
  if (Array.isArray(schema.oneOf) && schema.oneOf.length > 0) return sampleForSchema(schema.oneOf[0], depth + 1);
@@ -3639,8 +4047,8 @@ function sampleForSchema(schema, depth = 0) {
3639
4047
  return schema.minLength > 1 ? "example".padEnd(schema.minLength, "x") : "example";
3640
4048
  }
3641
4049
  if (schema.type === "null") return null;
3642
- if (schema.type === "object" || isRecord3(schema.properties)) {
3643
- const properties = isRecord3(schema.properties) ? schema.properties : {};
4050
+ if (schema.type === "object" || isRecord4(schema.properties)) {
4051
+ const properties = isRecord4(schema.properties) ? schema.properties : {};
3644
4052
  const required = new Set(Array.isArray(schema.required) ? schema.required : []);
3645
4053
  const entries = Object.entries(properties).filter(([key]) => required.has(key)).map(([key, child]) => [key, sampleForSchema(child, depth + 1)]);
3646
4054
  return Object.fromEntries(entries);
@@ -3690,21 +4098,32 @@ function defaultErrors(action) {
3690
4098
  const errors = [
3691
4099
  { status: 401, code: "authentication_required", recovery: "Authenticate again or provide a valid Topolo API key." },
3692
4100
  { status: 403, code: "permission_denied", recovery: `Grant the credential permission ${action.permission || action.permissionName || "required by this action"} and the required resource binding.` },
3693
- { status: 422, code: "invalid_input", recovery: "Correct the payload using inputSchema, then validate before retrying." }
4101
+ { status: 429, code: "rate_limited", recovery: "Wait for Retry-After when present, then retry without parallel duplicate calls." },
4102
+ { status: 422, code: "invalid_input", recovery: "Correct the payload using inputSchema, then validate before retrying." },
4103
+ { status: 503, code: "dependency_unavailable", recovery: "Retry the action. If it continues, inspect the structured error details using the request ID." }
3694
4104
  ];
3695
4105
  if (action.method !== "GET") {
3696
4106
  errors.push({ status: 409, code: "state_conflict", recovery: "Read the current resource state, reconcile the intended change, and retry only if still valid." });
3697
4107
  }
3698
4108
  return errors;
3699
4109
  }
4110
+ function mergeErrors(defaults, declared) {
4111
+ const merged = [...defaults];
4112
+ for (const error of Array.isArray(declared) ? declared : []) {
4113
+ const index = merged.findIndex((candidate) => candidate.status === error?.status && candidate.code === error?.code);
4114
+ if (index === -1) merged.push(error);
4115
+ else merged[index] = error;
4116
+ }
4117
+ return merged;
4118
+ }
3700
4119
  function enrichActionContract(manifest, action, allActions = manifest.actions || []) {
3701
4120
  const appId = String(manifest.appId || manifest.app_id || "").trim();
3702
4121
  const service = slugify(manifest.slug || appId.replace(/^app_/, "").replaceAll("_", "-") || manifest.name);
3703
4122
  const outputSchema = isConcreteOutputSchema(action.outputSchema) ? structuredClone(action.outputSchema) : derivedOutputSchema(action);
3704
4123
  const resource = resourceFor(action);
3705
4124
  const readOnly = action.readOnly !== void 0 ? action.readOnly === true : action.method === "GET";
3706
- const existing = isRecord3(action.agentContract) ? structuredClone(action.agentContract) : {};
3707
- const recovery = isRecord3(existing.recovery) ? existing.recovery : recoveryFor(action, allActions, appId);
4125
+ const existing = isRecord4(action.agentContract) ? structuredClone(action.agentContract) : {};
4126
+ const recovery = isRecord4(existing.recovery) ? existing.recovery : recoveryFor(action, allActions, appId);
3708
4127
  const rollbackActionId = typeof existing.rollbackActionId === "string" && existing.rollbackActionId.trim() ? existing.rollbackActionId.trim() : recovery.mode === "compensating_action" ? recovery.actionId : void 0;
3709
4128
  const inputExample = sampleForSchema(action.inputSchema);
3710
4129
  const examples = Array.isArray(existing.examples) && existing.examples.length > 0 ? existing.examples : [{ input: inputExample ?? {}, expected: `A response matching the published outputSchema for ${action.name}.` }];
@@ -3716,9 +4135,9 @@ function enrichActionContract(manifest, action, allActions = manifest.actions ||
3716
4135
  ...existing,
3717
4136
  docsUrl,
3718
4137
  ...existingDocsUrl && existingDocsUrl !== docsUrl ? { guideUrl: existingDocsUrl } : {},
3719
- resource: isRecord3(existing.resource) ? existing.resource : { kind: resource, idPath: readOnly && ["list", "search"].includes(operationFor(action)) ? "$.items[].id" : "$.id" },
4138
+ resource: isRecord4(existing.resource) ? existing.resource : { kind: resource, idPath: readOnly && ["list", "search"].includes(operationFor(action)) ? "$.items[].id" : "$.id" },
3720
4139
  effects: nonEmptyStrings(existing.effects) ? existing.effects : [readOnly ? `Reads ${resource.replaceAll("_", " ")} data without changing service state.` : action.destructive === true || action.method === "DELETE" ? `Permanently removes or invalidates ${resource.replaceAll("_", " ")} state.` : `Changes ${resource.replaceAll("_", " ")} state in the selected Topolo resource context.`],
3721
- errors: Array.isArray(existing.errors) && existing.errors.length > 0 ? existing.errors : defaultErrors(action),
4140
+ errors: mergeErrors(defaultErrors(action), existing.errors),
3722
4141
  verify: nonEmptyStrings(existing.verify) ? existing.verify : [`Validate the response against outputSchema and ${readOnly ? "confirm it belongs to the selected resource context" : "re-read the affected resource to confirm the intended state"}.`],
3723
4142
  examples,
3724
4143
  recovery,
@@ -3730,15 +4149,15 @@ function enrichActionContract(manifest, action, allActions = manifest.actions ||
3730
4149
  function actionContractCompleteness(action) {
3731
4150
  const contract = action?.agentContract;
3732
4151
  return {
3733
- inputSchema: isRecord3(action?.inputSchema),
4152
+ inputSchema: isRecord4(action?.inputSchema),
3734
4153
  outputSchema: isConcreteOutputSchema(action?.outputSchema),
3735
4154
  effects: nonEmptyStrings(contract?.effects),
3736
4155
  errors: Array.isArray(contract?.errors) && contract.errors.length > 0,
3737
4156
  docs: typeof contract?.docsUrl === "string" && contract.docsUrl.startsWith("https://"),
3738
4157
  examples: Array.isArray(contract?.examples) && contract.examples.length > 0,
3739
4158
  verification: nonEmptyStrings(contract?.verify),
3740
- recovery: isRecord3(contract?.recovery) && ["not_applicable", "compensating_action", "irreversible"].includes(contract.recovery.mode),
3741
- agentContract: isRecord3(contract) && isRecord3(contract.resource) && nonEmptyStrings(contract.effects) && Array.isArray(contract.errors) && contract.errors.length > 0
4159
+ recovery: isRecord4(contract?.recovery) && ["not_applicable", "compensating_action", "irreversible"].includes(contract.recovery.mode),
4160
+ agentContract: isRecord4(contract) && isRecord4(contract.resource) && nonEmptyStrings(contract.effects) && Array.isArray(contract.errors) && contract.errors.length > 0
3742
4161
  };
3743
4162
  }
3744
4163
 
@@ -4143,7 +4562,7 @@ function createTopoloWidgetError(error) {
4143
4562
  }
4144
4563
  function validateTopoloWidgetResponse(response) {
4145
4564
  const errors = [];
4146
- if (!isRecord4(response)) {
4565
+ if (!isRecord5(response)) {
4147
4566
  return { ok: false, errors: ["response must be an object"] };
4148
4567
  }
4149
4568
  if (typeof response.success !== "boolean") {
@@ -4162,7 +4581,7 @@ function validateTopoloWidgetResponse(response) {
4162
4581
  return { ok: errors.length === 0, errors };
4163
4582
  }
4164
4583
  function validateWidget(widget, path, errors) {
4165
- if (!isRecord4(widget)) {
4584
+ if (!isRecord5(widget)) {
4166
4585
  errors.push(`${path} must be an object`);
4167
4586
  return;
4168
4587
  }
@@ -4189,7 +4608,7 @@ function validateWidget(widget, path, errors) {
4189
4608
  case "chart":
4190
4609
  validateNonEmptyString(widget.chartType, `${path}.chartType`, errors);
4191
4610
  validateNonEmptyString(widget.title, `${path}.title`, errors);
4192
- if (!isRecord4(widget.data)) {
4611
+ if (!isRecord5(widget.data)) {
4193
4612
  errors.push(`${path}.data must be an object`);
4194
4613
  } else {
4195
4614
  validateArray2(widget.data.labels, `${path}.data.labels`, errors);
@@ -4205,7 +4624,7 @@ function validateSnapshotMetadata(snapshot, path, errors) {
4205
4624
  errors.push(`${path} must be an object`);
4206
4625
  return;
4207
4626
  }
4208
- if (!isRecord4(snapshot)) {
4627
+ if (!isRecord5(snapshot)) {
4209
4628
  errors.push(`${path} must be an object`);
4210
4629
  return;
4211
4630
  }
@@ -4225,7 +4644,7 @@ function validateSnapshotMetadata(snapshot, path, errors) {
4225
4644
  }
4226
4645
  snapshot.availableMetrics.forEach((metric, index) => {
4227
4646
  const metricPath = `${path}.availableMetrics[${index}]`;
4228
- if (!isRecord4(metric)) {
4647
+ if (!isRecord5(metric)) {
4229
4648
  errors.push(`${metricPath} must be an object`);
4230
4649
  return;
4231
4650
  }
@@ -4246,7 +4665,7 @@ function validateArray2(value, path, errors) {
4246
4665
  errors.push(`${path} must be an array`);
4247
4666
  }
4248
4667
  }
4249
- function isRecord4(value) {
4668
+ function isRecord5(value) {
4250
4669
  return typeof value === "object" && value !== null && !Array.isArray(value);
4251
4670
  }
4252
4671
  function toIsoTimestamp(input) {
@@ -4297,6 +4716,9 @@ export {
4297
4716
  defineTopoloStatsWidget,
4298
4717
  digestTopoloActionCatalog,
4299
4718
  enrichActionContract,
4719
+ parseTopoloActionUploadContract,
4720
+ prepareTopoloActionBase64UploadInput,
4721
+ prepareTopoloActionUploadInput,
4300
4722
  requirementsForApplication,
4301
4723
  resolveCatalogServiceUrl,
4302
4724
  resolveServiceUrl,