chore(ts-migration): migrate API transport to TypeScript
Phase 4a: `demoUser` and `httpClient`. Type-only. The production bundle is
byte-identical - built from this commit's parent and from this commit, all six
chunk hashes match.
`httpClient` is the contract boundary, so the annotations are deliberately
conservative:
- `KrowApiError` is a CAST, not a class. The three error sites still build a
plain `Error` and assign `.name`, `.status`, `.code` and `.details` onto it
exactly as before. `class KrowApiError extends Error` would have read
better and changed three things that callers depend on: the prototype
chain, `instanceof`, and how `name` comes to be set.
- `RESOURCE_PATHS` becomes `Record<EntityName, EntityResourcePath>`. That is
the first thing in the repo to check the eighteen entity names against the
eighteen in `ENTITY_NAMES`; until now the two lists agreed only by habit,
and a divergence would have surfaced as `base44.entities.Whatever` being
undefined with nothing to say why.
- `createEntity`'s parameter stays `string` and its return stays inferred.
Annotating the return `EntityClient<any>` was tried and reverted: it makes
`list()` return `Promise<any[]>` where inference gives `Promise<any>`, and
`agentStore.js` does `qc.getQueryData(KEY) || (await ...list(...))`. React
Query types `getQueryData` as `unknown`; `unknown || any` collapses to
`any`, `unknown || any[]` stays `unknown`, and `.find` on the next line
stopped compiling. Two new errors in a file this phase does not migrate,
for no gain. `EntityClientFor<K>` is ready for the phase that migrates
those consumers.
Unchanged and verified in the emitted output: `credentials: 'include'`, both
header branches on `body === undefined`, the URLSearchParams query encoding with
its repeated-array and undefined-omission rules, the `payload.data` unwrap that
drops `meta`, the verbatim server message, `status: 0` / `code: 'unreachable'`
for a transport failure, the `-created_date` and `limit` defaults, path
construction through `encodeURIComponent`, and `bulkCreate`'s sequential
`this.create` loop.
`DEMO_USER` is annotated `User`, which does real work: unannotated,
`role: 'admin'` widens to `string` and the default shape did not satisfy the
type the app uses for the thing it defaults.
Verified in isolation from the parallel feature work (d1425f9 plus these two
files): tsc 64 -> 53, the eleven removed being exactly this file's, and a
set-difference against the baseline showing none added. skill-check 1641/1642
with only the known stale-fixture failure, Owliver baseline 59/59, lint 0
errors, build succeeds with the API origin inlined.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBG1wnuRfJKCstGB8Fekr8
This commit is contained in:
41
src/api/demoUser.ts
Normal file
41
src/api/demoUser.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import type { User } from '@/types/user';
|
||||
|
||||
/**
|
||||
* The shape of a signed-in user, before the server has answered.
|
||||
*
|
||||
* This is a **default shape, not a record**. The user lives in PostgreSQL and
|
||||
* arrives from `GET /me`; what is here is the set of keys that must resolve
|
||||
* during the first render, in the fraction of a second before that response
|
||||
* lands.
|
||||
*
|
||||
* It exists as its own module for one reason: `base44Client.js` needs exactly
|
||||
* this, and it used to reach into `api/seed.js` to get it — a 1,900-line
|
||||
* fixture of demo positions, candidates, interviews, staff and shift records,
|
||||
* every byte of which was then in the production bundle so that three booleans
|
||||
* could be defaulted. The fixture is still the right thing for the test scripts
|
||||
* that read it (`scripts/skill-check.mjs`, `scripts/owliver-capture.mjs`); it
|
||||
* was never the right thing for the running app. `seed.js` re-exports this
|
||||
* constant, so those scripts are unchanged and the production import chain no
|
||||
* longer reaches them.
|
||||
*
|
||||
* Nothing here is a source of truth for anything. `preferences` is the one part
|
||||
* that is read: `auth.preferences()` is synchronous — `AssistantPanelContext`
|
||||
* decides whether Owliver starts open in a `useState` initialiser — so a key
|
||||
* the server has never stored still has to resolve to something rather than to
|
||||
* `undefined`.
|
||||
*/
|
||||
export const DEMO_USER: User = {
|
||||
id: 'user_demo',
|
||||
full_name: 'Alex Rivera',
|
||||
email: 'demo@krow.app',
|
||||
role: 'admin',
|
||||
account_type: 'employer',
|
||||
created_date: '2026-06-01T09:00:00.000Z',
|
||||
/* Product preferences travel with the account rather than in a store of their
|
||||
own, so there is one record to persist and one thing to read. */
|
||||
preferences: {
|
||||
owliverDefault: true,
|
||||
compactDensity: false,
|
||||
emailDigest: true,
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user