Skip to main content

ferridriver_script/
lib.rs

1#![allow(
2  clippy::missing_errors_doc,
3  clippy::missing_panics_doc,
4  clippy::must_use_candidate,
5  clippy::module_name_repetitions,
6  clippy::cast_possible_truncation,
7  clippy::cast_precision_loss,
8  clippy::cast_sign_loss,
9  clippy::too_many_lines,
10  clippy::uninlined_format_args,
11  clippy::needless_pass_by_value,
12  clippy::doc_markdown,
13  clippy::missing_fields_in_debug,
14  // rquickjs method wrappers intentionally produce new Locator instances that
15  // JS is free to discard (e.g. fluent chains like `loc.nth(0)` used directly).
16  clippy::return_self_not_must_use,
17  // Some web-API classes (TextEncoder, etc.) are legitimately stateless per
18  // their WHATWG spec, but `#[rquickjs::methods]` instance methods must still
19  // take `&self` to be callable on `new TextEncoder()` — not a fixable smell.
20  clippy::unused_self
21)]
22//! ferridriver-script: sandboxed `QuickJS` scripting engine.
23//!
24//! Exposes a `ScriptEngine` that runs user-provided JS against ferridriver's
25//! Page/Browser/Context API with:
26//!
27//! - One-shot isolation via [`ScriptEngine::run`] (fresh VM per call) or
28//!   REPL-style continuity via a persistent [`Session`] whose `globalThis`
29//!   survives across [`Session::execute`] calls; [`SessionTable`] owns a
30//!   set of them with a warm-VM cap, idle TTL, and browser-swap
31//!   invalidation.
32//! - Bound args (never interpolated into source) to prevent prompt injection.
33//! - Wall-clock and memory quotas enforced by the `QuickJS` runtime.
34//! - Sandboxed globals: scoped `fs`, captured `console`, session `vars`.
35//! - Module loader rooted at a configured `scripts/` directory with path
36//!   sanitization (rejects `..`, absolute paths, symlinks escaping root).
37//! - A poisoning timeout/OOM discards the session VM so the next
38//!   execution transparently gets a fresh one.
39//!
40//! Scripting is independent of the BDD step registry — scripts drive the
41//! browser through the `page` / `context` / `request` bindings directly.
42
43pub mod bindings;
44pub mod bundle;
45pub mod command_spec;
46pub mod console;
47pub mod discover;
48pub mod engine;
49pub mod error;
50pub mod fs;
51pub mod modules;
52pub mod result;
53pub mod session_procs;
54pub mod session_table;
55pub mod vars;
56
57pub use bindings::{
58  ArtifactsJs, BrowserContextJs, CollectedRegistry, HookArg, HttpClientJs, HttpResponseJs, JsArg, KeyboardJs,
59  LocatorJs, MouseJs, PageJs, PluginBinding, PluginCommandsJs, ScenarioWorld, ScriptAttachment, StepOutcome,
60  collect_registry, drain_attachments, install_plugins, invoke_hook, invoke_step, reset_world, set_scenario_world,
61};
62pub use bundle::{
63  CompiledBundle, CompiledPlugin, bundle_and_compile, bundle_source, compile_and_extract_plugins, eval_bundle,
64};
65pub use command_spec::{CommandOutput, CommandRun, CommandSpec, ResolvedCommand, ResolvedExec};
66pub use console::ConsoleCapture;
67pub use discover::{SOURCE_EXTENSIONS, is_source_file, walk_source_files};
68pub use engine::{
69  ExtensionHost, RunContext, RunOptions, ScriptCaps, ScriptEngine, ScriptEngineConfig, Session, SessionRun,
70};
71pub use error::{ScriptError, ScriptErrorKind};
72// Re-export so the BDD core can name the session's async context (the
73// bridge it drives JS step functions through) without a duplicate
74// rquickjs dependency/version.
75pub use fs::PathSandbox;
76pub use result::{ConsoleEntry, ConsoleLevel, Outcome, ScriptResult, ScriptSuccess};
77pub use rquickjs::AsyncContext;
78pub use session_procs::SessionProcs;
79pub use session_table::{BrowserSession, SessionTable};
80pub use vars::{InMemoryVars, VarsStore};