docs(rag): §7 npm path = handle-based nodebridge (FN_REQUIRE), pure Go
Replace the NODE_EVAL example with the faithful C++-fivenode Require() port: FN_REQUIRE/FN_CALL/FN_END over a persistent node sidecar, pure-Go glue, no C. Live demo at solmade.kr/qrcode. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -217,30 +217,34 @@ module, blank-import with `--rtl`, call the registered function. Single static b
|
||||
no runtime deps. Use whenever a good Go library exists (Postgres, PDF, XLSX,
|
||||
crypto/rand, argon2, bluemonday).
|
||||
|
||||
**(b) npm package via the pure-Go Node bridge (`nodertl`).** When the library you want
|
||||
is npm-only (QR, charting, JS SDKs), call Node from Go as a **subprocess** — no CGO,
|
||||
no C addon. RTL lives in `fivenode_go/hbrtl_ext/nodertl`:
|
||||
**(b) npm package via the handle-based Node bridge (`nodebridge`).** The original
|
||||
C++ fivenode was `Harbour ⇄ C glue ⇄ Node(npm)` — JS always ran in real Node, the C was
|
||||
only a JSON glue. The faithful Go port replaces the *glue* (not Node) with Go:
|
||||
`Five(Go) ⇄ nodebridge(Go) ⇄ persistent node ⇄ npm`. Same handle protocol as the C++
|
||||
version (`require` → handle, `call` → method with auto-await, `end`), **no C/CGO**, and
|
||||
**not** `node -e` string eval. RTL lives in `fivenode_go/hbrtl_ext/nodebridge`:
|
||||
|
||||
```five
|
||||
// NODE_EVAL(cJs [, cWorkdir [, cInput]]) -> {"ok","out","err"} JSON
|
||||
// cWorkdir = dir holding node_modules; cInput → env FIVE_NODE_INPUT (no code injection)
|
||||
LOCAL cJs := "const qr=require('qrcode');" + ;
|
||||
"qr.toString(process.env.FIVE_NODE_INPUT,{type:'svg'})" + ;
|
||||
".then(s=>process.stdout.write(s)).catch(e=>{process.stderr.write(String(e));process.exit(1);});"
|
||||
LOCAL hRes := hb_jsonDecode( NODE_EVAL( cJs, hb_GetEnv("SOLMADE_NODE_DIR","./node"), cUrl ) )
|
||||
IF hb_HGetDef( hRes, "ok", .f. )
|
||||
cSvg := hb_HGetDef( hRes, "out", "" )
|
||||
// FN_REQUIRE(cModule) -> nHandle ; FN_CALL(nH,cMethod[,cArgsJson]) -> {ok,type,value,err}
|
||||
LOCAL nH := FN_REQUIRE( "qrcode" ) // npm module handle (persistent)
|
||||
LOCAL cArgs := hb_jsonEncode( { cUrl, { "type" => "svg", "width" => 280 } } ) // JSON arg array
|
||||
LOCAL hR := hb_jsonDecode( FN_CALL( nH, "toString", cArgs ) ) // async method auto-awaited
|
||||
FN_END( nH )
|
||||
IF hb_HGetDef( hR, "ok", .f. )
|
||||
cSvg := hb_HGetDef( hR, "value", "" ) // type: string|number|boolean|json|buffer(b64)
|
||||
ENDIF
|
||||
```
|
||||
- Setup: a `node/` dir with `package.json` + `npm install <pkg>`; point cWorkdir there.
|
||||
- The JS prints its result to `process.stdout`; pass user data via `cInput`
|
||||
(env `FIVE_NODE_INPUT`), never by string-concatenating into the JS.
|
||||
- The Go binary stays pure Go and only spawns `node` when this RTL is called.
|
||||
- Real example: solmade `app/api/article_qr.prg` → npm `qrcode` → SVG, verified.
|
||||
- Setup: a `node/` dir with `package.json` + `npm install <pkg>`; the bridge points
|
||||
`NODE_PATH` there (default dir via env `SOLMADE_NODE_DIR`).
|
||||
- One persistent `node` per process; calls are serialized; the Go binary stays pure Go
|
||||
and only spawns node when `FN_*` is used. `FN_LASTERROR()` for diagnostics.
|
||||
- Args are a JSON array (`hb_jsonEncode`); user data never concatenated into JS code.
|
||||
- Real example + live demo: solmade `app/api/qr.prg` → npm `qrcode` → SVG, served at
|
||||
`solmade.kr/qrcode`.
|
||||
|
||||
**Legacy (do NOT build new on it):** the original `fivenode` (Node + N-API/koffi calling
|
||||
**C Harbour**) exposed `Require("pkg")`/`aWait()` directly in PRG. That path needs a C
|
||||
Harbour native lib (`libfivenode`) — against the C→Go goal — so prefer (b).
|
||||
**Legacy (do NOT build new on it):** the original `fivenode` used a **C** N-API/koffi
|
||||
addon (`libfivenode`) for the same `Require()`/`aWait()` protocol — against the C→Go
|
||||
goal. `nodebridge` reproduces that protocol with pure-Go glue instead.
|
||||
|
||||
**Choose (b) over (a)** only when the npm package has no good Go equivalent or would be
|
||||
heavy to reimplement; the cost is a Node runtime dependency for that feature.
|
||||
|
||||
Reference in New Issue
Block a user