@tomingtoming/kioq 0.8.1 → 0.8.3
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.
|
@@ -38,6 +38,8 @@ export class GitHubStorage {
|
|
|
38
38
|
fetchFn;
|
|
39
39
|
execFileAsyncFn;
|
|
40
40
|
setupValidated = false;
|
|
41
|
+
lastFetchMs = 0;
|
|
42
|
+
static FETCH_INTERVAL_MS = 10_000;
|
|
41
43
|
constructor(githubConfig, rootPath, deps = {}) {
|
|
42
44
|
this.githubConfig = githubConfig;
|
|
43
45
|
this.rootPath = rootPath;
|
|
@@ -48,17 +50,21 @@ export class GitHubStorage {
|
|
|
48
50
|
this.fetchFn = deps.fetchFn ?? fetch;
|
|
49
51
|
this.execFileAsyncFn = deps.execFileAsyncFn ?? execFileAsync;
|
|
50
52
|
}
|
|
51
|
-
// --- Reads: delegate to local clone ---
|
|
52
|
-
stat(relativePath) {
|
|
53
|
+
// --- Reads: delegate to local clone (with auto-refresh) ---
|
|
54
|
+
async stat(relativePath) {
|
|
55
|
+
await this.refreshIfStale();
|
|
53
56
|
return this.local.stat(relativePath);
|
|
54
57
|
}
|
|
55
|
-
readFile(relativePath) {
|
|
58
|
+
async readFile(relativePath) {
|
|
59
|
+
await this.refreshIfStale();
|
|
56
60
|
return this.local.readFile(relativePath);
|
|
57
61
|
}
|
|
58
|
-
readdir(relativePath) {
|
|
62
|
+
async readdir(relativePath) {
|
|
63
|
+
await this.refreshIfStale();
|
|
59
64
|
return this.local.readdir(relativePath);
|
|
60
65
|
}
|
|
61
|
-
listMarkdownFiles() {
|
|
66
|
+
async listMarkdownFiles() {
|
|
67
|
+
await this.refreshIfStale();
|
|
62
68
|
return this.local.listMarkdownFiles();
|
|
63
69
|
}
|
|
64
70
|
// --- Writes: GitHub Git Data API ---
|
|
@@ -116,7 +122,7 @@ export class GitHubStorage {
|
|
|
116
122
|
catch {
|
|
117
123
|
// .git not found — auto-clone below
|
|
118
124
|
}
|
|
119
|
-
const cloneUrl = `https://github.com/${this.githubConfig.owner}/${this.githubConfig.repo}.git`;
|
|
125
|
+
const cloneUrl = `https://x-access-token:${this.githubConfig.token}@github.com/${this.githubConfig.owner}/${this.githubConfig.repo}.git`;
|
|
120
126
|
try {
|
|
121
127
|
await fs.mkdir(path.dirname(this.rootPath), { recursive: true });
|
|
122
128
|
await this.execFileAsyncFn("git", ["clone", "--branch", this.branch, "--single-branch", cloneUrl, this.rootPath], { timeout: 120_000 });
|
|
@@ -352,6 +358,7 @@ export class GitHubStorage {
|
|
|
352
358
|
cwd: this.rootPath,
|
|
353
359
|
timeout: 30_000,
|
|
354
360
|
});
|
|
361
|
+
this.lastFetchMs = Date.now();
|
|
355
362
|
}
|
|
356
363
|
catch (error) {
|
|
357
364
|
throw new Error(`${context}: ${this.normalizeExecError(error)}`);
|
|
@@ -365,6 +372,25 @@ export class GitHubStorage {
|
|
|
365
372
|
// best-effort only
|
|
366
373
|
}
|
|
367
374
|
}
|
|
375
|
+
async refreshIfStale() {
|
|
376
|
+
const now = Date.now();
|
|
377
|
+
if (now - this.lastFetchMs < GitHubStorage.FETCH_INTERVAL_MS) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
try {
|
|
381
|
+
const { stdout: localRef } = await this.execFileAsyncFn("git", ["rev-parse", `refs/heads/${this.branch}`], { cwd: this.rootPath, timeout: 5_000 });
|
|
382
|
+
await this.execFileAsyncFn("git", ["fetch", "--no-tags", "origin", this.branch], { cwd: this.rootPath, timeout: 15_000 });
|
|
383
|
+
const { stdout: remoteRef } = await this.execFileAsyncFn("git", ["rev-parse", `refs/remotes/origin/${this.branch}`], { cwd: this.rootPath, timeout: 5_000 });
|
|
384
|
+
this.lastFetchMs = Date.now();
|
|
385
|
+
if (localRef.trim() !== remoteRef.trim()) {
|
|
386
|
+
await this.pullLocal("git pull --ff-only failed during auto-refresh");
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
catch {
|
|
390
|
+
// best-effort: if fetch fails, proceed with stale data
|
|
391
|
+
this.lastFetchMs = Date.now();
|
|
392
|
+
}
|
|
393
|
+
}
|
|
368
394
|
isRetryableBatchError(error) {
|
|
369
395
|
return (error instanceof GitHubApiError && error.retryable)
|
|
370
396
|
|| this.isTransientNetworkError(error);
|