@stim-cli/cache 1.20.1 → 1.21.0

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 CHANGED
@@ -6,8 +6,8 @@ front of it.
6
6
 
7
7
  The local filesystem is always the first tier. A project can add one optional
8
8
  second tier by pointing `cache.provider` at a module that implements this
9
- contract. Stim ships no network provider; writing one is an addition, not a
10
- change to Stim.
9
+ contract. `@stim-cli/cache-s3` provides an S3-compatible second tier, including
10
+ Cloudflare R2.
11
11
 
12
12
  If Stim is not installed globally, replace `stim` with `npx stim`.
13
13
 
@@ -30,6 +30,12 @@ provider, and lower-precedence layers that name the same provider resolved from
30
30
  the same directory; options written for another provider are ignored. Keep secrets out of
31
31
  committed settings; read them from the environment or from machine settings.
32
32
 
33
+ ## S3-compatible storage
34
+
35
+ Install the separate [`@stim-cli/cache-s3`](../cache-s3/README.md) provider and
36
+ select it through `cache.provider`. The AWS SDK and archive dependencies belong
37
+ to that opt-in package; `@stim-cli/cache` has no runtime dependencies.
38
+
33
39
  ## Writing a provider
34
40
 
35
41
  ```js
@@ -53,10 +59,22 @@ export async function createCacheProvider({ projectRoot, options }) {
53
59
  }
54
60
  ```
55
61
 
56
- A provider implements one or both capabilities. It owns transport,
62
+ A provider implements one or more capabilities. It owns transport,
57
63
  serialization, archive format, authentication, and remote retention. Stim owns
58
64
  fingerprints, cache keys, and local artifact paths.
59
65
 
66
+ The optional `files` capability stores opaque complete files with
67
+ `store({ key, sourcePath, overwrite, signal })` and downloads them with
68
+ `resolve({ key, destinationDir, signal })`. A hit returns a path inside
69
+ `destinationDir`; a miss returns `null` and leaves it empty. The CLI consumes
70
+ the `metro` and `builds` adapters. Native Gradle APK caching consumes `files`
71
+ directly to share the APK together with its verification receipt; a files-only
72
+ provider can support this flow. An optional
73
+ `stat({ key, signal })` returns `{ size }` or `null` without downloading;
74
+ consumers still verify downloaded bytes. `createFilesystemFileCache(directory)`
75
+ implements the same contract in an owned local directory without dependencies.
76
+ It publishes atomically and materializes downloads in the requested scratch directory.
77
+
60
78
  `metro.get` returns the stored value or `null`.
61
79
 
62
80
  `builds.resolve` returns an existing path to the artifact, or `null` for a
@@ -112,8 +130,8 @@ const results = await runCacheProviderContract({
112
130
  ```
113
131
 
114
132
  `cacheProviderContractChecks()` returns the same checks as individual cases for
115
- a test runner that reports each one separately. Both helpers only check the
116
- capabilities a provider advertises, bound every check with a deadline, and
133
+ a test runner that reports each one separately. Both helpers check advertised
134
+ Metro and build capabilities, bound every check with a deadline, and
117
135
  verify that a call settles once its `AbortSignal` aborts.
118
136
 
119
137
  ## Budgets
package/dist/index.d.mts CHANGED
@@ -76,7 +76,28 @@ interface BuildCacheCapability {
76
76
  resolve(input: BuildResolveInput): string | null | Promise<string | null>;
77
77
  store(input: BuildStoreInput): string | null | void | Promise<string | null | void>;
78
78
  }
79
+ interface FileCacheResolveInput {
80
+ key: string;
81
+ destinationDir: string;
82
+ signal: AbortSignal;
83
+ }
84
+ interface FileCacheStoreInput {
85
+ key: string;
86
+ sourcePath: string;
87
+ overwrite: boolean;
88
+ signal: AbortSignal;
89
+ }
90
+ /** Opaque, complete files. Downloads are materialized inside destinationDir; a miss leaves it empty. */
91
+ interface FileCacheCapability {
92
+ resolve(input: FileCacheResolveInput): Promise<string | null>;
93
+ store(input: FileCacheStoreInput): Promise<void>;
94
+ /** Checks presence without downloading; the bytes still need verification when fetched. */
95
+ stat?(input: Pick<FileCacheResolveInput, "key" | "signal">): Promise<{
96
+ size: number;
97
+ } | null>;
98
+ }
79
99
  interface CacheProvider {
100
+ files?: FileCacheCapability;
80
101
  metro?: MetroCacheCapability;
81
102
  builds?: BuildCacheCapability;
82
103
  }
@@ -108,7 +129,7 @@ declare function loadCacheProvider({ projectRoot, config, timeoutMs }: {
108
129
  config?: CacheProviderConfig | null;
109
130
  timeoutMs?: number;
110
131
  }): Promise<LoadCacheProviderResult>;
111
- declare function callWithTimeout<T>(call: (signal: AbortSignal) => T | Promise<T>, timeoutMs: number): Promise<ProviderCallResult<T>>;
132
+ declare function callWithTimeout<T>(call: (signal: AbortSignal) => T | Promise<T>, timeoutMs: number, signal?: AbortSignal): Promise<ProviderCallResult<T>>;
112
133
  declare function createWarnOnce(emit: (message: string) => void): WarnOnce;
113
134
  declare function cacheProviderEnv(config: CacheProviderConfig | null): string;
114
135
  /**
@@ -196,6 +217,7 @@ interface ResolveTieredBuildOptions {
196
217
  skipRead?: boolean;
197
218
  warn?: WarnOnce;
198
219
  timeoutMs?: number;
220
+ signal?: AbortSignal;
199
221
  }
200
222
  interface StoreTieredBuildOptions {
201
223
  local: BuildCacheCapability;
@@ -205,14 +227,15 @@ interface StoreTieredBuildOptions {
205
227
  overwrite: boolean;
206
228
  warn?: WarnOnce;
207
229
  timeoutMs?: number;
230
+ signal?: AbortSignal;
208
231
  }
209
232
  interface TieredBuildStoreResult {
210
233
  localPath: string | null;
211
234
  providerUpload: Promise<ProviderCallResult<void>> | null;
212
235
  providerName: string | null;
213
236
  }
214
- declare function resolveTieredBuild({ local, loadProvider, target, destinationDir, ensureDestination, skipRead, warn, timeoutMs }: ResolveTieredBuildOptions): Promise<TieredBuildResolution | null>;
215
- declare function storeTieredBuild({ local, loadProvider, target, sourcePath, overwrite, warn, timeoutMs }: StoreTieredBuildOptions): Promise<TieredBuildStoreResult>;
237
+ declare function resolveTieredBuild({ local, loadProvider, target, destinationDir, ensureDestination, skipRead, warn, timeoutMs, signal }: ResolveTieredBuildOptions): Promise<TieredBuildResolution | null>;
238
+ declare function storeTieredBuild({ local, loadProvider, target, sourcePath, overwrite, warn, timeoutMs, signal }: StoreTieredBuildOptions): Promise<TieredBuildStoreResult>;
216
239
  //#endregion
217
240
  //#region contract.d.ts
218
241
  interface CacheContractCheck {
@@ -249,4 +272,8 @@ declare const CACHE_CONTRACT_CHECK_TIMEOUT_MS = 3e4;
249
272
  declare function cacheProviderContractChecks(options: CacheContractOptions): CacheContractCheck[];
250
273
  declare function runCacheProviderContract(options: CacheContractOptions | CacheContractModuleOptions): Promise<CacheContractResult[]>;
251
274
  //#endregion
252
- export { BUILD_RESOLVE_TIMEOUT_ENV, BUILD_RESOLVE_TIMEOUT_MS, BUILD_UPLOAD_TIMEOUT_ENV, BUILD_UPLOAD_TIMEOUT_MS, BuildCacheCapability, BuildCacheContext, BuildCacheTarget, BuildResolveInput, BuildStoreInput, CACHE_CONTRACT_CHECK_TIMEOUT_MS, CACHE_PROVIDER_API_VERSION, CACHE_PROVIDER_ENV, CACHE_PROVIDER_ENV_NONE, CacheContractCheck, CacheContractModuleOptions, CacheContractOptions, CacheContractResult, CacheProvider, CacheProviderConfig, CacheProviderModule, LoadBuildProvider, LoadCacheProviderResult, METRO_READ_CONCURRENCY, METRO_READ_FAILURE_LIMIT, METRO_READ_TIMEOUT_ENV, METRO_READ_TIMEOUT_MS, METRO_UPLOAD_CONCURRENCY, METRO_UPLOAD_MAX_BYTES, METRO_UPLOAD_MAX_ITEMS, METRO_WRITE_TIMEOUT_ENV, METRO_WRITE_TIMEOUT_MS, MetroCacheCapability, MetroCacheContext, MetroCacheGetInput, MetroCacheSetInput, MetroCacheStore, MetroTierLimits, MetroTierTimeouts, PROVIDER_LOAD_TIMEOUT_ENV, PROVIDER_LOAD_TIMEOUT_MS, ProviderCallResult, ResolveTieredBuildOptions, StoreTieredBuildOptions, TieredBuildResolution, TieredBuildStoreResult, TieredMetroStore, TieredMetroStoreOptions, WarnOnce, buildResolveTimeoutMs, buildUploadTimeoutMs, cacheProviderConfigFromEnv, cacheProviderContractChecks, cacheProviderEnv, cacheProviderEnvIsSet, callWithTimeout, createTieredMetroStore, createWarnOnce, loadCacheProvider, metroCapabilityFromStore, resolveTieredBuild, runCacheProviderContract, storeTieredBuild, timeoutFromEnv };
275
+ //#region files.d.ts
276
+ /** Opaque immutable files in a caller-owned directory, with atomic publication and cancellable downloads. */
277
+ declare function createFilesystemFileCache(directory: string): FileCacheCapability & Required<Pick<FileCacheCapability, "stat">>;
278
+ //#endregion
279
+ export { BUILD_RESOLVE_TIMEOUT_ENV, BUILD_RESOLVE_TIMEOUT_MS, BUILD_UPLOAD_TIMEOUT_ENV, BUILD_UPLOAD_TIMEOUT_MS, BuildCacheCapability, BuildCacheContext, BuildCacheTarget, BuildResolveInput, BuildStoreInput, CACHE_CONTRACT_CHECK_TIMEOUT_MS, CACHE_PROVIDER_API_VERSION, CACHE_PROVIDER_ENV, CACHE_PROVIDER_ENV_NONE, CacheContractCheck, CacheContractModuleOptions, CacheContractOptions, CacheContractResult, CacheProvider, CacheProviderConfig, CacheProviderModule, FileCacheCapability, FileCacheResolveInput, FileCacheStoreInput, LoadBuildProvider, LoadCacheProviderResult, METRO_READ_CONCURRENCY, METRO_READ_FAILURE_LIMIT, METRO_READ_TIMEOUT_ENV, METRO_READ_TIMEOUT_MS, METRO_UPLOAD_CONCURRENCY, METRO_UPLOAD_MAX_BYTES, METRO_UPLOAD_MAX_ITEMS, METRO_WRITE_TIMEOUT_ENV, METRO_WRITE_TIMEOUT_MS, MetroCacheCapability, MetroCacheContext, MetroCacheGetInput, MetroCacheSetInput, MetroCacheStore, MetroTierLimits, MetroTierTimeouts, PROVIDER_LOAD_TIMEOUT_ENV, PROVIDER_LOAD_TIMEOUT_MS, ProviderCallResult, ResolveTieredBuildOptions, StoreTieredBuildOptions, TieredBuildResolution, TieredBuildStoreResult, TieredMetroStore, TieredMetroStoreOptions, WarnOnce, buildResolveTimeoutMs, buildUploadTimeoutMs, cacheProviderConfigFromEnv, cacheProviderContractChecks, cacheProviderEnv, cacheProviderEnvIsSet, callWithTimeout, createFilesystemFileCache, createTieredMetroStore, createWarnOnce, loadCacheProvider, metroCapabilityFromStore, resolveTieredBuild, runCacheProviderContract, storeTieredBuild, timeoutFromEnv };
package/dist/index.mjs CHANGED
@@ -1,8 +1,10 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { join, relative, sep } from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
- import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
5
- import { randomBytes, randomUUID } from "node:crypto";
4
+ import { createReadStream, createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
5
+ import { createHash, randomBytes, randomUUID } from "node:crypto";
6
+ import { link, lstat, mkdir, rename, rm } from "node:fs/promises";
7
+ import { pipeline } from "node:stream/promises";
6
8
  //#region provider.ts
7
9
  const CACHE_PROVIDER_API_VERSION = 1;
8
10
  const CACHE_PROVIDER_ENV = "STIM_CACHE_PROVIDER_CONFIG";
@@ -31,11 +33,13 @@ function moduleCandidate(namespace) {
31
33
  return "default" in namespace ? namespace.default : namespace;
32
34
  }
33
35
  function capabilityError(provider) {
36
+ const files = provider.files;
37
+ if (files !== void 0 && (typeof files?.resolve !== "function" || typeof files?.store !== "function")) return "the files capability must implement resolve() and store()";
34
38
  const metro = provider.metro;
35
39
  if (metro !== void 0 && (typeof metro?.get !== "function" || typeof metro?.set !== "function")) return "the metro capability must implement get() and set()";
36
40
  const builds = provider.builds;
37
41
  if (builds !== void 0 && (typeof builds?.resolve !== "function" || typeof builds?.store !== "function")) return "the builds capability must implement resolve() and store()";
38
- if (metro === void 0 && builds === void 0) return "the provider advertises neither a metro nor a builds capability";
42
+ if (metro === void 0 && builds === void 0 && files === void 0) return "the provider advertises no files, metro or builds capability";
39
43
  return null;
40
44
  }
41
45
  async function loadCacheProvider({ projectRoot, config, timeoutMs = timeoutFromEnv(PROVIDER_LOAD_TIMEOUT_ENV, PROVIDER_LOAD_TIMEOUT_MS) }) {
@@ -107,8 +111,11 @@ async function importCacheProvider({ projectRoot, config, reference }) {
107
111
  provider
108
112
  };
109
113
  }
110
- async function callWithTimeout(call, timeoutMs) {
114
+ async function callWithTimeout(call, timeoutMs, signal) {
115
+ signal?.throwIfAborted();
111
116
  const controller = new AbortController();
117
+ const abort = () => controller.abort(signal?.reason);
118
+ signal?.addEventListener("abort", abort, { once: true });
112
119
  let timer = null;
113
120
  const expired = new Promise((resolve) => {
114
121
  timer = setTimeout(() => {
@@ -117,9 +124,14 @@ async function callWithTimeout(call, timeoutMs) {
117
124
  }, timeoutMs);
118
125
  });
119
126
  try {
120
- const outcome = await Promise.race([Promise.resolve().then(() => call(controller.signal)).then((value) => ({ value }), (error) => ({ failed: reason(error) })), expired]);
127
+ const outcome = await Promise.race([Promise.resolve().then(() => {
128
+ signal?.throwIfAborted();
129
+ return call(controller.signal);
130
+ }).then((value) => ({ value }), (error) => ({ failed: reason(error) })), expired]);
131
+ signal?.throwIfAborted();
121
132
  return outcome === "timeout" ? { timedOut: true } : outcome;
122
133
  } finally {
134
+ signal?.removeEventListener("abort", abort);
123
135
  if (timer) clearTimeout(timer);
124
136
  }
125
137
  }
@@ -386,9 +398,10 @@ function buildResolveTimeoutMs(env) {
386
398
  function buildUploadTimeoutMs(env) {
387
399
  return timeoutFromEnv(BUILD_UPLOAD_TIMEOUT_ENV, BUILD_UPLOAD_TIMEOUT_MS, env);
388
400
  }
389
- async function providerTier(loadProvider, warn) {
401
+ async function providerTier(loadProvider, warn, signal) {
390
402
  if (!loadProvider) return null;
391
403
  const loaded = await loadProvider();
404
+ signal?.throwIfAborted();
392
405
  if (loaded?.unavailable) {
393
406
  warn("provider-load", `provider not usable (${loaded.name ?? "the cache provider"}): ${loaded.unavailable}; using local cache`);
394
407
  return null;
@@ -404,26 +417,29 @@ function neverAborted$1() {
404
417
  return new AbortController().signal;
405
418
  }
406
419
  function ignore() {}
407
- async function resolveTieredBuild({ local, loadProvider = null, target, destinationDir, ensureDestination, skipRead = false, warn = ignore, timeoutMs = buildResolveTimeoutMs() }) {
420
+ async function resolveTieredBuild({ local, loadProvider = null, target, destinationDir, ensureDestination, skipRead = false, warn = ignore, timeoutMs = buildResolveTimeoutMs(), signal = neverAborted$1() }) {
421
+ signal.throwIfAborted();
408
422
  if (skipRead) return null;
409
423
  const hit = await local.resolve({
410
424
  ...target,
411
425
  destinationDir,
412
- signal: neverAborted$1()
426
+ signal
413
427
  });
428
+ signal.throwIfAborted();
414
429
  if (hit) return {
415
430
  path: hit,
416
431
  tier: "local"
417
432
  };
418
- const tier = await providerTier(loadProvider, warn);
433
+ const tier = await providerTier(loadProvider, warn, signal);
434
+ signal.throwIfAborted();
419
435
  if (!tier) return null;
420
436
  const { capability: provider, name: label } = tier;
421
437
  ensureDestination?.(destinationDir);
422
- const outcome = await callWithTimeout((signal) => provider.resolve({
438
+ const outcome = await callWithTimeout((childSignal) => provider.resolve({
423
439
  ...target,
424
440
  destinationDir,
425
- signal
426
- }), timeoutMs);
441
+ signal: childSignal
442
+ }), timeoutMs, signal);
427
443
  if (outcome.timedOut) {
428
444
  warn("provider-resolve", `${label} did not answer within ${timeoutMs}ms; building instead`);
429
445
  return null;
@@ -444,11 +460,13 @@ async function resolveTieredBuild({ local, loadProvider = null, target, destinat
444
460
  ...target,
445
461
  sourcePath: path,
446
462
  overwrite: false,
447
- signal: neverAborted$1()
463
+ signal
448
464
  }) || null;
449
465
  } catch (error) {
466
+ signal.throwIfAborted();
450
467
  warn("provider-backfill", `a ${label} hit could not be stored locally: ${String(error?.message || error)}`);
451
468
  }
469
+ signal.throwIfAborted();
452
470
  return {
453
471
  path: stored || path,
454
472
  tier: "provider",
@@ -456,29 +474,34 @@ async function resolveTieredBuild({ local, loadProvider = null, target, destinat
456
474
  storedLocally: Boolean(stored)
457
475
  };
458
476
  }
459
- async function storeTieredBuild({ local, loadProvider = null, target, sourcePath, overwrite, warn = ignore, timeoutMs = buildUploadTimeoutMs() }) {
477
+ async function storeTieredBuild({ local, loadProvider = null, target, sourcePath, overwrite, warn = ignore, timeoutMs = buildUploadTimeoutMs(), signal = neverAborted$1() }) {
478
+ signal.throwIfAborted();
460
479
  const localPath = await local.store({
461
480
  ...target,
462
481
  sourcePath,
463
482
  overwrite,
464
- signal: neverAborted$1()
483
+ signal
465
484
  }) || null;
466
- const tier = await providerTier(loadProvider, warn);
485
+ signal.throwIfAborted();
486
+ const tier = await providerTier(loadProvider, warn, signal);
487
+ signal.throwIfAborted();
467
488
  if (!tier) return {
468
489
  localPath,
469
490
  providerUpload: null,
470
491
  providerName: null
471
492
  };
493
+ const providerUpload = callWithTimeout(async (childSignal) => {
494
+ await tier.capability.store({
495
+ ...target,
496
+ sourcePath,
497
+ overwrite,
498
+ signal: childSignal
499
+ });
500
+ }, timeoutMs, signal);
501
+ providerUpload.catch(() => {});
472
502
  return {
473
503
  localPath,
474
- providerUpload: callWithTimeout(async (signal) => {
475
- await tier.capability.store({
476
- ...target,
477
- sourcePath,
478
- overwrite,
479
- signal
480
- });
481
- }, timeoutMs),
504
+ providerUpload,
482
505
  providerName: tier.name
483
506
  };
484
507
  }
@@ -886,4 +909,74 @@ async function withDeadline(work, timeoutMs) {
886
909
  }
887
910
  }
888
911
  //#endregion
889
- export { BUILD_RESOLVE_TIMEOUT_ENV, BUILD_RESOLVE_TIMEOUT_MS, BUILD_UPLOAD_TIMEOUT_ENV, BUILD_UPLOAD_TIMEOUT_MS, CACHE_CONTRACT_CHECK_TIMEOUT_MS, CACHE_PROVIDER_API_VERSION, CACHE_PROVIDER_ENV, CACHE_PROVIDER_ENV_NONE, METRO_READ_CONCURRENCY, METRO_READ_FAILURE_LIMIT, METRO_READ_TIMEOUT_ENV, METRO_READ_TIMEOUT_MS, METRO_UPLOAD_CONCURRENCY, METRO_UPLOAD_MAX_BYTES, METRO_UPLOAD_MAX_ITEMS, METRO_WRITE_TIMEOUT_ENV, METRO_WRITE_TIMEOUT_MS, PROVIDER_LOAD_TIMEOUT_ENV, PROVIDER_LOAD_TIMEOUT_MS, buildResolveTimeoutMs, buildUploadTimeoutMs, cacheProviderConfigFromEnv, cacheProviderContractChecks, cacheProviderEnv, cacheProviderEnvIsSet, callWithTimeout, createTieredMetroStore, createWarnOnce, loadCacheProvider, metroCapabilityFromStore, resolveTieredBuild, runCacheProviderContract, storeTieredBuild, timeoutFromEnv };
912
+ //#region files.ts
913
+ /** Opaque immutable files in a caller-owned directory, with atomic publication and cancellable downloads. */
914
+ function createFilesystemFileCache(directory) {
915
+ const entry = (key) => join(directory, createHash("sha256").update(key).digest("hex"));
916
+ const present = async ({ key, signal }) => {
917
+ signal.throwIfAborted();
918
+ try {
919
+ const info = await lstat(entry(key));
920
+ signal.throwIfAborted();
921
+ return info.isFile() ? { size: info.size } : null;
922
+ } catch (error) {
923
+ if ((error instanceof Error && "code" in error ? error.code : void 0) === "ENOENT") return null;
924
+ throw error;
925
+ }
926
+ };
927
+ return {
928
+ stat: present,
929
+ async resolve({ key, destinationDir, signal }) {
930
+ if (!await present({
931
+ key,
932
+ signal
933
+ })) return null;
934
+ const temporary = join(destinationDir, `${randomUUID()}.partial`);
935
+ const complete = join(destinationDir, `${randomUUID()}.cache`);
936
+ try {
937
+ await mkdir(destinationDir, {
938
+ recursive: true,
939
+ mode: 448
940
+ });
941
+ await pipeline(createReadStream(entry(key)), createWriteStream(temporary, {
942
+ flags: "wx",
943
+ mode: 384
944
+ }), { signal });
945
+ signal.throwIfAborted();
946
+ await rename(temporary, complete);
947
+ return complete;
948
+ } catch (error) {
949
+ signal.throwIfAborted();
950
+ if ((error instanceof Error && "code" in error ? error.code : void 0) === "ENOENT") return null;
951
+ throw error;
952
+ } finally {
953
+ await rm(temporary, { force: true });
954
+ }
955
+ },
956
+ async store({ key, sourcePath, overwrite, signal }) {
957
+ signal.throwIfAborted();
958
+ await mkdir(directory, {
959
+ recursive: true,
960
+ mode: 448
961
+ });
962
+ const temporary = join(directory, `${randomUUID()}.partial`);
963
+ try {
964
+ await pipeline(createReadStream(sourcePath), createWriteStream(temporary, {
965
+ flags: "wx",
966
+ mode: 384
967
+ }), { signal });
968
+ signal.throwIfAborted();
969
+ if (overwrite) await rename(temporary, entry(key));
970
+ else try {
971
+ await link(temporary, entry(key));
972
+ } catch (error) {
973
+ if ((error instanceof Error && "code" in error ? error.code : void 0) !== "EEXIST") throw error;
974
+ }
975
+ } finally {
976
+ await rm(temporary, { force: true });
977
+ }
978
+ }
979
+ };
980
+ }
981
+ //#endregion
982
+ export { BUILD_RESOLVE_TIMEOUT_ENV, BUILD_RESOLVE_TIMEOUT_MS, BUILD_UPLOAD_TIMEOUT_ENV, BUILD_UPLOAD_TIMEOUT_MS, CACHE_CONTRACT_CHECK_TIMEOUT_MS, CACHE_PROVIDER_API_VERSION, CACHE_PROVIDER_ENV, CACHE_PROVIDER_ENV_NONE, METRO_READ_CONCURRENCY, METRO_READ_FAILURE_LIMIT, METRO_READ_TIMEOUT_ENV, METRO_READ_TIMEOUT_MS, METRO_UPLOAD_CONCURRENCY, METRO_UPLOAD_MAX_BYTES, METRO_UPLOAD_MAX_ITEMS, METRO_WRITE_TIMEOUT_ENV, METRO_WRITE_TIMEOUT_MS, PROVIDER_LOAD_TIMEOUT_ENV, PROVIDER_LOAD_TIMEOUT_MS, buildResolveTimeoutMs, buildUploadTimeoutMs, cacheProviderConfigFromEnv, cacheProviderContractChecks, cacheProviderEnv, cacheProviderEnvIsSet, callWithTimeout, createFilesystemFileCache, createTieredMetroStore, createWarnOnce, loadCacheProvider, metroCapabilityFromStore, resolveTieredBuild, runCacheProviderContract, storeTieredBuild, timeoutFromEnv };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stim-cli/cache",
3
- "version": "1.20.1",
3
+ "version": "1.21.0",
4
4
  "description": "Cache provider contract and tier coordination for Stim.",
5
5
  "license": "MIT",
6
6
  "repository": {