@ucdjs/cli 0.3.0 → 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.
@@ -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 { t as printHelp } from "./cli-utils-dAbyq59_.js";
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
- if (!isValidSubcommand(subcommand) || !isValidSubcommand(subcommand) && (flags?.help || flags?.h)) {
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,7 +20,7 @@ async function runCodegenRoot(subcommand, { flags }) {
18
20
  return;
19
21
  }
20
22
  if (subcommand === "fields") {
21
- const { runFieldCodegen } = await import("./fields-CbRX6M6H.js");
23
+ const { runFieldCodegen } = await import("./fields-BvLDPLGg.mjs");
22
24
  await runFieldCodegen({
23
25
  inputPath: flags._.slice(4)?.toString() ?? "",
24
26
  flags
@@ -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 };