@ucdjs/cli 0.2.2 → 0.3.1-beta.1
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/bin/ucd.js +1 -1
- package/dist/_shared-CgcKJJFf.mjs +113 -0
- package/dist/analyze-D8AeAGDR.mjs +75 -0
- package/dist/cli.mjs +403 -0
- package/dist/{fields-Cz0PV1Co.js → fields-BvLDPLGg.mjs} +9 -19
- package/dist/get-CHw5s5Ab.mjs +63 -0
- package/dist/hash-BAViT6QD.mjs +116 -0
- package/dist/info-D2uPOmns.mjs +100 -0
- package/dist/info-ZGysg0N_.mjs +75 -0
- package/dist/init-DilsO33p.mjs +126 -0
- package/dist/list-C1bd3Vq4.mjs +89 -0
- package/dist/mirror-DLYQGa33.mjs +130 -0
- package/dist/root-BnOeYqGP.mjs +57 -0
- package/dist/root-C_Ycy7kI.mjs +88 -0
- package/dist/{root-oHWSp_5G.js → root-CfALAyOQ.mjs} +6 -5
- package/dist/root-DXVHyPa6.mjs +61 -0
- package/dist/status-DI1HruH0.mjs +136 -0
- package/dist/sync-DEtQ-jZc.mjs +160 -0
- package/dist/validate-DVr1TaHK.mjs +114 -0
- package/dist/verify-ME4bDNIT.mjs +114 -0
- package/package.json +34 -18
- package/dist/cli-utils-DY82m2wz.js +0 -195
- package/dist/cli.js +0 -7
- package/dist/download-CCx2vdOj.js +0 -203
- /package/dist/{cli.d.ts → cli.d.mts} +0 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { _ as output, f as formatDuration, g as list, h as keyValue, l as cyan, m as header, p as green, s as blankLine, t as printHelp, v as red, y as yellow } from "./cli.mjs";
|
|
2
|
+
import { i as assertLocalStore, o as createStoreFromFlags, r as SHARED_FLAGS, t as LOCAL_STORE_FLAGS } from "./_shared-CgcKJJFf.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/cmd/store/mirror.ts
|
|
5
|
+
async function runMirrorStore({ flags, versions }) {
|
|
6
|
+
if (flags?.help || flags?.h) {
|
|
7
|
+
printHelp({
|
|
8
|
+
headline: "Mirror Unicode data files to local storage",
|
|
9
|
+
commandName: "ucd store mirror",
|
|
10
|
+
usage: "[...versions] [...flags]",
|
|
11
|
+
tables: { Flags: [
|
|
12
|
+
...LOCAL_STORE_FLAGS,
|
|
13
|
+
...SHARED_FLAGS,
|
|
14
|
+
["--concurrency", "Maximum concurrent downloads (default: 5)."],
|
|
15
|
+
["--json", "Output mirror results in JSON format."],
|
|
16
|
+
["--help (-h)", "See all available flags."]
|
|
17
|
+
] }
|
|
18
|
+
});
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
assertLocalStore(flags);
|
|
22
|
+
const { storeDir, baseUrl, include: patterns, exclude: excludePatterns, force, concurrency = 5, json } = flags;
|
|
23
|
+
const store = await createStoreFromFlags({
|
|
24
|
+
baseUrl,
|
|
25
|
+
storeDir,
|
|
26
|
+
remote: false,
|
|
27
|
+
include: patterns,
|
|
28
|
+
exclude: excludePatterns,
|
|
29
|
+
versions,
|
|
30
|
+
force,
|
|
31
|
+
requireExistingStore: true,
|
|
32
|
+
versionStrategy: "merge"
|
|
33
|
+
});
|
|
34
|
+
if (!json) {
|
|
35
|
+
header("Mirror Operation");
|
|
36
|
+
keyValue("Store Path", storeDir, { valueColor: cyan });
|
|
37
|
+
if (versions.length > 0) keyValue("Versions", versions.map((v) => cyan(v)).join(", "));
|
|
38
|
+
else keyValue("Versions", "all versions in lockfile");
|
|
39
|
+
blankLine();
|
|
40
|
+
}
|
|
41
|
+
const [mirrorResult, mirrorError] = await store.mirror({
|
|
42
|
+
versions: versions.length > 0 ? versions : void 0,
|
|
43
|
+
force,
|
|
44
|
+
concurrency,
|
|
45
|
+
filters: {
|
|
46
|
+
include: patterns,
|
|
47
|
+
exclude: excludePatterns
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
if (mirrorError) {
|
|
51
|
+
if (json) output.errorJson({
|
|
52
|
+
type: "MIRROR_FAILED",
|
|
53
|
+
message: "Mirror operation failed",
|
|
54
|
+
details: { reason: mirrorError.message }
|
|
55
|
+
});
|
|
56
|
+
else output.fail("Mirror operation failed", { details: [mirrorError.message] });
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (!mirrorResult) {
|
|
60
|
+
if (json) output.errorJson({
|
|
61
|
+
type: "NO_RESULT",
|
|
62
|
+
message: "Mirror operation returned no result"
|
|
63
|
+
});
|
|
64
|
+
else output.fail("Mirror operation returned no result");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (json) {
|
|
68
|
+
const jsonOutput = {
|
|
69
|
+
success: true,
|
|
70
|
+
storeDir,
|
|
71
|
+
versions: versions.length > 0 ? versions : Array.from(mirrorResult.versions.keys())
|
|
72
|
+
};
|
|
73
|
+
if (mirrorResult.summary) {
|
|
74
|
+
const { counts, duration, storage, metrics } = mirrorResult.summary;
|
|
75
|
+
jsonOutput.summary = {
|
|
76
|
+
versionsCount: mirrorResult.versions.size,
|
|
77
|
+
downloaded: counts.success,
|
|
78
|
+
skipped: counts.skipped,
|
|
79
|
+
failed: counts.failed,
|
|
80
|
+
totalSize: storage.totalSize,
|
|
81
|
+
successRate: metrics.successRate,
|
|
82
|
+
duration
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (mirrorResult.versions.size > 0) jsonOutput.versionDetails = Array.from(mirrorResult.versions.entries()).map(([version, report]) => ({
|
|
86
|
+
version,
|
|
87
|
+
downloaded: report.counts.success,
|
|
88
|
+
skipped: report.counts.skipped,
|
|
89
|
+
failed: report.counts.failed,
|
|
90
|
+
errors: report.errors.map((e) => ({
|
|
91
|
+
filePath: e.filePath,
|
|
92
|
+
reason: e.reason
|
|
93
|
+
}))
|
|
94
|
+
}));
|
|
95
|
+
output.json(jsonOutput);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
output.success("Mirror operation completed");
|
|
99
|
+
if (mirrorResult.summary) {
|
|
100
|
+
const { counts, duration, storage, metrics } = mirrorResult.summary;
|
|
101
|
+
header("Summary");
|
|
102
|
+
keyValue("Versions", String(mirrorResult.versions.size));
|
|
103
|
+
keyValue("Downloaded", `${green(String(counts.success))} files`);
|
|
104
|
+
keyValue("Skipped", `${yellow(String(counts.skipped))} files`);
|
|
105
|
+
if (counts.failed > 0) keyValue("Failed", `${red(String(counts.failed))} files`);
|
|
106
|
+
keyValue("Total Size", storage.totalSize);
|
|
107
|
+
keyValue("Success Rate", `${metrics.successRate.toFixed(1)}%`);
|
|
108
|
+
keyValue("Duration", formatDuration(duration));
|
|
109
|
+
}
|
|
110
|
+
if (mirrorResult.versions.size > 1) {
|
|
111
|
+
header("Version Details");
|
|
112
|
+
for (const [version, report] of mirrorResult.versions) {
|
|
113
|
+
output.log(` ${cyan(version)}`);
|
|
114
|
+
output.log(` Downloaded: ${green(String(report.counts.success))}, Skipped: ${yellow(String(report.counts.skipped))}`);
|
|
115
|
+
if (report.counts.failed > 0) {
|
|
116
|
+
output.log(` ${red(`Failed: ${report.counts.failed}`)}`);
|
|
117
|
+
list(report.errors.slice(0, 3).map((e) => `${e.filePath}: ${e.reason}`), {
|
|
118
|
+
indent: 4,
|
|
119
|
+
prefix: "-"
|
|
120
|
+
});
|
|
121
|
+
if (report.errors.length > 3) output.log(` ... and ${report.errors.length - 3} more errors`);
|
|
122
|
+
}
|
|
123
|
+
blankLine();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
blankLine();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
//#endregion
|
|
130
|
+
export { runMirrorStore };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { t as printHelp } from "./cli.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/cmd/lockfile/root.ts
|
|
4
|
+
const LOCKFILE_SUBCOMMANDS = [
|
|
5
|
+
"info",
|
|
6
|
+
"hash",
|
|
7
|
+
"validate"
|
|
8
|
+
];
|
|
9
|
+
function isValidSubcommand(subcommand) {
|
|
10
|
+
return LOCKFILE_SUBCOMMANDS.includes(subcommand);
|
|
11
|
+
}
|
|
12
|
+
async function runLockfileRoot(subcommand, { flags }) {
|
|
13
|
+
const isValidSub = isValidSubcommand(subcommand);
|
|
14
|
+
const requestsHelp = flags?.help || flags?.h;
|
|
15
|
+
if (!isValidSub || requestsHelp && !isValidSub) {
|
|
16
|
+
printHelp({
|
|
17
|
+
commandName: "ucd lockfile",
|
|
18
|
+
usage: "[command] [...flags]",
|
|
19
|
+
tables: {
|
|
20
|
+
Commands: [
|
|
21
|
+
["info", "Display lockfile information and summary."],
|
|
22
|
+
["validate", "Validate lockfile against the expected schema."],
|
|
23
|
+
["hash", "Compute content hash for a file (useful for debugging)."]
|
|
24
|
+
],
|
|
25
|
+
Flags: [
|
|
26
|
+
["--store-dir", "Directory where the UCD store is located."],
|
|
27
|
+
["--json", "Output in JSON format."],
|
|
28
|
+
["--help (-h)", "See all available flags."]
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (subcommand === "info") {
|
|
35
|
+
const { runLockfileInfo } = await import("./info-D2uPOmns.mjs");
|
|
36
|
+
await runLockfileInfo({ flags });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (subcommand === "hash") {
|
|
40
|
+
const { runLockfileHash } = await import("./hash-BAViT6QD.mjs");
|
|
41
|
+
const pathParts = flags._.slice(2);
|
|
42
|
+
await runLockfileHash({
|
|
43
|
+
filePath: pathParts.length > 0 ? pathParts.join(" ") : "",
|
|
44
|
+
flags
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (subcommand === "validate") {
|
|
49
|
+
const { runLockfileValidate } = await import("./validate-DVr1TaHK.mjs");
|
|
50
|
+
await runLockfileValidate({ flags });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
throw new Error(`Invalid subcommand: ${subcommand}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
export { runLockfileRoot };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { t as printHelp } from "./cli.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/cmd/store/root.ts
|
|
4
|
+
const STORE_SUBCOMMANDS = [
|
|
5
|
+
"init",
|
|
6
|
+
"sync",
|
|
7
|
+
"mirror",
|
|
8
|
+
"verify",
|
|
9
|
+
"analyze",
|
|
10
|
+
"status"
|
|
11
|
+
];
|
|
12
|
+
function isValidSubcommand(subcommand) {
|
|
13
|
+
return STORE_SUBCOMMANDS.includes(subcommand);
|
|
14
|
+
}
|
|
15
|
+
async function runStoreRoot(subcommand, { flags }) {
|
|
16
|
+
const isValidSub = isValidSubcommand(subcommand);
|
|
17
|
+
const requestsHelp = flags?.help || flags?.h;
|
|
18
|
+
if (!isValidSub || requestsHelp && !isValidSub) {
|
|
19
|
+
printHelp({
|
|
20
|
+
commandName: "ucd store",
|
|
21
|
+
usage: "[command] [...flags]",
|
|
22
|
+
tables: {
|
|
23
|
+
"Commands (local store only)": [
|
|
24
|
+
["init", "Initialize an UCD Store (create lockfile and download files)."],
|
|
25
|
+
["sync", "Sync files to match lockfile state (download missing, optionally remove orphaned)."],
|
|
26
|
+
["mirror", "Download Unicode data files to local storage."]
|
|
27
|
+
],
|
|
28
|
+
"Commands (local or remote)": [
|
|
29
|
+
["verify", "Verify store integrity against API."],
|
|
30
|
+
["analyze", "Analyze store contents and file status."],
|
|
31
|
+
["status", "Show store status and version information."]
|
|
32
|
+
],
|
|
33
|
+
"Flags": [["--force", "Force operation (command-specific behavior)."], ["--help (-h)", "See all available flags."]]
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const versions = flags._.slice(2);
|
|
39
|
+
if (subcommand === "init") {
|
|
40
|
+
const { runInitStore } = await import("./init-DilsO33p.mjs");
|
|
41
|
+
await runInitStore({
|
|
42
|
+
versions,
|
|
43
|
+
flags
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (subcommand === "sync") {
|
|
48
|
+
const { runSyncStore } = await import("./sync-DEtQ-jZc.mjs");
|
|
49
|
+
await runSyncStore({
|
|
50
|
+
flags,
|
|
51
|
+
versions
|
|
52
|
+
});
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (subcommand === "mirror") {
|
|
56
|
+
const { runMirrorStore } = await import("./mirror-DLYQGa33.mjs");
|
|
57
|
+
await runMirrorStore({
|
|
58
|
+
flags,
|
|
59
|
+
versions
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (subcommand === "verify") {
|
|
64
|
+
const { runVerifyStore } = await import("./verify-ME4bDNIT.mjs");
|
|
65
|
+
await runVerifyStore({
|
|
66
|
+
flags,
|
|
67
|
+
versions
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (subcommand === "analyze") {
|
|
72
|
+
const { runAnalyzeStore } = await import("./analyze-D8AeAGDR.mjs");
|
|
73
|
+
await runAnalyzeStore({
|
|
74
|
+
flags,
|
|
75
|
+
versions
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (subcommand === "status") {
|
|
80
|
+
const { runStatusStore } = await import("./status-DI1HruH0.mjs");
|
|
81
|
+
await runStatusStore({ flags });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
throw new Error(`Invalid subcommand: ${subcommand}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
//#endregion
|
|
88
|
+
export { runStoreRoot };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { printHelp } from "./cli
|
|
1
|
+
import { t as printHelp } from "./cli.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/cmd/codegen/root.ts
|
|
4
4
|
const CODEGEN_SUBCOMMANDS = ["fields"];
|
|
@@ -6,7 +6,9 @@ function isValidSubcommand(subcommand) {
|
|
|
6
6
|
return CODEGEN_SUBCOMMANDS.includes(subcommand);
|
|
7
7
|
}
|
|
8
8
|
async function runCodegenRoot(subcommand, { flags }) {
|
|
9
|
-
|
|
9
|
+
const isValidSub = isValidSubcommand(subcommand);
|
|
10
|
+
const requestsHelp = flags?.help || flags?.h;
|
|
11
|
+
if (!isValidSub || requestsHelp && !isValidSub) {
|
|
10
12
|
printHelp({
|
|
11
13
|
commandName: "ucd codegen",
|
|
12
14
|
usage: "[command] [...flags]",
|
|
@@ -18,10 +20,9 @@ async function runCodegenRoot(subcommand, { flags }) {
|
|
|
18
20
|
return;
|
|
19
21
|
}
|
|
20
22
|
if (subcommand === "fields") {
|
|
21
|
-
const { runFieldCodegen } = await import("./fields-
|
|
22
|
-
const inputPath = flags._.slice(4)?.toString() ?? "";
|
|
23
|
+
const { runFieldCodegen } = await import("./fields-BvLDPLGg.mjs");
|
|
23
24
|
await runFieldCodegen({
|
|
24
|
-
inputPath,
|
|
25
|
+
inputPath: flags._.slice(4)?.toString() ?? "",
|
|
25
26
|
flags
|
|
26
27
|
});
|
|
27
28
|
return;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { t as printHelp } from "./cli.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/cmd/files/root.ts
|
|
4
|
+
const FILES_SUBCOMMANDS = [
|
|
5
|
+
"list",
|
|
6
|
+
"get",
|
|
7
|
+
"info"
|
|
8
|
+
];
|
|
9
|
+
function isValidSubcommand(subcommand) {
|
|
10
|
+
return FILES_SUBCOMMANDS.includes(subcommand);
|
|
11
|
+
}
|
|
12
|
+
async function runFilesRoot(subcommand, { flags }) {
|
|
13
|
+
const isValidSub = isValidSubcommand(subcommand);
|
|
14
|
+
const requestsHelp = flags?.help || flags?.h;
|
|
15
|
+
if (!isValidSub || requestsHelp && !isValidSub) {
|
|
16
|
+
printHelp({
|
|
17
|
+
commandName: "ucd files",
|
|
18
|
+
usage: "[command] [...flags]",
|
|
19
|
+
tables: {
|
|
20
|
+
Commands: [
|
|
21
|
+
["list", "List files and directories from the UCD API."],
|
|
22
|
+
["get", "Get a specific file from the UCD API."],
|
|
23
|
+
["info", "Get metadata about a file or directory."]
|
|
24
|
+
],
|
|
25
|
+
Flags: [["--base-url", "Base URL for the UCD API (defaults to api.ucdjs.dev)."], ["--help (-h)", "See all available flags."]]
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (subcommand === "list") {
|
|
31
|
+
const { runFilesList } = await import("./list-C1bd3Vq4.mjs");
|
|
32
|
+
const pathParts = flags._.slice(2);
|
|
33
|
+
await runFilesList({
|
|
34
|
+
path: pathParts.length > 0 ? pathParts.join(" ") : "",
|
|
35
|
+
flags
|
|
36
|
+
});
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (subcommand === "get") {
|
|
40
|
+
const { runFilesGet } = await import("./get-CHw5s5Ab.mjs");
|
|
41
|
+
const pathParts = flags._.slice(2);
|
|
42
|
+
await runFilesGet({
|
|
43
|
+
path: pathParts.length > 0 ? pathParts.join(" ") : "",
|
|
44
|
+
flags
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (subcommand === "info") {
|
|
49
|
+
const { runFilesInfo } = await import("./info-ZGysg0N_.mjs");
|
|
50
|
+
const pathParts = flags._.slice(2);
|
|
51
|
+
await runFilesInfo({
|
|
52
|
+
path: pathParts.length > 0 ? pathParts.join(" ") : "",
|
|
53
|
+
flags
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
throw new Error(`Invalid subcommand: ${subcommand}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
export { runFilesRoot };
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { _ as output, p as green, t as printHelp, v as red, y as yellow } from "./cli.mjs";
|
|
2
|
+
import { a as assertRemoteOrStoreDir, n as REMOTE_CAPABLE_FLAGS, o as createStoreFromFlags, r as SHARED_FLAGS } from "./_shared-CgcKJJFf.mjs";
|
|
3
|
+
import { createUCDClient } from "@ucdjs/client";
|
|
4
|
+
import { UCDJS_API_BASE_URL } from "@ucdjs/env";
|
|
5
|
+
import { createDebugger } from "@ucdjs-internal/shared";
|
|
6
|
+
import { getLockfilePath, readLockfile, readSnapshotOrUndefined } from "@ucdjs/lockfile";
|
|
7
|
+
|
|
8
|
+
//#region src/cmd/store/status.ts
|
|
9
|
+
const debug = createDebugger("ucdjs:cli:store:status");
|
|
10
|
+
async function runStatusStore({ flags }) {
|
|
11
|
+
if (flags?.help || flags?.h) {
|
|
12
|
+
printHelp({
|
|
13
|
+
headline: "Show UCD Store status and lockfile information",
|
|
14
|
+
commandName: "ucd store status",
|
|
15
|
+
usage: "[...flags]",
|
|
16
|
+
tables: { Flags: [
|
|
17
|
+
...REMOTE_CAPABLE_FLAGS,
|
|
18
|
+
...SHARED_FLAGS,
|
|
19
|
+
["--json", "Output status information in JSON format."],
|
|
20
|
+
["--help (-h)", "See all available flags."]
|
|
21
|
+
] }
|
|
22
|
+
});
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
assertRemoteOrStoreDir(flags);
|
|
26
|
+
const { storeDir, remote, baseUrl, json } = flags;
|
|
27
|
+
const client = await createUCDClient(baseUrl || UCDJS_API_BASE_URL);
|
|
28
|
+
const configResult = await client.config.get();
|
|
29
|
+
let availableVersions = [];
|
|
30
|
+
if (configResult.error || !configResult.data) {
|
|
31
|
+
const apiResult = await client.versions.list();
|
|
32
|
+
if (apiResult.error) output.warn(yellow(`Warning: Could not fetch versions from API: ${apiResult.error.message}`));
|
|
33
|
+
else if (apiResult.data) availableVersions = apiResult.data.map(({ version }) => version);
|
|
34
|
+
} else {
|
|
35
|
+
availableVersions = configResult.data.versions ?? [];
|
|
36
|
+
if (availableVersions.length === 0) {
|
|
37
|
+
const apiResult = await client.versions.list();
|
|
38
|
+
if (apiResult.error) output.warn(yellow(`Warning: Could not fetch versions from API: ${apiResult.error.message}`));
|
|
39
|
+
else if (apiResult.data) availableVersions = apiResult.data.map(({ version }) => version);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (remote) {
|
|
43
|
+
if (json) {
|
|
44
|
+
output.json({
|
|
45
|
+
type: "remote",
|
|
46
|
+
baseUrl: baseUrl || UCDJS_API_BASE_URL,
|
|
47
|
+
availableVersions,
|
|
48
|
+
totalVersions: availableVersions.length
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
output.log(`Store Status: Remote (${baseUrl || UCDJS_API_BASE_URL})`);
|
|
53
|
+
output.log(` Available Versions: ${availableVersions.length}`);
|
|
54
|
+
output.log("");
|
|
55
|
+
for (const version of availableVersions.sort((a, b) => b.localeCompare(a))) output.log(` ${green("✓")} ${version}`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (!storeDir) {
|
|
59
|
+
output.error(red(`\n❌ Error: Store directory must be specified.`));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const store = await createStoreFromFlags({
|
|
63
|
+
baseUrl,
|
|
64
|
+
storeDir,
|
|
65
|
+
remote,
|
|
66
|
+
requireExistingStore: true
|
|
67
|
+
});
|
|
68
|
+
const lockfilePath = getLockfilePath();
|
|
69
|
+
const bridge = store.fs;
|
|
70
|
+
let lockfile;
|
|
71
|
+
try {
|
|
72
|
+
lockfile = await readLockfile(bridge, lockfilePath);
|
|
73
|
+
} catch (err) {
|
|
74
|
+
debug?.("Error reading lockfile:", err);
|
|
75
|
+
output.error(red(`\n❌ Error: Lockfile not found at ${lockfilePath}`));
|
|
76
|
+
output.error("Run 'ucd store init' to create a new store.");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const availableVersionsSet = new Set(availableVersions);
|
|
80
|
+
const lockfileVersions = Object.keys(lockfile.versions);
|
|
81
|
+
const versionStatuses = await Promise.all(lockfileVersions.map(async (version) => {
|
|
82
|
+
return {
|
|
83
|
+
version,
|
|
84
|
+
entry: lockfile.versions[version],
|
|
85
|
+
hasSnapshot: await readSnapshotOrUndefined(bridge, version) !== void 0,
|
|
86
|
+
isAvailableInAPI: availableVersionsSet.has(version)
|
|
87
|
+
};
|
|
88
|
+
}));
|
|
89
|
+
const mirroredCount = versionStatuses.filter((s) => s.hasSnapshot).length;
|
|
90
|
+
const availableCount = versionStatuses.filter((s) => s.isAvailableInAPI).length;
|
|
91
|
+
if (json) {
|
|
92
|
+
output.json({
|
|
93
|
+
storePath: storeDir,
|
|
94
|
+
lockfilePath,
|
|
95
|
+
lockfileVersion: lockfile.lockfileVersion,
|
|
96
|
+
totalVersions: lockfileVersions.length,
|
|
97
|
+
versions: versionStatuses.map((s) => ({
|
|
98
|
+
version: s.version,
|
|
99
|
+
snapshotPath: s.entry?.path,
|
|
100
|
+
fileCount: s.entry?.fileCount,
|
|
101
|
+
totalSize: s.entry?.totalSize,
|
|
102
|
+
mirrored: s.hasSnapshot,
|
|
103
|
+
availableInAPI: s.isAvailableInAPI
|
|
104
|
+
})),
|
|
105
|
+
summary: {
|
|
106
|
+
mirrored: mirroredCount,
|
|
107
|
+
availableInAPI: availableCount,
|
|
108
|
+
total: lockfileVersions.length
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
output.log(`Store Status: ${storeDir}`);
|
|
114
|
+
output.log(` Lockfile: ${lockfilePath}`);
|
|
115
|
+
output.log(` Lockfile Version: ${lockfile.lockfileVersion}`);
|
|
116
|
+
output.log(` Total Versions: ${lockfileVersions.length}`);
|
|
117
|
+
output.log("");
|
|
118
|
+
for (const status of versionStatuses.sort((a, b) => a.version.localeCompare(b.version))) {
|
|
119
|
+
const { version, entry, hasSnapshot, isAvailableInAPI } = status;
|
|
120
|
+
const statusIcon = hasSnapshot ? green("✓") : yellow("⚠");
|
|
121
|
+
const apiIcon = isAvailableInAPI ? green("✓") : red("✗");
|
|
122
|
+
output.log(` Version ${version}:`);
|
|
123
|
+
output.log(` Snapshot: ${entry?.path}`);
|
|
124
|
+
output.log(` Status: ${statusIcon} ${hasSnapshot ? "Mirrored" : "Not mirrored"}`);
|
|
125
|
+
output.log(` Files: ${entry?.fileCount}`);
|
|
126
|
+
output.log(` Size: ${((entry?.totalSize ?? 0) / 1024 / 1024).toFixed(2)} MB`);
|
|
127
|
+
output.log(` API: ${apiIcon} ${isAvailableInAPI ? "Available" : "Not available"}`);
|
|
128
|
+
output.log("");
|
|
129
|
+
}
|
|
130
|
+
output.log(" Summary:");
|
|
131
|
+
output.log(` Mirrored: ${mirroredCount}/${lockfileVersions.length} versions`);
|
|
132
|
+
output.log(` Available in API: ${availableCount}/${lockfileVersions.length} versions`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
//#endregion
|
|
136
|
+
export { runStatusStore };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { _ as output, f as formatDuration, g as list, h as keyValue, l as cyan, m as header, p as green, s as blankLine, t as printHelp, v as red, y as yellow } from "./cli.mjs";
|
|
2
|
+
import { i as assertLocalStore, o as createStoreFromFlags, r as SHARED_FLAGS, t as LOCAL_STORE_FLAGS } from "./_shared-CgcKJJFf.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/cmd/store/sync.ts
|
|
5
|
+
async function runSyncStore({ flags, versions }) {
|
|
6
|
+
if (flags?.help || flags?.h) {
|
|
7
|
+
printHelp({
|
|
8
|
+
headline: "Sync lockfile with API and mirror files",
|
|
9
|
+
commandName: "ucd store sync",
|
|
10
|
+
usage: "[...versions] [...flags]",
|
|
11
|
+
tables: { Flags: [
|
|
12
|
+
...LOCAL_STORE_FLAGS,
|
|
13
|
+
...SHARED_FLAGS,
|
|
14
|
+
["--concurrency", "Maximum concurrent downloads (default: 5)."],
|
|
15
|
+
["--remove-unavailable", "Remove versions from lockfile that are not available in API."],
|
|
16
|
+
["--clean", "Remove orphaned files (files not in expected files list)."],
|
|
17
|
+
["--json", "Output sync results in JSON format."],
|
|
18
|
+
["--help (-h)", "See all available flags."]
|
|
19
|
+
] }
|
|
20
|
+
});
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
assertLocalStore(flags);
|
|
24
|
+
const { storeDir, baseUrl, include: patterns, exclude: excludePatterns, force, concurrency = 5, removeUnavailable, clean, json } = flags;
|
|
25
|
+
const store = await createStoreFromFlags({
|
|
26
|
+
baseUrl,
|
|
27
|
+
storeDir,
|
|
28
|
+
remote: false,
|
|
29
|
+
include: patterns,
|
|
30
|
+
exclude: excludePatterns,
|
|
31
|
+
versions,
|
|
32
|
+
force,
|
|
33
|
+
requireExistingStore: true,
|
|
34
|
+
versionStrategy: "merge"
|
|
35
|
+
});
|
|
36
|
+
if (!json) {
|
|
37
|
+
header("Sync Operation");
|
|
38
|
+
keyValue("Store Path", storeDir, { valueColor: cyan });
|
|
39
|
+
if (versions.length > 0) keyValue("Versions", versions.map((v) => cyan(v)).join(", "));
|
|
40
|
+
else keyValue("Versions", "all versions in lockfile");
|
|
41
|
+
blankLine();
|
|
42
|
+
}
|
|
43
|
+
const [syncResult, syncError] = await store.sync({
|
|
44
|
+
versions: versions.length > 0 ? versions : void 0,
|
|
45
|
+
force,
|
|
46
|
+
concurrency,
|
|
47
|
+
removeUnavailable,
|
|
48
|
+
cleanOrphaned: clean,
|
|
49
|
+
filters: {
|
|
50
|
+
include: patterns,
|
|
51
|
+
exclude: excludePatterns
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
if (syncError) {
|
|
55
|
+
if (json) output.errorJson({
|
|
56
|
+
type: "SYNC_FAILED",
|
|
57
|
+
message: "Sync operation failed",
|
|
58
|
+
details: { reason: syncError.message }
|
|
59
|
+
});
|
|
60
|
+
else output.fail("Sync operation failed", { details: [syncError.message] });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (!syncResult) {
|
|
64
|
+
if (json) output.errorJson({
|
|
65
|
+
type: "NO_RESULT",
|
|
66
|
+
message: "Sync operation returned no result"
|
|
67
|
+
});
|
|
68
|
+
else output.fail("Sync operation returned no result");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (json) {
|
|
72
|
+
const jsonOutput = {
|
|
73
|
+
success: true,
|
|
74
|
+
storeDir,
|
|
75
|
+
lockfileChanges: {
|
|
76
|
+
added: syncResult.added,
|
|
77
|
+
removed: syncResult.removed,
|
|
78
|
+
unchanged: syncResult.unchanged,
|
|
79
|
+
totalVersions: syncResult.versions.length
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
if (syncResult.mirrorReport?.summary) {
|
|
83
|
+
const { counts, duration, storage } = syncResult.mirrorReport.summary;
|
|
84
|
+
jsonOutput.mirrorSummary = {
|
|
85
|
+
versionsCount: syncResult.mirrorReport.versions.size,
|
|
86
|
+
downloaded: counts.success,
|
|
87
|
+
skipped: counts.skipped,
|
|
88
|
+
failed: counts.failed,
|
|
89
|
+
totalSize: storage.totalSize,
|
|
90
|
+
duration
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (syncResult.removedFiles.size > 0) jsonOutput.orphanedFilesRemoved = Array.from(syncResult.removedFiles.entries()).map(([version, files]) => ({
|
|
94
|
+
version,
|
|
95
|
+
files
|
|
96
|
+
}));
|
|
97
|
+
output.json(jsonOutput);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
output.success("Sync completed");
|
|
101
|
+
if (syncResult.added.length > 0 || syncResult.removed.length > 0 || syncResult.unchanged.length > 0) {
|
|
102
|
+
header("Lockfile Changes");
|
|
103
|
+
if (syncResult.added.length > 0) {
|
|
104
|
+
keyValue("Added", `${green(String(syncResult.added.length))} version(s)`);
|
|
105
|
+
list(syncResult.added, {
|
|
106
|
+
prefix: "+",
|
|
107
|
+
itemColor: green,
|
|
108
|
+
indent: 4
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (syncResult.removed.length > 0) {
|
|
112
|
+
keyValue("Removed", `${yellow(String(syncResult.removed.length))} version(s)`);
|
|
113
|
+
list(syncResult.removed, {
|
|
114
|
+
prefix: "-",
|
|
115
|
+
itemColor: yellow,
|
|
116
|
+
indent: 4
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (syncResult.unchanged.length > 0) keyValue("Unchanged", `${syncResult.unchanged.length} version(s)`);
|
|
120
|
+
}
|
|
121
|
+
keyValue("Total Versions", String(syncResult.versions.length));
|
|
122
|
+
if (syncResult.mirrorReport) {
|
|
123
|
+
const report = syncResult.mirrorReport;
|
|
124
|
+
if (report.summary) {
|
|
125
|
+
const { counts, duration, storage } = report.summary;
|
|
126
|
+
header("Mirror Summary");
|
|
127
|
+
keyValue("Versions", String(report.versions.size));
|
|
128
|
+
keyValue("Downloaded", `${green(String(counts.success))} files`);
|
|
129
|
+
keyValue("Skipped", `${yellow(String(counts.skipped))} files`);
|
|
130
|
+
if (counts.failed > 0) keyValue("Failed", `${red(String(counts.failed))} files`);
|
|
131
|
+
keyValue("Total Size", storage.totalSize);
|
|
132
|
+
keyValue("Duration", formatDuration(duration));
|
|
133
|
+
}
|
|
134
|
+
if (report.versions.size > 1) {
|
|
135
|
+
header("Version Details");
|
|
136
|
+
for (const [version, versionReport] of report.versions) {
|
|
137
|
+
output.log(` ${cyan(version)}`);
|
|
138
|
+
output.log(` Downloaded: ${green(String(versionReport.counts.success))}, Skipped: ${yellow(String(versionReport.counts.skipped))}`);
|
|
139
|
+
if (versionReport.counts.failed > 0) output.log(` ${red(`Failed: ${versionReport.counts.failed}`)}`);
|
|
140
|
+
blankLine();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (syncResult.removedFiles.size > 0) {
|
|
145
|
+
header("Orphaned Files Removed");
|
|
146
|
+
for (const [version, removedFiles] of syncResult.removedFiles) if (removedFiles.length > 0) {
|
|
147
|
+
output.log(` ${cyan(version)}`);
|
|
148
|
+
list(removedFiles, {
|
|
149
|
+
prefix: "-",
|
|
150
|
+
indent: 4,
|
|
151
|
+
itemColor: yellow
|
|
152
|
+
});
|
|
153
|
+
blankLine();
|
|
154
|
+
}
|
|
155
|
+
} else if (clean) output.success("No orphaned files found");
|
|
156
|
+
blankLine();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
//#endregion
|
|
160
|
+
export { runSyncStore };
|