session.ts

Persistent analysis session — δ-shaped API over a ts.LanguageService.

Maps cleanly onto LSP and Vite/HMR consumers:

- setFile / setFiles — additive ingest; transform-if-Svelte, lex specifiers, resolve imports (parallel), push content/virtual to the LS. Returns ingest-time diagnostics + a changed flag. Cache-hit no-op when content matches AND the mode-specific cache key matches (resolver identity for lex+resolve, dependency-snapshot equality for pre-resolved). - deleteFile — drop owned entry, evict from LS. - has / list — owned-set introspection (covers what consumers used to get from their own mirror caches). - query — sync analysis pass against the current owned set; returns analysis-pass diagnostics only (ingest diagnostics surface via the setFile/setFiles returns). - dispose — release LS resources.

The session owns a single Map<id, OwnedEntry> covering content, svelte virtuals, unfiltered deps, the mode-specific cache key (resolver identity or pre-resolved snapshot), and ingest-time diagnostics. svelte2tsx runs at most once per content change. Resolver work parallelizes across the batch in phase 2 of the three-phase setFiles pipeline; fully pre-resolved batches skip phase 2 (and the default-resolver construction) entirely.

@see analyze-core.ts for the two-phase analysis orchestrator @see dep-resolver.ts for the ImportResolver token contract

Declarations
#

7 declarations

view source

AnalysisSession
#

session.ts view source

AnalysisSession

Persistent analysis handle.

Concurrency: not safe across overlapping calls. Serialize externally (each caller awaits the previous setFile/setFiles before starting the next). The LS underneath is sync, but the resolver phase awaits I/O for async resolvers (Vite/Rollup), so the session does cross await boundaries.

Cache-hit semantics: per-entry, all-or-nothing. The implementation must not split the guarantee across separate caches (e.g. transform-cache hit + lex re-run). The match criterion is mode-discriminated:

- lex+resolve mode: existing.content === incoming.content AND existing.resolverIdentity === incoming.resolverIdentity. - pre-resolved mode: existing.content === incoming.content AND arraysShallowEqual(existing.preResolvedDepsSnapshot, incoming.dependencies).

Mode flips (an entry previously ingested as lex+resolve now arrives with dependencies, or vice versa) always cache-miss.

Promise resolution: setFile / setFiles resolve only after the serial LS push (phase 3) completes for every file in the batch. Awaiting the returned promise is sufficient — no separate flush step.

AnalysisSessionOptions
#

session.ts view source

AnalysisSessionOptions

Options for createAnalysisSession.

documentRegistry flows through to the underlying LanguageService only. tsconfig and compilerOptions flow to both the LS *and* the lazy default ImportResolver (getDefaultResolver re-invokes loadTsconfig with them to produce a merged ts.CompilerOptions for module resolution). The two paths share the same merge semantics — user-supplied compilerOptions override parsed tsconfig keys, but never bypass the tsconfig.json file requirement.

projectRoot and virtualFiles from the LS options shape are excluded — the session derives projectRoot from sourceOptions and manages svelte2tsx virtuals internally per file.

inheritance

extends:
  • Omit< AnalysisLanguageServiceOptions, 'projectRoot' | 'virtualFiles' >

sourceOptions

Module source options for path extraction and source filtering.

Must be a fully-constructed ModuleSourceOptions — the session re-runs normalizeSourceOptions (idempotent) but does not apply any defaults. Pass through createSourceOptions(projectRoot, overrides?) to merge with DEFAULT_SOURCE_OPTIONS. (The Partial<SourceOptionsDefaults> ergonomic shape exists only on AnalyzeFromFilesOptions.sourceOptions, where the defaults merge happens inside analyzeFromFiles.)

resolveImport

Session-default custom import resolver used when no per-call override is supplied — a bare ResolveImportFn or a token-paired ImportResolver (see ResolveImport). A bare function is normalized once at construction, so its synthesized identity is stable for the session's lifetime (cache reuse works). When omitted, the session lazily constructs the TS+tsconfig default on first use.

log

Optional logger for session-level messages.

createAnalysisSession
#

session.ts view source

(options: AnalysisSessionOptions): AnalysisSession

Create a persistent analysis session.

options

returns

AnalysisSession

examples

Vite plugin integration

const session = createAnalysisSession({sourceOptions, resolveImport, log}); await session.setFiles(initialFiles); const result = session.query(); // on watcher events: await session.setFile({id, content}); await session.deleteFile(removedId); const next = session.query(); // on shutdown: session.dispose();

One-shot via the public wrapper

// Equivalent to `analyze(...)` — the wrapper goes through a session internally. const session = createAnalysisSession({sourceOptions}); try { await session.setFiles(sourceFiles); return session.query({onDuplicates: 'throw'}); } finally { session.dispose(); }

QueryOptions
#

session.ts view source

QueryOptions

Per-call input to query.

onDuplicates

Behavior when duplicate declaration names are found across modules.

log

Per-call logger override (defaults to the session-level logger).

SetFileOptions
#

session.ts view source

SetFileOptions

Options for a per-file or per-batch resolver override.

Identity is required (not optional) — silently coalescing missing identities to a function reference would destroy cache reuse when the same logical resolver is wrapped in fresh closures across calls.

resolveImport

Per-call override of the session-default resolver — a bare ResolveImportFn or a token-paired ImportResolver (see ResolveImport).

A bare function is normalized with a fresh identity on each call, so the files touched by this call re-resolve rather than cache-hitting — the expected behavior for a deliberate one-off override. To reuse the resolve cache across calls, pass an ImportResolver with a stable identity.

SetFileResult
#

session.ts view source

SetFileResult

Result of setFile (single-file ingest).

changed is true when content or the mode-specific cache key (resolver identity for lex+resolve; dependency snapshot for pre-resolved) differed from the cached entry — the owned entry was rewritten. An LS push accompanies the entry write only when the file is TS/JS or has a successful Svelte virtual; CSS/JSON and transform-failed Svelte rewrite the entry without touching the LS. false indicates a cache-hit no-op: the cached ingestDiagnostics are returned but no work ran.

changed

Whether content or the mode-specific cache key differed from the cached entry.

type boolean

diagnostics

Ingest-time diagnostics for this file (durable on the entry).

type Array<Diagnostic>

SetFilesResult
#

session.ts view source

SetFilesResult

Result of setFiles (batch ingest).

Carries both aggregate views (changedIds, pre-flattened diagnostics) and a structured perFile map. HMR-style consumers want changedIds.size > 0 as the hot check; LSP-style consumers want per-file diagnostic association via perFile. Both are populated in the same single-pass walk over the batch — no extra cost.

changedIds

IDs whose content or mode-specific cache key differed from the cached entry — the subset of input file IDs that actually triggered work. Empty when every file was a cache-hit no-op.

type ReadonlySet<string>

diagnostics

Pre-flattened union of every file's ingestDiagnostics. Consumers can group by Diagnostic.file for per-file publish.

type Array<Diagnostic>

perFile

Per-file SetFileResult keyed by input file ID. Use this when the grouping Diagnostic.file would do isn't enough — e.g., LSP wanting to publish empty-diagnostic-list updates for files that ingested cleanly.

type ReadonlyMap<string, SetFileResult>

Depends on
#

Imported by
#