rightsize 0.2.0 → 0.4.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.
- package/README.md +36 -26
- package/dist/backend-docker/backend.d.ts +41 -4
- package/dist/backend-docker/backend.d.ts.map +1 -1
- package/dist/backend-docker/backend.js +84 -6
- package/dist/backend-docker/backend.js.map +1 -1
- package/dist/backend-docker/cli.d.ts +29 -0
- package/dist/backend-docker/cli.d.ts.map +1 -0
- package/dist/backend-docker/cli.js +72 -0
- package/dist/backend-docker/cli.js.map +1 -0
- package/dist/backend-msb/backend.d.ts +91 -7
- package/dist/backend-msb/backend.d.ts.map +1 -1
- package/dist/backend-msb/backend.js +185 -11
- package/dist/backend-msb/backend.js.map +1 -1
- package/dist/backend-msb/commands.d.ts +16 -0
- package/dist/backend-msb/commands.d.ts.map +1 -1
- package/dist/backend-msb/commands.js +41 -1
- package/dist/backend-msb/commands.js.map +1 -1
- package/dist/backend-msb/snapshot-import.d.ts +33 -0
- package/dist/backend-msb/snapshot-import.d.ts.map +1 -0
- package/dist/backend-msb/snapshot-import.js +48 -0
- package/dist/backend-msb/snapshot-import.js.map +1 -0
- package/dist/backend-msb/snapshot-list.d.ts +43 -0
- package/dist/backend-msb/snapshot-list.d.ts.map +1 -0
- package/dist/backend-msb/snapshot-list.js +53 -0
- package/dist/backend-msb/snapshot-list.js.map +1 -0
- package/dist/backend-msb/snapshot-not-found.d.ts +25 -0
- package/dist/backend-msb/snapshot-not-found.d.ts.map +1 -0
- package/dist/backend-msb/snapshot-not-found.js +27 -0
- package/dist/backend-msb/snapshot-not-found.js.map +1 -0
- package/dist/core/backend.d.ts +86 -6
- package/dist/core/backend.d.ts.map +1 -1
- package/dist/core/checkpoint/api.d.ts +129 -0
- package/dist/core/checkpoint/api.d.ts.map +1 -0
- package/dist/core/checkpoint/api.js +261 -0
- package/dist/core/checkpoint/api.js.map +1 -0
- package/dist/core/checkpoint/archive.d.ts +52 -0
- package/dist/core/checkpoint/archive.d.ts.map +1 -0
- package/dist/core/checkpoint/archive.js +134 -0
- package/dist/core/checkpoint/archive.js.map +1 -0
- package/dist/core/checkpoint/name.d.ts +12 -0
- package/dist/core/checkpoint/name.d.ts.map +1 -0
- package/dist/core/checkpoint/name.js +17 -0
- package/dist/core/checkpoint/name.js.map +1 -0
- package/dist/core/checkpoint/ref.d.ts +12 -0
- package/dist/core/checkpoint/ref.d.ts.map +1 -0
- package/dist/core/checkpoint/ref.js +16 -0
- package/dist/core/checkpoint/ref.js.map +1 -0
- package/dist/core/checkpoint/registry.d.ts +109 -0
- package/dist/core/checkpoint/registry.d.ts.map +1 -0
- package/dist/core/checkpoint/registry.js +181 -0
- package/dist/core/checkpoint/registry.js.map +1 -0
- package/dist/core/checkpoint/tar-cli.d.ts +45 -0
- package/dist/core/checkpoint/tar-cli.d.ts.map +1 -0
- package/dist/core/checkpoint/tar-cli.js +86 -0
- package/dist/core/checkpoint/tar-cli.js.map +1 -0
- package/dist/core/errors.d.ts +97 -4
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +124 -7
- package/dist/core/errors.js.map +1 -1
- package/dist/core/generic-container.d.ts +87 -18
- package/dist/core/generic-container.d.ts.map +1 -1
- package/dist/core/generic-container.js +171 -22
- package/dist/core/generic-container.js.map +1 -1
- package/dist/core/model.d.ts +25 -8
- package/dist/core/model.d.ts.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/modules/keycloak.js +1 -1
- package/dist/modules/mysql.d.ts.map +1 -1
- package/dist/modules/mysql.js +4 -3
- package/dist/modules/mysql.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,109 @@
|
|
|
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
|
+
/** Structural validator for `CheckpointRegistrySpec` — also the shape a checkpoint archive's `checkpoint.json` `spec` field must match, see `checkpoint/archive.ts`. */
|
|
47
|
+
export declare function isCheckpointRegistrySpec(value: unknown): value is CheckpointRegistrySpec;
|
|
48
|
+
/** 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). */
|
|
49
|
+
export type CheckpointRegistryReadResult = {
|
|
50
|
+
readonly kind: "missing";
|
|
51
|
+
} | {
|
|
52
|
+
readonly kind: "corrupt";
|
|
53
|
+
} | {
|
|
54
|
+
readonly kind: "found";
|
|
55
|
+
readonly entry: CheckpointRegistryEntry;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Reads and parses `checkpoints/<name>.json`. `"missing"` means the file
|
|
59
|
+
* does not exist at all; `"corrupt"` means it exists but isn't a well-shaped
|
|
60
|
+
* `CheckpointRegistryEntry` — malformed JSON or a missing/mistyped required
|
|
61
|
+
* field. An invalid `name` (see `checkpointRegistryPath`) throws
|
|
62
|
+
* `InvalidCheckpointNameError` rather than resolving `"missing"` — the path
|
|
63
|
+
* is resolved BEFORE the file-read `try`, so that throw is never mistaken
|
|
64
|
+
* for an ordinary "no file here" miss.
|
|
65
|
+
*/
|
|
66
|
+
export declare function readCheckpointRegistry(cacheDir: string, name: string): Promise<CheckpointRegistryReadResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Atomically writes `checkpoints/<name>.json` (tmp file + rename, the same
|
|
69
|
+
* protocol as the reuse registry's `writeRegistryAtomic` and the reaping
|
|
70
|
+
* ledger's `writeRunRecord`) — called once, only after the backend
|
|
71
|
+
* checkpoint this entry describes has already succeeded. A concurrent reader
|
|
72
|
+
* only ever observes either the previous complete file (if this is a
|
|
73
|
+
* replace) or this one, never a partial write.
|
|
74
|
+
*/
|
|
75
|
+
export declare function writeCheckpointRegistryAtomic(cacheDir: string, name: string, entry: CheckpointRegistryEntry): Promise<void>;
|
|
76
|
+
/** Best-effort delete of `checkpoints/<name>.json`. A file already gone is not an error. */
|
|
77
|
+
export declare function removeCheckpointRegistryFile(cacheDir: string, name: string): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Every checkpoint name with a registry file on disk, derived from the
|
|
80
|
+
* directory listing itself (not from parsing) — includes names whose file
|
|
81
|
+
* turns out to be corrupt; `Checkpoints.list()` filters those out itself
|
|
82
|
+
* after reading each one. Excludes the atomic-write tmp files (`.<name>.json.tmp-...`),
|
|
83
|
+
* which sort before their target thanks to the leading dot but are never a
|
|
84
|
+
* real entry. `checkpoints/` not existing yet (nothing ever checkpointed
|
|
85
|
+
* with a name) is not an error — resolves to an empty list.
|
|
86
|
+
*/
|
|
87
|
+
export declare function listCheckpointNames(cacheDir: string): Promise<string[]>;
|
|
88
|
+
/**
|
|
89
|
+
* The write-side projection: `handle.spec` (a full `ContainerSpec`) down to
|
|
90
|
+
* the reduced, pinned shape the registry persists. `undefined` fields
|
|
91
|
+
* normalize to `null` (JSON has no `undefined`); `ports` keeps only the
|
|
92
|
+
* guest side (the source container's own host ports are never meaningful to
|
|
93
|
+
* a later restore, which allocates fresh ones).
|
|
94
|
+
*/
|
|
95
|
+
export declare function toCheckpointRegistrySpec(spec: ContainerSpec): CheckpointRegistrySpec;
|
|
96
|
+
/**
|
|
97
|
+
* The read-side counterpart: reconstructs a `ContainerSpec`-shaped object
|
|
98
|
+
* from a persisted `CheckpointRegistryEntry`, for handing back as a
|
|
99
|
+
* `Checkpoint`'s `spec` from `Checkpoints.find`/`list` (which never held the
|
|
100
|
+
* source container's actual `ContainerSpec`, only what the registry
|
|
101
|
+
* persisted). Only the four fields `GenericContainer.fromCheckpoint()`
|
|
102
|
+
* itself reads — `env`, `command`, `ports` (guest side only), and
|
|
103
|
+
* `memoryLimitMb` — carry real information; every other field is a stable
|
|
104
|
+
* placeholder, since the registry never persists it. `checkpointRef` mirrors
|
|
105
|
+
* what a live backend hands back after its own reboot-from-snapshot cycle:
|
|
106
|
+
* pointing at itself.
|
|
107
|
+
*/
|
|
108
|
+
export declare function fromCheckpointRegistryEntry(entry: CheckpointRegistryEntry): ContainerSpec;
|
|
109
|
+
//# 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;AAED,wKAAwK;AACxK,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,sBAAsB,CA8BxF;AAkBD,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,181 @@
|
|
|
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
|
+
/** Structural validator for `CheckpointRegistrySpec` — also the shape a checkpoint archive's `checkpoint.json` `spec` field must match, see `checkpoint/archive.ts`. */
|
|
22
|
+
export function isCheckpointRegistrySpec(value) {
|
|
23
|
+
if (typeof value !== "object" || value === null) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
const rec = value;
|
|
27
|
+
const env = rec["env"];
|
|
28
|
+
if (typeof env !== "object" || env === null || Array.isArray(env)) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
if (!Object.values(env).every((v) => typeof v === "string")) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const command = rec["command"];
|
|
35
|
+
if (command !== null && !(Array.isArray(command) && command.every((c) => typeof c === "string"))) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const exposedPorts = rec["exposedPorts"];
|
|
39
|
+
if (!Array.isArray(exposedPorts) || !exposedPorts.every((p) => typeof p === "number")) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const memoryLimitMb = rec["memoryLimitMb"];
|
|
43
|
+
if (memoryLimitMb !== null && typeof memoryLimitMb !== "number") {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
function isCheckpointRegistryEntry(value) {
|
|
49
|
+
if (typeof value !== "object" || value === null) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const rec = value;
|
|
53
|
+
if (typeof rec["name"] !== "string" ||
|
|
54
|
+
typeof rec["ref"] !== "string" ||
|
|
55
|
+
typeof rec["backend"] !== "string" ||
|
|
56
|
+
typeof rec["createdIso"] !== "string") {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return isCheckpointRegistrySpec(rec["spec"]);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Reads and parses `checkpoints/<name>.json`. `"missing"` means the file
|
|
63
|
+
* does not exist at all; `"corrupt"` means it exists but isn't a well-shaped
|
|
64
|
+
* `CheckpointRegistryEntry` — malformed JSON or a missing/mistyped required
|
|
65
|
+
* field. An invalid `name` (see `checkpointRegistryPath`) throws
|
|
66
|
+
* `InvalidCheckpointNameError` rather than resolving `"missing"` — the path
|
|
67
|
+
* is resolved BEFORE the file-read `try`, so that throw is never mistaken
|
|
68
|
+
* for an ordinary "no file here" miss.
|
|
69
|
+
*/
|
|
70
|
+
export async function readCheckpointRegistry(cacheDir, name) {
|
|
71
|
+
const registryPath = checkpointRegistryPath(cacheDir, name);
|
|
72
|
+
let text;
|
|
73
|
+
try {
|
|
74
|
+
text = await fsp.readFile(registryPath, "utf8");
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return { kind: "missing" };
|
|
78
|
+
}
|
|
79
|
+
let parsed;
|
|
80
|
+
try {
|
|
81
|
+
parsed = JSON.parse(text);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return { kind: "corrupt" };
|
|
85
|
+
}
|
|
86
|
+
if (!isCheckpointRegistryEntry(parsed)) {
|
|
87
|
+
return { kind: "corrupt" };
|
|
88
|
+
}
|
|
89
|
+
return { kind: "found", entry: parsed };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Atomically writes `checkpoints/<name>.json` (tmp file + rename, the same
|
|
93
|
+
* protocol as the reuse registry's `writeRegistryAtomic` and the reaping
|
|
94
|
+
* ledger's `writeRunRecord`) — called once, only after the backend
|
|
95
|
+
* checkpoint this entry describes has already succeeded. A concurrent reader
|
|
96
|
+
* only ever observes either the previous complete file (if this is a
|
|
97
|
+
* replace) or this one, never a partial write.
|
|
98
|
+
*/
|
|
99
|
+
export async function writeCheckpointRegistryAtomic(cacheDir, name, entry) {
|
|
100
|
+
const dir = checkpointsDir(cacheDir);
|
|
101
|
+
await fsp.mkdir(dir, { recursive: true });
|
|
102
|
+
const target = checkpointRegistryPath(cacheDir, name);
|
|
103
|
+
const tmp = path.join(dir, `.${name}.json.tmp-${process.pid}-${Date.now()}`);
|
|
104
|
+
await fsp.writeFile(tmp, JSON.stringify(entry));
|
|
105
|
+
await fsp.rename(tmp, target);
|
|
106
|
+
}
|
|
107
|
+
/** Best-effort delete of `checkpoints/<name>.json`. A file already gone is not an error. */
|
|
108
|
+
export async function removeCheckpointRegistryFile(cacheDir, name) {
|
|
109
|
+
await fsp.unlink(checkpointRegistryPath(cacheDir, name)).catch(() => { });
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Every checkpoint name with a registry file on disk, derived from the
|
|
113
|
+
* directory listing itself (not from parsing) — includes names whose file
|
|
114
|
+
* turns out to be corrupt; `Checkpoints.list()` filters those out itself
|
|
115
|
+
* after reading each one. Excludes the atomic-write tmp files (`.<name>.json.tmp-...`),
|
|
116
|
+
* which sort before their target thanks to the leading dot but are never a
|
|
117
|
+
* real entry. `checkpoints/` not existing yet (nothing ever checkpointed
|
|
118
|
+
* with a name) is not an error — resolves to an empty list.
|
|
119
|
+
*/
|
|
120
|
+
export async function listCheckpointNames(cacheDir) {
|
|
121
|
+
let entries;
|
|
122
|
+
try {
|
|
123
|
+
entries = await fsp.readdir(checkpointsDir(cacheDir));
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
return entries.filter((f) => f.endsWith(".json") && !f.startsWith(".")).map((f) => f.slice(0, -".json".length));
|
|
129
|
+
}
|
|
130
|
+
/** The inverse of the env part of `toCheckpointRegistrySpec`: `Record<string,string>` back to the array-of-pairs shape `ContainerSpec.env` uses. */
|
|
131
|
+
function envRecordToPairs(env) {
|
|
132
|
+
return Object.entries(env);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* The write-side projection: `handle.spec` (a full `ContainerSpec`) down to
|
|
136
|
+
* the reduced, pinned shape the registry persists. `undefined` fields
|
|
137
|
+
* normalize to `null` (JSON has no `undefined`); `ports` keeps only the
|
|
138
|
+
* guest side (the source container's own host ports are never meaningful to
|
|
139
|
+
* a later restore, which allocates fresh ones).
|
|
140
|
+
*/
|
|
141
|
+
export function toCheckpointRegistrySpec(spec) {
|
|
142
|
+
const env = {};
|
|
143
|
+
for (const [key, value] of spec.env) {
|
|
144
|
+
env[key] = value;
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
env,
|
|
148
|
+
command: spec.command ?? null,
|
|
149
|
+
exposedPorts: spec.ports.map((p) => p.guestPort),
|
|
150
|
+
memoryLimitMb: spec.memoryLimitMb ?? null,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The read-side counterpart: reconstructs a `ContainerSpec`-shaped object
|
|
155
|
+
* from a persisted `CheckpointRegistryEntry`, for handing back as a
|
|
156
|
+
* `Checkpoint`'s `spec` from `Checkpoints.find`/`list` (which never held the
|
|
157
|
+
* source container's actual `ContainerSpec`, only what the registry
|
|
158
|
+
* persisted). Only the four fields `GenericContainer.fromCheckpoint()`
|
|
159
|
+
* itself reads — `env`, `command`, `ports` (guest side only), and
|
|
160
|
+
* `memoryLimitMb` — carry real information; every other field is a stable
|
|
161
|
+
* placeholder, since the registry never persists it. `checkpointRef` mirrors
|
|
162
|
+
* what a live backend hands back after its own reboot-from-snapshot cycle:
|
|
163
|
+
* pointing at itself.
|
|
164
|
+
*/
|
|
165
|
+
export function fromCheckpointRegistryEntry(entry) {
|
|
166
|
+
return {
|
|
167
|
+
name: entry.name,
|
|
168
|
+
image: entry.ref,
|
|
169
|
+
env: envRecordToPairs(entry.spec.env),
|
|
170
|
+
command: entry.spec.command ?? undefined,
|
|
171
|
+
ports: entry.spec.exposedPorts.map((guestPort) => ({ hostPort: 0, guestPort })),
|
|
172
|
+
mounts: [],
|
|
173
|
+
networkId: undefined,
|
|
174
|
+
aliases: [],
|
|
175
|
+
runId: "",
|
|
176
|
+
memoryLimitMb: entry.spec.memoryLimitMb ?? undefined,
|
|
177
|
+
keepAlive: false,
|
|
178
|
+
checkpointRef: entry.ref,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
//# 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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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** One `tar <args>` invocation's outcome — never rejects on a nonzero exit, only on spawn failure or timeout (mirrors `backend-docker/cli.ts`'s own `runDockerCli` contract). */
|
|
2
|
+
export interface TarCliResult {
|
|
3
|
+
readonly exitCode: number;
|
|
4
|
+
readonly stdout: string;
|
|
5
|
+
readonly stderr: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Pure argv construction for the host `tar` binary — the archive container
|
|
9
|
+
* tool a checkpoint archive is built with (see the checkpoints guide's own
|
|
10
|
+
* note on why a plain tar, not a project-specific packer): present on Linux,
|
|
11
|
+
* macOS, and Windows 10+ (bsdtar as `tar.exe`). Kept separate from
|
|
12
|
+
* `runTar` so the shape can be unit-tested without ever spawning the real
|
|
13
|
+
* binary.
|
|
14
|
+
*/
|
|
15
|
+
export declare const TarCli: {
|
|
16
|
+
/**
|
|
17
|
+
* `tar -cf <archive basename> -C <workDir> <members...>` — members are written at
|
|
18
|
+
* the tar's root, never nested under `workDir`'s own path. The archive is named by
|
|
19
|
+
* BASENAME only: an absolute Windows path in the `-f` argument (`C:\...`) is parsed
|
|
20
|
+
* by GNU tar as a `host:path` remote-archive spec ("Cannot connect to C"), and which
|
|
21
|
+
* flavor `tar` resolves to on Windows depends on PATH order (System32's bsdtar vs
|
|
22
|
+
* Git's GNU tar). `runTar` pairs this with `cwd` = the archive's parent directory,
|
|
23
|
+
* which both flavors handle identically.
|
|
24
|
+
*/
|
|
25
|
+
create(archiveBasename: string, workDir: string, members: readonly string[]): string[];
|
|
26
|
+
/** `tar -xf <archive basename> -C <destDir>` — extracts every member into `destDir`; same basename-plus-cwd contract as {@link TarCli.create}. */
|
|
27
|
+
extract(archiveBasename: string, destDir: string): string[];
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Normalizes a directory path for tar's `-C` argument: on Windows, Git's GNU
|
|
31
|
+
* (MSYS) tar mangles backslash paths (`C:\Users\...` arrives as
|
|
32
|
+
* `C\:\\Users\\...` and fails "Cannot open"), while both it and System32's
|
|
33
|
+
* bsdtar accept the same path with forward slashes. Elsewhere the path is
|
|
34
|
+
* returned untouched — a backslash is a legal filename character on POSIX.
|
|
35
|
+
*/
|
|
36
|
+
export declare function tarDirArg(dir: string, platform?: NodeJS.Platform): string;
|
|
37
|
+
/**
|
|
38
|
+
* Runs one `tar <args>` invocation to completion over a child process — the
|
|
39
|
+
* one operation a checkpoint archive needs that isn't a backend CLI call.
|
|
40
|
+
* Shelling out rather than a tar-writing dependency for the same reason
|
|
41
|
+
* `backend-docker/cli.ts` shells out to `docker cp`: the tool already exists
|
|
42
|
+
* on every platform this library ships for.
|
|
43
|
+
*/
|
|
44
|
+
export declare function runTar(args: readonly string[], timeoutMs: number, cwd: string): Promise<TarCliResult>;
|
|
45
|
+
//# sourceMappingURL=tar-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tar-cli.d.ts","sourceRoot":"","sources":["../../../src/core/checkpoint/tar-cli.ts"],"names":[],"mappings":"AAGA,iLAAiL;AACjL,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM;IACjB;;;;;;;;OAQG;4BACqB,MAAM,WAAW,MAAM,WAAW,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE;IAGtF,kJAAkJ;6BACzH,MAAM,WAAW,MAAM,GAAG,MAAM,EAAE;CAG5D,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAAG,MAAM,CAE3F;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA0CrG"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { BackendError } from "../errors.js";
|
|
3
|
+
/**
|
|
4
|
+
* Pure argv construction for the host `tar` binary — the archive container
|
|
5
|
+
* tool a checkpoint archive is built with (see the checkpoints guide's own
|
|
6
|
+
* note on why a plain tar, not a project-specific packer): present on Linux,
|
|
7
|
+
* macOS, and Windows 10+ (bsdtar as `tar.exe`). Kept separate from
|
|
8
|
+
* `runTar` so the shape can be unit-tested without ever spawning the real
|
|
9
|
+
* binary.
|
|
10
|
+
*/
|
|
11
|
+
export const TarCli = {
|
|
12
|
+
/**
|
|
13
|
+
* `tar -cf <archive basename> -C <workDir> <members...>` — members are written at
|
|
14
|
+
* the tar's root, never nested under `workDir`'s own path. The archive is named by
|
|
15
|
+
* BASENAME only: an absolute Windows path in the `-f` argument (`C:\...`) is parsed
|
|
16
|
+
* by GNU tar as a `host:path` remote-archive spec ("Cannot connect to C"), and which
|
|
17
|
+
* flavor `tar` resolves to on Windows depends on PATH order (System32's bsdtar vs
|
|
18
|
+
* Git's GNU tar). `runTar` pairs this with `cwd` = the archive's parent directory,
|
|
19
|
+
* which both flavors handle identically.
|
|
20
|
+
*/
|
|
21
|
+
create(archiveBasename, workDir, members) {
|
|
22
|
+
return ["-cf", archiveBasename, "-C", tarDirArg(workDir), ...members];
|
|
23
|
+
},
|
|
24
|
+
/** `tar -xf <archive basename> -C <destDir>` — extracts every member into `destDir`; same basename-plus-cwd contract as {@link TarCli.create}. */
|
|
25
|
+
extract(archiveBasename, destDir) {
|
|
26
|
+
return ["-xf", archiveBasename, "-C", tarDirArg(destDir)];
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Normalizes a directory path for tar's `-C` argument: on Windows, Git's GNU
|
|
31
|
+
* (MSYS) tar mangles backslash paths (`C:\Users\...` arrives as
|
|
32
|
+
* `C\:\\Users\\...` and fails "Cannot open"), while both it and System32's
|
|
33
|
+
* bsdtar accept the same path with forward slashes. Elsewhere the path is
|
|
34
|
+
* returned untouched — a backslash is a legal filename character on POSIX.
|
|
35
|
+
*/
|
|
36
|
+
export function tarDirArg(dir, platform = process.platform) {
|
|
37
|
+
return platform === "win32" ? dir.replaceAll("\\", "/") : dir;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Runs one `tar <args>` invocation to completion over a child process — the
|
|
41
|
+
* one operation a checkpoint archive needs that isn't a backend CLI call.
|
|
42
|
+
* Shelling out rather than a tar-writing dependency for the same reason
|
|
43
|
+
* `backend-docker/cli.ts` shells out to `docker cp`: the tool already exists
|
|
44
|
+
* on every platform this library ships for.
|
|
45
|
+
*/
|
|
46
|
+
export function runTar(args, timeoutMs, cwd) {
|
|
47
|
+
return new Promise((resolveTar, rejectTar) => {
|
|
48
|
+
const child = spawn("tar", args, { stdio: ["ignore", "pipe", "pipe"], cwd });
|
|
49
|
+
let stdout = "";
|
|
50
|
+
let stderr = "";
|
|
51
|
+
child.stdout.setEncoding("utf8");
|
|
52
|
+
child.stderr.setEncoding("utf8");
|
|
53
|
+
child.stdout.on("data", (chunk) => {
|
|
54
|
+
stdout += chunk;
|
|
55
|
+
});
|
|
56
|
+
child.stderr.on("data", (chunk) => {
|
|
57
|
+
stderr += chunk;
|
|
58
|
+
});
|
|
59
|
+
let settled = false;
|
|
60
|
+
const timer = setTimeout(() => {
|
|
61
|
+
if (settled) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
settled = true;
|
|
65
|
+
child.kill("SIGKILL");
|
|
66
|
+
rejectTar(new BackendError(`tar ${args.join(" ")} timed out after ${timeoutMs}ms and was force-killed`));
|
|
67
|
+
}, timeoutMs);
|
|
68
|
+
child.once("error", (err) => {
|
|
69
|
+
if (settled) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
settled = true;
|
|
73
|
+
clearTimeout(timer);
|
|
74
|
+
rejectTar(new BackendError(`failed to spawn 'tar ${args.join(" ")}': ${err.message}`));
|
|
75
|
+
});
|
|
76
|
+
child.once("close", (code) => {
|
|
77
|
+
if (settled) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
settled = true;
|
|
81
|
+
clearTimeout(timer);
|
|
82
|
+
resolveTar({ exitCode: code ?? -1, stdout, stderr });
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=tar-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tar-cli.js","sourceRoot":"","sources":["../../../src/core/checkpoint/tar-cli.ts"],"names":[],"mappings":"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"}
|
package/dist/core/errors.d.ts
CHANGED
|
@@ -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` —
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* backend
|
|
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,96 @@ 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
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Thrown by `Checkpoints.exportTo` when the checkpoint's own backend artifact
|
|
150
|
+
* no longer exists (`hasCheckpoint` reports `false`) — exporting a stale
|
|
151
|
+
* checkpoint would otherwise produce an archive whose `artifact` member the
|
|
152
|
+
* backend simply failed to write. Thrown before any filesystem work (no temp
|
|
153
|
+
* dir, no tar), after the backend-match check has already passed.
|
|
154
|
+
*/
|
|
155
|
+
export declare class CheckpointArtifactMissingError extends Error {
|
|
156
|
+
/** The checkpoint's ref, exactly as recorded on the `Checkpoint` being exported. */
|
|
157
|
+
readonly ref: string;
|
|
158
|
+
/** The backend this ref was supposed to exist on. */
|
|
159
|
+
readonly backend: string;
|
|
160
|
+
constructor(
|
|
161
|
+
/** The checkpoint's ref, exactly as recorded on the `Checkpoint` being exported. */
|
|
162
|
+
ref: string,
|
|
163
|
+
/** The backend this ref was supposed to exist on. */
|
|
164
|
+
backend: string);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Thrown by `Checkpoints.importFrom` when `srcPath` isn't a well-formed
|
|
168
|
+
* rightsize checkpoint archive: the file doesn't exist, it isn't a valid tar,
|
|
169
|
+
* it has no `checkpoint.json` member, that member isn't valid JSON, isn't
|
|
170
|
+
* shaped like the pinned archive format, or its `rightsizeArchive` field
|
|
171
|
+
* isn't the version this library understands. Thrown before any backend call
|
|
172
|
+
* and before any registry write.
|
|
173
|
+
*/
|
|
174
|
+
export declare class MalformedCheckpointArchiveError extends Error {
|
|
175
|
+
/** The archive path passed to `importFrom`, exactly as given. */
|
|
176
|
+
readonly archivePath: string;
|
|
177
|
+
/** A noun-phrase-ish description of what's wrong with it. */
|
|
178
|
+
readonly reason: string;
|
|
179
|
+
constructor(
|
|
180
|
+
/** The archive path passed to `importFrom`, exactly as given. */
|
|
181
|
+
archivePath: string,
|
|
182
|
+
/** A noun-phrase-ish description of what's wrong with it. */
|
|
183
|
+
reason: string);
|
|
184
|
+
}
|
|
92
185
|
//# 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
|
|
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;AAED;;;;;;GAMG;AACH,qBAAa,8BAA+B,SAAQ,KAAK;IAErD,oFAAoF;IACpF,QAAQ,CAAC,GAAG,EAAE,MAAM;IACpB,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,MAAM;;IAHxB,oFAAoF;IAC3E,GAAG,EAAE,MAAM;IACpB,qDAAqD;IAC5C,OAAO,EAAE,MAAM;CAQ3B;AAED;;;;;;;GAOG;AACH,qBAAa,+BAAgC,SAAQ,KAAK;IAEtD,iEAAiE;IACjE,QAAQ,CAAC,WAAW,EAAE,MAAM;IAC5B,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM;;IAHvB,iEAAiE;IACxD,WAAW,EAAE,MAAM;IAC5B,6DAA6D;IACpD,MAAM,EAAE,MAAM;CAK1B"}
|