Rendered from specs/F08-schema-cache.md — edit it there, not here.
F08 — Local Schema Cache
Overview
The extension can download an authenticated remote schema and save it as a local file, then rewrite the VS Code json.schemas / yaml.schemas entry to point at the local copy. This eliminates IntelliSense red squiggles caused by the language server's inability to fetch authenticated endpoints.
User Stories
- As a developer, I want to cache a private schema locally so VS Code's language server uses it for IntelliSense without needing my credentials.
- As a developer, I want to refresh the local cache when the remote schema changes.
Functional Requirements
Download and Store
- F08-FR-01 The command
jsonschema.cacheSchemaLocallyMUST download the schema from its remote URL using stored authentication credentials. - F08-FR-02 The downloaded schema MUST be written to a stable local path managed by the extension (within the extension's global storage directory).
- F08-FR-03 The extension MUST record a mapping from the local path back to the original remote URL so the cache can be refreshed.
- F08-FR-04 Download MUST be performed under a progress notification showing the host being fetched.
Redirect
- F08-FR-05 After a successful download the binding for the data file MUST be updated to point at the local path instead of the remote URL.
- F08-FR-06 A success notification MUST confirm the cache location and instruct the user that the language server will now use the local copy.
Already Cached
- F08-FR-07 Running
jsonschema.cacheSchemaLocallyon a file whose schema is already cached MUST show an informational message and offer Refresh Schema Cache instead of re-downloading.
Refresh
- F08-FR-08 The command
jsonschema.refreshSchemaCacheMUST re-download the schema from the recorded original URL and overwrite the local cache file. - F08-FR-09 A progress notification MUST be shown during refresh.
- F08-FR-10 If no cached schema is found for the active file the command MUST show an informational message.
Freshness — Automatic Revalidation (planned)
- F08-FR-14 A setting
jsonschema.cache.autoRefreshMUST control automatic cache revalidation with the valuesoff(default),onOpen(revalidate a cached schema when a data file bound to it becomes the active editor, at most once per session per schema), anddaily(revalidate at most once per 24 hours per schema, checked lazily on activity). - F08-FR-15 When the original response carried an
ETagorLast-Modifiedheader, revalidation MUST be conditional (If-None-Match/If-Modified-Since); a304 Not Modifiedresponse MUST NOT rewrite the cache file. Responses without validators fall back to a full re-download. - F08-FR-16 The extension MUST persist per-entry cache metadata (
etag,lastModified,fetchedAt) alongside the existing URL mapping so conditional requests survive VS Code restarts. - F08-FR-17 A failed automatic revalidation MUST be silent — no modal or toast; the stale cached copy remains in use (see S04 stale-cache fallback) and the failure MAY be reflected in the schema-auth status bar tooltip. Manual Refresh Schema Cache keeps its existing error reporting (F08-FR-11/12).
- F08-FR-18 Fetched schema content MUST be validated as parseable (per its source format, JSON/JSONC or YAML) and re-serialized to canonical JSON before being persisted to the local cache file — the raw network response bytes MUST NOT be written to disk directly. Unparseable content MUST NOT be cached:
download()fails with an error (surfaced via F08-FR-11/12's existing reporting) andrevalidate()treats it like any other failure (F08-FR-17 — silent, stale copy kept). This prevents ever persisting a corrupted/malformed response and breaks the direct network-to-disk data flow a security scan flags on an unvalidated write. - F08-FR-19
revalidate()MUST NOT determine whether a cached copy exists via a separate existence check (e.g.fs.existsSync) whose result is then relied on by a later, distinct file operation — that check-then-use split is a TOCTOU race between the check and the (much later, post-network- await) write. Existence MUST instead be established by directly attempting the real file operation and handling its failure.
Error Handling
- F08-FR-11 If the download returns 401/403 an
AuthRequiredErrorMUST be thrown; the caller MUST offer a Configure Auth button. - F08-FR-12 Any other download failure MUST show an error notification with the error message.
Fetch Timeout
- F08-FR-13 Remote fetch requests MUST have a timeout to prevent the UI from hanging indefinitely on slow or unreachable endpoints.
Non-Functional Requirements
- F08-NFR-01 The local cache MUST survive VS Code restarts.
- F08-NFR-02 Cached files MUST NOT be committed to source control (they live in the extension's global storage, outside the workspace).
Acceptance Criteria
- Running Cache Schema Locally on a file with a remote
$schemaproduces a local file and updates thejson.schemasentry to point at it. - Running the command a second time shows "Schema is already cached" and suggests Refresh.
- Running Refresh Schema Cache re-downloads the file from the original URL.
- With
jsonschema.cache.autoRefresh: "onOpen", activating a bound data file sends a conditional request; on304the cache file's mtime is unchanged. - With the network unplugged and
autoRefreshenabled, activating a bound data file produces no error UI and validation still uses the cached schema.
Requirement traceability
Status of each requirement in specs/traceability.json. Test coverage is auto-discovered from [ID] tags in test titles and is not listed here.
| Requirement | Status | Implementation | Note |
|---|---|---|---|
F08-FR-01 | implemented | src/SchemaCache.ts | unit-tested via the vscode mock (2026-07 coverage expansion) |
F08-FR-02 | implemented | src/SchemaCache.ts | unit-tested via the vscode mock (2026-07 coverage expansion) |
F08-FR-03 | implemented | src/SchemaCache.ts | unit-tested via the vscode mock (2026-07 coverage expansion) |
F08-FR-04 | manual | src/extension.ts | VS Code API-bound; verified by manual / E2E testing |
F08-FR-05 | manual | src/SchemaBindingManager.ts | VS Code API-bound; verified by manual / E2E testing |
F08-FR-06 | manual | src/extension.ts | VS Code API-bound; verified by manual / E2E testing |
F08-FR-07 | manual | src/extension.ts | VS Code API-bound; verified by manual / E2E testing |
F08-FR-08 | implemented | src/SchemaCache.ts | unit-tested via the vscode mock (2026-07 coverage expansion) |
F08-FR-09 | manual | src/extension.ts | VS Code API-bound; verified by manual / E2E testing |
F08-FR-10 | manual | src/extension.ts | VS Code API-bound; verified by manual / E2E testing |
F08-FR-11 | manual | src/SchemaCache.ts | VS Code API-bound; verified by manual / E2E testing |
F08-FR-12 | manual | src/extension.ts | VS Code API-bound; verified by manual / E2E testing |
F08-FR-13 | implemented | src/SchemaAuthManager.ts | fetchText enforces an AbortSignal timeout (schemaAuthManager.test.ts) |
F08-FR-14 | implemented | src/settings.ts, src/SchemaCache.ts, src/extension.ts | getCacheAutoRefresh + SchemaCache.revalidate (off/onOpen/daily); wired on active-editor change |
F08-FR-15 | implemented | src/SchemaAuthManager.ts, src/SchemaCache.ts | fetchConditional sends If-None-Match/If-Modified-Since; 304 leaves file untouched, 200 overwrites |
F08-FR-16 | implemented | src/SchemaCache.ts | CacheEntry persists etag/lastModified/fetchedAt in globalState (survives restart) |
F08-FR-17 | implemented | src/SchemaCache.ts, src/extension.ts | revalidate() swallows failures returning "failed"; auto path is fire-and-forget |
F08-FR-18 | implemented | src/SchemaCache.ts | toCacheableText() parses fetched content per source format and writes JSON.stringify(parsed) instead of the raw response text; unparseable content throws before any write (surfaced via existing F08-FR-11/12 error reporting in download(), treated as a normal revalidate() failure per F08-FR-17). Fixes a CodeQL 'network data written to file' finding by deriving the write from a parsed value rather than passing tainted network text through verbatim. |
F08-FR-19 | implemented | src/SchemaCache.ts | revalidate() replaced its fs.existsSync(...) pre-check with a direct fs.readFileSync attempt in try/catch, eliminating the check-then-much-later-write (across an async network await) pattern CodeQL's file-system-race query flags. |
F08-NFR-01 | manual | src/SchemaCache.ts | VS Code API-bound; verified by manual / E2E testing |
F08-NFR-02 | manual | src/SchemaCache.ts | VS Code API-bound; verified by manual / E2E testing |