docs-cache 0.4.2 → 0.5.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.
Files changed (90) hide show
  1. package/dist/cli.mjs +20 -16
  2. package/dist/esm/api.d.ts +14 -0
  3. package/dist/esm/api.mjs +14 -0
  4. package/dist/esm/cache/cache-layout.d.ts +1 -0
  5. package/dist/esm/cache/cache-layout.mjs +12 -0
  6. package/dist/esm/cache/lock.d.ts +21 -0
  7. package/dist/esm/cache/lock.mjs +91 -0
  8. package/dist/esm/cache/manifest.d.ts +11 -0
  9. package/dist/esm/cache/manifest.mjs +68 -0
  10. package/dist/esm/cache/materialize.d.ts +26 -0
  11. package/dist/esm/cache/materialize.mjs +442 -0
  12. package/dist/esm/cache/targets.d.ts +19 -0
  13. package/dist/esm/cache/targets.mjs +66 -0
  14. package/dist/esm/cache/toc.d.ts +12 -0
  15. package/dist/esm/cache/toc.mjs +167 -0
  16. package/dist/esm/cli/exit-code.d.ts +11 -0
  17. package/dist/esm/cli/exit-code.mjs +5 -0
  18. package/dist/esm/cli/index.d.ts +5 -0
  19. package/dist/esm/cli/index.mjs +345 -0
  20. package/dist/esm/cli/live-output.d.ts +12 -0
  21. package/dist/esm/cli/live-output.mjs +30 -0
  22. package/dist/esm/cli/parse-args.d.ts +13 -0
  23. package/dist/esm/cli/parse-args.mjs +295 -0
  24. package/dist/esm/cli/run.d.ts +1 -0
  25. package/dist/esm/cli/run.mjs +2 -0
  26. package/dist/esm/cli/task-reporter.d.ts +32 -0
  27. package/dist/esm/cli/task-reporter.mjs +122 -0
  28. package/dist/esm/cli/types.d.ts +51 -0
  29. package/dist/esm/cli/types.mjs +0 -0
  30. package/dist/esm/cli/ui.d.ts +21 -0
  31. package/dist/esm/cli/ui.mjs +64 -0
  32. package/dist/esm/commands/add.d.ts +20 -0
  33. package/dist/esm/commands/add.mjs +81 -0
  34. package/dist/esm/commands/clean-git-cache.d.ts +10 -0
  35. package/dist/esm/commands/clean-git-cache.mjs +48 -0
  36. package/dist/esm/commands/clean.d.ts +10 -0
  37. package/dist/esm/commands/clean.mjs +27 -0
  38. package/dist/esm/commands/init.d.ts +19 -0
  39. package/dist/esm/commands/init.mjs +179 -0
  40. package/dist/esm/commands/prune.d.ts +11 -0
  41. package/dist/esm/commands/prune.mjs +52 -0
  42. package/dist/esm/commands/remove.d.ts +12 -0
  43. package/dist/esm/commands/remove.mjs +87 -0
  44. package/dist/esm/commands/status.d.ts +16 -0
  45. package/dist/esm/commands/status.mjs +78 -0
  46. package/dist/esm/commands/sync.d.ts +33 -0
  47. package/dist/esm/commands/sync.mjs +730 -0
  48. package/dist/esm/commands/verify.d.ts +11 -0
  49. package/dist/esm/commands/verify.mjs +120 -0
  50. package/dist/esm/config/index.d.ts +15 -0
  51. package/dist/esm/config/index.mjs +196 -0
  52. package/dist/esm/config/io.d.ts +30 -0
  53. package/dist/esm/config/io.mjs +112 -0
  54. package/dist/esm/config/schema.d.ts +171 -0
  55. package/dist/esm/config/schema.mjs +69 -0
  56. package/dist/esm/errors.d.ts +3 -0
  57. package/dist/esm/errors.mjs +2 -0
  58. package/dist/esm/git/cache-dir.d.ts +16 -0
  59. package/dist/esm/git/cache-dir.mjs +23 -0
  60. package/dist/esm/git/fetch-source.d.ts +19 -0
  61. package/dist/esm/git/fetch-source.mjs +477 -0
  62. package/dist/esm/git/redact.d.ts +1 -0
  63. package/dist/esm/git/redact.mjs +4 -0
  64. package/dist/esm/git/resolve-remote.d.ts +15 -0
  65. package/dist/esm/git/resolve-remote.mjs +87 -0
  66. package/dist/esm/git/resolve-repo.d.ts +5 -0
  67. package/dist/esm/git/resolve-repo.mjs +52 -0
  68. package/dist/esm/gitignore.d.ts +18 -0
  69. package/dist/esm/gitignore.mjs +80 -0
  70. package/dist/esm/paths.d.ts +8 -0
  71. package/dist/esm/paths.mjs +34 -0
  72. package/dist/esm/source-id.d.ts +1 -0
  73. package/dist/esm/source-id.mjs +29 -0
  74. package/dist/esm/types/sync.d.ts +25 -0
  75. package/dist/esm/types/sync.mjs +0 -0
  76. package/package.json +138 -91
  77. package/dist/chunks/add.mjs +0 -3
  78. package/dist/chunks/clean-git-cache.mjs +0 -2
  79. package/dist/chunks/clean.mjs +0 -2
  80. package/dist/chunks/init.mjs +0 -3
  81. package/dist/chunks/prune.mjs +0 -2
  82. package/dist/chunks/remove.mjs +0 -3
  83. package/dist/chunks/status.mjs +0 -2
  84. package/dist/chunks/sync.mjs +0 -9
  85. package/dist/chunks/verify.mjs +0 -2
  86. package/dist/shared/docs-cache.BSvQNKuf.mjs +0 -2
  87. package/dist/shared/docs-cache.DH8jN6rl.mjs +0 -5
  88. package/dist/shared/docs-cache.Oi01HUbh.mjs +0 -6
  89. package/dist/shared/docs-cache.bWkgSdUq.mjs +0 -3
  90. package/dist/shared/docs-cache.kK1DPQIQ.mjs +0 -2
@@ -0,0 +1,11 @@
1
+ /**
2
+ * CLI exit codes.
3
+ *
4
+ * @see https://nodejs.org/api/process.html#process_exit_codes
5
+ */
6
+ export declare const ExitCode: {
7
+ Success: number;
8
+ FatalError: number;
9
+ InvalidArgument: number;
10
+ };
11
+ export type ExitCode = (typeof ExitCode)[keyof typeof ExitCode];
@@ -0,0 +1,5 @@
1
+ export const ExitCode = {
2
+ Success: 0,
3
+ FatalError: 1,
4
+ InvalidArgument: 9
5
+ };
@@ -0,0 +1,5 @@
1
+ export declare const CLI_NAME = "docs-cache";
2
+ /**
3
+ * The main entry point of the CLI
4
+ */
5
+ export declare function main(): Promise<void>;
@@ -0,0 +1,345 @@
1
+ import path from "node:path";
2
+ import process from "node:process";
3
+ import pc from "picocolors";
4
+ import { ExitCode } from "#cli/exit-code";
5
+ import { parseArgs } from "#cli/parse-args";
6
+ import { setSilentMode, setVerboseMode, symbols, ui } from "#cli/ui";
7
+ export const CLI_NAME = "docs-cache";
8
+ const HELP_TEXT = `
9
+ Usage: ${CLI_NAME} <command> [options]
10
+
11
+ Commands:
12
+ add Add sources to the config (supports github:org/repo#ref)
13
+ remove Remove sources from the config and targets
14
+ sync Synchronize cache with config
15
+ status Show cache status
16
+ clean Remove project cache
17
+ clean-cache Clear global git cache
18
+ prune Remove unused data
19
+ verify Validate cache integrity
20
+ init Create a new config interactively
21
+
22
+ Global options:
23
+ --config <path>
24
+ --cache-dir <path>
25
+ --offline
26
+ --fail-on-miss
27
+ --lock-only
28
+ --concurrency <n>
29
+ --json
30
+ --timeout-ms <n>
31
+ --silent
32
+ --verbose
33
+
34
+ Add options:
35
+ --source <repo>
36
+ --target <dir>
37
+ --target-dir <path>
38
+ --id <id>
39
+ `;
40
+ const printHelp = () => {
41
+ process.stdout.write(HELP_TEXT.trimStart());
42
+ };
43
+ const printError = (message) => {
44
+ process.stderr.write(`${symbols.error} ${message}
45
+ `);
46
+ };
47
+ const runAdd = async (parsed) => {
48
+ const options = parsed.options;
49
+ const { addSources } = await import("#commands/add");
50
+ const { runSync } = await import("#commands/sync");
51
+ if (parsed.entries.length === 0) {
52
+ throw new Error(
53
+ "Usage: docs-cache add [--source <repo> --target <dir>] <repo...>"
54
+ );
55
+ }
56
+ const result = await addSources({
57
+ configPath: options.config,
58
+ entries: parsed.entries
59
+ });
60
+ if (options.offline) {
61
+ if (!options.json) {
62
+ ui.line(`${symbols.warn} Offline: skipped sync`);
63
+ }
64
+ } else {
65
+ await runSync({
66
+ configPath: options.config,
67
+ cacheDirOverride: options.cacheDir,
68
+ json: options.json,
69
+ lockOnly: options.lockOnly,
70
+ offline: options.offline,
71
+ failOnMiss: options.failOnMiss,
72
+ sourceFilter: result.sources.map((source) => source.id),
73
+ timeoutMs: options.timeoutMs,
74
+ verbose: options.verbose
75
+ });
76
+ }
77
+ if (options.json) {
78
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
79
+ `);
80
+ return;
81
+ }
82
+ for (const source of result.sources) {
83
+ const repoLabel = source.repo.replace(/^https?:\/\//, "").replace(/\.git$/, "");
84
+ const targetLabel = source.targetDir ? ` ${pc.dim("->")} ${pc.magenta(source.targetDir)}` : "";
85
+ ui.item(symbols.success, source.id, `${pc.blue(repoLabel)}${targetLabel}`);
86
+ }
87
+ if (result.skipped?.length) {
88
+ ui.line(
89
+ `${symbols.warn} Skipped ${result.skipped.length} existing source${result.skipped.length === 1 ? "" : "s"}: ${result.skipped.join(", ")}`
90
+ );
91
+ }
92
+ ui.line(
93
+ `${symbols.info} Updated ${pc.gray(path.relative(process.cwd(), result.configPath) || "docs.config.json")}`
94
+ );
95
+ if (result.gitignoreUpdated && result.gitignorePath) {
96
+ ui.line(
97
+ `${symbols.info} Updated ${pc.gray(ui.path(result.gitignorePath))}`
98
+ );
99
+ }
100
+ };
101
+ const runRemove = async (parsed) => {
102
+ const options = parsed.options;
103
+ const { removeSources } = await import("#commands/remove");
104
+ const { pruneCache } = await import("#commands/prune");
105
+ if (parsed.ids.length === 0) {
106
+ throw new Error("Usage: docs-cache remove <id...>");
107
+ }
108
+ const result = await removeSources({
109
+ configPath: options.config,
110
+ ids: parsed.ids
111
+ });
112
+ if (options.json) {
113
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
114
+ `);
115
+ return;
116
+ }
117
+ if (result.removed.length > 0) {
118
+ ui.line(
119
+ `${symbols.success} Removed ${result.removed.length} source${result.removed.length === 1 ? "" : "s"}: ${result.removed.join(", ")}`
120
+ );
121
+ }
122
+ if (result.missing.length > 0) {
123
+ ui.line(
124
+ `${symbols.warn} Missing ${result.missing.length} source${result.missing.length === 1 ? "" : "s"}: ${result.missing.join(", ")}`
125
+ );
126
+ }
127
+ if (result.targetsRemoved.length > 0) {
128
+ const targetLabels = result.targetsRemoved.map((entry) => `${entry.id} -> ${ui.path(entry.targetDir)}`).join(", ");
129
+ ui.line(
130
+ `${symbols.success} Removed ${result.targetsRemoved.length} target${result.targetsRemoved.length === 1 ? "" : "s"}: ${targetLabels}`
131
+ );
132
+ }
133
+ ui.line(
134
+ `${symbols.info} Updated ${pc.gray(path.relative(process.cwd(), result.configPath) || "docs.config.json")}`
135
+ );
136
+ if (options.prune) {
137
+ await pruneCache({
138
+ configPath: options.config,
139
+ cacheDirOverride: options.cacheDir,
140
+ json: options.json
141
+ });
142
+ }
143
+ };
144
+ const runStatus = async (parsed) => {
145
+ const options = parsed.options;
146
+ const { getStatus, printStatus } = await import("#commands/status");
147
+ const status = await getStatus({
148
+ configPath: options.config,
149
+ cacheDirOverride: options.cacheDir,
150
+ json: options.json
151
+ });
152
+ if (options.json) {
153
+ process.stdout.write(`${JSON.stringify(status, null, 2)}
154
+ `);
155
+ return;
156
+ }
157
+ printStatus(status);
158
+ };
159
+ const runClean = async (parsed) => {
160
+ const options = parsed.options;
161
+ const { cleanCache } = await import("#commands/clean");
162
+ const result = await cleanCache({
163
+ configPath: options.config,
164
+ cacheDirOverride: options.cacheDir,
165
+ json: options.json
166
+ });
167
+ if (options.json) {
168
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
169
+ `);
170
+ return;
171
+ }
172
+ if (result.removed) {
173
+ ui.line(`${symbols.success} Removed cache at ${ui.path(result.cacheDir)}`);
174
+ return;
175
+ }
176
+ ui.line(
177
+ `${symbols.info} Cache already missing at ${ui.path(result.cacheDir)}`
178
+ );
179
+ };
180
+ const runCleanCache = async (parsed) => {
181
+ const options = parsed.options;
182
+ const { cleanGitCache } = await import("#commands/clean-git-cache");
183
+ const result = await cleanGitCache();
184
+ if (options.json) {
185
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
186
+ `);
187
+ return;
188
+ }
189
+ if (!result.removed) {
190
+ ui.line(
191
+ `${symbols.info} Global git cache already empty at ${ui.path(result.cacheDir)}`
192
+ );
193
+ return;
194
+ }
195
+ const sizeInMB = result.bytesFreed !== void 0 ? `${(result.bytesFreed / 1024 / 1024).toFixed(2)} MB` : "unknown size";
196
+ const repoLabel = result.repoCount !== void 0 ? ` (${result.repoCount} cached repositor${result.repoCount === 1 ? "y" : "ies"})` : "";
197
+ ui.line(
198
+ `${symbols.success} Cleared global git cache${repoLabel}: ${sizeInMB} freed`
199
+ );
200
+ ui.line(`${symbols.info} Cache location: ${ui.path(result.cacheDir)}`);
201
+ };
202
+ const runPrune = async (parsed) => {
203
+ const options = parsed.options;
204
+ const { pruneCache } = await import("#commands/prune");
205
+ const result = await pruneCache({
206
+ configPath: options.config,
207
+ cacheDirOverride: options.cacheDir,
208
+ json: options.json
209
+ });
210
+ if (options.json) {
211
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
212
+ `);
213
+ return;
214
+ }
215
+ if (result.removed.length === 0) {
216
+ ui.line(`${symbols.info} No cache entries to prune.`);
217
+ return;
218
+ }
219
+ ui.line(
220
+ `${symbols.success} Pruned ${result.removed.length} cache entr${result.removed.length === 1 ? "y" : "ies"}: ${result.removed.join(", ")}`
221
+ );
222
+ };
223
+ const runSyncCommand = async (parsed) => {
224
+ const options = parsed.options;
225
+ const { printSyncPlan, runSync } = await import("#commands/sync");
226
+ const plan = await runSync({
227
+ configPath: options.config,
228
+ cacheDirOverride: options.cacheDir,
229
+ json: options.json,
230
+ lockOnly: options.lockOnly,
231
+ offline: options.offline,
232
+ failOnMiss: options.failOnMiss,
233
+ timeoutMs: options.timeoutMs,
234
+ verbose: options.verbose
235
+ });
236
+ if (options.json) {
237
+ process.stdout.write(`${JSON.stringify(plan, null, 2)}
238
+ `);
239
+ return;
240
+ }
241
+ printSyncPlan(plan);
242
+ };
243
+ const runVerify = async (parsed) => {
244
+ const options = parsed.options;
245
+ const { printVerify, verifyCache } = await import("#commands/verify");
246
+ const report = await verifyCache({
247
+ configPath: options.config,
248
+ cacheDirOverride: options.cacheDir,
249
+ json: options.json
250
+ });
251
+ if (options.json) {
252
+ process.stdout.write(`${JSON.stringify(report, null, 2)}
253
+ `);
254
+ } else {
255
+ printVerify(report);
256
+ }
257
+ if (report.results.some((result) => !result.ok)) {
258
+ process.exit(ExitCode.FatalError);
259
+ }
260
+ };
261
+ const runInit = async (parsed) => {
262
+ const options = parsed.options;
263
+ const { initConfig } = await import("#commands/init");
264
+ if (options.config) {
265
+ throw new Error("Init does not accept --config. Use the project root.");
266
+ }
267
+ const result = await initConfig({
268
+ cacheDirOverride: options.cacheDir,
269
+ json: options.json
270
+ });
271
+ if (options.json) {
272
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
273
+ `);
274
+ return;
275
+ }
276
+ ui.line(`${symbols.success} Wrote ${pc.gray(ui.path(result.configPath))}`);
277
+ if (result.gitignoreUpdated && result.gitignorePath) {
278
+ ui.line(
279
+ `${symbols.info} Updated ${pc.gray(ui.path(result.gitignorePath))}`
280
+ );
281
+ }
282
+ };
283
+ const runCommand = async (parsed) => {
284
+ switch (parsed.command) {
285
+ case "add":
286
+ await runAdd(parsed);
287
+ return;
288
+ case "remove":
289
+ await runRemove(parsed);
290
+ return;
291
+ case "status":
292
+ await runStatus(parsed);
293
+ return;
294
+ case "clean":
295
+ await runClean(parsed);
296
+ return;
297
+ case "clean-cache":
298
+ await runCleanCache(parsed);
299
+ return;
300
+ case "prune":
301
+ await runPrune(parsed);
302
+ return;
303
+ case "sync":
304
+ await runSyncCommand(parsed);
305
+ return;
306
+ case "verify":
307
+ await runVerify(parsed);
308
+ return;
309
+ case "init":
310
+ await runInit(parsed);
311
+ return;
312
+ default:
313
+ ui.line(`${CLI_NAME} ${parsed.command}: not implemented yet.`);
314
+ }
315
+ };
316
+ export async function main() {
317
+ try {
318
+ process.on("uncaughtException", errorHandler);
319
+ process.on("unhandledRejection", errorHandler);
320
+ const parsed = parseArgs();
321
+ setSilentMode(parsed.options.silent);
322
+ setVerboseMode(parsed.options.verbose);
323
+ if (parsed.help) {
324
+ printHelp();
325
+ process.exit(ExitCode.Success);
326
+ }
327
+ if (!parsed.command) {
328
+ printHelp();
329
+ process.exit(ExitCode.InvalidArgument);
330
+ }
331
+ if (parsed.command !== "add" && parsed.command !== "remove" && parsed.positionals.length > 0) {
332
+ printError(`${CLI_NAME}: unexpected arguments.`);
333
+ printHelp();
334
+ process.exit(ExitCode.InvalidArgument);
335
+ }
336
+ await runCommand(parsed.parsed);
337
+ } catch (error) {
338
+ errorHandler(error);
339
+ }
340
+ }
341
+ function errorHandler(error) {
342
+ const message = error instanceof Error ? error.message : String(error);
343
+ printError(message);
344
+ process.exit(ExitCode.FatalError);
345
+ }
@@ -0,0 +1,12 @@
1
+ type LiveOutputOptions = {
2
+ stdout?: NodeJS.WriteStream;
3
+ maxWidth?: number;
4
+ };
5
+ export type LiveOutput = {
6
+ render: (lines: string[]) => void;
7
+ persist: (lines: string[]) => void;
8
+ clear: () => void;
9
+ stop: () => void;
10
+ };
11
+ export declare const createLiveOutput: (options?: LiveOutputOptions) => LiveOutput;
12
+ export {};
@@ -0,0 +1,30 @@
1
+ import cliTruncate from "cli-truncate";
2
+ import { createLogUpdate } from "log-update";
3
+ const normalizeLines = (lines) => lines.map((line) => line.length === 0 ? line : line);
4
+ export const createLiveOutput = (options = {}) => {
5
+ const stdout = options.stdout ?? process.stdout;
6
+ const updater = createLogUpdate(stdout);
7
+ const maxWidth = options.maxWidth ?? Math.max(20, (stdout.columns ?? 80) - 2);
8
+ const truncate = (line) => cliTruncate(line, maxWidth, { position: "end" });
9
+ const render = (lines) => {
10
+ const output = normalizeLines(lines).map(truncate).join("\n");
11
+ updater(output);
12
+ };
13
+ const persist = (lines) => {
14
+ const output = normalizeLines(lines).map(truncate).join("\n");
15
+ updater(output);
16
+ updater.done();
17
+ };
18
+ const clear = () => {
19
+ updater.clear();
20
+ };
21
+ const stop = () => {
22
+ updater.done();
23
+ };
24
+ return {
25
+ render,
26
+ persist,
27
+ clear,
28
+ stop
29
+ };
30
+ };
@@ -0,0 +1,13 @@
1
+ import type { CliCommand, CliOptions } from "./types";
2
+ declare const COMMANDS: readonly ["add", "remove", "sync", "status", "clean", "clean-cache", "prune", "verify", "init"];
3
+ type Command = (typeof COMMANDS)[number];
4
+ export type ParsedArgs = {
5
+ command: Command | null;
6
+ options: CliOptions;
7
+ positionals: string[];
8
+ rawArgs: string[];
9
+ help: boolean;
10
+ parsed: CliCommand;
11
+ };
12
+ export declare const parseArgs: (argv?: any) => ParsedArgs;
13
+ export {};