diff --git a/docz/docs/03-demos/06-desktop/09-cli.md b/docz/docs/03-demos/06-desktop/09-cli.md
index 7707ffd..dc33631 100644
--- a/docz/docs/03-demos/06-desktop/09-cli.md
+++ b/docz/docs/03-demos/06-desktop/09-cli.md
@@ -17,6 +17,68 @@ it is feasible to build command-line tools for various workflows.
This demo covers a number of strategies for building standalone processors. The
goal is to generate CSV output from an arbitrary spreadsheet file.
+## V8
+
+The [V8](/docs/demos/engines/v8) demo covers standalone programs that embed the
+V8 engine. This demo uses the Rust integration to generate a command line tool.
+
+Tested Deployments (click to show)
+
+This demo was last tested in the following deployments:
+
+| Architecture | V8 Version | Date |
+|:-------------|:-------------|:-----------|
+| `darwin-x64` | `11.4.183.2` | 2023-05-22 |
+
+
+
+0) Make a new folder for the project:
+
+```bash
+mkdir sheetjs2csv
+cd sheetjs2csv
+```
+
+1) Download the following scripts:
+
+- [`Cargo.toml`](pathname:///cli/Cargo.toml)
+- [`snapshot.rs`](pathname:///cli/snapshot.rs)
+- [`sheet2csv.rs`](pathname:///cli/sheet2csv.rs)
+
+```bash
+curl -LO https://docs.sheetjs.com/cli/Cargo.toml
+curl -LO https://docs.sheetjs.com/cli/snapshot.rs
+curl -LO https://docs.sheetjs.com/cli/sheet2csv.rs
+```
+
+2) Download the [standalone build](/docs/getting-started/installation/standalone):
+
+{`\
+curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}
+
+
+3) Build the V8 snapshot:
+
+```bash
+cargo build --bin snapshot
+cargo run --bin snapshot
+```
+
+4) Build `sheet2csv`:
+
+```bash
+cargo build --release --bin sheet2csv
+mv target/release/sheet2csv .
+```
+
+5) Download the test file :
+
+```bash
+curl -LO https://sheetjs.com/pres.numbers
+```
+
+Test by running `./sheet2csv pres.numbers`
+
## NodeJS
There are a few popular tools for compiling NodeJS scripts to CLI programs.
@@ -185,8 +247,8 @@ deno compile -r --allow-read https://docs.sheetjs.com/cli/sheet2csv.ts
The following demos for JS engines produce standalone programs:
-- [ChakraCore](/docs/demos/engines/chakra)
- [Duktape](/docs/demos/engines/duktape)
+- [ChakraCore](/docs/demos/engines/chakra)
+- [QuickJS](/docs/demos/engines/quickjs)
- [Goja](/docs/demos/engines/goja)
- [JavaScriptCore](/docs/demos/engines/jsc)
-- [QuickJS](/docs/demos/engines/quickjs)
diff --git a/docz/docs/03-demos/11-bigdata/02-worker.md b/docz/docs/03-demos/11-bigdata/02-worker.md
index e17ac61..311f544 100644
--- a/docz/docs/03-demos/11-bigdata/02-worker.md
+++ b/docz/docs/03-demos/11-bigdata/02-worker.md
@@ -183,6 +183,20 @@ const worker = new Worker(
## Live Demos
+:::note
+
+Each browser demo was tested in the following environments:
+
+| Browser | Date | Comments |
+|:------------|:-----------|:----------------------------------------|
+| Chrome 113 | 2023-05-22 | |
+| Edge 113 | 2023-05-22 | |
+| Safari 16.4 | 2023-05-22 | File System Access API is not supported |
+| Brave 1.51 | 2023-05-22 | File System Access API is not supported |
+| Firefox 113 | 2023-05-22 | File System Access API is not supported |
+
+:::
+
### Downloading a Remote File
:::note fetch in Web Workers
diff --git a/docz/static/cli/Cargo.toml b/docz/static/cli/Cargo.toml
new file mode 100644
index 0000000..957c392
--- /dev/null
+++ b/docz/static/cli/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "sheetjs2csv"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+v8 = "0.71.2"
+
+[[bin]]
+name = "sheet2csv"
+path = "sheet2csv.rs"
+
+[[bin]]
+name = "snapshot"
+path = "snapshot.rs"
diff --git a/docz/static/cli/sheet2csv.rs b/docz/static/cli/sheet2csv.rs
new file mode 100644
index 0000000..8ec5731
--- /dev/null
+++ b/docz/static/cli/sheet2csv.rs
@@ -0,0 +1,42 @@
+/* run code, get result as a Rust String */
+fn eval_code(scope: &mut v8::HandleScope, code: &str) -> std::string::String {
+ let source = v8::String::new(scope, &code).unwrap();
+ let script = v8::Script::compile(scope, source, None).unwrap();
+ let result = script.run(scope).unwrap();
+ return result.to_string(scope).unwrap().to_rust_string_lossy(scope);
+}
+
+fn main() {
+ /* parse arguments */
+ let mut iter = std::env::args();
+ let path: String = iter.nth(1).expect("must specify a file name");
+ let sheetname: String = match iter.nth(0) {
+ Some(v) => format!("'{}'", v),
+ None => "wb.SheetNames[0]".to_string()
+ };
+
+ /* initialize */
+ let platform = v8::new_default_platform(0, false).make_shared();
+ v8::V8::initialize_platform(platform);
+ v8::V8::initialize();
+
+ /* read snapshot */
+ let startup_data: Vec = include_bytes!("./snapshot.bin").to_vec();
+ let params = v8::Isolate::create_params().snapshot_blob(startup_data);
+ let isolate = &mut v8::Isolate::new(params);
+ let handle_scope = &mut v8::HandleScope::new(isolate);
+ let context = v8::Context::new(handle_scope);
+ let context_scope = &mut v8::ContextScope::new(handle_scope, context);
+
+ /* read file */
+ let data: Vec = std::fs::read(path.clone()).unwrap();
+ let back: v8::UniqueRef = v8::ArrayBuffer::new_backing_store_from_vec(data);
+ let shared = back.make_shared();
+ let ab: v8::Local = v8::ArrayBuffer::with_backing_store(context_scope, &shared);
+ let key = v8::String::new(context_scope, "buf").unwrap();
+ context.global(context_scope).set(context_scope, key.into(), ab.into());
+
+ /* print CSV of specified sheet */
+ let result = eval_code(context_scope, &mut format!("var wb = XLSX.read(buf, {{dense: true}}); XLSX.utils.sheet_to_csv(wb.Sheets[{}])", sheetname));
+ println!("{}", result);
+}
diff --git a/docz/static/cli/snapshot.rs b/docz/static/cli/snapshot.rs
new file mode 100644
index 0000000..8644c08
--- /dev/null
+++ b/docz/static/cli/snapshot.rs
@@ -0,0 +1,38 @@
+/* run code, get result as a Rust String */
+fn eval_code(scope: &mut v8::HandleScope, code: &str) -> std::string::String {
+ let source = v8::String::new(scope, &code).unwrap();
+ let script = v8::Script::compile(scope, source, None).unwrap();
+ let result = script.run(scope).unwrap();
+ return result.to_string(scope).unwrap().to_rust_string_lossy(scope);
+}
+
+
+fn main() {
+ create_snapshot();
+}
+
+fn create_snapshot() {
+ /* initialize */
+ let platform = v8::new_default_platform(0, false).make_shared();
+ v8::V8::initialize_platform(platform);
+ v8::V8::initialize();
+
+ let startup_data = {
+ let mut isolate = v8::Isolate::snapshot_creator(None);
+ {
+ let handle_scope = &mut v8::HandleScope::new(&mut isolate);
+ let context = v8::Context::new(handle_scope);
+ let context_scope = &mut v8::ContextScope::new(handle_scope, context);
+
+ /* load library */
+ {
+ let script = std::fs::read_to_string("./xlsx.full.min.js").expect("Error reading xlsx.full.min.js");
+ let _result = eval_code(context_scope, &script);
+ }
+ context_scope.set_default_context(context);
+ }
+ isolate.create_blob(v8::FunctionCodeHandling::Clear).unwrap()
+ };
+ let blob: Vec = startup_data.to_vec();
+ std::fs::write("snapshot.bin", blob).unwrap();
+}