gofront 1.4.0 → 1.6.0

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 (77) hide show
  1. package/README.md +174 -403
  2. package/package.json +16 -19
  3. package/src/{codegen → backend/js}/expressions.js +265 -77
  4. package/src/{codegen → backend/js}/index.js +120 -89
  5. package/src/{codegen → backend/js}/runtime.js +102 -0
  6. package/src/{codegen → backend/js}/source-map.js +32 -0
  7. package/src/{codegen → backend/js}/statements.js +139 -64
  8. package/src/backend/js/stdlib/bits.js +32 -0
  9. package/src/{codegen → backend/js}/stdlib/errors.js +2 -2
  10. package/src/{codegen → backend/js}/stdlib/fmt.js +26 -8
  11. package/src/{codegen → backend/js}/stdlib/index.js +6 -0
  12. package/src/{codegen → backend/js}/stdlib/maps.js +1 -1
  13. package/src/{codegen → backend/js}/stdlib/path.js +2 -2
  14. package/src/backend/js/stdlib/shared.js +17 -0
  15. package/src/{codegen → backend/js}/stdlib/slices.js +1 -1
  16. package/src/{codegen → backend/js}/stdlib/sort.js +1 -1
  17. package/src/backend/js/stdlib/strconv.js +58 -0
  18. package/src/{codegen → backend/js}/stdlib/strings.js +4 -1
  19. package/src/{codegen → backend/js}/stdlib/testing.js +2 -2
  20. package/src/{codegen → backend/js}/stdlib/time.js +3 -3
  21. package/src/{codegen → backend/js}/templ.js +3 -3
  22. package/src/backend/wasm/boundary.js +1798 -0
  23. package/src/backend/wasm/emit-builtins.js +1103 -0
  24. package/src/backend/wasm/emit-exprs.js +1908 -0
  25. package/src/backend/wasm/emit-maps.js +1863 -0
  26. package/src/backend/wasm/emit-shared.js +519 -0
  27. package/src/backend/wasm/emit-stdlib.js +3006 -0
  28. package/src/backend/wasm/emit-stmts.js +2472 -0
  29. package/src/backend/wasm/emit.js +1376 -0
  30. package/src/backend/wasm/encode.js +937 -0
  31. package/src/backend/wasm/glue.js +144 -0
  32. package/src/backend/wasm/index.js +2790 -0
  33. package/src/backend/wasm/monomorph.js +711 -0
  34. package/src/backend/wasm/optimize.js +227 -0
  35. package/src/backend/wasm/types.js +544 -0
  36. package/src/backend/wasm/wat.js +302 -0
  37. package/src/cli-core.js +323 -75
  38. package/src/compiler.js +447 -14
  39. package/src/dev-server.js +56 -15
  40. package/src/index.js +99 -120
  41. package/src/lexer.js +18 -0
  42. package/src/lower/boxing.js +35 -0
  43. package/src/lower/captures.js +172 -0
  44. package/src/lower/embedding.js +30 -0
  45. package/src/lower/escape.js +164 -0
  46. package/src/lower/functions.js +302 -0
  47. package/src/lower/globals.js +316 -0
  48. package/src/lower/index.js +118 -0
  49. package/src/lower/ownership.js +137 -0
  50. package/src/lower/range.js +67 -0
  51. package/src/lower/walk.js +25 -0
  52. package/src/minifier.js +2 -0
  53. package/src/parser/index.js +8 -3
  54. package/src/resolver.js +3 -1
  55. package/src/test-runner.js +94 -20
  56. package/src/typechecker/assignability.js +42 -1
  57. package/src/typechecker/expressions.js +182 -3
  58. package/src/typechecker/index.js +261 -5
  59. package/src/typechecker/resolve.js +35 -6
  60. package/src/typechecker/stdlib/core.js +34 -19
  61. package/src/typechecker/stdlib/extended.js +18 -3
  62. package/src/typechecker/stdlib/shared.js +53 -0
  63. package/src/typechecker/stdlib.js +2 -0
  64. package/src/typechecker/types.js +259 -6
  65. package/src/vendor.js +2 -2
  66. package/src/codegen/stdlib/strconv.js +0 -33
  67. /package/src/{codegen → backend/js}/stdlib/builder.js +0 -0
  68. /package/src/{codegen → backend/js}/stdlib/bytes.js +0 -0
  69. /package/src/{codegen → backend/js}/stdlib/gom.js +0 -0
  70. /package/src/{codegen → backend/js}/stdlib/html.js +0 -0
  71. /package/src/{codegen → backend/js}/stdlib/io.js +0 -0
  72. /package/src/{codegen → backend/js}/stdlib/math.js +0 -0
  73. /package/src/{codegen → backend/js}/stdlib/os.js +0 -0
  74. /package/src/{codegen → backend/js}/stdlib/rand.js +0 -0
  75. /package/src/{codegen → backend/js}/stdlib/regexp.js +0 -0
  76. /package/src/{codegen → backend/js}/stdlib/unicode.js +0 -0
  77. /package/src/{codegen → backend/js}/stdlib/utf8.js +0 -0
package/README.md CHANGED
@@ -1,16 +1,67 @@
1
+ # GoFront
2
+ [![npm version](https://img.shields.io/npm/v/gofront.svg)](https://www.npmjs.com/package/gofront) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
3
+
4
+
5
+ > Go syntax and type safety, compiling to a seamless hybrid of JavaScript (ES modules) and WebAssembly (WasmGC).
6
+
1
7
  ## About
2
8
 
3
9
  Go for the backend: simple, type-safe, no nonsense. JavaScript for the frontend: runs
4
10
  everywhere, no setup. The problem is JavaScript's loose typing — and TypeScript never
5
11
  quite felt like home either.
6
12
 
7
- So I built GoFront. Go syntax and type safety, compiling to plain ES modules. One language
8
- front and back, no runtime, no framework, no tsconfig.json.
13
+ So I built GoFront. Go syntax and type safety, compiling to a **seamless hybrid of
14
+ JavaScript (ES modules) and WebAssembly (WasmGC)**. One language across the entire
15
+ stack, no runtime, no framework, no tsconfig.json.
9
16
 
10
- With built-in support for declarative DOM rendering via the [`gom`](https://www.gomponents.com) standard library, JSX-like [`.templ`](https://templ.guide/) files, and seamless integration with external JavaScript libraries through TypeScript definition files (`.d.ts`), GoFront is designed specifically as a frontend development target. Build complex, reactive user interfaces entirely in Go.
17
+ ### Why Hybrid?
18
+
19
+ Traditional web architectures force a painful choice:
20
+ - **Pure JavaScript:** Excellent DOM ergonomics and dynamic UI, but plagued by IEEE-754 float quirks, JIT deoptimizations, and garbage-collection micro-stutters during heavy compute.
21
+ - **Pure WebAssembly:** Predictable, high-performance execution for tight loops and math, but manipulating the DOM across host calls is notoriously slow, awkward, and requires heavy glue-code runtimes.
22
+
23
+ **GoFront gives you the best of both worlds in a single codebase:**
24
+ - **UI and DOM stay in JavaScript:** `.templ`, `gom`, event listeners, and browser APIs compile to native ES modules with zero-cost DOM access.
25
+ - **Compute runs in WebAssembly:** Simulation, physics, collision detection, and tight math loops compile to WasmGC with true integers, real `float32`, and near-zero memory churn.
26
+ - **The compiler owns the boundary:** It sees both sides and generates all the glue automatically. You write standard Go imports (`import "./physics"`) and the compiler handles the rest.
11
27
 
12
28
  Probably not useful. Definitely fun to build.
13
29
 
30
+ ```go
31
+ // physics/sim.go — compiled to WebAssembly (WasmGC)
32
+ //gofront:target wasm
33
+ package physics
34
+
35
+ type Body struct {
36
+ X, Y, VX, VY float32
37
+ }
38
+
39
+ func Step(b *Body, dt float32) {
40
+ b.X += b.VX * dt
41
+ b.Y += b.VY * dt
42
+ }
43
+ ```
44
+
45
+ ```go
46
+ // main.go — compiled to clean JavaScript (ES module)
47
+ //gofront:target js
48
+ package main
49
+
50
+ import (
51
+ "./physics"
52
+ "gom"
53
+ )
54
+
55
+ var body = physics.Body{X: 0, Y: 0, VX: 10, VY: 5}
56
+
57
+ func onTick() {
58
+ physics.Step(&body, 0.016) // calls directly into WasmGC with zero manual glue code
59
+ render()
60
+ }
61
+ ```
62
+
63
+ With built-in support for declarative DOM rendering via the [`gom`](https://www.gomponents.com) standard library, JSX-like [`.templ`](https://templ.guide/) files, and seamless integration with external JavaScript libraries through TypeScript definition files (`.d.ts`), GoFront is designed specifically as a frontend development target. Build complex, reactive user interfaces entirely in Go.
64
+
14
65
  ```go
15
66
  package main
16
67
 
@@ -35,168 +86,34 @@ func main() {
35
86
  }
36
87
  ```
37
88
 
38
- Compiles to clean, readable JavaScript — no runtime, no framework.
89
+ Compiles to clean, readable JavaScript and WebAssembly — no runtime, no framework.
39
90
 
40
91
  GoFront source files use the `.go` extension so editors automatically apply Go syntax
41
92
  highlighting, bracket matching, and indentation rules without any extra configuration.
42
93
 
43
94
  ---
44
95
 
45
- ## Install
96
+ ## Install & Quick Start
46
97
 
47
98
  ```sh
48
99
  npm install -g gofront
100
+ mkdir my-app && cd my-app
101
+ gofront init
102
+ gofront dev
49
103
  ```
50
104
 
51
- Requires Node.js 20+.
52
-
53
- ---
54
-
55
- ## How it works
56
-
57
- GoFront is a four-stage compiler written in pure Node.js (no dependencies). Every stage
58
- operates on the same AST (abstract syntax tree), running in a single pass per stage:
59
-
60
- ```
61
- source text (.go files)
62
- → Lexer tokenize + Go-style semicolon insertion
63
- → Parser recursive-descent → AST
64
- → Type Checker annotate AST with types + collect errors
65
- → Code Gen AST → JavaScript string
66
-
67
- source text (.templ files)
68
- → TemplLexer dual-mode: Go mode for declarations, HTML mode inside templ bodies
69
- → TemplParser extends Parser; produces TemplDecl AST nodes with TemplNode children
70
- → Type Checker registers each templ component as func(...) gom.Node
71
- → Code Gen TemplDecl → direct DOM calls (createElement / setAttribute / appendChild)
72
- ```
73
-
74
- ### 1. Lexer (`src/lexer.js`)
75
-
76
- Tokenises the source into a stream of tokens. Implements Go's semicolon insertion rules:
77
- a semicolon is automatically inserted after a line's final token if that token is an
78
- identifier, literal, `)`, `]`, `}`, or certain keywords (`return`, `break`, `continue`,
79
- `fallthrough`). This is why Go doesn't need explicit semicolons — and neither does
80
- GoFront.
81
-
82
- ### 2. Parser (`src/parser/`)
83
-
84
- A hand-written recursive-descent parser. No parser generators, no grammar files — just
85
- straightforward top-down parsing. Produces an AST where every node is a plain JS object
86
- with a `kind` field (`"FuncDecl"`, `"IfStmt"`, `"BinaryExpr"`, etc.).
87
-
88
- Operator precedence is handled via a Pratt-style expression parser with numeric
89
- precedence levels.
90
-
91
- ### 3. Type Checker (`src/typechecker/`)
92
-
93
- Three-pass type checker operating on the AST:
94
-
95
- 1. **Pass 1 — collect types**: register all `type` declarations (structs, interfaces,
96
- named types) so they can be referenced before definition.
97
- 2. **Pass 2 — collect functions & vars**: register function signatures and package-level
98
- variables. Resolve embedded struct fields and promote methods.
99
- 3. **Pass 3 — check bodies**: walk every function body, infer expression types, verify
100
- assignments and call arguments, and report errors with source location.
101
-
102
- Types are plain JS objects (`{ kind: "basic", name: "int" }`, `{ kind: "slice",
103
- elem: ... }`, etc.). The special `any` type acts as a recovery/escape hatch — any
104
- operation on it is silently permitted, preventing cascading errors.
105
-
106
- ### 4. Code Generator (`src/codegen/`)
107
-
108
- Walks the typed AST and emits clean, readable JavaScript. No intermediate representation
109
- — the codegen writes directly to an output buffer with indentation tracking.
110
-
111
- Runtime helpers (`__len`, `__append`, `__s`, `__sprintf`, `__equal`, `__cmul`, `__cdiv`,
112
- `__error`, `__errorIs`, `__timeFmt`, `__timeParse`, `__pathClean`, `__sortSlice`,
113
- `__sclone`, `__ifv`, `__ifp`) are tree-shaken: only emitted when actually used. Optional
114
- inline source maps are supported via VLQ-encoded mappings.
115
-
116
- ---
117
-
118
- ## Go → JavaScript mapping
105
+ Then open `http://localhost:3000` — the page live-reloads on every save.
119
106
 
120
- Every Go construct compiles to a specific JavaScript pattern. The output is designed to be
121
- readable and debuggable — no name mangling, no opaque wrappers.
122
-
123
- ### Data structures
124
-
125
- | Go | JavaScript | Notes |
126
- |---|---|---|
127
- | `struct` | ES6 `class` | Positional constructor with zero-value defaults: `new Point(1, 2)`; a generated `__clone()` gives struct values Go copy semantics on assignment, call, return, `append`, and `range` |
128
- | Methods | Class instance methods | Receiver is `this` (typed `*T` for pointer receivers) |
129
- | Embedded structs | Flattened fields + delegation stubs | `Greet(...a) { return Base.prototype.Greet.call(this, ...a); }` |
130
- | `[]T` (slice) | `Array` | `append` → `__append`, `len` → `__len` |
131
- | `[]float32`, `[]int8`…`[]int32`, `[]uint8`/`[]byte`…`[]uint32`, `[]rune` | TypedArray (`Float32Array`, `Int32Array`, `Uint8Array`, …) | `[]float64` and `[]int` stay plain `Array`; slicing → `.subarray` |
132
- | `map[K]V` | Plain object `{}` | Key access via `[]`, iteration via `Object.entries()` |
133
- | `nil` | `null` | |
134
- | `error` | `__error` object | `error("msg")` → `__error("msg")`, `.Error()` → real method call. `toString()` for string context compat |
135
- | Pointers (`*T`) | `{ value: T }` | `new(T)` allocates a boxed zero value |
136
-
137
- ### Functions
138
-
139
- | Go | JavaScript |
140
- |---|---|
141
- | `func f(a int) int` | `function f(a)` |
142
- | Multiple returns `return a, b` | `return [a, b]` — destructured at call site: `let [a, b] = f()` |
143
- | Named returns | Variables pre-declared; bare `return` returns them |
144
- | Variadic `func f(xs ...int)` | `function f(...xs)` |
145
- | `func` literal (closure) | Arrow or function expression |
146
- | `async func` / `await` | `async function` / `await` |
147
- | `init()` | Emitted as `(function() { ... })()` immediately-invoked |
148
- | `defer` | `try { ... } finally { deferred() }` |
149
-
150
- ### Control flow
151
-
152
- | Go | JavaScript |
153
- |---|---|
154
- | `for init; cond; post {}` | `for (init; cond; post) {}` |
155
- | `for cond {}` | `while (cond) {}` |
156
- | `for {}` | `while (true) {}` |
157
- | `for i, v := range slice` | `for (const [i, v] of arr.entries())` |
158
- | `for k, v := range map` | `for (const [k, v] of Object.entries(m))` |
159
- | `for i, r := range str` | `for (const [i, r] of Array.from(s, (c, i) => [i, c.codePointAt(0)]))` — `r` is a rune integer |
160
- | `for i := range n` | `for (let i = 0; i < n; i++)` |
161
- | `switch` / `fallthrough` | `switch` / case fall-through |
162
- | `switch v := x.(type)` | `if/else if` with `typeof` / `instanceof` checks |
163
- | `panic(msg)` | `throw new Error(msg)` |
164
- | `recover()` | Captured in `defer` via `try/catch` |
165
-
166
- ### Builtins
167
-
168
- | Go | JavaScript |
169
- |---|---|
170
- | `len(x)` | `__len(x)` (tree-shaken helper) or `Object.keys(x).length` for maps |
171
- | `cap(x)` | `x.length` |
172
- | `append(s, elems...)` | `[...s, ...elems]` |
173
- | `copy(dst, src)` | Inline splice helper |
174
- | `make([]T, n)` | `new Array(n).fill(zero)` |
175
- | `make(map[K]V)` | `{}` |
176
- | `delete(m, k)` | `delete m[k]` |
177
- | `new(T)` | `{ value: zeroOf(T) }` |
178
- | `min` / `max` | `Math.min` / `Math.max` |
179
- | `clear(x)` | `.length = 0` (slice) or delete-loop (map) |
180
- | `print` / `println` | `console.log` |
181
- | `complex(r, i)` | `{ re: r, im: i }` |
182
- | `real(z)` / `imag(z)` | `z.re` / `z.im` |
183
- | `fmt.Sprintf` | `__sprintf` tree-shaken helper |
184
-
185
- ### Type system at runtime
186
-
187
- All type checking happens at compile time. At runtime, types are erased — there are no
188
- type tags, no reflection, no runtime overhead. Sized integers (`int8`–`int64`,
189
- `uint8`–`uint64`) are all `number` at runtime. `float32` is `number`. Generic type
190
- parameters are erased completely — `func Map[T, U any](...)` compiles to `function Map(...)`.
191
- The type checker enforces correctness; JavaScript doesn't need to know.
107
+ Requires Node.js 20+ (WasmGC requires Node ≥ 22 or a modern browser).
192
108
 
193
109
  ---
194
110
 
195
111
  ## Examples
196
112
 
197
- There are five example apps. Four implement the same todo app to show different aspects
113
+ There are six example apps. Four implement the same todo app to show different aspects
198
114
  of GoFront; the fifth is a WebGL2 3D showcase demonstrating typed graphics APIs and
199
- zero-allocation rendering.
115
+ zero-allocation rendering; the sixth is a hybrid JS + WasmGC particle simulation
116
+ communicating across the boundary via zero-copy `gofront/shared` buffers.
200
117
 
201
118
  ### Simple (vanilla DOM)
202
119
 
@@ -317,6 +234,8 @@ Key syntax features:
317
234
  - `for _, v := range slice { }` — loop rendering inside template bodies
318
235
  - `switch expr { case v: ... default: ... }` — switch rendering inside template bodies
319
236
  - `@templ.Raw(htmlStr)` — inject raw/trusted HTML (uses `insertAdjacentHTML`)
237
+ - `ref="name"` — capture the element at mount time into an optional `refs map[string]any`
238
+ passed as `gom.Mount("#app", AppView(), refs)`; no `querySelector` needed afterwards
320
239
  - Components are called like regular functions (`AppView()`, `TodoItem(t)`) and return
321
240
  a `gom.Node`-compatible object, so `gom.Mount("#app", AppView())` works directly.
322
241
 
@@ -396,289 +315,143 @@ example/webgl/
396
315
  index.html ← canvas element
397
316
  ```
398
317
 
318
+ ### Hybrid (JS + WasmGC particle simulation)
319
+
320
+ A particle simulation stepped inside a `wasm` package (`sim`) and rendered to a 2D canvas
321
+ from JavaScript through a zero-copy `gofront/shared` `Float32` buffer, paired with `.templ`
322
+ UI controls.
323
+
324
+ ```
325
+ example/hybrid/
326
+ src/
327
+ controls.templ ← UI controls (step, reset, gravity toggle, particle count)
328
+ main.go ← canvas rendering loop reading sim.Positions + DOM event wiring
329
+ sim/
330
+ sim.go ← //gofront:target wasm · particle physics & shared.NewFloat32 buffer
331
+ app.js ← generated JS bundle + boundary facade
332
+ app.wasm ← generated WasmGC module
333
+ index.html ← canvas + controls shell
334
+ ```
335
+
399
336
  ### Build and run
400
337
 
401
338
  ```sh
402
- npm run build:simple # → example/simple/app.js
403
- npm run build:reactive # → example/reactive/app.js
404
- npm run build:gom # → example/gom/app.js
405
- npm run build:templ # → example/templ/app.js
406
- npm run build:webgl # → example/webgl/app.js
339
+ npm run build # compile all example applications
407
340
  # open the respective index.html in a browser
408
341
  ```
409
342
 
410
343
  ---
411
344
 
412
- ## CLI
413
-
414
- ```
415
- gofront dev [dir] watch + compile + asset sync + live reload (default port 3000)
416
- gofront dev [dir] --port 8080 use a custom port
417
- gofront build [dir] clean + compile + minify + vendor → production output
418
- gofront build [dir] --pwa also generate offline service worker (sw.js) + precache manifest
419
- gofront build [dir] --source-map include inline source maps in the release bundle
420
- gofront build [dir] --no-minify skip minification
421
- gofront build [dir] --no-mangle minify but keep original identifiers
422
- gofront prep [dir] [--minify] run asset copying + vendor bundling only (alias: gofront vendor)
423
- gofront check <dir> type-check a single package
424
- gofront check <dir>/... type-check every package under <dir> (Go-style `./...`)
425
- gofront test <dir> [--dom] run tests for a single package
426
- gofront test <dir>/... [--dom] [-v] [-run <regex>] run tests recursively
427
- gofront <file.go> compile single file → stdout
428
- gofront <dir> compile all *.go in directory → stdout
429
- gofront <input> -o out.js write output to file (prints elapsed compile time)
430
- gofront <input> -o out.js --copy-assets compile + copy static assets
431
- gofront <input> --check type-check only (single file / directory)
432
- gofront <input> --watch watch for changes and recompile
433
- gofront <input> -o out.js --serve watch + serve with live reload (legacy; prefer gofront dev)
434
- gofront <input> --source-map append inline source map
435
- gofront <input> --minify minify output (built-in minifier)
436
- gofront <input> --minify --mangle minify and rename local identifiers
437
- gofront <file.go> --ast dump AST (debug)
438
- gofront <file.go> --tokens dump tokens (debug)
439
- gofront init [dir] scaffold a new project
440
- gofront --version / -v print version
441
- gofront --help / -h print this help
442
- ```
443
- ### Project configuration
444
-
445
- `gofront dev`, `build`, `prep`, `check` and `test` read optional settings from a `gofront.json`
446
- file or a `"gofront": { … }` object in `package.json`:
447
-
448
- | Key | Default | Description |
449
- |---|---|---|
450
- | `src` | `app/src`, `src` or `main.go` | Package directory (or single file) to compile |
451
- | `serveDir` | `app`, `.` or `public` (first with `index.html`) | Directory served by `dev` and mirrored into the build |
452
- | `outDir` | `public` | Release output directory for `build` |
453
- | `output` | `app/app.js` | Dev-mode compiled bundle path |
454
- | `port` | `3000` | Dev server port |
455
- | `assetExtensions` | `[]` | Extra file extensions (e.g. `[".bmesh", ".mat"]`) copied from `serveDir` into `outDir` on `build`, in addition to the built-in web asset list (html, css, js, json, images, fonts, audio, video, wasm) |
456
- | `vendor` | `app/vendor.js` | Vendor bundle written by `prep`/`build` from `package.json` `dependencies`. Either a destination path string or `{ "dest": string \| string[], "packages": string[], "minify": boolean, "globals": { "<pkg>": string \| string[] } }` |
457
- | `assetCopy` | `[]` | Static files copied by `prep`/`build`: `[{ "source": "node_modules/x/font.woff2", "dest": "app/fonts/font.woff2" }]` |
458
-
459
- All settings live in this one place — top-level `"vendor"` / `"assetCopy"` keys in `package.json` are not read.
460
- ---
461
-
462
- ## Multi-file packages
345
+ ## The Hybrid Architecture: JS + WasmGC
463
346
 
464
- All `.go` files in a directory share the same namespace and are compiled as one unit.
465
- The compiler (`src/compiler.js`) orchestrates this:
466
-
467
- 1. Parse each `.go` file in the directory into a separate AST.
468
- 2. Resolve imports: `js:` d.ts files, npm packages (via `node_modules/` and `@types/`),
469
- and local GoFront sub-packages (`import "./subpkg"`) which are compiled recursively.
470
- 3. Run the type checker across all ASTs as a single unit — types, functions, and
471
- variables declared in one file are visible in all other files of the same package.
472
- 4. Generate code for each AST and concatenate. Sub-package code is inlined as a preamble.
347
+ GoFront apps can run **partly as JavaScript and partly as WebAssembly (WasmGC)**, chosen per package:
473
348
 
474
349
  ```
475
- myapp/
476
- types.go ← type Point struct { X, Y int }
477
- utils.go ← func distance(a, b Point) float64 { ... }
478
- main.go ← func main() { ... }
350
+ GoFront Source Code
351
+ ┌───────────────────────┬───────────────────────┐
352
+ │ //gofront:target js │ //gofront:target wasm │
353
+ │ UI, DOM, WebGL, Game │ Collision, Raycasting │
354
+ └───────────┬───────────┴───────────┬───────────┘
355
+ │ │
356
+ │ GoFront Compiler │
357
+ ▼ ▼
358
+ ┌───────────┐ ┌───────────┐
359
+ │ app.js │ ◄───────► │ app.wasm │
360
+ └─────┬─────┘ Boundary └─────┬─────┘
361
+ │ Facade │
362
+ ▼ ▼
363
+ DOM & Browser Tight Loops & Math
364
+ Dynamic & Ergonomic Predictable & Fast
479
365
  ```
480
366
 
481
- ```sh
482
- gofront myapp -o myapp/bundle.js
483
- ```
367
+ ### Package Targets
484
368
 
485
- Cross-package imports are supported via relative paths:
369
+ Each package declares its target at the top of any of its source files (before the `package` clause):
486
370
 
487
371
  ```go
488
- import "./mathpkg"
489
-
490
- func main() {
491
- x := math.Add(1, 2) // math package inlined into the bundle
492
- }
372
+ //gofront:target wasm // compiled to WebAssembly only
373
+ //gofront:target both // compiled to both JS and WASM; each side uses its own copy
374
+ package physics // no directive = js (default)
493
375
  ```
494
376
 
495
- ---
377
+ | Target | Compiled to | May import | Best for |
378
+ |---|---|---|---|
379
+ | `js` (default) | JavaScript ES module | anything | DOM manipulation, UI components (`gom`, `.templ`), browser APIs, `async`/`await` |
380
+ | `wasm` | WebAssembly GC module | `wasm`, `both`, stdlib (WASM subset) | Physics, spatial indexing, raycasting, simulation, tight numeric loops |
381
+ | `both` | JS *and* WASM | `both`, stdlib (WASM subset) | Shared math/utility libraries (e.g. `mathx`, `vec3`). Strict Go numeric semantics; no package-level mutable state |
496
382
 
497
- ## Type checking
383
+ - **Zero Boundary Overhead for Shared Types:** `both` packages never create a boundary overhead — JS callers use the JS copy and WASM callers use the WASM copy.
384
+ - **Strict Go Numeric Semantics:** Code compiled to `wasm` (and JS emitted for `both` packages) follows strict Go numeric rules: sized-integer wrapping, true `float32` rounding, and integer divide-by-zero panics.
498
385
 
499
- GoFront performs static type checking before emitting any code, accurately tracking source
500
- locations in error messages across multiple files:
386
+ **Choosing a target for a package** — the questions to ask, in order:
501
387
 
502
- ```go
503
- func greet(name string) {
504
- console.log("Hello, " + name)
505
- }
388
+ 1. **Does it touch the browser** (DOM, WebGPU, `fetch`, PeerJS, `gom`, `.templ`)? → `js`. There is no other option.
389
+ 2. **Is it a leaf that only imports `math`/`mathx`-style helpers and spends its time in numeric loops?** → candidate for `wasm`. Profile first; moving code that is not hot buys nothing.
390
+ 3. **What crosses the boundary per frame?** Scalars, handles and `shared` buffers are free; a slice result (`[]Mat4`, `[]float32`) is copied out on every call. If the package's main output is a slice the renderer reads each frame, it will measure at parity with JS — write that output into a `gofront/shared` buffer instead.
391
+ 4. **Do two `wasm` packages (or JS and WASM) both need the same types?** → make that package `both`, so neither side pays a boundary call for it.
392
+ 5. **Can't decide?** Compile both ways and measure: `gofront build --js-only` gives the all-JS build, or pin per-package targets in `gofront.json` (`"targets": { "engine/physics": "wasm" }`) without editing source. Ship with `--release` so `wasm-opt` shrinks `app.wasm` (~21–23%).
506
393
 
507
- greet(42)
508
- // → Type error in src/main.go at line 5:7: cannot use int as string
509
- // 5 | greet(42)
510
- // ^
511
- ```
394
+ ### Seamless Compile-Time Boundary
512
395
 
513
- External TypeScript type definitions are supported via `js:` imports:
396
+ Every `wasm` and `both` package an application imports is automatically linked into a single `app.wasm` module, emitted right alongside `app.js` by `gofront build`, `gofront dev`, and `-o`.
514
397
 
515
- ```go
516
- import "js:./dom.d.ts"
517
- ```
398
+ The compiler analyzes both sides of the boundary and synthesizes a facade into `app.js`:
399
+ - **Functions & Methods:** WASM functions, methods, and literal constants can be invoked directly from JS code as if they were native JS functions.
400
+ - **Primitives & Strings:** Numbers, booleans, strings, and `any` pass through or convert directly across the boundary.
401
+ - **Structs & Memory:**
402
+ - `both` struct values cross by copy into their matching JS classes (with write-back for `*T` parameters).
403
+ - `wasm` structs become opaque handle classes with stable identity. Reading aggregate fields through handles (`b.Pos.X`, `b.Tags[0]`) provides live views into WASM memory.
404
+ - **Slices & Arrays:** Slices and arrays copy element-wise in both directions; TypedArrays (`Float32Array`, `Int32Array`, etc.) are natively accepted.
405
+ - **Closures & Interfaces:** Function values, callbacks, and named non-empty interfaces cross the boundary transparently.
406
+ - **Shared Linear-Memory Buffers:** `gofront/shared` (`shared.NewFloat32`, etc.) exposes zero-copy TypedArray views over linear memory for bulk per-frame data.
407
+ - **WASM Loader:** The generated JS bundle automatically fetches `app.wasm` relative to `app.js` (`import.meta.url`, customizable via `globalThis.__GOFRONT_WASM_URL` or `globalThis.__GOFRONT_WASM_BYTES`).
408
+ - **WAT Inspection:** Passing `--emit-wat` writes human-readable `app.wat` alongside the binary module.
409
+ - **Binaryen Optimization:** Passing `--release` or `--wasm-opt` optimizes `app.wasm` via Binaryen (`-O3` + GUFA), achieving ~23% smaller binaries and generating `app.wasm.map` source maps.
518
410
 
519
- npm package types are resolved automatically from `node_modules/` and `@types/`.
520
- The resolver (`src/resolver.js`) walks up the directory tree to find `node_modules`,
521
- checks `package.json` `"types"` / `"typings"` fields, falls back to `index.d.ts`,
522
- then tries `@types/`. The `.d.ts` parser (`src/dts-parser.js`) extracts type
523
- signatures into GoFront's internal type representation.
411
+ ### Real-World Benchmark: 3D Raycasting
524
412
 
525
- ---
413
+ Splitting an app into high-level JavaScript orchestration and low-level WebAssembly compute delivers the best of both worlds. On a real-world Möller–Trumbore raycast benchmark (131,072 triangles, 100,000 rays; run via `npm run bench`):
526
414
 
527
- ## Language features
415
+ | Target | Throughput | Allocation | Engine Stability |
416
+ | :--- | :--- | :--- | :--- |
417
+ | **Pure JS** | `26,720 rays/s` ▰▰▰▰▰▰▰▰▱▱ | `6.20 B / ray` | Subject to periodic V8 young-gen GC pauses |
418
+ | **Hybrid WASM** | `31,293 rays/s` ▰▰▰▰▰▰▰▰▰▰ | `0.86 B / ray` | **Near-zero alloc (7.2× less)**, smooth 60 FPS |
528
419
 
529
- ### Core language
420
+ - **+17.1% higher throughput:** Direct WasmGC typed arrays, local-cached scratch globals, and hardware-trapped nil dereferences outperform JIT-compiled JS.
421
+ - **86% memory churn reduction:** Dropping allocations from 6.2 B/ray to 0.86 B/ray prevents garbage collection pauses from causing micro-stutter in 60 FPS loops.
422
+ - **Minimal boundary overhead:** The boundary trampoline consumes only ~0.5% of total runtime, ensuring batch computations cross between JS and WASM with virtually zero penalty.
530
423
 
531
- | Feature | Status |
532
- |---|---|
533
- | Variables (`var`, `:=` short re-declaration) | ✓ |
534
- | Constants (`const`, `iota`, untyped constants) | ✓ |
535
- | Functions, multiple returns, named returns, variadic | ✓ |
536
- | `init()` functions | ✓ |
537
- | Closures / function literals | ✓ |
538
- | `async func` / `await` expressions | ✓ |
539
- | `defer`, `panic()` / `recover()` | ✓ |
540
- | `print` / `println` builtins | ✓ — compile to `console.log` |
541
-
542
- ### Types & data structures
543
-
544
- | Feature | Status |
545
- |---|---|
546
- | Structs + methods (value & pointer receivers) | ✓ |
547
- | Embedded structs (flattened fields + promoted methods) | ✓ |
548
- | Anonymous struct types | ✓ — compile to plain JS objects |
549
- | Interfaces (with embedding) | ✓ |
550
- | Slices (`append`, `len`, `make`) | ✓ |
551
- | Maps (`make`, `delete`, comma-ok) | ✓ |
552
- | Arrays with compile-time enforcement | ✓ — reject `append`, bounds checking, size matching, `[...]T` inference, compile-time `len()` |
553
- | Slice → array conversion (`[N]T(slice)`) | ✓ — Go 1.20 |
554
- | Pointers (`&x`, `*p`, `new(T)`) | ✓ — scalar locals boxed as `{ value: T }` for shared mutation |
555
- | `error` type | ✓ — interface `{ Error() string }`; custom error types, `errors.Is`/`Unwrap`, `%w` wrapping |
556
- | Complex numbers (`complex64`, `complex128`, `3i`) | ✓ — `complex()`, `real()`, `imag()` builtins; `__cmul`/`__cdiv` helpers |
557
- | Type definitions, type aliases (`type A = B`) | ✓ |
558
- | Methods on named non-struct types | ✓ — `type T func(...)` or `type T []E` with methods; emitted as ES6 wrapper classes; satisfies interfaces |
559
- | Type conversions, type assertions (plain & comma-ok) | ✓ |
560
- | Type switch (`switch v := x.(type)`) | ✓ — compiles to `if/else if` with `typeof` / `instanceof` |
561
- | Sized integers (`int8`–`int64`, `uint8`–`uint64`, `float32`) | ✓ — mapped to `number` at runtime |
562
- | Generics (`func F[T any]`, `type S[T any] struct`) | ✓ — type erasure to JS; generic functions, structs, constraints (`any`, `comparable`, interfaces, unions), type inference |
563
- | Struct field tags | ✓ — parsed and ignored (no reflection) |
564
- | Struct and array equality (`a == b`) | ✓ — deep comparison via `__equal` helper |
565
-
566
- ### Control flow
567
-
568
- | Feature | Status |
569
- |---|---|
570
- | `if` / `else if` / `else` (with init statement) | ✓ |
571
- | `for` (C-style, condition-only, infinite, `range`) | ✓ |
572
- | `range` over slice, map, string, integer | ✓ |
573
- | Range over iterator functions (Go 1.23) | ✓ — `func(yield func(K, V) bool)` protocol; break/continue/return propagation |
574
- | `switch` / `fallthrough` (with init statement) | ✓ |
575
- | `break` / `continue` / labeled variants | ✓ |
576
- | Terminating statement analysis | ✓ — missing `return` in non-void functions is a type error |
424
+ **Full application: [simplefps](https://github.com/seriva/simplefps).** The game ships `mathx` as `both`, `collision`, `physics` and `animation` as `wasm`, and everything else (WebGPU renderer, scene, UI) as `js`. Its `npm run bench` compiles `physics` and `animation` once with `--js-only` and once hybrid and runs the same workload against both (Node 25):
577
425
 
578
- ### Expressions & literals
426
+ | Workload | Pure JS | Hybrid WASM | Ratio |
427
+ | :--- | :--- | :--- | :--- |
428
+ | Closest-hit raycasts, 2048-triangle octree (200k casts) | ~240k casts/s | ~263k casts/s | **1.11–1.15×** |
429
+ | FPS-controller fixed step across the boundary (pose in → `Update` + `MoveWithCamera` + `SyncCamera` → pose out, 50k frames) | ~46k frames/s | ~108k frames/s | **2.4×**, 0.2 B/frame |
430
+ | 64-joint skinned-character frame (`AnimationPlayer.Update` + `ComputeSkinningMatrices` + palette copy-out via `gofront/shared`) | ~141k frames/s | ~179k frames/s | **1.27×** |
431
+ | `app.wasm` (`mathx` + `collision` + `physics` + `animation`) | — | 98.5 kB unoptimised → **79.7 kB** with `--release` | −19% |
579
432
 
580
- | Feature | Status |
581
- |---|---|
582
- | Arithmetic, comparison, logical, bitwise operators | ✓ — includes `&^` (bit clear) |
583
- | Compound assignment, increment / decrement | ✓ |
584
- | Slice expressions (`s[lo:hi]`, `s[lo:hi:max]`) | ✓ |
585
- | Variadic spread (`f(slice...)`, `append(a, b...)`) | ✓ |
586
- | Positional struct literals (`Point{1, 2}`) | ✓ |
587
- | Method expressions (`T.Method`) / method values (`x.Method`) | ✓ |
588
- | Multi-value function forwarding (`f(g())`) | ✓ |
589
- | Raw string literals (backticks), rune literals | ✓ |
590
- | Numeric separators (`1_000_000`), binary/octal/hex literals | ✓ |
591
- | `[]byte(s)` / `[]rune(s)` conversions | ✓ |
592
-
593
- ### Standard library shims
594
-
595
- | Package | Functions |
596
- |---|---|
597
- | `fmt` | `Sprintf`, `Printf`, `Println`, `Print`, `Errorf`, `Fprintf`, `Fprintln`, `Fprint` — format verbs: `%v`, `%d`, `%s`, `%t`, `%x`, `%o`, `%b`, `%q`, `%e`, `%g`, `%w`, width/precision; **scanning**: `Sscan`, `Sscanln`, `Sscanf` |
598
- | `strings` | `Contains`, `HasPrefix`, `HasSuffix`, `Index`, `LastIndex`, `Count`, `Repeat`, `Replace`, `ReplaceAll`, `ToUpper`, `ToLower`, `TrimSpace`, `Trim`, `TrimPrefix`, `TrimSuffix`, `TrimLeft`, `TrimRight`, `Split`, `Join`, `EqualFold`, `Fields`, `Cut`, `CutPrefix`, `CutSuffix`, `SplitN`, `SplitAfter`, `SplitAfterN`, `IndexAny`, `LastIndexAny`, `ContainsAny`, `ContainsRune`, `IndexRune`, `IndexByte`, `LastIndexByte`, `Map`, `Title`, `ToTitle`, `TrimFunc`, `TrimLeftFunc`, `TrimRightFunc`, `IndexFunc`, `LastIndexFunc`, `NewReplacer`; **`Builder`** type; **`NewReader`** → reader shim |
599
- | `bytes` | `Contains`, `HasPrefix`, `HasSuffix`, `Index`, `Join`, `Split`, `Replace`, `ToUpper`, `ToLower`, `TrimSpace`, `Equal`, `Count`, `Repeat`, `ReplaceAll`, `TrimPrefix`, `TrimSuffix`, `TrimLeft`, `TrimRight`, `TrimFunc`, `IndexByte`, `LastIndex`, `LastIndexByte`, `Fields`, `Cut`, `ContainsAny`, `ContainsRune`, `Map`, `SplitN`; **`Buffer`** type; **`NewReader`** → reader shim |
600
- | `strconv` | `Itoa`, `Atoi`, `FormatBool`, `FormatInt`, `FormatFloat`, `ParseFloat`, `ParseInt`, `ParseBool`, `Quote`, `Unquote`, `AppendInt`, `AppendFloat` |
601
- | `sort` | `Ints`, `Float64s`, `Strings`, `Slice`, `SliceStable`, `SliceIsSorted`, `Search`, `IntsAreSorted`, `Float64sAreSorted`, `StringsAreSorted` |
602
- | `math` | `Abs`, `Floor`, `Ceil`, `Round`, `Sqrt`, `Cbrt`, `Pow`, `Log`, `Log2`, `Log10`, `Sin`, `Cos`, `Tan`, `Atan`, `Atan2`, `Asin`, `Acos`, `Exp`, `Exp2`, `Trunc`, `Hypot`, `Signbit`, `Copysign`, `Dim`, `Remainder`, `Min`, `Max`, `Mod`, `Inf`, `IsNaN`, `IsInf`, `NaN` + `Pi`, `E`, `MaxFloat64`, `SmallestNonzeroFloat64`, `MaxInt`, `MinInt` |
603
- | `math/rand` | `Intn`, `Float64`, `Float32`, `Int`, `Int63`, `Int63n`, `Int31`, `Int31n`, `Seed` (no-op), `Shuffle`, `Perm` |
604
- | `errors` | `New`, `Is`, `Unwrap` — custom error types via interface satisfaction |
605
- | `time` | `Now` → `time.Time`, `Since`, `Sleep`, `Parse`, `Unix`, `Date`; **`time.Time`** methods: `Format`, `String`, `Year`, `Month`, `Day`, `Hour`, `Minute`, `Second`, `Weekday`, `Unix`, `UnixMilli`, `Add`, `Sub`, `Before`, `After`, `Equal`; layout constants: `RFC3339`, `RFC3339Nano`, `DateOnly`, `TimeOnly`, `DateTime`; duration constants: `Millisecond`, `Second`, `Minute`, `Hour`; month/weekday constants |
606
- | `html` | `EscapeString`, `UnescapeString` |
607
- | `maps` | `Keys`, `Values`, `Clone`, `Copy`, `Equal`, `EqualFunc`, `Delete`, `DeleteFunc` |
608
- | `slices` | `Contains`, `Index`, `Equal`, `Compare`, `Sort`, `SortFunc`, `SortStableFunc`, `IsSorted`, `IsSortedFunc`, `Reverse`, `Max`, `Min`, `MaxFunc`, `MinFunc`, `Clone`, `Compact`, `CompactFunc`, `Concat`, `Delete`, `DeleteFunc`, `Insert`, `Replace`, `Grow`, `Clip` |
609
- | `regexp` | `MustCompile`, `Compile`, `MatchString`, `QuoteMeta`; **`*Regexp`** methods: `MatchString`, `FindString`, `FindStringIndex`, `FindAllString`, `FindStringSubmatch`, `FindAllStringSubmatch`, `ReplaceAllString`, `ReplaceAllLiteralString`, `Split`, `String`. Inline flags (`(?i)`, `(?m)`, `(?s)`) are extracted automatically into the JS `RegExp` constructor. |
610
- | `unicode` | `IsLetter`, `IsDigit`, `IsSpace`, `IsUpper`, `IsLower`, `IsPunct`, `IsControl`, `IsPrint`, `IsGraphic`, `ToUpper`, `ToLower` |
611
- | `unicode/utf8` | `RuneCountInString`, `RuneLen`, `ValidString`, `ValidRune`, `DecodeRuneInString`, `DecodeLastRuneInString`, `FullRuneInString`; constants `RuneError`, `MaxRune`, `UTFMax` |
612
- | `path` | `Base`, `Dir`, `Ext`, `Join`, `Clean`, `IsAbs`, `Split`, `Match`; `"path/filepath"` is an alias |
613
- | `os` | `Exit`, `Args`, `Getenv` |
614
- | `io` | `Writer`, `Reader`, `ReadWriter`, `Closer` interface types; `ReadAll`, `EOF`, `Discard`, `WriteString` |
615
- | `gom` | Browser-native declarative DOM component library. **Types**: `Node` (interface), `NodeFunc`, `Group`. **Core**: `El(tag, children...)`, `Text(s)`, `Mount(sel, node)`, `MountTo(sel, node)`. **Attributes**: `Attr`, `Class`, `Href`, `Type`, `Src`, `Placeholder`, `DataAttr`, `Style`, `For`, `Name`, `Value`, `Target`, `Rel`, `Alt`, `Title`, `Draggable`, `Role`, `AriaLabel`, `StyleAttr`; boolean: `Disabled`, `Checked`, `Selected`, `Readonly`. **Logic**: `If(cond, node)`, `Map(slice, fn)`. **Elements**: full HTML element set (`Div`, `Span`, `Button`, `Input`, `Ul`, `Li`, `Table`, `Form`, `Img`, `A`, `H1`–`H6`, …) |
616
- | `testing` | **`testing.T`**: `Error`, `Errorf`, `Fatal`, `Fatalf`, `Fail`, `Failed`, `FailNow`, `Log`, `Logf`, `Skip`, `Skipf`, `Skipped`, `Helper`, `Run`, `Name`. **Package functions**: `Short()`, `Verbose()`. Tests are `func TestXxx(t *testing.T)` in `*_test.go` files, excluded from normal compilation and run via `gofront test`. |
617
-
618
- ### Packages & imports
619
-
620
- | Feature | Status |
621
- |---|---|
622
- | Multi-file packages | ✓ |
623
- | Cross-package imports | ✓ |
624
- | Import aliases (`import m "./pkg"`) | ✓ |
625
- | Side-effect imports (`import _ "pkg"`) | ✓ |
626
- | Dot imports (`import . "pkg"`) | ✓ |
627
- | Unused import detection | ✓ |
628
- | External `.d.ts` types | ✓ |
629
- | npm package type resolution | ✓ |
630
- | `.templ` files in packages | ✓ — mix `.go` and `.templ` files freely; templ components and `css` declarations visible across the whole package |
631
-
632
- ---
633
-
634
- ## Go Compatibility
433
+ The controller step is where WASM wins most: each fixed step issues several raycasts, and keeping `physics` next to `collision` means those never cross the boundary — only the camera pose does. `animation` is the cautionary tale for step 3: compiled as `wasm` while `ComputeSkinningMatrices` still returned a `[]Mat4`, it measured at parity with JS (the slice was copied out every frame); writing the palette into a `gofront/shared` buffer instead turned that into a 1.27× win.
635
434
 
636
- GoFront implements a practical subset of the
637
- [Go Language Specification](https://go.dev/ref/spec) (go1.26). It is not aiming for
638
- byte-level parity — it is a Go-inspired language for the JavaScript platform.
435
+ For the full rules — `both`-package restrictions, strict numeric mode, which types can cross the boundary, loader overrides, and how `gofront test` runs hybrid packages — see the **[Hybrid JS + WebAssembly Guide](docs/hybrid-wasm.md)**.
639
436
 
640
- ### GoFront extensions (not in Go)
641
-
642
- These features are intentional additions for the JavaScript platform:
437
+ ---
643
438
 
644
- | Feature | Purpose |
645
- |---|---|
646
- | `async func` / `await` | First-class async syntax for frontend work. |
647
- | Browser globals (`document`, `console`, etc.) | Predeclared as `any` for practical DOM access. `WebGL2RenderingContext`, `WebGLRenderingContext`, `GPUDevice`, `GPUAdapter`, `GPUQueue`, `ArrayBuffer`, `DataView`, and TypedArrays have full static typings with method-level checking. |
648
- | `.d.ts` type imports (`import "js:./types.d.ts"`) | Type-safe interop with JavaScript libraries. |
649
- | npm package resolution | Import types from `node_modules/` and `@types/` automatically. |
439
+ ## Usage & Tooling
650
440
 
651
- ### What's not implemented
441
+ GoFront includes a fully-featured CLI for building, dev-serving (with live reload), and type-checking your projects.
652
442
 
653
- | Feature | Reason | Prospect |
654
- |---|---|---|
655
- | `goto` | No clean JS translation. Rare in idiomatic Go. | Not planned. |
656
- | Goroutines / channels / `select` | Go's concurrency model has no JS equivalent. A userland scheduler defeats the "no runtime" goal. | Out of scope. |
657
- | `unsafe`, `reflect`, `cgo` | Require memory model or runtime type metadata that JS cannot provide. | Out of scope. |
443
+ For detailed command flags, project configuration (`gofront.json`), multi-file package resolution, and type-checking rules, see the **[Usage & Tooling Guide](docs/usage.md)**.
658
444
 
659
- ### Semantic differences
445
+ ---
660
446
 
661
- These features are implemented but behave differently due to fundamental JS runtime
662
- constraints. These are **not bugs** — they are deliberate trade-offs documented here so
663
- you know exactly what to expect.
447
+ ## Documentation
664
448
 
665
- | Feature | GoFront | Go | Why |
666
- |---|---|---|---|
667
- | Map iteration order | Insertion-order (`Object.entries`) | Randomised | JS objects preserve insertion order. |
668
- | Integer overflow | IEEE 754 float64 semantics | Wraps at type boundary (e.g. `int32`) | All JS numbers are float64. |
669
- | Integer precision | Safe up to 2⁵³ | Full width per type (`int64` = 64 bits) | JS `number` limitation. |
670
- | `cap()` | Always equals `len()` | May exceed `len()` | JS arrays have no separate capacity. |
671
- | Fixed-size arrays (`[n]T`) | Compile-time enforcement (bounds, append rejected, size matching); plain JS arrays at runtime | Fixed at compile time, value-type copy semantics | Runtime enforcement adds overhead; compile-time checks catch most errors. |
672
- | `nil` | Maps to `null` | Typed nil (distinct per type) | JS has no typed nil concept. |
673
- | `rune` / `byte` | Treated as `int` | Distinct types with UTF-8 encoding | JS strings are UTF-16. |
674
- | `range` over string | Rune integers via `.codePointAt()` | Runes (UTF-8 code points) | Close match — indices are byte-sequential, values are code points. |
675
- | `len()` on strings | JS `.length` (UTF-16 code units) | Byte count (UTF-8) | Matching Go would require `TextEncoder` on every call. |
676
- | `error` type | Interface `{ Error() string }` with `__error` runtime objects | Interface `{ Error() string }` | Close match. Custom error types, `errors.Is`/`Unwrap`, `%w` wrapping all work. `toString()` added for JS string context compat. |
677
- | `defer` | `try`/`finally` with a defer stack | Runtime stack unwinding | Covers most cases but not identical to Go internals. |
678
- | Struct field tags | Parsed, silently discarded | Available via `reflect` | No reflection = no use for tag values. |
679
- | Pointers (`&x`, `*p`) | Address-taken scalars boxed as `{ value: T }`; structs/slices/maps are reference types (no boxing) | True memory indirection | Shared mutation works for scalars; struct fields and slice elements not yet addressable. |
680
- | Three-index slice (`a[lo:hi:max]`) | `max` is parsed but ignored | Sets result capacity | JS arrays have no capacity. |
681
- | Exported / unexported | Enforced for GoFront packages; external `.d.ts`/npm namespaces are exempt | Access enforced uniformly | External JS APIs use lowercase names by convention. |
449
+ * [Usage & Tooling Guide](docs/usage.md) — CLI, project config, testing, multi-file packages
450
+ * [Hybrid JS + WebAssembly Guide](docs/hybrid-wasm.md) — package targets, `both` rules, boundary types, loader
451
+ * [Language Features & Stdlib](docs/language-support.md) — what is implemented, stdlib shims
452
+ * [Go to JS Mapping](docs/compilation-mapping.md) — what each construct compiles to
453
+ * [Go Compatibility Differences](docs/go-compatibility.md) — where GoFront deliberately differs from Go
454
+ * [Compiler Architecture & Internals](docs/architecture.md) — pipeline stages and source layout
682
455
 
683
456
  ---
684
457
 
@@ -686,33 +459,31 @@ you know exactly what to expect.
686
459
 
687
460
  See [`docs/roadmap.md`](docs/roadmap.md) for the full roadmap and release history.
688
461
  Design documents for planned features are organised by release under `docs/v*/`
689
- (e.g. [`docs/v0.0.8/`](docs/v0.0.8/), [`docs/v1.2.0/`](docs/v1.2.0/), [`docs/v1.3.0/`](docs/v1.3.0/)).
462
+ (e.g. [`docs/v0.0.8/`](docs/v0.0.8/), [`docs/v1.2.0/`](docs/v1.2.0/), [`docs/v1.3.0/`](docs/v1.3.0/), [`docs/v1.6.0/`](docs/v1.6.0/)).
690
463
 
691
464
  ---
692
465
 
693
466
  ## Tests
694
467
 
695
468
  ```sh
696
- npm run test:unit # unit tests only (~1400 tests, no browser required)
697
- npm run test:perf # zero-allocation benchmark (100k ray-triangle intersections, 0 bytes/frame)
698
- npm run test:examples # GoFront-native unit tests across all example apps
699
- npm run test:examples:dom # same, with JSDOM for DOM/gom/templ component testing
700
- npm run test:e2e # E2E tests (Playwright, headless Chromium)
701
- npm run test:all # all of the above
469
+ npm test # unit, zero-alloc perf, and example tests (~1,800+ tests, no browser required)
470
+ npm run test:e2e # E2E browser tests (Playwright, headless Chromium)
471
+ npm run bench # Möller–Trumbore raycast benchmark comparing pure JS vs hybrid WASM
702
472
  ```
703
473
 
704
- **Unit tests** (~1,400+) cover language features, type errors, edge cases, DOM (jsdom),
474
+ **Unit tests** (~1,800+) cover language features, type errors, edge cases, DOM (jsdom),
705
475
  external `.d.ts`, npm resolver, multi-file compilation, embedded structs, string
706
476
  formatting, map iteration order, integer overflow semantics, unused variable detection,
707
477
  unused import detection, semantic difference verification, stdlib shim packages, generics,
708
- the `testing` framework itself, and `.templ` file compilation (element rendering,
478
+ the `testing` framework itself, the WasmGC backend and JS↔WASM boundary, `gofront/shared`
479
+ linear-memory buffers, Binaryen optimization, and `.templ` file compilation (element rendering,
709
480
  interpolation, boolean attrs, component calls, scoped `css` declarations with class hashing,
710
481
  `if/else/else-if` chains, `for range`, `switch/case/default`, `@templ.Raw()` raw HTML injection,
711
482
  SVG namespace handling, mixed `.go`+`.templ` packages).
712
483
 
713
- **E2E tests** (~105, Playwright) run all five example apps in a real browser and verify
484
+ **E2E tests** (~110, Playwright) run all six example apps in a real browser and verify
714
485
  CRUD, filtering, priority mode, persistence (reload), drag-and-drop reordering, and sync
715
486
  status. Per-app suites check app-specific behaviour: scoped styles, stats bar, loading
716
487
  placeholder, `gom.If` conditional rendering, templ-specific features (scoped `css` injection,
717
- `if/else` priority hint, `for` loop rendering, conditional bool attributes), and the WebGL2 cube
718
- rendering.
488
+ `if/else` priority hint, `for` loop rendering, conditional bool attributes), the WebGL2 cube
489
+ rendering, and the hybrid JS + WasmGC particle simulation.