Skip to content

Add @b9g/zen/schema entrypoint for client-side usage - #23

Open
brainkim wants to merge 1 commit into
mainfrom
feat/schema-entrypoint
Open

Add @b9g/zen/schema entrypoint for client-side usage#23
brainkim wants to merge 1 commit into
mainfrom
feat/schema-entrypoint

Conversation

@brainkim

Copy link
Copy Markdown
Member

Closes #7.

A table is just a Zod schema plus metadata, so it's useful well away from a database connection — form validation, API contracts, types shared between client and server. None of that needs Database, drivers, migrations, query building or DDL generation.

@b9g/zen/schema exposes table/view definition, the extended z, field metadata (for form generation), the SQL builtins, and the validation errors — and nothing that reaches impl/database.js.

Result

Bundled for the browser with zod external:

Entry Size
@b9g/zen/schema 37,896 B
@b9g/zen 108,829 B

65% smaller — ~71KB of database runtime excluded.

One subtlety

The main entrypoint re-exports the SQL builtins from impl/database.js (zen.ts:89), so even import {NOW} from "@b9g/zen" drags in the whole database runtime. This entrypoint re-exports them from impl/builtins.js instead — same symbols (they're Symbol.for(), so identity holds across both), none of the runtime.

extendZod is idempotent (it guards with if (!("db" in value.prototype))), so importing both entrypoints is safe.

The isolation is pinned, not trusted

Rather than assume the import graph stays clean, the tests bundle the entrypoint and assert Database / DatabaseUpgradeEvent / "upgradeneeded" are absent — plus a control test asserting they are present in the main bundle, so the assertions can't quietly rot into vacuous truths.

(Careful with markers: "class Database" is a substring of "class DatabaseError", which legitimately is in the schema bundle. The tests match on "class Database extends EventTarget".)

Verification

  • bun test603 pass / 0 fail (9 new)
  • tsc --noEmit — clean

🤖 Generated with Claude Code

Closes #7.

A table is just a Zod schema plus metadata, so it is useful well away from a
database connection: form validation, API request/response contracts, and types
shared between client and server. None of that needs Database, drivers,
migrations, query building or DDL generation.

@b9g/zen/schema exposes table/view definition, the extended zod, field metadata
(for form generation), the SQL builtins and the validation errors — and nothing
that reaches impl/database.js.

One subtlety worth noting: the main entrypoint re-exports the SQL builtins
*from* impl/database.js, so importing NOW from "@b9g/zen" pulls in the whole
database runtime. This entrypoint re-exports them from impl/builtins.js instead.
Same symbols (they are Symbol.for(), so identity holds across both), none of the
runtime. extendZod is idempotent, so importing both entrypoints is safe.

Bundled for the browser with zod external:

  @b9g/zen/schema    37,896 bytes
  @b9g/zen          108,829 bytes   (65% larger)

The isolation is asserted structurally rather than trusted: the tests bundle the
entrypoint and assert Database/DatabaseUpgradeEvent/"upgradeneeded" are absent,
with a control test asserting they ARE present in the main bundle, so the
assertions cannot quietly become vacuous if the import graph changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Vvtr747kUp1NobqCBeZJi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add @b9g/zen/schema entrypoint for client-side usage

1 participant