@topolo/sdk 0.10.2 → 0.10.4
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 +451 -34
- package/dist/index.d.cts +119 -1
- package/dist/index.d.ts +119 -1
- package/dist/index.js +437 -34
- package/dist/node/credential-store.cjs +254 -25
- package/dist/node/credential-store.d.cts +10 -3
- package/dist/node/credential-store.d.ts +10 -3
- package/dist/node/credential-store.js +251 -25
- package/package.json +2 -2
|
@@ -23,6 +23,8 @@ __export(credential_store_exports, {
|
|
|
23
23
|
ENVIRONMENT_DEFAULT_SERVICE_URLS: () => ENVIRONMENT_DEFAULT_SERVICE_URLS,
|
|
24
24
|
TOPOLO_ENVIRONMENTS: () => TOPOLO_ENVIRONMENTS,
|
|
25
25
|
activateContext: () => activateContext,
|
|
26
|
+
assertServiceUrlForEnvironment: () => assertServiceUrlForEnvironment,
|
|
27
|
+
assertTopoloServiceUrlEnvironment: () => assertTopoloServiceUrlEnvironment,
|
|
26
28
|
clearProfileCredential: () => clearProfileCredential,
|
|
27
29
|
configPath: () => configPath,
|
|
28
30
|
contextKey: () => contextKey,
|
|
@@ -38,6 +40,7 @@ __export(credential_store_exports, {
|
|
|
38
40
|
resolveServiceUrls: () => resolveServiceUrls,
|
|
39
41
|
resolvedProfileConfig: () => resolvedProfileConfig,
|
|
40
42
|
selectedEnvironment: () => selectedEnvironment,
|
|
43
|
+
topoloServiceOrigins: () => topoloServiceOrigins,
|
|
41
44
|
writeConfig: () => writeConfig,
|
|
42
45
|
writeContextCredential: () => writeContextCredential,
|
|
43
46
|
writeProfileConfig: () => writeProfileConfig
|
|
@@ -57,6 +60,90 @@ var TopoloSdkError = class extends Error {
|
|
|
57
60
|
this.code = code;
|
|
58
61
|
}
|
|
59
62
|
};
|
|
63
|
+
var PUBLIC_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
64
|
+
"action_app_identity_unavailable",
|
|
65
|
+
"action_authentication_required",
|
|
66
|
+
"action_catalog_summary_unavailable",
|
|
67
|
+
"action_catalog_unavailable",
|
|
68
|
+
"action_contract_mismatch",
|
|
69
|
+
"action_permission_required",
|
|
70
|
+
"action_runtime_authorization_required",
|
|
71
|
+
"action_service_not_allowed",
|
|
72
|
+
"auth_error",
|
|
73
|
+
"authentication_required",
|
|
74
|
+
"authorization_pending",
|
|
75
|
+
"bad_sig",
|
|
76
|
+
"confirmation_required",
|
|
77
|
+
"dependency_unavailable",
|
|
78
|
+
"environment_mismatch",
|
|
79
|
+
"environment_service_url_required",
|
|
80
|
+
"expired_token",
|
|
81
|
+
"forbidden",
|
|
82
|
+
"http_error",
|
|
83
|
+
"immutable_drift",
|
|
84
|
+
"integration_dependency_unavailable",
|
|
85
|
+
"integration_misconfigured",
|
|
86
|
+
"invalid_action",
|
|
87
|
+
"invalid_action_input",
|
|
88
|
+
"invalid_grant",
|
|
89
|
+
"invalid_input",
|
|
90
|
+
"invalid_token",
|
|
91
|
+
"mcp_error",
|
|
92
|
+
"membership_required",
|
|
93
|
+
"network_error",
|
|
94
|
+
"organization_context_required",
|
|
95
|
+
"organization_enrollment_required",
|
|
96
|
+
"permission_denied",
|
|
97
|
+
"rate_limited",
|
|
98
|
+
"request_failed",
|
|
99
|
+
"service_catalog_unavailable",
|
|
100
|
+
"service_path_origin_mismatch",
|
|
101
|
+
"service_required",
|
|
102
|
+
"slow_down",
|
|
103
|
+
"state_conflict",
|
|
104
|
+
"unauthorized",
|
|
105
|
+
"unknown_action",
|
|
106
|
+
"unknown_service"
|
|
107
|
+
]);
|
|
108
|
+
var ERROR_CODE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
|
|
109
|
+
var TOPOLO_REQUEST_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
110
|
+
var CLOUDFLARE_RAY_ID_PATTERN = /^[0-9a-f]{16}-[A-Z]{3}$/;
|
|
111
|
+
var PERMISSION_PATTERN = /^[a-z][a-z0-9_-]{0,63}(?:\.[a-z][a-z0-9_-]{0,63})*:(?:[a-z][a-z0-9_-]{0,63}|\*)$/;
|
|
112
|
+
var RETRY_AFTER_DATE_PATTERN = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/;
|
|
113
|
+
var PUBLIC_DETAIL_SOURCES = /* @__PURE__ */ new Set([
|
|
114
|
+
"api_key_introspection",
|
|
115
|
+
"api_key_validation",
|
|
116
|
+
"auth_context",
|
|
117
|
+
"auth_validate",
|
|
118
|
+
"catalog",
|
|
119
|
+
"credential",
|
|
120
|
+
"runtime",
|
|
121
|
+
"service_token_provider",
|
|
122
|
+
"service_verify_key",
|
|
123
|
+
"token_validation",
|
|
124
|
+
"upstream"
|
|
125
|
+
]);
|
|
126
|
+
function canonicalTopoloErrorCode(value) {
|
|
127
|
+
if (typeof value !== "string") return null;
|
|
128
|
+
const candidate = value.trim().toLowerCase();
|
|
129
|
+
if (!ERROR_CODE_PATTERN.test(candidate) || !PUBLIC_ERROR_CODES.has(candidate)) return null;
|
|
130
|
+
return candidate;
|
|
131
|
+
}
|
|
132
|
+
function canonicalTopoloRequestId(value) {
|
|
133
|
+
if (typeof value !== "string") return void 0;
|
|
134
|
+
const candidate = value.trim();
|
|
135
|
+
return TOPOLO_REQUEST_ID_PATTERN.test(candidate) || CLOUDFLARE_RAY_ID_PATTERN.test(candidate) ? candidate : void 0;
|
|
136
|
+
}
|
|
137
|
+
function canonicalTopoloRetryAfter(value) {
|
|
138
|
+
if (typeof value !== "string") return void 0;
|
|
139
|
+
const candidate = value.trim();
|
|
140
|
+
if (/^\d{1,10}$/.test(candidate) || RETRY_AFTER_DATE_PATTERN.test(candidate)) return candidate;
|
|
141
|
+
return void 0;
|
|
142
|
+
}
|
|
143
|
+
function canonicalTopoloRequired(value) {
|
|
144
|
+
if (!Array.isArray(value)) return [];
|
|
145
|
+
return [...new Set(value.filter((item) => typeof item === "string" && PERMISSION_PATTERN.test(item)))].slice(0, 32);
|
|
146
|
+
}
|
|
60
147
|
var TopoloAuthError = class extends TopoloSdkError {
|
|
61
148
|
status;
|
|
62
149
|
body;
|
|
@@ -64,27 +151,91 @@ var TopoloAuthError = class extends TopoloSdkError {
|
|
|
64
151
|
path;
|
|
65
152
|
requestId;
|
|
66
153
|
retryAfter;
|
|
154
|
+
details;
|
|
67
155
|
constructor(message, code = "auth_error", options = {}) {
|
|
68
|
-
super(code, message);
|
|
156
|
+
super(canonicalTopoloErrorCode(code) ?? "auth_error", message);
|
|
69
157
|
this.name = "TopoloAuthError";
|
|
70
158
|
this.status = options.status ?? 401;
|
|
71
159
|
this.body = options.body;
|
|
72
160
|
this.service = options.service;
|
|
73
161
|
this.path = options.path;
|
|
74
|
-
this.requestId = options.requestId;
|
|
75
|
-
this.retryAfter = options.retryAfter;
|
|
162
|
+
this.requestId = canonicalTopoloRequestId(options.requestId);
|
|
163
|
+
this.retryAfter = canonicalTopoloRetryAfter(options.retryAfter);
|
|
164
|
+
this.details = extractTopoloServiceErrorDetails(
|
|
165
|
+
options.details === void 0 ? options.body : { details: options.details }
|
|
166
|
+
);
|
|
76
167
|
}
|
|
77
168
|
};
|
|
78
|
-
function
|
|
79
|
-
if (!body || typeof body !== "object" || Array.isArray(body)) return
|
|
169
|
+
function extractTopoloServiceErrorCode(body) {
|
|
170
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
80
171
|
const record = body;
|
|
81
|
-
|
|
172
|
+
const topLevel = canonicalTopoloErrorCode(record.code);
|
|
173
|
+
if (topLevel) return topLevel;
|
|
82
174
|
const nested = record.error;
|
|
175
|
+
const nestedString = canonicalTopoloErrorCode(nested);
|
|
176
|
+
if (nestedString) return nestedString;
|
|
83
177
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
84
178
|
const code = nested.code;
|
|
85
|
-
|
|
179
|
+
const nestedCode = canonicalTopoloErrorCode(code);
|
|
180
|
+
if (nestedCode) return nestedCode;
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
function serviceErrorCode(body, fallback) {
|
|
185
|
+
return extractTopoloServiceErrorCode(body) ?? fallback;
|
|
186
|
+
}
|
|
187
|
+
function extractTopoloServiceErrorDetails(body) {
|
|
188
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return void 0;
|
|
189
|
+
const record = body;
|
|
190
|
+
let candidate = record.details;
|
|
191
|
+
const nested = record.error;
|
|
192
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
193
|
+
const nestedRecord = nested;
|
|
194
|
+
if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
|
|
195
|
+
}
|
|
196
|
+
if (candidate === void 0) candidate = record;
|
|
197
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return void 0;
|
|
198
|
+
const source = candidate;
|
|
199
|
+
const safe = {};
|
|
200
|
+
for (const [key, value] of Object.entries(source)) {
|
|
201
|
+
if (/present$/i.test(key) && typeof value === "boolean") {
|
|
202
|
+
safe[key] = value;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (key === "retryable" && typeof value === "boolean") {
|
|
206
|
+
safe.retryable = value;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (key === "code") {
|
|
210
|
+
const sanitized = canonicalTopoloErrorCode(value);
|
|
211
|
+
if (sanitized) safe.code = sanitized;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (key === "source") {
|
|
215
|
+
const sanitized = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
216
|
+
if (PUBLIC_DETAIL_SOURCES.has(sanitized)) safe.source = sanitized;
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (key === "dependency" || key === "platform") {
|
|
220
|
+
safe[`${key}Present`] = value !== null && value !== void 0;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (key === "error" && !safe.code) {
|
|
224
|
+
const sanitized = canonicalTopoloErrorCode(value);
|
|
225
|
+
if (sanitized) safe.code = sanitized;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (key === "required" && Array.isArray(value)) {
|
|
229
|
+
const required = canonicalTopoloRequired(value);
|
|
230
|
+
if (required.length) safe.required = required;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (["missingFields", "fields", "paths"].includes(key) && Array.isArray(value)) {
|
|
234
|
+
const paths = value.filter((item) => typeof item === "string" && /^[$.a-z0-9_[\]-]{1,160}$/i.test(item));
|
|
235
|
+
if (paths.length) safe[key] = paths;
|
|
236
|
+
}
|
|
86
237
|
}
|
|
87
|
-
return
|
|
238
|
+
return Object.keys(safe).length ? safe : void 0;
|
|
88
239
|
}
|
|
89
240
|
var TopoloHttpError = class extends TopoloSdkError {
|
|
90
241
|
status;
|
|
@@ -93,15 +244,19 @@ var TopoloHttpError = class extends TopoloSdkError {
|
|
|
93
244
|
path;
|
|
94
245
|
requestId;
|
|
95
246
|
retryAfter;
|
|
247
|
+
details;
|
|
96
248
|
constructor(service, path, status, body, message, options = {}) {
|
|
97
|
-
super(
|
|
249
|
+
super(serviceErrorCode(body, "http_error"), message ?? `HTTP ${status} from ${service}${path}`);
|
|
98
250
|
this.name = "TopoloHttpError";
|
|
99
251
|
this.status = status;
|
|
100
252
|
this.body = body;
|
|
101
253
|
this.service = service;
|
|
102
254
|
this.path = path;
|
|
103
|
-
this.requestId = options.requestId;
|
|
104
|
-
this.retryAfter = options.retryAfter;
|
|
255
|
+
this.requestId = canonicalTopoloRequestId(options.requestId);
|
|
256
|
+
this.retryAfter = canonicalTopoloRetryAfter(options.retryAfter);
|
|
257
|
+
this.details = extractTopoloServiceErrorDetails(
|
|
258
|
+
options.details === void 0 ? body : { details: options.details }
|
|
259
|
+
);
|
|
105
260
|
}
|
|
106
261
|
};
|
|
107
262
|
|
|
@@ -214,13 +369,11 @@ var TopoloOAuth = class {
|
|
|
214
369
|
}
|
|
215
370
|
};
|
|
216
371
|
|
|
217
|
-
// src/
|
|
218
|
-
var
|
|
219
|
-
|
|
220
|
-
return contextType === "organization" && organizationId ? `org:${organizationId}` : "personal";
|
|
221
|
-
}
|
|
222
|
-
var ENVIRONMENT_DEFAULT_SERVICE_URLS = {
|
|
372
|
+
// src/environment.ts
|
|
373
|
+
var TOPOLO_RUNTIME_ENVIRONMENTS = ["production", "staging", "development", "local"];
|
|
374
|
+
var TOPOLO_ENVIRONMENT_SERVICE_URLS = {
|
|
223
375
|
production: {
|
|
376
|
+
auth: "https://auth.topolo.app",
|
|
224
377
|
developers: "https://developers.topolo.app"
|
|
225
378
|
},
|
|
226
379
|
staging: {
|
|
@@ -234,6 +387,58 @@ var ENVIRONMENT_DEFAULT_SERVICE_URLS = {
|
|
|
234
387
|
},
|
|
235
388
|
local: {}
|
|
236
389
|
};
|
|
390
|
+
function hostnameHasSuffix(hostname, suffix) {
|
|
391
|
+
return hostname === suffix || hostname.endsWith(`.${suffix}`);
|
|
392
|
+
}
|
|
393
|
+
function workersDevAccountMatches(hostname, account) {
|
|
394
|
+
if (!hostname.endsWith(".workers.dev")) return false;
|
|
395
|
+
const labels = hostname.slice(0, -".workers.dev".length).split(".");
|
|
396
|
+
return labels.length >= 2 && labels.at(-1) === account;
|
|
397
|
+
}
|
|
398
|
+
function originMatchesEnvironment(url, environment) {
|
|
399
|
+
const hostname = url.hostname.toLowerCase();
|
|
400
|
+
if (environment === "local") {
|
|
401
|
+
return hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
402
|
+
}
|
|
403
|
+
if (url.protocol !== "https:") return false;
|
|
404
|
+
if (environment === "production") {
|
|
405
|
+
return hostnameHasSuffix(hostname, "topolo.app") || hostnameHasSuffix(hostname, "topolo.io") || workersDevAccountMatches(hostname, "topolo");
|
|
406
|
+
}
|
|
407
|
+
if (environment === "staging") {
|
|
408
|
+
return hostnameHasSuffix(hostname, "stg.topolo.us") || workersDevAccountMatches(hostname, "topolo-staging");
|
|
409
|
+
}
|
|
410
|
+
return hostnameHasSuffix(hostname, "topolo.dev") || workersDevAccountMatches(hostname, "topolo-development");
|
|
411
|
+
}
|
|
412
|
+
function assertTopoloServiceUrlEnvironment(service, value, environment) {
|
|
413
|
+
let url;
|
|
414
|
+
try {
|
|
415
|
+
url = new URL(value);
|
|
416
|
+
} catch {
|
|
417
|
+
throw new TopoloSdkError("invalid_service_url", `Invalid URL configured for Topolo service "${service}".`);
|
|
418
|
+
}
|
|
419
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password || !originMatchesEnvironment(url, environment)) {
|
|
420
|
+
throw new TopoloSdkError(
|
|
421
|
+
"environment_mismatch",
|
|
422
|
+
`Refusing Topolo service "${service}": ${url.origin} does not belong to ${environment}.`
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
return url.origin;
|
|
426
|
+
}
|
|
427
|
+
function topoloServiceOrigins(serviceUrls, environment) {
|
|
428
|
+
return Object.fromEntries(
|
|
429
|
+
Object.entries(serviceUrls ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(([service, value]) => [
|
|
430
|
+
service,
|
|
431
|
+
assertTopoloServiceUrlEnvironment(service, value, environment)
|
|
432
|
+
])
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// src/node/credential-store.ts
|
|
437
|
+
var TOPOLO_ENVIRONMENTS = TOPOLO_RUNTIME_ENVIRONMENTS;
|
|
438
|
+
function contextKey(contextType, organizationId) {
|
|
439
|
+
return contextType === "organization" && organizationId ? `org:${organizationId}` : "personal";
|
|
440
|
+
}
|
|
441
|
+
var ENVIRONMENT_DEFAULT_SERVICE_URLS = TOPOLO_ENVIRONMENT_SERVICE_URLS;
|
|
237
442
|
function configPath() {
|
|
238
443
|
const override = process.env.TOPOLO_CONFIG_PATH;
|
|
239
444
|
if (override) return override;
|
|
@@ -351,15 +556,17 @@ function credentialSourceFromConfig(config, environment = selectedEnvironment())
|
|
|
351
556
|
const prefix = envVarPrefix(environment);
|
|
352
557
|
if (process.env[`${prefix}_API_KEY`]) return `env:${prefix}_API_KEY`;
|
|
353
558
|
if (process.env[`${prefix}_ACCESS_TOKEN`]) return `env:${prefix}_ACCESS_TOKEN`;
|
|
354
|
-
if (process.env.TOPOLO_API_KEY) return "env:TOPOLO_API_KEY";
|
|
355
|
-
if (process.env.TOPOLO_ACCESS_TOKEN)
|
|
559
|
+
if (environment === "production" && process.env.TOPOLO_API_KEY) return "env:TOPOLO_API_KEY";
|
|
560
|
+
if (environment === "production" && process.env.TOPOLO_ACCESS_TOKEN) {
|
|
561
|
+
return "env:TOPOLO_ACCESS_TOKEN";
|
|
562
|
+
}
|
|
356
563
|
return profileForEnvironment(config, environment).credential ? "config-file" : "none";
|
|
357
564
|
}
|
|
358
565
|
async function resolveCredential(environment = selectedEnvironment()) {
|
|
359
566
|
const prefix = envVarPrefix(environment);
|
|
360
|
-
const apiKey = process.env[`${prefix}_API_KEY`] ?? process.env.TOPOLO_API_KEY;
|
|
567
|
+
const apiKey = process.env[`${prefix}_API_KEY`] ?? (environment === "production" ? process.env.TOPOLO_API_KEY : void 0);
|
|
361
568
|
if (apiKey) return { kind: "api_key", apiKey };
|
|
362
|
-
const accessToken = process.env[`${prefix}_ACCESS_TOKEN`] ?? process.env.TOPOLO_ACCESS_TOKEN;
|
|
569
|
+
const accessToken = process.env[`${prefix}_ACCESS_TOKEN`] ?? (environment === "production" ? process.env.TOPOLO_ACCESS_TOKEN : void 0);
|
|
363
570
|
if (accessToken) return { kind: "access_token", accessToken };
|
|
364
571
|
const config = await readConfig();
|
|
365
572
|
return profileForEnvironment(config, environment).credential ?? null;
|
|
@@ -367,7 +574,7 @@ async function resolveCredential(environment = selectedEnvironment()) {
|
|
|
367
574
|
var REFRESH_SKEW_SECONDS = 60;
|
|
368
575
|
async function refreshCredentialIfNeeded(environment = selectedEnvironment(), options = {}) {
|
|
369
576
|
const prefix = envVarPrefix(environment);
|
|
370
|
-
if (process.env[`${prefix}_API_KEY`] || process.env[`${prefix}_ACCESS_TOKEN`] || process.env.TOPOLO_API_KEY || process.env.TOPOLO_ACCESS_TOKEN) {
|
|
577
|
+
if (process.env[`${prefix}_API_KEY`] || process.env[`${prefix}_ACCESS_TOKEN`] || environment === "production" && process.env.TOPOLO_API_KEY || environment === "production" && process.env.TOPOLO_ACCESS_TOKEN) {
|
|
371
578
|
return resolveCredential(environment);
|
|
372
579
|
}
|
|
373
580
|
const config = await readConfig();
|
|
@@ -377,9 +584,16 @@ async function refreshCredentialIfNeeded(environment = selectedEnvironment(), op
|
|
|
377
584
|
if (!cred.refreshToken || !profile.oauthClientId) return cred;
|
|
378
585
|
const now = Math.floor(Date.now() / 1e3);
|
|
379
586
|
if (!options.force && cred.expiresAt && cred.expiresAt - REFRESH_SKEW_SECONDS > now) return cred;
|
|
587
|
+
const serviceUrls = resolveServiceUrls(profile.serviceUrls, environment);
|
|
588
|
+
if (environment === "local" && !serviceUrls?.auth) {
|
|
589
|
+
throw new TopoloSdkError(
|
|
590
|
+
"environment_service_url_required",
|
|
591
|
+
"Local Topolo credential refresh requires an explicit local Auth service origin."
|
|
592
|
+
);
|
|
593
|
+
}
|
|
380
594
|
try {
|
|
381
595
|
const oauth = new TopoloOAuth({
|
|
382
|
-
serviceUrls
|
|
596
|
+
serviceUrls
|
|
383
597
|
});
|
|
384
598
|
const tokens = await oauth.refreshToken({
|
|
385
599
|
clientId: profile.oauthClientId,
|
|
@@ -420,9 +634,16 @@ async function refreshContextCredentialIfNeeded(environment, key, options = {})
|
|
|
420
634
|
if (!credential.refreshToken || !oauthClientId) return credential;
|
|
421
635
|
const now = Math.floor(Date.now() / 1e3);
|
|
422
636
|
if (!options.force && credential.expiresAt && credential.expiresAt - REFRESH_SKEW_SECONDS > now) return credential;
|
|
637
|
+
const serviceUrls = resolveServiceUrls(profile.serviceUrls, environment);
|
|
638
|
+
if (environment === "local" && !serviceUrls?.auth) {
|
|
639
|
+
throw new TopoloSdkError(
|
|
640
|
+
"environment_service_url_required",
|
|
641
|
+
"Local Topolo context refresh requires an explicit local Auth service origin."
|
|
642
|
+
);
|
|
643
|
+
}
|
|
423
644
|
try {
|
|
424
645
|
const oauth = new TopoloOAuth({
|
|
425
|
-
serviceUrls
|
|
646
|
+
serviceUrls
|
|
426
647
|
});
|
|
427
648
|
const tokens = await oauth.refreshToken({
|
|
428
649
|
clientId: oauthClientId,
|
|
@@ -469,7 +690,12 @@ function resolveServiceUrls(configUrls, environment = selectedEnvironment()) {
|
|
|
469
690
|
merged[k.slice("TOPOLO_SERVICE_URL_".length).toLowerCase()] = v;
|
|
470
691
|
}
|
|
471
692
|
}
|
|
472
|
-
|
|
693
|
+
const origins = topoloServiceOrigins(merged, environment);
|
|
694
|
+
return Object.keys(origins).length > 0 ? origins : void 0;
|
|
695
|
+
}
|
|
696
|
+
function assertServiceUrlForEnvironment(service, serviceUrl, environment = selectedEnvironment()) {
|
|
697
|
+
if (!serviceUrl) return;
|
|
698
|
+
assertTopoloServiceUrlEnvironment(service, serviceUrl, environment);
|
|
473
699
|
}
|
|
474
700
|
function resolvedProfileConfig(config, environment = selectedEnvironment()) {
|
|
475
701
|
const profile = profileForEnvironment(config, environment);
|
|
@@ -485,6 +711,8 @@ function resolvedProfileConfig(config, environment = selectedEnvironment()) {
|
|
|
485
711
|
ENVIRONMENT_DEFAULT_SERVICE_URLS,
|
|
486
712
|
TOPOLO_ENVIRONMENTS,
|
|
487
713
|
activateContext,
|
|
714
|
+
assertServiceUrlForEnvironment,
|
|
715
|
+
assertTopoloServiceUrlEnvironment,
|
|
488
716
|
clearProfileCredential,
|
|
489
717
|
configPath,
|
|
490
718
|
contextKey,
|
|
@@ -500,6 +728,7 @@ function resolvedProfileConfig(config, environment = selectedEnvironment()) {
|
|
|
500
728
|
resolveServiceUrls,
|
|
501
729
|
resolvedProfileConfig,
|
|
502
730
|
selectedEnvironment,
|
|
731
|
+
topoloServiceOrigins,
|
|
503
732
|
writeConfig,
|
|
504
733
|
writeContextCredential,
|
|
505
734
|
writeProfileConfig
|
|
@@ -20,6 +20,11 @@ type TopoloCredential = {
|
|
|
20
20
|
expiresAt?: number;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
declare const TOPOLO_RUNTIME_ENVIRONMENTS: readonly ["production", "staging", "development", "local"];
|
|
24
|
+
type TopoloRuntimeEnvironment = (typeof TOPOLO_RUNTIME_ENVIRONMENTS)[number];
|
|
25
|
+
declare function assertTopoloServiceUrlEnvironment(service: string, value: string, environment: TopoloRuntimeEnvironment): string;
|
|
26
|
+
declare function topoloServiceOrigins(serviceUrls: Record<string, string> | undefined, environment: TopoloRuntimeEnvironment): Record<string, string>;
|
|
27
|
+
|
|
23
28
|
/**
|
|
24
29
|
* Node-only credential store shared by the `topolo` CLI and `@topolo/mcp`.
|
|
25
30
|
*
|
|
@@ -33,7 +38,8 @@ type TopoloCredential = {
|
|
|
33
38
|
* back to the on-disk credential when no `TOPOLO_*` env credential is set.
|
|
34
39
|
*/
|
|
35
40
|
declare const TOPOLO_ENVIRONMENTS: readonly ["production", "staging", "development", "local"];
|
|
36
|
-
type TopoloEnvironment =
|
|
41
|
+
type TopoloEnvironment = TopoloRuntimeEnvironment;
|
|
42
|
+
|
|
37
43
|
interface TopoloConfig {
|
|
38
44
|
credential?: TopoloCredential;
|
|
39
45
|
/** OAuth client_id used for the most recent login — needed to refresh the access token. */
|
|
@@ -75,7 +81,7 @@ declare function contextKey(contextType: 'personal' | 'organization', organizati
|
|
|
75
81
|
* local dev loop points services at ad-hoc ports, so it is driven entirely by
|
|
76
82
|
* `TOPOLO_LOCAL_SERVICE_URL_*` / `TOPOLO_SERVICE_URL_*` overrides.
|
|
77
83
|
*/
|
|
78
|
-
declare const ENVIRONMENT_DEFAULT_SERVICE_URLS: Record<
|
|
84
|
+
declare const ENVIRONMENT_DEFAULT_SERVICE_URLS: Record<"production" | "staging" | "development" | "local", Record<string, string>>;
|
|
79
85
|
declare function configPath(): string;
|
|
80
86
|
declare function readConfig(): Promise<TopoloConfig>;
|
|
81
87
|
declare function writeConfig(config: TopoloConfig): Promise<void>;
|
|
@@ -109,6 +115,7 @@ declare function refreshContextCredentialIfNeeded(environment: TopoloEnvironment
|
|
|
109
115
|
}): Promise<TopoloCredential | null>;
|
|
110
116
|
declare function resolveAgentName(configAgentName?: string): string | undefined;
|
|
111
117
|
declare function resolveServiceUrls(configUrls?: Record<string, string>, environment?: TopoloEnvironment): Record<string, string> | undefined;
|
|
118
|
+
declare function assertServiceUrlForEnvironment(service: string, serviceUrl: string | undefined, environment?: TopoloEnvironment): void;
|
|
112
119
|
declare function resolvedProfileConfig(config: TopoloConfig, environment?: TopoloEnvironment): TopoloEnvironmentProfile;
|
|
113
120
|
|
|
114
|
-
export { ENVIRONMENT_DEFAULT_SERVICE_URLS, TOPOLO_ENVIRONMENTS, type TopoloConfig, type TopoloContextCredential, type TopoloEnvironment, type TopoloEnvironmentProfile, activateContext, clearProfileCredential, configPath, contextKey, credentialSourceFromConfig, envVarPrefix, normalizeEnvironment, profileForEnvironment, readConfig, refreshContextCredentialIfNeeded, refreshCredentialIfNeeded, resolveAgentName, resolveCredential, resolveServiceUrls, resolvedProfileConfig, selectedEnvironment, writeConfig, writeContextCredential, writeProfileConfig };
|
|
121
|
+
export { ENVIRONMENT_DEFAULT_SERVICE_URLS, TOPOLO_ENVIRONMENTS, type TopoloConfig, type TopoloContextCredential, type TopoloEnvironment, type TopoloEnvironmentProfile, activateContext, assertServiceUrlForEnvironment, assertTopoloServiceUrlEnvironment, clearProfileCredential, configPath, contextKey, credentialSourceFromConfig, envVarPrefix, normalizeEnvironment, profileForEnvironment, readConfig, refreshContextCredentialIfNeeded, refreshCredentialIfNeeded, resolveAgentName, resolveCredential, resolveServiceUrls, resolvedProfileConfig, selectedEnvironment, topoloServiceOrigins, writeConfig, writeContextCredential, writeProfileConfig };
|
|
@@ -20,6 +20,11 @@ type TopoloCredential = {
|
|
|
20
20
|
expiresAt?: number;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
declare const TOPOLO_RUNTIME_ENVIRONMENTS: readonly ["production", "staging", "development", "local"];
|
|
24
|
+
type TopoloRuntimeEnvironment = (typeof TOPOLO_RUNTIME_ENVIRONMENTS)[number];
|
|
25
|
+
declare function assertTopoloServiceUrlEnvironment(service: string, value: string, environment: TopoloRuntimeEnvironment): string;
|
|
26
|
+
declare function topoloServiceOrigins(serviceUrls: Record<string, string> | undefined, environment: TopoloRuntimeEnvironment): Record<string, string>;
|
|
27
|
+
|
|
23
28
|
/**
|
|
24
29
|
* Node-only credential store shared by the `topolo` CLI and `@topolo/mcp`.
|
|
25
30
|
*
|
|
@@ -33,7 +38,8 @@ type TopoloCredential = {
|
|
|
33
38
|
* back to the on-disk credential when no `TOPOLO_*` env credential is set.
|
|
34
39
|
*/
|
|
35
40
|
declare const TOPOLO_ENVIRONMENTS: readonly ["production", "staging", "development", "local"];
|
|
36
|
-
type TopoloEnvironment =
|
|
41
|
+
type TopoloEnvironment = TopoloRuntimeEnvironment;
|
|
42
|
+
|
|
37
43
|
interface TopoloConfig {
|
|
38
44
|
credential?: TopoloCredential;
|
|
39
45
|
/** OAuth client_id used for the most recent login — needed to refresh the access token. */
|
|
@@ -75,7 +81,7 @@ declare function contextKey(contextType: 'personal' | 'organization', organizati
|
|
|
75
81
|
* local dev loop points services at ad-hoc ports, so it is driven entirely by
|
|
76
82
|
* `TOPOLO_LOCAL_SERVICE_URL_*` / `TOPOLO_SERVICE_URL_*` overrides.
|
|
77
83
|
*/
|
|
78
|
-
declare const ENVIRONMENT_DEFAULT_SERVICE_URLS: Record<
|
|
84
|
+
declare const ENVIRONMENT_DEFAULT_SERVICE_URLS: Record<"production" | "staging" | "development" | "local", Record<string, string>>;
|
|
79
85
|
declare function configPath(): string;
|
|
80
86
|
declare function readConfig(): Promise<TopoloConfig>;
|
|
81
87
|
declare function writeConfig(config: TopoloConfig): Promise<void>;
|
|
@@ -109,6 +115,7 @@ declare function refreshContextCredentialIfNeeded(environment: TopoloEnvironment
|
|
|
109
115
|
}): Promise<TopoloCredential | null>;
|
|
110
116
|
declare function resolveAgentName(configAgentName?: string): string | undefined;
|
|
111
117
|
declare function resolveServiceUrls(configUrls?: Record<string, string>, environment?: TopoloEnvironment): Record<string, string> | undefined;
|
|
118
|
+
declare function assertServiceUrlForEnvironment(service: string, serviceUrl: string | undefined, environment?: TopoloEnvironment): void;
|
|
112
119
|
declare function resolvedProfileConfig(config: TopoloConfig, environment?: TopoloEnvironment): TopoloEnvironmentProfile;
|
|
113
120
|
|
|
114
|
-
export { ENVIRONMENT_DEFAULT_SERVICE_URLS, TOPOLO_ENVIRONMENTS, type TopoloConfig, type TopoloContextCredential, type TopoloEnvironment, type TopoloEnvironmentProfile, activateContext, clearProfileCredential, configPath, contextKey, credentialSourceFromConfig, envVarPrefix, normalizeEnvironment, profileForEnvironment, readConfig, refreshContextCredentialIfNeeded, refreshCredentialIfNeeded, resolveAgentName, resolveCredential, resolveServiceUrls, resolvedProfileConfig, selectedEnvironment, writeConfig, writeContextCredential, writeProfileConfig };
|
|
121
|
+
export { ENVIRONMENT_DEFAULT_SERVICE_URLS, TOPOLO_ENVIRONMENTS, type TopoloConfig, type TopoloContextCredential, type TopoloEnvironment, type TopoloEnvironmentProfile, activateContext, assertServiceUrlForEnvironment, assertTopoloServiceUrlEnvironment, clearProfileCredential, configPath, contextKey, credentialSourceFromConfig, envVarPrefix, normalizeEnvironment, profileForEnvironment, readConfig, refreshContextCredentialIfNeeded, refreshCredentialIfNeeded, resolveAgentName, resolveCredential, resolveServiceUrls, resolvedProfileConfig, selectedEnvironment, topoloServiceOrigins, writeConfig, writeContextCredential, writeProfileConfig };
|