feat: near parity with the official SDKs, typed query builder included (3.0.0) #102
No reviewers
Labels
No labels
area:defold
area:docs
area:infra
area:protocol
area:testing
kind:bug
kind:chore
phase:breadth
phase:core
phase:hardening
phase:slice
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
yuris/spacetimedb-defold!102
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat-parity"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What this is
3.0.0. The SDK reaches near feature parity with the official SpacetimeDB 2.x clients, with the
names a reader of the official docs already knows. The one gap the comparison table called
impossible — a typed query builder — turned out to be possible, and is the largest piece here.
Built from the
spacetimedbnpm package 2.8.2's own source rather than from the docs site, soevery name below mirrors something real. The research is on the branch's commits; the two
grounding facts that shaped the work:
lib/query.ts'stoSql, consumed bysubscription_builder_impl.ts:122-126). Queries always leave the clientas text. That is why a Lua port is a string templater rather than a query planner.
SELECT * FROM t [JOIN t2 ON col = col] [WHERE pred], ops= != < <= > >=, literalsINTEGER FLOAT STRING HEX TRUE FALSE,'escaped by doubling, identifiers quotable with
". No projection, no ordering, no limit, atmost two tables, both join columns indexed.
The breaking change
Every row and lifecycle callback takes a context first, as the official SDKs do:
Clean cutover: the conformance harness, every test, the README, the wiki and the new example are
all migrated in this branch. No shim, no arity sniffing.
ctxcarriesdb,reducers,procedures,event, andis_active/disconnect/subscription_builder.What shipped
stdb/query.lua.tables.message:where(function(r) return r.sender:eq("yuris") end)→SELECT * FROM "message" WHERE "message"."sender" = 'yuris'. Operatorseq ne lt lte gt gte, combinatorsand_ or_ not_,left_semijoin/right_semijoin. Unknown column, wrong value type, a u64 above 2^53 passed as a number, a non-hex value for an Identity column, an unindexed join column — each raises at the callsite naming what was wrong.not_works despite the grammar having no NOT, by inverting comparisons and applying De Morgan.ctxfirst in every callback;stdb.builder():with_url(...):with_database(...):with_schema(gen):build();db.reducers.<name>(args, cb)anddb.procedures.<name>(args, cb)generated from the schema, raising on a typo with the real names listed;db:subscription_builder():on_applied(...):on_error(...):subscribe(...)withis_active/is_ended/unsubscribe/unsubscribe_then;db.db;db:disconnect().db:state()→connecting/connected/reconnecting/closed, assigned only at the five points the SDK already learns of a transition.db:latency()is an EMA at weight 1/8 (TCP's own SRTT smoothing) over request/response pairs the SDK already exchanges — no new traffic, no heartbeat.db:ping(cb)forces one measurement with a one-off query againstst_tablefiltered to match nothing.stdb.await(fn, ...)andstdb.await_event(db, type), following nakama-defold's opt-in wrapper rather than auto-detecting. A truthyerrraises inside the coroutine instead of being returned for the caller to forget. Thestateguard is what stopsresumeon a coroutine that never yielded.dev/example-chat/, a realgame.projectwith a GUI, plus a headless entry point that proves the wiring: it sends a message and only prints PASS when that row comes back throughon_insert..script_api: answered, not built.script_apito native extensions and separately says the editor completes Lua modules a script requires. A pure-Lua library needs none. Recorded on the wiki roadmap as a decision.Two real defects found on the way
reducers/procedures, so following it and calling a reducerfailed on the default wire with
no schema entry for reducer send_message. Found by buildingthe example against the documented path.
connect, where there is no socketyet:
stdb.connect's pre-socket seam raisessend before the socket was created. Sends now sitin the connected path, where the conformance harness always had them.
Verification
lua dev/test/runner.lua→ 12 passed, 0 failed (a newtest_query.lua).stylua --check stdb dev/test stdb/spacetimedb.editor_scriptclean.harness passing is what proves the ctx migration is complete rather than merely compiling.
sender = ''' OR 1=1 --'— the escaped injection attempt returns 200 with no rows instead ofexecuting.
row returned through
on_insert.ergonomics layer, 6 for the query builder, 4 for state/latency, 2 for the coroutine wrapper,
each reverted to green.
Not in this branch
Frame compression stays out for the reasons #77 records. A CI job for the example's headless proof
is worth adding and is not here; the exact verified command is in the example's README.
Wiki updated in the same shape:
c68d269. Refs #77.