# JSR packages avril supports native `jsr:` imports, without npm compatibility tarballs or `node_modules`: ```ts import { assertEquals } from "jsr:@std/assert@^1"; ``` Packages belong in the `deps` of `avril.conf`: ```text deps: [ 'jsr:@std/assert@^1.0.19', {from: 'jsr:@std/path@^1.1.6', name: 'path'}, ], ``` A dependency binds its package name, or the `name` given, together with its subpaths: here `@std/assert` and `@std/assert/equals` select the package's `.` and `./equals` exports, and `path/posix` selects `@std/path`'s `./posix`. The same package can be listed twice with different ranges if one entry is renamed. `imports` can still map an alias to a single export, or a slash-terminated alias to the exports beneath a prefix: ```text imports: { posix: 'jsr:@std/path@^1/posix', 'std-path/': 'jsr:@std/path@^1/', }, ``` Only declared public exports can be selected through `jsr:`. Published modules resolve their relative imports within their own package and version and their external dependencies through explicit `jsr:` references. They never inherit the application's aliases. Arbitrary HTTP imports, CommonJS, JSX, declaration-file execution, and runtime JSON imports are not supported. Npm packages use `npm:` specifiers instead; see [Npm packages](npm.md). TypeScript is transpiled, not type-checked; erased type-only dependencies do not need to exist at runtime. JSR does not guarantee runtime portability: packages that require `Deno.*`, Node APIs, or JSON imports will not work. Pure ECMAScript and supported Web API packages, such as `@std/assert`, are suitable candidates. ## Preparation and identity Before execution, the static graph is acquired and TypeScript is transformed. V8 linking reads prepared source and never performs network I/O. Computed dynamic imports asynchronously acquire the requested static closure through the host event loop, then link and evaluate it. Concurrent acquisition shares downloads, and modules share the same V8 identity regardless of their import spelling. Local identity is a canonical file path. Registry identity is the canonical, versioned registry source URL, not the cache path. `import.meta.url` and mapped TypeScript stacks expose that URL. Non-canonical percent-encoded aliases are rejected. Relative registry imports cannot cross package or version boundaries. Registry graph metadata is an optional, bounded prefetch hint; source parsing remains authoritative. ## Lockfile and cache The root package's `avril.lock` records version selections for the execution graph and SHA-256 integrity for immutable version metadata. Manifests in that metadata cover each source's SHA-256 and size. Requests with different constraints may use different versions; a locked constraint keeps its selection until explicitly updated. New selections skip yanked versions. Existing pins can keep yanked versions, and immutable integrity pins are never silently replaced by an update. Selections and integrity are saved after successful preparation, including for computed dynamic imports. The lock is [fJSON](fjson.md), the same format as `avril.conf`, and merges concurrent changes: an update is recomputed if another process replaced the file while it was being prepared, though a write landing in the instant before the rename can still be lost. A conflicting selection used by this preparation fails rather than overwriting another process's selection. Atomic writes preserve existing modes. `avril.conf` edits follow the same scheme. Commit `avril.lock`. Unchanged locks are not rewritten, so locked runs work in read-only project directories without requiring `--frozen`. Root selection uses trusted host-side discovery, independently of guest execution grants. Cached artifacts are immutable and verified on use. Writes are atomic, and per-artifact file locks coordinate processes when an artifact must be acquired or refreshed; verified cached hits are read without taking those locks. The default cache is `$AVRIL_CACHE_DIR`, `$XDG_CACHE_HOME/avril`, or `$HOME/.cache/avril`. Use `--cache-dir=PATH` to override it. A corrupt locked artifact fails with an integrity error; remove that artifact or use an empty cache to reacquire it online. `--offline` never contacts the registry and fails on cache misses. `--frozen` requires an existing lockfile, selections, and metadata integrity; it permits acquiring missing locked artifacts unless combined with `--offline`. `--lock=PATH` overrides the default root lock. `update --offline` reselects from cached indexes only and does not promise the registry's latest versions. ## Permissions Package acquisition is host tooling authority, separate from guest `net`, `read`, and `write`. It accesses only the configured registry, cache, and lockfile; it never follows HTTP redirects. Acquiring a package does not grant guest network access or access to the cache filesystem. Remote code still needs execution authority. Default execution is unrestricted; with `-0`, explicitly grant URLs, for example: ```sh avril run -0 --allow-js-exec=. --allow-js-exec=https://jsr.io/@std/ main.ts ``` A slash-terminated URL grant covers descendants; otherwise it grants one exact file. Denials cover the named path and descendants. URL paths are normalized before checking, so percent-encoding cannot bypass a denial. Network permission alone never grants remote execution. Prefer grants for individual packages and their transitive dependencies; scope and whole-registry grants let guest code request arbitrary package names within that grant, including names that can convey data to registry request logs even when guest `net` is denied. `pledge({jsExec: false})` revokes both local and remote execution, and mixed URL/path allow lists narrow both domains. Metadata acquisition checks whether some code in the requested package/version could be authorized; selecting an export and acquiring its source also check the exact module. Access is rechecked after asynchronous work and before cached modules are linked or evaluated. Revoking an already-evaluated dependency does not reexecute it when its consumer's existing namespace is reimported. ## Commands `avril add jsr:@std/assert@^1` adds the package to `deps` in the nearest trusted `avril.conf`, or replaces the entry already binding `@std/assert`. A subpath target such as `jsr:@std/path@^1/posix` adds its whole package. `avril add assert jsr:@std/assert@^1` binds the package as `assert` instead, while an alias for a subpath or slash prefix, such as `avril add posix jsr:@std/path@^1/posix`, goes in `imports`; slash-prefix aliases require slash-terminated targets. `--config=PATH` chooses a config explicitly. It edits source spans, preserving existing comments and other entries. It prints the resulting binding and config on stdout, preceded on stderr by whether it was added, updated, or unchanged. A target without a version, such as `jsr:@std/assert` or `jsr:@std/path/posix`, gets a caret range of the package's newest non-yanked stable release, as in `jsr:@std/assert@^1.0.13`; `--pre` lets that release be a prerelease. After editing, `add` caches what the new entry can import, like `avril cache`, and records it in the config's `avril.lock`: every export of a dependency or slash prefix, or an alias's single export. `--offline`, `--jsr-registry`, and `--cache-dir` apply as for other commands. `avril lsp` provides live type-resolution support; see [editor integration](editor.md). ```sh avril run main.ts avril cache main.ts avril info main.ts avril update main.ts avril vendor --include=jsr:@my/lazy@^1 main.ts avril run --offline --frozen --vendor=vendor main.ts ``` `cache`, `info`, `update`, and `vendor` prepare without executing application code. `info` emits the acquired graph as JSON. `update` explicitly reselects encountered constraints. `update --prune` additionally drops unused specifier and metadata pins; include all known dynamic entry points when pruning, and avoid concurrent lock-changing commands during this explicit cleanup. Ordinary runs never prune. `vendor` writes verified metadata and source into readable `vendor/@scope/package/version/...` paths. `--out=PATH` changes its output directory; `--vendor=PATH` supplies an existing vendor tree for reads. These commands prepare static imports only. Computed dependencies cannot be inferred ahead of time; repeat `--include=SPECIFIER` to prepare known dynamic entry points too. For a private computed file, use for example `--include-from=jsr:@my/pkg@^1 --include=./lazy.ts`; the referrer must be a source module, and normal package confinement applies. `--include-from=.` resets the referrer to the entry. A vendored run still verifies metadata against the root lock and sources against their manifests. Previously unused dynamic files require additional cached/vendored artifacts. The registry defaults to `https://jsr.io/`. `--jsr-registry=URL` selects another base; it must use HTTPS, except that HTTP is allowed for loopback fixture registries. Registry identity is part of the lock and cache keys, and changing registries requires a separate lockfile.