docs(rag): ecosystem §7 — pure-Go Node bridge (nodertl), no C

Rewrite §7 around the C→Go goal: reach external code without C/CGO. Path
(a) Go-package RTL (first choice); path (b) npm via pure-Go nodertl
(NODE_EVAL → node subprocess), with the real article_qr.prg example. Mark
the C N-API Require()/koffi path as legacy (needs C Harbour — avoid).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
CharlesKWON
2026-06-15 17:30:05 +09:00
parent 57d6350fa8
commit 4ce927a8de

View File

@@ -207,36 +207,40 @@ Deploy (launchd): `launchctl kickstart -k gui/$(id -u)/kr.solmade.web` (and
`...worker1/2/3`). The worker build (`build_worker.sh`) links `cmd_prg/job_worker.prg`
plus the shared `app/lib/*.prg` (so `LLM_CHAT` and prompts are available to it too).
## 7. Reaching the ecosystem — two paths (pick by runtime)
## 7. Reaching the ecosystem (no C — Five is the Go reimplementation)
There are two distinct ways to use the outside world from PRG; they belong to
different runtimes (see [[five-overview]]):
Five replaced Harbour's C with Go, so reach external code **without C/CGO/Harbour
native**. Two production-clean paths (see [[five-overview]]):
**(a) Go RTL — `fivenode_go` (single Go binary, NO Node).** Wrap a Go package in a
thin `hbrtl_ext` module, blank-import it with `--rtl`, call the registered function.
This is the production path (solmade): no runtime deps, one binary. Use for anything
with a good Go library (Postgres, PDF, XLSX, crypto/rand, argon2, bluemonday).
**(a) Go package via RTL — first choice.** Wrap a Go library in a thin `hbrtl_ext`
module, blank-import with `--rtl`, call the registered function. Single static binary,
no runtime deps. Use whenever a good Go library exists (Postgres, PDF, XLSX,
crypto/rand, argon2, bluemonday).
**(b) Node `Require()` — original `fivenode` (Node.js + N-API/koffi).** PRG calls an
**npm package directly**, Node-style, with no wrapper to write:
**(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`:
```five
oQR := Require( "qrcode" ) // npm package
cSvg := aWait( oQR:toString( cUrl, { "type" => "svg" } ) ) // async → aWait()
oQR:__end__() // release the handle
oId := Require( "crypto" ):randomUUID() // Node builtin, sync
oOs := Require( "os" ) // builtins: os, path, fs, ...
cHost := oOs:hostname() ; oOs:__end__()
// 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", "" )
ENDIF
```
- Sync npm: `obj:methodSync(...)` (e.g. qr-image `imageSync`). Async (Promise):
wrap the call in `aWait(...)`. Always `obj:__end__()` when done.
- Pass PRG hashes as JS objects: `{ "width" => 240, "color" => { "dark" => "#000" } }`.
- The server allowlists each handler — register new ones in `napi/test/server.js`
(`allowedPrg.add('/api/foo.prg')`).
- 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.
**When to choose (b):** the npm package has no good Go equivalent, or reimplementing it
would be heavy (QR codes, charting, specialized parsers, ML/JS-only SDKs). You get the
entire npm registry with zero glue code — the cost is a Node runtime instead of a single
static binary. Example: `napi/test/api/sharekit.prg` composes npm `qrcode` + Node
`crypto`/`os` in one PRG to build an article share kit (QR svg+png + tracking UUID).
**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).
**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.