gd

公式マニュアル・APIリファレンス

Manual and API reference

Download · Packages · Source

curl -fsSL https://gd-cli.progsha.com/install.sh | sh

gd 公式マニュアル

オンラインのマニュアル・APIリファレンスはブラウザーの優先言語に合わせて英語・日本語を表示し、対象外の言語では英語を表示します。 言語ボタンまたは?lang=jaで日本語へ切り替えられ、選択は端末内に保存されます。

gdは何のための道具か

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が書いて動かすことを前提に設計しています。

導入

対応環境は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の公証を通しています。

sourceからbuildする場合はPython、uv、SCons、C/C++ compilerを用意し、bin/にできるgd.*.template_release.*を使います。 TLSは内蔵しているため、別のTLSライブラリは要りません。


git clone https://github.com/prog-sha/gd-cli.git

cd gd-cli

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

用途から選ぶ

標準APIの入口はGD一つで、用途ごとの子を持ちます。scriptからはこの名前をそのまま書きます。

やりたいこと入口
file、文字、日時、HTTP client、非同期処理GDGD.file.read_text("a.txt")
WebサイトとWeb APIGD.webGD.web.app()
SQLiteまたはPostgreSQLGD.databaseGD.database.client()

GD.database.postgresGD.database.redisは、接続先固有の機能が必要なときに使う高度な入口です。

APIの調べ方

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.

戻り型のRErrGDWebRequestは名前だけで引きます。Node、SceneTree、TimerなどGodot由来の型は Godotのclass referenceも参照してください。 手引きの言語はLC_ALLまたはLANGjaで始まるとき日本語、それ以外は英語です。 Web版はgd-cli.progsha.comにあり、日本語と英語を切り替えられます。

GDScriptの基本

掲載例では型名を繰り返しません。:=で代入する変数と、return 値, 失敗で返す成功値は型を推論します。 注釈を省いた引数は動的型です。型で境界を固定したい箇所だけ注釈を足せます。

引数とflag

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

外のcommandを呼ぶ

外の道具は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で挙動を変えられます。

名前既定意味
timeout0諦めるまでの秒数。0は無期限。越えると子を畳んでErr.TIMED_OUTを返す
outputtrue出力を集める。falseなら親の標準入出力へ直結し、集めない

開発中のcommand


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 :=の形で両方を受け、enullでなければ失敗です。


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.kindErr.NOT_FOUNDErr.INVALID_DATAなどの種類。分岐に使う
Err.err("理由", Err.NOT_FOUND)自分で失敗を作る

失敗を呼出し元へ渡さないmain()ではvar 値, e :=!で受けます。

file操作の失敗ではe.infooppathsourcesource_codeが入ります。 renameはpathの代わりにoldnewを持ちます。sourceposixwin32engineのいずれかです。 NotFoundなど意味が確定した場合だけkindが付き、未知のI/O失敗はErr.NONEのまま元情報を保ちます。

戻り値の規則

型を書かなくても動きます。型を書く場合と細部の規則は次の通りです。

  • 戻り型は-> int, Errのように成功型一つとErrの二つです。実行時の型はRで、-> Rや省略もできます。
  • カンマ戻りは必ず二値です。末尾はErr型の値か成功時のnullに限ります。文字列は末尾に直接返せないのでErr.err(reason)で包みます。
  • 失敗を入れる変数はvar e: Err = ...var e := Err.err(...)で型を固定します。型が変わりうるvar e = ...は末尾に使えません。
  • 複数のdataは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, Errreturn R.ok("a")と書くとcompile errorです。型が動的なら実行時に検査します。
  • 型付きのArrayやDictionaryを成功値にするときは、元のcontainerにも同じ要素型を付けます。
  • 成功値と失敗を返す関数は、全ての経路でreturnします。?で伝播するだけの関数も最後にreturn null, nullを書きます。
  • 戻り型を-> intのように一つだけ書いた関数には、カンマ戻りを書けません。-> int, Errと書きます。
  • lambdaにはカンマ戻りを書けません。return R.ok(値)return R.err(理由)を使います。

Rで持ち運ぶ

値と失敗を一つの値として持ち運びたいときは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 res in got:

		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から呼び返される関数の中で呼ぶとerrorになるので、そこでは_asyncの版をawaitするか、coroutineから呼びます。

チュートリアル: SQLiteを使うメモAPI

ここまでの知識で、JSONを受けてSQLiteへ保存する小さなAPIを一枚のscriptで作ります。 できあがるのは、入力検査とSQLのparameter bindを備え、権限を絞って起動する開発用serverです。

1. 作業directoryを作る


mkdir notes-api

cd notes-api

2. 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), null





# 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), null





# 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_PATHuser://は、gdが利用者ごとに用意する書込み領域です。
  • app.route()に、HTTP method、path、そのときに呼ぶ関数(handler)を登録します。
  • handlerはGDWebRequestを受け取り、GD.web.json()で返事を作ります。途中の?は失敗をserverへ返し、状態番号500などになります。
  • POSTにはGD.web.json_body()を付けています。本文がruleに合うときだけhandlerが呼ばれ、通った値がreq.valid("body")に入ります。
  • SQLの値は$1へbindします。文字列連結でSQLを組み立てません。

3. 権限を絞って起動する

未確認のscriptや外へ公開するserverは、権限を既定で拒否する--strictで実行します。ここでは待受先をloopbackの一つのportに絞ります。 servemain()が返った後もprocessを残すcommandで、serverにはこれを使います。


gd check main.gd

gd --strict --allow-net=127.0.0.1:18080 serve main.gd

4. 別の端末から使う


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へ書かず、許可した環境変数から読みます。

権限

gdには二つの実行方式があります。

方式向く場面制限
通常実行信頼したsourceを開発中に動かすfileもnetworkも制限しない
--strict未確認のscript、公開serverres://と絶対pathはread-only。network、環境変数、子process、native extension、system情報を既定で拒否

--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より優先する拒否
-Afile以外を全て許す。開発中の一時的な利用向け

fileの置き場

scriptから見えるfileの置き場は次の4種類です。置き場の名前をpathの先頭に書くか、絶対pathをそのまま書きます。

書き方指す場所strictでの扱い
res://a.txtscriptを起動したdirectoryread-only
user://a.txtgdが利用者ごとに用意する書込み領域read/write
store://a.txt--mount store=/srv/app:rwで付けた名前指定した権限
/etc/hosts機械上のその場所read-only
  • res://より上へ遡る相対pathは、どちらの方式でも拒否します。
  • --mountと絶対pathはLinuxとmacOS用です。Windowsでは拒否するので、fileはres://user://へ置いてください。
  • mount名に使えるのは小文字の英数字と-です。resuseruidpipelocallibgodottcpunixhttphttpsfiledatacacheは予約済みで選べません。

networkとextensionの許可

  • --allow-netlocalhost:8080は、同じportのIPv4 loopback 127.0.0.0/8とIPv6 ::1も表します。
  • *.example.com:443はその下位hostを許します。
  • native extensionは同じprocessで動くため、信頼できるものに限ってください。

serveとSceneTree

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=autonは1以上の整数

serveではNodeを継承しただけのscriptはツリーへ追加されません。 --no-scene-treeはSceneTreeが作られた時点で診断を出し、終了code 1で止まります。--watch--workersの子にも引き継がれます。 通常実行は暗黙にSceneTreeを作るため、この旗を付けると失敗します。

TCPとUDP

低水準の通信には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から同時に呼べます。
  • 期限methodは今からの秒数を設定し、0で解除します。
  • close()は未完了のread/writeをErr.INTERRUPTEDで起こします。listenerの受付待ちも同じです。
  • 接続は名前解決で得たIPv4とIPv6の候補を順に試し、成功した一本だけを残します。全体のtimeoutは延びません。

TLS

TLSはGD.net.dial_tls(host, port, opts)で開きます。既定で証明書の鎖とhost名を検証し、失敗しても平文へ戻りません。 戻り値はTCPと同じGDTCPConnです。

opts意味
timeout接続と握手を合わせた期限の秒
ca_file私設CA。環境変数の設定より優先する
cert_filekey_fileclient認証。許可されたmount内のfileを対で指定する
server_name証明書を照合する宛名を接続先と別にする
next_protosALPN名の配列。1名は1–255 byte、全体で65535 byteまで
insecure_skip_verify検証を省く。検証不要と判断できる試験時だけ使う

交渉結果はconnection_state()negotiated_protocolversionで読めます。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)optsclient_ca(信頼CA bundle)とclient_authを渡します。 client_caが未指定ならsystemの信頼設定を使います。

client_auth動作
none証明書を要求しない
request任意提示。検証しない
require提示だけ必須。検証しない
verify_if_given提示された場合だけ検証する
require_and_verify検証済みの証明書を必須にする

UDPと名前解決

GD.net.listen_udp()GDUDPPacketConnを返します。read_from()datahostporttruncatedを持つ辞書を返します。 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にはsyscallsourcesource_codeが入ります。

fileとdata

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配信ができます。

大きなfileを読む

全量をmemoryへ置かず読むときはGD.file.open(path, mode)GDFileStreamを開きます。 modeはreadwriteappendread_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で扱います。

同時に更新されるfile

複数のprocessが同じfileを更新するときはGD.file.replace_text(path, old, body)を使います。 読み取ったoldと現在の内容が同じときだけ置き換えるため、並行編集を黙って上書きしません。新規作成ではoldnullを渡します。

data形式の入口

用途入口
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とULIDGD.id
日時の変換と計算GD.time
文字の整形と比較GD.text
HTML entity、tag、gdhtml(Mustache構文のマイクロテンプレート)GD.html
flagと環境変数GD.cli
配列と辞書の操作GD.collection
数学の特殊値とbit演算GD.math
versionの比較GD.version
端末とfileへのlogGD.log
testの検査GD.test

環境変数と.envは、読む対象で入口が分かれます。

読む対象入口
processの環境変数GD.cli.env(name, fallback)GD.cli.require_env(name)。strictでは--allow-envが要る
.env fileGD.file.read_env(path)。fileを読んで辞書にする
dotenv形式の文字列GD.data.env(src)GD.data.to_env(data)。memory上で辞書と変換する

memory上の変換は通常名がその場で計算し、_asyncの版は別のthreadで計算します。大きな入力には_asyncを使います。 正確な一覧はgd doc GD.filegd doc GD.dataで引けます。形式ごとの検査と上限は、APIリファレンスの各入口の説明にあります。

GD.collectionのCallableを使う操作は、約1 msごとにほかの処理へ実行権を譲ります。 GD.logの各呼出しは書込み完了まで待ち、本文を切り捨てません。失敗は戻り値のRで確認でき、GD.log.flush()でそれ以前の出力完了を待てます。

JSONの規則

値はGD.data.json_encode(value)でJSON byte列にし、外から受けたbyte列はGD.data.json_decode(bytes)で読みます。 どちらも成功値とErrを返します。GDWebRequest.json()GDHTTPResponse.json()、JSONLの各行も同じ規則です。

  • 不正UTF-8、重複名、非有限数、非対応型、循環参照は、曖昧な値へ変えず失敗にします。
  • signed 64-bitに収まる整数はintのまま戻り、小数、指数、範囲外だけがfloatになります。文字列とキーの\u0000は保持します。
  • 署名やcache keyのように同じ値から同じbyte列が必要なときは{"deterministic": true}を指定します。
  • 設定はdeterministicescape_htmlboolmax_bytesmax_depthintです。不正な型はErr.INVALID_DATA、上限超過はErr.LIMITEDです。
  • json_encode_async()が終わるまで、入力のArray・Dictionaryとその子要素を変更しないでください。設定の辞書は開始時に複製されます。

hashと鍵導出

GD.dataはSHA-1、SHA-224/256/384/512、SHA3-224/256/384/512を返します。HMAC、PBKDF2、HKDFでは sha1sha224sha256sha384sha512sha3-224sha3-256sha3-384sha3-512から方式を選べます。 PBKDF2とHKDFは出力長を指定でき、不正な方式、反復回数、出力長はRの失敗として返します。

threadの上限

GD.async.set_max_threads(max)はgdが管理するOS threadの上限を設定し、以前の値を返します。既定は10000です。 上限を越えるとprocessが終了します。現在数より小さい値への変更も終了します。外部libraryが直接作るthreadは数えません。

Webフレームワーク

GD.web.app()が返すrouterに、route、静的file、雛形、middlewareを登録します。HTMLを返すWebサイトも、JSONを返すWeb APIも同じ形で作ります。 まずHTMLを一枚返すsiteから始めます。


var app := GD.web.app()



func home(_req):

	return GD.web.html("<h1>gd</h1><p>hello</p>"), null



func hello(req):

	return GD.web.json({"message": "hello", "ip": req.ip}), null



func main():

	app.static("/assets", "res://public")

	app.route("GET", "/", home)

	app.route("GET", "/api/hello", hello)

	app.listen(8080, "127.0.0.1")!

	return 0


gd --strict --allow-net=127.0.0.1:8080 serve main.gd

servemain()が返ってもprocessを終わらせないcommandです。gd main.gdで実行すると、待受けを始めた直後にprocessごと終わります。 起動しても待受けの合図は出ないので、応答の確認はbrowserやcurlで接続して行います。

routeと返事

route(method, pattern, handler)でHTTP methodとpathをhandlerへ結びます。patternの:namereq.params["name"]に入ります。 handlerはGDWebRequestを受け取ります。本文はreq.read()bytes()text()json()save()で必要な分だけ読みます。 HTMLのformから届く本文はGD.http.decode_query(req.text()?)で辞書にします。

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に限り、他所へ送るときはawaytrueにする
GD.web.not_found()404
文字列text/plainの200
bodyを持たない辞書JSONの200
null204
失敗のRまたはErr種類に応じた状態番号。Err.NOT_FOUNDは404、Err.INVALID_DATAは400、他は500

ハンドラとmiddlewareは、awaitの後も含めてSignalを返せます。完了時の引数が0個ならnull、1個ならその値、複数ならArrayとして処理を再開します。利用できないSignalはエラーハンドラへ渡します。要求終了やapp停止時には待機中の購読を解除します。

  • text/htmlは第2引数、bytesは媒体型の後の引数で状態番号を変えられます。
  • GD.web.header(reply, name, value)で返事にheaderを足します。
  • GD.web.guard(reply)X-Content-Type-OptionsX-Frame-OptionsContent-Security-Policyなどの防御headerをまとめて足します。
  • 失敗の理由は既定では本文に出しません。開発中にapp.show_errors(true)とした間だけ出します。
  • req.pathは各segmentを一度だけ復号したpath、req.targetはpercent escapeとqueryを保った原文です。%2Fは経路の区切りになりません。
  • percent escapeを復号した結果が正しいUTF-8でない、または制御文字を含む要求は400を返します。
  • 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

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}), null



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雛形

頁が増えてきたらHTMLを雛形fileへ出し、GD.web.view(path, data)で描画します。 雛形はgdhtml(Mustache構文のマイクロテンプレート)で、{{name}}{{{html}}}#if#unless#each#withelse{{> 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"}), null

二重括弧の値は、置かれた位置から文脈を判定してescapeします。雛形の作者を信頼し、差し込む値を信頼しない前提です。

文脈扱い
HTML本文、引用・未引用属性、属性名HTML escape
href="{{url}}"相対URLとhttphttpsmailtoを通す。data-hrefも同じ
href="/work/{{path}}"href="/?q={{query}}"pathは区切りを保って正規化、queryはpercent escape
onclickscript本文JSON化し、application/jsonでも</script>が構造を壊さない形にする
style安全な単独CSS値とCSS文字列・URLを通す
危険なURL、srcset、CSS値、属性名画面全体を失敗させず、#ZgdunsafeZまたはZgdunsafeZへ置き換える
  • 三重括弧{{{html}}}はescapeしない唯一の入口で、HTML本文以外では使えません。固定HTMLか十分に検査済みの値だけを渡してください。
  • 「検査済み」の印を付けて二重括弧のescapeを省く方法はありません。
  • 分岐の両側やeachの反復が異なる文脈で終わる雛形、閉じていないtag、曖昧なURLやJavaScript文脈は描画の失敗になります。
  • 雛形の大きさに固定上限はありません。再帰する部品の深さだけは100000までです。
  • 描画が終わるまで、渡した辞書を変更しないでください。

同じ雛形を何度も描画するserverでは、起動時にGD.html.template(source, partials)?で一度だけ解析し、 返った値のexecute(data)?を各要求から呼びます。解析結果は不変で、複数の要求から同時に使えます。 execute_bytes(data)?はUTF-8のbyte列を直接作るので、GD.web.bytes(body, "text/html; charset=utf-8")でそのまま返せます。

認証とCSRF

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", ""))

	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)SecureHttpOnly付きで作ります。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

Webの運用と高度な機能

上限と大きなupload

大きな本文や長い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 header10 MiBまで
遅い接続その接続だけを待たせ、別の接続を巻き込まない
返事の追加header件数と全体量の固定上限なし。不正な名前と値だけを落とす
sessionとrate limitprocess内で共有し、--workers間では共有しない。共有が必要ならDBなど外部の保存先を使う
session値文字列と整数の識別子。保持件数はtotalper_userで設定
HS256 JWTkeyは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の取消でわかる
  • streamは一回限りです。応答ごとに新しく作ります。受信本文はstreamを返す前に読み終えてください。
  • lengthは送るbyte数です。宣言と実際が合わないと接続を閉じます。不明長(-1)はHTTP/2でDATA frame、HTTP/1.1でchunked、HTTP/1.0で接続終了が終端になります。
  • HEADと本文を持てない状態番号ではproducerを呼びません。
  • 長く待つproducerではreq.contextの取消を確認してください。
  • 1回のwriteが1つのchunkに対応するとは限りません。空の文字列や空のbyte列は終端になりません。

HTTPSとHTTP/2

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の表を参照してください。

gzip圧縮

GD.data.gzip_writer(writer, level=-1)は、書いたbyte列をgzipにして下のwriterへ渡すGDGzipWriterを作ります。 下のwriterにはGDFileStream、TCP接続、GDWebWriterを使えます。全量をmemoryに貯めません。

項目内容
methodwrite(bytes)flush()close()reset(writer)。どれもRを返す
level-2(Huffmanのみ)、-1(既定)、0..9
close()gzipの末尾を完成する。下のwriterは閉じない
reset(writer)エラーを消し、同じlevelで使い回す
headernamecomment(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の設定は呼出側で行います。秘密情報と外部入力を一緒に圧縮せず、圧縮済みの本文や部分応答には使わないでください。

待受addressとport

IPv6のlocalhostだけで待ち受けるにはapp.listen(8080, "::1")!を指定します。strictでは--allow-net=[::1]:8080、接続先はhttp://[::1]:8080/です。 ::1127.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()は診断用の瞬間的な確認で、その番号を確保する機能ではありません。

HTTP clientの接続

  • HTTPSではHTTP/2を使い、同じ宛先への並行要求は一本の接続を共有します。非対応の相手と平文HTTPにはHTTP/1.1を使います。
  • HTTP/1.1の接続は、本文を末尾まで読むと同じ宛先へ再利用します。空き接続は全体100本、宛先ごと2本、90秒まで保持します。
  • 再利用した直後に閉じられた場合、安全に再送できるmethodだけ1度開き直します。
  • HTTP/2では、相手が未処理と明示した要求だけを最大7回、間隔を延ばしながら再送します。要求の期限と取消は守ります。

Webの設定一覧

GD.http.fetch()GD.webの各関数に辞書で渡す設定と、その既定値です。時間は秒、大きさはbyteです。

入口設定と既定意味
GD.http.fetchmethod="GET", headers={}, body=nullHTTP method、送信header、送信body
同上timeout=30.0, max_body=0要求全体の秒と応答bodyのbyte。0は上限なし
同上save="", sha256=""2xx bodyをsaveへ逐次保存し、返却bodyは空。sha256save必須の64桁hexで、一致した完了fileだけを置く
同上authority="host:port"CONNECTだけのrequest target
GD.cli.runtimeout=0.0, output=true子processを諦める秒と、出力を集めるか
GDWebApp.limitsjobs=0, job_timeout=0.0保持する非同期handler数と秒。0は無制限
同上header_timeout=0.0, body_timeout=0.0request header/bodyを受け終える秒。0は無期限
同上header_bytes=1048576, header_values=2147483647request lineを含むheader byteと、header行数
GD.web.jwt_signttl=900iat/expを補う秒。0は自動付与しない
GD.web.jwt / jwt_verifyleeway=0.0, require_exp=true時刻許容秒とexp必須化
同上iss="", aud="", keep="jwt"空でない場合のissuer/audience一致と保持名
同上check=Callable()署名検証後にclaimを受け取る失効判定。指定時は真だけを許可
GD.web.sessionstotal=1024, per_user=3process内の全session数と同一user数
同上idle=1800, life=43200無操作と最大生存の秒
同上cookie="sid", keep="user"Cookie名とrequest内の保持名。Cookie名はASCIIのtoken文字
GD.web.ratelimit=60, window=60.0keyごとの回数と固定窓の秒
同上keys=10000, key=Callable()process内で保持するkey数とkey選択関数
同上trusted_proxies=PackedStringArray()転送元IPを信頼するproxyのIPまたはCIDR
GD.web.csrfallow_missing=false状態変更でFetch Metadataが無いclientを許すか
GD.web.text_rulemin=0, max=4096textの文字数
GD.web.int_rulemin=-9223372036854775808, max=922337203685477580764 bit整数の範囲
GD.web.number_rulemin=-1e308, max=1e308有限浮動小数の範囲
GD.web.list_rulemin=0, max=1024要素数
GD.web.object_ruleextra=false未定義fieldを残すか

GDWebApp.limitsは表にある6つの設定名だけを受け、綴り違いやbody_limitを誤りとして拒否します。

数値の設定が受け付ける範囲です。範囲外の値は設定時に失敗します。

設定受理範囲
jobs0..2147483647。0は無制限
header_values, sessionのtotal/per_user, rateのlimit/keys1..2147483647
job_timeout, header_timeout, body_timeout有限の0..9223372036.854776秒。0は無期限
sessionのidle/life1..9223372036秒
header_bytes1..2147479551 byte。本文とは別
req.limitGD.http.fetch.max_body0..9223372036854775807 byte。max_bodyの0は上限なし
ttl0以上
leeway有限の0以上

database

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()一つで送ります。受け取るのはcolumnsrowstagを持つ辞書で、 rowsは列名を鍵にした辞書の配列です。上の例ならout.rows[0].nameadaになります。 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に機械判定用の情報が入ります。violationduplicatenot_nullforeign_keyのいずれか、 columnsは関係する列名です。PostgreSQLではcodetableconstraintも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とmigration

複数の更新を一つの成否にするときは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)

	)

  • callbackでは渡されたtxを使い、必ずRを返してください。transaction中は元のclientのquery()と二重transactionを拒否します。
  • commitの失敗はそのまま失敗として返ります。
  • closeや取消はCOMMITの開始前ならrollbackし、開始後なら結果が確定してから接続を閉じます。
  • callbackが終わった後は、保存しておいた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)",

	])

databaseの高度な機能

driverの違い

項目SQLitePostgreSQL
向く用途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)の形で、接続先を引数に取ります。

SQLiteの並行

同じclientのquery()は受付順に実行します。別のclientは並行に進みますが、同じdatabase fileへの書込みはSQLiteのlockに従います。 GD.database.sqlite.open()が返すGDSQLiteDBGDSQLiteStatementは、呼出し元でそのまま実行する同期APIです。 短い処理だけに使い、同時利用はしないでください。並行処理にはGDDatabaseClientを使います。

PostgreSQLの接続と型

  • poolは最初の問い合わせまで接続を作らず、需要の分だけ最大数まで増やします。上限に達した後の問い合わせは受付順に待ちます。
  • 通常のquery()は使用中の接続へも続けて送ります(pipeline)。同じ接続では送った順に結果が返ります。
  • transactionとquery_rows()は接続を一本専有します。接続固有の状態を使う処理は、BEGINを単発で送らずtransaction APIを使ってください。
  • 同じ接続へ複数のSQLをまとめて送るときはquery_manyfetch_manyexec_manyを使います。
  • 取消と期限超過は呼出し元へすぐ通知しますが、server上のSQL停止までは保証しません。他のqueryは中断しません。
  • stats()wait_countは接続の取得待ちの回数で、pipeline内の応答待ちは含みません。
  • 認証はSCRAM-SHA-256とMD5をserverの要求に合わせます。auth="scram"またはauth="md5"で固定できます。MD5は旧server用です。平文passwordは明示した許可が要ります。
  • JSON・JSONB列は「fileとdata」のJSONと同じ規則で読み、64-bit整数を保ちます。重複名など曖昧な値は元のJSON文字列を返します。
  • bool[]int[]bigint[]text[]は要素の型、null、多次元構造を保ちます。下限を明示した配列は元の文字列を返します。
  • 接続はUTF8を指定します。serverが別のclient encodingへの変更を通知した場合はエラーで接続を閉じます。変更のSQL自体は実行済みの場合があります。

Redisの接続

  • TLSの選び方はPostgreSQLと同じです。open()timeoutで接続と応答の期限を秒指定できます。
  • poolのopen()は接続先を設定するだけで、通信は最初のquery()から始まります。
  • 使用中の接続は返却まで専有し、空きが無ければ受付順に待ちます。待機の取消は他の呼出しへ影響せず、実行中の取消はその接続を閉じます。
  • size()は確立中を含む接続数、in_flight()は取得待ちを含む未完了数です。

databaseの設定一覧

open()に辞書で渡す設定と、その既定値です。

入口設定と既定意味
GDDatabaseClient.opendriver="postgres", path=""driverとSQLite path。SQLite時はuser://...または:memory:が必要
同上host="127.0.0.1", port=5432PostgreSQLの接続先
同上pool=0PostgreSQL最大接続数。0はmax(4, CPU数)、SQLiteでは使わない
同上max_rows=0, max_bytes=0query()が集める1結果の行数とbyte。0は無制限。query_rows()には適用しない
GDPostgresClient.openuser="postgres", database="postgres", password=""認証とDB名
同上connect_timeout=15.0, timeout=0.0接続と問い合わせの秒。poolの接続待ちも問い合わせ時間に含む。0は無期限
同上auth="any", allow_cleartext_password=falseauth="scram"/"md5"で方式固定。平文password応答は明示時のみ
同上tls=<hostで決定>, ca=""外部hostはverify-full、loopbackはdisable。CA fileは明示時だけ
GD.database.sqlite.openbusy_ms=5000, max_ms=0lock待ちミリ秒と実行期限ミリ秒。0は無期限
同上max_rows=0, max_bytes=01結果の行数とbyte。0は無制限
GDRedisClient.openpassword="", timeout=10.0passwordと接続・応答期限の秒。0は無期限
同上tls=<hostで決定>, ca=""PostgreSQLと同じTLS選択
GD.database.postgres.poolsize既定0、0または1..21474836470はmax(4, CPU数)
GD.database.redis.poolsize既定0、0..2147483647最大接続数。0は無制限。同時に作る接続はCPU数の10倍まで、最大数の指定時はその数まで
GDRedisPool.openpool_timeout=timeout+1.0(timeoutが0なら30秒)接続の空きを待つ期限。明示0は無期限

max_rowsまたはmax_bytesを越えたquery()は、その問い合わせだけを失敗にします。 期限切れや壊れた応答で順序を失った場合は接続全体を閉じます。

設定受理範囲
max_rows, max_bytes, busy_ms, max_ms0..2147483647
bind値PostgreSQLは65535個、SQLiteはengineの変数上限まで。query_manyの件数に固定上限はない
PostgreSQLの1送信SQLとbind文字列をUTF-8のbyteで数え、約1 GiBまで
Redisの1送信server側の設定に従う
PostgreSQLとRedisのport1..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と導入方法
DiscordDiscordのGatewayとRESTを使う純GDScript文字BotDiscord Bot
GDMemcachedTCP接続を再利用するcache clientMemcached
GDSupabaseDatabaseとAuthのclientSupabase

各文書に公開class、method、戻り値、制限値、strict実行例をまとめています。任意導入のため、 本体だけから生成するAPIリファレンスには含まれません。

  • gd addで入れた拡張は起動時に信頼して読み込むため、旗は要りません。接続先の--allow-netは必要です。
  • --allow-ext--deny-extが効くのは、scriptが実行中にGDExtensionManager.load_extension()で読む場合です。
  • 入れた拡張はprocessと同じ権限で動くので、信頼する版をgd.lockで固定してcommitしてください。

packageと配布

scriptが増えたり他のpackageを使ったりする段階で、gd initgd.jsonを作ります。依存はgd.jsongd.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を使う

入れたpackageは、利用側が決めた呼び名を使ってpkg://<呼び名>/から読みます。


const Hello := preload("pkg://hello/mod.gd")

  • pkg://は利用者ごとの共有cacheを指し、projectへは何も複製しません。
  • gd.jsonに書いた依存がcacheに無ければ、最初の実行で取得します。--strictでは登録所への--allow-netが要ります。
  • gd addの既定の呼び名は、package名の-._にした識別子です。engine classやkeywordと同じ呼び名は断ります。
  • commitするのはgd.jsongd.lockです。gd initpkg/.gitignoreへ書きます。
  • --frozenはlockを変更しません。offlineの配布先では、networkのある環境で先に取得し、--cached-onlyを併用します。
  • install、add、updateが途中で失敗したときは、projectの配置とlockを元へ戻します。
  • lockは登録所に結び付いています。別の登録所へ切り替えるには明示的なlock移行が必要です。

importの短い書き方

@importconst 名 = preload(...)の短い書き方です。


@import greet                 # 呼び名 → pkg://greet/mod.gd、識別子は greet

@import greet/style as Style  # 呼び名の中のscript

@import "./util.gd" as Util   # 相対fileを明示

@import "./net/client.gd"    # 下位directoryのfile

@import "../shared/util.gd"   # . や scheme で始まるpathは引用符で書く

@import "./net/mod.gd" as n

  • 引用符の無い名前は、gd.jsonimportsに宣言した呼び名だけを解決します。同名のfileを探しに行きません。
  • 相対fileは引用符で./または../から書きます。
  • 識別子はasが無ければ最後の要素そのままで、mod.gdを持つdirectoryはdirectory名です。
  • gd fmt@importをそのまま残します。
  • 本家Godotは@importを知らないので、Godotと共有するfileではconstpreloadを書いてください。

packageを作る

packageはgd.jsonを根に持つ一つのprojectです。gd init @scope/namemod.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を明示します。
  • mainのdirectoryがpackageの根になるので、package内の相対preloadはそのまま動きます。
  • packageは自分のgd.jsonimportsで他の登録所packageを使えます。gd publishがそのimportsを登録所へ載せます。
  • class_nameは公開できます。installは同名classの衝突を検査し、衝突すれば全体を元へ戻します。
  • gd.jsongodottrueにすると、gd固有のAPIを使わず本家Godotでも動くという作者の宣言になり、gd search[godot]と示します。

開発中のpackageはgd add ../pathでlocalから足します。呼び名は先のgd.jsonnameから取ります。 checkoutをpkg/<呼び名>/へ複製し、内容の指紋が変われば次の実行で複製し直します。 .で始まるfile、pkg/tmp/gd.jsonを持つ下位directory、tokenは複製しません。 gd publishは、local importの先にnameversionのあるgd.jsonがあれば登録所の範囲に変換し、無ければ拒みます。

依存の解決

gd installは依存graph全体を解決します。版は、gd.lockが固定した版、今回すでに選んだ版のうち範囲を満たすもの、 登録所の最新一致の順で選びます。

gd.lockは解決したimportsの設定も保持します。設定が変わった実行では同じresolverで再解決し、要求外の古い版を使いません。 --frozenは設定の不一致を拒否します。同じpackageに複数の別名がある場合、辞書順で最初の別名を配置先に使います。

  • 純GDScript packageは、版ごとに別のものとして共存できます。
  • native拡張はprocessに一つしか読めないため、一つの版に揃えます。範囲が両立しなければ取得前に止まります。
  • 同じhost instanceを共有するpluginの仕組み(peer依存)はありません。
  • 登録所packageの正式なpathはpkg://@scope/name@版/です。pkg://<呼び名>/は、書いたscriptが属するpackageのimportsで正式pathへ展開されます。同じ呼び名でもpackageごとに違う版を指せ、同じ版はどこから辿っても一つのscriptです。
  • gd.lockには各packageのimportsの解決先も記録され、gd infoが一覧します。
  • gd removegd updateは、どのpackageも使わなくなったものをgd.lockpkg/から外します。
  • 検索の順位が変わっても、既知のpackageのinstallとlockの検証には影響しません。

Godotと共有する置き場

本家Godotなどres://しか読めない環境と共有するときは、gd.json"place": "project"を書きます。 packageをpkg/<呼び名>/へ複製し、pkg://res://pkg/もそこを指します。 他のpackageだけが使うものはpkg/@scope/name@版/へ置きます。

  • project.godotのあるdirectoryではplaceの既定がprojectになり、.gitignoreは書きません。gdの無い同僚が開けるようpkg/をcommitします。
  • installはpreloadloadextendsに書かれたres://参照を配置先へ書き換えます。文字列、コメント、実行時に組み立てるpathは書き換えません。
  • placeはfileの置き場を決めるだけで、gd固有のAPIや構文をGodot向けに変換する機能ではありません。共有するsourceは標準構文と相対preloadで書きます。

native拡張のpackage

  • native拡張は読込みに実fileが要るため、placeに関わらずpkg/<呼び名>/へ置きます。
  • scriptから名指せるのは、自分のpackageがext:で取り込んだ拡張のclassだけです。projectのscriptならgd.json、packageのscriptならそのpackageのimportsが基準です。
  • 登録所の外にある拡張はprojectのscriptだけが使えます。
  • 登録所のpackageの拡張が、manifestの[classes]に無いclassを登録すると起動時に止まります。
  • 配布先のOSで取得するか、gd compileを配布先のOSで実行してください。

設定と環境変数

gd.jsonの設定は次の10件です。

名前gd initの生成値 / 未指定時意味
namemy-tool / 必須project名。publishは@scope/nameが必要
version0.1.0 / 必須packageのversion
tasksrun/testの2件 / 無しgd taskから呼ぶcommand
imports{} / {}呼び名と依存先。publishするpackageでは登録所packageだけ
registry未指定 / 環境または公開登録所project固定の登録所URL
main未指定 / mod.gdpublishするmod.gdまたは.gdextension入口
include未指定 / mainだけ純GDScript packageへ含めるmain directory内のfileまたはdirectory
place未指定 / cacheproject.godotがあればprojectpackageの置き場。projectpkg/へ複製する
godot未指定 / falsegd固有のAPIを使わず本家Godotでも動くpackageの宣言
description未指定 / 空登録所に出す説明

gdが読む環境変数は次の通りです。scriptから環境を読む実行では--allow-envで名前を許可します。

環境変数用途
GD_CACHE_HOMEpackageのcache根。未指定はWindowsのLocalAppData内gd。macOS/Linuxは絶対pathのXDG_CACHE_HOME/gd、それがなければhome内.gdHOME未設定時はOSの利用者情報を使う
GD_REGISTRY登録所。未指定はhttps://gd-cli.progsha.com/pkggd.jsonregistryが優先
GD_TOKENpublishのtoken。設定fileへ書かず、publishするprocessだけへ渡す
LC_ALLLANGgd docの手引きの言語
GD_REGISTRY_HOST登録所の待受address。既定127.0.0.1。コンテナ内では0.0.0.0を指定
GD_REGISTRY_DATA登録所の保存先。既定data。strict起動では書込可能なmountを指定
PORT同梱の登録所tools/registry.gdの待受port。既定8787、1..65535
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を全部同梱します。
  • 同梱したWebアプリも./app serve --no-scene-tree --allow-netで常駐できます。
  • local pathのpackageからは、.envなど.で始まるfileとgd.jsontokenを除きます。
  • secretをsourceへ埋め込まないでください。compileは.envを除外しますが、sourceに書いた値は実行体へ残ります。

対応範囲と報告

gdはAPIが固まる前の公開版です。後方互換は前提にしないでください。変更した点と基準にしたGodotの版は CHANGELOGに書きます。 gdはGodot FoundationまたはGodot Engine projectの公式製品ではありません。

不具合はIssuesへ、公開すべきでない脆弱性は GitHubの非公開報告から知らせてください。

gd Manual

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.

What gd is for

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.

Install

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.

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 https://github.com/prog-sha/gd-cli.git

cd gd-cli

scons platform=macos target=template_release -j8

# Linux: platform=linuxbsd

# Windows: platform=windows windows_subsystem=console

Quick start

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

Pick by purpose

The standard API has one entry point, GD, with a child per purpose. Scripts write these names as they are.

What you wantEntryExample
Files, text, time, HTTP client, asyncGDGD.file.read_text("a.txt")
Websites and Web APIsGD.webGD.web.app()
SQLite or PostgreSQLGD.databaseGD.database.client()

GD.database.postgres and GD.database.redis are advanced entries for features specific to one backend.

Looking up the API

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-cli.progsha.com switches between Japanese and English.

GDScript basics

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 and flags

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

Calling external commands

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

The third argument, opts, changes the behavior.

NameDefaultMeaning
timeout0Seconds before giving up. 0 is unlimited. Past it, the child is shut down and Err.TIMED_OUT is returned
outputtrueCollect the output. With false, the child uses the parent's standard I/O directly and nothing is collected

Commands during development


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

Values and failures

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

Shorter failure handling

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

FormMeaning
var value, e := call()Receive the success value and the failure separately
return value, null / return null, failureReturn 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.kindThe 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.

Return value rules

Code works without written types. When you do write types, these are the detailed rules.

  • A result signature has two types, such as -> int, Err: one success type and Err. The runtime type is R; -> R or no return annotation is also allowed.
  • A comma return always has two values. The last must have type Err or be null for success. A string cannot be returned there directly; wrap it with Err.err(reason).
  • Fix a failure variable's type with var e: Err = ... or var e := Err.err(...). A variable declared with var e = ... can change type and cannot occupy the last slot.
  • To return several items, put them in one array or dictionary, as in return [1, 0.0, ""], null. return null, null returns null as the success value.
  • Unpack an 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.
  • Using ? in a function whose annotated return type lacks , Err gives The "?" operator needs a function returning "R" or "Err".
  • Writing return R.ok("a") under -> int, Err is a compile error. When the type is dynamic, it is checked at runtime.
  • When a typed Array or Dictionary is the success value, give the original container the same element type.
  • A function returning a value and a failure returns on every path. One that only propagates with ? still ends with return null, null.
  • A function declaring a single return type such as -> int cannot use a comma return. Declare -> int, Err.
  • A lambda cannot use a comma return. Use return R.ok(value) and return R.err(reason).

Carrying results in R

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 and concurrency

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 res in got:

		print(res.status)

	return 0

EntryPurpose
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. Inside a function that native code calls back, such as an Array.map() callback, _init(), a member initializer, or _to_string(), it is an error; there, await the _async form or call it from a coroutine.

Tutorial: a notes API on SQLite

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.

1. Create a working directory


mkdir notes-api

cd notes-api

2. Write the 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), null





# 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), null





# 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).
  • A handler receives a 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.
  • The POST route carries 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").
  • SQL values are bound to $1. SQL is never built by string concatenation.

3. Start with narrowed permissions

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

4. Use it from another terminal


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.

Permissions

gd has two ways to run.

ModeSuited toRestrictions
Normal executionRunning trusted source during developmentNeither files nor the network are restricted
--strictUnverified scripts, public serversres:// and absolute paths are read-only. Network, environment variables, child processes, native extensions, and system information are denied by default

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

FlagGrants
--mount name=path:r / --mount name=path:rwRead 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
-AAllow everything except files. For temporary use during development

File locations

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.

SpellingLocationUnder strict
res://a.txtThe directory the script was started fromRead-only
user://a.txtPer-user writable area provided by gdRead/write
store://a.txtThe name given by --mount store=/srv/app:rwAs specified
/etc/hostsThat location on the machineRead-only
  • A relative path that climbs above res:// is refused in either mode.
  • --mount and absolute paths are for Linux and macOS. Windows rejects them, so put files under res:// or user:// there.
  • A mount name uses lowercase letters, digits, and -. res, user, uid, pipe, local, libgodot, tcp, unix, http, https, file, data, and cache are reserved and cannot be chosen.

Network and extension permissions

  • In --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.
  • Native extensions run in the same process, so allow only ones you trust.

serve and SceneTree

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 wantHow
Keep a Web server or scheduled job residentgd serve main.gd
Use Node _process(), _physics_process(), process_frame, SceneTreeTimer, or high-level multiplayerNormal execution without serve
Run a script extending SceneTree or MainLoopNormal execution without serve
Check during development that no SceneTree slips ingd --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.

TCP and UDP

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.
  • Deadline methods set durations from now; zero clears them.
  • close() releases pending reads and writes with Err.INTERRUPTED. Listener accepts behave the same way.
  • A connection tries the IPv4 and IPv6 candidates from name resolution in order and keeps only the one that succeeds. The overall timeout is never extended.

TLS

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.

optsMeaning
timeoutOne deadline in seconds covering both connect and handshake
ca_fileA private CA. It takes precedence over the environment settings
cert_file, key_fileClient authentication. Provide both, as files inside permitted mounts
server_nameCheck the certificate against a name different from the dial address
next_protosAn array of ALPN names. One name is 1–255 bytes, and the whole list is up to 65535 bytes
insecure_skip_verifySkip 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_authBehavior
noneDoes not request a certificate
requestA certificate is optional. It is not verified
requireA certificate must be presented. It is not verified
verify_if_givenVerifies a certificate only when one is presented
require_and_verifyRequires a verified certificate

UDP and name resolution

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.

Files and data

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.

Reading large files

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.

MethodBehavior
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.

Files updated concurrently

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.

Entry points by data format

PurposeEntry
Reading CSV, TOML, YAML, JSONL, JSONC, XML, INI, TAR, front matter, and .env filesGD.file.read_csv(path) and similar
In-memory conversion of the same formats, JSON, codecs, hashes, HMAC, PBKDF2, HKDF, byte sequencesGD.data
UUID and ULIDGD.id
Time conversion and arithmeticGD.time
Text formatting and comparisonGD.text
HTML entities, tags, and gdhtml (a micro template with Mustache syntax)GD.html
Flags and environment variablesGD.cli
Array and dictionary operationsGD.collection
Special math values and bit operationsGD.math
Version comparisonGD.version
Logging to the terminal and filesGD.log
Test assertionsGD.test

Environment variables and .env have separate entries by what you read.

What you readEntry
Process environment variablesGD.cli.env(name, fallback) and GD.cli.require_env(name). Strict mode needs --allow-env
A .env fileGD.file.read_env(path). Reads the file into a dictionary
A dotenv stringGD.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.

JSON rules

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.

  • Invalid UTF-8, duplicate names, non-finite numbers, unsupported types, and cycles fail instead of being turned into ambiguous values.
  • An integer within the signed 64-bit range returns as int; only fractions, exponents, and out-of-range values become float. Strings and keys preserve \u0000.
  • Pass {"deterministic": true} when the same value must give the same bytes, as for signatures or cache keys.
  • The options 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.
  • Keep the input Arrays, Dictionaries, and their children unchanged until json_encode_async() finishes. The options dictionary is copied at the start.

Hashes and key derivation

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, iteration count, or length.

Thread limit

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.

Web framework

Register routes, static files, templates, and middleware on the router returned by GD.web.app(). A website that returns HTML and a Web API that returns JSON are built the same way. Start with a site that returns one HTML page.


var app := GD.web.app()



func home(_req):

	return GD.web.html("<h1>gd</h1><p>hello</p>"), null



func hello(req):

	return GD.web.json({"message": "hello", "ip": req.ip}), null



func main():

	app.static("/assets", "res://public")

	app.route("GET", "/", home)

	app.route("GET", "/api/hello", hello)

	app.listen(8080, "127.0.0.1")!

	return 0


gd --strict --allow-net=127.0.0.1:8080 serve main.gd

serve keeps the process alive after main() returns. Run with gd main.gd, the process exits right after it starts listening. There is no "listening" signal at startup, so confirm by connecting with a browser or curl.

Routes and replies

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()?).

The value a handler returns becomes the reply.

Returned valueReply
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 string200 as text/plain
A dictionary without body200 as JSON
null204
A failed R or an ErrStatus by kind. Err.NOT_FOUND is 404, Err.INVALID_DATA is 400, others 500

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.

  • text and html take the status as the second argument; bytes takes it after the media type.
  • 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.
  • The reason for a failure is not written to the body by default. It is shown only while 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.
  • A request whose percent-decoded result is not valid UTF-8 or contains control characters gets a 400.
  • Do not modify values passed to GD.web.json() or view() until the reply has been sent.

The router also accepts the following.

RegistrationPurpose
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

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).

RegistrationScope
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}), null



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.

HTML templates

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"}), null

Double-brace values are escaped by the context they appear in. The template author is trusted, the values inserted are not.

ContextHandling
HTML body, quoted and unquoted attributes, attribute namesHTML 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 bodyEncoded as JSON in a form where </script> cannot break the structure, even for application/json
styleSafe single CSS values and CSS strings and URLs pass
Dangerous URLs, srcset, CSS values, attribute namesReplaced with #ZgdunsafeZ or ZgdunsafeZ without failing the whole page
  • Triple braces {{{html}}} are the only unescaped entry, and they work only in the HTML body. Pass only fixed HTML or a sufficiently checked value.
  • A double brace cannot be marked "checked" to skip escaping.
  • A template whose branches or each iterations end in different contexts, an unclosed tag, or an ambiguous URL or JavaScript context fails to render.
  • Template size has no fixed limit. Only the depth of recursive partials is limited, to 100000.
  • Do not modify the dictionary you passed until rendering finishes.

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").

Authentication and CSRF

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", ""))

	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"]})

Shutdown

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

Web operations and advanced features

Limits and large uploads

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.

TargetHandling
Request bodyNo 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 headerDefault 1 MiB. The line count is limited only when header_values is set. Trailers 4096 bytes
HTTP client response headerUp to 10 MiB
Slow connectionsOnly that connection waits. Other connections are not affected
Extra reply headersNo fixed count or aggregate limit. Only invalid names and values are dropped
Sessions and rate limitsShared within a process, not across --workers. Use an external store such as a DB when sharing is needed
Session valuesString and integer identifiers. Retention counts are set with total and per_user
HS256 JWTKey at least 32 bytes. JSON and signature validity are checked
Rate limit keyRetention count is set with keys
HTTP status100..999. Out of range is sent as 500
Port0 is allowed for listening and as the search start of GD.net.free_port(). Targets and is_free() take 1..65535
Query stringGD.http.decode_query() reports a bare semicolon and a broken percent escape as failures

Streaming bodies

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.

GDWebWriterBehavior
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
  • A stream is single-use. Create a new one for each response. Finish reading the incoming body before returning the stream.
  • 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.
  • HEAD and statuses that cannot carry a body never call the producer.
  • Long-waiting producers should observe req.context cancellation.
  • One write does not necessarily correspond to one chunk. An empty string or empty byte array does not end the body.

HTTPS and HTTP/2

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".

gzip compression

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.

ItemDetails
Methodswrite(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
headername 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.

Listen address and port

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.

HTTP client connections

  • HTTPS uses HTTP/2, and concurrent requests to the same origin share one connection. Peers without HTTP/2 and plain HTTP use HTTP/1.1.
  • An HTTP/1.1 connection is reused for the same origin after its body is read to the end. Idle connections are kept up to 100 overall, 2 per origin, for 90 seconds.
  • If a reused connection closes just after reuse, only a safely replayable method is retried once on a fresh connection.
  • On HTTP/2, only requests the peer marks as unprocessed are replayed, up to seven times with growing intervals. The request deadline and cancellation still apply.

Web settings

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.

EntrySetting and defaultMeaning
GD.http.fetchmethod="GET", headers={}, body=nullHTTP method, request headers, request body
sametimeout=30.0, max_body=0Seconds for the whole request and bytes of the response body. 0 is unlimited
samesave="", 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
sameauthority="host:port"Request target for CONNECT only
GD.cli.runtimeout=0.0, output=trueSeconds before giving up on the child process, and whether to collect output
GDWebApp.limitsjobs=0, job_timeout=0.0Number of async handlers kept and seconds. 0 is unlimited
sameheader_timeout=0.0, body_timeout=0.0Seconds to finish receiving request header/body. 0 is unlimited
sameheader_bytes=1048576, header_values=2147483647Header bytes including the request line, and the header line count
GD.web.jwt_signttl=900Seconds used to fill iat/exp. 0 does not add them
GD.web.jwt / jwt_verifyleeway=0.0, require_exp=trueClock tolerance in seconds, and whether exp is required
sameiss="", aud="", keep="jwt"Issuer/audience match when non-empty, and the name kept on the request
samecheck=Callable()Revocation check receiving claims after signature verification. When set, only true passes
GD.web.sessionstotal=1024, per_user=3Sessions per process, and per user
sameidle=1800, life=43200Idle and maximum lifetime in seconds
samecookie="sid", keep="user"Cookie name and the name kept on the request. The cookie name uses ASCII token characters
GD.web.ratelimit=60, window=60.0Count per key and the fixed window in seconds
samekeys=10000, key=Callable()Keys kept per process and the key selector
sametrusted_proxies=PackedStringArray()IPs or CIDRs of proxies whose forwarded IP is trusted
GD.web.csrfallow_missing=falseWhether to allow state changes from clients without Fetch Metadata
GD.web.text_rulemin=0, max=4096Text length in characters
GD.web.int_rulemin=-9223372036854775808, max=922337203685477580764-bit integer range
GD.web.number_rulemin=-1e308, max=1e308Finite float range
GD.web.list_rulemin=0, max=1024Element count
GD.web.object_ruleextra=falseWhether 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.

SettingAccepted range
jobs0..2147483647. 0 is unlimited
header_values, session total/per_user, rate limit/keys1..2147483647
job_timeout, header_timeout, body_timeoutFinite 0..9223372036.854776 seconds. 0 is unlimited
session idle/life1..9223372036 seconds
header_bytes1..2147479551 bytes. Separate from the body
req.limit, GD.http.fetch.max_body0..9223372036854775807 bytes. 0 for max_body is unlimited
ttl0 or more
leewayFinite, 0 or more

Database

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.

MethodPurpose
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)

Transactions and migrations

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)

	)

  • Use the given tx in the callback and always return an R. During a transaction, query() on the original client and a nested transaction are rejected.
  • A commit failure is returned as a failure.
  • Close or cancel before COMMIT begins rolls back; after it begins, the connection closes once the result is settled.
  • After the callback finishes, a retained 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)",

	])

Advanced database features

Driver differences

ItemSQLitePostgreSQL
Suited toLocal development, a single processProduction, crash resilience, several workers
ConnectionOne per client. Journal and temporary tables live in memoryA pool of up to max(4, CPU count) by default. Set a maximum as in pool=25
Extra entriesGD.database.sqlite.open() for short work done in placeGD.database.postgres for batched sends, arrays, and JSONB
NotesA database with existing -journal, -wal, or -shm files must be recovered or checkpointed with regular SQLite before openingHosts 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).

SQLite concurrency

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.

PostgreSQL connections and types

  • The pool creates no connection until the first query and grows only for demand up to the maximum. Queries after the maximum is reached wait in arrival order.
  • An ordinary query() is also sent onto a busy connection (pipelining). Results on one connection return in the order sent.
  • Transactions and query_rows() reserve one connection. For work that uses connection-local state, use the transaction API instead of sending a standalone BEGIN.
  • Use query_many, fetch_many, or exec_many to send several SQL operations together on one connection.
  • Cancellation and deadline expiry notify the caller at once, but do not guarantee that the SQL stopped on the server. Other queries are not interrupted.
  • wait_count in stats() counts waits to acquire a connection and excludes response waits inside a pipeline.
  • Authentication follows the server's request with SCRAM-SHA-256 or MD5. Pin the method with auth="scram" or auth="md5". MD5 is for older servers. A cleartext password needs explicit permission.
  • JSON and JSONB columns are read by the same rules as JSON in "Files and data" and preserve 64-bit integers. Ambiguous values such as duplicate names return the original JSON string.
  • bool[], int[], bigint[], and text[] preserve element types, nulls, and nested dimensions. Arrays with explicit lower bounds return the original text.
  • Connections request UTF8. A server-reported change to another client encoding closes the connection with an error. The SQL that made the change may already have executed.

Redis connections

  • The TLS choice is the same as PostgreSQL. timeout on open() sets the connect and response deadline in seconds.
  • Pool open() only configures the destination; network activity begins with the first query().
  • A connection in use is held exclusively until it is returned, and callers wait in arrival order when none is free. Canceling a waiter does not affect other calls; canceling an active call closes its connection.
  • size() counts connections, including those connecting; in_flight() counts unfinished calls, including acquisition waiters.

Database settings

The settings passed as a dictionary to open(), with their defaults.

EntrySetting and defaultMeaning
GDDatabaseClient.opendriver="postgres", path=""Driver and SQLite path. SQLite needs user://... or :memory:
samehost="127.0.0.1", port=5432PostgreSQL target
samepool=0PostgreSQL maximum connections. 0 means max(4, CPU count). Unused by SQLite
samemax_rows=0, max_bytes=0Rows and bytes per result collected by query(). 0 is unlimited. Not applied to query_rows()
GDPostgresClient.openuser="postgres", database="postgres", password=""Credentials and database name
sameconnect_timeout=15.0, timeout=0.0Connect and query seconds. Waiting for a pool connection counts toward the query time. 0 is unlimited
sameauth="any", allow_cleartext_password=falsePin the method with auth="scram"/"md5". A cleartext password reply only when explicit
sametls=<decided by host>, ca=""External hosts use verify-full, loopback disable. A CA file only when explicit
GD.database.sqlite.openbusy_ms=5000, max_ms=0Lock wait and execution deadline in milliseconds. 0 is unlimited
samemax_rows=0, max_bytes=0Rows and bytes per result. 0 is unlimited
GDRedisClient.openpassword="", timeout=10.0Password, and connect and response deadline in seconds. 0 is unlimited
sametls=<decided by host>, ca=""The same TLS choice as PostgreSQL
GD.database.postgres.poolsize default 0; 0 or 1..21474836470 means max(4, CPU count)
GD.database.redis.poolsize default 0; 0..2147483647Maximum 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.openpool_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.

SettingAccepted range
max_rows, max_bytes, busy_ms, max_ms0..2147483647
Bound values65535 for PostgreSQL, and the engine's variable limit for SQLite. query_many has no fixed item count
One PostgreSQL sendSQL and bound strings are counted as UTF-8 bytes, up to roughly 1 GiB
One Redis sendServer-configured limits apply
PostgreSQL and Redis port1..65535
SecondsFinite 0..9223372036.854776 seconds. 0 is unlimited

Scheduled jobs

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.

Official extension modules

The core stays small. Features specific to an external service are added as GDScript packages or GDExtensions only to projects that need them.

EntryPurposeAPI and setup
DiscordPure-GDScript text bots on the Discord Gateway and RESTDiscord Bot
GDMemcachedCache client reusing TCP connectionsMemcached
GDSupabaseDatabase and Auth clientSupabase

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.

  • Extensions added with 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().
  • An added extension runs with the same privileges as the process, so pin the versions you trust in gd.lock and commit it.

Packages and distribution

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

Using packages

An installed package is read from pkg://<alias>/, using the alias chosen by the consumer.


const Hello := preload("pkg://hello/mod.gd")

  • pkg:// points into the per-user shared cache and copies nothing into the project.
  • A dependency named in gd.json but absent from the cache is fetched on the first run. Under --strict the registry needs --allow-net.
  • The default alias of gd add is the package name with - and . turned into _, so it is an identifier. Aliases that are engine classes or keywords are refused.
  • Commit 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.
  • When install, add, or update fails partway, project files and the lock are restored.
  • The lock is bound to its registry. Switching to another registry requires explicit lock migration.

Short import syntax

@import is the short form of const Name = preload(...).


@import greet                 # an alias: pkg://greet/mod.gd, bound as greet

@import greet/style as Style  # a script inside the alias

@import "./util.gd" as Util   # explicitly relative file

@import "./net/client.gd"    # a file below this directory

@import "../shared/util.gd"   # quote paths that start with . or a scheme

@import "./net/mod.gd" as n

  • Unquoted names resolve only aliases declared in the imports of gd.json. Files with the same name are not searched.
  • Quote relative files, starting with ./ or ../.
  • Without 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.
  • Upstream Godot does not know @import, so files shared with Godot should spell out const and preload.

Creating a package

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

  • The entry is mod.gd. For multiple files, list files or directories in include.
  • The main file's directory becomes the package root, so relative preloads inside the package keep working.
  • A package may use other registry packages through the 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.
  • Setting 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.

Dependency resolution

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.

  • Pure GDScript packages coexist as distinct versions.
  • A native extension loads only once per process, so it is unified to one version. If the ranges cannot agree, it stops before anything is fetched.
  • There is no mechanism for plugins that share one host instance (peer dependencies).
  • The canonical path of a registry package is 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/.
  • A change in search ranking does not affect installation or lock verification for a known package.

Sharing a location with Godot

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/.

  • Where project.godot exists, place defaults to project and no .gitignore is written. Commit pkg/ so teammates without gd can open the project.
  • Installation rewrites 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.

Native extension packages

  • A native extension needs real files to load, so it lives under pkg/<alias>/ whatever place is.
  • A script may only name classes of the extensions its own package imports with ext:: the project's gd.json for project scripts, the package's own imports for package scripts.
  • An extension outside the registry is usable by project scripts only.
  • A registry package's extension that registers a class missing from its manifest's [classes] stops startup.
  • Fetch on the target OS, or run gd compile on the target OS.

Settings and environment variables

gd.json has these ten settings.

NameWritten by gd init / when omittedMeaning
namemy-tool / requiredProject name. Publishing needs @scope/name
version0.1.0 / requiredPackage version
tasksrun and test / noneCommands invoked by gd task
imports{} / {}Alias and dependency source. A published package may name registry packages only
registryomitted / environment or the public registryRegistry URL pinned to the project
mainomitted / mod.gdmod.gd or .gdextension entry published
includeomitted / main onlyFiles or directories inside the main directory included in a pure GDScript package
placeomitted / cache, or project beside project.godotWhere packages live. project copies them under pkg/
godotomitted / falseDeclares a package that runs on upstream Godot without gd's own API
descriptionomitted / emptyDescription shown in the registry

gd reads these environment variables. A script that reads the environment needs the names allowed with --allow-env.

VariablePurpose
GD_CACHE_HOMEPackage 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_REGISTRYRegistry. Defaults to https://gd-cli.progsha.com/pkg. registry in gd.json wins
GD_TOKENPublish token. Keep it out of config files and pass it only to the publishing process
LC_ALL, LANGLanguage of the manual shown by gd doc
GD_REGISTRY_HOSTRegistry listening address. Defaults to 127.0.0.1; use 0.0.0.0 inside a container
GD_REGISTRY_DATARegistry storage directory. Defaults to data; use a writable mount in strict mode
PORTListening port of the bundled registry tools/registry.gd. Default 8787, 1..65535
GD_WORKERInternal 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.

Distributing a single executable

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

  • Every package gd.json names, and every package those import, is embedded.
  • An embedded Web app can also stay resident with ./app serve --no-scene-tree --allow-net.
  • From a local path package, files starting with . such as .env and the token in gd.json are left out.
  • Do not embed secrets in source. compile excludes .env, but values written in source remain in the executable.

Scope and reporting

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.

APIリファレンスAPI reference

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.

GD

file、data、非同期処理、日時など、端末programの標準機能をまとめる入口。

Entry to the standard features of a command-line program: files, data, async, time, and more.

extends Object

Properties

async: GD.async
log: GD.log
net: GD.net
http: GD.http
web: GD.web
file: GD.file
collection: GD.collection
data: GD.data
id: GD.id
text: GD.text
html: GD.html
math: GD.math
version: GD.version
time: GD.time
cli: GD.cli
test: GD.test
database: GD.database

GD.web

Webサイトと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

Application
app() -> GDWebApp
server() -> GDWebServer
Replies
text(body: String, status: int = 200) -> Dictionary
html(body: String, status: int = 200) -> Dictionary
json(data: Variant, status: int = 200) -> Dictionary
bytes(body: PackedByteArray, type: String = "application/octet-stream", status: int = 200) -> Dictionary
stream(producer: Callable, length: int = -1, type: String = "application/octet-stream", status: int = 200) -> Dictionary
redirect(to: String, status: int = 302, away: bool = false) -> Dictionary
guard(reply: Dictionary) -> Dictionary
not_found(msg: String = "Not Found") -> Dictionary
header(reply: Dictionary, name: String, value: Variant) -> Dictionary
add_header(reply: Dictionary, name: String, value: Variant) -> Dictionary
view(path: String, data: Dictionary = { }, status: int = 200, renderer: Callable = null) -> Dictionary
view_async(path: String, data: Dictionary = { }, status: int = 200, renderer: Callable = null) -> await Dictionary, Err
json_async(data: Variant, status: int = 200) -> await Dictionary
error_status(error: Err) -> int
Middleware
jwt(key: Variant, opts: Dictionary = { }) -> GDWebMiddleware
jwt_sign(claims: Dictionary, key: Variant, opts: Dictionary = { }) -> String, Err
jwt_verify(token: String, key: Variant, opts: Dictionary = { }) -> Dictionary, Err
jwt_sign_async(claims: Dictionary, key: Variant, opts: Dictionary = { }) -> await String, Err
jwt_verify_async(token: String, key: Variant, opts: Dictionary = { }) -> await Dictionary, Err
csrf(opts: Dictionary = { }) -> GDWebMiddleware
sessions(opts: Dictionary = { }) -> GDWebSessionStore
rate(opts: Dictionary = { }) -> GDWebMiddleware
Validation
text_rule(opts: Dictionary = { }) -> Dictionary
int_rule(opts: Dictionary = { }) -> Dictionary
number_rule(opts: Dictionary = { }) -> Dictionary
bool_rule() -> Dictionary
list_rule(item: Dictionary = { }, opts: Dictionary = { }) -> Dictionary
object_rule(fields: Dictionary, opts: Dictionary = { }) -> Dictionary
optional(rule: Dictionary, fallback: Variant = null) -> Dictionary
one_of(values: Array) -> Dictionary
validate(value: Variant, rule: Dictionary) -> Variant, Err
validate_async(value: Variant, rule: Dictionary) -> await Variant, Err
json_body(rule: Dictionary, name: String = "body") -> GDWebMiddleware
query(rule: Dictionary, name: String = "query") -> GDWebMiddleware
params(rule: Dictionary, name: String = "params") -> GDWebMiddleware

GD.database

SQLiteと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

Properties

sqlite: GD.database.sqlite
postgres: GD.database.postgres
redis: GD.database.redis
Methods
client() -> GDDatabaseClient
標準モジュールStandard modules

GD.async

待機、並行実行、競争、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

Methods
set_max_threads(max: int) -> int
sleep(sec: float) -> Signal
spawn(fn: Callable) -> Signal
all(signals: Array) -> await Array
race(signals: Array) -> await int
with_timeout(signal: Signal, sec: float) -> await int
with_context(context: GDAsyncContext, signal: Signal) -> await Variant
context() -> GDAsyncContext

GD.log

level、時刻、出力先を揃え、順序付き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

Methods
setup(name: String = "", level: String = "info") -> void
set_file(path: String) -> void
set_time(on: bool) -> void
set_color(on: bool) -> void
write(level: String, msg: String, extra: Variant = null) -> Variant, Err
debug(msg: String, extra: Variant = null) -> Variant, Err
info(msg: String, extra: Variant = null) -> Variant, Err
warn(msg: String, extra: Variant = null) -> Variant, Err
error(msg: String, extra: Variant = null) -> Variant, Err
result(r: R, msg: String) -> Variant, Err
write_async(level: String, msg: String, extra: Variant = null) -> await Variant, Err
debug_async(msg: String, extra: Variant = null) -> await Variant, Err
info_async(msg: String, extra: Variant = null) -> await Variant, Err
warn_async(msg: String, extra: Variant = null) -> await Variant, Err
error_async(msg: String, extra: Variant = null) -> await Variant, Err
result_async(r: R, msg: String) -> await Variant, Err
format(level: String, msg: String, extra: Variant = null) -> String
flush() -> Variant, Err
flush_async() -> await Variant, Err

GD.net

TCP・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

Methods
is_free(port: int, host: String = "127.0.0.1") -> bool
free_port(from: int = 0, host: String = "127.0.0.1") -> int, Err
resolve(host: String) -> String, Err
resolve_async(host: String) -> await String, Err
dial_tcp(host: String, port: int, opts: Dictionary = { }) -> GDTCPConn, Err
dial_tcp_async(host: String, port: int, opts: Dictionary = { }) -> await GDTCPConn, Err
dial_tls(host: String, port: int, opts: Dictionary = { }) -> GDTCPConn, Err
dial_tls_async(host: String, port: int, opts: Dictionary = { }) -> await GDTCPConn, Err
listen_tcp(host: String = "127.0.0.1", port: int = 0) -> GDTCPListener, Err
listen_udp(host: String = "127.0.0.1", port: int = 0, buffer: int = 0) -> GDUDPPacketConn, Err
is_free_async(port: int, host: String = "127.0.0.1") -> await bool
free_port_async(from: int = 0, host: String = "127.0.0.1") -> await int, Err
local_addresses() -> PackedStringArray, Err
local_addresses_async() -> await PackedStringArray, Err
is_ip(text: String) -> bool
split_host(text: String, default_port: int = 80) -> Dictionary

GD.http

HTTP 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

Methods
fetch(url: String, opts: Dictionary = { }) -> GDHTTPResponse
fetch_async(url: String, opts: Dictionary = { }) -> await GDHTTPResponse
parse_url(raw: String) -> Dictionary, Err
parse_url_async(raw: String) -> await Dictionary, Err
build_url(url: Dictionary) -> String
build_url_async(url: Dictionary) -> await String
request_target(url: Dictionary) -> String
request_target_async(url: Dictionary) -> await String
default_port(scheme: String) -> int
decode_query(raw: String) -> Dictionary, Err
decode_query_async(raw: String) -> await Dictionary, Err
encode_query(query: Dictionary) -> String
encode_query_async(query: Dictionary) -> await String
media_type(path: String) -> String
media_type_for_extension(ext: String) -> String
is_textual(kind: String) -> bool
extension_for_media_type(kind: String) -> String

GD.file

file全量操作と逐次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

File I/O
open(path: String, mode: String = "read") -> GDFileStream, Err
open_async(path: String, mode: String = "read") -> await GDFileStream, Err
read_text(path: String) -> String, Err
read_bytes(path: String, offset: int = 0, max: int = 0) -> PackedByteArray, Err
write_text(path: String, body: String) -> Variant, Err
replace_text(path: String, old: Variant, body: String) -> Variant, Err
write_bytes(path: String, body: PackedByteArray) -> Variant, Err
append_bytes(path: String, body: PackedByteArray) -> Variant, Err
append_text(path: String, body: String) -> Variant, Err
exists(path: String) -> bool
remove(path: String) -> Variant, Err
size_of(path: String) -> int, Err
copy(src: String, dst: String) -> Variant, Err
rename(src: String, dst: String) -> Variant, Err
list_dir(path: String) -> Array, Err
make_dir(path: String) -> Variant, Err
ensure_dir(path: String) -> Variant, Err
walk(path: String, want_dirs: bool = false, hidden: bool = false) -> Array, Err
glob(path: String, pattern: String, hidden: bool = false) -> Array, Err
remove_all(path: String) -> Variant, Err
can_read(path: String) -> bool
read_text_async(path: String) -> await String, Err
read_bytes_async(path: String, offset: int = 0, max: int = 0) -> await PackedByteArray, Err
write_text_async(path: String, body: String) -> await Variant, Err
write_bytes_async(path: String, body: PackedByteArray) -> await Variant, Err
append_text_async(path: String, body: String) -> await Variant, Err
append_bytes_async(path: String, body: PackedByteArray) -> await Variant, Err
copy_async(src: String, dst: String) -> await Variant, Err
replace_text_async(path: String, old: Variant, body: String) -> await Variant, Err
exists_async(path: String) -> await bool
remove_async(path: String) -> await Variant, Err
size_of_async(path: String) -> await int, Err
rename_async(src: String, dst: String) -> await Variant, Err
list_dir_async(path: String) -> await Array, Err
make_dir_async(path: String) -> await Variant, Err
ensure_dir_async(path: String) -> await Variant, Err
walk_async(path: String, want_dirs: bool = false, hidden: bool = false) -> await Array, Err
glob_async(path: String, pattern: String, hidden: bool = false) -> await Array, Err
remove_all_async(path: String) -> await Variant, Err
can_read_async(path: String) -> await bool
Paths
parse_path(path: String) -> Dictionary
format_path(parts: Dictionary) -> String
join(parts: PackedStringArray) -> String
dirname(path: String) -> String
basename(path: String, suffix: String = "") -> String
extname(path: String) -> String
is_absolute(path: String) -> bool
normalize(path: String) -> String
relative(from: String, to: String) -> String
under(dir: String, name: String) -> String
File formats
create_tar(root: String) -> PackedByteArray, Err
extract_tar(data: PackedByteArray, root: String) -> int, Err
read_csv(path: String, sep: String = ",") -> Array, Err
read_ini(path: String) -> Dictionary, Err
read_toml(path: String) -> Dictionary, Err
read_yaml(path: String) -> Variant, Err
read_jsonc(path: String) -> Variant, Err
read_jsonl(path: String) -> Array, Err
read_front_matter(path: String) -> Dictionary, Err
read_xml(path: String) -> Dictionary, Err
read_env(path: String = ".env") -> Dictionary, Err
read_tar(path: String) -> Array, Err
read_csv_async(path: String, sep: String = ",") -> await Array, Err
read_ini_async(path: String) -> await Dictionary, Err
read_toml_async(path: String) -> await Dictionary, Err
read_yaml_async(path: String) -> await Variant, Err
read_jsonc_async(path: String) -> await Variant, Err
read_jsonl_async(path: String) -> await Array, Err
read_front_matter_async(path: String) -> await Dictionary, Err
read_xml_async(path: String) -> await Dictionary, Err
read_env_async(path: String = ".env") -> await Dictionary, Err
read_tar_async(path: String) -> await Array, Err
create_tar_async(root: String) -> await PackedByteArray, Err
extract_tar_async(data: PackedByteArray, root: String) -> await int, Err

GD.collection

Arrayと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

Methods
group_by(items: Array, key: Callable) -> Dictionary
map_values(src: Dictionary, fn: Callable) -> Dictionary
filter_keys(src: Dictionary, pred: Callable) -> Dictionary
partition(items: Array, pred: Callable) -> Array
chunk(items: Array, size: int) -> Array
unique_by(items: Array, key: Callable) -> Array
unique(items: Array) -> Array
sort_by(items: Array, pick: Callable) -> Array
sort_key(items: Array, key: String) -> Array
zip(a: Array, b: Array) -> Array
sum_of(items: Array, pick: Callable) -> float
max_by(items: Array, pick: Callable) -> Variant
min_by(items: Array, pick: Callable) -> Variant
index_by(items: Array, key: Callable) -> Dictionary
deep_merge(base: Dictionary, over: Dictionary) -> Dictionary
binary_heap(pick: Callable = null) -> GDBinaryHeap
priority_queue() -> GDPriorityQueue
lru_cache(limit: int = 128, ttl_ms: int = 0) -> GDLRUCache
memo(fn: Callable, limit: int = 128) -> GDMemoizedCallable
group_by_async(items: Array, key: Callable) -> await Dictionary
map_values_async(src: Dictionary, fn: Callable) -> await Dictionary
filter_keys_async(src: Dictionary, pred: Callable) -> await Dictionary
partition_async(items: Array, pred: Callable) -> await Array
unique_by_async(items: Array, key: Callable) -> await Array
sort_by_async(items: Array, pick: Callable) -> await Array
sum_of_async(items: Array, pick: Callable) -> await float
max_by_async(items: Array, pick: Callable) -> await Variant
min_by_async(items: Array, pick: Callable) -> await Variant
index_by_async(items: Array, key: Callable) -> await Dictionary
chunk_async(items: Array, size: int) -> await Array
unique_async(items: Array) -> await Array
sort_key_async(items: Array, key: String) -> await Array
zip_async(a: Array, b: Array) -> await Array
deep_merge_async(base: Dictionary, over: Dictionary) -> await Dictionary

GD.data

byte列、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

Hash
gzip_writer(writer: RefCounted, level: int = -1) -> GDGzipWriter, Err
sha224(msg: PackedByteArray) -> PackedByteArray
sha256(msg: PackedByteArray) -> PackedByteArray
sha384(msg: PackedByteArray) -> PackedByteArray
sha512(msg: PackedByteArray) -> PackedByteArray
sha3_224(msg: PackedByteArray) -> PackedByteArray
sha3_256(msg: PackedByteArray) -> PackedByteArray
sha3_384(msg: PackedByteArray) -> PackedByteArray
sha3_512(msg: PackedByteArray) -> PackedByteArray
sha1(msg: PackedByteArray) -> PackedByteArray
hmac(hash: String, key: PackedByteArray, msg: PackedByteArray) -> PackedByteArray, Err
hmac_sha256(key: PackedByteArray, msg: PackedByteArray) -> PackedByteArray
equal_ct(a: PackedByteArray, b: PackedByteArray) -> bool
pbkdf2_sha256(password: PackedByteArray, salt: PackedByteArray, rounds: int) -> PackedByteArray
pbkdf2(hash: String, password: PackedByteArray, salt: PackedByteArray, rounds: int, size: int) -> PackedByteArray, Err
hkdf(hash: String, secret: PackedByteArray, salt: PackedByteArray, info: PackedByteArray, size: int) -> PackedByteArray, Err
hkdf_extract(hash: String, secret: PackedByteArray, salt: PackedByteArray) -> PackedByteArray, Err
hkdf_expand(hash: String, key: PackedByteArray, info: PackedByteArray, size: int) -> PackedByteArray, Err
pbkdf2_sha256_async(password: PackedByteArray, salt: PackedByteArray, rounds: int) -> await PackedByteArray
pbkdf2_async(hash: String, password: PackedByteArray, salt: PackedByteArray, rounds: int, size: int) -> await PackedByteArray, Err
hkdf_async(hash: String, secret: PackedByteArray, salt: PackedByteArray, info: PackedByteArray, size: int) -> await PackedByteArray, Err
hkdf_extract_async(hash: String, secret: PackedByteArray, salt: PackedByteArray) -> await PackedByteArray, Err
hkdf_expand_async(hash: String, key: PackedByteArray, info: PackedByteArray, size: int) -> await PackedByteArray, Err
sha224_async(msg: PackedByteArray) -> await PackedByteArray
sha256_async(msg: PackedByteArray) -> await PackedByteArray
sha384_async(msg: PackedByteArray) -> await PackedByteArray
sha512_async(msg: PackedByteArray) -> await PackedByteArray
sha3_224_async(msg: PackedByteArray) -> await PackedByteArray
sha3_256_async(msg: PackedByteArray) -> await PackedByteArray
sha3_384_async(msg: PackedByteArray) -> await PackedByteArray
sha3_512_async(msg: PackedByteArray) -> await PackedByteArray
sha1_async(msg: PackedByteArray) -> await PackedByteArray
hmac_async(hash: String, key: PackedByteArray, msg: PackedByteArray) -> await PackedByteArray, Err
hmac_sha256_async(key: PackedByteArray, msg: PackedByteArray) -> await PackedByteArray
equal_ct_async(a: PackedByteArray, b: PackedByteArray) -> await bool
Serialization
csv(src: String, sep: String = ",") -> Array, Err
to_csv(rows: Array, sep: String = ",") -> String
csv_objects(src: String, sep: String = ",") -> Array, Err
to_csv_objects(items: Array, sep: String = ",") -> String
ini(src: String) -> Dictionary, Err
to_ini(data: Dictionary) -> String
toml(src: String) -> Dictionary, Err
to_toml(data: Dictionary, prefix: String = "") -> String
yaml(src: String) -> Variant, Err
to_yaml(data: Variant, depth: int = 0) -> String
jsonc(src: String) -> Variant, Err
strip_jsonc(src: String) -> String
jsonl(src: String) -> Array, Err
to_jsonl(items: Array) -> String, Err
jsonl_reader() -> GDJSONLReader
front_matter(src: String) -> Dictionary, Err
has_front_matter(src: String) -> bool
to_front_matter(attrs: Dictionary, body: String, kind: String = "yaml") -> String
xml(src: String) -> Dictionary, Err
to_xml(data: Dictionary, indent: int = 0) -> String
env(src: String) -> Dictionary
to_env(data: Dictionary) -> String
tar(entries: Array) -> PackedByteArray
untar(data: PackedByteArray) -> Array, Err
json_encode(value: Variant, opts: Dictionary = { }) -> PackedByteArray, Err
json_decode(data: PackedByteArray) -> Variant, Err
msgpack(value: Variant) -> PackedByteArray
unmsgpack(data: PackedByteArray) -> Variant, Err
cbor(value: Variant) -> PackedByteArray
uncbor(data: PackedByteArray) -> Variant, Err
json_encode_async(value: Variant, opts: Dictionary = { }) -> await PackedByteArray, Err
json_decode_async(data: PackedByteArray) -> await Variant, Err
msgpack_async(value: Variant) -> await PackedByteArray
unmsgpack_async(data: PackedByteArray) -> await Variant, Err
cbor_async(value: Variant) -> await PackedByteArray
uncbor_async(data: PackedByteArray) -> await Variant, Err
csv_async(src: String, sep: String = ",") -> await Array, Err
to_csv_async(rows: Array, sep: String = ",") -> await String
csv_objects_async(src: String, sep: String = ",") -> await Array, Err
to_csv_objects_async(items: Array, sep: String = ",") -> await String
ini_async(src: String) -> await Dictionary, Err
to_ini_async(data: Dictionary) -> await String
toml_async(src: String) -> await Dictionary, Err
to_toml_async(data: Dictionary, prefix: String = "") -> await String
yaml_async(src: String) -> await Variant, Err
to_yaml_async(data: Variant, depth: int = 0) -> await String
jsonc_async(src: String) -> await Variant, Err
strip_jsonc_async(src: String) -> await String
jsonl_async(src: String) -> await Array, Err
to_jsonl_async(items: Array) -> await String, Err
front_matter_async(src: String) -> await Dictionary, Err
to_front_matter_async(attrs: Dictionary, body: String, kind: String = "yaml") -> await String
xml_async(src: String) -> await Dictionary, Err
to_xml_async(data: Dictionary, indent: int = 0) -> await String
env_async(src: String) -> await Dictionary
to_env_async(data: Dictionary) -> await String
tar_async(entries: Array) -> await PackedByteArray
untar_async(data: PackedByteArray) -> await Array, Err
Codec
hex_encode(data: PackedByteArray) -> String
hex_decode(text: String) -> PackedByteArray, Err
base64_encode(data: PackedByteArray) -> String
base64_decode(text: String) -> PackedByteArray, Err
base64url_encode(data: PackedByteArray) -> String
base64url_decode(text: String) -> PackedByteArray, Err
base32_encode(data: PackedByteArray) -> String
base32_decode(text: String) -> PackedByteArray, Err
varint_encode(n: int) -> PackedByteArray
varint_decode(data: PackedByteArray, at: int = 0) -> Dictionary, Err
hex_encode_async(data: PackedByteArray) -> await String
hex_decode_async(text: String) -> await PackedByteArray, Err
base64_encode_async(data: PackedByteArray) -> await String
base64_decode_async(text: String) -> await PackedByteArray, Err
base64url_encode_async(data: PackedByteArray) -> await String
base64url_decode_async(text: String) -> await PackedByteArray, Err
base32_encode_async(data: PackedByteArray) -> await String
base32_decode_async(text: String) -> await PackedByteArray, Err
Bytes
concat(parts: Array) -> PackedByteArray
equals(a: PackedByteArray, b: PackedByteArray) -> bool
includes(hay: PackedByteArray, needle: PackedByteArray) -> bool
index_of(hay: PackedByteArray, needle: PackedByteArray, from: int = 0) -> int
last_index_of(hay: PackedByteArray, needle: PackedByteArray) -> int
starts_with(hay: PackedByteArray, prefix: PackedByteArray) -> bool
ends_with(hay: PackedByteArray, suffix: PackedByteArray) -> bool
repeat(src: PackedByteArray, times: int) -> PackedByteArray
fit(src: PackedByteArray, size: int) -> PackedByteArray
split(src: PackedByteArray, sep: PackedByteArray) -> Array
xor_bytes(a: PackedByteArray, b: PackedByteArray) -> PackedByteArray
concat_async(parts: Array) -> await PackedByteArray
equals_async(a: PackedByteArray, b: PackedByteArray) -> await bool
includes_async(hay: PackedByteArray, needle: PackedByteArray) -> await bool
index_of_async(hay: PackedByteArray, needle: PackedByteArray, from: int = 0) -> await int
last_index_of_async(hay: PackedByteArray, needle: PackedByteArray) -> await int
starts_with_async(hay: PackedByteArray, prefix: PackedByteArray) -> await bool
ends_with_async(hay: PackedByteArray, suffix: PackedByteArray) -> await bool
repeat_async(src: PackedByteArray, times: int) -> await PackedByteArray
fit_async(src: PackedByteArray, size: int) -> await PackedByteArray
split_async(src: PackedByteArray, sep: PackedByteArray) -> await Array
xor_bytes_async(a: PackedByteArray, b: PackedByteArray) -> await PackedByteArray

GD.id

UUIDと時刻順ULIDを生成、検査する入口。

Entry for generating and checking UUIDs and time-ordered ULIDs.

extends Object

Methods
ulid(ms: int = -1) -> String
is_ulid(text: String) -> bool
ulid_time(text: String) -> int, Err
uuid() -> String
uuid_v5(space: String, name: String) -> String, Err
uuid_v5_async(space: String, name: String) -> await String, Err
is_uuid(text: String) -> bool
uuid_bytes(text: String) -> PackedByteArray, Err
uuid_version(text: String) -> int
nil_uuid() -> String
dns_namespace() -> String
url_namespace() -> String
oid_namespace() -> String

GD.text

文字の整形と比較を扱う入口。

Entry for formatting and comparing text.

extends Object

Methods
closest(word: String, options: PackedStringArray) -> String
ellipsis(text: String, width: int) -> String
size_of(bytes: int) -> String
duration(ms: float) -> String
distance(a: String, b: String) -> int
snake(text: String) -> String
camel(text: String) -> String
title(text: String) -> String
table(rows: Array, gap: int = 2) -> String
distance_async(a: String, b: String) -> await int
closest_async(word: String, options: PackedStringArray) -> await String
ellipsis_async(text: String, width: int) -> await String
snake_async(text: String) -> await String
camel_async(text: String) -> await String
title_async(text: String) -> await String
table_async(rows: Array, gap: int = 2) -> await String

GD.html

HTML 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

Methods
fill(tpl: String, data: Dictionary, partials: Dictionary = { }) -> String
template(tpl: String, partials: Dictionary = { }) -> GDHTMLTemplate, Err
attr(value: String) -> String
tag(name: String, body: String, attrs: Dictionary = { }) -> String
escape(text: String) -> String
unescape(text: String) -> String
escape_async(text: String) -> await String
unescape_async(text: String) -> await String
attr_async(value: String) -> await String
tag_async(name: String, body: String, attrs: Dictionary = { }) -> await String
fill_async(tpl: String, data: Dictionary, partials: Dictionary = { }) -> await String

GD.math

標準math packageに対応する数学関数とIEEE 754の特殊値、bit変換を扱う入口。

Entry for standard mathematical functions, IEEE 754 special values, and bit conversions.

extends Object

Properties

bits: GD.math.bits
e: float
pi: float
phi: float
sqrt2: float
sqrt_e: float
sqrt_pi: float
sqrt_phi: float
ln2: float
log2_e: float
ln10: float
log10_e: float
max_float64: float
smallest_nonzero_float64: float
Methods
abs(x: float) -> float
acos(x: float) -> float
acosh(x: float) -> float
asin(x: float) -> float
asinh(x: float) -> float
atan(x: float) -> float
atanh(x: float) -> float
cbrt(x: float) -> float
ceil(x: float) -> float
cos(x: float) -> float
cosh(x: float) -> float
erf(x: float) -> float
erfc(x: float) -> float
erfinv(x: float) -> float
erfcinv(x: float) -> float
exp(x: float) -> float
exp2(x: float) -> float
expm1(x: float) -> float
floor(x: float) -> float
gamma(x: float) -> float
ilogb(x: float) -> int
j0(x: float) -> float
j1(x: float) -> float
log(x: float) -> float
log1p(x: float) -> float
log2(x: float) -> float
log10(x: float) -> float
logb(x: float) -> float
round(x: float) -> float
round_to_even(x: float) -> float
signbit(x: float) -> bool
sin(x: float) -> float
sinh(x: float) -> float
sqrt(x: float) -> float
tan(x: float) -> float
tanh(x: float) -> float
trunc(x: float) -> float
y0(x: float) -> float
y1(x: float) -> float
float64_bits(x: float) -> int
float64_from_bits(x: int) -> float
float32_bits(x: float) -> int
float32_from_bits(x: int) -> float
nan() -> float
inf(sign: int) -> float
is_inf(x: float, sign: int = 0) -> bool
is_nan(x: float) -> bool
atan2(y: float, x: float) -> float
copysign(value: float, sign: float) -> float
dim(x: float, y: float) -> float
fma(x: float, y: float, z: float) -> float
hypot(x: float, y: float) -> float
ldexp(frac: float, exp: int) -> float
max(x: float, y: float) -> float
min(x: float, y: float) -> float
mod(x: float, y: float) -> float
nextafter(x: float, y: float) -> float
nextafter32(x: float, y: float) -> float
pow(x: float, y: float) -> float
pow10(n: int) -> float
remainder(x: float, y: float) -> float
frexp(x: float) -> Array
lgamma(x: float) -> Array
modf(x: float) -> Array
sincos(x: float) -> Array

GD.version

Semantic Versionを解釈し、比較、範囲判定する入口。

Entry for parsing, comparing, and range-matching Semantic Versions.

extends Object

Methods
parse(raw: String) -> Dictionary, Err
is_canonical(raw: String) -> bool
compare(a: Dictionary, b: Dictionary) -> int
is_stable(v: Dictionary) -> bool
text(v: Dictionary) -> String
best(list: PackedStringArray, range: String = "*") -> Dictionary, Err
best_async(list: PackedStringArray, range: String = "*") -> await Dictionary, Err
satisfies(v: Dictionary, range: String) -> bool

GD.time

Unix時刻、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

Methods
now() -> int
to_parts(unix: int) -> Dictionary
from_parts(parts: Dictionary) -> int
to_iso(unix: int) -> String
to_http(unix: int) -> String
parse_iso(text: String) -> int, Err
add(unix: int, amount: int, unit: String = "second") -> int
diff(a: int, b: int, unit: String = "second") -> int
start_of_day(unix: int) -> int
weekday(unix: int) -> int
is_leap(year: int) -> bool
days_in_month(year: int, month: int) -> int
ago(unix: int, base: int = -1) -> String
format(unix: int, pattern: String) -> String

GD.cli

gdの版と文書の入口、flag、許可された環境とsystem情報を読む入口。

Entry for gd's version and docs, flags, and allowed environment and system information.

extends Object

Methods
version() -> String
docs_url() -> String
flags() -> GDCLIFlags
env(name: String, fallback: Variant = null) -> Variant
require_env(name: String) -> String, Err
cwd() -> String
platform() -> String
arch() -> String
stdin_tty() -> bool
stdout_tty() -> bool
stderr_tty() -> bool
paint(text: String, code: int) -> String
red(text: String) -> String
green(text: String) -> String
yellow(text: String) -> String
blue(text: String) -> String
gray(text: String) -> String
bold(text: String) -> String
run(path: String, args: PackedStringArray = [], opts: Dictionary = { }) -> Dictionary, Err
run_async(path: String, args: PackedStringArray = [], opts: Dictionary = { }) -> await Dictionary, Err

GD.test

小さなassertを集めるtest補助の入口。

Entry for small test helpers that collect asserts.

extends Object

Methods
check() -> GDTestCheck

GD.net.dial_tcp

同時利用可能な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

Methods
read(max: int = 65536) -> PackedByteArray, Err
read_async(max: int = 65536) -> await PackedByteArray, Err
write(data: PackedByteArray) -> int, Err
write_async(data: PackedByteArray) -> await int, Err
set_deadline(seconds: float) -> Variant, Err
set_read_deadline(seconds: float) -> Variant, Err
set_write_deadline(seconds: float) -> Variant, Err
local_addr() -> Dictionary
remote_addr() -> Dictionary
connection_state() -> Dictionary
is_open() -> bool
close() -> void

GD.net.listen_tcp

TCP接続を受け付けるlistener。期限とCloseによる受付待ち解除を持つ。

TCP listener with deadlines and Close wakeups for pending accepts.

extends RefCounted

Methods
accept() -> GDTCPConn, Err
accept_async() -> await GDTCPConn, Err
set_deadline(seconds: float) -> Variant, Err
addr() -> Dictionary
is_open() -> bool
close() -> void

GD.net.listen_udp

packet境界と送信元addressを保つUDP通信口。期限とCloseによる待機解除を持つ。

UDP endpoint preserving packet boundaries and source addresses, with deadlines and Close wakeups.

extends RefCounted

Methods
read_from(max: int = 65536) -> Dictionary, Err
read_from_async(max: int = 65536) -> await Dictionary, Err
write_to(data: PackedByteArray, host: String, port: int) -> int, Err
write_to_async(data: PackedByteArray, host: String, port: int) -> await int, Err
set_deadline(seconds: float) -> Variant, Err
set_read_deadline(seconds: float) -> Variant, Err
set_write_deadline(seconds: float) -> Variant, Err
addr() -> Dictionary
is_open() -> bool
close() -> void

GD.file.open

通常fileを受付順に逐次読み書きするReader、Writer、Closer。別streamは並列に進む。

Reader, Writer, and Closer for ordered incremental access to a regular file. Separate streams proceed concurrently.

extends RefCounted

Methods
read(max: int = 32768) -> PackedByteArray, Err
read_async(max: int = 32768) -> await PackedByteArray, Err
write(data: PackedByteArray) -> int, Err
write_async(data: PackedByteArray) -> await int, Err
seek(offset: int) -> Variant, Err
seek_async(offset: int) -> await Variant, Err
close() -> Variant, Err
close_async() -> await Variant, Err
is_open() -> bool

GD.math.bits

uint64のbit数、回転、反転、桁上がり付き演算を扱う整数math入口。

Integer math entry for uint64 bit counts, rotations, reversals, and operations with carry.

extends Object

Methods
len64(x: int) -> int
leading_zeros64(x: int) -> int
trailing_zeros64(x: int) -> int
ones_count64(x: int) -> int
rotate_left64(x: int, k: int) -> int
reverse64(x: int) -> int
reverse_bytes64(x: int) -> int
add64(x: int, y: int, carry: int) -> Array
sub64(x: int, y: int, borrow: int) -> Array
mul64(x: int, y: int) -> Array
高度な接続先Advanced backends

GD.database.sqlite

組込みSQLiteを同期操作する低水準接続を開く入口。

Entry for opening low-level synchronous connections to embedded SQLite databases.

extends Object

Methods
open(path: String, opts: Dictionary = { }) -> GDSQLiteDB, Err

GD.database.postgres

PostgreSQL clientとconnection poolを作る入口。

Entry for creating PostgreSQL clients and connection pools.

extends Object

Methods
client() -> GDPostgresClient
pool(size: int = 0) -> GDPostgresPool

GD.database.redis

Redis clientとconnection poolを作る入口。

Entry for creating Redis clients and connection pools.

extends Object

Methods
client() -> GDRedisClient
pool(size: int = 0) -> GDRedisPool
戻り型Return types

Err

失敗の種類、説明、機械判定用info、原因、作業文脈を保持する値。

A value holding the kind of failure, its description, machine-readable info, cause, and working context.

extends RefCounted

Properties

msg: String
kind: Err.Kind
info: Dictionary
cause: Err
Methods
err(msg: String, kind: Err.Kind = 0, info: Dictionary = { }) -> Err
note(msg: String) -> Err
is(kind: Err.Kind) -> bool
find(kind: Err.Kind) -> Err
text() -> String
name_of(kind: Err.Kind) -> String

Constants

NONE = 0
NOT_FOUND = 1
PERMISSION_DENIED = 2
ALREADY_EXISTS = 3
INVALID_DATA = 4
TIMED_OUT = 5
INTERRUPTED = 6
UNSUPPORTED = 7
UNAUTHENTICATED = 8
LIMITED = 9

R

成功値またはErrを運ぶ結果。

A result carrying a success value or an Err.

extends RefCounted

Properties

v: Variant
e: Err
ok: bool
Methods
ok(v: Variant = null) -> R
err(reason: Variant, kind: Err.Kind = 0, v: Variant = null) -> R
v_or(fallback: Variant) -> Variant
note(msg: String) -> R

GDTestCheck

assertの件数と失敗を集め、test終了codeを作る検査器。

Checker collecting assert counts and failures and producing the test exit code.

extends RefCounted

Properties

failures: int
count: int
Methods
eq(got: Variant, want: Variant, label: String = "") -> void
ne(got: Variant, other: Variant, label: String = "") -> void
ok(cond: bool, label: String = "") -> void
no(cond: bool, label: String = "") -> void
near(got: float, want: float, slack: float = 0.000000001, label: String = "") -> void
has(box: Variant, item: Variant, label: String = "") -> void
succeeds(r: R, label: String = "") -> void
fails(r: R, kind: Err.Kind = 0, label: String = "") -> void
code() -> int
report() -> void

GDAsyncContext

親から子へ最初の打ち切り理由と期限を伝えるcontext。

Context passing the first cancellation reason and deadline from parent to child.

extends RefCounted

Properties

reason: Err
Methods
cancel(msg: String = "canceled", kind: Err.Kind = 0) -> void
with_cancel() -> GDAsyncContext
with_timeout(sec: float) -> GDAsyncContext
is_done() -> bool

GDCLIFlags

true/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

Methods
set_name(name: String) -> void
flag_bool(name: String, fallback: bool, help: String = "") -> void
flag_str(name: String, fallback: String, help: String = "") -> void
flag_int(name: String, fallback: int, help: String = "") -> void
parse(args: Array) -> R
get_bool(name: String) -> bool
get_str(name: String) -> String
get_int(name: String) -> int
get_rest() -> PackedStringArray
usage() -> String

GDBodySource

fileと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

GDWebServer

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

Methods
body_limit(bytes: int) -> void
header_limits(bytes: int, values: int) -> void
listen(port: int, host: String = "127.0.0.1") -> R
port() -> int
stop() -> void
is_listening() -> bool
poll() -> PackedInt32Array
get_method(id: int) -> String
get_path(id: int) -> String
get_query(id: int) -> String
get_header(id: int, name: String) -> String
get_headers(id: int) -> Dictionary
read_body(id: int, bytes: int = 32768) -> Dictionary
respond(id: int, status: int, body: PackedByteArray, content_type: String = "text/plain; charset=utf-8") -> void
respond_with(id: int, status: int, headers: Dictionary, body: PackedByteArray) -> void
connection_count() -> int
dropped_headers() -> int

GDWebWriter

GD.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

Methods
write(data: PackedByteArray, offset: int = 0, count: int = -1) -> int, Err
write_async(data: PackedByteArray, offset: int = 0, count: int = -1) -> await int, Err
write_text(text: String, offset: int = 0, count: int = -1) -> int, Err
write_text_async(text: String, offset: int = 0, count: int = -1) -> await int, Err
flush() -> int, Err
flush_async() -> await int, Err

GDGzipWriter

ファイル・接続・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

Properties

header: Dictionary
Methods
set_header(header: Dictionary) -> void
write(data: PackedByteArray) -> int, Err
write_async(data: PackedByteArray) -> await int, Err
flush() -> Variant, Err
flush_async() -> await Variant, Err
close() -> Variant, Err
close_async() -> await Variant, Err
reset(writer: RefCounted) -> Variant, Err
reset_async(writer: RefCounted) -> await Variant, Err

GDWebRequest

route 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

Properties

context: GDAsyncContext
method: String
path: String
ip: String
query: Dictionary
params: Dictionary
target: String
Methods
header(name: String) -> String
headers() -> Dictionary
read(bytes: int = 32768) -> PackedByteArray, Err
bytes() -> PackedByteArray, Err
body_size() -> int
limit(bytes: int) -> void
save(path: String) -> int, Err
text() -> String, Err
json() -> Variant, Err
read_async(bytes: int = 32768) -> await PackedByteArray, Err
bytes_async() -> await PackedByteArray, Err
save_async(path: String) -> await int, Err
text_async() -> await String, Err
json_async() -> await Variant, Err
keep(name: String, value: Variant) -> void
kept(name: String, fallback: Variant = null) -> Variant
valid(name: String, fallback: Variant = null) -> Variant

GDWebMiddleware

handlerの前で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

GDWebSessionStore

cookie IDに対応する期限付きsessionをprocess内で保持する保存先。

In-process store of expiring sessions keyed by cookie ID.

extends GDWebMiddleware

Methods
issue(value: Variant) -> String
take(id: String) -> R
drop(id: String) -> void
clear() -> void
size() -> int
cookie(id: String, secure: bool = true) -> String
clear_cookie(secure: bool = true) -> String
handle(req: GDWebRequest) -> Variant

GDWebApp

route、静的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

Methods
route(method: String, pattern: String, handler: Callable, middleware: Array = []) -> void
pre(middleware: Variant) -> void
use(middleware: Variant) -> void
after(handler: Callable) -> void
on_error(handler: Callable) -> void
show_errors(on: bool) -> void
body_limit(bytes: int) -> void
limits(opts: Dictionary) -> void
dropped_headers() -> int
group(prefix: String, middleware: Array = []) -> GDWebRouteGroup
static(prefix: String, dir: String) -> void
fallback(handler: Callable) -> void
file_at(path: String) -> Dictionary
file_at_async(path: String) -> await Dictionary
listen(port: int, host: String = "127.0.0.1") -> Variant, Err
listen_tls(port: int, cert: String, key: String, host: String = "127.0.0.1", opts: Dictionary = { }) -> Variant, Err
listen_tls_async(port: int, cert: String, key: String, host: String = "127.0.0.1", opts: Dictionary = { }) -> await Variant, Err
port() -> int
shutdown(context: GDAsyncContext) -> Variant, Err
shutdown_async(context: GDAsyncContext) -> await Variant, Err
stop() -> void
is_listening() -> bool
poll() -> void

GDWebRouteGroup

共通prefixとmiddlewareを持つroute group。

Route group sharing a prefix and middleware.

extends RefCounted

Methods
use(middleware: Variant) -> void
route(method: String, pattern: String, handler: Callable, middleware: Array = []) -> void

GDHTTPResponse

受信したHTTP status、header、bodyを保持し、厳密JSONをRで返すresponse。

Response holding the received HTTP status, headers, and body, returning strict JSON as an R.

extends RefCounted

Properties

status: int
headers: Dictionary
body: PackedByteArray
error: String
Methods
ok() -> bool
text() -> String
json() -> Variant, Err

GDHTMLTemplate

GD.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

Methods
execute(data: Dictionary) -> String, Err
execute_bytes(data: Dictionary) -> PackedByteArray, Err

GDPostgresClient

一つの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

Methods
open(host: String = "127.0.0.1", port: int = 5432, opts: Dictionary = { }) -> Variant, Err
query(sql: String, args: Array = []) -> Dictionary, Err
query_row(sql: String, args: Array = [], max_bytes: int = 0) -> Dictionary, Err
query_rows(sql: String, args: Array = []) -> GDDatabaseRows, Err
query_values(sql: String, args: Array = []) -> Dictionary, Err
query_flat(sql: String, args: Array = []) -> Dictionary, Err
query_many(sql: String, rows: Array) -> Array, Err
fetch_many(sql: String, rows: Array) -> Array, Err
fetch_values_many(sql: String, rows: Array) -> Array, Err
fetch_flat_many(sql: String, rows: Array) -> Array, Err
exec_many(sql: String, rows: Array) -> int, Err
open_async(host: String = "127.0.0.1", port: int = 5432, opts: Dictionary = { }) -> await Variant, Err
query_async(sql: String, args: Array = []) -> await Dictionary, Err
query_row_async(sql: String, args: Array = [], max_bytes: int = 0) -> await Dictionary, Err
query_rows_async(sql: String, args: Array = []) -> await GDDatabaseRows, Err
query_values_async(sql: String, args: Array = []) -> await Dictionary, Err
query_flat_async(sql: String, args: Array = []) -> await Dictionary, Err
query_many_async(sql: String, rows: Array) -> await Array, Err
fetch_many_async(sql: String, rows: Array) -> await Array, Err
fetch_values_many_async(sql: String, rows: Array) -> await Array, Err
fetch_flat_many_async(sql: String, rows: Array) -> await Array, Err
exec_many_async(sql: String, rows: Array) -> await int, Err
is_open() -> bool
close() -> void
check(sql: String) -> Variant, Err
check_async(sql: String) -> await Variant, Err
in_flight() -> int
cached_stmts() -> int

GDPostgresPool

需要に応じてPostgreSQL接続を最大数まで作り、待機列と接続統計を持つ同時利用可能なpool。

Concurrency-safe pool opening PostgreSQL connections on demand up to a maximum, with a wait queue and connection statistics.

extends RefCounted

Methods
open(host: String = "127.0.0.1", port: int = 5432, opts: Dictionary = { }, size: int = 0) -> Variant, Err
query(sql: String, args: Array = []) -> Dictionary, Err
query_row(sql: String, args: Array = [], max_bytes: int = 0) -> Dictionary, Err
query_rows(sql: String, args: Array = []) -> GDDatabaseRows, Err
query_values(sql: String, args: Array = []) -> Dictionary, Err
query_flat(sql: String, args: Array = []) -> Dictionary, Err
query_many(sql: String, rows: Array) -> Array, Err
fetch_many(sql: String, rows: Array) -> Array, Err
fetch_values_many(sql: String, rows: Array) -> Array, Err
fetch_flat_many(sql: String, rows: Array) -> Array, Err
exec_many(sql: String, rows: Array) -> int, Err
open_async(host: String = "127.0.0.1", port: int = 5432, opts: Dictionary = { }, size: int = 0) -> await Variant, Err
query_async(sql: String, args: Array = []) -> await Dictionary, Err
query_row_async(sql: String, args: Array = [], max_bytes: int = 0) -> await Dictionary, Err
query_rows_async(sql: String, args: Array = []) -> await GDDatabaseRows, Err
query_values_async(sql: String, args: Array = []) -> await Dictionary, Err
query_flat_async(sql: String, args: Array = []) -> await Dictionary, Err
query_many_async(sql: String, rows: Array) -> await Array, Err
fetch_many_async(sql: String, rows: Array) -> await Array, Err
fetch_values_many_async(sql: String, rows: Array) -> await Array, Err
fetch_flat_many_async(sql: String, rows: Array) -> await Array, Err
exec_many_async(sql: String, rows: Array) -> await int, Err
close() -> void
size() -> int
in_flight() -> int
stats() -> Dictionary

GDDatabaseClient

SQLiteと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

Methods
open(opts: Dictionary) -> Variant, Err
open_async(opts: Dictionary) -> await Variant, Err
query(sql: String, args: Array = []) -> Dictionary, Err
query_async(sql: String, args: Array = []) -> await Dictionary, Err
query_row(sql: String, args: Array = []) -> Dictionary, Err
query_row_async(sql: String, args: Array = []) -> await Dictionary, Err
query_rows(sql: String, args: Array = []) -> GDDatabaseRows, Err
query_rows_async(sql: String, args: Array = []) -> await GDDatabaseRows, Err
transaction(action: Callable) -> Variant, Err
transaction_async(action: Callable) -> await Variant, Err
migrate(statements: Array) -> int, Err
migrate_async(statements: Array) -> await int, Err
stats() -> Dictionary
close() -> void
is_open() -> bool

GDDatabaseTx

一つの物理接続をtransaction終了まで占有する専用client。

Dedicated client holding one physical connection until the transaction ends.

extends RefCounted

Methods
query(sql: String, args: Array = []) -> Dictionary, Err
query_async(sql: String, args: Array = []) -> await Dictionary, Err
query_row(sql: String, args: Array = []) -> Dictionary, Err
query_row_async(sql: String, args: Array = []) -> await Dictionary, Err
query_rows(sql: String, args: Array = []) -> GDDatabaseRows, Err
query_rows_async(sql: String, args: Array = []) -> await GDDatabaseRows, Err
is_active() -> bool

GDRedisClient

一つの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

Methods
open(host: String = "127.0.0.1", port: int = 6379, opts: Dictionary = { }) -> Variant, Err
query(cmd: String, args: Array = []) -> Variant, Err
pipeline(cmds: Array) -> Array, Err
transaction(cmds: Array) -> Array, Err
subscribe(channels: PackedStringArray) -> Variant, Err
open_async(host: String = "127.0.0.1", port: int = 6379, opts: Dictionary = { }) -> await Variant, Err
query_async(cmd: String, args: Array = []) -> await Variant, Err
pipeline_async(cmds: Array) -> await Array, Err
transaction_async(cmds: Array) -> await Array, Err
subscribe_async(channels: PackedStringArray) -> await Variant, Err
is_open() -> bool
is_subscribed() -> bool
in_flight() -> int
close() -> void

GDRedisPool

複数のRedis接続へ処理を割り振る同時利用可能なpool。

Concurrency-safe pool distributing work across several Redis connections.

extends RefCounted

Methods
open(host: String = "127.0.0.1", port: int = 6379, opts: Dictionary = { }, size: int = 0) -> Variant, Err
query(cmd: String, args: Array = []) -> Variant, Err
open_async(host: String = "127.0.0.1", port: int = 6379, opts: Dictionary = { }, size: int = 0) -> await Variant, Err
query_async(cmd: String, args: Array = []) -> await Variant, Err
close() -> void
size() -> int
in_flight() -> int

GDSQLiteDB

組込みSQLiteへの同期接続。

Synchronous connection to the embedded SQLite.

extends RefCounted

設定Settings: busy_ms max_rows max_bytes max_ms

Methods
exec(sql: String, params: Array = []) -> Dictionary, Err
query(sql: String, params: Array = []) -> Array, Err
prepare(sql: String) -> GDSQLiteStatement, Err
close() -> void
is_open() -> bool

GDSQLiteStatement

一度prepareして繰り返し使うSQLite文。

A SQLite statement prepared once and reused.

extends RefCounted

Methods
run(params: Array = []) -> Dictionary, Err
run_many(rows: Array) -> Dictionary, Err
one(params: Array = []) -> Variant, Err
all(params: Array = []) -> Array, Err
close() -> void
is_valid() -> bool

GDJSONLReader

分割して届くJSON Linesを一行ずつ復号するreader。

Reader decoding JSON Lines one line at a time as chunks arrive.

extends RefCounted

Methods
feed(chunk: String) -> Array, Err
finish() -> Array, Err

GDBinaryHeap

pickで決めた順位が小さい値から取り出すheap。

Heap that pops the value with the smallest rank chosen by pick first.

extends RefCounted

Methods
set_pick(pick: Callable) -> void
push(value: Variant) -> void
pop() -> Variant
peek() -> Variant
drain() -> Array
size() -> int
is_empty() -> bool

GDPriorityQueue

priorityの数が小さい順に、同じ数なら入れた順に値を取り出すqueue。

Queue that pops the smallest priority number first, and insertion order among equals.

extends RefCounted

Methods
push(value: Variant, priority: float = 0.0) -> void
pop() -> Variant
peek() -> Variant
size() -> int
is_empty() -> bool

GDLRUCache

件数と期限を制限して最近使った値を保持し、無ければ作って返すcache。

Cache holding recently used values under a count and expiry limit, creating a value when missing.

extends RefCounted

Methods
setup(limit: int, ttl_ms: int = 0) -> void
put(key: Variant, value: Variant) -> void
take(key: Variant, fallback: Variant = null) -> Variant
fetch(key: Variant, make: Callable) -> Variant
has(key: Variant) -> bool
erase(key: Variant) -> void
clear() -> void
size() -> int
limit() -> int
hit_count() -> int
miss_count() -> int
hit_rate() -> float

GDMemoizedCallable

関数の引数と戻り値をLRU cacheへ記憶するwrapper。

Wrapper remembering a function's arguments and results in an LRU cache.

extends RefCounted

Methods
setup(fn: Callable, limit: int = 128) -> void
call_with(args: Array) -> Variant

GDPostgresClient.query_rows

next()で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

Methods
next() -> bool
next_async() -> await bool
scan() -> Dictionary, Err
values() -> Array, Err
columns() -> PackedStringArray
err() -> Err
command_tag() -> String
close() -> void
cancel() -> void
is_closed() -> bool