{"slug": "show-hn-webbase-iii-dbase-iii-rebuilt-in-the-browser-with-its-own-interpreter", "title": "Show HN: WebBase-III – dBASE III rebuilt in the browser with its own interpreter", "summary": "WebBase-III, a browser-based reimplementation of the dBASE III database system, has been released as an open-source project. The tool includes a full W3Script interpreter, BROWSE grid, form engine, indexing, and multi-user support, all built with TypeScript, Node.js, WebSockets, and SQLite. It allows users to run classic dBASE commands and programs directly in a web browser without installation.", "body_md": "**dBASE III is back. In your browser. USE customers like it's 1984.**\n\nRemember the dot prompt? Before SQL won, before ORMs, before anyone said \"full-stack\" — there was dBASE III. You typed `USE customers`\n\n, then `LIST`\n\n, and your data was just *there*. WebBase-III brings that whole world back: the terminal, the language, `BROWSE`\n\n, `@ SAY GET`\n\nforms, `.prg`\n\nprograms, indexes, reports — rebuilt from scratch as a modern web app with its own interpreter in TypeScript, backed by Node.js, WebSockets, and SQLite.\n\n**Try it in one click — no install:**\n\nThe Codespace installs dependencies and starts the dev server automatically. Open the forwarded port **5173** and you're at the dot prompt.\n\nThe command interface — type W3Script and see results instantly.\n\n`LIST`\n\nprints all records in active index order. The status bar shows the active database and table.\n\n`INDEX ON name TO BYNAME`\n\ncreates a SQLite index and activates it — subsequent `LIST`\n\noutput is sorted alphabetically. `SEEK \"Delta NV\"`\n\njumps the record pointer to the first match in O(log n).\n\n`BROWSE`\n\nopens a spreadsheet-style grid. Records are shown in active index order. Tab/Enter to edit a cell, Ctrl+N for a new row, Delete to remove a row, Esc to return to the terminal.\n\n`EDIT <name>`\n\nopens the built-in `.prg`\n\nsource editor. Programs support the full W3Script language: `DO CASE/ENDCASE`\n\n, `DO WHILE/ENDDO`\n\n, `IF/ENDIF`\n\n, form layouts, and all data commands. Ctrl+S saves, Esc cancels.\n\n`@ row,col SAY \"label\" GET variable`\n\nlays out character-cell form fields. `READ`\n\nrenders them as a live form and waits for the user to fill in values and submit.\n\nThe permanent left sidebar with category pickers and action buttons.\n\nWizards open in the main area with a live W3Script preview.\n\n| Feature | Details |\n|---|---|\nW3Script interpreter |\ndBASE III command dialect: navigation, filters, variables, loops, conditionals, forms, programs |\nBROWSE grid |\nInline cell editing, keyboard nav, index-ordered display |\nForm engine |\n`@ ROW,COL SAY … GET` character-cell layout with `READ` |\nIndexing |\n`INDEX ON` , `SEEK` , `FIND` — active index controls all record order |\nDO CASE |\nMulti-branch conditional, `OTHERWISE` fallback |\nBuilt-in functions |\n`EOF()` , `BOF()` , `FOUND()` , `RECNO()` , `SUBSTR()` , `STR()` , `AT()` , `CTOD()` , `DTOC()` and more |\nProgram files |\nSave, edit, and run `.prg` scripts with `DO` / `EDIT` |\nThe Assistant |\nPermanent left sidebar — open databases/tables, browse, filter, index, search, design reports, run programs without typing |\nMulti-user |\nEach WebSocket connection gets its own isolated interpreter session |\nPersistent storage |\n`better-sqlite3` with WAL mode — databases survive server restart |\n\nThe sidebar on the left drives everything without typing: open or create databases and tables, browse and filter data, build indexes, search, design and run reports, and run programs. Every click generates a real W3Script command that echoes into the terminal — watch it to learn the language. Wizards (New table, Filter, report designer, …) open in the main area and show a live preview of the command they will run.\n\n```\nnpm install\nnpm run dev        # http://localhost:5173\n```\n\nProduction:\n\n```\nnpm run serve      # builds, then serves everything on http://localhost:3000\n```\n\nLAN / Tailscale: the server binds to `0.0.0.0`\n\n, so `http://<tailscale-ip>:3000`\n\nworks out of the box.\n\n```\nUSE DATABASE mydb\nCREATE TABLE customers (name CHAR(40), phone CHAR(20), country CHAR(30))\nUSE customers\n\nAPPEND RECORD\nREPLACE name WITH \"Acme Corp\", phone WITH \"555-1234\", country WITH \"BE\"\nAPPEND RECORD\nREPLACE name WITH \"Zeta Ltd\", phone WITH \"555-5678\", country WITH \"NL\"\n\nINDEX ON name TO BYNAME\nLIST                        * sorted A→Z\n\nSEEK \"Zeta Ltd\"             * jump to record instantly\nBROWSE                      * open editable grid\nSET FILTER TO country == \"BE\"\nLIST                        * filtered view\nSET FILTER TO               * clear filter\n```\n\nWebBase-III supports **unlimited work areas** — each independently holding a table, record pointer, filter, and index. Link areas by key field using `SET RELATION TO`\n\nfor relational data access. Cross-area field access uses `alias.field`\n\ndot notation.\n\nNote:dBASE III supported a maximum of 10 work areas (DOS file handle limit). WebBase-III has no such limit. dBASE III used`alias->field`\n\narrow syntax; WebBase-III uses modern`alias.field`\n\ndot notation.\n\n| Command | What it does |\n|---|---|\n`SELECT <alias>` |\nActivate (or create) a work area by name |\n`USE <table> [ALIAS <name>]` |\nOpen table in active area; optional alias override |\n`SET RELATION TO <expr> INTO <alias>` |\nLink active area to another; auto-seeks on every navigation |\n`SET RELATION TO` |\nClear relation on active area |\n`LIST [col, alias.col, ...]` |\nList records; optional column list with cross-area fields |\n`LIST AREAS` |\nShow all open work areas, pointers, indexes, and relations |\n`CLOSE` |\nClose active area's table |\n`CLOSE ALL` |\nClose all work areas, reset to single empty area `1` |\n\n**Cross-area field access**: use `alias.field`\n\ndot notation anywhere an expression is accepted — `SET FILTER TO`\n\n, `IF`\n\n, `REPLACE`\n\n, `LIST`\n\n, `INDEX ON`\n\n.\n\n| Command | What it does |\n|---|---|\n`USE <table>` |\nSelect a table; restores any saved active index |\n`USE DATABASE <name>` |\nOpen a named SQLite database |\n`LIST` |\nPrint records in active index order (up to 500) |\n`LIST STRUCTURE` |\nShow column schema |\n`LIST TABLES` |\nShow all tables with record counts |\n`LIST DATABASES` |\nShow all databases on disk (alias: `LIST DBS` ) |\n`BROWSE` |\nOpen the editable grid |\n`CLEAR` |\nClear terminal output |\n`CREATE TABLE <n> (col TYPE, ...)` |\nCreate a table |\n`DROP TABLE <name>` |\nDelete a table |\n`APPEND RECORD` |\nInsert a blank row |\n`DELETE` / `DELETE ALL` |\nDelete current or all records |\n`PACK` |\nVACUUM the SQLite file |\n`GO TOP` / `GO BOTTOM` / `GO <n>` |\nMove record pointer |\n`SKIP <n>` |\nMove pointer forward/back |\n`REPLACE <field> WITH <val>, ...` |\nUpdate field(s) on current row |\n`REPLACE ALL <field> WITH <val>, ...` |\nUpdate all (filtered) rows |\n`SET FILTER TO <expr>` |\nSet a WHERE clause; empty clears it |\n\n| Command | What it does |\n|---|---|\n`INDEX ON <expr> TO <tag>` |\nCreate index on expression; sets it active immediately |\n`SET INDEX TO <tag>` |\nActivate a previously created index |\n`SET INDEX TO` |\nClear active index — restores natural insert order |\n`REINDEX` |\nRebuild SQLite indexes for current table |\n`LIST INDEXES` |\nPrint all indexes for current table with `*` active marker |\n`SEEK <expr>` |\nPosition record pointer at first index match |\n`FIND <string>` |\nAlias for SEEK (unquoted string — dBASE III legacy form) |\n\n| Command | What it does |\n|---|---|\n`CREATE REPORT <name>` |\nCreate a new report definition (opens JSON editor) |\n`MODIFY REPORT <name>` |\nEdit an existing report definition |\n`REPORT FORM <name>` |\nRun report — ASCII to terminal + HTML preview panel |\n`LIST REPORTS` |\nList all saved report definitions |\n`DELETE REPORT <name>` |\nDelete a report definition |\n\n| Command | What it does |\n|---|---|\n`DO <name>` |\nRun a saved `.prg` program |\n`EDIT <name>` |\nOpen `.prg` source editor |\n`LIST PROGRAMS` |\nShow all saved programs |\n\nDemo programs live in\n\n`demos/*.prg`\n\nand are the single source of truth: they are seeded into the program store on every server start, overwriting any store copy. Try`DO inventory`\n\nfor a full interactive showcase (work areas, relations, indexes, forms).\n\n| Command | What it does |\n|---|---|\n`STORE <val> TO <var>` |\nAssign a variable |\n`INPUT \"prompt\" TO <var>` |\nCollect keyboard input |\n`@ r,c SAY \"text\" GET <var>` |\nDefine a form field |\n`READ` |\nDisplay the form and wait for submit |\n\n| Command | What it does |\n|---|---|\n`IF <cond> … ENDIF` |\nConditional block |\n`DO WHILE <cond> … ENDDO` |\nLoop |\n`DO CASE … ENDCASE` |\nMulti-branch conditional (`CASE` , `OTHERWISE` ) |\n`HELP` |\nPrint command reference |\n`QUIT` |\nExit |\n\nFunctions work anywhere an expression is accepted — `IF`\n\n, `DO WHILE`\n\n, `STORE`\n\n, `REPLACE`\n\n, `INDEX ON`\n\n, `SET FILTER TO`\n\n, etc.\n\n| Function | Returns |\n|---|---|\n`EOF()` |\nTrue if record pointer is past last record |\n`BOF()` |\nTrue if record pointer is before first record |\n`FOUND()` |\nTrue if last `SEEK` / `FIND` matched |\n`RECNO()` |\nCurrent record number |\n`RECCOUNT()` |\nTotal records in current table |\n`UPPER(str)` |\nUppercase |\n`LOWER(str)` |\nLowercase |\n`TRIM(str)` |\nStrip leading and trailing spaces |\n`LTRIM(str)` |\nStrip leading spaces only |\n`SUBSTR(str, start, len)` |\nSubstring — 1-based; `len` optional (to end) |\n`LEN(str)` |\nString length |\n`AT(needle, haystack)` |\n1-based position; 0 if not found (case-sensitive) |\n`STR(num, len, dec)` |\nNumber to right-justified string; default len=10, dec=0 |\n`VAL(str)` |\nString to number; non-numeric → 0 |\n`INT(n)` |\nTruncate toward zero |\n`ABS(n)` |\nAbsolute value |\n`SPACE(n)` |\nString of n spaces |\n`REPLICATE(str, n)` |\nRepeat string n times |\n`DATE()` |\nToday as `MM/DD/YY` |\n`DTOC(date)` |\nDate to display string `MM/DD/YY` |\n`CTOD(str)` |\nDisplay string `MM/DD/YY` to ISO date |\n\nW3Script supports both styles:\n\n| Syntax | Value |\n|---|---|\n`TRUE` / `FALSE` |\nBoolean true/false |\n`.T.` / `.TRUE.` |\nBoolean true (dBASE III style) |\n`.F.` / `.FALSE.` |\nBoolean false (dBASE III style) |\n\nBoolean values display as `.T.`\n\n/ `.F.`\n\nin output to match dBASE conventions.\n\nLogical operators are accepted in both styles too: `NOT`\n\n/ `.NOT.`\n\n, `AND`\n\n/ `.AND.`\n\n, `OR`\n\n/ `.OR.`\n\n(e.g. `DO WHILE .NOT. EOF()`\n\n).\n\n| Key | Action |\n|---|---|\n| Arrow keys | Navigate cells |\n| Enter / F2 | Edit selected cell |\n| Tab / Shift+Tab | Move right / left |\n| Ctrl+N | New row |\n| Delete | Delete current row |\n| F5 | Refresh from DB |\n| Esc | Exit grid, return to terminal |\n\n```\nserver/\n  index.ts              Node.js HTTP + WebSocket server (port 3000)\n  Session.ts            Per-connection session: parses commands, drives Executor\n  SessionManager.ts     Tracks all active sessions\n  ServerDatabaseBridge.ts  IDatabaseBridge impl wrapping better-sqlite3\n  ProgramStore.ts       .prg program storage in data/system.sqlite3\n  IndexStore.ts         Index metadata + active index in data/system.sqlite3\n\nsrc/\n  interpreter/\n    Lexer.ts            Tokenises W3Script input (case-insensitive)\n    Parser.ts           Recursive-descent AST builder\n    Executor.ts         Async AST runner; manages db/table/filter/vars/rowPtr/activeIndex\n    Builtins.ts         Stateless built-in function implementations\n\n  terminal/\n    Terminal.ts         REPL UI — command history, multi-line block accumulation\n\n  ui/\n    Grid.ts             BROWSE spreadsheet — inline cell editing, keyboard nav\n    FormLayout.ts       @ SAY GET form engine — character-cell coordinates\n    ProgramEditor.ts    .prg source editor UI\n\n  ws/\n    WsClient.ts         Browser WebSocket client\nnpm test                    # unit + integration tests (Vitest)\nnpx playwright test         # end-to-end browser tests (requires dev server running)\n```\n\nThe Playwright suite (`tests/integration.spec.ts`\n\n, `tests/crm.spec.ts`\n\n) drives a real browser against the running app and covers navigation, filters, indexing, programs, forms, and BROWSE.\n\nAGPL-3.0 — see [LICENSE.md](/DDecoene/WebBaseIII/blob/main/LICENSE.md).\n\nWhy AGPL? WebBase-III is a toy, and the license keeps it that way: anyone can use it, fork it, and learn from it, but nobody can take it closed and sell it as a hosted service without giving their changes back. If you want to run it, hack it, or ship features from your dBASE memories — that's exactly what it's for.", "url": "https://wpnews.pro/news/show-hn-webbase-iii-dbase-iii-rebuilt-in-the-browser-with-its-own-interpreter", "canonical_source": "https://github.com/DDecoene/WebBaseIII", "published_at": "2026-06-24 08:46:16+00:00", "updated_at": "2026-06-24 09:14:30.612474+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence"], "entities": ["WebBase-III", "dBASE III", "TypeScript", "Node.js", "WebSockets", "SQLite", "W3Script"], "alternates": {"html": "https://wpnews.pro/news/show-hn-webbase-iii-dbase-iii-rebuilt-in-the-browser-with-its-own-interpreter", "markdown": "https://wpnews.pro/news/show-hn-webbase-iii-dbase-iii-rebuilt-in-the-browser-with-its-own-interpreter.md", "text": "https://wpnews.pro/news/show-hn-webbase-iii-dbase-iii-rebuilt-in-the-browser-with-its-own-interpreter.txt", "jsonld": "https://wpnews.pro/news/show-hn-webbase-iii-dbase-iii-rebuilt-in-the-browser-with-its-own-interpreter.jsonld"}}