@topolo/sdk 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -94,6 +94,29 @@ that contract into catalog-backed conformance reports and migration-queue items.
94
94
  Catalog-visible evidence is marked directly; implementation details that require
95
95
  code or docs inspection are deliberately marked `needs_review`.
96
96
 
97
+ ## Native dashboard widgets
98
+
99
+ Launchable first-party applications populate the TopoloOne live workspace through
100
+ their own `GET /api/widget` endpoint. The SDK owns the shared payload contract:
101
+
102
+ ```ts
103
+ import { createTopoloWidgetResponse } from '@topolo/sdk';
104
+
105
+ return Response.json(createTopoloWidgetResponse({
106
+ serviceId: 'srv_topolo_example',
107
+ serviceName: 'Example',
108
+ widgets: [
109
+ {
110
+ type: 'stats',
111
+ stats: [{ label: 'Open items', value: 3 }],
112
+ },
113
+ ],
114
+ }));
115
+ ```
116
+
117
+ Use `validateTopoloWidgetResponse()` in endpoint tests to prevent app-local
118
+ payload drift.
119
+
97
120
  ## Escape hatch
98
121
 
99
122
  ```ts
package/dist/index.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  var __defProp = Object.defineProperty;
2
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -26,6 +27,8 @@ __export(index_exports, {
26
27
  DEFAULT_SERVICE_URLS: () => DEFAULT_SERVICE_URLS,
27
28
  IdentityModule: () => IdentityModule,
28
29
  PLATFORM_SERVICE_IDS: () => PLATFORM_SERVICE_IDS,
30
+ TOPOLO_WIDGET_ENDPOINT: () => TOPOLO_WIDGET_ENDPOINT,
31
+ TOPOLO_WIDGET_REFRESH_INTERVAL_MS: () => TOPOLO_WIDGET_REFRESH_INTERVAL_MS,
29
32
  TopoloAuthError: () => TopoloAuthError,
30
33
  TopoloClient: () => TopoloClient,
31
34
  TopoloHttpError: () => TopoloHttpError,
@@ -36,8 +39,16 @@ __export(index_exports, {
36
39
  auditAllApplicationRequirements: () => auditAllApplicationRequirements,
37
40
  auditApplicationRequirements: () => auditApplicationRequirements,
38
41
  createTopolo: () => createTopolo,
42
+ createTopoloWidgetError: () => createTopoloWidgetError,
43
+ createTopoloWidgetResponse: () => createTopoloWidgetResponse,
44
+ defineTopoloChartWidget: () => defineTopoloChartWidget,
45
+ defineTopoloNotificationsWidget: () => defineTopoloNotificationsWidget,
46
+ defineTopoloQuickActionsWidget: () => defineTopoloQuickActionsWidget,
47
+ defineTopoloRecentActivityWidget: () => defineTopoloRecentActivityWidget,
48
+ defineTopoloStatsWidget: () => defineTopoloStatsWidget,
39
49
  requirementsForApplication: () => requirementsForApplication,
40
- resolveServiceUrl: () => resolveServiceUrl
50
+ resolveServiceUrl: () => resolveServiceUrl,
51
+ validateTopoloWidgetResponse: () => validateTopoloWidgetResponse
41
52
  });
42
53
  module.exports = __toCommonJS(index_exports);
43
54
 
@@ -103,14 +114,20 @@ var TopoloHttpError = class extends TopoloSdkError {
103
114
  var SERVICES = {
104
115
  agent: "https://topolo-agent-worker.topolo.workers.dev",
105
116
  auth: "https://auth.topolo.app",
117
+ books: "https://books.topolo.app",
106
118
  bugfix: "https://bugfix.topolo.app",
107
119
  bytes: "https://r2-media-manager-worker.topolo.workers.dev",
108
120
  calendar: "https://calendar.topolo.app",
109
121
  chat: "https://chat.topolo.app",
110
122
  commerce: "https://topolo-commerce-api.topolo.workers.dev",
111
123
  crm: "https://crm-api.topolo.app",
124
+ customer_success: "https://customer-success.topolo.app",
112
125
  feed: "https://topolo-feed-api.topolo.app",
126
+ flow: "https://flow.topolo.app",
113
127
  forecast: "https://forecast-worker-production.topolo.workers.dev",
128
+ forms: "https://forms.topolo.app",
129
+ insights: "https://insights.topolo.app",
130
+ inventory: "https://inventory.topolo.app",
114
131
  learn: "https://topololearn-api.topolo.workers.dev",
115
132
  mail: "https://mail.topolo.app",
116
133
  mdm: "https://topolo-mdm-api.topolo.app",
@@ -118,16 +135,30 @@ var SERVICES = {
118
135
  nexus: "https://topolo-nexus-gateway.topolo.workers.dev",
119
136
  one: "https://one.topolo.app",
120
137
  pay: "https://topolo-pay.topolo.workers.dev",
138
+ people: "https://people.topolo.app",
121
139
  "quro.api": "https://api.ol0.me",
122
140
  "quro.redirect": "https://ol0.me",
123
141
  roadmapper: "https://roadmapper-api.topolo.app",
142
+ sign: "https://sign.topolo.app",
124
143
  social_studio: "https://topolo-social-studio-api.topolo.workers.dev",
125
144
  socialize: "https://socialize-prod-api.topolo.workers.dev",
126
145
  support: "https://support.topolo.app",
146
+ survey: "https://survey.topolo.app",
147
+ voice: "https://voice.topolo.app",
127
148
  web: "https://web-api.topolo.app"
128
149
  };
129
150
  var PLATFORM_SERVICE_IDS = {
130
- crm: "srv_iCwM4jGXcwlj"
151
+ books: "srv_topolo_books",
152
+ crm: "srv_iCwM4jGXcwlj",
153
+ customer_success: "srv_topolo_customer_success",
154
+ flow: "srv_topolo_flow",
155
+ forms: "srv_topolo_forms",
156
+ insights: "srv_topolo_insights",
157
+ inventory: "srv_topolo_inventory",
158
+ people: "srv_topolo_people",
159
+ sign: "srv_topolo_sign",
160
+ survey: "srv_topolo_survey",
161
+ voice: "srv_topolo_voice"
131
162
  };
132
163
 
133
164
  // src/applications.generated.ts
@@ -202,6 +233,30 @@ var APPLICATIONS = {
202
233
  }
203
234
  ]
204
235
  },
236
+ "books": {
237
+ "id": "books",
238
+ "appDir": "TopoloBooks",
239
+ "name": "topolo-books",
240
+ "packageName": "topolo-books",
241
+ "productionUrl": "https://books.topolo.app",
242
+ "services": [
243
+ "books"
244
+ ],
245
+ "deployTargets": [
246
+ {
247
+ "name": "topolo-books",
248
+ "kind": "worker",
249
+ "apiUrl": "https://books.topolo.app",
250
+ "domains": [
251
+ "books.topolo.app"
252
+ ],
253
+ "routes": [
254
+ "books.topolo.app"
255
+ ],
256
+ "serviceId": "books"
257
+ }
258
+ ]
259
+ },
205
260
  "bugfix": {
206
261
  "id": "bugfix",
207
262
  "appDir": "TopoloBugFix",
@@ -403,6 +458,30 @@ var APPLICATIONS = {
403
458
  }
404
459
  ]
405
460
  },
461
+ "customer_success": {
462
+ "id": "customer_success",
463
+ "appDir": "TopoloCustomerSuccess",
464
+ "name": "topolo-customer-success",
465
+ "packageName": "topolo-customer-success",
466
+ "productionUrl": "https://customer-success.topolo.app",
467
+ "services": [
468
+ "customer_success"
469
+ ],
470
+ "deployTargets": [
471
+ {
472
+ "name": "topolo-customer-success",
473
+ "kind": "worker",
474
+ "apiUrl": "https://customer-success.topolo.app",
475
+ "domains": [
476
+ "customer-success.topolo.app"
477
+ ],
478
+ "routes": [
479
+ "customer-success.topolo.app"
480
+ ],
481
+ "serviceId": "customer_success"
482
+ }
483
+ ]
484
+ },
406
485
  "developers": {
407
486
  "id": "developers",
408
487
  "appDir": "TopoloDevelopers",
@@ -479,6 +558,30 @@ var APPLICATIONS = {
479
558
  }
480
559
  ]
481
560
  },
561
+ "flow": {
562
+ "id": "flow",
563
+ "appDir": "TopoloFlow",
564
+ "name": "topolo-flow",
565
+ "packageName": "topolo-flow",
566
+ "productionUrl": "https://flow.topolo.app",
567
+ "services": [
568
+ "flow"
569
+ ],
570
+ "deployTargets": [
571
+ {
572
+ "name": "topolo-flow",
573
+ "kind": "worker",
574
+ "apiUrl": "https://flow.topolo.app",
575
+ "domains": [
576
+ "flow.topolo.app"
577
+ ],
578
+ "routes": [
579
+ "flow.topolo.app"
580
+ ],
581
+ "serviceId": "flow"
582
+ }
583
+ ]
584
+ },
482
585
  "forecast": {
483
586
  "id": "forecast",
484
587
  "appDir": "TopoloForecast",
@@ -507,6 +610,78 @@ var APPLICATIONS = {
507
610
  }
508
611
  ]
509
612
  },
613
+ "forms": {
614
+ "id": "forms",
615
+ "appDir": "TopoloForms",
616
+ "name": "topolo-forms",
617
+ "packageName": "topolo-forms",
618
+ "productionUrl": "https://forms.topolo.app",
619
+ "services": [
620
+ "forms"
621
+ ],
622
+ "deployTargets": [
623
+ {
624
+ "name": "topolo-forms",
625
+ "kind": "worker",
626
+ "apiUrl": "https://forms.topolo.app",
627
+ "domains": [
628
+ "forms.topolo.app"
629
+ ],
630
+ "routes": [
631
+ "forms.topolo.app"
632
+ ],
633
+ "serviceId": "forms"
634
+ }
635
+ ]
636
+ },
637
+ "insights": {
638
+ "id": "insights",
639
+ "appDir": "TopoloInsights",
640
+ "name": "topolo-insights",
641
+ "packageName": "topolo-insights",
642
+ "productionUrl": "https://insights.topolo.app",
643
+ "services": [
644
+ "insights"
645
+ ],
646
+ "deployTargets": [
647
+ {
648
+ "name": "topolo-insights",
649
+ "kind": "worker",
650
+ "apiUrl": "https://insights.topolo.app",
651
+ "domains": [
652
+ "insights.topolo.app"
653
+ ],
654
+ "routes": [
655
+ "insights.topolo.app"
656
+ ],
657
+ "serviceId": "insights"
658
+ }
659
+ ]
660
+ },
661
+ "inventory": {
662
+ "id": "inventory",
663
+ "appDir": "TopoloInventory",
664
+ "name": "topolo-inventory",
665
+ "packageName": "topolo-inventory",
666
+ "productionUrl": "https://inventory.topolo.app",
667
+ "services": [
668
+ "inventory"
669
+ ],
670
+ "deployTargets": [
671
+ {
672
+ "name": "topolo-inventory",
673
+ "kind": "worker",
674
+ "apiUrl": "https://inventory.topolo.app",
675
+ "domains": [
676
+ "inventory.topolo.app"
677
+ ],
678
+ "routes": [
679
+ "inventory.topolo.app"
680
+ ],
681
+ "serviceId": "inventory"
682
+ }
683
+ ]
684
+ },
510
685
  "learn": {
511
686
  "id": "learn",
512
687
  "appDir": "TopoloLearn",
@@ -707,7 +882,9 @@ var APPLICATIONS = {
707
882
  "kind": "worker",
708
883
  "apiUrl": null,
709
884
  "domains": [
710
- "one.topolo.io"
885
+ "one.topolo.io",
886
+ "topolo.io",
887
+ "www.topolo.io"
711
888
  ],
712
889
  "routes": [],
713
890
  "serviceId": null
@@ -744,6 +921,30 @@ var APPLICATIONS = {
744
921
  }
745
922
  ]
746
923
  },
924
+ "people": {
925
+ "id": "people",
926
+ "appDir": "TopoloPeople",
927
+ "name": "topolo-people",
928
+ "packageName": "topolo-people",
929
+ "productionUrl": "https://people.topolo.app",
930
+ "services": [
931
+ "people"
932
+ ],
933
+ "deployTargets": [
934
+ {
935
+ "name": "topolo-people",
936
+ "kind": "worker",
937
+ "apiUrl": "https://people.topolo.app",
938
+ "domains": [
939
+ "people.topolo.app"
940
+ ],
941
+ "routes": [
942
+ "people.topolo.app"
943
+ ],
944
+ "serviceId": "people"
945
+ }
946
+ ]
947
+ },
747
948
  "quro": {
748
949
  "id": "quro",
749
950
  "appDir": "TopoloQuro",
@@ -815,6 +1016,30 @@ var APPLICATIONS = {
815
1016
  }
816
1017
  ]
817
1018
  },
1019
+ "sign": {
1020
+ "id": "sign",
1021
+ "appDir": "TopoloSign",
1022
+ "name": "topolo-sign",
1023
+ "packageName": "topolo-sign",
1024
+ "productionUrl": "https://sign.topolo.app",
1025
+ "services": [
1026
+ "sign"
1027
+ ],
1028
+ "deployTargets": [
1029
+ {
1030
+ "name": "topolo-sign",
1031
+ "kind": "worker",
1032
+ "apiUrl": "https://sign.topolo.app",
1033
+ "domains": [
1034
+ "sign.topolo.app"
1035
+ ],
1036
+ "routes": [
1037
+ "sign.topolo.app"
1038
+ ],
1039
+ "serviceId": "sign"
1040
+ }
1041
+ ]
1042
+ },
818
1043
  "social_studio": {
819
1044
  "id": "social_studio",
820
1045
  "appDir": "TopoloSocialStudio",
@@ -911,6 +1136,54 @@ var APPLICATIONS = {
911
1136
  }
912
1137
  ]
913
1138
  },
1139
+ "survey": {
1140
+ "id": "survey",
1141
+ "appDir": "TopoloSurvey",
1142
+ "name": "topolo-survey",
1143
+ "packageName": "topolo-survey",
1144
+ "productionUrl": "https://survey.topolo.app",
1145
+ "services": [
1146
+ "survey"
1147
+ ],
1148
+ "deployTargets": [
1149
+ {
1150
+ "name": "topolo-survey",
1151
+ "kind": "worker",
1152
+ "apiUrl": "https://survey.topolo.app",
1153
+ "domains": [
1154
+ "survey.topolo.app"
1155
+ ],
1156
+ "routes": [
1157
+ "survey.topolo.app"
1158
+ ],
1159
+ "serviceId": "survey"
1160
+ }
1161
+ ]
1162
+ },
1163
+ "voice": {
1164
+ "id": "voice",
1165
+ "appDir": "TopoloVoice",
1166
+ "name": "topolo-voice",
1167
+ "packageName": "topolo-voice",
1168
+ "productionUrl": "https://voice.topolo.app",
1169
+ "services": [
1170
+ "voice"
1171
+ ],
1172
+ "deployTargets": [
1173
+ {
1174
+ "name": "topolo-voice",
1175
+ "kind": "worker",
1176
+ "apiUrl": "https://voice.topolo.app",
1177
+ "domains": [
1178
+ "voice.topolo.app"
1179
+ ],
1180
+ "routes": [
1181
+ "voice.topolo.app"
1182
+ ],
1183
+ "serviceId": "voice"
1184
+ }
1185
+ ]
1186
+ },
914
1187
  "web": {
915
1188
  "id": "web",
916
1189
  "appDir": "TopoloWeb",
@@ -956,7 +1229,7 @@ var APPLICATIONS = {
956
1229
  };
957
1230
 
958
1231
  // src/application-requirements.ts
959
- var APPLICATION_REQUIREMENTS_VERSION = "2026-04-22.1";
1232
+ var APPLICATION_REQUIREMENTS_VERSION = "2026-04-25.1";
960
1233
  var APPLICATION_REQUIREMENTS = [
961
1234
  {
962
1235
  id: "platform-metadata",
@@ -1088,6 +1361,22 @@ var APPLICATION_REQUIREMENTS = [
1088
1361
  "Keep command output JSON-stable for agents and human-readable with --no-json"
1089
1362
  ]
1090
1363
  },
1364
+ {
1365
+ id: "native-dashboard-widget",
1366
+ title: "Expose a native dashboard widget contract",
1367
+ summary: "Launchable first-party applications must populate TopoloOne live workspace through an app-owned /api/widget endpoint using the SDK widget response contract.",
1368
+ appliesTo: ["api"],
1369
+ evidence: [
1370
+ "The app-owned API exposes GET /api/widget",
1371
+ "The endpoint returns TopoloWidgetApiResponse from @topolo/sdk",
1372
+ "The endpoint derives organization/user context from the app auth boundary"
1373
+ ],
1374
+ implementation: [
1375
+ "Import createTopoloWidgetResponse from @topolo/sdk in the app backend",
1376
+ "Return native app metrics or action widgets instead of generic launch-card fallbacks",
1377
+ "Add a route-level test that validates the response with validateTopoloWidgetResponse"
1378
+ ]
1379
+ },
1091
1380
  {
1092
1381
  id: "observability-and-audit",
1093
1382
  title: "Emit platform audit and debugging signals",
@@ -1123,9 +1412,8 @@ var APPLICATION_REQUIREMENTS = [
1123
1412
  ];
1124
1413
  function applicationRequirementScopes(application) {
1125
1414
  const scopes = /* @__PURE__ */ new Set(["all"]);
1126
- const deployTargets = application.deployTargets;
1127
- const hasBrowserSurface = Boolean(application.productionUrl) || deployTargets.some((target) => target.kind === "pages");
1128
- const hasApiSurface = application.services.length > 0 || deployTargets.some((target) => Boolean(target.apiUrl));
1415
+ const hasBrowserSurface = Boolean(application.productionUrl) || application.deployTargets.some((target) => target.kind === "pages");
1416
+ const hasApiSurface = application.services.length > 0 || application.deployTargets.some((target) => Boolean(target.apiUrl));
1129
1417
  if (hasBrowserSurface) scopes.add("browser");
1130
1418
  if (hasApiSurface) scopes.add("api");
1131
1419
  if (application.id === "cli" || application.id === "mcp" || application.id === "cloudcontrol") {
@@ -1260,6 +1548,13 @@ function evaluateRequirement(application, requirement) {
1260
1548
  }
1261
1549
  return finding(requirement, "missing", "No SDK service or tooling surface is registered.", evidence);
1262
1550
  }
1551
+ case "native-dashboard-widget":
1552
+ return finding(
1553
+ requirement,
1554
+ "needs_review",
1555
+ "Catalog metadata cannot verify app-owned /api/widget implementation details.",
1556
+ evidence
1557
+ );
1263
1558
  case "observability-and-audit":
1264
1559
  return finding(
1265
1560
  requirement,
@@ -1313,11 +1608,10 @@ function statusRank(status) {
1313
1608
  // src/services.ts
1314
1609
  var DEFAULT_SERVICE_URLS = SERVICES;
1315
1610
  function resolveServiceUrl(service, overrides) {
1316
- var _a;
1317
- const override = overrides == null ? void 0 : overrides[service];
1611
+ const override = overrides?.[service];
1318
1612
  if (override) return override;
1319
1613
  const envKey = `TOPOLO_SERVICE_URL_${service.replace(/\./g, "_").toUpperCase()}`;
1320
- const envValue = typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a[envKey] : void 0;
1614
+ const envValue = typeof process !== "undefined" ? process.env?.[envKey] : void 0;
1321
1615
  if (envValue) return envValue;
1322
1616
  return DEFAULT_SERVICE_URLS[service];
1323
1617
  }
@@ -1333,9 +1627,8 @@ var TopoloClient = class {
1333
1627
  fetchImpl;
1334
1628
  debug;
1335
1629
  constructor(options) {
1336
- var _a;
1337
1630
  if (!options.credential) throw new TopoloAuthError("credential is required");
1338
- if (!((_a = options.agent) == null ? void 0 : _a.clientName)) throw new TopoloAuthError("agent.clientName is required");
1631
+ if (!options.agent?.clientName) throw new TopoloAuthError("agent.clientName is required");
1339
1632
  this.credential = options.credential;
1340
1633
  this.agent = options.agent;
1341
1634
  this.serviceUrls = options.serviceUrls;
@@ -1525,7 +1818,6 @@ var CrmModule = class {
1525
1818
  }
1526
1819
  client;
1527
1820
  async listContacts(options = {}) {
1528
- var _a, _b, _c;
1529
1821
  const res = await this.client.request({
1530
1822
  service: "crm",
1531
1823
  path: "/api/contacts",
@@ -1535,12 +1827,12 @@ var CrmModule = class {
1535
1827
  pageSize: options.pageSize
1536
1828
  }
1537
1829
  });
1538
- const rows = Array.isArray(res == null ? void 0 : res.contacts) ? res.contacts : Array.isArray(res == null ? void 0 : res.data) ? res.data : [];
1830
+ const rows = Array.isArray(res?.contacts) ? res.contacts : Array.isArray(res?.data) ? res.data : [];
1539
1831
  return {
1540
1832
  contacts: rows.map(toSummary),
1541
- total: (res == null ? void 0 : res.total) ?? ((_a = res == null ? void 0 : res.pagination) == null ? void 0 : _a.total) ?? rows.length,
1542
- page: (res == null ? void 0 : res.page) ?? ((_b = res == null ? void 0 : res.pagination) == null ? void 0 : _b.page) ?? options.page ?? 1,
1543
- pageSize: (res == null ? void 0 : res.pageSize) ?? ((_c = res == null ? void 0 : res.pagination) == null ? void 0 : _c.pageSize) ?? options.pageSize ?? rows.length
1833
+ total: res?.total ?? res?.pagination?.total ?? rows.length,
1834
+ page: res?.page ?? res?.pagination?.page ?? options.page ?? 1,
1835
+ pageSize: res?.pageSize ?? res?.pagination?.pageSize ?? options.pageSize ?? rows.length
1544
1836
  };
1545
1837
  }
1546
1838
  async getContact(contactId) {
@@ -1661,6 +1953,126 @@ var TopoloOAuth = class {
1661
1953
  }
1662
1954
  };
1663
1955
 
1956
+ // src/widgets.ts
1957
+ var TOPOLO_WIDGET_ENDPOINT = "/api/widget";
1958
+ var TOPOLO_WIDGET_REFRESH_INTERVAL_MS = 6e4;
1959
+ function defineTopoloStatsWidget(widget) {
1960
+ return { ...widget, type: "stats" };
1961
+ }
1962
+ function defineTopoloRecentActivityWidget(widget) {
1963
+ return { ...widget, type: "recent-activity" };
1964
+ }
1965
+ function defineTopoloQuickActionsWidget(widget) {
1966
+ return { ...widget, type: "quick-actions" };
1967
+ }
1968
+ function defineTopoloNotificationsWidget(widget) {
1969
+ return { ...widget, type: "notifications" };
1970
+ }
1971
+ function defineTopoloChartWidget(widget) {
1972
+ return { ...widget, type: "chart" };
1973
+ }
1974
+ function createTopoloWidgetResponse({
1975
+ serviceId,
1976
+ serviceName,
1977
+ widgets,
1978
+ now
1979
+ }) {
1980
+ const lastUpdated = toIsoTimestamp(now);
1981
+ return {
1982
+ success: true,
1983
+ widgets: widgets.map((widget) => ({
1984
+ ...widget,
1985
+ serviceId: widget.serviceId?.trim() || serviceId,
1986
+ serviceName: widget.serviceName?.trim() || serviceName,
1987
+ lastUpdated: widget.lastUpdated?.trim() || lastUpdated
1988
+ }))
1989
+ };
1990
+ }
1991
+ function createTopoloWidgetError(error) {
1992
+ return {
1993
+ success: false,
1994
+ widgets: [],
1995
+ error
1996
+ };
1997
+ }
1998
+ function validateTopoloWidgetResponse(response) {
1999
+ const errors = [];
2000
+ if (!isRecord(response)) {
2001
+ return { ok: false, errors: ["response must be an object"] };
2002
+ }
2003
+ if (typeof response.success !== "boolean") {
2004
+ errors.push("success must be a boolean");
2005
+ }
2006
+ if (!Array.isArray(response.widgets)) {
2007
+ errors.push("widgets must be an array");
2008
+ return { ok: errors.length === 0, errors };
2009
+ }
2010
+ response.widgets.forEach((widget, index) => {
2011
+ validateWidget(widget, `widgets[${index}]`, errors);
2012
+ });
2013
+ if (response.success === false && response.error !== void 0 && typeof response.error !== "string") {
2014
+ errors.push("error must be a string when provided");
2015
+ }
2016
+ return { ok: errors.length === 0, errors };
2017
+ }
2018
+ function validateWidget(widget, path, errors) {
2019
+ if (!isRecord(widget)) {
2020
+ errors.push(`${path} must be an object`);
2021
+ return;
2022
+ }
2023
+ validateNonEmptyString(widget.serviceId, `${path}.serviceId`, errors);
2024
+ validateNonEmptyString(widget.serviceName, `${path}.serviceName`, errors);
2025
+ validateNonEmptyString(widget.lastUpdated, `${path}.lastUpdated`, errors);
2026
+ switch (widget.type) {
2027
+ case "stats":
2028
+ validateArray(widget.stats, `${path}.stats`, errors);
2029
+ break;
2030
+ case "recent-activity":
2031
+ validateArray(widget.activities, `${path}.activities`, errors);
2032
+ break;
2033
+ case "quick-actions":
2034
+ validateArray(widget.actions, `${path}.actions`, errors);
2035
+ break;
2036
+ case "notifications":
2037
+ validateArray(widget.notifications, `${path}.notifications`, errors);
2038
+ if (typeof widget.unreadCount !== "number") {
2039
+ errors.push(`${path}.unreadCount must be a number`);
2040
+ }
2041
+ break;
2042
+ case "chart":
2043
+ validateNonEmptyString(widget.chartType, `${path}.chartType`, errors);
2044
+ validateNonEmptyString(widget.title, `${path}.title`, errors);
2045
+ if (!isRecord(widget.data)) {
2046
+ errors.push(`${path}.data must be an object`);
2047
+ } else {
2048
+ validateArray(widget.data.labels, `${path}.data.labels`, errors);
2049
+ validateArray(widget.data.values, `${path}.data.values`, errors);
2050
+ }
2051
+ break;
2052
+ default:
2053
+ errors.push(`${path}.type must be a supported Topolo widget type`);
2054
+ }
2055
+ }
2056
+ function validateNonEmptyString(value, path, errors) {
2057
+ if (typeof value !== "string" || value.trim().length === 0) {
2058
+ errors.push(`${path} must be a non-empty string`);
2059
+ }
2060
+ }
2061
+ function validateArray(value, path, errors) {
2062
+ if (!Array.isArray(value)) {
2063
+ errors.push(`${path} must be an array`);
2064
+ }
2065
+ }
2066
+ function isRecord(value) {
2067
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2068
+ }
2069
+ function toIsoTimestamp(input) {
2070
+ if (input instanceof Date) {
2071
+ return input.toISOString();
2072
+ }
2073
+ return input ?? (/* @__PURE__ */ new Date()).toISOString();
2074
+ }
2075
+
1664
2076
  // src/index.ts
1665
2077
  function createTopolo(options) {
1666
2078
  const client = new TopoloClient(options);
@@ -1679,6 +2091,8 @@ function createTopolo(options) {
1679
2091
  DEFAULT_SERVICE_URLS,
1680
2092
  IdentityModule,
1681
2093
  PLATFORM_SERVICE_IDS,
2094
+ TOPOLO_WIDGET_ENDPOINT,
2095
+ TOPOLO_WIDGET_REFRESH_INTERVAL_MS,
1682
2096
  TopoloAuthError,
1683
2097
  TopoloClient,
1684
2098
  TopoloHttpError,
@@ -1689,6 +2103,14 @@ function createTopolo(options) {
1689
2103
  auditAllApplicationRequirements,
1690
2104
  auditApplicationRequirements,
1691
2105
  createTopolo,
2106
+ createTopoloWidgetError,
2107
+ createTopoloWidgetResponse,
2108
+ defineTopoloChartWidget,
2109
+ defineTopoloNotificationsWidget,
2110
+ defineTopoloQuickActionsWidget,
2111
+ defineTopoloRecentActivityWidget,
2112
+ defineTopoloStatsWidget,
1692
2113
  requirementsForApplication,
1693
- resolveServiceUrl
2114
+ resolveServiceUrl,
2115
+ validateTopoloWidgetResponse
1694
2116
  });