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.
Files changed (55) hide show
  1. package/README.md +12 -1
  2. package/bin/openzoo.js +21 -46
  3. package/lib/chatgpt-install.js +106 -0
  4. package/lib/chatgpt.js +108 -90
  5. package/lib/config.js +34 -0
  6. package/lib/modelcommand.js +443 -0
  7. package/lib/pay.js +60 -4
  8. package/lib/proxy.js +39 -0
  9. package/package.json +4 -7
  10. package/transmute/README.md +0 -385
  11. package/transmute/bin/openzoo-transmute.js +0 -7
  12. package/transmute/docs/OPENZOO_CLI_PATCH.md +0 -139
  13. package/transmute/docs/VERCEL_TO_SOLANA.md +0 -381
  14. package/transmute/lib/build.js +0 -381
  15. package/transmute/lib/cli.js +0 -293
  16. package/transmute/lib/compile/bytecode.js +0 -591
  17. package/transmute/lib/compile/hostids.js +0 -11
  18. package/transmute/lib/compile/index.js +0 -159
  19. package/transmute/lib/compile/ir.js +0 -1634
  20. package/transmute/lib/compile/parse.js +0 -753
  21. package/transmute/lib/compile/rust.js +0 -781
  22. package/transmute/lib/deploy.js +0 -357
  23. package/transmute/lib/eligibility.js +0 -211
  24. package/transmute/lib/gateway.js +0 -710
  25. package/transmute/lib/hub.js +0 -290
  26. package/transmute/lib/solana.js +0 -387
  27. package/transmute/lib/vercel.js +0 -296
  28. package/transmute/lib/wallet.js +0 -41
  29. package/transmute/lib/wire.js +0 -208
  30. package/transmute/prebuilt/zoo_vm.v0.so +0 -0
  31. package/transmute/runtime/zoo-host/Cargo.lock +0 -588
  32. package/transmute/runtime/zoo-host/Cargo.toml +0 -30
  33. package/transmute/runtime/zoo-host/examples/vmrun.rs +0 -43
  34. package/transmute/runtime/zoo-host/src/assets.rs +0 -214
  35. package/transmute/runtime/zoo-host/src/ctx.rs +0 -350
  36. package/transmute/runtime/zoo-host/src/fmt.rs +0 -188
  37. package/transmute/runtime/zoo-host/src/helpers.rs +0 -245
  38. package/transmute/runtime/zoo-host/src/json.rs +0 -263
  39. package/transmute/runtime/zoo-host/src/kv.rs +0 -206
  40. package/transmute/runtime/zoo-host/src/lib.rs +0 -137
  41. package/transmute/runtime/zoo-host/src/site.rs +0 -94
  42. package/transmute/runtime/zoo-host/src/val.rs +0 -1050
  43. package/transmute/runtime/zoo-host/src/vm.rs +0 -902
  44. package/transmute/runtime/zoo-host/src/wire.rs +0 -239
  45. package/transmute/runtime/zoo-vm/Cargo.lock +0 -596
  46. package/transmute/runtime/zoo-vm/Cargo.toml +0 -23
  47. package/transmute/runtime/zoo-vm/src/lib.rs +0 -92
  48. package/transmute/site/.env.production +0 -2
  49. package/transmute/site/api/echo.js +0 -10
  50. package/transmute/site/api/hello.js +0 -12
  51. package/transmute/site/api/hits.js +0 -13
  52. package/transmute/site/api/time.js +0 -5
  53. package/transmute/site/package.json +0 -6
  54. package/transmute/site/public/app.js +0 -16
  55. package/transmute/site/public/index.html +0 -96
@@ -1,1050 +0,0 @@
1
- //! A JS-semantics dynamic value. Every expression in a transmuted handler
2
- //! evaluates to a `Val`; the operators here follow ECMAScript coercion rules
3
- //! for the subset the transmuter accepts.
4
- use alloc::{string::String, vec::Vec};
5
- use crate::fmt::{push_f64, push_hex2, pow10};
6
- use core::cmp::Ordering;
7
-
8
- #[derive(Clone)]
9
- pub enum Val {
10
- Undef,
11
- Null,
12
- Bool(bool),
13
- Num(f64),
14
- Str(String),
15
- Arr(Vec<Val>),
16
- Obj(Vec<(String, Val)>),
17
- }
18
-
19
- impl Default for Val {
20
- fn default() -> Self {
21
- Val::Undef
22
- }
23
- }
24
-
25
- impl From<&str> for Val {
26
- fn from(s: &str) -> Self {
27
- Val::Str(String::from(s))
28
- }
29
- }
30
- impl From<String> for Val {
31
- fn from(s: String) -> Self {
32
- Val::Str(s)
33
- }
34
- }
35
- impl From<f64> for Val {
36
- fn from(n: f64) -> Self {
37
- Val::Num(n)
38
- }
39
- }
40
- impl From<i64> for Val {
41
- fn from(n: i64) -> Self {
42
- Val::Num(n as f64)
43
- }
44
- }
45
- impl From<bool> for Val {
46
- fn from(b: bool) -> Self {
47
- Val::Bool(b)
48
- }
49
- }
50
-
51
- /// JS Number::toString for the common cases (see fmt::push_f64).
52
- pub fn num_to_string(n: f64) -> String {
53
- let mut s = String::new();
54
- push_f64(&mut s, n);
55
- s
56
- }
57
-
58
- /// JS ToNumber for strings.
59
- pub fn str_to_num(s: &str) -> f64 {
60
- let t = s.trim();
61
- if t.is_empty() {
62
- return 0.0;
63
- }
64
- if let Some(h) = t.strip_prefix("0x").or_else(|| t.strip_prefix("0X")) {
65
- let mut v: f64 = 0.0;
66
- for c in h.chars() {
67
- match c.to_digit(16) { Some(d) => v = v * 16.0 + d as f64, None => return f64::NAN }
68
- }
69
- return v;
70
- }
71
- match t {
72
- "Infinity" | "+Infinity" => return f64::INFINITY,
73
- "-Infinity" => return f64::NEG_INFINITY,
74
- _ => {}
75
- }
76
- parse_decimal(t).unwrap_or(f64::NAN)
77
- }
78
-
79
- /// A small decimal parser (no_std has no `str::parse::<f64>` we can trust to be
80
- /// tiny on SBF; this one is enough for JSON and querystrings).
81
- pub fn parse_decimal(s: &str) -> Option<f64> {
82
- let b = s.as_bytes();
83
- let mut i = 0;
84
- let mut neg = false;
85
- if i < b.len() && (b[i] == b'+' || b[i] == b'-') {
86
- neg = b[i] == b'-';
87
- i += 1;
88
- }
89
- let mut mant: f64 = 0.0;
90
- let mut digits = 0;
91
- while i < b.len() && b[i].is_ascii_digit() {
92
- mant = mant * 10.0 + (b[i] - b'0') as f64;
93
- i += 1;
94
- digits += 1;
95
- }
96
- let mut frac_exp: i32 = 0;
97
- if i < b.len() && b[i] == b'.' {
98
- i += 1;
99
- while i < b.len() && b[i].is_ascii_digit() {
100
- mant = mant * 10.0 + (b[i] - b'0') as f64;
101
- frac_exp -= 1;
102
- i += 1;
103
- digits += 1;
104
- }
105
- }
106
- if digits == 0 {
107
- return None;
108
- }
109
- let mut exp: i32 = 0;
110
- if i < b.len() && (b[i] == b'e' || b[i] == b'E') {
111
- i += 1;
112
- let mut eneg = false;
113
- if i < b.len() && (b[i] == b'+' || b[i] == b'-') {
114
- eneg = b[i] == b'-';
115
- i += 1;
116
- }
117
- let mut e: i32 = 0;
118
- let mut ed = 0;
119
- while i < b.len() && b[i].is_ascii_digit() {
120
- e = e.saturating_mul(10).saturating_add((b[i] - b'0') as i32);
121
- i += 1;
122
- ed += 1;
123
- }
124
- if ed == 0 {
125
- return None;
126
- }
127
- exp = if eneg { -e } else { e };
128
- }
129
- if i != b.len() {
130
- return None;
131
- }
132
- let total = frac_exp + exp;
133
- let v = if total >= 0 {
134
- mant * pow10(total as u32)
135
- } else {
136
- mant / pow10((-total) as u32)
137
- };
138
- Some(if neg { -v } else { v })
139
- }
140
-
141
- impl Val {
142
- pub fn obj() -> Val {
143
- Val::Obj(Vec::new())
144
- }
145
- pub fn arr() -> Val {
146
- Val::Arr(Vec::new())
147
- }
148
- pub fn str(s: &str) -> Val {
149
- Val::Str(String::from(s))
150
- }
151
-
152
- pub fn is_undef(&self) -> bool {
153
- matches!(self, Val::Undef)
154
- }
155
- pub fn is_nullish(&self) -> bool {
156
- matches!(self, Val::Undef | Val::Null)
157
- }
158
-
159
- pub fn type_of(&self) -> &'static str {
160
- match self {
161
- Val::Undef => "undefined",
162
- Val::Null => "object",
163
- Val::Bool(_) => "boolean",
164
- Val::Num(_) => "number",
165
- Val::Str(_) => "string",
166
- Val::Arr(_) | Val::Obj(_) => "object",
167
- }
168
- }
169
-
170
- pub fn truthy(&self) -> bool {
171
- match self {
172
- Val::Undef | Val::Null => false,
173
- Val::Bool(b) => *b,
174
- Val::Num(n) => !(*n == 0.0 || n.is_nan()),
175
- Val::Str(s) => !s.is_empty(),
176
- Val::Arr(_) | Val::Obj(_) => true,
177
- }
178
- }
179
-
180
- pub fn to_num(&self) -> f64 {
181
- match self {
182
- Val::Undef => f64::NAN,
183
- Val::Null => 0.0,
184
- Val::Bool(b) => {
185
- if *b {
186
- 1.0
187
- } else {
188
- 0.0
189
- }
190
- }
191
- Val::Num(n) => *n,
192
- Val::Str(s) => str_to_num(s),
193
- Val::Arr(a) => match a.len() {
194
- 0 => 0.0,
195
- 1 => a[0].to_num(),
196
- _ => f64::NAN,
197
- },
198
- Val::Obj(_) => f64::NAN,
199
- }
200
- }
201
-
202
- /// JS ToString (objects stringify like `String(obj)` would; arrays join).
203
- pub fn to_js_string(&self) -> String {
204
- match self {
205
- Val::Undef => String::from("undefined"),
206
- Val::Null => String::from("null"),
207
- Val::Bool(b) => String::from(if *b { "true" } else { "false" }),
208
- Val::Num(n) => num_to_string(*n),
209
- Val::Str(s) => s.clone(),
210
- Val::Arr(a) => {
211
- let mut out = String::new();
212
- for (i, v) in a.iter().enumerate() {
213
- if i > 0 {
214
- out.push(',');
215
- }
216
- if !v.is_nullish() {
217
- out.push_str(&v.to_js_string());
218
- }
219
- }
220
- out
221
- }
222
- Val::Obj(_) => String::from("[object Object]"),
223
- }
224
- }
225
-
226
- pub fn as_str(&self) -> Option<&str> {
227
- match self {
228
- Val::Str(s) => Some(s.as_str()),
229
- _ => None,
230
- }
231
- }
232
-
233
- /// Property read with a string key (`obj.key`, `arr.length`, `str.length`).
234
- pub fn get_str(&self, key: &str) -> Val {
235
- match self {
236
- Val::Obj(kv) => kv
237
- .iter()
238
- .find(|(k, _)| k == key)
239
- .map(|(_, v)| v.clone())
240
- .unwrap_or(Val::Undef),
241
- Val::Arr(a) => {
242
- if key == "length" {
243
- return Val::Num(a.len() as f64);
244
- }
245
- match parse_index(key) {
246
- Some(i) => a.get(i).cloned().unwrap_or(Val::Undef),
247
- None => Val::Undef,
248
- }
249
- }
250
- Val::Str(s) => {
251
- if key == "length" {
252
- return Val::Num(s.chars().count() as f64);
253
- }
254
- match parse_index(key) {
255
- Some(i) => s
256
- .chars()
257
- .nth(i)
258
- .map(|c| Val::Str(c.into()))
259
- .unwrap_or(Val::Undef),
260
- None => Val::Undef,
261
- }
262
- }
263
- _ => Val::Undef,
264
- }
265
- }
266
-
267
- /// Property read with a computed key (`a[b]`).
268
- pub fn get(&self, key: &Val) -> Val {
269
- match (self, key) {
270
- (Val::Arr(a), Val::Num(n)) => {
271
- if *n >= 0.0 && *n == libm::trunc(*n) {
272
- a.get(*n as usize).cloned().unwrap_or(Val::Undef)
273
- } else {
274
- Val::Undef
275
- }
276
- }
277
- (Val::Str(s), Val::Num(n)) => {
278
- if *n >= 0.0 && *n == libm::trunc(*n) {
279
- s.chars()
280
- .nth(*n as usize)
281
- .map(|c| Val::Str(c.into()))
282
- .unwrap_or(Val::Undef)
283
- } else {
284
- Val::Undef
285
- }
286
- }
287
- _ => self.get_str(&key.to_js_string()),
288
- }
289
- }
290
-
291
- /// Property write (`obj.k = v`, `arr[i] = v`).
292
- pub fn set(&mut self, key: &Val, v: Val) {
293
- match self {
294
- Val::Obj(kv) => {
295
- let k = key.to_js_string();
296
- if let Some(slot) = kv.iter_mut().find(|(kk, _)| *kk == k) {
297
- slot.1 = v;
298
- } else {
299
- kv.push((k, v));
300
- }
301
- }
302
- Val::Arr(a) => {
303
- let idx = match key {
304
- Val::Num(n) if *n >= 0.0 && *n == libm::trunc(*n) => Some(*n as usize),
305
- Val::Str(s) => parse_index(s),
306
- _ => None,
307
- };
308
- if let Some(i) = idx {
309
- if i >= a.len() {
310
- a.resize(i + 1, Val::Undef);
311
- }
312
- a[i] = v;
313
- }
314
- }
315
- _ => {}
316
- }
317
- }
318
-
319
- pub fn set_str(&mut self, key: &str, v: Val) {
320
- self.set(&Val::str(key), v)
321
- }
322
-
323
- pub fn delete(&mut self, key: &Val) {
324
- if let Val::Obj(kv) = self {
325
- let k = key.to_js_string();
326
- kv.retain(|(kk, _)| *kk != k);
327
- }
328
- }
329
-
330
- pub fn has(&self, key: &Val) -> bool {
331
- match self {
332
- Val::Obj(kv) => {
333
- let k = key.to_js_string();
334
- kv.iter().any(|(kk, _)| *kk == k)
335
- }
336
- Val::Arr(a) => match key {
337
- Val::Num(n) => *n >= 0.0 && (*n as usize) < a.len(),
338
- _ => key.to_js_string() == "length",
339
- },
340
- _ => false,
341
- }
342
- }
343
-
344
- pub fn keys(&self) -> Val {
345
- match self {
346
- Val::Obj(kv) => Val::Arr(kv.iter().map(|(k, _)| Val::Str(k.clone())).collect()),
347
- Val::Arr(a) => Val::Arr((0..a.len()).map(|i| Val::Str(num_to_string(i as f64))).collect()),
348
- _ => Val::arr(),
349
- }
350
- }
351
- pub fn values(&self) -> Val {
352
- match self {
353
- Val::Obj(kv) => Val::Arr(kv.iter().map(|(_, v)| v.clone()).collect()),
354
- Val::Arr(a) => Val::Arr(a.clone()),
355
- _ => Val::arr(),
356
- }
357
- }
358
- pub fn entries(&self) -> Val {
359
- match self {
360
- Val::Obj(kv) => Val::Arr(
361
- kv.iter()
362
- .map(|(k, v)| Val::Arr(alloc::vec![Val::Str(k.clone()), v.clone()]))
363
- .collect(),
364
- ),
365
- Val::Arr(a) => Val::Arr(
366
- a.iter()
367
- .enumerate()
368
- .map(|(i, v)| Val::Arr(alloc::vec![Val::Num(i as f64), v.clone()]))
369
- .collect(),
370
- ),
371
- _ => Val::arr(),
372
- }
373
- }
374
-
375
- /// Iteration order for `for (const x of v)`.
376
- pub fn iter_values(&self) -> Vec<Val> {
377
- match self {
378
- Val::Arr(a) => a.clone(),
379
- Val::Str(s) => s.chars().map(|c| Val::Str(c.into())).collect(),
380
- _ => Vec::new(),
381
- }
382
- }
383
-
384
- pub fn push(&mut self, v: Val) -> Val {
385
- if let Val::Arr(a) = self {
386
- a.push(v);
387
- Val::Num(a.len() as f64)
388
- } else {
389
- Val::Undef
390
- }
391
- }
392
-
393
- // ---- operators ----
394
-
395
- pub fn add(&self, other: &Val) -> Val {
396
- let lstr = matches!(self, Val::Str(_) | Val::Arr(_) | Val::Obj(_));
397
- let rstr = matches!(other, Val::Str(_) | Val::Arr(_) | Val::Obj(_));
398
- if lstr || rstr {
399
- let mut s = self.to_js_string();
400
- s.push_str(&other.to_js_string());
401
- Val::Str(s)
402
- } else {
403
- Val::Num(self.to_num() + other.to_num())
404
- }
405
- }
406
- pub fn sub(&self, o: &Val) -> Val {
407
- Val::Num(self.to_num() - o.to_num())
408
- }
409
- pub fn mul(&self, o: &Val) -> Val {
410
- Val::Num(self.to_num() * o.to_num())
411
- }
412
- pub fn div(&self, o: &Val) -> Val {
413
- Val::Num(self.to_num() / o.to_num())
414
- }
415
- pub fn rem(&self, o: &Val) -> Val {
416
- Val::Num(libm::fmod(self.to_num(), o.to_num()))
417
- }
418
- pub fn pow(&self, o: &Val) -> Val {
419
- Val::Num(js_pow(self.to_num(), o.to_num()))
420
- }
421
- pub fn neg(&self) -> Val {
422
- Val::Num(-self.to_num())
423
- }
424
- pub fn not(&self) -> Val {
425
- Val::Bool(!self.truthy())
426
- }
427
- pub fn bit_and(&self, o: &Val) -> Val {
428
- Val::Num((to_i32(self.to_num()) & to_i32(o.to_num())) as f64)
429
- }
430
- pub fn bit_or(&self, o: &Val) -> Val {
431
- Val::Num((to_i32(self.to_num()) | to_i32(o.to_num())) as f64)
432
- }
433
- pub fn bit_xor(&self, o: &Val) -> Val {
434
- Val::Num((to_i32(self.to_num()) ^ to_i32(o.to_num())) as f64)
435
- }
436
- pub fn shl(&self, o: &Val) -> Val {
437
- Val::Num(to_i32(self.to_num()).wrapping_shl(to_i32(o.to_num()) as u32 & 31) as f64)
438
- }
439
- pub fn shr(&self, o: &Val) -> Val {
440
- Val::Num((to_i32(self.to_num()) >> (to_i32(o.to_num()) as u32 & 31)) as f64)
441
- }
442
- pub fn ushr(&self, o: &Val) -> Val {
443
- Val::Num(((to_i32(self.to_num()) as u32) >> (to_i32(o.to_num()) as u32 & 31)) as f64)
444
- }
445
-
446
- pub fn strict_eq(&self, o: &Val) -> bool {
447
- match (self, o) {
448
- (Val::Undef, Val::Undef) | (Val::Null, Val::Null) => true,
449
- (Val::Bool(a), Val::Bool(b)) => a == b,
450
- (Val::Num(a), Val::Num(b)) => a == b,
451
- (Val::Str(a), Val::Str(b)) => a == b,
452
- // Reference equality is not observable in our value model; two
453
- // structurally equal literals are treated as equal.
454
- (Val::Arr(a), Val::Arr(b)) => a.len() == b.len() && a.iter().zip(b).all(|(x, y)| x.strict_eq(y)),
455
- (Val::Obj(a), Val::Obj(b)) => {
456
- a.len() == b.len()
457
- && a.iter().all(|(k, v)| b.iter().any(|(k2, v2)| k == k2 && v.strict_eq(v2)))
458
- }
459
- _ => false,
460
- }
461
- }
462
-
463
- pub fn loose_eq(&self, o: &Val) -> bool {
464
- match (self, o) {
465
- (Val::Undef | Val::Null, Val::Undef | Val::Null) => true,
466
- (Val::Undef | Val::Null, _) | (_, Val::Undef | Val::Null) => false,
467
- (Val::Num(_), Val::Str(_)) | (Val::Str(_), Val::Num(_)) => self.to_num() == o.to_num(),
468
- (Val::Bool(_), _) => Val::Num(self.to_num()).loose_eq(o),
469
- (_, Val::Bool(_)) => self.loose_eq(&Val::Num(o.to_num())),
470
- (Val::Arr(_) | Val::Obj(_), Val::Num(_) | Val::Str(_)) => Val::Str(self.to_js_string()).loose_eq(o),
471
- (Val::Num(_) | Val::Str(_), Val::Arr(_) | Val::Obj(_)) => self.loose_eq(&Val::Str(o.to_js_string())),
472
- _ => self.strict_eq(o),
473
- }
474
- }
475
-
476
- fn compare(&self, o: &Val) -> Option<Ordering> {
477
- match (self, o) {
478
- (Val::Str(a), Val::Str(b)) => Some(a.cmp(b)),
479
- _ => {
480
- let (a, b) = (self.to_num(), o.to_num());
481
- a.partial_cmp(&b)
482
- }
483
- }
484
- }
485
- pub fn lt(&self, o: &Val) -> bool {
486
- matches!(self.compare(o), Some(Ordering::Less))
487
- }
488
- pub fn le(&self, o: &Val) -> bool {
489
- matches!(self.compare(o), Some(Ordering::Less | Ordering::Equal))
490
- }
491
- pub fn gt(&self, o: &Val) -> bool {
492
- matches!(self.compare(o), Some(Ordering::Greater))
493
- }
494
- pub fn ge(&self, o: &Val) -> bool {
495
- matches!(self.compare(o), Some(Ordering::Greater | Ordering::Equal))
496
- }
497
-
498
- pub fn r#in(&self, o: &Val) -> bool {
499
- o.has(self)
500
- }
501
-
502
- // ---- built-in methods (`recv.name(args)`) ----
503
-
504
- /// Method call on a value. Higher-order methods (map/filter/...) take
505
- /// their callback through `cb`.
506
- pub fn call(&mut self, name: &str, args: &[Val], cb: Option<&mut dyn FnMut(&[Val]) -> Val>) -> Result<Val, Val> {
507
- let arg = |i: usize| args.get(i).cloned().unwrap_or(Val::Undef);
508
- match self {
509
- Val::Str(s) => Ok(str_method(s, name, args)?),
510
- Val::Num(n) => match name {
511
- "toFixed" => {
512
- let d = if arg(0).is_undef() { 0 } else { arg(0).to_num() as usize };
513
- Ok(Val::Str(to_fixed(*n, d)))
514
- }
515
- "toString" => Ok(Val::Str(num_to_string(*n))),
516
- _ => Err(type_error(name)),
517
- },
518
- Val::Bool(b) => match name {
519
- "toString" => Ok(Val::Str(String::from(if *b { "true" } else { "false" }))),
520
- _ => Err(type_error(name)),
521
- },
522
- Val::Arr(a) => {
523
- match name {
524
- "push" => {
525
- for v in args {
526
- a.push(v.clone());
527
- }
528
- Ok(Val::Num(a.len() as f64))
529
- }
530
- "pop" => Ok(a.pop().unwrap_or(Val::Undef)),
531
- "shift" => Ok(if a.is_empty() { Val::Undef } else { a.remove(0) }),
532
- "unshift" => {
533
- for (i, v) in args.iter().enumerate() {
534
- a.insert(i, v.clone());
535
- }
536
- Ok(Val::Num(a.len() as f64))
537
- }
538
- "join" => {
539
- let sep = if arg(0).is_undef() { String::from(",") } else { arg(0).to_js_string() };
540
- let parts: Vec<String> = a
541
- .iter()
542
- .map(|v| if v.is_nullish() { String::new() } else { v.to_js_string() })
543
- .collect();
544
- Ok(Val::Str(parts.join(&sep)))
545
- }
546
- "includes" => Ok(Val::Bool(a.iter().any(|v| v.strict_eq(&arg(0))))),
547
- "indexOf" => Ok(Val::Num(
548
- a.iter().position(|v| v.strict_eq(&arg(0))).map(|i| i as f64).unwrap_or(-1.0),
549
- )),
550
- "slice" => {
551
- let (s, e) = slice_bounds(a.len(), &arg(0), &arg(1));
552
- Ok(Val::Arr(a[s..e].to_vec()))
553
- }
554
- "splice" => {
555
- let start = clamp_index(a.len(), &arg(0));
556
- let del = if arg(1).is_undef() { a.len() - start } else { (arg(1).to_num().max(0.0) as usize).min(a.len() - start) };
557
- let removed: Vec<Val> = a.drain(start..start + del).collect();
558
- for (i, v) in args.iter().skip(2).enumerate() {
559
- a.insert(start + i, v.clone());
560
- }
561
- Ok(Val::Arr(removed))
562
- }
563
- "concat" => {
564
- let mut out = a.clone();
565
- for v in args {
566
- match v {
567
- Val::Arr(b) => out.extend(b.iter().cloned()),
568
- other => out.push(other.clone()),
569
- }
570
- }
571
- Ok(Val::Arr(out))
572
- }
573
- "reverse" => {
574
- a.reverse();
575
- Ok(Val::Arr(a.clone()))
576
- }
577
- "flat" => {
578
- let mut out = Vec::new();
579
- for v in a.iter() {
580
- match v {
581
- Val::Arr(b) => out.extend(b.iter().cloned()),
582
- other => out.push(other.clone()),
583
- }
584
- }
585
- Ok(Val::Arr(out))
586
- }
587
- "toString" => Ok(Val::Str(self.to_js_string())),
588
- "map" | "filter" | "find" | "findIndex" | "some" | "every" | "forEach" | "reduce" | "flatMap" | "sort" => {
589
- let f = match cb {
590
- Some(f) => f,
591
- None => return Err(type_error(name)),
592
- };
593
- let items = a.clone();
594
- match name {
595
- "map" => {
596
- let mut out = Vec::with_capacity(items.len());
597
- for (i, v) in items.iter().enumerate() {
598
- out.push(f(&[v.clone(), Val::Num(i as f64)]));
599
- }
600
- Ok(Val::Arr(out))
601
- }
602
- "flatMap" => {
603
- let mut out = Vec::new();
604
- for (i, v) in items.iter().enumerate() {
605
- match f(&[v.clone(), Val::Num(i as f64)]) {
606
- Val::Arr(b) => out.extend(b),
607
- o => out.push(o),
608
- }
609
- }
610
- Ok(Val::Arr(out))
611
- }
612
- "filter" => {
613
- let mut out = Vec::new();
614
- for (i, v) in items.iter().enumerate() {
615
- if f(&[v.clone(), Val::Num(i as f64)]).truthy() {
616
- out.push(v.clone());
617
- }
618
- }
619
- Ok(Val::Arr(out))
620
- }
621
- "find" => {
622
- for (i, v) in items.iter().enumerate() {
623
- if f(&[v.clone(), Val::Num(i as f64)]).truthy() {
624
- return Ok(v.clone());
625
- }
626
- }
627
- Ok(Val::Undef)
628
- }
629
- "findIndex" => {
630
- for (i, v) in items.iter().enumerate() {
631
- if f(&[v.clone(), Val::Num(i as f64)]).truthy() {
632
- return Ok(Val::Num(i as f64));
633
- }
634
- }
635
- Ok(Val::Num(-1.0))
636
- }
637
- "some" => Ok(Val::Bool(
638
- items.iter().enumerate().any(|(i, v)| f(&[v.clone(), Val::Num(i as f64)]).truthy()),
639
- )),
640
- "every" => Ok(Val::Bool(
641
- items.iter().enumerate().all(|(i, v)| f(&[v.clone(), Val::Num(i as f64)]).truthy()),
642
- )),
643
- "forEach" => {
644
- for (i, v) in items.iter().enumerate() {
645
- f(&[v.clone(), Val::Num(i as f64)]);
646
- }
647
- Ok(Val::Undef)
648
- }
649
- "reduce" => {
650
- let mut it = items.iter().enumerate();
651
- let mut acc = if args.is_empty() {
652
- match it.next() {
653
- Some((_, v)) => v.clone(),
654
- None => return Err(type_error("reduce of empty array with no initial value")),
655
- }
656
- } else {
657
- arg(0)
658
- };
659
- for (i, v) in it {
660
- acc = f(&[acc, v.clone(), Val::Num(i as f64)]);
661
- }
662
- Ok(acc)
663
- }
664
- "sort" => {
665
- let mut items = items;
666
- // insertion sort: stable, tiny, no core::slice::sort_by cost
667
- for i in 1..items.len() {
668
- let mut j = i;
669
- while j > 0 {
670
- let r = f(&[items[j - 1].clone(), items[j].clone()]).to_num();
671
- if r > 0.0 {
672
- items.swap(j - 1, j);
673
- j -= 1;
674
- } else {
675
- break;
676
- }
677
- }
678
- }
679
- *a = items.clone();
680
- Ok(Val::Arr(items))
681
- }
682
- _ => unreachable!(),
683
- }
684
- }
685
- _ => Err(type_error(name)),
686
- }
687
- }
688
- Val::Obj(kv) => match name {
689
- "hasOwnProperty" => Ok(Val::Bool(kv.iter().any(|(k, _)| *k == arg(0).to_js_string()))),
690
- "toString" => Ok(Val::Str(self.to_js_string())),
691
- _ => Err(type_error(name)),
692
- },
693
- Val::Undef | Val::Null => {
694
- let mut m = String::from("TypeError: Cannot read properties of ");
695
- m.push_str(&self.to_js_string());
696
- m.push_str(" (reading '");
697
- m.push_str(name);
698
- m.push_str("')");
699
- Err(Val::Str(m))
700
- }
701
- }
702
- }
703
- }
704
-
705
- fn type_error(name: &str) -> Val {
706
- let mut m = String::from("TypeError: ");
707
- m.push_str(name);
708
- m.push_str(" is not a function");
709
- Val::Str(m)
710
- }
711
-
712
- /// `**` / Math.pow: exact for integer exponents (a loop), libm only with the
713
- /// `mathx` feature.
714
- pub fn js_pow(base: f64, exp: f64) -> f64 {
715
- if exp == libm::trunc(exp) && libm::fabs(exp) <= 64.0 {
716
- let mut r = 1.0;
717
- let n = libm::fabs(exp) as u32;
718
- for _ in 0..n {
719
- r *= base;
720
- }
721
- return if exp < 0.0 { 1.0 / r } else { r };
722
- }
723
- #[cfg(feature = "mathx")]
724
- { libm::pow(base, exp) }
725
- #[cfg(not(feature = "mathx"))]
726
- { f64::NAN }
727
- }
728
-
729
- fn to_i32(n: f64) -> i32 {
730
- if n.is_nan() || n.is_infinite() {
731
- 0
732
- } else {
733
- (libm::trunc(n) as i64 as u64 as u32) as i32
734
- }
735
- }
736
-
737
- fn parse_index(s: &str) -> Option<usize> {
738
- if s.is_empty() || s.len() > 10 || !s.bytes().all(|b| b.is_ascii_digit()) {
739
- return None;
740
- }
741
- if s.len() > 1 && s.starts_with('0') {
742
- return None;
743
- }
744
- let mut v: usize = 0;
745
- for b in s.bytes() { v = v * 10 + (b - b'0') as usize; }
746
- Some(v)
747
- }
748
-
749
- fn clamp_index(len: usize, v: &Val) -> usize {
750
- if v.is_undef() {
751
- return 0;
752
- }
753
- let n = v.to_num();
754
- if n.is_nan() {
755
- return 0;
756
- }
757
- if n < 0.0 {
758
- let back = (-n) as usize;
759
- len.saturating_sub(back)
760
- } else {
761
- (n as usize).min(len)
762
- }
763
- }
764
-
765
- fn slice_bounds(len: usize, start: &Val, end: &Val) -> (usize, usize) {
766
- let s = clamp_index(len, start);
767
- let e = if end.is_undef() { len } else { clamp_index(len, end) };
768
- (s, e.max(s))
769
- }
770
-
771
- pub fn to_fixed(n: f64, digits: usize) -> String {
772
- crate::fmt::to_fixed(n, digits)
773
- }
774
-
775
- fn str_method(s: &mut String, name: &str, args: &[Val]) -> Result<Val, Val> {
776
- let arg = |i: usize| args.get(i).cloned().unwrap_or(Val::Undef);
777
- let chars: Vec<char> = s.chars().collect();
778
- Ok(match name {
779
- "toUpperCase" => Val::Str(s.to_ascii_uppercase()),
780
- "toLowerCase" => Val::Str(s.to_ascii_lowercase()),
781
- "trim" => Val::Str(String::from(s.trim())),
782
- "trimStart" => Val::Str(String::from(s.trim_start())),
783
- "trimEnd" => Val::Str(String::from(s.trim_end())),
784
- "includes" => Val::Bool(s.contains(arg(0).to_js_string().as_str())),
785
- "startsWith" => Val::Bool(s.starts_with(arg(0).to_js_string().as_str())),
786
- "endsWith" => Val::Bool(s.ends_with(arg(0).to_js_string().as_str())),
787
- "indexOf" => {
788
- let needle = arg(0).to_js_string();
789
- Val::Num(match s.find(&needle) {
790
- Some(b) => s[..b].chars().count() as f64,
791
- None => -1.0,
792
- })
793
- }
794
- "split" => {
795
- if arg(0).is_undef() {
796
- Val::Arr(alloc::vec![Val::Str(s.clone())])
797
- } else {
798
- let sep = arg(0).to_js_string();
799
- if sep.is_empty() {
800
- Val::Arr(chars.iter().map(|c| Val::Str((*c).into())).collect())
801
- } else {
802
- Val::Arr(s.split(sep.as_str()).map(|p| Val::Str(String::from(p))).collect())
803
- }
804
- }
805
- }
806
- "slice" => {
807
- let (a, b) = slice_bounds(chars.len(), &arg(0), &arg(1));
808
- Val::Str(chars[a..b].iter().collect())
809
- }
810
- "substring" => {
811
- let a = clamp_index(chars.len(), &arg(0).max0());
812
- let b = if arg(1).is_undef() { chars.len() } else { clamp_index(chars.len(), &arg(1).max0()) };
813
- let (a, b) = if a <= b { (a, b) } else { (b, a) };
814
- Val::Str(chars[a..b].iter().collect())
815
- }
816
- "substr" => {
817
- let a = clamp_index(chars.len(), &arg(0));
818
- let n = if arg(1).is_undef() { chars.len() - a } else { (arg(1).to_num().max(0.0) as usize).min(chars.len() - a) };
819
- Val::Str(chars[a..a + n].iter().collect())
820
- }
821
- "charAt" => {
822
- let i = if arg(0).is_undef() { 0.0 } else { arg(0).to_num() };
823
- Val::Str(if i >= 0.0 { chars.get(i as usize).map(|c| String::from(*c)).unwrap_or_default() } else { String::new() })
824
- }
825
- "charCodeAt" => {
826
- let i = if arg(0).is_undef() { 0.0 } else { arg(0).to_num() };
827
- Val::Num(if i >= 0.0 { chars.get(i as usize).map(|c| *c as u32 as f64).unwrap_or(f64::NAN) } else { f64::NAN })
828
- }
829
- "replace" | "replaceAll" => {
830
- let from = arg(0).to_js_string();
831
- let to = arg(1).to_js_string();
832
- if name == "replace" {
833
- Val::Str(s.replacen(from.as_str(), to.as_str(), 1))
834
- } else {
835
- Val::Str(s.replace(from.as_str(), to.as_str()))
836
- }
837
- }
838
- "repeat" => Val::Str(s.repeat(arg(0).to_num().max(0.0) as usize)),
839
- "padStart" | "padEnd" => {
840
- let target = arg(0).to_num().max(0.0) as usize;
841
- let pad = if arg(1).is_undef() { String::from(" ") } else { arg(1).to_js_string() };
842
- let cur = chars.len();
843
- if target <= cur || pad.is_empty() {
844
- Val::Str(s.clone())
845
- } else {
846
- let need = target - cur;
847
- let padc: Vec<char> = pad.chars().collect();
848
- let filler: String = (0..need).map(|i| padc[i % padc.len()]).collect();
849
- if name == "padStart" {
850
- Val::Str(filler + s)
851
- } else {
852
- Val::Str(s.clone() + &filler)
853
- }
854
- }
855
- }
856
- "toString" | "valueOf" => Val::Str(s.clone()),
857
- "concat" => {
858
- let mut out = s.clone();
859
- for a in args {
860
- out.push_str(&a.to_js_string());
861
- }
862
- Val::Str(out)
863
- }
864
- "at" => {
865
- let i = arg(0).to_num();
866
- let idx = if i < 0.0 { chars.len() as f64 + i } else { i };
867
- if idx >= 0.0 && (idx as usize) < chars.len() { Val::Str(chars[idx as usize].into()) } else { Val::Undef }
868
- }
869
- _ => return Err(type_error(name)),
870
- })
871
- }
872
-
873
- impl Val {
874
- fn max0(&self) -> Val {
875
- Val::Num(self.to_num().max(0.0))
876
- }
877
- }
878
-
879
- /// `Math.*` for the subset the transmuter accepts.
880
- pub fn math(name: &str, args: &[Val]) -> Result<Val, Val> {
881
- let a = |i: usize| args.get(i).map(|v| v.to_num()).unwrap_or(f64::NAN);
882
- Ok(Val::Num(match name {
883
- "floor" => libm::floor(a(0)),
884
- "ceil" => libm::ceil(a(0)),
885
- "round" => libm::floor(a(0) + 0.5),
886
- "trunc" => libm::trunc(a(0)),
887
- "abs" => libm::fabs(a(0)),
888
- "sqrt" => libm::sqrt(a(0)),
889
- "pow" => js_pow(a(0), a(1)),
890
- "sign" => {
891
- let x = a(0);
892
- if x > 0.0 { 1.0 } else if x < 0.0 { -1.0 } else { x }
893
- }
894
- #[cfg(feature = "mathx")]
895
- "log" => libm::log(a(0)),
896
- #[cfg(feature = "mathx")]
897
- "log2" => libm::log2(a(0)),
898
- #[cfg(feature = "mathx")]
899
- "log10" => libm::log10(a(0)),
900
- #[cfg(feature = "mathx")]
901
- "exp" => libm::exp(a(0)),
902
- #[cfg(feature = "mathx")]
903
- "sin" => libm::sin(a(0)),
904
- #[cfg(feature = "mathx")]
905
- "cos" => libm::cos(a(0)),
906
- #[cfg(feature = "mathx")]
907
- "tan" => libm::tan(a(0)),
908
- #[cfg(feature = "mathx")]
909
- "atan2" => libm::atan2(a(0), a(1)),
910
- "min" => args.iter().map(|v| v.to_num()).fold(f64::INFINITY, f64::min),
911
- "max" => args.iter().map(|v| v.to_num()).fold(f64::NEG_INFINITY, f64::max),
912
- "hypot" => libm::sqrt(args.iter().map(|v| v.to_num() * v.to_num()).sum()),
913
- _ => return Err(type_error(name)),
914
- }))
915
- }
916
-
917
- /// `Number(x)`, `parseInt`, `parseFloat`, `isNaN`, `String(x)`, `Boolean(x)`.
918
- pub fn global_call(name: &str, args: &[Val]) -> Result<Val, Val> {
919
- let arg = |i: usize| args.get(i).cloned().unwrap_or(Val::Undef);
920
- Ok(match name {
921
- "Number" => Val::Num(if args.is_empty() { 0.0 } else { arg(0).to_num() }),
922
- "String" => Val::Str(if args.is_empty() { String::new() } else { arg(0).to_js_string() }),
923
- "Boolean" => Val::Bool(arg(0).truthy()),
924
- "isNaN" => Val::Bool(arg(0).to_num().is_nan()),
925
- "isFinite" => Val::Bool(arg(0).to_num().is_finite()),
926
- "parseFloat" => {
927
- let s = arg(0).to_js_string();
928
- let t = s.trim();
929
- // longest numeric prefix
930
- let mut end = 0;
931
- let b = t.as_bytes();
932
- let mut seen_dot = false;
933
- let mut seen_e = false;
934
- while end < b.len() {
935
- let c = b[end];
936
- let ok = c.is_ascii_digit()
937
- || (c == b'.' && !seen_dot && !seen_e)
938
- || ((c == b'e' || c == b'E') && !seen_e && end > 0)
939
- || ((c == b'+' || c == b'-') && (end == 0 || matches!(b[end - 1], b'e' | b'E')));
940
- if !ok {
941
- break;
942
- }
943
- if c == b'.' {
944
- seen_dot = true;
945
- }
946
- if c == b'e' || c == b'E' {
947
- seen_e = true;
948
- }
949
- end += 1;
950
- }
951
- let mut v = f64::NAN;
952
- let mut e = end;
953
- while e > 0 {
954
- if let Some(x) = parse_decimal(&t[..e]) {
955
- v = x;
956
- break;
957
- }
958
- e -= 1;
959
- }
960
- Val::Num(v)
961
- }
962
- "parseInt" => {
963
- let s = arg(0).to_js_string();
964
- let radix = if arg(1).is_undef() { 10 } else { arg(1).to_num() as u32 };
965
- let t = s.trim();
966
- let (neg, t) = match t.strip_prefix('-') {
967
- Some(r) => (true, r),
968
- None => (false, t.strip_prefix('+').unwrap_or(t)),
969
- };
970
- let (radix, t) = if (radix == 16 || radix == 10 && false) && (t.starts_with("0x") || t.starts_with("0X")) {
971
- (16, &t[2..])
972
- } else if radix == 10 && (t.starts_with("0x") || t.starts_with("0X")) {
973
- (16, &t[2..])
974
- } else {
975
- (radix, t)
976
- };
977
- let mut v: f64 = 0.0;
978
- let mut any = false;
979
- for c in t.chars() {
980
- match c.to_digit(radix) {
981
- Some(d) => {
982
- v = v * radix as f64 + d as f64;
983
- any = true;
984
- }
985
- None => break,
986
- }
987
- }
988
- Val::Num(if !any { f64::NAN } else if neg { -v } else { v })
989
- }
990
- "encodeURIComponent" => Val::Str(url_encode(&arg(0).to_js_string(), true)),
991
- "encodeURI" => Val::Str(url_encode(&arg(0).to_js_string(), false)),
992
- "decodeURIComponent" | "decodeURI" => Val::Str(url_decode(&arg(0).to_js_string())),
993
- _ => return Err(type_error(name)),
994
- })
995
- }
996
-
997
- pub fn url_encode(s: &str, component: bool) -> String {
998
- let mut out = String::new();
999
- for b in s.bytes() {
1000
- let keep = b.is_ascii_alphanumeric()
1001
- || b"-_.!~*'()".contains(&b)
1002
- || (!component && b";,/?:@&=+$#".contains(&b));
1003
- if keep {
1004
- out.push(b as char);
1005
- } else {
1006
- out.push('%');
1007
- push_hex2(&mut out, b);
1008
- }
1009
- }
1010
- out
1011
- }
1012
-
1013
- pub fn url_decode(s: &str) -> String {
1014
- let b = s.as_bytes();
1015
- let mut out: Vec<u8> = Vec::with_capacity(b.len());
1016
- let mut i = 0;
1017
- while i < b.len() {
1018
- if b[i] == b'%' && i + 2 < b.len() + 0 && i + 2 <= b.len() - 1 {
1019
- let h = core::str::from_utf8(&b[i + 1..i + 3]).ok().and_then(|h| u8::from_str_radix(h, 16).ok());
1020
- if let Some(v) = h {
1021
- out.push(v);
1022
- i += 3;
1023
- continue;
1024
- }
1025
- }
1026
- if b[i] == b'+' {
1027
- out.push(b' ');
1028
- } else {
1029
- out.push(b[i]);
1030
- }
1031
- i += 1;
1032
- }
1033
- String::from_utf8_lossy(&out).into_owned()
1034
- }
1035
-
1036
- /// Parse `a=1&b=x%20y` into an object (last write wins, like `req.query`).
1037
- pub fn parse_query(q: &str) -> Val {
1038
- let mut obj = Val::obj();
1039
- for pair in q.trim_start_matches('?').split('&') {
1040
- if pair.is_empty() {
1041
- continue;
1042
- }
1043
- let (k, v) = match pair.find('=') {
1044
- Some(i) => (&pair[..i], &pair[i + 1..]),
1045
- None => (pair, ""),
1046
- };
1047
- obj.set_str(&url_decode(k), Val::Str(url_decode(v)));
1048
- }
1049
- obj
1050
- }