docs-cache 0.4.3 → 0.5.2

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 +13 -13
  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 +67 -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 +51 -7
  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.BOr9BnyP.mjs +0 -5
  87. package/dist/shared/docs-cache.BSvQNKuf.mjs +0 -2
  88. package/dist/shared/docs-cache.CQiaFDb_.mjs +0 -7
  89. package/dist/shared/docs-cache.CaOcl4OS.mjs +0 -3
  90. package/dist/shared/docs-cache.kK1DPQIQ.mjs +0 -2
@@ -0,0 +1,10 @@
1
+ export type CleanGitCacheResult = {
2
+ removed: boolean;
3
+ cacheDir: string;
4
+ repoCount?: number;
5
+ bytesFreed?: number;
6
+ };
7
+ export type CleanGitCacheOptions = {
8
+ json?: boolean;
9
+ };
10
+ export declare const cleanGitCache: () => Promise<CleanGitCacheResult>;
@@ -0,0 +1,48 @@
1
+ import { readdir, rm, stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { exists, resolveGitCacheDir } from "#git/cache-dir";
4
+ const getDirSize = async (dirPath) => {
5
+ try {
6
+ const entries = await readdir(dirPath, { withFileTypes: true });
7
+ let totalSize = 0;
8
+ for (const entry of entries) {
9
+ const fullPath = path.join(dirPath, entry.name);
10
+ if (entry.isDirectory()) {
11
+ totalSize += await getDirSize(fullPath);
12
+ } else {
13
+ const stats = await stat(fullPath);
14
+ totalSize += stats.size;
15
+ }
16
+ }
17
+ return totalSize;
18
+ } catch {
19
+ return 0;
20
+ }
21
+ };
22
+ const countCachedRepos = async (cacheDir) => {
23
+ try {
24
+ const entries = await readdir(cacheDir);
25
+ return entries.length;
26
+ } catch {
27
+ return 0;
28
+ }
29
+ };
30
+ export const cleanGitCache = async () => {
31
+ const cacheDir = resolveGitCacheDir();
32
+ const cacheExists = await exists(cacheDir);
33
+ if (!cacheExists) {
34
+ return {
35
+ removed: false,
36
+ cacheDir
37
+ };
38
+ }
39
+ const repoCount = await countCachedRepos(cacheDir);
40
+ const bytesFreed = await getDirSize(cacheDir);
41
+ await rm(cacheDir, { recursive: true, force: true });
42
+ return {
43
+ removed: true,
44
+ cacheDir,
45
+ repoCount,
46
+ bytesFreed
47
+ };
48
+ };
@@ -0,0 +1,10 @@
1
+ type CleanOptions = {
2
+ configPath?: string;
3
+ cacheDirOverride?: string;
4
+ json: boolean;
5
+ };
6
+ export declare const cleanCache: (options: CleanOptions) => Promise<{
7
+ cacheDir: any;
8
+ removed: boolean;
9
+ }>;
10
+ export {};
@@ -0,0 +1,27 @@
1
+ import { access, rm } from "node:fs/promises";
2
+ import { DEFAULT_CACHE_DIR, loadConfig } from "#config";
3
+ import { resolveCacheDir } from "#core/paths";
4
+ const exists = async (target) => {
5
+ try {
6
+ await access(target);
7
+ return true;
8
+ } catch {
9
+ return false;
10
+ }
11
+ };
12
+ export const cleanCache = async (options) => {
13
+ const { config, resolvedPath } = await loadConfig(options.configPath);
14
+ const cacheDir = resolveCacheDir(
15
+ resolvedPath,
16
+ config.cacheDir ?? DEFAULT_CACHE_DIR,
17
+ options.cacheDirOverride
18
+ );
19
+ const cacheDirExists = await exists(cacheDir);
20
+ if (cacheDirExists) {
21
+ await rm(cacheDir, { recursive: true, force: true });
22
+ }
23
+ return {
24
+ cacheDir,
25
+ removed: cacheDirExists
26
+ };
27
+ };
@@ -0,0 +1,19 @@
1
+ import { confirm as clackConfirm, isCancel as clackIsCancel, select as clackSelect, text as clackText } from "@clack/prompts";
2
+ type InitOptions = {
3
+ cacheDirOverride?: string;
4
+ json: boolean;
5
+ cwd?: string;
6
+ };
7
+ type PromptDeps = {
8
+ confirm?: typeof clackConfirm;
9
+ isCancel?: typeof clackIsCancel;
10
+ select?: typeof clackSelect;
11
+ text?: typeof clackText;
12
+ };
13
+ export declare const initConfig: (options: InitOptions, deps?: PromptDeps) => Promise<{
14
+ configPath: string;
15
+ created: boolean;
16
+ gitignoreUpdated: any;
17
+ gitignorePath: any;
18
+ }>;
19
+ export {};
@@ -0,0 +1,179 @@
1
+ import { access, readFile, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import {
4
+ confirm as clackConfirm,
5
+ isCancel as clackIsCancel,
6
+ select as clackSelect,
7
+ text as clackText
8
+ } from "@clack/prompts";
9
+ import {
10
+ DEFAULT_CACHE_DIR,
11
+ DEFAULT_CONFIG_FILENAME,
12
+ stripDefaultConfigValues,
13
+ writeConfig
14
+ } from "#config";
15
+ import { ensureGitignoreEntry, getGitignoreStatus } from "#core/gitignore";
16
+ const exists = async (target) => {
17
+ try {
18
+ await access(target);
19
+ return true;
20
+ } catch {
21
+ return false;
22
+ }
23
+ };
24
+ const readJson = async (filePath) => {
25
+ const raw = await readFile(filePath, "utf8");
26
+ return JSON.parse(raw);
27
+ };
28
+ const findExistingConfigPaths = async (cwd) => {
29
+ const defaultConfigPath = path.resolve(cwd, DEFAULT_CONFIG_FILENAME);
30
+ const packagePath = path.resolve(cwd, "package.json");
31
+ const existingConfigPaths = [];
32
+ if (await exists(defaultConfigPath)) {
33
+ existingConfigPaths.push(defaultConfigPath);
34
+ }
35
+ if (await exists(packagePath)) {
36
+ const parsed = await readJson(packagePath);
37
+ if (parsed["docs-cache"]) {
38
+ existingConfigPaths.push(packagePath);
39
+ }
40
+ }
41
+ return { existingConfigPaths, defaultConfigPath, packagePath };
42
+ };
43
+ const selectConfigPath = async (packagePath, defaultConfigPath, select, isCancel) => {
44
+ if (!await exists(packagePath)) {
45
+ return defaultConfigPath;
46
+ }
47
+ const parsed = await readJson(packagePath);
48
+ if (parsed["docs-cache"]) {
49
+ return defaultConfigPath;
50
+ }
51
+ const locationAnswer = await select({
52
+ message: "Config location",
53
+ options: [
54
+ { value: "config", label: "docs.config.json" },
55
+ { value: "package", label: "package.json" }
56
+ ],
57
+ initialValue: "config"
58
+ });
59
+ if (isCancel(locationAnswer)) {
60
+ throw new Error("Init cancelled.");
61
+ }
62
+ return locationAnswer === "package" ? packagePath : defaultConfigPath;
63
+ };
64
+ const promptInitAnswers = async (cacheDir, cwd, confirm, text, isCancel) => {
65
+ const cacheDirAnswer = await text({
66
+ message: "Cache directory",
67
+ initialValue: cacheDir
68
+ });
69
+ if (isCancel(cacheDirAnswer)) {
70
+ throw new Error("Init cancelled.");
71
+ }
72
+ const cacheDirValue = cacheDirAnswer || DEFAULT_CACHE_DIR;
73
+ const tocAnswer = await confirm({
74
+ message: "Generate TOC.md (table of contents with links to all documentation)",
75
+ initialValue: true
76
+ });
77
+ if (isCancel(tocAnswer)) {
78
+ throw new Error("Init cancelled.");
79
+ }
80
+ const gitignoreStatus = await getGitignoreStatus(cwd, cacheDirValue);
81
+ let gitignoreAnswer = false;
82
+ if (gitignoreStatus.entry && !gitignoreStatus.hasEntry) {
83
+ const reply = await confirm({
84
+ message: "Add cache directory to .gitignore",
85
+ initialValue: true
86
+ });
87
+ if (isCancel(reply)) {
88
+ throw new Error("Init cancelled.");
89
+ }
90
+ gitignoreAnswer = reply;
91
+ }
92
+ return {
93
+ cacheDir: cacheDirValue,
94
+ toc: tocAnswer,
95
+ gitignore: gitignoreAnswer
96
+ };
97
+ };
98
+ const buildBaseConfig = (cacheDir, toc) => {
99
+ const config = {
100
+ $schema: "https://raw.githubusercontent.com/fbosch/docs-cache/main/docs.config.schema.json",
101
+ sources: []
102
+ };
103
+ if (cacheDir !== DEFAULT_CACHE_DIR) {
104
+ config.cacheDir = cacheDir;
105
+ }
106
+ if (!toc) {
107
+ config.defaults = { toc: false };
108
+ }
109
+ return config;
110
+ };
111
+ const writePackageConfig = async (configPath, config, gitignore) => {
112
+ const raw = await readFile(configPath, "utf8");
113
+ const pkg = JSON.parse(raw);
114
+ if (pkg["docs-cache"]) {
115
+ throw new Error(`docs-cache config already exists in ${configPath}.`);
116
+ }
117
+ pkg["docs-cache"] = stripDefaultConfigValues(config);
118
+ await writeFile(configPath, `${JSON.stringify(pkg, null, 2)}
119
+ `, "utf8");
120
+ const gitignoreResult = gitignore ? await ensureGitignoreEntry(
121
+ path.dirname(configPath),
122
+ config.cacheDir ?? DEFAULT_CACHE_DIR
123
+ ) : null;
124
+ return {
125
+ configPath,
126
+ created: true,
127
+ gitignoreUpdated: gitignoreResult?.updated ?? false,
128
+ gitignorePath: gitignoreResult?.gitignorePath ?? null
129
+ };
130
+ };
131
+ const writeStandaloneConfig = async (configPath, config, gitignore) => {
132
+ await writeConfig(configPath, config);
133
+ const gitignoreResult = gitignore ? await ensureGitignoreEntry(
134
+ path.dirname(configPath),
135
+ config.cacheDir ?? DEFAULT_CACHE_DIR
136
+ ) : null;
137
+ return {
138
+ configPath,
139
+ created: true,
140
+ gitignoreUpdated: gitignoreResult?.updated ?? false,
141
+ gitignorePath: gitignoreResult?.gitignorePath ?? null
142
+ };
143
+ };
144
+ export const initConfig = async (options, deps = {}) => {
145
+ const confirm = deps.confirm ?? clackConfirm;
146
+ const isCancel = deps.isCancel ?? clackIsCancel;
147
+ const select = deps.select ?? clackSelect;
148
+ const text = deps.text ?? clackText;
149
+ const cwd = options.cwd ?? process.cwd();
150
+ const { existingConfigPaths, defaultConfigPath, packagePath } = await findExistingConfigPaths(cwd);
151
+ if (existingConfigPaths.length > 0) {
152
+ throw new Error(
153
+ `Config already exists at ${existingConfigPaths.join(", ")}. Init aborted.`
154
+ );
155
+ }
156
+ const configPath = await selectConfigPath(
157
+ packagePath,
158
+ defaultConfigPath,
159
+ select,
160
+ isCancel
161
+ );
162
+ const cacheDir = options.cacheDirOverride ?? DEFAULT_CACHE_DIR;
163
+ const answers = await promptInitAnswers(
164
+ cacheDir,
165
+ cwd,
166
+ confirm,
167
+ text,
168
+ isCancel
169
+ );
170
+ const resolvedConfigPath = path.resolve(cwd, configPath);
171
+ const config = buildBaseConfig(answers.cacheDir, answers.toc);
172
+ if (path.basename(resolvedConfigPath) === "package.json") {
173
+ return writePackageConfig(resolvedConfigPath, config, answers.gitignore);
174
+ }
175
+ if (await exists(resolvedConfigPath)) {
176
+ throw new Error(`Config already exists at ${resolvedConfigPath}.`);
177
+ }
178
+ return writeStandaloneConfig(resolvedConfigPath, config, answers.gitignore);
179
+ };
@@ -0,0 +1,11 @@
1
+ type PruneOptions = {
2
+ configPath?: string;
3
+ cacheDirOverride?: string;
4
+ json: boolean;
5
+ };
6
+ export declare const pruneCache: (options: PruneOptions) => Promise<{
7
+ cacheDir: any;
8
+ removed: string[];
9
+ kept: any;
10
+ }>;
11
+ export {};
@@ -0,0 +1,52 @@
1
+ import { access, readdir, rm } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { DEFAULT_CACHE_DIR, loadConfig } from "#config";
4
+ import { resolveCacheDir } from "#core/paths";
5
+ const exists = async (target) => {
6
+ try {
7
+ await access(target);
8
+ return true;
9
+ } catch {
10
+ return false;
11
+ }
12
+ };
13
+ export const pruneCache = async (options) => {
14
+ const { config, resolvedPath, sources } = await loadConfig(
15
+ options.configPath
16
+ );
17
+ const cacheDir = resolveCacheDir(
18
+ resolvedPath,
19
+ config.cacheDir ?? DEFAULT_CACHE_DIR,
20
+ options.cacheDirOverride
21
+ );
22
+ const cacheDirExists = await exists(cacheDir);
23
+ if (!cacheDirExists) {
24
+ return {
25
+ cacheDir,
26
+ removed: [],
27
+ kept: sources.map((source) => source.id)
28
+ };
29
+ }
30
+ const keepIds = new Set(sources.map((source) => source.id));
31
+ const entries = await readdir(cacheDir, { withFileTypes: true });
32
+ const removed = [];
33
+ for (const entry of entries) {
34
+ if (!entry.isDirectory()) {
35
+ continue;
36
+ }
37
+ const name = entry.name;
38
+ if (keepIds.has(name)) {
39
+ continue;
40
+ }
41
+ if (name.startsWith(".tmp-")) {
42
+ continue;
43
+ }
44
+ await rm(path.join(cacheDir, name), { recursive: true, force: true });
45
+ removed.push(name);
46
+ }
47
+ return {
48
+ cacheDir,
49
+ removed,
50
+ kept: sources.map((source) => source.id)
51
+ };
52
+ };
@@ -0,0 +1,12 @@
1
+ export declare const removeSources: (params: {
2
+ configPath?: string;
3
+ ids: string[];
4
+ }) => Promise<{
5
+ configPath: any;
6
+ removed: any;
7
+ missing: string[];
8
+ targetsRemoved: {
9
+ id: string;
10
+ targetDir: string;
11
+ }[];
12
+ }>;
@@ -0,0 +1,87 @@
1
+ import { rm } from "node:fs/promises";
2
+ import {
3
+ mergeConfigBase,
4
+ readConfigAtPath,
5
+ resolveConfigTarget,
6
+ writeConfigFile
7
+ } from "#config/io";
8
+ import { resolveTargetDir } from "#core/paths";
9
+ import { resolveRepoInput } from "#git/resolve-repo";
10
+ const resolveIdsToRemove = (ids, config) => {
11
+ const sourcesById = new Map(
12
+ config.sources.map((source) => [source.id, source])
13
+ );
14
+ const sourcesByRepo = new Map(
15
+ config.sources.map((source) => [source.repo, source])
16
+ );
17
+ const idsToRemove = /* @__PURE__ */ new Set();
18
+ const missing = [];
19
+ for (const token of ids) {
20
+ if (sourcesById.has(token)) {
21
+ idsToRemove.add(token);
22
+ continue;
23
+ }
24
+ const resolved = resolveRepoInput(token);
25
+ if (resolved.repoUrl && sourcesByRepo.has(resolved.repoUrl)) {
26
+ const source = sourcesByRepo.get(resolved.repoUrl);
27
+ if (source) {
28
+ idsToRemove.add(source.id);
29
+ }
30
+ continue;
31
+ }
32
+ if (resolved.inferredId && sourcesById.has(resolved.inferredId)) {
33
+ idsToRemove.add(resolved.inferredId);
34
+ continue;
35
+ }
36
+ missing.push(token);
37
+ }
38
+ return { idsToRemove, missing };
39
+ };
40
+ const removeTargets = async (resolvedPath, removedSources) => {
41
+ const targetRemovals = [];
42
+ for (const source of removedSources) {
43
+ if (!source.targetDir) {
44
+ continue;
45
+ }
46
+ const targetDir = resolveTargetDir(resolvedPath, source.targetDir);
47
+ await rm(targetDir, { recursive: true, force: true });
48
+ targetRemovals.push({ id: source.id, targetDir });
49
+ }
50
+ return targetRemovals;
51
+ };
52
+ export const removeSources = async (params) => {
53
+ if (params.ids.length === 0) {
54
+ throw new Error("No sources specified to remove.");
55
+ }
56
+ const target = await resolveConfigTarget(params.configPath);
57
+ const resolvedPath = target.resolvedPath;
58
+ const { config, rawConfig, rawPackage } = await readConfigAtPath(target);
59
+ if (target.mode === "package" && !rawConfig) {
60
+ throw new Error(`Missing docs-cache config in ${resolvedPath}.`);
61
+ }
62
+ const { idsToRemove, missing } = resolveIdsToRemove(params.ids, config);
63
+ const remaining = config.sources.filter(
64
+ (source) => !idsToRemove.has(source.id)
65
+ );
66
+ const removed = config.sources.filter((source) => idsToRemove.has(source.id)).map((source) => source.id);
67
+ const removedSources = config.sources.filter(
68
+ (source) => idsToRemove.has(source.id)
69
+ );
70
+ if (removed.length === 0) {
71
+ throw new Error("No matching sources found to remove.");
72
+ }
73
+ const nextConfig = mergeConfigBase(rawConfig ?? config, remaining);
74
+ await writeConfigFile({
75
+ mode: target.mode,
76
+ resolvedPath,
77
+ config: nextConfig,
78
+ rawPackage
79
+ });
80
+ const targetRemovals = await removeTargets(resolvedPath, removedSources);
81
+ return {
82
+ configPath: resolvedPath,
83
+ removed,
84
+ missing,
85
+ targetsRemoved: targetRemovals
86
+ };
87
+ };
@@ -0,0 +1,16 @@
1
+ type StatusOptions = {
2
+ configPath?: string;
3
+ cacheDirOverride?: string;
4
+ json: boolean;
5
+ };
6
+ export declare const getStatus: (options: StatusOptions) => Promise<{
7
+ configPath: any;
8
+ cacheDir: any;
9
+ cacheDirExists: boolean;
10
+ lockPath: any;
11
+ lockExists: boolean;
12
+ lockValid: boolean;
13
+ sources: any;
14
+ }>;
15
+ export declare const printStatus: (status: Awaited<ReturnType<typeof getStatus>>) => void;
16
+ export {};
@@ -0,0 +1,78 @@
1
+ import { access } from "node:fs/promises";
2
+ import pc from "picocolors";
3
+ import { DEFAULT_LOCK_FILENAME, readLock, resolveLockPath } from "#cache/lock";
4
+ import { symbols, ui } from "#cli/ui";
5
+ import { DEFAULT_CACHE_DIR, loadConfig } from "#config";
6
+ import { getCacheLayout, resolveCacheDir } from "#core/paths";
7
+ const exists = async (target) => {
8
+ try {
9
+ await access(target);
10
+ return true;
11
+ } catch {
12
+ return false;
13
+ }
14
+ };
15
+ export const getStatus = async (options) => {
16
+ const { config, resolvedPath, sources } = await loadConfig(
17
+ options.configPath
18
+ );
19
+ const resolvedCacheDir = resolveCacheDir(
20
+ resolvedPath,
21
+ config.cacheDir ?? DEFAULT_CACHE_DIR,
22
+ options.cacheDirOverride
23
+ );
24
+ const cacheDirExists = await exists(resolvedCacheDir);
25
+ const lockPath = resolveLockPath(resolvedPath);
26
+ const lockExists = await exists(lockPath);
27
+ let lockValid = false;
28
+ let lockData = null;
29
+ if (lockExists) {
30
+ try {
31
+ lockData = await readLock(lockPath);
32
+ lockValid = true;
33
+ } catch {
34
+ lockValid = false;
35
+ }
36
+ }
37
+ const sourceStatus = await Promise.all(
38
+ sources.map(async (source) => {
39
+ const layout = getCacheLayout(resolvedCacheDir, source.id);
40
+ const docsExists = await exists(layout.sourceDir);
41
+ const lockEntry = lockData?.sources?.[source.id] ?? null;
42
+ return {
43
+ id: source.id,
44
+ docsPath: layout.sourceDir,
45
+ docsExists,
46
+ lockEntry
47
+ };
48
+ })
49
+ );
50
+ return {
51
+ configPath: resolvedPath,
52
+ cacheDir: resolvedCacheDir,
53
+ cacheDirExists,
54
+ lockPath,
55
+ lockExists,
56
+ lockValid,
57
+ sources: sourceStatus
58
+ };
59
+ };
60
+ export const printStatus = (status) => {
61
+ const relCache = ui.path(status.cacheDir);
62
+ const cacheState = status.cacheDirExists ? pc.green("present") : pc.red("missing");
63
+ const lockState = status.lockExists ? status.lockValid ? pc.green("valid") : pc.red("invalid") : pc.yellow("missing");
64
+ ui.header("Cache", `${relCache} (${cacheState})`);
65
+ ui.header("Lock", `${DEFAULT_LOCK_FILENAME} (${lockState})`);
66
+ if (status.sources.length === 0) {
67
+ ui.line();
68
+ ui.line(`${symbols.warn} No sources configured.`);
69
+ return;
70
+ }
71
+ ui.line();
72
+ for (const source of status.sources) {
73
+ const icon = source.docsExists ? symbols.success : symbols.error;
74
+ const lockLabel = source.lockEntry ? pc.green("locked") : pc.yellow("new");
75
+ const shortHash = ui.hash(source.lockEntry?.resolvedCommit);
76
+ ui.item(icon, source.id.padEnd(20), `${lockLabel.padEnd(10)} ${shortHash}`);
77
+ }
78
+ };
@@ -0,0 +1,33 @@
1
+ import { materializeSource } from "#cache/materialize";
2
+ import { fetchSource } from "#git/fetch-source";
3
+ import { resolveRemoteCommit } from "#git/resolve-remote";
4
+ import type { SyncOptions } from "#types/sync";
5
+ type SyncDeps = {
6
+ resolveRemoteCommit?: typeof resolveRemoteCommit;
7
+ fetchSource?: typeof fetchSource;
8
+ materializeSource?: typeof materializeSource;
9
+ };
10
+ export declare const getSyncPlan: (options: SyncOptions, deps?: SyncDeps) => Promise<{
11
+ config: any;
12
+ configPath: any;
13
+ cacheDir: any;
14
+ lockPath: any;
15
+ lockExists: boolean;
16
+ lockData: any;
17
+ results: SyncResult[];
18
+ sources: any;
19
+ defaults: DocsCacheDefaults;
20
+ }>;
21
+ export declare const runSync: (options: SyncOptions, deps?: SyncDeps) => Promise<{
22
+ config: any;
23
+ configPath: any;
24
+ cacheDir: any;
25
+ lockPath: any;
26
+ lockExists: boolean;
27
+ lockData: any;
28
+ results: SyncResult[];
29
+ sources: any;
30
+ defaults: DocsCacheDefaults;
31
+ }>;
32
+ export declare const printSyncPlan: (plan: Awaited<ReturnType<typeof getSyncPlan>>) => void;
33
+ export {};