@volter/world-runtime 3.0.34 → 3.0.36
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/local-branches.js +21 -3
- package/dist/src/served-world.js +4 -1
- package/package.json +4 -4
- package/src/local-branches.ts +19 -3
- package/src/served-world.ts +3 -1
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
// parent through its own doors (its history cut at an instant, when one is asked), its clock frozen at
|
|
4
4
|
// that instant so no twin's catch-up walks it forward, and removed when its time runs out. world-host
|
|
5
5
|
// and `volter world view` both make them with this; a hosted World's supervisor makes its own.
|
|
6
|
-
import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
7
7
|
import { dirname, join } from 'node:path';
|
|
8
|
-
import { stateDirName, withStateRemoval } from '@volter/world-core/runtime';
|
|
8
|
+
import { readBranchMeta, stateDirName, withHistoryLock, withStateRemoval, worldPaths, worldStateRoot, writeBranchMeta } from '@volter/world-core/runtime';
|
|
9
9
|
import { inheritWorldBrowsers, materializeBrowserFiles } from "./browsers.js";
|
|
10
10
|
import { clockFile, statusWorld } from "./runtime.js";
|
|
11
11
|
import { loadWorldConfig } from "./configs.js";
|
|
@@ -169,7 +169,25 @@ export class LocalBranches {
|
|
|
169
169
|
writeFileSync(file, `${clock}\n`);
|
|
170
170
|
}
|
|
171
171
|
const child = statusWorld(id, root);
|
|
172
|
-
|
|
172
|
+
// The HTTP clone holds the history view here, but its URL parent holds no local resources.
|
|
173
|
+
// Retain the actual parent's state through the kernel's pointer/pin protocol: blob reads can
|
|
174
|
+
// follow it without copying uploads, while history keeps using the downloaded immutable view.
|
|
175
|
+
const base = statusWorld(world, from.root);
|
|
176
|
+
for (const service of Object.keys(child.services)) {
|
|
177
|
+
const control = join(child.dirs.data, service);
|
|
178
|
+
for (const state of readdirSync(worldStateRoot(control), { withFileTypes: true })) {
|
|
179
|
+
if (!state.isDirectory())
|
|
180
|
+
continue;
|
|
181
|
+
const directory = worldPaths(state.name, control).dir;
|
|
182
|
+
withHistoryLock(directory, () => {
|
|
183
|
+
const meta = readBranchMeta(state.name, control);
|
|
184
|
+
if (!meta?.parent)
|
|
185
|
+
throw new Error(`cloning ${parent}: ${service}/${state.name} has no parent`);
|
|
186
|
+
writeBranchMeta(state.name, { ...meta, parent: { ...meta.parent, at: join(base.dirs.data, service), viewDirectory: directory } }, control);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
inheritWorldBrowsers(base.dirs.data, child.dirs.data);
|
|
173
191
|
materializeBrowserFiles(child.dirs.data, root, id, child.config);
|
|
174
192
|
const createdAt = new Date().toISOString();
|
|
175
193
|
const expiresAt = ttlSeconds === null ? null : new Date(Date.now() + ttlSeconds * 1000).toISOString();
|
package/dist/src/served-world.js
CHANGED
|
@@ -659,6 +659,9 @@ export class WorldDoors {
|
|
|
659
659
|
}
|
|
660
660
|
const answer = await this.site(request, site, browser, held);
|
|
661
661
|
const out = new Headers(answer.headers);
|
|
662
|
+
// A document belongs to this live page session and browser state, not an earlier opening of its URL.
|
|
663
|
+
if (out.get('content-type')?.split(';', 1)[0]?.trim().toLowerCase() === 'text/html')
|
|
664
|
+
out.set('cache-control', 'private, no-store');
|
|
662
665
|
out.append('content-security-policy', `frame-ancestors 'self'${held.framer ? ` ${held.framer}` : ''}`);
|
|
663
666
|
this.keepCookiesHere(answer, out);
|
|
664
667
|
return new Response(answer.body, { status: answer.status, statusText: answer.statusText, headers: out });
|
|
@@ -1153,7 +1156,7 @@ export class WorldDoors {
|
|
|
1153
1156
|
}
|
|
1154
1157
|
putBrowserLoad(this.up().dirs.data, loadId, { name, site, version: browser.version, cookies: cookies.filter(c => !protectedCookie(c.name)).map(c => ({ name: c.name, path: c.path })) });
|
|
1155
1158
|
headers.append('set-cookie', `${marker}=${loadId}; Path=/; HttpOnly; Secure; SameSite=None; Partitioned`);
|
|
1156
|
-
const script = `localStorage.clear(); for (const entry of ${JSON.stringify(storage).replace(/</g, '\\u003c')}) localStorage.setItem(entry.name, entry.value); location.
|
|
1159
|
+
const script = `localStorage.clear(); for (const entry of ${JSON.stringify(storage).replace(/</g, '\\u003c')}) localStorage.setItem(entry.name, entry.value); location.reload();`;
|
|
1157
1160
|
const hash = createHash('sha256').update(script).digest('base64');
|
|
1158
1161
|
const framer = this.pageSession(request, url, site, name)?.framer;
|
|
1159
1162
|
headers.set('content-security-policy', `default-src 'none'; script-src 'sha256-${hash}'; base-uri 'none'; frame-ancestors 'self'${framer ? ` ${framer}` : ''}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/world-runtime",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.36",
|
|
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",
|
|
@@ -68,14 +68,14 @@
|
|
|
68
68
|
"node": ">=22.3"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
|
-
"@volter/world-core": "3.0.
|
|
72
|
-
"@volter/world-console": "3.0.
|
|
71
|
+
"@volter/world-core": "3.0.36",
|
|
72
|
+
"@volter/world-console": "3.0.31"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
75
|
"@electric-sql/pglite": "0.5.8",
|
|
76
76
|
"@electric-sql/pglite-pgvector": "0.0.9",
|
|
77
77
|
"@volter/world-access": "3.0.1",
|
|
78
|
-
"@volter/world-core": "3.0.
|
|
78
|
+
"@volter/world-core": "3.0.36",
|
|
79
79
|
"pg-gateway": "0.3.0-beta.4",
|
|
80
80
|
"smol-toml": "^1.8.0",
|
|
81
81
|
"yaml": "2.9.0"
|
package/src/local-branches.ts
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
// parent through its own doors (its history cut at an instant, when one is asked), its clock frozen at
|
|
4
4
|
// that instant so no twin's catch-up walks it forward, and removed when its time runs out. world-host
|
|
5
5
|
// and `volter world view` both make them with this; a hosted World's supervisor makes its own.
|
|
6
|
-
import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
7
7
|
import { dirname, join } from 'node:path';
|
|
8
|
-
import { stateDirName, withStateRemoval } from '@volter/world-core/runtime';
|
|
8
|
+
import { readBranchMeta, stateDirName, withHistoryLock, withStateRemoval, worldPaths, worldStateRoot, writeBranchMeta } from '@volter/world-core/runtime';
|
|
9
9
|
import { inheritWorldBrowsers, materializeBrowserFiles } from './browsers.ts';
|
|
10
10
|
import { clockFile, statusWorld } from './runtime.ts';
|
|
11
11
|
import { loadWorldConfig } from './configs.ts';
|
|
@@ -162,7 +162,23 @@ export class LocalBranches {
|
|
|
162
162
|
const clock = at.instant ?? this.frozenClock(from) ?? (at.live ? null : new Date().toISOString());
|
|
163
163
|
if (clock) { const file = clockFile(root, id); mkdirSync(dirname(file), { recursive: true }); writeFileSync(file, `${clock}\n`); }
|
|
164
164
|
const child = statusWorld(id, root);
|
|
165
|
-
|
|
165
|
+
// The HTTP clone holds the history view here, but its URL parent holds no local resources.
|
|
166
|
+
// Retain the actual parent's state through the kernel's pointer/pin protocol: blob reads can
|
|
167
|
+
// follow it without copying uploads, while history keeps using the downloaded immutable view.
|
|
168
|
+
const base = statusWorld(world, from.root);
|
|
169
|
+
for (const service of Object.keys(child.services)) {
|
|
170
|
+
const control = join(child.dirs.data, service);
|
|
171
|
+
for (const state of readdirSync(worldStateRoot(control), { withFileTypes: true })) {
|
|
172
|
+
if (!state.isDirectory()) continue;
|
|
173
|
+
const directory = worldPaths(state.name, control).dir;
|
|
174
|
+
withHistoryLock(directory, () => {
|
|
175
|
+
const meta = readBranchMeta(state.name, control);
|
|
176
|
+
if (!meta?.parent) throw new Error(`cloning ${parent}: ${service}/${state.name} has no parent`);
|
|
177
|
+
writeBranchMeta(state.name, { ...meta, parent: { ...meta.parent, at: join(base.dirs.data, service), viewDirectory: directory } }, control);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
inheritWorldBrowsers(base.dirs.data, child.dirs.data);
|
|
166
182
|
materializeBrowserFiles(child.dirs.data, root, id, child.config);
|
|
167
183
|
const createdAt = new Date().toISOString();
|
|
168
184
|
const expiresAt = ttlSeconds === null ? null : new Date(Date.now() + ttlSeconds * 1000).toISOString();
|
package/src/served-world.ts
CHANGED
|
@@ -645,6 +645,8 @@ export class WorldDoors {
|
|
|
645
645
|
}
|
|
646
646
|
const answer = await this.site(request, site, browser, held);
|
|
647
647
|
const out = new Headers(answer.headers);
|
|
648
|
+
// A document belongs to this live page session and browser state, not an earlier opening of its URL.
|
|
649
|
+
if (out.get('content-type')?.split(';', 1)[0]?.trim().toLowerCase() === 'text/html') out.set('cache-control', 'private, no-store');
|
|
648
650
|
out.append('content-security-policy', `frame-ancestors 'self'${held.framer ? ` ${held.framer}` : ''}`);
|
|
649
651
|
this.keepCookiesHere(answer, out);
|
|
650
652
|
return new Response(answer.body, { status: answer.status, statusText: answer.statusText, headers: out });
|
|
@@ -1043,7 +1045,7 @@ export class WorldDoors {
|
|
|
1043
1045
|
}
|
|
1044
1046
|
putBrowserLoad(this.up().dirs.data, loadId, { name, site, version: browser.version, cookies: cookies.filter(c => !protectedCookie(c.name)).map(c => ({ name: c.name, path: c.path })) });
|
|
1045
1047
|
headers.append('set-cookie', `${marker}=${loadId}; Path=/; HttpOnly; Secure; SameSite=None; Partitioned`);
|
|
1046
|
-
const script = `localStorage.clear(); for (const entry of ${JSON.stringify(storage).replace(/</g, '\\u003c')}) localStorage.setItem(entry.name, entry.value); location.
|
|
1048
|
+
const script = `localStorage.clear(); for (const entry of ${JSON.stringify(storage).replace(/</g, '\\u003c')}) localStorage.setItem(entry.name, entry.value); location.reload();`;
|
|
1047
1049
|
const hash = createHash('sha256').update(script).digest('base64');
|
|
1048
1050
|
const framer = this.pageSession(request, url, site, name)?.framer;
|
|
1049
1051
|
headers.set('content-security-policy', `default-src 'none'; script-src 'sha256-${hash}'; base-uri 'none'; frame-ancestors 'self'${framer ? ` ${framer}` : ''}`);
|