rightsize 0.1.0

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 (216) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +4 -0
  3. package/README.md +255 -0
  4. package/dist/backend-docker/backend.d.ts +53 -0
  5. package/dist/backend-docker/backend.d.ts.map +1 -0
  6. package/dist/backend-docker/backend.js +336 -0
  7. package/dist/backend-docker/backend.js.map +1 -0
  8. package/dist/backend-docker/client.d.ts +82 -0
  9. package/dist/backend-docker/client.d.ts.map +1 -0
  10. package/dist/backend-docker/client.js +190 -0
  11. package/dist/backend-docker/client.js.map +1 -0
  12. package/dist/backend-docker/frames.d.ts +44 -0
  13. package/dist/backend-docker/frames.d.ts.map +1 -0
  14. package/dist/backend-docker/frames.js +105 -0
  15. package/dist/backend-docker/frames.js.map +1 -0
  16. package/dist/backend-docker/index.d.ts +5 -0
  17. package/dist/backend-docker/index.d.ts.map +1 -0
  18. package/dist/backend-docker/index.js +20 -0
  19. package/dist/backend-docker/index.js.map +1 -0
  20. package/dist/backend-docker/json.d.ts +13 -0
  21. package/dist/backend-docker/json.d.ts.map +1 -0
  22. package/dist/backend-docker/json.js +47 -0
  23. package/dist/backend-docker/json.js.map +1 -0
  24. package/dist/backend-docker/labels.d.ts +11 -0
  25. package/dist/backend-docker/labels.d.ts.map +1 -0
  26. package/dist/backend-docker/labels.js +13 -0
  27. package/dist/backend-docker/labels.js.map +1 -0
  28. package/dist/backend-docker/port-conflict.d.ts +9 -0
  29. package/dist/backend-docker/port-conflict.d.ts.map +1 -0
  30. package/dist/backend-docker/port-conflict.js +12 -0
  31. package/dist/backend-docker/port-conflict.js.map +1 -0
  32. package/dist/backend-docker/provider.d.ts +22 -0
  33. package/dist/backend-docker/provider.d.ts.map +1 -0
  34. package/dist/backend-docker/provider.js +36 -0
  35. package/dist/backend-docker/provider.js.map +1 -0
  36. package/dist/backend-msb/backend.d.ts +169 -0
  37. package/dist/backend-msb/backend.d.ts.map +1 -0
  38. package/dist/backend-msb/backend.js +664 -0
  39. package/dist/backend-msb/backend.js.map +1 -0
  40. package/dist/backend-msb/commands.d.ts +28 -0
  41. package/dist/backend-msb/commands.d.ts.map +1 -0
  42. package/dist/backend-msb/commands.js +67 -0
  43. package/dist/backend-msb/commands.js.map +1 -0
  44. package/dist/backend-msb/exec-tunnel.d.ts +33 -0
  45. package/dist/backend-msb/exec-tunnel.d.ts.map +1 -0
  46. package/dist/backend-msb/exec-tunnel.js +163 -0
  47. package/dist/backend-msb/exec-tunnel.js.map +1 -0
  48. package/dist/backend-msb/follow-replay.d.ts +14 -0
  49. package/dist/backend-msb/follow-replay.d.ts.map +1 -0
  50. package/dist/backend-msb/follow-replay.js +20 -0
  51. package/dist/backend-msb/follow-replay.js.map +1 -0
  52. package/dist/backend-msb/image-cache.d.ts +25 -0
  53. package/dist/backend-msb/image-cache.d.ts.map +1 -0
  54. package/dist/backend-msb/image-cache.js +27 -0
  55. package/dist/backend-msb/image-cache.js.map +1 -0
  56. package/dist/backend-msb/index.d.ts +6 -0
  57. package/dist/backend-msb/index.d.ts.map +1 -0
  58. package/dist/backend-msb/index.js +21 -0
  59. package/dist/backend-msb/index.js.map +1 -0
  60. package/dist/backend-msb/invoke.d.ts +24 -0
  61. package/dist/backend-msb/invoke.d.ts.map +1 -0
  62. package/dist/backend-msb/invoke.js +73 -0
  63. package/dist/backend-msb/invoke.js.map +1 -0
  64. package/dist/backend-msb/ls-json.d.ts +23 -0
  65. package/dist/backend-msb/ls-json.d.ts.map +1 -0
  66. package/dist/backend-msb/ls-json.js +113 -0
  67. package/dist/backend-msb/ls-json.js.map +1 -0
  68. package/dist/backend-msb/network-links.d.ts +7 -0
  69. package/dist/backend-msb/network-links.d.ts.map +1 -0
  70. package/dist/backend-msb/network-links.js +31 -0
  71. package/dist/backend-msb/network-links.js.map +1 -0
  72. package/dist/backend-msb/platform.d.ts +29 -0
  73. package/dist/backend-msb/platform.d.ts.map +1 -0
  74. package/dist/backend-msb/platform.js +128 -0
  75. package/dist/backend-msb/platform.js.map +1 -0
  76. package/dist/backend-msb/port-conflict.d.ts +10 -0
  77. package/dist/backend-msb/port-conflict.d.ts.map +1 -0
  78. package/dist/backend-msb/port-conflict.js +16 -0
  79. package/dist/backend-msb/port-conflict.js.map +1 -0
  80. package/dist/backend-msb/provider.d.ts +10 -0
  81. package/dist/backend-msb/provider.d.ts.map +1 -0
  82. package/dist/backend-msb/provider.js +40 -0
  83. package/dist/backend-msb/provider.js.map +1 -0
  84. package/dist/backend-msb/provisioner.d.ts +19 -0
  85. package/dist/backend-msb/provisioner.d.ts.map +1 -0
  86. package/dist/backend-msb/provisioner.js +332 -0
  87. package/dist/backend-msb/provisioner.js.map +1 -0
  88. package/dist/backend-msb/reaper.d.ts +10 -0
  89. package/dist/backend-msb/reaper.d.ts.map +1 -0
  90. package/dist/backend-msb/reaper.js +73 -0
  91. package/dist/backend-msb/reaper.js.map +1 -0
  92. package/dist/core/backend.d.ts +98 -0
  93. package/dist/core/backend.d.ts.map +1 -0
  94. package/dist/core/backend.js +2 -0
  95. package/dist/core/backend.js.map +1 -0
  96. package/dist/core/backends.d.ts +37 -0
  97. package/dist/core/backends.d.ts.map +1 -0
  98. package/dist/core/backends.js +127 -0
  99. package/dist/core/backends.js.map +1 -0
  100. package/dist/core/cleanup.d.ts +37 -0
  101. package/dist/core/cleanup.d.ts.map +1 -0
  102. package/dist/core/cleanup.js +86 -0
  103. package/dist/core/cleanup.js.map +1 -0
  104. package/dist/core/errors.d.ts +51 -0
  105. package/dist/core/errors.d.ts.map +1 -0
  106. package/dist/core/errors.js +66 -0
  107. package/dist/core/errors.js.map +1 -0
  108. package/dist/core/free-ports.d.ts +23 -0
  109. package/dist/core/free-ports.d.ts.map +1 -0
  110. package/dist/core/free-ports.js +63 -0
  111. package/dist/core/free-ports.js.map +1 -0
  112. package/dist/core/generic-container.d.ts +145 -0
  113. package/dist/core/generic-container.d.ts.map +1 -0
  114. package/dist/core/generic-container.js +385 -0
  115. package/dist/core/generic-container.js.map +1 -0
  116. package/dist/core/model.d.ts +60 -0
  117. package/dist/core/model.d.ts.map +1 -0
  118. package/dist/core/model.js +2 -0
  119. package/dist/core/model.js.map +1 -0
  120. package/dist/core/mountable-file.d.ts +25 -0
  121. package/dist/core/mountable-file.d.ts.map +1 -0
  122. package/dist/core/mountable-file.js +64 -0
  123. package/dist/core/mountable-file.js.map +1 -0
  124. package/dist/core/network.d.ts +58 -0
  125. package/dist/core/network.d.ts.map +1 -0
  126. package/dist/core/network.js +84 -0
  127. package/dist/core/network.js.map +1 -0
  128. package/dist/core/run-id.d.ts +18 -0
  129. package/dist/core/run-id.d.ts.map +1 -0
  130. package/dist/core/run-id.js +18 -0
  131. package/dist/core/run-id.js.map +1 -0
  132. package/dist/core/wait.d.ts +59 -0
  133. package/dist/core/wait.d.ts.map +1 -0
  134. package/dist/core/wait.js +239 -0
  135. package/dist/core/wait.js.map +1 -0
  136. package/dist/index.d.ts +26 -0
  137. package/dist/index.d.ts.map +1 -0
  138. package/dist/index.js +22 -0
  139. package/dist/index.js.map +1 -0
  140. package/dist/modules/arango.d.ts +14 -0
  141. package/dist/modules/arango.d.ts.map +1 -0
  142. package/dist/modules/arango.js +37 -0
  143. package/dist/modules/arango.js.map +1 -0
  144. package/dist/modules/clickhouse.d.ts +37 -0
  145. package/dist/modules/clickhouse.d.ts.map +1 -0
  146. package/dist/modules/clickhouse.js +69 -0
  147. package/dist/modules/clickhouse.js.map +1 -0
  148. package/dist/modules/flink.d.ts +65 -0
  149. package/dist/modules/flink.d.ts.map +1 -0
  150. package/dist/modules/flink.js +125 -0
  151. package/dist/modules/flink.js.map +1 -0
  152. package/dist/modules/floci.d.ts +46 -0
  153. package/dist/modules/floci.d.ts.map +1 -0
  154. package/dist/modules/floci.js +62 -0
  155. package/dist/modules/floci.js.map +1 -0
  156. package/dist/modules/index.d.ts +31 -0
  157. package/dist/modules/index.d.ts.map +1 -0
  158. package/dist/modules/index.js +31 -0
  159. package/dist/modules/index.js.map +1 -0
  160. package/dist/modules/kafka.d.ts +11 -0
  161. package/dist/modules/kafka.d.ts.map +1 -0
  162. package/dist/modules/kafka.js +41 -0
  163. package/dist/modules/kafka.js.map +1 -0
  164. package/dist/modules/keycloak.d.ts +40 -0
  165. package/dist/modules/keycloak.d.ts.map +1 -0
  166. package/dist/modules/keycloak.js +67 -0
  167. package/dist/modules/keycloak.js.map +1 -0
  168. package/dist/modules/mariadb.d.ts +56 -0
  169. package/dist/modules/mariadb.d.ts.map +1 -0
  170. package/dist/modules/mariadb.js +89 -0
  171. package/dist/modules/mariadb.js.map +1 -0
  172. package/dist/modules/memcached.d.ts +24 -0
  173. package/dist/modules/memcached.d.ts.map +1 -0
  174. package/dist/modules/memcached.js +105 -0
  175. package/dist/modules/memcached.js.map +1 -0
  176. package/dist/modules/mongodb.d.ts +19 -0
  177. package/dist/modules/mongodb.d.ts.map +1 -0
  178. package/dist/modules/mongodb.js +69 -0
  179. package/dist/modules/mongodb.js.map +1 -0
  180. package/dist/modules/mysql.d.ts +61 -0
  181. package/dist/modules/mysql.d.ts.map +1 -0
  182. package/dist/modules/mysql.js +101 -0
  183. package/dist/modules/mysql.js.map +1 -0
  184. package/dist/modules/neo4j.d.ts +60 -0
  185. package/dist/modules/neo4j.d.ts.map +1 -0
  186. package/dist/modules/neo4j.js +83 -0
  187. package/dist/modules/neo4j.js.map +1 -0
  188. package/dist/modules/pinot.d.ts +30 -0
  189. package/dist/modules/pinot.d.ts.map +1 -0
  190. package/dist/modules/pinot.js +49 -0
  191. package/dist/modules/pinot.js.map +1 -0
  192. package/dist/modules/postgres.d.ts +29 -0
  193. package/dist/modules/postgres.d.ts.map +1 -0
  194. package/dist/modules/postgres.js +73 -0
  195. package/dist/modules/postgres.js.map +1 -0
  196. package/dist/modules/rabbitmq.d.ts +46 -0
  197. package/dist/modules/rabbitmq.d.ts.map +1 -0
  198. package/dist/modules/rabbitmq.js +68 -0
  199. package/dist/modules/rabbitmq.js.map +1 -0
  200. package/dist/modules/redis.d.ts +15 -0
  201. package/dist/modules/redis.d.ts.map +1 -0
  202. package/dist/modules/redis.js +24 -0
  203. package/dist/modules/redis.js.map +1 -0
  204. package/dist/modules/redpanda.d.ts +27 -0
  205. package/dist/modules/redpanda.d.ts.map +1 -0
  206. package/dist/modules/redpanda.js +58 -0
  207. package/dist/modules/redpanda.js.map +1 -0
  208. package/dist/modules/spring-cloud-config.d.ts +18 -0
  209. package/dist/modules/spring-cloud-config.d.ts.map +1 -0
  210. package/dist/modules/spring-cloud-config.js +28 -0
  211. package/dist/modules/spring-cloud-config.js.map +1 -0
  212. package/dist/modules/wiremock.d.ts +21 -0
  213. package/dist/modules/wiremock.d.ts.map +1 -0
  214. package/dist/modules/wiremock.js +32 -0
  215. package/dist/modules/wiremock.js.map +1 -0
  216. package/package.json +104 -0
@@ -0,0 +1,332 @@
1
+ import * as fs from "node:fs";
2
+ import * as fsp from "node:fs/promises";
3
+ import * as http from "node:http";
4
+ import * as https from "node:https";
5
+ import * as path from "node:path";
6
+ import * as os from "node:os";
7
+ import { createHash } from "node:crypto";
8
+ import { ProvisionError } from "../core/errors.js";
9
+ import { PlatformInfo } from "./platform.js";
10
+ export const MSB_VERSION = "0.6.3";
11
+ const DEFAULT_BASE = `https://github.com/superradcompany/microsandbox/releases/download/v${MSB_VERSION}`;
12
+ const CONNECT_READ_TIMEOUT_MS = 300_000;
13
+ const STALE_LOCK_AGE_MS = 5 * 60 * 1000;
14
+ const LOCK_POLL_MS = 200;
15
+ const LOCK_WAIT_MAX_MS = 30_000;
16
+ /**
17
+ * `%LOCALAPPDATA%` is the Windows-idiomatic location for a machine-local,
18
+ * non-roaming native toolchain cache (as opposed to `%USERPROFILE%`, which a
19
+ * roaming-profile setup can sync, or a Unix-style dotfile under the home
20
+ * dir). Falls back to `%USERPROFILE%\AppData\Local` if the env var is unset,
21
+ * matching what `os.homedir()` resolves to on a normal Windows install when
22
+ * `LOCALAPPDATA` isn't populated (rare, but seen on some minimal/CI images).
23
+ */
24
+ function defaultCacheDir() {
25
+ const override = process.env["RIGHTSIZE_CACHE_DIR"];
26
+ if (override !== undefined) {
27
+ return override;
28
+ }
29
+ if (process.platform === "win32") {
30
+ const localAppData = process.env["LOCALAPPDATA"] ?? path.join(os.homedir(), "AppData", "Local");
31
+ return path.join(localAppData, "rightsize");
32
+ }
33
+ return path.join(os.homedir(), ".cache", "rightsize");
34
+ }
35
+ /**
36
+ * "Is this a usable, already-provisioned msb binary": on macOS/Linux this
37
+ * means POSIX-executable (the release asset ships without the exec bit set,
38
+ * so a bare `existsSync` would pass on a half-downloaded or permission-wrong
39
+ * file); Windows has no execute-bit concept, so an existing regular `.exe`
40
+ * file is the whole check there — `fs.constants.X_OK` on Windows is
41
+ * documented to reduce to an existence check anyway, but an explicit branch
42
+ * keeps the intent visible rather than relying on that platform quirk.
43
+ */
44
+ function isExecutableFile(p) {
45
+ if (process.platform === "win32") {
46
+ try {
47
+ return fs.statSync(p).isFile();
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }
53
+ try {
54
+ fs.accessSync(p, fs.constants.X_OK);
55
+ return true;
56
+ }
57
+ catch {
58
+ return false;
59
+ }
60
+ }
61
+ function isInstalled(msbPath, krunPath) {
62
+ if (!isExecutableFile(msbPath)) {
63
+ return false;
64
+ }
65
+ return fs.existsSync(krunPath);
66
+ }
67
+ /** Follows a bounded chain of 30x redirects — the release CDN issues one hop to the actual asset host. */
68
+ function fetchBuffer(url, maxRedirects = 5) {
69
+ const transport = url.startsWith("https:") ? https : http;
70
+ return new Promise((resolveFetch, rejectFetch) => {
71
+ const req = transport.get(url, { timeout: CONNECT_READ_TIMEOUT_MS }, (res) => {
72
+ const status = res.statusCode ?? 0;
73
+ if (status >= 300 && status < 400 && res.headers.location !== undefined) {
74
+ res.resume();
75
+ if (maxRedirects <= 0) {
76
+ rejectFetch(new ProvisionError(`too many redirects fetching ${url}`));
77
+ return;
78
+ }
79
+ const nextUrl = new URL(res.headers.location, url).toString();
80
+ fetchBuffer(nextUrl, maxRedirects - 1).then(resolveFetch, rejectFetch);
81
+ return;
82
+ }
83
+ if (status !== 200) {
84
+ res.resume();
85
+ rejectFetch(new ProvisionError(`HTTP ${status} fetching ${url}`));
86
+ return;
87
+ }
88
+ const chunks = [];
89
+ res.on("data", (chunk) => chunks.push(chunk));
90
+ res.on("end", () => resolveFetch(Buffer.concat(chunks)));
91
+ res.on("error", (err) => rejectFetch(err));
92
+ });
93
+ req.on("timeout", () => req.destroy(new ProvisionError(`timed out fetching ${url}`)));
94
+ req.on("error", (err) => rejectFetch(err));
95
+ });
96
+ }
97
+ /** Tolerant of extra whitespace and either column order — `sha256sum` output vs a hand-authored manifest can vary. */
98
+ function parseChecksums(text) {
99
+ const sums = new Map();
100
+ for (const rawLine of text.split("\n")) {
101
+ const line = rawLine.trim();
102
+ if (line.length === 0) {
103
+ continue;
104
+ }
105
+ const parts = line.split(/\s+/);
106
+ if (parts.length < 2) {
107
+ throw new ProvisionError(`malformed line in checksums.sha256: '${line}'`);
108
+ }
109
+ const [hex, filename] = parts;
110
+ if (hex !== undefined && filename !== undefined) {
111
+ sums.set(filename, hex.toLowerCase());
112
+ }
113
+ }
114
+ return sums;
115
+ }
116
+ function sha256Hex(buf) {
117
+ return createHash("sha256").update(buf).digest("hex");
118
+ }
119
+ function parseLockInfo(text) {
120
+ const [pidStr, tsStr] = text.trim().split("\n");
121
+ const pid = Number(pidStr);
122
+ const timestamp = Number(tsStr);
123
+ if (!Number.isFinite(pid) || !Number.isFinite(timestamp)) {
124
+ return undefined;
125
+ }
126
+ return { pid, timestamp };
127
+ }
128
+ function isProcessAlive(pid) {
129
+ try {
130
+ process.kill(pid, 0);
131
+ return true;
132
+ }
133
+ catch (err) {
134
+ // ESRCH: no such process. EPERM: a live process we don't own — still alive.
135
+ return err.code === "EPERM";
136
+ }
137
+ }
138
+ function isLockStale(info) {
139
+ if (info === undefined) {
140
+ // An unparseable lock file cannot be trusted as "held" — treat it as stale.
141
+ return true;
142
+ }
143
+ if (!isProcessAlive(info.pid)) {
144
+ return true;
145
+ }
146
+ return Date.now() - info.timestamp > STALE_LOCK_AGE_MS;
147
+ }
148
+ function sleep(ms) {
149
+ return new Promise((r) => setTimeout(r, ms));
150
+ }
151
+ /**
152
+ * Cross-process install-dir lock via `fs.open(path, "wx")` (O_EXCL create) —
153
+ * the closest Node stdlib equivalent to a kernel-held advisory lock, except
154
+ * that unlike an OS advisory lock it is NOT released automatically
155
+ * when the holding process dies. The lock file therefore records
156
+ * `${pid}\n${timestamp}` so a waiter can positively detect a dead holder
157
+ * (`process.kill(pid, 0)` throwing `ESRCH`) rather than only ever guessing
158
+ * from file age. Staleness is thus two independent conditions, either one
159
+ * sufficient to take over: the recorded PID is provably dead, OR the lock
160
+ * predates the staleness threshold (a live-but-wedged holder).
161
+ */
162
+ async function withInstallLock(lockPath, fn) {
163
+ const deadline = Date.now() + LOCK_WAIT_MAX_MS;
164
+ for (;;) {
165
+ let handle;
166
+ try {
167
+ handle = await fsp.open(lockPath, "wx");
168
+ }
169
+ catch (err) {
170
+ if (err.code !== "EEXIST") {
171
+ throw err;
172
+ }
173
+ // Someone else holds the lock file (or a stale one is sitting there).
174
+ let existing;
175
+ try {
176
+ existing = await fsp.readFile(lockPath, "utf8");
177
+ }
178
+ catch {
179
+ existing = "";
180
+ }
181
+ const info = parseLockInfo(existing);
182
+ if (isLockStale(info)) {
183
+ // Stale takeover: remove and retry immediately. A second racer may
184
+ // beat us to the same removal+recreate — that's fine, they'll just
185
+ // loop again on the next EEXIST.
186
+ try {
187
+ await fsp.unlink(lockPath);
188
+ }
189
+ catch {
190
+ // Already removed by a concurrent racer — fall through to retry.
191
+ }
192
+ continue;
193
+ }
194
+ if (Date.now() >= deadline) {
195
+ throw new ProvisionError(`timed out waiting for the msb install lock at ${lockPath}`);
196
+ }
197
+ await sleep(LOCK_POLL_MS);
198
+ continue;
199
+ }
200
+ try {
201
+ await handle.writeFile(`${process.pid}\n${Date.now()}`);
202
+ return await fn();
203
+ }
204
+ finally {
205
+ await handle.close();
206
+ await fsp.unlink(lockPath).catch(() => {
207
+ // Best-effort: if it's already gone (a stale-takeover racer beat us
208
+ // to cleanup) there's nothing left to do.
209
+ });
210
+ }
211
+ }
212
+ }
213
+ /**
214
+ * `mode` is a POSIX permission-bits concept; Windows has no execute bit and
215
+ * `fs.writeFile`'s `mode` option is a documented no-op there for anything
216
+ * beyond the read-only attribute, which this provisioner never needs to set.
217
+ * The caller's `executable` flag therefore only matters on macOS/Linux —
218
+ * passing it through unconditionally is harmless on Windows (Node ignores
219
+ * it), but the chmod-shaped intent is Windows-irrelevant, so it is spelled
220
+ * out here rather than left implicit.
221
+ */
222
+ async function downloadVerified(baseUrl, asset, destDir, sums, executable) {
223
+ const expected = sums.get(asset);
224
+ if (expected === undefined) {
225
+ throw new ProvisionError(`No SHA-256 for '${asset}' in ${baseUrl}/checksums.sha256`);
226
+ }
227
+ await fsp.mkdir(destDir, { recursive: true });
228
+ const tmp = path.join(destDir, `.dl-${process.pid}-${Date.now()}-${asset}.part`);
229
+ let ok = false;
230
+ try {
231
+ const buf = await fetchBuffer(`${baseUrl}/${asset}`);
232
+ const actual = sha256Hex(buf);
233
+ if (actual !== expected) {
234
+ throw new ProvisionError(`SHA-256 mismatch for ${asset} from ${baseUrl} (expected ${expected}, got ${actual}) ` +
235
+ `— delete ${destDir} and retry, or set MSB_PATH to a trusted msb binary`);
236
+ }
237
+ const mode = process.platform === "win32" ? undefined : executable ? 0o755 : 0o644;
238
+ await fsp.writeFile(tmp, buf, mode !== undefined ? { mode } : undefined);
239
+ ok = true;
240
+ return tmp;
241
+ }
242
+ finally {
243
+ if (!ok) {
244
+ await fsp.unlink(tmp).catch(() => { });
245
+ }
246
+ }
247
+ }
248
+ /**
249
+ * Downloads and SHA-256-verifies BOTH assets to temp files before moving
250
+ * either into place, then renames krun into `lib/` FIRST and the `msb`
251
+ * binary into `bin/` LAST. `msb`'s presence is therefore the commit marker
252
+ * for a genuinely complete install — a crash between the two renames can
253
+ * never leave a present-msb/missing-krun state, because `isInstalled`
254
+ * requires both files and whichever half is missing gets re-downloaded on
255
+ * the next call.
256
+ */
257
+ async function downloadAndInstall(baseUrl, platform, installDir, msbPath, krunPath) {
258
+ const checksumsText = (await fetchBuffer(`${baseUrl}/checksums.sha256`)).toString("utf8");
259
+ const sums = parseChecksums(checksumsText);
260
+ const msbAsset = PlatformInfo.msbAsset(platform);
261
+ const krunAsset = PlatformInfo.krunAsset(platform);
262
+ const msbTmp = await downloadVerified(baseUrl, msbAsset, path.join(installDir, "bin"), sums, true);
263
+ const krunTmp = await downloadVerified(baseUrl, krunAsset, path.join(installDir, "lib"), sums, false);
264
+ try {
265
+ await fsp.rename(krunTmp, krunPath);
266
+ await fsp.rename(msbTmp, msbPath);
267
+ }
268
+ finally {
269
+ await fsp.unlink(msbTmp).catch(() => { });
270
+ await fsp.unlink(krunTmp).catch(() => { });
271
+ }
272
+ }
273
+ /** Test seam: the full resolution logic parameterized over the release base URL, cache dir, and environment. */
274
+ export async function ensureInstalledAt(baseUrl, cacheDir, env) {
275
+ const override = env["MSB_PATH"];
276
+ if (override !== undefined) {
277
+ // Windows has no execute-bit concept: "is a real file that exists" is
278
+ // the whole validity check there (see isExecutableFile's doc comment);
279
+ // macOS/Linux keep requiring the POSIX execute bit.
280
+ if (!isExecutableFile(override)) {
281
+ throw new ProvisionError(`MSB_PATH='${override}' is not an executable file`);
282
+ }
283
+ return override;
284
+ }
285
+ const platform = PlatformInfo.current();
286
+ if (platform === undefined) {
287
+ throw new ProvisionError(`microsandbox has no build for ${process.platform}/${process.arch} — ` +
288
+ `use the docker backend (RIGHTSIZE_BACKEND=docker) or set MSB_PATH to a binary you provide`);
289
+ }
290
+ const installDir = path.join(cacheDir, "msb", MSB_VERSION);
291
+ const msbPath = path.join(installDir, "bin", PlatformInfo.msbBinaryName(platform));
292
+ // Installed under the canonical name msb resolves (`../lib/` next to its binary),
293
+ // not the release-asset name it is downloaded as — msb never probes the asset name.
294
+ const krunPath = path.join(installDir, "lib", PlatformInfo.krunInstallName(platform));
295
+ if (isInstalled(msbPath, krunPath)) {
296
+ return msbPath;
297
+ }
298
+ if (env["RIGHTSIZE_MSB_SKIP_DOWNLOAD"] === "true") {
299
+ throw new ProvisionError(`msb ${MSB_VERSION} not found at ${msbPath} and RIGHTSIZE_MSB_SKIP_DOWNLOAD=true — ` +
300
+ `pre-install it there or point MSB_PATH at an msb binary`);
301
+ }
302
+ await fsp.mkdir(installDir, { recursive: true });
303
+ await withInstallLock(path.join(installDir, ".lock"), async () => {
304
+ // Another process may have finished the install while we waited for the lock.
305
+ if (isInstalled(msbPath, krunPath)) {
306
+ return;
307
+ }
308
+ await downloadAndInstall(baseUrl, platform, installDir, msbPath, krunPath);
309
+ });
310
+ return msbPath;
311
+ }
312
+ /** Resolves (downloading and verifying if needed) a runnable `msb` binary path, using the real GitHub release base, `RIGHTSIZE_CACHE_DIR`, and `process.env`. */
313
+ export function ensureInstalled() {
314
+ return ensureInstalledAt(DEFAULT_BASE, defaultCacheDir(), process.env);
315
+ }
316
+ /** Test-only access to the pure checksum-line parser, exercised against real-shape and malformed input. */
317
+ export function _parseChecksumsForTests(text) {
318
+ return parseChecksums(text);
319
+ }
320
+ /**
321
+ * Test-only seam: `downloadAndInstall` parameterized over an injected
322
+ * `Platform` value, rather than the real `PlatformInfo.current()`. This is
323
+ * how the `.exe`/`.dll` install shape (Windows asset names, `bin\msb.exe`,
324
+ * unversioned `lib\libkrunfw.dll`) is exercised against the real
325
+ * download-verify-rename pipeline from a non-Windows CI/dev machine — the
326
+ * public `ensureInstalledAt` always resolves the platform from the real
327
+ * process, so it can only ever prove the CURRENT host's shape without this.
328
+ */
329
+ export async function _downloadAndInstallForTests(baseUrl, platform, installDir, msbPath, krunPath) {
330
+ return downloadAndInstall(baseUrl, platform, installDir, msbPath, krunPath);
331
+ }
332
+ //# sourceMappingURL=provisioner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provisioner.js","sourceRoot":"","sources":["../../src/backend-msb/provisioner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Every `rz-<runid>-<seq>` name mentioned in `msb ls`'s output that does NOT
3
+ * belong to `thisRunId` — leftovers from a run that crashed before its own
4
+ * cleanup ran. Filtering happens against the SAME `RunId.value` core used to
5
+ * name this run's own containers (imported from `rightsize`, never
6
+ * recomputed here) — a backend-local run id would either reap this run's own
7
+ * live sandboxes or fail to recognize a genuinely stale one.
8
+ */
9
+ export declare function orphanNames(lsOutput: string, thisRunId: string): string[];
10
+ //# sourceMappingURL=reaper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reaper.d.ts","sourceRoot":"","sources":["../../src/backend-msb/reaper.ts"],"names":[],"mappings":"AA0DA;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CASzE"}
@@ -0,0 +1,73 @@
1
+ function isHexDigit(ch) {
2
+ return ch !== undefined && /^[0-9a-f]$/i.test(ch);
3
+ }
4
+ function isDigit(ch) {
5
+ return ch !== undefined && ch >= "0" && ch <= "9";
6
+ }
7
+ /** A character that continues an identifier token: alphanumeric, `-`, or `_`. */
8
+ function isIdentContinue(ch) {
9
+ return ch !== undefined && /^[A-Za-z0-9_-]$/.test(ch);
10
+ }
11
+ /**
12
+ * Extracts every `rz-<8 hex>-<seq>`-shaped name appearing anywhere in `ls`
13
+ * output — a manual character scan rather than
14
+ * a `\b`-delimited regex. `\b` is a `\w` boundary and `\w` does not include
15
+ * `-`, so a naive `/\brz-[0-9a-f]{8}-\d+\b/` would treat `rz-deadbeef-1` as a
16
+ * complete match inside `rz-deadbeef-1-extra` (the `-` after the digits
17
+ * counts as a boundary) even though that's a glued-on continuation of a
18
+ * longer, different token — exactly the shape a second run's id could
19
+ * produce as a prefix of a name it doesn't own. Requiring the character
20
+ * immediately after the matched digits to NOT be an identifier-continue
21
+ * character keeps this a whole-token match instead of a false-positive
22
+ * prefix.
23
+ */
24
+ function orphanCandidateNames(lsOutput) {
25
+ const names = [];
26
+ let i = 0;
27
+ const n = lsOutput.length;
28
+ while (i < n) {
29
+ if (lsOutput.startsWith("rz-", i)) {
30
+ const start = i;
31
+ let j = i + 3;
32
+ let hexLen = 0;
33
+ while (j < n && hexLen < 8 && isHexDigit(lsOutput[j])) {
34
+ j++;
35
+ hexLen++;
36
+ }
37
+ if (hexLen === 8 && lsOutput[j] === "-") {
38
+ let k = j + 1;
39
+ let digitLen = 0;
40
+ while (k < n && isDigit(lsOutput[k])) {
41
+ k++;
42
+ digitLen++;
43
+ }
44
+ if (digitLen > 0 && (k >= n || !isIdentContinue(lsOutput[k]))) {
45
+ names.push(lsOutput.slice(start, k));
46
+ i = k;
47
+ continue;
48
+ }
49
+ }
50
+ }
51
+ i++;
52
+ }
53
+ return names;
54
+ }
55
+ /**
56
+ * Every `rz-<runid>-<seq>` name mentioned in `msb ls`'s output that does NOT
57
+ * belong to `thisRunId` — leftovers from a run that crashed before its own
58
+ * cleanup ran. Filtering happens against the SAME `RunId.value` core used to
59
+ * name this run's own containers (imported from `rightsize`, never
60
+ * recomputed here) — a backend-local run id would either reap this run's own
61
+ * live sandboxes or fail to recognize a genuinely stale one.
62
+ */
63
+ export function orphanNames(lsOutput, thisRunId) {
64
+ const mine = `rz-${thisRunId}-`;
65
+ const seen = new Set();
66
+ for (const name of orphanCandidateNames(lsOutput)) {
67
+ if (!name.startsWith(mine)) {
68
+ seen.add(name);
69
+ }
70
+ }
71
+ return [...seen];
72
+ }
73
+ //# sourceMappingURL=reaper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reaper.js","sourceRoot":"","sources":["../../src/backend-msb/reaper.ts"],"names":[],"mappings":"AAAA,SAAS,UAAU,CAAC,EAAsB;IACxC,OAAO,EAAE,KAAK,SAAS,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,OAAO,CAAC,EAAsB;IACrC,OAAO,EAAE,KAAK,SAAS,IAAI,EAAE,IAAI,GAAG,IAAI,EAAE,IAAI,GAAG,CAAC;AACpD,CAAC;AAED,iFAAiF;AACjF,SAAS,eAAe,CAAC,EAAsB;IAC7C,OAAO,EAAE,KAAK,SAAS,IAAI,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,oBAAoB,CAAC,QAAgB;IAC5C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC1B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACb,IAAI,QAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,CAAC,CAAC;YAChB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,IAAI,MAAM,GAAG,CAAC,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtD,CAAC,EAAE,CAAC;gBACJ,MAAM,EAAE,CAAC;YACX,CAAC;YACD,IAAI,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACxC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACd,IAAI,QAAQ,GAAG,CAAC,CAAC;gBACjB,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBACrC,CAAC,EAAE,CAAC;oBACJ,QAAQ,EAAE,CAAC;gBACb,CAAC;gBACD,IAAI,QAAQ,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC9D,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;oBACrC,CAAC,GAAG,CAAC,CAAC;oBACN,SAAS;gBACX,CAAC;YACH,CAAC;QACH,CAAC;QACD,CAAC,EAAE,CAAC;IACN,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB,EAAE,SAAiB;IAC7D,MAAM,IAAI,GAAG,MAAM,SAAS,GAAG,CAAC;IAChC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;AACnB,CAAC"}
@@ -0,0 +1,98 @@
1
+ import type { ContainerSpec, ExecResult } from "./model.js";
2
+ /**
3
+ * One alias a container should be reachable under from a running sibling on
4
+ * the same `Network`, computed by `Network.linksForNewMember()`. A native
5
+ * backend (docker) ignores these — its bridge network already resolves
6
+ * aliases; an emulating backend (msb) uses them to wire up its tunnels.
7
+ */
8
+ export interface NetworkLink {
9
+ /** The name the sibling should be reachable under. */
10
+ readonly alias: string;
11
+ /** The sibling's exposed guest port. */
12
+ readonly guestPort: number;
13
+ /** The sibling's host-side mapped port to tunnel/route traffic to. */
14
+ readonly targetHostPort: number;
15
+ }
16
+ /**
17
+ * Backend-native opaque container reference. Immutable id + spec; any
18
+ * mutable runtime state (attached child process, log tail, tunnels) lives
19
+ * backend-side keyed by `id`, never bolted onto this object.
20
+ */
21
+ export interface SandboxHandle {
22
+ /** The backend-native container id (or name, for msb). */
23
+ readonly id: string;
24
+ /** The spec this container was created from. */
25
+ readonly spec: ContainerSpec;
26
+ }
27
+ /** Owns the close mechanism for a live followOutput stream: stop delivery, never flush. */
28
+ export interface FollowHandle {
29
+ /** Stops delivering further lines. Never flushes a trailing fragment — that only happens on the workload's own natural end. */
30
+ close(): Promise<void>;
31
+ }
32
+ /**
33
+ * The one interface every container backend implements. Host ports arrive
34
+ * in `ContainerSpec.ports` already chosen (see `FreePorts`) — a backend
35
+ * binds them, it never allocates its own.
36
+ */
37
+ export interface SandboxBackend {
38
+ /** The backend's own name (`"microsandbox"` or `"docker"`). */
39
+ readonly name: string;
40
+ /** `true` for docker (native bridge networks); `false` for msb, which emulates links over exec-stream tunnels instead. */
41
+ readonly supportsNativeNetworks: boolean;
42
+ /** Allocate the backend-native container without starting it. Must not bind ports or run the workload yet. */
43
+ create(spec: ContainerSpec): Promise<SandboxHandle>;
44
+ /** Boot the workload. On a bind conflict, throw (or wrap a cause chain ending in) `PortBindConflictError` so `GenericContainer`'s retry loop can classify it. */
45
+ start(handle: SandboxHandle): Promise<void>;
46
+ /** Best-effort stop; callers swallow failures during teardown. */
47
+ stop(handle: SandboxHandle): Promise<void>;
48
+ /** Best-effort removal of the backend-native resource; callers swallow failures during teardown. */
49
+ remove(handle: SandboxHandle): Promise<void>;
50
+ /** Run a one-shot command inside a running container and wait for it to exit. */
51
+ exec(handle: SandboxHandle, cmd: ReadonlyArray<string>): Promise<ExecResult>;
52
+ /** Fetch everything logged so far (bounded tail), for a one-shot read. */
53
+ logs(handle: SandboxHandle): Promise<string>;
54
+ /**
55
+ * Stream log lines to `consumer` as they're produced, in order, with no
56
+ * duplicates — including the final unterminated fragment after the
57
+ * workload exits, delivered exactly once. Returns a handle whose `close()`
58
+ * stops delivery without flushing anything further.
59
+ */
60
+ followLogs(handle: SandboxHandle, consumer: (line: string) => void): Promise<FollowHandle>;
61
+ /** Idempotently ensure a network with this id exists. */
62
+ ensureNetwork(networkId: string): Promise<void>;
63
+ /** Best-effort removal of a network created by `ensureNetwork`. */
64
+ removeNetwork(networkId: string): Promise<void>;
65
+ /** Default no-op: docker relies on native networks. Only an emulating backend overrides it. */
66
+ installNetworkLinks(handle: SandboxHandle, links: ReadonlyArray<NetworkLink>): Promise<void>;
67
+ /** Best-effort teardown of backend-owned resources (sockets, child processes). */
68
+ close(): Promise<void>;
69
+ /**
70
+ * Synchronous, blocking teardown of ONE container, for the process-exit
71
+ * path only (`node:process`'s `"exit"` handler runs synchronously and
72
+ * cannot `await` the backend's normal async `stop`/`remove`). Every
73
+ * backend implements this with a blocking primitive of its own —
74
+ * `child_process.spawnSync` for msb, a blocking unix-socket call for
75
+ * docker — never by calling back into the async API. Best-effort: swallow
76
+ * failures, the process is exiting regardless.
77
+ */
78
+ cleanupSync(id: string): void;
79
+ }
80
+ /**
81
+ * The `ServiceLoader` analog: a backend subpath (`rightsize/backend-msb`,
82
+ * `rightsize/backend-docker`) registers one of these at import time via
83
+ * `registerBackend`. `Backends.resolve` picks the highest-priority supported
84
+ * provider, or honors `RIGHTSIZE_BACKEND` when set.
85
+ */
86
+ export interface BackendProvider {
87
+ /** The provider's backend name, matched case-insensitively against `RIGHTSIZE_BACKEND`. */
88
+ readonly name: string;
89
+ /** Higher wins when multiple providers are supported (msb=20, docker=10). */
90
+ readonly priority: number;
91
+ /** MUST be synchronous — never return a Promise (a truthy Promise reads as "supported" to callers that don't await it). */
92
+ isSupported(): boolean;
93
+ /** Human-readable reason this provider isn't usable right now, surfaced when no supported provider exists. */
94
+ unsupportedReason(): string;
95
+ /** Construct the backend. Called once resolution has picked this provider. */
96
+ create(): SandboxBackend;
97
+ }
98
+ //# sourceMappingURL=backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/core/backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,wCAAwC;IACxC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sEAAsE;IACtE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;CAC9B;AAED,2FAA2F;AAC3F,MAAM,WAAW,YAAY;IAC3B,+HAA+H;IAC/H,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0HAA0H;IAC1H,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAC;IACzC,8GAA8G;IAC9G,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACpD,iKAAiK;IACjK,KAAK,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,kEAAkE;IAClE,IAAI,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,oGAAoG;IACpG,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,iFAAiF;IACjF,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7E,0EAA0E;IAC1E,IAAI,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C;;;;;OAKG;IACH,UAAU,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC3F,yDAAyD;IACzD,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,mEAAmE;IACnE,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,+FAA+F;IAC/F,mBAAmB,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7F,kFAAkF;IAClF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,2FAA2F;IAC3F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,2HAA2H;IAC3H,WAAW,IAAI,OAAO,CAAC;IACvB,8GAA8G;IAC9G,iBAAiB,IAAI,MAAM,CAAC;IAC5B,8EAA8E;IAC9E,MAAM,IAAI,cAAc,CAAC;CAC1B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend.js","sourceRoot":"","sources":["../../src/core/backend.ts"],"names":[],"mappings":""}
@@ -0,0 +1,37 @@
1
+ import type { BackendProvider, SandboxBackend } from "./backend.js";
2
+ /** Adds a provider to the process-global registry. Called once, at import time, by each backend subpath's `index.ts`. */
3
+ export declare function registerBackend(provider: BackendProvider): void;
4
+ /** Test seam: clears the process-global registry. Never call from library code. */
5
+ export declare function _resetRegistryForTests(): void;
6
+ /**
7
+ * Test seam: snapshot of the currently registered providers, so an impure
8
+ * suite that wipes the registry can put the real ones back when it finishes —
9
+ * under a single-process test runner, whatever it leaves behind is what every
10
+ * later-loaded test file sees.
11
+ */
12
+ export declare function _providersSnapshotForTests(): BackendProvider[];
13
+ /**
14
+ * Pure resolution: no I/O, no module-global registry access, so it is fully
15
+ * unit-testable with fake providers. `Backends.active()` is the impure
16
+ * caller that supplies the real registry and `process.env`.
17
+ */
18
+ export declare function resolve(providers: readonly BackendProvider[], requested: string | undefined): SandboxBackend;
19
+ /**
20
+ * Resolves once per process (memoized) against the real registry and
21
+ * `process.env.RIGHTSIZE_BACKEND`, then registers the best-effort exit-path
22
+ * cleanup (see `src/core/cleanup.ts`) the first time a backend is created.
23
+ */
24
+ declare function active(): SandboxBackend;
25
+ /** Test seam: forces the next `Backends.active()` call to re-resolve. */
26
+ declare function _resetActiveForTests(): void;
27
+ /** The library's single entry point for obtaining the active `SandboxBackend`. */
28
+ export declare const Backends: {
29
+ /** Pure resolution against an explicit provider list — see `resolve` above. */
30
+ resolve: typeof resolve;
31
+ /** The memoized, per-process active backend — resolved from whatever's registered plus `RIGHTSIZE_BACKEND`. */
32
+ active: typeof active;
33
+ /** Test seam: forces the next `active()` call to re-resolve. Never call from library code. */
34
+ _resetActiveForTests: typeof _resetActiveForTests;
35
+ };
36
+ export {};
37
+ //# sourceMappingURL=backends.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backends.d.ts","sourceRoot":"","sources":["../../src/core/backends.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAQpE,yHAAyH;AACzH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAE/D;AAED,mFAAmF;AACnF,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,IAAI,eAAe,EAAE,CAE9D;AAMD;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,CA2B5G;AAKD;;;;GAIG;AACH,iBAAS,MAAM,IAAI,cAAc,CAkDhC;AAED,yEAAyE;AACzE,iBAAS,oBAAoB,IAAI,IAAI,CAGpC;AAED,kFAAkF;AAClF,eAAO,MAAM,QAAQ;IACnB,+EAA+E;;IAE/E,+GAA+G;;IAE/G,8FAA8F;;CAE/F,CAAC"}