Make it a script.
日々の作業を、自動化。
Files, text, processes, and scheduled jobs. Turn repeated work into a command.
ファイル、文字列、プロセス、定期実行。繰り返す作業を、ひとつのコマンドに。
Explore the tools →道具を見る →From a quick script to a web service. Bring the language you know to your terminal, with tools for the work ahead.
ちょっとした自動化からWebサービスまで。使い慣れた言語と、必要な道具をターミナルへ。
var app := GD.web.app()
func hello(_req):
return GD.web.json({"message": "Hello!"})
func main():
app.route("GET", "/", hello)
app.listen(8080, "127.0.0.1")!
return 0curl -fsSL https://gd.progsha.com/install.sh | shInvoke-WebRequest -UseBasicParsing https://gd.progsha.com/install.ps1 -OutFile install-gd.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File .\install-gd.ps1Install guide →導入ガイド →Prefer a package manager? Homebrew & apt instructions. Windows installs without administrator access.
パッケージ管理からも導入できます。Homebrew・aptの手順。Windowsは管理者権限なしで使えます。
Files, text, processes, and scheduled jobs. Turn repeated work into a command.
ファイル、文字列、プロセス、定期実行。繰り返す作業を、ひとつのコマンドに。
Explore the tools →道具を見る →Routes, JSON, templates, and middleware. Build an API with the web tools already included.
ルーティング、JSON、テンプレート、ミドルウェア。組み込みの道具でAPIを作る。
Build a web service →Webサービスを作る →Read structured files, query a database, and add packages as your project grows.
構造化ファイルの読み書きから、データベースへのクエリまで。パッケージで広がる開発。
Connect a database →データベースを使う →gd 0.7.4 / REFERENCE
オンラインのマニュアル・APIリファレンスはブラウザーの優先言語に合わせて英語・日本語を表示し、対象外の言語では英語を表示します。 言語ボタンまたは?lang=jaで日本語へ切り替えられ、選択は端末内に保存されます。
gdは、GDScriptで端末の道具、Webサイト、Web API、定期処理、データ処理を書くための単体コマンドです。 Godotを画面なしで小さく組んであり、project.godotを用意せずに.gd fileを一枚書いて実行できます。
PythonやNode.jsでscriptを書く感覚で始められ、必要になった時点で型検査、test、package、database、 Web server、単一実行体へ同じGDScriptのまま進めます。ゲームの画面や描画を作る用途にはGodot本家を使ってください。
Godot本家とあわせれば、アプリもフロントもserverも端末ツールも、一つの言語GDScriptで書けます。 同じscriptがmacOS、Linux、Windowsで動き、通信とdatabaseの待ちはほかの処理を止めません。 GDExtensionでC++と直接つながります。AI agentが書いて動かすことを前提に設計しています。
HTTP/2の多重化とノンブロッキング通信を内蔵。UTF-8・JSON・hex処理にはSIMD/SWARを活用し、接続・バッファ・解析結果の再利用で不要な処理と割当を抑えています。
対応環境はmacOS arm64/x86_64、Linux x86_64、Windows x86_64です。 ReleasesからOSに合うarchiveを取得し、 中のgdをPATHの通ったdirectoryへ置きます。gd --versionが版を表示すれば導入は完了です。 配布物のSHA-256は同梱のSHA256SUMSで照合できます。macOS版はDeveloper ID署名とAppleの公証を通しています。
curl -fsSL https://gd.progsha.com/install.sh | sh
チェックサムを照合して~/.local/binへ導入します。案内が出た場合は、このdirectoryをPATHへ追加してください。Linux配布バイナリにはx86_64とglibc 2.38以降が必要です。
Invoke-WebRequest -UseBasicParsing https://gd.progsha.com/install.ps1 -OutFile install-gd.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File .\install-gd.ps1
PowerShellで実行してください。管理者権限は不要です。SHA-256を照合し、%LOCALAPPDATA%\gd\binへgd.exeを置き、ユーザーのPATHへ追加します。新しいターミナルを開き、gd --versionで確認します。
手動の場合はReleasesからgd-windows-x86_64.zipを取得して展開し、gd.exeを置いたdirectoryをユーザーのPATHへ追加します。PowerShellでUnix用のcurl | shは実行しません。
brew tap prog-sha/gd https://github.com/prog-sha/gd
brew install prog-sha/gd/gd
製品リポジトリをtapとして使い、署名済みUniversal実行体を導入します。更新はbrew update && brew upgrade prog-sha/gd/gd、削除はbrew uninstall gdです。
Ubuntu 24.04、Debian 13など、glibc 2.38以降のapt対応環境で利用できます。Ubuntu 22.04やDebian 12ではsourceからbuildしてください。専用の署名鍵を、この配布元だけに適用します。
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://gd.progsha.com/apt/gd.asc | sudo tee /etc/apt/keyrings/gd.asc >/dev/null
sudo chmod 0644 /etc/apt/keyrings/gd.asc
echo 'deb [arch=amd64 signed-by=/etc/apt/keyrings/gd.asc] https://gd.progsha.com/apt stable main' | sudo tee /etc/apt/sources.list.d/gd.list
sudo apt update
sudo apt install gd
以降は通常のapt更新で新しい版を取得できます。削除はsudo apt remove gdです。HTTPSに加え、aptが配布メタデータの署名を検証します。
公式カタログへの登録申請・審査は未完了です。登録までは上記のPowerShellインストーラーを使ってください。管理者向けの申請用マニフェストを用意していますが、winget install prog-sha.gdはまだ利用できません。
sourceからbuildする場合はPython、uv、SCons、C/C++ compilerを用意し、bin/にできるgd.*.template_release.*を使います。 TLSは内蔵しているため、別のTLSライブラリは要りません。
git clone --branch 0.7 https://github.com/prog-sha/gd.git
cd gd
scons platform=macos target=template_release -j8
# Linux: platform=linuxbsd
# Windows: platform=windows windows_subsystem=console
hello.gdを一枚作ります。設定fileやpackageは要りません。main()が入口で、返した整数がprocessの終了codeになります。
func main():
print("Hello, world")
return 0
gd hello.gd
実行せずに型と構文を調べるにはcheckを使います。
gd check hello.gd
ローカルfileは@import "./settings"、導入済みpackageは@import hello。読み込んだ名前をそのまま使います。.gdやas settingsは省略できます。
共有する値をsettings.gdに置きます。
const TITLE = "Hello"
const USER = "world"
main.gdから使います。
@import "./settings"
func main():
print(settings.TITLE, ", ", settings.USER, "!")
return 0
これでHello, world!と表示します。共有が必要になったときの書き方です。
settings.TITLEのように使えば、値ごとのconst宣言を利用側へコピーする必要はありません。1つのscriptでしか使わない値や処理は、そのscriptに置けば十分です。asは名前の衝突や読み分けが必要なときだけ使います。組み込みのGD.webなどにはimportは不要です。
packageの導入とversion固定はpackageと配布を参照してください。
標準APIの入口はGD一つで、用途ごとの子を持ちます。scriptからはこの名前をそのまま書きます。
| やりたいこと | 入口 | 例 |
|---|---|---|
| file、文字、日時、HTTP client、非同期処理 | GD | GD.file.read_text("a.txt") |
| WebサイトとWeb API | GD.web | GD.web.app() |
| SQLiteまたはPostgreSQL | GD.database | GD.database.client() |
GD.database.postgresとGD.database.redisは、接続先固有の機能が必要なときに使う高度な入口です。
gd docに、scriptへ書く綴りをそのまま渡します。署名は実行体から作るため、実装と一致します。
gd doc # Show a short guide and entry points.
gd doc manual # Read the complete manual.
gd doc GD # List the standard modules.
gd doc GD.file # file API
gd doc GD.http.fetch # Inspect the returned HTTP response.
gd doc GD.web.app # Web application
gd doc SceneTree # Inspect a public engine class.
gd doc all # List public types.
戻り型のR、Err、GDWebRequestは名前だけで引きます。Node、SceneTree、TimerなどGodot由来の型は Godotのclass referenceも参照してください。 手引きの言語はLC_ALLまたはLANGがjaで始まるとき日本語、それ以外は英語です。 Web版はgd.progsha.comにあり、日本語と英語を切り替えられます。
掲載例では型名を繰り返しません。:=で代入する変数と、return 値, 失敗で返す成功値は型を推論します。 注釈を省いた引数は動的型です。型で境界を固定したい箇所だけ注釈を足せます。
script名の後ろに置いた引数はmain(argv)で受け取ります。--name=gdのようにgd自身のflagと紛らわしい引数は、 --の後ろへ置くとscriptへ渡ります。
func main(argv):
for arg in argv:
print(arg)
return 0
gd main.gd apple orange
gd main.gd -- --name=gd
flagとして解釈したいときはGD.cli.flags()を使います。--name gd、--name=gd、-name=gdのどの綴りも受けます。
func main(argv):
var flags := GD.cli.flags()
flags.flag_str("name", "world", "挨拶する相手")
var parsed := flags.parse(argv)
if not parsed.ok:
print(flags.usage())
return 1
print("Hello, " + flags.get_str("name"))
return 0
外の道具はGD.cli.run()で呼びます。待つのは呼び出したGDScriptだけなので、gd serveのhandlerの中から呼んでも他のrequestは進みます。 --strictでは--allow-runが要ります。--allow-run=/usr/bin/gitのように相手を絞れます。
func main():
var got := GD.cli.run("git", ["rev-parse", "HEAD"])
if not got.ok:
return 1
print("code=", got.v["code"], " out=", got.v["output"])
return 0
第3引数のoptsで挙動を変えられます。
子の非ゼロ終了はErrです。終了コードはvalue.codeとe.info.code、出力はvalue.outputで確認できます。期限や取消でも取得済み出力は残ります。
| 名前 | 既定 | 意味 |
|---|---|---|
timeout | 0 | 諦めるまでの秒数。0は無期限。越えると子を畳んでErr.TIMED_OUTを返す |
output | true | 出力を集める。falseなら親の標準入出力へ直結し、集めない |
gd check main.gd # Check syntax and types without execution.
gd fmt main.gd # Format the source consistently.
gd test # Discover and run *_test.gd files.
gd --watch main.gd # Restart after each source save.
gd eval 'print(1 + 1)' # Evaluate one expression.
gd repl # Start an interactive session.
失敗しうる関数は、例外を投げる代わりに「成功値と失敗」の二つの値を返します。 受け取る側はvar 値, e :=の形で両方を受け、eがnullでなければ失敗です。
func main():
var text, e := GD.file.read_text("note.txt")
if e:
print(e.text())
return 1
print(text)
return 0
毎回if eを書く代わりに、呼出しの末尾へ?を付けると、失敗をそのまま呼出し元へ返して成功値だけが残ります。 ?を使う関数は、自分もreturn 値, 失敗で成功値と失敗を返します。
func title(path):
var text := GD.file.read_text(path)?
return text.strip_edges(), null
func main():
var text, e := title("note.txt")
if e:
print(e.note("題名を読む").text())
return 1
print(text)
return 0
| 書き方 | 意味 |
|---|---|
var value, e := call() | 成功値と失敗を分けて受ける |
return 値, null / return null, 失敗 | 成功または失敗を返す。成功値の型は値から推論する |
call()? | 失敗なら呼出し元へそのまま返す |
call()! | 失敗なら理由を表示して、programをその場で止める(終了codeは1)。試作やtest向き。gd serveではそのhandlerだけが失敗する |
e.note("目的") | 失敗に作業の文脈を足す。表示は「目的: 元の理由」の形になる |
e.kind | Err.NOT_FOUND、Err.INVALID_DATAなどの種類。分岐に使う |
Err.err("理由", Err.NOT_FOUND) | 自分で失敗を作る |
失敗を呼出し元へ渡さないmain()ではvar 値, e :=か!で受けます。
file操作の失敗ではe.infoにop、path、source、source_codeが入ります。 renameはpathの代わりにoldとnewを持ちます。sourceはposix、win32、engineのいずれかです。 NotFoundなど意味が確定した場合だけkindが付き、未知のI/O失敗はErr.NONEのまま元情報を保ちます。
失敗しても処理済みの値が残るAPIがあります。復号、クエリ解析、子プロセス、Redisの一括実行では、var value, e := call()で両方を受け取ってください。元の原因はe.cause、分類の検査はe.is(kind)で辿れます。
GD.dataのMessagePack・CBOR・TAR・TOML・YAML・XML・dotenv・front matter生成とPBKDF2は、同期・asyncとも値とErrを返します。変換できない項目を黙って省略しません。
base64_decode(text)とbase32_decode(text)は標準padding付きで復号します。paddingなしは第2引数にtrueを渡します。Base32は大文字を使います。base64url_decode(text)は既定がpaddingなしで、付きの入力は第2引数をfalseにします。同期・asyncとも失敗時の部分byte列を保持します。
GD.http.decode_query(raw)はキーごとの全値をArrayで保持し、最初のErrと返します。req.queryは先頭値、req.query_allは全値です。NULや不正UTF-8を含むキー・値はPackedByteArrayのまま保持します。encode_query()は値のArrayを同じキーで繰り返し、バイト列も扱います。parse_url()のraw_queryは元の表記です。queryを編集してURLを組み直す場合は、先にurl.erase("raw_query")で元の表記を外してください。
型を書かなくても動きます。型を書く場合と細部の規則は次の通りです。
-> int, Errのように成功型一つとErrの二つです。実行時の型はRで、-> Rや省略もできます。Err型の値か成功時のnullに限ります。文字列は末尾に直接返せないのでErr.err(reason)で包みます。var e: Err = ...かvar e := Err.err(...)で型を固定します。型が変わりうるvar e = ...は末尾に使えません。return [1, 0.0, ""], nullのように配列や辞書一つへまとめます。return null, nullはnullを成功値として返します。Rの分解はvar value, e := call()の二つの名前に固定です。var a, b, c := 1, "a", 0.0のように式を並べる宣言は別物で、個数の制限はなく、各名前を対応する式から推論します。新しい名前を一つ以上含めば、その関数で見えている変数(外側のblockの変数を含む)にも代入でき、右辺は代入の前に全て評価します。定数、引数、lambdaが外から取り込んだ変数には代入できません。, Errが無い関数で?を書くとThe "?" operator needs a function returning "R" or "Err".になります。-> int, Errでreturn R.ok("a")と書くとcompile errorです。型が動的なら実行時に検査します。returnします。?で伝播するだけの関数も最後にreturn null, nullを書きます。-> intのように一つだけ書いた関数には、カンマ戻りを書けません。-> int, Errと書きます。return R.ok(値)とreturn R.err(理由)を使います。値と失敗を一つの値として持ち運びたいときはRを使います。okで成否、vで成功値、eで失敗を読みます。 R.ok(値)とR.err(理由, 種類, 部分値)で作ります。Webのhandlerやdatabaseのtransactionは、このRを返す形でも書けます。
func find(items, want):
for item in items:
if item == want:
return R.ok(item)
return R.err("not found: " + want, Err.NOT_FOUND)
func main():
var got = find(["a", "b"], "c")
if not got.ok:
print(got.e.text())
return 1
print(got.v)
return 0
R.ok()の成功値はnullで、intの0にはなりません。途中まで進むI/O APIは、失敗したときも完了した量を部分値としてvに残します。 note()は部分値を保ち、v_or(代替値)は失敗なら代替値を返します。 ?で伝播するとき、部分値が呼出し元の成功型に合わなければ部分値だけを捨て、失敗の理由と種類は保ちます。
HTTP、database、GD.net、fileなどの待つmethodは、普通の関数呼出しとして書けます。 待つのは呼び出したGDScriptだけで、ほかの通信やtimerは進みます。
func main():
var res := GD.http.fetch("https://example.com/")!
print(res.status)
return 0
複数の処理を同時に始めたいときは、末尾が_asyncの版とGD.async.all()を使います。
func main():
var got = await GD.async.all([
GD.http.fetch_async.bind("https://example.com/a"),
GD.http.fetch_async.bind("https://example.com/b"),
])
for result in got:
var res, e := result as R
if e:
print(e.text())
return 1
print(res.status)
return 0
| 入口 | 用途 |
|---|---|
名前_async() | 処理を始めてSignalを返す。awaitすると通常名と同じ結果になる |
GD.async.all(list) | CallableとSignalを受け取り、全部の結果を入力順に返す。無効な入力は対応する欄がエラーになる |
GD.async.spawn(fn) | GDScriptの関数を裏で走らせる。main()が返った後も動く |
GD.async.sleep(sec) | 指定秒だけ待つ |
all()へはSignalよりCallableを渡してください。開始前に完了を購読するため、先に終わった結果を取りこぼしません。
:=で保存したSignalは完了時の型も保持します。異なる型や型不明のSignalへの再代入は拒否します。 実行時に型を決める場合は受け側をSignalと明示し、完了値にも必要な型を付けます(例: var result: R = await pending)。 spawn()はCPU処理を別threadへ移す機能ではありません。長いGDScriptは自動的にほかの処理へ実行権を譲りますが、 native methodの内部は中断しないため、大きな入力を標準moduleへ渡すときは_asyncの版を使います。 待つmethodを呼べるのは、GDScriptから呼ばれた関数の中だけです。Array.map()のcallback、_init()、member変数の初期化、_to_string()などのnative callbackは完了を待てません。callback内にawaitを加えても、nativeの呼出し元は結果を待ちません。非同期の初期化はmain()へ移し、非同期のmap()は非同期関数内のfor、または上のGD.async.all()へ渡すCallableで書きます。
ここまでの知識で、JSONを受けてSQLiteへ保存する小さなAPIを一枚のscriptで作ります。 できあがるのは、入力検査とSQLのparameter bindを備え、権限を絞って起動する開発用serverです。
mkdir notes-api
cd notes-api
次をmain.gdとして保存します。
# Store notes in an embedded database and expose a JSON API.
extends RefCounted
const PORT := 18080 # Development listener port on loopback.
const DB_PATH := "user://notes.sqlite3" # Writable storage isolated per user.
var app := GD.web.app()
var db := GD.database.client()
# Return notes as JSON in newest-first order.
func list_notes(_req):
var got := db.query("SELECT id, title FROM notes ORDER BY id DESC")?
return GD.web.json(got.rows)
# Save a validated title and return the created row.
func add_note(req):
var body := req.valid("body")
var made := db.query(
"INSERT INTO notes(title) VALUES($1) RETURNING id, title",
[body.title]
)?
return GD.web.json(made.rows[0], 201)
# Prepare the database and routes, then listen on loopback.
func main():
db.open({"driver": "sqlite", "path": DB_PATH})?
db.query("CREATE TABLE IF NOT EXISTS notes(id INTEGER PRIMARY KEY, title TEXT NOT NULL)")?
app.route("GET", "/notes", list_notes)
app.route("POST", "/notes", add_note, [GD.web.json_body(GD.web.object_rule({
"title": GD.web.text_rule({"min": 1, "max": 120}),
}))])
app.listen(PORT, "127.0.0.1")?
print("listening on http://127.0.0.1:%d" % PORT)
return 0, null
上から順に読みます。
appはrouter、dbはdatabase接続です。main()が返った後もserverが動き続けられるよう、両方ともscriptの変数として持ちます。main()はまずSQLiteを開き、表を作ります。DB_PATHのuser://は、gdが利用者ごとに用意する書込み領域です。app.route()に、HTTP method、path、そのときに呼ぶ関数(handler)を登録します。GDWebRequestを受け取り、GD.web.json()で返事を作ります。途中の?は失敗をserverへ返し、状態番号500などになります。GD.web.json_body()を付けています。本文がruleに合うときだけhandlerが呼ばれ、通った値がreq.valid("body")に入ります。$1へbindします。文字列連結でSQLを組み立てません。未確認のscriptや外へ公開するserverは、権限を既定で拒否する--strictで実行します。ここでは待受先をloopbackの一つのportに絞ります。 serveはmain()が返った後もprocessを残すcommandで、serverにはこれを使います。
gd check main.gd
gd --strict --allow-net=127.0.0.1:18080 serve main.gd
curl -s -X POST http://127.0.0.1:18080/notes \
-H 'Content-Type: application/json' \
-d '{"title":"gdを試す"}'
curl -s http://127.0.0.1:18080/notes
最初はstatus 201と作成した一件、次は保存済みの配列が返ります。空の題名、120文字を超える題名、 JSONでない本文は400で拒否されます。止めるときは起動した端末でCtrl-Cを押します。
公開環境ではこのprocessをloopbackのままTLS reverse proxyの後ろへ置き、異常終了耐性が必要な保存先は PostgreSQLへ切り替えます。接続情報はsourceへ書かず、許可した環境変数から読みます。
最初は通常実行を使います。--strictは、必要な権限を自分で設計・設定する上級者向けの実行方式です。
| 方式 | 向く場面 | 制限 |
|---|---|---|
| 通常実行 | 信頼したsourceを開発中に動かす | fileもnetworkも制限しない |
--strict | 未確認のscript、公開server | 起動directory配下はread-onlyで、外部の絶対pathは拒否。network、環境変数、子process、native extension、system情報を既定で拒否 |
v0.7.3のmacOS/Linuxでは、--strictでも起動directory外の絶対pathを読めました。
--strictでは、使うものを挙げて起動します。
gd --strict \
--mount store=/srv/app:rw \
--allow-net=db.example.com:5432 \
--allow-env=DATABASE_URL \
main.gd
| 指定 | 許すもの |
|---|---|
--mount name=path:r / --mount name=path:rw | 名前付きdirectoryのreadまたはread/write |
--allow-net=host:port,... | 接続と待受。値を省くと全て |
--allow-env=name,... | 環境変数 |
--allow-run=command,... | 子process |
--allow-ext=path,... | scriptが実行中に読むnative extension |
--allow-sys=item,... | 機種とsystem情報 |
--deny-* | 対応するallowより優先する拒否 |
-A | file以外を全て許す。開発中の一時的な利用向け |
scriptから見えるfileの置き場は次の4種類です。置き場の名前をpathの先頭に書くか、絶対pathをそのまま書きます。
| 書き方 | 指す場所 | strictでの扱い |
|---|---|---|
res://a.txt | scriptを起動したdirectory | read-only |
user://a.txt | gdが利用者ごとに用意する書込み領域 | read/write |
store://a.txt | --mount store=/srv/app:rwで付けた名前 | 指定した権限 |
/etc/hosts | 機械上のその場所 | 起動directoryの外なら拒否 |
res://より上へ遡る相対pathは、どちらの方式でも拒否します。--strictの暗黙read範囲は起動時の作業directoryです。entry scriptが子directoryにあっても基準は変わりません。配下の絶対pathはres://と同じread-only検査を通し、親・兄弟directoryは拒否します。リンクでも外へ出られません。外部は明示的にmountし、store://などの名前で使います。-Aでもfileの範囲は広がりません。--mountと絶対pathはLinuxとmacOS用です。Windowsでは拒否するので、fileはres://かuser://へ置いてください。-です。res、user、uid、pipe、local、libgodot、tcp、unix、http、https、file、data、cacheは予約済みで選べません。--allow-netのlocalhost:8080は、同じportのIPv4 loopback 127.0.0.0/8とIPv6 ::1も表します。*.example.com:443はその下位hostを許します。gd serveはSceneTreeを作らない常駐用の実行方式です。通信、timer、await、自作Signal、GD.async.sleep()、 ツリー外Nodeのqueue_free()は動きます。仕事が無ければ次の期限か通信の通知まで眠るため、周期の調整は要りません。
| やりたいこと | 方法 |
|---|---|
| Web serverや定期処理を常駐させる | gd serve main.gd |
Nodeの_process()、_physics_process()、process_frame、SceneTreeTimer、高水準multiplayerを使う | serveを付けない通常実行 |
| SceneTreeやMainLoopを継承したscriptを動かす | serveを付けない通常実行 |
| SceneTreeが紛れ込んでいないか開発中に調べる | gd --no-scene-tree --allow-net serve app.gd |
| 複数processで待ち受ける | --workers=<n>または--workers=auto。nは1以上の整数 |
serveではNodeを継承しただけのscriptはツリーへ追加されません。 --no-scene-treeはSceneTreeが作られた時点で診断を出し、終了code 1で止まります。--watchと--workersの子にも引き継がれます。 通常実行は暗黙にSceneTreeを作るため、この旗を付けると失敗します。
低水準の通信にはGD.netを使います。Godot本家の低水準型も互換用に残っていますが、新しいcodeはGD.netで書きます。
func echo():
var listener := GD.net.listen_tcp("127.0.0.1", 8080)?
var conn := listener.accept()?
var data := conn.read(65536)?
conn.write(data)?
conn.close()
return 0, null
GDTCPConnのreadとwriteは別々の列なので、複数のGDScriptから同時に呼べます。close()は未完了のread/writeをErr.INTERRUPTEDで起こします。listenerの受付待ちも同じです。timeoutは延びません。TLSはGD.net.dial_tls(host, port, opts)で開きます。既定で証明書の鎖とhost名を検証し、失敗しても平文へ戻りません。 戻り値はTCPと同じGDTCPConnです。
opts | 意味 |
|---|---|
timeout | 接続と握手を合わせた期限の秒 |
ca_file | 私設CA。環境変数の設定より優先する |
cert_file、key_file | client認証。許可されたmount内のfileを対で指定する |
server_name | 証明書を照合する宛名を接続先と別にする |
next_protos | ALPN名の配列。1名は1–255 byte、全体で65535 byteまで |
insecure_skip_verify | 検証を省く。検証不要と判断できる試験時だけ使う |
交渉結果はconnection_state()のnegotiated_protocolとversionで読めます。TLS1.2は771、TLS1.3は772です。
信頼するCAは、未指定ならmacOSとWindowsではOSの信頼設定、Linuxではsystem CA bundleです。 起動前にSSL_CERT_FILEまたはSSL_CERT_DIRを設定すると、どのOSでも指定したCAを使います。 directoryの区切りはUnixで:、Windowsで;です。
serverがclient証明書を求めるときは、app.listen_tls(port, cert, key, host, opts)のoptsへclient_ca(信頼CA bundle)とclient_authを渡します。 client_caが未指定ならsystemの信頼設定を使います。
client_auth | 動作 |
|---|---|
none | 証明書を要求しない |
request | 任意提示。検証しない |
require | 提示だけ必須。検証しない |
verify_if_given | 提示された場合だけ検証する |
require_and_verify | 検証済みの証明書を必須にする |
GD.net.listen_udp()はGDUDPPacketConnを返します。read_from()はdata、host、port、truncatedを持つ辞書を返します。 write_to()のhostにはGD.net.resolve()で解決したIP addressを渡します。packetは結合されません。 buffer=0(既定)はOSの受信bufferをそのまま使い、正の値を指定した場合だけ変更を要求します。
GD.net.resolve()はOSが選んだ先頭のaddressを一つ返します。名前のcacheは持ちません。 GD.net.local_addresses()は機械のaddress一覧を返し、空の一覧とOSの失敗を区別します。 失敗のe.infoにはsyscall、source、source_codeが入ります。
GD.fileでfileの読み書きとpath操作をします。起動したdirectoryがres://で、絶対pathも書けます。 strictで外部directoryへ書くときは--mount store=/srv/app:rwで付けた名前をstore://users.csvのように書きます。
func main():
var rows := GD.data.csv_objects(GD.file.read_text("store://users.csv")?)?
GD.file.write_text("store://users.json", JSON.stringify(rows))?
return 0, null
fileの操作は、通常名でも呼び出したGDScriptだけを待たせます。Web serverのhandlerから読んでも他のrequestは進みます。 複数の操作を同時に始めるときだけ、末尾が_asyncの版を使います。
func handler(_req):
var body := GD.file.read_text("store://big.json")
if not body.ok:
return GD.web.text("読めません", 500)
return GD.web.text(body.v)
compileで同梱したfileも、同じAPIで読取、列挙、static配信ができます。
全量をmemoryへ置かず読むときはGD.file.open(path, mode)でGDFileStreamを開きます。 modeはread、write、append、read_writeで、使い終えたらclose()を呼びます。
| method | 動作 |
|---|---|
read(max) | 最大max byteを返す。少なく返ることがある。空の成功値がEOF |
write(bytes) | 全て書いてbyte数を返す。途中で失敗してもR.vに書込み済みbyte数が残る |
同じstreamの操作は受付順、別のstreamは並列に進みます。appendはseekの後も常に末尾へ書きます。 read_bytes()も途中で失敗したときは取得済みのbyte列をR.vに残します。 read_text()はStringに収まらない大きさの入力を切り詰めずエラーにするので、大きなfileはbyte列かstreamで扱います。
複数のprocessが同じfileを更新するときはGD.file.replace_text(path, old, body)を使います。 読み取ったoldと現在の内容が同じときだけ置き換えるため、並行編集を黙って上書きしません。新規作成ではoldにnullを渡します。
| 用途 | 入口 |
|---|---|
CSV、TOML、YAML、JSONL、JSONC、XML、INI、TAR、front matter、.envのfileを読む | GD.file.read_csv(path)など |
| 同じ形式のmemory上の変換、JSON、codec、hash、HMAC、PBKDF2、HKDF、byte列 | GD.data |
| UUIDとULID | GD.id |
| 日時の変換と計算 | GD.time |
| 文字の整形と比較 | GD.text |
| HTML entity、tag、gdhtml(Mustache構文のマイクロテンプレート) | GD.html |
| flagと環境変数 | GD.cli |
| 配列と辞書の操作 | GD.collection |
| 数学の特殊値とbit演算 | GD.math |
| versionの比較 | GD.version |
| 端末とfileへのlog | GD.log |
| testの検査 | GD.test |
環境変数と.envは、読む対象で入口が分かれます。
| 読む対象 | 入口 |
|---|---|
| processの環境変数 | GD.cli.env(name, fallback)、GD.cli.require_env(name)。strictでは--allow-envが要る |
.env file | GD.file.read_env(path)。fileを読んで辞書にする |
| dotenv形式の文字列 | GD.data.env(src)とGD.data.to_env(data)。memory上で辞書と変換する |
memory上の変換は通常名がその場で計算し、_asyncの版は別のthreadで計算します。大きな入力には_asyncを使います。 正確な一覧はgd doc GD.fileとgd doc GD.dataで引けます。形式ごとの検査と上限は、APIリファレンスの各入口の説明にあります。
GD.collectionのCallableを使う操作は、約1 msごとにほかの処理へ実行権を譲ります。 GD.logの各呼出しは書込み完了まで待ち、本文を切り捨てません。失敗は戻り値のRで確認でき、GD.log.flush()でそれ以前の出力完了を待てます。
値はGD.data.json_encode(value)でJSON byte列にし、外から受けたbyte列はGD.data.json_decode(bytes)で読みます。 どちらも成功値とErrを返します。GDWebRequest.json()、GDHTTPResponse.json()、JSONLの各行も同じ規則です。
intのまま戻り、小数、指数、範囲外だけがfloatになります。文字列とキーの\u0000は保持します。{"deterministic": true}を指定します。deterministicとescape_htmlがbool、max_bytesとmax_depthがintです。不正な型はErr.INVALID_DATA、上限超過はErr.LIMITEDです。json_encode_async()が終わるまで、入力のArray・Dictionaryとその子要素を変更しないでください。設定の辞書は開始時に複製されます。GD.dataはSHA-1、SHA-224/256/384/512、SHA3-224/256/384/512を返します。HMAC、PBKDF2、HKDFでは sha1、sha224、sha256、sha384、sha512、sha3-224、sha3-256、sha3-384、sha3-512から方式を選べます。 PBKDF2とHKDFは出力長を指定でき、不正な方式や表現範囲外の設定はRの失敗として返します。PBKDF2の反復回数が1以下なら1回として計算します。
GD.async.set_max_threads(max)はgdが管理するOS threadの上限を設定し、以前の値を返します。既定は10000です。 上限を越えるとprocessが終了します。現在数より小さい値への変更も終了します。外部libraryが直接作るthreadは数えません。
同じディレクトリにmain.gdとindex.htmlを作ります。一方のrouteでHTMLテンプレートに挨拶を埋め込み、もう一方で同じデータをJSONとして返します。
main.gd:
# Serve a greeting as an HTML page and a JSON response.
var app := GD.web.app()
# Build the shared response data from the requested name.
func greeting(req):
var name := req.query.get("name", "world")
if not name is String:
return null, Err.err("name must be text", Err.INVALID_DATA)
return {"name": name, "message": "Hello, " + name + "!"}, null
# Render the page with escaped template values.
func home(req):
return GD.web.view("index.html", greeting(req)?)
# Return the same data as JSON.
func hello(req):
return GD.web.json(greeting(req)?)
# Listen locally, using an optional port argument.
func main(args):
var port := 8080 if args.is_empty() else int(args[0])
app.route("GET", "/", home)
app.route("GET", "/api/hello", hello)
app.listen(port, "127.0.0.1")!
print("http://127.0.0.1:", app.port())
return 0
index.html:
<!doctype html>
<!-- Display a greeting and let visitors choose the name. -->
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{message}}</title>
<style>
body { max-width: 36rem; margin: 4rem auto; padding: 0 1rem; font-family: system-ui, sans-serif; line-height: 1.6; }
input, button { padding: .6rem; font: inherit; max-width: 100%; box-sizing: border-box; }
</style>
</head>
<body>
<h1>{{message}}</h1>
<form action="/" method="get">
<label>Your name <input name="name" value="{{name}}"></label>
<button type="submit">Say hello</button>
</form>
<p><a href="/api/hello?name={{name}}">View the JSON response</a></p>
</body>
</html>
そのディレクトリで実行します。
gd serve main.gd
http://127.0.0.1:8080/?name=Aliceを開くと、{{message}}へ「Hello, Alice!」を埋め込みます。/api/hello?name=Aliceは{"name":"Alice","message":"Hello, Alice!"}を返します。テンプレートの値はHTMLの文脈に応じてエスケープされます。
serveはmain()が返ったあとも待受けを続けます。終了はCtrl+Cです。
route(method, pattern, handler)でHTTP methodとpathをhandlerへ結びます。patternの:nameはreq.params["name"]に入ります。 handlerはGDWebRequestを受け取ります。本文はreq.read()、bytes()、text()、json()、save()で必要な分だけ読みます。 HTMLのformから届く本文はGD.http.decode_query(req.text()?)で辞書にします。
reqは受信した要求です。失敗する操作はvar value, e := call()で確認してreturn null, eを返すか、call()?で伝播します。return 応答, nullは、すべての失敗を確認した成功経路だけで使います。
GD.web.view()とGD.web.json()は応答, Errを返します。handlerではreturn GD.web.json(data)のように結果をそのまま返せます。テンプレートの読込み・描画やJSON変換の失敗は、元のErrを保持してapp.on_error(req, e)へ届きます。middleware、after、自動JSON変換も同じ経路です。エラーハンドラ自身が失敗した場合は500で終了し、呼び直しません。streamの送信開始後はHTTP状態を変更できないため、writerのErrを処理します。
handlerが返した値が返事になります。
| 返した値 | 返事 |
|---|---|
GD.web.html(body)、GD.web.view(path, data) | HTML |
GD.web.json(data) | JSON |
GD.web.text(body)、GD.web.bytes(body, type) | text、任意の媒体型 |
GD.web.stream(producer) | 少しずつ書く本文。「Webの運用と高度な機能」を参照 |
GD.web.redirect(to) | 302。toは同じsite内のpathに限り、他所へ送るときはawayをtrueにする |
GD.web.not_found() | 404 |
| 文字列 | text/plainの200 |
bodyを持たない辞書 | JSONの200 |
null | 204 |
失敗のRまたはErr | 種類に応じた状態番号。INVALID_DATA → 400、UNAUTHENTICATED → 401、PERMISSION_DENIED → 403、NOT_FOUND → 404、LIMITED → 429、UNSUPPORTED → 501、TIMED_OUT → 504、他は500(いずれもErrの定数) |
ハンドラとmiddlewareは、awaitの後も含めてSignalを返せます。完了時の引数が0個ならnull、1個ならその値、複数ならArrayとして処理を再開します。利用できないSignalはエラーハンドラへ渡します。要求終了やapp停止時には待機中の購読を解除します。
GD.web.header(reply, name, value)で返事にheaderを足します。GD.web.guard(reply)でX-Content-Type-Options、X-Frame-Options、Content-Security-Policyなどの防御headerをまとめて足します。app.show_errors(true)とした間だけ出します。req.pathは各segmentを一度だけ復号したpath、req.targetはpercent escapeとqueryを保った原文です。%2Fは経路の区切りになりません。PackedByteArrayとして保持するため、文字列として使う前に型を検査してください。req.queryは壊れた組を省略します。正常な組とErrを両方受け取るにはGD.http.decode_query()を使います。GD.web.json()やview()へ渡した値は、返事を送り終えるまで変更しないでください。routerには次も登録できます。
| 登録 | 用途 |
|---|---|
app.static("/assets", "res://public") | prefix以下のGETをdirectoryのfileで返す。媒体型は拡張子から決め、directoryの外は返さない。/のindexはrouteで書く |
app.group("/api", [middleware]) | 共通prefixとmiddlewareを持つroute group。返り値にroute()とuse()がある |
app.fallback(handler) | どのrouteにも一致しない要求。404頁をここで返す |
app.on_error(handler) | handlerが失敗を返したときの返事 |
app.after(handler) | 返事を送る前の加工。func(req, reply)で受け、headerを足して返す |
middlewareは、handlerの前に呼ばれる関数です。GDWebRequestを受け取り、nullを返すと次へ進み、返事を返すとそこで止まります。 handle(req)を持つobjectも使えます。後段へ渡す値はreq.keep(name, value)で置き、req.kept(name)で読みます。
| 登録 | 掛かる範囲 |
|---|---|
app.pre(mw) | route選択の前。全要求 |
app.use(mw) | route選択の後。全route。req.paramsを読める |
group.use(mw) | そのgroupのroute |
app.route(method, pattern, handler, [mw]) | そのrouteだけ |
入力検査もmiddlewareです。GD.web.json_body(rule)、GD.web.query(rule)、GD.web.params(rule)が本文、query、pathの値を検査し、 通った値をreq.valid("body")、req.valid("query")、req.valid("params")に入れます。 ruleはGD.web.text_rule()、int_rule()、number_rule()、bool_rule()、list_rule()、object_rule()で組み、 GD.web.optional()とGD.web.one_of()で省略と選択肢を表します。queryとparamsの値は文字列なのでtext_rule()で検査し、必要ならto_int()で変換します。
var app := GD.web.app()
func show(req):
var params := req.valid("params")
return GD.web.json({"id": params.id})
func main():
app.route("GET", "/posts/:id", show, [GD.web.params(GD.web.object_rule({"id": GD.web.text_rule({"min": 1, "max": 20})}))])
app.listen(8080)!
return 0
組込みのmiddlewareはGD.web.sessions()、GD.web.csrf()、GD.web.jwt()、GD.web.rate()です。認証の節で使います。
頁が増えてきたらHTMLを雛形fileへ出し、GD.web.view(path, data)で描画します。 雛形はgdhtml(Mustache構文のマイクロテンプレート)で、{{name}}、{{{html}}}、#if、#unless、#each、 #with、else、{{> header}}を扱います。views/page.htmlから{{> header}}を使うと、 同じ階層のviews/partials/header.htmlを読みます。
<!-- views/page.html -->
{{> header}}
<main><h1>{{title}}</h1></main>
<!-- views/partials/header.html -->
<header><a href="/">gd app</a></header>
func page(_req):
return GD.web.view("views/page.html", {"title": "Top"})
二重括弧の値は、置かれた位置から文脈を判定してescapeします。雛形の作者を信頼し、差し込む値を信頼しない前提です。
| 文脈 | 扱い |
|---|---|
| HTML本文、引用・未引用属性、属性名 | HTML escape |
href="{{url}}" | 相対URLとhttp、https、mailtoを通す。data-hrefも同じ |
href="/work/{{path}}"、href="/?q={{query}}" | pathは区切りを保って正規化、queryはpercent escape |
onclick、script本文 | JSON化し、application/jsonでも</script>が構造を壊さない形にする |
style | 安全な単独CSS値とCSS文字列・URLを通す |
| 危険なURL、srcset、CSS値、属性名 | 画面全体を失敗させず、#ZgdunsafeZまたはZgdunsafeZへ置き換える |
{{{html}}}はescapeしない唯一の入口で、HTML本文以外では使えません。固定HTMLか十分に検査済みの値だけを渡してください。eachの反復が異なる文脈で終わる雛形、閉じていないtag、曖昧なURLやJavaScript文脈は描画の失敗になります。同じ雛形を何度も描画するserverでは、起動時にGD.html.template(source, partials)?で一度だけ解析し、 返った値のexecute(data)?を各要求から呼びます。解析結果は不変で、複数の要求から同時に使えます。 execute_bytes(data)?はUTF-8のbyte列を直接作るので、GD.web.bytes(body, "text/html; charset=utf-8")でそのまま返せます。
loginの状態はGD.web.sessions()で持ちます。issue(value)でsession IDを作り、cookie(id)の値をSet-Cookieで返します。 同じstoreをmiddlewareとして付けたrouteでは、cookieのIDに対応する値がreq.kept("user")に入り、無ければ401になります。
var app := GD.web.app()
var sessions := GD.web.sessions()
func login(req):
var form := GD.http.decode_query(req.text()?)?
var user := str(form.get("user", [""])[0])
if user.is_empty():
return GD.web.text("user is required", 400), null
var reply := GD.web.redirect("/me")
return GD.web.header(reply, "Set-Cookie", sessions.cookie(sessions.issue(user)?)), null
func me(req):
return GD.web.text("hello, " + str(req.kept("user"))), null
func main():
app.route("POST", "/login", login)
app.route("GET", "/me", me, [sessions])
app.listen(8080)!
return 0
cookie(id)はSecureとHttpOnly付きで作ります。TLSなしの開発中に届かない場合はcookie(id, false)にします。 logoutはdrop(id)とclear_cookie()で行います。sessionはprocess内で持つため、--workersで複数processにするときはJWTか外部の保存先を使います。
cookieで認証する書き込み経路にはGD.web.csrf()を付けます。GET、HEAD、OPTIONS以外はBrowserの Sec-Fetch-Site: same-originが必要です。古いBrowserやBrowser以外のclientも受ける場合に GD.web.csrf({"allow_missing": true})を選び、別のtoken検証を組み合わせてください。
var app := GD.web.app()
var sessions := GD.web.sessions()
func save_email(_r):
return "saved"
func main():
app.route("POST", "/account/email", save_email, [GD.web.csrf(), sessions])
app.listen(8080)!
return 0
JWTをlogin sessionに使う場合は、password変更やlogoutで既発行tokenを失効させます。 checkは署名と標準claimの検証後に呼ばれ、trueを返したときだけ認証を通します。 例えばtokenへ利用者のverを入れ、password変更時に保存済みversionを増やします。 複数workerでは各processの辞書でなく、共有DBから同期したcacheなどで照合します。
func token_auth(key, versions):
return GD.web.jwt(key, {"check": func(claims):
return versions.get(claims.get("sub", ""), -1) == claims.get("ver", -2)
})
reverse proxyの後ろでIP単位に制限するときは、そのproxyのIPまたはCIDRをtrusted_proxiesへ明示します。 gdはX-Forwarded-Forの右端から信頼済みproxyを除き、最初の未信頼IPをkeyにします。 未指定のときと未信頼の接続元からのX-Forwarded-Forは無視するため、client自身によるIP偽装を許しません。 IPv4とIPv4-mapped IPv6は別物として照合するので、mapped addressを信頼する場合はIPv6のCIDRを指定します。zone付きのproxy設定は拒否します。
var per_ip := GD.web.rate({"limit": 60, "trusted_proxies": ["127.0.0.1", "172.18.0.0/16"]})
終了待ちはapp.shutdown(context)を使います。新規受付とkeep-aliveを止め、処理中requestの完了を待ちます。 期限を越えた場合はErr.TIMED_OUTを返しますが、処理中requestは強制終了しません。 直ちに全接続を閉じる必要がある場合にapp.stop()を使います。
func close(app):
var context := GD.async.context().with_timeout(10.0)
var stopped := app.shutdown(context)
if not stopped.ok:
app.stop()
handlerではreq.contextからrequestの完了と切断を受け取れます。 with_cancel()とwith_timeout()は親を変更せず子のcontextを返し、親の打ち切りは子へ伝わります。 HTTP、database、processなどの待ちを打ち切れるようにするには、contextを先頭に渡してwith_context()で包みます。 処理が先に終わればその結果を返し、contextが先に終われば処理を取り消します。
func load(req, db):
var result = await GD.async.with_context(req.context, db.query_async("SELECT * FROM posts"))
return result
大きな本文や長いhandlerを扱うときは、待受前にlimits()で上限を明示します。
func main():
var limited_app := GD.web.app()
limited_app.limits({"header_bytes": 1048576, "header_values": 500, "header_timeout": 15.0, "body_timeout": 10.0, "job_timeout": 30.0, "jobs": 128})
return 0
1 GBのZIPを受ける場合は要求ごとの上限を置き、書込み可能なmountへ逐次保存します。
func main():
var app := GD.web.app()
app.limits({"body_timeout": 600.0})
app.route("POST", "/upload", func(req):
req.limit(1000 * 1000 * 1000)?
req.save("uploads://package.zip")?
return GD.web.text("saved")
)
return 0 if app.listen(8080, "127.0.0.1").ok else 1
gd --strict --allow-net=127.0.0.1:8080 --mount=uploads=/srv/uploads:rw serve main.gd
本文とmemoryの扱いは次の通りです。
| 対象 | 扱い |
|---|---|
| request body | 既定上限なし。見出しの後ですぐhandlerを呼び、本文はhandlerが読んだ分だけ接続から読む |
read()、save() | 本文を逐次読む。read()の空の成功値はEOF。save()は本文全体をmemoryへ置かない |
bytes()、text()、json() | 残りの本文全体をmemoryへ読む。大容量にはsave()を使う。text()はStringに収まる大きさまで |
req.limit(bytes) | 要求ごとの本文上限。超過は本文を読んだ操作へ失敗として返る |
| request header | 既定1 MiB。行数はheader_valuesを指定した場合だけ制限。trailerは4096 byte |
| HTTP clientのresponse header | 10 MiBまで |
| 遅い接続 | その接続だけを待たせ、別の接続を巻き込まない |
| 返事の追加header | 件数と全体量の固定上限なし。不正な名前と値だけを落とす |
| sessionとrate limit | process内で共有し、--workers間では共有しない。共有が必要ならDBなど外部の保存先を使う |
| session値 | 文字列と整数の識別子。保持件数はtotalとper_userで設定 |
| HS256 JWT | keyは32 byte以上。JSONと署名の妥当性を検査 |
| rate limitのkey | 保持件数はkeysで設定 |
| HTTP状態番号 | 100..999。範囲外は500として送る |
| port | 待受とGD.net.free_port()の探索開始は0を許し、接続先とis_free()は1..65535 |
| 問い合わせ文字列 | GD.http.decode_query()は素のsemicolonと壊れたpercent escapeを失敗として返す |
GD.web.stream(producer, length=-1, type="application/octet-stream", status=200)は、producer(writer)がGDWebWriterへ書いた分だけ送ります。 全量をmemoryに結合しません。producerの中でawaitでき、voidまたはRを返して終わります。
GDWebWriter | 動作 |
|---|---|
write(data, offset=0, count=-1) | byte列の範囲を送り、受け付けたbyte数を返す。送信が詰まっていれば進むまで待つ |
write_text(text, offset=0, count=-1) | 文字列の範囲をUTF-8で送る。offsetとcountの単位は文字、結果の単位はbyte |
flush() | それまでのwriteの送信完了を待つ。切断はここのエラーとreq.contextの取消でわかる |
lengthは送るbyte数です。宣言と実際が合わないと接続を閉じます。不明長(-1)はHTTP/2でDATA frame、HTTP/1.1でchunked、HTTP/1.0で接続終了が終端になります。req.contextの取消を確認してください。HTTPSはapp.listen_tls(8443, "cert://chain.pem", "cert://key.pem", "127.0.0.1")で開始し、結果のRを確認します。 証明書のdirectoryは--mount cert=/path/to/certs:rで読取専用にします。PEMの鎖と暗号化されていない秘密鍵を渡します。 鍵の検証に失敗したときはportを開きません。
TLS 1.2と1.3に対応し、ALPNでHTTP/2とHTTP/1.1を選びます。HTTP/2の各streamは独立に進み、一つの取消は他のstreamを閉じません。 header_timeoutは未完了の握手にも適用されます。client証明書の要求は「TCPとUDP」のTLSの表を参照してください。
GD.data.gzip_writer(writer, level=-1)は、書いたbyte列をgzipにして下のwriterへ渡すGDGzipWriterを作ります。 下のwriterにはGDFileStream、TCP接続、GDWebWriterを使えます。全量をmemoryに貯めません。
| 項目 | 内容 |
|---|---|
| method | write(bytes)、flush()、close()、reset(writer)。どれもRを返す |
level | -2(Huffmanのみ)、-1(既定)、0..9 |
close() | gzipの末尾を完成する。下のwriterは閉じない |
reset(writer) | エラーを消し、同じlevelで使い回す |
header | name、comment(NULを含まないLatin-1)、extra(65535 byteまで)、mod_time(Unix秒)、os(既定255)。最初の書込みより前に設定する |
HTTPで返すときはGD.web.header(GD.web.stream(producer), "Content-Encoding", "gzip")を返し、producerの中で圧縮器を作って書き、close()の結果を返します。 Accept-Encodingの確認とVaryの設定は呼出側で行います。秘密情報と外部入力を一緒に圧縮せず、圧縮済みの本文や部分応答には使わないでください。
IPv6のlocalhostだけで待ち受けるにはapp.listen(8080, "::1")!を指定します。strictでは--allow-net=[::1]:8080、接続先はhttp://[::1]:8080/です。 ::1と127.0.0.1は別の待受で、全interfaceを示す::とも異なります。
空きportをOSに選ばせる場合はapp.listen(0)の直後にapp.port()を読みます。待受けを保持したまま番号を得るので、他のprocessに取られません。 strictでは選ばれるportを事前に限定できないため、--allow-net=127.0.0.1のようにhost全体を許可します。 GD.net.free_port()とis_free()は診断用の瞬間的な確認で、その番号を確保する機能ではありません。
GD.http.fetch()とGD.webの各関数に辞書で渡す設定と、その既定値です。時間は秒、大きさはbyteです。
| 入口 | 設定と既定 | 意味 |
|---|---|---|
GD.http.fetch | method="GET", headers={}, body=null | HTTP method、送信header、送信body |
| 同上 | timeout=30.0, max_body=0 | 要求全体の秒と応答bodyのbyte。0は上限なし |
| 同上 | save="", sha256="" | 2xx bodyをsaveへ逐次保存し、返却bodyは空。sha256はsave必須の64桁hexで、一致した完了fileだけを置く |
| 同上 | authority="host:port" | CONNECTだけのrequest target |
GD.cli.run | timeout=0.0, output=true | 子processを諦める秒と、出力を集めるか |
GDWebApp.limits | jobs=0, job_timeout=0.0 | 保持する非同期handler数と秒。0は無制限 |
| 同上 | header_timeout=0.0, body_timeout=0.0 | request header/bodyを受け終える秒。0は無期限 |
| 同上 | header_bytes=1048576, header_values=2147483647 | request lineを含むheader byteと、header行数 |
GD.web.jwt_sign | ttl=900 | iat/expを補う秒。0は自動付与しない |
GD.web.jwt / jwt_verify | leeway=0.0, require_exp=true | 時刻許容秒とexp必須化 |
| 同上 | iss="", aud="", keep="jwt" | 空でない場合のissuer/audience一致と保持名 |
| 同上 | check=Callable() | 署名検証後にclaimを受け取る失効判定。指定時は真だけを許可 |
GD.web.sessions | total=1024, per_user=3 | process内の全session数と同一user数 |
| 同上 | idle=1800, life=43200 | 無操作と最大生存の秒 |
| 同上 | cookie="sid", keep="user" | Cookie名とrequest内の保持名。Cookie名はASCIIのtoken文字 |
GD.web.rate | limit=60, window=60.0 | keyごとの回数と固定窓の秒 |
| 同上 | keys=10000, key=Callable() | process内で保持するkey数とkey選択関数 |
| 同上 | trusted_proxies=PackedStringArray() | 転送元IPを信頼するproxyのIPまたはCIDR |
GD.web.csrf | allow_missing=false | 状態変更でFetch Metadataが無いclientを許すか |
GD.web.text_rule | min=0, max=4096 | textの文字数 |
GD.web.int_rule | min=-9223372036854775808, max=9223372036854775807 | 64 bit整数の範囲 |
GD.web.number_rule | min=-1e308, max=1e308 | 有限浮動小数の範囲 |
GD.web.list_rule | min=0, max=1024 | 要素数 |
GD.web.object_rule | extra=false | 未定義fieldを残すか |
GDWebApp.limitsは表にある6つの設定名だけを受け、綴り違いやbody_limitを誤りとして拒否します。
数値の設定が受け付ける範囲です。範囲外の値は設定時に失敗します。
| 設定 | 受理範囲 |
|---|---|
jobs | 0..2147483647。0は無制限 |
header_values, sessionのtotal/per_user, rateのlimit/keys | 1..2147483647 |
job_timeout, header_timeout, body_timeout | 有限の0..9223372036.854776秒。0は無期限 |
sessionのidle/life | 1..9223372036秒 |
header_bytes | 1..2147479551 byte。本文とは別 |
req.limit、GD.http.fetch.max_body | 0..9223372036854775807 byte。max_bodyの0は上限なし |
ttl | 0以上 |
leeway | 有限の0以上 |
GD.database.client()が返すclientは、SQLiteとPostgreSQLを同じ書き方で扱います。 local開発は組込みSQLite、本番はPostgreSQLという切り替えは、open()に渡すdriverで行います。
func main():
var local := GD.cli.env("DB_DRIVER", "sqlite") == "sqlite"
var db := GD.database.client()
db.open({
"driver": "sqlite" if local else "postgres",
"path": "user://app.sqlite3",
"host": "127.0.0.1",
"database": "app",
"user": "app",
"password": GD.cli.env("PGPASSWORD", ""),
})?
db.query("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)")?
db.query("INSERT INTO users (id, name) VALUES ($1, $2) ON CONFLICT (id) DO NOTHING", [1, "ada"])?
var out := db.query("SELECT id, name FROM users WHERE id=$1", [1])?
print(out.rows[0].name)
db.close()
return 0, null
表の作成もINSERTもSELECTもquery()一つで送ります。受け取るのはcolumns、rows、tagを持つ辞書で、 rowsは列名を鍵にした辞書の配列です。上の例ならout.rows[0].nameがadaになります。 SQLの値は$1、$2の順でbindし、両driverで同じ書き方です。SQLは変換しないため、両方で通るSQLを使います。
| method | 用途 |
|---|---|
query(sql, args) | 結果を全部集めて返す |
query_row(sql, args) | 先頭1行だけ返す。行が無ければErr.NOT_FOUND |
query_rows(sql, args) | GDDatabaseRowsを開き、1行ずつ読む。大量の結果向き |
stats() | 接続数、使用中、空き、待ち回数、待ち時間、接続を閉じた理由別の累積数 |
query_rows()はwhile rows.next()で進め、scan()で列名付きの辞書、values()で列順の配列を得ます。 next()がfalseになったらerr()を調べます。途中で止める場合はclose()を呼びます。
func list_users(db):
var rows := db.query_rows("SELECT id, name FROM users ORDER BY id")?
while rows.next():
var user := rows.scan()?
print(user.id, " ", user.name)
if rows.err() != null:
return R.err(rows.err())
return R.ok()
制約違反ではresult.e.infoに機械判定用の情報が入ります。violationはduplicate、not_null、foreign_keyのいずれか、 columnsは関係する列名です。PostgreSQLではcode、table、constraintもserverが返した場合に入ります。 値そのものはinfoへ残しません。SQLite自身が報告した失敗ではsource="sqlite"と拡張source_codeを保ちます。 SQLiteのforeign key文面には列名が無いため、その場合のcolumnsは空です。
func save(db):
var saved := db.query(
"INSERT INTO users(id,name) VALUES($1,$2)",
[1, "ada"])
if not saved.ok and saved.e.info.get("violation") == "duplicate":
var columns := saved.e.info.get("columns", PackedStringArray())
print("重複した列: ", columns)
複数の更新を一つの成否にするときはtransaction()を使います。callbackには同じ接続へ固定された GDDatabaseTxが渡ります。callbackが成功のRを返すとcommitし、失敗のRを返すとrollbackします。
func save(db, id, title):
return db.transaction(func(tx):
tx.query("INSERT INTO posts(id,title) VALUES($1,$2)", [id, title])?
tx.query("UPDATE counters SET value=value+1 WHERE name='posts'")?
return R.ok(id)
)
txを使い、必ずRを返してください。transaction中は元のclientのquery()と二重transactionを拒否します。txも新しいSQLを受け付けません。schemaを順番に適用するときは、SQLをsemicolonで分割せず、statementの配列をmigrate()へ渡します。 途中の一文が失敗すると全体をrollbackし、成功時は適用した文の数を返します。 versionとchecksumはapplication側で管理します。
func migrate(db):
return db.migrate([
"CREATE TABLE posts(id INTEGER PRIMARY KEY, title TEXT NOT NULL)",
"CREATE INDEX posts_title ON posts(title)",
])
| 項目 | SQLite | PostgreSQL |
|---|---|---|
| 向く用途 | local開発、単一process | 本番、異常終了耐性、複数worker |
| 接続 | clientごとに一つ。journalと一時表はmemoryに置く | 既定max(4, CPU数)までのpool。pool=25のように最大数を指定できる |
| 追加の入口 | 短い処理をその場で行うGD.database.sqlite.open() | まとめ送り、配列、JSONBを使うGD.database.postgres |
| 注意 | 既存の-journal、-wal、-shmがあるdatabaseは、通常のSQLiteで回復またはcheckpointしてから開く | loopback以外のhostではTLS証明書とhost名を既定で検証。loopbackはTLSなしが既定 |
GD.database.postgres.client()とGD.database.redis.client()のopen()はopen(host, port, opts)の形で、接続先を引数に取ります。
同じclientのquery()は受付順に実行します。別のclientは並行に進みますが、同じdatabase fileへの書込みはSQLiteのlockに従います。 GD.database.sqlite.open()が返すGDSQLiteDBとGDSQLiteStatementは、呼出し元でそのまま実行する同期APIです。 短い処理だけに使い、同時利用はしないでください。並行処理にはGDDatabaseClientを使います。
query()は使用中の接続へも続けて送ります(pipeline)。同じ接続では送った順に結果が返ります。query_rows()は接続を一本専有します。接続固有の状態を使う処理は、BEGINを単発で送らずtransaction APIを使ってください。query_many、fetch_many、exec_manyを使います。stats()のwait_countは接続の取得待ちの回数で、pipeline内の応答待ちは含みません。auth="scram"またはauth="md5"で固定できます。MD5は旧server用です。平文passwordは明示した許可が要ります。bool[]、int[]、bigint[]、text[]は要素の型、null、多次元構造を保ちます。下限を明示した配列は元の文字列を返します。GETの値なしはErr.NOT_FOUNDとe.info.code == "nil"で返します。MGETの配列内nullはそのままです。pipeline()は1件でもArrayを返し、通信失敗時も取得済みの返答を残して未受信位置にErrを置きます。WATCH中断はErr.INTERRUPTEDとe.info.code == "tx_failed"で識別できます。
open()のtimeoutで接続と応答の期限を秒指定できます。open()は接続先を設定するだけで、通信は最初のquery()から始まります。size()は確立中を含む接続数、in_flight()は取得待ちを含む未完了数です。open()に辞書で渡す設定と、その既定値です。
| 入口 | 設定と既定 | 意味 |
|---|---|---|
GDDatabaseClient.open | driver="postgres", path="" | driverとSQLite path。SQLite時はuser://...または:memory:が必要 |
| 同上 | host="127.0.0.1", port=5432 | PostgreSQLの接続先 |
| 同上 | pool=0 | PostgreSQL最大接続数。0はmax(4, CPU数)、SQLiteでは使わない |
| 同上 | max_rows=0, max_bytes=0 | query()が集める1結果の行数とbyte。0は無制限。query_rows()には適用しない |
GDPostgresClient.open | user="postgres", database="postgres", password="" | 認証とDB名 |
| 同上 | connect_timeout=15.0, timeout=0.0 | 接続と問い合わせの秒。poolの接続待ちも問い合わせ時間に含む。0は無期限 |
| 同上 | auth="any", allow_cleartext_password=false | auth="scram"/"md5"で方式固定。平文password応答は明示時のみ |
| 同上 | tls=<hostで決定>, ca="" | 外部hostはverify-full、loopbackはdisable。CA fileは明示時だけ |
GD.database.sqlite.open | busy_ms=5000, max_ms=0 | lock待ちミリ秒と実行期限ミリ秒。0は無期限 |
| 同上 | max_rows=0, max_bytes=0 | 1結果の行数とbyte。0は無制限 |
GDRedisClient.open | password="", timeout=10.0 | passwordと接続・応答期限の秒。0は無期限 |
| 同上 | tls=<hostで決定>, ca="" | PostgreSQLと同じTLS選択 |
GD.database.postgres.pool | size既定0、0または1..2147483647 | 0はmax(4, CPU数) |
GD.database.redis.pool | size既定0、0..2147483647 | 最大接続数。0は無制限。同時に作る接続はCPU数の10倍まで、最大数の指定時はその数まで |
GDRedisPool.open | pool_timeout=timeout+1.0(timeoutが0なら30秒) | 接続の空きを待つ期限。明示0は無期限 |
max_rowsまたはmax_bytesを越えたquery()は、その問い合わせだけを失敗にします。 期限切れや壊れた応答で順序を失った場合は接続全体を閉じます。
| 設定 | 受理範囲 |
|---|---|
max_rows, max_bytes, busy_ms, max_ms | 0..2147483647 |
| bind値 | PostgreSQLは65535個、SQLiteはengineの変数上限まで。query_manyの件数に固定上限はない |
| PostgreSQLの1送信 | SQLとbind文字列をUTF-8のbyteで数え、約1 GiBまで |
| Redisの1送信 | server側の設定に従う |
| PostgreSQLとRedisのport | 1..65535 |
| 秒指定 | 有限の0..9223372036.854776秒。0は無期限 |
決まった時刻に一度だけ動かす仕事は、普通のscriptとして書き、OSのcronやsystemd timerから呼びます。 gd側に常駐の仕組みは要りません。
func collect():
var now := GD.time.to_iso(GD.time.now())
GD.file.append_text("store://log.txt", now + "\n")?
return 0, null
func main():
collect()?
return 0, null
gd --strict --mount store=/var/lib/app:rw collect.gd
自分で間隔を持って回り続ける仕事は、GD.async.spawn()へ渡してgd serveで常駐させます。 spawn()へ渡した処理はmain()が返った後も動き続けます。
func every(sec, fn):
while true:
await GD.async.sleep(sec)
fn.call()
func collect():
print(GD.time.to_iso(GD.time.now()))
func main():
var _job := GD.async.spawn(every.bind(60.0, collect))
return 0
gd serve schedule.gd
止めるときはprocessを終わらせます。Web serverと同じ常駐なので、ここでもserveが必要です。
本体を小さく保ち、外部service固有の機能は必要なprojectだけへGDScript packageまたはGDExtensionとして加えます。
| 入口 | 用途 | APIと導入方法 |
|---|---|---|
Discord | DiscordのGatewayとRESTを使う純GDScript文字Bot | Discord Bot |
GDMemcached | TCP接続を再利用するcache client | Memcached |
GDSupabase | DatabaseとAuthのclient | Supabase |
各文書に公開class、method、戻り値、制限値、strict実行例をまとめています。任意導入のため、 本体だけから生成するAPIリファレンスには含まれません。
gd addで入れた拡張は起動時に信頼して読み込むため、旗は要りません。接続先の--allow-netは必要です。--allow-extと--deny-extが効くのは、scriptが実行中にGDExtensionManager.load_extension()で読む場合です。gd.lockで固定してcommitしてください。scriptが増えたり他のpackageを使ったりする段階で、gd initでgd.jsonを作ります。依存はgd.jsonとgd.lockで固定します。
gd init
gd search discord bot
gd add gd:@scope/script-package@^1.0.0
gd add ext:@scope/name@^1.0.0
gd add short-name https://example.com/module.gd
gd install --frozen
gd task test
入れたpackageは、利用側が決めた呼び名を使ってpkg://<呼び名>/から読みます。
@import hello
pkg://は利用者ごとの共有cacheを指し、projectへは何も複製しません。gd.jsonに書いた依存がcacheに無ければ、最初の実行で取得します。--strictでは登録所への--allow-netが要ります。gd addの既定の呼び名は、package名の-と.を_にした識別子です。engine classやkeywordと同じ呼び名は断ります。gd.jsonとgd.lockです。gd initはpkg/を.gitignoreへ書きます。--frozenはlockを変更しません。offlineの配布先では、networkのある環境で先に取得し、--cached-onlyを併用します。@importはconst 名 = preload(...)の短い書き方です。
外部依存がなくても使えます。実際の登録所の依存とlockfileを含むsamples/packagesを用意しています。
@import greet
@import "./util"
@import greet/style as theme
gd.jsonのimportsに宣言した呼び名だけを解決します。同名のfileを探しに行きません。./または../から書きます。 .gdは省略できます。同名のfileとdirectoryが両方あるときは"./util.gd"のように指定します。asが無ければ最後の要素そのままで、mod.gdを持つdirectoryはdirectory名です。gd fmtは@importをそのまま残します。@importを知らないので、Godotと共有するfileではconstとpreloadを書いてください。packageはgd.jsonを根に持つ一つのprojectです。gd init @scope/nameがmod.gdとtestの雛形を作り、 gd testで回し、gd publishで公開します。
{"name":"@scope/hello","version":"1.0.0","main":"src/mod.gd","include":["src"]}
gd publish
gd add hello gd:@scope/hello@^1.0.0
mod.gdです。複数fileならincludeへfileまたはdirectoryを明示します。gd.jsonのimportsで他の登録所packageを使えます。gd publishがそのimportsを登録所へ載せます。class_nameは公開できます。installは同名classの衝突を検査し、衝突すれば全体を元へ戻します。gd.jsonのgodotをtrueにすると、gd固有のAPIを使わず本家Godotでも動くという作者の宣言になり、gd searchが[godot]と示します。開発中のpackageはgd add ../pathでlocalから足します。呼び名は先のgd.jsonのnameから取ります。 checkoutをpkg/<呼び名>/へ複製し、内容の指紋が変われば次の実行で複製し直します。 .で始まるfile、pkg/、tmp/、gd.jsonを持つ下位directory、tokenは複製しません。 gd publishは、local importの先にnameとversionのあるgd.jsonがあれば登録所の範囲に変換し、無ければ拒みます。
gd installは依存graph全体を解決します。版は、gd.lockが固定した版、今回すでに選んだ版のうち範囲を満たすもの、 登録所の最新一致の順で選びます。
gd.lockは解決したimportsの設定も保持します。設定が変わった実行では同じresolverで再解決し、要求外の古い版を使いません。 --frozenは設定の不一致を拒否します。同じpackageに複数の別名がある場合、辞書順で最初の別名を配置先に使います。
pkg://@scope/name@版/です。pkg://<呼び名>/は、書いたscriptが属するpackageのimportsで正式pathへ展開されます。同じ呼び名でもpackageごとに違う版を指せ、同じ版はどこから辿っても一つのscriptです。gd.lockには各packageのimportsの解決先も記録され、gd infoが一覧します。gd removeとgd updateは、どのpackageも使わなくなったものをgd.lockとpkg/から外します。本家Godotなどres://しか読めない環境と共有するときは、gd.jsonへ"place": "project"を書きます。 packageをpkg/<呼び名>/へ複製し、pkg://もres://pkg/もそこを指します。 他のpackageだけが使うものはpkg/@scope/name@版/へ置きます。
project.godotのあるdirectoryではplaceの既定がprojectになり、.gitignoreは書きません。gdの無い同僚が開けるようpkg/をcommitします。preload、load、extendsに書かれたres://参照を配置先へ書き換えます。文字列、コメント、実行時に組み立てるpathは書き換えません。placeはfileの置き場を決めるだけで、gd固有のAPIや構文をGodot向けに変換する機能ではありません。共有するsourceは標準構文と相対preloadで書きます。placeに関わらずpkg/<呼び名>/へ置きます。ext:で取り込んだ拡張のclassだけです。projectのscriptならgd.json、packageのscriptならそのpackageのimportsが基準です。[classes]に無いclassを登録すると起動時に止まります。gd compileを配布先のOSで実行してください。gd.jsonの設定は次の10件です。
| 名前 | gd initの生成値 / 未指定時 | 意味 |
|---|---|---|
name | my-tool / 必須 | project名。publishは@scope/nameが必要 |
version | 0.1.0 / 必須 | packageのversion |
tasks | run/testの2件 / 無し | gd taskから呼ぶcommand |
imports | {} / {} | 呼び名と依存先。publishするpackageでは登録所packageだけ |
registry | 未指定 / 環境または公開登録所 | project固定の登録所URL |
main | 未指定 / mod.gd | publishするmod.gdまたは.gdextension入口 |
include | 未指定 / mainだけ | 純GDScript packageへ含めるmain directory内のfileまたはdirectory |
place | 未指定 / cache(project.godotがあればproject) | packageの置き場。projectでpkg/へ複製する |
godot | 未指定 / false | gd固有のAPIを使わず本家Godotでも動くpackageの宣言 |
description | 未指定 / 空 | 登録所に出す説明 |
gdが読む環境変数は次の通りです。scriptから環境を読む実行では--allow-envで名前を許可します。
| 環境変数 | 用途 |
|---|---|
GD_CACHE_HOME | packageのcache根。未指定はWindowsのLocalAppData内gd。macOS/Linuxは絶対pathのXDG_CACHE_HOME/gd、それがなければhome内.gd。HOME未設定時はOSの利用者情報を使う |
GD_REGISTRY | 登録所。未指定はhttps://gd.progsha.com/pkg。gd.jsonのregistryが優先 |
GD_TOKEN | publishのtoken。設定fileへ書かず、publishするprocessだけへ渡す |
LC_ALL、LANG | gd docの手引きの言語 |
GD_WORKER | --workersが作る内部印。利用者が設定する値ではない |
遠隔packageと登録所はHTTPSを使います。loopbackの開発用登録所に限りHTTPも使えます。 取得したpackageとnative libraryは登録所索引のSHA-256と照合します。 .gdextension manifestは16 MiB、packageの全file合計は500 MiBまでです。
compileで、script、view、静的file、migration、依存package、対象OSのGDExtensionを一つの実行体へまとめます。配布先にcacheは要りません。
gd compile -o app main.gd
./app
gd.jsonが名指すpackageと、それらが取り込むpackageを全部同梱します。./app serve --no-scene-tree --allow-netで常駐できます。.envなど.で始まるfileとgd.jsonのtokenを除きます。.envを除外しますが、sourceに書いた値は実行体へ残ります。gdはAPIが固まる前の公開版です。後方互換は前提にしないでください。変更した点と基準にしたGodotの版は CHANGELOGに書きます。 gdはGodot FoundationまたはGodot Engine projectの公式製品ではありません。
不具合はIssuesへ、公開すべきでない脆弱性は GitHubの非公開報告から知らせてください。
The online manual and API reference uses your browser’s preferred English or Japanese language, with English as the fallback. Use the language button or ?lang=ja for Japanese; an explicit selection is remembered locally.
gd is a single command for writing command-line tools, websites, Web APIs, scheduled jobs, and data processing in GDScript. It is Godot built small without a display, and it runs a single .gd file without a project.godot.
You start the way you would with a Python or Node.js script. When you need them, type checking, tests, packages, databases, a Web server, and a standalone executable are available in the same GDScript. For game screens and rendering, use upstream Godot.
Together with Godot itself, apps, frontends, servers, and CLI tools can all be written in one language, GDScript. The same script runs on macOS, Linux, and Windows, and waits on networking and databases do not stop other work. It links directly with C++ through GDExtension. It is designed for AI agents to write and run code.
Built-in HTTP/2 multiplexing and nonblocking networking keep concurrent connections moving. SIMD/SWAR paths accelerate UTF-8, JSON, and hex processing, while connection, buffer, and parsed-template reuse reduce repeated work and allocations.
Supported platforms are macOS arm64/x86_64, Linux x86_64, and Windows x86_64. Download the archive for your OS from Releases and put the gd inside on your PATH. When gd --version prints a version, the install is done. The SHA-256 of each archive can be checked against the bundled SHA256SUMS. The macOS build is signed with a Developer ID and notarized by Apple.
curl -fsSL https://gd.progsha.com/install.sh | sh
The installer checks the archive checksum and installs to ~/.local/bin. Add that directory to your PATH if prompted. Linux binaries require x86_64 and glibc 2.38 or newer.
Invoke-WebRequest -UseBasicParsing https://gd.progsha.com/install.ps1 -OutFile install-gd.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File .\install-gd.ps1
Run these commands in PowerShell. Administrator access is not required. The installer verifies the SHA-256 checksum, installs gd.exe into %LOCALAPPDATA%\gd\bin, and adds that directory to your user PATH. Open a new terminal and run gd --version.
For a manual install, download gd-windows-x86_64.zip from Releases, extract it, and add the directory containing gd.exe to your user PATH. Do not run the Unix curl | sh command in PowerShell.
brew tap prog-sha/gd https://github.com/prog-sha/gd
brew install prog-sha/gd/gd
This tap lives in the product repository and installs the signed Universal binary. Update with brew update && brew upgrade prog-sha/gd/gd; remove with brew uninstall gd.
Use an apt-based distribution with glibc 2.38 or newer, such as Ubuntu 24.04 or Debian 13. Ubuntu 22.04 and Debian 12 need a source build. The dedicated repository uses a signing key restricted to this source:
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://gd.progsha.com/apt/gd.asc | sudo tee /etc/apt/keyrings/gd.asc >/dev/null
sudo chmod 0644 /etc/apt/keyrings/gd.asc
echo 'deb [arch=amd64 signed-by=/etc/apt/keyrings/gd.asc] https://gd.progsha.com/apt stable main' | sudo tee /etc/apt/sources.list.d/gd.list
sudo apt update
sudo apt install gd
Subsequent versions arrive through normal apt updates. Remove with sudo apt remove gd. Repository metadata and packages use HTTPS; apt also verifies the signed metadata.
The official winget catalog entry is pending submission and review. Until it is accepted, use the PowerShell installer above. Maintainers can validate or submit the prepared manifests; winget install prog-sha.gd is not yet an available installation route.
Building from source needs Python, uv, SCons, and a C/C++ compiler. The executable is gd.*.template_release.* under bin/. TLS is built in, so no separate TLS library is needed.
git clone --branch 0.7 https://github.com/prog-sha/gd.git
cd gd
scons platform=macos target=template_release -j8
# Linux: platform=linuxbsd
# Windows: platform=windows windows_subsystem=console
Create one file, hello.gd. No config file or package is needed. main() is the entry point, and the integer it returns becomes the process exit code.
func main():
print("Hello, world")
return 0
gd hello.gd
To check types and syntax without running, use check.
gd check hello.gd
Use @import "./settings" for a local file and @import hello for an installed package. Use the imported name directly; .gd and as settings can be omitted.
Put shared values in settings.gd:
const TITLE = "Hello"
const USER = "world"
Use them from main.gd:
@import "./settings"
func main():
print(settings.TITLE, ", ", settings.USER, "!")
return 0
This prints Hello, world!. Use this pattern when values need to be shared.
Access values as settings.TITLE instead of copying a const declaration for each value into the caller. Keep values and functions used by only one script in that script. Use as when a name conflicts or needs clarification. Built-in APIs such as GD.web need no import.
See Packages and distribution for installation and version locking.
The standard API has one entry point, GD, with a child per purpose. Scripts write these names as they are.
| What you want | Entry | Example |
|---|---|---|
| Files, text, time, HTTP client, async | GD | GD.file.read_text("a.txt") |
| Websites and Web APIs | GD.web | GD.web.app() |
| SQLite or PostgreSQL | GD.database | GD.database.client() |
GD.database.postgres and GD.database.redis are advanced entries for features specific to one backend.
Pass the same spelling you write in a script to gd doc. Signatures come from the executable, so they match the implementation.
gd doc # short guide and entry points
gd doc manual # the full manual
gd doc GD # the children of GD
gd doc GD.file # file API
gd doc GD.http.fetch # the returned HTTP response
gd doc GD.web.app # Web application
gd doc SceneTree # Inspect a public engine class.
gd doc all # every public class
Return types such as R, Err, and GDWebRequest are looked up by name alone. For Godot classes such as Node, SceneTree, and Timer, see the Godot class reference as well. The manual is shown in Japanese when LC_ALL or LANG starts with ja, and in English otherwise. The web version at gd.progsha.com switches between Japanese and English.
The examples avoid repeating type names. Variables assigned with := and success values returned as return value, failure infer their types. Parameters without annotations remain dynamic. Add annotations only where you want to fix a type boundary.
Arguments placed after the script name arrive in main(argv). An argument that could be mistaken for gd's own flag, such as --name=gd, reaches the script when placed after --.
func main(argv):
for arg in argv:
print(arg)
return 0
gd main.gd apple orange
gd main.gd -- --name=gd
To interpret them as flags, use GD.cli.flags(). It accepts --name gd, --name=gd, and -name=gd alike.
func main(argv):
var flags := GD.cli.flags()
flags.flag_str("name", "world", "who to greet")
var parsed := flags.parse(argv)
if not parsed.ok:
print(flags.usage())
return 1
print("Hello, " + flags.get_str("name"))
return 0
Call external tools with GD.cli.run(). Only the calling GDScript waits, so other requests proceed even when it is called from a gd serve handler. Under --strict it needs --allow-run. You can narrow the target, as in --allow-run=/usr/bin/git.
func main():
var got := GD.cli.run("git", ["rev-parse", "HEAD"])
if not got.ok:
return 1
print("code=", got.v["code"], " out=", got.v["output"])
return 0
A nonzero child exit returns Err. Inspect value.code or e.info.code for its status and value.output for captured output. Timeout and cancellation retain captured output too.
The third argument, opts, changes the behavior.
| Name | Default | Meaning |
|---|---|---|
timeout | 0 | Seconds before giving up. 0 is unlimited. Past it, the child is shut down and Err.TIMED_OUT is returned |
output | true | Collect the output. With false, the child uses the parent's standard I/O directly and nothing is collected |
gd check main.gd # check types and syntax without running
gd fmt main.gd # normalize formatting
gd test # collect and run *_test.gd
gd --watch main.gd # run again on every save
gd eval 'print(1 + 1)' # try one line
gd repl # try interactively
A function that can fail returns two values, the success value and the failure, instead of throwing. The caller receives both with var value, e :=, and a non-null e means failure.
func main():
var text, e := GD.file.read_text("note.txt")
if e:
print(e.text())
return 1
print(text)
return 0
Instead of writing if e every time, append ? to the call. The failure is returned to the caller as is, and only the success value remains. A function that uses ? also returns a success value and a failure itself, with return value, failure.
func title(path):
var text := GD.file.read_text(path)?
return text.strip_edges(), null
func main():
var text, e := title("note.txt")
if e:
print(e.note("read the title").text())
return 1
print(text)
return 0
| Form | Meaning |
|---|---|
var value, e := call() | Receive the success value and the failure separately |
return value, null / return null, failure | Return success or failure. The success type is inferred from value |
call()? | On failure, return it to the caller as is |
call()! | On failure, print the reason and stop the program there with exit code 1. For prototypes and tests. Under gd serve, only that handler fails |
e.note("purpose") | Add working context to a failure. It prints as "purpose: original reason" |
e.kind | The kind, such as Err.NOT_FOUND or Err.INVALID_DATA. Use it to branch |
Err.err("reason", Err.NOT_FOUND) | Create a failure yourself |
In main(), which does not pass failures up, receive them with var value, e := or !.
File-operation failures put op, path, source, and source_code in e.info. A rename has old and new instead of path. source is one of posix, win32, and engine. A kind is set only when its meaning, such as NotFound, is known; an unknown I/O failure keeps its source details with Err.NONE.
Some APIs retain processed values on failure. Receive both with var value, e := call() when decoding, parsing queries, running child processes, or executing Redis batches. Causes remain accessible through e.cause; use e.is(kind) to inspect their categories.
The MessagePack, CBOR, TAR, TOML, YAML, XML, dotenv, front-matter encoding, and PBKDF2 APIs in GD.data return a value and Err in both synchronous and async forms. Unrepresentable entries are not silently omitted.
base64_decode(text) and base32_decode(text) require standard padding; pass true as the second argument for raw input. Base32 uses uppercase letters. base64url_decode(text) uses raw input by default; pass false for padded input. Both synchronous and async decoders retain partial bytes on failure.
GD.http.decode_query(raw) returns Arrays of all values per key and the first Err. req.query provides the first value; req.query_all retains every value. Keys and values containing NUL or invalid UTF-8 remain PackedByteArray, preserving their exact bytes. encode_query() repeats Array values and accepts byte arrays. parse_url() preserves the original spelling in raw_query. After editing query, call url.erase("raw_query") before rebuilding the URL to use the edited dictionary.
Code works without written types. When you do write types, these are the detailed rules.
-> int, Err: one success type and Err. The runtime type is R; -> R or no return annotation is also allowed.Err or be null for success. A string cannot be returned there directly; wrap it with Err.err(reason).var e: Err = ... or var e := Err.err(...). A variable declared with var e = ... can change type and cannot occupy the last slot.return [1, 0.0, ""], null. return null, null returns null as the success value.R with exactly two names: var value, e := call(). A declaration that lists expressions, such as var a, b, c := 1, "a", 0.0, is a different thing: it has no count limit and infers each name from its expression. It may also assign variables visible in the function, including those of enclosing blocks, when it introduces at least one new name, and every right-hand expression is evaluated before any assignment. Constants, parameters, and variables a lambda captured cannot be assigned.? in a function whose annotated return type lacks , Err gives The "?" operator needs a function returning "R" or "Err".return R.ok("a") under -> int, Err is a compile error. When the type is dynamic, it is checked at runtime.? still ends with return null, null.-> int cannot use a comma return. Declare -> int, Err.return R.ok(value) and return R.err(reason).To carry the value and the failure around as one value, use R. Read ok for the outcome, v for the success value, and e for the failure. Build one with R.ok(value) or R.err(reason, kind, partial_value). Web handlers and database transactions can also be written to return this R.
func find(items, want):
for item in items:
if item == want:
return R.ok(item)
return R.err("not found: " + want, Err.NOT_FOUND)
func main():
var got = find(["a", "b"], "c")
if not got.ok:
print(got.e.text())
return 1
print(got.v)
return 0
R.ok() carries null as its success value, not integer 0. I/O APIs that make partial progress keep the completed amount in v as a partial value even on failure. note() preserves the partial value, and v_or(fallback) returns the fallback on failure. When ? propagates a failure whose partial value does not fit the caller's success type, only the partial value is dropped; the reason and kind are kept.
Waiting methods in HTTP, databases, GD.net, files, and others are written as ordinary function calls. Only the calling GDScript waits; other networking and timers keep running.
func main():
var res := GD.http.fetch("https://example.com/")!
print(res.status)
return 0
To start several operations together, use the variants ending in _async with GD.async.all().
func main():
var got = await GD.async.all([
GD.http.fetch_async.bind("https://example.com/a"),
GD.http.fetch_async.bind("https://example.com/b"),
])
for result in got:
var res, e := result as R
if e:
print(e.text())
return 1
print(res.status)
return 0
| Entry | Purpose |
|---|---|
name_async() | Start the operation and return a Signal. await gives the same result as the regular name |
GD.async.all(list) | Accept Callables and Signals and return all results in input order. An invalid input becomes an error in its result slot |
GD.async.spawn(fn) | Run a GDScript function in the background. It keeps running after main() returns |
GD.async.sleep(sec) | Wait the given number of seconds |
Pass Callables rather than Signals to all(). It subscribes to completion before starting each one, so it cannot lose a result that finishes early.
A signal saved with := retains its completion type. Reassignment from a different or unknown completion type is rejected. For dynamic completions, declare the receiver as Signal and annotate the awaited value as needed, for example var result: R = await pending.
spawn() does not move CPU work to another thread. Long GDScript yields to other work automatically, but the inside of a native method is not interrupted, so use the _async variant when passing large input to a standard module. A waiting method may be called only from a function that GDScript itself called. Native callbacks such as Array.map(), _init(), member initializers, and _to_string() cannot wait for it. Adding await inside such a callback does not make the native caller await the result. Move asynchronous initialization into main(); replace asynchronous map() callbacks with a for loop in an asynchronous function or the Callables passed to GD.async.all() above.
With what you have read so far, this builds a small API that accepts JSON and stores it in SQLite, in one script. The result is a development server with input validation and SQL parameter binding, started with narrowed permissions.
mkdir notes-api
cd notes-api
Save the following as main.gd.
# Store notes in an embedded database and expose a JSON API.
extends RefCounted
const PORT := 18080 # development port listening on loopback
const DB_PATH := "user://notes.sqlite3" # per-user writable area provided by gd
var app := GD.web.app()
var db := GD.database.client()
# Return notes as JSON, newest first.
func list_notes(_req):
var got := db.query("SELECT id, title FROM notes ORDER BY id DESC")?
return GD.web.json(got.rows)
# Store a validated title and return the created row.
func add_note(req):
var body := req.valid("body")
var made := db.query(
"INSERT INTO notes(title) VALUES($1) RETURNING id, title",
[body.title]
)?
return GD.web.json(made.rows[0], 201)
# Prepare the database and routes, then listen on loopback.
func main():
db.open({"driver": "sqlite", "path": DB_PATH})?
db.query("CREATE TABLE IF NOT EXISTS notes(id INTEGER PRIMARY KEY, title TEXT NOT NULL)")?
app.route("GET", "/notes", list_notes)
app.route("POST", "/notes", add_note, [GD.web.json_body(GD.web.object_rule({
"title": GD.web.text_rule({"min": 1, "max": 120}),
}))])
app.listen(PORT, "127.0.0.1")?
print("listening on http://127.0.0.1:%d" % PORT)
return 0, null
Read it from the top.
app is the router and db is the database connection. Both are script variables so the server can keep running after main() returns.main() first opens SQLite and creates the table. user:// in DB_PATH is a per-user writable area provided by gd.app.route() registers an HTTP method, a path, and the function to call for it (the handler).GDWebRequest and builds the reply with GD.web.json(). A ? in the middle returns the failure to the server, which becomes a status such as 500.GD.web.json_body(). The handler is called only when the body matches the rule, and the value that passed arrives in req.valid("body").$1. SQL is never built by string concatenation.Run unverified scripts and servers exposed to the outside with --strict, which denies permissions by default. Here the listener is limited to one loopback port. serve keeps the process alive after main() returns, and it is the command to use for servers.
gd check main.gd
gd --strict --allow-net=127.0.0.1:18080 serve main.gd
curl -s -X POST http://127.0.0.1:18080/notes \
-H 'Content-Type: application/json' \
-d '{"title":"try gd"}'
curl -s http://127.0.0.1:18080/notes
The first call returns status 201 with the created row, the second the stored array. An empty title, a title over 120 characters, or a non-JSON body is rejected with 400. Press Ctrl-C in the terminal that started it to stop.
In production, keep this process on loopback behind a TLS reverse proxy, and switch storage that must survive crashes to PostgreSQL. Keep connection details out of the source and read them from allowed environment variables.
Start with normal execution. --strict is an advanced mode for explicitly designing and configuring the permissions a script needs.
| Mode | Suited to | Restrictions |
|---|---|---|
| Normal execution | Running trusted source during development | Neither files nor the network are restricted |
--strict | Unverified scripts, public servers | The startup directory is read-only; absolute paths outside it are denied. Network, environment variables, child processes, native extensions, and system information are denied by default |
On macOS/Linux, v0.7.3 --strict still permitted absolute reads outside the startup directory.
Under --strict, start by listing what the script uses.
gd --strict \
--mount store=/srv/app:rw \
--allow-net=db.example.com:5432 \
--allow-env=DATABASE_URL \
main.gd
| Flag | Grants |
|---|---|
--mount name=path:r / --mount name=path:rw | Read or read/write on a named directory |
--allow-net=host:port,... | Connecting and listening. Without a value, everything |
--allow-env=name,... | Environment variables |
--allow-run=command,... | Child processes |
--allow-ext=path,... | Native extensions a script loads while running |
--allow-sys=item,... | Machine and system information |
--deny-* | A denial that wins over the matching allow |
-A | Allow everything except files. For temporary use during development |
A script sees files through four kinds of location. Write the name of the location at the start of the path, or write an absolute path as it is.
| Spelling | Location | Under strict |
|---|---|---|
res://a.txt | The directory the script was started from | Read-only |
user://a.txt | Per-user writable area provided by gd | Read/write |
store://a.txt | The name given by --mount store=/srv/app:rw | As specified |
/etc/hosts | That location on the machine | Denied unless it is inside the startup directory |
res:// is refused in either mode.--strict, the startup working directory is the implicit read boundary, even when the entry script is in a subdirectory. Absolute paths inside it use the same read-only checks as res://; parent and sibling directories are denied. Links cannot bypass this boundary. To access an outside directory, explicitly mount it and use its named scheme, such as store://. -A does not expand file access.--mount and absolute paths are for Linux and macOS. Windows rejects them, so put files under res:// or user:// there.-. res, user, uid, pipe, local, libgodot, tcp, unix, http, https, file, data, and cache are reserved and cannot be chosen.--allow-net, localhost:8080 also covers IPv4 loopback 127.0.0.0/8 and IPv6 ::1 on the same port.*.example.com:443 allows its subdomains.gd serve is the resident way to run, and it creates no SceneTree. Networking, timers, await, custom Signals, GD.async.sleep(), and queue_free() on nodes outside a tree all work. With no work to do, it sleeps until the next deadline or network notification, so there is no cycle to tune.
| What you want | How |
|---|---|
| Keep a Web server or scheduled job resident | gd serve main.gd |
Use Node _process(), _physics_process(), process_frame, SceneTreeTimer, or high-level multiplayer | Normal execution without serve |
| Run a script extending SceneTree or MainLoop | Normal execution without serve |
| Check during development that no SceneTree slips in | gd --no-scene-tree --allow-net serve app.gd |
| Listen with several processes | --workers=<n> or --workers=auto. n is an integer of 1 or more |
Under serve, a script that only extends Node is not added to a tree. --no-scene-tree reports a diagnostic as soon as a SceneTree is created and exits with code 1. It is inherited by --watch and --workers children. Normal execution creates an implicit SceneTree, so it fails with this flag.
Use GD.net for low-level networking. Godot's low-level types remain for compatibility, but new code should use GD.net.
func echo():
var listener := GD.net.listen_tcp("127.0.0.1", 8080)?
var conn := listener.accept()?
var data := conn.read(65536)?
conn.write(data)?
conn.close()
return 0, null
GDTCPConn keeps reads and writes in separate queues, so several GDScripts may call it concurrently.close() releases pending reads and writes with Err.INTERRUPTED. Listener accepts behave the same way.timeout is never extended.Open TLS with GD.net.dial_tls(host, port, opts). It verifies the certificate chain and host name by default, and a failure never falls back to plaintext. The result is the same GDTCPConn used for TCP.
opts | Meaning |
|---|---|
timeout | One deadline in seconds covering both connect and handshake |
ca_file | A private CA. It takes precedence over the environment settings |
cert_file, key_file | Client authentication. Provide both, as files inside permitted mounts |
server_name | Check the certificate against a name different from the dial address |
next_protos | An array of ALPN names. One name is 1–255 bytes, and the whole list is up to 65535 bytes |
insecure_skip_verify | Skip verification. Use only for tests where verification is deliberately unnecessary |
Read the negotiated result from negotiated_protocol and version in connection_state(). TLS 1.2 is 771 and TLS 1.3 is 772.
Without other settings, the trusted CAs are the OS trust settings on macOS and Windows, and the system CA bundle on Linux. Set SSL_CERT_FILE or SSL_CERT_DIR before starting the process to use the given CAs on any OS. Directory lists use : on Unix and ; on Windows.
When a server requires client certificates, pass client_ca (a trusted CA bundle) and client_auth in the opts of app.listen_tls(port, cert, key, host, opts). A missing client_ca uses the system trust settings.
client_auth | Behavior |
|---|---|
none | Does not request a certificate |
request | A certificate is optional. It is not verified |
require | A certificate must be presented. It is not verified |
verify_if_given | Verifies a certificate only when one is presented |
require_and_verify | Requires a verified certificate |
GD.net.listen_udp() returns a GDUDPPacketConn. read_from() returns a dictionary containing data, host, port, and truncated. Pass an IP address resolved by GD.net.resolve() as the host of write_to(). Packets are never merged. The default buffer=0 keeps the OS receive buffer as it is; only a positive value requests a change.
GD.net.resolve() returns the first address selected by the OS. It keeps no name cache. GD.net.local_addresses() returns the machine's address list and distinguishes an empty list from an OS failure. The failure's e.info carries syscall, source, and source_code.
GD.file reads and writes files and handles paths. The directory you started from is res://, and absolute paths work as written. To write to an outside directory under strict, write the name given by --mount store=/srv/app:rw as in store://users.csv.
func main():
var rows := GD.data.csv_objects(GD.file.read_text("store://users.csv")?)?
GD.file.write_text("store://users.json", JSON.stringify(rows))?
return 0, null
File operations suspend only the calling GDScript, even under their regular names. Other requests proceed while a Web server handler reads a file. Use the variants ending in _async only to start several operations together.
func handler(_req):
var body := GD.file.read_text("store://big.json")
if not body.ok:
return GD.web.text("cannot read", 500)
return GD.web.text(body.v)
Files embedded by compile can be read, listed, and served statically through the same API.
To read without holding the whole file in memory, open a GDFileStream with GD.file.open(path, mode). Modes are read, write, append, and read_write. Call close() when done.
| Method | Behavior |
|---|---|
read(max) | Returns up to max bytes. It may return fewer. An empty successful value is EOF |
write(bytes) | Writes all bytes and returns the count. On a failure partway, R.v retains the number already written |
Operations on one stream run in arrival order, and separate streams proceed in parallel. Append always writes at the end, even after a seek. read_bytes() also retains the bytes already read in R.v when it fails partway. read_text() rejects input too large for a String instead of truncating it, so handle large files as bytes or a stream.
When several processes update the same file, use GD.file.replace_text(path, old, body). It replaces the content only when the old you read still matches the current content, so a concurrent edit is never silently overwritten. Pass null as old to create a new file.
| Purpose | Entry |
|---|---|
Reading CSV, TOML, YAML, JSONL, JSONC, XML, INI, TAR, front matter, and .env files | GD.file.read_csv(path) and similar |
| In-memory conversion of the same formats, JSON, codecs, hashes, HMAC, PBKDF2, HKDF, byte sequences | GD.data |
| UUID and ULID | GD.id |
| Time conversion and arithmetic | GD.time |
| Text formatting and comparison | GD.text |
| HTML entities, tags, and gdhtml (a micro template with Mustache syntax) | GD.html |
| Flags and environment variables | GD.cli |
| Array and dictionary operations | GD.collection |
| Special math values and bit operations | GD.math |
| Version comparison | GD.version |
| Logging to the terminal and files | GD.log |
| Test assertions | GD.test |
Environment variables and .env have separate entries by what you read.
| What you read | Entry |
|---|---|
| Process environment variables | GD.cli.env(name, fallback) and GD.cli.require_env(name). Strict mode needs --allow-env |
A .env file | GD.file.read_env(path). Reads the file into a dictionary |
| A dotenv string | GD.data.env(src) and GD.data.to_env(data). Convert to and from a dictionary in memory |
In-memory conversions compute in place under their regular names, and their _async variants compute on another thread. Use _async for large inputs. Run gd doc GD.file and gd doc GD.data for the exact lists. Checks and limits per format are in the description of each entry in the API reference.
GD.collection operations that take a Callable yield to other work about every 1 ms. Each GD.log call waits until the write completes and never truncates the message. Check failures through the returned R; GD.log.flush() waits for all earlier output.
Use GD.data.json_encode(value) to produce JSON bytes and GD.data.json_decode(bytes) to read bytes received from outside. Both return a success value and an Err. GDWebRequest.json(), GDHTTPResponse.json(), and each JSONL line follow the same rules.
int; only fractions, exponents, and out-of-range values become float. Strings and keys preserve \u0000.{"deterministic": true} when the same value must give the same bytes, as for signatures or cache keys.deterministic and escape_html are bool; max_bytes and max_depth are int. An invalid type returns Err.INVALID_DATA, and exceeding a limit returns Err.LIMITED.json_encode_async() finishes. The options dictionary is copied at the start.GD.data returns SHA-1, SHA-224/256/384/512, and SHA3-224/256/384/512 digests. HMAC, PBKDF2, and HKDF accept sha1, sha224, sha256, sha384, sha512, sha3-224, sha3-256, sha3-384, or sha3-512 as the hash name. PBKDF2 and HKDF accept an output length and return an R failure for an invalid hash or settings outside the supported range. PBKDF2 iteration counts of one or less perform one iteration.
GD.async.set_max_threads(max) sets the limit on OS threads managed by gd and returns the previous value. The default is 10000. Exceeding the limit terminates the process. Lowering it below the current count also terminates it. Threads created directly by external libraries are not counted.
Create main.gd and index.html in the same directory. One route renders a greeting through an HTML template; another returns the same data as JSON.
main.gd:
# Serve a greeting as an HTML page and a JSON response.
var app := GD.web.app()
# Build the shared response data from the requested name.
func greeting(req):
var name := req.query.get("name", "world")
if not name is String:
return null, Err.err("name must be text", Err.INVALID_DATA)
return {"name": name, "message": "Hello, " + name + "!"}, null
# Render the page with escaped template values.
func home(req):
return GD.web.view("index.html", greeting(req)?)
# Return the same data as JSON.
func hello(req):
return GD.web.json(greeting(req)?)
# Listen locally, using an optional port argument.
func main(args):
var port := 8080 if args.is_empty() else int(args[0])
app.route("GET", "/", home)
app.route("GET", "/api/hello", hello)
app.listen(port, "127.0.0.1")!
print("http://127.0.0.1:", app.port())
return 0
index.html:
<!doctype html>
<!-- Display a greeting and let visitors choose the name. -->
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{message}}</title>
<style>
body { max-width: 36rem; margin: 4rem auto; padding: 0 1rem; font-family: system-ui, sans-serif; line-height: 1.6; }
input, button { padding: .6rem; font: inherit; max-width: 100%; box-sizing: border-box; }
</style>
</head>
<body>
<h1>{{message}}</h1>
<form action="/" method="get">
<label>Your name <input name="name" value="{{name}}"></label>
<button type="submit">Say hello</button>
</form>
<p><a href="/api/hello?name={{name}}">View the JSON response</a></p>
</body>
</html>
Run from that directory:
gd serve main.gd
Open http://127.0.0.1:8080/?name=Alice to insert “Hello, Alice!” into {{message}}. /api/hello?name=Alice returns {"name":"Alice","message":"Hello, Alice!"}. Template values are escaped for their HTML context.
serve keeps listening after main() returns. Stop with Ctrl+C.
route(method, pattern, handler) binds an HTTP method and a path to a handler. :name in the pattern arrives in req.params["name"]. A handler receives a GDWebRequest. It reads only the needed body through req.read(), bytes(), text(), json(), or save(). A body sent by an HTML form becomes a dictionary with GD.http.decode_query(req.text()?).
req is the incoming request. Inspect fallible operations with var value, e := call() and return null, e, or propagate with call()?. Use return response, null only on a successful path after handling every possible failure.
GD.web.view() and GD.web.json() return response, Err. A handler can forward the result directly: return GD.web.json(data). Template loading/rendering and JSON encoding preserve the original Err for app.on_error(req, e). Middleware, postprocessors, and automatic JSON encoding share that boundary. If the error handler itself fails, the server returns 500 without invoking it again. Once streaming starts, the HTTP status is committed; handle errors returned by the writer.
The value a handler returns becomes the reply.
| Returned value | Reply |
|---|---|
GD.web.html(body), GD.web.view(path, data) | HTML |
GD.web.json(data) | JSON |
GD.web.text(body), GD.web.bytes(body, type) | Text, or any media type |
GD.web.stream(producer) | A body written a little at a time. See "Web operations and advanced features" |
GD.web.redirect(to) | 302. to is limited to a path on the same site. Set away to true to send elsewhere |
GD.web.not_found() | 404 |
| A string | 200 as text/plain |
A dictionary without body | 200 as JSON |
null | 204 |
A failed R or an Err | Status by kind. INVALID_DATA → 400, UNAUTHENTICATED → 401, PERMISSION_DENIED → 403, NOT_FOUND → 404, LIMITED → 429, UNSUPPORTED → 501, TIMED_OUT → 504; other kinds → 500 (all are Err constants) |
Route handlers and middleware may return a Signal, including after await. Processing resumes when it completes: no arguments become null, one argument becomes that value, and multiple arguments become an Array. An unavailable Signal goes through the error handler. Pending subscriptions are removed when the request ends or the app stops.
GD.web.header(reply, name, value) adds a header to a reply.GD.web.guard(reply) adds the defensive headers such as X-Content-Type-Options, X-Frame-Options, and Content-Security-Policy at once.app.show_errors(true) is set during development.req.path is the path with each segment decoded once. req.target is the original text, keeping percent escapes and the query. %2F does not become a path separator.PackedByteArray; validate their type before using them as text. req.query skips malformed pairs; use GD.http.decode_query() to receive both the valid pairs and an Err.GD.web.json() or view() until the reply has been sent.The router also accepts the following.
| Registration | Purpose |
|---|---|
app.static("/assets", "res://public") | Answer GET under the prefix with files from the directory. The media type comes from the extension, and nothing outside the directory is served. Write the index of / as a route |
app.group("/api", [middleware]) | A route group with a shared prefix and middleware. The result has route() and use() |
app.fallback(handler) | Requests matching no route. Return the 404 page here |
app.on_error(handler) | The reply when a handler returns a failure |
app.after(handler) | Reshape the reply before sending. Receives func(req, reply) and returns it with headers added |
Middleware is a function called before the handler. It receives a GDWebRequest, returns null to continue, or returns a reply to stop there. An object with handle(req) also works. Pass values to later stages with req.keep(name, value) and read them with req.kept(name).
| Registration | Scope |
|---|---|
app.pre(mw) | Before route selection. Every request |
app.use(mw) | After route selection. Every route. Can read req.params |
group.use(mw) | Routes in that group |
app.route(method, pattern, handler, [mw]) | That route only |
Input validation is middleware too. GD.web.json_body(rule), GD.web.query(rule), and GD.web.params(rule) check the body, query, and path values, and put the values that pass into req.valid("body"), req.valid("query"), and req.valid("params"). Rules are built from GD.web.text_rule(), int_rule(), number_rule(), bool_rule(), list_rule(), and object_rule(), with GD.web.optional() and GD.web.one_of() for omission and choices. Query and path values are strings, so check them with text_rule() and convert with to_int() when needed.
var app := GD.web.app()
func show(req):
var params := req.valid("params")
return GD.web.json({"id": params.id})
func main():
app.route("GET", "/posts/:id", show, [GD.web.params(GD.web.object_rule({"id": GD.web.text_rule({"min": 1, "max": 20})}))])
app.listen(8080)!
return 0
The built-in middleware are GD.web.sessions(), GD.web.csrf(), GD.web.jwt(), and GD.web.rate(). The authentication section uses them.
As pages grow, move the HTML into template files and render them with GD.web.view(path, data). The template language is gdhtml, a micro template with Mustache syntax. It handles {{name}}, {{{html}}}, #if, #unless, #each, #with, else, and {{> header}}. Using {{> header}} from views/page.html reads views/partials/header.html at the same level.
<!-- views/page.html -->
{{> header}}
<main><h1>{{title}}</h1></main>
<!-- views/partials/header.html -->
<header><a href="/">gd app</a></header>
func page(_req):
return GD.web.view("views/page.html", {"title": "Top"})
Double-brace values are escaped by the context they appear in. The template author is trusted, the values inserted are not.
| Context | Handling |
|---|---|
| HTML body, quoted and unquoted attributes, attribute names | HTML escape |
href="{{url}}" | Relative URLs and http, https, mailto pass. data-href is treated the same |
href="/work/{{path}}", href="/?q={{query}}" | Paths are normalized keeping separators, query values are percent-escaped |
onclick, script body | Encoded as JSON in a form where </script> cannot break the structure, even for application/json |
style | Safe single CSS values and CSS strings and URLs pass |
| Dangerous URLs, srcset, CSS values, attribute names | Replaced with #ZgdunsafeZ or ZgdunsafeZ without failing the whole page |
{{{html}}} are the only unescaped entry, and they work only in the HTML body. Pass only fixed HTML or a sufficiently checked value.each iterations end in different contexts, an unclosed tag, or an ambiguous URL or JavaScript context fails to render.For a server that renders the same template repeatedly, parse it once at startup with GD.html.template(source, partials)?, then call execute(data)? on the returned value from each request. The parsed value is immutable and can be used by several requests at once. execute_bytes(data)? produces UTF-8 bytes directly, so they can be returned as is with GD.web.bytes(body, "text/html; charset=utf-8").
Login state is held by GD.web.sessions(). issue(value) creates a session ID, and the value of cookie(id) is returned as Set-Cookie. On routes that carry the same store as middleware, the value behind the cookie's ID arrives in req.kept("user"), and a missing session is a 401.
var app := GD.web.app()
var sessions := GD.web.sessions()
func login(req):
var form := GD.http.decode_query(req.text()?)?
var user := str(form.get("user", [""])[0])
if user.is_empty():
return GD.web.text("user is required", 400), null
var reply := GD.web.redirect("/me")
return GD.web.header(reply, "Set-Cookie", sessions.cookie(sessions.issue(user)?)), null
func me(req):
return GD.web.text("hello, " + str(req.kept("user"))), null
func main():
app.route("POST", "/login", login)
app.route("GET", "/me", me, [sessions])
app.listen(8080)!
return 0
cookie(id) sets Secure and HttpOnly. If the cookie does not arrive during development without TLS, use cookie(id, false). Log out with drop(id) and clear_cookie(). Sessions live in one process, so with several processes under --workers use JWT or an external store.
Attach GD.web.csrf() to write paths that authenticate with cookies. Requests other than GET, HEAD, and OPTIONS need the browser's Sec-Fetch-Site: same-origin. When old browsers or non-browser clients must be accepted, choose GD.web.csrf({"allow_missing": true}) and combine it with separate token verification.
var app := GD.web.app()
var sessions := GD.web.sessions()
func save_email(_r):
return "saved"
func main():
app.route("POST", "/account/email", save_email, [GD.web.csrf(), sessions])
app.listen(8080)!
return 0
When JWT is used as a login session, revoke issued tokens on password change and logout. check is called after the signature and standard claims are verified, and authentication passes only when it returns true. For example, put the user's ver in the token and increment the stored version on password change. With several workers, compare against something like a cache synced from a shared DB, not a per-process dictionary.
func token_auth(key, versions):
return GD.web.jwt(key, {"check": func(claims):
return versions.get(claims.get("sub", ""), -1) == claims.get("ver", -2)
})
To limit per IP behind a reverse proxy, list the proxy's IPs or CIDRs in trusted_proxies. gd strips trusted proxies from the right end of X-Forwarded-For and uses the first untrusted IP as the key. X-Forwarded-For is ignored when trusted_proxies is unset and when it comes from an untrusted peer, so a client cannot forge its own IP. IPv4 and IPv4-mapped IPv6 are matched as different things, so use an IPv6 CIDR to trust mapped addresses. Proxy settings with zones are rejected.
var per_ip := GD.web.rate({"limit": 60, "trusted_proxies": ["127.0.0.1", "172.18.0.0/16"]})
Wait for shutdown with app.shutdown(context). It stops accepting new connections and keep-alive, then waits for in-flight requests. Past the deadline it returns Err.TIMED_OUT but does not kill in-flight requests. Use app.stop() when every connection must close immediately.
func close(app):
var context := GD.async.context().with_timeout(10.0)
var stopped := app.shutdown(context)
if not stopped.ok:
app.stop()
A handler can observe request completion and disconnection through req.context. with_cancel() and with_timeout() return a child context without changing the parent, and the parent's cancellation reaches the child. To make HTTP, database, process, and other waits cancelable, wrap them with with_context(), passing the context first. The operation result is returned when it finishes first; when the context finishes first, the operation is canceled.
func load(req, db):
var result = await GD.async.with_context(req.context, db.query_async("SELECT * FROM posts"))
return result
When handling large bodies or long handlers, set the limits explicitly with limits() before listening.
func main():
var limited_app := GD.web.app()
limited_app.limits({"header_bytes": 1048576, "header_values": 500, "header_timeout": 15.0, "body_timeout": 10.0, "job_timeout": 30.0, "jobs": 128})
return 0
To accept a 1 GB ZIP, put a per-request limit on it and stream it to a writable mount.
func main():
var app := GD.web.app()
app.limits({"body_timeout": 600.0})
app.route("POST", "/upload", func(req):
req.limit(1000 * 1000 * 1000)?
req.save("uploads://package.zip")?
return GD.web.text("saved")
)
return 0 if app.listen(8080, "127.0.0.1").ok else 1
gd --strict --allow-net=127.0.0.1:8080 --mount=uploads=/srv/uploads:rw serve main.gd
Bodies and memory are handled as follows.
| Target | Handling |
|---|---|
| Request body | No default size limit. The handler starts right after the header, and the body is read from the connection only as the handler reads it |
read(), save() | Stream the body. An empty successful read() is EOF. save() never holds the complete body in memory |
bytes(), text(), json() | Read the whole remaining body into memory. Use save() for large bodies. text() is limited to what fits in a String |
req.limit(bytes) | Per-request body limit. Overflow is returned as a failure to the body-reading operation |
| Request header | Default 1 MiB. The line count is limited only when header_values is set. Trailers 4096 bytes |
| HTTP client response header | Up to 10 MiB |
| Slow connections | Only that connection waits. Other connections are not affected |
| Extra reply headers | No fixed count or aggregate limit. Only invalid names and values are dropped |
| Sessions and rate limits | Shared within a process, not across --workers. Use an external store such as a DB when sharing is needed |
| Session values | String and integer identifiers. Retention counts are set with total and per_user |
| HS256 JWT | Key at least 32 bytes. JSON and signature validity are checked |
| Rate limit key | Retention count is set with keys |
| HTTP status | 100..999. Out of range is sent as 500 |
| Port | 0 is allowed for listening and as the search start of GD.net.free_port(). Targets and is_free() take 1..65535 |
| Query string | GD.http.decode_query() reports a bare semicolon and a broken percent escape as failures |
GD.web.stream(producer, length=-1, type="application/octet-stream", status=200) sends only what producer(writer) writes to the GDWebWriter. It never joins the whole body in memory. The producer may await, and it finishes by returning void or an R.
GDWebWriter | Behavior |
|---|---|
write(data, offset=0, count=-1) | Sends a range of a byte array and returns the accepted bytes. When sending is backed up, it waits until it progresses |
write_text(text, offset=0, count=-1) | Sends a range of a string as UTF-8. Offset and count are in characters; the result is in bytes |
flush() | Waits for preceding writes to be sent. A disconnect shows up as an error here and as req.context cancellation |
length is the number of bytes to send. If the declared and actual lengths differ, the connection is closed. Unknown length (-1) uses DATA frames on HTTP/2, chunked framing on HTTP/1.1, and connection close as the end on HTTP/1.0.req.context cancellation.Start HTTPS with app.listen_tls(8443, "cert://chain.pem", "cert://key.pem", "127.0.0.1") and check the returned R. Mount the certificate directory read-only with --mount cert=/path/to/certs:r. Pass a PEM chain and an unencrypted private key. When key validation fails, no port is opened.
TLS 1.2 and 1.3 are supported, and ALPN selects HTTP/2 or HTTP/1.1. Each HTTP/2 stream proceeds independently, and canceling one does not close the others. header_timeout also applies to an incomplete handshake. For requiring client certificates, see the TLS tables in "TCP and UDP".
GD.data.gzip_writer(writer, level=-1) creates a GDGzipWriter that gzips the bytes written to it and passes them to the writer below. The writer below can be a GDFileStream, a TCP connection, or a GDWebWriter. The whole body is never held in memory.
| Item | Details |
|---|---|
| Methods | write(bytes), flush(), close(), and reset(writer). Each returns R |
level | -2 (Huffman only), -1 (default), and 0..9 |
close() | Finishes the gzip trailer. It does not close the writer below |
reset(writer) | Clears errors and reuses the compressor at the same level |
header | name and comment (non-NUL Latin-1), extra (up to 65535 bytes), mod_time (Unix seconds), and os (default 255). Set it before the first write |
For HTTP, return GD.web.header(GD.web.stream(producer), "Content-Encoding", "gzip"); the producer creates the compressor, writes, and returns the result of close(). Checking Accept-Encoding and setting Vary are up to the caller. Do not compress secrets together with external input, and do not apply it to an already compressed body or a partial response.
For an IPv6-only localhost listener, use app.listen(8080, "::1")!. Under strict use --allow-net=[::1]:8080, and connect to http://[::1]:8080/. ::1 and 127.0.0.1 are separate listeners, and both differ from ::, which means every interface.
To let the OS pick a free port, read app.port() right after app.listen(0). The number is obtained while holding the listener, so no other process can take it. Under strict the chosen port cannot be limited ahead of time, so allow the whole host, as in --allow-net=127.0.0.1. GD.net.free_port() and is_free() are momentary diagnostics, not a way to reserve that number.
The settings passed as a dictionary to GD.http.fetch() and the GD.web functions, with their defaults. Times are seconds and sizes are bytes.
| Entry | Setting and default | Meaning |
|---|---|---|
GD.http.fetch | method="GET", headers={}, body=null | HTTP method, request headers, request body |
| same | timeout=30.0, max_body=0 | Seconds for the whole request and bytes of the response body. 0 is unlimited |
| same | save="", sha256="" | Stream a 2xx body to save, returning an empty body. sha256 requires save, is 64 hex digits, and only a matching completed file is placed |
| same | authority="host:port" | Request target for CONNECT only |
GD.cli.run | timeout=0.0, output=true | Seconds before giving up on the child process, and whether to collect output |
GDWebApp.limits | jobs=0, job_timeout=0.0 | Number of async handlers kept and seconds. 0 is unlimited |
| same | header_timeout=0.0, body_timeout=0.0 | Seconds to finish receiving request header/body. 0 is unlimited |
| same | header_bytes=1048576, header_values=2147483647 | Header bytes including the request line, and the header line count |
GD.web.jwt_sign | ttl=900 | Seconds used to fill iat/exp. 0 does not add them |
GD.web.jwt / jwt_verify | leeway=0.0, require_exp=true | Clock tolerance in seconds, and whether exp is required |
| same | iss="", aud="", keep="jwt" | Issuer/audience match when non-empty, and the name kept on the request |
| same | check=Callable() | Revocation check receiving claims after signature verification. When set, only true passes |
GD.web.sessions | total=1024, per_user=3 | Sessions per process, and per user |
| same | idle=1800, life=43200 | Idle and maximum lifetime in seconds |
| same | cookie="sid", keep="user" | Cookie name and the name kept on the request. The cookie name uses ASCII token characters |
GD.web.rate | limit=60, window=60.0 | Count per key and the fixed window in seconds |
| same | keys=10000, key=Callable() | Keys kept per process and the key selector |
| same | trusted_proxies=PackedStringArray() | IPs or CIDRs of proxies whose forwarded IP is trusted |
GD.web.csrf | allow_missing=false | Whether to allow state changes from clients without Fetch Metadata |
GD.web.text_rule | min=0, max=4096 | Text length in characters |
GD.web.int_rule | min=-9223372036854775808, max=9223372036854775807 | 64-bit integer range |
GD.web.number_rule | min=-1e308, max=1e308 | Finite float range |
GD.web.list_rule | min=0, max=1024 | Element count |
GD.web.object_rule | extra=false | Whether to keep undeclared fields |
GDWebApp.limits accepts only the six listed setting names and rejects misspellings and body_limit.
Numeric settings accept the following ranges. A value outside the range fails when set.
| Setting | Accepted range |
|---|---|
jobs | 0..2147483647. 0 is unlimited |
header_values, session total/per_user, rate limit/keys | 1..2147483647 |
job_timeout, header_timeout, body_timeout | Finite 0..9223372036.854776 seconds. 0 is unlimited |
session idle/life | 1..9223372036 seconds |
header_bytes | 1..2147479551 bytes. Separate from the body |
req.limit, GD.http.fetch.max_body | 0..9223372036854775807 bytes. 0 for max_body is unlimited |
ttl | 0 or more |
leeway | Finite, 0 or more |
The client returned by GD.database.client() handles SQLite and PostgreSQL with the same code. Switching from the embedded SQLite in local development to PostgreSQL in production is done through the driver passed to open().
func main():
var local := GD.cli.env("DB_DRIVER", "sqlite") == "sqlite"
var db := GD.database.client()
db.open({
"driver": "sqlite" if local else "postgres",
"path": "user://app.sqlite3",
"host": "127.0.0.1",
"database": "app",
"user": "app",
"password": GD.cli.env("PGPASSWORD", ""),
})?
db.query("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)")?
db.query("INSERT INTO users (id, name) VALUES ($1, $2) ON CONFLICT (id) DO NOTHING", [1, "ada"])?
var out := db.query("SELECT id, name FROM users WHERE id=$1", [1])?
print(out.rows[0].name)
db.close()
return 0, null
Table creation, INSERT, and SELECT all go through the one query(). It yields a dictionary with columns, rows, and tag, where rows is an array of dictionaries keyed by column name. In the example, out.rows[0].name is ada. SQL values are bound in order as $1, $2, and the spelling is the same on both drivers. SQL is not translated, so use SQL that works on both.
| Method | Purpose |
|---|---|
query(sql, args) | Collect and return the whole result |
query_row(sql, args) | Return only the first row. Err.NOT_FOUND when there is no row |
query_rows(sql, args) | Open GDDatabaseRows and read one row at a time. For large results |
stats() | Connection count, in use, idle, wait count, wait duration, and cumulative close counts by reason |
Advance query_rows() with while rows.next(). scan() returns a dictionary keyed by column name and values() returns an array in column order. After next() returns false, inspect err(). Call close() when stopping early.
func list_users(db):
var rows := db.query_rows("SELECT id, name FROM users ORDER BY id")?
while rows.next():
var user := rows.scan()?
print(user.id, " ", user.name)
if rows.err() != null:
return R.err(rows.err())
return R.ok()
On a constraint violation, result.e.info carries machine-readable details. violation is one of duplicate, not_null, or foreign_key, and columns lists the related column names. On PostgreSQL, code, table, and constraint are included when the server returns them. Values themselves are never kept in info. A failure reported by SQLite itself keeps source="sqlite" and its extended source_code. SQLite's foreign key message has no column names, so columns is empty there.
func save(db):
var saved := db.query(
"INSERT INTO users(id,name) VALUES($1,$2)",
[1, "ada"])
if not saved.ok and saved.e.info.get("violation") == "duplicate":
var columns := saved.e.info.get("columns", PackedStringArray())
print("duplicate columns: ", columns)
To make several updates one success or failure, use transaction(). The callback receives a GDDatabaseTx pinned to one connection. Returning a successful R commits, returning a failed R rolls back.
func save(db, id, title):
return db.transaction(func(tx):
tx.query("INSERT INTO posts(id,title) VALUES($1,$2)", [id, title])?
tx.query("UPDATE counters SET value=value+1 WHERE name='posts'")?
return R.ok(id)
)
tx in the callback and always return an R. During a transaction, query() on the original client and a nested transaction are rejected.tx no longer accepts new SQL.To apply a schema in order, pass an array of statements to migrate() instead of splitting SQL on semicolons. If one statement fails, everything rolls back. On success it returns the number of statements applied. Versions and checksums are managed by the application.
func migrate(db):
return db.migrate([
"CREATE TABLE posts(id INTEGER PRIMARY KEY, title TEXT NOT NULL)",
"CREATE INDEX posts_title ON posts(title)",
])
| Item | SQLite | PostgreSQL |
|---|---|---|
| Suited to | Local development, a single process | Production, crash resilience, several workers |
| Connection | One per client. Journal and temporary tables live in memory | A pool of up to max(4, CPU count) by default. Set a maximum as in pool=25 |
| Extra entries | GD.database.sqlite.open() for short work done in place | GD.database.postgres for batched sends, arrays, and JSONB |
| Notes | A database with existing -journal, -wal, or -shm files must be recovered or checkpointed with regular SQLite before opening | Hosts other than loopback verify the TLS certificate and host name by default. Loopback defaults to no TLS |
open() on GD.database.postgres.client() and GD.database.redis.client() takes the target as arguments, in the form open(host, port, opts).
query() calls on one client run in arrival order. Separate clients proceed concurrently, while writes to the same database file follow SQLite locking. The GDSQLiteDB and GDSQLiteStatement returned by GD.database.sqlite.open() are a synchronous API that runs directly on the caller. Use them only for short work and never concurrently. Use GDDatabaseClient for concurrent work.
query() is also sent onto a busy connection (pipelining). Results on one connection return in the order sent.query_rows() reserve one connection. For work that uses connection-local state, use the transaction API instead of sending a standalone BEGIN.query_many, fetch_many, or exec_many to send several SQL operations together on one connection.wait_count in stats() counts waits to acquire a connection and excludes response waits inside a pipeline.auth="scram" or auth="md5". MD5 is for older servers. A cleartext password needs explicit permission.bool[], int[], bigint[], and text[] preserve element types, nulls, and nested dimensions. Arrays with explicit lower bounds return the original text.A missing GET returns Err.NOT_FOUND with e.info.code == "nil"; null items inside an MGET Array remain null. pipeline() always returns an Array, including a single command. On a transport failure, known replies remain available and unknown positions contain Err. A WATCH abort returns Err.INTERRUPTED with e.info.code == "tx_failed".
timeout on open() sets the connect and response deadline in seconds.open() only configures the destination; network activity begins with the first query().size() counts connections, including those connecting; in_flight() counts unfinished calls, including acquisition waiters.The settings passed as a dictionary to open(), with their defaults.
| Entry | Setting and default | Meaning |
|---|---|---|
GDDatabaseClient.open | driver="postgres", path="" | Driver and SQLite path. SQLite needs user://... or :memory: |
| same | host="127.0.0.1", port=5432 | PostgreSQL target |
| same | pool=0 | PostgreSQL maximum connections. 0 means max(4, CPU count). Unused by SQLite |
| same | max_rows=0, max_bytes=0 | Rows and bytes per result collected by query(). 0 is unlimited. Not applied to query_rows() |
GDPostgresClient.open | user="postgres", database="postgres", password="" | Credentials and database name |
| same | connect_timeout=15.0, timeout=0.0 | Connect and query seconds. Waiting for a pool connection counts toward the query time. 0 is unlimited |
| same | auth="any", allow_cleartext_password=false | Pin the method with auth="scram"/"md5". A cleartext password reply only when explicit |
| same | tls=<decided by host>, ca="" | External hosts use verify-full, loopback disable. A CA file only when explicit |
GD.database.sqlite.open | busy_ms=5000, max_ms=0 | Lock wait and execution deadline in milliseconds. 0 is unlimited |
| same | max_rows=0, max_bytes=0 | Rows and bytes per result. 0 is unlimited |
GDRedisClient.open | password="", timeout=10.0 | Password, and connect and response deadline in seconds. 0 is unlimited |
| same | tls=<decided by host>, ca="" | The same TLS choice as PostgreSQL |
GD.database.postgres.pool | size default 0; 0 or 1..2147483647 | 0 means max(4, CPU count) |
GD.database.redis.pool | size default 0; 0..2147483647 | Maximum connections. 0 is unlimited. Connections being opened at once are capped at ten times the CPU count, or at the maximum when one is set |
GDRedisPool.open | pool_timeout=timeout+1.0 (30 seconds when timeout is 0) | Deadline for waiting for a free connection. An explicit 0 is unlimited |
A query() over max_rows or max_bytes fails only that query. The whole connection is closed when ordering is lost through a deadline or a corrupt reply.
| Setting | Accepted range |
|---|---|
max_rows, max_bytes, busy_ms, max_ms | 0..2147483647 |
| Bound values | 65535 for PostgreSQL, and the engine's variable limit for SQLite. query_many has no fixed item count |
| One PostgreSQL send | SQL and bound strings are counted as UTF-8 bytes, up to roughly 1 GiB |
| One Redis send | Server-configured limits apply |
| PostgreSQL and Redis port | 1..65535 |
| Seconds | Finite 0..9223372036.854776 seconds. 0 is unlimited |
A job that runs once at a fixed time is an ordinary script, called from the OS's cron or a systemd timer. gd needs no resident scheduler for it.
func collect():
var now := GD.time.to_iso(GD.time.now())
GD.file.append_text("store://log.txt", now + "\n")?
return 0, null
func main():
collect()?
return 0, null
gd --strict --mount store=/var/lib/app:rw collect.gd
A job that loops on its own interval is passed to GD.async.spawn() and kept resident with gd serve. Work passed to spawn() keeps running after main() returns.
func every(sec, fn):
while true:
await GD.async.sleep(sec)
fn.call()
func collect():
print(GD.time.to_iso(GD.time.now()))
func main():
var _job := GD.async.spawn(every.bind(60.0, collect))
return 0
gd serve schedule.gd
Stop it by ending the process. It is resident like a Web server, so serve is needed here too.
The core stays small. Features specific to an external service are added as GDScript packages or GDExtensions only to projects that need them.
| Entry | Purpose | API and setup |
|---|---|---|
Discord | Pure-GDScript text bots on the Discord Gateway and REST | Discord Bot |
GDMemcached | Cache client reusing TCP connections | Memcached |
GDSupabase | Database and Auth client | Supabase |
Each document lists public classes, methods, return values, limits, and strict-mode examples. Because they are optional, they are not part of the API reference generated from the core alone.
gd add are trusted and loaded at startup, so no flag is needed. --allow-net for their target is still needed.--allow-ext and --deny-ext apply when a script loads one while running with GDExtensionManager.load_extension().gd.lock and commit it.When scripts multiply or you start using other packages, create gd.json with gd init. Dependencies are pinned with gd.json and gd.lock.
gd init
gd search discord bot
gd add gd:@scope/script-package@^1.0.0
gd add ext:@scope/name@^1.0.0
gd add short-name https://example.com/module.gd
gd install --frozen
gd task test
An installed package is read from pkg://<alias>/, using the alias chosen by the consumer.
@import hello
pkg:// points into the per-user shared cache and copies nothing into the project.gd.json but absent from the cache is fetched on the first run. Under --strict the registry needs --allow-net.gd add is the package name with - and . turned into _, so it is an identifier. Aliases that are engine classes or keywords are refused.gd.json and gd.lock. gd init writes pkg/ into .gitignore.--frozen does not change the lock. For an offline target, fetch first where a network is available, and add --cached-only.@import is the short form of const Name = preload(...).
It is useful even without external dependencies: see samples/packages for a real registry dependency and its lockfile.
@import greet
@import "./util"
@import greet/style as theme
imports of gd.json. Files with the same name are not searched../ or ../. The .gd extension is optional. If a file and directory share a name, specify the file as "./util.gd".as, the identifier is the last segment as written, and a directory holding mod.gd binds its directory name.gd fmt keeps @import as it is.@import, so files shared with Godot should spell out const and preload.A package is one project rooted at its gd.json. gd init @scope/name seeds mod.gd and a test, gd test runs it, and gd publish releases it.
{"name":"@scope/hello","version":"1.0.0","main":"src/mod.gd","include":["src"]}
gd publish
gd add hello gd:@scope/hello@^1.0.0
mod.gd. For multiple files, list files or directories in include.imports of its own gd.json. gd publish records those imports in the registry.class_name may be published. Installation checks for conflicts between classes of the same name and rolls everything back on a conflict.godot to true in gd.json is the author's declaration that the package runs on upstream Godot without gd's own API, and gd search marks it [godot].A package under development is added from a local path with gd add ../path. Its alias comes from the name in its gd.json. The checkout is copied under pkg/<alias>/ and copied again on the next run whenever its content fingerprint changes. Files starting with ., pkg/, tmp/, subdirectories holding a gd.json, and token are not copied. gd publish turns a local import into its registry range when the target's gd.json has name and version, and refuses it otherwise.
gd install resolves the whole dependency graph. It picks a version in this order: the version gd.lock pins, a version already chosen this time that satisfies the range, then the newest match in the registry.
gd.lock also records the resolved imports configuration. A run after a configuration change uses the same resolver instead of silently loading an outdated version. --frozen rejects mismatched requests. When multiple aliases name one package, the lexicographically first alias selects its copy directory.
pkg://@scope/name@version/. pkg://<alias>/ expands to it through the imports of the package the script belongs to. The same alias may name different versions in different packages, and one version is one script however it is reached.gd.lock also records each package's resolved imports, and gd info lists them.gd remove and gd update drop what no package uses any more from gd.lock and pkg/.To share a project with tools that only read res://, such as upstream Godot, set "place": "project" in gd.json. Packages are copied under pkg/<alias>/, and both pkg:// and res://pkg/ point there. A package that only other packages use goes under pkg/@scope/name@version/.
project.godot exists, place defaults to project and no .gitignore is written. Commit pkg/ so teammates without gd can open the project.res:// references written in preload, load, and extends to the placement. Strings, comments, and paths built at run time are not rewritten.place only selects where files live. It does not convert gd's own API or syntax for Godot. Shared source should use standard syntax and relative preloads.pkg/<alias>/ whatever place is.ext:: the project's gd.json for project scripts, the package's own imports for package scripts.[classes] stops startup.gd compile on the target OS.gd.json has these ten settings.
| Name | Written by gd init / when omitted | Meaning |
|---|---|---|
name | my-tool / required | Project name. Publishing needs @scope/name |
version | 0.1.0 / required | Package version |
tasks | run and test / none | Commands invoked by gd task |
imports | {} / {} | Alias and dependency source. A published package may name registry packages only |
registry | omitted / environment or the public registry | Registry URL pinned to the project |
main | omitted / mod.gd | mod.gd or .gdextension entry published |
include | omitted / main only | Files or directories inside the main directory included in a pure GDScript package |
place | omitted / cache, or project beside project.godot | Where packages live. project copies them under pkg/ |
godot | omitted / false | Declares a package that runs on upstream Godot without gd's own API |
description | omitted / empty | Description shown in the registry |
gd reads these environment variables. A script that reads the environment needs the names allowed with --allow-env.
| Variable | Purpose |
|---|---|
GD_CACHE_HOME | Package cache root. Defaults to gd in Windows LocalAppData. On macOS/Linux, uses an absolute XDG_CACHE_HOME plus /gd, or .gd under the home directory. The OS account directory is used when HOME is unset |
GD_REGISTRY | Registry. Defaults to https://gd.progsha.com/pkg. registry in gd.json wins |
GD_TOKEN | Publish token. Keep it out of config files and pass it only to the publishing process |
LC_ALL, LANG | Language of the manual shown by gd doc |
GD_WORKER | Internal mark set by --workers. Not a user setting |
Remote packages and registries use HTTPS. A development registry on loopback may also use HTTP. Fetched packages and native libraries are checked against the SHA-256 in the registry index. A .gdextension manifest is limited to 16 MiB, and all files in a package to 500 MiB in total.
compile collects scripts, views, static files, migrations, dependency packages, and the target OS's GDExtensions into one executable. The target needs no cache.
gd compile -o app main.gd
./app
gd.json names, and every package those import, is embedded../app serve --no-scene-tree --allow-net.. such as .env and the token in gd.json are left out..env, but values written in source remain in the executable.gd is a public release before the API has settled. Do not assume backward compatibility. Changes and the Godot version used as the base are recorded in the CHANGELOG. gd is not an official product of the Godot Foundation or the Godot Engine project.
Report bugs in Issues. Report vulnerabilities that should not be public through GitHub private reporting.
gd固有の入口と、そこから返るobjectの署名です。Godot由来の型はGodotのclass referenceを参照してください。
Signatures of gd's own entry points and of the objects they return. For Godot classes, see the Godot class reference.
一致するAPIがありません。No matching API.
file、data、非同期処理、日時など、端末programの標準機能をまとめる入口。
Entry to the standard features of a command-line program: files, data, async, time, and more.
extends Object
async: GD.asynclog: GD.lognet: GD.nethttp: GD.httpweb: GD.webfile: GD.filecollection: GD.collectiondata: GD.dataid: GD.idtext: GD.texthtml: GD.htmlmath: GD.mathversion: GD.versiontime: GD.timecli: GD.clitest: GD.testdatabase: GD.databaseWebサイトとWeb APIを作る入口。route、静的file、gdhtml雛形、session、CSRF、JWT、rate limit、入力検査を持つ。
Entry for building websites and Web APIs, with routes, static files, gdhtml templates, sessions, CSRF, JWT, rate limiting, and input validation.
extends Object
設定Settings: ttl leeway require_exp iss aud keep total per_user idle life cookie limit keys window key allow_missing min max extra
app() -> GDWebAppserver() -> GDWebServertext(body: String, status: int = 200) -> Dictionaryhtml(body: String, status: int = 200) -> Dictionaryjson(data: Variant, status: int = 200) -> Dictionary, Errbytes(body: PackedByteArray, type: String = "application/octet-stream", status: int = 200) -> Dictionarystream(producer: Callable, length: int = -1, type: String = "application/octet-stream", status: int = 200) -> Dictionaryredirect(to: String, status: int = 302, away: bool = false) -> Dictionaryguard(reply: Dictionary) -> Dictionarynot_found(msg: String = "Not Found") -> Dictionaryheader(reply: Dictionary, name: String, value: Variant) -> Dictionaryadd_header(reply: Dictionary, name: String, value: Variant) -> Dictionaryview(path: String, data: Dictionary = { }, status: int = 200, renderer: Callable = null) -> Dictionary, Errview_async(path: String, data: Dictionary = { }, status: int = 200, renderer: Callable = null) -> await Dictionary, Errjson_async(data: Variant, status: int = 200) -> await Dictionary, Errerror_status(error: Err) -> intjwt(key: Variant, opts: Dictionary = { }) -> GDWebMiddlewarejwt_sign(claims: Dictionary, key: Variant, opts: Dictionary = { }) -> String, Errjwt_verify(token: String, key: Variant, opts: Dictionary = { }) -> Dictionary, Errjwt_sign_async(claims: Dictionary, key: Variant, opts: Dictionary = { }) -> await String, Errjwt_verify_async(token: String, key: Variant, opts: Dictionary = { }) -> await Dictionary, Errcsrf(opts: Dictionary = { }) -> GDWebMiddlewaresessions(opts: Dictionary = { }) -> GDWebSessionStorerate(opts: Dictionary = { }) -> GDWebMiddlewaretext_rule(opts: Dictionary = { }) -> Dictionaryint_rule(opts: Dictionary = { }) -> Dictionarynumber_rule(opts: Dictionary = { }) -> Dictionarybool_rule() -> Dictionarylist_rule(item: Dictionary = { }, opts: Dictionary = { }) -> Dictionaryobject_rule(fields: Dictionary, opts: Dictionary = { }) -> Dictionaryoptional(rule: Dictionary, fallback: Variant = null) -> Dictionaryone_of(values: Array) -> Dictionaryvalidate(value: Variant, rule: Dictionary) -> Variant, Errvalidate_async(value: Variant, rule: Dictionary) -> await Variant, Errjson_body(rule: Dictionary, name: String = "body") -> GDWebMiddlewarequery(rule: Dictionary, name: String = "query") -> GDWebMiddlewareparams(rule: Dictionary, name: String = "params") -> GDWebMiddlewareSQLiteとPostgreSQLで共通に使えるdatabase入口。同期形の呼出しでも待つGDScriptだけを中断し、*_asyncは合成用Signalを返す。
Database entry shared by SQLite and PostgreSQL. Synchronous-looking calls suspend only their GDScript; *_async returns Signals for composition.
extends Object
sqlite: GD.database.sqlitepostgres: GD.database.postgresredis: GD.database.redisclient() -> GDDatabaseClient待機、並行実行、競争、timeout、打ち切りを扱う非同期処理の入口。spawnはmain threadのコルーチン開始器であり、CPU処理は各標準moduleの_async入口へ渡す。
Entry for async work: waiting, concurrency, racing, timeouts, and cancellation. spawn starts a main-thread coroutine; CPU work belongs in each standard module's _async entry points.
extends Object
set_max_threads(max: int) -> intsleep(sec: float) -> Signalspawn(fn: Callable) -> Signalall(signals: Array) -> await Arrayrace(signals: Array) -> await intwith_timeout(signal: Signal, sec: float) -> await intwith_context(context: GDAsyncContext, signal: Signal) -> await Variantcontext() -> GDAsyncContextlevel、時刻、出力先を揃え、順序付きI/O列で一行ずつ記録する入口。各呼出しは書込完了を待ちRを返す。flushで先行出力の完了を待てる。
Entry for ordered I/O of one line at a time with a consistent level, time, and destination. flush waits for earlier output.
extends Object
setup(name: String = "", level: String = "info") -> voidset_file(path: String) -> voidset_time(on: bool) -> voidset_color(on: bool) -> voidwrite(level: String, msg: String, extra: Variant = null) -> Variant, Errdebug(msg: String, extra: Variant = null) -> Variant, Errinfo(msg: String, extra: Variant = null) -> Variant, Errwarn(msg: String, extra: Variant = null) -> Variant, Errerror(msg: String, extra: Variant = null) -> Variant, Errresult(r: R, msg: String) -> Variant, Errwrite_async(level: String, msg: String, extra: Variant = null) -> await Variant, Errdebug_async(msg: String, extra: Variant = null) -> await Variant, Errinfo_async(msg: String, extra: Variant = null) -> await Variant, Errwarn_async(msg: String, extra: Variant = null) -> await Variant, Errerror_async(msg: String, extra: Variant = null) -> await Variant, Errresult_async(r: R, msg: String) -> await Variant, Errformat(level: String, msg: String, extra: Variant = null) -> Stringflush() -> Variant, Errflush_async() -> await Variant, ErrTCP・TLS stream、listener、UDP packet通信と、名前解決、address検査を提供するnetwork入口。TLSは既定で証明書の鎖と宛名を検証し、待つ操作は呼出し元だけを中断する。
Network entry for TCP and TLS streams, listeners, UDP packets, name resolution, and address checks. TLS verifies the certificate chain and server name by default; waiting operations suspend only their caller.
extends Object
is_free(port: int, host: String = "127.0.0.1") -> boolfree_port(from: int = 0, host: String = "127.0.0.1") -> int, Errresolve(host: String) -> String, Errresolve_async(host: String) -> await String, Errdial_tcp(host: String, port: int, opts: Dictionary = { }) -> GDTCPConn, Errdial_tcp_async(host: String, port: int, opts: Dictionary = { }) -> await GDTCPConn, Errdial_tls(host: String, port: int, opts: Dictionary = { }) -> GDTCPConn, Errdial_tls_async(host: String, port: int, opts: Dictionary = { }) -> await GDTCPConn, Errlisten_tcp(host: String = "127.0.0.1", port: int = 0) -> GDTCPListener, Errlisten_udp(host: String = "127.0.0.1", port: int = 0, buffer: int = 0) -> GDUDPPacketConn, Erris_free_async(port: int, host: String = "127.0.0.1") -> await boolfree_port_async(from: int = 0, host: String = "127.0.0.1") -> await int, Errlocal_addresses() -> PackedStringArray, Errlocal_addresses_async() -> await PackedStringArray, Erris_ip(text: String) -> boolsplit_host(text: String, default_port: int = 80) -> DictionaryHTTP requestを送り、URLとquery、媒体型を組み立てて解釈する入口。fetchは呼出し元だけを待たせ、fetch_asyncは並行処理用Signalを返す。
Entry for HTTP requests plus URL, query, and media-type handling. fetch suspends only its caller; fetch_async returns a Signal for composition.
extends Object
設定Settings: timeout max_body method authority headers body save sha256
fetch(url: String, opts: Dictionary = { }) -> GDHTTPResponse, Errfetch_async(url: String, opts: Dictionary = { }) -> await GDHTTPResponse, Errparse_url(raw: String) -> Dictionary, Errparse_url_async(raw: String) -> await Dictionary, Errbuild_url(url: Dictionary) -> Stringbuild_url_async(url: Dictionary) -> await Stringrequest_target(url: Dictionary) -> Stringrequest_target_async(url: Dictionary) -> await Stringdefault_port(scheme: String) -> intdecode_query(raw: String) -> Dictionary, Errdecode_query_async(raw: String) -> await Dictionary, Errencode_query(query: Dictionary) -> Stringencode_query_async(query: Dictionary) -> await Stringmedia_type(path: String) -> Stringmedia_type_for_extension(ext: String) -> Stringis_textual(kind: String) -> boolextension_for_media_type(kind: String) -> Stringfile全量操作と逐次streamは呼出し元だけを待たせ、path操作とfile形式の読込みも扱う入口。*_asyncは並行開始用Signalを返す。
Whole-file operations and streaming file I/O suspend only their caller. This entry also provides path operations and file-format readers; *_async returns Signals for concurrent starts.
extends Object
open(path: String, mode: String = "read") -> GDFileStream, Erropen_async(path: String, mode: String = "read") -> await GDFileStream, Errread_text(path: String) -> String, Errread_bytes(path: String, offset: int = 0, max: int = 0) -> PackedByteArray, Errwrite_text(path: String, body: String) -> Variant, Errreplace_text(path: String, old: Variant, body: String) -> Variant, Errwrite_bytes(path: String, body: PackedByteArray) -> Variant, Errappend_bytes(path: String, body: PackedByteArray) -> Variant, Errappend_text(path: String, body: String) -> Variant, Errexists(path: String) -> boolremove(path: String) -> Variant, Errsize_of(path: String) -> int, Errcopy(src: String, dst: String) -> Variant, Errrename(src: String, dst: String) -> Variant, Errlist_dir(path: String) -> Array, Errmake_dir(path: String) -> Variant, Errensure_dir(path: String) -> Variant, Errwalk(path: String, want_dirs: bool = false, hidden: bool = false) -> Array, Errglob(path: String, pattern: String, hidden: bool = false) -> Array, Errremove_all(path: String) -> Variant, Errcan_read(path: String) -> boolread_text_async(path: String) -> await String, Errread_bytes_async(path: String, offset: int = 0, max: int = 0) -> await PackedByteArray, Errwrite_text_async(path: String, body: String) -> await Variant, Errwrite_bytes_async(path: String, body: PackedByteArray) -> await Variant, Errappend_text_async(path: String, body: String) -> await Variant, Errappend_bytes_async(path: String, body: PackedByteArray) -> await Variant, Errcopy_async(src: String, dst: String) -> await Variant, Errreplace_text_async(path: String, old: Variant, body: String) -> await Variant, Errexists_async(path: String) -> await boolremove_async(path: String) -> await Variant, Errsize_of_async(path: String) -> await int, Errrename_async(src: String, dst: String) -> await Variant, Errlist_dir_async(path: String) -> await Array, Errmake_dir_async(path: String) -> await Variant, Errensure_dir_async(path: String) -> await Variant, Errwalk_async(path: String, want_dirs: bool = false, hidden: bool = false) -> await Array, Errglob_async(path: String, pattern: String, hidden: bool = false) -> await Array, Errremove_all_async(path: String) -> await Variant, Errcan_read_async(path: String) -> await boolparse_path(path: String) -> Dictionaryformat_path(parts: Dictionary) -> Stringjoin(parts: PackedStringArray) -> Stringdirname(path: String) -> Stringbasename(path: String, suffix: String = "") -> Stringextname(path: String) -> Stringis_absolute(path: String) -> boolnormalize(path: String) -> Stringrelative(from: String, to: String) -> Stringunder(dir: String, name: String) -> Stringcreate_tar(root: String) -> PackedByteArray, Errextract_tar(data: PackedByteArray, root: String) -> int, Errread_csv(path: String, sep: String = ",") -> Array, Errread_ini(path: String) -> Dictionary, Errread_toml(path: String) -> Dictionary, Errread_yaml(path: String) -> Variant, Errread_jsonc(path: String) -> Variant, Errread_jsonl(path: String) -> Array, Errread_front_matter(path: String) -> Dictionary, Errread_xml(path: String) -> Dictionary, Errread_env(path: String = ".env") -> Dictionary, Errread_tar(path: String) -> Array, Errread_csv_async(path: String, sep: String = ",") -> await Array, Errread_ini_async(path: String) -> await Dictionary, Errread_toml_async(path: String) -> await Dictionary, Errread_yaml_async(path: String) -> await Variant, Errread_jsonc_async(path: String) -> await Variant, Errread_jsonl_async(path: String) -> await Array, Errread_front_matter_async(path: String) -> await Dictionary, Errread_xml_async(path: String) -> await Dictionary, Errread_env_async(path: String = ".env") -> await Dictionary, Errread_tar_async(path: String) -> await Array, Errcreate_tar_async(root: String) -> await PackedByteArray, Errextract_tar_async(data: PackedByteArray, root: String) -> await int, ErrArrayとDictionaryをまとめ直し、heap、queue、cacheを作る入口。待機可能な処理はCPU workerまたはruntimeのready queueで進み、Callable処理もSceneTreeのframeを必要とせず他のtaskへ実行を譲る。
Reshape Arrays and Dictionaries and create heaps, queues, and caches. Awaitable operations use CPU workers or the runtime ready queue; callable work cooperates with other tasks without requiring SceneTree frames.
extends Object
group_by(items: Array, key: Callable) -> Dictionarymap_values(src: Dictionary, fn: Callable) -> Dictionaryfilter_keys(src: Dictionary, pred: Callable) -> Dictionarypartition(items: Array, pred: Callable) -> Arraychunk(items: Array, size: int) -> Arrayunique_by(items: Array, key: Callable) -> Arrayunique(items: Array) -> Arraysort_by(items: Array, pick: Callable) -> Arraysort_key(items: Array, key: String) -> Arrayzip(a: Array, b: Array) -> Arraysum_of(items: Array, pick: Callable) -> floatmax_by(items: Array, pick: Callable) -> Variantmin_by(items: Array, pick: Callable) -> Variantindex_by(items: Array, key: Callable) -> Dictionarydeep_merge(base: Dictionary, over: Dictionary) -> Dictionarybinary_heap(pick: Callable = null) -> GDBinaryHeappriority_queue() -> GDPriorityQueuelru_cache(limit: int = 128, ttl_ms: int = 0) -> GDLRUCachememo(fn: Callable, limit: int = 128) -> GDMemoizedCallablegroup_by_async(items: Array, key: Callable) -> await Dictionarymap_values_async(src: Dictionary, fn: Callable) -> await Dictionaryfilter_keys_async(src: Dictionary, pred: Callable) -> await Dictionarypartition_async(items: Array, pred: Callable) -> await Arrayunique_by_async(items: Array, key: Callable) -> await Arraysort_by_async(items: Array, pick: Callable) -> await Arraysum_of_async(items: Array, pick: Callable) -> await floatmax_by_async(items: Array, pick: Callable) -> await Variantmin_by_async(items: Array, pick: Callable) -> await Variantindex_by_async(items: Array, key: Callable) -> await Dictionarychunk_async(items: Array, size: int) -> await Arrayunique_async(items: Array) -> await Arraysort_key_async(items: Array, key: String) -> await Arrayzip_async(a: Array, b: Array) -> await Arraydeep_merge_async(base: Dictionary, over: Dictionary) -> await Dictionarybyte列、binary codec、hash、鍵導出と、CSV・INI・TOML・YAML・JSON・XMLなどmemory上のdata変換を扱う入口。不正な入力と設定は失敗として返す。
Data conversion for bytes, binary codecs, hashes, key derivation, and in-memory CSV, INI, TOML, YAML, JSON, XML, and related formats. Invalid input and settings return failures.
extends Object
gzip_writer(writer: RefCounted, level: int = -1) -> GDGzipWriter, Errsha224(msg: PackedByteArray) -> PackedByteArraysha256(msg: PackedByteArray) -> PackedByteArraysha384(msg: PackedByteArray) -> PackedByteArraysha512(msg: PackedByteArray) -> PackedByteArraysha3_224(msg: PackedByteArray) -> PackedByteArraysha3_256(msg: PackedByteArray) -> PackedByteArraysha3_384(msg: PackedByteArray) -> PackedByteArraysha3_512(msg: PackedByteArray) -> PackedByteArraysha1(msg: PackedByteArray) -> PackedByteArrayhmac(hash: String, key: PackedByteArray, msg: PackedByteArray) -> PackedByteArray, Errhmac_sha256(key: PackedByteArray, msg: PackedByteArray) -> PackedByteArrayequal_ct(a: PackedByteArray, b: PackedByteArray) -> boolpbkdf2_sha256(password: PackedByteArray, salt: PackedByteArray, rounds: int) -> PackedByteArray, Errpbkdf2(hash: String, password: PackedByteArray, salt: PackedByteArray, rounds: int, size: int) -> PackedByteArray, Errhkdf(hash: String, secret: PackedByteArray, salt: PackedByteArray, info: PackedByteArray, size: int) -> PackedByteArray, Errhkdf_extract(hash: String, secret: PackedByteArray, salt: PackedByteArray) -> PackedByteArray, Errhkdf_expand(hash: String, key: PackedByteArray, info: PackedByteArray, size: int) -> PackedByteArray, Errpbkdf2_sha256_async(password: PackedByteArray, salt: PackedByteArray, rounds: int) -> await PackedByteArray, Errpbkdf2_async(hash: String, password: PackedByteArray, salt: PackedByteArray, rounds: int, size: int) -> await PackedByteArray, Errhkdf_async(hash: String, secret: PackedByteArray, salt: PackedByteArray, info: PackedByteArray, size: int) -> await PackedByteArray, Errhkdf_extract_async(hash: String, secret: PackedByteArray, salt: PackedByteArray) -> await PackedByteArray, Errhkdf_expand_async(hash: String, key: PackedByteArray, info: PackedByteArray, size: int) -> await PackedByteArray, Errsha224_async(msg: PackedByteArray) -> await PackedByteArraysha256_async(msg: PackedByteArray) -> await PackedByteArraysha384_async(msg: PackedByteArray) -> await PackedByteArraysha512_async(msg: PackedByteArray) -> await PackedByteArraysha3_224_async(msg: PackedByteArray) -> await PackedByteArraysha3_256_async(msg: PackedByteArray) -> await PackedByteArraysha3_384_async(msg: PackedByteArray) -> await PackedByteArraysha3_512_async(msg: PackedByteArray) -> await PackedByteArraysha1_async(msg: PackedByteArray) -> await PackedByteArrayhmac_async(hash: String, key: PackedByteArray, msg: PackedByteArray) -> await PackedByteArray, Errhmac_sha256_async(key: PackedByteArray, msg: PackedByteArray) -> await PackedByteArrayequal_ct_async(a: PackedByteArray, b: PackedByteArray) -> await boolcsv(src: String, sep: String = ",") -> Array, Errto_csv(rows: Array, sep: String = ",") -> Stringcsv_objects(src: String, sep: String = ",") -> Array, Errto_csv_objects(items: Array, sep: String = ",") -> Stringini(src: String) -> Dictionary, Errto_ini(data: Dictionary) -> Stringtoml(src: String) -> Dictionary, Errto_toml(data: Dictionary, prefix: String = "") -> String, Erryaml(src: String) -> Variant, Errto_yaml(data: Variant, depth: int = 0) -> String, Errjsonc(src: String) -> Variant, Errstrip_jsonc(src: String) -> Stringjsonl(src: String) -> Array, Errto_jsonl(items: Array) -> String, Errjsonl_reader() -> GDJSONLReaderfront_matter(src: String) -> Dictionary, Errhas_front_matter(src: String) -> boolto_front_matter(attrs: Dictionary, body: String, kind: String = "yaml") -> String, Errxml(src: String) -> Dictionary, Errto_xml(data: Dictionary, indent: int = 0) -> String, Errenv(src: String) -> Dictionary, Errto_env(data: Dictionary) -> String, Errtar(entries: Array) -> PackedByteArray, Erruntar(data: PackedByteArray) -> Array, Errjson_encode(value: Variant, opts: Dictionary = { }) -> PackedByteArray, Errjson_decode(data: PackedByteArray) -> Variant, Errmsgpack(value: Variant) -> PackedByteArray, Errunmsgpack(data: PackedByteArray) -> Variant, Errcbor(value: Variant) -> PackedByteArray, Erruncbor(data: PackedByteArray) -> Variant, Errjson_encode_async(value: Variant, opts: Dictionary = { }) -> await PackedByteArray, Errjson_decode_async(data: PackedByteArray) -> await Variant, Errmsgpack_async(value: Variant) -> await PackedByteArray, Errunmsgpack_async(data: PackedByteArray) -> await Variant, Errcbor_async(value: Variant) -> await PackedByteArray, Erruncbor_async(data: PackedByteArray) -> await Variant, Errcsv_async(src: String, sep: String = ",") -> await Array, Errto_csv_async(rows: Array, sep: String = ",") -> await Stringcsv_objects_async(src: String, sep: String = ",") -> await Array, Errto_csv_objects_async(items: Array, sep: String = ",") -> await Stringini_async(src: String) -> await Dictionary, Errto_ini_async(data: Dictionary) -> await Stringtoml_async(src: String) -> await Dictionary, Errto_toml_async(data: Dictionary, prefix: String = "") -> await String, Erryaml_async(src: String) -> await Variant, Errto_yaml_async(data: Variant, depth: int = 0) -> await String, Errjsonc_async(src: String) -> await Variant, Errstrip_jsonc_async(src: String) -> await Stringjsonl_async(src: String) -> await Array, Errto_jsonl_async(items: Array) -> await String, Errfront_matter_async(src: String) -> await Dictionary, Errto_front_matter_async(attrs: Dictionary, body: String, kind: String = "yaml") -> await String, Errxml_async(src: String) -> await Dictionary, Errto_xml_async(data: Dictionary, indent: int = 0) -> await String, Errenv_async(src: String) -> await Dictionary, Errto_env_async(data: Dictionary) -> await String, Errtar_async(entries: Array) -> await PackedByteArray, Erruntar_async(data: PackedByteArray) -> await Array, Errhex_encode(data: PackedByteArray) -> Stringhex_decode(text: String) -> PackedByteArray, Errbase64_encode(data: PackedByteArray) -> Stringbase64_decode(text: String, raw: bool = false) -> PackedByteArray, Errbase64url_encode(data: PackedByteArray) -> Stringbase64url_decode(text: String, raw: bool = true) -> PackedByteArray, Errbase32_encode(data: PackedByteArray) -> Stringbase32_decode(text: String, raw: bool = false) -> PackedByteArray, Errvarint_encode(n: int) -> PackedByteArrayvarint_decode(data: PackedByteArray, at: int = 0) -> Dictionary, Errhex_encode_async(data: PackedByteArray) -> await Stringhex_decode_async(text: String) -> await PackedByteArray, Errbase64_encode_async(data: PackedByteArray) -> await Stringbase64_decode_async(text: String, raw: bool = false) -> await PackedByteArray, Errbase64url_encode_async(data: PackedByteArray) -> await Stringbase64url_decode_async(text: String, raw: bool = true) -> await PackedByteArray, Errbase32_encode_async(data: PackedByteArray) -> await Stringbase32_decode_async(text: String, raw: bool = false) -> await PackedByteArray, Errconcat(parts: Array) -> PackedByteArrayequals(a: PackedByteArray, b: PackedByteArray) -> boolincludes(hay: PackedByteArray, needle: PackedByteArray) -> boolindex_of(hay: PackedByteArray, needle: PackedByteArray, from: int = 0) -> intlast_index_of(hay: PackedByteArray, needle: PackedByteArray) -> intstarts_with(hay: PackedByteArray, prefix: PackedByteArray) -> boolends_with(hay: PackedByteArray, suffix: PackedByteArray) -> boolrepeat(src: PackedByteArray, times: int) -> PackedByteArrayfit(src: PackedByteArray, size: int) -> PackedByteArraysplit(src: PackedByteArray, sep: PackedByteArray) -> Arrayxor_bytes(a: PackedByteArray, b: PackedByteArray) -> PackedByteArrayconcat_async(parts: Array) -> await PackedByteArrayequals_async(a: PackedByteArray, b: PackedByteArray) -> await boolincludes_async(hay: PackedByteArray, needle: PackedByteArray) -> await boolindex_of_async(hay: PackedByteArray, needle: PackedByteArray, from: int = 0) -> await intlast_index_of_async(hay: PackedByteArray, needle: PackedByteArray) -> await intstarts_with_async(hay: PackedByteArray, prefix: PackedByteArray) -> await boolends_with_async(hay: PackedByteArray, suffix: PackedByteArray) -> await boolrepeat_async(src: PackedByteArray, times: int) -> await PackedByteArrayfit_async(src: PackedByteArray, size: int) -> await PackedByteArraysplit_async(src: PackedByteArray, sep: PackedByteArray) -> await Arrayxor_bytes_async(a: PackedByteArray, b: PackedByteArray) -> await PackedByteArrayUUIDと時刻順ULIDを生成、検査する入口。
Entry for generating and checking UUIDs and time-ordered ULIDs.
extends Object
ulid(ms: int = -1) -> Stringis_ulid(text: String) -> boolulid_time(text: String) -> int, Erruuid() -> Stringuuid_v5(space: String, name: String) -> String, Erruuid_v5_async(space: String, name: String) -> await String, Erris_uuid(text: String) -> booluuid_bytes(text: String) -> PackedByteArray, Erruuid_version(text: String) -> intnil_uuid() -> Stringdns_namespace() -> Stringurl_namespace() -> Stringoid_namespace() -> String文字の整形と比較を扱う入口。
Entry for formatting and comparing text.
extends Object
closest(word: String, options: PackedStringArray) -> Stringellipsis(text: String, width: int) -> Stringsize_of(bytes: int) -> Stringduration(ms: float) -> Stringdistance(a: String, b: String) -> intsnake(text: String) -> Stringcamel(text: String) -> Stringtitle(text: String) -> Stringtable(rows: Array, gap: int = 2) -> Stringdistance_async(a: String, b: String) -> await intclosest_async(word: String, options: PackedStringArray) -> await Stringellipsis_async(text: String, width: int) -> await Stringsnake_async(text: String) -> await Stringcamel_async(text: String) -> await Stringtitle_async(text: String) -> await Stringtable_async(rows: Array, gap: int = 2) -> await StringHTML entity、tag、文脈安全なtemplateを扱う入口。本文、属性、URL、style、scriptに応じて値をescapeする。
Entry for HTML entities, tags, and templates with context-sensitive escaping for text, attributes, URLs, styles, and scripts.
extends Object
fill(tpl: String, data: Dictionary, partials: Dictionary = { }) -> String, Errtemplate(tpl: String, partials: Dictionary = { }) -> GDHTMLTemplate, Errattr(value: String) -> Stringtag(name: String, body: String, attrs: Dictionary = { }) -> Stringescape(text: String) -> Stringunescape(text: String) -> Stringescape_async(text: String) -> await Stringunescape_async(text: String) -> await Stringattr_async(value: String) -> await Stringtag_async(name: String, body: String, attrs: Dictionary = { }) -> await Stringfill_async(tpl: String, data: Dictionary, partials: Dictionary = { }) -> await String, Err標準math packageに対応する数学関数とIEEE 754の特殊値、bit変換を扱う入口。
Entry for standard mathematical functions, IEEE 754 special values, and bit conversions.
extends Object
bits: GD.math.bitse: floatpi: floatphi: floatsqrt2: floatsqrt_e: floatsqrt_pi: floatsqrt_phi: floatln2: floatlog2_e: floatln10: floatlog10_e: floatmax_float64: floatsmallest_nonzero_float64: floatabs(x: float) -> floatacos(x: float) -> floatacosh(x: float) -> floatasin(x: float) -> floatasinh(x: float) -> floatatan(x: float) -> floatatanh(x: float) -> floatcbrt(x: float) -> floatceil(x: float) -> floatcos(x: float) -> floatcosh(x: float) -> floaterf(x: float) -> floaterfc(x: float) -> floaterfinv(x: float) -> floaterfcinv(x: float) -> floatexp(x: float) -> floatexp2(x: float) -> floatexpm1(x: float) -> floatfloor(x: float) -> floatgamma(x: float) -> floatilogb(x: float) -> intj0(x: float) -> floatj1(x: float) -> floatlog(x: float) -> floatlog1p(x: float) -> floatlog2(x: float) -> floatlog10(x: float) -> floatlogb(x: float) -> floatround(x: float) -> floatround_to_even(x: float) -> floatsignbit(x: float) -> boolsin(x: float) -> floatsinh(x: float) -> floatsqrt(x: float) -> floattan(x: float) -> floattanh(x: float) -> floattrunc(x: float) -> floaty0(x: float) -> floaty1(x: float) -> floatfloat64_bits(x: float) -> intfloat64_from_bits(x: int) -> floatfloat32_bits(x: float) -> intfloat32_from_bits(x: int) -> floatnan() -> floatinf(sign: int) -> floatis_inf(x: float, sign: int = 0) -> boolis_nan(x: float) -> boolatan2(y: float, x: float) -> floatcopysign(value: float, sign: float) -> floatdim(x: float, y: float) -> floatfma(x: float, y: float, z: float) -> floathypot(x: float, y: float) -> floatldexp(frac: float, exp: int) -> floatmax(x: float, y: float) -> floatmin(x: float, y: float) -> floatmod(x: float, y: float) -> floatnextafter(x: float, y: float) -> floatnextafter32(x: float, y: float) -> floatpow(x: float, y: float) -> floatpow10(n: int) -> floatremainder(x: float, y: float) -> floatfrexp(x: float) -> Arraylgamma(x: float) -> Arraymodf(x: float) -> Arraysincos(x: float) -> ArraySemantic Versionを解釈し、比較、範囲判定する入口。
Entry for parsing, comparing, and range-matching Semantic Versions.
extends Object
parse(raw: String) -> Dictionary, Erris_canonical(raw: String) -> boolcompare(a: Dictionary, b: Dictionary) -> intis_stable(v: Dictionary) -> booltext(v: Dictionary) -> Stringbest(list: PackedStringArray, range: String = "*") -> Dictionary, Errbest_async(list: PackedStringArray, range: String = "*") -> await Dictionary, Errsatisfies(v: Dictionary, range: String) -> boolUnix時刻、ISO 8601、HTTP日付を変換、計算する入口。parse_isoはRFC 3339を受け、加算と差分は64 bit整数の端で止まる。
Entry for converting and computing Unix time, ISO 8601, and HTTP dates. parse_iso accepts RFC 3339, and add and diff clamp at the 64-bit integer ends.
extends Object
now() -> intto_parts(unix: int) -> Dictionaryfrom_parts(parts: Dictionary) -> intto_iso(unix: int) -> Stringto_http(unix: int) -> Stringparse_iso(text: String) -> int, Erradd(unix: int, amount: int, unit: String = "second") -> intdiff(a: int, b: int, unit: String = "second") -> intstart_of_day(unix: int) -> intweekday(unix: int) -> intis_leap(year: int) -> booldays_in_month(year: int, month: int) -> intago(unix: int, base: int = -1) -> Stringformat(unix: int, pattern: String) -> Stringgdの版と文書の入口、flag、許可された環境とsystem情報を読む入口。
Entry for gd's version and docs, flags, and allowed environment and system information.
extends Object
version() -> Stringdocs_url() -> Stringflags() -> GDCLIFlagsenv(name: String, fallback: Variant = null) -> Variantrequire_env(name: String) -> String, Errcwd() -> Stringplatform() -> Stringarch() -> Stringstdin_tty() -> boolstdout_tty() -> boolstderr_tty() -> boolpaint(text: String, code: int) -> Stringred(text: String) -> Stringgreen(text: String) -> Stringyellow(text: String) -> Stringblue(text: String) -> Stringgray(text: String) -> Stringbold(text: String) -> Stringrun(path: String, args: PackedStringArray = [], opts: Dictionary = { }) -> Dictionary, Errrun_async(path: String, args: PackedStringArray = [], opts: Dictionary = { }) -> await Dictionary, Err小さなassertを集めるtest補助の入口。
Entry for small test helpers that collect asserts.
extends Object
check() -> GDTestCheck同時利用可能なTCPまたはTLS byte stream。FIFOのread/write、期限、Closeによる待機解除、addressを持つ。
Concurrency-safe TCP or TLS byte stream with FIFO reads and writes, deadlines, Close wakeups, and addresses.
extends RefCounted
read(max: int = 65536) -> PackedByteArray, Errread_async(max: int = 65536) -> await PackedByteArray, Errwrite(data: PackedByteArray) -> int, Errwrite_async(data: PackedByteArray) -> await int, Errset_deadline(seconds: float) -> Variant, Errset_read_deadline(seconds: float) -> Variant, Errset_write_deadline(seconds: float) -> Variant, Errlocal_addr() -> Dictionaryremote_addr() -> Dictionaryconnection_state() -> Dictionaryis_open() -> boolclose() -> voidTCP接続を受け付けるlistener。期限とCloseによる受付待ち解除を持つ。
TCP listener with deadlines and Close wakeups for pending accepts.
extends RefCounted
accept() -> GDTCPConn, Erraccept_async() -> await GDTCPConn, Errset_deadline(seconds: float) -> Variant, Erraddr() -> Dictionaryis_open() -> boolclose() -> voidpacket境界と送信元addressを保つUDP通信口。期限とCloseによる待機解除を持つ。
UDP endpoint preserving packet boundaries and source addresses, with deadlines and Close wakeups.
extends RefCounted
read_from(max: int = 65536) -> Dictionary, Errread_from_async(max: int = 65536) -> await Dictionary, Errwrite_to(data: PackedByteArray, host: String, port: int) -> int, Errwrite_to_async(data: PackedByteArray, host: String, port: int) -> await int, Errset_deadline(seconds: float) -> Variant, Errset_read_deadline(seconds: float) -> Variant, Errset_write_deadline(seconds: float) -> Variant, Erraddr() -> Dictionaryis_open() -> boolclose() -> void通常fileを受付順に逐次読み書きするReader、Writer、Closer。別streamは並列に進む。
Reader, Writer, and Closer for ordered incremental access to a regular file. Separate streams proceed concurrently.
extends RefCounted
read(max: int = 32768) -> PackedByteArray, Errread_async(max: int = 32768) -> await PackedByteArray, Errwrite(data: PackedByteArray) -> int, Errwrite_async(data: PackedByteArray) -> await int, Errseek(offset: int) -> Variant, Errseek_async(offset: int) -> await Variant, Errclose() -> Variant, Errclose_async() -> await Variant, Erris_open() -> booluint64のbit数、回転、反転、桁上がり付き演算を扱う整数math入口。
Integer math entry for uint64 bit counts, rotations, reversals, and operations with carry.
extends Object
len64(x: int) -> intleading_zeros64(x: int) -> inttrailing_zeros64(x: int) -> intones_count64(x: int) -> introtate_left64(x: int, k: int) -> intreverse64(x: int) -> intreverse_bytes64(x: int) -> intadd64(x: int, y: int, carry: int) -> Arraysub64(x: int, y: int, borrow: int) -> Arraymul64(x: int, y: int) -> Array組込みSQLiteを同期操作する低水準接続を開く入口。
Entry for opening low-level synchronous connections to embedded SQLite databases.
extends Object
open(path: String, opts: Dictionary = { }) -> GDSQLiteDB, ErrPostgreSQL clientとconnection poolを作る入口。
Entry for creating PostgreSQL clients and connection pools.
extends Object
client() -> GDPostgresClientpool(size: int = 0) -> GDPostgresPoolRedis clientとconnection poolを作る入口。
Entry for creating Redis clients and connection pools.
extends Object
client() -> GDRedisClientpool(size: int = 0) -> GDRedisPool失敗の種類、説明、機械判定用info、原因、作業文脈を保持する値。
A value holding the kind of failure, its description, machine-readable info, cause, and working context.
extends RefCounted
msg: Stringkind: Err.Kindinfo: Dictionarycause: Errerr(msg: String, kind: Err.Kind = 0, info: Dictionary = { }) -> Errnote(msg: String) -> Erris(kind: Err.Kind) -> boolfind(kind: Err.Kind) -> Errtext() -> Stringname_of(kind: Err.Kind) -> StringNONE = 0NOT_FOUND = 1PERMISSION_DENIED = 2ALREADY_EXISTS = 3INVALID_DATA = 4TIMED_OUT = 5INTERRUPTED = 6UNSUPPORTED = 7UNAUTHENTICATED = 8LIMITED = 9成功値または部分結果とErrを同時に保持する結果。
A result retaining a success value or partial result together with an Err.
extends RefCounted
v: Variante: Errok: boolok(v: Variant = null) -> Rerr(reason: Variant, kind: Err.Kind = 0, v: Variant = null) -> Rv_or(fallback: Variant) -> Variantnote(msg: String) -> Rassertの件数と失敗を集め、test終了codeを作る検査器。
Checker collecting assert counts and failures and producing the test exit code.
extends RefCounted
failures: intcount: inteq(got: Variant, want: Variant, label: String = "") -> voidne(got: Variant, other: Variant, label: String = "") -> voidok(cond: bool, label: String = "") -> voidno(cond: bool, label: String = "") -> voidnear(got: float, want: float, slack: float = 0.000000001, label: String = "") -> voidhas(box: Variant, item: Variant, label: String = "") -> voidsucceeds(r: R, label: String = "") -> voidfails(r: R, kind: Err.Kind = 0, label: String = "") -> voidcode() -> intreport() -> void親から子へ最初の打ち切り理由と期限を伝えるcontext。
Context passing the first cancellation reason and deadline from parent to child.
extends RefCounted
reason: Errcancel(msg: String = "canceled", kind: Err.Kind = 0) -> voidwith_cancel() -> GDAsyncContextwith_timeout(sec: float) -> GDAsyncContextis_done() -> booltrue/false、1/0などのbool綴りと、0x、0o、0b付きの整数を型付きで読み、位置引数を集めるparser。
Parser reading typed bool flags such as true/false and 1/0, integers with 0x, 0o, and 0b prefixes, and collecting positional arguments.
extends RefCounted
set_name(name: String) -> voidflag_bool(name: String, fallback: bool, help: String = "") -> voidflag_str(name: String, fallback: String, help: String = "") -> voidflag_int(name: String, fallback: int, help: String = "") -> voidparse(args: Array) -> Rget_bool(name: String) -> boolget_str(name: String) -> Stringget_int(name: String) -> intget_rest() -> PackedStringArrayusage() -> StringfileとHTTP応答の本文を逐次供給する公開基底型。利用者は通常、GDFileStream、GDWebWriter、GDGzipWriterとして扱う。
Public base for incrementally supplying file and HTTP response bodies. Users normally work with it as GDFileStream, GDWebWriter, or GDGzipWriter.
extends RefCounted
HTTP/1要求にはHTTP/1.1で応え、TLS交渉されたHTTP/2要求を独立streamとして扱う低水準server。
Low-level server using HTTP/1.1 responses for HTTP/1 requests and independent streams for TLS-negotiated HTTP/2 requests.
extends RefCounted
body_limit(bytes: int) -> voidheader_limits(bytes: int, values: int) -> voidlisten(port: int, host: String = "127.0.0.1") -> Rport() -> intstop() -> voidis_listening() -> boolpoll() -> PackedInt32Arrayget_method(id: int) -> Stringget_path(id: int) -> Stringget_query(id: int) -> Stringget_header(id: int, name: String) -> Stringget_headers(id: int) -> Dictionaryread_body(id: int, bytes: int = 32768) -> Dictionaryrespond(id: int, status: int, body: PackedByteArray, content_type: String = "text/plain; charset=utf-8") -> voidrespond_with(id: int, status: int, headers: Dictionary, body: PackedByteArray) -> voidconnection_count() -> intdropped_headers() -> intGD.web.streamのproducerが受け取るbyte出力先。write(data, offset=0, count=-1)は範囲を送って受付byte数を返し、送信が詰まっていれば進むまで待つ。write_text(text, offset=0, count=-1)は文字範囲をUTF-8で送る。count=-1は残り全部。flushは先行writeの送信完了を待つ。切断はflushのエラーとrequest contextの取消で確認する。
The byte destination passed to a GD.web.stream producer. write(data, offset=0, count=-1) sends a range and returns accepted bytes, waiting while the connection is backed up. write_text(text, offset=0, count=-1) sends a character range as UTF-8. count=-1 selects the remainder. flush waits until preceding writes are sent. Detect disconnects through flush errors and request-context cancellation.
extends GDBodySource
write(data: PackedByteArray, offset: int = 0, count: int = -1) -> int, Errwrite_async(data: PackedByteArray, offset: int = 0, count: int = -1) -> await int, Errwrite_text(text: String, offset: int = 0, count: int = -1) -> int, Errwrite_text_async(text: String, offset: int = 0, count: int = -1) -> await int, Errflush() -> int, Errflush_async() -> await int, Errファイル・接続・HTTP応答Writerへの逐次gzip出力。writeは受理した入力byte数、flushは途中出力、closeは終端、resetは状態とエラーの初期化。操作を順番に処理し、失敗はresetまで保持する。呼出元だけを中断し、明示的なSignalには_async版を使う。
Incremental gzip output to files, connections, or HTTP response Writers. write returns the accepted input-byte count; flush publishes buffered output; close completes the member; reset discards state and clears errors. Operations are ordered; failures remain sticky until reset. Methods suspend only the calling routine, with explicit _async variants for signals.
extends RefCounted
header: Dictionaryset_header(header: Dictionary) -> voidwrite(data: PackedByteArray) -> int, Errwrite_async(data: PackedByteArray) -> await int, Errflush() -> Variant, Errflush_async() -> await Variant, Errclose() -> Variant, Errclose_async() -> await Variant, Errreset(writer: RefCounted) -> Variant, Errreset_async(writer: RefCounted) -> await Variant, Errroute handlerが受け取るHTTP request。厳密JSON、request単位context、大容量本文の逐次保存を持つ。
The HTTP request received by a route handler, with strict JSON, a per-request context, and streaming of large bodies.
extends RefCounted
context: GDAsyncContextmethod: Stringpath: Stringip: Stringquery: Dictionaryquery_all: Dictionaryparams: Dictionarytarget: Stringheader(name: String) -> Stringheaders() -> Dictionaryread(bytes: int = 32768) -> PackedByteArray, Errbytes() -> PackedByteArray, Errbody_size() -> intlimit(bytes: int) -> Variant, Errsave(path: String) -> int, Errtext() -> String, Errjson() -> Variant, Errread_async(bytes: int = 32768) -> await PackedByteArray, Errbytes_async() -> await PackedByteArray, Errsave_async(path: String) -> await int, Errtext_async() -> await String, Errjson_async() -> await Variant, Errkeep(name: String, value: Variant) -> voidkept(name: String, fallback: Variant = null) -> Variantvalid(name: String, fallback: Variant = null) -> Varianthandlerの前でrequestを検査、加工するmiddlewareの基底。自作するときは関数か、handle(req)を持つobjectを渡す。
Base of middleware that inspects and reshapes a request before the handler. To write your own, pass a function or an object with handle(req).
extends RefCounted
cookie IDに対応する期限付きsessionをprocess内で保持する保存先。
In-process store of expiring sessions keyed by cookie ID.
extends GDWebMiddleware
issue(value: Variant) -> String, Errtake(id: String) -> Rdrop(id: String) -> voidclear() -> voidsize() -> intcookie(id: String, secure: bool = true) -> Stringclear_cookie(secure: bool = true) -> Stringhandle(req: GDWebRequest) -> Variantroute、静的file、middlewareをまとめて待受けるWeb application。
Web application that listens with routes, static files, and middleware together.
extends RefCounted
設定Settings: jobs job_timeout header_timeout body_timeout header_bytes header_values
route(method: String, pattern: String, handler: Callable, middleware: Array = []) -> voidpre(middleware: Variant) -> voiduse(middleware: Variant) -> voidafter(handler: Callable) -> voidon_error(handler: Callable) -> voidshow_errors(on: bool) -> voidbody_limit(bytes: int) -> voidlimits(opts: Dictionary) -> voiddropped_headers() -> intgroup(prefix: String, middleware: Array = []) -> GDWebRouteGroupstatic(prefix: String, dir: String) -> voidfallback(handler: Callable) -> voidfile_at(path: String) -> Dictionary, Errfile_at_async(path: String) -> await Dictionary, Errlisten(port: int, host: String = "127.0.0.1") -> Variant, Errlisten_tls(port: int, cert: String, key: String, host: String = "127.0.0.1", opts: Dictionary = { }) -> Variant, Errlisten_tls_async(port: int, cert: String, key: String, host: String = "127.0.0.1", opts: Dictionary = { }) -> await Variant, Errport() -> intshutdown(context: GDAsyncContext) -> Variant, Errshutdown_async(context: GDAsyncContext) -> await Variant, Errstop() -> voidis_listening() -> boolpoll() -> void共通prefixとmiddlewareを持つroute group。
Route group sharing a prefix and middleware.
extends RefCounted
use(middleware: Variant) -> voidroute(method: String, pattern: String, handler: Callable, middleware: Array = []) -> void受信したHTTP status、header、bodyを保持し、厳密JSONをRで返すresponse。
Response holding the received HTTP status, headers, and body, returning strict JSON as an R.
extends RefCounted
status: intheaders: Dictionarybody: PackedByteArrayok() -> booltext() -> Stringjson() -> Variant, ErrGD.html.templateで解析した不変の雛形。executeは文字列、execute_bytesはUTF-8 byte列を生成し、複数の要求から共有できる。
Immutable template parsed by GD.html.template. execute renders text, execute_bytes renders UTF-8 bytes, and the value can be shared across requests.
extends RefCounted
execute(data: Dictionary) -> String, Errexecute_bytes(data: Dictionary) -> PackedByteArray, Err一つのPostgreSQL接続を操作するclient。通常methodは呼出し元だけを待たせ、*_asyncは並行処理用Signalを返す。
Client for one PostgreSQL connection. Regular methods suspend only their caller; *_async returns Signals for concurrent composition.
extends RefCounted
設定Settings: user database password connect_timeout timeout auth allow_cleartext_password tls ca
open(host: String = "127.0.0.1", port: int = 5432, opts: Dictionary = { }) -> Variant, Errquery(sql: String, args: Array = []) -> Dictionary, Errquery_row(sql: String, args: Array = [], max_bytes: int = 0) -> Dictionary, Errquery_rows(sql: String, args: Array = []) -> GDDatabaseRows, Errquery_values(sql: String, args: Array = []) -> Dictionary, Errquery_flat(sql: String, args: Array = []) -> Dictionary, Errquery_many(sql: String, rows: Array) -> Array, Errfetch_many(sql: String, rows: Array) -> Array, Errfetch_values_many(sql: String, rows: Array) -> Array, Errfetch_flat_many(sql: String, rows: Array) -> Array, Errexec_many(sql: String, rows: Array) -> int, Erropen_async(host: String = "127.0.0.1", port: int = 5432, opts: Dictionary = { }) -> await Variant, Errquery_async(sql: String, args: Array = []) -> await Dictionary, Errquery_row_async(sql: String, args: Array = [], max_bytes: int = 0) -> await Dictionary, Errquery_rows_async(sql: String, args: Array = []) -> await GDDatabaseRows, Errquery_values_async(sql: String, args: Array = []) -> await Dictionary, Errquery_flat_async(sql: String, args: Array = []) -> await Dictionary, Errquery_many_async(sql: String, rows: Array) -> await Array, Errfetch_many_async(sql: String, rows: Array) -> await Array, Errfetch_values_many_async(sql: String, rows: Array) -> await Array, Errfetch_flat_many_async(sql: String, rows: Array) -> await Array, Errexec_many_async(sql: String, rows: Array) -> await int, Erris_open() -> boolclose() -> voidcheck(sql: String) -> Variant, Errcheck_async(sql: String) -> await Variant, Errin_flight() -> intcached_stmts() -> int需要に応じてPostgreSQL接続を最大数まで作り、待機列と接続統計を持つ同時利用可能なpool。
Concurrency-safe pool opening PostgreSQL connections on demand up to a maximum, with a wait queue and connection statistics.
extends RefCounted
open(host: String = "127.0.0.1", port: int = 5432, opts: Dictionary = { }, size: int = 0) -> Variant, Errquery(sql: String, args: Array = []) -> Dictionary, Errquery_row(sql: String, args: Array = [], max_bytes: int = 0) -> Dictionary, Errquery_rows(sql: String, args: Array = []) -> GDDatabaseRows, Errquery_values(sql: String, args: Array = []) -> Dictionary, Errquery_flat(sql: String, args: Array = []) -> Dictionary, Errquery_many(sql: String, rows: Array) -> Array, Errfetch_many(sql: String, rows: Array) -> Array, Errfetch_values_many(sql: String, rows: Array) -> Array, Errfetch_flat_many(sql: String, rows: Array) -> Array, Errexec_many(sql: String, rows: Array) -> int, Erropen_async(host: String = "127.0.0.1", port: int = 5432, opts: Dictionary = { }, size: int = 0) -> await Variant, Errquery_async(sql: String, args: Array = []) -> await Dictionary, Errquery_row_async(sql: String, args: Array = [], max_bytes: int = 0) -> await Dictionary, Errquery_rows_async(sql: String, args: Array = []) -> await GDDatabaseRows, Errquery_values_async(sql: String, args: Array = []) -> await Dictionary, Errquery_flat_async(sql: String, args: Array = []) -> await Dictionary, Errquery_many_async(sql: String, rows: Array) -> await Array, Errfetch_many_async(sql: String, rows: Array) -> await Array, Errfetch_values_many_async(sql: String, rows: Array) -> await Array, Errfetch_flat_many_async(sql: String, rows: Array) -> await Array, Errexec_many_async(sql: String, rows: Array) -> await int, Errclose() -> voidsize() -> intin_flight() -> intstats() -> DictionarySQLiteとPostgreSQLの共通query、逐次Rows、先頭行取得、接続統計を提供するclient。
Client providing shared SQLite and PostgreSQL queries, streaming Rows, first-row lookup, and connection statistics.
extends RefCounted
設定Settings: driver path host port max_rows max_bytes
open(opts: Dictionary) -> Variant, Erropen_async(opts: Dictionary) -> await Variant, Errquery(sql: String, args: Array = []) -> Dictionary, Errquery_async(sql: String, args: Array = []) -> await Dictionary, Errquery_row(sql: String, args: Array = []) -> Dictionary, Errquery_row_async(sql: String, args: Array = []) -> await Dictionary, Errquery_rows(sql: String, args: Array = []) -> GDDatabaseRows, Errquery_rows_async(sql: String, args: Array = []) -> await GDDatabaseRows, Errtransaction(action: Callable) -> Variant, Errtransaction_async(action: Callable) -> await Variant, Errmigrate(statements: Array) -> int, Errmigrate_async(statements: Array) -> await int, Errstats() -> Dictionaryclose() -> voidis_open() -> bool一つの物理接続をtransaction終了まで占有する専用client。
Dedicated client holding one physical connection until the transaction ends.
extends RefCounted
query(sql: String, args: Array = []) -> Dictionary, Errquery_async(sql: String, args: Array = []) -> await Dictionary, Errquery_row(sql: String, args: Array = []) -> Dictionary, Errquery_row_async(sql: String, args: Array = []) -> await Dictionary, Errquery_rows(sql: String, args: Array = []) -> GDDatabaseRows, Errquery_rows_async(sql: String, args: Array = []) -> await GDDatabaseRows, Erris_active() -> bool一つのRedis接続を操作するclient。通常methodは呼出し元だけを待たせ、*_asyncは並行処理用Signalを返す。
Client for one Redis connection. Regular methods suspend only their caller; *_async returns Signals for concurrent composition.
extends RefCounted
設定Settings: password timeout tls ca
open(host: String = "127.0.0.1", port: int = 6379, opts: Dictionary = { }) -> Variant, Errquery(cmd: String, args: Array = []) -> Variant, Errpipeline(cmds: Array) -> Array, Errtransaction(cmds: Array) -> Array, Errsubscribe(channels: PackedStringArray) -> Variant, Erropen_async(host: String = "127.0.0.1", port: int = 6379, opts: Dictionary = { }) -> await Variant, Errquery_async(cmd: String, args: Array = []) -> await Variant, Errpipeline_async(cmds: Array) -> await Array, Errtransaction_async(cmds: Array) -> await Array, Errsubscribe_async(channels: PackedStringArray) -> await Variant, Erris_open() -> boolis_subscribed() -> boolin_flight() -> intclose() -> void複数のRedis接続へ処理を割り振る同時利用可能なpool。
Concurrency-safe pool distributing work across several Redis connections.
extends RefCounted
open(host: String = "127.0.0.1", port: int = 6379, opts: Dictionary = { }, size: int = 0) -> Variant, Errquery(cmd: String, args: Array = []) -> Variant, Erropen_async(host: String = "127.0.0.1", port: int = 6379, opts: Dictionary = { }, size: int = 0) -> await Variant, Errquery_async(cmd: String, args: Array = []) -> await Variant, Errclose() -> voidsize() -> intin_flight() -> int組込みSQLiteへの同期接続。
Synchronous connection to the embedded SQLite.
extends RefCounted
設定Settings: busy_ms max_rows max_bytes max_ms
exec(sql: String, params: Array = []) -> Dictionary, Errquery(sql: String, params: Array = []) -> Array, Errprepare(sql: String) -> GDSQLiteStatement, Errclose() -> voidis_open() -> bool一度prepareして繰り返し使うSQLite文。
A SQLite statement prepared once and reused.
extends RefCounted
run(params: Array = []) -> Dictionary, Errrun_many(rows: Array) -> Dictionary, Errone(params: Array = []) -> Variant, Errall(params: Array = []) -> Array, Errclose() -> voidis_valid() -> bool分割して届くJSON Linesを一行ずつ復号するreader。
Reader decoding JSON Lines one line at a time as chunks arrive.
extends RefCounted
feed(chunk: String) -> Array, Errfinish() -> Array, Errpickで決めた順位が小さい値から取り出すheap。
Heap that pops the value with the smallest rank chosen by pick first.
extends RefCounted
set_pick(pick: Callable) -> voidpush(value: Variant) -> voidpop() -> Variantpeek() -> Variantdrain() -> Arraysize() -> intis_empty() -> boolpriorityの数が小さい順に、同じ数なら入れた順に値を取り出すqueue。
Queue that pops the smallest priority number first, and insertion order among equals.
extends RefCounted
push(value: Variant, priority: float = 0.0) -> voidpop() -> Variantpeek() -> Variantsize() -> intis_empty() -> bool件数と期限を制限して最近使った値を保持し、無ければ作って返すcache。
Cache holding recently used values under a count and expiry limit, creating a value when missing.
extends RefCounted
setup(limit: int, ttl_ms: int = 0) -> voidput(key: Variant, value: Variant) -> voidtake(key: Variant, fallback: Variant = null) -> Variantfetch(key: Variant, make: Callable) -> Varianthas(key: Variant) -> boolerase(key: Variant) -> voidclear() -> voidsize() -> intlimit() -> inthit_count() -> intmiss_count() -> inthit_rate() -> float関数の引数と戻り値をLRU cacheへ記憶するwrapper。
Wrapper remembering a function's arguments and results in an LRU cache.
extends RefCounted
setup(fn: Callable, limit: int = 128) -> voidcall_with(args: Array) -> Variantnext()で1行ずつ進み、scan()・values()で現在行を取得する逐次結果。err()で失敗を検査し、close()で資源を解放する。
Incremental query results. Advance with next(), decode the current row with scan() or values(), inspect err(), and release resources with close().
extends RefCounted
next() -> boolnext_async() -> await boolscan() -> Dictionary, Errvalues() -> Array, Errcolumns() -> PackedStringArrayerr() -> Errcommand_tag() -> Stringclose() -> voidcancel() -> voidis_closed() -> bool