@skydiveai/git-cache 0.1.0-beta.2620 → 0.1.0-beta.2728

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/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { f as startServer } from "./server-DDcKo4yG.mjs";
2
+ import { f as startServer } from "./server-BrGhh8-W.mjs";
3
3
  //#region src/cli.ts
4
4
  /**
5
5
  * `git-cache` CLI — the published binary for @skydiveai/git-cache.
package/dist/index.d.mts CHANGED
@@ -151,6 +151,32 @@ declare function shouldBackfillAfterPassthrough({
151
151
  cacheable: any;
152
152
  sha256: any;
153
153
  }): boolean;
154
+ /**
155
+ * Decide how to serve a warm mirror for one request.
156
+ *
157
+ * `revalidateRefs` is set for the `info/refs` half of a fetch — the ref
158
+ * advertisement, where the client learns the remote's HEAD and branch tips.
159
+ * That answer must never be stale: serving a pre-merge advertisement is how a
160
+ * `git fetch` right after an upstream squash-merge silently returns the old
161
+ * `origin/<branch>` (the object-reuse win of the cache is not worth lying about
162
+ * where HEAD is). So for `info/refs` a stale mirror BLOCKS on a synchronous
163
+ * refresh before it answers, exactly as an explicit push invalidation does.
164
+ * The `git-upload-pack` half keeps stale-while-revalidate: its wants come from
165
+ * the just-served fresh advertisement, so the client only asks for objects the
166
+ * fresh refs named, and the caller's `mirrorHasObjects` gate already falls back
167
+ * to origin for any the mirror is missing (the new objects a merge introduced).
168
+ *
169
+ * A blocked refresh that FAILS (network blip, expired token, upstream 5xx)
170
+ * returns `cacheState: 'STALE_REVALIDATE_FAILED'` rather than throwing, so the
171
+ * caller can proxy `info/refs` straight to origin — fresh refs from upstream,
172
+ * never a 502 and never the stale advertisement.
173
+ */
174
+ /**
175
+ * Whether a mirror stamped `lastFetchMs` is fresh: past the invalidation fence
176
+ * AND within the TTL window. `lastFetchMs` is a fetch-START time (see
177
+ * writeMeta), so a fetch that began before the fence is stale no matter when
178
+ * it completed — the data it holds predates the invalidation by construction.
179
+ */
154
180
  /**
155
181
  * True when every OID is already present in the mirror.
156
182
  *
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as isProtocolV2Request, c as parseAdvertisement, d as shouldBackfillAfterPassthrough, f as startServer, i as explainRejection, l as parseUpstream, n as classifyUploadRequest, o as metricsSnapshot, r as copyRequestHeaders, s as mirrorHasObjects, t as SidebandPackExtractor, u as recordColdBuildOutcome } from "./server-DDcKo4yG.mjs";
1
+ import { a as isProtocolV2Request, c as parseAdvertisement, d as shouldBackfillAfterPassthrough, f as startServer, i as explainRejection, l as parseUpstream, n as classifyUploadRequest, o as metricsSnapshot, r as copyRequestHeaders, s as mirrorHasObjects, t as SidebandPackExtractor, u as recordColdBuildOutcome } from "./server-BrGhh8-W.mjs";
2
2
  export { SidebandPackExtractor, classifyUploadRequest, copyRequestHeaders, explainRejection, isProtocolV2Request, metricsSnapshot, mirrorHasObjects, parseAdvertisement, parseUpstream, recordColdBuildOutcome, shouldBackfillAfterPassthrough, startServer };
@@ -821,10 +821,10 @@ var SidebandPackExtractor = class {
821
821
  if (!this.errored) this.writable.end();
822
822
  }
823
823
  };
824
- async function writeMeta(local, extra = {}) {
824
+ async function writeMeta(local, fetchStartMs, extra = {}) {
825
825
  const tmp = `${metaPath(local)}.tmp.${INSTANCE_ID}`;
826
826
  await writeFile(tmp, JSON.stringify({
827
- lastFetchMs: Date.now(),
827
+ lastFetchMs: fetchStartMs,
828
828
  ...extra
829
829
  }), "utf8");
830
830
  await rename(tmp, metaPath(local));
@@ -1019,7 +1019,7 @@ async function backfillMirror(repo, upstreamUrl, credHeader = null) {
1019
1019
  const published = await withRepoLock(repo, "write", async () => {
1020
1020
  if (await pathExists(local)) return false;
1021
1021
  await rename(temp, local);
1022
- await writeMeta(local, { publishedMs: Date.now() });
1022
+ await writeMeta(local, startedMs, { publishedMs: Date.now() });
1023
1023
  return true;
1024
1024
  });
1025
1025
  if (published !== true) {
@@ -1056,6 +1056,7 @@ async function backfillMirror(repo, upstreamUrl, credHeader = null) {
1056
1056
  }
1057
1057
  function startColdStream(req, res, upstreamUrl, repo, requestBody, credHeader) {
1058
1058
  const local = localPath(repo);
1059
+ const captureStartedMs = Date.now();
1059
1060
  const temp = `${config.cacheDir}/quarantine/${repo.replace(/\//g, "__")}.stream-${INSTANCE_ID}-${Date.now()}`;
1060
1061
  metrics.cold_requests++;
1061
1062
  coldLeaders.add(repo);
@@ -1230,7 +1231,7 @@ function startColdStream(req, res, upstreamUrl, repo, requestBody, credHeader) {
1230
1231
  const published = await withRepoLock(repo, "write", async () => {
1231
1232
  if (await pathExists(local)) return false;
1232
1233
  await rename(temp, local);
1233
- await writeMeta(local, { publishedMs: Date.now() });
1234
+ await writeMeta(local, captureStartedMs, { publishedMs: Date.now() });
1234
1235
  return true;
1235
1236
  });
1236
1237
  if (published === true) {
@@ -1294,6 +1295,7 @@ async function refreshMirror(repo, local, credHeader = null) {
1294
1295
  "true"
1295
1296
  ]);
1296
1297
  await disableAutoMaintenance(local);
1298
+ const fetchStartMs = Date.now();
1297
1299
  await runGit(upstreamGitArgs([
1298
1300
  "--git-dir",
1299
1301
  local,
@@ -1306,7 +1308,7 @@ async function refreshMirror(repo, local, credHeader = null) {
1306
1308
  ...GIT_ENV,
1307
1309
  ...upstreamGitCredentialEnv(credHeader)
1308
1310
  } });
1309
- await writeMeta(local);
1311
+ await writeMeta(local, fetchStartMs);
1310
1312
  metrics.refreshes++;
1311
1313
  log("info", "refresh", { repo });
1312
1314
  return local;
@@ -1331,10 +1333,21 @@ async function refreshMirror(repo, local, credHeader = null) {
1331
1333
  * caller can proxy `info/refs` straight to origin — fresh refs from upstream,
1332
1334
  * never a 502 and never the stale advertisement.
1333
1335
  */
1336
+ /**
1337
+ * Whether a mirror stamped `lastFetchMs` is fresh: past the invalidation fence
1338
+ * AND within the TTL window. `lastFetchMs` is a fetch-START time (see
1339
+ * writeMeta), so a fetch that began before the fence is stale no matter when
1340
+ * it completed — the data it holds predates the invalidation by construction.
1341
+ */
1342
+ function isMetaFresh(lastFetchMs, fenceMs, ttlSeconds, nowMs) {
1343
+ if (lastFetchMs <= 0) return false;
1344
+ if (lastFetchMs > nowMs) return false;
1345
+ return lastFetchMs >= fenceMs && (nowMs - lastFetchMs) / 1e3 < ttlSeconds;
1346
+ }
1334
1347
  async function ensureWarm(repo, credHeader = null, revalidateRefs = false) {
1335
1348
  const local = localPath(repo);
1336
1349
  const lastFetchMs = (await readMeta(local))?.lastFetchMs ?? 0;
1337
- if (!((Date.now() - lastFetchMs) / 1e3 >= config.ttlSeconds || lastFetchMs < invalidateBeforeMs)) {
1350
+ if (!!isMetaFresh(lastFetchMs, invalidateBeforeMs, config.ttlSeconds, Date.now())) {
1338
1351
  metrics.warm_hits++;
1339
1352
  return {
1340
1353
  local,
@@ -1357,8 +1370,7 @@ async function ensureWarm(repo, credHeader = null, revalidateRefs = false) {
1357
1370
  }).finally(() => inflightRefresh.delete(repo)));
1358
1371
  if (mustBlock) {
1359
1372
  await inflightRefresh.get(repo);
1360
- const refreshedMs = (await readMeta(local))?.lastFetchMs ?? 0;
1361
- if (!(refreshedMs >= invalidateBeforeMs && (Date.now() - refreshedMs) / 1e3 < config.ttlSeconds)) {
1373
+ if (!isMetaFresh((await readMeta(local))?.lastFetchMs ?? 0, invalidateBeforeMs, config.ttlSeconds, Date.now())) {
1362
1374
  if (revalidateRefs && !fencedByPush) {
1363
1375
  metrics.revalidate_failures++;
1364
1376
  log("warn", "revalidate_failed", { repo });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skydiveai/git-cache",
3
- "version": "0.1.0-beta.2620",
3
+ "version": "0.1.0-beta.2728",
4
4
  "description": "Sandbox-local Git smart-HTTP clone/fetch cache: a loopback daemon that transparently mirrors HTTPS git reads so repeated clone/fetch/ls-remote skip the origin round-trip. Runs inside an agent sandbox; pushes stay direct.",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",