@imfusion/web-ui 0.5.1-dev.39.gfc64049e → 0.5.1-dev.41.g45c10e85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -143,7 +143,7 @@ namespace.
143
143
 
144
144
  Release-facing changes are recorded as one-line entries in [`CHANGELOG.md`](./CHANGELOG.md) under `## [Unreleased]`. The
145
145
  commit workflow requires an entry for pull-request commits and direct commits to `master`, with only narrow mechanical
146
- exceptions.
146
+ exceptions. Each entry ends with its author as ` — Name <email>`, taken from the committer's git config.
147
147
 
148
148
  The published version is derived from git tags, not from `package.json`.
149
149
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imfusion/web-ui",
3
- "version": "0.5.1-dev.39.gfc64049e",
3
+ "version": "0.5.1-dev.41.g45c10e85",
4
4
  "description": "The official Web UI component library for ImFusion web apps",
5
5
  "author": "ImFusion GmbH",
6
6
  "license": "UNLICENSED",
@@ -11,7 +11,7 @@ project-owned — no provider is prescribed or named.
11
11
 
12
12
  ```text
13
13
  src/
14
- api/auth/ # getCurrentUser query options, query-key.ts, types.ts, index.ts barrel
14
+ api/auth/ # getUser query options, keys.ts, types.ts, index.ts barrel
15
15
  lib/auth/
16
16
  login-url.ts # pure login navigation helper; not an API topic file
17
17
  login-url.test.ts # focused helper tests
@@ -25,10 +25,10 @@ src/
25
25
 
26
26
  - One identity source of truth: a server-backed current-user query; never copy session credentials or access tokens into
27
27
  React state.
28
- - The auth topic follows the default [data.md](data.md) shape: `getCurrentUser` in `api/auth/auth.ts`, key leading with the
28
+ - The auth topic follows the default [data.md](data.md) shape: `getUser` in `api/auth/queries.ts`, key leading with the
29
29
  `auth` topic, schema in `types.ts`.
30
30
  - `lib/auth/login-url.ts`: project-owned navigation helper, not an options factory; tests colocated.
31
- - Reached as `context.api.auth.getCurrentUser()`; returns query options, never a hook or a user value — callers pick
31
+ - Reached as `context.api.auth.getUser()`; returns query options, never a hook or a user value — callers pick
32
32
  `ensureQueryData` or a query hook.
33
33
  - Never infer authentication from local storage, a decoded token, a route flag, or permission-gated chrome — stale or
34
34
  forgeable; the server response and its schema define the current user.
@@ -7,9 +7,11 @@ the boundary principles — validate at the edge, errors as values — not this
7
7
 
8
8
  - One folder per API topic: options factories, key factory, Zod schemas together, so a query key is never spelled out at a
9
9
  call site.
10
- - Default topic shape `<topic>.ts` + `query-key.ts` + `types.ts`; the reference project's GraphQL transport expands to
11
- `keys.ts`, `queries.ts`/`queryFns.ts`, `mutations.ts`/`mutationFns.ts`, `index.ts` — GraphQL-specific, not a transport
12
- requirement.
10
+ - Name a file for what it holds, never for the topic it sits in — the folder already says that: `keys.ts`, not `<topic>.ts`
11
+ or `query-key.ts` (it holds mutation keys too).
12
+ - A file appears only when the topic needs it: a topic with only queries has no `mutations.ts`, and vice versa.
13
+ - A GraphQL (or similarly document-based) transport may split `queries.ts`/`mutations.ts` further into
14
+ `queryFns.ts`/`mutationFns.ts`; that's an elaboration of the same options-vs-mutations split, not its origin.
13
15
  - Transport code (base client, error normalisation): `http/` and nowhere else.
14
16
  - Topic helpers: `lib/`, framework-free, tested beside themselves (`build-query-string.ts` + its test) — never inside the
15
17
  topic folder.
@@ -19,8 +21,13 @@ src/
19
21
  api/
20
22
  topics.ts # one topic registry, reused by keys and namespaces
21
23
  index.ts # topic barrels composed as api.<topic>
22
- user/ # expanded topic (used in the examples): index.ts barrel, keys.ts, queries.ts, mutations.ts
23
- <topic>/ # default shape: <topic>.ts, query-key.ts, types.ts
24
+ <topic>/
25
+ index.ts # barrel: named re-exports of what other modules call
26
+ keys.ts # createApiKeys — query and mutation keys
27
+ queries.ts # query options factories — present when the topic has queries
28
+ mutations.ts # mutation options factories — present when the topic has mutations
29
+ types.ts # Zod schemas + z.infer types
30
+ types.test.ts # schema cases, when the schema encodes product behaviour
24
31
  http/ # transport: client, error normalisation — the only transport-aware place
25
32
  lib/ # framework-free helpers shared by API topics and other callers
26
33
  ```
@@ -94,6 +101,8 @@ export type API = typeof api;
94
101
  - No topic prefix, no `Options` suffix: `context.api.<topic>.<fn>()` carries both.
95
102
  - Queries `get*` (`getAll`, `getById`, `getBySlug`, `getDetails`, `getActive`, `getFiltered`); mutations bare verbs
96
103
  (`update`, `login`, `addItem`, `deleteAddress`, `setShippingMethod`).
104
+ - Same suffix-free naming applies to the local binding a query or mutation resolves to —
105
+ [typescript.md](typescript.md#naming).
97
106
 
98
107
  ```ts
99
108
  // api/user/queries.ts — returns options; keys come from the factory
@@ -166,13 +175,13 @@ export const Route = createFileRoute("/user")({
166
175
 
167
176
  function UserPage() {
168
177
  const context = Route.useRouteContext();
169
- const { data: user } = useSuspenseQuery(context.api.user.getDetails());
178
+ const user = useSuspenseQuery(context.api.user.getDetails());
170
179
  const updateUser = useMutation(
171
180
  context.api.user.update({
172
181
  onSuccess: () => context.queryClient.invalidateQueries({ queryKey: context.api.orders.keys.topic })
173
182
  })
174
183
  );
175
- return <input defaultValue={user.name} onBlur={e => updateUser.mutate({ name: e.target.value })} />;
184
+ return <input defaultValue={user.data.name} onBlur={e => updateUser.mutate({ name: e.target.value })} />;
176
185
  }
177
186
  ```
178
187
 
@@ -44,9 +44,9 @@ function UserCard({ name, role, onEdit }: { name: string; role: string; onEdit:
44
44
  ```tsx
45
45
  // Smart — knows where data comes from, renders the dumb component
46
46
  function UserCardContainer({ userId }: { userId: string }) {
47
- const { data } = useUserQuery(userId);
47
+ const user = useUserQuery(userId);
48
48
  const openEditor = useEditorNavigation(userId);
49
- return <UserCard name={data.name} role={data.role} onEdit={openEditor} />;
49
+ return <UserCard name={user.data.name} role={user.data.role} onEdit={openEditor} />;
50
50
  }
51
51
  ```
52
52
 
@@ -95,6 +95,8 @@ useEffect(
95
95
  );
96
96
  ```
97
97
 
98
+ - A dependency sourced from a hook or query return: destructure the primitive first — [typescript.md](typescript.md#naming).
99
+
98
100
  ## Reading list
99
101
 
100
102
  Consult while building; each is the authority for its topic:
@@ -43,3 +43,31 @@ const activeNames = users.filter(u => u.isActive).map(u => u.name);
43
43
  - Booleans read as assertions: `isOpen`, `hasAccess`, `canSubmit`.
44
44
  - Handlers: `onX` as props, `handleX` as implementations.
45
45
  - Match the vocabulary the product and the API already use — no synonyms for terms the backend named.
46
+ - Keep the object a hook or query returns as a namespace instead of destructuring it, and drop the `Query`/`Mutation` suffix
47
+ from the local binding — the binding reads as the domain name, and every field access shows where it came from:
48
+
49
+ ```tsx
50
+ const search = Route.useSearch(); // not const { page, filter } = ...
51
+ const user = useSuspenseQuery(context.api.user.getDetails()); // not const { data } = ...
52
+ const updateUser = useMutation(context.api.user.update());
53
+
54
+ search.page;
55
+ user.data.name;
56
+ updateUser.mutate(values);
57
+ updateUser.isPending;
58
+ ```
59
+
60
+ - Exception: a `useEffect` dependency array needs a stable primitive, since deps compare by identity — destructure the
61
+ primitive out first rather than listing the object or a deep path into it:
62
+
63
+ ```tsx
64
+ const user = useSuspenseQuery(context.api.auth.getUser());
65
+ const { name } = user.data; // destructure for the dep array
66
+
67
+ useEffect(
68
+ function syncDocumentTitle() {
69
+ document.title = name;
70
+ },
71
+ [name]
72
+ );
73
+ ```