Restructure world packages, add SPA defaults registry, fix create-house race - #270
Merged
Conversation
…se race
Contract package
- Rename `createWorldClient` → `createClient`, drop unused `createWorldQuery`
helper (and its `@orpc/tanstack-query` dep on the client side).
- Reorganise `lib/router/{me,workspace}` into a router-as-folder layout that
mirrors `world-server/lib/router/`: `lib/router/workspace/sync.ts` for the
procedure, `lib/router/workspace/index.ts` aggregates, `lib/router/index.ts`
re-exports.
- Drop the `me` procedure; nothing in the SPA was consuming it (better-auth's
session already carries the user).
- Add wildcard `./router/*` subpath to `package.json` exports so TS composite
mode can portably name types that reference inner router modules.
Server package
- Switch `lib/contract.ts` to `import * as definition from
"@openkitten/world-contract"` and `lib/rpc-handler.ts` to
`import * as router from "~/lib/router"` so adding a new router file flows
through every consumer without edits.
- Add `lib/types.ts` and a `./types` subpath export for SPA-facing type
re-exports (currently exposes `ActiveMember`).
- Fix the create-house failure (`isPersonalHouse` no longer throws when the
workspace row is missing). The old throw fired during the better-auth org
plugin's `beforeAddMember` hook before `afterCreateOrganization` could
provision the workspace, leaving orphan `house` rows that surfaced as
`ORGANIZATION_ALREADY_EXISTS` on retry with the same slug.
SPA package
- Replace `rpc-query.ts` with `lib/orpc-client.ts` + `lib/orpc-utils.ts`
(`createTanstackQueryUtils` from `@orpc/tanstack-query`), mirroring the
pdfvector layout.
- Add `lib/apply-defaults.ts` invoked from `entry.client.tsx`; registers
mutation defaults for every better-auth org operation
(create / delete / setActive / update / inviteMember / removeMember /
updateMemberRole / cancelInvitation / acceptInvitation / rejectInvitation /
leave) plus query defaults for `orpcUtils.workspace.sync`,
`orgQueryKeys.list`, and a 60s `staleTime` on the session key.
- Split keys into `lib/org-query-keys.ts` (scope tree:
`all/list/full/members/invitations/invitation`) and
`lib/org-mutation-keys.ts` (`organizations`/`active`) so query-options files
derive their keys from the same source the registry components use for
invalidation.
- Each `*-query-options.ts` file now imports the scope from
`org-query-keys.ts` instead of re-hardcoding the key shape.
- New `lib/query-error-alert.tsx` helper for the data-fetching rule, plus a
README rule mandating one `useQuery` per component with full state handling
(`isPending`, `isError`, `error`, `isRefetching`, `refetch`) and a Retry
button wired through `getErrorMessage`.
- Switch `app/root.tsx` from `import.meta.env.DEV` to `!isLive` from
`@openkitten/world-util`; rewrite `app/routes/{index,workspace}.tsx` to use
the new pattern.
- Align `sessionQueryOptions.queryKey` to `["auth", "getSession", null]` so
the SPA's `clientLoader` and `@better-auth-ui/react`'s `useSession` share
one cache (down from 8 to 2 `/auth/get-session` requests per nav). Make
`start-auth-session-sync.ts` smart: only invalidate when the session id
actually changes, instead of refetching on every `$sessionSignal`.
- File renames matching the export name: `lib/active-organization-id.ts`
→ `get-active-organization-id.ts`, `auth-session-sync.ts`
→ `start-auth-session-sync.ts`, `utils.ts` → `cn.ts`.
- Components: shadcn/better-auth-ui registry output stays under
`components/`; hand-written customs live under `lib/`. The original
`components/auth/`, `components/settings/`, `components/user/` are kept
intact (they're CLI-managed).
- Migrate `formatError` into `getErrorMessage` in `world-util` (now handles
Error / string / nested `{error: {message}}` / top-level `{message}` / falls
back to "Something went wrong"); delete `lib/format-error.ts`; route every
caller through the util helper.
Server runtime
- Migrate `is-personal-house.ts` to return `false` for missing-workspace
instead of throwing; the strict invariant is enforced by `auth-contract.ts`
middleware, so the permission gate doesn't need to.
Deps (all minor / patch)
- server: nodemailer 8.0.5→8.0.7, react-email 6.0.0→6.0.5,
@react-email/ui 6.0.0→6.0.5
- spa: @mantine/hooks 9.1.0→9.1.1, @tanstack/react-query 5.100.1→5.100.6,
@tanstack/react-query-devtools 5.100.1→5.100.6, lucide-react 1.12.0→1.14.0,
recharts 3.8.0→3.8.1, jsdom 29.0.2→29.1.0, shadcn 4.4.0→4.6.0
- spa: add `@orpc/tanstack-query`
100% coverage held across `world-contract` (13), `world-util` (27),
`world-client` (6), `world-server` (92), `world-spa` (580).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
khanhduyvt0101
enabled auto-merge (squash)
April 29, 2026 12:43
khanhduyvt0101
disabled auto-merge
April 29, 2026 12:44
…ullAssertion warnings
AGENTS.md prohibits non-null assertions (`!`). The 22 outstanding biome
warnings were all `.closest("form")!` in dialog test files. Replace with a
typed helper at `test/lib/closest-form.ts` that throws if the lookup misses.
Also pick up the one fixable `useTemplate` lint via biome --write --unsafe.
biome check is now 0 errors / 0 warnings / 0 info; tsc clean; all 580 SPA
tests still green at 100% coverage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
lib/router/{me,workspace}→lib/router/workspace/{index,sync}.ts),createWorldClientrenamed tocreateClient, deadmeprocedure +createWorldQueryhelper removed, wildcard./router/*subpath added so TS composite-mode declaration emit can portably name inner router types.import * as definition from "@openkitten/world-contract",import * as router from "~/lib/router") so adding a new router file flows through every consumer with no edits; newlib/types.ts+./typessubpath for SPA-facing types;is-personal-house.tsreturnsfalseinstead of throwing on missing workspace, fixing the create-house failure where better-auth'sbeforeAddMemberhook fired beforeafterCreateOrganizationhad provisioned the workspace row, leaving orphanhouserows that surfaced asORGANIZATION_ALREADY_EXISTSon retry.apply-defaults.tsinvoked fromentry.client.tsxregistering mutation defaults for every better-auth org operation and query defaults forworkspace.sync,orgQueryKeys.list, and a 60sstaleTimeon the session key.lib/org-query-keys.tsis a scope tree (all/list/full/members/invitations/invitation) andlib/org-mutation-keys.tscarries the mutation scopes (organizations/active) so registry components and our defaults invalidate the same cache.sessionQueryOptions.queryKeyaligned to["auth", "getSession", null]to match@better-auth-ui/react, dropping/auth/get-sessiontraffic from 8 to 2 calls per nav.start-auth-session-sync.tsonly invalidates when the session id actually changes (was: refetch + avalanche-invalidate on every$sessionSignal).formatErrorconsolidated intogetErrorMessagein@openkitten/world-util;lib/format-error.tsdeleted; every caller migrated. New helper handles Error / string / nested{error: {message}}/ top-level{message}/ falls back to"Something went wrong".components/is for registry-generated output (shadcn, better-auth-ui); custom components live underlib/(flat for multi-route,lib/<route>/<component>.tsxfor single-route, function files always flat). Newlib/query-error-alert.tsxcodifies the data-fetching pattern with retry.lib/active-organization-id.ts→get-active-organization-id.ts,auth-session-sync.ts→start-auth-session-sync.ts,utils.ts→cn.ts.@orpc/tanstack-query.Test plan
bun --bun tsc --buildcleanbun run --workspaces --if-present test— every world package green:world-contract13,world-util27,world-client6,world-server92,world-spa580bun --bun biome check packages/worldreports 0 errors (22 pre-existing warnings in registry test files left alone)/→/workspace/members→ Create house → Update name → Update slug → Invite member → Delete house → Sign-out, all flows render correctly with no orphan DB rows/auth/get-sessionrequest count on hard reload is 2 (one TanStack, one nanostore)🤖 Generated with Claude Code