FiveAI Fusion 語言
SpaceFive · Five 官方展示

一種語言。
一個執行檔。
整個 Web 就在這裡。

SpaceFive 是一個以純 Five 打造、開箱即用的 Web 框架。HTTP、WebSocket、PostgreSQL、bcrypt、AES、多語系,全部由執行環境直接搞定。不需要 Node,不需要 FFI,也不需要 ORM 伺服器。單一靜態執行檔直接連上 Postgres,上面跑著三個真實的應用程式——此刻正在運作。

—
在冊資產
—
資產總值
—
在職員工
1
執行檔
01 — 線上展示

三個應用程式, 一套框架,
同一種質感。

你點到的每一處,背後都是一個 Five 處理函式:局部更新的超媒體、伺服器端產生的表單、CSV 匯出、照片上傳、即時事件。

02 — 寫過 xBase 或 Harbour?

這門語言,你早就會了。
變的只是螢幕。

如果你寫過 Clipper、FoxPro 或 Harbour,SpaceFive 的頁面讀起來就像你寫過幾百次的對話框。下面的範例全是真的:右邊的元件,就是左邊這段程式碼在這次請求裡當場畫出來的。

選單 — MENU … ENDMENU
MENU hMenu MENUBAR
   MENUITEM "Assets"      ACTION "/assets"
   MENUITEM "People"      ACTION "/people"
   MENUITEM "+ New asset" ACTION "/assets/new" GOLD
ENDMENU
cHtml := MENU_HTML( hMenu )
本次請求即時產生

和 FiveWin 一模一樣的寫法。MENU_HTML() 負責產生標記,長什麼樣由皮膚決定。

數據卡片 — DASHBOARD … STAT
hStat := DB_ONE( cAssetStats, {} )

DASHBOARD hDash
   STAT "Total assets"   VALUE hStat[ "n" ] GOLD
   STAT "Total value"    VALUE Money( hStat[ "total" ], "USD" ) GOLD
   STAT "Active"         VALUE hStat[ "active" ] OK
   STAT "In maintenance" VALUE hStat[ "maint" ] WARN
ENDDASHBOARD
cHtml := DASH_HTML( hDash )
本次請求即時產生

一條查詢,四行 STAT。GOLD、OK、WARN 是顏色,數字是剛從 PostgreSQL 讀出來的。

Total assets
14
Total value
$66,745
Active
12
In maintenance
1
瀏覽表 — TABLE … COLUMN
aRows := DB_QUERY( cFirstEmployees, {} )

TABLE hList OF aRows LINK "/people/:id"
   COLUMN "name"       HEADER "Employee" THUMB "photo" SUB "emp_no"
   COLUMN "position"   HEADER "Position"
   COLUMN "status"     HEADER "Status" BADGE
   COLUMN "salary"     HEADER "Salary" MONEY CURRENCY "currency" RIGHT
ENDTABLE
cHtml := TABLE_HTML( hList )
本次請求即時產生

Web 版的 TBrowse:每個欄位一行 COLUMN,用 THUMB、BADGE、MONEY、RIGHT 決定怎麼顯示,加上 LINK 每一列都能點。

EmployeePositionStatusSalary
照片卡片 — GALLERY … CARD
GALLERY hCards OF aRows LINK "/people/:id"
   CARD PHOTO "photo"  INITIALS "name"
   CARD BADGE "status"
   CARD TAG   "position"
   CARD TITLE "name"
   CARD META  "department"
   CARD VALUE "salary" CURRENCY "currency"
ENDGALLERY
cHtml := GALLERY_HTML( hCards )
本次請求即時產生

CARD 把欄位放到卡片上的位置:照片、徽章、標籤、標題、附註、數值。卡片本身只是皮膚資料夾裡的一個 HTML 檔。

表單 — FORM … GET … COMBO
FORM hForm ACTION "/people" OF hReq
   GET   "emp_no" LABEL "Employee no." VALUE "E-2001"
   GET   "name"   LABEL "Name"
   COMBO "status" LABEL "Status" VALUE "active" ITEMS aStatuses
   GET   "salary" LABEL "Salary (annual)" VALUE 90000
   BUTTON "Save" GOLD
   LINK   "Cancel" ACTION "/"
ENDFORM
cHtml := FORM_HTML( hForm )
本次請求即時產生

瀏覽器版的 @ SAY … GET。CSRF 權杖隨表單一起送出,按下 Save,真的會新增一位員工。

Cancel

從 DOS 畫面到瀏覽器

當年這麼寫現在這麼寫作用
@ 10, 5 SAY "Name" GET cNameGET "name" LABEL "Name" VALUE cName表單裡一個帶標籤的輸入欄。
MENU oMenu … MENUITEM … ENDMENUMENU hMenu MENUBAR … MENUITEM … ENDMENU頁首按鈕、側欄篩選、分頁,一套寫法全搞定。
TBrowse / dbEdit()TABLE hList OF aRows … COLUMN … ENDTABLE可點擊的清單,內建縮圖、徽章和金額欄。
USE customers INDEX ON nameaRows := DB_QUERY( cSql, { cName } )參數綁定,直接查 PostgreSQL,沒有 ORM 伺服器。
SET COLOR TO W+/Bpublic/skins/<skin>/skin.css外觀交給皮膚:設計師能直接改的 CSS 和幾個小 HTML 範本。
ACTIVATE WINDOW oWndFV_SERVE( nPort, "public", {| hReq | APP_DISPATCH( hReq ) } )整個 Web 伺服器,就是 main.prg 裡的一行。
03 — 運作原理

執行環境直接上手, 不靠膠水。

每一層都是 Five 執行環境函式庫的呼叫。除了 PostgreSQL,什麼都不用裝。

FV_SERVE

HTTP 全程自己來

收請求標頭和 Cookie,回狀態碼和 set-cookie。每個請求跑在自己的 VM 執行緒裡。

WsServe · WsSend

即時

零相依的 WebSocket 伺服器,本頁底部的動態流正在監聽它。

PG_OPEN · PG_QUERY

直連 PostgreSQL

基於 pgx 的參數綁定查詢。遷移在啟動時自動執行,跑幾次結果都一樣。

PWHASH · ENCRYPT · SECEQ

預設就安全

bcrypt 密碼、AES-256-GCM 工作階段、常數時間比較、CSRF 雙重提交。

T()

內建多語系

目錄式翻譯,依請求決定語言。你正在看的這一頁,就是由 locales/*.json 以六種語言提供的。

hx.js

超媒體 UI

54 行的 HTMX 風格小墊片:局部替換、CSRF 標頭,沒有建置步驟,也沒有前端框架。

04 — 程式碼

請求是雜湊,回應也是雜湊。
處理函式就是一個程式碼區塊。

這是正在運作的應用程式裡真實的路由表和真實的處理函式。

routes/routes.prg
// asset manager — gallery, list, detail, CRUD, export
ROUTE( "GET",  "/assets",            {| hReq | ASSET_DASHBOARD( hReq ) } )
ROUTE( "GET",  "/assets/cards",      {| hReq | ASSET_CARDS( hReq ) } )
ROUTE( "POST", "/assets",            {| hReq | ASSET_CREATE( hReq ) } )
ROUTE( "GET",  "/assets/export.csv", {| hReq | ASSET_EXPORT( hReq ) } )

// main.prg — the whole server
FV_SERVE( hCfg[ "port" ], "public", {| hReq | APP_DISPATCH( hReq ) } )

// a handler: query → hash → JSON. The result lives in a variable; RETURN returns it.
FUNCTION ASSET_LIST( hReq )
   LOCAL aRows := Rows( hReq, 1000, 0 )
   LOCAL hRes  := RES_JSON( { "ok" => .T., "assets" => aRows, "count" => Len( aRows ) } )

RETURN hRes
 wss · /ws連線中
這是 RT_BROADCAST 推送的即時動態。在另一個分頁新增一筆資料試試,馬上就會出現在這裡: 新增資產 · 新增員工