@volter/world-runtime 3.0.163 → 3.0.165
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/ancestry.js +2 -2
- package/dist/src/app-url.d.ts +5 -1
- package/dist/src/app-url.js +14 -1
- package/dist/src/branch.js +28 -8
- package/dist/src/browser-files.d.ts +5 -0
- package/dist/src/browser-files.js +14 -0
- package/dist/src/browsers.d.ts +2 -3
- package/dist/src/browsers.js +4 -4
- package/dist/src/catalog.d.ts +2 -1
- package/dist/src/catalog.js +9 -3
- package/dist/src/changeset.js +1 -1
- package/dist/src/cli-commands.js +71 -18
- package/dist/src/cli-load-clock.d.ts +1 -0
- package/dist/src/cli-load-clock.js +2 -0
- package/dist/src/cli.js +5 -1
- package/dist/src/clickhouse-backing.d.ts +2 -0
- package/dist/src/clickhouse-backing.js +145 -0
- package/dist/src/command-lifetime.d.mts +1 -1
- package/dist/src/command-lifetime.mjs +5 -2
- package/dist/src/configs.js +1 -1
- package/dist/src/consumers.js +1 -1
- package/dist/src/covers.d.ts +4 -2
- package/dist/src/covers.js +8 -4
- package/dist/src/go-mysql-binding.d.ts +19 -0
- package/dist/src/go-mysql-binding.js +51 -0
- package/dist/src/guest-processes.d.mts +1 -1
- package/dist/src/guest-processes.mjs +22 -14
- package/dist/src/host-cli.js +8 -1
- package/dist/src/host-ready.d.ts +16 -2
- package/dist/src/host-ready.js +66 -39
- package/dist/src/host-worker.js +7 -1
- package/dist/src/host.d.ts +26 -0
- package/dist/src/host.js +93 -3
- package/dist/src/infra-definition.d.ts +35 -0
- package/dist/src/infra-definition.js +146 -0
- package/dist/src/infra.d.ts +3 -0
- package/dist/src/infra.js +375 -105
- package/dist/src/init.d.ts +46 -0
- package/dist/src/init.js +446 -19
- package/dist/src/lifecycle-record.d.ts +5 -1
- package/dist/src/lifecycle-record.js +27 -4
- package/dist/src/local-branches.js +1 -1
- package/dist/src/mariadb-bootstrap.d.ts +32 -0
- package/dist/src/mariadb-bootstrap.js +90 -0
- package/dist/src/mariadb-supplier.d.ts +23 -0
- package/dist/src/mariadb-supplier.js +215 -0
- package/dist/src/mongodb-backing.d.ts +4 -0
- package/dist/src/mongodb-backing.js +71 -1
- package/dist/src/native-mariadb-backing.d.ts +13 -0
- package/dist/src/native-mariadb-backing.js +512 -0
- package/dist/src/native-postgres-backing.d.ts +4 -10
- package/dist/src/native-postgres-backing.js +428 -43
- package/dist/src/native-server-inputs.d.ts +16 -0
- package/dist/src/native-server-inputs.js +204 -0
- package/dist/src/origin.js +3 -3
- package/dist/src/original-inputs.d.ts +14 -0
- package/dist/src/original-inputs.js +84 -0
- package/dist/src/pack-facts.d.ts +9 -1
- package/dist/src/pglite-backing.d.ts +41 -17
- package/dist/src/pglite-backing.js +213 -79
- package/dist/src/pglite-host.mjs +482 -10
- package/dist/src/process-groups.d.mts +6 -1
- package/dist/src/process-groups.d.ts +2 -1
- package/dist/src/process-groups.js +1 -1
- package/dist/src/process-groups.mjs +82 -15
- package/dist/src/process-signal.d.mts +2 -2
- package/dist/src/process-signal.mjs +29 -9
- package/dist/src/project-inspect.d.ts +12 -0
- package/dist/src/project-inspect.js +41 -4
- package/dist/src/proxy-env.d.ts +34 -0
- package/dist/src/proxy-env.js +114 -0
- package/dist/src/redirect-proxy.d.ts +1 -31
- package/dist/src/redirect-proxy.js +3 -112
- package/dist/src/redis-backing.d.ts +6 -4
- package/dist/src/redis-backing.js +20 -168
- package/dist/src/runtime.d.ts +1 -0
- package/dist/src/runtime.js +503 -218
- package/dist/src/schema.d.ts +1 -0
- package/dist/src/schema.js +5 -3
- package/dist/src/served-world.js +3 -3
- package/dist/src/twin-backing.d.ts +47 -0
- package/dist/src/twin-backing.js +343 -0
- package/dist/src/up-cli.js +56 -3
- package/package.json +3 -3
- package/src/ancestry.ts +2 -2
- package/src/app-url.ts +9 -2
- package/src/branch.ts +26 -8
- package/src/browser-files.ts +15 -0
- package/src/browsers.ts +4 -4
- package/src/catalog.ts +9 -4
- package/src/changeset.ts +1 -1
- package/src/cli-commands.ts +85 -18
- package/src/cli-load-clock.ts +2 -0
- package/src/cli.ts +5 -1
- package/src/clickhouse-backing.ts +70 -0
- package/src/command-lifetime.d.mts +1 -1
- package/src/command-lifetime.mjs +5 -2
- package/src/configs.ts +1 -1
- package/src/consumers.ts +1 -1
- package/src/covers.ts +9 -4
- package/src/go-mysql-binding.ts +49 -0
- package/src/guest-processes.d.mts +1 -1
- package/src/guest-processes.mjs +22 -14
- package/src/host-cli.ts +7 -1
- package/src/host-ready.ts +62 -31
- package/src/host-worker.ts +6 -2
- package/src/host.ts +88 -6
- package/src/infra-definition.ts +135 -0
- package/src/infra.ts +250 -45
- package/src/init.ts +417 -18
- package/src/lifecycle-record.ts +18 -4
- package/src/local-branches.ts +1 -1
- package/src/mariadb-bootstrap.ts +107 -0
- package/src/mariadb-supplier.ts +199 -0
- package/src/mongodb-backing.ts +59 -1
- package/src/native-mariadb-backing.ts +418 -0
- package/src/native-postgres-backing.ts +353 -41
- package/src/native-server-inputs.ts +169 -0
- package/src/origin.ts +3 -3
- package/src/original-inputs.ts +72 -0
- package/src/pack-facts.ts +1 -1
- package/src/pglite-backing.ts +192 -80
- package/src/pglite-host.mjs +482 -10
- package/src/process-groups.d.mts +6 -1
- package/src/process-groups.mjs +82 -15
- package/src/process-groups.ts +2 -1
- package/src/process-signal.d.mts +2 -2
- package/src/process-signal.mjs +29 -9
- package/src/project-inspect.ts +37 -4
- package/src/proxy-env.ts +128 -0
- package/src/redirect-proxy.ts +3 -125
- package/src/redis-backing.ts +19 -136
- package/src/runtime.ts +342 -107
- package/src/schema.ts +5 -4
- package/src/served-world.ts +3 -3
- package/src/twin-backing.ts +300 -0
- package/src/up-cli.ts +52 -3
package/src/redis-backing.ts
CHANGED
|
@@ -8,41 +8,15 @@
|
|
|
8
8
|
// in a container is state outside it (a branch of the World would share the container's queue and cache). The
|
|
9
9
|
// twin keeps each key a subject of the World (the Redis pack's storage module). It needs nothing
|
|
10
10
|
// installed: no container runtime, no redis-server binary.
|
|
11
|
-
|
|
11
|
+
//
|
|
12
|
+
// The lifecycle (start, ownership, readiness, retirement) is every twin-served service's and lives in
|
|
13
|
+
// twin-backing.ts, and the pack that serves the kind is found there from pack facts (`managedService`), not named
|
|
14
|
+
// here. This file is what is Redis's own in the runtime: the two questions asked in Redis's protocol.
|
|
12
15
|
import { connect } from 'node:net';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { publishProcessReceipt, readProcessReceipt, readSpawnReceipt, spawnReceiptPath, signalOwnedProcess } from './process-receipt.ts';
|
|
16
|
-
import { spawn, type ChildProcess } from 'node:child_process';
|
|
17
|
-
import { survivingOwnedGroups } from './process-groups.ts';
|
|
18
|
-
import { errorMessage } from './error-message.ts';
|
|
19
|
-
import { findInstalledPackage, packCli } from './catalog.ts';
|
|
20
|
-
import { vendorPackageDir } from '@volter/world-core/runtime';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { managedDirectory, managedTwin, twinBackingCli, twinBackingDown, twinBackingStatus, twinBackingUp, type ManagedProtocol, type TwinServedBacking } from './twin-backing.ts';
|
|
21
18
|
import type { HostLaunch, InfraService } from './pglite-backing.ts';
|
|
22
19
|
|
|
23
|
-
interface RedisHost { child: ChildProcess; lifetime: ReturnType<typeof commandLifetime>; closed: boolean }
|
|
24
|
-
const hosts = new Map<string, RedisHost>();
|
|
25
|
-
const PACKAGE = '@volter/twin-redis';
|
|
26
|
-
const READY_TIMEOUT_MS = 60_000;
|
|
27
|
-
|
|
28
|
-
/** The redis twin's cli: installed beside this runtime (its node_modules walk), else beside the World's config. */
|
|
29
|
-
export function redisTwinCli(from: string[]): string | undefined {
|
|
30
|
-
for (const dir of from) {
|
|
31
|
-
const found = vendorPackageDir(dir, 'redis') ?? findInstalledPackage(dir, PACKAGE);
|
|
32
|
-
const cli = found === undefined ? undefined : packCli(found);
|
|
33
|
-
if (cli !== undefined && existsSync(cli)) return cli;
|
|
34
|
-
}
|
|
35
|
-
return undefined;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const pidPath = (dataDir: string, service: InfraService): string => join(dataDir, `redis-twin-${service.hostPort}.pid`);
|
|
39
|
-
/** Where the service's keys live: the World's tree for the `redis` service, under the World's data. */
|
|
40
|
-
export const redisTwinRoot = (dataDir: string): string => join(dataDir, 'redis-twin');
|
|
41
|
-
|
|
42
|
-
function alive(pid: number): boolean {
|
|
43
|
-
try { process.kill(pid, 0); return true; } catch (error) { if ((error as NodeJS.ErrnoException).code === 'ESRCH') return false; throw error; }
|
|
44
|
-
}
|
|
45
|
-
|
|
46
20
|
function listening(port: number): Promise<boolean> {
|
|
47
21
|
return new Promise((resolve) => {
|
|
48
22
|
const socket = connect({ host: '127.0.0.1', port, timeout: 1000 });
|
|
@@ -75,108 +49,17 @@ function redisServes(port: number, pid: number): Promise<boolean> {
|
|
|
75
49
|
});
|
|
76
50
|
}
|
|
77
51
|
|
|
78
|
-
const
|
|
52
|
+
export const REDIS_PROTOCOL: ManagedProtocol = { name: 'Redis', answers: listening, serves: redisServes };
|
|
53
|
+
const twin = (): TwinServedBacking => {
|
|
54
|
+
const found = managedTwin('redis', REDIS_PROTOCOL);
|
|
55
|
+
if (!found) throw new Error('no pack declares the redis kind (managedService) in pack facts');
|
|
56
|
+
return found;
|
|
57
|
+
};
|
|
79
58
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// hand this World's app someone else's keys, so it is refused, never taken as ready
|
|
88
|
-
if (await listening(service.hostPort)) throw new Error(`port ${service.hostPort} already serves Redis for another process`);
|
|
89
|
-
const root = redisTwinRoot(dataDir);
|
|
90
|
-
mkdirSync(root, { recursive: true });
|
|
91
|
-
const log = join(dataDir, `redis-twin-${service.hostPort}.log`);
|
|
92
|
-
mkdirSync(dirname(log), { recursive: true });
|
|
93
|
-
// a real fd, not a pipe: the server outlives this process untethered
|
|
94
|
-
const logFd = openSync(log, 'a');
|
|
95
|
-
// the runtime this runs on: Bun runs the pack's TypeScript as it is; Node (volter-world-infra's shebang) needs
|
|
96
|
-
// type transformation, not only stripping (the kernel declares constructor parameter properties)
|
|
97
|
-
const runtime = typeof (globalThis as { Bun?: unknown }).Bun !== 'undefined' || !cli.endsWith('.ts') ? [] : ['--experimental-transform-types', '--no-warnings'];
|
|
98
|
-
let held: RedisHost | undefined;
|
|
99
|
-
try {
|
|
100
|
-
try {
|
|
101
|
-
const child = spawn(process.execPath, [...runtime, cli, 'serve', '--port', String(service.hostPort), '--root', root], {
|
|
102
|
-
env: launch.env ?? process.env, detached: true, windowsHide: true, stdio: ['ignore', logFd, logFd],
|
|
103
|
-
});
|
|
104
|
-
// Retain BEFORE descriptor close, birth capture or either publication can fail.
|
|
105
|
-
held = { child, lifetime: commandLifetime(child, launch.signal), closed: false };
|
|
106
|
-
const observed = held;
|
|
107
|
-
child.once('close', () => { observed.closed = true; });
|
|
108
|
-
child.on('error', () => {}); // lifetime joins spawn failure and close
|
|
109
|
-
hosts.set(pidFile, held);
|
|
110
|
-
} finally { closeSync(logFd); }
|
|
111
|
-
if (!held.child.pid) throw new Error('Redis child did not start');
|
|
112
|
-
const identity = await held.lifetime.identity;
|
|
113
|
-
if (!identity?.start) throw new Error('Redis birth capture failed; retiring held child');
|
|
114
|
-
publishProcessReceipt(spawnReceiptPath(pidFile), JSON.stringify(identity) + '\n');
|
|
115
|
-
publishProcessReceipt(pidFile, `${held.child.pid}\n`);
|
|
116
|
-
const deadline = Date.now() + READY_TIMEOUT_MS;
|
|
117
|
-
while (!alive(held.child.pid) || !(await redisServes(service.hostPort, held.child.pid))) {
|
|
118
|
-
if (!alive(held.child.pid)) throw new Error(`redis exited before serving — see ${log}`);
|
|
119
|
-
launch.signal?.throwIfAborted();
|
|
120
|
-
if (Date.now() > deadline) throw new Error(`redis did not serve port ${service.hostPort} within ${READY_TIMEOUT_MS / 1000}s — see ${log}`);
|
|
121
|
-
await sleep(25);
|
|
122
|
-
}
|
|
123
|
-
launch.signal?.throwIfAborted();
|
|
124
|
-
held.child.unref();
|
|
125
|
-
} catch (error) {
|
|
126
|
-
if (held) {
|
|
127
|
-
try { await redisTwinDown([service], dataDir); }
|
|
128
|
-
catch (cleanup) { throw new AggregateError([error, cleanup], 'Redis startup and retirement failed; ownership retained'); }
|
|
129
|
-
}
|
|
130
|
-
throw error;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export async function redisTwinStatus(services: InfraService[], dataDir: string): Promise<number> {
|
|
136
|
-
let ready = 0;
|
|
137
|
-
for (const service of services) {
|
|
138
|
-
const pidFile = pidPath(dataDir, service);
|
|
139
|
-
if (!existsSync(pidFile)) continue;
|
|
140
|
-
const pid = Number(readFileSync(pidFile, 'utf8').trim());
|
|
141
|
-
if (alive(pid) && await redisServes(service.hostPort, pid)) ready += 1;
|
|
142
|
-
}
|
|
143
|
-
return ready;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export async function redisTwinDown(services: InfraService[], dataDir: string): Promise<void> {
|
|
147
|
-
for (const service of services) {
|
|
148
|
-
const pidFile = pidPath(dataDir, service);
|
|
149
|
-
const recorded = readProcessReceipt(pidFile, text => {
|
|
150
|
-
const pid = Number(text.trim());
|
|
151
|
-
if (!Number.isInteger(pid) || pid <= 0) throw new Error('Invalid Redis ownership PID');
|
|
152
|
-
return pid;
|
|
153
|
-
});
|
|
154
|
-
const identity = readSpawnReceipt(pidFile);
|
|
155
|
-
const held = hosts.get(pidFile);
|
|
156
|
-
if (recorded.error || identity.error) process.stderr.write(`${errorMessage(new AggregateError([recorded.error, identity.error].filter(Boolean), 'Redis ownership uncertain; retrying established ownership'))}\n`);
|
|
157
|
-
if (held) {
|
|
158
|
-
const retired = () => held!.closed && (!held!.child.pid || !survivingOwnedGroups([held!.child.pid]).length);
|
|
159
|
-
if (!retired()) {
|
|
160
|
-
held.child.ref(); held.lifetime.cancel('SIGTERM');
|
|
161
|
-
try { await held.lifetime.finish(); }
|
|
162
|
-
catch (error) { if (!retired()) throw error; }
|
|
163
|
-
}
|
|
164
|
-
} else {
|
|
165
|
-
if (!recorded.present && !identity.present) continue;
|
|
166
|
-
const pid = identity.value?.pid ?? recorded.value;
|
|
167
|
-
if (!pid) throw new AggregateError([recorded.error, identity.error].filter(Boolean), 'Redis ownership uncertain; receipts retained');
|
|
168
|
-
if (recorded.value && identity.value && recorded.value !== identity.value.pid) throw new Error('Redis ownership receipts disagree; retirement refused; receipts retained');
|
|
169
|
-
const live = () => survivingOwnedGroups([pid]).length > 0;
|
|
170
|
-
for (const [signal, limit] of [['SIGTERM', COMMAND_GRACE_MS], ['SIGKILL', COMMAND_CONFIRM_MS]] as const) {
|
|
171
|
-
if (!live()) break;
|
|
172
|
-
await signalOwnedProcess(identity.value, signal, true);
|
|
173
|
-
const deadline = Date.now() + limit;
|
|
174
|
-
while (live() && Date.now() < deadline) await sleep(20);
|
|
175
|
-
}
|
|
176
|
-
if (live()) throw new Error('Redis retirement unconfirmed; receipts retained');
|
|
177
|
-
}
|
|
178
|
-
rmSync(pidFile, { force: true });
|
|
179
|
-
rmSync(spawnReceiptPath(pidFile), { force: true });
|
|
180
|
-
hosts.delete(pidFile);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
59
|
+
/** The redis twin's cli: installed beside this runtime (its node_modules walk), else beside the World's config. */
|
|
60
|
+
export const redisTwinCli = (from: string[]): string | undefined => { const found = managedTwin('redis', REDIS_PROTOCOL); return found && twinBackingCli(found, from); };
|
|
61
|
+
/** Where the service's keys live: the World's tree for the `redis` service, under the World's data. */
|
|
62
|
+
export const redisTwinRoot = (dataDir: string): string => join(dataDir, managedDirectory('redis'));
|
|
63
|
+
export const redisTwinUp = (services: InfraService[], dataDir: string, cli: string, launch: HostLaunch = {}): Promise<void> => twinBackingUp(twin(), services, dataDir, cli, launch);
|
|
64
|
+
export const redisTwinStatus = (services: InfraService[], dataDir: string): Promise<number> => twinBackingStatus(twin(), services, dataDir);
|
|
65
|
+
export const redisTwinDown = (services: InfraService[], dataDir: string): Promise<void> => twinBackingDown(twin(), services, dataDir);
|