@ogygie/cli 0.16.0 → 0.17.0

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/dist/cli.js CHANGED
@@ -15940,6 +15940,18 @@ var governedChecksumsByVersion = {
15940
15940
  "scripts/verify-specs.ts": "03d58baa11822ad7ae67080f990f7a82f4f94d223b12777728b8b9ddd7ebd843",
15941
15941
  "tsconfig.base.json": "0f8919e9ed8bbc88e0308fa2fa91471dabb18d1df721bd78c04076597eba89a2"
15942
15942
  },
15943
+ "0.17.0": {
15944
+ ".github/workflows/ogygie-gates.yml": "48c40efe9fe66dd0f9c3b94287026d80eae9afd6782b5833fdf8846d8662babf",
15945
+ "AGENTS.md": "e058a54ff4750abf19f7c4b026e8be661d8ce25419596d22c20de2c387469370",
15946
+ "CLAUDE.md": "785ee8d90e368806d8cf0b4d84a15f29d1dc961168bda86cf1a6c60a923c8c9b",
15947
+ "scripts/lint-interdits.ts": "4644e1aaabc1d19ed42a0bdf8e2e55b9451f789d87d59754bce3432de7c357e7",
15948
+ "scripts/ogygie-design-brief.ts": "aba1585717b30808dd0bc8930a37a1ad2d83ee6869f969dbd06ca595e37a4a89",
15949
+ "scripts/ogygie-impact.ts": "f06cc8b4a46ddfbcfb32ba747c2454548ca49918123d04ab13d210b4a89c1fb9",
15950
+ "scripts/verify-contracts.ts": "9cd64c88dcc3b54b65bf1e1b7a68eb00353ee16f2a9ce8bca3c49ba063c2a08b",
15951
+ "scripts/verify-manifest.ts": "6c448fa3a19b87666f686ca57c6ca834d9e169b1380e4fe6e4c19b995bf94f3c",
15952
+ "scripts/verify-specs.ts": "03d58baa11822ad7ae67080f990f7a82f4f94d223b12777728b8b9ddd7ebd843",
15953
+ "tsconfig.base.json": "0f8919e9ed8bbc88e0308fa2fa91471dabb18d1df721bd78c04076597eba89a2"
15954
+ },
15943
15955
  "0.2.0": {
15944
15956
  ".github/workflows/ogygie-gates.yml": "48c40efe9fe66dd0f9c3b94287026d80eae9afd6782b5833fdf8846d8662babf",
15945
15957
  "AGENTS.md": "ca73ab619d8c55fac11858d815bf5fbab6537ecebe7ae4123cbdeb3e3e36bf1a",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ogygie/cli",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Ogygie framework CLI: deterministic scaffolding (init), governed-file upgrades (upgrade) and lockstep verification (check) for Ogygie products.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  "access": "public"
18
18
  },
19
19
  "devDependencies": {
20
- "@ogygie/core": "0.16.0",
20
+ "@ogygie/core": "0.17.0",
21
21
  "@types/node": "^24.0.0",
22
22
  "esbuild": "^0.28.0",
23
23
  "tsx": "^4.20.0",
@@ -295,12 +295,19 @@ dérogation qui s'oublie. Elle ne couvre JAMAIS le code du produit lui-même.
295
295
  ### Stack imposée (toute autre lib = amendement DECISIONS.md d'abord)
296
296
  Vite + React 19 + TanStack Router (routing EN CODE) + TanStack Query (état serveur) ;
297
297
  PAS de lib d'état client (useState suffit) ; Tailwind v4 thémé depuis `design/tokens.json`
298
- (valeurs arbitraires `p-[13px]` interdites par lint) ; primitives UI copiées dans
299
- `apps/web/src/patterns/` (jamais consommées directement depuis node_modules par une
300
- feature) ; react-hook-form UNIQUEMENT via le pattern `ContractForm` ; TanStack
301
- Table UNIQUEMENT via `DataTable` ; Recharts UNIQUEMENT via `VarianceChart` ;
298
+ (valeurs arbitraires `p-[13px]` interdites par lint) ; le socle de patterns vient du
299
+ paquet `@ogygie/ui` — `ScreenState`, `EmptyState`, `ErrorBanner`, `EditableText`,
300
+ `fetchContract`, et derrière leur propre point d'entrée `DataTable`
301
+ (`@ogygie/ui/data-table`), `ContractForm` (`@ogygie/ui/contract-form`),
302
+ `VarianceChart` (`@ogygie/ui/variance-chart`) — IMPORTÉ, jamais recopié ; les
303
+ patterns PRODUIT vivent dans `apps/web/src/patterns/` (jamais consommés directement
304
+ depuis node_modules par une feature) ; react-hook-form UNIQUEMENT via `ContractForm` ;
305
+ TanStack Table UNIQUEMENT via `DataTable` ; Recharts UNIQUEMENT via `VarianceChart` ;
302
306
  date-fns via les helpers `@ogygie/core` (jamais de `new Date()` nu) ; i18n Lingui
303
- (ICU, extraction vérifiée en CI). Noms de patterns en anglais.
307
+ (ICU, extraction vérifiée en CI) — `@ogygie/ui` ne porte AUCUNE chaîne : tout texte
308
+ lui arrive en prop, traduit par la feature. Noms de patterns en anglais. La feuille
309
+ Tailwind du produit déclare le paquet comme source
310
+ (`@source "../node_modules/@ogygie/ui/src";`), sinon ses classes ne sont pas générées.
304
311
 
305
312
  ### Structure d'une feature front (miroir de l'API)
306
313
  ```
@@ -319,7 +326,7 @@ apps/web/features/orders/
319
326
  /** @intent Data access of the orders feature. Re-validates contracts in dev. */
320
327
  import { useQuery } from "@tanstack/react-query";
321
328
  import { ListOrdersOutputSchema } from "@product/api/features/orders/schemas";
322
- import { fetchContract } from "../../lib/fetchContract"; // Zod parse in dev, OgygieError on broken contract
329
+ import { fetchContract } from "@ogygie/ui"; // Zod parse of the answer; an OgygieFailure on a refused call or a broken contract
323
330
 
324
331
  export function useOrders() {
325
332
  return useQuery({
@@ -334,18 +341,25 @@ export function useOrders() {
334
341
  // apps/web/features/orders/OrdersList.tsx
335
342
  /** @intent Orders list screen: composition of patterns, zero business logic. */
336
343
  import { Trans } from "@lingui/react/macro";
337
- import { DataTable } from "../../patterns/DataTable";
338
- import { EmptyState } from "../../patterns/EmptyState";
339
- import { ErrorBanner } from "../../patterns/ErrorBanner";
344
+ import { ScreenState } from "@ogygie/ui";
345
+ import { DataTable } from "@ogygie/ui/data-table";
340
346
  import { useOrders } from "./queries";
341
347
 
342
348
  export function OrdersList() {
343
- const { data, isPending, error } = useOrders();
344
- if (error) return <ErrorBanner error={error} />;
345
- if (isPending) return <DataTable.Skeleton />;
346
- if (data.orders.length === 0)
347
- return <EmptyState title={<Trans>Ajoutez votre première commande</Trans>} />;
348
- return <DataTable rows={data.orders} columns={orderColumns} />;
349
+ return (
350
+ <ScreenState
351
+ query={useOrders()}
352
+ errorTitle={<Trans>Cette vue n'a pas pu être chargée</Trans>}
353
+ loading={<DataTable.Skeleton />}
354
+ isEmpty={(data) => data.orders.length === 0}
355
+ emptyState={{
356
+ title: <Trans>Ajoutez votre première commande</Trans>,
357
+ explanation: <Trans>Les commandes enregistrées apparaissent ici.</Trans>,
358
+ }}
359
+ >
360
+ {(data) => <DataTable rows={data.orders} columns={orderColumns} rowKey={(order) => order.id} />}
361
+ </ScreenState>
362
+ );
349
363
  }
350
364
  ```
351
365
 
@@ -353,6 +367,9 @@ export function OrdersList() {
353
367
  - Un écran est une COMPOSITION de patterns : jamais de `<table>`, `<form>` ou fetch nus
354
368
  dans une feature. Si un pattern manque, on le crée dans `patterns/` (et on met à jour
355
369
  `design/patterns.md`), on ne bricole pas dans la feature.
370
+ - Les états d'un écran — accès refusé, erreur, chargement, vide — passent par
371
+ `ScreenState`, jamais par une cascade de `if` propre à l'écran : le corps ne reçoit
372
+ que des données servies.
356
373
  - Chaque état UI listé dans la spec (vide/chargement/nominal/erreur) a son test.
357
374
  - Toute chaîne visible passe par Lingui (`<Trans>`/`t`) — la CI casse sinon.
358
375
  - Chiffres financiers : `tabular-nums`, alignés à droite, unité explicite.
@@ -2,27 +2,35 @@
2
2
 
3
3
  > Les patterns sont les SEULES briques d'écran autorisées (AGENTS.md §Front) :
4
4
  > un écran est une COMPOSITION de patterns, jamais de `<table>`, `<form>` ni
5
- > fetch nus. Le starter n'a pas encore d'app front produit : à l'ouverture du
6
- > premier front, chaque pattern retenu est COPIÉ dans `apps/web/src/patterns/`
7
- > et sa fiche ci-dessous devient son contrat. Un pattern manquant se crée ICI
8
- > d'abord (fiche + décision), jamais en one-off dans un écran.
5
+ > fetch nus. Les patterns GÉNÉRIQUES ci-dessous sont LIVRÉS par le paquet
6
+ > `@ogygie/ui` : un produit les importe, il ne les recopie pas, et leur fiche
7
+ > ci-dessous est leur contrat. Un pattern PRODUIT — propre au domaine — naît
8
+ > dans `apps/web/src/patterns/` avec sa fiche ICI d'abord, jamais en one-off
9
+ > dans un écran. Tout texte lu par un humain arrive à un pattern générique en
10
+ > prop, traduit par la feature (Lingui) : le paquet ne porte aucune chaîne.
9
11
  >
10
12
  > Ce catalogue est une source de vérité du brief de design
11
13
  > (`pnpm ogygie:design-brief <feature>`) : chaque fiche `## <Name>` devient
12
14
  > une entrée du vocabulaire de composition. Noms en anglais (machine-lisible),
13
15
  > fiches en français (document humain).
14
16
 
17
+ ## ScreenState
18
+ Le composant d'état UNIQUE d'un écran (`@ogygie/ui`). Reçoit la lecture (`QueryLike`, sous-ensemble d'un résultat TanStack Query) et le corps en fonction de rendu, et décide dans cet ordre : accès refusé (`denied` de l'appelant, ou une réponse 401/403 quand une copie `accessDenied` est donnée), erreur (`ErrorBanner`, titré par `errorTitle`), chargement (`loading`, à défaut `Skeleton`), vide (`isEmpty` + `emptyState`, un `EmptyState` avec son action), puis le corps avec des données servies. Un corps ne branche jamais sur `undefined`.
19
+
15
20
  ## DataTable
16
- Tableau de données (TanStack Table, seul usage autorisé). États : squelette de chargement (`DataTable.Skeleton`), vide (déléguer à EmptyState), nominal trié/paginé, erreur (déléguer à ErrorBanner). Chiffres en `tabular-nums` alignés à droite, unité explicite.
21
+ Tableau de données (`@ogygie/ui/data-table`, TanStack Table — seul usage autorisé). Colonnes typées `{ id, header, cell, sortValue?, align? }` ; tri par clic d'en-tête (`aria-sort`), chiffres `align: "end"` en `tabular-nums` alignés à droite, unité explicite dans la cellule. États : squelette de chargement (`DataTable.Skeleton`), vide DÉLÉGUÉ (`emptyState` : un `EmptyState` qui dit pourquoi), nominal trié ; l'erreur appartient à `ScreenState`. La liste de colonnes reste stable (module ou `useMemo`).
17
22
 
18
23
  ## EmptyState
19
- État vide intentionnel : un titre, une explication courte, UNE action de création. Jamais de tableau ou de liste vides nus — l'écran vide est le premier écran que voit chaque nouvel utilisateur.
24
+ État vide intentionnel (`@ogygie/ui`) : un titre qui nomme la CAUSE, une explication qui dit ce qui le remplirait, UNE action de création au plus. Jamais de tableau ou de liste vides nus — l'écran vide est le premier écran que voit chaque nouvel utilisateur. Deux causes, deux formulations : rien d'enregistré, ou un filtre qui ne trouve rien.
20
25
 
21
26
  ## ErrorBanner
22
- Bandeau d'erreur qui affiche une OgygieError : message traduit via `userMessageKey` (clé ICU), code stable et `suggestedFix` repliables. Tout état d'erreur d'écran passe par lui — jamais de message d'erreur improvisé.
27
+ Bandeau d'erreur (`@ogygie/ui`) : affiche une `OgygieFailure` en entier — le titre du produit, le message, le code stable avec le statut et la cause, la correction suggérée — et dégrade toute autre erreur à son message. `kind: "save"` marque une écriture refusée (le produit titre autrement) ; `retry` ajoute le bouton que le produit libelle. Tout état d'erreur d'écran passe par lui — jamais de message improvisé.
23
28
 
24
29
  ## ContractForm
25
- Formulaire DÉRIVÉ d'un contrat Zod (react-hook-form, seul usage autorisé) : les contraintes du schéma (min/max/regex, obligatoire) sont les validations des champs — rien n'est redéclaré à la main. États : saisie, soumission en cours (bouton désactivé), erreurs de champ, erreur serveur (ErrorBanner).
30
+ Formulaire DÉRIVÉ d'un contrat Zod (`@ogygie/ui/contract-form`, react-hook-form — seul usage autorisé) : les champs viennent de la forme du schéma, leurs contrôles de leur genre (texte, nombre, date, sélection d'enum, case à cocher), leurs contraintes des bornes du schéma — rien n'est redéclaré à la main — et le schéma valide chaque soumission (`safeParse`). Libellés, aides, libellés d'options, `submitLabel` et `cancel` sont des props traduites ; `renderIssue` traduit la phrase d'un refus. États : saisie, soumission en cours (bouton inactif, `pending`), erreur de champ (une phrase sous le champ), erreur de formulaire (un `refine`), erreur serveur (`error` : un `ErrorBanner` save au-dessus des actions). `onSubmit` reçoit la SORTIE parsée du contrat.
26
31
 
27
32
  ## VarianceChart
28
- Graphique d'écart (Recharts, seul usage autorisé) : compare une valeur constatée à une référence. États : chargement, vide, nominal, erreur. Montants en `tabular-nums`, axe et unité toujours étiquetés.
33
+ Graphique d'écart (`@ogygie/ui/variance-chart`, Recharts — seul usage autorisé) : une valeur constatée (`measured`) contre une référence (`reference`), par libellé, en deux barres groupées ; l'écart signé se lit dans l'infobulle ; une ligne zéro quand les valeurs la traversent. Couleurs tirées des tokens du produit par variables CSS, noms de séries et unité en props traduites. États : vide DÉLÉGUÉ (`emptyState`), squelette (`VarianceChart.Skeleton`) ; chargement et erreur appartiennent à `ScreenState`. Montants en `tabular-nums`, axe et unité toujours étiquetés.
34
+
35
+ ## EditableText
36
+ Texte qui s'écrit là où il se lit (`@ogygie/ui`) : un clic le transforme en champ, quitter le champ ou Entrée enregistre, Échap abandonne. Un lecteur sans droit voit le texte nu, jamais un contrôle inactif. Un brouillon blanc est refusé sans écriture (sauf `allowEmpty`) ; une borne (`maxLength`) arrête le champ et affiche le compteur ; `pending` rend le contrôle inactif le temps d'une écriture. `placeholder` et `ariaLabel` sont traduits par la feature.