rightsize 0.2.0 → 0.3.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 (58) hide show
  1. package/README.md +12 -4
  2. package/dist/backend-docker/backend.d.ts +27 -4
  3. package/dist/backend-docker/backend.d.ts.map +1 -1
  4. package/dist/backend-docker/backend.js +56 -6
  5. package/dist/backend-docker/backend.js.map +1 -1
  6. package/dist/backend-docker/cli.d.ts +25 -0
  7. package/dist/backend-docker/cli.d.ts.map +1 -0
  8. package/dist/backend-docker/cli.js +64 -0
  9. package/dist/backend-docker/cli.js.map +1 -0
  10. package/dist/backend-msb/backend.d.ts +61 -7
  11. package/dist/backend-msb/backend.d.ts.map +1 -1
  12. package/dist/backend-msb/backend.js +121 -11
  13. package/dist/backend-msb/backend.js.map +1 -1
  14. package/dist/backend-msb/commands.d.ts +10 -0
  15. package/dist/backend-msb/commands.d.ts.map +1 -1
  16. package/dist/backend-msb/commands.js +29 -1
  17. package/dist/backend-msb/commands.js.map +1 -1
  18. package/dist/backend-msb/snapshot-not-found.d.ts +25 -0
  19. package/dist/backend-msb/snapshot-not-found.d.ts.map +1 -0
  20. package/dist/backend-msb/snapshot-not-found.js +27 -0
  21. package/dist/backend-msb/snapshot-not-found.js.map +1 -0
  22. package/dist/core/backend.d.ts +57 -6
  23. package/dist/core/backend.d.ts.map +1 -1
  24. package/dist/core/checkpoint/api.d.ts +80 -0
  25. package/dist/core/checkpoint/api.d.ts.map +1 -0
  26. package/dist/core/checkpoint/api.js +138 -0
  27. package/dist/core/checkpoint/api.js.map +1 -0
  28. package/dist/core/checkpoint/name.d.ts +12 -0
  29. package/dist/core/checkpoint/name.d.ts.map +1 -0
  30. package/dist/core/checkpoint/name.js +17 -0
  31. package/dist/core/checkpoint/name.js.map +1 -0
  32. package/dist/core/checkpoint/ref.d.ts +12 -0
  33. package/dist/core/checkpoint/ref.d.ts.map +1 -0
  34. package/dist/core/checkpoint/ref.js +16 -0
  35. package/dist/core/checkpoint/ref.js.map +1 -0
  36. package/dist/core/checkpoint/registry.d.ts +107 -0
  37. package/dist/core/checkpoint/registry.d.ts.map +1 -0
  38. package/dist/core/checkpoint/registry.js +180 -0
  39. package/dist/core/checkpoint/registry.js.map +1 -0
  40. package/dist/core/errors.d.ts +60 -4
  41. package/dist/core/errors.d.ts.map +1 -1
  42. package/dist/core/errors.js +80 -7
  43. package/dist/core/errors.js.map +1 -1
  44. package/dist/core/generic-container.d.ts +87 -18
  45. package/dist/core/generic-container.d.ts.map +1 -1
  46. package/dist/core/generic-container.js +171 -22
  47. package/dist/core/generic-container.js.map +1 -1
  48. package/dist/core/model.d.ts +25 -8
  49. package/dist/core/model.d.ts.map +1 -1
  50. package/dist/index.d.ts +4 -2
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +4 -2
  53. package/dist/index.js.map +1 -1
  54. package/dist/modules/keycloak.js +1 -1
  55. package/dist/modules/mysql.d.ts.map +1 -1
  56. package/dist/modules/mysql.js +4 -3
  57. package/dist/modules/mysql.js.map +1 -1
  58. package/package.json +1 -1
@@ -0,0 +1,138 @@
1
+ import { cacheDir } from "../cache-dir.js";
2
+ import { Backends } from "../backends.js";
3
+ import { requireValidCheckpointName } from "./name.js";
4
+ import { readCheckpointRegistry, removeCheckpointRegistryFile, listCheckpointNames, fromCheckpointRegistryEntry, } from "./registry.js";
5
+ /** Every field of a `Checkpoint` comes straight from the registry entry except `spec`, which is reconstructed via `fromCheckpointRegistryEntry` — see that function's own doc for what is and isn't meaningful in the result. */
6
+ function toCheckpoint(entry) {
7
+ return { ref: entry.ref, backend: entry.backend, spec: fromCheckpointRegistryEntry(entry) };
8
+ }
9
+ /**
10
+ * Rediscovers a named checkpoint written by an earlier `checkpoint(name)`
11
+ * call — in this process or an entirely different one, since the registry
12
+ * lives on disk under the shared rightsize cache directory. No entry for
13
+ * `name` resolves to `undefined`. A corrupt entry is treated the same as
14
+ * absent, with a best-effort delete of the bad file.
15
+ *
16
+ * When the entry's own recorded backend matches the CURRENTLY active
17
+ * backend, the underlying artifact is probed via the backend's
18
+ * `hasCheckpoint` SPI before this resolves — an artifact that's gone (removed
19
+ * by hand, or by something outside this library) makes the entry stale: it's
20
+ * best-effort deleted and this resolves to `undefined`, the same as if it
21
+ * had never existed. A probe FAILURE (the backend call itself throws) is
22
+ * never swallowed into a `false` — only a confirmed "does not exist"
23
+ * resolves that way, so a probe failure propagates out of this call.
24
+ *
25
+ * When the entry's recorded backend DIFFERS from the active one, this
26
+ * returns the entry unprobed — an msb ref means nothing to a docker probe
27
+ * and vice versa. `GenericContainer.fromCheckpoint(cp).start()`'s own
28
+ * `CheckpointBackendMismatchError` gate stays the sole authority for that
29
+ * mismatch; this function must not force-resolve a backend the host may not
30
+ * even have.
31
+ *
32
+ * `name` is validated against `CHECKPOINT_NAME_PATTERN` before anything else
33
+ * — including before the registry file is even looked up — so a `name`
34
+ * carrying `../` segments can never reach path construction; an invalid name
35
+ * throws `InvalidCheckpointNameError` and touches no file.
36
+ */
37
+ export async function find(name) {
38
+ requireValidCheckpointName(name);
39
+ const dir = cacheDir();
40
+ const read = await readCheckpointRegistry(dir, name);
41
+ if (read.kind === "missing") {
42
+ return undefined;
43
+ }
44
+ if (read.kind === "corrupt") {
45
+ await removeCheckpointRegistryFile(dir, name);
46
+ return undefined;
47
+ }
48
+ const entry = read.entry;
49
+ const active = Backends.active();
50
+ if (entry.backend !== active.name) {
51
+ return toCheckpoint(entry);
52
+ }
53
+ const exists = await active.hasCheckpoint(entry.ref);
54
+ if (!exists) {
55
+ await removeCheckpointRegistryFile(dir, name);
56
+ return undefined;
57
+ }
58
+ return toCheckpoint(entry);
59
+ }
60
+ /**
61
+ * Every named checkpoint currently in the registry — registry contents
62
+ * only, never probed against a backend (unlike `find`), so a stale entry
63
+ * whose artifact is gone still appears here until something calls `find` or
64
+ * `remove` on it. A corrupt entry is silently skipped, never removed (only
65
+ * `find`/`remove` clean those up, since `list` never resolves a single name
66
+ * the caller could target for a retry).
67
+ */
68
+ export async function list() {
69
+ const dir = cacheDir();
70
+ const names = await listCheckpointNames(dir);
71
+ const checkpoints = [];
72
+ for (const name of names) {
73
+ const read = await readCheckpointRegistry(dir, name);
74
+ if (read.kind === "found") {
75
+ checkpoints.push(toCheckpoint(read.entry));
76
+ }
77
+ }
78
+ return checkpoints;
79
+ }
80
+ /**
81
+ * Deletes a named checkpoint: best-effort removal of the backend artifact
82
+ * (only when the entry's recorded backend matches the currently active
83
+ * one — this call never touches a backend that isn't active) plus the
84
+ * registry file, regardless of order of failure in either. Idempotent and
85
+ * always best-effort: "not found" anywhere (no registry entry at all) is
86
+ * success, reported as `false`; an existing entry — valid or corrupt —
87
+ * reports `true` once its registry file is gone.
88
+ *
89
+ * When the entry's recorded backend DIFFERS from the currently active one,
90
+ * only the registry record is deleted — the underlying artifact (the docker
91
+ * image, or the microsandbox snapshot) is left on disk PERMANENTLY, since
92
+ * this call never touches a backend that isn't active. Nothing in this
93
+ * library reclaims it automatically — and once the record is gone, a later
94
+ * `remove(name)` finds nothing to act on. Remove a checkpoint under its
95
+ * creating backend in the first place, or clean the leftover artifact
96
+ * directly with that backend's own CLI one-liner (see the
97
+ * [checkpoints guide](/guide/checkpoints#cleanup-checkpoints-are-not-auto-reaped)).
98
+ *
99
+ * `name` is validated against `CHECKPOINT_NAME_PATTERN` before anything else
100
+ * — including before the registry file is even looked up — so a `name`
101
+ * carrying `../` segments can never reach path construction; an invalid name
102
+ * throws `InvalidCheckpointNameError` and touches no file.
103
+ */
104
+ export async function remove(name) {
105
+ requireValidCheckpointName(name);
106
+ const dir = cacheDir();
107
+ const read = await readCheckpointRegistry(dir, name);
108
+ if (read.kind === "missing") {
109
+ return false;
110
+ }
111
+ if (read.kind === "corrupt") {
112
+ await removeCheckpointRegistryFile(dir, name);
113
+ return true;
114
+ }
115
+ const entry = read.entry;
116
+ const active = Backends.active();
117
+ if (entry.backend === active.name) {
118
+ await active.removeCheckpoint(entry.ref).catch(() => { });
119
+ }
120
+ await removeCheckpointRegistryFile(dir, name);
121
+ return true;
122
+ }
123
+ /**
124
+ * The library's entry point for rediscovering NAMED checkpoints across
125
+ * processes — see the [checkpoints guide](/guide/checkpoints#reusing-checkpoints-across-runs)
126
+ * for the `find(...) ?? seed()` first-run/later-run pattern this exists to
127
+ * support. Unnamed `checkpoint()` calls never appear here; only a
128
+ * `checkpoint(name)` call writes a registry entry these functions can find.
129
+ */
130
+ export const Checkpoints = {
131
+ /** Rediscovers a named checkpoint — see `find` above. */
132
+ find,
133
+ /** Every named checkpoint currently in the registry — see `list` above. */
134
+ list,
135
+ /** Deletes a named checkpoint — see `remove` above. */
136
+ remove,
137
+ };
138
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/core/checkpoint/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Pinned identically across every rightsize language implementation (see the
3
+ * named-checkpoints spec's own "Names and refs" section): lowercase letters,
4
+ * digits, and hyphens only, starting with a letter or digit, at most 41
5
+ * characters. This is also exactly the alphabet both backends' ref formats
6
+ * embed verbatim (`rz-ckpt-<name>`, `rightsize/checkpoint:<name>`), so a
7
+ * valid name is guaranteed to produce a valid ref on either backend.
8
+ */
9
+ export declare const CHECKPOINT_NAME_PATTERN: RegExp;
10
+ /** Fails fast — before any backend or filesystem call — on a `name` that doesn't match `CHECKPOINT_NAME_PATTERN`. */
11
+ export declare function requireValidCheckpointName(name: string): void;
12
+ //# sourceMappingURL=name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"name.d.ts","sourceRoot":"","sources":["../../../src/core/checkpoint/name.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,QAA8B,CAAC;AAEnE,qHAAqH;AACrH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAI7D"}
@@ -0,0 +1,17 @@
1
+ import { InvalidCheckpointNameError } from "../errors.js";
2
+ /**
3
+ * Pinned identically across every rightsize language implementation (see the
4
+ * named-checkpoints spec's own "Names and refs" section): lowercase letters,
5
+ * digits, and hyphens only, starting with a letter or digit, at most 41
6
+ * characters. This is also exactly the alphabet both backends' ref formats
7
+ * embed verbatim (`rz-ckpt-<name>`, `rightsize/checkpoint:<name>`), so a
8
+ * valid name is guaranteed to produce a valid ref on either backend.
9
+ */
10
+ export const CHECKPOINT_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,40}$/;
11
+ /** Fails fast — before any backend or filesystem call — on a `name` that doesn't match `CHECKPOINT_NAME_PATTERN`. */
12
+ export function requireValidCheckpointName(name) {
13
+ if (!CHECKPOINT_NAME_PATTERN.test(name)) {
14
+ throw new InvalidCheckpointNameError(name);
15
+ }
16
+ }
17
+ //# sourceMappingURL=name.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"name.js","sourceRoot":"","sources":["../../../src/core/checkpoint/name.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAE1D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,2BAA2B,CAAC;AAEnE,qHAAqH;AACrH,MAAM,UAAU,0BAA0B,CAAC,IAAY;IACrD,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,0BAA0B,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Mints the backend-specific checkpoint ref: `rz-ckpt-<suffix>` on
3
+ * microsandbox, `rightsize/checkpoint:<suffix>` elsewhere. `name` present
4
+ * (a NAMED checkpoint) makes the suffix — and therefore the whole ref —
5
+ * deterministic: re-checkpointing the same name reproduces the exact same
6
+ * ref, which is what makes the registry's replace semantics (remove the old
7
+ * artifact under this ref, then create the new one) correct. `name`
8
+ * `undefined` mints a fresh random 12-hex suffix instead, byte-for-byte the
9
+ * pre-named-checkpoints behavior.
10
+ */
11
+ export declare function checkpointRef(backendName: string, name: string | undefined): string;
12
+ //# sourceMappingURL=ref.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ref.d.ts","sourceRoot":"","sources":["../../../src/core/checkpoint/ref.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAGnF"}
@@ -0,0 +1,16 @@
1
+ import { randomBytes } from "node:crypto";
2
+ /**
3
+ * Mints the backend-specific checkpoint ref: `rz-ckpt-<suffix>` on
4
+ * microsandbox, `rightsize/checkpoint:<suffix>` elsewhere. `name` present
5
+ * (a NAMED checkpoint) makes the suffix — and therefore the whole ref —
6
+ * deterministic: re-checkpointing the same name reproduces the exact same
7
+ * ref, which is what makes the registry's replace semantics (remove the old
8
+ * artifact under this ref, then create the new one) correct. `name`
9
+ * `undefined` mints a fresh random 12-hex suffix instead, byte-for-byte the
10
+ * pre-named-checkpoints behavior.
11
+ */
12
+ export function checkpointRef(backendName, name) {
13
+ const suffix = name ?? randomBytes(6).toString("hex");
14
+ return backendName === "microsandbox" ? `rz-ckpt-${suffix}` : `rightsize/checkpoint:${suffix}`;
15
+ }
16
+ //# sourceMappingURL=ref.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ref.js","sourceRoot":"","sources":["../../../src/core/checkpoint/ref.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,WAAmB,EAAE,IAAwB;IACzE,MAAM,MAAM,GAAG,IAAI,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACtD,OAAO,WAAW,KAAK,cAAc,CAAC,CAAC,CAAC,WAAW,MAAM,EAAE,CAAC,CAAC,CAAC,wBAAwB,MAAM,EAAE,CAAC;AACjG,CAAC"}
@@ -0,0 +1,107 @@
1
+ import type { ContainerSpec } from "../model.js";
2
+ /**
3
+ * The reduced, cross-language-pinned subset of `ContainerSpec` a named
4
+ * checkpoint's registry entry carries — exactly the fields
5
+ * `GenericContainer.fromCheckpoint()` actually reads (env, command, exposed
6
+ * ports, memory limit), never the rest (name, image, host ports, mounts,
7
+ * network topology, `runId`, `keepAlive`). Field names and shapes are part
8
+ * of the wire format: `env` as a plain object (not an array of pairs, unlike
9
+ * `ContainerSpec.env`), `command` as an array or `null` (never `undefined` —
10
+ * JSON has no `undefined`), `exposedPorts` as guest ports only.
11
+ */
12
+ export interface CheckpointRegistrySpec {
13
+ readonly env: Record<string, string>;
14
+ readonly command: ReadonlyArray<string> | null;
15
+ readonly exposedPorts: ReadonlyArray<number>;
16
+ readonly memoryLimitMb: number | null;
17
+ }
18
+ /**
19
+ * One `checkpoints/<name>.json` record: written atomically only after the
20
+ * backend checkpoint it describes has actually succeeded, and read by
21
+ * `Checkpoints.find`/`list`/`remove` in this process or a later one. Every
22
+ * field here is part of the cross-language contract pinned by the
23
+ * named-checkpoints spec — a Kotlin or Rust process must be able to parse
24
+ * exactly this shape.
25
+ */
26
+ export interface CheckpointRegistryEntry {
27
+ readonly name: string;
28
+ readonly ref: string;
29
+ /** The backend that created this checkpoint (e.g. `"microsandbox"`, `"docker"`) — `find`/`remove` only probe/touch the artifact when this matches the CURRENTLY active backend. */
30
+ readonly backend: string;
31
+ readonly createdIso: string;
32
+ readonly spec: CheckpointRegistrySpec;
33
+ }
34
+ /** `<cacheDir>/checkpoints` — the directory every named checkpoint's registry file lives under. */
35
+ export declare function checkpointsDir(cacheDir: string): string;
36
+ /**
37
+ * `checkpoints/<name>.json`. Validates `name` against
38
+ * `CHECKPOINT_NAME_PATTERN` itself (throwing `InvalidCheckpointNameError` on
39
+ * a miss) rather than trusting every caller to have done so already — this
40
+ * is the one function every registry read/write ultimately funnels through
41
+ * to build a path, so a defensive check here is what stands between a `../`
42
+ * name and a path that escapes `checkpoints/` even if some future caller
43
+ * forgets `requireValidCheckpointName` at its own boundary.
44
+ */
45
+ export declare function checkpointRegistryPath(cacheDir: string, name: string): string;
46
+ /** The three outcomes reading a registry file can settle to — corrupt is deliberately distinct from missing, since `find`/`remove` react differently to each (see their own docs). */
47
+ export type CheckpointRegistryReadResult = {
48
+ readonly kind: "missing";
49
+ } | {
50
+ readonly kind: "corrupt";
51
+ } | {
52
+ readonly kind: "found";
53
+ readonly entry: CheckpointRegistryEntry;
54
+ };
55
+ /**
56
+ * Reads and parses `checkpoints/<name>.json`. `"missing"` means the file
57
+ * does not exist at all; `"corrupt"` means it exists but isn't a well-shaped
58
+ * `CheckpointRegistryEntry` — malformed JSON or a missing/mistyped required
59
+ * field. An invalid `name` (see `checkpointRegistryPath`) throws
60
+ * `InvalidCheckpointNameError` rather than resolving `"missing"` — the path
61
+ * is resolved BEFORE the file-read `try`, so that throw is never mistaken
62
+ * for an ordinary "no file here" miss.
63
+ */
64
+ export declare function readCheckpointRegistry(cacheDir: string, name: string): Promise<CheckpointRegistryReadResult>;
65
+ /**
66
+ * Atomically writes `checkpoints/<name>.json` (tmp file + rename, the same
67
+ * protocol as the reuse registry's `writeRegistryAtomic` and the reaping
68
+ * ledger's `writeRunRecord`) — called once, only after the backend
69
+ * checkpoint this entry describes has already succeeded. A concurrent reader
70
+ * only ever observes either the previous complete file (if this is a
71
+ * replace) or this one, never a partial write.
72
+ */
73
+ export declare function writeCheckpointRegistryAtomic(cacheDir: string, name: string, entry: CheckpointRegistryEntry): Promise<void>;
74
+ /** Best-effort delete of `checkpoints/<name>.json`. A file already gone is not an error. */
75
+ export declare function removeCheckpointRegistryFile(cacheDir: string, name: string): Promise<void>;
76
+ /**
77
+ * Every checkpoint name with a registry file on disk, derived from the
78
+ * directory listing itself (not from parsing) — includes names whose file
79
+ * turns out to be corrupt; `Checkpoints.list()` filters those out itself
80
+ * after reading each one. Excludes the atomic-write tmp files (`.<name>.json.tmp-...`),
81
+ * which sort before their target thanks to the leading dot but are never a
82
+ * real entry. `checkpoints/` not existing yet (nothing ever checkpointed
83
+ * with a name) is not an error — resolves to an empty list.
84
+ */
85
+ export declare function listCheckpointNames(cacheDir: string): Promise<string[]>;
86
+ /**
87
+ * The write-side projection: `handle.spec` (a full `ContainerSpec`) down to
88
+ * the reduced, pinned shape the registry persists. `undefined` fields
89
+ * normalize to `null` (JSON has no `undefined`); `ports` keeps only the
90
+ * guest side (the source container's own host ports are never meaningful to
91
+ * a later restore, which allocates fresh ones).
92
+ */
93
+ export declare function toCheckpointRegistrySpec(spec: ContainerSpec): CheckpointRegistrySpec;
94
+ /**
95
+ * The read-side counterpart: reconstructs a `ContainerSpec`-shaped object
96
+ * from a persisted `CheckpointRegistryEntry`, for handing back as a
97
+ * `Checkpoint`'s `spec` from `Checkpoints.find`/`list` (which never held the
98
+ * source container's actual `ContainerSpec`, only what the registry
99
+ * persisted). Only the four fields `GenericContainer.fromCheckpoint()`
100
+ * itself reads — `env`, `command`, `ports` (guest side only), and
101
+ * `memoryLimitMb` — carry real information; every other field is a stable
102
+ * placeholder, since the registry never persists it. `checkpointRef` mirrors
103
+ * what a live backend hands back after its own reboot-from-snapshot cycle:
104
+ * pointing at itself.
105
+ */
106
+ export declare function fromCheckpointRegistryEntry(entry: CheckpointRegistryEntry): ContainerSpec;
107
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/core/checkpoint/registry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD;;;;;;;;;GASG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IAC/C,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,mLAAmL;IACnL,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;CACvC;AAED,mGAAmG;AACnG,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7E;AAkDD,sLAAsL;AACtL,MAAM,MAAM,4BAA4B,GACpC;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,uBAAuB,CAAA;CAAE,CAAC;AAExE;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAkBlH;AAED;;;;;;;GAOG;AACH,wBAAsB,6BAA6B,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOjI;AAED,4FAA4F;AAC5F,wBAAsB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhG;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAQ7E;AAOD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,aAAa,GAAG,sBAAsB,CAWpF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,uBAAuB,GAAG,aAAa,CAezF"}
@@ -0,0 +1,180 @@
1
+ import * as fsp from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import { requireValidCheckpointName } from "./name.js";
4
+ /** `<cacheDir>/checkpoints` — the directory every named checkpoint's registry file lives under. */
5
+ export function checkpointsDir(cacheDir) {
6
+ return path.join(cacheDir, "checkpoints");
7
+ }
8
+ /**
9
+ * `checkpoints/<name>.json`. Validates `name` against
10
+ * `CHECKPOINT_NAME_PATTERN` itself (throwing `InvalidCheckpointNameError` on
11
+ * a miss) rather than trusting every caller to have done so already — this
12
+ * is the one function every registry read/write ultimately funnels through
13
+ * to build a path, so a defensive check here is what stands between a `../`
14
+ * name and a path that escapes `checkpoints/` even if some future caller
15
+ * forgets `requireValidCheckpointName` at its own boundary.
16
+ */
17
+ export function checkpointRegistryPath(cacheDir, name) {
18
+ requireValidCheckpointName(name);
19
+ return path.join(checkpointsDir(cacheDir), `${name}.json`);
20
+ }
21
+ function isCheckpointRegistrySpec(value) {
22
+ if (typeof value !== "object" || value === null) {
23
+ return false;
24
+ }
25
+ const rec = value;
26
+ const env = rec["env"];
27
+ if (typeof env !== "object" || env === null || Array.isArray(env)) {
28
+ return false;
29
+ }
30
+ if (!Object.values(env).every((v) => typeof v === "string")) {
31
+ return false;
32
+ }
33
+ const command = rec["command"];
34
+ if (command !== null && !(Array.isArray(command) && command.every((c) => typeof c === "string"))) {
35
+ return false;
36
+ }
37
+ const exposedPorts = rec["exposedPorts"];
38
+ if (!Array.isArray(exposedPorts) || !exposedPorts.every((p) => typeof p === "number")) {
39
+ return false;
40
+ }
41
+ const memoryLimitMb = rec["memoryLimitMb"];
42
+ if (memoryLimitMb !== null && typeof memoryLimitMb !== "number") {
43
+ return false;
44
+ }
45
+ return true;
46
+ }
47
+ function isCheckpointRegistryEntry(value) {
48
+ if (typeof value !== "object" || value === null) {
49
+ return false;
50
+ }
51
+ const rec = value;
52
+ if (typeof rec["name"] !== "string" ||
53
+ typeof rec["ref"] !== "string" ||
54
+ typeof rec["backend"] !== "string" ||
55
+ typeof rec["createdIso"] !== "string") {
56
+ return false;
57
+ }
58
+ return isCheckpointRegistrySpec(rec["spec"]);
59
+ }
60
+ /**
61
+ * Reads and parses `checkpoints/<name>.json`. `"missing"` means the file
62
+ * does not exist at all; `"corrupt"` means it exists but isn't a well-shaped
63
+ * `CheckpointRegistryEntry` — malformed JSON or a missing/mistyped required
64
+ * field. An invalid `name` (see `checkpointRegistryPath`) throws
65
+ * `InvalidCheckpointNameError` rather than resolving `"missing"` — the path
66
+ * is resolved BEFORE the file-read `try`, so that throw is never mistaken
67
+ * for an ordinary "no file here" miss.
68
+ */
69
+ export async function readCheckpointRegistry(cacheDir, name) {
70
+ const registryPath = checkpointRegistryPath(cacheDir, name);
71
+ let text;
72
+ try {
73
+ text = await fsp.readFile(registryPath, "utf8");
74
+ }
75
+ catch {
76
+ return { kind: "missing" };
77
+ }
78
+ let parsed;
79
+ try {
80
+ parsed = JSON.parse(text);
81
+ }
82
+ catch {
83
+ return { kind: "corrupt" };
84
+ }
85
+ if (!isCheckpointRegistryEntry(parsed)) {
86
+ return { kind: "corrupt" };
87
+ }
88
+ return { kind: "found", entry: parsed };
89
+ }
90
+ /**
91
+ * Atomically writes `checkpoints/<name>.json` (tmp file + rename, the same
92
+ * protocol as the reuse registry's `writeRegistryAtomic` and the reaping
93
+ * ledger's `writeRunRecord`) — called once, only after the backend
94
+ * checkpoint this entry describes has already succeeded. A concurrent reader
95
+ * only ever observes either the previous complete file (if this is a
96
+ * replace) or this one, never a partial write.
97
+ */
98
+ export async function writeCheckpointRegistryAtomic(cacheDir, name, entry) {
99
+ const dir = checkpointsDir(cacheDir);
100
+ await fsp.mkdir(dir, { recursive: true });
101
+ const target = checkpointRegistryPath(cacheDir, name);
102
+ const tmp = path.join(dir, `.${name}.json.tmp-${process.pid}-${Date.now()}`);
103
+ await fsp.writeFile(tmp, JSON.stringify(entry));
104
+ await fsp.rename(tmp, target);
105
+ }
106
+ /** Best-effort delete of `checkpoints/<name>.json`. A file already gone is not an error. */
107
+ export async function removeCheckpointRegistryFile(cacheDir, name) {
108
+ await fsp.unlink(checkpointRegistryPath(cacheDir, name)).catch(() => { });
109
+ }
110
+ /**
111
+ * Every checkpoint name with a registry file on disk, derived from the
112
+ * directory listing itself (not from parsing) — includes names whose file
113
+ * turns out to be corrupt; `Checkpoints.list()` filters those out itself
114
+ * after reading each one. Excludes the atomic-write tmp files (`.<name>.json.tmp-...`),
115
+ * which sort before their target thanks to the leading dot but are never a
116
+ * real entry. `checkpoints/` not existing yet (nothing ever checkpointed
117
+ * with a name) is not an error — resolves to an empty list.
118
+ */
119
+ export async function listCheckpointNames(cacheDir) {
120
+ let entries;
121
+ try {
122
+ entries = await fsp.readdir(checkpointsDir(cacheDir));
123
+ }
124
+ catch {
125
+ return [];
126
+ }
127
+ return entries.filter((f) => f.endsWith(".json") && !f.startsWith(".")).map((f) => f.slice(0, -".json".length));
128
+ }
129
+ /** The inverse of the env part of `toCheckpointRegistrySpec`: `Record<string,string>` back to the array-of-pairs shape `ContainerSpec.env` uses. */
130
+ function envRecordToPairs(env) {
131
+ return Object.entries(env);
132
+ }
133
+ /**
134
+ * The write-side projection: `handle.spec` (a full `ContainerSpec`) down to
135
+ * the reduced, pinned shape the registry persists. `undefined` fields
136
+ * normalize to `null` (JSON has no `undefined`); `ports` keeps only the
137
+ * guest side (the source container's own host ports are never meaningful to
138
+ * a later restore, which allocates fresh ones).
139
+ */
140
+ export function toCheckpointRegistrySpec(spec) {
141
+ const env = {};
142
+ for (const [key, value] of spec.env) {
143
+ env[key] = value;
144
+ }
145
+ return {
146
+ env,
147
+ command: spec.command ?? null,
148
+ exposedPorts: spec.ports.map((p) => p.guestPort),
149
+ memoryLimitMb: spec.memoryLimitMb ?? null,
150
+ };
151
+ }
152
+ /**
153
+ * The read-side counterpart: reconstructs a `ContainerSpec`-shaped object
154
+ * from a persisted `CheckpointRegistryEntry`, for handing back as a
155
+ * `Checkpoint`'s `spec` from `Checkpoints.find`/`list` (which never held the
156
+ * source container's actual `ContainerSpec`, only what the registry
157
+ * persisted). Only the four fields `GenericContainer.fromCheckpoint()`
158
+ * itself reads — `env`, `command`, `ports` (guest side only), and
159
+ * `memoryLimitMb` — carry real information; every other field is a stable
160
+ * placeholder, since the registry never persists it. `checkpointRef` mirrors
161
+ * what a live backend hands back after its own reboot-from-snapshot cycle:
162
+ * pointing at itself.
163
+ */
164
+ export function fromCheckpointRegistryEntry(entry) {
165
+ return {
166
+ name: entry.name,
167
+ image: entry.ref,
168
+ env: envRecordToPairs(entry.spec.env),
169
+ command: entry.spec.command ?? undefined,
170
+ ports: entry.spec.exposedPorts.map((guestPort) => ({ hostPort: 0, guestPort })),
171
+ mounts: [],
172
+ networkId: undefined,
173
+ aliases: [],
174
+ runId: "",
175
+ memoryLimitMb: entry.spec.memoryLimitMb ?? undefined,
176
+ keepAlive: false,
177
+ checkpointRef: entry.ref,
178
+ };
179
+ }
180
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/core/checkpoint/registry.ts"],"names":[],"mappings":"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"}
@@ -77,10 +77,11 @@ export declare class IsolationRequiredError extends Error {
77
77
  }
78
78
  /**
79
79
  * Thrown by `checkpoint()` when the active backend's
80
- * `capabilities.checkpoint` is `false` — microsandbox has no upstream
81
- * microVM snapshot support today. Thrown before any backend call: the
82
- * generic layer gates on the capability itself rather than letting the
83
- * backend's own `commitToImage` reject.
80
+ * `capabilities.checkpoint` is `false` — both real backends (docker, image
81
+ * commit; microsandbox, disk snapshot) support it today, so this only fires
82
+ * against a backend that genuinely lacks the capability (a test double).
83
+ * Thrown before any backend call: the generic layer gates on the capability
84
+ * itself rather than letting the backend's own `createCheckpoint` reject.
84
85
  */
85
86
  export declare class CheckpointUnsupportedError extends Error {
86
87
  /** The active backend's name (e.g. `"microsandbox"`). */
@@ -89,4 +90,59 @@ export declare class CheckpointUnsupportedError extends Error {
89
90
  /** The active backend's name (e.g. `"microsandbox"`). */
90
91
  backend: string);
91
92
  }
93
+ /**
94
+ * Thrown by `GenericContainer.fromCheckpoint(cp).start()` when the active
95
+ * backend's name doesn't match the backend that created `cp` — a checkpoint
96
+ * ref is only meaningful to the backend that minted it (a docker image tag
97
+ * means nothing to `msb`, and a msb snapshot name means nothing to docker).
98
+ * Thrown before any backend call.
99
+ */
100
+ export declare class CheckpointBackendMismatchError extends Error {
101
+ /** The backend that created the checkpoint. */
102
+ readonly createdOnBackend: string;
103
+ /** The backend `start()` actually resolved. */
104
+ readonly activeBackend: string;
105
+ constructor(
106
+ /** The backend that created the checkpoint. */
107
+ createdOnBackend: string,
108
+ /** The backend `start()` actually resolved. */
109
+ activeBackend: string);
110
+ }
111
+ /**
112
+ * Thrown at `start()` when a container built via `GenericContainer.fromCheckpoint()`
113
+ * is also marked `withReuse()` — reuse's identity hash never covers
114
+ * `checkpointRef`, so an adopted sandbox from an earlier process could never
115
+ * be verified against the checkpoint this container was meant to restore.
116
+ * Thrown only once reuse is actually double opt-in active, the same
117
+ * placement as `ReuseWithNetworkError`.
118
+ */
119
+ export declare class ReuseFromCheckpointError extends Error {
120
+ constructor();
121
+ }
122
+ /**
123
+ * Thrown by `copyFileToContainer`/`copyContentToContainer`/
124
+ * `copyFileFromContainer` when `containerPath` is not absolute — both
125
+ * backends require an absolute `NAME:/path` shape, so a relative path can
126
+ * never reach either CLI. Thrown before any backend call.
127
+ */
128
+ export declare class RelativeContainerPathError extends Error {
129
+ /** The rejected path, exactly as passed in. */
130
+ readonly containerPath: string;
131
+ constructor(
132
+ /** The rejected path, exactly as passed in. */
133
+ containerPath: string);
134
+ }
135
+ /**
136
+ * Thrown by `checkpoint(name)` when `name` doesn't match
137
+ * `^[a-z0-9][a-z0-9-]{0,40}$` — the same pattern pinned across every
138
+ * rightsize language implementation. Thrown before any backend or
139
+ * filesystem call, so a bad name never mints a ref or touches the registry.
140
+ */
141
+ export declare class InvalidCheckpointNameError extends Error {
142
+ /** The rejected name, exactly as passed in. */
143
+ readonly checkpointName: string;
144
+ constructor(
145
+ /** The rejected name, exactly as passed in. */
146
+ checkpointName: string);
147
+ }
92
148
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;IAEhD,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,qFAAqF;IACrF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM;;IALxB,uDAAuD;IAC9C,OAAO,EAAE,MAAM;IACxB,yDAAyD;IAChD,OAAO,EAAE,MAAM;IACxB,qFAAqF;IAC5E,MAAM,CAAC,EAAE,MAAM,YAAA;CAK3B;AAED;;;;;;GAMG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAG5C,4FAA4F;IAC5F,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO;gBAFxB,OAAO,EAAE,MAAM;IACf,4FAA4F;IACnF,KAAK,CAAC,EAAE,OAAO,YAAA;CAK3B;AAED,gFAAgF;AAChF,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AAED,8EAA8E;AAC9E,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAED,kIAAkI;AAClI,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;GASG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;;CAS/C;AAED;;;;;;GAMG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAE7C,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,MAAM;;IADxB,mDAAmD;IAC1C,OAAO,EAAE,MAAM;CAS3B;AAED;;;;;;GAMG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;IAEjD,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM;;IADxB,yDAAyD;IAChD,OAAO,EAAE,MAAM;CAS3B"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;IAEhD,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,qFAAqF;IACrF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM;;IALxB,uDAAuD;IAC9C,OAAO,EAAE,MAAM;IACxB,yDAAyD;IAChD,OAAO,EAAE,MAAM;IACxB,qFAAqF;IAC5E,MAAM,CAAC,EAAE,MAAM,YAAA;CAK3B;AAED;;;;;;GAMG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAG5C,4FAA4F;IAC5F,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO;gBAFxB,OAAO,EAAE,MAAM;IACf,4FAA4F;IACnF,KAAK,CAAC,EAAE,OAAO,YAAA;CAK3B;AAED,gFAAgF;AAChF,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AAED,8EAA8E;AAC9E,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAED,kIAAkI;AAClI,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;GASG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;;CAS/C;AAED;;;;;;GAMG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAE7C,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,MAAM;;IADxB,mDAAmD;IAC1C,OAAO,EAAE,MAAM;CAS3B;AAED;;;;;;;GAOG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;IAEjD,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM;;IADxB,yDAAyD;IAChD,OAAO,EAAE,MAAM;CAQ3B;AAED;;;;;;GAMG;AACH,qBAAa,8BAA+B,SAAQ,KAAK;IAErD,+CAA+C;IAC/C,QAAQ,CAAC,gBAAgB,EAAE,MAAM;IACjC,+CAA+C;IAC/C,QAAQ,CAAC,aAAa,EAAE,MAAM;;IAH9B,+CAA+C;IACtC,gBAAgB,EAAE,MAAM;IACjC,+CAA+C;IACtC,aAAa,EAAE,MAAM;CASjC;AAED;;;;;;;GAOG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;;CASlD;AAED;;;;;GAKG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;IAEjD,+CAA+C;IAC/C,QAAQ,CAAC,aAAa,EAAE,MAAM;;IAD9B,+CAA+C;IACtC,aAAa,EAAE,MAAM;CAKjC;AAED;;;;;GAKG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;IAEjD,+CAA+C;IAC/C,QAAQ,CAAC,cAAc,EAAE,MAAM;;IAD/B,+CAA+C;IACtC,cAAc,EAAE,MAAM;CASlC"}