@topolo/sdk 0.4.7 → 0.4.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
@@ -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
- nexus: "https://nexus-api.topolo.app",
88
+ nexus: "https://nexus.topolo.app",
89
89
  notify: "https://notify.topolo.app",
90
90
  observability_watch: "https://observability-watch.topolo.app",
91
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
  },
@@ -650,7 +641,7 @@ var APPLICATIONS = {
650
641
  "feed-api.topolo.app"
651
642
  ],
652
643
  "routes": [],
653
- "serviceId": "feed"
644
+ "serviceId": null
654
645
  },
655
646
  {
656
647
  "name": "topolo-feed-web",
@@ -660,7 +651,7 @@ var APPLICATIONS = {
660
651
  "feed.topolo.app"
661
652
  ],
662
653
  "routes": [],
663
- "serviceId": null
654
+ "serviceId": "feed"
664
655
  }
665
656
  ]
666
657
  },
@@ -712,17 +703,15 @@ var APPLICATIONS = {
712
703
  "forecast.topolo.app"
713
704
  ],
714
705
  "routes": [],
715
- "serviceId": null
706
+ "serviceId": "forecast"
716
707
  },
717
708
  {
718
709
  "name": "topolo-forecast-api",
719
710
  "kind": "worker",
720
- "apiUrl": "https://forecast-api.topolo.app",
721
- "domains": [
722
- "forecast-api.topolo.app"
723
- ],
711
+ "apiUrl": null,
712
+ "domains": [],
724
713
  "routes": [],
725
- "serviceId": "forecast"
714
+ "serviceId": null
726
715
  }
727
716
  ]
728
717
  },
@@ -864,17 +853,15 @@ var APPLICATIONS = {
864
853
  "learn.topolo.app"
865
854
  ],
866
855
  "routes": [],
867
- "serviceId": null
856
+ "serviceId": "learn"
868
857
  },
869
858
  {
870
859
  "name": "topolo-learn-api",
871
860
  "kind": "worker",
872
- "apiUrl": "https://learn-api.topolo.app",
873
- "domains": [
874
- "learn-api.topolo.app"
875
- ],
861
+ "apiUrl": null,
862
+ "domains": [],
876
863
  "routes": [],
877
- "serviceId": "learn"
864
+ "serviceId": null
878
865
  }
879
866
  ]
880
867
  },
@@ -920,17 +907,15 @@ var APPLICATIONS = {
920
907
  "mail.topolo.app"
921
908
  ],
922
909
  "routes": [],
923
- "serviceId": null
910
+ "serviceId": "mail"
924
911
  },
925
912
  {
926
913
  "name": "topolo-mail-api",
927
914
  "kind": "worker",
928
- "apiUrl": "https://mail-api.topolo.app",
929
- "domains": [
930
- "mail-api.topolo.app"
931
- ],
915
+ "apiUrl": null,
916
+ "domains": [],
932
917
  "routes": [],
933
- "serviceId": "mail"
918
+ "serviceId": null
934
919
  }
935
920
  ]
936
921
  },
@@ -947,12 +932,10 @@ var APPLICATIONS = {
947
932
  {
948
933
  "name": "topolo-mdm-api",
949
934
  "kind": "worker",
950
- "apiUrl": "https://mdm-api.topolo.app",
951
- "domains": [
952
- "mdm-api.topolo.app"
953
- ],
935
+ "apiUrl": null,
936
+ "domains": [],
954
937
  "routes": [],
955
- "serviceId": "mdm"
938
+ "serviceId": null
956
939
  },
957
940
  {
958
941
  "name": "topolo-mdm-console",
@@ -962,7 +945,7 @@ var APPLICATIONS = {
962
945
  "mdm.topolo.app"
963
946
  ],
964
947
  "routes": [],
965
- "serviceId": null
948
+ "serviceId": "mdm"
966
949
  }
967
950
  ]
968
951
  },
@@ -1027,9 +1010,9 @@ var APPLICATIONS = {
1027
1010
  {
1028
1011
  "name": "topolo-nexus-gateway",
1029
1012
  "kind": "worker",
1030
- "apiUrl": "https://nexus-api.topolo.app",
1013
+ "apiUrl": "https://nexus.topolo.app",
1031
1014
  "domains": [
1032
- "nexus-api.topolo.app"
1015
+ "nexus.topolo.app"
1033
1016
  ],
1034
1017
  "routes": [],
1035
1018
  "serviceId": "nexus"
@@ -1221,36 +1204,6 @@ var APPLICATIONS = {
1221
1204
  }
1222
1205
  ]
1223
1206
  },
1224
- "pulse": {
1225
- "id": "pulse",
1226
- "appDir": "TopoloPulse",
1227
- "name": "topolo-pulse",
1228
- "packageName": "topolo-pulse",
1229
- "productionUrl": "https://pulse.topolo.app",
1230
- "services": [
1231
- "pulse"
1232
- ],
1233
- "deployTargets": [
1234
- {
1235
- "name": "topolo-pulse",
1236
- "kind": "worker",
1237
- "apiUrl": "https://pulse.topolo.app",
1238
- "domains": [
1239
- "pulse.topolo.app"
1240
- ],
1241
- "routes": [],
1242
- "serviceId": "pulse"
1243
- },
1244
- {
1245
- "name": "topolo-pulse-api",
1246
- "kind": "worker",
1247
- "apiUrl": null,
1248
- "domains": [],
1249
- "routes": [],
1250
- "serviceId": null
1251
- }
1252
- ]
1253
- },
1254
1207
  "quro": {
1255
1208
  "id": "quro",
1256
1209
  "appDir": "TopoloQuro",
@@ -1316,17 +1269,15 @@ var APPLICATIONS = {
1316
1269
  "roadmapper.topolo.app"
1317
1270
  ],
1318
1271
  "routes": [],
1319
- "serviceId": null
1272
+ "serviceId": "roadmapper"
1320
1273
  },
1321
1274
  {
1322
1275
  "name": "topolo-roadmapper-api",
1323
1276
  "kind": "worker",
1324
- "apiUrl": "https://roadmapper-api.topolo.app",
1325
- "domains": [
1326
- "roadmapper-api.topolo.app"
1327
- ],
1277
+ "apiUrl": null,
1278
+ "domains": [],
1328
1279
  "routes": [],
1329
- "serviceId": "roadmapper"
1280
+ "serviceId": null
1330
1281
  }
1331
1282
  ]
1332
1283
  },
@@ -1378,17 +1329,15 @@ var APPLICATIONS = {
1378
1329
  "studio.topolo.app"
1379
1330
  ],
1380
1331
  "routes": [],
1381
- "serviceId": null
1332
+ "serviceId": "social_studio"
1382
1333
  },
1383
1334
  {
1384
1335
  "name": "topolo-social-studio-api",
1385
1336
  "kind": "worker",
1386
- "apiUrl": "https://studio-api.topolo.app",
1387
- "domains": [
1388
- "studio-api.topolo.app"
1389
- ],
1337
+ "apiUrl": null,
1338
+ "domains": [],
1390
1339
  "routes": [],
1391
- "serviceId": "social_studio"
1340
+ "serviceId": null
1392
1341
  },
1393
1342
  {
1394
1343
  "name": "topolo-social-studio-queue",
@@ -1418,7 +1367,7 @@ var APPLICATIONS = {
1418
1367
  "socialize.topolo.app"
1419
1368
  ],
1420
1369
  "routes": [],
1421
- "serviceId": null
1370
+ "serviceId": "socialize"
1422
1371
  },
1423
1372
  {
1424
1373
  "name": "topolo-socialize-api",
@@ -1428,7 +1377,7 @@ var APPLICATIONS = {
1428
1377
  "socialize-api.topolo.app"
1429
1378
  ],
1430
1379
  "routes": [],
1431
- "serviceId": "socialize"
1380
+ "serviceId": null
1432
1381
  },
1433
1382
  {
1434
1383
  "name": "topolo-socialize-scheduler",
@@ -1629,12 +1578,10 @@ var APPLICATIONS = {
1629
1578
  {
1630
1579
  "name": "topolo-web-api",
1631
1580
  "kind": "worker",
1632
- "apiUrl": "https://web-api.topolo.app",
1633
- "domains": [
1634
- "web-api.topolo.app"
1635
- ],
1581
+ "apiUrl": null,
1582
+ "domains": [],
1636
1583
  "routes": [],
1637
- "serviceId": "web"
1584
+ "serviceId": null
1638
1585
  },
1639
1586
  {
1640
1587
  "name": "topolo-web-runtime",
@@ -1657,7 +1604,7 @@ var APPLICATIONS = {
1657
1604
  "web.topolo.app"
1658
1605
  ],
1659
1606
  "routes": [],
1660
- "serviceId": null
1607
+ "serviceId": "web"
1661
1608
  }
1662
1609
  ]
1663
1610
  }
@@ -1720,16 +1667,20 @@ var APPLICATION_REQUIREMENTS = [
1720
1667
  summary: "Browser applications should feel like one platform and should not create isolated navigation or account surfaces.",
1721
1668
  appliesTo: ["browser"],
1722
1669
  evidence: [
1723
- "@topolo/ui-kit is used for the platform shell where the app has authenticated UI",
1724
- "The shared launcher/app switcher is reachable from authenticated app chrome",
1725
- "Account menu and sign-out behavior use TopoloAppShell or TopoloAccountMenu",
1670
+ "@topolo-io/app-shell is used for the platform shell where the app has authenticated UI",
1671
+ "@topolo-io/app-launcher keeps the shared launcher/app switcher reachable from authenticated app chrome",
1672
+ "Account menu and sign-out behavior use TopoloAppShell or the ui-kit TopoloAccountMenu primitive",
1673
+ "Repeated app body surfaces use shared TopoloAppLayout primitives instead of local stat cards, empty states, or status badges",
1674
+ "Local preview supports deterministic visual QA fixtures for overview, list, detail, settings, and empty/loading states",
1726
1675
  "Authenticated app body content stays inside the viewport on mobile and desktop widths"
1727
1676
  ],
1728
1677
  implementation: [
1729
- "Use TopoloAppShell for authenticated app layouts",
1730
- "Use TopoloAccountMenu when a partial-shell surface still needs account chrome",
1678
+ "Use @topolo-io/app-shell TopoloAppShell for authenticated app layouts",
1679
+ "Use the @topolo-io/ui-kit TopoloAccountMenu primitive when a partial-shell surface still needs account chrome",
1731
1680
  "Pass app-specific account actions as additive menu items instead of forking dropdowns",
1732
1681
  "Use shared design tokens and platform UI primitives before creating local equivalents",
1682
+ "Use TopoloAppMetricCard, TopoloAppEmptyState, TopoloAppStatusBadge, TopoloAppGrid, and TopoloAppSection for repeated app body patterns",
1683
+ "Keep visual QA fixtures available through visualQa=1 or __devpreview=1 so screenshot checks do not depend on live API state",
1733
1684
  "Keep mobile and desktop shell behavior aligned with the platform UI kit",
1734
1685
  "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"
1735
1686
  ]
@@ -2111,6 +2062,8 @@ function firstString(...values) {
2111
2062
  var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
2112
2063
  var TOPOLO_ACTION_ID_HEADER = "X-Topolo-Action-Id";
2113
2064
  var TOPOLO_ACTION_NAME_HEADER = "X-Topolo-Action-Name";
2065
+ var TOPOLO_RESOURCE_TYPE_HEADER = "X-Topolo-Resource-Type";
2066
+ var TOPOLO_RESOURCE_ID_HEADER = "X-Topolo-Resource-ID";
2114
2067
  var POSITIVE_CACHE_TTL_MS = 5 * 60 * 1e3;
2115
2068
  var NEGATIVE_CACHE_TTL_MS = 60 * 1e3;
2116
2069
  var CATALOG_CACHE_TTL_MS = 60 * 1e3;
@@ -2189,6 +2142,18 @@ var TopoloClient = class _TopoloClient {
2189
2142
  if (opts.headers) {
2190
2143
  for (const [k, v] of Object.entries(opts.headers)) headers.set(k, v);
2191
2144
  }
2145
+ if (opts.resource) {
2146
+ const resourceType = opts.resource.resourceType.trim();
2147
+ const resourceId = opts.resource.resourceId.trim();
2148
+ if (!resourceType || !resourceId) {
2149
+ throw new TopoloSdkError(
2150
+ "invalid_resource_context",
2151
+ "Both resource.resourceType and resource.resourceId are required when resource context is provided."
2152
+ );
2153
+ }
2154
+ headers.set(TOPOLO_RESOURCE_TYPE_HEADER, resourceType);
2155
+ headers.set(TOPOLO_RESOURCE_ID_HEADER, resourceId);
2156
+ }
2192
2157
  const controller = new AbortController();
2193
2158
  const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
2194
2159
  const signal = opts.signal ? mergeSignals(opts.signal, controller.signal) : controller.signal;
@@ -2260,10 +2225,14 @@ var TopoloClient = class _TopoloClient {
2260
2225
  if (!serviceKey) {
2261
2226
  throw new TopoloSdkError("unknown_service", "service is required");
2262
2227
  }
2263
- if (serviceKey === "auth") {
2228
+ if (serviceKey === "auth" || serviceKey === "developers") {
2264
2229
  const overrideUrl = resolveConfiguredServiceUrl(serviceKey, this.serviceUrls);
2265
2230
  if (overrideUrl) return { serviceKey, baseUrl: overrideUrl };
2266
- return { serviceKey, baseUrl: DEFAULT_SERVICE_URLS.auth };
2231
+ if (serviceKey === "developers") {
2232
+ const derived = deriveDevelopersFromAuth(resolveConfiguredServiceUrl("auth", this.serviceUrls));
2233
+ if (derived) return { serviceKey, baseUrl: derived };
2234
+ }
2235
+ return { serviceKey, baseUrl: DEFAULT_SERVICE_URLS[serviceKey] };
2267
2236
  }
2268
2237
  const live = await this.resolveLiveCallableService(serviceKey);
2269
2238
  if (live) {
@@ -2303,7 +2272,7 @@ var TopoloClient = class _TopoloClient {
2303
2272
  if (!normalized) return null;
2304
2273
  const configuredServiceId = resolveConfiguredServiceId(normalized, this.serviceIds);
2305
2274
  if (configuredServiceId) return configuredServiceId;
2306
- const authBase = resolveServiceUrl("auth", this.serviceUrls);
2275
+ const authBase = resolveServiceUrl("developers", this.serviceUrls);
2307
2276
  const cacheKey = `${authBase}|${normalized}`;
2308
2277
  const now = Date.now();
2309
2278
  const cached = _TopoloClient.serviceIdCache.get(cacheKey);
@@ -2357,7 +2326,7 @@ var TopoloClient = class _TopoloClient {
2357
2326
  if (this.serviceCatalogCache && this.serviceCatalogCache.expiresAt > now) {
2358
2327
  return this.serviceCatalogCache.entries;
2359
2328
  }
2360
- const res = await this.request({ service: "auth", method: "GET", path: "/api/services/catalog" });
2329
+ const res = await this.request({ service: "developers", method: "GET", path: "/api/services/catalog" });
2361
2330
  const raw = res.data?.services ?? res.services;
2362
2331
  if (!Array.isArray(raw)) {
2363
2332
  throw new TopoloSdkError(
@@ -2371,6 +2340,11 @@ var TopoloClient = class _TopoloClient {
2371
2340
  const serviceId = typeof s.service_id === "string" ? s.service_id : typeof s.id === "string" ? s.id : null;
2372
2341
  if (!serviceId) continue;
2373
2342
  const permissions = Array.isArray(s.granted_permissions) ? s.granted_permissions.filter((permission) => typeof permission === "string" && permission.trim().length > 0) : [];
2343
+ const catalogApiKeyResources = normalizeApiKeyResourceMetadataList(
2344
+ s.api_key_resources ?? s.apiKeyResources
2345
+ );
2346
+ const apiKeyResource = normalizeApiKeyResourceMetadata(s.api_key_resource ?? s.apiKeyResource) ?? catalogApiKeyResources[0] ?? null;
2347
+ const apiKeyResources = catalogApiKeyResources.length > 0 ? catalogApiKeyResources : apiKeyResource ? [apiKeyResource] : [];
2374
2348
  entries.push({
2375
2349
  serviceId,
2376
2350
  slug: typeof s.app_slug === "string" ? s.app_slug : null,
@@ -2381,7 +2355,9 @@ var TopoloClient = class _TopoloClient {
2381
2355
  status: typeof s.status === "string" ? s.status : null,
2382
2356
  surfaceKind: typeof s.surface_kind === "string" ? s.surface_kind : null,
2383
2357
  launchable: s.launchable === true,
2384
- permissions
2358
+ permissions,
2359
+ apiKeyResource,
2360
+ apiKeyResources
2385
2361
  });
2386
2362
  }
2387
2363
  this.serviceCatalogCache = {
@@ -2467,7 +2443,9 @@ var TopoloClient = class _TopoloClient {
2467
2443
  status: "bundled",
2468
2444
  surfaceKind: "application",
2469
2445
  launchable: Boolean(app.productionUrl),
2470
- permissions: []
2446
+ permissions: [],
2447
+ apiKeyResource: null,
2448
+ apiKeyResources: []
2471
2449
  }));
2472
2450
  }
2473
2451
  /**
@@ -2516,6 +2494,46 @@ function resolveConfiguredServiceId(slug, serviceIds) {
2516
2494
  }
2517
2495
  return null;
2518
2496
  }
2497
+ function normalizeApiKeyResourceMetadata(input) {
2498
+ if (!isRecord(input)) return null;
2499
+ const resourceType = readString(input.resourceType ?? input.resource_type);
2500
+ const label = readString(input.label);
2501
+ const emptyLabel = readString(input.emptyLabel ?? input.empty_label);
2502
+ const resourceConcept = readString(input.resourceConcept ?? input.resource_concept);
2503
+ const canonicalResourceType = readString(input.canonicalResourceType ?? input.canonical_resource_type);
2504
+ const defaultEntitlement = readString(input.defaultEntitlement ?? input.default_entitlement);
2505
+ const exceptionPolicy = readString(input.exceptionPolicy ?? input.exception_policy);
2506
+ if (!resourceType || !label || !emptyLabel || resourceConcept !== "workspace" || canonicalResourceType !== "workspace" || defaultEntitlement !== "enterprise" || exceptionPolicy !== "app_or_organization") {
2507
+ return null;
2508
+ }
2509
+ return {
2510
+ resourceType,
2511
+ label,
2512
+ emptyLabel,
2513
+ resourceConcept,
2514
+ canonicalResourceType,
2515
+ defaultEntitlement,
2516
+ exceptionPolicy
2517
+ };
2518
+ }
2519
+ function normalizeApiKeyResourceMetadataList(input) {
2520
+ if (!Array.isArray(input)) return [];
2521
+ const resources = [];
2522
+ const seenTypes = /* @__PURE__ */ new Set();
2523
+ for (const item of input) {
2524
+ const resource = normalizeApiKeyResourceMetadata(item);
2525
+ if (!resource || seenTypes.has(resource.resourceType)) continue;
2526
+ seenTypes.add(resource.resourceType);
2527
+ resources.push(resource);
2528
+ }
2529
+ return resources;
2530
+ }
2531
+ function readString(value) {
2532
+ return typeof value === "string" ? value.trim() : "";
2533
+ }
2534
+ function isRecord(value) {
2535
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2536
+ }
2519
2537
  function resolveConfiguredServiceUrl(slug, serviceUrls) {
2520
2538
  for (const serviceKey of serviceKeyCandidates2(slug)) {
2521
2539
  const configured = serviceUrls?.[serviceKey];
@@ -2539,6 +2557,17 @@ function serviceKeyCandidates2(slug) {
2539
2557
  function normalizeLookupKey(value) {
2540
2558
  return String(value || "").trim().toLowerCase().replace(/\s+/g, "-");
2541
2559
  }
2560
+ function deriveDevelopersFromAuth(authUrl) {
2561
+ if (!authUrl) return null;
2562
+ try {
2563
+ const url = new URL(authUrl);
2564
+ if (!/^auth\./i.test(url.hostname)) return null;
2565
+ url.hostname = url.hostname.replace(/^auth\./i, "developers.");
2566
+ return url.origin;
2567
+ } catch {
2568
+ return null;
2569
+ }
2570
+ }
2542
2571
  function catalogEntryMatches(entry, needle) {
2543
2572
  return catalogEntryLookupKeys(entry).includes(needle);
2544
2573
  }
@@ -2686,7 +2715,6 @@ var CrmModule = class {
2686
2715
  constructor(client) {
2687
2716
  this.client = client;
2688
2717
  }
2689
- client;
2690
2718
  async listContacts(options = {}) {
2691
2719
  const res = await this.client.request({
2692
2720
  service: "crm",
@@ -2734,7 +2762,6 @@ var IdentityModule = class {
2734
2762
  constructor(client) {
2735
2763
  this.client = client;
2736
2764
  }
2737
- client;
2738
2765
  /** Returns the resolved user, organization, and permission set for the
2739
2766
  * current credential. Equivalent to `TopoloClient.introspect()` — exposed
2740
2767
  * on this module for symmetry with the other domain modules. */
@@ -2748,7 +2775,6 @@ var P2PModule = class {
2748
2775
  constructor(client) {
2749
2776
  this.client = client;
2750
2777
  }
2751
- client;
2752
2778
  // Directory ----------------------------------------------------------------
2753
2779
  async listDirectory(options = {}) {
2754
2780
  const res = await this.client.request({
@@ -3083,7 +3109,7 @@ function createTopoloWidgetError(error) {
3083
3109
  }
3084
3110
  function validateTopoloWidgetResponse(response) {
3085
3111
  const errors = [];
3086
- if (!isRecord(response)) {
3112
+ if (!isRecord2(response)) {
3087
3113
  return { ok: false, errors: ["response must be an object"] };
3088
3114
  }
3089
3115
  if (typeof response.success !== "boolean") {
@@ -3102,7 +3128,7 @@ function validateTopoloWidgetResponse(response) {
3102
3128
  return { ok: errors.length === 0, errors };
3103
3129
  }
3104
3130
  function validateWidget(widget, path, errors) {
3105
- if (!isRecord(widget)) {
3131
+ if (!isRecord2(widget)) {
3106
3132
  errors.push(`${path} must be an object`);
3107
3133
  return;
3108
3134
  }
@@ -3129,7 +3155,7 @@ function validateWidget(widget, path, errors) {
3129
3155
  case "chart":
3130
3156
  validateNonEmptyString(widget.chartType, `${path}.chartType`, errors);
3131
3157
  validateNonEmptyString(widget.title, `${path}.title`, errors);
3132
- if (!isRecord(widget.data)) {
3158
+ if (!isRecord2(widget.data)) {
3133
3159
  errors.push(`${path}.data must be an object`);
3134
3160
  } else {
3135
3161
  validateArray(widget.data.labels, `${path}.data.labels`, errors);
@@ -3145,7 +3171,7 @@ function validateSnapshotMetadata(snapshot, path, errors) {
3145
3171
  errors.push(`${path} must be an object`);
3146
3172
  return;
3147
3173
  }
3148
- if (!isRecord(snapshot)) {
3174
+ if (!isRecord2(snapshot)) {
3149
3175
  errors.push(`${path} must be an object`);
3150
3176
  return;
3151
3177
  }
@@ -3165,7 +3191,7 @@ function validateSnapshotMetadata(snapshot, path, errors) {
3165
3191
  }
3166
3192
  snapshot.availableMetrics.forEach((metric, index) => {
3167
3193
  const metricPath = `${path}.availableMetrics[${index}]`;
3168
- if (!isRecord(metric)) {
3194
+ if (!isRecord2(metric)) {
3169
3195
  errors.push(`${metricPath} must be an object`);
3170
3196
  return;
3171
3197
  }
@@ -3186,7 +3212,7 @@ function validateArray(value, path, errors) {
3186
3212
  errors.push(`${path} must be an array`);
3187
3213
  }
3188
3214
  }
3189
- function isRecord(value) {
3215
+ function isRecord2(value) {
3190
3216
  return typeof value === "object" && value !== null && !Array.isArray(value);
3191
3217
  }
3192
3218
  function toIsoTimestamp(input) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topolo/sdk",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
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",