@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.cjs CHANGED
@@ -113,54 +113,53 @@ var TopoloHttpError = class extends TopoloSdkError {
113
113
 
114
114
  // src/services.generated.ts
115
115
  var SERVICES = {
116
- agent: "https://agent-api.topolo.app",
116
+ agent: "https://agent.topolo.app",
117
117
  auth: "https://auth.topolo.app",
118
118
  backup: "https://backup.topolo.app",
119
119
  books: "https://books.topolo.app",
120
120
  bugfix: "https://bugfix.topolo.app",
121
- bytes: "https://bytes-api.topolo.app",
121
+ bytes: "https://bytes.topolo.app",
122
122
  calendar: "https://calendar.topolo.app",
123
123
  capacity: "https://capacity.topolo.app",
124
124
  chat: "https://chat.topolo.app",
125
- commerce: "https://commerce-api.topolo.app",
125
+ commerce: "https://commerce.topolo.app",
126
126
  compose: "https://compose.topolo.app",
127
127
  consent: "https://consent.topolo.app",
128
- crm: "https://crm-api.topolo.app",
128
+ crm: "https://crm.topolo.app",
129
129
  developers: "https://developers.topolo.app",
130
130
  director: "https://director.topolo.app",
131
- feed: "https://feed-api.topolo.app",
131
+ feed: "https://feed.topolo.app",
132
132
  flow: "https://flow.topolo.app",
133
- forecast: "https://forecast-api.topolo.app",
133
+ forecast: "https://forecast.topolo.app",
134
134
  forms: "https://forms.topolo.app",
135
135
  home: "https://home.topolo.app",
136
136
  insights: "https://insights.topolo.app",
137
137
  inventory: "https://inventory.topolo.app",
138
- learn: "https://learn-api.topolo.app",
138
+ learn: "https://learn.topolo.app",
139
139
  localize: "https://localize.topolo.app",
140
- mail: "https://mail-api.topolo.app",
141
- mdm: "https://mdm-api.topolo.app",
140
+ mail: "https://mail.topolo.app",
141
+ mdm: "https://mdm.topolo.app",
142
142
  messages: "https://messages.topolo.app",
143
- nexus: "https://nexus-api.topolo.app",
143
+ nexus: "https://nexus.topolo.app",
144
144
  notify: "https://notify.topolo.app",
145
145
  observability_watch: "https://observability-watch.topolo.app",
146
146
  one: "https://www.topolo.app",
147
147
  p2p: "https://p2p.topolo.app",
148
148
  pay: "https://pay.topolo.app",
149
149
  people: "https://people.topolo.app",
150
- pulse: "https://pulse.topolo.app",
151
150
  "quro.api": "https://api.ol0.me",
152
151
  "quro.redirect": "https://ol0.me",
153
- roadmapper: "https://roadmapper-api.topolo.app",
152
+ roadmapper: "https://roadmapper.topolo.app",
154
153
  sign: "https://sign.topolo.app",
155
- social_studio: "https://studio-api.topolo.app",
156
- socialize: "https://socialize-api.topolo.app",
154
+ social_studio: "https://studio.topolo.app",
155
+ socialize: "https://socialize.topolo.app",
157
156
  spaces: "https://spaces.topolo.app",
158
157
  status: "https://status.topolo.app",
159
158
  success: "https://success.topolo.app",
160
159
  support: "https://support.topolo.app",
161
160
  survey: "https://survey.topolo.app",
162
161
  voice: "https://voice.topolo.app",
163
- web: "https://web-api.topolo.app"
162
+ web: "https://web.topolo.app"
164
163
  };
165
164
 
166
165
  // src/applications.generated.ts
@@ -211,17 +210,15 @@ var APPLICATIONS = {
211
210
  "agent.topolo.app"
212
211
  ],
213
212
  "routes": [],
214
- "serviceId": null
213
+ "serviceId": "agent"
215
214
  },
216
215
  {
217
216
  "name": "topolo-agent-api",
218
217
  "kind": "worker",
219
- "apiUrl": "https://agent-api.topolo.app",
220
- "domains": [
221
- "agent-api.topolo.app"
222
- ],
218
+ "apiUrl": null,
219
+ "domains": [],
223
220
  "routes": [],
224
- "serviceId": "agent"
221
+ "serviceId": null
225
222
  }
226
223
  ]
227
224
  },
@@ -341,17 +338,15 @@ var APPLICATIONS = {
341
338
  "bytes.topolo.app"
342
339
  ],
343
340
  "routes": [],
344
- "serviceId": null
341
+ "serviceId": "bytes"
345
342
  },
346
343
  {
347
344
  "name": "topolo-bytes-api",
348
345
  "kind": "worker",
349
- "apiUrl": "https://bytes-api.topolo.app",
350
- "domains": [
351
- "bytes-api.topolo.app"
352
- ],
346
+ "apiUrl": null,
347
+ "domains": [],
353
348
  "routes": [],
354
- "serviceId": "bytes"
349
+ "serviceId": null
355
350
  }
356
351
  ]
357
352
  },
@@ -450,12 +445,10 @@ var APPLICATIONS = {
450
445
  {
451
446
  "name": "topolo-commerce-api",
452
447
  "kind": "worker",
453
- "apiUrl": "https://commerce-api.topolo.app",
454
- "domains": [
455
- "commerce-api.topolo.app"
456
- ],
448
+ "apiUrl": null,
449
+ "domains": [],
457
450
  "routes": [],
458
- "serviceId": "commerce"
451
+ "serviceId": null
459
452
  },
460
453
  {
461
454
  "name": "topolo-commerce-guest-web",
@@ -475,7 +468,7 @@ var APPLICATIONS = {
475
468
  "commerce.topolo.app"
476
469
  ],
477
470
  "routes": [],
478
- "serviceId": null
471
+ "serviceId": "commerce"
479
472
  },
480
473
  {
481
474
  "name": "topolo-commerce-station-web",
@@ -562,12 +555,10 @@ var APPLICATIONS = {
562
555
  {
563
556
  "name": "topolo-crm-api",
564
557
  "kind": "worker",
565
- "apiUrl": "https://crm-api.topolo.app",
566
- "domains": [
567
- "crm-api.topolo.app"
568
- ],
558
+ "apiUrl": null,
559
+ "domains": [],
569
560
  "routes": [],
570
- "serviceId": "crm"
561
+ "serviceId": null
571
562
  },
572
563
  {
573
564
  "name": "topolo-crm-app",
@@ -577,7 +568,7 @@ var APPLICATIONS = {
577
568
  "crm.topolo.app"
578
569
  ],
579
570
  "routes": [],
580
- "serviceId": null
571
+ "serviceId": "crm"
581
572
  }
582
573
  ]
583
574
  },
@@ -705,7 +696,7 @@ var APPLICATIONS = {
705
696
  "feed-api.topolo.app"
706
697
  ],
707
698
  "routes": [],
708
- "serviceId": "feed"
699
+ "serviceId": null
709
700
  },
710
701
  {
711
702
  "name": "topolo-feed-web",
@@ -715,7 +706,7 @@ var APPLICATIONS = {
715
706
  "feed.topolo.app"
716
707
  ],
717
708
  "routes": [],
718
- "serviceId": null
709
+ "serviceId": "feed"
719
710
  }
720
711
  ]
721
712
  },
@@ -767,17 +758,15 @@ var APPLICATIONS = {
767
758
  "forecast.topolo.app"
768
759
  ],
769
760
  "routes": [],
770
- "serviceId": null
761
+ "serviceId": "forecast"
771
762
  },
772
763
  {
773
764
  "name": "topolo-forecast-api",
774
765
  "kind": "worker",
775
- "apiUrl": "https://forecast-api.topolo.app",
776
- "domains": [
777
- "forecast-api.topolo.app"
778
- ],
766
+ "apiUrl": null,
767
+ "domains": [],
779
768
  "routes": [],
780
- "serviceId": "forecast"
769
+ "serviceId": null
781
770
  }
782
771
  ]
783
772
  },
@@ -919,17 +908,15 @@ var APPLICATIONS = {
919
908
  "learn.topolo.app"
920
909
  ],
921
910
  "routes": [],
922
- "serviceId": null
911
+ "serviceId": "learn"
923
912
  },
924
913
  {
925
914
  "name": "topolo-learn-api",
926
915
  "kind": "worker",
927
- "apiUrl": "https://learn-api.topolo.app",
928
- "domains": [
929
- "learn-api.topolo.app"
930
- ],
916
+ "apiUrl": null,
917
+ "domains": [],
931
918
  "routes": [],
932
- "serviceId": "learn"
919
+ "serviceId": null
933
920
  }
934
921
  ]
935
922
  },
@@ -975,17 +962,15 @@ var APPLICATIONS = {
975
962
  "mail.topolo.app"
976
963
  ],
977
964
  "routes": [],
978
- "serviceId": null
965
+ "serviceId": "mail"
979
966
  },
980
967
  {
981
968
  "name": "topolo-mail-api",
982
969
  "kind": "worker",
983
- "apiUrl": "https://mail-api.topolo.app",
984
- "domains": [
985
- "mail-api.topolo.app"
986
- ],
970
+ "apiUrl": null,
971
+ "domains": [],
987
972
  "routes": [],
988
- "serviceId": "mail"
973
+ "serviceId": null
989
974
  }
990
975
  ]
991
976
  },
@@ -1002,12 +987,10 @@ var APPLICATIONS = {
1002
987
  {
1003
988
  "name": "topolo-mdm-api",
1004
989
  "kind": "worker",
1005
- "apiUrl": "https://mdm-api.topolo.app",
1006
- "domains": [
1007
- "mdm-api.topolo.app"
1008
- ],
990
+ "apiUrl": null,
991
+ "domains": [],
1009
992
  "routes": [],
1010
- "serviceId": "mdm"
993
+ "serviceId": null
1011
994
  },
1012
995
  {
1013
996
  "name": "topolo-mdm-console",
@@ -1017,7 +1000,7 @@ var APPLICATIONS = {
1017
1000
  "mdm.topolo.app"
1018
1001
  ],
1019
1002
  "routes": [],
1020
- "serviceId": null
1003
+ "serviceId": "mdm"
1021
1004
  }
1022
1005
  ]
1023
1006
  },
@@ -1082,9 +1065,9 @@ var APPLICATIONS = {
1082
1065
  {
1083
1066
  "name": "topolo-nexus-gateway",
1084
1067
  "kind": "worker",
1085
- "apiUrl": "https://nexus-api.topolo.app",
1068
+ "apiUrl": "https://nexus.topolo.app",
1086
1069
  "domains": [
1087
- "nexus-api.topolo.app"
1070
+ "nexus.topolo.app"
1088
1071
  ],
1089
1072
  "routes": [],
1090
1073
  "serviceId": "nexus"
@@ -1276,36 +1259,6 @@ var APPLICATIONS = {
1276
1259
  }
1277
1260
  ]
1278
1261
  },
1279
- "pulse": {
1280
- "id": "pulse",
1281
- "appDir": "TopoloPulse",
1282
- "name": "topolo-pulse",
1283
- "packageName": "topolo-pulse",
1284
- "productionUrl": "https://pulse.topolo.app",
1285
- "services": [
1286
- "pulse"
1287
- ],
1288
- "deployTargets": [
1289
- {
1290
- "name": "topolo-pulse",
1291
- "kind": "worker",
1292
- "apiUrl": "https://pulse.topolo.app",
1293
- "domains": [
1294
- "pulse.topolo.app"
1295
- ],
1296
- "routes": [],
1297
- "serviceId": "pulse"
1298
- },
1299
- {
1300
- "name": "topolo-pulse-api",
1301
- "kind": "worker",
1302
- "apiUrl": null,
1303
- "domains": [],
1304
- "routes": [],
1305
- "serviceId": null
1306
- }
1307
- ]
1308
- },
1309
1262
  "quro": {
1310
1263
  "id": "quro",
1311
1264
  "appDir": "TopoloQuro",
@@ -1371,17 +1324,15 @@ var APPLICATIONS = {
1371
1324
  "roadmapper.topolo.app"
1372
1325
  ],
1373
1326
  "routes": [],
1374
- "serviceId": null
1327
+ "serviceId": "roadmapper"
1375
1328
  },
1376
1329
  {
1377
1330
  "name": "topolo-roadmapper-api",
1378
1331
  "kind": "worker",
1379
- "apiUrl": "https://roadmapper-api.topolo.app",
1380
- "domains": [
1381
- "roadmapper-api.topolo.app"
1382
- ],
1332
+ "apiUrl": null,
1333
+ "domains": [],
1383
1334
  "routes": [],
1384
- "serviceId": "roadmapper"
1335
+ "serviceId": null
1385
1336
  }
1386
1337
  ]
1387
1338
  },
@@ -1433,17 +1384,15 @@ var APPLICATIONS = {
1433
1384
  "studio.topolo.app"
1434
1385
  ],
1435
1386
  "routes": [],
1436
- "serviceId": null
1387
+ "serviceId": "social_studio"
1437
1388
  },
1438
1389
  {
1439
1390
  "name": "topolo-social-studio-api",
1440
1391
  "kind": "worker",
1441
- "apiUrl": "https://studio-api.topolo.app",
1442
- "domains": [
1443
- "studio-api.topolo.app"
1444
- ],
1392
+ "apiUrl": null,
1393
+ "domains": [],
1445
1394
  "routes": [],
1446
- "serviceId": "social_studio"
1395
+ "serviceId": null
1447
1396
  },
1448
1397
  {
1449
1398
  "name": "topolo-social-studio-queue",
@@ -1473,7 +1422,7 @@ var APPLICATIONS = {
1473
1422
  "socialize.topolo.app"
1474
1423
  ],
1475
1424
  "routes": [],
1476
- "serviceId": null
1425
+ "serviceId": "socialize"
1477
1426
  },
1478
1427
  {
1479
1428
  "name": "topolo-socialize-api",
@@ -1483,7 +1432,7 @@ var APPLICATIONS = {
1483
1432
  "socialize-api.topolo.app"
1484
1433
  ],
1485
1434
  "routes": [],
1486
- "serviceId": "socialize"
1435
+ "serviceId": null
1487
1436
  },
1488
1437
  {
1489
1438
  "name": "topolo-socialize-scheduler",
@@ -1684,12 +1633,10 @@ var APPLICATIONS = {
1684
1633
  {
1685
1634
  "name": "topolo-web-api",
1686
1635
  "kind": "worker",
1687
- "apiUrl": "https://web-api.topolo.app",
1688
- "domains": [
1689
- "web-api.topolo.app"
1690
- ],
1636
+ "apiUrl": null,
1637
+ "domains": [],
1691
1638
  "routes": [],
1692
- "serviceId": "web"
1639
+ "serviceId": null
1693
1640
  },
1694
1641
  {
1695
1642
  "name": "topolo-web-runtime",
@@ -1712,7 +1659,7 @@ var APPLICATIONS = {
1712
1659
  "web.topolo.app"
1713
1660
  ],
1714
1661
  "routes": [],
1715
- "serviceId": null
1662
+ "serviceId": "web"
1716
1663
  }
1717
1664
  ]
1718
1665
  }
@@ -1775,16 +1722,20 @@ var APPLICATION_REQUIREMENTS = [
1775
1722
  summary: "Browser applications should feel like one platform and should not create isolated navigation or account surfaces.",
1776
1723
  appliesTo: ["browser"],
1777
1724
  evidence: [
1778
- "@topolo/ui-kit is used for the platform shell where the app has authenticated UI",
1779
- "The shared launcher/app switcher is reachable from authenticated app chrome",
1780
- "Account menu and sign-out behavior use TopoloAppShell or TopoloAccountMenu",
1725
+ "@topolo-io/app-shell is used for the platform shell where the app has authenticated UI",
1726
+ "@topolo-io/app-launcher keeps the shared launcher/app switcher reachable from authenticated app chrome",
1727
+ "Account menu and sign-out behavior use TopoloAppShell or the ui-kit TopoloAccountMenu primitive",
1728
+ "Repeated app body surfaces use shared TopoloAppLayout primitives instead of local stat cards, empty states, or status badges",
1729
+ "Local preview supports deterministic visual QA fixtures for overview, list, detail, settings, and empty/loading states",
1781
1730
  "Authenticated app body content stays inside the viewport on mobile and desktop widths"
1782
1731
  ],
1783
1732
  implementation: [
1784
- "Use TopoloAppShell for authenticated app layouts",
1785
- "Use TopoloAccountMenu when a partial-shell surface still needs account chrome",
1733
+ "Use @topolo-io/app-shell TopoloAppShell for authenticated app layouts",
1734
+ "Use the @topolo-io/ui-kit TopoloAccountMenu primitive when a partial-shell surface still needs account chrome",
1786
1735
  "Pass app-specific account actions as additive menu items instead of forking dropdowns",
1787
1736
  "Use shared design tokens and platform UI primitives before creating local equivalents",
1737
+ "Use TopoloAppMetricCard, TopoloAppEmptyState, TopoloAppStatusBadge, TopoloAppGrid, and TopoloAppSection for repeated app body patterns",
1738
+ "Keep visual QA fixtures available through visualQa=1 or __devpreview=1 so screenshot checks do not depend on live API state",
1788
1739
  "Keep mobile and desktop shell behavior aligned with the platform UI kit",
1789
1740
  "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"
1790
1741
  ]
@@ -2166,6 +2117,8 @@ function firstString(...values) {
2166
2117
  var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
2167
2118
  var TOPOLO_ACTION_ID_HEADER = "X-Topolo-Action-Id";
2168
2119
  var TOPOLO_ACTION_NAME_HEADER = "X-Topolo-Action-Name";
2120
+ var TOPOLO_RESOURCE_TYPE_HEADER = "X-Topolo-Resource-Type";
2121
+ var TOPOLO_RESOURCE_ID_HEADER = "X-Topolo-Resource-ID";
2169
2122
  var POSITIVE_CACHE_TTL_MS = 5 * 60 * 1e3;
2170
2123
  var NEGATIVE_CACHE_TTL_MS = 60 * 1e3;
2171
2124
  var CATALOG_CACHE_TTL_MS = 60 * 1e3;
@@ -2244,6 +2197,18 @@ var TopoloClient = class _TopoloClient {
2244
2197
  if (opts.headers) {
2245
2198
  for (const [k, v] of Object.entries(opts.headers)) headers.set(k, v);
2246
2199
  }
2200
+ if (opts.resource) {
2201
+ const resourceType = opts.resource.resourceType.trim();
2202
+ const resourceId = opts.resource.resourceId.trim();
2203
+ if (!resourceType || !resourceId) {
2204
+ throw new TopoloSdkError(
2205
+ "invalid_resource_context",
2206
+ "Both resource.resourceType and resource.resourceId are required when resource context is provided."
2207
+ );
2208
+ }
2209
+ headers.set(TOPOLO_RESOURCE_TYPE_HEADER, resourceType);
2210
+ headers.set(TOPOLO_RESOURCE_ID_HEADER, resourceId);
2211
+ }
2247
2212
  const controller = new AbortController();
2248
2213
  const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
2249
2214
  const signal = opts.signal ? mergeSignals(opts.signal, controller.signal) : controller.signal;
@@ -2315,10 +2280,14 @@ var TopoloClient = class _TopoloClient {
2315
2280
  if (!serviceKey) {
2316
2281
  throw new TopoloSdkError("unknown_service", "service is required");
2317
2282
  }
2318
- if (serviceKey === "auth") {
2283
+ if (serviceKey === "auth" || serviceKey === "developers") {
2319
2284
  const overrideUrl = resolveConfiguredServiceUrl(serviceKey, this.serviceUrls);
2320
2285
  if (overrideUrl) return { serviceKey, baseUrl: overrideUrl };
2321
- return { serviceKey, baseUrl: DEFAULT_SERVICE_URLS.auth };
2286
+ if (serviceKey === "developers") {
2287
+ const derived = deriveDevelopersFromAuth(resolveConfiguredServiceUrl("auth", this.serviceUrls));
2288
+ if (derived) return { serviceKey, baseUrl: derived };
2289
+ }
2290
+ return { serviceKey, baseUrl: DEFAULT_SERVICE_URLS[serviceKey] };
2322
2291
  }
2323
2292
  const live = await this.resolveLiveCallableService(serviceKey);
2324
2293
  if (live) {
@@ -2358,7 +2327,7 @@ var TopoloClient = class _TopoloClient {
2358
2327
  if (!normalized) return null;
2359
2328
  const configuredServiceId = resolveConfiguredServiceId(normalized, this.serviceIds);
2360
2329
  if (configuredServiceId) return configuredServiceId;
2361
- const authBase = resolveServiceUrl("auth", this.serviceUrls);
2330
+ const authBase = resolveServiceUrl("developers", this.serviceUrls);
2362
2331
  const cacheKey = `${authBase}|${normalized}`;
2363
2332
  const now = Date.now();
2364
2333
  const cached = _TopoloClient.serviceIdCache.get(cacheKey);
@@ -2412,7 +2381,7 @@ var TopoloClient = class _TopoloClient {
2412
2381
  if (this.serviceCatalogCache && this.serviceCatalogCache.expiresAt > now) {
2413
2382
  return this.serviceCatalogCache.entries;
2414
2383
  }
2415
- const res = await this.request({ service: "auth", method: "GET", path: "/api/services/catalog" });
2384
+ const res = await this.request({ service: "developers", method: "GET", path: "/api/services/catalog" });
2416
2385
  const raw = res.data?.services ?? res.services;
2417
2386
  if (!Array.isArray(raw)) {
2418
2387
  throw new TopoloSdkError(
@@ -2426,6 +2395,11 @@ var TopoloClient = class _TopoloClient {
2426
2395
  const serviceId = typeof s.service_id === "string" ? s.service_id : typeof s.id === "string" ? s.id : null;
2427
2396
  if (!serviceId) continue;
2428
2397
  const permissions = Array.isArray(s.granted_permissions) ? s.granted_permissions.filter((permission) => typeof permission === "string" && permission.trim().length > 0) : [];
2398
+ const catalogApiKeyResources = normalizeApiKeyResourceMetadataList(
2399
+ s.api_key_resources ?? s.apiKeyResources
2400
+ );
2401
+ const apiKeyResource = normalizeApiKeyResourceMetadata(s.api_key_resource ?? s.apiKeyResource) ?? catalogApiKeyResources[0] ?? null;
2402
+ const apiKeyResources = catalogApiKeyResources.length > 0 ? catalogApiKeyResources : apiKeyResource ? [apiKeyResource] : [];
2429
2403
  entries.push({
2430
2404
  serviceId,
2431
2405
  slug: typeof s.app_slug === "string" ? s.app_slug : null,
@@ -2436,7 +2410,9 @@ var TopoloClient = class _TopoloClient {
2436
2410
  status: typeof s.status === "string" ? s.status : null,
2437
2411
  surfaceKind: typeof s.surface_kind === "string" ? s.surface_kind : null,
2438
2412
  launchable: s.launchable === true,
2439
- permissions
2413
+ permissions,
2414
+ apiKeyResource,
2415
+ apiKeyResources
2440
2416
  });
2441
2417
  }
2442
2418
  this.serviceCatalogCache = {
@@ -2522,7 +2498,9 @@ var TopoloClient = class _TopoloClient {
2522
2498
  status: "bundled",
2523
2499
  surfaceKind: "application",
2524
2500
  launchable: Boolean(app.productionUrl),
2525
- permissions: []
2501
+ permissions: [],
2502
+ apiKeyResource: null,
2503
+ apiKeyResources: []
2526
2504
  }));
2527
2505
  }
2528
2506
  /**
@@ -2571,6 +2549,46 @@ function resolveConfiguredServiceId(slug, serviceIds) {
2571
2549
  }
2572
2550
  return null;
2573
2551
  }
2552
+ function normalizeApiKeyResourceMetadata(input) {
2553
+ if (!isRecord(input)) return null;
2554
+ const resourceType = readString(input.resourceType ?? input.resource_type);
2555
+ const label = readString(input.label);
2556
+ const emptyLabel = readString(input.emptyLabel ?? input.empty_label);
2557
+ const resourceConcept = readString(input.resourceConcept ?? input.resource_concept);
2558
+ const canonicalResourceType = readString(input.canonicalResourceType ?? input.canonical_resource_type);
2559
+ const defaultEntitlement = readString(input.defaultEntitlement ?? input.default_entitlement);
2560
+ const exceptionPolicy = readString(input.exceptionPolicy ?? input.exception_policy);
2561
+ if (!resourceType || !label || !emptyLabel || resourceConcept !== "workspace" || canonicalResourceType !== "workspace" || defaultEntitlement !== "enterprise" || exceptionPolicy !== "app_or_organization") {
2562
+ return null;
2563
+ }
2564
+ return {
2565
+ resourceType,
2566
+ label,
2567
+ emptyLabel,
2568
+ resourceConcept,
2569
+ canonicalResourceType,
2570
+ defaultEntitlement,
2571
+ exceptionPolicy
2572
+ };
2573
+ }
2574
+ function normalizeApiKeyResourceMetadataList(input) {
2575
+ if (!Array.isArray(input)) return [];
2576
+ const resources = [];
2577
+ const seenTypes = /* @__PURE__ */ new Set();
2578
+ for (const item of input) {
2579
+ const resource = normalizeApiKeyResourceMetadata(item);
2580
+ if (!resource || seenTypes.has(resource.resourceType)) continue;
2581
+ seenTypes.add(resource.resourceType);
2582
+ resources.push(resource);
2583
+ }
2584
+ return resources;
2585
+ }
2586
+ function readString(value) {
2587
+ return typeof value === "string" ? value.trim() : "";
2588
+ }
2589
+ function isRecord(value) {
2590
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2591
+ }
2574
2592
  function resolveConfiguredServiceUrl(slug, serviceUrls) {
2575
2593
  for (const serviceKey of serviceKeyCandidates2(slug)) {
2576
2594
  const configured = serviceUrls?.[serviceKey];
@@ -2594,6 +2612,17 @@ function serviceKeyCandidates2(slug) {
2594
2612
  function normalizeLookupKey(value) {
2595
2613
  return String(value || "").trim().toLowerCase().replace(/\s+/g, "-");
2596
2614
  }
2615
+ function deriveDevelopersFromAuth(authUrl) {
2616
+ if (!authUrl) return null;
2617
+ try {
2618
+ const url = new URL(authUrl);
2619
+ if (!/^auth\./i.test(url.hostname)) return null;
2620
+ url.hostname = url.hostname.replace(/^auth\./i, "developers.");
2621
+ return url.origin;
2622
+ } catch {
2623
+ return null;
2624
+ }
2625
+ }
2597
2626
  function catalogEntryMatches(entry, needle) {
2598
2627
  return catalogEntryLookupKeys(entry).includes(needle);
2599
2628
  }
@@ -2741,7 +2770,6 @@ var CrmModule = class {
2741
2770
  constructor(client) {
2742
2771
  this.client = client;
2743
2772
  }
2744
- client;
2745
2773
  async listContacts(options = {}) {
2746
2774
  const res = await this.client.request({
2747
2775
  service: "crm",
@@ -2789,7 +2817,6 @@ var IdentityModule = class {
2789
2817
  constructor(client) {
2790
2818
  this.client = client;
2791
2819
  }
2792
- client;
2793
2820
  /** Returns the resolved user, organization, and permission set for the
2794
2821
  * current credential. Equivalent to `TopoloClient.introspect()` — exposed
2795
2822
  * on this module for symmetry with the other domain modules. */
@@ -2803,7 +2830,6 @@ var P2PModule = class {
2803
2830
  constructor(client) {
2804
2831
  this.client = client;
2805
2832
  }
2806
- client;
2807
2833
  // Directory ----------------------------------------------------------------
2808
2834
  async listDirectory(options = {}) {
2809
2835
  const res = await this.client.request({
@@ -3138,7 +3164,7 @@ function createTopoloWidgetError(error) {
3138
3164
  }
3139
3165
  function validateTopoloWidgetResponse(response) {
3140
3166
  const errors = [];
3141
- if (!isRecord(response)) {
3167
+ if (!isRecord2(response)) {
3142
3168
  return { ok: false, errors: ["response must be an object"] };
3143
3169
  }
3144
3170
  if (typeof response.success !== "boolean") {
@@ -3157,7 +3183,7 @@ function validateTopoloWidgetResponse(response) {
3157
3183
  return { ok: errors.length === 0, errors };
3158
3184
  }
3159
3185
  function validateWidget(widget, path, errors) {
3160
- if (!isRecord(widget)) {
3186
+ if (!isRecord2(widget)) {
3161
3187
  errors.push(`${path} must be an object`);
3162
3188
  return;
3163
3189
  }
@@ -3184,7 +3210,7 @@ function validateWidget(widget, path, errors) {
3184
3210
  case "chart":
3185
3211
  validateNonEmptyString(widget.chartType, `${path}.chartType`, errors);
3186
3212
  validateNonEmptyString(widget.title, `${path}.title`, errors);
3187
- if (!isRecord(widget.data)) {
3213
+ if (!isRecord2(widget.data)) {
3188
3214
  errors.push(`${path}.data must be an object`);
3189
3215
  } else {
3190
3216
  validateArray(widget.data.labels, `${path}.data.labels`, errors);
@@ -3200,7 +3226,7 @@ function validateSnapshotMetadata(snapshot, path, errors) {
3200
3226
  errors.push(`${path} must be an object`);
3201
3227
  return;
3202
3228
  }
3203
- if (!isRecord(snapshot)) {
3229
+ if (!isRecord2(snapshot)) {
3204
3230
  errors.push(`${path} must be an object`);
3205
3231
  return;
3206
3232
  }
@@ -3220,7 +3246,7 @@ function validateSnapshotMetadata(snapshot, path, errors) {
3220
3246
  }
3221
3247
  snapshot.availableMetrics.forEach((metric, index) => {
3222
3248
  const metricPath = `${path}.availableMetrics[${index}]`;
3223
- if (!isRecord(metric)) {
3249
+ if (!isRecord2(metric)) {
3224
3250
  errors.push(`${metricPath} must be an object`);
3225
3251
  return;
3226
3252
  }
@@ -3241,7 +3267,7 @@ function validateArray(value, path, errors) {
3241
3267
  errors.push(`${path} must be an array`);
3242
3268
  }
3243
3269
  }
3244
- function isRecord(value) {
3270
+ function isRecord2(value) {
3245
3271
  return typeof value === "object" && value !== null && !Array.isArray(value);
3246
3272
  }
3247
3273
  function toIsoTimestamp(input) {