@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.
Files changed (140) hide show
  1. package/dist/src/ancestry.js +2 -2
  2. package/dist/src/app-url.d.ts +5 -1
  3. package/dist/src/app-url.js +14 -1
  4. package/dist/src/branch.js +28 -8
  5. package/dist/src/browser-files.d.ts +5 -0
  6. package/dist/src/browser-files.js +14 -0
  7. package/dist/src/browsers.d.ts +2 -3
  8. package/dist/src/browsers.js +4 -4
  9. package/dist/src/catalog.d.ts +2 -1
  10. package/dist/src/catalog.js +3 -2
  11. package/dist/src/changeset.js +1 -1
  12. package/dist/src/cli-commands.js +71 -18
  13. package/dist/src/cli-load-clock.d.ts +1 -0
  14. package/dist/src/cli-load-clock.js +2 -0
  15. package/dist/src/cli.js +5 -1
  16. package/dist/src/clickhouse-backing.d.ts +2 -0
  17. package/dist/src/clickhouse-backing.js +145 -0
  18. package/dist/src/command-lifetime.d.mts +1 -1
  19. package/dist/src/command-lifetime.mjs +5 -2
  20. package/dist/src/configs.js +1 -1
  21. package/dist/src/consumers.js +1 -1
  22. package/dist/src/covers.d.ts +4 -2
  23. package/dist/src/covers.js +8 -4
  24. package/dist/src/go-mysql-binding.d.ts +19 -0
  25. package/dist/src/go-mysql-binding.js +51 -0
  26. package/dist/src/guest-processes.d.mts +1 -1
  27. package/dist/src/guest-processes.mjs +22 -14
  28. package/dist/src/host-cli.js +8 -1
  29. package/dist/src/host-ready.d.ts +16 -2
  30. package/dist/src/host-ready.js +66 -39
  31. package/dist/src/host-worker.js +7 -1
  32. package/dist/src/host.d.ts +26 -0
  33. package/dist/src/host.js +93 -3
  34. package/dist/src/infra-definition.d.ts +35 -0
  35. package/dist/src/infra-definition.js +146 -0
  36. package/dist/src/infra.d.ts +3 -0
  37. package/dist/src/infra.js +375 -105
  38. package/dist/src/init.d.ts +46 -0
  39. package/dist/src/init.js +445 -18
  40. package/dist/src/lifecycle-record.d.ts +5 -1
  41. package/dist/src/lifecycle-record.js +27 -4
  42. package/dist/src/local-branches.js +1 -1
  43. package/dist/src/mariadb-bootstrap.d.ts +32 -0
  44. package/dist/src/mariadb-bootstrap.js +90 -0
  45. package/dist/src/mariadb-supplier.d.ts +23 -0
  46. package/dist/src/mariadb-supplier.js +215 -0
  47. package/dist/src/mongodb-backing.d.ts +4 -0
  48. package/dist/src/mongodb-backing.js +71 -1
  49. package/dist/src/native-mariadb-backing.d.ts +13 -0
  50. package/dist/src/native-mariadb-backing.js +512 -0
  51. package/dist/src/native-postgres-backing.d.ts +4 -10
  52. package/dist/src/native-postgres-backing.js +428 -43
  53. package/dist/src/native-server-inputs.d.ts +16 -0
  54. package/dist/src/native-server-inputs.js +204 -0
  55. package/dist/src/origin.js +3 -3
  56. package/dist/src/original-inputs.d.ts +14 -0
  57. package/dist/src/original-inputs.js +84 -0
  58. package/dist/src/pack-facts.d.ts +9 -1
  59. package/dist/src/pglite-backing.d.ts +41 -17
  60. package/dist/src/pglite-backing.js +213 -79
  61. package/dist/src/pglite-host.mjs +482 -10
  62. package/dist/src/process-groups.d.mts +6 -1
  63. package/dist/src/process-groups.d.ts +2 -1
  64. package/dist/src/process-groups.js +1 -1
  65. package/dist/src/process-groups.mjs +82 -15
  66. package/dist/src/process-signal.d.mts +2 -2
  67. package/dist/src/process-signal.mjs +29 -9
  68. package/dist/src/project-inspect.d.ts +12 -0
  69. package/dist/src/project-inspect.js +41 -4
  70. package/dist/src/proxy-env.d.ts +34 -0
  71. package/dist/src/proxy-env.js +114 -0
  72. package/dist/src/redirect-proxy.d.ts +1 -31
  73. package/dist/src/redirect-proxy.js +3 -112
  74. package/dist/src/redis-backing.d.ts +6 -4
  75. package/dist/src/redis-backing.js +20 -168
  76. package/dist/src/runtime.d.ts +1 -0
  77. package/dist/src/runtime.js +503 -218
  78. package/dist/src/schema.d.ts +1 -0
  79. package/dist/src/schema.js +5 -3
  80. package/dist/src/served-world.js +15 -10
  81. package/dist/src/service-exit.d.ts +3 -2
  82. package/dist/src/service-exit.js +7 -5
  83. package/dist/src/twin-backing.d.ts +47 -0
  84. package/dist/src/twin-backing.js +343 -0
  85. package/dist/src/up-cli.js +56 -3
  86. package/package.json +4 -4
  87. package/src/ancestry.ts +2 -2
  88. package/src/app-url.ts +9 -2
  89. package/src/branch.ts +26 -8
  90. package/src/browser-files.ts +15 -0
  91. package/src/browsers.ts +4 -4
  92. package/src/catalog.ts +4 -3
  93. package/src/changeset.ts +1 -1
  94. package/src/cli-commands.ts +85 -18
  95. package/src/cli-load-clock.ts +2 -0
  96. package/src/cli.ts +5 -1
  97. package/src/clickhouse-backing.ts +70 -0
  98. package/src/command-lifetime.d.mts +1 -1
  99. package/src/command-lifetime.mjs +5 -2
  100. package/src/configs.ts +1 -1
  101. package/src/consumers.ts +1 -1
  102. package/src/covers.ts +9 -4
  103. package/src/go-mysql-binding.ts +49 -0
  104. package/src/guest-processes.d.mts +1 -1
  105. package/src/guest-processes.mjs +22 -14
  106. package/src/host-cli.ts +7 -1
  107. package/src/host-ready.ts +62 -31
  108. package/src/host-worker.ts +6 -2
  109. package/src/host.ts +88 -6
  110. package/src/infra-definition.ts +135 -0
  111. package/src/infra.ts +250 -45
  112. package/src/init.ts +416 -17
  113. package/src/lifecycle-record.ts +18 -4
  114. package/src/local-branches.ts +1 -1
  115. package/src/mariadb-bootstrap.ts +107 -0
  116. package/src/mariadb-supplier.ts +199 -0
  117. package/src/mongodb-backing.ts +59 -1
  118. package/src/native-mariadb-backing.ts +418 -0
  119. package/src/native-postgres-backing.ts +353 -41
  120. package/src/native-server-inputs.ts +169 -0
  121. package/src/origin.ts +3 -3
  122. package/src/original-inputs.ts +72 -0
  123. package/src/pack-facts.ts +1 -1
  124. package/src/pglite-backing.ts +192 -80
  125. package/src/pglite-host.mjs +482 -10
  126. package/src/process-groups.d.mts +6 -1
  127. package/src/process-groups.mjs +82 -15
  128. package/src/process-groups.ts +2 -1
  129. package/src/process-signal.d.mts +2 -2
  130. package/src/process-signal.mjs +29 -9
  131. package/src/project-inspect.ts +37 -4
  132. package/src/proxy-env.ts +128 -0
  133. package/src/redirect-proxy.ts +3 -125
  134. package/src/redis-backing.ts +19 -136
  135. package/src/runtime.ts +342 -107
  136. package/src/schema.ts +5 -4
  137. package/src/served-world.ts +14 -10
  138. package/src/service-exit.ts +5 -3
  139. package/src/twin-backing.ts +300 -0
  140. package/src/up-cli.ts +52 -3
@@ -0,0 +1,146 @@
1
+ import { goMysqlBinding, goMysqlEndpoint } from "./go-mysql-binding.js";
2
+ import * as managedOriginalInputs from '@volter/world-core/managed-original-inputs';
3
+ // Definition metadata is independent of database host lifetimes.
4
+ import { createRequire } from 'node:module';
5
+ import { nativeMariaDBReference } from "./mariadb-supplier.js";
6
+ import { mariaDBCredentials } from "./mariadb-bootstrap.js";
7
+ /** Read the generated definition's loopback port mappings and backing-independent options.
8
+ * The Compose extension carries a MongoDB replica-set name. Older generated commands remain readable. */
9
+ export function parseInfraDefinition(text) {
10
+ const { parse } = createRequire(import.meta.url)('yaml');
11
+ const definition = parse(text);
12
+ const services = [];
13
+ for (const [kind, value] of Object.entries(definition?.services ?? {})) {
14
+ const declared = value;
15
+ const nativeReference = declared['x-volter-native-server'] === undefined ? undefined : nativeMariaDBReference(declared['x-volter-native-server']);
16
+ if (nativeReference && (kind !== 'mysql' || declared['x-volter-interfaces'] !== 3 || typeof declared.image !== 'string'
17
+ || !/^(?:docker\.io\/)?(?:library\/)?mariadb(?::[^@]+)?@sha256:[a-f0-9]{64}$/u.test(declared.image)))
18
+ throw new Error('Managed native MariaDB needs mysql, infrastructure interface3 and its exact declared image');
19
+ const originalInputs = declared['x-volter-original-inputs'] === undefined ? undefined : managedOriginalInputs.referenceOf(declared['x-volter-original-inputs']);
20
+ if (originalInputs && declared['x-volter-interfaces'] !== 2)
21
+ throw new Error('Managed original inputs require infrastructure interface2');
22
+ const option = declared['x-volter-replica-set'];
23
+ if (option !== undefined && (kind !== 'mongodb' || typeof option !== 'string' || !/^[A-Za-z][A-Za-z0-9_-]*$/.test(option))) {
24
+ throw new Error('World-managed replica-set metadata must name one valid MongoDB replica set');
25
+ }
26
+ let replicaSet = option;
27
+ if (kind === 'mongodb' && declared.command !== undefined) {
28
+ const command = declared.command;
29
+ if (!Array.isArray(command) || command.length !== 3 || command[0] !== 'mongod' || command[1] !== '--replSet'
30
+ || typeof command[2] !== 'string' || !/^[A-Za-z][A-Za-z0-9_-]*$/.test(command[2])) {
31
+ throw new Error('World-managed MongoDB command must be ["mongod", "--replSet", "<name>"] or absent for standalone');
32
+ }
33
+ if (replicaSet !== undefined && replicaSet !== command[2])
34
+ throw new Error('World-managed MongoDB command and replica-set metadata disagree');
35
+ replicaSet = command[2];
36
+ }
37
+ const mapped = (declared.ports ?? []).flatMap((port) => {
38
+ const match = typeof port === 'string' ? port.match(/^127\.0\.0\.1:(\d+):(\d+)$/) : null;
39
+ if ((kind === 'clickhouse' || nativeReference) && (!match || ![Number(match[1]), Number(match[2])].every(value => Number.isInteger(value) && value > 0 && value <= 65535)))
40
+ throw new Error(`managed infrastructure ${kind} has an invalid loopback port`);
41
+ // Other kinds retain their earlier reader: unrecognized mappings are skipped.
42
+ return match ? [{ hostPort: Number(match[1]), containerPort: Number(match[2]) }] : [];
43
+ });
44
+ let sides, configuration;
45
+ if (kind === 'clickhouse') {
46
+ if (declared['x-volter-interfaces'] !== (originalInputs ? 2 : 1) || mapped.length !== 2 || mapped[0].containerPort !== 8123 || mapped[1].containerPort !== 9000 || mapped[0].hostPort === mapped[1].hostPort)
47
+ throw new Error('Managed ClickHouse needs named-interface version1, HTTP8123 and native9000 mappings');
48
+ const raw = declared['x-volter-side-ports'];
49
+ const native = raw?.native;
50
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw) || Object.keys(raw).join() !== 'native' || !native || native.hostPort !== mapped[1].hostPort || native.containerPort !== 9000)
51
+ throw new Error('Managed ClickHouse native side metadata differs from its declared port');
52
+ sides = { native: mapped[1] };
53
+ const input = declared['x-volter-configuration'];
54
+ if (!input || typeof input !== 'object' || Array.isArray(input))
55
+ throw new Error('Managed ClickHouse needs explicit operator configuration');
56
+ configuration = input;
57
+ }
58
+ else if (mapped.length !== 1 || declared['x-volter-interfaces'] !== (nativeReference ? 3 : originalInputs ? 2 : undefined) || declared['x-volter-side-ports'] !== undefined || declared['x-volter-configuration'] !== undefined) {
59
+ throw new Error(`managed infrastructure ${kind} needs exactly one declared loopback port and no multiwire metadata`);
60
+ }
61
+ const ports = mapped.map(port => port.hostPort);
62
+ let nativeMariaDB;
63
+ if (nativeReference) {
64
+ if (mapped[0].containerPort !== 3306 || originalInputs || declared.command !== undefined || declared.entrypoint !== undefined)
65
+ throw new Error('Managed native MariaDB has an unconsumed port, original-input, command or entrypoint requirement');
66
+ const volumes = declared.volumes;
67
+ if (volumes !== undefined && (!Array.isArray(volumes) || volumes.length !== 1
68
+ || volumes[0] !== '${VOLTER_WORLD_DATA:?}/native-mariadb-data:/var/lib/mysql'))
69
+ throw new Error('Managed native MariaDB has an unconsumed source mount requirement');
70
+ nativeMariaDB = { hostPort: ports[0], image: declared.image,
71
+ reference: nativeReference, credentials: mariaDBCredentials(declared.environment ?? {}) };
72
+ }
73
+ const virtual = declared['x-volter-virtual-tcp-endpoint'];
74
+ let virtualTcpEndpoint;
75
+ if (virtual !== undefined) {
76
+ if (!virtual || typeof virtual !== 'object' || Array.isArray(virtual) || Object.keys(virtual).sort().join() !== 'host,port,targetPort,version'
77
+ || virtual.version !== 1 || typeof virtual.host !== 'string' || !/^[a-z0-9][a-z0-9-]{0,62}\.world\.volter\.internal$/.test(virtual.host)
78
+ || virtual.port !== mapped[0].containerPort || virtual.targetPort !== mapped[0].hostPort)
79
+ throw new Error(`Managed ${kind} virtual TCP endpoint differs from its owned listener`);
80
+ virtualTcpEndpoint = { version: 1, host: virtual.host, port: Number(virtual.port), targetPort: Number(virtual.targetPort) };
81
+ }
82
+ let postgres;
83
+ if (kind === 'postgres') {
84
+ const environment = declared.environment ?? {};
85
+ const user = environment.POSTGRES_USER ?? 'postgres';
86
+ const database = environment.POSTGRES_DB ?? user;
87
+ const password = environment.POSTGRES_PASSWORD;
88
+ if ([user, database, password].some(value => value !== undefined && (typeof value !== 'string' || !value || /[\r\n\0]|\$\{/.test(value)))) {
89
+ throw new Error('World-managed PGlite requires literal POSTGRES_USER, POSTGRES_DB and POSTGRES_PASSWORD');
90
+ }
91
+ if ([user, database].some(value => Buffer.byteLength(value, 'utf8') > 63))
92
+ throw new Error('World-managed PGlite identifiers must fit PostgreSQL\'s 63-byte name limit');
93
+ postgres = { user: user, database: database, ...(password !== undefined ? { password: password } : {}) };
94
+ }
95
+ const typed = declared['x-volter-issued-dsn-bindings'];
96
+ let connectionBindings;
97
+ if (typed !== undefined) {
98
+ if (kind !== 'mysql' || !typed || typeof typed !== 'object' || Array.isArray(typed))
99
+ throw new Error('Managed DSN bindings need a declared MySQL service');
100
+ connectionBindings = Object.create(null);
101
+ for (const [name, value] of Object.entries(typed)) {
102
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || !value || typeof value !== 'object' || Array.isArray(value))
103
+ throw new Error('Invalid managed DSN binding');
104
+ const row = value;
105
+ const parsed = goMysqlBinding({ ...row, scheme: 'mysql' }, 6, name, mapped[0].containerPort);
106
+ if (!parsed.dsnFormat || Object.keys(row).some(key => !['dsnFormat', 'endpoint', 'dsnPrefix', 'dsnProtocol', 'containerPort', 'query'].includes(key)))
107
+ throw new Error(`Invalid managed DSN declaration for ${name}`);
108
+ connectionBindings[name] = { ...parsed, containerPort: mapped[0].containerPort };
109
+ }
110
+ }
111
+ services.push({ kind, ...(nativeMariaDB ? { nativeMariaDB } : {}), ...(virtualTcpEndpoint ? { virtualTcpEndpoint } : {}), ...(connectionBindings ? { connectionBindings } : {}), ...(originalInputs ? { originalInputs } : {}), hostPort: ports[0], ...(sides ? { sides } : {}), ...(configuration ? { configuration } : {}), ...(replicaSet ? { replicaSet } : {}), ...(postgres ? { postgres } : {}) });
112
+ }
113
+ return services;
114
+ }
115
+ /** Endpoints of the declared infrastructure, published through external.discover. Only URLs
116
+ * whose protocol and loopback port match a declared service belong to this lifecycle. */
117
+ export function infraConnections(services, env) {
118
+ const connections = {};
119
+ for (const [name, value] of Object.entries(env)) {
120
+ if (value === undefined)
121
+ continue;
122
+ const typed = services.filter(service => service.connectionBindings?.[name] !== undefined);
123
+ if (typed.length > 1)
124
+ throw new Error(`Managed DSN ${name} belongs to multiple infrastructure services`);
125
+ if (typed.length === 1) {
126
+ const service = typed[0], endpoint = goMysqlEndpoint(value, service.connectionBindings[name], service.connectionBindings[name].containerPort);
127
+ const virtual = service.virtualTcpEndpoint, binding = service.connectionBindings[name];
128
+ const expectedHost = binding.endpoint === 'default-port' && virtual ? virtual.host : '127.0.0.1';
129
+ const expectedPort = binding.endpoint === 'default-port' && virtual ? virtual.port : service.hostPort;
130
+ if (!endpoint || endpoint.host !== expectedHost || endpoint.port !== expectedPort)
131
+ throw new Error(`Managed DSN ${name} does not match its owned endpoint`);
132
+ connections[name] = value;
133
+ continue;
134
+ }
135
+ try {
136
+ const url = new URL(value);
137
+ if (url.hostname !== '127.0.0.1')
138
+ continue;
139
+ const kind = url.protocol === 'postgresql:' ? 'postgres' : url.protocol.slice(0, -1);
140
+ if (services.some((service) => (service.kind === kind && service.hostPort === Number(url.port)) || (service.kind === 'clickhouse' && ((url.protocol === 'http:' && service.hostPort === Number(url.port)) || (url.protocol === 'clickhouse:' && service.sides?.native?.hostPort === Number(url.port))))))
141
+ connections[name] = value;
142
+ }
143
+ catch { /* not an infrastructure endpoint */ }
144
+ }
145
+ return connections;
146
+ }
@@ -1,6 +1,9 @@
1
1
  export interface InfrastructureIo {
2
2
  out: (text: string) => void;
3
3
  err: (text: string) => void;
4
+ /** The runtime's own in-process `up` (runtime.ts): handed the status its `status` would print, when this `up` itself
5
+ * proved everything that status proves (every service twin-served, each held and serving). */
6
+ verified?: (status: string) => void;
4
7
  }
5
8
  /** One phase of the World-managed infrastructure. `env` is the infrastructure service's environment, which the
6
9
  * services this starts run in; `signal` is the boot's abort (the World's own up passes it; the tool has none). */