@topolo/sdk 0.4.5 → 0.4.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/dist/index.cjs +9 -41
- package/dist/index.d.cts +5 -7
- package/dist/index.d.ts +5 -7
- package/dist/index.js +9 -41
- package/package.json +1 -1
- package/src/applications.generated.ts +6 -4
- package/src/client.test.ts +19 -14
- package/src/client.ts +1 -13
- package/src/modules/p2p.test.ts +2 -1
- package/src/services.generated.ts +1 -1
- package/src/services.test.ts +9 -8
- package/src/services.ts +1 -31
package/dist/index.cjs
CHANGED
|
@@ -137,7 +137,7 @@ var SERVICES = {
|
|
|
137
137
|
inventory: "https://inventory.topolo.app",
|
|
138
138
|
learn: "https://learn-api.topolo.app",
|
|
139
139
|
localize: "https://localize.topolo.app",
|
|
140
|
-
mail: "https://mail.topolo.app",
|
|
140
|
+
mail: "https://mail-api.topolo.app",
|
|
141
141
|
mdm: "https://mdm-api.topolo.app",
|
|
142
142
|
messages: "https://messages.topolo.app",
|
|
143
143
|
nexus: "https://nexus-api.topolo.app",
|
|
@@ -975,15 +975,17 @@ var APPLICATIONS = {
|
|
|
975
975
|
"mail.topolo.app"
|
|
976
976
|
],
|
|
977
977
|
"routes": [],
|
|
978
|
-
"serviceId":
|
|
978
|
+
"serviceId": null
|
|
979
979
|
},
|
|
980
980
|
{
|
|
981
981
|
"name": "topolo-mail-api",
|
|
982
982
|
"kind": "worker",
|
|
983
|
-
"apiUrl":
|
|
984
|
-
"domains": [
|
|
983
|
+
"apiUrl": "https://mail-api.topolo.app",
|
|
984
|
+
"domains": [
|
|
985
|
+
"mail-api.topolo.app"
|
|
986
|
+
],
|
|
985
987
|
"routes": [],
|
|
986
|
-
"serviceId":
|
|
988
|
+
"serviceId": "mail"
|
|
987
989
|
}
|
|
988
990
|
]
|
|
989
991
|
},
|
|
@@ -2106,10 +2108,7 @@ function resolveCatalogServiceUrl(entry, options = {}) {
|
|
|
2106
2108
|
if (override) return override;
|
|
2107
2109
|
const apiBaseUrl = firstString(entry.apiBaseUrl, entry.api_base_url);
|
|
2108
2110
|
if (apiBaseUrl) return apiBaseUrl;
|
|
2109
|
-
|
|
2110
|
-
const liveUrl = firstString(entry.baseUrl);
|
|
2111
|
-
if (bundledUrl && (!liveUrl || sameUrlEnvironment(bundledUrl, liveUrl))) return bundledUrl;
|
|
2112
|
-
return liveUrl ?? bundledUrl ?? null;
|
|
2111
|
+
return null;
|
|
2113
2112
|
}
|
|
2114
2113
|
function resolveServiceUrl(service, overrides) {
|
|
2115
2114
|
const override = resolveServiceUrlOverride([service], overrides);
|
|
@@ -2151,34 +2150,12 @@ function resolveServiceUrlOverride(keys, overrides) {
|
|
|
2151
2150
|
}
|
|
2152
2151
|
return null;
|
|
2153
2152
|
}
|
|
2154
|
-
function firstBundledServiceUrl(keys) {
|
|
2155
|
-
for (const key of keys) {
|
|
2156
|
-
const url = DEFAULT_SERVICE_URLS[key];
|
|
2157
|
-
if (url) return url;
|
|
2158
|
-
}
|
|
2159
|
-
return null;
|
|
2160
|
-
}
|
|
2161
2153
|
function firstString(...values) {
|
|
2162
2154
|
for (const value of values) {
|
|
2163
2155
|
if (typeof value === "string" && value.trim()) return value.trim();
|
|
2164
2156
|
}
|
|
2165
2157
|
return null;
|
|
2166
2158
|
}
|
|
2167
|
-
function sameUrlEnvironment(left, right) {
|
|
2168
|
-
return urlEnvironment(left) === urlEnvironment(right);
|
|
2169
|
-
}
|
|
2170
|
-
function urlEnvironment(value) {
|
|
2171
|
-
try {
|
|
2172
|
-
const url = new URL(value);
|
|
2173
|
-
const host = url.hostname.toLowerCase();
|
|
2174
|
-
if (host === "localhost" || host === "127.0.0.1") return "local";
|
|
2175
|
-
if (host.endsWith(".stg.topolo.us") || host.includes("topolo-staging")) return "staging";
|
|
2176
|
-
if (host.endsWith(".topolo.app") || host.endsWith(".topolo.us")) return "production";
|
|
2177
|
-
return "external";
|
|
2178
|
-
} catch {
|
|
2179
|
-
return "external";
|
|
2180
|
-
}
|
|
2181
|
-
}
|
|
2182
2159
|
|
|
2183
2160
|
// src/client.ts
|
|
2184
2161
|
var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
@@ -2351,7 +2328,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2351
2328
|
async resolveLiveCallableService(serviceKey) {
|
|
2352
2329
|
const catalog = await this.listServices();
|
|
2353
2330
|
const match = catalog.find((entry) => catalogEntryMatches(entry, serviceKey));
|
|
2354
|
-
if (!match
|
|
2331
|
+
if (!match) return null;
|
|
2355
2332
|
const baseUrl = resolveCatalogServiceUrl(match, {
|
|
2356
2333
|
serviceKey,
|
|
2357
2334
|
overrides: this.serviceUrls
|
|
@@ -2449,7 +2426,6 @@ var TopoloClient = class _TopoloClient {
|
|
|
2449
2426
|
slug: typeof s.app_slug === "string" ? s.app_slug : null,
|
|
2450
2427
|
name: typeof s.name === "string" ? s.name : null,
|
|
2451
2428
|
description: typeof s.description === "string" ? s.description : null,
|
|
2452
|
-
baseUrl: typeof s.base_url === "string" ? s.base_url : null,
|
|
2453
2429
|
apiBaseUrl: typeof s.api_base_url === "string" ? s.api_base_url : typeof s.apiBaseUrl === "string" ? s.apiBaseUrl : null,
|
|
2454
2430
|
launchUrl: typeof s.launch_url === "string" ? s.launch_url : typeof s.launchUrl === "string" ? s.launchUrl : null,
|
|
2455
2431
|
status: typeof s.status === "string" ? s.status : null,
|
|
@@ -2536,7 +2512,6 @@ var TopoloClient = class _TopoloClient {
|
|
|
2536
2512
|
slug: app.id,
|
|
2537
2513
|
name: app.name ?? app.id,
|
|
2538
2514
|
description: null,
|
|
2539
|
-
baseUrl: app.productionUrl ?? null,
|
|
2540
2515
|
apiBaseUrl: null,
|
|
2541
2516
|
launchUrl: app.productionUrl ?? null,
|
|
2542
2517
|
status: "bundled",
|
|
@@ -2629,13 +2604,6 @@ function catalogEntryLookupKeys(entry) {
|
|
|
2629
2604
|
}
|
|
2630
2605
|
return [...keys];
|
|
2631
2606
|
}
|
|
2632
|
-
function isCallableCatalogEntry(entry) {
|
|
2633
|
-
const kind = normalizeLookupKey(entry.surfaceKind || "");
|
|
2634
|
-
if (kind === "api" || kind === "technical_service" || kind === "service") return true;
|
|
2635
|
-
return Boolean(
|
|
2636
|
-
(entry.baseUrl || entry.apiBaseUrl) && catalogEntryLookupKeys(entry).some((key) => Object.prototype.hasOwnProperty.call(DEFAULT_SERVICE_URLS, key))
|
|
2637
|
-
);
|
|
2638
|
-
}
|
|
2639
2607
|
function normalizeActionCatalogEntry(item) {
|
|
2640
2608
|
if (!item || typeof item !== "object" || Array.isArray(item)) return null;
|
|
2641
2609
|
const a = item;
|
package/dist/index.d.cts
CHANGED
|
@@ -104,7 +104,6 @@ interface TopoloServiceCatalogEntry {
|
|
|
104
104
|
slug: string | null;
|
|
105
105
|
name: string | null;
|
|
106
106
|
description: string | null;
|
|
107
|
-
baseUrl: string | null;
|
|
108
107
|
apiBaseUrl?: string | null;
|
|
109
108
|
launchUrl?: string | null;
|
|
110
109
|
status: string | null;
|
|
@@ -259,7 +258,7 @@ declare const SERVICES: {
|
|
|
259
258
|
readonly inventory: "https://inventory.topolo.app";
|
|
260
259
|
readonly learn: "https://learn-api.topolo.app";
|
|
261
260
|
readonly localize: "https://localize.topolo.app";
|
|
262
|
-
readonly mail: "https://mail.topolo.app";
|
|
261
|
+
readonly mail: "https://mail-api.topolo.app";
|
|
263
262
|
readonly mdm: "https://mdm-api.topolo.app";
|
|
264
263
|
readonly messages: "https://messages.topolo.app";
|
|
265
264
|
readonly nexus: "https://nexus-api.topolo.app";
|
|
@@ -891,14 +890,14 @@ declare const APPLICATIONS: {
|
|
|
891
890
|
readonly apiUrl: "https://mail.topolo.app";
|
|
892
891
|
readonly domains: readonly ["mail.topolo.app"];
|
|
893
892
|
readonly routes: readonly [];
|
|
894
|
-
readonly serviceId:
|
|
893
|
+
readonly serviceId: null;
|
|
895
894
|
}, {
|
|
896
895
|
readonly name: "topolo-mail-api";
|
|
897
896
|
readonly kind: "worker";
|
|
898
|
-
readonly apiUrl:
|
|
899
|
-
readonly domains: readonly [];
|
|
897
|
+
readonly apiUrl: "https://mail-api.topolo.app";
|
|
898
|
+
readonly domains: readonly ["mail-api.topolo.app"];
|
|
900
899
|
readonly routes: readonly [];
|
|
901
|
-
readonly serviceId:
|
|
900
|
+
readonly serviceId: "mail";
|
|
902
901
|
}];
|
|
903
902
|
};
|
|
904
903
|
readonly mdm: {
|
|
@@ -1585,7 +1584,6 @@ interface ServiceCatalogUrlEntry {
|
|
|
1585
1584
|
serviceId?: string | null;
|
|
1586
1585
|
slug?: string | null;
|
|
1587
1586
|
name?: string | null;
|
|
1588
|
-
baseUrl?: string | null;
|
|
1589
1587
|
apiBaseUrl?: string | null;
|
|
1590
1588
|
launchUrl?: string | null;
|
|
1591
1589
|
api_base_url?: string | null;
|
package/dist/index.d.ts
CHANGED
|
@@ -104,7 +104,6 @@ interface TopoloServiceCatalogEntry {
|
|
|
104
104
|
slug: string | null;
|
|
105
105
|
name: string | null;
|
|
106
106
|
description: string | null;
|
|
107
|
-
baseUrl: string | null;
|
|
108
107
|
apiBaseUrl?: string | null;
|
|
109
108
|
launchUrl?: string | null;
|
|
110
109
|
status: string | null;
|
|
@@ -259,7 +258,7 @@ declare const SERVICES: {
|
|
|
259
258
|
readonly inventory: "https://inventory.topolo.app";
|
|
260
259
|
readonly learn: "https://learn-api.topolo.app";
|
|
261
260
|
readonly localize: "https://localize.topolo.app";
|
|
262
|
-
readonly mail: "https://mail.topolo.app";
|
|
261
|
+
readonly mail: "https://mail-api.topolo.app";
|
|
263
262
|
readonly mdm: "https://mdm-api.topolo.app";
|
|
264
263
|
readonly messages: "https://messages.topolo.app";
|
|
265
264
|
readonly nexus: "https://nexus-api.topolo.app";
|
|
@@ -891,14 +890,14 @@ declare const APPLICATIONS: {
|
|
|
891
890
|
readonly apiUrl: "https://mail.topolo.app";
|
|
892
891
|
readonly domains: readonly ["mail.topolo.app"];
|
|
893
892
|
readonly routes: readonly [];
|
|
894
|
-
readonly serviceId:
|
|
893
|
+
readonly serviceId: null;
|
|
895
894
|
}, {
|
|
896
895
|
readonly name: "topolo-mail-api";
|
|
897
896
|
readonly kind: "worker";
|
|
898
|
-
readonly apiUrl:
|
|
899
|
-
readonly domains: readonly [];
|
|
897
|
+
readonly apiUrl: "https://mail-api.topolo.app";
|
|
898
|
+
readonly domains: readonly ["mail-api.topolo.app"];
|
|
900
899
|
readonly routes: readonly [];
|
|
901
|
-
readonly serviceId:
|
|
900
|
+
readonly serviceId: "mail";
|
|
902
901
|
}];
|
|
903
902
|
};
|
|
904
903
|
readonly mdm: {
|
|
@@ -1585,7 +1584,6 @@ interface ServiceCatalogUrlEntry {
|
|
|
1585
1584
|
serviceId?: string | null;
|
|
1586
1585
|
slug?: string | null;
|
|
1587
1586
|
name?: string | null;
|
|
1588
|
-
baseUrl?: string | null;
|
|
1589
1587
|
apiBaseUrl?: string | null;
|
|
1590
1588
|
launchUrl?: string | null;
|
|
1591
1589
|
api_base_url?: string | null;
|
package/dist/index.js
CHANGED
|
@@ -82,7 +82,7 @@ var SERVICES = {
|
|
|
82
82
|
inventory: "https://inventory.topolo.app",
|
|
83
83
|
learn: "https://learn-api.topolo.app",
|
|
84
84
|
localize: "https://localize.topolo.app",
|
|
85
|
-
mail: "https://mail.topolo.app",
|
|
85
|
+
mail: "https://mail-api.topolo.app",
|
|
86
86
|
mdm: "https://mdm-api.topolo.app",
|
|
87
87
|
messages: "https://messages.topolo.app",
|
|
88
88
|
nexus: "https://nexus-api.topolo.app",
|
|
@@ -920,15 +920,17 @@ var APPLICATIONS = {
|
|
|
920
920
|
"mail.topolo.app"
|
|
921
921
|
],
|
|
922
922
|
"routes": [],
|
|
923
|
-
"serviceId":
|
|
923
|
+
"serviceId": null
|
|
924
924
|
},
|
|
925
925
|
{
|
|
926
926
|
"name": "topolo-mail-api",
|
|
927
927
|
"kind": "worker",
|
|
928
|
-
"apiUrl":
|
|
929
|
-
"domains": [
|
|
928
|
+
"apiUrl": "https://mail-api.topolo.app",
|
|
929
|
+
"domains": [
|
|
930
|
+
"mail-api.topolo.app"
|
|
931
|
+
],
|
|
930
932
|
"routes": [],
|
|
931
|
-
"serviceId":
|
|
933
|
+
"serviceId": "mail"
|
|
932
934
|
}
|
|
933
935
|
]
|
|
934
936
|
},
|
|
@@ -2051,10 +2053,7 @@ function resolveCatalogServiceUrl(entry, options = {}) {
|
|
|
2051
2053
|
if (override) return override;
|
|
2052
2054
|
const apiBaseUrl = firstString(entry.apiBaseUrl, entry.api_base_url);
|
|
2053
2055
|
if (apiBaseUrl) return apiBaseUrl;
|
|
2054
|
-
|
|
2055
|
-
const liveUrl = firstString(entry.baseUrl);
|
|
2056
|
-
if (bundledUrl && (!liveUrl || sameUrlEnvironment(bundledUrl, liveUrl))) return bundledUrl;
|
|
2057
|
-
return liveUrl ?? bundledUrl ?? null;
|
|
2056
|
+
return null;
|
|
2058
2057
|
}
|
|
2059
2058
|
function resolveServiceUrl(service, overrides) {
|
|
2060
2059
|
const override = resolveServiceUrlOverride([service], overrides);
|
|
@@ -2096,34 +2095,12 @@ function resolveServiceUrlOverride(keys, overrides) {
|
|
|
2096
2095
|
}
|
|
2097
2096
|
return null;
|
|
2098
2097
|
}
|
|
2099
|
-
function firstBundledServiceUrl(keys) {
|
|
2100
|
-
for (const key of keys) {
|
|
2101
|
-
const url = DEFAULT_SERVICE_URLS[key];
|
|
2102
|
-
if (url) return url;
|
|
2103
|
-
}
|
|
2104
|
-
return null;
|
|
2105
|
-
}
|
|
2106
2098
|
function firstString(...values) {
|
|
2107
2099
|
for (const value of values) {
|
|
2108
2100
|
if (typeof value === "string" && value.trim()) return value.trim();
|
|
2109
2101
|
}
|
|
2110
2102
|
return null;
|
|
2111
2103
|
}
|
|
2112
|
-
function sameUrlEnvironment(left, right) {
|
|
2113
|
-
return urlEnvironment(left) === urlEnvironment(right);
|
|
2114
|
-
}
|
|
2115
|
-
function urlEnvironment(value) {
|
|
2116
|
-
try {
|
|
2117
|
-
const url = new URL(value);
|
|
2118
|
-
const host = url.hostname.toLowerCase();
|
|
2119
|
-
if (host === "localhost" || host === "127.0.0.1") return "local";
|
|
2120
|
-
if (host.endsWith(".stg.topolo.us") || host.includes("topolo-staging")) return "staging";
|
|
2121
|
-
if (host.endsWith(".topolo.app") || host.endsWith(".topolo.us")) return "production";
|
|
2122
|
-
return "external";
|
|
2123
|
-
} catch {
|
|
2124
|
-
return "external";
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
2104
|
|
|
2128
2105
|
// src/client.ts
|
|
2129
2106
|
var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
@@ -2296,7 +2273,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2296
2273
|
async resolveLiveCallableService(serviceKey) {
|
|
2297
2274
|
const catalog = await this.listServices();
|
|
2298
2275
|
const match = catalog.find((entry) => catalogEntryMatches(entry, serviceKey));
|
|
2299
|
-
if (!match
|
|
2276
|
+
if (!match) return null;
|
|
2300
2277
|
const baseUrl = resolveCatalogServiceUrl(match, {
|
|
2301
2278
|
serviceKey,
|
|
2302
2279
|
overrides: this.serviceUrls
|
|
@@ -2394,7 +2371,6 @@ var TopoloClient = class _TopoloClient {
|
|
|
2394
2371
|
slug: typeof s.app_slug === "string" ? s.app_slug : null,
|
|
2395
2372
|
name: typeof s.name === "string" ? s.name : null,
|
|
2396
2373
|
description: typeof s.description === "string" ? s.description : null,
|
|
2397
|
-
baseUrl: typeof s.base_url === "string" ? s.base_url : null,
|
|
2398
2374
|
apiBaseUrl: typeof s.api_base_url === "string" ? s.api_base_url : typeof s.apiBaseUrl === "string" ? s.apiBaseUrl : null,
|
|
2399
2375
|
launchUrl: typeof s.launch_url === "string" ? s.launch_url : typeof s.launchUrl === "string" ? s.launchUrl : null,
|
|
2400
2376
|
status: typeof s.status === "string" ? s.status : null,
|
|
@@ -2481,7 +2457,6 @@ var TopoloClient = class _TopoloClient {
|
|
|
2481
2457
|
slug: app.id,
|
|
2482
2458
|
name: app.name ?? app.id,
|
|
2483
2459
|
description: null,
|
|
2484
|
-
baseUrl: app.productionUrl ?? null,
|
|
2485
2460
|
apiBaseUrl: null,
|
|
2486
2461
|
launchUrl: app.productionUrl ?? null,
|
|
2487
2462
|
status: "bundled",
|
|
@@ -2574,13 +2549,6 @@ function catalogEntryLookupKeys(entry) {
|
|
|
2574
2549
|
}
|
|
2575
2550
|
return [...keys];
|
|
2576
2551
|
}
|
|
2577
|
-
function isCallableCatalogEntry(entry) {
|
|
2578
|
-
const kind = normalizeLookupKey(entry.surfaceKind || "");
|
|
2579
|
-
if (kind === "api" || kind === "technical_service" || kind === "service") return true;
|
|
2580
|
-
return Boolean(
|
|
2581
|
-
(entry.baseUrl || entry.apiBaseUrl) && catalogEntryLookupKeys(entry).some((key) => Object.prototype.hasOwnProperty.call(DEFAULT_SERVICE_URLS, key))
|
|
2582
|
-
);
|
|
2583
|
-
}
|
|
2584
2552
|
function normalizeActionCatalogEntry(item) {
|
|
2585
2553
|
if (!item || typeof item !== "object" || Array.isArray(item)) return null;
|
|
2586
2554
|
const a = item;
|
package/package.json
CHANGED
|
@@ -813,15 +813,17 @@ export const APPLICATIONS = {
|
|
|
813
813
|
"mail.topolo.app"
|
|
814
814
|
],
|
|
815
815
|
"routes": [],
|
|
816
|
-
"serviceId":
|
|
816
|
+
"serviceId": null
|
|
817
817
|
},
|
|
818
818
|
{
|
|
819
819
|
"name": "topolo-mail-api",
|
|
820
820
|
"kind": "worker",
|
|
821
|
-
"apiUrl":
|
|
822
|
-
"domains": [
|
|
821
|
+
"apiUrl": "https://mail-api.topolo.app",
|
|
822
|
+
"domains": [
|
|
823
|
+
"mail-api.topolo.app"
|
|
824
|
+
],
|
|
823
825
|
"routes": [],
|
|
824
|
-
"serviceId":
|
|
826
|
+
"serviceId": "mail"
|
|
825
827
|
}
|
|
826
828
|
]
|
|
827
829
|
},
|
package/src/client.test.ts
CHANGED
|
@@ -25,7 +25,8 @@ const MAIL_CATALOG = {
|
|
|
25
25
|
service_id: 'srv_mail',
|
|
26
26
|
app_slug: 'topolo-mail',
|
|
27
27
|
name: 'Topolo Mail',
|
|
28
|
-
|
|
28
|
+
api_base_url: 'https://mail-api.topolo.app',
|
|
29
|
+
launch_url: 'https://mail.topolo.app',
|
|
29
30
|
surface_kind: 'api',
|
|
30
31
|
status: 'active',
|
|
31
32
|
granted_permissions: ['srv_mail.messages:read'],
|
|
@@ -376,7 +377,7 @@ describe('TopoloClient.getServiceIdBySlug', () => {
|
|
|
376
377
|
|
|
377
378
|
it('falls back to /api/services/by-slug/:slug for unknown slugs', async () => {
|
|
378
379
|
const { impl, calls } = stubFetch([
|
|
379
|
-
{ body: { success: true, data: { service_id: 'service_abc123',
|
|
380
|
+
{ body: { success: true, data: { service_id: 'service_abc123', api_base_url: 'https://x-api' } } },
|
|
380
381
|
]);
|
|
381
382
|
const client = freshClient(impl, { auth: 'https://auth.example.com' });
|
|
382
383
|
const id = await client.getServiceIdBySlug('external-service');
|
|
@@ -452,7 +453,8 @@ describe('TopoloClient.listServices', () => {
|
|
|
452
453
|
app_slug: 'topolo-director',
|
|
453
454
|
name: 'Topolo Director',
|
|
454
455
|
description: 'Demo control plane',
|
|
455
|
-
|
|
456
|
+
api_base_url: 'https://director-api.topolo.app',
|
|
457
|
+
launch_url: 'https://director.topolo.app',
|
|
456
458
|
status: 'active',
|
|
457
459
|
surface_kind: 'application',
|
|
458
460
|
launchable: true,
|
|
@@ -476,9 +478,8 @@ describe('TopoloClient.listServices', () => {
|
|
|
476
478
|
slug: 'topolo-director',
|
|
477
479
|
name: 'Topolo Director',
|
|
478
480
|
description: 'Demo control plane',
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
launchUrl: null,
|
|
481
|
+
apiBaseUrl: 'https://director-api.topolo.app',
|
|
482
|
+
launchUrl: 'https://director.topolo.app',
|
|
482
483
|
status: 'active',
|
|
483
484
|
surfaceKind: 'application',
|
|
484
485
|
launchable: true,
|
|
@@ -514,7 +515,8 @@ describe('TopoloClient.listServices', () => {
|
|
|
514
515
|
service_id: 'srv_mail',
|
|
515
516
|
app_slug: 'topolo-mail',
|
|
516
517
|
name: 'Topolo Mail',
|
|
517
|
-
|
|
518
|
+
api_base_url: 'https://mail-api.topolo.app',
|
|
519
|
+
launch_url: 'https://mail.topolo.app',
|
|
518
520
|
surface_kind: 'application',
|
|
519
521
|
status: 'active',
|
|
520
522
|
granted_permissions: ['srv_mail.messages:read'],
|
|
@@ -535,7 +537,7 @@ describe('TopoloClient.listServices', () => {
|
|
|
535
537
|
await client.request({ service: 'mail', path: '/api/health' });
|
|
536
538
|
|
|
537
539
|
expect(calls[0]!.url).toBe('https://auth.example.com/api/services/catalog');
|
|
538
|
-
expect(calls[1]!.url).toBe('https://mail.topolo.app/api/health');
|
|
540
|
+
expect(calls[1]!.url).toBe('https://mail-api.topolo.app/api/health');
|
|
539
541
|
});
|
|
540
542
|
|
|
541
543
|
it('routes known split-surface services to the callable API URL', async () => {
|
|
@@ -548,7 +550,8 @@ describe('TopoloClient.listServices', () => {
|
|
|
548
550
|
service_id: 'srv_crm',
|
|
549
551
|
app_slug: 'topolo-crm',
|
|
550
552
|
name: 'Topolo CRM',
|
|
551
|
-
|
|
553
|
+
api_base_url: 'https://crm-api.topolo.app',
|
|
554
|
+
launch_url: 'https://crm.topolo.app',
|
|
552
555
|
surface_kind: 'application',
|
|
553
556
|
status: 'active',
|
|
554
557
|
granted_permissions: ['srv_crm.contacts:read'],
|
|
@@ -582,8 +585,8 @@ describe('TopoloClient.listServices', () => {
|
|
|
582
585
|
service_id: 'srv_future',
|
|
583
586
|
app_slug: 'topolo-future',
|
|
584
587
|
name: 'Future',
|
|
585
|
-
base_url: 'https://future.topolo.app',
|
|
586
588
|
api_base_url: 'https://future-api.topolo.app',
|
|
589
|
+
launch_url: 'https://future.topolo.app',
|
|
587
590
|
surface_kind: 'api',
|
|
588
591
|
status: 'active',
|
|
589
592
|
granted_permissions: ['srv_future.read:all'],
|
|
@@ -616,7 +619,8 @@ describe('TopoloClient.listServices', () => {
|
|
|
616
619
|
service_id: 'srv_crm',
|
|
617
620
|
app_slug: 'topolo-crm',
|
|
618
621
|
name: 'Topolo CRM',
|
|
619
|
-
|
|
622
|
+
api_base_url: 'https://crm-api.topolo.app',
|
|
623
|
+
launch_url: 'https://crm.topolo.app',
|
|
620
624
|
surface_kind: 'api',
|
|
621
625
|
status: 'active',
|
|
622
626
|
granted_permissions: ['srv_crm.contacts:read'],
|
|
@@ -649,7 +653,7 @@ describe('TopoloClient.listServices', () => {
|
|
|
649
653
|
service_id: 'srv_future_app',
|
|
650
654
|
app_slug: 'topolo-future-app',
|
|
651
655
|
name: 'Future App',
|
|
652
|
-
|
|
656
|
+
launch_url: 'https://future-app.topolo.app',
|
|
653
657
|
surface_kind: 'application',
|
|
654
658
|
status: 'active',
|
|
655
659
|
granted_permissions: ['srv_future_app.launch:read'],
|
|
@@ -682,7 +686,8 @@ describe('TopoloClient.listServices', () => {
|
|
|
682
686
|
service_id: 'srv_future',
|
|
683
687
|
app_slug: 'topolo-warehouse',
|
|
684
688
|
name: 'Warehouse',
|
|
685
|
-
|
|
689
|
+
api_base_url: 'https://warehouse.topolo.app',
|
|
690
|
+
launch_url: 'https://warehouse.topolo.app',
|
|
686
691
|
surface_kind: 'api',
|
|
687
692
|
status: 'active',
|
|
688
693
|
granted_permissions: ['srv_future.read:all'],
|
|
@@ -741,7 +746,7 @@ describe('TopoloClient action catalog', () => {
|
|
|
741
746
|
await client.callAction('messages.list', { mailboxId: 'primary', q: 'Ada', page: 2 });
|
|
742
747
|
const call = requestCalls(calls)[0]!;
|
|
743
748
|
const url = new URL(call.url);
|
|
744
|
-
expect(url.toString()).toBe('https://mail.topolo.app/api/mail/mailboxes/primary/messages?q=Ada&page=2');
|
|
749
|
+
expect(url.toString()).toBe('https://mail-api.topolo.app/api/mail/mailboxes/primary/messages?q=Ada&page=2');
|
|
745
750
|
expect(call.init.method).toBe('GET');
|
|
746
751
|
const headers = new Headers(call.init.headers);
|
|
747
752
|
expect(headers.get('X-Topolo-Action-Id')).toBe('srv_mail.messages.list');
|
package/src/client.ts
CHANGED
|
@@ -106,7 +106,6 @@ export interface TopoloServiceCatalogEntry {
|
|
|
106
106
|
slug: string | null;
|
|
107
107
|
name: string | null;
|
|
108
108
|
description: string | null;
|
|
109
|
-
baseUrl: string | null;
|
|
110
109
|
apiBaseUrl?: string | null;
|
|
111
110
|
launchUrl?: string | null;
|
|
112
111
|
status: string | null;
|
|
@@ -328,7 +327,7 @@ export class TopoloClient {
|
|
|
328
327
|
private async resolveLiveCallableService(serviceKey: string): Promise<{ serviceKey: string; baseUrl: string } | null> {
|
|
329
328
|
const catalog = await this.listServices();
|
|
330
329
|
const match = catalog.find((entry) => catalogEntryMatches(entry, serviceKey));
|
|
331
|
-
if (!match
|
|
330
|
+
if (!match) return null;
|
|
332
331
|
const baseUrl = resolveCatalogServiceUrl(match, {
|
|
333
332
|
serviceKey,
|
|
334
333
|
overrides: this.serviceUrls,
|
|
@@ -454,7 +453,6 @@ export class TopoloClient {
|
|
|
454
453
|
name: typeof s.name === 'string' ? s.name : null,
|
|
455
454
|
description:
|
|
456
455
|
typeof s.description === 'string' ? s.description : null,
|
|
457
|
-
baseUrl: typeof s.base_url === 'string' ? s.base_url : null,
|
|
458
456
|
apiBaseUrl:
|
|
459
457
|
typeof s.api_base_url === 'string'
|
|
460
458
|
? s.api_base_url
|
|
@@ -573,7 +571,6 @@ export class TopoloClient {
|
|
|
573
571
|
slug: app.id,
|
|
574
572
|
name: app.name ?? app.id,
|
|
575
573
|
description: null,
|
|
576
|
-
baseUrl: app.productionUrl ?? null,
|
|
577
574
|
apiBaseUrl: null,
|
|
578
575
|
launchUrl: app.productionUrl ?? null,
|
|
579
576
|
status: 'bundled',
|
|
@@ -695,15 +692,6 @@ function catalogEntryLookupKeys(entry: TopoloServiceCatalogEntry): string[] {
|
|
|
695
692
|
return [...keys];
|
|
696
693
|
}
|
|
697
694
|
|
|
698
|
-
function isCallableCatalogEntry(entry: TopoloServiceCatalogEntry): boolean {
|
|
699
|
-
const kind = normalizeLookupKey(entry.surfaceKind || '');
|
|
700
|
-
if (kind === 'api' || kind === 'technical_service' || kind === 'service') return true;
|
|
701
|
-
return Boolean(
|
|
702
|
-
(entry.baseUrl || entry.apiBaseUrl) &&
|
|
703
|
-
catalogEntryLookupKeys(entry).some((key) => Object.prototype.hasOwnProperty.call(DEFAULT_SERVICE_URLS, key)),
|
|
704
|
-
);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
695
|
function normalizeActionCatalogEntry(item: unknown): TopoloActionCatalogEntry | null {
|
|
708
696
|
if (!item || typeof item !== 'object' || Array.isArray(item)) return null;
|
|
709
697
|
const a = item as Record<string, unknown>;
|
package/src/modules/p2p.test.ts
CHANGED
|
@@ -22,7 +22,8 @@ function stubFetch(responses: Array<{ status?: number; body?: unknown }>) {
|
|
|
22
22
|
service_id: 'srv_p2p',
|
|
23
23
|
app_slug: 'topolo-p2p',
|
|
24
24
|
name: 'Topolo P2P',
|
|
25
|
-
|
|
25
|
+
api_base_url: 'https://p2p.topolo.app',
|
|
26
|
+
launch_url: 'https://p2p.topolo.app',
|
|
26
27
|
surface_kind: 'api',
|
|
27
28
|
status: 'active',
|
|
28
29
|
granted_permissions: ['srv_p2p.directory:read'],
|
|
@@ -27,7 +27,7 @@ export const SERVICES = {
|
|
|
27
27
|
inventory: "https://inventory.topolo.app",
|
|
28
28
|
learn: "https://learn-api.topolo.app",
|
|
29
29
|
localize: "https://localize.topolo.app",
|
|
30
|
-
mail: "https://mail.topolo.app",
|
|
30
|
+
mail: "https://mail-api.topolo.app",
|
|
31
31
|
mdm: "https://mdm-api.topolo.app",
|
|
32
32
|
messages: "https://messages.topolo.app",
|
|
33
33
|
nexus: "https://nexus-api.topolo.app",
|
package/src/services.test.ts
CHANGED
|
@@ -70,7 +70,7 @@ describe('service registry', () => {
|
|
|
70
70
|
books: 'https://books.topolo.app',
|
|
71
71
|
consent: 'https://consent.topolo.app',
|
|
72
72
|
feed: 'https://feed-api.topolo.app',
|
|
73
|
-
mail: 'https://mail.topolo.app',
|
|
73
|
+
mail: 'https://mail-api.topolo.app',
|
|
74
74
|
one: 'https://one.topolo.app',
|
|
75
75
|
web: 'https://web-api.topolo.app',
|
|
76
76
|
});
|
|
@@ -99,30 +99,31 @@ describe('service registry', () => {
|
|
|
99
99
|
}
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
-
it('resolves
|
|
102
|
+
it('resolves services from explicit catalog API URLs', () => {
|
|
103
103
|
expect(resolveCatalogServiceUrl({
|
|
104
104
|
serviceId: 'srv_crm',
|
|
105
105
|
slug: 'topolo-crm',
|
|
106
106
|
name: 'Topolo CRM',
|
|
107
|
-
|
|
107
|
+
launchUrl: 'https://crm.topolo.app',
|
|
108
|
+
apiBaseUrl: 'https://crm-api.topolo.app',
|
|
108
109
|
}, { serviceKey: 'crm' })).toBe('https://crm-api.topolo.app');
|
|
109
110
|
});
|
|
110
111
|
|
|
111
|
-
it('
|
|
112
|
+
it('does not infer callable URLs from launcher URLs', () => {
|
|
112
113
|
expect(resolveCatalogServiceUrl({
|
|
113
114
|
serviceId: 'srv_crm',
|
|
114
115
|
slug: 'topolo-crm',
|
|
115
116
|
name: 'Topolo CRM',
|
|
116
|
-
|
|
117
|
-
}, { serviceKey: 'crm' })).
|
|
117
|
+
launchUrl: 'https://crm.stg.topolo.us',
|
|
118
|
+
}, { serviceKey: 'crm' })).toBeNull();
|
|
118
119
|
});
|
|
119
120
|
|
|
120
|
-
it('prefers explicit catalog API URLs over
|
|
121
|
+
it('prefers explicit catalog API URLs over launch URLs', () => {
|
|
121
122
|
expect(resolveCatalogServiceUrl({
|
|
122
123
|
serviceId: 'srv_future',
|
|
123
124
|
slug: 'topolo-future',
|
|
124
125
|
name: 'Future',
|
|
125
|
-
|
|
126
|
+
launchUrl: 'https://future.topolo.app',
|
|
126
127
|
apiBaseUrl: 'https://future-api.topolo.app',
|
|
127
128
|
})).toBe('https://future-api.topolo.app');
|
|
128
129
|
});
|
package/src/services.ts
CHANGED
|
@@ -51,7 +51,6 @@ export interface ServiceCatalogUrlEntry {
|
|
|
51
51
|
serviceId?: string | null;
|
|
52
52
|
slug?: string | null;
|
|
53
53
|
name?: string | null;
|
|
54
|
-
baseUrl?: string | null;
|
|
55
54
|
apiBaseUrl?: string | null;
|
|
56
55
|
launchUrl?: string | null;
|
|
57
56
|
api_base_url?: string | null;
|
|
@@ -72,11 +71,7 @@ export function resolveCatalogServiceUrl(
|
|
|
72
71
|
const apiBaseUrl = firstString(entry.apiBaseUrl, entry.api_base_url);
|
|
73
72
|
if (apiBaseUrl) return apiBaseUrl;
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
const liveUrl = firstString(entry.baseUrl);
|
|
77
|
-
if (bundledUrl && (!liveUrl || sameUrlEnvironment(bundledUrl, liveUrl))) return bundledUrl;
|
|
78
|
-
|
|
79
|
-
return liveUrl ?? bundledUrl ?? null;
|
|
74
|
+
return null;
|
|
80
75
|
}
|
|
81
76
|
|
|
82
77
|
export function resolveServiceUrl(
|
|
@@ -135,34 +130,9 @@ function resolveServiceUrlOverride(
|
|
|
135
130
|
return null;
|
|
136
131
|
}
|
|
137
132
|
|
|
138
|
-
function firstBundledServiceUrl(keys: string[]): string | null {
|
|
139
|
-
for (const key of keys) {
|
|
140
|
-
const url = DEFAULT_SERVICE_URLS[key as ServiceId];
|
|
141
|
-
if (url) return url;
|
|
142
|
-
}
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
133
|
function firstString(...values: Array<string | null | undefined>): string | null {
|
|
147
134
|
for (const value of values) {
|
|
148
135
|
if (typeof value === 'string' && value.trim()) return value.trim();
|
|
149
136
|
}
|
|
150
137
|
return null;
|
|
151
138
|
}
|
|
152
|
-
|
|
153
|
-
function sameUrlEnvironment(left: string, right: string): boolean {
|
|
154
|
-
return urlEnvironment(left) === urlEnvironment(right);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function urlEnvironment(value: string): 'staging' | 'production' | 'local' | 'external' {
|
|
158
|
-
try {
|
|
159
|
-
const url = new URL(value);
|
|
160
|
-
const host = url.hostname.toLowerCase();
|
|
161
|
-
if (host === 'localhost' || host === '127.0.0.1') return 'local';
|
|
162
|
-
if (host.endsWith('.stg.topolo.us') || host.includes('topolo-staging')) return 'staging';
|
|
163
|
-
if (host.endsWith('.topolo.app') || host.endsWith('.topolo.us')) return 'production';
|
|
164
|
-
return 'external';
|
|
165
|
-
} catch {
|
|
166
|
-
return 'external';
|
|
167
|
-
}
|
|
168
|
-
}
|