mirror of
https://github.com/trailbaseio/trailbase.git
synced 2026-01-06 09:50:10 -06:00
Add TypeScript WASM guest runtime and examples.
This commit is contained in:
3
examples/wasm-guest-js/.gitignore
vendored
Normal file
3
examples/wasm-guest-js/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
dist/
|
||||
node_modules/
|
||||
traildepot/
|
||||
22
examples/wasm-guest-js/Makefile
Normal file
22
examples/wasm-guest-js/Makefile
Normal file
@@ -0,0 +1,22 @@
|
||||
TRAILBIN ?= RUST_BACKTRACE=1 cargo run --
|
||||
TRAILDEPOT := traildepot
|
||||
|
||||
APPDIR := .
|
||||
DISTDIR := ${APPDIR}/dist
|
||||
ADDRESS := 127.0.0.1:4000
|
||||
|
||||
run: guest
|
||||
${TRAILBIN} --data-dir=${TRAILDEPOT} run --address=${ADDRESS}
|
||||
|
||||
guest: ${TRAILDEPOT}/wasm/component.wasm
|
||||
|
||||
${TRAILDEPOT}/wasm/component.wasm: ${DISTDIR}/component.wasm
|
||||
mkdir -p ${TRAILDEPOT}/wasm && cp $< $@
|
||||
|
||||
${DISTDIR}/component.wasm: src/*.js
|
||||
pnpm i && pnpm build
|
||||
|
||||
clean:
|
||||
rm -rf dist traildepot
|
||||
|
||||
.PHONY: guest run clean
|
||||
20
examples/wasm-guest-js/eslint.config.mjs
Normal file
20
examples/wasm-guest-js/eslint.config.mjs
Normal file
@@ -0,0 +1,20 @@
|
||||
import pluginJs from "@eslint/js";
|
||||
import globals from "globals";
|
||||
|
||||
export default [
|
||||
pluginJs.configs.recommended,
|
||||
{
|
||||
ignores: ["dist/", "node_modules/"],
|
||||
},
|
||||
{
|
||||
files: ["src/**/*.{js,mjs,cjs,mts,jsx}"],
|
||||
rules: {
|
||||
"no-var": "off",
|
||||
},
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
}
|
||||
}
|
||||
},
|
||||
];
|
||||
24
examples/wasm-guest-js/package.json
Normal file
24
examples/wasm-guest-js/package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "example-wasm-guest-js",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"build:wasm": "jco componentize dist/index.mjs -w ../../guests/typescript/wit -o dist/component.wasm",
|
||||
"build:wasm:aot": "jco componentize dist/index.mjs -w ../../guests/typescript/wit --aot -o dist/component.wasm",
|
||||
"build": "vite build && npm run build:wasm",
|
||||
"check": "eslint",
|
||||
"format": "prettier -w src"
|
||||
},
|
||||
"dependencies": {
|
||||
"trailbase-wasm": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@bytecodealliance/componentize-js": "^0.18.4",
|
||||
"@bytecodealliance/jco": "^1.13.3",
|
||||
"@eslint/js": "^9.33.0",
|
||||
"eslint": "^9.33.0",
|
||||
"globals": "^16.3.0",
|
||||
"prettier": "^3.6.2",
|
||||
"vite": "^7.1.2"
|
||||
}
|
||||
}
|
||||
3
examples/wasm-guest-js/src/component.js
Normal file
3
examples/wasm-guest-js/src/component.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import e from "./index";
|
||||
|
||||
export const { initEndpoint, incomingHandler } = e;
|
||||
71
examples/wasm-guest-js/src/index.js
Normal file
71
examples/wasm-guest-js/src/index.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import { defineConfig, addPeriodicCallback } from "trailbase-wasm";
|
||||
import { HttpHandler, buildJsonResponse } from "trailbase-wasm/http";
|
||||
import { JobHandler } from "trailbase-wasm/job";
|
||||
import { query } from "trailbase-wasm/db";
|
||||
|
||||
export default defineConfig({
|
||||
httpHandlers: [
|
||||
HttpHandler.get("/fibonacci", (req) => {
|
||||
const n = req.getQueryParam("n");
|
||||
return fibonacci(n ? parseInt(n) : 40).toString();
|
||||
}),
|
||||
HttpHandler.get("/json", jsonHandler),
|
||||
HttpHandler.post("/json", jsonHandler),
|
||||
HttpHandler.get("/a", (req) => {
|
||||
const n = req.getQueryParam("n");
|
||||
return "a".repeat(n ? parseInt(n) : 5000);
|
||||
}),
|
||||
HttpHandler.get("/interval", () => {
|
||||
let i = 0;
|
||||
addPeriodicCallback(250, (cancel) => {
|
||||
console.log(`callback #${i}`);
|
||||
i += 1;
|
||||
if (i >= 10) {
|
||||
cancel();
|
||||
}
|
||||
});
|
||||
}),
|
||||
HttpHandler.get("/sleep", async (req) => {
|
||||
const param = req.getQueryParam("ms");
|
||||
const ms = param ? parseInt(param) : 500;
|
||||
await sleep(ms);
|
||||
return `slept: ${ms}ms`;
|
||||
}),
|
||||
HttpHandler.get("/count/{table}/", async (req) => {
|
||||
const table = req.getPathParam("table");
|
||||
if (table) {
|
||||
const rows = await query(`SELECT COUNT(*) FROM ${table}`, []);
|
||||
return `Got ${rows[0][0]} rows\n`;
|
||||
}
|
||||
}),
|
||||
],
|
||||
jobHandlers: [JobHandler.minutely("myjob", () => console.log("Hello Job!"))],
|
||||
});
|
||||
|
||||
function jsonHandler(req) {
|
||||
return buildJsonResponse(
|
||||
req.json() ?? {
|
||||
int: 5,
|
||||
real: 4.2,
|
||||
msg: "foo",
|
||||
obj: {
|
||||
nested: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function fibonacci(num) {
|
||||
switch (num) {
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return 1;
|
||||
default:
|
||||
return fibonacci(num - 1) + fibonacci(num - 2);
|
||||
}
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
16
examples/wasm-guest-js/vite.config.ts
Normal file
16
examples/wasm-guest-js/vite.config.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { defineConfig } from "vite";
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
outDir: "./dist",
|
||||
minify: false,
|
||||
lib: {
|
||||
entry: "./src/component.js",
|
||||
fileName: "index",
|
||||
formats: ["es"],
|
||||
},
|
||||
rollupOptions: {
|
||||
external: (source) => source.startsWith("wasi:") || source.startsWith("trailbase:"),
|
||||
},
|
||||
},
|
||||
})
|
||||
3
examples/wasm-guest-ts/.gitignore
vendored
Normal file
3
examples/wasm-guest-ts/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
dist/
|
||||
node_modules/
|
||||
traildepot/
|
||||
22
examples/wasm-guest-ts/Makefile
Normal file
22
examples/wasm-guest-ts/Makefile
Normal file
@@ -0,0 +1,22 @@
|
||||
TRAILBIN ?= RUST_BACKTRACE=1 cargo run --
|
||||
TRAILDEPOT := traildepot
|
||||
|
||||
APPDIR := .
|
||||
DISTDIR := ${APPDIR}/dist
|
||||
ADDRESS := 127.0.0.1:4000
|
||||
|
||||
run: guest
|
||||
${TRAILBIN} --data-dir=${TRAILDEPOT} run --address=${ADDRESS}
|
||||
|
||||
guest: ${TRAILDEPOT}/wasm/component.wasm
|
||||
|
||||
${TRAILDEPOT}/wasm/component.wasm: ${DISTDIR}/component.wasm
|
||||
mkdir -p ${TRAILDEPOT}/wasm && cp $< $@
|
||||
|
||||
${DISTDIR}/component.wasm: src/*.ts
|
||||
pnpm i && pnpm build
|
||||
|
||||
clean:
|
||||
rm -rf dist traildepot
|
||||
|
||||
.PHONY: guest run clean
|
||||
30
examples/wasm-guest-ts/eslint.config.mjs
Normal file
30
examples/wasm-guest-ts/eslint.config.mjs
Normal file
@@ -0,0 +1,30 @@
|
||||
import pluginJs from "@eslint/js";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
export default [
|
||||
pluginJs.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
ignores: ["dist/", "node_modules/"],
|
||||
},
|
||||
{
|
||||
files: ["src/**/*.{js,mjs,cjs,mts,ts,tsx,jsx}"],
|
||||
rules: {
|
||||
// https://typescript-eslint.io/rules/no-explicit-any/
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/no-wrapper-object-types": "warn",
|
||||
"@typescript-eslint/no-namespace": "off",
|
||||
"no-var": "off",
|
||||
// http://eslint.org/docs/rules/no-unused-vars
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
vars: "all",
|
||||
args: "after-used",
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
30
examples/wasm-guest-ts/package.json
Normal file
30
examples/wasm-guest-ts/package.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "example-wasm-guest-ts",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"generate:types": "jco types ../../guests/typescript/wit/ -o generated/types",
|
||||
"build:tsc": "vite build",
|
||||
"build:wasm": "jco componentize dist/index.mjs -w ../../guests/typescript/wit -o dist/component.wasm",
|
||||
"build:wasm:aot": "jco componentize dist/index.mjs -w ../../guests/typescript/wit --aot -o dist/component.wasm",
|
||||
"build": "npm run build:tsc && npm run build:wasm",
|
||||
"check": "tsc --noEmit --skipLibCheck && eslint",
|
||||
"format": "prettier -w src"
|
||||
},
|
||||
"dependencies": {
|
||||
"trailbase-wasm": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@bytecodealliance/componentize-js": "^0.18.4",
|
||||
"@bytecodealliance/jco": "^1.13.3",
|
||||
"@eslint/js": "^9.33.0",
|
||||
"@types/node": "^24.3.0",
|
||||
"eslint": "^9.33.0",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript-eslint": "^8.39.1",
|
||||
"vite": "^7.1.2",
|
||||
"vitest": "^3.2.4"
|
||||
}
|
||||
}
|
||||
3
examples/wasm-guest-ts/src/component.ts
Normal file
3
examples/wasm-guest-ts/src/component.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import e from "./index";
|
||||
|
||||
export const { initEndpoint, incomingHandler } = e;
|
||||
71
examples/wasm-guest-ts/src/index.ts
Normal file
71
examples/wasm-guest-ts/src/index.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { defineConfig, addPeriodicCallback } from "trailbase-wasm";
|
||||
import { HttpHandler, Request, buildJsonResponse } from "trailbase-wasm/http";
|
||||
import { JobHandler } from "trailbase-wasm/job";
|
||||
import { query } from "trailbase-wasm/db";
|
||||
|
||||
export default defineConfig({
|
||||
httpHandlers: [
|
||||
HttpHandler.get("/fibonacci", (req: Request) => {
|
||||
const n = req.getQueryParam("n");
|
||||
return fibonacci(n ? parseInt(n) : 40).toString();
|
||||
}),
|
||||
HttpHandler.get("/json", jsonHandler),
|
||||
HttpHandler.post("/json", jsonHandler),
|
||||
HttpHandler.get("/a", (req: Request) => {
|
||||
const n = req.getQueryParam("n");
|
||||
return "a".repeat(n ? parseInt(n) : 5000);
|
||||
}),
|
||||
HttpHandler.get("/interval", () => {
|
||||
let i = 0;
|
||||
addPeriodicCallback(250, (cancel) => {
|
||||
console.log(`callback #${i}`);
|
||||
i += 1;
|
||||
if (i >= 10) {
|
||||
cancel();
|
||||
}
|
||||
});
|
||||
}),
|
||||
HttpHandler.get("/sleep", async (req: Request) => {
|
||||
const param = req.getQueryParam("ms");
|
||||
const ms: number = param ? parseInt(param) : 500;
|
||||
await sleep(ms);
|
||||
return `slept: ${ms}ms`;
|
||||
}),
|
||||
HttpHandler.get("/count/{table}/", async (req: Request) => {
|
||||
const table = req.getPathParam("table");
|
||||
if (table) {
|
||||
const rows = await query(`SELECT COUNT(*) FROM ${table}`, []);
|
||||
return `Got ${rows[0][0]} rows\n`;
|
||||
}
|
||||
}),
|
||||
],
|
||||
jobHandlers: [JobHandler.minutely("myjob", () => console.log("Hello Job!"))],
|
||||
});
|
||||
|
||||
function jsonHandler(req: Request) {
|
||||
return buildJsonResponse(
|
||||
req.json() ?? {
|
||||
int: 5,
|
||||
real: 4.2,
|
||||
msg: "foo",
|
||||
obj: {
|
||||
nested: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function fibonacci(num: number): number {
|
||||
switch (num) {
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return 1;
|
||||
default:
|
||||
return fibonacci(num - 1) + fibonacci(num - 2);
|
||||
}
|
||||
}
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
13
examples/wasm-guest-ts/tsconfig.json
Normal file
13
examples/wasm-guest-ts/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
"module": "es2022", /* Specify what module code is generated. */
|
||||
"moduleResolution": "bundler",
|
||||
"paths": {},
|
||||
"outDir": "./dist/", /* Specify an output folder for all emitted files. */
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
||||
17
examples/wasm-guest-ts/vite.config.ts
Normal file
17
examples/wasm-guest-ts/vite.config.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { defineConfig } from "vite";
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
outDir: "./dist",
|
||||
minify: false,
|
||||
lib: {
|
||||
entry: "./src/component.ts",
|
||||
name: "runtime",
|
||||
fileName: "index",
|
||||
formats: ["es"],
|
||||
},
|
||||
rollupOptions: {
|
||||
external: (source) => source.startsWith("wasi:") || source.startsWith("trailbase:"),
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user