@topolo/sdk 0.1.2 → 0.2.2
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 +17 -0
- package/dist/index.cjs +1209 -4
- package/dist/index.d.cts +779 -11
- package/dist/index.d.ts +779 -11
- package/dist/index.js +1202 -4
- package/package.json +4 -2
- package/src/application-requirements.ts +468 -0
- package/src/applications.generated.ts +821 -0
- package/src/index.ts +18 -0
- package/src/services.generated.ts +35 -0
- package/src/services.test.ts +152 -0
- package/src/services.ts +41 -19
package/src/index.ts
CHANGED
|
@@ -8,8 +8,26 @@ export {
|
|
|
8
8
|
export type { TopoloCredential, AgentIdentity } from './auth.js';
|
|
9
9
|
export {
|
|
10
10
|
DEFAULT_SERVICE_URLS,
|
|
11
|
+
APPLICATIONS,
|
|
12
|
+
APPLICATION_REQUIREMENTS,
|
|
13
|
+
APPLICATION_REQUIREMENTS_VERSION,
|
|
11
14
|
PLATFORM_SERVICE_IDS,
|
|
15
|
+
auditAllApplicationRequirements,
|
|
16
|
+
auditApplicationRequirements,
|
|
17
|
+
applicationRequirementScopes,
|
|
18
|
+
requirementsForApplication,
|
|
12
19
|
resolveServiceUrl,
|
|
20
|
+
type ApplicationCatalogEntry,
|
|
21
|
+
type ApplicationDeployTarget,
|
|
22
|
+
type ApplicationId,
|
|
23
|
+
type ApplicationRequirement,
|
|
24
|
+
type ApplicationRequirementAudit,
|
|
25
|
+
type ApplicationRequirementFinding,
|
|
26
|
+
type ApplicationRequirementMigrationItem,
|
|
27
|
+
type ApplicationRequirementScore,
|
|
28
|
+
type ApplicationRequirementScope,
|
|
29
|
+
type ApplicationRequirementStatus,
|
|
30
|
+
type ApplicationRequirementsAuditReport,
|
|
13
31
|
type ServiceId,
|
|
14
32
|
} from './services.js';
|
|
15
33
|
export {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// AUTO-GENERATED by scripts/generate-services.mjs. Do not edit by hand.
|
|
2
|
+
// Source of truth: topolo.cloudcontrol.json in every app repo.
|
|
3
|
+
// Regenerate: npm run generate:services
|
|
4
|
+
|
|
5
|
+
export const SERVICES = {
|
|
6
|
+
agent: "https://topolo-agent-worker.topolo.workers.dev",
|
|
7
|
+
auth: "https://auth.topolo.app",
|
|
8
|
+
bugfix: "https://bugfix.topolo.app",
|
|
9
|
+
bytes: "https://r2-media-manager-worker.topolo.workers.dev",
|
|
10
|
+
calendar: "https://calendar.topolo.app",
|
|
11
|
+
chat: "https://chat.topolo.app",
|
|
12
|
+
commerce: "https://topolo-commerce-api.topolo.workers.dev",
|
|
13
|
+
crm: "https://crm-api.topolo.app",
|
|
14
|
+
forecast: "https://forecast-worker-production.topolo.workers.dev",
|
|
15
|
+
learn: "https://topololearn-api.topolo.workers.dev",
|
|
16
|
+
mail: "https://mail.topolo.app",
|
|
17
|
+
mdm: "https://topolo-mdm-api.topolo.app",
|
|
18
|
+
messaging: "https://messages.topolo.app",
|
|
19
|
+
nexus: "https://topolo-nexus-gateway.topolo.workers.dev",
|
|
20
|
+
one: "https://topoloone-worker.topolo.workers.dev",
|
|
21
|
+
pay: "https://topolo-pay.topolo.workers.dev",
|
|
22
|
+
"quro.api": "https://api.ol0.me",
|
|
23
|
+
"quro.redirect": "https://ol0.me",
|
|
24
|
+
roadmapper: "https://roadmapper-api.topolo.app",
|
|
25
|
+
social_studio: "https://topolo-social-studio-api.topolo.workers.dev",
|
|
26
|
+
socialize: "https://socialize-prod-api.topolo.workers.dev",
|
|
27
|
+
support: "https://support.topolo.app",
|
|
28
|
+
web: "https://web-api.topolo.app",
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
export type ServiceId = keyof typeof SERVICES;
|
|
32
|
+
|
|
33
|
+
export const PLATFORM_SERVICE_IDS: Partial<Record<ServiceId, string>> = {
|
|
34
|
+
crm: "srv_iCwM4jGXcwlj",
|
|
35
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
APPLICATION_REQUIREMENTS,
|
|
4
|
+
APPLICATION_REQUIREMENTS_VERSION,
|
|
5
|
+
APPLICATIONS,
|
|
6
|
+
DEFAULT_SERVICE_URLS,
|
|
7
|
+
auditAllApplicationRequirements,
|
|
8
|
+
auditApplicationRequirements,
|
|
9
|
+
applicationRequirementScopes,
|
|
10
|
+
requirementsForApplication,
|
|
11
|
+
} from './services.js';
|
|
12
|
+
|
|
13
|
+
const EXPECTED_APPLICATION_IDS = [
|
|
14
|
+
'admin',
|
|
15
|
+
'agent',
|
|
16
|
+
'auth',
|
|
17
|
+
'bugfix',
|
|
18
|
+
'bytes',
|
|
19
|
+
'calendar',
|
|
20
|
+
'chat',
|
|
21
|
+
'cli',
|
|
22
|
+
'cloudcontrol',
|
|
23
|
+
'commerce',
|
|
24
|
+
'crm',
|
|
25
|
+
'developers',
|
|
26
|
+
'docs',
|
|
27
|
+
'feed',
|
|
28
|
+
'forecast',
|
|
29
|
+
'learn',
|
|
30
|
+
'mail',
|
|
31
|
+
'mcp',
|
|
32
|
+
'mdm',
|
|
33
|
+
'messaging',
|
|
34
|
+
'nexus',
|
|
35
|
+
'one',
|
|
36
|
+
'pay',
|
|
37
|
+
'quro',
|
|
38
|
+
'roadmapper',
|
|
39
|
+
'social_studio',
|
|
40
|
+
'socialize',
|
|
41
|
+
'support',
|
|
42
|
+
'web',
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
describe('service registry', () => {
|
|
46
|
+
it('includes every production worker API that belongs to a platform application', () => {
|
|
47
|
+
expect(DEFAULT_SERVICE_URLS).toMatchObject({
|
|
48
|
+
calendar: 'https://calendar.topolo.app',
|
|
49
|
+
mail: 'https://mail.topolo.app',
|
|
50
|
+
web: 'https://web-api.topolo.app',
|
|
51
|
+
});
|
|
52
|
+
expect(DEFAULT_SERVICE_URLS).not.toHaveProperty('web.api');
|
|
53
|
+
expect(DEFAULT_SERVICE_URLS).not.toHaveProperty('web.runtime');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('maps every callable service back to an application catalog entry', () => {
|
|
57
|
+
const applicationServices = new Set<string>(
|
|
58
|
+
Object.values(APPLICATIONS).flatMap((application) => [...application.services]),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
for (const serviceId of Object.keys(DEFAULT_SERVICE_URLS)) {
|
|
62
|
+
expect(applicationServices.has(serviceId)).toBe(true);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('application catalog', () => {
|
|
68
|
+
it('covers every current Topolo application directory', () => {
|
|
69
|
+
expect(Object.keys(APPLICATIONS).sort()).toEqual(EXPECTED_APPLICATION_IDS);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('only references registered service IDs from application metadata', () => {
|
|
73
|
+
const registeredServices = new Set<string>(Object.keys(DEFAULT_SERVICE_URLS));
|
|
74
|
+
|
|
75
|
+
for (const application of Object.values(APPLICATIONS)) {
|
|
76
|
+
for (const serviceId of application.services) {
|
|
77
|
+
expect(registeredServices.has(serviceId)).toBe(true);
|
|
78
|
+
}
|
|
79
|
+
for (const target of application.deployTargets) {
|
|
80
|
+
if (target.serviceId) expect(registeredServices.has(target.serviceId)).toBe(true);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('treats TopoloWeb runtime as a deploy target, not an agent-callable API service', () => {
|
|
86
|
+
expect(APPLICATIONS.web.services).toEqual(['web']);
|
|
87
|
+
expect(
|
|
88
|
+
APPLICATIONS.web.deployTargets.find((target) => target.name === 'topoloweb-api')?.serviceId,
|
|
89
|
+
).toBe('web');
|
|
90
|
+
expect(
|
|
91
|
+
APPLICATIONS.web.deployTargets.find((target) => target.name === 'topoloweb-runtime')?.serviceId,
|
|
92
|
+
).toBeNull();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('application requirements', () => {
|
|
97
|
+
it('exposes a stable versioned requirement list', () => {
|
|
98
|
+
expect(APPLICATION_REQUIREMENTS_VERSION).toMatch(/^\d{4}-\d{2}-\d{2}\.\d+$/);
|
|
99
|
+
expect(APPLICATION_REQUIREMENTS.length).toBeGreaterThan(5);
|
|
100
|
+
expect(APPLICATION_REQUIREMENTS.map((requirement) => requirement.id)).toContain(
|
|
101
|
+
'shared-auth-boundary',
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('derives browser and API scopes from app metadata', () => {
|
|
106
|
+
expect(applicationRequirementScopes(APPLICATIONS.commerce)).toEqual(
|
|
107
|
+
expect.arrayContaining(['all', 'browser', 'api', 'agent_surface']),
|
|
108
|
+
);
|
|
109
|
+
expect(applicationRequirementScopes(APPLICATIONS.mail)).toEqual(
|
|
110
|
+
expect.arrayContaining(['all', 'browser', 'api', 'agent_surface']),
|
|
111
|
+
);
|
|
112
|
+
expect(applicationRequirementScopes(APPLICATIONS.cli)).toEqual(
|
|
113
|
+
expect.arrayContaining(['all', 'tooling', 'agent_surface']),
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('filters requirements for a target application', () => {
|
|
118
|
+
const adminRequirementIds = requirementsForApplication('admin').map((requirement) => requirement.id);
|
|
119
|
+
const mailRequirementIds = requirementsForApplication('mail').map((requirement) => requirement.id);
|
|
120
|
+
const cliRequirementIds = requirementsForApplication('cli').map((requirement) => requirement.id);
|
|
121
|
+
|
|
122
|
+
expect(adminRequirementIds).toContain('shared-shell-and-launcher');
|
|
123
|
+
expect(adminRequirementIds).not.toContain('organization-scoped-data');
|
|
124
|
+
expect(mailRequirementIds).toContain('organization-scoped-data');
|
|
125
|
+
expect(mailRequirementIds).toContain('service-registration-and-scopes');
|
|
126
|
+
expect(cliRequirementIds).toContain('agent-and-operator-surfaces');
|
|
127
|
+
expect(cliRequirementIds).not.toContain('service-registration-and-scopes');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('audits one application against the catalog-backed requirements', () => {
|
|
131
|
+
const audit = auditApplicationRequirements('mail');
|
|
132
|
+
const byRequirement = new Map(audit.findings.map((finding) => [finding.requirementId, finding]));
|
|
133
|
+
|
|
134
|
+
expect(audit.version).toBe(APPLICATION_REQUIREMENTS_VERSION);
|
|
135
|
+
expect(audit.application.id).toBe('mail');
|
|
136
|
+
expect(audit.score.total).toBe(audit.findings.length);
|
|
137
|
+
expect(byRequirement.get('platform-metadata')?.status).toBe('met');
|
|
138
|
+
expect(byRequirement.get('service-registration-and-scopes')?.status).toBe('partial');
|
|
139
|
+
expect(byRequirement.get('canonical-docs')?.status).toBe('needs_review');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('builds a sorted migration queue for all applications', () => {
|
|
143
|
+
const report = auditAllApplicationRequirements(['mail', 'admin']);
|
|
144
|
+
expect(report.applications).toHaveLength(2);
|
|
145
|
+
expect(report.migrationQueue.length).toBeGreaterThan(0);
|
|
146
|
+
expect(report.migrationQueue[0]).toMatchObject({
|
|
147
|
+
applicationId: expect.any(String),
|
|
148
|
+
requirementId: expect.any(String),
|
|
149
|
+
status: expect.stringMatching(/missing|partial|needs_review/),
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
});
|
package/src/services.ts
CHANGED
|
@@ -1,30 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Service registry — base URLs for
|
|
2
|
+
* Service registry — base URLs for every Topolo platform service.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* `
|
|
4
|
+
* Source of truth lives in each app's `topolo.cloudcontrol.json`. The
|
|
5
|
+
* `SERVICES` map is regenerated from those files via
|
|
6
|
+
* `scripts/generate-services.mjs`; edit the JSON, not the generated file.
|
|
7
|
+
*
|
|
8
|
+
* Any entry can be overridden per-call via `TopoloClient` options, or globally
|
|
9
|
+
* via env vars like `TOPOLO_SERVICE_URL_CRM` or `TOPOLO_SERVICE_URL_WEB_API`
|
|
10
|
+
* (dots in service IDs become underscores in env vars).
|
|
6
11
|
*
|
|
7
12
|
* IMPORTANT: The SDK NEVER accepts `orgId` as a parameter. Every request is
|
|
8
13
|
* scoped to the organization embedded in the auth credential (JWT claim or
|
|
9
14
|
* API-key binding). This is the load-bearing cross-org isolation guarantee.
|
|
10
15
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
import {
|
|
17
|
+
SERVICES,
|
|
18
|
+
PLATFORM_SERVICE_IDS,
|
|
19
|
+
type ServiceId,
|
|
20
|
+
} from './services.generated.js';
|
|
21
|
+
import {
|
|
22
|
+
APPLICATIONS,
|
|
23
|
+
type ApplicationCatalogEntry,
|
|
24
|
+
type ApplicationDeployTarget,
|
|
25
|
+
type ApplicationId,
|
|
26
|
+
} from './applications.generated.js';
|
|
27
|
+
export {
|
|
28
|
+
APPLICATION_REQUIREMENTS,
|
|
29
|
+
APPLICATION_REQUIREMENTS_VERSION,
|
|
30
|
+
auditAllApplicationRequirements,
|
|
31
|
+
auditApplicationRequirements,
|
|
32
|
+
applicationRequirementScopes,
|
|
33
|
+
requirementsForApplication,
|
|
34
|
+
type ApplicationRequirement,
|
|
35
|
+
type ApplicationRequirementAudit,
|
|
36
|
+
type ApplicationRequirementFinding,
|
|
37
|
+
type ApplicationRequirementMigrationItem,
|
|
38
|
+
type ApplicationRequirementScore,
|
|
39
|
+
type ApplicationRequirementScope,
|
|
40
|
+
type ApplicationRequirementStatus,
|
|
41
|
+
type ApplicationRequirementsAuditReport,
|
|
42
|
+
} from './application-requirements.js';
|
|
14
43
|
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
};
|
|
44
|
+
export { SERVICES, PLATFORM_SERVICE_IDS };
|
|
45
|
+
export type { ServiceId };
|
|
46
|
+
export { APPLICATIONS };
|
|
47
|
+
export type { ApplicationCatalogEntry, ApplicationDeployTarget, ApplicationId };
|
|
19
48
|
|
|
20
|
-
|
|
21
|
-
* Service IDs as registered in TopoloAuth's service catalog. These are sent in
|
|
22
|
-
* the `X-Service-ID` header when the platform needs to route an API-key
|
|
23
|
-
* introspection or permission check.
|
|
24
|
-
*/
|
|
25
|
-
export const PLATFORM_SERVICE_IDS: Partial<Record<ServiceId, string>> = {
|
|
26
|
-
crm: 'srv_iCwM4jGXcwlj',
|
|
27
|
-
};
|
|
49
|
+
export const DEFAULT_SERVICE_URLS: Record<ServiceId, string> = SERVICES;
|
|
28
50
|
|
|
29
51
|
export function resolveServiceUrl(
|
|
30
52
|
service: ServiceId,
|
|
@@ -33,7 +55,7 @@ export function resolveServiceUrl(
|
|
|
33
55
|
const override = overrides?.[service];
|
|
34
56
|
if (override) return override;
|
|
35
57
|
|
|
36
|
-
const envKey = `TOPOLO_SERVICE_URL_${service.toUpperCase()}`;
|
|
58
|
+
const envKey = `TOPOLO_SERVICE_URL_${service.replace(/\./g, '_').toUpperCase()}`;
|
|
37
59
|
const envValue = typeof process !== 'undefined' ? process.env?.[envKey] : undefined;
|
|
38
60
|
if (envValue) return envValue;
|
|
39
61
|
|