@volter/world-runtime 3.0.58 → 3.0.60
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/src/covers.js +34 -23
- package/package.json +4 -4
- package/src/covers.ts +32 -20
package/dist/src/covers.js
CHANGED
|
@@ -25,7 +25,7 @@ import { errorMessage } from "./error-message.js";
|
|
|
25
25
|
import { existsSync, readFileSync } from 'node:fs';
|
|
26
26
|
import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
27
27
|
import { fileURLToPath } from 'node:url';
|
|
28
|
-
import { vendorOfPackModule } from '@volter/world-core/runtime';
|
|
28
|
+
import { installedPackageDir, packVendor, vendorOfPackModule } from '@volter/world-core/runtime';
|
|
29
29
|
import { loadWorldConfig } from "./configs.js";
|
|
30
30
|
import { VENDOR_SERVER_BACKINGS } from "./backings.js";
|
|
31
31
|
import { injectableVendorKeys, loadInject, twinUrlVendorFor } from "./inject-map.js";
|
|
@@ -260,7 +260,7 @@ function serverBacking(service) {
|
|
|
260
260
|
return undefined;
|
|
261
261
|
return backing;
|
|
262
262
|
}
|
|
263
|
-
function addBackingIds(ids, services, effectiveEnv) {
|
|
263
|
+
function addBackingIds(ids, services, effectiveEnv, root = process.cwd()) {
|
|
264
264
|
for (const service of services) {
|
|
265
265
|
const backing = serverBacking(service);
|
|
266
266
|
if (backing) {
|
|
@@ -276,7 +276,7 @@ function addBackingIds(ids, services, effectiveEnv) {
|
|
|
276
276
|
}
|
|
277
277
|
// A protocol already proved owned by this service can satisfy its declared native driver.
|
|
278
278
|
// HTTP-only siblings of the same vendor retain their independent interface obligations.
|
|
279
|
-
for (const binding of serviceConnections([service], effectiveEnv).values()) {
|
|
279
|
+
for (const binding of serviceConnections([service], effectiveEnv, root).values()) {
|
|
280
280
|
for (const [vendor, facts] of Object.entries(packFacts())) {
|
|
281
281
|
if (facts.transport === 'raw-tcp' && facts.managedService?.scheme === binding.protocol) {
|
|
282
282
|
addId(ids, normalizeId(vendor), `${binding.label}; managed ${facts.managedService.kind} backing`, true);
|
|
@@ -285,18 +285,17 @@ function addBackingIds(ids, services, effectiveEnv) {
|
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
|
-
function declaredTwin(services, effectiveEnv) {
|
|
288
|
+
function declaredTwin(services, effectiveEnv, root = process.cwd()) {
|
|
289
289
|
const declared = new Map();
|
|
290
290
|
for (const service of services) {
|
|
291
291
|
if (service.type !== undefined && service.type !== 'twin' && !serverBacking(service))
|
|
292
292
|
continue;
|
|
293
|
-
const
|
|
294
|
-
const identity = config.package?.match(/^@volter\/twin-(.+)$/)?.[1] ?? servicePackNames(config)[0] ?? service.id;
|
|
293
|
+
const identity = servicePackNames(service, root)[0] ?? service.id;
|
|
295
294
|
const vendor = Object.keys(packFacts()).find((candidate) => worldIdsFor(candidate).includes(normalizeId(identity))) ?? identity;
|
|
296
295
|
const ids = new Map();
|
|
297
296
|
if (!serverBacking(service))
|
|
298
|
-
addServiceIds(ids, service);
|
|
299
|
-
addBackingIds(ids, [service], effectiveEnv);
|
|
297
|
+
addServiceIds(ids, service, root);
|
|
298
|
+
addBackingIds(ids, [service], effectiveEnv, root);
|
|
300
299
|
const match = [...worldIdsFor(vendor), vendor].map(normalizeId).map((id) => ids.get(id)).find(Boolean);
|
|
301
300
|
declared.set(vendor, match ?? { label: `${service.id} (declared twin)`, interceptable: false });
|
|
302
301
|
}
|
|
@@ -311,14 +310,18 @@ function addId(ids, key, label, interceptable) {
|
|
|
311
310
|
else if (interceptable && !existing.interceptable)
|
|
312
311
|
existing.interceptable = true;
|
|
313
312
|
}
|
|
314
|
-
function servicePackNames(service) {
|
|
313
|
+
function servicePackNames(service, root = process.cwd()) {
|
|
314
|
+
const installed = service.package && installedPackageDir(root, service.package);
|
|
315
|
+
const vendor = installed ? packVendor(installed) : undefined;
|
|
316
|
+
if (vendor)
|
|
317
|
+
return [vendor];
|
|
315
318
|
const commandParts = [
|
|
316
319
|
...(service.package ? [service.package] : []),
|
|
317
320
|
...(Array.isArray(service.command) ? service.command : service.command ? [service.command] : []),
|
|
318
321
|
...(service.args ?? []),
|
|
319
322
|
...(service.colocate?.module ? [service.colocate.module] : []),
|
|
320
323
|
];
|
|
321
|
-
|
|
324
|
+
const parts = commandParts.flatMap((argument, index) => {
|
|
322
325
|
// A shared host command is recorded on each service. Only that service's
|
|
323
326
|
// module identifies its pack; its neighbours' modules must not cover it.
|
|
324
327
|
let part = argument;
|
|
@@ -328,23 +331,31 @@ function servicePackNames(service) {
|
|
|
328
331
|
return [];
|
|
329
332
|
part = module;
|
|
330
333
|
}
|
|
334
|
+
return [part];
|
|
335
|
+
});
|
|
336
|
+
const vendors = parts.flatMap((part) => {
|
|
337
|
+
const identity = /\.(?:[cm]?js|tsx?)$/.test(part) ? vendorOfPackModule(resolve(root, part)) : undefined;
|
|
338
|
+
return identity ? [identity] : [];
|
|
339
|
+
});
|
|
340
|
+
if (vendors.length)
|
|
341
|
+
return [...new Set(vendors)];
|
|
342
|
+
return parts.flatMap((part) => {
|
|
331
343
|
const fromPath = part.match(/(?:^|[/\\])twin-packs-p[23][/\\]([a-z0-9-]+)[/\\]/);
|
|
332
344
|
const fromBin = part.match(/^world-([a-z0-9-]+)$/);
|
|
333
345
|
const fromPkg = part.match(/(?:^|[/\\])@volter\/twin-([a-z0-9-]+)(?:[/\\]|$)/);
|
|
334
|
-
|
|
335
|
-
return [fromManifest, fromPath?.[1], fromBin?.[1], fromPkg?.[1]].filter((name) => name !== undefined);
|
|
346
|
+
return [fromPath?.[1], fromBin?.[1], fromPkg?.[1]].filter((name) => name !== undefined);
|
|
336
347
|
});
|
|
337
348
|
}
|
|
338
349
|
/** Endpoint wiring owned by a service, never a random URL from global config/env. Config
|
|
339
350
|
* templates must use the service's allocated host/port. Instance values must point at that
|
|
340
351
|
* listener, or have been discovered by the external lifecycle. HTTP pack facts veto native
|
|
341
352
|
* coverage even when someone has put a mysql:// template on an HTTP twin. */
|
|
342
|
-
function serviceConnections(services, effectiveEnv) {
|
|
353
|
+
function serviceConnections(services, effectiveEnv, root = process.cwd()) {
|
|
343
354
|
const bindings = new Map();
|
|
344
355
|
for (const service of services) {
|
|
345
356
|
const instance = 'pid' in service;
|
|
346
357
|
const config = instance ? undefined : service;
|
|
347
|
-
const facts = servicePackNames(service).map((name) => packFacts()[name]).filter((fact) => fact !== undefined);
|
|
358
|
+
const facts = servicePackNames(service, root).map((name) => packFacts()[name]).filter((fact) => fact !== undefined);
|
|
348
359
|
if (!instance && config?.injectEnv)
|
|
349
360
|
bindings.set(config.injectEnv, { protocol: 'http', label: `${service.id} (${config.injectEnv})` });
|
|
350
361
|
const values = instance ? service.env : config?.injectEnvTemplates ?? {};
|
|
@@ -392,9 +403,9 @@ function serviceConnections(services, effectiveEnv) {
|
|
|
392
403
|
}
|
|
393
404
|
return bindings;
|
|
394
405
|
}
|
|
395
|
-
function addServiceIds(ids, service) {
|
|
406
|
+
function addServiceIds(ids, service, root = process.cwd()) {
|
|
396
407
|
const type = service.type ?? 'twin';
|
|
397
|
-
const packNames = servicePackNames(service);
|
|
408
|
+
const packNames = servicePackNames(service, root);
|
|
398
409
|
// How would traffic actually reach this twin?
|
|
399
410
|
// • the injector/ambient proxy intercepts its vendor hosts — true when its injectEnv is a
|
|
400
411
|
// `*_TWIN_URL` var the injector reads, or its service id / pack name IS an injector
|
|
@@ -450,15 +461,15 @@ export function worldTwinInventory(world, root) {
|
|
|
450
461
|
const ids = new Map();
|
|
451
462
|
for (const service of Object.values(status.services))
|
|
452
463
|
if (!serverBacking(service))
|
|
453
|
-
addServiceIds(ids, service);
|
|
454
|
-
addBackingIds(ids, Object.values(status.services), status.env);
|
|
464
|
+
addServiceIds(ids, service, root);
|
|
465
|
+
addBackingIds(ids, Object.values(status.services), status.env, root);
|
|
455
466
|
for (const key of Object.keys(status.env)) {
|
|
456
467
|
const stem = key.match(/^(.+)_TWIN_URL$/);
|
|
457
468
|
// a bare env `*_TWIN_URL` var is only a REACHABLE twin when the injector reads it
|
|
458
469
|
if (stem)
|
|
459
470
|
addId(ids, normalizeId(stem[1]), `env ${key}`, twinUrlVendorFor(key) !== null);
|
|
460
471
|
}
|
|
461
|
-
return { services: Object.values(status.services), source: 'instance', path: `${status.dirs.instance}/instance.json`, ids, connections: serviceConnections(Object.values(status.services), status.env), selection: status.selection, declared: declaredTwin(Object.values(status.services), status.env) };
|
|
472
|
+
return { services: Object.values(status.services), source: 'instance', path: `${status.dirs.instance}/instance.json`, ids, connections: serviceConnections(Object.values(status.services), status.env, root), selection: status.selection, declared: declaredTwin(Object.values(status.services), status.env, root) };
|
|
462
473
|
}
|
|
463
474
|
catch (error) {
|
|
464
475
|
instanceError = errorMessage(error);
|
|
@@ -468,16 +479,16 @@ export function worldTwinInventory(world, root) {
|
|
|
468
479
|
const ids = new Map();
|
|
469
480
|
for (const service of config.services)
|
|
470
481
|
if (!serverBacking(service))
|
|
471
|
-
addServiceIds(ids, service);
|
|
472
|
-
addBackingIds(ids, config.services);
|
|
473
|
-
const connections = serviceConnections(config.services);
|
|
482
|
+
addServiceIds(ids, service, root);
|
|
483
|
+
addBackingIds(ids, config.services, undefined, root);
|
|
484
|
+
const connections = serviceConnections(config.services, undefined, root);
|
|
474
485
|
// a name the World's env leaves to a twin's credential door (`$issue:<service>`) is that twin's, whatever scheme the
|
|
475
486
|
// issued value has: the twin that issues it serves it
|
|
476
487
|
for (const [name, value] of Object.entries(config.env ?? {})) {
|
|
477
488
|
if (value.startsWith(ISSUE_ENV_PREFIX))
|
|
478
489
|
connections.set(name, { protocol: 'issued', label: `${value.slice(ISSUE_ENV_PREFIX.length)} (${name}; issued)` });
|
|
479
490
|
}
|
|
480
|
-
return { services: config.services, source: 'config', path, ids, connections, selection: config.selection, declared: declaredTwin(config.services) };
|
|
491
|
+
return { services: config.services, source: 'config', path, ids, connections, selection: config.selection, declared: declaredTwin(config.services, undefined, root) };
|
|
481
492
|
}
|
|
482
493
|
catch (error) {
|
|
483
494
|
const configError = errorMessage(error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/world-runtime",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.60",
|
|
4
4
|
"description": "World configs for twins: boot named local runtimes, allocate ports, generate world.env/instance.json, and run apps against fake-key twin worlds.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"twin",
|
|
@@ -69,14 +69,14 @@
|
|
|
69
69
|
"node": ">=22.3"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
72
|
-
"@volter/world-core": "3.0.
|
|
73
|
-
"@volter/world-console": "3.0.
|
|
72
|
+
"@volter/world-core": "3.0.60",
|
|
73
|
+
"@volter/world-console": "3.0.55"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"@electric-sql/pglite": "0.5.8",
|
|
77
77
|
"@electric-sql/pglite-pgvector": "0.0.9",
|
|
78
78
|
"@volter/world-access": "3.0.1",
|
|
79
|
-
"@volter/world-core": "3.0.
|
|
79
|
+
"@volter/world-core": "3.0.60",
|
|
80
80
|
"mongodb": "~6.20.0",
|
|
81
81
|
"pg-gateway": "0.3.0-beta.4",
|
|
82
82
|
"smol-toml": "^1.8.0",
|
package/src/covers.ts
CHANGED
|
@@ -26,7 +26,7 @@ import { errorMessage } from './error-message.ts';
|
|
|
26
26
|
import { existsSync, readFileSync } from 'node:fs';
|
|
27
27
|
import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
28
28
|
import { fileURLToPath } from 'node:url';
|
|
29
|
-
import { vendorOfPackModule } from '@volter/world-core/runtime';
|
|
29
|
+
import { installedPackageDir, packVendor, vendorOfPackModule } from '@volter/world-core/runtime';
|
|
30
30
|
import { loadWorldConfig } from './configs.ts';
|
|
31
31
|
import { VENDOR_SERVER_BACKINGS } from './backings.ts';
|
|
32
32
|
import { injectableVendorKeys, loadInject, twinUrlVendorFor } from './inject-map.ts';
|
|
@@ -367,6 +367,7 @@ function addBackingIds(
|
|
|
367
367
|
ids: Map<string, WorldTwinId>,
|
|
368
368
|
services: Array<WorldServiceConfig | WorldServiceInstance>,
|
|
369
369
|
effectiveEnv?: Record<string, string>,
|
|
370
|
+
root = process.cwd(),
|
|
370
371
|
): void {
|
|
371
372
|
for (const service of services) {
|
|
372
373
|
const backing = serverBacking(service);
|
|
@@ -383,7 +384,7 @@ function addBackingIds(
|
|
|
383
384
|
}
|
|
384
385
|
// A protocol already proved owned by this service can satisfy its declared native driver.
|
|
385
386
|
// HTTP-only siblings of the same vendor retain their independent interface obligations.
|
|
386
|
-
for (const binding of serviceConnections([service], effectiveEnv).values()) {
|
|
387
|
+
for (const binding of serviceConnections([service], effectiveEnv, root).values()) {
|
|
387
388
|
for (const [vendor, facts] of Object.entries(packFacts())) {
|
|
388
389
|
if (facts.transport === 'raw-tcp' && facts.managedService?.scheme === binding.protocol) {
|
|
389
390
|
addId(ids, normalizeId(vendor), `${binding.label}; managed ${facts.managedService.kind} backing`, true);
|
|
@@ -393,16 +394,15 @@ function addBackingIds(
|
|
|
393
394
|
}
|
|
394
395
|
}
|
|
395
396
|
|
|
396
|
-
function declaredTwin(services: Array<WorldServiceConfig | WorldServiceInstance>, effectiveEnv?: Record<string, string
|
|
397
|
+
function declaredTwin(services: Array<WorldServiceConfig | WorldServiceInstance>, effectiveEnv?: Record<string, string>, root = process.cwd()): Map<string, WorldTwinId> {
|
|
397
398
|
const declared = new Map<string, WorldTwinId>();
|
|
398
399
|
for (const service of services) {
|
|
399
400
|
if (service.type !== undefined && service.type !== 'twin' && !serverBacking(service)) continue;
|
|
400
|
-
const
|
|
401
|
-
const identity = config.package?.match(/^@volter\/twin-(.+)$/)?.[1] ?? servicePackNames(config)[0] ?? service.id;
|
|
401
|
+
const identity = servicePackNames(service, root)[0] ?? service.id;
|
|
402
402
|
const vendor = Object.keys(packFacts()).find((candidate) => worldIdsFor(candidate).includes(normalizeId(identity))) ?? identity;
|
|
403
403
|
const ids = new Map<string, WorldTwinId>();
|
|
404
|
-
if (!serverBacking(service)) addServiceIds(ids, service);
|
|
405
|
-
addBackingIds(ids, [service], effectiveEnv);
|
|
404
|
+
if (!serverBacking(service)) addServiceIds(ids, service, root);
|
|
405
|
+
addBackingIds(ids, [service], effectiveEnv, root);
|
|
406
406
|
const match = [...worldIdsFor(vendor), vendor].map(normalizeId).map((id) => ids.get(id)).find(Boolean);
|
|
407
407
|
declared.set(vendor, match ?? { label: `${service.id} (declared twin)`, interceptable: false });
|
|
408
408
|
}
|
|
@@ -417,14 +417,17 @@ function addId(ids: Map<string, WorldTwinId>, key: string, label: string, interc
|
|
|
417
417
|
else if (interceptable && !existing.interceptable) existing.interceptable = true;
|
|
418
418
|
}
|
|
419
419
|
|
|
420
|
-
function servicePackNames(service: { id?: string; package?: string; command?: string | string[]; args?: string[]; colocate?: { module?: string } }): string[] {
|
|
420
|
+
function servicePackNames(service: { id?: string; package?: string; command?: string | string[]; args?: string[]; colocate?: { module?: string } }, root = process.cwd()): string[] {
|
|
421
|
+
const installed = service.package && installedPackageDir(root, service.package);
|
|
422
|
+
const vendor = installed ? packVendor(installed) : undefined;
|
|
423
|
+
if (vendor) return [vendor];
|
|
421
424
|
const commandParts = [
|
|
422
425
|
...(service.package ? [service.package] : []),
|
|
423
426
|
...(Array.isArray(service.command) ? service.command : service.command ? [service.command] : []),
|
|
424
427
|
...(service.args ?? []),
|
|
425
428
|
...(service.colocate?.module ? [service.colocate.module] : []),
|
|
426
429
|
];
|
|
427
|
-
|
|
430
|
+
const parts = commandParts.flatMap((argument, index) => {
|
|
428
431
|
// A shared host command is recorded on each service. Only that service's
|
|
429
432
|
// module identifies its pack; its neighbours' modules must not cover it.
|
|
430
433
|
let part = argument;
|
|
@@ -433,11 +436,18 @@ function servicePackNames(service: { id?: string; package?: string; command?: st
|
|
|
433
436
|
if (id !== service.id || !module) return [];
|
|
434
437
|
part = module;
|
|
435
438
|
}
|
|
439
|
+
return [part];
|
|
440
|
+
});
|
|
441
|
+
const vendors = parts.flatMap((part) => {
|
|
442
|
+
const identity = /\.(?:[cm]?js|tsx?)$/.test(part) ? vendorOfPackModule(resolve(root, part)) : undefined;
|
|
443
|
+
return identity ? [identity] : [];
|
|
444
|
+
});
|
|
445
|
+
if (vendors.length) return [...new Set(vendors)];
|
|
446
|
+
return parts.flatMap((part) => {
|
|
436
447
|
const fromPath = part.match(/(?:^|[/\\])twin-packs-p[23][/\\]([a-z0-9-]+)[/\\]/);
|
|
437
448
|
const fromBin = part.match(/^world-([a-z0-9-]+)$/);
|
|
438
449
|
const fromPkg = part.match(/(?:^|[/\\])@volter\/twin-([a-z0-9-]+)(?:[/\\]|$)/);
|
|
439
|
-
|
|
440
|
-
return [fromManifest, fromPath?.[1], fromBin?.[1], fromPkg?.[1]].filter((name): name is string => name !== undefined);
|
|
450
|
+
return [fromPath?.[1], fromBin?.[1], fromPkg?.[1]].filter((name): name is string => name !== undefined);
|
|
441
451
|
});
|
|
442
452
|
}
|
|
443
453
|
|
|
@@ -448,12 +458,13 @@ function servicePackNames(service: { id?: string; package?: string; command?: st
|
|
|
448
458
|
function serviceConnections(
|
|
449
459
|
services: Array<WorldServiceConfig | WorldServiceInstance>,
|
|
450
460
|
effectiveEnv?: Record<string, string>,
|
|
461
|
+
root = process.cwd(),
|
|
451
462
|
): WorldTwinInventory['connections'] {
|
|
452
463
|
const bindings: WorldTwinInventory['connections'] = new Map();
|
|
453
464
|
for (const service of services) {
|
|
454
465
|
const instance = 'pid' in service;
|
|
455
466
|
const config = instance ? undefined : service;
|
|
456
|
-
const facts = servicePackNames(service).map((name) => packFacts()[name]).filter((fact) => fact !== undefined);
|
|
467
|
+
const facts = servicePackNames(service, root).map((name) => packFacts()[name]).filter((fact) => fact !== undefined);
|
|
457
468
|
if (!instance && config?.injectEnv) bindings.set(config.injectEnv, { protocol: 'http', label: `${service.id} (${config.injectEnv})` });
|
|
458
469
|
const values = instance ? service.env : config?.injectEnvTemplates ?? {};
|
|
459
470
|
for (const [name, value] of Object.entries(values)) {
|
|
@@ -512,9 +523,10 @@ function addServiceIds(
|
|
|
512
523
|
package?: string;
|
|
513
524
|
colocate?: { module?: string };
|
|
514
525
|
},
|
|
526
|
+
root = process.cwd(),
|
|
515
527
|
): void {
|
|
516
528
|
const type = service.type ?? 'twin';
|
|
517
|
-
const packNames = servicePackNames(service);
|
|
529
|
+
const packNames = servicePackNames(service, root);
|
|
518
530
|
// How would traffic actually reach this twin?
|
|
519
531
|
// • the injector/ambient proxy intercepts its vendor hosts — true when its injectEnv is a
|
|
520
532
|
// `*_TWIN_URL` var the injector reads, or its service id / pack name IS an injector
|
|
@@ -571,29 +583,29 @@ export function worldTwinInventory(world: string, root: string): WorldTwinInvent
|
|
|
571
583
|
try {
|
|
572
584
|
const status = statusWorld(world, root, { readOnly: true });
|
|
573
585
|
const ids = new Map<string, WorldTwinId>();
|
|
574
|
-
for (const service of Object.values(status.services)) if (!serverBacking(service)) addServiceIds(ids, service);
|
|
575
|
-
addBackingIds(ids, Object.values(status.services), status.env);
|
|
586
|
+
for (const service of Object.values(status.services)) if (!serverBacking(service)) addServiceIds(ids, service, root);
|
|
587
|
+
addBackingIds(ids, Object.values(status.services), status.env, root);
|
|
576
588
|
for (const key of Object.keys(status.env)) {
|
|
577
589
|
const stem = key.match(/^(.+)_TWIN_URL$/);
|
|
578
590
|
// a bare env `*_TWIN_URL` var is only a REACHABLE twin when the injector reads it
|
|
579
591
|
if (stem) addId(ids, normalizeId(stem[1]!), `env ${key}`, twinUrlVendorFor(key) !== null);
|
|
580
592
|
}
|
|
581
|
-
return { services: Object.values(status.services), source: 'instance', path: `${status.dirs.instance}/instance.json`, ids, connections: serviceConnections(Object.values(status.services), status.env), selection: status.selection, declared: declaredTwin(Object.values(status.services), status.env) };
|
|
593
|
+
return { services: Object.values(status.services), source: 'instance', path: `${status.dirs.instance}/instance.json`, ids, connections: serviceConnections(Object.values(status.services), status.env, root), selection: status.selection, declared: declaredTwin(Object.values(status.services), status.env, root) };
|
|
582
594
|
} catch (error) {
|
|
583
595
|
instanceError = errorMessage(error);
|
|
584
596
|
}
|
|
585
597
|
try {
|
|
586
598
|
const { path, config } = loadWorldConfig(world, root);
|
|
587
599
|
const ids = new Map<string, WorldTwinId>();
|
|
588
|
-
for (const service of config.services as WorldServiceConfig[]) if (!serverBacking(service)) addServiceIds(ids, service);
|
|
589
|
-
addBackingIds(ids, config.services);
|
|
590
|
-
const connections = serviceConnections(config.services);
|
|
600
|
+
for (const service of config.services as WorldServiceConfig[]) if (!serverBacking(service)) addServiceIds(ids, service, root);
|
|
601
|
+
addBackingIds(ids, config.services, undefined, root);
|
|
602
|
+
const connections = serviceConnections(config.services, undefined, root);
|
|
591
603
|
// a name the World's env leaves to a twin's credential door (`$issue:<service>`) is that twin's, whatever scheme the
|
|
592
604
|
// issued value has: the twin that issues it serves it
|
|
593
605
|
for (const [name, value] of Object.entries(config.env ?? {})) {
|
|
594
606
|
if (value.startsWith(ISSUE_ENV_PREFIX)) connections.set(name, { protocol: 'issued', label: `${value.slice(ISSUE_ENV_PREFIX.length)} (${name}; issued)` });
|
|
595
607
|
}
|
|
596
|
-
return { services: config.services, source: 'config', path, ids, connections, selection: config.selection, declared: declaredTwin(config.services) };
|
|
608
|
+
return { services: config.services, source: 'config', path, ids, connections, selection: config.selection, declared: declaredTwin(config.services, undefined, root) };
|
|
597
609
|
} catch (error) {
|
|
598
610
|
const configError = errorMessage(error);
|
|
599
611
|
throw new Error(
|