0013 — The VS Code extension: one package per platform with the release binary
Status: Accepted
Context
jlens lsp is a language server (ADR 0007). Neovim, Helix and Emacs start it from a few lines
of configuration, but VS Code starts language servers only from extensions, and most people who
write JSONata use VS Code. The extension has to answer five questions: where the server comes
from, how the user points it to another jlens, which language it claims, under which name it
is published, and where its code lives.
The server is the jlens binary. The release builds it for five targets (binaries.yml): Linux
x86-64 and ARM64 (static, musl), macOS x86-64 and ARM64, Windows x86-64, attested and listed in
SHA256SUMS. Three ways to give it to the extension:
- A
jlensfound on thePATH. The extension stays small, but nothing works until the user installs the CLI through another channel, and the server’s version drifts from the extension’s. - A download at first start, from the GitHub release. The extension would need the network on first use (not always there, often behind a proxy), and would have to verify the archive itself before running it.
- One VSIX per platform with the release binary, as rust-analyzer does. Since VS Code 1.61,
the Visual Studio Marketplace and Open VSX serve each platform its own package (
vsce package --target), and a package without a target to the platforms that have none.
VS Code’s targets include alpine-x64 and alpine-arm64. The musl binaries are static, so the
two Linux archives serve both the linux- and the alpine- targets.
Other JSONata extensions, checked on 2026-10-14: on the Marketplace,
bigbug.vscode-language-jsonata (a grammar, about 6,000 installs, last updated in 2023),
nawed-diwan.jsonata-intellisense and Fitmavincent.jsonata-validator; on Open VSX, none. All
three use the language id jsonata, the extension .jsonata and the grammar scope
source.jsonata, as the editors guide does for Neovim and Helix.
Names, checked on 2026-10-14: the publisher thkprado exists on neither registry, and neither
does the extension thkprado.jlens.
Open VSX publishes with an access token of an Eclipse account (ovsx publish). The Marketplace
publishes with a personal access token of Azure DevOps, but retires global tokens on 2026-12-01
and recommends a Microsoft Entra ID identity instead (vsce publish --azure-credential): a
managed identity, which is an Azure resource and needs an Azure subscription.
Decision
- One VSIX per release target, with the release binary.
linux-x64,linux-arm64,alpine-x64,alpine-arm64,darwin-x64,darwin-arm64andwin32-x64, each holding thejlensbinary of its archive, as built and attested by the release, never rebuilt. One more VSIX, without a target and without a binary, serves the other platforms (Windows on ARM64, 32-bit ARM Linux) and uses thejlenson thePATH. No download at first start. - The server is found in this order: the setting
jlens.server.path, then the bundled binary, thenjlenson thePATH. The setting has themachinescope and is restricted in untrusted workspaces, so that a repository’s settings cannot choose which program runs. When no server is found, the extension says so once, with a link to the installation page, and leaves highlighting working. The server’s version, from itsinitializeanswer, goes to the output channel. - The language is
jsonata, with the extension.jsonata, the aliasJSONataand the grammar scopesource.jsonata, the same as the other extensions and editors. A file opened in VS Code has one language id, so a different id would split JSONata files between extensions.jlens.jsonis checked through a JSON Schema contributed withjsonValidation. - The extension is
thkprado.jlens, namedjlensand displayed as “jlens — JSONata”, on both registries: the publisherthkpradoon the Marketplace and the namespacethkpradoon Open VSX, created by the maintainer. - One version for all. The extension has the workspace version and is published by
release.ymlafter the binaries, as the npm packages are (ADR 0012), skipping a version already published, so re-running the job completes a release that failed halfway through. - The Marketplace credential is chosen with the publishing job (roadmap 0.15 #5), among:
an Entra ID managed identity federated with the release workflow (no long-lived secret, but
an Azure subscription); a personal access token, which works only until 2026-12-01; or no
credential, with the maintainer uploading the VSIX files attached to the GitHub release.
Chosen on 2026-10-14: an Entra ID identity federated with the GitHub environment
vscodeof the release job. It became an application (a service principal) rather than a managed identity: it needs no subscription, so it outlives a free one. Open VSX is published to with a token for the first version, which only a token can publish, then through trusted publishing (the run’s OIDC token), so that neither registry keeps a long-lived secret.CONTRIBUTING.mdsays how to set both up. - The code lives in
editors/vscode/, outside the Cargo workspace:package.json, the TypeScript client overvscode-languageclient, bundled into one file with esbuild, the TextMate grammar and the language configuration, and the extension tests (@vscode/test-electron). Acargo xtask vscodecommand builds the VSIX files from the release archives, ascargo xtask npm-clibuilds the npm packages.
Consequences
- Installing the extension is enough on the five release platforms: no other step, and the server always has the extension’s version. Other platforms install the CLI another way.
- Each release publishes eight VSIX files to two registries. Seven of them hold a binary of a few megabytes; the alpine packages repeat the Linux ones.
- With
jlens.server.pathor ajlensfrom thePATH, the server’s version may differ from the extension’s. The output channel shows it. - With another JSONata extension installed, VS Code uses both: two grammars for the same scope, of which one wins, and diagnostics from each server. The extension’s README says to disable the other’s language features.
- The release needs an Entra ID tenant for the application, which an Azure sign-up creates,
and a GitHub environment whose rules accept only version tags. Only a workflow run can sign
in as the application, so a small workflow (
marketplace-identity.yml) reads the ID the publisher takes as a member. - VS Code for the Web (the
webtarget) is left out. The WebAssembly package offers the same language service, and could serve it later.