@volter/world-runtime 3.0.162 → 3.0.164
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 +3 -2
- 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 +445 -18
- 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 +15 -10
- package/dist/src/service-exit.d.ts +3 -2
- package/dist/src/service-exit.js +7 -5
- 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 +4 -4
- 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 +4 -3
- 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 +416 -17
- 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 +14 -10
- package/src/service-exit.ts +5 -3
- package/src/twin-backing.ts +300 -0
- package/src/up-cli.ts +52 -3
package/dist/src/infra.js
CHANGED
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
// that owns the World's lifetime, as every other service's recorder is, and not of a tool that exits once they
|
|
8
8
|
// serve. A process layer where a child does not outlive its parent (a browser tab's) otherwise ended the PGlite host
|
|
9
9
|
// the moment `volter-world-infra up` returned, and the World's readiness probe never saw it.
|
|
10
|
-
import { accessSync, constants, existsSync, readFileSync, statSync } from 'node:fs';
|
|
10
|
+
import { accessSync, constants, existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
11
11
|
import { delimiter, dirname, join } from 'node:path';
|
|
12
|
+
import { setWorldEventsFile, worldEvent } from "./process-groups.js";
|
|
12
13
|
import { signalChild } from './process-signal.mjs';
|
|
13
14
|
import { spawn } from 'node:child_process';
|
|
14
15
|
import { fileURLToPath } from 'node:url';
|
|
@@ -142,11 +143,48 @@ export async function runInfrastructure(phase, env, io, signal) {
|
|
|
142
143
|
io.err('managed infrastructure: missing World lifecycle context\n');
|
|
143
144
|
return 2;
|
|
144
145
|
}
|
|
146
|
+
// What this command does to a backing's process is said in the World's own log, beside its services' events.
|
|
147
|
+
setWorldEventsFile(join(dirname(worldData), 'logs', 'events.jsonl'));
|
|
148
|
+
const phaseAsked = Date.now();
|
|
149
|
+
// Retirement follows retained ownership even if the definition or supplier
|
|
150
|
+
// can no longer be read. Other infrastructure still reports its own refusal.
|
|
151
|
+
let retainedMariaDB = false;
|
|
152
|
+
if (phase === 'down') {
|
|
153
|
+
try {
|
|
154
|
+
retainedMariaDB = existsSync(join(worldData, 'native-mariadb.owner.json'))
|
|
155
|
+
|| existsSync(join(worldData, 'native-mariadb.owner.json.spawn.json'))
|
|
156
|
+
|| existsSync(join(worldData, 'native-mariadb.starting.json'))
|
|
157
|
+
// install-db can outlive its caller before any server receipt exists.
|
|
158
|
+
// Either half of a helper pair enters retirement; a missing birth
|
|
159
|
+
// token remains an explicit uncertainty in nativeMariaDBDown.
|
|
160
|
+
|| (existsSync(worldData) && readdirSync(worldData).some(name => /^native-mariadb-tool-.*\.json(?:\.spawn\.json)?$/u.test(name)));
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
io.err(`managed MariaDB ownership inventory is unreadable: ${errorMessage(error)}\n`);
|
|
164
|
+
return 1;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (retainedMariaDB) {
|
|
168
|
+
try {
|
|
169
|
+
await (await import("./native-mariadb-backing.js")).nativeMariaDBDown(worldData);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
io.err(`managed MariaDB down failed: ${errorMessage(error)}\n`);
|
|
173
|
+
return 1;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
145
176
|
const definition = join(dirname(worldConfig), 'world.infrastructure.yml');
|
|
146
177
|
if (!existsSync(definition)) {
|
|
147
178
|
io.err('managed infrastructure: declared definition is missing\n');
|
|
148
179
|
return 2;
|
|
149
180
|
}
|
|
181
|
+
const declared = parseInfraDefinition(readFileSync(definition, 'utf8'));
|
|
182
|
+
// Both the CLI and the World's in-process up enter here. Endpoint metadata cannot supply host DNS or a native
|
|
183
|
+
// default-port listener: refuse before choosing or starting any backing when the substrate carrier is absent.
|
|
184
|
+
if (phase === 'up' && declared.some(service => service.virtualTcpEndpoint) && env.VOLTER_WORLD_VIRTUAL_TCP !== '1') {
|
|
185
|
+
io.err('managed infrastructure up: virtual TCP endpoints require the substrate network carrier; native host DNS/default ports are not supplied\n');
|
|
186
|
+
return 2;
|
|
187
|
+
}
|
|
150
188
|
// A development World says so every time it comes up: its use is never silent.
|
|
151
189
|
if (phase === 'up' && developmentMigratesAtRuntime(worldConfig))
|
|
152
190
|
io.out(`${DEVELOPMENT_MIGRATION_NOTICE}\n`);
|
|
@@ -244,7 +282,7 @@ export async function runInfrastructure(phase, env, io, signal) {
|
|
|
244
282
|
const { unsupportedKinds } = await import("./pglite-backing.js");
|
|
245
283
|
const { nativePostgresTools } = await import("./native-postgres-backing.js");
|
|
246
284
|
const postgres = declared.filter(s => s.kind === 'postgres');
|
|
247
|
-
if (postgres.length !== 1 || unsupportedKinds(declared.filter(s => s.kind !== 'postgres' && !twinned.includes(s))).length)
|
|
285
|
+
if (postgres.length !== 1 || unsupportedKinds(declared.filter(s => s.kind !== 'postgres' && !s.nativeMariaDB && !twinned.includes(s))).length)
|
|
248
286
|
return selected;
|
|
249
287
|
const native = existsSync(join(worldData, 'native-postgres-data'));
|
|
250
288
|
const pglite = existsSync(join(worldData, 'pglite-postgres-data'));
|
|
@@ -287,7 +325,7 @@ export async function runInfrastructure(phase, env, io, signal) {
|
|
|
287
325
|
io.err(`managed infrastructure up failed: ${diagnostic(errorMessage(error))}\n`);
|
|
288
326
|
return 1;
|
|
289
327
|
}
|
|
290
|
-
|
|
328
|
+
sayReady();
|
|
291
329
|
return 0;
|
|
292
330
|
}
|
|
293
331
|
if (phase === 'status') {
|
|
@@ -305,18 +343,76 @@ export async function runInfrastructure(phase, env, io, signal) {
|
|
|
305
343
|
io.out('managed infrastructure stopped\n');
|
|
306
344
|
return 0;
|
|
307
345
|
}
|
|
308
|
-
// ----
|
|
309
|
-
// A
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
// the
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const
|
|
316
|
-
|
|
346
|
+
// ---- a kind a twin serves: the twin, containerless --------------------------
|
|
347
|
+
// A service whose kind an installed pack declares (`managedService` in pack facts; twin-backing.ts) is served by
|
|
348
|
+
// that pack on every machine, container runtime or not, unless the operator forces the container backing; the
|
|
349
|
+
// other services take the backing chosen above. Which pack is the pack's declaration, never a name here. Where no
|
|
350
|
+
// pack declares the kind, or the one that does is not installed, the kind goes to that backing too: the container
|
|
351
|
+
// where there is one, else the backing's refusal naming the kind (the vendor's own server for MongoDB).
|
|
352
|
+
// The kinds listed are those whose protocol this runtime can ask its two questions in (ManagedProtocol).
|
|
353
|
+
const mariaDB = declared.flatMap(service => service.nativeMariaDB ? [service.nativeMariaDB] : []);
|
|
354
|
+
if (phase !== 'down' && mariaDB.length && ['docker', 'pglite'].includes(env.VOLTER_WORLD_INFRA_BACKING ?? '')) {
|
|
355
|
+
io.err('managed MariaDB: explicit native selection cannot be replaced by the forced backing\n');
|
|
356
|
+
return 1;
|
|
357
|
+
}
|
|
358
|
+
// Resolve only kinds the definition actually asks for. Looking up a pack before filtering its services made a
|
|
359
|
+
// PostgreSQL-only World scan installed Redis/MongoDB packs and their CLI graphs on every infrastructure phase.
|
|
360
|
+
const kinds = ['redis', 'mongodb', 'clickhouse'].filter(kind => declared.some(service => service.kind === kind));
|
|
361
|
+
const managed = env.VOLTER_WORLD_INFRA_BACKING === 'docker' || kinds.length === 0
|
|
362
|
+
? undefined : await import("./twin-backing.js");
|
|
363
|
+
const protocols = {};
|
|
364
|
+
if (managed) {
|
|
365
|
+
if (kinds.includes('redis'))
|
|
366
|
+
protocols.redis = (await import("./redis-backing.js")).REDIS_PROTOCOL;
|
|
367
|
+
if (kinds.includes('mongodb'))
|
|
368
|
+
protocols.mongodb = (await import("./mongodb-backing.js")).MONGODB_PROTOCOL;
|
|
369
|
+
if (kinds.includes('clickhouse'))
|
|
370
|
+
protocols.clickhouse = (await import("./clickhouse-backing.js")).CLICKHOUSE_PROTOCOL;
|
|
371
|
+
}
|
|
372
|
+
const expectedTwinPackages = new Map();
|
|
373
|
+
const twins = !managed ? [] : kinds.flatMap((kind) => {
|
|
374
|
+
const services = declared.filter((s) => s.kind === kind);
|
|
375
|
+
const beside = [dirname(fileURLToPath(import.meta.url)), dirname(worldConfig)];
|
|
376
|
+
const twin = managed.managedTwin(kind, protocols[kind], beside, phase !== 'down' && services.some(service => service.originalInputs !== undefined));
|
|
377
|
+
if (twin)
|
|
378
|
+
expectedTwinPackages.set(kind, twin.package);
|
|
379
|
+
const cli = twin && managed.twinBackingCli(twin, beside);
|
|
380
|
+
return twin && (cli || phase === 'down') ? [{ twin, cli: cli ?? '', services }] : [];
|
|
381
|
+
});
|
|
382
|
+
const twinned = twins.flatMap((served) => served.services);
|
|
383
|
+
// A declared original-input carrier cannot fall through to a backing that drops it.
|
|
384
|
+
// Down still retires established owners even when a pack or source snapshot is unavailable.
|
|
385
|
+
if (phase !== 'down') {
|
|
386
|
+
try {
|
|
387
|
+
for (const service of declared)
|
|
388
|
+
if (service.originalInputs) {
|
|
389
|
+
const consumer = twins.find(entry => entry.services.includes(service));
|
|
390
|
+
if (!managed || !consumer)
|
|
391
|
+
throw new Error(`managed ${service.kind} has original inputs and no installed declaring consumer`);
|
|
392
|
+
managed.originalInputArguments(consumer.twin, service, worldConfig);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
io.err(`managed infrastructure ${phase} refused original inputs: ${diagnostic(errorMessage(error))}\n`);
|
|
397
|
+
return 1;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
317
400
|
/** The services the backing below answers for: every one, or those the twin does not serve (by compose service name). */
|
|
318
|
-
const native = declared.filter((s) => s.kind === 'mongodb');
|
|
319
|
-
|
|
401
|
+
const native = declared.filter((s) => s.kind === 'mongodb' && !twinned.includes(s));
|
|
402
|
+
// Tab hosts expose their kernel tree on this symbol (world-store.ts). Redis/MongoDB's managed kinds need
|
|
403
|
+
// their installed declaring twins there; a missing pack is an input refusal, never a native-server attempt.
|
|
404
|
+
// PostgreSQL is separate: its available WALI server tools or PGlite still own that declared backing.
|
|
405
|
+
const inTab = process[Symbol.for('tabnode.run.vfs')] !== undefined;
|
|
406
|
+
if (inTab && phase !== 'down') {
|
|
407
|
+
const missing = kinds.filter(kind => !twinned.some(service => service.kind === kind)
|
|
408
|
+
&& (env.VOLTER_WORLD_INFRA_BACKING !== 'docker' || native.some(service => service.kind === kind)));
|
|
409
|
+
if (missing.length) {
|
|
410
|
+
for (const kind of missing)
|
|
411
|
+
io.err(`managed ${kind} needs ${expectedTwinPackages.get(kind) ?? `@volter/twin-${kind}`} installed in this tab World\n`);
|
|
412
|
+
return 1;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
const composed = twinned.length === 0 && native.length === 0 && mariaDB.length === 0 ? null : declared.filter((s) => !twinned.includes(s) && !native.includes(s) && !s.nativeMariaDB).map((s) => s.kind);
|
|
320
416
|
// A database's native wire and storage are its vendor server's. MongoDB never needs a Docker probe or container.
|
|
321
417
|
if (native.length > 0) {
|
|
322
418
|
const { pgliteUp, pgliteStatus, pgliteDown } = await import("./pglite-backing.js");
|
|
@@ -339,119 +435,293 @@ export async function runInfrastructure(phase, env, io, signal) {
|
|
|
339
435
|
return 1;
|
|
340
436
|
}
|
|
341
437
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
438
|
+
// THE TWIN-SERVED KINDS WAIT FOR THE DATABASE'S CLUSTER PLACEMENT. A store staged with a prepared cluster is placed where its
|
|
439
|
+
// twin serves from, and only beside a database that carries the same migration (takePreparedStore). Brought up
|
|
440
|
+
// first, a twin's store was placed before the database's backing was chosen, and where that backing then
|
|
441
|
+
// declined the prepared cluster (its programs' stamp, PGlite serving a native cluster) the World had an
|
|
442
|
+
// unprepared database and a prepared store. On `up` with the native backing, its onPlaced starts the twins once
|
|
443
|
+
// the cluster is placed, beside readiness; otherwise they start after the database. The live cluster is then
|
|
444
|
+
// there to be read; `status` and `down` keep their order. The ready line is said once,
|
|
445
|
+
// after both.
|
|
446
|
+
/** What `status` prints when every declared service is twin-served: one fact, said by status and handed over by up. */
|
|
447
|
+
const twinServedStatus = () => ({ ok: true, services: declared.length, connections: infraConnections(declared, env), ...databaseFacts() });
|
|
448
|
+
/** The native backing's status, its proof asked as status asks it: set by the native branch's `up` once it is ready. */
|
|
449
|
+
let nativeProof;
|
|
450
|
+
/** What this `up` has proved, handed to the runtime's own in-process up (InfrastructureIo.verified) as the status its
|
|
451
|
+
* `status` would print, its proofs asked again now: every twin held (twinBackingHeld), and then either every service
|
|
452
|
+
* twin-served or, with no MongoDB native, the native backing's own status proof (nativeStatus: the containerless
|
|
453
|
+
* others serving, pgliteStatus, and the postmaster's, nativePostgresStatus). It does not repeat status's choice of
|
|
454
|
+
* backing (selectBacking, a `docker info` probe): the backing is the one this up just brought up. Any doubt hands
|
|
455
|
+
* nothing over, and the runtime's probe runs. */
|
|
456
|
+
const handOver = async () => {
|
|
457
|
+
if (!io.verified || phase !== 'up' || native.length !== 0)
|
|
458
|
+
return;
|
|
459
|
+
for (const { twin, services } of twins)
|
|
460
|
+
if (!await managed.twinBackingHeld(twin, services, worldData))
|
|
461
|
+
return;
|
|
462
|
+
if (mariaDB.length && !await (await import("./native-mariadb-backing.js")).nativeMariaDBStatus(mariaDB[0], worldData, worldConfig, launch))
|
|
463
|
+
return;
|
|
464
|
+
if (composed?.length === 0) {
|
|
465
|
+
io.verified(`${JSON.stringify(twinServedStatus())}\n`);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if (!nativeProof)
|
|
469
|
+
return;
|
|
470
|
+
let text;
|
|
471
|
+
try {
|
|
472
|
+
text = await nativeProof();
|
|
473
|
+
}
|
|
474
|
+
catch {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
if (text)
|
|
478
|
+
io.verified(text);
|
|
479
|
+
};
|
|
480
|
+
let twinsPending = false;
|
|
481
|
+
let readyHeld = false;
|
|
482
|
+
// THE TWINS COME UP BESIDE THE DATABASE'S READINESS (S2 R79), not after it: Twenty's redis twin began only when the
|
|
483
|
+
// postmaster answered (+1.36 s) and held up 330 ms more. 840a1ae3's rule stands: a twin's staged store is placed beside
|
|
484
|
+
// the cluster the backing chose, so the twins start no earlier than the native backing's onPlaced, said after the
|
|
485
|
+
// cluster is placed (or made by initdb) and its server spawned. Every other backing still runs them after it returns.
|
|
486
|
+
let twinsBeside;
|
|
487
|
+
// A twin that fails beside the readiness wait ends that wait (its signal), so the up fails then, not at the
|
|
488
|
+
// database's readiness or deadline: the native start retires its postmaster as on any abort, and the failed-database
|
|
489
|
+
// path below brings the rest down.
|
|
490
|
+
const twinFailed = new AbortController();
|
|
491
|
+
const startTwinsBeside = () => {
|
|
492
|
+
if (!twinsPending || twinsBeside)
|
|
493
|
+
return;
|
|
494
|
+
io.out('managed infrastructure: twins started beside the database\'s readiness (its cluster placed)\n');
|
|
495
|
+
twinsBeside = twinsPhase().then((code) => {
|
|
496
|
+
if (code !== 0)
|
|
497
|
+
twinFailed.abort(new Error('a twin failed beside the database\'s readiness, so its start was ended'));
|
|
498
|
+
return code;
|
|
499
|
+
});
|
|
500
|
+
};
|
|
501
|
+
const sayReady = () => { if (twinsPending)
|
|
502
|
+
readyHeld = true;
|
|
503
|
+
else
|
|
504
|
+
io.out('managed infrastructure ready\n'); };
|
|
505
|
+
const twinsPhase = async () => {
|
|
506
|
+
if (!managed)
|
|
507
|
+
return 0;
|
|
508
|
+
for (const { twin, cli, services } of twins) {
|
|
509
|
+
if (phase === 'up') {
|
|
510
|
+
io.out(`managed infrastructure backing: ${twin.id} twin (containerless)\n`);
|
|
511
|
+
try {
|
|
512
|
+
await managed.twinBackingUp(twin, services, worldData, cli, launch);
|
|
513
|
+
}
|
|
514
|
+
catch (error) {
|
|
515
|
+
io.err(`managed infrastructure up failed: ${diagnostic(errorMessage(error))}\n`);
|
|
516
|
+
return 1;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
else if (phase === 'status') {
|
|
520
|
+
const ready = await managed.twinBackingStatus(twin, services, worldData);
|
|
521
|
+
if (ready !== services.length) {
|
|
522
|
+
io.err(`managed infrastructure status failed: ${ready}/${declared.length} declared services are ready\n`);
|
|
523
|
+
return 1;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
await managed.twinBackingDown(twin, services, worldData);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return 0;
|
|
531
|
+
};
|
|
532
|
+
if (phase === 'up' && twins.length > 0)
|
|
533
|
+
twinsPending = true;
|
|
534
|
+
else {
|
|
535
|
+
const code = await twinsPhase();
|
|
536
|
+
if (code !== 0)
|
|
537
|
+
return code;
|
|
538
|
+
}
|
|
539
|
+
const databasePhase = async () => {
|
|
540
|
+
if (mariaDB.length) {
|
|
541
|
+
const { nativeMariaDBUp, nativeMariaDBStatus, nativeMariaDBDown } = await import("./native-mariadb-backing.js");
|
|
542
|
+
if (mariaDB.length !== 1) {
|
|
543
|
+
io.err('managed MariaDB needs one explicit native provider per World\n');
|
|
544
|
+
return 1;
|
|
545
|
+
}
|
|
347
546
|
try {
|
|
348
|
-
|
|
547
|
+
if (phase === 'up') {
|
|
548
|
+
io.out(`managed infrastructure backing: native MariaDB ${mariaDB[0].reference.release}, exact declared provider\n`);
|
|
549
|
+
await nativeMariaDBUp(mariaDB[0], worldData, worldConfig, launch);
|
|
550
|
+
}
|
|
551
|
+
else if (phase === 'status') {
|
|
552
|
+
if (!await nativeMariaDBStatus(mariaDB[0], worldData, worldConfig, launch)) {
|
|
553
|
+
io.err('managed MariaDB status: owned server, installed supply or authenticated readiness is not confirmed\n');
|
|
554
|
+
return 1;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
else
|
|
558
|
+
await nativeMariaDBDown(worldData);
|
|
349
559
|
}
|
|
350
560
|
catch (error) {
|
|
351
|
-
io.err(`managed
|
|
561
|
+
io.err(`managed MariaDB ${phase} failed: ${diagnostic(errorMessage(error))}\n`);
|
|
352
562
|
return 1;
|
|
353
563
|
}
|
|
354
564
|
}
|
|
565
|
+
if (composed?.length === 0) {
|
|
566
|
+
if (phase === 'up')
|
|
567
|
+
sayReady();
|
|
568
|
+
else if (phase === 'status')
|
|
569
|
+
io.out(`${JSON.stringify(twinServedStatus())}\n`);
|
|
570
|
+
else
|
|
571
|
+
io.out('managed infrastructure stopped\n');
|
|
572
|
+
return 0;
|
|
573
|
+
}
|
|
574
|
+
// How long the choice of backing took, and how far into the phase it was made (S2, Twenty's World up): Twenty's up spent 220 ms between
|
|
575
|
+
// its infrastructure starting beside the host and the backing's line, none of it said.
|
|
576
|
+
const choosing = Date.now();
|
|
577
|
+
const backing = await containerlessPostgres(await selectBacking());
|
|
578
|
+
const chosen = `backing chosen in ${Date.now() - choosing} ms, ${Date.now() - phaseAsked} ms into the phase`;
|
|
579
|
+
if (typeof backing === 'number')
|
|
580
|
+
return backing;
|
|
581
|
+
if (backing === 'pglite')
|
|
582
|
+
return runPglite();
|
|
583
|
+
if (backing === 'native') {
|
|
584
|
+
const { nativePostgresUp, nativePostgresStatus, nativePostgresDown } = await import("./native-postgres-backing.js");
|
|
585
|
+
const postgres = declared.filter(s => s.kind === 'postgres');
|
|
586
|
+
const others = declared.filter(s => s.kind !== 'postgres' && !s.nativeMariaDB && !twinned.includes(s));
|
|
587
|
+
const { unsupportedKinds, pgliteUp, pgliteStatus, pgliteDown } = await import("./pglite-backing.js");
|
|
588
|
+
if (postgres.length !== 1 || unsupportedKinds(others).length) {
|
|
589
|
+
io.err('managed infrastructure native backing requires one postgres and only supported containerless services alongside it\n');
|
|
590
|
+
return 1;
|
|
591
|
+
}
|
|
592
|
+
/** The native backing's status: its containerless others serving and the postmaster's own proof (ownership, its
|
|
593
|
+
* live group and identity, its port answering), then the status line; undefined when either does not hold. */
|
|
594
|
+
const nativeStatus = async () => {
|
|
595
|
+
const ready = others.length === 0 || (await pgliteStatus(others, worldData)).ok;
|
|
596
|
+
if (!ready || !await nativePostgresStatus(worldData, signal))
|
|
597
|
+
return undefined;
|
|
598
|
+
postgresData = join(worldData, 'native-postgres-data');
|
|
599
|
+
return `${JSON.stringify({ ok: true, services: declared.length, connections: infraConnections(declared, env), ...backingFact('native'), ...databaseFacts() })}\n`;
|
|
600
|
+
};
|
|
601
|
+
try {
|
|
602
|
+
if (phase === 'up') {
|
|
603
|
+
io.out(`managed infrastructure backing: native PostgreSQL (multi-session)${forcedBy('native')} (${chosen})\n`);
|
|
604
|
+
await nativePostgresUp(postgres, worldData, readFileSync(definition, 'utf8'), { ...launch, signal: launch.signal ? AbortSignal.any([launch.signal, twinFailed.signal]) : twinFailed.signal, onPlaced: startTwinsBeside });
|
|
605
|
+
try {
|
|
606
|
+
await pgliteUp(others, worldData, launch);
|
|
607
|
+
}
|
|
608
|
+
catch (error) {
|
|
609
|
+
try {
|
|
610
|
+
await nativePostgresDown(worldData);
|
|
611
|
+
}
|
|
612
|
+
catch (cleanup) {
|
|
613
|
+
throw new AggregateError([error, cleanup], 'managed infrastructure startup and native cleanup failed');
|
|
614
|
+
}
|
|
615
|
+
throw error;
|
|
616
|
+
}
|
|
617
|
+
nativeProof = nativeStatus;
|
|
618
|
+
sayReady();
|
|
619
|
+
}
|
|
620
|
+
else if (phase === 'status') {
|
|
621
|
+
const text = await nativeStatus();
|
|
622
|
+
if (text === undefined)
|
|
623
|
+
return 1;
|
|
624
|
+
io.out(text);
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
await pgliteDown(others, worldData);
|
|
628
|
+
await nativePostgresDown(worldData, 'infra down');
|
|
629
|
+
worldEvent({ event: 'world.infra.down', backing: 'native', ms: Date.now() - phaseAsked });
|
|
630
|
+
io.out('managed infrastructure stopped\n');
|
|
631
|
+
}
|
|
632
|
+
return 0;
|
|
633
|
+
}
|
|
634
|
+
catch (error) {
|
|
635
|
+
io.err(`managed infrastructure ${phase} failed: ${diagnostic(errorMessage(error))}\n`);
|
|
636
|
+
return 1;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
const only = composed ?? [];
|
|
640
|
+
if (phase === 'up') {
|
|
641
|
+
if (forcedBy('docker'))
|
|
642
|
+
io.out(`managed infrastructure backing: container runtime${forcedBy('docker')}\n`);
|
|
643
|
+
const result = await run(['up', '-d', '--wait', ...only]);
|
|
644
|
+
if (result.status !== 0)
|
|
645
|
+
return fail(result);
|
|
646
|
+
sayReady();
|
|
647
|
+
}
|
|
355
648
|
else if (phase === 'status') {
|
|
356
|
-
const
|
|
357
|
-
if (
|
|
358
|
-
|
|
649
|
+
const expected = await run(['config', '--services']);
|
|
650
|
+
if (expected.status !== 0)
|
|
651
|
+
return fail(expected);
|
|
652
|
+
const running = await run(['ps', '--status', 'running', '--services']);
|
|
653
|
+
if (running.status !== 0)
|
|
654
|
+
return fail(running);
|
|
655
|
+
const expectedNames = expected.stdout.split(/\s+/u).filter(Boolean).filter((n) => composed === null || composed.includes(n)).sort();
|
|
656
|
+
const runningNames = running.stdout.split(/\s+/u).filter(Boolean).filter((n) => composed === null || composed.includes(n)).sort();
|
|
657
|
+
if (expectedNames.length === 0 || expectedNames.join('\0') !== runningNames.join('\0')) {
|
|
658
|
+
io.err(`managed infrastructure status failed: ${runningNames.length + twinned.length + native.length}/${expectedNames.length + twinned.length + native.length} declared services are ready\n`);
|
|
359
659
|
return 1;
|
|
360
660
|
}
|
|
661
|
+
io.out(`${JSON.stringify({ ok: true, services: expectedNames.length + twinned.length + native.length, connections: infraConnections(declared, env), ...backingFact('docker'), ...databaseFacts() })}\n`);
|
|
361
662
|
}
|
|
362
663
|
else {
|
|
363
|
-
await
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
if (composed?.length === 0) {
|
|
367
|
-
if (phase === 'up')
|
|
368
|
-
io.out('managed infrastructure ready\n');
|
|
369
|
-
else if (phase === 'status')
|
|
370
|
-
io.out(`${JSON.stringify({ ok: true, services: declared.length, connections: infraConnections(declared, env), ...databaseFacts() })}\n`);
|
|
371
|
-
else
|
|
664
|
+
const result = await run(['down', '--remove-orphans']);
|
|
665
|
+
if (result.status !== 0)
|
|
666
|
+
return fail(result);
|
|
372
667
|
io.out('managed infrastructure stopped\n');
|
|
668
|
+
}
|
|
373
669
|
return 0;
|
|
670
|
+
};
|
|
671
|
+
let code;
|
|
672
|
+
try {
|
|
673
|
+
code = await databasePhase();
|
|
374
674
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
if (backing === 'native') {
|
|
381
|
-
const { nativePostgresUp, nativePostgresStatus, nativePostgresDown } = await import("./native-postgres-backing.js");
|
|
382
|
-
const postgres = declared.filter(s => s.kind === 'postgres');
|
|
383
|
-
const others = declared.filter(s => s.kind !== 'postgres' && !twinned.includes(s));
|
|
384
|
-
const { unsupportedKinds, pgliteUp, pgliteStatus, pgliteDown } = await import("./pglite-backing.js");
|
|
385
|
-
if (postgres.length !== 1 || unsupportedKinds(others).length) {
|
|
386
|
-
io.err('managed infrastructure native backing requires one postgres and only supported containerless services alongside it\n');
|
|
387
|
-
return 1;
|
|
388
|
-
}
|
|
389
|
-
try {
|
|
390
|
-
if (phase === 'up') {
|
|
391
|
-
io.out(`managed infrastructure backing: native PostgreSQL (multi-session)${forcedBy('native')}\n`);
|
|
392
|
-
await nativePostgresUp(postgres, worldData, readFileSync(definition, 'utf8'), launch);
|
|
393
|
-
try {
|
|
394
|
-
await pgliteUp(others, worldData, launch);
|
|
395
|
-
}
|
|
396
|
-
catch (error) {
|
|
397
|
-
try {
|
|
398
|
-
await nativePostgresDown(worldData);
|
|
399
|
-
}
|
|
400
|
-
catch (cleanup) {
|
|
401
|
-
throw new AggregateError([error, cleanup], 'managed infrastructure startup and native cleanup failed');
|
|
402
|
-
}
|
|
403
|
-
throw error;
|
|
404
|
-
}
|
|
405
|
-
io.out('managed infrastructure ready\n');
|
|
406
|
-
}
|
|
407
|
-
else if (phase === 'status') {
|
|
408
|
-
const ready = others.length === 0 || (await pgliteStatus(others, worldData)).ok;
|
|
409
|
-
if (!ready || !await nativePostgresStatus(worldData))
|
|
410
|
-
return 1;
|
|
411
|
-
postgresData = join(worldData, 'native-postgres-data');
|
|
412
|
-
io.out(`${JSON.stringify({ ok: true, services: declared.length, connections: infraConnections(declared, env), ...backingFact('native'), ...databaseFacts() })}\n`);
|
|
675
|
+
catch (error) {
|
|
676
|
+
if (phase === 'up' && mariaDB.length) {
|
|
677
|
+
try {
|
|
678
|
+
if (await runInfrastructure('down', env, { out: () => { }, err: io.err }) !== 0)
|
|
679
|
+
throw new Error('managed infrastructure failed-start teardown is unconfirmed');
|
|
413
680
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
await nativePostgresDown(worldData);
|
|
417
|
-
io.out('managed infrastructure stopped\n');
|
|
681
|
+
catch (cleanup) {
|
|
682
|
+
throw new AggregateError([error, cleanup], 'native MariaDB sibling startup and retirement failed');
|
|
418
683
|
}
|
|
419
|
-
return 0;
|
|
420
684
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
685
|
+
throw error;
|
|
686
|
+
}
|
|
687
|
+
if (code !== 0 && phase === 'up' && mariaDB.length) {
|
|
688
|
+
try {
|
|
689
|
+
if (await runInfrastructure('down', env, { out: () => { }, err: io.err }) !== 0)
|
|
690
|
+
io.err('managed infrastructure failed-start teardown is unconfirmed; ownership retained\n');
|
|
691
|
+
}
|
|
692
|
+
catch (cleanup) {
|
|
693
|
+
io.err(`managed MariaDB failed-start retirement: ${diagnostic(errorMessage(cleanup))}\n`);
|
|
424
694
|
}
|
|
425
695
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
const result = await run(['up', '-d', '--wait', ...only]);
|
|
431
|
-
if (result.status !== 0)
|
|
432
|
-
return fail(result);
|
|
433
|
-
io.out('managed infrastructure ready\n');
|
|
696
|
+
if (!twinsPending) {
|
|
697
|
+
if (code === 0)
|
|
698
|
+
await handOver();
|
|
699
|
+
return code;
|
|
434
700
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
io.err(`managed infrastructure status failed: ${runningNames.length + twinned.length + native.length}/${expectedNames.length + twinned.length + native.length} declared services are ready\n`);
|
|
446
|
-
return 1;
|
|
701
|
+
twinsPending = false;
|
|
702
|
+
if (code !== 0) {
|
|
703
|
+
// A database that failed after its twins began: they finish starting or fail (each retires its own half-started
|
|
704
|
+
// child, twinBackingUp), then come down with the rest, as when a twin fails, so no twin's process or receipt stays.
|
|
705
|
+
if (twinsBeside) {
|
|
706
|
+
await twinsBeside;
|
|
707
|
+
try {
|
|
708
|
+
await runInfrastructure('down', env, { out: () => { }, err: io.err }, signal);
|
|
709
|
+
}
|
|
710
|
+
catch { /* the failure above is the one to report */ }
|
|
447
711
|
}
|
|
448
|
-
|
|
712
|
+
return code;
|
|
449
713
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
714
|
+
const twinsCode = await (twinsBeside ?? twinsPhase());
|
|
715
|
+
if (twinsCode !== 0) {
|
|
716
|
+
// The database came up and a twin did not: nothing of this `up` is left serving.
|
|
717
|
+
try {
|
|
718
|
+
await runInfrastructure('down', env, { out: () => { }, err: io.err }, signal);
|
|
719
|
+
}
|
|
720
|
+
catch { /* the failure above is the one to report */ }
|
|
721
|
+
return twinsCode;
|
|
455
722
|
}
|
|
723
|
+
await handOver();
|
|
724
|
+
if (readyHeld)
|
|
725
|
+
io.out('managed infrastructure ready\n');
|
|
456
726
|
return 0;
|
|
457
727
|
}
|
package/dist/src/init.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { type NativeServerInputs } from './native-server-inputs.js';
|
|
2
|
+
import { type GoMysqlBinding } from './go-mysql-binding.js';
|
|
3
|
+
import { type OriginalInputReference } from './original-inputs.js';
|
|
1
4
|
import { type CoverageReport } from './covers.js';
|
|
2
5
|
import { type ProjectMigrationTool } from './project-inspect.js';
|
|
3
6
|
import { type WorldConfig, type WorldServiceConfig } from './schema.js';
|
|
@@ -84,9 +87,31 @@ export type InitInfraStub = {
|
|
|
84
87
|
export type InitComposeService = {
|
|
85
88
|
kind: string;
|
|
86
89
|
image: string;
|
|
90
|
+
/** An explicit MongoDB connection mode, also carried to the selected backing. */
|
|
91
|
+
replicaSet?: string;
|
|
87
92
|
/** deterministic host port, derived from the world name (see composePort) */
|
|
88
93
|
hostPort: number;
|
|
89
94
|
containerPort: number;
|
|
95
|
+
virtualTcpEndpoint?: {
|
|
96
|
+
version: 1;
|
|
97
|
+
host: string;
|
|
98
|
+
port: number;
|
|
99
|
+
targetPort: number;
|
|
100
|
+
};
|
|
101
|
+
connectionBindings?: Record<string, GoMysqlBinding & {
|
|
102
|
+
containerPort: number;
|
|
103
|
+
query?: string;
|
|
104
|
+
}>;
|
|
105
|
+
sides?: Record<string, {
|
|
106
|
+
hostPort: number;
|
|
107
|
+
containerPort: number;
|
|
108
|
+
}>;
|
|
109
|
+
configuration?: Record<string, unknown>;
|
|
110
|
+
originalInputs?: OriginalInputReference;
|
|
111
|
+
originalInputsFrom?: string;
|
|
112
|
+
nativeServerInputs?: NativeServerInputs;
|
|
113
|
+
nativeServerInputsFrom?: string;
|
|
114
|
+
nativeCredentialAliases?: string[];
|
|
90
115
|
memoryMiB: number;
|
|
91
116
|
/** compose volume key, or null (the on-disk docker volume is `<project>_<key>` — the compose
|
|
92
117
|
* project name carries the world name, so volumes are named per world) */
|
|
@@ -118,6 +143,12 @@ export type InitPlan = {
|
|
|
118
143
|
* (runtime.database.prepare): reported, never written, and no part of the vendor-coverage verdict */
|
|
119
144
|
undeclaredMigrations: ProjectMigrationTool[];
|
|
120
145
|
infra: InitInfraStub[];
|
|
146
|
+
/** each service of the project's own stack and what was made of it, where `--needs` gave the stack */
|
|
147
|
+
stackNeeds?: InitStackNeed[];
|
|
148
|
+
/** The same declared application names used by selection and its coverage proof. */
|
|
149
|
+
stackEnvironmentNames?: string[];
|
|
150
|
+
/** the project files a World made for an image left unread: the source does not hold them, so they are the person's own */
|
|
151
|
+
leftAsThePersonsOwn?: string[];
|
|
121
152
|
/** the services emitted into the private managed-infrastructure definition */
|
|
122
153
|
compose: InitComposeService[];
|
|
123
154
|
/** private managed-infrastructure definition, or null when no detected infra has a recipe */
|
|
@@ -195,6 +226,21 @@ export type InitOptions = {
|
|
|
195
226
|
sources?: Record<string, string>;
|
|
196
227
|
/** A world with no app: served as `/<org>/<world>/`. */
|
|
197
228
|
bare?: string;
|
|
229
|
+
/** The project's own stack as its image builder read it (`needs.json`): architecture, "The project's own stack as evidence". */
|
|
230
|
+
needs?: string;
|
|
231
|
+
};
|
|
232
|
+
/** One service of the project's stack, as `init` took it. */
|
|
233
|
+
export type InitStackNeed = {
|
|
234
|
+
service: string;
|
|
235
|
+
image?: string;
|
|
236
|
+
family?: string;
|
|
237
|
+
/** the managed kind it is, or null where the World has none for it */
|
|
238
|
+
kind: string | null;
|
|
239
|
+
/** the application's variables given this service's address */
|
|
240
|
+
names: string[];
|
|
241
|
+
/** true where the stack named no variable and the conventional names were supplied */
|
|
242
|
+
conventional: boolean;
|
|
243
|
+
note: string;
|
|
198
244
|
};
|
|
199
245
|
/** The twins available at `root`, sorted — a deterministic input (catalog.ts: this checkout's
|
|
200
246
|
* vendor directories in the pack repository, or the `@volter/twin-<vendor>` packages installed above root). */
|