init d2.js
This commit is contained in:
parent
13af63a032
commit
8d71e8fff8
21 changed files with 1176 additions and 32 deletions
5
Makefile
5
Makefile
|
|
@ -1,7 +1,7 @@
|
||||||
.POSIX:
|
.POSIX:
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: fmt gen lint build test
|
all: fmt gen js lint build test
|
||||||
|
|
||||||
.PHONY: fmt
|
.PHONY: fmt
|
||||||
fmt:
|
fmt:
|
||||||
|
|
@ -21,3 +21,6 @@ test: fmt
|
||||||
.PHONY: race
|
.PHONY: race
|
||||||
race: fmt
|
race: fmt
|
||||||
prefix "$@" ./ci/test.sh --race ./...
|
prefix "$@" ./ci/test.sh --race ./...
|
||||||
|
.PHONY: js
|
||||||
|
js:
|
||||||
|
cd d2js/js && prefix "$@" ./make.sh
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
# D2 as a Javascript library
|
|
||||||
|
|
||||||
D2 is runnable as a Javascript library, on both the client and server side. This means you
|
|
||||||
can run D2 entirely on the browser.
|
|
||||||
|
|
||||||
This is achieved by a JS wrapper around a WASM file.
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
### NPM
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install @terrastruct/d2
|
|
||||||
```
|
|
||||||
|
|
||||||
### Yarn
|
|
||||||
|
|
||||||
```sh
|
|
||||||
yarn add @terrastruct/d2
|
|
||||||
```
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
```sh
|
|
||||||
GOOS=js GOARCH=wasm go build -ldflags='-s -w' -trimpath -o main.wasm ./d2js
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
todo
|
|
||||||
|
|
@ -151,7 +151,7 @@ func Compile(args []js.Value) (interface{}, error) {
|
||||||
|
|
||||||
renderOpts := &d2svg.RenderOpts{}
|
renderOpts := &d2svg.RenderOpts{}
|
||||||
var fontFamily *d2fonts.FontFamily
|
var fontFamily *d2fonts.FontFamily
|
||||||
if input.Opts != nil && input.Opts.Sketch != nil {
|
if input.Opts != nil && input.Opts.Sketch != nil && *input.Opts.Sketch {
|
||||||
fontFamily = go2.Pointer(d2fonts.HandDrawn)
|
fontFamily = go2.Pointer(d2fonts.HandDrawn)
|
||||||
renderOpts.Sketch = input.Opts.Sketch
|
renderOpts.Sketch = input.Opts.Sketch
|
||||||
}
|
}
|
||||||
|
|
|
||||||
27
d2js/js/.gitignore
vendored
Normal file
27
d2js/js/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
node_modules
|
||||||
|
.npm
|
||||||
|
bun.lockb
|
||||||
|
|
||||||
|
wasm/d2.wasm
|
||||||
|
dist/
|
||||||
|
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
.cache/
|
||||||
8
d2js/js/CHANGELOG.md
Normal file
8
d2js/js/CHANGELOG.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to only the d2.js package will be documented in this file. **Does not
|
||||||
|
include changes to the main d2 project.**
|
||||||
|
|
||||||
|
## [0.1.0] - 2025-01-12
|
||||||
|
|
||||||
|
First public release
|
||||||
20
d2js/js/Makefile
Normal file
20
d2js/js/Makefile
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
.POSIX:
|
||||||
|
.PHONY: all
|
||||||
|
all: fmt build test
|
||||||
|
|
||||||
|
.PHONY: fmt
|
||||||
|
fmt: node_modules
|
||||||
|
prefix "$@" ../../ci/sub/bin/fmt.sh
|
||||||
|
prefix "$@" rm -f yarn.lock
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build: node_modules
|
||||||
|
prefix "$@" ./ci/build.sh
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test: build
|
||||||
|
prefix "$@" bun test:all
|
||||||
|
|
||||||
|
.PHONY: node_modules
|
||||||
|
node_modules:
|
||||||
|
prefix "$@" bun install $${CI:+--frozen-lockfile}
|
||||||
112
d2js/js/README.md
Normal file
112
d2js/js/README.md
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
# D2.js
|
||||||
|
|
||||||
|
[](https://www.npmjs.com/package/@terrastruct/d2)
|
||||||
|
[](https://mozilla.org/MPL/2.0/)
|
||||||
|
|
||||||
|
D2.js is a JavaScript wrapper around D2, the modern diagram scripting language. It enables running D2 directly in browsers and Node environments through WebAssembly.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- 🌐 **Universal** - Works in both browser and Node environments
|
||||||
|
- 🚀 **Modern** - Built with ESM modules, with CJS fallback
|
||||||
|
- 🔄 **Isomorphic** - Same API everywhere
|
||||||
|
- ⚡ **Fast** - Powered by WebAssembly for near-native performance
|
||||||
|
- 📦 **Lightweight** - Minimal wrapper around the core D2 engine
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# npm
|
||||||
|
npm install @terrastruct/d2
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn add @terrastruct/d2
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
pnpm add @terrastruct/d2
|
||||||
|
|
||||||
|
# bun
|
||||||
|
bun add @terrastruct/d2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Browser
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { D2 } from '@terrastruct/d2';
|
||||||
|
|
||||||
|
const d2 = new D2();
|
||||||
|
|
||||||
|
const result = await d2.compile('x -> y');
|
||||||
|
const svg = await d2.render(result.diagram);
|
||||||
|
|
||||||
|
const result = await d2.compile('x -> y', {
|
||||||
|
layout: 'dagre',
|
||||||
|
sketch: true
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Node
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { D2 } from '@terrastruct/d2';
|
||||||
|
|
||||||
|
const d2 = new D2();
|
||||||
|
|
||||||
|
async function createDiagram() {
|
||||||
|
const result = await d2.compile('x -> y');
|
||||||
|
const svg = await d2.render(result.diagram);
|
||||||
|
console.log(svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
createDiagram();
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### `new D2()`
|
||||||
|
Creates a new D2 instance.
|
||||||
|
|
||||||
|
### `compile(input: string, options?: CompileOptions): Promise<CompileResult>`
|
||||||
|
Compiles D2 markup into an intermediate representation.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
- `layout`: Layout engine to use ('dagre' | 'elk') [default: 'dagre']
|
||||||
|
- `sketch`: Enable sketch mode [default: false]
|
||||||
|
|
||||||
|
### `render(diagram: Diagram, options?: RenderOptions): Promise<string>`
|
||||||
|
Renders a compiled diagram to SVG.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
D2.js uses Bun, so install this first.
|
||||||
|
|
||||||
|
### Building from source
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/terrastruct/d2.git
|
||||||
|
cd d2/d2js/js
|
||||||
|
./make.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
If you change the main D2 source code, you should regenerate the WASM file:
|
||||||
|
```bash
|
||||||
|
./make.sh build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running the Development Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Visit `http://localhost:3000` to see the example page.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are welcome!
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the Mozilla Public License Version 2.0.
|
||||||
35
d2js/js/build.js
Normal file
35
d2js/js/build.js
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { build } from "bun";
|
||||||
|
import { copyFile, mkdir } from "node:fs/promises";
|
||||||
|
import { join } from "node:path";
|
||||||
|
|
||||||
|
await mkdir("./dist/esm", { recursive: true });
|
||||||
|
await mkdir("./dist/cjs", { recursive: true });
|
||||||
|
|
||||||
|
const commonConfig = {
|
||||||
|
target: "node",
|
||||||
|
splitting: false,
|
||||||
|
sourcemap: "external",
|
||||||
|
minify: true,
|
||||||
|
naming: {
|
||||||
|
entry: "[dir]/[name].js",
|
||||||
|
chunk: "[name]-[hash].js",
|
||||||
|
asset: "[name]-[hash][ext]",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
async function buildAndCopy(format) {
|
||||||
|
const outdir = `./dist/${format}`;
|
||||||
|
|
||||||
|
await build({
|
||||||
|
...commonConfig,
|
||||||
|
entrypoints: ["./src/index.js", "./src/worker.js", "./src/platform.js"],
|
||||||
|
outdir,
|
||||||
|
format,
|
||||||
|
});
|
||||||
|
|
||||||
|
await copyFile("./wasm/d2.wasm", join(outdir, "d2.wasm"));
|
||||||
|
await copyFile("./wasm/wasm_exec.js", join(outdir, "wasm_exec.js"));
|
||||||
|
}
|
||||||
|
|
||||||
|
await buildAndCopy("esm");
|
||||||
|
await buildAndCopy("cjs");
|
||||||
BIN
d2js/js/bun.lockb
Executable file
BIN
d2js/js/bun.lockb
Executable file
Binary file not shown.
18
d2js/js/ci/build.sh
Executable file
18
d2js/js/ci/build.sh
Executable file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
. "$(dirname "$0")/../../../ci/sub/lib.sh"
|
||||||
|
cd -- "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
cd ../..
|
||||||
|
sh_c "GOOS=js GOARCH=wasm go build -ldflags='-s -w' -trimpath -o main.wasm ./d2js"
|
||||||
|
sh_c "mv main.wasm ./d2js/js/wasm/d2.wasm"
|
||||||
|
|
||||||
|
if [ ! -f ./d2js/js/wasm/d2.wasm ]; then
|
||||||
|
echoerr "Error: d2.wasm is missing"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
stat --printf="Size: %s bytes\n" ./d2js/js/wasm/d2.wasm || ls -lh ./d2js/js/wasm/d2.wasm
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd d2js/js
|
||||||
|
sh_c bun run build
|
||||||
57
d2js/js/dev-server.js
Normal file
57
d2js/js/dev-server.js
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
const MIME_TYPES = {
|
||||||
|
".html": "text/html",
|
||||||
|
".js": "text/javascript",
|
||||||
|
".mjs": "text/javascript",
|
||||||
|
".css": "text/css",
|
||||||
|
".wasm": "application/wasm",
|
||||||
|
".svg": "image/svg+xml",
|
||||||
|
};
|
||||||
|
|
||||||
|
const server = Bun.serve({
|
||||||
|
port: 3000,
|
||||||
|
async fetch(request) {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
let path = url.pathname;
|
||||||
|
|
||||||
|
// Serve index page by default
|
||||||
|
if (path === "/") {
|
||||||
|
path = "/examples/basic.html";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle attempts to access files in src
|
||||||
|
if (path.startsWith("/src/")) {
|
||||||
|
const wasmFile = path.includes("wasm_exec.js") || path.includes("d2.wasm");
|
||||||
|
if (wasmFile) {
|
||||||
|
path = path.replace("/src/", "/wasm/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const filePath = path.slice(1);
|
||||||
|
const file = Bun.file(filePath);
|
||||||
|
const exists = await file.exists();
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
return new Response(`File not found: ${path}`, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get file extension and corresponding MIME type
|
||||||
|
const ext = "." + filePath.split(".").pop();
|
||||||
|
const mimeType = MIME_TYPES[ext] || "application/octet-stream";
|
||||||
|
|
||||||
|
return new Response(file, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": mimeType,
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Cross-Origin-Opener-Policy": "same-origin",
|
||||||
|
"Cross-Origin-Embedder-Policy": "require-corp",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error serving ${path}:`, err);
|
||||||
|
return new Response(`Server error: ${err.message}`, { status: 500 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Server running at http://localhost:3000`);
|
||||||
49
d2js/js/examples/basic.html
Normal file
49
d2js/js/examples/basic.html
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
padding: 20px;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
width: 400px;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
#output {
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
#output svg {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 90vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<textarea id="input">x -> y</textarea>
|
||||||
|
<button onclick="compile()">Compile</button>
|
||||||
|
</div>
|
||||||
|
<div id="output"></div>
|
||||||
|
<script type="module">
|
||||||
|
import { D2 } from "../src/index.js";
|
||||||
|
const d2 = new D2();
|
||||||
|
window.compile = async () => {
|
||||||
|
const input = document.getElementById("input").value;
|
||||||
|
try {
|
||||||
|
const result = await d2.compile(input);
|
||||||
|
const svg = await d2.render(result.diagram);
|
||||||
|
document.getElementById("output").innerHTML = svg;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
document.getElementById("output").textContent = err.message;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
compile();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
23
d2js/js/make.sh
Executable file
23
d2js/js/make.sh
Executable file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
if [ ! -e "$(dirname "$0")/../../ci/sub/.git" ]; then
|
||||||
|
set -x
|
||||||
|
git submodule update --init
|
||||||
|
set +x
|
||||||
|
fi
|
||||||
|
. "$(dirname "$0")/../../ci/sub/lib.sh"
|
||||||
|
PATH="$(cd -- "$(dirname "$0")" && pwd)/../../ci/sub/bin:$PATH"
|
||||||
|
cd -- "$(dirname "$0")"
|
||||||
|
|
||||||
|
if ! command -v bun >/dev/null 2>&1; then
|
||||||
|
if [ -n "${CI-}" ]; then
|
||||||
|
echo "Bun is not installed. Installing Bun..."
|
||||||
|
curl -fsSL https://bun.sh/install | bash
|
||||||
|
export PATH="$HOME/.bun/bin:$PATH"
|
||||||
|
else
|
||||||
|
echoerr "You need bun to build d2.js: curl -fsSL https://bun.sh/install | bash"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_make "$@"
|
||||||
53
d2js/js/package.json
Normal file
53
d2js/js/package.json
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
{
|
||||||
|
"name": "@terrastruct/d2",
|
||||||
|
"author": "Terrastruct, Inc.",
|
||||||
|
"description": "D2.js is a wrapper around the WASM build of D2, the modern text-to-diagram language.",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/terrastruct/d2.git",
|
||||||
|
"directory": "d2js/js"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/terrastruct/d2/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/terrastruct/d2/tree/master/d2js/js#readme",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"main": "./dist/cjs/index.js",
|
||||||
|
"module": "./dist/esm/index.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./dist/esm/index.js",
|
||||||
|
"require": "./dist/cjs/index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "bun build.js",
|
||||||
|
"test": "bun test test/unit",
|
||||||
|
"test:integration": "bun run build && bun test test/integration",
|
||||||
|
"test:all": "bun run test && bun run test:integration",
|
||||||
|
"dev": "bun --watch dev-server.js",
|
||||||
|
"prepublishOnly": "./make.sh"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"d2",
|
||||||
|
"d2lang",
|
||||||
|
"diagram",
|
||||||
|
"wasm",
|
||||||
|
"text-to-diagram",
|
||||||
|
"go"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"bun": ">=1.0.0"
|
||||||
|
},
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"bun": "latest"
|
||||||
|
}
|
||||||
|
}
|
||||||
107
d2js/js/src/index.js
Normal file
107
d2js/js/src/index.js
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
import { createWorker, loadFile } from "./platform.js";
|
||||||
|
|
||||||
|
const DEFAULT_OPTIONS = {
|
||||||
|
layout: "dagre",
|
||||||
|
sketch: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class D2 {
|
||||||
|
constructor() {
|
||||||
|
this.ready = this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupMessageHandler() {
|
||||||
|
const isNode = typeof window === "undefined";
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (isNode) {
|
||||||
|
this.worker.on("message", (data) => {
|
||||||
|
if (data.type === "ready") resolve();
|
||||||
|
if (data.type === "error") reject(new Error(data.error));
|
||||||
|
if (data.type === "result" && this.currentResolve) {
|
||||||
|
this.currentResolve(data.data);
|
||||||
|
}
|
||||||
|
if (data.type === "error" && this.currentReject) {
|
||||||
|
this.currentReject(new Error(data.error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.worker.onmessage = (e) => {
|
||||||
|
if (e.data.type === "ready") resolve();
|
||||||
|
if (e.data.type === "error") reject(new Error(e.data.error));
|
||||||
|
if (e.data.type === "result" && this.currentResolve) {
|
||||||
|
this.currentResolve(e.data.data);
|
||||||
|
}
|
||||||
|
if (e.data.type === "error" && this.currentReject) {
|
||||||
|
this.currentReject(new Error(e.data.error));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
this.worker = await createWorker();
|
||||||
|
|
||||||
|
const wasmExecContent = await loadFile("./wasm_exec.js");
|
||||||
|
const wasmBinary = await loadFile("./d2.wasm");
|
||||||
|
|
||||||
|
const isNode = typeof window === "undefined";
|
||||||
|
const messageHandler = this.setupMessageHandler();
|
||||||
|
|
||||||
|
if (isNode) {
|
||||||
|
this.worker.on("error", (error) => {
|
||||||
|
console.error("Worker encountered an error:", error.message || error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.worker.onerror = (error) => {
|
||||||
|
console.error("Worker encountered an error:", error.message || error);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.worker.postMessage({
|
||||||
|
type: "init",
|
||||||
|
data: {
|
||||||
|
wasm: wasmBinary,
|
||||||
|
wasmExecContent: isNode ? wasmExecContent.toString() : null,
|
||||||
|
wasmExecUrl: isNode
|
||||||
|
? null
|
||||||
|
: URL.createObjectURL(
|
||||||
|
new Blob([wasmExecContent], { type: "application/javascript" })
|
||||||
|
),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return messageHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendMessage(type, data) {
|
||||||
|
await this.ready;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.currentResolve = resolve;
|
||||||
|
this.currentReject = reject;
|
||||||
|
this.worker.postMessage({ type, data });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async compile(input, options = {}) {
|
||||||
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
||||||
|
const request =
|
||||||
|
typeof input === "string"
|
||||||
|
? { fs: { index: input }, options: opts }
|
||||||
|
: { ...input, options: { ...opts, ...input.options } };
|
||||||
|
return this.sendMessage("compile", request);
|
||||||
|
}
|
||||||
|
|
||||||
|
async render(diagram, options = {}) {
|
||||||
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
||||||
|
return this.sendMessage("render", { diagram, options: opts });
|
||||||
|
}
|
||||||
|
|
||||||
|
async encode(script) {
|
||||||
|
return this.sendMessage("encode", script);
|
||||||
|
}
|
||||||
|
|
||||||
|
async decode(encoded) {
|
||||||
|
return this.sendMessage("decode", encoded);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
d2js/js/src/platform.js
Normal file
37
d2js/js/src/platform.js
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
export async function loadFile(path) {
|
||||||
|
if (typeof window === "undefined") {
|
||||||
|
const fs = await import("node:fs/promises");
|
||||||
|
const { fileURLToPath } = await import("node:url");
|
||||||
|
const { join, dirname } = await import("node:path");
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await fs.readFile(join(__dirname, path));
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code === "ENOENT") {
|
||||||
|
return await fs.readFile(join(__dirname, "../wasm", path.replace("./", "")));
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await fetch(new URL(path, import.meta.url));
|
||||||
|
return await response.arrayBuffer();
|
||||||
|
} catch {
|
||||||
|
const response = await fetch(
|
||||||
|
new URL(`../wasm/${path.replace("./", "")}`, import.meta.url)
|
||||||
|
);
|
||||||
|
return await response.arrayBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createWorker() {
|
||||||
|
if (typeof window === "undefined") {
|
||||||
|
const { Worker } = await import("node:worker_threads");
|
||||||
|
const { fileURLToPath } = await import("node:url");
|
||||||
|
const { join, dirname } = await import("node:path");
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
return new Worker(join(__dirname, "worker.js"));
|
||||||
|
}
|
||||||
|
return new window.Worker(new URL("./worker.js", import.meta.url));
|
||||||
|
}
|
||||||
94
d2js/js/src/worker.js
Normal file
94
d2js/js/src/worker.js
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
const isNode = typeof process !== "undefined" && process.release?.name === "node";
|
||||||
|
let currentPort;
|
||||||
|
let wasm;
|
||||||
|
let d2;
|
||||||
|
|
||||||
|
async function initWasm(wasmBinary) {
|
||||||
|
const go = new Go();
|
||||||
|
const result = await WebAssembly.instantiate(wasmBinary, go.importObject);
|
||||||
|
go.run(result.instance);
|
||||||
|
return isNode ? global.d2 : self.d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupMessageHandler(port) {
|
||||||
|
currentPort = port;
|
||||||
|
if (isNode) {
|
||||||
|
port.on("message", handleMessage);
|
||||||
|
} else {
|
||||||
|
port.onmessage = (e) => handleMessage(e.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleMessage(e) {
|
||||||
|
const { type, data } = e;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "init":
|
||||||
|
try {
|
||||||
|
if (isNode) {
|
||||||
|
eval(data.wasmExecContent);
|
||||||
|
} else {
|
||||||
|
importScripts(data.wasmExecUrl);
|
||||||
|
}
|
||||||
|
d2 = await initWasm(data.wasm);
|
||||||
|
currentPort.postMessage({ type: "ready" });
|
||||||
|
} catch (err) {
|
||||||
|
currentPort.postMessage({
|
||||||
|
type: "error",
|
||||||
|
error: err.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "compile":
|
||||||
|
try {
|
||||||
|
const result = await d2.compile(JSON.stringify(data));
|
||||||
|
const response = JSON.parse(result);
|
||||||
|
if (response.error) {
|
||||||
|
throw new Error(response.error.message);
|
||||||
|
}
|
||||||
|
currentPort.postMessage({
|
||||||
|
type: "result",
|
||||||
|
data: response.data,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
currentPort.postMessage({
|
||||||
|
type: "error",
|
||||||
|
error: err.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "render":
|
||||||
|
try {
|
||||||
|
const result = await d2.render(JSON.stringify(data));
|
||||||
|
const response = JSON.parse(result);
|
||||||
|
if (response.error) {
|
||||||
|
throw new Error(response.error.message);
|
||||||
|
}
|
||||||
|
currentPort.postMessage({
|
||||||
|
type: "result",
|
||||||
|
data: atob(response.data),
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
currentPort.postMessage({
|
||||||
|
type: "error",
|
||||||
|
error: err.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
if (isNode) {
|
||||||
|
const { parentPort } = await import("node:worker_threads");
|
||||||
|
setupMessageHandler(parentPort);
|
||||||
|
} else {
|
||||||
|
setupMessageHandler(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init().catch((err) => {
|
||||||
|
console.error("Initialization error:", err);
|
||||||
|
});
|
||||||
11
d2js/js/test/integration/cjs.test.js
Normal file
11
d2js/js/test/integration/cjs.test.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { expect, test, describe } from "bun:test";
|
||||||
|
|
||||||
|
describe("D2 CJS Integration", () => {
|
||||||
|
test("can require and use CJS build", async () => {
|
||||||
|
const { D2 } = require("../../dist/cjs/index.js");
|
||||||
|
const d2 = new D2();
|
||||||
|
const result = await d2.compile("x -> y");
|
||||||
|
expect(result.diagram).toBeDefined();
|
||||||
|
await d2.worker.terminate();
|
||||||
|
}, 20000);
|
||||||
|
});
|
||||||
11
d2js/js/test/integration/esm.test.js
Normal file
11
d2js/js/test/integration/esm.test.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { expect, test, describe } from "bun:test";
|
||||||
|
import { D2 } from "../../dist/esm/index.js";
|
||||||
|
|
||||||
|
describe("D2 ESM Integration", () => {
|
||||||
|
test("can import and use ESM build", async () => {
|
||||||
|
const d2 = new D2();
|
||||||
|
const result = await d2.compile("x -> y");
|
||||||
|
expect(result.diagram).toBeDefined();
|
||||||
|
await d2.worker.terminate();
|
||||||
|
}, 20000);
|
||||||
|
});
|
||||||
32
d2js/js/test/unit/basic.test.js
Normal file
32
d2js/js/test/unit/basic.test.js
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { expect, test, describe } from "bun:test";
|
||||||
|
import { D2 } from "../../src/index.js";
|
||||||
|
|
||||||
|
describe("D2 Unit Tests", () => {
|
||||||
|
test("basic compilation works", async () => {
|
||||||
|
const d2 = new D2();
|
||||||
|
const result = await d2.compile("x -> y");
|
||||||
|
expect(result.diagram).toBeDefined();
|
||||||
|
await d2.worker.terminate();
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
test("render works", async () => {
|
||||||
|
const d2 = new D2();
|
||||||
|
const result = await d2.compile("x -> y");
|
||||||
|
const svg = await d2.render(result.diagram);
|
||||||
|
expect(svg).toContain("<svg");
|
||||||
|
expect(svg).toContain("</svg>");
|
||||||
|
await d2.worker.terminate();
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
test("handles syntax errors correctly", async () => {
|
||||||
|
const d2 = new D2();
|
||||||
|
try {
|
||||||
|
await d2.compile("invalid -> -> syntax");
|
||||||
|
throw new Error("Should have thrown syntax error");
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).toBeDefined();
|
||||||
|
expect(err.message).not.toContain("Should have thrown syntax error");
|
||||||
|
}
|
||||||
|
await d2.worker.terminate();
|
||||||
|
}, 20000);
|
||||||
|
});
|
||||||
477
d2js/js/wasm/wasm_exec.js
Normal file
477
d2js/js/wasm/wasm_exec.js
Normal file
|
|
@ -0,0 +1,477 @@
|
||||||
|
"use strict";
|
||||||
|
(() => {
|
||||||
|
const o = () => {
|
||||||
|
const h = new Error("not implemented");
|
||||||
|
return (h.code = "ENOSYS"), h;
|
||||||
|
};
|
||||||
|
if (!globalThis.fs) {
|
||||||
|
let h = "";
|
||||||
|
globalThis.fs = {
|
||||||
|
constants: {
|
||||||
|
O_WRONLY: -1,
|
||||||
|
O_RDWR: -1,
|
||||||
|
O_CREAT: -1,
|
||||||
|
O_TRUNC: -1,
|
||||||
|
O_APPEND: -1,
|
||||||
|
O_EXCL: -1,
|
||||||
|
},
|
||||||
|
writeSync(n, s) {
|
||||||
|
h += y.decode(s);
|
||||||
|
const i = h.lastIndexOf(`
|
||||||
|
`);
|
||||||
|
return (
|
||||||
|
i != -1 && (console.log(h.substring(0, i)), (h = h.substring(i + 1))), s.length
|
||||||
|
);
|
||||||
|
},
|
||||||
|
write(n, s, i, r, f, u) {
|
||||||
|
if (i !== 0 || r !== s.length || f !== null) {
|
||||||
|
u(o());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const d = this.writeSync(n, s);
|
||||||
|
u(null, d);
|
||||||
|
},
|
||||||
|
chmod(n, s, i) {
|
||||||
|
i(o());
|
||||||
|
},
|
||||||
|
chown(n, s, i, r) {
|
||||||
|
r(o());
|
||||||
|
},
|
||||||
|
close(n, s) {
|
||||||
|
s(o());
|
||||||
|
},
|
||||||
|
fchmod(n, s, i) {
|
||||||
|
i(o());
|
||||||
|
},
|
||||||
|
fchown(n, s, i, r) {
|
||||||
|
r(o());
|
||||||
|
},
|
||||||
|
fstat(n, s) {
|
||||||
|
s(o());
|
||||||
|
},
|
||||||
|
fsync(n, s) {
|
||||||
|
s(null);
|
||||||
|
},
|
||||||
|
ftruncate(n, s, i) {
|
||||||
|
i(o());
|
||||||
|
},
|
||||||
|
lchown(n, s, i, r) {
|
||||||
|
r(o());
|
||||||
|
},
|
||||||
|
link(n, s, i) {
|
||||||
|
i(o());
|
||||||
|
},
|
||||||
|
lstat(n, s) {
|
||||||
|
s(o());
|
||||||
|
},
|
||||||
|
mkdir(n, s, i) {
|
||||||
|
i(o());
|
||||||
|
},
|
||||||
|
open(n, s, i, r) {
|
||||||
|
r(o());
|
||||||
|
},
|
||||||
|
read(n, s, i, r, f, u) {
|
||||||
|
u(o());
|
||||||
|
},
|
||||||
|
readdir(n, s) {
|
||||||
|
s(o());
|
||||||
|
},
|
||||||
|
readlink(n, s) {
|
||||||
|
s(o());
|
||||||
|
},
|
||||||
|
rename(n, s, i) {
|
||||||
|
i(o());
|
||||||
|
},
|
||||||
|
rmdir(n, s) {
|
||||||
|
s(o());
|
||||||
|
},
|
||||||
|
stat(n, s) {
|
||||||
|
s(o());
|
||||||
|
},
|
||||||
|
symlink(n, s, i) {
|
||||||
|
i(o());
|
||||||
|
},
|
||||||
|
truncate(n, s, i) {
|
||||||
|
i(o());
|
||||||
|
},
|
||||||
|
unlink(n, s) {
|
||||||
|
s(o());
|
||||||
|
},
|
||||||
|
utimes(n, s, i, r) {
|
||||||
|
r(o());
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(globalThis.process ||
|
||||||
|
(globalThis.process = {
|
||||||
|
getuid() {
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
getgid() {
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
geteuid() {
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
getegid() {
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
getgroups() {
|
||||||
|
throw o();
|
||||||
|
},
|
||||||
|
pid: -1,
|
||||||
|
ppid: -1,
|
||||||
|
umask() {
|
||||||
|
throw o();
|
||||||
|
},
|
||||||
|
cwd() {
|
||||||
|
throw o();
|
||||||
|
},
|
||||||
|
chdir() {
|
||||||
|
throw o();
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
!globalThis.crypto)
|
||||||
|
)
|
||||||
|
throw new Error(
|
||||||
|
"globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)"
|
||||||
|
);
|
||||||
|
if (!globalThis.performance)
|
||||||
|
throw new Error(
|
||||||
|
"globalThis.performance is not available, polyfill required (performance.now only)"
|
||||||
|
);
|
||||||
|
if (!globalThis.TextEncoder)
|
||||||
|
throw new Error("globalThis.TextEncoder is not available, polyfill required");
|
||||||
|
if (!globalThis.TextDecoder)
|
||||||
|
throw new Error("globalThis.TextDecoder is not available, polyfill required");
|
||||||
|
const g = new TextEncoder("utf-8"),
|
||||||
|
y = new TextDecoder("utf-8");
|
||||||
|
globalThis.Go = class {
|
||||||
|
constructor() {
|
||||||
|
(this.argv = ["js"]),
|
||||||
|
(this.env = {}),
|
||||||
|
(this.exit = (t) => {
|
||||||
|
t !== 0 && console.warn("exit code:", t);
|
||||||
|
}),
|
||||||
|
(this._exitPromise = new Promise((t) => {
|
||||||
|
this._resolveExitPromise = t;
|
||||||
|
})),
|
||||||
|
(this._pendingEvent = null),
|
||||||
|
(this._scheduledTimeouts = new Map()),
|
||||||
|
(this._nextCallbackTimeoutID = 1);
|
||||||
|
const h = (t, e) => {
|
||||||
|
this.mem.setUint32(t + 0, e, !0),
|
||||||
|
this.mem.setUint32(t + 4, Math.floor(e / 4294967296), !0);
|
||||||
|
},
|
||||||
|
n = (t, e) => {
|
||||||
|
this.mem.setUint32(t + 0, e, !0);
|
||||||
|
},
|
||||||
|
s = (t) => {
|
||||||
|
const e = this.mem.getUint32(t + 0, !0),
|
||||||
|
l = this.mem.getInt32(t + 4, !0);
|
||||||
|
return e + l * 4294967296;
|
||||||
|
},
|
||||||
|
i = (t) => {
|
||||||
|
const e = this.mem.getFloat64(t, !0);
|
||||||
|
if (e === 0) return;
|
||||||
|
if (!isNaN(e)) return e;
|
||||||
|
const l = this.mem.getUint32(t, !0);
|
||||||
|
return this._values[l];
|
||||||
|
},
|
||||||
|
r = (t, e) => {
|
||||||
|
if (typeof e == "number" && e !== 0) {
|
||||||
|
if (isNaN(e)) {
|
||||||
|
this.mem.setUint32(t + 4, 2146959360, !0), this.mem.setUint32(t, 0, !0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.mem.setFloat64(t, e, !0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e === void 0) {
|
||||||
|
this.mem.setFloat64(t, 0, !0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let a = this._ids.get(e);
|
||||||
|
a === void 0 &&
|
||||||
|
((a = this._idPool.pop()),
|
||||||
|
a === void 0 && (a = this._values.length),
|
||||||
|
(this._values[a] = e),
|
||||||
|
(this._goRefCounts[a] = 0),
|
||||||
|
this._ids.set(e, a)),
|
||||||
|
this._goRefCounts[a]++;
|
||||||
|
let c = 0;
|
||||||
|
switch (typeof e) {
|
||||||
|
case "object":
|
||||||
|
e !== null && (c = 1);
|
||||||
|
break;
|
||||||
|
case "string":
|
||||||
|
c = 2;
|
||||||
|
break;
|
||||||
|
case "symbol":
|
||||||
|
c = 3;
|
||||||
|
break;
|
||||||
|
case "function":
|
||||||
|
c = 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.mem.setUint32(t + 4, 2146959360 | c, !0), this.mem.setUint32(t, a, !0);
|
||||||
|
},
|
||||||
|
f = (t) => {
|
||||||
|
const e = s(t + 0),
|
||||||
|
l = s(t + 8);
|
||||||
|
return new Uint8Array(this._inst.exports.mem.buffer, e, l);
|
||||||
|
},
|
||||||
|
u = (t) => {
|
||||||
|
const e = s(t + 0),
|
||||||
|
l = s(t + 8),
|
||||||
|
a = new Array(l);
|
||||||
|
for (let c = 0; c < l; c++) a[c] = i(e + c * 8);
|
||||||
|
return a;
|
||||||
|
},
|
||||||
|
d = (t) => {
|
||||||
|
const e = s(t + 0),
|
||||||
|
l = s(t + 8);
|
||||||
|
return y.decode(new DataView(this._inst.exports.mem.buffer, e, l));
|
||||||
|
},
|
||||||
|
m = Date.now() - performance.now();
|
||||||
|
this.importObject = {
|
||||||
|
_gotest: { add: (t, e) => t + e },
|
||||||
|
gojs: {
|
||||||
|
"runtime.wasmExit": (t) => {
|
||||||
|
t >>>= 0;
|
||||||
|
const e = this.mem.getInt32(t + 8, !0);
|
||||||
|
(this.exited = !0),
|
||||||
|
delete this._inst,
|
||||||
|
delete this._values,
|
||||||
|
delete this._goRefCounts,
|
||||||
|
delete this._ids,
|
||||||
|
delete this._idPool,
|
||||||
|
this.exit(e);
|
||||||
|
},
|
||||||
|
"runtime.wasmWrite": (t) => {
|
||||||
|
t >>>= 0;
|
||||||
|
const e = s(t + 8),
|
||||||
|
l = s(t + 16),
|
||||||
|
a = this.mem.getInt32(t + 24, !0);
|
||||||
|
fs.writeSync(e, new Uint8Array(this._inst.exports.mem.buffer, l, a));
|
||||||
|
},
|
||||||
|
"runtime.resetMemoryDataView": (t) => {
|
||||||
|
(t >>>= 0), (this.mem = new DataView(this._inst.exports.mem.buffer));
|
||||||
|
},
|
||||||
|
"runtime.nanotime1": (t) => {
|
||||||
|
(t >>>= 0), h(t + 8, (m + performance.now()) * 1e6);
|
||||||
|
},
|
||||||
|
"runtime.walltime": (t) => {
|
||||||
|
t >>>= 0;
|
||||||
|
const e = new Date().getTime();
|
||||||
|
h(t + 8, e / 1e3), this.mem.setInt32(t + 16, (e % 1e3) * 1e6, !0);
|
||||||
|
},
|
||||||
|
"runtime.scheduleTimeoutEvent": (t) => {
|
||||||
|
t >>>= 0;
|
||||||
|
const e = this._nextCallbackTimeoutID;
|
||||||
|
this._nextCallbackTimeoutID++,
|
||||||
|
this._scheduledTimeouts.set(
|
||||||
|
e,
|
||||||
|
setTimeout(() => {
|
||||||
|
for (this._resume(); this._scheduledTimeouts.has(e); )
|
||||||
|
console.warn("scheduleTimeoutEvent: missed timeout event"),
|
||||||
|
this._resume();
|
||||||
|
}, s(t + 8))
|
||||||
|
),
|
||||||
|
this.mem.setInt32(t + 16, e, !0);
|
||||||
|
},
|
||||||
|
"runtime.clearTimeoutEvent": (t) => {
|
||||||
|
t >>>= 0;
|
||||||
|
const e = this.mem.getInt32(t + 8, !0);
|
||||||
|
clearTimeout(this._scheduledTimeouts.get(e)),
|
||||||
|
this._scheduledTimeouts.delete(e);
|
||||||
|
},
|
||||||
|
"runtime.getRandomData": (t) => {
|
||||||
|
(t >>>= 0), crypto.getRandomValues(f(t + 8));
|
||||||
|
},
|
||||||
|
"syscall/js.finalizeRef": (t) => {
|
||||||
|
t >>>= 0;
|
||||||
|
const e = this.mem.getUint32(t + 8, !0);
|
||||||
|
if ((this._goRefCounts[e]--, this._goRefCounts[e] === 0)) {
|
||||||
|
const l = this._values[e];
|
||||||
|
(this._values[e] = null), this._ids.delete(l), this._idPool.push(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"syscall/js.stringVal": (t) => {
|
||||||
|
(t >>>= 0), r(t + 24, d(t + 8));
|
||||||
|
},
|
||||||
|
"syscall/js.valueGet": (t) => {
|
||||||
|
t >>>= 0;
|
||||||
|
const e = Reflect.get(i(t + 8), d(t + 16));
|
||||||
|
(t = this._inst.exports.getsp() >>> 0), r(t + 32, e);
|
||||||
|
},
|
||||||
|
"syscall/js.valueSet": (t) => {
|
||||||
|
(t >>>= 0), Reflect.set(i(t + 8), d(t + 16), i(t + 32));
|
||||||
|
},
|
||||||
|
"syscall/js.valueDelete": (t) => {
|
||||||
|
(t >>>= 0), Reflect.deleteProperty(i(t + 8), d(t + 16));
|
||||||
|
},
|
||||||
|
"syscall/js.valueIndex": (t) => {
|
||||||
|
(t >>>= 0), r(t + 24, Reflect.get(i(t + 8), s(t + 16)));
|
||||||
|
},
|
||||||
|
"syscall/js.valueSetIndex": (t) => {
|
||||||
|
(t >>>= 0), Reflect.set(i(t + 8), s(t + 16), i(t + 24));
|
||||||
|
},
|
||||||
|
"syscall/js.valueCall": (t) => {
|
||||||
|
t >>>= 0;
|
||||||
|
try {
|
||||||
|
const e = i(t + 8),
|
||||||
|
l = Reflect.get(e, d(t + 16)),
|
||||||
|
a = u(t + 32),
|
||||||
|
c = Reflect.apply(l, e, a);
|
||||||
|
(t = this._inst.exports.getsp() >>> 0),
|
||||||
|
r(t + 56, c),
|
||||||
|
this.mem.setUint8(t + 64, 1);
|
||||||
|
} catch (e) {
|
||||||
|
(t = this._inst.exports.getsp() >>> 0),
|
||||||
|
r(t + 56, e),
|
||||||
|
this.mem.setUint8(t + 64, 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"syscall/js.valueInvoke": (t) => {
|
||||||
|
t >>>= 0;
|
||||||
|
try {
|
||||||
|
const e = i(t + 8),
|
||||||
|
l = u(t + 16),
|
||||||
|
a = Reflect.apply(e, void 0, l);
|
||||||
|
(t = this._inst.exports.getsp() >>> 0),
|
||||||
|
r(t + 40, a),
|
||||||
|
this.mem.setUint8(t + 48, 1);
|
||||||
|
} catch (e) {
|
||||||
|
(t = this._inst.exports.getsp() >>> 0),
|
||||||
|
r(t + 40, e),
|
||||||
|
this.mem.setUint8(t + 48, 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"syscall/js.valueNew": (t) => {
|
||||||
|
t >>>= 0;
|
||||||
|
try {
|
||||||
|
const e = i(t + 8),
|
||||||
|
l = u(t + 16),
|
||||||
|
a = Reflect.construct(e, l);
|
||||||
|
(t = this._inst.exports.getsp() >>> 0),
|
||||||
|
r(t + 40, a),
|
||||||
|
this.mem.setUint8(t + 48, 1);
|
||||||
|
} catch (e) {
|
||||||
|
(t = this._inst.exports.getsp() >>> 0),
|
||||||
|
r(t + 40, e),
|
||||||
|
this.mem.setUint8(t + 48, 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"syscall/js.valueLength": (t) => {
|
||||||
|
(t >>>= 0), h(t + 16, parseInt(i(t + 8).length));
|
||||||
|
},
|
||||||
|
"syscall/js.valuePrepareString": (t) => {
|
||||||
|
t >>>= 0;
|
||||||
|
const e = g.encode(String(i(t + 8)));
|
||||||
|
r(t + 16, e), h(t + 24, e.length);
|
||||||
|
},
|
||||||
|
"syscall/js.valueLoadString": (t) => {
|
||||||
|
t >>>= 0;
|
||||||
|
const e = i(t + 8);
|
||||||
|
f(t + 16).set(e);
|
||||||
|
},
|
||||||
|
"syscall/js.valueInstanceOf": (t) => {
|
||||||
|
(t >>>= 0), this.mem.setUint8(t + 24, i(t + 8) instanceof i(t + 16) ? 1 : 0);
|
||||||
|
},
|
||||||
|
"syscall/js.copyBytesToGo": (t) => {
|
||||||
|
t >>>= 0;
|
||||||
|
const e = f(t + 8),
|
||||||
|
l = i(t + 32);
|
||||||
|
if (!(l instanceof Uint8Array || l instanceof Uint8ClampedArray)) {
|
||||||
|
this.mem.setUint8(t + 48, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const a = l.subarray(0, e.length);
|
||||||
|
e.set(a), h(t + 40, a.length), this.mem.setUint8(t + 48, 1);
|
||||||
|
},
|
||||||
|
"syscall/js.copyBytesToJS": (t) => {
|
||||||
|
t >>>= 0;
|
||||||
|
const e = i(t + 8),
|
||||||
|
l = f(t + 16);
|
||||||
|
if (!(e instanceof Uint8Array || e instanceof Uint8ClampedArray)) {
|
||||||
|
this.mem.setUint8(t + 48, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const a = l.subarray(0, e.length);
|
||||||
|
e.set(a), h(t + 40, a.length), this.mem.setUint8(t + 48, 1);
|
||||||
|
},
|
||||||
|
debug: (t) => {
|
||||||
|
console.log(t);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async run(h) {
|
||||||
|
if (!(h instanceof WebAssembly.Instance))
|
||||||
|
throw new Error("Go.run: WebAssembly.Instance expected");
|
||||||
|
(this._inst = h),
|
||||||
|
(this.mem = new DataView(this._inst.exports.mem.buffer)),
|
||||||
|
(this._values = [NaN, 0, null, !0, !1, globalThis, this]),
|
||||||
|
(this._goRefCounts = new Array(this._values.length).fill(1 / 0)),
|
||||||
|
(this._ids = new Map([
|
||||||
|
[0, 1],
|
||||||
|
[null, 2],
|
||||||
|
[!0, 3],
|
||||||
|
[!1, 4],
|
||||||
|
[globalThis, 5],
|
||||||
|
[this, 6],
|
||||||
|
])),
|
||||||
|
(this._idPool = []),
|
||||||
|
(this.exited = !1);
|
||||||
|
let n = 4096;
|
||||||
|
const s = (m) => {
|
||||||
|
const t = n,
|
||||||
|
e = g.encode(m + "\0");
|
||||||
|
return (
|
||||||
|
new Uint8Array(this.mem.buffer, n, e.length).set(e),
|
||||||
|
(n += e.length),
|
||||||
|
n % 8 !== 0 && (n += 8 - (n % 8)),
|
||||||
|
t
|
||||||
|
);
|
||||||
|
},
|
||||||
|
i = this.argv.length,
|
||||||
|
r = [];
|
||||||
|
this.argv.forEach((m) => {
|
||||||
|
r.push(s(m));
|
||||||
|
}),
|
||||||
|
r.push(0),
|
||||||
|
Object.keys(this.env)
|
||||||
|
.sort()
|
||||||
|
.forEach((m) => {
|
||||||
|
r.push(s(`${m}=${this.env[m]}`));
|
||||||
|
}),
|
||||||
|
r.push(0);
|
||||||
|
const u = n;
|
||||||
|
if (
|
||||||
|
(r.forEach((m) => {
|
||||||
|
this.mem.setUint32(n, m, !0), this.mem.setUint32(n + 4, 0, !0), (n += 8);
|
||||||
|
}),
|
||||||
|
n >= 12288)
|
||||||
|
)
|
||||||
|
throw new Error(
|
||||||
|
"total length of command line and environment variables exceeds limit"
|
||||||
|
);
|
||||||
|
this._inst.exports.run(i, u),
|
||||||
|
this.exited && this._resolveExitPromise(),
|
||||||
|
await this._exitPromise;
|
||||||
|
}
|
||||||
|
_resume() {
|
||||||
|
if (this.exited) throw new Error("Go program has already exited");
|
||||||
|
this._inst.exports.resume(), this.exited && this._resolveExitPromise();
|
||||||
|
}
|
||||||
|
_makeFuncWrapper(h) {
|
||||||
|
const n = this;
|
||||||
|
return function () {
|
||||||
|
const s = { id: h, this: this, args: arguments };
|
||||||
|
return (n._pendingEvent = s), n._resume(), s.result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
Loading…
Reference in a new issue