diff --git a/docz/docs/02-installation/01-standalone.mdx b/docz/docs/02-installation/01-standalone.mdx
index 5b50fde..0736474 100644
--- a/docz/docs/02-installation/01-standalone.mdx
+++ b/docz/docs/02-installation/01-standalone.mdx
@@ -24,7 +24,7 @@ The `latest` tag references the latest version and updates with each release:
```
-:::warning
+:::warning
A number of CDNs host older versions of the SheetJS libraries. Due to syncing
issues, they are generally out of date.
diff --git a/docz/docs/04-getting-started/03-demos/03-database.md b/docz/docs/04-getting-started/03-demos/03-database.md
index 1cde595..d697d6a 100644
--- a/docz/docs/04-getting-started/03-demos/03-database.md
+++ b/docz/docs/04-getting-started/03-demos/03-database.md
@@ -311,8 +311,8 @@ var result = sql.all();
/* Loop across each name */
result.forEach(function(row) {
/* Get first 100K rows */
- var aoo = db.prepare("SELECT * FROM '" + row.name + "' LIMIT 100000").all();
- if(aoo.length > 0) {
+ var aoo = db.prepare("SELECT * FROM '" + row.name + "' LIMIT 100000").all();
+ if(aoo.length > 0) {
/* Create Worksheet from the row objects */
var ws = XLSX.utils.json_to_sheet(aoo, {dense: true});
/* Add to Workbook */
@@ -361,8 +361,8 @@ var result = sql.all();
/* Loop across each name */
result.forEach(function(row) {
/* Get first 100K rows */
- var aoo = db.prepare("SELECT * FROM '" + row.name + "' LIMIT 100000").all();
- if(aoo.length > 0) {
+ var aoo = db.prepare("SELECT * FROM '" + row.name + "' LIMIT 100000").all();
+ if(aoo.length > 0) {
/* Create Worksheet from the row objects */
var ws = XLSX.utils.json_to_sheet(aoo, {dense: true});
/* Add to Workbook */
@@ -804,9 +804,9 @@ const db = client.db(db_name);
try { await db.collection('pres').drop(); } catch(e) {}
const pres = db.collection('pres');
await pres.insertMany([
- { name: "Barack Obama", idx: 44 },
- { name: "Donald Trump", idx: 45 },
- { name: "Joseph Biden", idx: 46 }
+ { name: "Barack Obama", idx: 44 },
+ { name: "Donald Trump", idx: 45 },
+ { name: "Joseph Biden", idx: 46 }
], {ordered: true});
// highlight-start
diff --git a/docz/docs/04-getting-started/03-demos/18-engines.md b/docz/docs/04-getting-started/03-demos/18-engines.md
new file mode 100644
index 0000000..031e0ad
--- /dev/null
+++ b/docz/docs/04-getting-started/03-demos/18-engines.md
@@ -0,0 +1,843 @@
+---
+sidebar_position: 18
+title: JavaScript Engines
+---
+
+import current from '/version.js';
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+The most popular JavaScript engine is V8. Designed for embedding in software,
+it powers Chrome, NodeJS, UXP, Deno and many other platforms and runtimes.
+
+There are many other runtimes with different design goals. Some are designed
+for low-power or low-memory environments. Others aim for interoperability with
+specific programming languages or environments. Typically they support a
+superset of ES3 and are capable of running SheetJS code.
+
+
+## General Caveats
+
+Common browser and NodeJS APIs are often missing from light-weight JS engines.
+
+**Global**
+
+Some engines do not provide `globalThis` or `global` or `window`. A `global`
+variable can be exposed in one line that should be run in the JS engine:
+
+```js
+var global = (function(){ return this; }).call(null);
+```
+
+**Console**
+
+Some engines do not provide a `console` object. `console.log` can be shimmed
+using the engine functionality. For example, `hermes` provides `print()`:
+
+```js
+var console = { log: function(x) { print(x); } };
+```
+
+**Binary Data**
+
+Some engines do not provide easy ways of marshalling binary data. For example,
+it is common to pass null-terminated arrays, which would truncate XLSX and XLS
+files. APIs that accept pointers without length should be avoided.
+
+Base64 strings are safe for passing between JS and native code, but they should
+only be used when there is no safe way to pass `ArrayBuffer` or `Uint8Array`.
+
+
+## Duktape
+
+Duktape is an embeddable JS engine written in C. It has been ported to a number
+of exotic architectures and operating systems.
+
+**Reading data**
+
+Duktape supports `Buffer` natively but should be sliced before processing:
+
+```c
+/* parse a C char array as a workbook object */
+duk_push_external_buffer(ctx);
+duk_config_buffer(ctx, -1, buf, len);
+duk_put_global_string(ctx, "buf");
+duk_eval_string_noresult("workbook = XLSX.read(buf.slice(0, buf.length), {type:'buffer'});");
+```
+
+**Writing data**
+
+`duk_get_buffer_data` can pull `Buffer` object data into the C code:
+
+```c
+/* write a workbook object to a C char array */
+duk_eval_string(ctx, "XLSX.write(workbook, {type:'array', bookType:'xlsx'})");
+duk_size_t sz;
+char *buf = (char *)duk_get_buffer_data(ctx, -1, sz);
+duk_pop(ctx);
+```
+
+Complete Example (click to show)
+
+:::note
+
+This demo was tested on macOS x64.
+
+:::
+
+0) Download and extract the latest release (2.7.0 at the time of writing)
+
+```bash
+curl -LO https://duktape.org/duktape-2.7.0.tar.xz
+tar -xJf duktape-2.7.0.tar.xz
+mv duktape-2.7.0/src/*.{c,h} .
+```
+
+1) Download the standalone script, shim and test file:
+
+
+
+2) Bundle the test file and create `payload.js`:
+
+```bash
+node -e "fs.writeFileSync('payload.js', 'var payload = \"' + fs.readFileSync('pres.numbers').toString('base64') + '\";')"
+```
+
+3) Create support scripts:
+
+- `global.js` creates a `global` variable and defines a fake `console`:
+
+```js title="global.js"
+var global = (function(){ return this; }).call(null);
+var console = { log: function(x) { print(x); } };
+```
+
+- `hermes.js` will call `XLSX.read` and `XLSX.utils.sheet_to_csv`:
+
+```js title="hermes.js"
+/* sheetjs (C) 2013-present SheetJS -- http://sheetjs.com */
+var wb = XLSX.read(payload, {type:'base64'});
+console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
+```
+
+4) Create the amalgamation `xlsx.hermes.js`:
+
+```bash
+cat global.js xlsx.full.min.js payload.js hermes.js > xlsx.hermes.js
+```
+
+The final script defines `global` before loading the standalone library. Once
+ready, it will read the hardcoded test file and print the contents as CSV.
+
+5) Run the script using the Hermes standalone binary:
+
+```
+hermes xlsx.hermes.js
+```
+
+
+
+## JavaScriptCore
+
+:::warning Platform Limitations
+
+JavaScriptCore is primarily deployed in macOS and iOS applications. There is
+some experimental support through the Bun runtime, but production applications
+intending to support Windows / Linux / Android should try to embed V8.
+
+:::
+
+iOS and OSX ship with the JavaScriptCore framework for running JS scripts from
+Swift and Objective-C. Hybrid function invocation is tricky, but explicit data
+passing is straightforward. The demo shows a standalone Swift example for OSX.
+
+Binary strings can be passed back and forth using `String.Encoding.isoLatin1`.
+
+**Reading data**
+
+`String(contentsOf:encoding:)` reads from a path and returns an encoded string:
+
+```swift
+/* read sheetjs.xls as base64 string */
+let file_path = shared_dir.appendingPathComponent("sheetjs.xls");
+let data: String! = try String(contentsOf: file_path, encoding: String.Encoding.isoLatin1);
+```
+
+This string can be loaded into the JS engine and processed:
+
+```swift
+/* load data in JSC */
+context.setObject(data, forKeyedSubscript: "payload" as (NSCopying & NSObjectProtocol));
+
+/* `payload` (the "forKeyedSubscript" parameter) is a binary string */
+context.evaluateScript("var wb = XLSX.read(payload, {type:'binary'});");
+```
+
+**Writing data**
+
+When writing to binary string in JSC, the result should be stored in a variable
+and stringified in Swift:
+
+```swift
+/* write to binary string */
+context.evaluateScript("var out = XLSX.write(wb, {type:'binary', bookType:'xlsx'})");
+
+/* `out` from the script is a binary string that can be stringified in Swift */
+let outvalue: JSValue! = context.objectForKeyedSubscript("out");
+var out: String! = outvalue.toString();
+```
+
+`String#write(to:atomically:encoding)` writes the string to the specified path:
+
+```swift
+/* write to sheetjsw.xlsx */
+let out_path = shared_dir.appendingPathComponent("sheetjsw.xlsx");
+try? out.write(to: out_path, atomically: false, encoding: String.Encoding.isoLatin1);
+```
+
+The demo includes a sample `SheetJSCore` Wrapper class to simplify operations.
+
+Complete Example (click to show)
+
+:::caution This demo only runs on macOS
+
+This example requires macOS + Swift and will not work on Windows or Linux!
+
+:::
+
+0) Ensure Xcode is installed
+
+1) Download the standalone script, the shim and the test file:
+
+
+
+2) Download the Swift scripts for the demo
+
+- [`SheetJSCore.swift`](pathname:///swift/SheetJSCore.swift) Wrapper library
+- [`main.swift`](pathname:///swift/main.swift) Command-line script
+
+3) Build the `SheetJSwift` binary:
+
+```bash
+swiftc SheetJSCore.swift main.swift -o SheetJSwift
+```
+
+4) Test the program:
+
+```bash
+./SheetJSwift pres.numbers
+```
+
+If successful, a CSV will be printed to console. The script also tries to write
+to `SheetJSwift.xlsx`. That file can be verified by opening in Excel / Numbers.
+
+
+
+
+## QuickJS
+
+QuickJS is an embeddable JS engine written in C. It provides a separate set of
+functions for interacting with the filesystem and the global object. It can run
+the standalone browser scripts.
+
+Complete Example (click to show)
+
+0) Ensure `quickjs` command line utility is installed
+
+1) Download the standalone script, the shim and the test file:
+
+
+
+2) Save the following script to `SheetJSQuick.js`:
+
+```js title="SheetJSQuick.js
+/* sheetjs (C) 2013-present SheetJS -- http://sheetjs.com */
+/* load XLSX */
+import * as std from "std";
+globalThis.global = globalThis;
+std.loadScript("xlsx.full.min.js");
+
+/* read contents of file */
+var rh = std.open("pres.numbers", "rb");
+rh.seek(0, std.SEEK_END);
+var sz = rh.tell();
+var ab = new ArrayBuffer(sz);
+rh.seek();
+rh.read(ab, 0, sz);
+rh.close();
+
+/* parse file */
+var wb = XLSX.read(ab, {type: 'array'});
+
+/* write array */
+var out = XLSX.write(wb, {type: 'array'});
+
+/* write contents to file */
+var wh = std.open("SheetJSQuick.xlsx", "wb");
+wh.write(out, 0, out.byteLength);
+wh.close();
+```
+
+3) Test the program:
+
+```bash
+quickjs SheetJSQuick.js
+```
+
+If successful, the script will generate `SheetJSQuick.xlsx`.
+
+
+
+
+## Rhino
+
+Rhino is an ES3+ engine in Java. The `SheetJSRhino` class and `com.sheetjs`
+package show a complete JAR deployment, including the full XLSX source.
+
+Due to code generation errors, optimization must be turned off:
+
+```java
+Context context = Context.enter();
+context.setOptimizationLevel(-1);
+```
+
+Complete Example (click to show)
+
+0) Download the appropriate Rhino build and rename to `rhino.jar`
+
+1) Download [`SheetJSRhino.zip`](pathname:///rhino/SheetJSRhino.zip) and unzip
+
+2) Save the following code to `SheetJSRhino.java`:
+
+```java title="SheetJSRhino.java"
+/* sheetjs (C) 2013-present SheetJS -- http://sheetjs.com */
+/* vim: set ts=2: */
+import com.sheetjs.SheetJS;
+import com.sheetjs.SheetJSFile;
+import com.sheetjs.SheetJSSheet;
+
+public class SheetJSRhino {
+ public static void main(String args[]) throws Exception {
+ try {
+ SheetJS sjs = new SheetJS();
+
+ /* open file */
+ SheetJSFile xl = sjs.read_file(args[0]);
+
+ /* get sheetnames */
+ String[] sheetnames = xl.get_sheet_names();
+ System.err.println(sheetnames[0]);
+
+ /* convert to CSV */
+ SheetJSSheet sheet = xl.get_sheet(0);
+ String csv = sheet.get_csv();
+
+ System.out.println(csv);
+
+ } catch(Exception e) {
+ throw e;
+ } finally {
+ SheetJS.close();
+ }
+ }
+}
+```
+
+3) Assemble `SheetJS.jar` from the demo code:
+
+```bash
+javac -cp .:rhino.jar SheetJSRhino.java
+jar -cf SheetJS.jar SheetJSRhino.class com/sheetjs/*.class
+```
+
+4) Download and test:
+
+```bash
+java -cp .:SheetJS.jar:rhino.jar SheetJSRhino pres.xlsx
+```
+
+
+
+## Legacy Engines
+
+:::warning
+
+These examples were written when the engines were maintained. New projects
+should not use these engines. The demos are included for legacy deployments.
+
+:::
+
+### ChakraCore
+
+:::caution
+
+ChakraCore was an open source JavaScript engine released by Microsoft. It was a
+fork of the Chakra engine that powered Internet Explorer. When Microsoft Edge
+switched to become a fork of Chromium, Microsoft stopped providing support.
+
+:::
+
+ChakraCore is an embeddable JS engine written in C++. The library and binary
+distributions include a command-line tool `chakra` for running JS scripts.
+
+The simplest way to interact with the engine is to pass Base64 strings. The make
+target builds a very simple payload with the data.
+
+:::note
+
+The official release includes the `ch` standalone binary. While applications
+should link against the official libraries, the standalone tool is useful for
+verifying functionality.
+
+:::
+
+Complete Example (click to show)
+
+Due to limitations of the standalone binary, this demo will encode a test file
+as a Base64 string and directly add it to an amalgamated script.
+
+0) Download and extract the ChakraCore release ZIP. Copy the binary (`bin/ch`)
+ to your project folder.
+
+1) Download the standalone script, shim, and test file:
+
+
+
+2) Bundle the test file and create `payload.js`:
+
+```bash
+node -e "fs.writeFileSync('payload.js', 'var payload = \"' + fs.readFileSync('pres.numbers').toString('base64') + '\";')"
+```
+
+3) Create support scripts:
+
+- `global.js` creates a `global` variable:
+
+```js title="global.js"
+var global = (function(){ return this; }).call(null);
+```
+
+- `chakra.js` will call `XLSX.read` and `XLSX.utils.sheet_to_csv`:
+
+```js title="chakra.js"
+/* sheetjs (C) 2013-present SheetJS -- http://sheetjs.com */
+var wb = XLSX.read(payload, {type:'base64'});
+console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
+```
+
+4) Create the amalgamation `xlsx.chakra.js`:
+
+```bash
+cat global.js xlsx.full.min.js payload.js chakra.js > xlsx.chakra.js
+```
+
+The final script defines `global` before loading the standalone library. Once
+ready, it will read the hardcoded test file and print the contents as CSV.
+
+5) Run the script using the ChakraCore standalone binary:
+
+```
+./ch xlsx.chakra.js
+```
+
+
+
+
+### Nashorn
+
+:::caution
+
+Nashorn shipped with Java 8. It was deprecated in Java 11 and was officially
+removed in JDK 15. New Java applications should use [Rhino](#rhino).
+
+:::
+
+
+Nashorn ships with Java. It includes a command-line tool `jjs` for running JS
+scripts. It is somewhat limited but does offer access to the full Java runtime.
+
+The `load` function in `jjs` can load the minified source directly:
+
+```js
+var global = (function(){ return this; }).call(null);
+load('xlsx.full.min.js');
+```
+
+The Java `nio` API provides the `Files.readAllBytes` method to read a file into
+a byte array. To use in `XLSX.read`, the demo copies the bytes into a plain JS
+array and calls `XLSX.read` with type `"array"`.
+
+Complete Example (click to show)
+
+0) Ensure `jjs` is available on system path
+
+1) Download the standalone script, the shim and the test file:
+
+