mirror of
https://github.com/trailbaseio/trailbase.git
synced 2026-01-06 09:50:10 -06:00
Move js runtime out with offline transpilation.
This commit is contained in:
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@@ -208,6 +208,15 @@ importers:
|
||||
specifier: ^8.14.0
|
||||
version: 8.14.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)
|
||||
|
||||
trailbase-core/js:
|
||||
devDependencies:
|
||||
prettier:
|
||||
specifier: ^3.3.3
|
||||
version: 3.3.3
|
||||
typescript:
|
||||
specifier: ^5.6.3
|
||||
version: 5.6.3
|
||||
|
||||
ui/admin:
|
||||
dependencies:
|
||||
'@bufbuild/protobuf':
|
||||
|
||||
@@ -5,6 +5,7 @@ packages:
|
||||
- 'ui/auth'
|
||||
- 'examples/blog/web'
|
||||
- 'examples/tutorial/scripts'
|
||||
- 'trailbase-core/js'
|
||||
options:
|
||||
prefer-workspace-packages: true
|
||||
strict-peer-dependencies: true
|
||||
|
||||
@@ -25,7 +25,7 @@ fn copy_dir(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fn build_ui(path: &str) -> Result<()> {
|
||||
fn build_js(path: &str) -> Result<()> {
|
||||
let pnpm_run = |args: &[&str]| -> Result<std::process::Output> {
|
||||
let output = std::process::Command::new("pnpm")
|
||||
.current_dir("..")
|
||||
@@ -45,12 +45,12 @@ fn build_ui(path: &str) -> Result<()> {
|
||||
// NOTE: We don't want to break backend-builds on frontend errors, at least for dev builds.
|
||||
if Ok("release") == env::var("PROFILE").as_deref() {
|
||||
panic!(
|
||||
"Failed to build ui '{path}': {}",
|
||||
"Failed to build js '{path}': {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
warn!(
|
||||
"Failed to build ui '{path}': {}",
|
||||
"Failed to build js '{path}': {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
@@ -97,7 +97,7 @@ fn main() -> Result<()> {
|
||||
let path = "ui/admin";
|
||||
println!("cargo::rerun-if-changed=../{path}/src/components/");
|
||||
println!("cargo::rerun-if-changed=../{path}/src/lib/");
|
||||
let _ = build_ui(path);
|
||||
let _ = build_js(path);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -106,7 +106,12 @@ fn main() -> Result<()> {
|
||||
println!("cargo::rerun-if-changed=../{path}/src/lib/");
|
||||
println!("cargo::rerun-if-changed=../{path}/src/pages/");
|
||||
println!("cargo::rerun-if-changed=../{path}/src/layouts/");
|
||||
let _ = build_ui("ui/auth");
|
||||
let _ = build_js(path);
|
||||
}
|
||||
|
||||
{
|
||||
println!("cargo::rerun-if-changed=js/src/");
|
||||
let _ = build_js("trailbase-core/js");
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
|
||||
1
trailbase-core/js/.gitignore
vendored
Normal file
1
trailbase-core/js/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
dist/
|
||||
16
trailbase-core/js/package.json
Normal file
16
trailbase-core/js/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "trailbase-js-runtime",
|
||||
"description": "Runtime for JS/TS execution within TrailBase",
|
||||
"version": "0.1.0",
|
||||
"license": "OSL-3.0",
|
||||
"type": "module",
|
||||
"homepage": "https://trailbase.io",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"format": "prettier -w src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^3.3.3",
|
||||
"typescript": "^5.6.3"
|
||||
}
|
||||
}
|
||||
60
trailbase-core/js/src/index.ts
Normal file
60
trailbase-core/js/src/index.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
declare var rustyscript: any;
|
||||
declare var globalThis: any;
|
||||
|
||||
type Headers = { [key: string]: string };
|
||||
type Request = {
|
||||
uri: string;
|
||||
headers: Headers;
|
||||
body: string;
|
||||
};
|
||||
type Response = {
|
||||
headers?: Headers;
|
||||
status?: number;
|
||||
body?: string;
|
||||
};
|
||||
type CbType = (req: Request) => Response | undefined;
|
||||
|
||||
const callbacks = new Map<string, CbType>();
|
||||
|
||||
export function addRoute(method: string, route: string, callback: CbType) {
|
||||
rustyscript.functions.route(method, route);
|
||||
callbacks.set(`${method}:${route}`, callback);
|
||||
|
||||
console.log("JS: Added route:", method, route);
|
||||
}
|
||||
|
||||
export async function query(
|
||||
queryStr: string,
|
||||
params: unknown[],
|
||||
): Promise<unknown[][]> {
|
||||
return await rustyscript.async_functions.query(queryStr, params);
|
||||
}
|
||||
|
||||
export async function execute(
|
||||
queryStr: string,
|
||||
params: unknown[],
|
||||
): Promise<number> {
|
||||
return await rustyscript.async_functions.execute(queryStr, params);
|
||||
}
|
||||
|
||||
export function dispatch(
|
||||
method: string,
|
||||
route: string,
|
||||
uri: string,
|
||||
headers: Headers,
|
||||
body: string,
|
||||
): Response | undefined {
|
||||
const key = `${method}:${route}`;
|
||||
const cb = callbacks.get(key);
|
||||
if (!cb) {
|
||||
throw Error(`Missing callback: ${key}`);
|
||||
}
|
||||
|
||||
return cb({
|
||||
uri,
|
||||
headers,
|
||||
body,
|
||||
});
|
||||
}
|
||||
|
||||
globalThis.__dispatch = dispatch;
|
||||
13
trailbase-core/js/tsconfig.json
Normal file
13
trailbase-core/js/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"esModuleInterop": false,
|
||||
"moduleResolution": "bundler",
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"outDir": "dist"
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ use axum::response::{IntoResponse, Response};
|
||||
use axum::Router;
|
||||
use libsql::Connection;
|
||||
use parking_lot::Mutex;
|
||||
use rust_embed::RustEmbed;
|
||||
use rustyscript::{json_args, Module, Runtime};
|
||||
use serde::Deserialize;
|
||||
use serde_json::from_value;
|
||||
@@ -13,6 +14,7 @@ use std::str::FromStr;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::assets::cow_to_string;
|
||||
use crate::records::sql_to_json::rows_to_json_arrays;
|
||||
use crate::AppState;
|
||||
|
||||
@@ -20,68 +22,6 @@ mod import_provider;
|
||||
|
||||
type AnyError = Box<dyn std::error::Error + Send + Sync>;
|
||||
|
||||
const TRAILBASE_MAIN: &str = r#"
|
||||
type Headers = { [key: string]: string };
|
||||
type Request = {
|
||||
uri: string;
|
||||
headers: Headers;
|
||||
body: string;
|
||||
};
|
||||
type Response = {
|
||||
headers?: Headers;
|
||||
status?: number;
|
||||
body?: string;
|
||||
};
|
||||
type CbType = (req: Request) => Response | undefined;
|
||||
|
||||
const callbacks = new Map<string, CbType>();
|
||||
|
||||
export function addRoute(method: string, route: string, callback: CbType) {
|
||||
rustyscript.functions.route(method, route);
|
||||
callbacks.set(`${method}:${route}`, callback);
|
||||
|
||||
console.log("JS: Added route:", method, route);
|
||||
}
|
||||
|
||||
export async function query(queryStr: string, params: unknown[]) : unknown[][] {
|
||||
return await rustyscript.async_functions.query(queryStr, params);
|
||||
}
|
||||
|
||||
export async function execute(queryStr: string, params: unknown[]) : number {
|
||||
return await rustyscript.async_functions.execute(queryStr, params);
|
||||
}
|
||||
|
||||
export function dispatch(
|
||||
method: string,
|
||||
route: string,
|
||||
uri: string,
|
||||
headers: Headers,
|
||||
body: string,
|
||||
) : Response | undefined {
|
||||
console.log("JS: Dispatching:", method, route, body);
|
||||
|
||||
const key = `${method}:${route}`;
|
||||
const cb = callbacks.get(key);
|
||||
if (!cb) {
|
||||
throw Error(`Missing callback: ${key}`);
|
||||
}
|
||||
|
||||
return cb({
|
||||
uri,
|
||||
headers,
|
||||
body,
|
||||
});
|
||||
};
|
||||
|
||||
globalThis.__dispatch = dispatch;
|
||||
globalThis.__trailbase = {
|
||||
addRoute,
|
||||
dispatch,
|
||||
query,
|
||||
execute,
|
||||
};
|
||||
"#;
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
struct JsResponse {
|
||||
headers: Option<HashMap<String, String>>,
|
||||
@@ -116,9 +56,6 @@ impl RuntimeSingleton {
|
||||
let handle = std::thread::spawn(move || {
|
||||
let mut runtime = Self::init_runtime().unwrap();
|
||||
|
||||
let module = Module::new("__index.ts", TRAILBASE_MAIN);
|
||||
let _handle = runtime.load_module(&module).unwrap();
|
||||
|
||||
#[allow(clippy::never_loop)]
|
||||
while let Ok(message) = receiver.recv() {
|
||||
match message {
|
||||
@@ -141,13 +78,7 @@ impl RuntimeSingleton {
|
||||
|
||||
cache.set(
|
||||
"trailbase:main",
|
||||
r#"
|
||||
export const _test = "test0";
|
||||
export const addRoute = globalThis.__trailbase.addRoute;
|
||||
export const query = globalThis.__trailbase.query;
|
||||
export const execute = globalThis.__trailbase.execute;
|
||||
"#
|
||||
.to_string(),
|
||||
cow_to_string(JsRuntimeAssets::get("index.js").unwrap().data),
|
||||
);
|
||||
|
||||
let runtime = rustyscript::Runtime::new(rustyscript::RuntimeOptions {
|
||||
@@ -171,10 +102,10 @@ pub fn json_value_to_param(value: serde_json::Value) -> Result<libsql::Value, ru
|
||||
use rustyscript::Error;
|
||||
return Ok(match value {
|
||||
serde_json::Value::Object(ref _map) => {
|
||||
return Err(Error::Runtime(format!("Object unsupported")));
|
||||
return Err(Error::Runtime("Object unsupported".to_string()));
|
||||
}
|
||||
serde_json::Value::Array(ref _arr) => {
|
||||
return Err(Error::Runtime(format!("Array unsupported")));
|
||||
return Err(Error::Runtime("Array unsupported".to_string()));
|
||||
}
|
||||
serde_json::Value::Null => libsql::Value::Null,
|
||||
serde_json::Value::Bool(b) => libsql::Value::Integer(b as i64),
|
||||
@@ -221,10 +152,8 @@ impl RuntimeHandle {
|
||||
.await
|
||||
.map_err(|err| rustyscript::Error::Runtime(err.to_string()))?;
|
||||
|
||||
return Ok(
|
||||
serde_json::to_value(values)
|
||||
.map_err(|err| rustyscript::Error::Runtime(err.to_string()))?,
|
||||
);
|
||||
return serde_json::to_value(values)
|
||||
.map_err(|err| rustyscript::Error::Runtime(err.to_string()));
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
@@ -246,8 +175,6 @@ impl RuntimeHandle {
|
||||
.await
|
||||
.map_err(|err| rustyscript::Error::Runtime(err.to_string()))?;
|
||||
|
||||
log::error!("ROWS AFF {rows_affected}");
|
||||
|
||||
return Ok(serde_json::Value::Number(rows_affected.into()));
|
||||
})
|
||||
})
|
||||
@@ -498,11 +425,8 @@ mod tests {
|
||||
.load_module(&Module::new(
|
||||
"module.js",
|
||||
r#"
|
||||
import { _test } from "trailbase:main";
|
||||
import { dispatch } from "./__index.ts";
|
||||
|
||||
export function test_fun() {
|
||||
return _test;
|
||||
return "test0";
|
||||
}
|
||||
"#,
|
||||
))
|
||||
@@ -547,7 +471,7 @@ mod tests {
|
||||
r#"
|
||||
import { query } from "trailbase:main";
|
||||
|
||||
export async function test_query(queryStr: string) : unknown[][] {
|
||||
export async function test_query(queryStr: string) : Promise<unknown[][]> {
|
||||
return await query(queryStr, []);
|
||||
}
|
||||
"#,
|
||||
@@ -610,7 +534,7 @@ mod tests {
|
||||
r#"
|
||||
import { execute } from "trailbase:main";
|
||||
|
||||
export async function test_execute(queryStr: string) : number {
|
||||
export async function test_execute(queryStr: string) : Promise<number> {
|
||||
return await execute(queryStr, []);
|
||||
}
|
||||
"#,
|
||||
@@ -648,3 +572,7 @@ mod tests {
|
||||
assert_eq!(0, count);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(RustEmbed, Clone)]
|
||||
#[folder = "js/dist/"]
|
||||
struct JsRuntimeAssets;
|
||||
|
||||
Reference in New Issue
Block a user