openzoo 0.51.4 → 0.51.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -1
- package/bin/openzoo.js +21 -46
- package/lib/chatgpt-install.js +106 -0
- package/lib/chatgpt.js +108 -90
- package/lib/config.js +34 -0
- package/lib/modelcommand.js +443 -0
- package/lib/pay.js +60 -4
- package/lib/proxy.js +39 -0
- package/package.json +4 -7
- package/transmute/README.md +0 -385
- package/transmute/bin/openzoo-transmute.js +0 -7
- package/transmute/docs/OPENZOO_CLI_PATCH.md +0 -139
- package/transmute/docs/VERCEL_TO_SOLANA.md +0 -381
- package/transmute/lib/build.js +0 -381
- package/transmute/lib/cli.js +0 -293
- package/transmute/lib/compile/bytecode.js +0 -591
- package/transmute/lib/compile/hostids.js +0 -11
- package/transmute/lib/compile/index.js +0 -159
- package/transmute/lib/compile/ir.js +0 -1634
- package/transmute/lib/compile/parse.js +0 -753
- package/transmute/lib/compile/rust.js +0 -781
- package/transmute/lib/deploy.js +0 -357
- package/transmute/lib/eligibility.js +0 -211
- package/transmute/lib/gateway.js +0 -710
- package/transmute/lib/hub.js +0 -290
- package/transmute/lib/solana.js +0 -387
- package/transmute/lib/vercel.js +0 -296
- package/transmute/lib/wallet.js +0 -41
- package/transmute/lib/wire.js +0 -208
- package/transmute/prebuilt/zoo_vm.v0.so +0 -0
- package/transmute/runtime/zoo-host/Cargo.lock +0 -588
- package/transmute/runtime/zoo-host/Cargo.toml +0 -30
- package/transmute/runtime/zoo-host/examples/vmrun.rs +0 -43
- package/transmute/runtime/zoo-host/src/assets.rs +0 -214
- package/transmute/runtime/zoo-host/src/ctx.rs +0 -350
- package/transmute/runtime/zoo-host/src/fmt.rs +0 -188
- package/transmute/runtime/zoo-host/src/helpers.rs +0 -245
- package/transmute/runtime/zoo-host/src/json.rs +0 -263
- package/transmute/runtime/zoo-host/src/kv.rs +0 -206
- package/transmute/runtime/zoo-host/src/lib.rs +0 -137
- package/transmute/runtime/zoo-host/src/site.rs +0 -94
- package/transmute/runtime/zoo-host/src/val.rs +0 -1050
- package/transmute/runtime/zoo-host/src/vm.rs +0 -902
- package/transmute/runtime/zoo-host/src/wire.rs +0 -239
- package/transmute/runtime/zoo-vm/Cargo.lock +0 -596
- package/transmute/runtime/zoo-vm/Cargo.toml +0 -23
- package/transmute/runtime/zoo-vm/src/lib.rs +0 -92
- package/transmute/site/.env.production +0 -2
- package/transmute/site/api/echo.js +0 -10
- package/transmute/site/api/hello.js +0 -12
- package/transmute/site/api/hits.js +0 -13
- package/transmute/site/api/time.js +0 -5
- package/transmute/site/package.json +0 -6
- package/transmute/site/public/app.js +0 -16
- package/transmute/site/public/index.html +0 -96
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
//! Number and hex formatting without `core::fmt`: the float/integer printers
|
|
2
|
-
//! behind `format!` cost ~50 KB of program (flt2dec dragon/grisu + Formatter),
|
|
3
|
-
//! which at Solana rent rates is a third of a SOL per site. These are the
|
|
4
|
-
//! handful of shapes the runtime actually needs.
|
|
5
|
-
use alloc::string::String;
|
|
6
|
-
|
|
7
|
-
pub fn push_u64(out: &mut String, mut n: u64) {
|
|
8
|
-
if n == 0 {
|
|
9
|
-
out.push('0');
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
let mut buf = [0u8; 20];
|
|
13
|
-
let mut i = buf.len();
|
|
14
|
-
while n > 0 {
|
|
15
|
-
i -= 1;
|
|
16
|
-
buf[i] = b'0' + (n % 10) as u8;
|
|
17
|
-
n /= 10;
|
|
18
|
-
}
|
|
19
|
-
for &b in &buf[i..] {
|
|
20
|
-
out.push(b as char);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
pub fn push_i64(out: &mut String, n: i64) {
|
|
25
|
-
if n < 0 {
|
|
26
|
-
out.push('-');
|
|
27
|
-
push_u64(out, n.unsigned_abs());
|
|
28
|
-
} else {
|
|
29
|
-
push_u64(out, n as u64);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/// Zero-padded decimal (for dates: `2026`, `09`, `007`).
|
|
34
|
-
pub fn push_padded(out: &mut String, n: u64, width: usize) {
|
|
35
|
-
let mut tmp = String::new();
|
|
36
|
-
push_u64(&mut tmp, n);
|
|
37
|
-
for _ in tmp.len()..width {
|
|
38
|
-
out.push('0');
|
|
39
|
-
}
|
|
40
|
-
out.push_str(&tmp);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
pub fn push_hex2(out: &mut String, b: u8) {
|
|
44
|
-
const H: &[u8; 16] = b"0123456789ABCDEF";
|
|
45
|
-
out.push(H[(b >> 4) as usize] as char);
|
|
46
|
-
out.push(H[(b & 15) as usize] as char);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
pub fn u64_string(n: u64) -> String {
|
|
50
|
-
let mut s = String::new();
|
|
51
|
-
push_u64(&mut s, n);
|
|
52
|
-
s
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/// 10^k for small k without `libm::pow`.
|
|
56
|
-
pub fn pow10(k: u32) -> f64 {
|
|
57
|
-
let mut v = 1.0;
|
|
58
|
-
for _ in 0..k {
|
|
59
|
-
v *= 10.0;
|
|
60
|
-
}
|
|
61
|
-
v
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/// JS `Number.prototype.toString()` for the common cases, hand-rolled:
|
|
65
|
-
/// integers exactly (|n| < 1e21), otherwise up to 15 significant digits with
|
|
66
|
-
/// trailing zeros trimmed (`0.1 + 0.2` prints `0.30000000000000004` in JS and
|
|
67
|
-
/// `0.3` here — the one visible difference), exponent form outside
|
|
68
|
-
/// [1e-6, 1e21) like JS.
|
|
69
|
-
pub fn push_f64(out: &mut String, n: f64) {
|
|
70
|
-
if n.is_nan() {
|
|
71
|
-
out.push_str("NaN");
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
if n.is_infinite() {
|
|
75
|
-
out.push_str(if n > 0.0 { "Infinity" } else { "-Infinity" });
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
if n == 0.0 {
|
|
79
|
-
out.push('0');
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
let neg = n < 0.0;
|
|
83
|
-
let a = if neg { -n } else { n };
|
|
84
|
-
if neg {
|
|
85
|
-
out.push('-');
|
|
86
|
-
}
|
|
87
|
-
if a == libm::trunc(a) && a < 9.0e18 {
|
|
88
|
-
push_u64(out, a as u64);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
// Scientific decomposition: a = m × 10^e with 1 <= m < 10.
|
|
92
|
-
let mut e: i32 = 0;
|
|
93
|
-
let mut m = a;
|
|
94
|
-
while m >= 10.0 {
|
|
95
|
-
m /= 10.0;
|
|
96
|
-
e += 1;
|
|
97
|
-
}
|
|
98
|
-
while m < 1.0 {
|
|
99
|
-
m *= 10.0;
|
|
100
|
-
e -= 1;
|
|
101
|
-
}
|
|
102
|
-
// 15 significant digits, rounded.
|
|
103
|
-
let mut digits = [0u8; 17];
|
|
104
|
-
let mut nd = 0;
|
|
105
|
-
let mut scaled = m * 1e14; // 15 digits before the point
|
|
106
|
-
scaled = libm::round(scaled);
|
|
107
|
-
if scaled >= 1e15 {
|
|
108
|
-
scaled /= 10.0;
|
|
109
|
-
e += 1;
|
|
110
|
-
}
|
|
111
|
-
let mut v = scaled as u64;
|
|
112
|
-
let mut tmp = [0u8; 20];
|
|
113
|
-
let mut i = tmp.len();
|
|
114
|
-
while v > 0 {
|
|
115
|
-
i -= 1;
|
|
116
|
-
tmp[i] = (v % 10) as u8;
|
|
117
|
-
v /= 10;
|
|
118
|
-
}
|
|
119
|
-
for &d in &tmp[i..] {
|
|
120
|
-
digits[nd] = d;
|
|
121
|
-
nd += 1;
|
|
122
|
-
}
|
|
123
|
-
while nd > 1 && digits[nd - 1] == 0 {
|
|
124
|
-
nd -= 1;
|
|
125
|
-
}
|
|
126
|
-
if !(-7..21).contains(&e) {
|
|
127
|
-
out.push((b'0' + digits[0]) as char);
|
|
128
|
-
if nd > 1 {
|
|
129
|
-
out.push('.');
|
|
130
|
-
for &d in &digits[1..nd] {
|
|
131
|
-
out.push((b'0' + d) as char);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
out.push('e');
|
|
135
|
-
out.push(if e < 0 { '-' } else { '+' });
|
|
136
|
-
push_u64(out, e.unsigned_abs() as u64);
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
if e < 0 {
|
|
140
|
-
out.push_str("0.");
|
|
141
|
-
for _ in 0..(-e - 1) {
|
|
142
|
-
out.push('0');
|
|
143
|
-
}
|
|
144
|
-
for &d in &digits[..nd] {
|
|
145
|
-
out.push((b'0' + d) as char);
|
|
146
|
-
}
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
let int_len = (e + 1) as usize;
|
|
150
|
-
for k in 0..int_len {
|
|
151
|
-
out.push((b'0' + if k < nd { digits[k] } else { 0 }) as char);
|
|
152
|
-
}
|
|
153
|
-
if nd > int_len {
|
|
154
|
-
out.push('.');
|
|
155
|
-
for &d in &digits[int_len..nd] {
|
|
156
|
-
out.push((b'0' + d) as char);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/// `toFixed(digits)`.
|
|
162
|
-
pub fn to_fixed(n: f64, digits: usize) -> String {
|
|
163
|
-
let mut out = String::new();
|
|
164
|
-
if n.is_nan() {
|
|
165
|
-
out.push_str("NaN");
|
|
166
|
-
return out;
|
|
167
|
-
}
|
|
168
|
-
let neg = n < 0.0;
|
|
169
|
-
let a = if neg { -n } else { n };
|
|
170
|
-
let m = pow10(digits as u32);
|
|
171
|
-
let scaled = libm::round(a * m) as u64;
|
|
172
|
-
let int = scaled / (m as u64);
|
|
173
|
-
let frac = scaled % (m as u64);
|
|
174
|
-
if neg && scaled != 0 {
|
|
175
|
-
out.push('-');
|
|
176
|
-
}
|
|
177
|
-
push_u64(&mut out, int);
|
|
178
|
-
if digits > 0 {
|
|
179
|
-
out.push('.');
|
|
180
|
-
let mut f = String::new();
|
|
181
|
-
push_u64(&mut f, frac);
|
|
182
|
-
for _ in f.len()..digits {
|
|
183
|
-
out.push('0');
|
|
184
|
-
}
|
|
185
|
-
out.push_str(&f);
|
|
186
|
-
}
|
|
187
|
-
out
|
|
188
|
-
}
|
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
//! Bridge helpers shared by compiled sites (the generated prelude calls
|
|
2
|
-
//! these) and the bytecode interpreter (by id).
|
|
3
|
-
use crate::json;
|
|
4
|
-
use crate::val::{url_decode, Val};
|
|
5
|
-
use crate::Ctx;
|
|
6
|
-
use alloc::{string::String, vec::Vec};
|
|
7
|
-
|
|
8
|
-
/// Response value for app-router handlers: a tagged object `send` turns into
|
|
9
|
-
/// the bridge response.
|
|
10
|
-
pub fn resp(kind: &str, a: Val, b: Val) -> Val {
|
|
11
|
-
let mut o = Val::obj();
|
|
12
|
-
o.set_str("__zoo", Val::str(kind));
|
|
13
|
-
o.set_str("a", a);
|
|
14
|
-
o.set_str("b", b);
|
|
15
|
-
o
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
pub fn send(cx: &mut Ctx, v: &Val) -> Result<(), Val> {
|
|
19
|
-
let kind = v.get_str("__zoo");
|
|
20
|
-
match kind.as_str() {
|
|
21
|
-
Some("json") => { let a = v.get_str("a"); let b = v.get_str("b"); cx.respond_json(&a, &b); Ok(()) }
|
|
22
|
-
Some("raw") => { let a = v.get_str("a"); let b = v.get_str("b"); cx.respond(&a, &b); Ok(()) }
|
|
23
|
-
Some("redirect") => {
|
|
24
|
-
let url = v.get_str("a");
|
|
25
|
-
let mut st = v.get_str("b");
|
|
26
|
-
if let Val::Obj(_) = st { st = st.get_str("status"); }
|
|
27
|
-
cx.respond_redirect(&url, &st);
|
|
28
|
-
Ok(())
|
|
29
|
-
}
|
|
30
|
-
_ => if v.is_undef() { Ok(()) } else { Err(Val::str("TypeError: the handler returned a value that is not a Response")) },
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/// Runtime throws are strings ("SyntaxError: ..."); in a catch they become
|
|
35
|
-
/// Error-like objects so `e.message` / `e.name` read as in Node.
|
|
36
|
-
pub fn caught(v: Val) -> Val {
|
|
37
|
-
if let Val::Str(s) = &v {
|
|
38
|
-
if let Some(i) = s.find(": ") {
|
|
39
|
-
let name = &s[..i];
|
|
40
|
-
if name.ends_with("Error") && !name.contains(' ') {
|
|
41
|
-
let mut o = Val::obj();
|
|
42
|
-
o.set_str("name", Val::str(name));
|
|
43
|
-
o.set_str("message", Val::str(&s[i + 2..]));
|
|
44
|
-
return o;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
v
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
pub fn param_value(s: &str, catch_all: bool) -> Val {
|
|
52
|
-
if catch_all {
|
|
53
|
-
Val::Arr(s.split('/').filter(|p| !p.is_empty()).map(|p| Val::Str(url_decode(p))).collect())
|
|
54
|
-
} else {
|
|
55
|
-
Val::Str(url_decode(s))
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/// A dynamic segment, passed by the gateway as the `x-zoo-param-<name>` header.
|
|
60
|
-
pub fn param(cx: &mut Ctx, name: &str, catch_all: bool) -> Val {
|
|
61
|
-
let mut h = String::from("x-zoo-param-");
|
|
62
|
-
h.push_str(name);
|
|
63
|
-
match cx.req_header(&Val::Str(h)) {
|
|
64
|
-
Val::Str(s) => param_value(&s, catch_all),
|
|
65
|
-
_ => Val::Undef,
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
pub fn params(cx: &mut Ctx, params: &[(&str, bool)]) -> Val {
|
|
70
|
-
let mut o = Val::obj();
|
|
71
|
-
for (name, catch_all) in params.iter() {
|
|
72
|
-
let v = param(cx, name, *catch_all);
|
|
73
|
-
if !v.is_undef() { o.set_str(name, v); }
|
|
74
|
-
}
|
|
75
|
-
o
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/// `req.query`: the query string plus the route's dynamic segments.
|
|
79
|
-
pub fn query(cx: &mut Ctx, params: &[(&str, bool)]) -> Val {
|
|
80
|
-
let mut q = cx.req_query();
|
|
81
|
-
for (name, catch_all) in params.iter() {
|
|
82
|
-
let v = param(cx, name, *catch_all);
|
|
83
|
-
if !v.is_undef() { q.set_str(name, v); }
|
|
84
|
-
}
|
|
85
|
-
q
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
pub fn cookies(cx: &mut Ctx) -> Val {
|
|
89
|
-
let mut o = Val::obj();
|
|
90
|
-
if let Val::Str(h) = cx.req_header(&Val::str("cookie")) {
|
|
91
|
-
for part in h.split(';') {
|
|
92
|
-
let p = part.trim();
|
|
93
|
-
if let Some(i) = p.find('=') { o.set_str(&p[..i], Val::Str(url_decode(&p[i + 1..]))); }
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
o
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
pub fn log(a: &[Val]) -> Val {
|
|
100
|
-
let mut s = String::new();
|
|
101
|
-
for (i, v) in a.iter().enumerate() {
|
|
102
|
-
if i > 0 { s.push(' '); }
|
|
103
|
-
match v {
|
|
104
|
-
Val::Str(x) => s.push_str(x),
|
|
105
|
-
Val::Obj(_) | Val::Arr(_) => s.push_str(&json::stringify(v)),
|
|
106
|
-
other => s.push_str(&other.to_js_string()),
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
crate::wire::log_str(&s);
|
|
110
|
-
Val::Undef
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/// Helper ids shared with the bytecode emitter (lib/compile/bytecode.js).
|
|
114
|
-
pub const H_COOKIES: u8 = 0;
|
|
115
|
-
pub const H_COOKIE_GET: u8 = 1;
|
|
116
|
-
pub const H_COOKIE_HAS: u8 = 2;
|
|
117
|
-
pub const H_COOKIE_ALL: u8 = 3;
|
|
118
|
-
pub const H_HEADER_HAS: u8 = 4;
|
|
119
|
-
pub const H_QUERY_HAS: u8 = 5;
|
|
120
|
-
pub const H_QUERY_GET_ALL: u8 = 6;
|
|
121
|
-
pub const H_SEARCH: u8 = 7;
|
|
122
|
-
pub const H_ASSIGN: u8 = 8;
|
|
123
|
-
pub const H_OMIT: u8 = 9;
|
|
124
|
-
pub const H_FROM_ENTRIES: u8 = 10;
|
|
125
|
-
pub const H_ARRAY_FROM: u8 = 11;
|
|
126
|
-
pub const H_SLICE_FROM: u8 = 12;
|
|
127
|
-
pub const H_URL: u8 = 13;
|
|
128
|
-
pub const H_NUM: u8 = 14;
|
|
129
|
-
pub const H_SET_HEADERS: u8 = 15;
|
|
130
|
-
|
|
131
|
-
pub fn helper_id(name: &str) -> Option<u8> {
|
|
132
|
-
Some(match name {
|
|
133
|
-
"cookies" => H_COOKIES, "cookie_get" => H_COOKIE_GET, "cookie_has" => H_COOKIE_HAS, "cookie_all" => H_COOKIE_ALL,
|
|
134
|
-
"header_has" => H_HEADER_HAS, "query_has" => H_QUERY_HAS, "query_get_all" => H_QUERY_GET_ALL, "search" => H_SEARCH,
|
|
135
|
-
"assign" => H_ASSIGN, "omit" => H_OMIT, "from_entries" => H_FROM_ENTRIES, "array_from" => H_ARRAY_FROM,
|
|
136
|
-
"slice_from" => H_SLICE_FROM, "url" => H_URL, "num" => H_NUM, "set_headers" => H_SET_HEADERS,
|
|
137
|
-
_ => return None,
|
|
138
|
-
})
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
pub fn helper(cx: &mut Ctx, id: u8, a: &[Val]) -> Val {
|
|
142
|
-
let arg = |i: usize| a.get(i).cloned().unwrap_or(Val::Undef);
|
|
143
|
-
match id {
|
|
144
|
-
H_COOKIES => cookies(cx),
|
|
145
|
-
H_COOKIE_GET => {
|
|
146
|
-
let name = arg(0);
|
|
147
|
-
let v = cookies(cx).get(&name);
|
|
148
|
-
if v.is_undef() { return Val::Undef; }
|
|
149
|
-
let mut o = Val::obj();
|
|
150
|
-
o.set_str("name", name);
|
|
151
|
-
o.set_str("value", v);
|
|
152
|
-
o
|
|
153
|
-
}
|
|
154
|
-
H_COOKIE_HAS => Val::Bool(cookies(cx).has(&arg(0))),
|
|
155
|
-
H_COOKIE_ALL => {
|
|
156
|
-
let mut out = Val::arr();
|
|
157
|
-
if let Val::Obj(kv) = cookies(cx) {
|
|
158
|
-
for (k, v) in kv {
|
|
159
|
-
let mut o = Val::obj();
|
|
160
|
-
o.set_str("name", Val::Str(k));
|
|
161
|
-
o.set_str("value", v);
|
|
162
|
-
out.push(o);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
out
|
|
166
|
-
}
|
|
167
|
-
H_HEADER_HAS => Val::Bool(!cx.req_header(&arg(0)).is_nullish()),
|
|
168
|
-
H_QUERY_HAS => Val::Bool(cx.req_query().has(&arg(0))),
|
|
169
|
-
H_QUERY_GET_ALL => match cx.req_query_get(&arg(0)) {
|
|
170
|
-
Val::Null | Val::Undef => Val::arr(),
|
|
171
|
-
v => Val::Arr(alloc::vec![v]),
|
|
172
|
-
},
|
|
173
|
-
H_SEARCH => {
|
|
174
|
-
let with_q = arg(0).truthy();
|
|
175
|
-
let u = cx.req_url().to_js_string();
|
|
176
|
-
match u.find('?') {
|
|
177
|
-
Some(i) => Val::Str(String::from(if with_q { &u[i..] } else { &u[i + 1..] })),
|
|
178
|
-
None => Val::str(""),
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
H_ASSIGN => {
|
|
182
|
-
let mut t = if a.is_empty() { Val::obj() } else { arg(0) };
|
|
183
|
-
for src in a.iter().skip(1) {
|
|
184
|
-
if let Val::Obj(kv) = src { for (k, v) in kv.iter() { t.set_str(k, v.clone()); } }
|
|
185
|
-
}
|
|
186
|
-
t
|
|
187
|
-
}
|
|
188
|
-
H_OMIT => {
|
|
189
|
-
let mut out = Val::obj();
|
|
190
|
-
let skip: Vec<String> = arg(1).iter_values().iter().map(|k| k.to_js_string()).collect();
|
|
191
|
-
if let Val::Obj(kv) = arg(0) {
|
|
192
|
-
for (k, v) in kv.iter() { if !skip.iter().any(|s| s == k) { out.set_str(k, v.clone()); } }
|
|
193
|
-
}
|
|
194
|
-
out
|
|
195
|
-
}
|
|
196
|
-
H_FROM_ENTRIES => {
|
|
197
|
-
let mut o = Val::obj();
|
|
198
|
-
for pair in arg(0).iter_values() {
|
|
199
|
-
let k = pair.get(&Val::Num(0.0));
|
|
200
|
-
let v = pair.get(&Val::Num(1.0));
|
|
201
|
-
o.set(&k, v);
|
|
202
|
-
}
|
|
203
|
-
o
|
|
204
|
-
}
|
|
205
|
-
H_ARRAY_FROM => Val::Arr(arg(0).iter_values()),
|
|
206
|
-
H_SLICE_FROM => {
|
|
207
|
-
let items = arg(0).iter_values();
|
|
208
|
-
let from = arg(1).to_num().max(0.0) as usize;
|
|
209
|
-
Val::Arr(items.into_iter().skip(from).collect())
|
|
210
|
-
}
|
|
211
|
-
H_URL => {
|
|
212
|
-
let input = arg(0).to_js_string();
|
|
213
|
-
let base = if arg(1).is_undef() { String::new() } else { arg(1).to_js_string() };
|
|
214
|
-
if input.contains("://") || base.is_empty() { return Val::Str(input); }
|
|
215
|
-
let origin_end = match base.find("://") {
|
|
216
|
-
Some(i) => base[i + 3..].find('/').map(|j| i + 3 + j).unwrap_or(base.len()),
|
|
217
|
-
None => 0,
|
|
218
|
-
};
|
|
219
|
-
let mut out = String::new();
|
|
220
|
-
if input.starts_with('/') { out.push_str(&base[..origin_end]); out.push_str(&input); return Val::Str(out); }
|
|
221
|
-
let path_end = base.rfind('/').map(|i| i + 1).unwrap_or(base.len()).max(origin_end).min(base.len());
|
|
222
|
-
out.push_str(&base[..path_end]);
|
|
223
|
-
out.push_str(&input);
|
|
224
|
-
Val::Str(out)
|
|
225
|
-
}
|
|
226
|
-
H_NUM => {
|
|
227
|
-
let name = arg(0).to_js_string();
|
|
228
|
-
let n = match a.get(1) { Some(Val::Num(n)) => *n, _ => return Val::Bool(false) };
|
|
229
|
-
Val::Bool(match name.as_str() {
|
|
230
|
-
"isInteger" => n.is_finite() && n == libm::trunc(n),
|
|
231
|
-
"isNaN" => n.is_nan(),
|
|
232
|
-
"isFinite" => n.is_finite(),
|
|
233
|
-
"isSafeInteger" => n.is_finite() && n == libm::trunc(n) && libm::fabs(n) <= 9007199254740991.0,
|
|
234
|
-
_ => false,
|
|
235
|
-
})
|
|
236
|
-
}
|
|
237
|
-
H_SET_HEADERS => {
|
|
238
|
-
if let Val::Obj(kv) = arg(0) {
|
|
239
|
-
for (k, v) in kv.iter() { cx.res_header(&Val::str(k), v); }
|
|
240
|
-
}
|
|
241
|
-
Val::Undef
|
|
242
|
-
}
|
|
243
|
-
_ => Val::Undef,
|
|
244
|
-
}
|
|
245
|
-
}
|
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
//! JSON <-> `Val`. Hand-rolled: serde would triple the program size.
|
|
2
|
-
use crate::val::{num_to_string, parse_decimal, Val};
|
|
3
|
-
use alloc::{string::String, vec::Vec};
|
|
4
|
-
|
|
5
|
-
pub fn stringify(v: &Val) -> String {
|
|
6
|
-
let mut out = String::new();
|
|
7
|
-
write(v, &mut out);
|
|
8
|
-
out
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
fn write(v: &Val, out: &mut String) {
|
|
12
|
-
match v {
|
|
13
|
-
// JSON.stringify(undefined) is undefined; inside containers we map to null.
|
|
14
|
-
Val::Undef | Val::Null => out.push_str("null"),
|
|
15
|
-
Val::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
|
|
16
|
-
Val::Num(n) => {
|
|
17
|
-
if n.is_finite() {
|
|
18
|
-
out.push_str(&num_to_string(*n))
|
|
19
|
-
} else {
|
|
20
|
-
out.push_str("null")
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
Val::Str(s) => write_str(s, out),
|
|
24
|
-
Val::Arr(a) => {
|
|
25
|
-
out.push('[');
|
|
26
|
-
for (i, x) in a.iter().enumerate() {
|
|
27
|
-
if i > 0 {
|
|
28
|
-
out.push(',');
|
|
29
|
-
}
|
|
30
|
-
write(x, out);
|
|
31
|
-
}
|
|
32
|
-
out.push(']');
|
|
33
|
-
}
|
|
34
|
-
Val::Obj(kv) => {
|
|
35
|
-
out.push('{');
|
|
36
|
-
let mut first = true;
|
|
37
|
-
for (k, x) in kv.iter() {
|
|
38
|
-
if x.is_undef() {
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
if !first {
|
|
42
|
-
out.push(',');
|
|
43
|
-
}
|
|
44
|
-
first = false;
|
|
45
|
-
write_str(k, out);
|
|
46
|
-
out.push(':');
|
|
47
|
-
write(x, out);
|
|
48
|
-
}
|
|
49
|
-
out.push('}');
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
pub fn write_str(s: &str, out: &mut String) {
|
|
55
|
-
out.push('"');
|
|
56
|
-
for c in s.chars() {
|
|
57
|
-
match c {
|
|
58
|
-
'"' => out.push_str("\\\""),
|
|
59
|
-
'\\' => out.push_str("\\\\"),
|
|
60
|
-
'\n' => out.push_str("\\n"),
|
|
61
|
-
'\r' => out.push_str("\\r"),
|
|
62
|
-
'\t' => out.push_str("\\t"),
|
|
63
|
-
'\u{08}' => out.push_str("\\b"),
|
|
64
|
-
'\u{0c}' => out.push_str("\\f"),
|
|
65
|
-
c if (c as u32) < 0x20 => {
|
|
66
|
-
out.push_str("\\u00");
|
|
67
|
-
let b = c as u32;
|
|
68
|
-
out.push(hex((b >> 4) as u8));
|
|
69
|
-
out.push(hex((b & 0xf) as u8));
|
|
70
|
-
}
|
|
71
|
-
c => out.push(c),
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
out.push('"');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
fn hex(n: u8) -> char {
|
|
78
|
-
b"0123456789abcdef"[n as usize] as char
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
pub fn parse(s: &str) -> Result<Val, Val> {
|
|
82
|
-
let mut p = Parser { b: s.as_bytes(), i: 0 };
|
|
83
|
-
p.ws();
|
|
84
|
-
let v = p.value()?;
|
|
85
|
-
p.ws();
|
|
86
|
-
if p.i != p.b.len() {
|
|
87
|
-
return Err(p.err("Unexpected non-whitespace character after JSON"));
|
|
88
|
-
}
|
|
89
|
-
Ok(v)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
struct Parser<'a> {
|
|
93
|
-
b: &'a [u8],
|
|
94
|
-
i: usize,
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
impl<'a> Parser<'a> {
|
|
98
|
-
fn err(&self, msg: &str) -> Val {
|
|
99
|
-
let mut m = String::from("SyntaxError: ");
|
|
100
|
-
m.push_str(msg);
|
|
101
|
-
m.push_str(" at position ");
|
|
102
|
-
crate::fmt::push_u64(&mut m, self.i as u64);
|
|
103
|
-
Val::Str(m)
|
|
104
|
-
}
|
|
105
|
-
fn ws(&mut self) {
|
|
106
|
-
while self.i < self.b.len() && matches!(self.b[self.i], b' ' | b'\n' | b'\r' | b'\t') {
|
|
107
|
-
self.i += 1;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
fn peek(&self) -> Option<u8> {
|
|
111
|
-
self.b.get(self.i).copied()
|
|
112
|
-
}
|
|
113
|
-
fn value(&mut self) -> Result<Val, Val> {
|
|
114
|
-
match self.peek() {
|
|
115
|
-
None => Err(self.err("Unexpected end of JSON input")),
|
|
116
|
-
Some(b'{') => self.object(),
|
|
117
|
-
Some(b'[') => self.array(),
|
|
118
|
-
Some(b'"') => Ok(Val::Str(self.string()?)),
|
|
119
|
-
Some(b't') => self.lit("true", Val::Bool(true)),
|
|
120
|
-
Some(b'f') => self.lit("false", Val::Bool(false)),
|
|
121
|
-
Some(b'n') => self.lit("null", Val::Null),
|
|
122
|
-
Some(c) if c == b'-' || c.is_ascii_digit() => self.number(),
|
|
123
|
-
Some(_) => Err(self.err("Unexpected token")),
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
fn lit(&mut self, word: &str, v: Val) -> Result<Val, Val> {
|
|
127
|
-
if self.b[self.i..].starts_with(word.as_bytes()) {
|
|
128
|
-
self.i += word.len();
|
|
129
|
-
Ok(v)
|
|
130
|
-
} else {
|
|
131
|
-
Err(self.err("Unexpected token"))
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
fn number(&mut self) -> Result<Val, Val> {
|
|
135
|
-
let start = self.i;
|
|
136
|
-
if self.peek() == Some(b'-') {
|
|
137
|
-
self.i += 1;
|
|
138
|
-
}
|
|
139
|
-
while let Some(c) = self.peek() {
|
|
140
|
-
if c.is_ascii_digit() || matches!(c, b'.' | b'e' | b'E' | b'+' | b'-') {
|
|
141
|
-
self.i += 1;
|
|
142
|
-
} else {
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
let text = core::str::from_utf8(&self.b[start..self.i]).map_err(|_| self.err("bad number"))?;
|
|
147
|
-
parse_decimal(text).map(Val::Num).ok_or_else(|| self.err("bad number"))
|
|
148
|
-
}
|
|
149
|
-
fn string(&mut self) -> Result<String, Val> {
|
|
150
|
-
self.i += 1; // opening quote
|
|
151
|
-
let mut out: Vec<u8> = Vec::new();
|
|
152
|
-
loop {
|
|
153
|
-
let c = match self.peek() {
|
|
154
|
-
None => return Err(self.err("Unterminated string")),
|
|
155
|
-
Some(c) => c,
|
|
156
|
-
};
|
|
157
|
-
self.i += 1;
|
|
158
|
-
match c {
|
|
159
|
-
b'"' => break,
|
|
160
|
-
b'\\' => {
|
|
161
|
-
let e = self.peek().ok_or_else(|| self.err("Unterminated string"))?;
|
|
162
|
-
self.i += 1;
|
|
163
|
-
match e {
|
|
164
|
-
b'"' => out.push(b'"'),
|
|
165
|
-
b'\\' => out.push(b'\\'),
|
|
166
|
-
b'/' => out.push(b'/'),
|
|
167
|
-
b'b' => out.push(8),
|
|
168
|
-
b'f' => out.push(12),
|
|
169
|
-
b'n' => out.push(b'\n'),
|
|
170
|
-
b'r' => out.push(b'\r'),
|
|
171
|
-
b't' => out.push(b'\t'),
|
|
172
|
-
b'u' => {
|
|
173
|
-
let mut cp = self.hex4()?;
|
|
174
|
-
if (0xD800..0xDC00).contains(&cp) && self.b[self.i..].starts_with(b"\\u") {
|
|
175
|
-
self.i += 2;
|
|
176
|
-
let lo = self.hex4()?;
|
|
177
|
-
if (0xDC00..0xE000).contains(&lo) {
|
|
178
|
-
cp = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
let ch = char::from_u32(cp).unwrap_or('\u{FFFD}');
|
|
182
|
-
let mut buf = [0u8; 4];
|
|
183
|
-
out.extend_from_slice(ch.encode_utf8(&mut buf).as_bytes());
|
|
184
|
-
}
|
|
185
|
-
_ => return Err(self.err("Bad escaped character")),
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
_ => out.push(c),
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
String::from_utf8(out).map_err(|_| self.err("Bad UTF-8 in string"))
|
|
192
|
-
}
|
|
193
|
-
fn hex4(&mut self) -> Result<u32, Val> {
|
|
194
|
-
if self.i + 4 > self.b.len() {
|
|
195
|
-
return Err(self.err("Bad unicode escape"));
|
|
196
|
-
}
|
|
197
|
-
let mut v: u32 = 0;
|
|
198
|
-
for &c in &self.b[self.i..self.i + 4] {
|
|
199
|
-
v = v * 16 + match (c as char).to_digit(16) { Some(d) => d, None => return Err(self.err("Bad unicode escape")) };
|
|
200
|
-
}
|
|
201
|
-
self.i += 4;
|
|
202
|
-
Ok(v)
|
|
203
|
-
}
|
|
204
|
-
fn array(&mut self) -> Result<Val, Val> {
|
|
205
|
-
self.i += 1;
|
|
206
|
-
let mut items = Vec::new();
|
|
207
|
-
self.ws();
|
|
208
|
-
if self.peek() == Some(b']') {
|
|
209
|
-
self.i += 1;
|
|
210
|
-
return Ok(Val::Arr(items));
|
|
211
|
-
}
|
|
212
|
-
loop {
|
|
213
|
-
self.ws();
|
|
214
|
-
items.push(self.value()?);
|
|
215
|
-
self.ws();
|
|
216
|
-
match self.peek() {
|
|
217
|
-
Some(b',') => self.i += 1,
|
|
218
|
-
Some(b']') => {
|
|
219
|
-
self.i += 1;
|
|
220
|
-
return Ok(Val::Arr(items));
|
|
221
|
-
}
|
|
222
|
-
_ => return Err(self.err("Expected ',' or ']'")),
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
fn object(&mut self) -> Result<Val, Val> {
|
|
227
|
-
self.i += 1;
|
|
228
|
-
let mut kv: Vec<(String, Val)> = Vec::new();
|
|
229
|
-
self.ws();
|
|
230
|
-
if self.peek() == Some(b'}') {
|
|
231
|
-
self.i += 1;
|
|
232
|
-
return Ok(Val::Obj(kv));
|
|
233
|
-
}
|
|
234
|
-
loop {
|
|
235
|
-
self.ws();
|
|
236
|
-
if self.peek() != Some(b'"') {
|
|
237
|
-
return Err(self.err("Expected property name"));
|
|
238
|
-
}
|
|
239
|
-
let k = self.string()?;
|
|
240
|
-
self.ws();
|
|
241
|
-
if self.peek() != Some(b':') {
|
|
242
|
-
return Err(self.err("Expected ':'"));
|
|
243
|
-
}
|
|
244
|
-
self.i += 1;
|
|
245
|
-
self.ws();
|
|
246
|
-
let v = self.value()?;
|
|
247
|
-
if let Some(slot) = kv.iter_mut().find(|(kk, _)| *kk == k) {
|
|
248
|
-
slot.1 = v;
|
|
249
|
-
} else {
|
|
250
|
-
kv.push((k, v));
|
|
251
|
-
}
|
|
252
|
-
self.ws();
|
|
253
|
-
match self.peek() {
|
|
254
|
-
Some(b',') => self.i += 1,
|
|
255
|
-
Some(b'}') => {
|
|
256
|
-
self.i += 1;
|
|
257
|
-
return Ok(Val::Obj(kv));
|
|
258
|
-
}
|
|
259
|
-
_ => return Err(self.err("Expected ',' or '}'")),
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|