char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
7b01146
main
JavaScript REPL
Run avril or avril repl to start a persistent JavaScript session. The REPL supports top-level await, multiline syntax, persistent lexical bindings, recoverable syntax/runtime errors, and results formatted by inspect from avril:std, as console.log does; objects can customize this with a Symbol.for("runtime.avril.inspect") method. Results and console output are colored when written to a terminal, unless NO_COLOR is set. Use .help for the built-in commands and .exit (or EOF) to leave. Ctrl-C interrupts an active evaluation; at a terminal prompt it cancels the current line.
Piped input is supported without prompts or banners:
printf 'await Promise.resolve(42)\n' | avril
While evaluating or inspecting a foreground input at a terminal, the REPL reports package downloads and materialization to stderr. It stays quiet while waiting at the prompt.
The REPL uses the same capability defaults as script execution: allow-all unless -0 removes ambient grants. For a restricted session, grant only what it needs:
avril repl-0 --allow-read=./data --allow-js-exec=./scripts --allow-net=example.com:443
Unlike script execution, a -0 REPL has no entry-file grant. --deny-*, registry/cache/lock options, and offline/frozen package settings retain their normal meanings. --include is not used by the REPL; imports entered in the session are prepared on demand.
The REPL never creates or updates a lockfile. Importing JSR, npm, or remote modules resolves against an existing avril.lock where one is present — so its selections and integrity checks, and --frozen, still apply — but new selections are kept only in memory for the session. To record them, run the module with avril run or avril cache instead.
Relative imports resolve from the current working directory. The REPL uses a virtual referrer for resolution; it does not create or load a filesystem entry module and grants no permission to that referrer. Dynamic imports use avril's normal module graph, package resolution, cache, and live capability checks. Built-in avril: modules remain available, while private ext: modules cannot be imported. Both import declarations and dynamic import() are supported; input is JavaScript, not TypeScript. Since the REPL owns standard input, guest reads from avril:std.stdin reject with an explicit “not yet implemented” error.
Import declarations are a REPL convenience, rewritten to await import() with
persistent const bindings:
import { inspect } from 'avril:std' inspect ({ answer :42 })
Default, named, namespace, and side-effect imports are supported. Declarations run in input order rather than being hoisted as in an ES module. Default and named bindings capture exported values at import time; namespace imports retain the module's live properties. Import attributes remain unsupported, just as with dynamic imports.
Background timers and native operations continue to make progress while the REPL waits for input; uncaught background errors are reported without ending the session. EOF and .exit end the session without draining pending background work. With piped input, an await that cannot progress because no host work remains is reported as an error so subsequent input can run. At a terminal, use Ctrl-C to interrupt a pending await; background work is left alone, except that a subprocess blocked in a synchronous wait is stopped so the evaluation can finish. Terminal history is held in memory for the current session.
1# JavaScript REPL 2 3Run `avril` or `avril repl` to start a persistent JavaScript session. The REPL supports top-level `await`, multiline syntax, persistent lexical bindings, recoverable syntax/runtime errors, and results formatted by `inspect` from `avril:std`, as `console.log` does; objects can customize this with a `Symbol.for("runtime.avril.inspect")` method. Results and `console` output are colored when written to a terminal, unless `NO_COLOR` is set. Use `.help` for the built-in commands and `.exit` (or EOF) to leave. Ctrl-C interrupts an active evaluation; at a terminal prompt it cancels the current line. 4 5Piped input is supported without prompts or banners: 6 7``` sh 8printf 'await Promise.resolve(42)\n' | avril 9``` 10 11While evaluating or inspecting a foreground input at a terminal, the REPL reports 12package downloads and materialization to stderr. It stays quiet while waiting at 13the prompt. 14 15The REPL uses the same capability defaults as script execution: allow-all unless `-0` removes ambient grants. For a restricted session, grant only what it needs: 16 17``` sh 18avril repl -0 --allow-read=./data --allow-js-exec=./scripts --allow-net=example.com:443 19``` 20 21Unlike script execution, a `-0` REPL has no entry-file grant. `--deny-*`, registry/cache/lock options, and offline/frozen package settings retain their normal meanings. `--include` is not used by the REPL; imports entered in the session are prepared on demand. 22 23The REPL never creates or updates a lockfile. Importing JSR, npm, or remote modules resolves against an existing `avril.lock` where one is present — so its selections and integrity checks, and `--frozen`, still apply — but new selections are kept only in memory for the session. To record them, run the module with `avril run` or `avril cache` instead. 24 25Relative imports resolve from the current working directory. The REPL uses a virtual referrer for resolution; it does not create or load a filesystem entry module and grants no permission to that referrer. Dynamic imports use avril's normal module graph, package resolution, cache, and live capability checks. Built-in `avril:` modules remain available, while private `ext:` modules cannot be imported. Both import declarations and dynamic `import()` are supported; input is JavaScript, not TypeScript. Since the REPL owns standard input, guest reads from `avril:std.stdin` reject with an explicit “not yet implemented” error. 26 27Import declarations are a REPL convenience, rewritten to `await import()` with 28persistent `const` bindings: 29 30``` js 31import { inspect } from 'avril:std' 32inspect({ answer: 42 }) 33``` 34 35Default, named, namespace, and side-effect imports are supported. Declarations 36run in input order rather than being hoisted as in an ES module. Default and 37named bindings capture exported values at import time; namespace imports retain 38the module's live properties. Import attributes remain unsupported, just as with 39dynamic imports. 40 41Background timers and native operations continue to make progress while the REPL waits for input; uncaught background errors are reported without ending the session. EOF and `.exit` end the session without draining pending background work. With piped input, an `await` that cannot progress because no host work remains is reported as an error so subsequent input can run. At a terminal, use Ctrl-C to interrupt a pending `await`; background work is left alone, except that a subprocess blocked in a synchronous wait is stopped so the evaluation can finish. Terminal history is held in memory for the current session.