gofront 1.6.0 → 1.6.1

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 (68) hide show
  1. package/package.json +1 -1
  2. package/src/backend/js/expressions.js +238 -109
  3. package/src/backend/js/index.js +38 -1
  4. package/src/backend/js/runtime.js +953 -53
  5. package/src/backend/js/statements.js +55 -67
  6. package/src/backend/js/stdlib/base64.js +12 -0
  7. package/src/backend/js/stdlib/binary.js +102 -0
  8. package/src/backend/js/stdlib/bits.js +15 -1
  9. package/src/backend/js/stdlib/builder.js +28 -9
  10. package/src/backend/js/stdlib/bytes.js +75 -18
  11. package/src/backend/js/stdlib/errors.js +20 -1
  12. package/src/backend/js/stdlib/fmt.js +70 -13
  13. package/src/backend/js/stdlib/hex.js +17 -0
  14. package/src/backend/js/stdlib/html.js +1 -1
  15. package/src/backend/js/stdlib/index.js +16 -0
  16. package/src/backend/js/stdlib/io.js +105 -3
  17. package/src/backend/js/stdlib/math.js +13 -1
  18. package/src/backend/js/stdlib/path.js +18 -4
  19. package/src/backend/js/stdlib/rand.js +10 -5
  20. package/src/backend/js/stdlib/sha256.js +12 -0
  21. package/src/backend/js/stdlib/slices.js +52 -26
  22. package/src/backend/js/stdlib/strconv.js +21 -5
  23. package/src/backend/js/stdlib/strings.js +148 -15
  24. package/src/backend/js/stdlib/sync.js +13 -0
  25. package/src/backend/js/stdlib/time.js +90 -19
  26. package/src/backend/js/stdlib/unicode.js +2 -2
  27. package/src/backend/wasm/boundary.js +1207 -41
  28. package/src/backend/wasm/const-fold.js +205 -0
  29. package/src/backend/wasm/emit-builtins.js +203 -76
  30. package/src/backend/wasm/emit-exprs.js +464 -276
  31. package/src/backend/wasm/emit-maps.js +144 -8
  32. package/src/backend/wasm/emit-stdlib.js +12 -3005
  33. package/src/backend/wasm/emit-stmts.js +264 -270
  34. package/src/backend/wasm/emit.js +115 -87
  35. package/src/backend/wasm/encode.js +55 -29
  36. package/src/backend/wasm/index.js +1001 -32
  37. package/src/backend/wasm/optimize.js +13 -4
  38. package/src/backend/wasm/stdlib/bytes.js +5039 -0
  39. package/src/backend/wasm/stdlib/core.js +8903 -0
  40. package/src/backend/wasm/stdlib/index.js +40 -0
  41. package/src/backend/wasm/stdlib/io.js +2161 -0
  42. package/src/backend/wasm/stdlib/packages.js +2400 -0
  43. package/src/backend/wasm/stdlib/pure.js +720 -0
  44. package/src/backend/wasm/stdlib/time.js +1200 -0
  45. package/src/backend/wasm/types.js +153 -1
  46. package/src/cli-core.js +1 -1
  47. package/src/compiler.js +18 -4
  48. package/src/dts-parser.js +79 -126
  49. package/src/index.js +2 -6
  50. package/src/lexer.js +80 -29
  51. package/src/lower/boxing.js +1 -2
  52. package/src/lower/captures.js +2 -6
  53. package/src/lower/ownership.js +30 -9
  54. package/src/minifier.js +20 -68
  55. package/src/parser/declarations.js +3 -6
  56. package/src/parser/expressions.js +3 -8
  57. package/src/parser/index.js +18 -3
  58. package/src/parser/statements.js +25 -38
  59. package/src/parser/types.js +1 -2
  60. package/src/resolver.js +2 -27
  61. package/src/templ-lexer.js +29 -63
  62. package/src/templ-parser.js +22 -45
  63. package/src/typechecker/assignability.js +81 -13
  64. package/src/typechecker/expressions.js +102 -2
  65. package/src/typechecker/resolve.js +36 -6
  66. package/src/typechecker/stdlib/core.js +280 -89
  67. package/src/typechecker/stdlib/extended.js +453 -1
  68. package/src/typechecker/types.js +344 -6
@@ -34,8 +34,22 @@
34
34
  // - `*testing.T`: the harness's JS test object, passed as an opaque
35
35
  // externref (test methods are routed back to JS via `env.testing_*`).
36
36
 
37
- import { HELPER_SPRINTF, HELPER_STRCONV } from "../js/runtime.js";
38
- import { getSharedInfo, isTestingT } from "./types.js";
37
+ import {
38
+ HELPER_BASE64,
39
+ HELPER_DURATION,
40
+ HELPER_ERROR,
41
+ HELPER_HEX,
42
+ HELPER_PATH_CLEAN,
43
+ HELPER_SHA256,
44
+ HELPER_SPRINTF,
45
+ HELPER_STRCONV,
46
+ } from "../js/runtime.js";
47
+ import {
48
+ anonIfaceName,
49
+ getInterfaceMethodList,
50
+ getSharedInfo,
51
+ isTestingT,
52
+ } from "./types.js";
39
53
 
40
54
  // ── Type classification ──────────────────────────────────────
41
55
 
@@ -94,6 +108,14 @@ function classifyType(goType, mod) {
94
108
  if (isTestingT(goType)) return { k: "extern", wType: "externref" };
95
109
 
96
110
  if (goType.kind === "TypeName" || goType.kind === "Ident") {
111
+ if (goType.name === "error") return { k: "error", wType: "anyref" };
112
+ if (goType.name === "time.Duration" || goType.name === "Duration")
113
+ return { k: "i64", unsigned: false, wType: "i64" };
114
+ if (goType.name === "time.Time" || goType.name === "Time") {
115
+ const info = mod.getStructType("time.Time");
116
+ if (info)
117
+ return { k: "time", name: "time.Time", info, wType: toRef(info) };
118
+ }
97
119
  const info = mod.getStructType(goType.name);
98
120
  if (info) return structDesc(info, false);
99
121
  const resolved = mod.checker?.types?.get(goType.name);
@@ -117,6 +139,10 @@ function classifyType(goType, mod) {
117
139
  return classifySlice(t, mod);
118
140
  case "array":
119
141
  return classifyArray(t, mod);
142
+ case "map":
143
+ return classifyMap(t, mod);
144
+ case "struct":
145
+ return classifyStruct(t, mod);
120
146
  case "func":
121
147
  return classifyFunc(t, mod);
122
148
  case "interface":
@@ -130,11 +156,6 @@ function classifyType(goType, mod) {
130
156
  }
131
157
 
132
158
  const UNSUPPORTED_KINDS = {
133
- map: "map",
134
- MapType: "map",
135
- struct: "anonymous struct",
136
- StructType: "anonymous struct",
137
- InterfaceType: "interface",
138
159
  ChanType: "chan",
139
160
  chan: "chan",
140
161
  };
@@ -149,13 +170,21 @@ function normalizeAstType(t) {
149
170
  return { kind: "slice", elem: t.elem };
150
171
  case "ArrayType":
151
172
  return { kind: "array", elem: t.elem, size: t.size };
173
+ case "MapType":
174
+ return { kind: "map", key: t.key, val: t.value ?? t.elem };
152
175
  case "FuncType":
153
176
  return funcSigFromAst(t);
154
177
  case "InterfaceType":
155
178
  return {
156
179
  kind: "interface",
157
180
  name: t.name,
158
- methods: new Set((t.methods ?? []).map((m) => m.name ?? m)),
181
+ methods: t.methods,
182
+ };
183
+ case "StructType":
184
+ return {
185
+ kind: "struct",
186
+ name: t.name,
187
+ fields: t.fields,
159
188
  };
160
189
  default:
161
190
  return t;
@@ -169,7 +198,7 @@ const BASIC_DESCS = {
169
198
  float64: { k: "f64", wType: "f64" },
170
199
  string: { k: "string", wType: "externref" },
171
200
  any: { k: "any", wType: "anyref" },
172
- error: { k: "unsupported", what: "error" },
201
+ error: { k: "error", wType: "anyref" },
173
202
  };
174
203
 
175
204
  function classifyBasic(n) {
@@ -182,17 +211,111 @@ function classifyBasic(n) {
182
211
  }
183
212
 
184
213
  function classifyNamed(t, mod) {
214
+ if (t.name === "error") return { k: "error", wType: "anyref" };
215
+ if (t.name === "time.Duration")
216
+ return { k: "i64", unsigned: false, wType: "i64" };
217
+ if (t.name === "time.Time") {
218
+ const info = mod.getStructType("time.Time");
219
+ if (info) return { k: "time", name: "time.Time", info, wType: toRef(info) };
220
+ }
185
221
  const info = mod.getStructType(t.name);
186
222
  if (info) return structDesc(info, false);
187
223
  if (t.underlying) return classifyType(t.underlying, mod);
188
224
  return { k: "unsupported", what: `named type '${t.name}'` };
189
225
  }
190
226
 
227
+ function classifyAnonStruct(t, mod) {
228
+ const rawFields = [];
229
+ if (t.fields instanceof Map) {
230
+ for (const [k, v] of t.fields.entries())
231
+ rawFields.push({ name: k, goType: v });
232
+ } else if (Array.isArray(t.fields)) {
233
+ for (const f of t.fields) {
234
+ const names = f.names ?? (f.name ? [f.name] : []);
235
+ for (const n of names) rawFields.push({ name: n, goType: f.type });
236
+ }
237
+ }
238
+ const info = mod.getAnonStructType(rawFields);
239
+ const fields = [];
240
+ for (const f of info.fields) {
241
+ const fd = classifyType(f.goType, mod);
242
+ if (fd.k === "unsupported") return fd;
243
+ fields.push({ name: f.name, desc: fd });
244
+ }
245
+ return {
246
+ k: "anon_struct",
247
+ name: info.name,
248
+ key: info.name,
249
+ info,
250
+ fields,
251
+ wType: toRef(info),
252
+ };
253
+ }
254
+
255
+ function classifyStruct(t, mod) {
256
+ if (t.name) {
257
+ const info = mod.getStructType(t.name);
258
+ if (info) return structDesc(info, false);
259
+ }
260
+ return classifyAnonStruct(t, mod);
261
+ }
262
+
191
263
  function classifyPointer(t, mod) {
192
264
  const base = classifyType(t.base, mod);
265
+ if (base.k === "unsupported") return base;
193
266
  if (base.k === "struct" && !base.ptr) return { ...base, ptr: true };
194
- const what = base.k === "unsupported" ? base.what : (t.base?.name ?? base.k);
195
- return { k: "unsupported", what: `pointer to ${what}` };
267
+ if (base.k === "anon_struct" && !base.ptr) return { ...base, ptr: true };
268
+ if (base.k === "time" && !base.ptr) return { ...base, ptr: true };
269
+ const boxInfo = mod.getBoxType(t.base);
270
+ const key = `${base.k}${base.unsigned ? "_u" : ""}_${boxInfo.typeIndex}`;
271
+ return {
272
+ k: "ptr_box",
273
+ base,
274
+ boxInfo,
275
+ key,
276
+ wType: { kind: "ref", nullable: true, typeIndex: boxInfo.typeIndex },
277
+ };
278
+ }
279
+
280
+ function isAllowedMapKey(kDesc) {
281
+ if (kDesc.k === "string" || kDesc.k === "bool") return true;
282
+ if (
283
+ kDesc.k === "i32" ||
284
+ kDesc.k === "i64" ||
285
+ kDesc.k === "f32" ||
286
+ kDesc.k === "f64"
287
+ )
288
+ return true;
289
+ return false;
290
+ }
291
+
292
+ function classifyMap(t, mod) {
293
+ const keyType = t.key;
294
+ const valType = t.val ?? t.value ?? t.elem;
295
+ const keyDesc = classifyType(keyType, mod);
296
+ if (keyDesc.k === "unsupported") return keyDesc;
297
+ const valDesc = classifyType(valType, mod);
298
+ if (valDesc.k === "unsupported") return valDesc;
299
+ if (!isAllowedMapKey(keyDesc)) {
300
+ return {
301
+ k: "unsupported",
302
+ what: `map with unsupported key type '${keyType?.name ?? keyDesc.k}'`,
303
+ };
304
+ }
305
+ const mapInfo = mod.getMapType(keyType, valType);
306
+ const keySliceDesc = classifySlice({ kind: "slice", elem: keyType }, mod);
307
+ const valSliceDesc = classifySlice({ kind: "slice", elem: valType }, mod);
308
+ const key = `m_${mapInfo.suffix}`;
309
+ return {
310
+ k: "map",
311
+ key,
312
+ mapInfo,
313
+ keyDesc,
314
+ valDesc,
315
+ keySliceDesc,
316
+ valSliceDesc,
317
+ wType: mapInfo.wType,
318
+ };
196
319
  }
197
320
 
198
321
  function classifyShared(t, mod) {
@@ -263,12 +386,50 @@ function classifyFunc(t, mod) {
263
386
  }
264
387
 
265
388
  function classifyInterface(t, mod) {
266
- if (t.name === "error") return { k: "unsupported", what: "error" };
267
- if (!t.methods || t.methods.size === 0) return { k: "any", wType: "anyref" };
268
- if (!t.name || !(t.methods instanceof Map))
269
- return { k: "unsupported", what: "anonymous interface" };
389
+ if (t.name === "error") return { k: "error", wType: "anyref" };
390
+ const mList = getInterfaceMethodList(t);
391
+ if (mList.length === 0) return { k: "any", wType: "anyref" };
270
392
  mod._boundaryIfaces ??= new Map();
271
- return mod._boundaryIfaces.get(t.name) ?? classifyNamedInterface(t, mod);
393
+ if (t.name && t.methods instanceof Map) {
394
+ return mod._boundaryIfaces.get(t.name) ?? classifyNamedInterface(t, mod);
395
+ }
396
+ return classifyAnonInterface(t, mod);
397
+ }
398
+
399
+ function classifyAnonInterface(t, mod) {
400
+ const key = anonIfaceName(t, mod.checker, mod);
401
+ if (mod._boundaryIfaces.has(key)) return mod._boundaryIfaces.get(key);
402
+ const mList = getInterfaceMethodList(t);
403
+ const desc = {
404
+ k: "iface",
405
+ name: key,
406
+ key,
407
+ wType: "anyref",
408
+ methods: [],
409
+ concrete: [],
410
+ isAnon: true,
411
+ };
412
+ mod._boundaryIfaces.set(key, desc);
413
+ for (const m of mList) {
414
+ const sig = classifySig(
415
+ { kind: "func", params: m.params, returns: m.returns },
416
+ mod,
417
+ );
418
+ if (sig.k === "unsupported") {
419
+ const err = {
420
+ k: "unsupported",
421
+ what: `${sig.what} (in anonymous interface method ${m.name})`,
422
+ };
423
+ mod._boundaryIfaces.set(key, err);
424
+ return err;
425
+ }
426
+ desc.methods.push({
427
+ name: m.name,
428
+ dispatch: `__dispatch_${key}_${m.name}`,
429
+ ...sig,
430
+ });
431
+ }
432
+ return desc;
272
433
  }
273
434
 
274
435
  function classifyNamedInterface(t, mod) {
@@ -325,7 +486,14 @@ function literalConst(spec, i) {
325
486
  }
326
487
  if (node?.kind !== "BasicLit") return null;
327
488
  switch (node.litKind) {
328
- case "INT":
489
+ case "INT": {
490
+ const raw = String(node.value);
491
+ const clean = /^0[0-7]+$/.test(raw) ? `0o${raw.slice(1)}` : raw;
492
+ const bi = BigInt(`${sign}${clean}`);
493
+ return bi > 9007199254740991n || bi < -9007199254740991n
494
+ ? `${sign}${clean}n`
495
+ : `${sign}${clean}`;
496
+ }
329
497
  case "FLOAT":
330
498
  return `${sign}${node.value}`;
331
499
  case "STRING":
@@ -394,14 +562,48 @@ function collectConstDecl(d, pkgName, meta, errors) {
394
562
  const name = spec.names[i];
395
563
  if (!isExported(name)) continue;
396
564
  const value = literalConst(spec, i);
397
- if (value !== null) meta.consts.push({ name, value });
398
- else
565
+ if (value !== null) {
566
+ meta.consts.push({ name, value, dynamic: false });
567
+ continue;
568
+ }
569
+ const folded = meta._mod.constCache?.get(name);
570
+ if (folded) {
571
+ let val = null;
572
+ if (folded.litKind === "INT") {
573
+ const s = String(folded.value);
574
+ const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
575
+ const bi = BigInt(clean);
576
+ val =
577
+ bi > 9007199254740991n || bi < -9007199254740991n
578
+ ? `${clean}n`
579
+ : clean;
580
+ } else if (folded.litKind === "FLOAT") {
581
+ val = folded.value;
582
+ } else if (folded.litKind === "STRING") {
583
+ val = JSON.stringify(String(folded.value));
584
+ } else if (folded.litKind === "BOOL") {
585
+ val = folded.value === "true" ? "true" : "false";
586
+ }
587
+ if (val !== null) {
588
+ meta.consts.push({ name, value: val, dynamic: false });
589
+ continue;
590
+ }
591
+ }
592
+ const glob = meta._mod.resolveGlobal(name);
593
+ const rawType = spec.type ?? spec.value?.[i]?._type ?? glob?.goType;
594
+ let desc = rawType ? classifyType(rawType, meta._mod) : null;
595
+ if (!desc || desc.k === "void") {
596
+ if (glob?.type) {
597
+ desc = { k: glob.type, wType: glob.type };
598
+ }
599
+ }
600
+ if (!desc || desc.k === "unsupported") {
399
601
  errors.push(
400
- unsupportedMsg(
401
- `${pkgName}.${name}`,
402
- "a constant with a non-literal value",
403
- ),
602
+ unsupportedMsg(`${pkgName}.${name}`, desc?.what ?? "unsupported"),
404
603
  );
604
+ } else {
605
+ meta.consts.push({ name, dynamic: true, desc });
606
+ }
405
607
  }
406
608
  }
407
609
  }
@@ -483,6 +685,17 @@ function visitNeed(desc, needs, errors, mod) {
483
685
  case "struct":
484
686
  visitStructNeed(desc, needs, errors, mod);
485
687
  return;
688
+ case "anon_struct":
689
+ if (needs.anonStructs.has(desc.name)) return;
690
+ needs.anonStructs.set(desc.name, desc);
691
+ for (const f of desc.fields) visitNeed(f.desc, needs, errors, mod);
692
+ return;
693
+ case "error":
694
+ needs.error = true;
695
+ return;
696
+ case "time":
697
+ needs.time = true;
698
+ return;
486
699
  case "slice":
487
700
  case "array": {
488
701
  const map = desc.k === "slice" ? needs.slices : needs.arrays;
@@ -491,6 +704,19 @@ function visitNeed(desc, needs, errors, mod) {
491
704
  visitNeed(desc.elem, needs, errors, mod);
492
705
  return;
493
706
  }
707
+ case "map":
708
+ if (needs.maps.has(desc.key)) return;
709
+ needs.maps.set(desc.key, desc);
710
+ visitNeed(desc.keyDesc, needs, errors, mod);
711
+ visitNeed(desc.valDesc, needs, errors, mod);
712
+ visitNeed(desc.keySliceDesc, needs, errors, mod);
713
+ visitNeed(desc.valSliceDesc, needs, errors, mod);
714
+ return;
715
+ case "ptr_box":
716
+ if (needs.boxes.has(desc.key)) return;
717
+ needs.boxes.set(desc.key, desc);
718
+ visitNeed(desc.base, needs, errors, mod);
719
+ return;
494
720
  case "func":
495
721
  if (needs.funcs.has(desc.key)) return;
496
722
  needs.funcs.set(desc.key, desc);
@@ -538,10 +764,15 @@ function collectNeeds(meta) {
538
764
  const mod = meta._mod;
539
765
  const needs = {
540
766
  structs: new Map(), // name -> { info, fields: [{ name, desc }] }
767
+ anonStructs: new Map(), // name -> desc
541
768
  slices: new Map(), // key -> desc
542
769
  arrays: new Map(), // key -> desc
770
+ maps: new Map(), // key -> desc
771
+ boxes: new Map(), // key -> desc
543
772
  funcs: new Map(), // key -> desc
544
773
  ifaces: new Map(), // interface name -> desc
774
+ error: false,
775
+ time: false,
545
776
  shared: false, // any shared buffer on the surface
546
777
  };
547
778
  const errors = [];
@@ -552,6 +783,9 @@ function collectNeeds(meta) {
552
783
  }
553
784
  for (const f of meta.funcs) visitSignatureNeeds(f, needs, errors, mod);
554
785
  for (const v of meta.vars ?? []) visitNeed(v.desc, needs, errors, mod);
786
+ for (const c of meta.consts ?? []) {
787
+ if (c.dynamic) visitNeed(c.desc, needs, errors, mod);
788
+ }
555
789
  if (errors.length > 0) throw new Error([...new Set(errors)].join("\n"));
556
790
  for (const desc of needs.ifaces.values())
557
791
  desc.concrete = concreteCandidates(desc, needs, meta, mod);
@@ -968,6 +1202,231 @@ export function addBoundaryHelpers(mod, meta) {
968
1202
  );
969
1203
  }
970
1204
  }
1205
+
1206
+ for (const [name, desc] of needs.anonStructs) {
1207
+ const { info } = desc;
1208
+ const ref = desc.wType;
1209
+ const ti = info.typeIndex;
1210
+ const fieldWTypes = info.fields.map((f) => f.wType);
1211
+ addFunc(
1212
+ `__new_${name}`,
1213
+ fieldWTypes.map(extType),
1214
+ [EXT],
1215
+ [
1216
+ ...fieldWTypes.flatMap((w, i) => getIn(i, w)),
1217
+ { op: "struct.new", typeIndex: ti },
1218
+ ...toExt(ref),
1219
+ ],
1220
+ );
1221
+ addFunc(
1222
+ `__zero_${name}`,
1223
+ [],
1224
+ [EXT],
1225
+ [
1226
+ ...fieldWTypes.map((w) => zeroInstr(w)),
1227
+ { op: "struct.new", typeIndex: ti },
1228
+ ...toExt(ref),
1229
+ ],
1230
+ );
1231
+ for (let i = 0; i < info.fields.length; i++) {
1232
+ const f = info.fields[i];
1233
+ addFunc(
1234
+ `__get_${name}_${f.name}`,
1235
+ [EXT],
1236
+ [extType(f.wType)],
1237
+ [
1238
+ ...getIn(0, ref),
1239
+ { op: "struct.get", typeIndex: ti, fieldIndex: i },
1240
+ ...toExt(f.wType),
1241
+ ],
1242
+ );
1243
+ addFunc(
1244
+ `__set_${name}_${f.name}`,
1245
+ [EXT, extType(f.wType)],
1246
+ [],
1247
+ [
1248
+ ...getIn(0, ref),
1249
+ ...getIn(1, f.wType),
1250
+ { op: "struct.set", typeIndex: ti, fieldIndex: i },
1251
+ ],
1252
+ );
1253
+ }
1254
+ }
1255
+
1256
+ for (const [key, desc] of needs.boxes) {
1257
+ const { boxInfo } = desc;
1258
+ const ref = desc.wType;
1259
+ const ti = boxInfo.typeIndex;
1260
+ const w = boxInfo.wType;
1261
+ addFunc(
1262
+ `__box_new_${key}`,
1263
+ [extType(w)],
1264
+ [EXT],
1265
+ [...getIn(0, w), { op: "struct.new", typeIndex: ti }, ...toExt(ref)],
1266
+ );
1267
+ addFunc(
1268
+ `__box_get_${key}`,
1269
+ [EXT],
1270
+ [extType(w)],
1271
+ [
1272
+ ...getIn(0, ref),
1273
+ { op: "struct.get", typeIndex: ti, fieldIndex: 0 },
1274
+ ...toExt(w),
1275
+ ],
1276
+ );
1277
+ addFunc(
1278
+ `__box_set_${key}`,
1279
+ [EXT, extType(w)],
1280
+ [],
1281
+ [
1282
+ ...getIn(0, ref),
1283
+ ...getIn(1, w),
1284
+ { op: "struct.set", typeIndex: ti, fieldIndex: 0 },
1285
+ ],
1286
+ );
1287
+ }
1288
+
1289
+ for (const [key, desc] of needs.maps) {
1290
+ const { mapInfo, keySliceDesc, valSliceDesc } = desc;
1291
+ const makeIdx = mod.resolveFuncIndex(mapInfo.makeFuncName);
1292
+ addFunc(
1293
+ `__map_new_${key}`,
1294
+ ["i32"],
1295
+ [EXT],
1296
+ [get(0), { op: "call", funcIndex: makeIdx }, ...toExt(desc.wType)],
1297
+ );
1298
+ const setIdx = mod.resolveFuncIndex(mapInfo.setFuncName);
1299
+ addFunc(
1300
+ `__map_set_${key}`,
1301
+ [EXT, extType(mapInfo.keyWType), extType(mapInfo.valWType)],
1302
+ [],
1303
+ [
1304
+ ...getIn(0, desc.wType),
1305
+ ...getIn(1, mapInfo.keyWType),
1306
+ ...getIn(2, mapInfo.valWType),
1307
+ { op: "call", funcIndex: setIdx },
1308
+ ],
1309
+ );
1310
+ const keysIdx = mod.resolveFuncIndex(mapInfo.keysFuncName);
1311
+ addFunc(
1312
+ `__map_keys_${key}`,
1313
+ [EXT],
1314
+ [EXT],
1315
+ [
1316
+ ...getIn(0, desc.wType),
1317
+ { op: "call", funcIndex: keysIdx },
1318
+ ...toExt(keySliceDesc.wType),
1319
+ ],
1320
+ );
1321
+ const valuesIdx = mod.resolveFuncIndex(mapInfo.valuesFuncName);
1322
+ addFunc(
1323
+ `__map_values_${key}`,
1324
+ [EXT],
1325
+ [EXT],
1326
+ [
1327
+ ...getIn(0, desc.wType),
1328
+ { op: "call", funcIndex: valuesIdx },
1329
+ ...toExt(valSliceDesc.wType),
1330
+ ],
1331
+ );
1332
+ }
1333
+
1334
+ if (needs.error) {
1335
+ const errDispatcherIdx = mod.funcMap.get("__dispatch_error_Error");
1336
+ if (errDispatcherIdx !== undefined) {
1337
+ addFunc(
1338
+ "__error_msg",
1339
+ [EXT],
1340
+ [EXT],
1341
+ [
1342
+ get(0),
1343
+ { op: "ref.is_null" },
1344
+ { op: "if", blockType: "externref" },
1345
+ { op: "ref.null", heapType: "extern" },
1346
+ { op: "else" },
1347
+ ...getIn(0, "anyref"),
1348
+ { op: "call", funcIndex: errDispatcherIdx },
1349
+ ...toExt("externref"),
1350
+ { op: "end" },
1351
+ ],
1352
+ );
1353
+ }
1354
+ const errCandidates = mod
1355
+ .findInterfaceCandidates(null, "Error")
1356
+ .map((c) => c.testTypeIndex);
1357
+ addFunc(
1358
+ "__is_error",
1359
+ [EXT],
1360
+ ["i32"],
1361
+ refTestChain(errCandidates, () => 1),
1362
+ );
1363
+ }
1364
+
1365
+ if (needs.time) {
1366
+ const timeInfo = mod.getStructType("time.Time");
1367
+ if (timeInfo) {
1368
+ const timeWType = toRef(timeInfo);
1369
+ addFunc(
1370
+ "__time_make",
1371
+ ["f64", "i32"],
1372
+ [EXT],
1373
+ [
1374
+ get(0),
1375
+ { op: "f64.const", value: 1000000 },
1376
+ { op: "f64.mul" },
1377
+ { op: "i64.trunc_sat_f64_s" },
1378
+ get(1),
1379
+ { op: "struct.new", typeIndex: timeInfo.typeIndex },
1380
+ ...toExt(timeWType),
1381
+ ],
1382
+ );
1383
+ addFunc(
1384
+ "__time_unix_milli",
1385
+ [EXT],
1386
+ ["f64"],
1387
+ [
1388
+ get(0),
1389
+ { op: "ref.is_null" },
1390
+ { op: "if", blockType: "f64" },
1391
+ { op: "f64.const", value: 0 },
1392
+ { op: "else" },
1393
+ ...getIn(0, timeWType),
1394
+ { op: "struct.get", typeIndex: timeInfo.typeIndex, fieldIndex: 0 },
1395
+ { op: "i64.const", value: 1000000n },
1396
+ { op: "i64.div_s" },
1397
+ { op: "f64.convert_i64_s" },
1398
+ { op: "end" },
1399
+ ],
1400
+ );
1401
+ addFunc(
1402
+ "__time_offset",
1403
+ [EXT],
1404
+ ["i32"],
1405
+ [
1406
+ get(0),
1407
+ { op: "ref.is_null" },
1408
+ { op: "if", blockType: "i32" },
1409
+ { op: "i32.const", value: 0 },
1410
+ { op: "else" },
1411
+ ...getIn(0, timeWType),
1412
+ { op: "struct.get", typeIndex: timeInfo.typeIndex, fieldIndex: 1 },
1413
+ { op: "end" },
1414
+ ],
1415
+ );
1416
+ }
1417
+ }
1418
+
1419
+ for (const c of meta.consts ?? []) {
1420
+ if (!c.dynamic) continue;
1421
+ const g = mod.resolveGlobal(c.name);
1422
+ if (!g) continue;
1423
+ addFunc(
1424
+ `__const_${c.name}`,
1425
+ [],
1426
+ [extType(c.desc.wType)],
1427
+ [{ op: "global.get", index: g.index }, ...toExt(c.desc.wType)],
1428
+ );
1429
+ }
971
1430
  }
972
1431
 
973
1432
  // `(externref) -> i32`: `ref.test`s param 0 against each type index and
@@ -1050,6 +1509,18 @@ function inExpr(desc, v) {
1050
1509
  return `${P}strin(${v})`;
1051
1510
  case "any":
1052
1511
  return `(${v} === undefined ? null : ${v})`;
1512
+ case "error":
1513
+ return `${P}errin(${v})`;
1514
+ case "time":
1515
+ return desc.ptr ? `${P}timein_ptr(${v})` : `${P}timein(${v})`;
1516
+ case "map":
1517
+ return `${P}mapin_${desc.key}(${v})`;
1518
+ case "ptr_box":
1519
+ return `${P}boxin_${desc.key}(${v})`;
1520
+ case "anon_struct":
1521
+ return desc.ptr
1522
+ ? `(${v} == null ? null : ${P}to_${desc.name}(${v}))`
1523
+ : `${P}to_${desc.name}(${v})`;
1053
1524
  case "struct":
1054
1525
  if (desc.info.pkgTarget === "both")
1055
1526
  return `${P}to_${desc.name}(${v}, ${desc.ptr ? "false" : "true"})`;
@@ -1082,6 +1553,18 @@ function outExpr(desc, v) {
1082
1553
  return desc.unsigned ? `${P}u64out(${v})` : `${P}i64out(${v})`;
1083
1554
  case "string":
1084
1555
  return `(${v} ?? "")`;
1556
+ case "error":
1557
+ return `${P}errout(${v})`;
1558
+ case "time":
1559
+ return desc.ptr ? `${P}timeout_ptr(${v})` : `${P}timeout(${v})`;
1560
+ case "map":
1561
+ return `${P}mapout_${desc.key}(${v})`;
1562
+ case "ptr_box":
1563
+ return `${P}boxout_${desc.key}(${v})`;
1564
+ case "anon_struct":
1565
+ return desc.ptr
1566
+ ? `(${v} == null ? null : ${P}from_${desc.name}(${v}))`
1567
+ : `${P}from_${desc.name}(${v})`;
1085
1568
  case "struct":
1086
1569
  return desc.info.pkgTarget === "both"
1087
1570
  ? `${P}from_${desc.name}(${v})`
@@ -1112,6 +1595,15 @@ function zeroExpr(desc) {
1112
1595
  return "0";
1113
1596
  case "string":
1114
1597
  return '""';
1598
+ case "error":
1599
+ case "time":
1600
+ case "map":
1601
+ case "ptr_box":
1602
+ return "null";
1603
+ case "anon_struct":
1604
+ return desc.ptr
1605
+ ? "null"
1606
+ : `{ ${desc.fields.map((f) => `"${f.name}": ${zeroExpr(f.desc)}`).join(", ")} }`;
1115
1607
  case "struct":
1116
1608
  return desc.ptr ? "null" : `new ${desc.name}()`;
1117
1609
  case "array":
@@ -1147,6 +1639,10 @@ function isBothStruct(d) {
1147
1639
  return d.k === "struct" && d.info.pkgTarget === "both";
1148
1640
  }
1149
1641
 
1642
+ function isPtrWriteBack(d) {
1643
+ return isPtrBoth(d) || d.k === "ptr_box" || (d.k === "anon_struct" && d.ptr);
1644
+ }
1645
+
1150
1646
  function returnLines(returns, resultExpr) {
1151
1647
  if (returns.length === 0) return [];
1152
1648
  if (returns.length === 1)
@@ -1161,13 +1657,21 @@ function genFuncBody(exportName, params, returns, leadArgs = []) {
1161
1657
  const names = params.map((_, i) => `a${i}`);
1162
1658
  const lines = [];
1163
1659
  const argExprs = [...leadArgs];
1164
- const writeBack = params.some(isPtrBoth);
1165
- if (writeBack) lines.push("\tconst __m = new Map();");
1660
+ const writeBack = params.some(isPtrWriteBack);
1661
+ if (params.some(isPtrBoth)) lines.push("\tconst __m = new Map();");
1166
1662
  for (let i = 0; i < params.length; i++) {
1167
1663
  const d = params[i];
1168
1664
  if (isPtrBoth(d)) {
1169
1665
  lines.push(`\tconst __p${i} = ${P}to_${d.name}(a${i}, false, __m);`);
1170
1666
  argExprs.push(`__p${i}`);
1667
+ } else if (d.k === "ptr_box") {
1668
+ lines.push(`\tconst __p${i} = ${P}boxin_${d.key}(a${i});`);
1669
+ argExprs.push(`__p${i}`);
1670
+ } else if (d.k === "anon_struct" && d.ptr) {
1671
+ lines.push(
1672
+ `\tconst __p${i} = a${i} == null ? null : ${P}to_${d.name}(a${i});`,
1673
+ );
1674
+ argExprs.push(`__p${i}`);
1171
1675
  } else {
1172
1676
  argExprs.push(inExpr(d, names[i]));
1173
1677
  }
@@ -1185,8 +1689,12 @@ function genFuncBody(exportName, params, returns, leadArgs = []) {
1185
1689
  } else {
1186
1690
  lines.push(`\t\tconst __r = ${call};`);
1187
1691
  for (let i = 0; i < params.length; i++) {
1188
- if (isPtrBoth(params[i]))
1189
- lines.push(`\t\t${P}back_${params[i].name}(__p${i}, a${i});`);
1692
+ const d = params[i];
1693
+ if (isPtrBoth(d)) lines.push(`\t\t${P}back_${d.name}(__p${i}, a${i});`);
1694
+ else if (d.k === "ptr_box")
1695
+ lines.push(`\t\t${P}boxback_${d.key}(__p${i}, a${i});`);
1696
+ else if (d.k === "anon_struct" && d.ptr)
1697
+ lines.push(`\t\t${P}back_${d.name}(__p${i}, a${i});`);
1190
1698
  }
1191
1699
  lines.push(...returnLines(returns, "__r").map((l) => `\t${l}`));
1192
1700
  }
@@ -1206,6 +1714,7 @@ export const WASM_IMPORTS_JS = `const ${P}MAX = 9007199254740991n;
1206
1714
  function ${P}imports(stringTable, extraEnv, tag, write) {
1207
1715
  let lineBuf = "";
1208
1716
  let targs = [];
1717
+ let fmtStructMap = new WeakMap();
1209
1718
  const late = { exports: null };
1210
1719
  const flush = () => { write(lineBuf); lineBuf = ""; };
1211
1720
  const print = (v) => {
@@ -1224,6 +1733,8 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
1224
1733
  flush();
1225
1734
  };
1226
1735
  const targ = (v) => { targs.push(v); };
1736
+ let utf8Encoded = null;
1737
+ let convBytes = null, convPos = 0;
1227
1738
  const env = {
1228
1739
  "panicTag": tag,
1229
1740
  "panic": (msg) => {
@@ -1276,6 +1787,25 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
1276
1787
  "str_from_i32": (n) => String(n),
1277
1788
  "str_from_f64": (f) => String(f),
1278
1789
  "is_string": (v) => (typeof v === "string" ? 1 : 0),
1790
+ "str_to_bytes": (s) => {
1791
+ utf8Encoded = new TextEncoder().encode(s ?? "");
1792
+ return utf8Encoded.length;
1793
+ },
1794
+ "str_byte_at": (i) => (utf8Encoded ? (utf8Encoded[i] ?? 0) : 0),
1795
+ "bytes_to_str_init": (len) => {
1796
+ convBytes = new Uint8Array(len);
1797
+ convPos = 0;
1798
+ },
1799
+ "bytes_to_str_push": (b) => {
1800
+ if (convBytes && convPos < convBytes.length) {
1801
+ convBytes[convPos++] = b & 0xFF;
1802
+ }
1803
+ },
1804
+ "bytes_to_str_finish": () => {
1805
+ const res = convBytes ? new TextDecoder().decode(convBytes) : "";
1806
+ convBytes = null;
1807
+ return res;
1808
+ },
1279
1809
  "print_i32": print, "print_i64": print, "print_f32": print, "print_f64": print,
1280
1810
  "print_str": print, "print_any": print,
1281
1811
  "print_bool": (b) => print(b !== 0 ? "true" : "false"),
@@ -1283,11 +1813,31 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
1283
1813
  "println_str": println, "println_any": println,
1284
1814
  "println_bool": (b) => println(b !== 0 ? "true" : "false"),
1285
1815
  "println_empty": flush,
1816
+ "fmt_new_array": () => [],
1817
+ "fmt_array_push": (arr, v) => { arr.push(v); },
1818
+ "fmt_new_map": () => new Map(),
1819
+ "fmt_map_set": (m, k, v) => { m.set(k, v); },
1820
+ "fmt_new_struct": (ref, isPtr) => {
1821
+ if (ref !== null && ref !== undefined && typeof ref === "object" && fmtStructMap.has(ref)) {
1822
+ return fmtStructMap.get(ref);
1823
+ }
1824
+ const o = {};
1825
+ Object.defineProperty(o, "__struct", { value: true, configurable: true });
1826
+ if (isPtr !== 0) Object.defineProperty(o, "__p", { value: true, configurable: true });
1827
+ if (ref !== null && ref !== undefined && typeof ref === "object") fmtStructMap.set(ref, o);
1828
+ return o;
1829
+ },
1830
+ "fmt_struct_set": (o, k, v) => { o[typeof k === "number" ? stringTable[k] : k] = v; },
1831
+ "fmt_box_i32": (v) => v,
1832
+ "fmt_box_i64": (v) => (v > ${P}MAX || v < -${P}MAX ? v : Number(v)),
1833
+ "fmt_box_f64": (v) => v,
1834
+ "fmt_box_bool": (v) => v !== 0,
1835
+ "fmt_new_box": () => ({ value: null, __p: true, __box: true }),
1286
1836
  "testing_arg_i32": targ, "testing_arg_f32": targ, "testing_arg_f64": targ,
1287
1837
  "testing_arg_str": targ, "testing_arg_any": targ,
1288
1838
  "testing_arg_i64": (v) => targ(v > ${P}MAX || v < -${P}MAX ? v : Number(v)),
1289
1839
  "testing_arg_bool": (v) => targ(v !== 0),
1290
- "testing_call": (t, i) => { const a = targs; targs = []; t[stringTable[i]](...a); },
1840
+ "testing_call": (t, i) => { const a = targs; targs = []; fmtStructMap = new WeakMap(); t[stringTable[i]](...a); },
1291
1841
  "testing_name": (t) => t.Name(),
1292
1842
  "testing_flag": (t, i) => (t[stringTable[i]]() ? 1 : 0),
1293
1843
  };
@@ -1295,7 +1845,7 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
1295
1845
  // modules that never call them do not ship it (see WASM_STDLIB_JS).
1296
1846
  if (typeof ${P}stdlib === "function") {
1297
1847
  Object.assign(env, ${P}stdlib(stringTable, {
1298
- "take": () => { const a = targs; targs = []; return a; },
1848
+ "take": () => { const a = targs; targs = []; fmtStructMap = new WeakMap(); return a; },
1299
1849
  "printf": (s) => {
1300
1850
  lineBuf += s;
1301
1851
  const nl = lineBuf.lastIndexOf("\\n");
@@ -1306,7 +1856,7 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
1306
1856
  }
1307
1857
  Object.assign(env, extraEnv);
1308
1858
  const m = {};
1309
- for (const k of ["sin", "cos", "tan", "asin", "acos", "atan", "atan2", "pow", "exp", "log", "log2", "log10", "cbrt", "hypot"]) m[k] = Math[k];
1859
+ for (const k of ["sin", "cos", "tan", "asin", "acos", "atan", "atan2", "pow", "exp", "log", "log2", "log10", "cbrt", "hypot", "random", "sinh", "cosh", "tanh"]) m[k] = Math[k];
1310
1860
  m.mod = (x, y) => x % y;
1311
1861
  // Go rounds half away from zero; Math.round rounds half toward +Infinity.
1312
1862
  m.round = (x) => Math.sign(x) * Math.round(Math.abs(x));
@@ -1316,21 +1866,169 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
1316
1866
 
1317
1867
  // JS side of the STDLIB_IMPORTS table (index.js); emitted only when the
1318
1868
  // module imports one of the packages that use it.
1319
- export const WASM_STDLIB_JS = `${HELPER_SPRINTF}
1869
+ export const WASM_STDLIB_JS = `${HELPER_ERROR}
1870
+ ${HELPER_SPRINTF}
1320
1871
  ${HELPER_STRCONV}
1872
+ ${HELPER_PATH_CLEAN}
1873
+ ${HELPER_DURATION}
1874
+ ${HELPER_SHA256}
1875
+ ${HELPER_HEX}
1876
+ ${HELPER_BASE64}
1321
1877
  function ${P}stdlib(stringTable, io) {
1322
1878
  let parts = [];
1323
- let strconvErr = null;
1879
+ let cutBefore = "", cutAfter = "";
1880
+ let strconvErr = null, strconvErrKind = 0;
1324
1881
  let decodeSize = 0;
1882
+ let replacerTarget = "", replacerPairs = [];
1883
+ let splitDir = "", splitFile = "";
1884
+ let joinParts = [];
1885
+ let matchHasErr = 0;
1886
+ let trimStart = 0, trimEnd = 0;
1887
+ let sscanTokens = [];
1888
+ let sscanHasErr = 0, sscanErrMsg = "";
1889
+ let timeParseErr = null, timeParseOffset = 0;
1890
+ let rawBytes = new Uint8Array(0);
1891
+ let rawPos = 0;
1892
+ let hexErr = null;
1893
+ let base64Err = null;
1894
+ let customBase64Encodings = [];
1325
1895
  return {
1896
+ "fmt_sscan_init": (s, mode) => {
1897
+ sscanHasErr = 0;
1898
+ sscanErrMsg = "";
1899
+ const str = s ?? "";
1900
+ sscanTokens = (mode === 1 ? str.split("\\n")[0] : str).trim().split(/\\s+/).filter(Boolean);
1901
+ return sscanTokens.length;
1902
+ },
1903
+ "fmt_sscanf_init": (s, fmt) => {
1904
+ sscanHasErr = 0;
1905
+ sscanErrMsg = "";
1906
+ const str = s ?? "";
1907
+ const fmtStr = fmt ?? "";
1908
+ let si = 0, fi = 0;
1909
+ sscanTokens = [];
1910
+ while (fi < fmtStr.length) {
1911
+ if (/\\s/.test(fmtStr[fi])) {
1912
+ while (fi < fmtStr.length && /\\s/.test(fmtStr[fi])) fi++;
1913
+ while (si < str.length && /\\s/.test(str[si])) si++;
1914
+ continue;
1915
+ }
1916
+ if (fmtStr[fi] === "%") {
1917
+ if (fi + 1 < fmtStr.length && fmtStr[fi + 1] === "%") {
1918
+ if (str[si] !== "%") {
1919
+ sscanHasErr = 1;
1920
+ sscanErrMsg = "input does not match format";
1921
+ break;
1922
+ }
1923
+ si++;
1924
+ fi += 2;
1925
+ continue;
1926
+ }
1927
+ const vm = fmtStr.slice(fi).match(/^%[vdsfqtxXbcoeEfFgG]/);
1928
+ if (vm) {
1929
+ const verb = vm[0];
1930
+ fi += verb.length;
1931
+ if (verb !== "%c") {
1932
+ while (si < str.length && /\\s/.test(str[si])) si++;
1933
+ }
1934
+ if (si >= str.length) break;
1935
+ let tokRe = /^\\S+/;
1936
+ if (verb === "%d" || verb === "%i") tokRe = /^[+-]?\\d+/;
1937
+ else if (verb === "%f" || verb === "%F" || verb === "%e" || verb === "%E" || verb === "%g" || verb === "%G") tokRe = /^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?/;
1938
+ else if (verb === "%t") tokRe = /^(?:true|false|1|0|t|f|T|F|TRUE|FALSE)/;
1939
+ else if (verb === "%x" || verb === "%X") tokRe = /^[+-]?(?:0x)?[0-9a-fA-F]+/;
1940
+ else if (verb === "%c") tokRe = /^[\\s\\S]/;
1941
+ const m = str.slice(si).match(tokRe);
1942
+ if (!m) {
1943
+ sscanHasErr = 1;
1944
+ sscanErrMsg = "input does not match format";
1945
+ break;
1946
+ }
1947
+ sscanTokens.push(m[0]);
1948
+ si += m[0].length;
1949
+ continue;
1950
+ }
1951
+ }
1952
+ if (si >= str.length) break;
1953
+ if (str[si] !== fmtStr[fi]) {
1954
+ sscanHasErr = 1;
1955
+ sscanErrMsg = "input does not match format";
1956
+ break;
1957
+ }
1958
+ si++;
1959
+ fi++;
1960
+ }
1961
+ return sscanTokens.length;
1962
+ },
1963
+ "fmt_sscan_has_err": () => sscanHasErr,
1964
+ "fmt_sscan_err_msg": () => sscanErrMsg || "syntax error",
1965
+ "fmt_sscan_token_str": (i) => sscanTokens[i] ?? "",
1966
+ "fmt_sscan_token_i64": (i) => {
1967
+ if (i >= sscanTokens.length) { sscanHasErr = 1; sscanErrMsg = "unexpected EOF"; return 0n; }
1968
+ const t = sscanTokens[i];
1969
+ if (/^[+-]?(?:0[xX])?[0-9a-fA-F]+$/.test(t)) {
1970
+ try {
1971
+ if (/^[+-]?[0-9a-fA-F]+$/.test(t) && /[a-fA-F]/.test(t)) {
1972
+ return BigInt("0x" + t);
1973
+ }
1974
+ return BigInt(t);
1975
+ } catch {}
1976
+ }
1977
+ if (t.length === 1) {
1978
+ return BigInt(t.charCodeAt(0));
1979
+ }
1980
+ sscanHasErr = 1;
1981
+ sscanErrMsg = "expected integer";
1982
+ return 0n;
1983
+ },
1984
+ "fmt_sscan_token_f64": (i) => {
1985
+ if (i >= sscanTokens.length) { sscanHasErr = 1; sscanErrMsg = "unexpected EOF"; return 0; }
1986
+ const t = sscanTokens[i];
1987
+ const n = Number(t);
1988
+ if (Number.isNaN(n) || !/^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?$/.test(t)) {
1989
+ sscanHasErr = 1;
1990
+ sscanErrMsg = "expected float";
1991
+ return 0;
1992
+ }
1993
+ return n;
1994
+ },
1995
+ "fmt_sscan_token_bool": (i) => {
1996
+ if (i >= sscanTokens.length) { sscanHasErr = 1; sscanErrMsg = "unexpected EOF"; return 0; }
1997
+ const t = sscanTokens[i]?.toLowerCase();
1998
+ if (t === "true" || t === "1" || t === "t") return 1;
1999
+ if (t === "false" || t === "0" || t === "f") return 0;
2000
+ sscanHasErr = 1;
2001
+ sscanErrMsg = "expected boolean";
2002
+ return 0;
2003
+ },
1326
2004
  "testing_run": (t, name, fn) => (t.Run(name, (sub) => { io.late.exports.__testing_run_cb(fn, sub); }) ? 1 : 0),
1327
2005
  "fmt_sprintf": (f) => __sprintf(f ?? "", ...io.take()),
1328
2006
  "fmt_printf": (f) => { io.printf(__sprintf(f ?? "", ...io.take())); },
2007
+ "fmt_format_v": (v) => { const res = __sprintf("%v", v); io.take(); return res; },
1329
2008
  "str_trim": (s, c) => ${P}trim(s ?? "", c ?? "", true, true),
1330
2009
  "str_trim_left": (s, c) => ${P}trim(s ?? "", c ?? "", true, false),
1331
2010
  "str_trim_right": (s, c) => ${P}trim(s ?? "", c ?? "", false, true),
1332
2011
  "str_trim_prefix": (s, p) => (s && p && s.startsWith(p) ? s.slice(p.length) : (s ?? "")),
1333
2012
  "str_trim_suffix": (s, p) => (s && p && s.endsWith(p) ? s.slice(0, s.length - p.length) : (s ?? "")),
2013
+ "str_cut": (s, sep) => {
2014
+ const i = (s ?? "").indexOf(sep ?? "");
2015
+ if (i < 0) { cutBefore = s ?? ""; cutAfter = ""; return 0; }
2016
+ cutBefore = (s ?? "").slice(0, i);
2017
+ cutAfter = (s ?? "").slice(i + (sep ?? "").length);
2018
+ return 1;
2019
+ },
2020
+ "str_cut_before": () => cutBefore,
2021
+ "str_cut_after": () => cutAfter,
2022
+ "str_cut_prefix": (s, p) => {
2023
+ s = s ?? ""; p = p ?? "";
2024
+ if (s.startsWith(p)) { cutAfter = s.slice(p.length); return 1; }
2025
+ cutAfter = s; return 0;
2026
+ },
2027
+ "str_cut_suffix": (s, suf) => {
2028
+ s = s ?? ""; suf = suf ?? "";
2029
+ if (s.endsWith(suf)) { cutBefore = s.slice(0, s.length - suf.length); return 1; }
2030
+ cutBefore = s; return 0;
2031
+ },
1334
2032
  "str_replace": (s, o, n, cnt) => {
1335
2033
  s = s ?? ""; o = o ?? ""; n = n ?? "";
1336
2034
  if (cnt < 0) return s.replaceAll(o, n);
@@ -1350,18 +2048,175 @@ function ${P}stdlib(stringTable, io) {
1350
2048
  "str_index_rune": (s, r) => (s ?? "").indexOf(String.fromCodePoint(r)),
1351
2049
  "str_index_any": (s, cs) => { s = s ?? ""; let best = -1; for (const c of (cs ?? "")) { const i = s.indexOf(c); if (i >= 0 && (best < 0 || i < best)) best = i; } return best; },
1352
2050
  "str_last_index_byte": (s, b) => (s ?? "").lastIndexOf(String.fromCharCode(b)),
2051
+ "str_last_index_any": (s, cs) => {
2052
+ s = s ?? ""; let best = -1;
2053
+ for (const c of (cs ?? "")) {
2054
+ const i = s.lastIndexOf(c);
2055
+ if (i > best) best = i;
2056
+ }
2057
+ return best;
2058
+ },
1353
2059
  "str_title": (s) => (s ?? "").replace(/(^|[^\\p{L}\\p{N}_'])(\\p{Ll})/gu, (m, a, b) => a + b.toUpperCase()),
1354
2060
  "str_split": (s, sep) => { parts = (s ?? "").split(sep ?? ""); return parts.length; },
2061
+ "str_split_n": (s, sep, n) => {
2062
+ s = s ?? ""; sep = sep ?? "";
2063
+ if (n === 0) return -1;
2064
+ if (n < 0) { parts = sep === "" ? [...s] : s.split(sep); return parts.length; }
2065
+ if (sep === "") {
2066
+ const chars = [...s];
2067
+ if (chars.length <= n) { parts = chars; return parts.length; }
2068
+ const r = chars.slice(0, n - 1);
2069
+ r.push(chars.slice(n - 1).join(""));
2070
+ parts = r;
2071
+ return parts.length;
2072
+ }
2073
+ const r = [];
2074
+ let cur = s;
2075
+ for (let i = 1; i < n && cur.length; i++) {
2076
+ const j = cur.indexOf(sep);
2077
+ if (j < 0) break;
2078
+ r.push(cur.slice(0, j));
2079
+ cur = cur.slice(j + sep.length);
2080
+ }
2081
+ r.push(cur);
2082
+ parts = r;
2083
+ return parts.length;
2084
+ },
2085
+ "str_split_after": (s, sep) => {
2086
+ s = s ?? ""; sep = sep ?? "";
2087
+ if (sep === "") { parts = [...s]; return parts.length; }
2088
+ const r = [];
2089
+ let cur = s;
2090
+ while (true) {
2091
+ const j = cur.indexOf(sep);
2092
+ if (j < 0) { r.push(cur); break; }
2093
+ r.push(cur.slice(0, j + sep.length));
2094
+ cur = cur.slice(j + sep.length);
2095
+ }
2096
+ parts = r;
2097
+ return parts.length;
2098
+ },
2099
+ "str_split_after_n": (s, sep, n) => {
2100
+ s = s ?? ""; sep = sep ?? "";
2101
+ if (n === 0) return -1;
2102
+ if (sep === "") {
2103
+ const chars = [...s];
2104
+ if (n < 0 || chars.length <= n) parts = chars;
2105
+ else {
2106
+ const r = chars.slice(0, n - 1);
2107
+ r.push(chars.slice(n - 1).join(""));
2108
+ parts = r;
2109
+ }
2110
+ return parts.length;
2111
+ }
2112
+ const r = [];
2113
+ let cur = s;
2114
+ for (let i = 1; (n < 0 || i < n) && cur.length; i++) {
2115
+ const j = cur.indexOf(sep);
2116
+ if (j < 0) break;
2117
+ r.push(cur.slice(0, j + sep.length));
2118
+ cur = cur.slice(j + sep.length);
2119
+ }
2120
+ r.push(cur);
2121
+ parts = r;
2122
+ return parts.length;
2123
+ },
1355
2124
  "str_fields": (s) => { parts = (s ?? "").split(/\\s+/).filter((p) => p !== ""); return parts.length; },
1356
2125
  "str_part": (i) => parts[i] ?? "",
1357
- "strconv_atoi": (s) => { const r = __strconv_atoi(s ?? ""); strconvErr = r[1]; return r[0]; },
1358
- "strconv_parse_int": (s, base, bits) => { const r = __strconv_parse_int(s ?? "", base, bits); strconvErr = r[1]; return r[0]; },
1359
- "strconv_parse_float": (s) => { const r = __strconv_parse_float(s ?? ""); strconvErr = r[1]; return r[0]; },
1360
- "strconv_parse_bool": (s) => { const r = __strconv_parse_bool(s ?? ""); strconvErr = r[1]; return r[0] ? 1 : 0; },
2126
+ "str_replacer_init": (s) => {
2127
+ replacerTarget = s ?? "";
2128
+ replacerPairs = [];
2129
+ },
2130
+ "str_replacer_add": (o, n) => {
2131
+ replacerPairs.push([o ?? "", n ?? ""]);
2132
+ },
2133
+ "str_replacer_run": () => {
2134
+ let s = replacerTarget;
2135
+ if (!replacerPairs.length || s === "") return s;
2136
+ let out = "";
2137
+ let i = 0;
2138
+ while (i < s.length) {
2139
+ let matched = false;
2140
+ for (let k = 0; k < replacerPairs.length; k++) {
2141
+ const [oldStr, newStr] = replacerPairs[k];
2142
+ if (oldStr.length > 0 && s.startsWith(oldStr, i)) {
2143
+ out += newStr;
2144
+ i += oldStr.length;
2145
+ matched = true;
2146
+ break;
2147
+ }
2148
+ }
2149
+ if (!matched) {
2150
+ out += s[i];
2151
+ i++;
2152
+ }
2153
+ }
2154
+ return out;
2155
+ },
2156
+ "strconv_atoi": (s) => {
2157
+ const r = __strconv_atoi(s ?? "");
2158
+ strconvErr = r[1];
2159
+ strconvErrKind = r[1] ? (r[1].includes("invalid syntax") ? 1 : r[1].includes("value out of range") ? 2 : 0) : 0;
2160
+ return r[0];
2161
+ },
2162
+ "strconv_parse_int": (s, base, bits) => {
2163
+ const r = __strconv_parse_int(s ?? "", base, bits);
2164
+ strconvErr = r[1];
2165
+ strconvErrKind = r[1] ? (r[1].includes("invalid syntax") ? 1 : r[1].includes("value out of range") ? 2 : 0) : 0;
2166
+ return r[0];
2167
+ },
2168
+ "strconv_parse_float": (s) => {
2169
+ const r = __strconv_parse_float(s ?? "");
2170
+ strconvErr = r[1];
2171
+ strconvErrKind = r[1] ? (r[1].includes("invalid syntax") ? 1 : r[1].includes("value out of range") ? 2 : 0) : 0;
2172
+ return r[0];
2173
+ },
2174
+ "strconv_parse_bool": (s) => {
2175
+ const r = __strconv_parse_bool(s ?? "");
2176
+ strconvErr = r[1];
2177
+ strconvErrKind = r[1] ? (r[1].includes("invalid syntax") ? 1 : r[1].includes("value out of range") ? 2 : 0) : 0;
2178
+ return r[0] ? 1 : 0;
2179
+ },
1361
2180
  "strconv_err": () => strconvErr,
2181
+ "strconv_err_kind": () => strconvErrKind,
1362
2182
  "strconv_format_float": (f, fmtc, prec, _bits) => __strconv_format_float(f, fmtc, prec),
1363
2183
  "strconv_format_int": (n, base) => n.toString(base),
2184
+ "strconv_format_uint": (n, base) => BigInt.asUintN(64, BigInt(n)).toString(base),
2185
+ "strconv_parse_uint": (s, base, bits) => {
2186
+ const r = __strconv_parse_uint(s ?? "", base, bits);
2187
+ strconvErr = r[1];
2188
+ strconvErrKind = r[1] ? (r[1].includes("invalid syntax") ? 1 : r[1].includes("value out of range") ? 2 : 0) : 0;
2189
+ return r[0];
2190
+ },
1364
2191
  "strconv_quote": (s) => JSON.stringify(s ?? ""),
2192
+ "strconv_unquote": (s) => {
2193
+ s = s ?? "";
2194
+ if (s.startsWith("\`") && s.endsWith("\`") && s.length >= 2) {
2195
+ if (s.slice(1, -1).includes("\`")) {
2196
+ strconvErr = "invalid syntax";
2197
+ strconvErrKind = 1;
2198
+ return "";
2199
+ }
2200
+ strconvErr = null;
2201
+ strconvErrKind = 0;
2202
+ return s.slice(1, -1);
2203
+ }
2204
+ try {
2205
+ const v = JSON.parse(s);
2206
+ if (typeof v !== "string") {
2207
+ strconvErr = "invalid syntax";
2208
+ strconvErrKind = 1;
2209
+ return "";
2210
+ }
2211
+ strconvErr = null;
2212
+ strconvErrKind = 0;
2213
+ return v;
2214
+ } catch (e) {
2215
+ strconvErr = "invalid syntax";
2216
+ strconvErrKind = 1;
2217
+ return "";
2218
+ }
2219
+ },
1365
2220
  "utf8_rune_count": (s) => { let n = 0; for (const _ of (s ?? "")) n++; return n; },
1366
2221
  "utf8_rune_len": (r) => (r < 0 || r > 0x10ffff || (r >= 0xd800 && r <= 0xdfff) ? -1 : r < 0x80 ? 1 : r < 0x800 ? 2 : r < 0x10000 ? 3 : 4),
1367
2222
  "utf8_valid_string": (s) => (s ?? "").isWellFormed ? ((s ?? "").isWellFormed() ? 1 : 0) : (/[\\uD800-\\uDFFF]/.test((s ?? "").replace(/[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/g, "")) ? 0 : 1),
@@ -1378,14 +2233,205 @@ function ${P}stdlib(stringTable, io) {
1378
2233
  },
1379
2234
  "utf8_decode_size": () => decodeSize,
1380
2235
  "utf8_full_rune": (s) => { s = s ?? ""; if (s.length === 0) return 0; const c = s.charCodeAt(0); return (c >= 0xd800 && c <= 0xdbff && s.length < 2) ? 0 : 1; },
2236
+ // unicode
2237
+ "unicode_is_letter": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{L}/u.test(c)) ? 1 : 0; },
2238
+ "unicode_is_digit": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Nd}/u.test(c)) ? 1 : 0; },
2239
+ "unicode_is_space": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\s/u.test(c)) ? 1 : 0; },
2240
+ "unicode_is_upper": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Lu}/u.test(c)) ? 1 : 0; },
2241
+ "unicode_is_lower": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Ll}/u.test(c)) ? 1 : 0; },
2242
+ "unicode_is_punct": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{P}/u.test(c)) ? 1 : 0; },
2243
+ "unicode_is_control": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Cc}/u.test(c)) ? 1 : 0; },
2244
+ "unicode_is_print": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && !/\\p{Cc}/u.test(c)) ? 1 : 0; },
2245
+ "unicode_is_graphic": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && !/\\p{Cc}/u.test(c)) ? 1 : 0; },
2246
+ "unicode_to_upper": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return c ? (c.toUpperCase().codePointAt(0) ?? r) : r; },
2247
+ "unicode_to_lower": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return c ? (c.toLowerCase().codePointAt(0) ?? r) : r; },
2248
+ // html
2249
+ "html_escape": (s) => (s ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&#34;").replace(/'/g, "&#39;"),
2250
+ "html_unescape": (s) => (s ?? "").replace(/&(?:quot|apos|#39|#34|gt|lt|amp|#(?:[0-9]+|[xX][0-9a-fA-F]+));/g, (m) => {
2251
+ switch (m) {
2252
+ case "&quot;": case "&#34;": return '"';
2253
+ case "&apos;": case "&#39;": return "'";
2254
+ case "&gt;": return ">";
2255
+ case "&lt;": return "<";
2256
+ case "&amp;": return "&";
2257
+ default: {
2258
+ if (m.startsWith("&#")) {
2259
+ const raw = m.slice(2, -1);
2260
+ const cp = (raw.startsWith("x") || raw.startsWith("X")) ? parseInt(raw.slice(1), 16) : parseInt(raw, 10);
2261
+ return (cp >= 0 && cp <= 0x10FFFF && !(cp >= 0xD800 && cp <= 0xDFFF)) ? String.fromCodePoint(cp) : "\\uFFFD";
2262
+ }
2263
+ return m;
2264
+ }
2265
+ }
2266
+ }),
2267
+ // path
2268
+ "path_base": (p) => { if (!p) return "."; const stripped = p.replace(/\\/+$/, ""); if (!stripped) return "/"; const i = stripped.lastIndexOf("/"); return i < 0 ? stripped : stripped.slice(i + 1) || "/"; },
2269
+ "path_dir": (p) => { const s = p ?? ""; const i = s.lastIndexOf("/"); if (i < 0) return "."; const res = s.slice(0, i); return res === "" ? "/" : __pathClean(res); },
2270
+ "path_ext": (p) => { const s = p ?? ""; const b = s.slice(s.lastIndexOf("/") + 1); const i = b.lastIndexOf("."); return i <= 0 ? "" : b.slice(i); },
2271
+ "path_is_abs": (p) => (p ?? "").startsWith("/") ? 1 : 0,
2272
+ "path_clean": (p) => __pathClean(p ?? ""),
2273
+ "path_split": (p) => {
2274
+ const s = p ?? "";
2275
+ const i = s.lastIndexOf("/");
2276
+ if (i < 0) { splitDir = ""; splitFile = s; }
2277
+ else { splitDir = s.slice(0, i + 1); splitFile = s.slice(i + 1); }
2278
+ },
2279
+ "path_split_dir": () => splitDir,
2280
+ "path_split_file": () => splitFile,
2281
+ "path_join_init": () => { joinParts = []; },
2282
+ "path_join_push": (s) => { if (s) joinParts.push(s); },
2283
+ "path_join_run": () => {
2284
+ if (joinParts.length === 0) return "";
2285
+ const res = __pathClean(joinParts.join("/"));
2286
+ joinParts = [];
2287
+ return res;
2288
+ },
2289
+ "path_match": (pat, name) => {
2290
+ const r = __pathMatch(pat ?? "", name ?? "");
2291
+ matchHasErr = r[1] ? 1 : 0;
2292
+ return r[0] ? 1 : 0;
2293
+ },
2294
+ "path_match_has_err": () => matchHasErr,
2295
+ // bytes trim
2296
+ "bytes_trim": (s, cutset, left, right) => {
2297
+ const cs = new Set(Array.from(cutset ?? ""));
2298
+ const chars = Array.from(s ?? "");
2299
+ let a = 0, b = chars.length;
2300
+ if (left) while (a < b && cs.has(chars[a])) a++;
2301
+ if (right) while (b > a && cs.has(chars[b - 1])) b--;
2302
+ const enc = new TextEncoder();
2303
+ trimStart = a === 0 ? 0 : enc.encode(chars.slice(0, a).join("")).length;
2304
+ trimEnd = b === chars.length ? enc.encode(s ?? "").length : enc.encode(chars.slice(0, b).join("")).length;
2305
+ },
2306
+ "bytes_trim_start": () => trimStart,
2307
+ "bytes_trim_end": () => trimEnd,
2308
+ // time
2309
+ "time_now_ms": () => (typeof globalThis.__GOFRONT_CLOCK === "function" ? globalThis.__GOFRONT_CLOCK() : Date.now()),
2310
+ "time_timezone_offset": (ms) => -new Date(ms).getTimezoneOffset() * 60,
2311
+ "time_duration_string": (d) => __durationString(d),
2312
+ "time_parse": (layout, val) => {
2313
+ try {
2314
+ const d = new Date(val ?? "");
2315
+ if (isNaN(d.getTime())) throw 0;
2316
+ timeParseErr = null;
2317
+ timeParseOffset = -d.getTimezoneOffset() * 60;
2318
+ return d.getTime();
2319
+ } catch {
2320
+ timeParseErr = "parsing time: cannot parse";
2321
+ timeParseOffset = 0;
2322
+ return 0;
2323
+ }
2324
+ },
2325
+ "time_parse_err": () => timeParseErr,
2326
+ "time_parse_offset": () => timeParseOffset,
2327
+ "time_format": (y, m, d, hr, min, sec, wd, ms, offsetSec, layout) => {
2328
+ const DAYS = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
2329
+ const MONTHS = ["January","February","March","April","May","June","July","August","September","October","November","December"];
2330
+ const pad = (n, w = 2) => String(n).padStart(w, "0");
2331
+ return (layout ?? "").replace(/2006|06|Monday|Mon|January|Jan|01|02|15|PM|pm|03|04|05|Z07:00|\\.(?:0+|9+)|1|2|3|4|5/g, t => {
2332
+ if (t.startsWith(".")) {
2333
+ const isZeroPad = t.includes("0");
2334
+ const maxLen = t.length - 1;
2335
+ let frac = pad(ms % 1000, 3);
2336
+ if (maxLen > 3) frac = frac.padEnd(maxLen, "0");
2337
+ else frac = frac.slice(0, maxLen);
2338
+ if (!isZeroPad) {
2339
+ frac = frac.replace(/0+$/, "");
2340
+ return frac.length > 0 ? "." + frac : "";
2341
+ }
2342
+ return "." + frac;
2343
+ }
2344
+ switch (t) {
2345
+ case "2006": return String(y);
2346
+ case "06": return String(y).slice(-2);
2347
+ case "Monday": return DAYS[wd];
2348
+ case "Mon": return DAYS[wd].slice(0, 3);
2349
+ case "January": return MONTHS[m - 1];
2350
+ case "Jan": return MONTHS[m - 1].slice(0, 3);
2351
+ case "01": return pad(m);
2352
+ case "1": return String(m);
2353
+ case "02": return pad(d);
2354
+ case "2": return String(d);
2355
+ case "15": return pad(hr);
2356
+ case "PM": return hr < 12 ? "AM" : "PM";
2357
+ case "pm": return hr < 12 ? "am" : "pm";
2358
+ case "03": return pad(hr % 12 || 12);
2359
+ case "3": return String(hr % 12 || 12);
2360
+ case "04": return pad(min);
2361
+ case "4": return String(min);
2362
+ case "05": return pad(sec);
2363
+ case "5": return String(sec);
2364
+ case "Z07:00": {
2365
+ const o = Math.floor(offsetSec / 60);
2366
+ return o === 0 ? "Z" : (o > 0 ? "+" : "-") + pad(Math.floor(Math.abs(o) / 60)) + ":" + pad(Math.abs(o) % 60);
2367
+ }
2368
+ default: return t;
2369
+ }
2370
+ });
2371
+ },
2372
+ "raw_bytes_init": (len) => {
2373
+ rawBytes = new Uint8Array(len);
2374
+ rawPos = 0;
2375
+ },
2376
+ "raw_bytes_push": (b) => {
2377
+ if (rawPos < rawBytes.length) {
2378
+ rawBytes[rawPos++] = b & 0xff;
2379
+ }
2380
+ },
2381
+ "raw_bytes_len": () => rawBytes.length,
2382
+ "raw_bytes_get": (i) => rawBytes[i] ?? 0,
2383
+ "crypto_rand_fill": (len) => {
2384
+ rawBytes = new Uint8Array(len);
2385
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
2386
+ const CHUNK = 65536;
2387
+ for (let offset = 0; offset < len; offset += CHUNK) {
2388
+ crypto.getRandomValues(rawBytes.subarray(offset, Math.min(offset + CHUNK, len)));
2389
+ }
2390
+ } else {
2391
+ for (let i = 0; i < len; i++) rawBytes[i] = Math.floor(Math.random() * 256);
2392
+ }
2393
+ return len;
2394
+ },
2395
+ "crypto_sha256_sum": () => {
2396
+ const d = __sha256.Sum256(rawBytes);
2397
+ rawBytes = new Uint8Array(d);
2398
+ return 32;
2399
+ },
2400
+ "hex_encode_to_string": () => {
2401
+ return __hex_encode_to_string(rawBytes);
2402
+ },
2403
+ "hex_decode_string": (s) => {
2404
+ const [res, err] = __hex_decode_string(s ?? "");
2405
+ hexErr = err ? (err.message ?? String(err)) : null;
2406
+ rawBytes = new Uint8Array(res);
2407
+ return rawBytes.length;
2408
+ },
2409
+ "hex_get_err": () => hexErr,
2410
+ "base64_new_encoding": (alphabet) => {
2411
+ const id = customBase64Encodings.length + 4;
2412
+ customBase64Encodings.push(__base64_make(alphabet ?? "", "="));
2413
+ return id;
2414
+ },
2415
+ "base64_encode_to_string": (mode) => {
2416
+ const enc = mode === 1 ? __base64_url : mode === 2 ? __base64_raw_std : mode === 3 ? __base64_raw_url : mode >= 4 ? customBase64Encodings[mode - 4] : __base64_std;
2417
+ return (enc ?? __base64_std).EncodeToString(rawBytes);
2418
+ },
2419
+ "base64_decode_string": (mode, s) => {
2420
+ const enc = mode === 1 ? __base64_url : mode === 2 ? __base64_raw_std : mode === 3 ? __base64_raw_url : mode >= 4 ? customBase64Encodings[mode - 4] : __base64_std;
2421
+ const [res, err] = (enc ?? __base64_std).DecodeString(s ?? "");
2422
+ base64Err = err ? (err.message ?? String(err)) : null;
2423
+ rawBytes = new Uint8Array(res);
2424
+ return rawBytes.length;
2425
+ },
2426
+ "base64_get_err": () => base64Err,
1381
2427
  };
1382
2428
  }
1383
2429
  function ${P}trim(s, cutset, left, right) {
1384
- const cs = Array.from(cutset);
2430
+ const cs = new Set(Array.from(cutset));
1385
2431
  const chars = Array.from(s);
1386
2432
  let a = 0, b = chars.length;
1387
- if (left) while (a < b && cs.includes(chars[a])) a++;
1388
- if (right) while (b > a && cs.includes(chars[b - 1])) b--;
2433
+ if (left) while (a < b && cs.has(chars[a])) a++;
2434
+ if (right) while (b > a && cs.has(chars[b - 1])) b--;
1389
2435
  return chars.slice(a, b).join("");
1390
2436
  }`;
1391
2437
 
@@ -1512,6 +2558,39 @@ export function generateFacade(
1512
2558
  }
1513
2559
  }
1514
2560
 
2561
+ for (const [name, desc] of needs.anonStructs) {
2562
+ out.push(`function ${P}to_${name}(o) {
2563
+ if (o == null) return __w.__zero_${name}();
2564
+ return __w.__new_${name}(${desc.fields.map((f) => inExpr(f.desc, `(o.${f.name} ?? ${zeroExpr(f.desc)})`)).join(", ")});
2565
+ }
2566
+ function ${P}from_${name}(r) {
2567
+ if (r == null) return null;
2568
+ return {
2569
+ ${desc.fields.map((f) => `\t\t"${f.name}": ${outExpr(f.desc, `__w.__get_${name}_${f.name}(r)`)}`).join(",\n")}
2570
+ };
2571
+ }
2572
+ function ${P}back_${name}(r, o) {
2573
+ if (r == null || o == null) return;
2574
+ ${desc.fields.map((f) => `\to.${f.name} = ${outExpr(f.desc, `__w.__get_${name}_${f.name}(r)`)};`).join("\n")}
2575
+ }`);
2576
+ }
2577
+
2578
+ for (const [key, desc] of needs.boxes) {
2579
+ out.push(`function ${P}boxin_${key}(b) {
2580
+ if (b == null) return null;
2581
+ const v = b.value !== undefined ? b.value : ${zeroExpr(desc.base)};
2582
+ return __w.__box_new_${key}(${inExpr(desc.base, "v")});
2583
+ }
2584
+ function ${P}boxout_${key}(r) {
2585
+ if (r == null) return null;
2586
+ return { "value": ${outExpr(desc.base, `__w.__box_get_${key}(r)`)} };
2587
+ }
2588
+ function ${P}boxback_${key}(r, b) {
2589
+ if (r == null || b == null) return;
2590
+ b.value = ${outExpr(desc.base, `__w.__box_get_${key}(r)`)};
2591
+ }`);
2592
+ }
2593
+
1515
2594
  for (const [key, desc] of needs.slices) {
1516
2595
  out.push(`function ${P}slin_${key}(arr) {
1517
2596
  if (arr == null) return __w.__slice_nil_${key}();
@@ -1530,8 +2609,34 @@ function ${P}slout_${key}(s) {
1530
2609
  function ${P}slview_${key}(s) {
1531
2610
  if (s == null || __w.__slice_is_nil_${key}(s)) return null;
1532
2611
  return ${P}idxview(__w.__slice_len_${key}(s), (i) => ${viewExpr(desc.elem, `__w.__slice_get_${key}(s, i)`)}, (i, v) => __w.__slice_set_${key}(s, i, ${inExpr(desc.elem, "v")}));
2612
+ } `);
2613
+ }
2614
+
2615
+ for (const [key, desc] of needs.maps) {
2616
+ out.push(`function ${P}mapin_${key}(m) {
2617
+ if (m == null) return null;
2618
+ const entries = m instanceof Map ? m.entries() : Object.entries(m);
2619
+ const size = m instanceof Map ? m.size : Object.keys(m).length;
2620
+ const r = __w.__map_new_${key}(size);
2621
+ for (const [k, v] of entries) {
2622
+ __w.__map_set_${key}(r, ${desc.keyDesc.k === "bool" ? `(k === "true" || k === true ? 1 : 0)` : inExpr(desc.keyDesc, "k")}, ${inExpr(desc.valDesc, "v")});
2623
+ }
2624
+ return r;
2625
+ }
2626
+ function ${P}mapout_${key}(m) {
2627
+ if (m == null) return null;
2628
+ const ks = ${P}slout_${desc.keySliceDesc.key}(__w.__map_keys_${key}(m));
2629
+ const vs = ${P}slout_${desc.valSliceDesc.key}(__w.__map_values_${key}(m));
2630
+ const out = new Map();
2631
+ if (ks) {
2632
+ for (let i = 0; i < ks.length; i++) {
2633
+ out.set(ks[i], vs[i]);
2634
+ }
2635
+ }
2636
+ return out;
1533
2637
  }`);
1534
2638
  }
2639
+
1535
2640
  for (const [key, desc] of needs.arrays) {
1536
2641
  out.push(`function ${P}arrzero_${key}(n) {
1537
2642
  const out = new Array(n);
@@ -1555,7 +2660,7 @@ function ${P}arrout_${key}(a) {
1555
2660
  function ${P}arrview_${key}(a) {
1556
2661
  if (a == null) return null;
1557
2662
  return ${P}idxview(__w.__array_len_${key}(a), (i) => ${viewExpr(desc.elem, `__w.__array_get_${key}(a, i)`)}, (i, v) => __w.__array_set_${key}(a, i, ${inExpr(desc.elem, "v")}));
1558
- }`);
2663
+ } `);
1559
2664
  }
1560
2665
 
1561
2666
  for (const [key, desc] of needs.funcs) {
@@ -1594,6 +2699,59 @@ ${indent(body, "\t")}
1594
2699
 
1595
2700
  for (const [key, desc] of needs.ifaces) genIfaceFacade(out, key, desc);
1596
2701
 
2702
+ if (needs.error) {
2703
+ out.push(`class ${P}Error extends Error {
2704
+ constructor(msg, ref) {
2705
+ super(msg);
2706
+ this.name = "GoError";
2707
+ this._msg = msg;
2708
+ this.__ref = ref;
2709
+ }
2710
+ Error() {
2711
+ return this.message;
2712
+ }
2713
+ }
2714
+ const ${P}err_w2j = new WeakMap();
2715
+ function ${P}errout(r) {
2716
+ if (r == null) return null;
2717
+ let e = ${P}err_w2j.get(r);
2718
+ if (!e) {
2719
+ const msg = __w.__error_msg ? (__w.__error_msg(r) ?? "") : "";
2720
+ e = new ${P}Error(msg, r);
2721
+ ${P}err_w2j.set(r, e);
2722
+ }
2723
+ return e;
2724
+ }
2725
+ function ${P}errin(e) {
2726
+ if (e == null) return null;
2727
+ if (e.__ref === undefined) throw new TypeError("GoFront: expected a WASM-owned error");
2728
+ if (!__w.__is_error(e.__ref)) throw new TypeError("GoFront: value does not implement error");
2729
+ return e.__ref;
2730
+ }`);
2731
+ }
2732
+
2733
+ if (needs.time) {
2734
+ out.push(`function ${P}timeout(r) {
2735
+ if (r == null) return null;
2736
+ const ms = __w.__time_unix_milli ? __w.__time_unix_milli(r) : 0;
2737
+ const off = __w.__time_offset ? __w.__time_offset(r) : 0;
2738
+ return { _d: new Date(ms), _offset: off };
2739
+ }
2740
+ function ${P}timeout_ptr(r) {
2741
+ return ${P}timeout(r);
2742
+ }
2743
+ function ${P}timein(v) {
2744
+ if (v == null) return null;
2745
+ const d = v._d ? v._d : (v instanceof Date ? v : new Date(v));
2746
+ const ms = d.getTime();
2747
+ const off = typeof v?._offset === "number" ? v._offset : -d.getTimezoneOffset() * 60;
2748
+ return __w.__time_make ? __w.__time_make(ms, off) : null;
2749
+ }
2750
+ function ${P}timein_ptr(v) {
2751
+ return ${P}timein(v);
2752
+ }`);
2753
+ }
2754
+
1597
2755
  if (needs.shared) {
1598
2756
  // Zero-copy TypedArray views over the exported memory. Memory never
1599
2757
  // grows after the start function (allocation is startup-only), but the
@@ -1615,7 +2773,15 @@ function ${P}shin(v, Ctor) {
1615
2773
  }`);
1616
2774
  }
1617
2775
 
1618
- for (const c of meta.consts) out.push(`const ${c.name} = ${c.value};`);
2776
+ for (const c of meta.consts) {
2777
+ if (c.dynamic) {
2778
+ out.push(
2779
+ `const ${c.name} = ${outExpr(c.desc, `__w.__const_${c.name}()`)};`,
2780
+ );
2781
+ } else {
2782
+ out.push(`const ${c.name} = ${c.value};`);
2783
+ }
2784
+ }
1619
2785
  for (const v of meta.vars ?? [])
1620
2786
  out.push(
1621
2787
  `const ${v.name} = ${P}shout(__w.__var_${v.name}(), ${v.desc.ctor});`,