FiveAI Fusion 言語
SpaceFive · Five 公式デモ

言語はひとつ。
バイナリもひとつ。
それで、Web のすべて。

SpaceFive は、純粋な Five だけで書かれた、必要なものがすべて揃った Web フレームワークです。HTTP も WebSocket も PostgreSQL も、bcrypt、AES、多言語対応まで、すべてランタイムが直接こなします。Node も FFI も ORM サーバーも要りません。静的バイナリひとつが Postgres とじかにやり取りし、その上で本物のアプリが 3 つ、いままさに動いています。

—
登録資産
—
資産総額
—
在籍メンバー
1
バイナリ
01 — ライブデモ

アプリは 3 つ、 骨組みはひとつ、
手ざわりは同じ。

クリックするものはすべて 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 が 4 行。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 を 1 行、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 の 1 行。
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 から 6 言語で配信しています。

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 が流すリアルタイムフィードです。別のタブでレコードをひとつ作ってみてください。ここにすぐ届きます。 資産を追加 · 社員を追加