@volter/world-runtime 3.0.59 → 3.0.61

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.
@@ -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 config = service;
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
- return commandParts.flatMap((argument, index) => {
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
- const fromManifest = /\.(?:[cm]?js|tsx?)$/.test(part) ? vendorOfPackModule(part) : undefined;
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.59",
3
+ "version": "3.0.61",
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.59",
73
- "@volter/world-console": "3.0.54"
72
+ "@volter/world-core": "3.0.61",
73
+ "@volter/world-console": "3.0.56"
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.59",
79
+ "@volter/world-core": "3.0.61",
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>): Map<string, WorldTwinId> {
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 config = service as WorldServiceConfig;
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
- return commandParts.flatMap((argument, index) => {
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
- const fromManifest = /\.(?:[cm]?js|tsx?)$/.test(part) ? vendorOfPackModule(part) : undefined;
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(