@topolo/sdk 0.4.6 → 0.4.8

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
@@ -58,54 +58,53 @@ var TopoloHttpError = class extends TopoloSdkError {
58
58
 
59
59
  // src/services.generated.ts
60
60
  var SERVICES = {
61
- agent: "https://agent-api.topolo.app",
61
+ agent: "https://agent.topolo.app",
62
62
  auth: "https://auth.topolo.app",
63
63
  backup: "https://backup.topolo.app",
64
64
  books: "https://books.topolo.app",
65
65
  bugfix: "https://bugfix.topolo.app",
66
- bytes: "https://bytes-api.topolo.app",
66
+ bytes: "https://bytes.topolo.app",
67
67
  calendar: "https://calendar.topolo.app",
68
68
  capacity: "https://capacity.topolo.app",
69
69
  chat: "https://chat.topolo.app",
70
- commerce: "https://commerce-api.topolo.app",
70
+ commerce: "https://commerce.topolo.app",
71
71
  compose: "https://compose.topolo.app",
72
72
  consent: "https://consent.topolo.app",
73
- crm: "https://crm-api.topolo.app",
73
+ crm: "https://crm.topolo.app",
74
74
  developers: "https://developers.topolo.app",
75
75
  director: "https://director.topolo.app",
76
- feed: "https://feed-api.topolo.app",
76
+ feed: "https://feed.topolo.app",
77
77
  flow: "https://flow.topolo.app",
78
- forecast: "https://forecast-api.topolo.app",
78
+ forecast: "https://forecast.topolo.app",
79
79
  forms: "https://forms.topolo.app",
80
80
  home: "https://home.topolo.app",
81
81
  insights: "https://insights.topolo.app",
82
82
  inventory: "https://inventory.topolo.app",
83
- learn: "https://learn-api.topolo.app",
83
+ learn: "https://learn.topolo.app",
84
84
  localize: "https://localize.topolo.app",
85
- mail: "https://mail-api.topolo.app",
86
- mdm: "https://mdm-api.topolo.app",
85
+ mail: "https://mail.topolo.app",
86
+ mdm: "https://mdm.topolo.app",
87
87
  messages: "https://messages.topolo.app",
88
88
  nexus: "https://nexus-api.topolo.app",
89
89
  notify: "https://notify.topolo.app",
90
90
  observability_watch: "https://observability-watch.topolo.app",
91
- one: "https://one.topolo.app",
91
+ one: "https://www.topolo.app",
92
92
  p2p: "https://p2p.topolo.app",
93
93
  pay: "https://pay.topolo.app",
94
94
  people: "https://people.topolo.app",
95
- pulse: "https://pulse.topolo.app",
96
95
  "quro.api": "https://api.ol0.me",
97
96
  "quro.redirect": "https://ol0.me",
98
- roadmapper: "https://roadmapper-api.topolo.app",
97
+ roadmapper: "https://roadmapper.topolo.app",
99
98
  sign: "https://sign.topolo.app",
100
- social_studio: "https://studio-api.topolo.app",
101
- socialize: "https://socialize-api.topolo.app",
99
+ social_studio: "https://studio.topolo.app",
100
+ socialize: "https://socialize.topolo.app",
102
101
  spaces: "https://spaces.topolo.app",
103
102
  status: "https://status.topolo.app",
104
103
  success: "https://success.topolo.app",
105
104
  support: "https://support.topolo.app",
106
105
  survey: "https://survey.topolo.app",
107
106
  voice: "https://voice.topolo.app",
108
- web: "https://web-api.topolo.app"
107
+ web: "https://web.topolo.app"
109
108
  };
110
109
 
111
110
  // src/applications.generated.ts
@@ -156,17 +155,15 @@ var APPLICATIONS = {
156
155
  "agent.topolo.app"
157
156
  ],
158
157
  "routes": [],
159
- "serviceId": null
158
+ "serviceId": "agent"
160
159
  },
161
160
  {
162
161
  "name": "topolo-agent-api",
163
162
  "kind": "worker",
164
- "apiUrl": "https://agent-api.topolo.app",
165
- "domains": [
166
- "agent-api.topolo.app"
167
- ],
163
+ "apiUrl": null,
164
+ "domains": [],
168
165
  "routes": [],
169
- "serviceId": "agent"
166
+ "serviceId": null
170
167
  }
171
168
  ]
172
169
  },
@@ -286,17 +283,15 @@ var APPLICATIONS = {
286
283
  "bytes.topolo.app"
287
284
  ],
288
285
  "routes": [],
289
- "serviceId": null
286
+ "serviceId": "bytes"
290
287
  },
291
288
  {
292
289
  "name": "topolo-bytes-api",
293
290
  "kind": "worker",
294
- "apiUrl": "https://bytes-api.topolo.app",
295
- "domains": [
296
- "bytes-api.topolo.app"
297
- ],
291
+ "apiUrl": null,
292
+ "domains": [],
298
293
  "routes": [],
299
- "serviceId": "bytes"
294
+ "serviceId": null
300
295
  }
301
296
  ]
302
297
  },
@@ -395,12 +390,10 @@ var APPLICATIONS = {
395
390
  {
396
391
  "name": "topolo-commerce-api",
397
392
  "kind": "worker",
398
- "apiUrl": "https://commerce-api.topolo.app",
399
- "domains": [
400
- "commerce-api.topolo.app"
401
- ],
393
+ "apiUrl": null,
394
+ "domains": [],
402
395
  "routes": [],
403
- "serviceId": "commerce"
396
+ "serviceId": null
404
397
  },
405
398
  {
406
399
  "name": "topolo-commerce-guest-web",
@@ -420,7 +413,7 @@ var APPLICATIONS = {
420
413
  "commerce.topolo.app"
421
414
  ],
422
415
  "routes": [],
423
- "serviceId": null
416
+ "serviceId": "commerce"
424
417
  },
425
418
  {
426
419
  "name": "topolo-commerce-station-web",
@@ -507,12 +500,10 @@ var APPLICATIONS = {
507
500
  {
508
501
  "name": "topolo-crm-api",
509
502
  "kind": "worker",
510
- "apiUrl": "https://crm-api.topolo.app",
511
- "domains": [
512
- "crm-api.topolo.app"
513
- ],
503
+ "apiUrl": null,
504
+ "domains": [],
514
505
  "routes": [],
515
- "serviceId": "crm"
506
+ "serviceId": null
516
507
  },
517
508
  {
518
509
  "name": "topolo-crm-app",
@@ -522,7 +513,7 @@ var APPLICATIONS = {
522
513
  "crm.topolo.app"
523
514
  ],
524
515
  "routes": [],
525
- "serviceId": null
516
+ "serviceId": "crm"
526
517
  }
527
518
  ]
528
519
  },
@@ -645,12 +636,10 @@ var APPLICATIONS = {
645
636
  {
646
637
  "name": "topolo-feed-api",
647
638
  "kind": "worker",
648
- "apiUrl": "https://feed-api.topolo.app",
649
- "domains": [
650
- "feed-api.topolo.app"
651
- ],
639
+ "apiUrl": null,
640
+ "domains": [],
652
641
  "routes": [],
653
- "serviceId": "feed"
642
+ "serviceId": null
654
643
  },
655
644
  {
656
645
  "name": "topolo-feed-web",
@@ -660,7 +649,7 @@ var APPLICATIONS = {
660
649
  "feed.topolo.app"
661
650
  ],
662
651
  "routes": [],
663
- "serviceId": null
652
+ "serviceId": "feed"
664
653
  }
665
654
  ]
666
655
  },
@@ -712,17 +701,15 @@ var APPLICATIONS = {
712
701
  "forecast.topolo.app"
713
702
  ],
714
703
  "routes": [],
715
- "serviceId": null
704
+ "serviceId": "forecast"
716
705
  },
717
706
  {
718
707
  "name": "topolo-forecast-api",
719
708
  "kind": "worker",
720
- "apiUrl": "https://forecast-api.topolo.app",
721
- "domains": [
722
- "forecast-api.topolo.app"
723
- ],
709
+ "apiUrl": null,
710
+ "domains": [],
724
711
  "routes": [],
725
- "serviceId": "forecast"
712
+ "serviceId": null
726
713
  }
727
714
  ]
728
715
  },
@@ -864,17 +851,15 @@ var APPLICATIONS = {
864
851
  "learn.topolo.app"
865
852
  ],
866
853
  "routes": [],
867
- "serviceId": null
854
+ "serviceId": "learn"
868
855
  },
869
856
  {
870
857
  "name": "topolo-learn-api",
871
858
  "kind": "worker",
872
- "apiUrl": "https://learn-api.topolo.app",
873
- "domains": [
874
- "learn-api.topolo.app"
875
- ],
859
+ "apiUrl": null,
860
+ "domains": [],
876
861
  "routes": [],
877
- "serviceId": "learn"
862
+ "serviceId": null
878
863
  }
879
864
  ]
880
865
  },
@@ -920,17 +905,15 @@ var APPLICATIONS = {
920
905
  "mail.topolo.app"
921
906
  ],
922
907
  "routes": [],
923
- "serviceId": null
908
+ "serviceId": "mail"
924
909
  },
925
910
  {
926
911
  "name": "topolo-mail-api",
927
912
  "kind": "worker",
928
- "apiUrl": "https://mail-api.topolo.app",
929
- "domains": [
930
- "mail-api.topolo.app"
931
- ],
913
+ "apiUrl": null,
914
+ "domains": [],
932
915
  "routes": [],
933
- "serviceId": "mail"
916
+ "serviceId": null
934
917
  }
935
918
  ]
936
919
  },
@@ -947,12 +930,10 @@ var APPLICATIONS = {
947
930
  {
948
931
  "name": "topolo-mdm-api",
949
932
  "kind": "worker",
950
- "apiUrl": "https://mdm-api.topolo.app",
951
- "domains": [
952
- "mdm-api.topolo.app"
953
- ],
933
+ "apiUrl": null,
934
+ "domains": [],
954
935
  "routes": [],
955
- "serviceId": "mdm"
936
+ "serviceId": null
956
937
  },
957
938
  {
958
939
  "name": "topolo-mdm-console",
@@ -962,7 +943,7 @@ var APPLICATIONS = {
962
943
  "mdm.topolo.app"
963
944
  ],
964
945
  "routes": [],
965
- "serviceId": null
946
+ "serviceId": "mdm"
966
947
  }
967
948
  ]
968
949
  },
@@ -1095,20 +1076,23 @@ var APPLICATIONS = {
1095
1076
  {
1096
1077
  "name": "topolo-one",
1097
1078
  "kind": "worker",
1098
- "apiUrl": null,
1099
- "domains": [],
1100
- "routes": [],
1079
+ "apiUrl": "https://one.topolo.app",
1080
+ "domains": [
1081
+ "one.topolo.app"
1082
+ ],
1083
+ "routes": [
1084
+ "one.topolo.app/*"
1085
+ ],
1101
1086
  "serviceId": null
1102
1087
  },
1103
1088
  {
1104
1089
  "name": "topolo-one-api",
1105
1090
  "kind": "worker",
1106
- "apiUrl": "https://one.topolo.app",
1091
+ "apiUrl": "https://www.topolo.app",
1107
1092
  "domains": [
1108
- "one.topolo.app"
1093
+ "www.topolo.app"
1109
1094
  ],
1110
1095
  "routes": [
1111
- "one.topolo.app/*",
1112
1096
  "www.topolo.app/*"
1113
1097
  ],
1114
1098
  "serviceId": "one"
@@ -1218,36 +1202,6 @@ var APPLICATIONS = {
1218
1202
  }
1219
1203
  ]
1220
1204
  },
1221
- "pulse": {
1222
- "id": "pulse",
1223
- "appDir": "TopoloPulse",
1224
- "name": "topolo-pulse",
1225
- "packageName": "topolo-pulse",
1226
- "productionUrl": "https://pulse.topolo.app",
1227
- "services": [
1228
- "pulse"
1229
- ],
1230
- "deployTargets": [
1231
- {
1232
- "name": "topolo-pulse",
1233
- "kind": "worker",
1234
- "apiUrl": "https://pulse.topolo.app",
1235
- "domains": [
1236
- "pulse.topolo.app"
1237
- ],
1238
- "routes": [],
1239
- "serviceId": "pulse"
1240
- },
1241
- {
1242
- "name": "topolo-pulse-api",
1243
- "kind": "worker",
1244
- "apiUrl": null,
1245
- "domains": [],
1246
- "routes": [],
1247
- "serviceId": null
1248
- }
1249
- ]
1250
- },
1251
1205
  "quro": {
1252
1206
  "id": "quro",
1253
1207
  "appDir": "TopoloQuro",
@@ -1313,17 +1267,15 @@ var APPLICATIONS = {
1313
1267
  "roadmapper.topolo.app"
1314
1268
  ],
1315
1269
  "routes": [],
1316
- "serviceId": null
1270
+ "serviceId": "roadmapper"
1317
1271
  },
1318
1272
  {
1319
1273
  "name": "topolo-roadmapper-api",
1320
1274
  "kind": "worker",
1321
- "apiUrl": "https://roadmapper-api.topolo.app",
1322
- "domains": [
1323
- "roadmapper-api.topolo.app"
1324
- ],
1275
+ "apiUrl": null,
1276
+ "domains": [],
1325
1277
  "routes": [],
1326
- "serviceId": "roadmapper"
1278
+ "serviceId": null
1327
1279
  }
1328
1280
  ]
1329
1281
  },
@@ -1375,17 +1327,15 @@ var APPLICATIONS = {
1375
1327
  "studio.topolo.app"
1376
1328
  ],
1377
1329
  "routes": [],
1378
- "serviceId": null
1330
+ "serviceId": "social_studio"
1379
1331
  },
1380
1332
  {
1381
1333
  "name": "topolo-social-studio-api",
1382
1334
  "kind": "worker",
1383
- "apiUrl": "https://studio-api.topolo.app",
1384
- "domains": [
1385
- "studio-api.topolo.app"
1386
- ],
1335
+ "apiUrl": null,
1336
+ "domains": [],
1387
1337
  "routes": [],
1388
- "serviceId": "social_studio"
1338
+ "serviceId": null
1389
1339
  },
1390
1340
  {
1391
1341
  "name": "topolo-social-studio-queue",
@@ -1415,17 +1365,15 @@ var APPLICATIONS = {
1415
1365
  "socialize.topolo.app"
1416
1366
  ],
1417
1367
  "routes": [],
1418
- "serviceId": null
1368
+ "serviceId": "socialize"
1419
1369
  },
1420
1370
  {
1421
1371
  "name": "topolo-socialize-api",
1422
1372
  "kind": "worker",
1423
- "apiUrl": "https://socialize-api.topolo.app",
1424
- "domains": [
1425
- "socialize-api.topolo.app"
1426
- ],
1373
+ "apiUrl": null,
1374
+ "domains": [],
1427
1375
  "routes": [],
1428
- "serviceId": "socialize"
1376
+ "serviceId": null
1429
1377
  },
1430
1378
  {
1431
1379
  "name": "topolo-socialize-scheduler",
@@ -1626,12 +1574,10 @@ var APPLICATIONS = {
1626
1574
  {
1627
1575
  "name": "topolo-web-api",
1628
1576
  "kind": "worker",
1629
- "apiUrl": "https://web-api.topolo.app",
1630
- "domains": [
1631
- "web-api.topolo.app"
1632
- ],
1577
+ "apiUrl": null,
1578
+ "domains": [],
1633
1579
  "routes": [],
1634
- "serviceId": "web"
1580
+ "serviceId": null
1635
1581
  },
1636
1582
  {
1637
1583
  "name": "topolo-web-runtime",
@@ -1654,14 +1600,14 @@ var APPLICATIONS = {
1654
1600
  "web.topolo.app"
1655
1601
  ],
1656
1602
  "routes": [],
1657
- "serviceId": null
1603
+ "serviceId": "web"
1658
1604
  }
1659
1605
  ]
1660
1606
  }
1661
1607
  };
1662
1608
 
1663
1609
  // src/application-requirements.ts
1664
- var APPLICATION_REQUIREMENTS_VERSION = "2026-05-01.1";
1610
+ var APPLICATION_REQUIREMENTS_VERSION = "2026-06-07.1";
1665
1611
  var APPLICATION_REQUIREMENTS = [
1666
1612
  {
1667
1613
  id: "platform-metadata",
@@ -1717,16 +1663,20 @@ var APPLICATION_REQUIREMENTS = [
1717
1663
  summary: "Browser applications should feel like one platform and should not create isolated navigation or account surfaces.",
1718
1664
  appliesTo: ["browser"],
1719
1665
  evidence: [
1720
- "@topolo/ui-kit is used for the platform shell where the app has authenticated UI",
1721
- "The shared launcher/app switcher is reachable from authenticated app chrome",
1722
- "Account menu and sign-out behavior use TopoloAppShell or TopoloAccountMenu",
1666
+ "@topolo-io/app-shell is used for the platform shell where the app has authenticated UI",
1667
+ "@topolo-io/app-launcher keeps the shared launcher/app switcher reachable from authenticated app chrome",
1668
+ "Account menu and sign-out behavior use TopoloAppShell or the ui-kit TopoloAccountMenu primitive",
1669
+ "Repeated app body surfaces use shared TopoloAppLayout primitives instead of local stat cards, empty states, or status badges",
1670
+ "Local preview supports deterministic visual QA fixtures for overview, list, detail, settings, and empty/loading states",
1723
1671
  "Authenticated app body content stays inside the viewport on mobile and desktop widths"
1724
1672
  ],
1725
1673
  implementation: [
1726
- "Use TopoloAppShell for authenticated app layouts",
1727
- "Use TopoloAccountMenu when a partial-shell surface still needs account chrome",
1674
+ "Use @topolo-io/app-shell TopoloAppShell for authenticated app layouts",
1675
+ "Use the @topolo-io/ui-kit TopoloAccountMenu primitive when a partial-shell surface still needs account chrome",
1728
1676
  "Pass app-specific account actions as additive menu items instead of forking dropdowns",
1729
1677
  "Use shared design tokens and platform UI primitives before creating local equivalents",
1678
+ "Use TopoloAppMetricCard, TopoloAppEmptyState, TopoloAppStatusBadge, TopoloAppGrid, and TopoloAppSection for repeated app body patterns",
1679
+ "Keep visual QA fixtures available through visualQa=1 or __devpreview=1 so screenshot checks do not depend on live API state",
1730
1680
  "Keep mobile and desktop shell behavior aligned with the platform UI kit",
1731
1681
  "Constrain app-owned workspace wrappers with min-width: 0, width: auto, and viewport-aware max-width so shell padding cannot create page-level horizontal overflow"
1732
1682
  ]
@@ -1805,11 +1755,13 @@ var APPLICATION_REQUIREMENTS = [
1805
1755
  evidence: [
1806
1756
  "The app-owned API exposes GET /api/widget",
1807
1757
  "The endpoint returns TopoloWidgetApiResponse from @topolo/sdk",
1758
+ "Every returned widget includes snapshot.summary with app-owned operational prose",
1808
1759
  "The endpoint derives organization/user context from the app auth boundary"
1809
1760
  ],
1810
1761
  implementation: [
1811
1762
  "Import createTopoloWidgetResponse from @topolo/sdk in the app backend",
1812
1763
  "Return native app metrics or action widgets instead of generic launch-card fallbacks",
1764
+ "Populate snapshot.summary in the producing app for every widget instead of synthesizing summaries in TopoloOne",
1813
1765
  "Add a route-level test that validates the response with validateTopoloWidgetResponse"
1814
1766
  ]
1815
1767
  },
@@ -2106,6 +2058,8 @@ function firstString(...values) {
2106
2058
  var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
2107
2059
  var TOPOLO_ACTION_ID_HEADER = "X-Topolo-Action-Id";
2108
2060
  var TOPOLO_ACTION_NAME_HEADER = "X-Topolo-Action-Name";
2061
+ var TOPOLO_RESOURCE_TYPE_HEADER = "X-Topolo-Resource-Type";
2062
+ var TOPOLO_RESOURCE_ID_HEADER = "X-Topolo-Resource-ID";
2109
2063
  var POSITIVE_CACHE_TTL_MS = 5 * 60 * 1e3;
2110
2064
  var NEGATIVE_CACHE_TTL_MS = 60 * 1e3;
2111
2065
  var CATALOG_CACHE_TTL_MS = 60 * 1e3;
@@ -2184,6 +2138,18 @@ var TopoloClient = class _TopoloClient {
2184
2138
  if (opts.headers) {
2185
2139
  for (const [k, v] of Object.entries(opts.headers)) headers.set(k, v);
2186
2140
  }
2141
+ if (opts.resource) {
2142
+ const resourceType = opts.resource.resourceType.trim();
2143
+ const resourceId = opts.resource.resourceId.trim();
2144
+ if (!resourceType || !resourceId) {
2145
+ throw new TopoloSdkError(
2146
+ "invalid_resource_context",
2147
+ "Both resource.resourceType and resource.resourceId are required when resource context is provided."
2148
+ );
2149
+ }
2150
+ headers.set(TOPOLO_RESOURCE_TYPE_HEADER, resourceType);
2151
+ headers.set(TOPOLO_RESOURCE_ID_HEADER, resourceId);
2152
+ }
2187
2153
  const controller = new AbortController();
2188
2154
  const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
2189
2155
  const signal = opts.signal ? mergeSignals(opts.signal, controller.signal) : controller.signal;
@@ -2366,6 +2332,11 @@ var TopoloClient = class _TopoloClient {
2366
2332
  const serviceId = typeof s.service_id === "string" ? s.service_id : typeof s.id === "string" ? s.id : null;
2367
2333
  if (!serviceId) continue;
2368
2334
  const permissions = Array.isArray(s.granted_permissions) ? s.granted_permissions.filter((permission) => typeof permission === "string" && permission.trim().length > 0) : [];
2335
+ const catalogApiKeyResources = normalizeApiKeyResourceMetadataList(
2336
+ s.api_key_resources ?? s.apiKeyResources
2337
+ );
2338
+ const apiKeyResource = normalizeApiKeyResourceMetadata(s.api_key_resource ?? s.apiKeyResource) ?? catalogApiKeyResources[0] ?? null;
2339
+ const apiKeyResources = catalogApiKeyResources.length > 0 ? catalogApiKeyResources : apiKeyResource ? [apiKeyResource] : [];
2369
2340
  entries.push({
2370
2341
  serviceId,
2371
2342
  slug: typeof s.app_slug === "string" ? s.app_slug : null,
@@ -2376,7 +2347,9 @@ var TopoloClient = class _TopoloClient {
2376
2347
  status: typeof s.status === "string" ? s.status : null,
2377
2348
  surfaceKind: typeof s.surface_kind === "string" ? s.surface_kind : null,
2378
2349
  launchable: s.launchable === true,
2379
- permissions
2350
+ permissions,
2351
+ apiKeyResource,
2352
+ apiKeyResources
2380
2353
  });
2381
2354
  }
2382
2355
  this.serviceCatalogCache = {
@@ -2462,7 +2435,9 @@ var TopoloClient = class _TopoloClient {
2462
2435
  status: "bundled",
2463
2436
  surfaceKind: "application",
2464
2437
  launchable: Boolean(app.productionUrl),
2465
- permissions: []
2438
+ permissions: [],
2439
+ apiKeyResource: null,
2440
+ apiKeyResources: []
2466
2441
  }));
2467
2442
  }
2468
2443
  /**
@@ -2511,6 +2486,46 @@ function resolveConfiguredServiceId(slug, serviceIds) {
2511
2486
  }
2512
2487
  return null;
2513
2488
  }
2489
+ function normalizeApiKeyResourceMetadata(input) {
2490
+ if (!isRecord(input)) return null;
2491
+ const resourceType = readString(input.resourceType ?? input.resource_type);
2492
+ const label = readString(input.label);
2493
+ const emptyLabel = readString(input.emptyLabel ?? input.empty_label);
2494
+ const resourceConcept = readString(input.resourceConcept ?? input.resource_concept);
2495
+ const canonicalResourceType = readString(input.canonicalResourceType ?? input.canonical_resource_type);
2496
+ const defaultEntitlement = readString(input.defaultEntitlement ?? input.default_entitlement);
2497
+ const exceptionPolicy = readString(input.exceptionPolicy ?? input.exception_policy);
2498
+ if (!resourceType || !label || !emptyLabel || resourceConcept !== "workspace" || canonicalResourceType !== "workspace" || defaultEntitlement !== "enterprise" || exceptionPolicy !== "app_or_organization") {
2499
+ return null;
2500
+ }
2501
+ return {
2502
+ resourceType,
2503
+ label,
2504
+ emptyLabel,
2505
+ resourceConcept,
2506
+ canonicalResourceType,
2507
+ defaultEntitlement,
2508
+ exceptionPolicy
2509
+ };
2510
+ }
2511
+ function normalizeApiKeyResourceMetadataList(input) {
2512
+ if (!Array.isArray(input)) return [];
2513
+ const resources = [];
2514
+ const seenTypes = /* @__PURE__ */ new Set();
2515
+ for (const item of input) {
2516
+ const resource = normalizeApiKeyResourceMetadata(item);
2517
+ if (!resource || seenTypes.has(resource.resourceType)) continue;
2518
+ seenTypes.add(resource.resourceType);
2519
+ resources.push(resource);
2520
+ }
2521
+ return resources;
2522
+ }
2523
+ function readString(value) {
2524
+ return typeof value === "string" ? value.trim() : "";
2525
+ }
2526
+ function isRecord(value) {
2527
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2528
+ }
2514
2529
  function resolveConfiguredServiceUrl(slug, serviceUrls) {
2515
2530
  for (const serviceKey of serviceKeyCandidates2(slug)) {
2516
2531
  const configured = serviceUrls?.[serviceKey];
@@ -3078,7 +3093,7 @@ function createTopoloWidgetError(error) {
3078
3093
  }
3079
3094
  function validateTopoloWidgetResponse(response) {
3080
3095
  const errors = [];
3081
- if (!isRecord(response)) {
3096
+ if (!isRecord2(response)) {
3082
3097
  return { ok: false, errors: ["response must be an object"] };
3083
3098
  }
3084
3099
  if (typeof response.success !== "boolean") {
@@ -3097,7 +3112,7 @@ function validateTopoloWidgetResponse(response) {
3097
3112
  return { ok: errors.length === 0, errors };
3098
3113
  }
3099
3114
  function validateWidget(widget, path, errors) {
3100
- if (!isRecord(widget)) {
3115
+ if (!isRecord2(widget)) {
3101
3116
  errors.push(`${path} must be an object`);
3102
3117
  return;
3103
3118
  }
@@ -3124,7 +3139,7 @@ function validateWidget(widget, path, errors) {
3124
3139
  case "chart":
3125
3140
  validateNonEmptyString(widget.chartType, `${path}.chartType`, errors);
3126
3141
  validateNonEmptyString(widget.title, `${path}.title`, errors);
3127
- if (!isRecord(widget.data)) {
3142
+ if (!isRecord2(widget.data)) {
3128
3143
  errors.push(`${path}.data must be an object`);
3129
3144
  } else {
3130
3145
  validateArray(widget.data.labels, `${path}.data.labels`, errors);
@@ -3137,12 +3152,14 @@ function validateWidget(widget, path, errors) {
3137
3152
  }
3138
3153
  function validateSnapshotMetadata(snapshot, path, errors) {
3139
3154
  if (snapshot === void 0) {
3155
+ errors.push(`${path} must be an object`);
3140
3156
  return;
3141
3157
  }
3142
- if (!isRecord(snapshot)) {
3143
- errors.push(`${path} must be an object when provided`);
3158
+ if (!isRecord2(snapshot)) {
3159
+ errors.push(`${path} must be an object`);
3144
3160
  return;
3145
3161
  }
3162
+ validateNonEmptyString(snapshot.summary, `${path}.summary`, errors);
3146
3163
  if (snapshot.defaultMetricKeys !== void 0 && !Array.isArray(snapshot.defaultMetricKeys)) {
3147
3164
  errors.push(`${path}.defaultMetricKeys must be an array when provided`);
3148
3165
  }
@@ -3158,7 +3175,7 @@ function validateSnapshotMetadata(snapshot, path, errors) {
3158
3175
  }
3159
3176
  snapshot.availableMetrics.forEach((metric, index) => {
3160
3177
  const metricPath = `${path}.availableMetrics[${index}]`;
3161
- if (!isRecord(metric)) {
3178
+ if (!isRecord2(metric)) {
3162
3179
  errors.push(`${metricPath} must be an object`);
3163
3180
  return;
3164
3181
  }
@@ -3179,7 +3196,7 @@ function validateArray(value, path, errors) {
3179
3196
  errors.push(`${path} must be an array`);
3180
3197
  }
3181
3198
  }
3182
- function isRecord(value) {
3199
+ function isRecord2(value) {
3183
3200
  return typeof value === "object" && value !== null && !Array.isArray(value);
3184
3201
  }
3185
3202
  function toIsoTimestamp(input) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topolo/sdk",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "description": "Typed client SDK for the Topolo platform. Used by TopoloCli, TopoloMCP, and third-party agents.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",