@imfusion/web-ui 0.5.1-dev.40.gfb5bc73e → 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/package.json +1 -1
- package/src/llms/skills/imf-web-ui-conventions/topics/authentication.md +3 -3
- package/src/llms/skills/imf-web-ui-conventions/topics/data.md +16 -7
- package/src/llms/skills/imf-web-ui-conventions/topics/react.md +4 -2
- package/src/llms/skills/imf-web-ui-conventions/topics/typescript.md +28 -0
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@ project-owned — no provider is prescribed or named.
|
|
|
11
11
|
|
|
12
12
|
```text
|
|
13
13
|
src/
|
|
14
|
-
api/auth/ #
|
|
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: `
|
|
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.
|
|
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
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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
|
|
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
|
|
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
|
+
```
|