@volter/world-host 3.0.12 → 3.0.14
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/twins-host.js +24 -2
- package/package.json +5 -5
- package/src/twins-host.ts +17 -2
package/dist/src/twins-host.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { chmodSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
18
18
|
import { assertStateRemovable, serveHttp, stateDirName, withAncestryLock, withStateRemoval } from '@volter/world-core/runtime';
|
|
19
19
|
import { dirname, join, resolve } from 'node:path';
|
|
20
|
-
import { assertWorldStateRemovable, resolveCatalog, CONSOLE_BASE, consoleRedirect, initWorld, LocalBranches, loadWorldConfig, localWorldOrigin, mountWorld, pathOfWorld, worldOriginLabel, serveConsoleApart, siteOfHost, worldDoorUnderConsole, TOKEN_HEADER, worldOfHost } from '@volter/world-runtime';
|
|
20
|
+
import { assertWorldStateRemovable, resolveCatalog, CONSOLE_BASE, consoleRedirect, followParentPins, initWorld, LocalBranches, loadWorldConfig, localWorldOrigin, mountWorld, pagesOfHost, pagesOriginOf, pathOfWorld, worldOriginLabel, serveConsoleApart, siteOfHost, worldDoorUnderConsole, TOKEN_HEADER, worldOfHost } from '@volter/world-runtime';
|
|
21
21
|
import { issuerKeys, trustableIssuer } from '@volter/world-access';
|
|
22
22
|
export { CONSOLE_BASE };
|
|
23
23
|
const NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/i;
|
|
@@ -115,6 +115,10 @@ export async function createWorldHost(options) {
|
|
|
115
115
|
: (served) => (port ? localWorldOrigin(served, port) : null);
|
|
116
116
|
// sites are routed only at this machine's loopback origins (the front's siteOfHost)
|
|
117
117
|
const sitesOrigin = origins && !originBase ? (served) => (port ? localWorldOrigin(served, port) : null) : undefined;
|
|
118
|
+
// each World's content plane beside its origin (`<world>--<org>--pages`): its twins' pages, apart from its doors
|
|
119
|
+
const pagesOrigin = !origins ? undefined : originBase
|
|
120
|
+
? (served) => pagesOriginOf(served, advertised?.protocol ?? 'https:', originBase, advertised?.port || undefined)
|
|
121
|
+
: (served) => (port ? pagesOriginOf(served, 'http:', 'localhost', port) : null);
|
|
118
122
|
// one key reader per trusted platform, shared by every World here (each caches its platform's key set)
|
|
119
123
|
// https only, or http on this machine: keys read over plain http elsewhere could be swapped, and a swapped key forges passes
|
|
120
124
|
const passIssuers = (options.trust ?? []).map((origin) => {
|
|
@@ -128,8 +132,22 @@ export async function createWorldHost(options) {
|
|
|
128
132
|
const branches = new LocalBranches({ dir, origin: () => loopback, worlds: byServed, mount: (root) => mount({ name: '', root }), ...(options.announce ? { announce: options.announce } : {}) });
|
|
129
133
|
const mount = async (w) => {
|
|
130
134
|
const config = loadWorldConfig(join(w.root, '.volter', 'world.json'), w.root).config;
|
|
131
|
-
return mountWorld(config.id, { root: w.root, branches: (served) => branches.doorsFor(served), ...(browserOrigin ? { browserOrigin } : {}), ...(sitesOrigin ? { sitesOrigin } : {}), ...(passIssuers.length ? { passIssuers } : {}) });
|
|
135
|
+
return mountWorld(config.id, { root: w.root, branches: (served) => branches.doorsFor(served), ...(browserOrigin ? { browserOrigin } : {}), ...(sitesOrigin ? { sitesOrigin } : {}), ...(pagesOrigin ? { pagesOrigin } : {}), ...(passIssuers.length ? { passIssuers } : {}) });
|
|
132
136
|
};
|
|
137
|
+
// a branch beside its parent takes the parent's twin versions before it mounts (followParentPins), parents first: a
|
|
138
|
+
// branch of a branch follows its parent only once that parent has followed its own
|
|
139
|
+
const fromOf = new Map(found.map((w) => { try {
|
|
140
|
+
return [w.name, JSON.parse(readFileSync(join(w.root, stateDirName(), 'branch.json'), 'utf8')).from];
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return [w.name, undefined];
|
|
144
|
+
} }));
|
|
145
|
+
const depth = (name, seen = new Set()) => { const up = fromOf.get(name); return up && fromOf.has(up) && !seen.has(up) ? 1 + depth(up, seen.add(name)) : 0; };
|
|
146
|
+
for (const w of [...found].sort((a, b) => depth(a.name) - depth(b.name))) {
|
|
147
|
+
const parent = found.find((p) => p.name === fromOf.get(w.name));
|
|
148
|
+
if (parent)
|
|
149
|
+
followParentPins(parent.root, w.root);
|
|
150
|
+
}
|
|
133
151
|
for (const w of found)
|
|
134
152
|
byServed.set(w.name, await mount(w));
|
|
135
153
|
const host = options.host ?? '127.0.0.1';
|
|
@@ -281,6 +299,10 @@ export async function createWorldHost(options) {
|
|
|
281
299
|
const site = origins && !originBase ? siteOfHost(at.hostname, byServed.keys()) : undefined;
|
|
282
300
|
if (site)
|
|
283
301
|
return byServed.get(site.world).site(request, site.site);
|
|
302
|
+
// a World's content plane serves that World's twins' pages and nothing else of this host's (no console, no admin)
|
|
303
|
+
const pagesFor = origins ? pagesOfHost(at.hostname, byServed.keys(), originBase) : undefined;
|
|
304
|
+
if (pagesFor)
|
|
305
|
+
return path === '/-/ping' ? json({ ok: true, worlds: [pagesFor] }) : byServed.get(pagesFor).handle(request);
|
|
284
306
|
const own = origins ? worldOfHost(at.hostname, byServed.keys(), originBase) : undefined;
|
|
285
307
|
if (own === null)
|
|
286
308
|
return json({ error: `no world at ${at.host}` }, 404);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/world-host",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.14",
|
|
4
4
|
"description": "The hosted remote: worlds under one URL, each holding the canonical history of a real vendor account and its sealed credential — what a local world clones from and pushes to.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"twin",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@volter/world-access": "3.0.1",
|
|
30
|
-
"@volter/world-core": "3.0.
|
|
31
|
-
"@volter/world-runtime": "3.0.
|
|
30
|
+
"@volter/world-core": "3.0.14",
|
|
31
|
+
"@volter/world-runtime": "3.0.14"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@volter/world-console": "3.0.
|
|
34
|
+
"@volter/world-console": "3.0.9"
|
|
35
35
|
},
|
|
36
36
|
"peerDependenciesMeta": {
|
|
37
37
|
"@volter/world-console": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@volter/world-console": "3.0.
|
|
42
|
+
"@volter/world-console": "3.0.9"
|
|
43
43
|
},
|
|
44
44
|
"exports": {
|
|
45
45
|
".": {
|
package/src/twins-host.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { chmodSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
18
18
|
import { assertStateRemovable, serveHttp, stateDirName, withAncestryLock, withStateRemoval } from '@volter/world-core/runtime';
|
|
19
19
|
import { dirname, join, resolve } from 'node:path';
|
|
20
|
-
import { assertWorldStateRemovable, resolveCatalog, CONSOLE_BASE, consoleRedirect, initWorld, LocalBranches, loadWorldConfig, localWorldOrigin, mountWorld, pathOfWorld, worldOriginLabel, serveConsoleApart, siteOfHost, worldDoorUnderConsole, TOKEN_HEADER, worldOfHost, type ConsoleMount, type MountedWorld } from '@volter/world-runtime';
|
|
20
|
+
import { assertWorldStateRemovable, resolveCatalog, CONSOLE_BASE, consoleRedirect, followParentPins, initWorld, LocalBranches, loadWorldConfig, localWorldOrigin, mountWorld, pagesOfHost, pagesOriginOf, pathOfWorld, worldOriginLabel, serveConsoleApart, siteOfHost, worldDoorUnderConsole, TOKEN_HEADER, worldOfHost, type ConsoleMount, type MountedWorld } from '@volter/world-runtime';
|
|
21
21
|
|
|
22
22
|
import { issuerKeys, trustableIssuer, type TrustedIssuer } from '@volter/world-access';
|
|
23
23
|
|
|
@@ -124,6 +124,10 @@ export async function createWorldHost(options: WorldHostOptions): Promise<WorldH
|
|
|
124
124
|
: (served: string) => (port ? localWorldOrigin(served, port) : null);
|
|
125
125
|
// sites are routed only at this machine's loopback origins (the front's siteOfHost)
|
|
126
126
|
const sitesOrigin = origins && !originBase ? (served: string) => (port ? localWorldOrigin(served, port) : null) : undefined;
|
|
127
|
+
// each World's content plane beside its origin (`<world>--<org>--pages`): its twins' pages, apart from its doors
|
|
128
|
+
const pagesOrigin = !origins ? undefined : originBase
|
|
129
|
+
? (served: string) => pagesOriginOf(served, advertised?.protocol ?? 'https:', originBase, advertised?.port || undefined)
|
|
130
|
+
: (served: string) => (port ? pagesOriginOf(served, 'http:', 'localhost', port) : null);
|
|
127
131
|
// one key reader per trusted platform, shared by every World here (each caches its platform's key set)
|
|
128
132
|
// https only, or http on this machine: keys read over plain http elsewhere could be swapped, and a swapped key forges passes
|
|
129
133
|
const passIssuers: TrustedIssuer[] = (options.trust ?? []).map((origin) => {
|
|
@@ -136,8 +140,16 @@ export async function createWorldHost(options: WorldHostOptions): Promise<WorldH
|
|
|
136
140
|
const branches: LocalBranches = new LocalBranches({ dir, origin: () => loopback, worlds: byServed, mount: (root) => mount({ name: '', root }), ...(options.announce ? { announce: options.announce } : {}) });
|
|
137
141
|
const mount = async (w: { name: string; root: string }): Promise<MountedWorld> => {
|
|
138
142
|
const config = loadWorldConfig(join(w.root, '.volter', 'world.json'), w.root).config;
|
|
139
|
-
return mountWorld(config.id, { root: w.root, branches: (served) => branches.doorsFor(served), ...(browserOrigin ? { browserOrigin } : {}), ...(sitesOrigin ? { sitesOrigin } : {}), ...(passIssuers.length ? { passIssuers } : {}) });
|
|
143
|
+
return mountWorld(config.id, { root: w.root, branches: (served) => branches.doorsFor(served), ...(browserOrigin ? { browserOrigin } : {}), ...(sitesOrigin ? { sitesOrigin } : {}), ...(pagesOrigin ? { pagesOrigin } : {}), ...(passIssuers.length ? { passIssuers } : {}) });
|
|
140
144
|
};
|
|
145
|
+
// a branch beside its parent takes the parent's twin versions before it mounts (followParentPins), parents first: a
|
|
146
|
+
// branch of a branch follows its parent only once that parent has followed its own
|
|
147
|
+
const fromOf = new Map(found.map((w) => { try { return [w.name, (JSON.parse(readFileSync(join(w.root, stateDirName(), 'branch.json'), 'utf8')) as { from?: string }).from] as const; } catch { return [w.name, undefined] as const; } }));
|
|
148
|
+
const depth = (name: string, seen = new Set<string>()): number => { const up = fromOf.get(name); return up && fromOf.has(up) && !seen.has(up) ? 1 + depth(up, seen.add(name)) : 0; };
|
|
149
|
+
for (const w of [...found].sort((a, b) => depth(a.name) - depth(b.name))) {
|
|
150
|
+
const parent = found.find((p) => p.name === fromOf.get(w.name));
|
|
151
|
+
if (parent) followParentPins(parent.root, w.root);
|
|
152
|
+
}
|
|
141
153
|
for (const w of found) byServed.set(w.name, await mount(w));
|
|
142
154
|
const host = options.host ?? '127.0.0.1';
|
|
143
155
|
let url = '';
|
|
@@ -245,6 +257,9 @@ export async function createWorldHost(options: WorldHostOptions): Promise<WorldH
|
|
|
245
257
|
// this machine's loopback origins, where the World's own page needs no token either
|
|
246
258
|
const site = origins && !originBase ? siteOfHost(at.hostname, byServed.keys()) : undefined;
|
|
247
259
|
if (site) return byServed.get(site.world)!.site(request, site.site);
|
|
260
|
+
// a World's content plane serves that World's twins' pages and nothing else of this host's (no console, no admin)
|
|
261
|
+
const pagesFor = origins ? pagesOfHost(at.hostname, byServed.keys(), originBase) : undefined;
|
|
262
|
+
if (pagesFor) return path === '/-/ping' ? json({ ok: true, worlds: [pagesFor] }) : byServed.get(pagesFor)!.handle(request);
|
|
248
263
|
const own = origins ? worldOfHost(at.hostname, byServed.keys(), originBase) : undefined;
|
|
249
264
|
if (own === null) return json({ error: `no world at ${at.host}` }, 404);
|
|
250
265
|
if (own && !pathOfWorld(path, own, CONSOLE_BASE)) return json({ error: `this origin serves ${own} only` }, 404);
|