/** * RestCatalogSource — `ICatalogSource` HTTP client for the forge-store REST backend. * * Wire shape: * * GET /api/catalog/get?ref=/@ → asset row | null * GET /api/catalog/list?publisher=&kind=&category= → asset row[] * GET /api/tarball/manifest?sha256= → { sha256, url, sizeBytes, fileCount, public: true } * GET → binary tarball (follows 307 redirect) * * The forge-store REST API is served by Nuxt 4 + Nitro (`forge-store/server/api/`). * All catalog endpoints are anonymous (no authentication required). The tarball * download URL may be a presigned S3 URL or a CDN URL; the client follows the * redirect and downloads the bytes directly. * * The client verifies SHA256 on the client side after download (spec invariant: * "Hash pinning ... Mismatch = hard fail"). * * Cache / air-gapped: identical semantics to {@link RemoteCatalogSource} — stale-while- * revalidate on metadata, content-addressed tarballs cached forever. * * @docLink packages/library/api-reference#rest-catalog-source */ import type { CatalogAsset, CatalogAssetFilter, CatalogSourceInfo, ICatalogSource } from "@skaile/workspaces/plugins"; import { CatalogCache } from "./cache.js"; import { type HttpClient } from "./remote-catalog-source.js"; /** * Constructor options for {@link RestCatalogSource}. * * @docLink packages/library/api-reference#rest-catalog-source */ export interface RestCatalogSourceOptions { /** Base URL of the forge-store instance, e.g. `http://localhost:3000`. No trailing slash required. */ baseUrl: string; /** * Metadata cache TTL in milliseconds. * Default `86_400_000` (24h). `0` disables network — air-gapped mode. */ cacheTtlMs?: number; /** Override `fetch` for tests. */ httpClient?: HttpClient; /** Override the cache directory. Default is `~/.skaile/cache/catalog/`. */ cacheDir?: string; /** Override clock for tests. */ now?: () => number; /** Pre-built cache instance (test injection). When provided, `cacheTtlMs`/`cacheDir`/`now` are ignored. */ cache?: CatalogCache; /** * If true, suppresses the background revalidation `unhandledRejection` log when * a stale-while-revalidate fetch fails. Default false. */ silentBackgroundErrors?: boolean; } /** * HTTP client implementation of `ICatalogSource` for the forge-store REST API. * See module docstring for the wire contract. * * @docLink packages/library/api-reference#rest-catalog-source */ export declare class RestCatalogSource implements ICatalogSource { readonly id: string; readonly baseUrl: string; readonly cache: CatalogCache; private readonly cacheTtlMs; private readonly httpClient; private readonly silentBackgroundErrors; /** Tracks in-flight background revalidations so callers can `await` them in tests. */ private readonly inFlight; constructor(opts: RestCatalogSourceOptions); /** * Resolve a single asset by canonical ref (`/@`). * * Cache: hit returns immediately. Miss fetches `/api/catalog/get?ref=`. Stale * entry serves the cached value and triggers a background refresh. * * Air-gapped: throws {@link OfflineError} on cache miss. */ resolve(ref: string): Promise; /** * List assets matching `filter`. * * Cache: hit returns immediately. Miss fetches `/api/catalog/list`. Stale * entry serves the cached array and triggers a background refresh. * * Air-gapped: throws {@link OfflineError} on cache miss. * * Note: `filter.prefix` is not supported by the forge-store REST API and is silently ignored. */ listAssets(filter?: CatalogAssetFilter): Promise; /** * Fetch the content-addressed tarball for an asset. * * Steps: * 1. `GET /api/tarball/manifest?sha256=` → JSON with `{ url, ... }` * 2. `GET ` following redirects → raw bytes * 3. Verify SHA256 client-side — hard fail on mismatch * 4. Write to cache on success * * Cache: tarballs are content-addressed and cached forever once written. * * Air-gapped: throws {@link OfflineError} on cache miss. * * @throws {@link TarballHashMismatchError} when the downloaded SHA256 does not match. */ fetchTarball(_ref: string, sha256: string): Promise; /** * Enumerate available versions for a version-less ref. The forge-store REST * API has no version-list route, so this derives versions from `listAssets` * (filtered to the publisher) and keeps only the matching kind+name. Reuses * the cached list path, so repeated calls are cheap. */ listVersions(ref: string): Promise; /** * Single synthetic Source pointing at the configured forge-store URL. */ listSources(): Promise; /** * Force a metadata-cache refresh. Wipes all resolve/list entries and * re-fetches `listAssets()` (no filter) to pre-warm the canonical query. * * Driven by `skaile update`. Tarballs (content-addressed) are not touched. * * In air-gapped mode this method **does** make a network call (bypassing * the TTL=0 gate); it is the only way to repopulate the cache. */ refresh(): Promise<{ assetsCached: number; }>; /** * Wait for any in-flight stale-while-revalidate background fetches to settle. * Test-only escape hatch. */ waitForBackground(): Promise; private scheduleBackground; private fetchAndStoreResolve; private fetchAndStoreList; /** Raw network call: GET /api/catalog/get?ref= */ private fetchResolveNetwork; /** Raw network call: GET /api/catalog/list?publisher=&kind=&category= */ private fetchListNetwork; } //# sourceMappingURL=rest-catalog-source.d.ts.map