@topolo/sdk 0.2.6 → 0.2.7

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
@@ -64,8 +64,10 @@ var SERVICES = {
64
64
  bugfix: "https://bugfix.topolo.app",
65
65
  bytes: "https://r2-media-manager-worker.topolo.workers.dev",
66
66
  calendar: "https://calendar.topolo.app",
67
+ capacity: "https://topolo-capacity.topolo.workers.dev",
67
68
  chat: "https://chat.topolo.app",
68
69
  commerce: "https://topolo-commerce-api.topolo.workers.dev",
70
+ compose: "https://compose.topolo.app",
69
71
  crm: "https://crm-api.topolo.app",
70
72
  customer_success: "https://customer-success.topolo.app",
71
73
  feed: "https://topolo-feed-api.topolo.app",
@@ -79,6 +81,7 @@ var SERVICES = {
79
81
  mdm: "https://topolo-mdm-api.topolo.app",
80
82
  messaging: "https://messages.topolo.app",
81
83
  nexus: "https://topolo-nexus-gateway.topolo.workers.dev",
84
+ notify: "https://notify.topolo.app",
82
85
  one: "https://one.topolo.app",
83
86
  pay: "https://topolo-pay.topolo.workers.dev",
84
87
  people: "https://people.topolo.app",
@@ -95,12 +98,14 @@ var SERVICES = {
95
98
  };
96
99
  var PLATFORM_SERVICE_IDS = {
97
100
  books: "srv_topolo_books",
101
+ compose: "srv_topolo_compose",
98
102
  crm: "srv_iCwM4jGXcwlj",
99
103
  customer_success: "srv_topolo_customer_success",
100
104
  flow: "srv_topolo_flow",
101
105
  forms: "srv_topolo_forms",
102
106
  insights: "srv_topolo_insights",
103
107
  inventory: "srv_topolo_inventory",
108
+ notify: "svc_topolo_notify",
104
109
  people: "srv_topolo_people",
105
110
  sign: "srv_topolo_sign",
106
111
  survey: "srv_topolo_survey",
@@ -279,6 +284,29 @@ var APPLICATIONS = {
279
284
  }
280
285
  ]
281
286
  },
287
+ "capacity": {
288
+ "id": "capacity",
289
+ "appDir": "TopoloCapacity",
290
+ "name": "topolo-capacity",
291
+ "packageName": "topolo-capacity",
292
+ "productionUrl": "https://capacity.topolo.app",
293
+ "services": [
294
+ "capacity"
295
+ ],
296
+ "deployTargets": [
297
+ {
298
+ "name": "topolo-capacity",
299
+ "kind": "worker",
300
+ "apiUrl": "https://topolo-capacity.topolo.workers.dev",
301
+ "domains": [
302
+ "capacity.topolo.app",
303
+ "topolo-capacity.topolo.workers.dev"
304
+ ],
305
+ "routes": [],
306
+ "serviceId": "capacity"
307
+ }
308
+ ]
309
+ },
282
310
  "chat": {
283
311
  "id": "chat",
284
312
  "appDir": "TopoloChat",
@@ -376,6 +404,30 @@ var APPLICATIONS = {
376
404
  }
377
405
  ]
378
406
  },
407
+ "compose": {
408
+ "id": "compose",
409
+ "appDir": "TopoloCompose",
410
+ "name": "topolo-compose",
411
+ "packageName": "@topolo/compose",
412
+ "productionUrl": "https://compose.topolo.app",
413
+ "services": [
414
+ "compose"
415
+ ],
416
+ "deployTargets": [
417
+ {
418
+ "name": "topolo-compose",
419
+ "kind": "worker",
420
+ "apiUrl": "https://compose.topolo.app",
421
+ "domains": [
422
+ "compose.topolo.app"
423
+ ],
424
+ "routes": [
425
+ "compose.topolo.app"
426
+ ],
427
+ "serviceId": "compose"
428
+ }
429
+ ]
430
+ },
379
431
  "crm": {
380
432
  "id": "crm",
381
433
  "appDir": "TopoloCRM",
@@ -793,6 +845,30 @@ var APPLICATIONS = {
793
845
  }
794
846
  ]
795
847
  },
848
+ "notify": {
849
+ "id": "notify",
850
+ "appDir": "TopoloNotify",
851
+ "name": "topolo-notify",
852
+ "packageName": "@topolo/notify",
853
+ "productionUrl": "https://notify.topolo.app",
854
+ "services": [
855
+ "notify"
856
+ ],
857
+ "deployTargets": [
858
+ {
859
+ "name": "topolo-notify",
860
+ "kind": "worker",
861
+ "apiUrl": "https://notify.topolo.app",
862
+ "domains": [
863
+ "notify.topolo.app"
864
+ ],
865
+ "routes": [
866
+ "notify.topolo.app"
867
+ ],
868
+ "serviceId": "notify"
869
+ }
870
+ ]
871
+ },
796
872
  "one": {
797
873
  "id": "one",
798
874
  "appDir": "TopoloOne",
@@ -1969,6 +2045,7 @@ function validateWidget(widget, path, errors) {
1969
2045
  validateNonEmptyString(widget.serviceId, `${path}.serviceId`, errors);
1970
2046
  validateNonEmptyString(widget.serviceName, `${path}.serviceName`, errors);
1971
2047
  validateNonEmptyString(widget.lastUpdated, `${path}.lastUpdated`, errors);
2048
+ validateSnapshotMetadata(widget.snapshot, `${path}.snapshot`, errors);
1972
2049
  switch (widget.type) {
1973
2050
  case "stats":
1974
2051
  validateArray(widget.stats, `${path}.stats`, errors);
@@ -1999,6 +2076,40 @@ function validateWidget(widget, path, errors) {
1999
2076
  errors.push(`${path}.type must be a supported Topolo widget type`);
2000
2077
  }
2001
2078
  }
2079
+ function validateSnapshotMetadata(snapshot, path, errors) {
2080
+ if (snapshot === void 0) {
2081
+ return;
2082
+ }
2083
+ if (!isRecord(snapshot)) {
2084
+ errors.push(`${path} must be an object when provided`);
2085
+ return;
2086
+ }
2087
+ if (snapshot.defaultMetricKeys !== void 0 && !Array.isArray(snapshot.defaultMetricKeys)) {
2088
+ errors.push(`${path}.defaultMetricKeys must be an array when provided`);
2089
+ }
2090
+ if (snapshot.highlights !== void 0 && !Array.isArray(snapshot.highlights)) {
2091
+ errors.push(`${path}.highlights must be an array when provided`);
2092
+ }
2093
+ if (snapshot.availableMetrics === void 0) {
2094
+ return;
2095
+ }
2096
+ if (!Array.isArray(snapshot.availableMetrics)) {
2097
+ errors.push(`${path}.availableMetrics must be an array when provided`);
2098
+ return;
2099
+ }
2100
+ snapshot.availableMetrics.forEach((metric, index) => {
2101
+ const metricPath = `${path}.availableMetrics[${index}]`;
2102
+ if (!isRecord(metric)) {
2103
+ errors.push(`${metricPath} must be an object`);
2104
+ return;
2105
+ }
2106
+ validateNonEmptyString(metric.key, `${metricPath}.key`, errors);
2107
+ validateNonEmptyString(metric.label, `${metricPath}.label`, errors);
2108
+ if (typeof metric.value !== "number" || Number.isNaN(metric.value)) {
2109
+ errors.push(`${metricPath}.value must be a number`);
2110
+ }
2111
+ });
2112
+ }
2002
2113
  function validateNonEmptyString(value, path, errors) {
2003
2114
  if (typeof value !== "string" || value.trim().length === 0) {
2004
2115
  errors.push(`${path} must be a non-empty string`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topolo/sdk",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
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",
@@ -173,6 +173,29 @@ export const APPLICATIONS = {
173
173
  }
174
174
  ]
175
175
  },
176
+ "capacity": {
177
+ "id": "capacity",
178
+ "appDir": "TopoloCapacity",
179
+ "name": "topolo-capacity",
180
+ "packageName": "topolo-capacity",
181
+ "productionUrl": "https://capacity.topolo.app",
182
+ "services": [
183
+ "capacity"
184
+ ],
185
+ "deployTargets": [
186
+ {
187
+ "name": "topolo-capacity",
188
+ "kind": "worker",
189
+ "apiUrl": "https://topolo-capacity.topolo.workers.dev",
190
+ "domains": [
191
+ "capacity.topolo.app",
192
+ "topolo-capacity.topolo.workers.dev"
193
+ ],
194
+ "routes": [],
195
+ "serviceId": "capacity"
196
+ }
197
+ ]
198
+ },
176
199
  "chat": {
177
200
  "id": "chat",
178
201
  "appDir": "TopoloChat",
@@ -270,6 +293,30 @@ export const APPLICATIONS = {
270
293
  }
271
294
  ]
272
295
  },
296
+ "compose": {
297
+ "id": "compose",
298
+ "appDir": "TopoloCompose",
299
+ "name": "topolo-compose",
300
+ "packageName": "@topolo/compose",
301
+ "productionUrl": "https://compose.topolo.app",
302
+ "services": [
303
+ "compose"
304
+ ],
305
+ "deployTargets": [
306
+ {
307
+ "name": "topolo-compose",
308
+ "kind": "worker",
309
+ "apiUrl": "https://compose.topolo.app",
310
+ "domains": [
311
+ "compose.topolo.app"
312
+ ],
313
+ "routes": [
314
+ "compose.topolo.app"
315
+ ],
316
+ "serviceId": "compose"
317
+ }
318
+ ]
319
+ },
273
320
  "crm": {
274
321
  "id": "crm",
275
322
  "appDir": "TopoloCRM",
@@ -687,6 +734,30 @@ export const APPLICATIONS = {
687
734
  }
688
735
  ]
689
736
  },
737
+ "notify": {
738
+ "id": "notify",
739
+ "appDir": "TopoloNotify",
740
+ "name": "topolo-notify",
741
+ "packageName": "@topolo/notify",
742
+ "productionUrl": "https://notify.topolo.app",
743
+ "services": [
744
+ "notify"
745
+ ],
746
+ "deployTargets": [
747
+ {
748
+ "name": "topolo-notify",
749
+ "kind": "worker",
750
+ "apiUrl": "https://notify.topolo.app",
751
+ "domains": [
752
+ "notify.topolo.app"
753
+ ],
754
+ "routes": [
755
+ "notify.topolo.app"
756
+ ],
757
+ "serviceId": "notify"
758
+ }
759
+ ]
760
+ },
690
761
  "one": {
691
762
  "id": "one",
692
763
  "appDir": "TopoloOne",
package/src/index.ts CHANGED
@@ -4,8 +4,8 @@ export {
4
4
  type RequestOptions,
5
5
  type CredentialIntrospection,
6
6
  type TopoloDebugEvent,
7
- } from './client.js';
8
- export type { TopoloCredential, AgentIdentity } from './auth.js';
7
+ } from "./client.js";
8
+ export type { TopoloCredential, AgentIdentity } from "./auth.js";
9
9
  export {
10
10
  DEFAULT_SERVICE_URLS,
11
11
  APPLICATIONS,
@@ -29,21 +29,26 @@ export {
29
29
  type ApplicationRequirementStatus,
30
30
  type ApplicationRequirementsAuditReport,
31
31
  type ServiceId,
32
- } from './services.js';
32
+ } from "./services.js";
33
33
  export {
34
34
  TopoloSdkError,
35
35
  TopoloAuthError,
36
36
  TopoloPermissionError,
37
37
  TopoloHttpError,
38
- } from './errors.js';
39
- export { CrmModule, type CrmContactSummary, type ListContactsOptions, type ListContactsResult } from './modules/crm.js';
40
- export { IdentityModule } from './modules/identity.js';
38
+ } from "./errors.js";
39
+ export {
40
+ CrmModule,
41
+ type CrmContactSummary,
42
+ type ListContactsOptions,
43
+ type ListContactsResult,
44
+ } from "./modules/crm.js";
45
+ export { IdentityModule } from "./modules/identity.js";
41
46
  export {
42
47
  TopoloOAuth,
43
48
  type OAuthHelperOptions,
44
49
  type DeviceAuthorizationResponse,
45
50
  type TokenResponse,
46
- } from './oauth.js';
51
+ } from "./oauth.js";
47
52
  export {
48
53
  TOPOLO_WIDGET_ENDPOINT,
49
54
  TOPOLO_WIDGET_REFRESH_INTERVAL_MS,
@@ -70,13 +75,15 @@ export {
70
75
  type TopoloWidgetMetric,
71
76
  type TopoloWidgetNotificationItem,
72
77
  type TopoloWidgetResponseOptions,
78
+ type TopoloWidgetSnapshotMetadata,
79
+ type TopoloWidgetSnapshotMetric,
73
80
  type TopoloWidgetType,
74
81
  type TopoloWidgetValidationResult,
75
- } from './widgets.js';
82
+ } from "./widgets.js";
76
83
 
77
- import { TopoloClient, type TopoloClientOptions } from './client.js';
78
- import { CrmModule } from './modules/crm.js';
79
- import { IdentityModule } from './modules/identity.js';
84
+ import { TopoloClient, type TopoloClientOptions } from "./client.js";
85
+ import { CrmModule } from "./modules/crm.js";
86
+ import { IdentityModule } from "./modules/identity.js";
80
87
 
81
88
  /**
82
89
  * Convenience factory that returns a ready-to-use typed Topolo client plus
@@ -9,8 +9,10 @@ export const SERVICES = {
9
9
  bugfix: "https://bugfix.topolo.app",
10
10
  bytes: "https://r2-media-manager-worker.topolo.workers.dev",
11
11
  calendar: "https://calendar.topolo.app",
12
+ capacity: "https://topolo-capacity.topolo.workers.dev",
12
13
  chat: "https://chat.topolo.app",
13
14
  commerce: "https://topolo-commerce-api.topolo.workers.dev",
15
+ compose: "https://compose.topolo.app",
14
16
  crm: "https://crm-api.topolo.app",
15
17
  customer_success: "https://customer-success.topolo.app",
16
18
  feed: "https://topolo-feed-api.topolo.app",
@@ -24,6 +26,7 @@ export const SERVICES = {
24
26
  mdm: "https://topolo-mdm-api.topolo.app",
25
27
  messaging: "https://messages.topolo.app",
26
28
  nexus: "https://topolo-nexus-gateway.topolo.workers.dev",
29
+ notify: "https://notify.topolo.app",
27
30
  one: "https://one.topolo.app",
28
31
  pay: "https://topolo-pay.topolo.workers.dev",
29
32
  people: "https://people.topolo.app",
@@ -43,12 +46,14 @@ export type ServiceId = keyof typeof SERVICES;
43
46
 
44
47
  export const PLATFORM_SERVICE_IDS: Partial<Record<ServiceId, string>> = {
45
48
  books: "srv_topolo_books",
49
+ compose: "srv_topolo_compose",
46
50
  crm: "srv_iCwM4jGXcwlj",
47
51
  customer_success: "srv_topolo_customer_success",
48
52
  flow: "srv_topolo_flow",
49
53
  forms: "srv_topolo_forms",
50
54
  insights: "srv_topolo_insights",
51
55
  inventory: "srv_topolo_inventory",
56
+ notify: "svc_topolo_notify",
52
57
  people: "srv_topolo_people",
53
58
  sign: "srv_topolo_sign",
54
59
  survey: "srv_topolo_survey",
@@ -18,10 +18,12 @@ const EXPECTED_APPLICATION_IDS = [
18
18
  'bugfix',
19
19
  'bytes',
20
20
  'calendar',
21
+ 'capacity',
21
22
  'chat',
22
23
  'cli',
23
24
  'cloudcontrol',
24
25
  'commerce',
26
+ 'compose',
25
27
  'crm',
26
28
  'customer_success',
27
29
  'developers',
@@ -38,6 +40,7 @@ const EXPECTED_APPLICATION_IDS = [
38
40
  'mdm',
39
41
  'messaging',
40
42
  'nexus',
43
+ 'notify',
41
44
  'one',
42
45
  'pay',
43
46
  'people',
@@ -1,27 +1,27 @@
1
- import { describe, expect, it } from 'vitest';
1
+ import { describe, expect, it } from "vitest";
2
2
  import {
3
3
  createTopoloWidgetError,
4
4
  createTopoloWidgetResponse,
5
5
  TOPOLO_WIDGET_ENDPOINT,
6
6
  TOPOLO_WIDGET_REFRESH_INTERVAL_MS,
7
7
  validateTopoloWidgetResponse,
8
- } from './widgets.js';
8
+ } from "./widgets.js";
9
9
 
10
- describe('Topolo widget contract', () => {
11
- it('exports the platform widget endpoint defaults', () => {
12
- expect(TOPOLO_WIDGET_ENDPOINT).toBe('/api/widget');
10
+ describe("Topolo widget contract", () => {
11
+ it("exports the platform widget endpoint defaults", () => {
12
+ expect(TOPOLO_WIDGET_ENDPOINT).toBe("/api/widget");
13
13
  expect(TOPOLO_WIDGET_REFRESH_INTERVAL_MS).toBe(60_000);
14
14
  });
15
15
 
16
- it('stamps service metadata and timestamp onto widget drafts', () => {
16
+ it("stamps service metadata and timestamp onto widget drafts", () => {
17
17
  const response = createTopoloWidgetResponse({
18
- serviceId: 'srv_bytes',
19
- serviceName: 'Bytes',
20
- now: '2026-04-25T00:00:00.000Z',
18
+ serviceId: "srv_bytes",
19
+ serviceName: "Bytes",
20
+ now: "2026-04-25T00:00:00.000Z",
21
21
  widgets: [
22
22
  {
23
- type: 'stats',
24
- stats: [{ label: 'Files', value: 12 }],
23
+ type: "stats",
24
+ stats: [{ label: "Files", value: 12 }],
25
25
  },
26
26
  ],
27
27
  });
@@ -30,63 +30,63 @@ describe('Topolo widget contract', () => {
30
30
  success: true,
31
31
  widgets: [
32
32
  {
33
- serviceId: 'srv_bytes',
34
- serviceName: 'Bytes',
35
- lastUpdated: '2026-04-25T00:00:00.000Z',
36
- type: 'stats',
37
- stats: [{ label: 'Files', value: 12 }],
33
+ serviceId: "srv_bytes",
34
+ serviceName: "Bytes",
35
+ lastUpdated: "2026-04-25T00:00:00.000Z",
36
+ type: "stats",
37
+ stats: [{ label: "Files", value: 12 }],
38
38
  },
39
39
  ],
40
40
  });
41
41
  });
42
42
 
43
- it('keeps explicit widget metadata when an app needs multiple service cards', () => {
43
+ it("keeps explicit widget metadata when an app needs multiple service cards", () => {
44
44
  const response = createTopoloWidgetResponse({
45
- serviceId: 'srv_platform',
46
- serviceName: 'Platform',
47
- now: '2026-04-25T00:00:00.000Z',
45
+ serviceId: "srv_platform",
46
+ serviceName: "Platform",
47
+ now: "2026-04-25T00:00:00.000Z",
48
48
  widgets: [
49
49
  {
50
- serviceId: 'srv_crm',
51
- serviceName: 'CRM',
52
- lastUpdated: '2026-04-24T23:00:00.000Z',
53
- type: 'stats',
54
- stats: [{ label: 'Contacts', value: 3 }],
50
+ serviceId: "srv_crm",
51
+ serviceName: "CRM",
52
+ lastUpdated: "2026-04-24T23:00:00.000Z",
53
+ type: "stats",
54
+ stats: [{ label: "Contacts", value: 3 }],
55
55
  },
56
56
  ],
57
57
  });
58
58
 
59
59
  expect(response.widgets[0]).toMatchObject({
60
- serviceId: 'srv_crm',
61
- serviceName: 'CRM',
62
- lastUpdated: '2026-04-24T23:00:00.000Z',
60
+ serviceId: "srv_crm",
61
+ serviceName: "CRM",
62
+ lastUpdated: "2026-04-24T23:00:00.000Z",
63
63
  });
64
64
  });
65
65
 
66
- it('returns a stable error response shape', () => {
67
- expect(createTopoloWidgetError('not configured')).toEqual({
66
+ it("returns a stable error response shape", () => {
67
+ expect(createTopoloWidgetError("not configured")).toEqual({
68
68
  success: false,
69
69
  widgets: [],
70
- error: 'not configured',
70
+ error: "not configured",
71
71
  });
72
72
  });
73
73
 
74
- it('validates a compliant response', () => {
74
+ it("validates a compliant response", () => {
75
75
  const response = createTopoloWidgetResponse({
76
- serviceId: 'srv_crm',
77
- serviceName: 'CRM',
78
- now: '2026-04-25T00:00:00.000Z',
76
+ serviceId: "srv_crm",
77
+ serviceName: "CRM",
78
+ now: "2026-04-25T00:00:00.000Z",
79
79
  widgets: [
80
80
  {
81
- type: 'notifications',
81
+ type: "notifications",
82
82
  unreadCount: 1,
83
83
  notifications: [
84
84
  {
85
- id: 'n_1',
86
- title: 'New lead',
87
- message: 'A lead was created.',
88
- type: 'info',
89
- timestamp: '2026-04-25T00:00:00.000Z',
85
+ id: "n_1",
86
+ title: "New lead",
87
+ message: "A lead was created.",
88
+ type: "info",
89
+ timestamp: "2026-04-25T00:00:00.000Z",
90
90
  read: false,
91
91
  },
92
92
  ],
@@ -100,24 +100,63 @@ describe('Topolo widget contract', () => {
100
100
  });
101
101
  });
102
102
 
103
- it('reports missing metadata and unsupported widget types', () => {
103
+ it("keeps optional snapshot metadata for configurable TopoloOne datapoints", () => {
104
+ const response = createTopoloWidgetResponse({
105
+ serviceId: "srv_admin",
106
+ serviceName: "Admin",
107
+ now: "2026-04-28T00:00:00.000Z",
108
+ widgets: [
109
+ {
110
+ type: "stats",
111
+ stats: [{ label: "Team Members", value: 5 }],
112
+ snapshot: {
113
+ presetKey: "admin_morning_snapshot",
114
+ defaultMetricKeys: ["team_members"],
115
+ availableMetrics: [
116
+ {
117
+ key: "team_members",
118
+ label: "Team Members",
119
+ value: 5,
120
+ unit: "count",
121
+ },
122
+ ],
123
+ },
124
+ },
125
+ ],
126
+ });
127
+
128
+ expect(validateTopoloWidgetResponse(response)).toEqual({
129
+ ok: true,
130
+ errors: [],
131
+ });
132
+ expect(response.widgets[0]).toMatchObject({
133
+ snapshot: {
134
+ defaultMetricKeys: ["team_members"],
135
+ availableMetrics: [
136
+ { key: "team_members", label: "Team Members", value: 5 },
137
+ ],
138
+ },
139
+ });
140
+ });
141
+
142
+ it("reports missing metadata and unsupported widget types", () => {
104
143
  const result = validateTopoloWidgetResponse({
105
144
  success: true,
106
145
  widgets: [
107
146
  {
108
- serviceId: '',
109
- serviceName: 'Bad App',
110
- lastUpdated: '',
111
- type: 'calendar',
147
+ serviceId: "",
148
+ serviceName: "Bad App",
149
+ lastUpdated: "",
150
+ type: "calendar",
112
151
  },
113
152
  ],
114
153
  });
115
154
 
116
155
  expect(result.ok).toBe(false);
117
156
  expect(result.errors).toEqual([
118
- 'widgets[0].serviceId must be a non-empty string',
119
- 'widgets[0].lastUpdated must be a non-empty string',
120
- 'widgets[0].type must be a supported Topolo widget type',
157
+ "widgets[0].serviceId must be a non-empty string",
158
+ "widgets[0].lastUpdated must be a non-empty string",
159
+ "widgets[0].type must be a supported Topolo widget type",
121
160
  ]);
122
161
  });
123
162
  });