@volter/world-runtime 3.0.38 → 3.0.39
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/known-external-services.json +3 -10
- package/dist/src/actions-runner-cli.js +3 -3
- package/dist/src/actions-runner.js +2 -1
- package/dist/src/app-url.js +2 -1
- package/dist/src/backings.d.ts +10 -1
- package/dist/src/backings.js +66 -7
- package/dist/src/branch.js +85 -52
- package/dist/src/browser-routes.cjs +59 -0
- package/dist/src/ca-trust.js +4 -3
- package/dist/src/changeset.js +4 -3
- package/dist/src/cli-commands.js +25 -9
- package/dist/src/cli.js +2 -1
- package/dist/src/command-lifetime.d.mts +12 -0
- package/dist/src/command-lifetime.d.ts +1 -15
- package/dist/src/command-lifetime.js +1 -98
- package/dist/src/command-lifetime.mjs +90 -0
- package/dist/src/configs.js +1 -1
- package/dist/src/console-apart.js +3 -2
- package/dist/src/covers.d.ts +3 -2
- package/dist/src/covers.js +95 -18
- package/dist/src/error-message.d.mts +1 -0
- package/dist/src/error-message.d.ts +1 -0
- package/dist/src/error-message.js +2 -0
- package/dist/src/error-message.mjs +10 -0
- package/dist/src/guest-command.d.mts +4 -0
- package/dist/src/guest-command.mjs +26 -0
- package/dist/src/guest-control.d.mts +1 -0
- package/dist/src/guest-control.mjs +17 -0
- package/dist/src/guest-entry.mjs +146 -0
- package/dist/src/guest-processes.d.mts +9 -0
- package/dist/src/guest-processes.mjs +47 -0
- package/dist/src/guest-runner.d.ts +6 -0
- package/dist/src/guest-runner.js +567 -0
- package/dist/src/host.js +5 -4
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/infra-cli.js +4 -0
- package/dist/src/infra.js +133 -23
- package/dist/src/init.js +60 -23
- package/dist/src/lifecycle-record.d.ts +3 -1
- package/dist/src/lifecycle-record.js +29 -6
- package/dist/src/livekit-backing.mjs +37 -0
- package/dist/src/local-branches.js +11 -3
- package/dist/src/mongodb-backing.d.ts +7 -0
- package/dist/src/mongodb-backing.js +58 -0
- package/dist/src/native-postgres-backing.d.ts +4 -0
- package/dist/src/native-postgres-backing.js +404 -0
- package/dist/src/origin.js +2 -1
- package/dist/src/owned-path.d.ts +4 -0
- package/dist/src/owned-path.js +37 -0
- package/dist/src/pack-facts.d.ts +1 -0
- package/dist/src/pglite-backing.d.ts +4 -13
- package/dist/src/pglite-backing.js +198 -106
- package/dist/src/pglite-host.mjs +47 -17
- package/dist/src/placeholder.d.ts +3 -2
- package/dist/src/placeholder.js +9 -4
- package/dist/src/process-groups.d.mts +2 -0
- package/dist/src/process-groups.d.ts +1 -4
- package/dist/src/process-groups.js +1 -49
- package/dist/src/process-groups.mjs +59 -0
- package/dist/src/process-receipt.d.mts +6 -0
- package/dist/src/process-receipt.d.ts +11 -0
- package/dist/src/process-receipt.js +3 -0
- package/dist/src/process-receipt.mjs +45 -0
- package/dist/src/process-signal.d.mts +9 -0
- package/dist/src/process-signal.mjs +92 -0
- package/dist/src/project-inspect.d.ts +1 -1
- package/dist/src/project-inspect.js +4 -2
- package/dist/src/proxy-daemon.js +2 -1
- package/dist/src/redirect-proxy.js +186 -19
- package/dist/src/redis-backing.js +99 -35
- package/dist/src/reflect.js +2 -1
- package/dist/src/root.js +7 -6
- package/dist/src/run-task-worker.js +2 -1
- package/dist/src/runtime.d.ts +24 -7
- package/dist/src/runtime.js +703 -286
- package/dist/src/schema.d.ts +12 -1
- package/dist/src/schema.js +20 -5
- package/dist/src/served-world.d.ts +1 -0
- package/dist/src/served-world.js +26 -14
- package/dist/src/service-recorder.js +8 -3
- package/dist/src/signals.d.mts +1 -0
- package/dist/src/signals.d.ts +1 -1
- package/dist/src/signals.js +1 -11
- package/dist/src/signals.mjs +10 -0
- package/dist/src/tail.d.ts +2 -0
- package/dist/src/tail.js +9 -2
- package/dist/src/tcp-port.js +27 -13
- package/dist/src/temporal-backing.mjs +28 -0
- package/dist/src/up-cli.js +8 -3
- package/dist/src/up-task-worker.js +2 -1
- package/dist/src/websocket-relay.d.ts +1 -1
- package/dist/src/websocket-relay.js +23 -10
- package/dist/src/world-view.js +2 -2
- package/known-external-services.json +3 -10
- package/package.json +12 -7
- package/src/actions-runner-cli.ts +3 -3
- package/src/actions-runner.ts +2 -1
- package/src/app-url.ts +2 -1
- package/src/backings.ts +65 -7
- package/src/branch.ts +73 -34
- package/src/browser-routes.cjs +59 -0
- package/src/ca-trust.ts +4 -3
- package/src/changeset.ts +4 -3
- package/src/cli-commands.ts +21 -9
- package/src/cli.ts +2 -1
- package/src/command-lifetime.d.mts +12 -0
- package/src/command-lifetime.mjs +90 -0
- package/src/command-lifetime.ts +1 -79
- package/src/configs.ts +1 -1
- package/src/console-apart.ts +2 -1
- package/src/covers.ts +97 -19
- package/src/error-message.d.mts +1 -0
- package/src/error-message.mjs +10 -0
- package/src/error-message.ts +2 -0
- package/src/guest-command.d.mts +4 -0
- package/src/guest-command.mjs +26 -0
- package/src/guest-control.d.mts +1 -0
- package/src/guest-control.mjs +17 -0
- package/src/guest-entry.mjs +146 -0
- package/src/guest-processes.d.mts +9 -0
- package/src/guest-processes.mjs +47 -0
- package/src/guest-runner.ts +414 -0
- package/src/host.ts +5 -4
- package/src/index.ts +1 -0
- package/src/infra-cli.ts +4 -0
- package/src/infra.ts +103 -22
- package/src/init.ts +55 -22
- package/src/lifecycle-record.ts +21 -6
- package/src/livekit-backing.mjs +37 -0
- package/src/local-branches.ts +6 -3
- package/src/mongodb-backing.ts +55 -0
- package/src/native-postgres-backing.ts +329 -0
- package/src/origin.ts +2 -1
- package/src/owned-path.ts +32 -0
- package/src/pack-facts.ts +1 -0
- package/src/pglite-backing.ts +155 -79
- package/src/pglite-host.mjs +47 -17
- package/src/placeholder.ts +10 -5
- package/src/process-groups.d.mts +2 -0
- package/src/process-groups.mjs +59 -0
- package/src/process-groups.ts +1 -33
- package/src/process-receipt.d.mts +6 -0
- package/src/process-receipt.mjs +45 -0
- package/src/process-receipt.ts +5 -0
- package/src/process-signal.d.mts +9 -0
- package/src/process-signal.mjs +92 -0
- package/src/project-inspect.ts +4 -2
- package/src/proxy-daemon.ts +2 -1
- package/src/redirect-proxy.ts +153 -18
- package/src/redis-backing.ts +74 -25
- package/src/reflect.ts +2 -1
- package/src/root.ts +7 -6
- package/src/run-task-worker.ts +2 -1
- package/src/runtime.ts +596 -265
- package/src/schema.ts +30 -6
- package/src/served-world.ts +21 -15
- package/src/service-recorder.ts +4 -2
- package/src/signals.d.mts +1 -0
- package/src/signals.mjs +10 -0
- package/src/signals.ts +1 -10
- package/src/tail.ts +10 -3
- package/src/tcp-port.ts +24 -13
- package/src/temporal-backing.mjs +28 -0
- package/src/up-cli.ts +6 -3
- package/src/up-task-worker.ts +2 -1
- package/src/websocket-relay.ts +18 -11
- package/src/world-view.ts +2 -2
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
},
|
|
20
20
|
"@daily-co/daily-js": {
|
|
21
21
|
"disposition": "demanded",
|
|
22
|
-
"reason": "vendor daily (daily.co), its call client (the call machine from c.daily.co, signalling on gs.daily.co, WebRTC media): the
|
|
22
|
+
"reason": "vendor daily (daily.co), its call client (the call machine from c.daily.co, signalling on gs.daily.co, WebRTC media): the Daily REST spec does not describe call-client media or signalling, so this SDK remains an uncovered wire"
|
|
23
23
|
},
|
|
24
24
|
"@daily-co/daily-react": {
|
|
25
25
|
"disposition": "demanded",
|
|
26
|
-
"reason": "vendor daily (daily.co), React hooks over its call client (@daily-co/daily-js): the
|
|
26
|
+
"reason": "vendor daily (daily.co), React hooks over its call client (@daily-co/daily-js): the Daily REST spec does not describe call-client media or signalling, so this SDK remains an uncovered wire"
|
|
27
27
|
},
|
|
28
28
|
"@azure/communication-email": {
|
|
29
29
|
"disposition": "demanded",
|
|
@@ -193,10 +193,6 @@
|
|
|
193
193
|
"disposition": "demanded",
|
|
194
194
|
"reason": "vendor matrix, pack demanded by census 2026-08-31 (×1)"
|
|
195
195
|
},
|
|
196
|
-
"node-telegram-bot-api": {
|
|
197
|
-
"disposition": "demanded",
|
|
198
|
-
"reason": "vendor telegram, pack demanded by census 2026-08-31 (×2); already curated in covers.ts UNKNOWN_DEP_KNOWN_SERVICES"
|
|
199
|
-
},
|
|
200
196
|
"npm-registry@npm.jsr.io": {
|
|
201
197
|
"disposition": "acknowledged",
|
|
202
198
|
"reason": "acknowledged: registry destination — the npm-registry twin deliberately owns npmjs + GitHub only (census 2026-08-31, ×2)"
|
|
@@ -217,10 +213,6 @@
|
|
|
217
213
|
"disposition": "demanded",
|
|
218
214
|
"reason": "vendor shopify, well-known service client with no twin pack (curated in covers.ts UNKNOWN_DEP_KNOWN_SERVICES)"
|
|
219
215
|
},
|
|
220
|
-
"telegraf": {
|
|
221
|
-
"disposition": "demanded",
|
|
222
|
-
"reason": "vendor telegram, pack demanded by census 2026-08-31 (×1); already curated in covers.ts UNKNOWN_DEP_KNOWN_SERVICES"
|
|
223
|
-
},
|
|
224
216
|
"twenty-client-sdk": {
|
|
225
217
|
"disposition": "demanded",
|
|
226
218
|
"reason": "vendor twenty, pack demanded by census 2026-08-31 (×1)"
|
|
@@ -925,6 +917,7 @@
|
|
|
925
917
|
}
|
|
926
918
|
},
|
|
927
919
|
"notExternal": {
|
|
920
|
+
"@chat-adapter/state-memory": "Chat SDK local in-memory state adapter, no external connection: @chat-adapter/state-memory 4.39.0 dist/index.js stores subscriptions in Set and locks, cache and queues in Map; https://chat-sdk.dev/adapters/official/memory",
|
|
928
921
|
"@agentclientprotocol/sdk": "Agent Client Protocol library, not a vendor",
|
|
929
922
|
"@ai-sdk/devtools": "local dev-tools UI for the AI SDK",
|
|
930
923
|
"@ai-sdk/langchain": "LangChain interop adapter, not a service",
|
|
@@ -7,9 +7,9 @@ import { hasFlag, optionValue } from '@volter/world-core/args';
|
|
|
7
7
|
import { mkdirSync } from 'node:fs';
|
|
8
8
|
import { runQueued } from "./actions-runner.js";
|
|
9
9
|
const args = process.argv.slice(2);
|
|
10
|
-
const github = optionValue(args, '--github')
|
|
11
|
-
const token = optionValue(args, '--token')
|
|
12
|
-
const work = optionValue(args, '--work'
|
|
10
|
+
const github = optionValue(args, '--github') || process.env.GITHUB_API_URL;
|
|
11
|
+
const token = optionValue(args, '--token') || process.env.GITHUB_TOKEN;
|
|
12
|
+
const work = optionValue(args, '--work', '.volter/actions-runner');
|
|
13
13
|
if (!github || !token) {
|
|
14
14
|
process.stderr.write('usage: volter-world-actions-runner --github <url> --token <token> [--work <dir>] [--once]\n');
|
|
15
15
|
process.exit(2);
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
//
|
|
8
8
|
// What it does not do (named, so a workflow that needs them fails plainly): matrices beyond their first combination,
|
|
9
9
|
// services and containers, composite and JavaScript actions from the marketplace, reusable workflows, concurrency groups.
|
|
10
|
+
import { errorMessage } from "./error-message.js";
|
|
10
11
|
import { spawn } from 'node:child_process';
|
|
11
12
|
import { appendFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
12
13
|
import { join } from 'node:path';
|
|
@@ -256,7 +257,7 @@ export async function runOnce(opts, repo, run) {
|
|
|
256
257
|
}
|
|
257
258
|
}
|
|
258
259
|
catch (error) {
|
|
259
|
-
log(`run ${repo}#${id}: ${error
|
|
260
|
+
log(`run ${repo}#${id}: ${errorMessage(error)}`);
|
|
260
261
|
conclusion = 'failure';
|
|
261
262
|
}
|
|
262
263
|
finally {
|
package/dist/src/app-url.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
// once, at the caller's request, and stores what it saw. The record lives in the instance dir, so
|
|
20
20
|
// a re-`up` (which wipes the dir) or `down --purge` clears it: a recorded URL never outlives the
|
|
21
21
|
// instance it described.
|
|
22
|
+
import { errorMessage } from "./error-message.js";
|
|
22
23
|
import { existsSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
23
24
|
import { withFileLock } from '@volter/world-core/runtime';
|
|
24
25
|
import { join } from 'node:path';
|
|
@@ -208,7 +209,7 @@ export function addAppHosts(name, hosts, options = {}) {
|
|
|
208
209
|
function listeningTcpPorts(pid) {
|
|
209
210
|
const result = spawnSync('lsof', ['-a', '-p', String(pid), '-iTCP', '-sTCP:LISTEN', '-P', '-n', '-Fn'], { windowsHide: true, encoding: 'utf8' });
|
|
210
211
|
if (result.error) {
|
|
211
|
-
throw new Error(`volter-world app-url: \`lsof\` is unavailable (${result.error
|
|
212
|
+
throw new Error(`volter-world app-url: \`lsof\` is unavailable (${errorMessage(result.error)}) — pass the port, or --set the URL directly`);
|
|
212
213
|
}
|
|
213
214
|
const ports = new Set();
|
|
214
215
|
for (const line of (result.stdout ?? '').split('\n')) {
|
package/dist/src/backings.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export type ServerBackingFacts = {
|
|
|
3
3
|
serverBacking: {
|
|
4
4
|
command: string;
|
|
5
5
|
};
|
|
6
|
+
maxRequestBodySize?: number;
|
|
6
7
|
adoption: {
|
|
7
8
|
sdks: string[];
|
|
8
9
|
pypi: string[];
|
|
@@ -16,8 +17,11 @@ export type ServerBackingFacts = {
|
|
|
16
17
|
};
|
|
17
18
|
};
|
|
18
19
|
/**
|
|
20
|
+
* MongoDB is declared infrastructure, not an API service in this table: its native server uses
|
|
21
|
+
* mongodb-backing.ts through the same managed lifecycle as Postgres (pglite-backing.ts).
|
|
22
|
+
*
|
|
19
23
|
* A vendor whose open-source server a World runs as its service instead of a twin (architecture, "A vendor's own
|
|
20
|
-
* server as a backing"): the media plane no pack can hold as data. The service is the vendor's own binary on an 'auto'
|
|
24
|
+
* server as a backing"): the wire or media plane no pack can hold as data. The service is the vendor's own binary on an 'auto'
|
|
21
25
|
* port, loopback only; the application's key and secret are the ones the server is started with, so its SDK signs
|
|
22
26
|
* what the server verifies. What the server does not do on its own (LiveKit's egress, a separate service) the
|
|
23
27
|
* service's note names.
|
|
@@ -27,3 +31,8 @@ export declare const VENDOR_SERVER_BACKINGS: Record<string, {
|
|
|
27
31
|
env: Record<string, string>;
|
|
28
32
|
facts: ServerBackingFacts;
|
|
29
33
|
}>;
|
|
34
|
+
/** Declare a backing's application-owned env stems as service bindings; omitted stems use the vendor's defaults. */
|
|
35
|
+
export declare function serverBackingService(vendor: string, declaration?: {
|
|
36
|
+
envStems?: readonly string[];
|
|
37
|
+
service?: WorldServiceConfig;
|
|
38
|
+
}): WorldServiceConfig;
|
package/dist/src/backings.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
1
2
|
/**
|
|
3
|
+
* MongoDB is declared infrastructure, not an API service in this table: its native server uses
|
|
4
|
+
* mongodb-backing.ts through the same managed lifecycle as Postgres (pglite-backing.ts).
|
|
5
|
+
*
|
|
2
6
|
* A vendor whose open-source server a World runs as its service instead of a twin (architecture, "A vendor's own
|
|
3
|
-
* server as a backing"): the media plane no pack can hold as data. The service is the vendor's own binary on an 'auto'
|
|
7
|
+
* server as a backing"): the wire or media plane no pack can hold as data. The service is the vendor's own binary on an 'auto'
|
|
4
8
|
* port, loopback only; the application's key and secret are the ones the server is started with, so its SDK signs
|
|
5
9
|
* what the server verifies. What the server does not do on its own (LiveKit's egress, a separate service) the
|
|
6
10
|
* service's note names.
|
|
@@ -10,6 +14,31 @@ export const VENDOR_SERVER_BACKINGS = (() => {
|
|
|
10
14
|
// livekit-server wants a secret of at least 32 characters
|
|
11
15
|
const secret = 'twin-livekit-secret-for-the-worlds-own-server';
|
|
12
16
|
return {
|
|
17
|
+
temporal: {
|
|
18
|
+
service: {
|
|
19
|
+
'//': 'Temporal CLI development server on PATH, loopback only, SQLite in World state. No cluster, Cloud, TLS, World clock or branching; reset clears workflow history. See cookbook/temporal-backing.',
|
|
20
|
+
id: 'temporal', type: 'process', command: 'node',
|
|
21
|
+
args: [fileURLToPath(new URL('./temporal-backing.mjs', import.meta.url))],
|
|
22
|
+
port: 'auto', portArg: false, controlPlane: true, branchState: 'unsupported',
|
|
23
|
+
// Existing service readiness budgets, no new timeout. Native dev-server checkout took 0.84 s
|
|
24
|
+
// on 6e0123e + backing error handling changes with Node 23.9.0; SDK consumer only in this World, host not isolated.
|
|
25
|
+
// Reproduce: cookbook/temporal-backing/README.md, timed checkout and reset commands.
|
|
26
|
+
ready: { command: 'node', args: [fileURLToPath(new URL('./temporal-backing.mjs', import.meta.url)), '--health'] },
|
|
27
|
+
injectEnvTemplates: {
|
|
28
|
+
TEMPORAL_ADDRESS: '${host}:${port}', TEMPORAL_NAMESPACE: 'default', TEMPORAL_TLS: 'false', TEMPORAL_API_KEY: '',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
env: { TEMPORAL_NAMESPACE: 'default', TEMPORAL_TLS: 'false', TEMPORAL_API_KEY: '' },
|
|
32
|
+
facts: {
|
|
33
|
+
serverBacking: { command: 'temporal' },
|
|
34
|
+
adoption: { sdks: ['@temporalio/client', '@temporalio/worker', '@temporalio/workflow', 'nestjs-temporal-core'],
|
|
35
|
+
pypi: ['temporalio'], envStems: ['TEMPORAL'] },
|
|
36
|
+
hostsNone: 'The World runs the vendor development server at its declared loopback gRPC endpoint.',
|
|
37
|
+
endpointEnv: { name: 'TEMPORAL_ADDRESS', templates: {
|
|
38
|
+
TEMPORAL_ADDRESS: '${host}:${port}', TEMPORAL_NAMESPACE: 'default', TEMPORAL_TLS: 'false', TEMPORAL_API_KEY: '',
|
|
39
|
+
}, note: 'The local development server creates default; no TLS or Cloud API key.' },
|
|
40
|
+
},
|
|
41
|
+
},
|
|
13
42
|
livekit: {
|
|
14
43
|
service: {
|
|
15
44
|
'//': 'LiveKit\'s own server (livekit-server, Apache-2.0; `brew install livekit` or LiveKit\'s install script puts it '
|
|
@@ -18,11 +47,11 @@ export const VENDOR_SERVER_BACKINGS = (() => {
|
|
|
18
47
|
+ `"webhook: {api_key: ${key}, urls: [<the app's webhook URL>]}" to args.`,
|
|
19
48
|
id: 'livekit',
|
|
20
49
|
type: 'twin',
|
|
21
|
-
command: '
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
args: ['--bind', '127.0.0.1', '--node-ip', '127.0.0.1', '--keys', `${key}: ${secret}`, '--rtc.tcp_port', '0'],
|
|
50
|
+
command: 'node',
|
|
51
|
+
// The wrapper supplies a local TURN relay using UDP at the allocated HTTP port.
|
|
52
|
+
args: [fileURLToPath(new URL('./livekit-backing.mjs', import.meta.url)), '--keys', `${key}: ${secret}`],
|
|
25
53
|
port: 'auto',
|
|
54
|
+
portArg: false,
|
|
26
55
|
rootArg: false,
|
|
27
56
|
controlPlane: true,
|
|
28
57
|
ready: { httpUrl: '${url}/' },
|
|
@@ -34,12 +63,42 @@ export const VENDOR_SERVER_BACKINGS = (() => {
|
|
|
34
63
|
},
|
|
35
64
|
env: { LIVEKIT_API_KEY: key, LIVEKIT_API_SECRET: secret },
|
|
36
65
|
facts: {
|
|
37
|
-
//
|
|
66
|
+
// LiveKit's client and server SDKs use its own server as the backing.
|
|
38
67
|
serverBacking: { command: 'livekit-server' },
|
|
39
|
-
adoption: { sdks: ['livekit-client', 'livekit-server-sdk', '@livekit/rtc-node'
|
|
68
|
+
adoption: { sdks: ['livekit-client', 'livekit-server-sdk', '@livekit/rtc-node', '@livekit/react-native',
|
|
69
|
+
'@livekit/react-native-expo-plugin', '@livekit/react-native-webrtc'],
|
|
70
|
+
pypi: ['livekit', 'livekit-agents', 'livekit-api'], envStems: ['LIVEKIT'] },
|
|
40
71
|
hostsNone: 'The World runs the vendor server on its declared endpoint; SDKs read LIVEKIT_URL.',
|
|
41
72
|
endpointEnv: { name: 'LIVEKIT_URL', templates: { LIVEKIT_URL: 'ws://${host}:${port}', LIVEKIT_API_KEY: key, LIVEKIT_API_SECRET: secret }, note: 'Credentials and URL are the same ones the World starts its LiveKit server with.' },
|
|
42
73
|
},
|
|
43
74
|
},
|
|
44
75
|
};
|
|
45
76
|
})();
|
|
77
|
+
/** Declare a backing's application-owned env stems as service bindings; omitted stems use the vendor's defaults. */
|
|
78
|
+
export function serverBackingService(vendor, declaration = {}) {
|
|
79
|
+
const backing = VENDOR_SERVER_BACKINGS[vendor];
|
|
80
|
+
// Native infrastructure supplies its already-declared lifecycle rather than an API service from the table.
|
|
81
|
+
const service = declaration.service ?? backing?.service;
|
|
82
|
+
if (!service)
|
|
83
|
+
throw new Error(`No server backing declared for ${vendor}`);
|
|
84
|
+
const base = backing?.facts.adoption.envStems[0] ?? vendor.toUpperCase();
|
|
85
|
+
const stems = declaration.envStems ?? backing?.facts.adoption.envStems ?? [base];
|
|
86
|
+
if (!stems.length || stems.some((stem) => !/^[A-Z][A-Z0-9_]*$/.test(stem)))
|
|
87
|
+
throw new Error('Server backing env stems must be nonempty uppercase env names');
|
|
88
|
+
const unique = [...new Set(stems)];
|
|
89
|
+
const bindingName = (name, stem) => {
|
|
90
|
+
if (!name.startsWith(`${base}_`))
|
|
91
|
+
throw new Error(`Server backing ${vendor} binding ${name} has no vendor stem`);
|
|
92
|
+
return `${stem}${name.slice(base.length)}`;
|
|
93
|
+
};
|
|
94
|
+
const injectEnvTemplates = Object.fromEntries(unique.flatMap((stem) => Object.entries(service.injectEnvTemplates ?? {}).map(([name, value]) => [bindingName(name, stem), value])));
|
|
95
|
+
const external = service.external === undefined ? undefined : {
|
|
96
|
+
...service.external,
|
|
97
|
+
...(service.external.discover === undefined ? {} : { discover: unique.flatMap((stem) => service.external.discover.map((mapping) => {
|
|
98
|
+
const as = bindingName(mapping.as, stem);
|
|
99
|
+
// The producer keeps its own keys; only the application binding gets the declared stem.
|
|
100
|
+
return { ...mapping, as };
|
|
101
|
+
})) }),
|
|
102
|
+
};
|
|
103
|
+
return { ...service, ...(external ? { external } : { injectEnvTemplates }) };
|
|
104
|
+
}
|
package/dist/src/branch.js
CHANGED
|
@@ -3,16 +3,18 @@
|
|
|
3
3
|
// `branch <world> <name>` boots a new world from the base's config and forks every twin's mirror
|
|
4
4
|
// into it (core's `forkTwin`: a POINTER to the base at its current position — the base's whole
|
|
5
5
|
// history, its own entries included, is the branch's parent; nothing is copied). A managed database (containerless
|
|
6
|
-
// Postgres) keeps no history, so it is the one thing copied: the stopped base's data, as of now. Two branches of one
|
|
6
|
+
// Postgres and MongoDB) keeps no history, so it is the one thing copied: the stopped base's data, as of now. Two branches of one
|
|
7
7
|
// base running at once share the database port its definition names; run one at a time (the SDK does). `checkout <name>`
|
|
8
8
|
// brings a stopped world back up from its own instance record, state intact.
|
|
9
|
+
import { runInfrastructure } from "./infra.js";
|
|
9
10
|
import { cpSync, existsSync, readdirSync, readFileSync, rmSync } from 'node:fs';
|
|
10
11
|
import { dirname, join, resolve } from 'node:path';
|
|
11
12
|
import { fileURLToPath } from 'node:url';
|
|
12
13
|
import { captureHistory, historyAtInstant, forkTwin, stateGeneration, withHistoryLocks, worldPaths, worldStateRoot } from '@volter/world-core/runtime';
|
|
13
14
|
import { inheritWorldBrowsers, materializeBrowserFiles } from "./browsers.js";
|
|
14
|
-
import { downWorld, saveWorldInstance,
|
|
15
|
-
import {
|
|
15
|
+
import { externalServiceEnv, downWorld, recordedWorldNetwork, saveWorldInstance, readWorldBootRecord, upWorld } from "./runtime.js";
|
|
16
|
+
import { loadWorldConfig } from "./configs.js";
|
|
17
|
+
import { containerlessDataDirectory, parseInfraDefinition, pgliteDown, pgliteUp } from "./pglite-backing.js";
|
|
16
18
|
import { redisTwinCli, redisTwinDown, redisTwinUp } from "./redis-backing.js";
|
|
17
19
|
function stateServices(controlRoot) {
|
|
18
20
|
const stateRoot = worldStateRoot(controlRoot);
|
|
@@ -23,7 +25,12 @@ function stateServices(controlRoot) {
|
|
|
23
25
|
/** A new world over the base's mirrors. */
|
|
24
26
|
export async function branchWorld(base, name, options = {}) {
|
|
25
27
|
const root = resolve(options.root ?? process.cwd());
|
|
26
|
-
const from =
|
|
28
|
+
const from = readWorldBootRecord(base, root);
|
|
29
|
+
const { config } = loadWorldConfig(existsSync(from.configPath) ? from.configPath : from.config, root);
|
|
30
|
+
const unsupported = [...new Set([...config.services, ...Object.values(from.services)]
|
|
31
|
+
.filter((service) => service.branchState === 'unsupported').map((service) => service.id))];
|
|
32
|
+
if (unsupported.length)
|
|
33
|
+
throw new Error(`volter-world branch: services do not support carrying state to a branch: ${unsupported.join(', ')}; retain the original World or reset it explicitly`);
|
|
27
34
|
if (name === base)
|
|
28
35
|
throw new Error(`volter-world branch: the branch needs a name other than the base's ("${base}")`);
|
|
29
36
|
const envFile = options.envFile ?? join(dirname(from.envFile), `${name}.world.env`);
|
|
@@ -48,42 +55,9 @@ export async function branchWorld(base, name, options = {}) {
|
|
|
48
55
|
throw new Error(`volter-world branch: ${base} has a managed database, which keeps no history to branch from at an instant or position; branch from its current state`);
|
|
49
56
|
}
|
|
50
57
|
refuseLiveDatabases(from, databases);
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
const
|
|
54
|
-
statusWorld(name, root);
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
return false;
|
|
59
|
-
} })();
|
|
60
|
-
const began = Date.now();
|
|
61
|
-
let instance;
|
|
62
|
-
try {
|
|
63
|
-
instance = await upWorld(existsSync(from.configPath) ? from.configPath : from.config, { name, root, mode: from.mode, envFile, forceEnvOutput: options.forceEnvOutput });
|
|
64
|
-
}
|
|
65
|
-
catch (error) {
|
|
66
|
-
// only a record made during this call is ours to remove, and it is removed by its own createdAt, as the fork's
|
|
67
|
-
// cleanup below does: a concurrent `branch` of the same name that recorded first keeps its World
|
|
68
|
-
let recorded = null;
|
|
69
|
-
if (!existed) {
|
|
70
|
-
try {
|
|
71
|
-
const made = statusWorld(name, root).createdAt;
|
|
72
|
-
if (Date.parse(made) >= began)
|
|
73
|
-
recorded = made;
|
|
74
|
-
}
|
|
75
|
-
catch { /* the boot recorded nothing */ }
|
|
76
|
-
}
|
|
77
|
-
if (recorded) {
|
|
78
|
-
try {
|
|
79
|
-
await downWorld(name, root, { purge: true, expectedCreatedAt: recorded });
|
|
80
|
-
}
|
|
81
|
-
catch (cleanup) {
|
|
82
|
-
throw new Error(`${error instanceof Error ? error.message : String(error)} (and the half-made branch ${name} could not be removed: ${cleanup instanceof Error ? cleanup.message : String(cleanup)})`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
throw error;
|
|
86
|
-
}
|
|
58
|
+
// up owns rollback. If boot throws, retain its diagnostics: a name/time lookup cannot prove
|
|
59
|
+
// that a partial record belongs to this invocation, especially under a frozen World clock.
|
|
60
|
+
const instance = await upWorld(existsSync(from.configPath) ? from.configPath : from.config, { name, root, mode: from.mode, owner: from.lifecycle?.owner ?? from.resources?.owner, networkCeiling: recordedWorldNetwork(from), envFile, forceEnvOutput: options.forceEnvOutput });
|
|
87
61
|
const forked = {};
|
|
88
62
|
try {
|
|
89
63
|
// the branch's origin is its base's, or none: never one a stopped World of the same name left
|
|
@@ -105,23 +79,34 @@ export async function branchWorld(base, name, options = {}) {
|
|
|
105
79
|
await branchManagedDatabases(from, instance, databases);
|
|
106
80
|
}
|
|
107
81
|
catch (error) {
|
|
108
|
-
|
|
82
|
+
try {
|
|
83
|
+
await downWorld(name, root, { purge: true, expectedInstance: instance.instance });
|
|
84
|
+
}
|
|
85
|
+
catch (cleanup) {
|
|
86
|
+
throw new AggregateError([error, cleanup], 'Copied database startup and branch cleanup failed');
|
|
87
|
+
}
|
|
109
88
|
throw error;
|
|
110
89
|
}
|
|
111
90
|
return { instance, forked };
|
|
112
91
|
}
|
|
113
|
-
/** The base's containerless managed databases: each PGlite `pglite-<kind>-data
|
|
114
|
-
* `
|
|
92
|
+
/** The base's containerless managed databases: each PGlite `pglite-<kind>-data`, MongoDB's
|
|
93
|
+
* `mongod-mongodb-data` (pglite-backing.ts), the redis twin's `redis-twin` tree (redis-backing.ts), and native PostgreSQL. A stopped World's
|
|
115
94
|
* hosts leave no pid file; a docker-backed World keeps its data under the container's volume, which a branch does not
|
|
116
95
|
* carry. */
|
|
117
96
|
function managedDatabases(from) {
|
|
118
97
|
const data = from.dirs.data;
|
|
119
98
|
if (!existsSync(data))
|
|
120
99
|
return [];
|
|
121
|
-
|
|
100
|
+
const known = (name) => /^pglite-[a-z0-9_-]+-data$/.test(name) || name === 'mongod-mongodb-data' || name === 'native-postgres-data';
|
|
101
|
+
if (readdirSync(data).some(name => name.endsWith('-data') && !known(name) && !from.services[name])) {
|
|
102
|
+
throw new Error('volter-world branch: unrecognised storage; retain the original World');
|
|
103
|
+
}
|
|
104
|
+
return readdirSync(data).filter((n) => /^pglite-[a-z0-9_-]+-data$/.test(n) || n === 'mongod-mongodb-data' || n === 'redis-twin' || n === 'native-postgres-data');
|
|
122
105
|
}
|
|
123
106
|
/** The pid files of the hosts serving one managed database directory. */
|
|
124
107
|
function hostPidFiles(data, db) {
|
|
108
|
+
if (db === 'native-postgres-data')
|
|
109
|
+
return [join(data, 'native-postgres.owner.json')];
|
|
125
110
|
if (db === 'redis-twin')
|
|
126
111
|
return readdirSync(data).filter((n) => /^redis-twin-\d+\.pid$/.test(n)).map((n) => join(data, n));
|
|
127
112
|
return [join(data, `${db.slice(0, -'-data'.length)}.pid`)];
|
|
@@ -131,7 +116,8 @@ function hostPidFiles(data, db) {
|
|
|
131
116
|
function refuseLiveDatabases(from, dbs) {
|
|
132
117
|
for (const db of dbs) {
|
|
133
118
|
for (const pidFile of hostPidFiles(from.dirs.data, db)) {
|
|
134
|
-
const
|
|
119
|
+
const recorded = existsSync(pidFile) ? readFileSync(pidFile, 'utf8').trim() : '';
|
|
120
|
+
const pid = db === 'native-postgres-data' && recorded ? JSON.parse(recorded).pid : Number(recorded);
|
|
135
121
|
let live = false;
|
|
136
122
|
if (Number.isInteger(pid) && pid > 0) {
|
|
137
123
|
try {
|
|
@@ -147,18 +133,65 @@ function refuseLiveDatabases(from, dbs) {
|
|
|
147
133
|
}
|
|
148
134
|
}
|
|
149
135
|
}
|
|
136
|
+
/** Compare actual cold-storage formats, not the edited config's requested backing. */
|
|
137
|
+
function requireMatchingBackings(source, child) {
|
|
138
|
+
const kinds = {
|
|
139
|
+
postgres: ['native-postgres-data', 'pglite-postgres-data'],
|
|
140
|
+
mongodb: ['mongod-mongodb-data'],
|
|
141
|
+
};
|
|
142
|
+
for (const [kind, formats] of Object.entries(kinds)) {
|
|
143
|
+
const from = source.filter(directory => formats.includes(directory));
|
|
144
|
+
if (!from.length)
|
|
145
|
+
continue;
|
|
146
|
+
const to = child.filter(directory => formats.includes(directory));
|
|
147
|
+
if (from.length !== 1 || to.length !== 1 || from[0] !== to[0]) {
|
|
148
|
+
throw new Error(`volter-world branch: ${kind} source backing ${from.join(', ')} differs from child backing ${to.join(', ') || 'none'}; refusing to copy database bytes`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
for (const directory of source) {
|
|
152
|
+
if (!child.includes(directory))
|
|
153
|
+
throw new Error(`volter-world branch: source backing ${directory} differs from child backing ${child.join(', ') || 'none'}; refusing to copy database bytes`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
150
156
|
/** The branch's managed databases as the base holds them: the branch's own, just booted empty by `up`, are stopped,
|
|
151
157
|
* replaced by a copy of the base's data, and started again, so a branch of a migrated and seeded World serves that
|
|
152
158
|
* data, as its twins serve the base's history. */
|
|
153
159
|
async function branchManagedDatabases(from, instance, dbs) {
|
|
154
160
|
if (!dbs.length)
|
|
155
161
|
return;
|
|
162
|
+
requireMatchingBackings(dbs, managedDatabases(instance));
|
|
156
163
|
const definition = join(dirname(instance.configPath), 'world.infrastructure.yml');
|
|
157
164
|
if (!existsSync(definition))
|
|
158
|
-
|
|
165
|
+
throw new Error('volter-world branch: source has managed database bytes but child has no infrastructure definition; refusing to copy');
|
|
159
166
|
const services = parseInfraDefinition(readFileSync(definition, 'utf8'));
|
|
160
167
|
const redis = services.filter((s) => s.kind === 'redis');
|
|
161
|
-
const hosted = services.filter((
|
|
168
|
+
const hosted = services.filter((service) => {
|
|
169
|
+
const directory = containerlessDataDirectory(service.kind);
|
|
170
|
+
if (directory === undefined || !dbs.includes(directory))
|
|
171
|
+
return false;
|
|
172
|
+
if (!existsSync(join(instance.dirs.data, directory))) {
|
|
173
|
+
throw new Error(`volter-world branch: ${service.kind}'s copied state belongs to a different backing than the branch booted; refusing to replace its database`);
|
|
174
|
+
}
|
|
175
|
+
return true;
|
|
176
|
+
});
|
|
177
|
+
const config = loadWorldConfig(instance.configPath, instance.root).config;
|
|
178
|
+
const infrastructure = config.services.find((service) => service.type === 'external' && (service.external?.up[0] === 'volter-world-infra' || service.id === 'infrastructure'));
|
|
179
|
+
if (!infrastructure)
|
|
180
|
+
throw new Error(`volter-world branch: ${instance.name}'s managed databases have no declared infrastructure service`);
|
|
181
|
+
const launch = { env: externalServiceEnv(infrastructure, instance.dirs, instance.env, config.stripEnv) };
|
|
182
|
+
if (dbs.includes('native-postgres-data')) {
|
|
183
|
+
const io = { out: (text) => process.stdout.write(text), err: (text) => process.stderr.write(text) };
|
|
184
|
+
if (await runInfrastructure('down', launch.env, io) !== 0)
|
|
185
|
+
throw new Error('managed native database branch could not stop its new backing');
|
|
186
|
+
for (const db of dbs) {
|
|
187
|
+
const to = join(instance.dirs.data, db);
|
|
188
|
+
rmSync(to, { recursive: true, force: true });
|
|
189
|
+
cpSync(join(from.dirs.data, db), to, { recursive: true });
|
|
190
|
+
}
|
|
191
|
+
if (await runInfrastructure('up', launch.env, io) !== 0)
|
|
192
|
+
throw new Error('managed native database branch could not resume its copied backing');
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
162
195
|
const redisCli = dbs.includes('redis-twin') && redis.length ? redisTwinCli([dirname(fileURLToPath(import.meta.url)), dirname(instance.configPath)]) : undefined;
|
|
163
196
|
await pgliteDown(hosted, instance.dirs.data);
|
|
164
197
|
if (redisCli)
|
|
@@ -170,14 +203,14 @@ async function branchManagedDatabases(from, instance, dbs) {
|
|
|
170
203
|
rmSync(to, { recursive: true, force: true });
|
|
171
204
|
cpSync(join(from.dirs.data, db), to, { recursive: true });
|
|
172
205
|
}
|
|
173
|
-
if (
|
|
174
|
-
await pgliteUp(hosted, instance.dirs.data);
|
|
206
|
+
if (hosted.length)
|
|
207
|
+
await pgliteUp(hosted, instance.dirs.data, launch);
|
|
175
208
|
if (redisCli)
|
|
176
|
-
await redisTwinUp(redis, instance.dirs.data, redisCli);
|
|
209
|
+
await redisTwinUp(redis, instance.dirs.data, redisCli, launch);
|
|
177
210
|
}
|
|
178
211
|
/** A stopped world back up, from its own record. */
|
|
179
212
|
export async function checkoutWorld(name, options = {}) {
|
|
180
213
|
const root = resolve(options.root ?? process.cwd());
|
|
181
|
-
const was =
|
|
182
|
-
return upWorld(existsSync(was.configPath) ? was.configPath : was.config, { name, root, mode: was.mode, envFile: was.envFile, keepState: true });
|
|
214
|
+
const was = readWorldBootRecord(name, root);
|
|
215
|
+
return upWorld(existsSync(was.configPath) ? was.configPath : was.config, { name, root, mode: was.mode, owner: was.lifecycle?.owner ?? was.resources?.owner, envFile: was.envFile, keepState: true });
|
|
183
216
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Bind an existing browser page to a World-attached HTTP transport. A namespace gives the World its own page and cookie
|
|
2
|
+
// paths on that page's origin; vendor URLs remain the wire targets. Published external JavaScript is never rewritten.
|
|
3
|
+
exports.installWorldBrowserRoutes = async function (router, transport, { namespace, origin, onExchange, context = false } = {}) {
|
|
4
|
+
if (namespace && (!/^\/[A-Za-z0-9._~/-]+$/.test(namespace) || !origin)) throw new Error('A World page needs a path namespace and its browser origin');
|
|
5
|
+
const base = namespace?.replace(/\/$/, '');
|
|
6
|
+
const forward = async route => {
|
|
7
|
+
try {
|
|
8
|
+
const request = route.request();
|
|
9
|
+
if (context && namespace) {
|
|
10
|
+
let documentUrl;
|
|
11
|
+
try { documentUrl = request.frame().page().url(); } catch {}
|
|
12
|
+
const ownPath = `${origin}${base}/`;
|
|
13
|
+
if (!request.url().startsWith(ownPath) && !documentUrl?.startsWith(ownPath)) return route.fallback();
|
|
14
|
+
}
|
|
15
|
+
let url = new URL(request.url()), method = request.method(), response;
|
|
16
|
+
let prefix;
|
|
17
|
+
if (namespace && url.origin === origin && url.pathname.startsWith(base + '/')) {
|
|
18
|
+
const rest = url.pathname.slice(base.length + 1), at = rest.indexOf('/');
|
|
19
|
+
const host = at < 0 ? rest : rest.slice(0, at);
|
|
20
|
+
prefix = `${base}/${host}`;
|
|
21
|
+
url = new URL(`https://${host}${at < 0 ? '/' : rest.slice(at)}${url.search}`);
|
|
22
|
+
}
|
|
23
|
+
if (namespace && !prefix) prefix = `${base}/${url.host}`;
|
|
24
|
+
// Complete wire headers stay opaque: the abbreviated request header view omits authentication headers.
|
|
25
|
+
const headers = await request.allHeaders();
|
|
26
|
+
if (prefix) headers['x-forwarded-prefix'] = prefix;
|
|
27
|
+
const visited = new Set();
|
|
28
|
+
for (;;) {
|
|
29
|
+
if (visited.has(url.href)) throw new Error('World browser redirect loop');
|
|
30
|
+
visited.add(url.href);
|
|
31
|
+
response = await route.fetch({ url: transport + '/?url=' + encodeURIComponent(url.href), method, headers, maxRedirects: 0, timeout: 1000 });
|
|
32
|
+
const status = response.status(), location = response.headers().location;
|
|
33
|
+
onExchange?.({ method, path: url.pathname, status });
|
|
34
|
+
if (![301, 302, 303, 307, 308].includes(status) || !location) break;
|
|
35
|
+
const next = new URL(location, url);
|
|
36
|
+
// Chrome owns the application's HTTP loopback callback.
|
|
37
|
+
if (next.protocol !== 'https:') break;
|
|
38
|
+
if (next.origin !== url.origin) {
|
|
39
|
+
for (const name of Object.keys(headers)) if (/^(authorization|cookie|proxy-authorization|x-volter-twin-|x-forwarded-prefix)/i.test(name)) delete headers[name];
|
|
40
|
+
}
|
|
41
|
+
if (status === 303 || ([301, 302].includes(status) && method === 'POST')) method = 'GET';
|
|
42
|
+
url = next;
|
|
43
|
+
}
|
|
44
|
+
if (namespace && /text\/html/i.test(response.headers()['content-type'] ?? '')) {
|
|
45
|
+
// Input values are protocol data (OAuth redirect_uri, state and code). Keep them byte-for-byte. Links and inline
|
|
46
|
+
// scripts name browser destinations, mapped to this World's page; external script bytes remain as published.
|
|
47
|
+
const body = (await response.text()).split(/(<input\b[^>]*>)/gi).map(part => /^<input\b/i.test(part) ? part : part.replace(/https:\/\/([A-Za-z0-9.-]+(?::\d+)?)(?=[/\s"'&<]|$)/g, (_, host) => `${origin}${base}/${host}`)).join('');
|
|
48
|
+
const outHeaders = { ...response.headers() };
|
|
49
|
+
delete outHeaders['content-length'];
|
|
50
|
+
await route.fulfill({ response, headers: outHeaders, body });
|
|
51
|
+
} else await route.fulfill({ response });
|
|
52
|
+
} catch (error) {
|
|
53
|
+
onExchange?.({ error: error.message });
|
|
54
|
+
await route.abort('blockedbyclient').catch(() => {});
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
await router.route('https://**/*', forward);
|
|
58
|
+
return { message: 'World routing installed on auth tab', namespace: base ?? null, dispose: () => router.unroute('https://**/*', forward) };
|
|
59
|
+
};
|
package/dist/src/ca-trust.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { errorMessage } from "./error-message.js";
|
|
1
2
|
import { createHash, randomUUID, X509Certificate } from 'node:crypto';
|
|
2
3
|
import { existsSync, readFileSync, writeFileSync, renameSync, rmSync } from 'node:fs';
|
|
3
4
|
import { dirname, join } from 'node:path';
|
|
@@ -22,7 +23,7 @@ export function sessionTrustEnv(caCertPath, inputs = process.env) {
|
|
|
22
23
|
return pem;
|
|
23
24
|
}
|
|
24
25
|
catch (error) {
|
|
25
|
-
throw new CaTrustError(`Cannot read configured CA file ${path}: ${
|
|
26
|
+
throw new CaTrustError(`Cannot read configured CA file ${path}: ${errorMessage(error)}`);
|
|
26
27
|
}
|
|
27
28
|
};
|
|
28
29
|
const session = read(caCertPath);
|
|
@@ -47,9 +48,9 @@ export function sessionTrustEnv(caCertPath, inputs = process.env) {
|
|
|
47
48
|
rmSync(temporary, { force: true });
|
|
48
49
|
}
|
|
49
50
|
catch (cleanupError) {
|
|
50
|
-
throw new CaTrustError(`Cannot materialize session trust bundle: ${
|
|
51
|
+
throw new CaTrustError(`Cannot materialize session trust bundle: ${errorMessage(error)}; cleanup: ${errorMessage(cleanupError)}`);
|
|
51
52
|
}
|
|
52
|
-
throw new CaTrustError(`Cannot materialize session trust bundle: ${
|
|
53
|
+
throw new CaTrustError(`Cannot materialize session trust bundle: ${errorMessage(error)}`);
|
|
53
54
|
}
|
|
54
55
|
return path;
|
|
55
56
|
};
|
package/dist/src/changeset.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { errorMessage } from "./error-message.js";
|
|
1
2
|
import { worldChangesetRules } from "./configs.js";
|
|
2
3
|
import { blobDigest, captureHistory, captureParentHistory, getActiveBlobStore, getActiveWorldStore, withHistoryLocks, worldPaths } from '@volter/world-core/runtime';
|
|
3
4
|
// `volter-world mark` / `diff` / `changeset` — the WORLD-side verbs over the changeset
|
|
@@ -41,7 +42,7 @@ function requireWorld(name, root) {
|
|
|
41
42
|
return statusWorld(name, root);
|
|
42
43
|
}
|
|
43
44
|
catch (error) {
|
|
44
|
-
if (!/World instance not found/.test(String(error
|
|
45
|
+
if (!/World instance not found/.test(String(errorMessage(error))))
|
|
45
46
|
throw error;
|
|
46
47
|
const known = listWorlds(root).map((world) => world.name);
|
|
47
48
|
throw new Error(`World "${name}" not found in ${root}${known.length ? ` (known worlds: ${known.join(', ')})` : ' (no worlds here)'} — start it with \`volter world up\` in the app's folder (the operator's own: \`volter-world up <config> --env-out <path> --name ${name}\`)`);
|
|
@@ -482,7 +483,7 @@ export async function pushWorldChangeset(name, options) {
|
|
|
482
483
|
res = { ok: true, status: 200 };
|
|
483
484
|
}
|
|
484
485
|
catch (error) {
|
|
485
|
-
body = { error:
|
|
486
|
+
body = { error: errorMessage(error) };
|
|
486
487
|
res = { ok: false, status: 409 };
|
|
487
488
|
}
|
|
488
489
|
}
|
|
@@ -547,7 +548,7 @@ export async function pushWorldChangeset(name, options) {
|
|
|
547
548
|
// the remote's record of who pushed it and who owns it, kept on this copy too (outside the hash)
|
|
548
549
|
const changeset = { ...withApplication(located.changeset, application), ...(body.owner && !located.changeset.owner ? { owner: body.owner } : {}), ...(body.pushedBy ? { pushedBy: body.pushedBy } : {}) };
|
|
549
550
|
writeChangesetFile(located.path, changeset);
|
|
550
|
-
const unsent = namedFailure ? `records the landed entries name did not reach the origin (${
|
|
551
|
+
const unsent = namedFailure ? `records the landed entries name did not reach the origin (${errorMessage(namedFailure)}); the next push from this branch that touches ${twins.map(([t]) => t).join(', ')} sends them` : undefined;
|
|
551
552
|
return { ...(unsent ? { unsent } : {}), application, changeset, world: located.world, path: located.path, remote: { url, namespace } };
|
|
552
553
|
}
|
|
553
554
|
/** `volter-world changeset rebase <name>`: the merge onto the authoring world's moved mirror. */
|