Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
In one sentence: **Fun is a scripting language that goes all the way from JSON to machine code.**

- One runtime spans the whole spectrum, from high-level data work (JSON/FD) down to low-level system programming (FFI, C at runtime, JIT, machine code);
- 64 standard-library modules written in Fun itself ship together as one embeddable `fun.dll`;
- 65 standard-library modules written in Fun itself ship together as one embeddable `fun.dll`;
- in AI-agent and domestic-IT scenarios, it is the lightweight glue connecting OSes, databases, chips, and business logic.

Fun is designed around a simple premise: **data and code should be equally first-class, and a script should be able to reach down to the machine without leaving the language.**
Expand Down Expand Up @@ -70,7 +70,7 @@ Fun is written in Pascal (Delphi / Free Pascal). The core is a small tree-walkin
- **Keyword aliasing** — a canonical English keyword set with pluggable alias packs (Chinese, French, custom) selected at runtime (see [below](#keyword-aliasing)).
- **Cross-platform** — Windows (32/64-bit), Linux, ARM, and Windows CE.
- **Dual distribution** — command-line `fun.exe` and embeddable runtime `fun.dll` exporting a simple `Run` interface.
- **Self-hosted standard library** — 64 modules written in Fun itself, from JSON/YAML/XML to ORM, async, UI, and native bindings.
- **Self-hosted standard library** — 65 modules written in Fun itself, from JSON/YAML/XML to ORM, async, UI, and native bindings.

---

Expand Down Expand Up @@ -372,7 +372,7 @@ fun/
│ ├── utils/ # Utility functions
│ └── prj/fun/ # Projects & build scripts (funcmd.dpr, Delphi/FPC)
├── fun/
│ ├── lib/ # Standard library (pure Fun, 64 modules)
│ ├── lib/ # Standard library (pure Fun, 65 modules)
│ ├── demo/ # Language syntax demos
│ ├── demos/ # Full applications & benchmarks
│ └── key_*.ini # Keyword alias packs (CN / FR / PUA)
Expand Down Expand Up @@ -497,16 +497,16 @@ win64/aarch64 target, so there is nothing to build with.

## Standard library

The standard library in [`fun/lib`](fun/lib) is written in pure Fun (64 modules):
The standard library in [`fun/lib`](fun/lib) is written in pure Fun (65 modules):

> In total roughly 6,000 lines of logical code (excluding comments), about 100 lines per module — all 64 modules written in Fun itself.
> In total roughly 6,000 lines of logical code (excluding comments), about 100 lines per module — all 65 modules written in Fun itself.

- **Data**: `lib-json`, `lib-yaml`, `lib-xml`, `lib-base64`, `lib-cstruct`, `lib-md5`, `lib-crypt`
- **Utility**: `uuid`
- **Collections / algorithms**: `lib-set`, `lib-tree`, `lib-stack`, `lib-dyns`, `lib-math`
- **Text**: `lib-string`, `lib-regex`, `lib-match`, `lib-unicode`
- **System / IO**: `lib-file`, `lib-os`, `lib-host`, `lib-time`, `lib-cmdline`, `lib-proc`
- **Network**: `lib-winsock`, `lib-ajax`, `lib-jsonrpc`, `http-server`, `http-client`, `http-auth`
- **Network**: `lib-winsock`, `lib-ajax`, `lib-jsonrpc`, `http-server`, `http-client`, `http-auth`, `http-cors`
- **Databases**: `lib-orm`, `lib-orm-pro`, `lib-orm-gen`, `lib-orm-rpc`, `lib-orm-cte`, `lib-ado`, `lib-ado-schema`
- **Native / low-level**: `lib-winapi`, `lib-winole`, `lib-tcc` (C compile), `lib-jit`, `lib-asm`, `lib-asm-pro`
- **UI**: `lib-ui`, `lib-ui-base`, `lib-dialog`, `lib-trayicon`
Expand All @@ -531,6 +531,7 @@ The standard library in [`fun/lib`](fun/lib) is written in pure Fun (64 modules)
- [`fun/demos`](fun/demos) — full applications and benchmarks: the `notepad--` editor, `odbc-search`, and performance/assembly/JIT benchmarks.
- [`fun/demos/package-demo`](fun/demos/package-demo) — a small package with a path dependency: `tidy`, `run` (project and flat mode), and `pack`.
- [`fun/demos/test-demo`](fun/demos/test-demo) — a lib-only package with tests, a shared helper, and a failing-assertion example.
- [`fun/demos/http-cors-demo`](fun/demos/http-cors-demo) — CORS preflight, credentials, exposed headers, and a browser page.

---

Expand Down
13 changes: 7 additions & 6 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
一句话定位:**Fun 是一门从 JSON 一路写到机器码的脚本语言。**

- 用同一个运行时,覆盖从高层数据处理(JSON/FD)到底层系统编程(FFI、运行时 C、JIT、机器码)的全谱系;
- 自带 64 个用 Fun 自身编写的标准库模块,全栈能力打包进单个可嵌入的 `fun.dll`;
- 自带 65 个用 Fun 自身编写的标准库模块,全栈能力打包进单个可嵌入的 `fun.dll`;
- 在信创与 AI Agent 场景中,作为连接国产 OS、数据库、芯片与业务逻辑的轻量级胶水。

Fun 的设计出发点很简单:**数据与代码都应该是头等公民,而且一段脚本应该能在不离开语言的情况下一路触达机器底层。**
Expand Down Expand Up @@ -70,7 +70,7 @@ Fun 使用 Pascal(Delphi / Free Pascal)编写。核心是一个小巧的树
- **关键字别名** —— 规范英文关键字 + 可插拔的别名包(中文、法文、自定义),运行时选择(见[下文](#关键字别名))。
- **跨平台** —— Windows(32/64 位)、Linux、ARM 与 Windows CE。
- **双形态分发** —— 命令行 `fun.exe` 与嵌入运行时 `fun.dll`(导出简单的 `Run` 接口)。
- **自托管的标准库** —— 64 个用 Fun 自身编写的模块,覆盖 JSON/YAML/XML 到 ORM、异步、UI 与原生绑定。
- **自托管的标准库** —— 65 个用 Fun 自身编写的模块,覆盖 JSON/YAML/XML 到 ORM、异步、UI 与原生绑定。

---

Expand Down Expand Up @@ -365,7 +365,7 @@ fun/
│ ├── utils/ # 工具函数
│ └── prj/fun/ # 工程与构建脚本(funcmd.dpr,Delphi/FPC)
├── fun/
│ ├── lib/ # 标准库(纯 Fun 编写,64 个模块)
│ ├── lib/ # 标准库(纯 Fun 编写,65 个模块)
│ ├── demo/ # 语言语法演示
│ ├── demos/ # 完整应用与基准测试
│ └── key_*.ini # 关键字别名包(CN / FR / PUA)
Expand Down Expand Up @@ -477,16 +477,16 @@ float/混合参数与 `@toCallback` 闭包在 win64 上也有覆盖;`ffi-scala

## 标准库

标准库位于 [`fun/lib`](fun/lib),以纯 Fun 编写(64 个模块):
标准库位于 [`fun/lib`](fun/lib),以纯 Fun 编写(65 个模块):

> 总代码量约 6,000 行逻辑代码(不含注释),平均每个模块约 100 行——64 个模块全部用 Fun 自身写成。
> 总代码量约 6,000 行逻辑代码(不含注释),平均每个模块约 100 行——65 个模块全部用 Fun 自身写成。

- **数据**:`lib-json`、`lib-yaml`、`lib-xml`、`lib-base64`、`lib-cstruct`、`lib-md5`、`lib-crypt`
- **工具**:`uuid`
- **集合 / 算法**:`lib-set`、`lib-tree`、`lib-stack`、`lib-dyns`、`lib-math`
- **文本**:`lib-string`、`lib-regex`、`lib-match`、`lib-unicode`
- **系统 / IO**:`lib-file`、`lib-os`、`lib-host`、`lib-time`、`lib-cmdline`、`lib-proc`
- **网络**:`lib-winsock`、`lib-ajax`、`lib-jsonrpc`、`http-server`、`http-client`、`http-auth`
- **网络**:`lib-winsock`、`lib-ajax`、`lib-jsonrpc`、`http-server`、`http-client`、`http-auth`、`http-cors`
- **数据库**:`lib-orm`、`lib-orm-pro`、`lib-orm-gen`、`lib-orm-rpc`、`lib-orm-cte`、`lib-ado`、`lib-ado-schema`
- **原生 / 底层**:`lib-winapi`、`lib-winole`、`lib-tcc`(C 编译)、`lib-jit`、`lib-asm`、`lib-asm-pro`
- **UI**:`lib-ui`、`lib-ui-base`、`lib-dialog`、`lib-trayicon`
Expand All @@ -508,6 +508,7 @@ float/混合参数与 `@toCallback` 闭包在 win64 上也有覆盖;`ffi-scala
- [`fun/demos`](fun/demos) —— 完整应用与基准测试:记事本 `notepad--`、`odbc-search`,以及性能 / 汇编 / JIT 基准。
- [`fun/demos/package-demo`](fun/demos/package-demo) —— 带路径依赖的小项目:`tidy`、`run`(项目/平铺)与 `pack`。
- [`fun/demos/test-demo`](fun/demos/test-demo) —— 只有库的包及测试:共享 helper、通过用例与失败断言示例。
- [`fun/demos/http-cors-demo`](fun/demos/http-cors-demo) —— CORS 预检、credentials、暴露响应头与浏览器页面示例。

---

Expand Down
86 changes: 86 additions & 0 deletions fun/demos/http-cors-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# http-cors-demo

A small API and browser page showing the CORS middleware:

- preflight `OPTIONS` is answered before routing and authentication;
- `credentials: true` requires an exact origin and sends
`Access-Control-Allow-Credentials: true`;
- `X-Request-Id` is exposed so browser JavaScript can read it;
- `Vary: Origin` keeps shared caches correct.

```text
package.fun # manifest
src/main.fun # API on :8080
web/index.html # page served from :5173
```

## Run

`fun` below is the command-line binary (`fun.exe` on Windows). From the
repository root, after building, the in-tree binary is
`src/prj/fun/funcmd`; from this directory that is
`../../../src/prj/fun/funcmd`.

```text
cd fun/demos/http-cors-demo

# terminal 1: API on http://127.0.0.1:8080
fun run
# or: ../../../src/prj/fun/funcmd run

# terminal 2: page on http://127.0.0.1:5173
python3 -m http.server 5173 --bind 127.0.0.1 --directory web
```

Open <http://127.0.0.1:5173> and click the buttons.

The allowed origin is exactly `http://127.0.0.1:5173`; opening the page from
another origin is rejected by the browser (preflight returns 403, actual
requests have no `Access-Control-Allow-Origin`).

## Curl

The browser sends the preflight automatically. To see it directly:

```text
curl -i -X OPTIONS http://127.0.0.1:8080/api/public \
-H 'Origin: http://127.0.0.1:5173' \
-H 'Access-Control-Request-Method: GET'
```

Expected: `204` with `Access-Control-Allow-Origin`,
`Access-Control-Allow-Methods`, `Access-Control-Allow-Credentials`,
`Access-Control-Max-Age`, and `Vary`.

A simple request:

```text
curl -i http://127.0.0.1:8080/api/public \
-H 'Origin: http://127.0.0.1:5173'
```

Expected: `200` with `Access-Control-Allow-Origin`,
`Access-Control-Allow-Credentials`, `Access-Control-Expose-Headers`, and
`Vary: Origin`.

A JSON POST triggers a preflight for `Content-Type`:

```text
curl -i -X OPTIONS http://127.0.0.1:8080/api/echo \
-H 'Origin: http://127.0.0.1:5173' \
-H 'Access-Control-Request-Method: POST' \
-H 'Access-Control-Request-Headers: content-type'
```

## Notes

- CORS is a browser read policy, not authentication or CSRF protection. The
demo endpoints are public.
- `origins: ['*']` is the default for quick public APIs. It cannot be combined
with `credentials: true`; credentialed APIs must list exact origins.
- Register CORS before authentication middleware. Preflight requests do not
carry credentials and must not be rejected by auth.
- `headers` omitted means "reflect the requested headers"; list them explicitly
when you want a fixed allowlist.
- In production, set explicit origins, use HTTPS, and keep `Vary: Origin`
intact through proxies/CDNs.
15 changes: 15 additions & 0 deletions fun/demos/http-cors-demo/package.fun
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
fun package()
result = [
name: 'http-cors-demo',
version: '0.1.0',
bin: 'src/main.fun',
lib: '',
'fun': '9.4',

dependencies: [],

// >>> generated - do not edit; run 'fun package tidy' to refresh
resolved: []
// <<< generated
];
end fun;
24 changes: 24 additions & 0 deletions fun/demos/http-cors-demo/src/main.fun
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use '../../../lib/http-server.fun';
use '../../../lib/http-cors.fun';

fun publicH(req, res)
res.header('X-Request-Id', 'demo-1').json([ok: true, from: 'api']);
end fun;

fun echoH(req, res)
res.json([got: req.json()]);
end fun;

var s = HttpServer(8080);
s.Use(HttpLogger());
s.Use(HttpCors([
origins: ['http://127.0.0.1:5173'],
methods: ['GET', 'POST'],
headers: ['Content-Type', 'Authorization'],
exposeHeaders: ['X-Request-Id'],
credentials: true,
maxAge: 600
]));
s.get('/api/public', publicH);
s.post('/api/echo', echoH);
s.run();
55 changes: 55 additions & 0 deletions fun/demos/http-cors-demo/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fun CORS demo</title>
<style>
body { font: 15px/1.5 system-ui, sans-serif; margin: 2rem auto; max-width: 760px; padding: 0 1rem; }
button { margin: 0 .5rem .5rem 0; padding: .4rem .8rem; }
pre { background: #f4f4f4; padding: 1rem; overflow: auto; white-space: pre-wrap; }
code { background: #eee; padding: .1rem .3rem; }
</style>
</head>
<body>
<h1>Fun CORS demo</h1>
<p>
Page origin: <code>http://127.0.0.1:5173</code><br>
API origin: <code>http://127.0.0.1:8080</code>
</p>
<button id="get">GET /api/public</button>
<button id="post">POST /api/echo</button>
<pre id="out">Click a button.</pre>

<script>
const API = 'http://127.0.0.1:8080';
const out = document.getElementById('out');

async function show(label, url, opts) {
out.textContent = label + ' ...';
try {
const r = await fetch(url, opts);
const body = await r.text();
out.textContent =
label + '\n' +
'status: ' + r.status + '\n' +
'X-Request-Id: ' + (r.headers.get('X-Request-Id') || '(not exposed)') + '\n\n' +
body;
} catch (e) {
out.textContent = label + '\nCORS/fetch error: ' + e;
}
}

document.getElementById('get').onclick = () =>
show('GET /api/public', API + '/api/public', { credentials: 'include' });

document.getElementById('post').onclick = () =>
show('POST /api/echo', API + '/api/echo', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ hello: 'fun' })
});
</script>
</body>
</html>
Loading
Loading