docs-cache 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/esm/cache/materialize.mjs +1 -1
- package/dist/esm/config/index.mjs +1 -1
- package/dist/esm/git/fetch-source.mjs +68 -40
- package/dist/esm/source-id.mjs +18 -4
- package/package.json +138 -135
package/README.md
CHANGED
|
@@ -102,7 +102,7 @@ These fields can be set in `defaults` and are inherited by every source unless o
|
|
|
102
102
|
| `ignoreHidden` | Skip hidden files and directories (dotfiles). Default: `false`. |
|
|
103
103
|
| `allowHosts` | Allowed Git hosts. Default: `["github.com", "gitlab.com", "visualstudio.com"]`. |
|
|
104
104
|
| `toc` | Generate per-source `TOC.md`. Default: `true`. Supports `true`, `false`, or a format: `"tree"` (human readable), `"compressed"` |
|
|
105
|
-
| `unwrapSingleRootDir` | If the materialized output is nested under a single directory, unwrap it (recursively). Default: `
|
|
105
|
+
| `unwrapSingleRootDir` | If the materialized output is nested under a single directory, unwrap it (recursively). Default: `true`. |
|
|
106
106
|
|
|
107
107
|
### Source options
|
|
108
108
|
|
|
@@ -121,7 +121,7 @@ const resolveMaterializeParams = (params) => ({
|
|
|
121
121
|
...params,
|
|
122
122
|
exclude: params.exclude ?? [],
|
|
123
123
|
ignoreHidden: params.ignoreHidden ?? false,
|
|
124
|
-
unwrapSingleRootDir: params.unwrapSingleRootDir ??
|
|
124
|
+
unwrapSingleRootDir: params.unwrapSingleRootDir ?? true,
|
|
125
125
|
json: params.json ?? false,
|
|
126
126
|
progressThrottleMs: params.progressThrottleMs ?? 120
|
|
127
127
|
});
|
|
@@ -177,35 +177,54 @@ const ensureCommitAvailable = async (repoPath, commit, options) => {
|
|
|
177
177
|
logger: options?.logger
|
|
178
178
|
});
|
|
179
179
|
};
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
180
|
+
const patternHasGlob = (pattern) => pattern.includes("*") || pattern.includes("?") || pattern.includes("[");
|
|
181
|
+
const normalizeSparsePatterns = (include) => (include ?? []).map((pattern) => pattern.replace(/\\/g, "/")).filter(Boolean);
|
|
182
|
+
const isDirectoryLiteral = (pattern) => pattern.endsWith("/");
|
|
183
|
+
const toNoConePattern = (pattern) => {
|
|
184
|
+
if (!patternHasGlob(pattern) && isDirectoryLiteral(pattern)) {
|
|
185
|
+
return pattern.endsWith("/") ? pattern : `${pattern}/`;
|
|
186
|
+
}
|
|
187
|
+
return pattern;
|
|
188
|
+
};
|
|
189
|
+
const resolveSparseSpec = (include) => {
|
|
190
|
+
const normalized = normalizeSparsePatterns(include);
|
|
191
|
+
if (normalized.length === 0) {
|
|
192
|
+
return { enabled: false, mode: "cone", patterns: [] };
|
|
183
193
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
194
|
+
const conePaths = [];
|
|
195
|
+
let coneEligible = true;
|
|
196
|
+
for (const pattern of normalized) {
|
|
197
|
+
if (pattern.includes("**")) {
|
|
198
|
+
coneEligible = false;
|
|
199
|
+
break;
|
|
187
200
|
}
|
|
201
|
+
if (patternHasGlob(pattern)) {
|
|
202
|
+
coneEligible = false;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
if (isDirectoryLiteral(pattern)) {
|
|
206
|
+
conePaths.push(pattern.replace(/\/+$/, ""));
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
coneEligible = false;
|
|
210
|
+
break;
|
|
188
211
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (!include) {
|
|
193
|
-
return [];
|
|
212
|
+
const uniquePaths = Array.from(new Set(conePaths.filter(Boolean)));
|
|
213
|
+
if (coneEligible && uniquePaths.length > 0) {
|
|
214
|
+
return { enabled: true, mode: "cone", patterns: uniquePaths };
|
|
194
215
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
});
|
|
201
|
-
return Array.from(new Set(paths.filter((value) => value.length > 0)));
|
|
216
|
+
return {
|
|
217
|
+
enabled: true,
|
|
218
|
+
mode: "no-cone",
|
|
219
|
+
patterns: normalized.map(toNoConePattern)
|
|
220
|
+
};
|
|
202
221
|
};
|
|
203
222
|
const cloneRepo = async (params, outDir) => {
|
|
204
223
|
if (params.offline) {
|
|
205
224
|
throw new Error(`Cannot clone ${params.repo} while offline.`);
|
|
206
225
|
}
|
|
207
226
|
const isCommitRef = /^[0-9a-f]{7,40}$/i.test(params.ref);
|
|
208
|
-
const
|
|
227
|
+
const sparseSpec = resolveSparseSpec(params.include);
|
|
209
228
|
const buildCloneArgs = () => {
|
|
210
229
|
const cloneArgs2 = [
|
|
211
230
|
"clone",
|
|
@@ -218,7 +237,7 @@ const cloneRepo = async (params, outDir) => {
|
|
|
218
237
|
return cloneArgs2;
|
|
219
238
|
};
|
|
220
239
|
const cloneArgs = buildCloneArgs();
|
|
221
|
-
if (
|
|
240
|
+
if (sparseSpec.enabled) {
|
|
222
241
|
cloneArgs.push("--sparse");
|
|
223
242
|
}
|
|
224
243
|
if (!isCommitRef) {
|
|
@@ -239,14 +258,16 @@ const cloneRepo = async (params, outDir) => {
|
|
|
239
258
|
logger: params.logger,
|
|
240
259
|
offline: params.offline
|
|
241
260
|
});
|
|
242
|
-
if (
|
|
243
|
-
const
|
|
244
|
-
if (
|
|
245
|
-
|
|
246
|
-
timeoutMs: params.timeoutMs,
|
|
247
|
-
logger: params.logger
|
|
248
|
-
});
|
|
261
|
+
if (sparseSpec.enabled) {
|
|
262
|
+
const sparseArgs = ["-C", outDir, "sparse-checkout", "set"];
|
|
263
|
+
if (sparseSpec.mode === "no-cone") {
|
|
264
|
+
sparseArgs.push("--no-cone");
|
|
249
265
|
}
|
|
266
|
+
sparseArgs.push(...sparseSpec.patterns);
|
|
267
|
+
await git(sparseArgs, {
|
|
268
|
+
timeoutMs: params.timeoutMs,
|
|
269
|
+
logger: params.logger
|
|
270
|
+
});
|
|
250
271
|
}
|
|
251
272
|
await git(
|
|
252
273
|
["-C", outDir, "checkout", "--quiet", "--detach", params.resolvedCommit],
|
|
@@ -281,9 +302,14 @@ const addWorktreeFromCache = async (params, cachePath, outDir) => {
|
|
|
281
302
|
allowFileProtocol: true
|
|
282
303
|
}
|
|
283
304
|
);
|
|
284
|
-
const
|
|
285
|
-
if (
|
|
286
|
-
|
|
305
|
+
const sparseSpec = resolveSparseSpec(params.include);
|
|
306
|
+
if (sparseSpec.enabled) {
|
|
307
|
+
const sparseArgs = ["-C", outDir, "sparse-checkout", "set"];
|
|
308
|
+
if (sparseSpec.mode === "no-cone") {
|
|
309
|
+
sparseArgs.push("--no-cone");
|
|
310
|
+
}
|
|
311
|
+
sparseArgs.push(...sparseSpec.patterns);
|
|
312
|
+
await git(sparseArgs, {
|
|
287
313
|
timeoutMs: params.timeoutMs,
|
|
288
314
|
logger: params.logger,
|
|
289
315
|
allowFileProtocol: true
|
|
@@ -380,7 +406,7 @@ const cloneOrUpdateRepo = async (params, outDir) => {
|
|
|
380
406
|
const cacheExists = await exists(cachePath);
|
|
381
407
|
const cacheValid = cacheExists && await isValidGitRepo(cachePath);
|
|
382
408
|
const isCommitRef = /^[0-9a-f]{7,40}$/i.test(params.ref);
|
|
383
|
-
const
|
|
409
|
+
const sparseSpec = resolveSparseSpec(params.include);
|
|
384
410
|
let usedCache = cacheValid;
|
|
385
411
|
let worktreeUsed = false;
|
|
386
412
|
const cacheRoot = resolveGitCacheDir();
|
|
@@ -410,7 +436,7 @@ const cloneOrUpdateRepo = async (params, outDir) => {
|
|
|
410
436
|
if (await isPartialClone(cachePath)) {
|
|
411
437
|
localCloneArgs.splice(2, 0, "--filter=blob:none");
|
|
412
438
|
}
|
|
413
|
-
if (
|
|
439
|
+
if (sparseSpec.enabled) {
|
|
414
440
|
localCloneArgs.push("--sparse");
|
|
415
441
|
}
|
|
416
442
|
if (!isCommitRef) {
|
|
@@ -428,15 +454,17 @@ const cloneOrUpdateRepo = async (params, outDir) => {
|
|
|
428
454
|
progressLogger: params.progressLogger,
|
|
429
455
|
forceProgress: Boolean(params.progressLogger)
|
|
430
456
|
});
|
|
431
|
-
if (
|
|
432
|
-
const
|
|
433
|
-
if (
|
|
434
|
-
|
|
435
|
-
timeoutMs: params.timeoutMs,
|
|
436
|
-
allowFileProtocol: true,
|
|
437
|
-
logger: params.logger
|
|
438
|
-
});
|
|
457
|
+
if (sparseSpec.enabled) {
|
|
458
|
+
const sparseArgs = ["-C", outDir, "sparse-checkout", "set"];
|
|
459
|
+
if (sparseSpec.mode === "no-cone") {
|
|
460
|
+
sparseArgs.push("--no-cone");
|
|
439
461
|
}
|
|
462
|
+
sparseArgs.push(...sparseSpec.patterns);
|
|
463
|
+
await git(sparseArgs, {
|
|
464
|
+
timeoutMs: params.timeoutMs,
|
|
465
|
+
allowFileProtocol: true,
|
|
466
|
+
logger: params.logger
|
|
467
|
+
});
|
|
440
468
|
}
|
|
441
469
|
await ensureCommitAvailable(outDir, params.resolvedCommit, {
|
|
442
470
|
timeoutMs: params.timeoutMs,
|
package/dist/esm/source-id.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
const
|
|
1
|
+
const INVALID_ID_PATTERN = /[<>:"/\\|?*]/;
|
|
2
|
+
const TRAILING_DOT_SPACE_PATTERN = /[.\s]+$/;
|
|
2
3
|
const MAX_ID_LENGTH = 200;
|
|
3
4
|
const RESERVED_NAMES = /* @__PURE__ */ new Set([
|
|
4
5
|
".",
|
|
@@ -14,15 +15,28 @@ export const assertSafeSourceId = (value, label) => {
|
|
|
14
15
|
if (typeof value !== "string" || value.length === 0) {
|
|
15
16
|
throw new Error(`${label} must be a non-empty string.`);
|
|
16
17
|
}
|
|
18
|
+
if (value.trim().length === 0) {
|
|
19
|
+
throw new Error(`${label} must not be blank.`);
|
|
20
|
+
}
|
|
17
21
|
if (value.length > MAX_ID_LENGTH) {
|
|
18
22
|
throw new Error(`${label} exceeds maximum length of ${MAX_ID_LENGTH}.`);
|
|
19
23
|
}
|
|
20
|
-
|
|
24
|
+
for (const char of value) {
|
|
25
|
+
const code = char.codePointAt(0);
|
|
26
|
+
if (code !== void 0 && (code <= 31 || code === 127)) {
|
|
27
|
+
throw new Error(`${label} must not contain control characters.`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (TRAILING_DOT_SPACE_PATTERN.test(value)) {
|
|
31
|
+
throw new Error(`${label} must not end with dots or spaces.`);
|
|
32
|
+
}
|
|
33
|
+
if (INVALID_ID_PATTERN.test(value) || value.includes("\0")) {
|
|
21
34
|
throw new Error(
|
|
22
|
-
`${label} must contain
|
|
35
|
+
`${label} must not contain path separators or reserved characters (< > : " / \\ | ? *).`
|
|
23
36
|
);
|
|
24
37
|
}
|
|
25
|
-
|
|
38
|
+
const normalized = value.replace(/[.\s]+$/g, "");
|
|
39
|
+
if (RESERVED_NAMES.has(normalized.toUpperCase())) {
|
|
26
40
|
throw new Error(`${label} uses reserved name '${value}'.`);
|
|
27
41
|
}
|
|
28
42
|
return value;
|
package/package.json
CHANGED
|
@@ -1,136 +1,139 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
2
|
+
"name": "docs-cache",
|
|
3
|
+
"private": false,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"version": "0.5.4",
|
|
6
|
+
"packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748",
|
|
7
|
+
"description": "CLI for deterministic local caching of external documentation for agents and tools",
|
|
8
|
+
"author": "Frederik Bosch",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"homepage": "https://github.com/fbosch/docs-cache#readme",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/fbosch/docs-cache.git"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/fbosch/docs-cache/issues"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"docs",
|
|
20
|
+
"documentation",
|
|
21
|
+
"cache",
|
|
22
|
+
"agent",
|
|
23
|
+
"ai",
|
|
24
|
+
"git",
|
|
25
|
+
"cli"
|
|
26
|
+
],
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
},
|
|
31
|
+
"bin": {
|
|
32
|
+
"docs-cache": "./bin/docs-cache.mjs"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"bin",
|
|
36
|
+
"dist/cli.mjs",
|
|
37
|
+
"dist/esm/**/*.mjs",
|
|
38
|
+
"dist/esm/**/*.d.ts",
|
|
39
|
+
"dist/lock.mjs",
|
|
40
|
+
"dist/shared/*.mjs",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE"
|
|
43
|
+
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "unbuild",
|
|
46
|
+
"dev": "unbuild --stub",
|
|
47
|
+
"lint": "biome check .",
|
|
48
|
+
"prepublishOnly": "pnpm audit --audit-level=high && pnpm build && pnpm size && pnpm schema:build",
|
|
49
|
+
"release": "pnpm run lint && pnpm run typecheck && bumpp && pnpm publish --access public",
|
|
50
|
+
"test": "pnpm build && node --test",
|
|
51
|
+
"test:coverage": "pnpm build && c8 --include dist --exclude bin --reporter=text node --test",
|
|
52
|
+
"bench": "pnpm build && node scripts/benchmarks/run.mjs",
|
|
53
|
+
"complexity": "node scripts/complexity/run.mjs",
|
|
54
|
+
"schema:build": "node scripts/generate-schema.mjs",
|
|
55
|
+
"size": "size-limit",
|
|
56
|
+
"test:watch": "node --test --watch",
|
|
57
|
+
"typecheck": "tsc --noEmit",
|
|
58
|
+
"prepare": "simple-git-hooks"
|
|
59
|
+
},
|
|
60
|
+
"imports": {
|
|
61
|
+
"#cache/*": {
|
|
62
|
+
"types": "./dist/esm/cache/*.d.ts",
|
|
63
|
+
"default": "./dist/esm/cache/*.mjs"
|
|
64
|
+
},
|
|
65
|
+
"#cli/*": {
|
|
66
|
+
"types": "./dist/esm/cli/*.d.ts",
|
|
67
|
+
"default": "./dist/esm/cli/*.mjs"
|
|
68
|
+
},
|
|
69
|
+
"#commands/*": {
|
|
70
|
+
"types": "./dist/esm/commands/*.d.ts",
|
|
71
|
+
"default": "./dist/esm/commands/*.mjs"
|
|
72
|
+
},
|
|
73
|
+
"#core/*": {
|
|
74
|
+
"types": "./dist/esm/*.d.ts",
|
|
75
|
+
"default": "./dist/esm/*.mjs"
|
|
76
|
+
},
|
|
77
|
+
"#config": {
|
|
78
|
+
"types": "./dist/esm/config/index.d.ts",
|
|
79
|
+
"default": "./dist/esm/config/index.mjs"
|
|
80
|
+
},
|
|
81
|
+
"#config/*": {
|
|
82
|
+
"types": "./dist/esm/config/*.d.ts",
|
|
83
|
+
"default": "./dist/esm/config/*.mjs"
|
|
84
|
+
},
|
|
85
|
+
"#git/*": {
|
|
86
|
+
"types": "./dist/esm/git/*.d.ts",
|
|
87
|
+
"default": "./dist/esm/git/*.mjs"
|
|
88
|
+
},
|
|
89
|
+
"#types/*": {
|
|
90
|
+
"types": "./dist/esm/types/*.d.ts",
|
|
91
|
+
"default": "./dist/esm/types/*.mjs"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"dependencies": {
|
|
95
|
+
"@clack/prompts": "^1.0.0",
|
|
96
|
+
"cac": "^6.7.14",
|
|
97
|
+
"cli-truncate": "^4.0.0",
|
|
98
|
+
"execa": "^9.6.1",
|
|
99
|
+
"fast-glob": "^3.3.2",
|
|
100
|
+
"log-update": "^7.0.2",
|
|
101
|
+
"picocolors": "^1.1.1",
|
|
102
|
+
"picomatch": "^4.0.3",
|
|
103
|
+
"zod": "^4.3.6"
|
|
104
|
+
},
|
|
105
|
+
"devDependencies": {
|
|
106
|
+
"@biomejs/biome": "^2.3.14",
|
|
107
|
+
"@size-limit/file": "^12.0.0",
|
|
108
|
+
"@types/node": "^25.2.0",
|
|
109
|
+
"bumpp": "^10.3.2",
|
|
110
|
+
"c8": "^10.1.3",
|
|
111
|
+
"jiti": "^2.5.1",
|
|
112
|
+
"lint-staged": "^16.2.7",
|
|
113
|
+
"simple-git-hooks": "^2.13.1",
|
|
114
|
+
"size-limit": "^12.0.0",
|
|
115
|
+
"tinybench": "^6.0.0",
|
|
116
|
+
"ts-complex": "^1.0.0",
|
|
117
|
+
"typescript": "^5.9.3",
|
|
118
|
+
"unbuild": "^3.6.1"
|
|
119
|
+
},
|
|
120
|
+
"size-limit": [
|
|
121
|
+
{
|
|
122
|
+
"path": "dist/cli.mjs",
|
|
123
|
+
"limit": "10 kB"
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
"complexity": {
|
|
127
|
+
"maxCyclomatic": 20,
|
|
128
|
+
"minMaintainability": 60,
|
|
129
|
+
"top": 10
|
|
130
|
+
},
|
|
131
|
+
"simple-git-hooks": {
|
|
132
|
+
"pre-commit": "pnpm lint-staged && pnpm typecheck"
|
|
133
|
+
},
|
|
134
|
+
"lint-staged": {
|
|
135
|
+
"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [
|
|
136
|
+
"biome check --write --no-errors-on-unmatched"
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
}
|