@supersoniks/concorde 4.6.0 → 4.7.3

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.
Files changed (182) hide show
  1. package/.gitlab-ci.yml +23 -0
  2. package/README.md +106 -55
  3. package/ai/AGENTS.md +52 -0
  4. package/ai/README.md +30 -0
  5. package/ai/cursor/rules/concorde-menu.mdc +15 -0
  6. package/ai/cursor/rules/concorde-scope.mdc +14 -0
  7. package/ai/cursor/rules/concorde-theme.mdc +13 -0
  8. package/ai/cursor/rules/concorde.mdc +49 -0
  9. package/ai/jetbrains/rules/concorde.md +39 -0
  10. package/ai/skills/concorde/SKILL.md +273 -0
  11. package/ai/skills/concorde-get-set-dp/SKILL.md +194 -0
  12. package/ai/skills/concorde-imports/SKILL.md +78 -0
  13. package/ai/skills/concorde-menu/SKILL.md +74 -0
  14. package/ai/skills/concorde-scope/SKILL.md +70 -0
  15. package/ai/skills/concorde-theme/SKILL.md +46 -0
  16. package/build-infos.json +1 -1
  17. package/concorde-core.bundle.js +152 -152
  18. package/concorde-core.es.js +1853 -1689
  19. package/dist/altcha-widget.js +2662 -0
  20. package/dist/concorde-core.bundle.js +152 -152
  21. package/dist/concorde-core.es.js +1853 -1689
  22. package/dist/docs-mock-api-sw.js +589 -0
  23. package/dist/docs-mock-api-sw.js.map +7 -0
  24. package/docs/altcha-widget.js +2662 -0
  25. package/docs/assets/index-D9pxaQYK.js +7508 -0
  26. package/docs/assets/index-t0-i22oI.css +1 -0
  27. package/docs/docs-mock-api-sw.js +589 -0
  28. package/docs/docs-mock-api-sw.js.map +7 -0
  29. package/docs/index.html +2 -2
  30. package/docs/src/core/components/functional/fetch/fetch.md +13 -11
  31. package/docs/src/core/components/functional/if/if.md +4 -11
  32. package/docs/src/core/components/functional/list/list.md +60 -194
  33. package/docs/src/core/components/functional/queue/queue.md +70 -85
  34. package/docs/src/core/components/functional/router/router.md +62 -97
  35. package/docs/src/core/components/functional/states/states.md +2 -2
  36. package/docs/src/core/components/functional/submit/submit.md +86 -55
  37. package/docs/src/core/components/ui/captcha/captcha.md +2 -2
  38. package/docs/src/core/components/ui/card/card.md +1 -1
  39. package/docs/src/core/components/ui/form/checkbox/checkbox.md +5 -32
  40. package/docs/src/core/components/ui/form/input/input.md +5 -30
  41. package/docs/src/core/components/ui/form/input-autocomplete/input-autocomplete.md +6 -4
  42. package/docs/src/core/components/ui/form/radio/radio.md +5 -32
  43. package/docs/src/core/components/ui/form/select/select.md +5 -31
  44. package/docs/src/core/components/ui/form/switch/switch.md +5 -32
  45. package/docs/src/core/components/ui/loader/loader.md +1 -13
  46. package/docs/src/core/components/ui/table/table.md +3 -3
  47. package/docs/src/docs/_core-concept/dataFlow.md +73 -0
  48. package/docs/src/docs/_core-concept/subscriber.md +9 -10
  49. package/docs/src/docs/_decorators/ancestor-attribute.md +4 -3
  50. package/docs/src/docs/_decorators/auto-subscribe.md +19 -16
  51. package/docs/src/docs/_decorators/bind.md +20 -17
  52. package/docs/src/docs/_decorators/get.md +7 -4
  53. package/docs/src/docs/_decorators/handle.md +171 -0
  54. package/docs/src/docs/_decorators/on-assign.md +99 -73
  55. package/docs/src/docs/_decorators/publish.md +2 -1
  56. package/docs/src/docs/_decorators/subscribe.md +70 -9
  57. package/docs/src/docs/_decorators/wait-for-ancestors.md +13 -10
  58. package/docs/src/docs/_directives/sub.md +91 -0
  59. package/docs/src/docs/_getting-started/ai-agents.md +56 -0
  60. package/docs/src/docs/_getting-started/concorde-manual-install.md +133 -0
  61. package/docs/src/docs/_getting-started/concorde-outside.md +13 -123
  62. package/docs/src/docs/_getting-started/create-a-component.md +2 -0
  63. package/docs/src/docs/_getting-started/my-first-component.md +236 -0
  64. package/docs/src/docs/_getting-started/my-first-subscriber.md +29 -83
  65. package/docs/src/docs/_getting-started/pubsub.md +21 -134
  66. package/docs/src/docs/_getting-started/start.md +26 -18
  67. package/docs/src/docs/_misc/api-configuration.md +79 -0
  68. package/docs/src/docs/_misc/dataProviderKey.md +38 -5
  69. package/docs/src/docs/_misc/docs-mock-api.md +60 -0
  70. package/docs/src/docs/_misc/endpoint.md +2 -1
  71. package/docs/src/docs/_misc/html-integration.md +13 -0
  72. package/docs/src/docs/search/docs-search.json +4163 -873
  73. package/docs/src/tsconfig.json +380 -317
  74. package/gitlab/job_tests.sh +55 -0
  75. package/package.json +34 -3
  76. package/public/altcha-widget.js +2662 -0
  77. package/public/docs-mock-api-sw.js +589 -0
  78. package/public/docs-mock-api-sw.js.map +7 -0
  79. package/scripts/ai-init.mjs +167 -0
  80. package/scripts/docs-mock-api-vite-plugin.ts +116 -0
  81. package/scripts/docs-open-in-editor-plugin.ts +130 -0
  82. package/scripts/pre-publish.mjs +2 -1
  83. package/src/core/components/functional/example/example.ts +1 -1
  84. package/src/core/components/functional/fetch/fetch.md +13 -11
  85. package/src/core/components/functional/if/if.md +4 -11
  86. package/src/core/components/functional/list/list.demo.ts +4 -4
  87. package/src/core/components/functional/list/list.md +60 -194
  88. package/src/core/components/functional/list/list.ts +8 -7
  89. package/src/core/components/functional/queue/queue.demo.ts +1 -1
  90. package/src/core/components/functional/queue/queue.md +70 -85
  91. package/src/core/components/functional/queue/queue.ts +4 -4
  92. package/src/core/components/functional/router/router.md +62 -97
  93. package/src/core/components/functional/router/router.ts +1 -1
  94. package/src/core/components/functional/states/states.md +2 -2
  95. package/src/core/components/functional/submit/submit.md +86 -55
  96. package/src/core/components/functional/submit/submit.ts +10 -3
  97. package/src/core/components/ui/captcha/captcha.md +2 -2
  98. package/src/core/components/ui/card/card.md +1 -1
  99. package/src/core/components/ui/form/checkbox/checkbox.md +5 -32
  100. package/src/core/components/ui/form/input/input.md +5 -30
  101. package/src/core/components/ui/form/input-autocomplete/input-autocomplete.md +6 -4
  102. package/src/core/components/ui/form/radio/radio.md +5 -32
  103. package/src/core/components/ui/form/select/select.md +5 -31
  104. package/src/core/components/ui/form/switch/switch.md +5 -32
  105. package/src/core/components/ui/loader/loader.md +1 -13
  106. package/src/core/components/ui/table/table.md +3 -3
  107. package/src/core/decorators/api.spec.ts +8 -1
  108. package/src/core/decorators/api.ts +126 -15
  109. package/src/core/directives/DataProvider.sub.spec.ts +96 -0
  110. package/src/core/directives/DataProvider.ts +109 -40
  111. package/src/core/utils/HTML.ts +42 -10
  112. package/src/core/utils/PublisherProxy.ts +33 -18
  113. package/src/core/utils/dataProviderKey.ts +23 -0
  114. package/src/core/utils/publisherPathKey.spec.ts +58 -0
  115. package/src/docs/_core-concept/dataFlow.md +73 -0
  116. package/src/docs/_core-concept/subscriber.md +9 -10
  117. package/src/docs/_decorators/ancestor-attribute.md +4 -3
  118. package/src/docs/_decorators/auto-subscribe.md +19 -16
  119. package/src/docs/_decorators/bind.md +19 -16
  120. package/src/docs/_decorators/get.md +7 -4
  121. package/src/docs/_decorators/handle.md +15 -13
  122. package/src/docs/_decorators/on-assign.md +53 -53
  123. package/src/docs/_decorators/publish.md +2 -1
  124. package/src/docs/_decorators/subscribe.md +70 -9
  125. package/src/docs/_decorators/wait-for-ancestors.md +13 -10
  126. package/src/docs/_directives/sub.md +91 -0
  127. package/src/docs/_getting-started/ai-agents.md +56 -0
  128. package/src/docs/_getting-started/concorde-manual-install.md +133 -0
  129. package/src/docs/_getting-started/concorde-outside.md +13 -123
  130. package/src/docs/_getting-started/create-a-component.md +2 -0
  131. package/src/docs/_getting-started/my-first-component.md +236 -0
  132. package/src/docs/_getting-started/my-first-subscriber.md +29 -83
  133. package/src/docs/_getting-started/pubsub.md +21 -134
  134. package/src/docs/_getting-started/start.md +26 -18
  135. package/src/docs/_misc/api-configuration.md +79 -0
  136. package/src/docs/_misc/dataProviderKey.md +34 -1
  137. package/src/docs/_misc/docs-mock-api.md +60 -0
  138. package/src/docs/_misc/endpoint.md +2 -1
  139. package/src/docs/_misc/html-integration.md +13 -0
  140. package/src/docs/code.ts +58 -12
  141. package/src/docs/components/docs-demo-sources.ts +397 -0
  142. package/src/docs/components/docs-lit-demo-raw.ts +28 -0
  143. package/src/docs/components/docs-lit-demo.ts +166 -0
  144. package/src/docs/components/docs-source-link.ts +72 -0
  145. package/src/docs/docs-location.ts +54 -0
  146. package/src/docs/docs.ts +12 -0
  147. package/src/docs/example/decorators-demo-bind-demos.ts +41 -46
  148. package/src/docs/example/decorators-demo-geo.ts +16 -11
  149. package/src/docs/example/decorators-demo-init.ts +2 -228
  150. package/src/docs/example/decorators-demo-subscribe-publish-get-demos.ts +54 -14
  151. package/src/docs/example/decorators-demo.ts +71 -70
  152. package/src/docs/example/docs-api-config-demos.ts +234 -0
  153. package/src/docs/example/docs-joke-demos.ts +297 -0
  154. package/src/docs/example/docs-list-demos.ts +179 -0
  155. package/src/docs/example/docs-provider-keys.ts +315 -0
  156. package/src/docs/example/docs-queue-demos.ts +114 -0
  157. package/src/docs/example/docs-router-demos.ts +89 -0
  158. package/src/docs/example/docs-submit-demos.ts +455 -0
  159. package/src/docs/example/docs-toggle-demos.ts +73 -0
  160. package/src/docs/example/docs-user-two-scopes.ts +37 -0
  161. package/src/docs/example/docs-users-list.ts +71 -0
  162. package/src/docs/example/users.ts +41 -24
  163. package/src/docs/mock-api/api-config-mock.ts +152 -0
  164. package/src/docs/mock-api/fixtures.ts +377 -0
  165. package/src/docs/mock-api/register.ts +25 -0
  166. package/src/docs/mock-api/router.ts +234 -0
  167. package/src/docs/mock-api/service-worker.ts +23 -0
  168. package/src/docs/mock-api/urls.ts +11 -0
  169. package/src/docs/navigation/navigation.ts +39 -7
  170. package/src/docs/search/docs-search.json +4021 -936
  171. package/src/docs/search/markdown-renderer.ts +7 -3
  172. package/src/docs/search/page.ts +11 -14
  173. package/src/docs/search/sonic-code-markdown.spec.ts +29 -0
  174. package/src/docs/search/sonic-code-markdown.ts +28 -0
  175. package/src/docs.ts +4 -0
  176. package/src/tsconfig.json +87 -0
  177. package/src/tsconfig.tsbuildinfo +1 -1
  178. package/vite.config.mts +8 -0
  179. package/docs/assets/index-CaysOMFz.js +0 -5046
  180. package/docs/assets/index-D8mGoXzF.css +0 -1
  181. package/docs/src/docs/_misc/templates-demo.md +0 -19
  182. package/src/docs/_misc/templates-demo.md +0 -19
@@ -0,0 +1,273 @@
1
+ ---
2
+ name: concorde
3
+ description: >-
4
+ Composants Web Concorde (Lit) : DataProvider, scope, theme, Endpoint,
5
+ formDataProvider, sonic-list/sonic-queue, @subscribe/@handle/@get.
6
+ @subscribe feuille par feuille (pas d’objet parent + getters).
7
+ Imports courts (@supersoniks/concorde/menu, /list, /utils…).
8
+ ---
9
+
10
+ # Concorde — patterns et migrations
11
+
12
+ Source de vérité : fichiers `.md` dans `node_modules/@supersoniks/concorde/src/` (composants, décorateurs, docs).
13
+
14
+ **Imports** : toujours les chemins **les plus courts** — voir skill `concorde-imports` et section ci-dessous.
15
+
16
+ **Vocabulaire** : toujours **DataProvider**. Accès programmatique : **`get(chemin)`** (alias Concorde `dp`), **`set(chemin, valeur)`**. **Pas** `PublisherManager` ni `getDataProvider`.
17
+
18
+ ## Imports (règle prioritaire)
19
+
20
+ Préférer `@supersoniks/concorde/menu`, `/list`, etc. **dans les apps consommatrices**. Dans le **dépôt Concorde** (lib + doc), utiliser `core/components/…` ou imports relatifs — voir skill `concorde-imports` (section « Dans le dépôt Concorde »).
21
+
22
+ Référence complète : skill **`concorde-imports`** (`.cursor/skills/concorde-imports/SKILL.md`).
23
+
24
+ ## Interdits pour du **nouveau** code
25
+
26
+ - **Ne pas** étendre les mixins `Subscriber` / `Fetcher` sur des composants métier.
27
+ - **Ne pas** utiliser `data-bind`, `data-publish`, `data-subscribe` en HTML.
28
+ - **Ne pas** mettre `@input`, `@change` ou `.value` + assignation manuelle sur **`sonic-input`** quand `formDataProvider` + `name` suffit.
29
+ - **Ne pas** introduire `@onAssign` — préférer `@handle` + `DataProviderKey`.
30
+ - **Ne pas** utiliser **`@bind`** sur les composants métier — préférer **`@subscribe`** + `DataProviderKey<T, U>` (typage + placeholders `${prop}` sur l’hôte).
31
+ - **Ne pas** utiliser **`sonic-fetch`** — préférer **`sonic-queue`** (+ filtre `formDataProvider`) ou **`@get`** + `Endpoint`.
32
+ - **Ne pas** importer **`PublisherManager`** — utiliser **`get` / `set`** (réexport Concorde ou wrapper projet).
33
+ - **Ne pas** promouvoir les templates HTML (`<template>`, `data-value`) pour **`sonic-list`** / **`sonic-queue`** — préférer **`.items`**, **`.noItems`**, **`.skeleton`**, **`.separator`** (binding propriété Lit obligatoire pour les fonctions).
34
+
35
+ ## DataProvider
36
+
37
+ Store observable adressé par un chemin string :
38
+
39
+ ```typescript
40
+ import { dp, get, set } from "@supersoniks/concorde/utils";
41
+ import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
42
+
43
+ const counterKey = new DataProviderKey<{ count: number }>("myCounter");
44
+
45
+ set(counterKey, { count: 0 });
46
+ dp(counterKey.count).set(1);
47
+ get(counterKey); // snapshot { count: 1 }
48
+ ```
49
+
50
+ Chemins **statiques** uniquement pour `get` / `set` / `dp`. Clés avec `${…}` → décorateurs ou **`sub(clé)`** dans les templates. Migration : skill **`concorde-get-set-dp`**.
51
+
52
+ Attributs HTML courants :
53
+
54
+ | Attribut | Rôle |
55
+ |----------|------|
56
+ | `dataProvider` | Lie un composant au DataProvider à ce chemin |
57
+ | `formDataProvider` | Conteneur de formulaire — les champs `name` écrivent dedans |
58
+ | `dataFilterProvider` | Filtre lu par `sonic-queue` (relance les requêtes) |
59
+
60
+ ## DataProviderKey
61
+
62
+ ### Chemins statiques
63
+
64
+ ```typescript
65
+ const counterKey = new DataProviderKey<{ count: number }>("myCounter");
66
+ counterKey.count.path; // "myCounter.count"
67
+ ```
68
+
69
+ ### Chemins dynamiques (placeholder)
70
+
71
+ Placeholder `${prop}` dans une **chaîne normale** (pas de backticks). Résolu depuis les propriétés du composant hôte ; ré-abonnement automatique quand elles changent.
72
+
73
+ ```typescript
74
+ export const userKey = new DataProviderKey<User, { userIndex: number }>(
75
+ "users.${userIndex}",
76
+ );
77
+
78
+ @property({ type: Number }) userIndex = 0;
79
+
80
+ @subscribe(userKey)
81
+ @state() user: User | null = null;
82
+ ```
83
+
84
+ Même mécanisme pour `@handle`, `@publish`, `@subscribe`. Éviter `@bind` (bidirectionnel / chemins string legacy).
85
+
86
+ Doc : `src/docs/_misc/dataProviderKey.md`, `src/docs/_decorators/subscribe.md`, `handle.md`.
87
+
88
+ ## Endpoint
89
+
90
+ ```typescript
91
+ const users = new Endpoint<UsersResponse>("users?offset=$offset&limit=$limit");
92
+ users.path;
93
+ ```
94
+
95
+ Utilisé avec `@get(endpoint)` ou `@get(endpoint, apiConfigKey)` — voir **Scope** ci-dessous.
96
+
97
+ ## Scope — defaults hérités
98
+
99
+ Skill dédié : **`concorde-scope`**.
100
+
101
+ `<sonic-scope>` (light DOM) : les descendants héritent des attributs (`serviceURL`, `token`, `formDataProvider`, icônes custom…).
102
+
103
+ ```typescript
104
+ import "@supersoniks/concorde/sonic-scope";
105
+ ```
106
+
107
+ ```html
108
+ <sonic-scope serviceURL="https://api.example.com" formDataProvider="checkout">
109
+ …app…
110
+ </sonic-scope>
111
+ ```
112
+
113
+ **API** — type `APIConfiguration` (`@supersoniks/concorde/utils/api`) :
114
+
115
+ | Mode | `@get` | Config |
116
+ |------|--------|--------|
117
+ | Scope | `@get(endpoint)` | hérite `serviceURL`, `token`, … du scope |
118
+ | DataProvider | `@get(endpoint, apiConfigKey)` | objet publié via `set(apiConfigKey, { … })` |
119
+
120
+ Champs courants : `serviceURL`, `token`, `userName`/`password`, `credentials`, `authToken`, `tokenProvider`.
121
+ Avancés : `cache`, `blockUntilDone`, `keepAlive`, `addHTTPResponse`.
122
+
123
+ **Ne pas confondre** avec **theme** (couleurs / typo) — skill **`concorde-theme`**.
124
+
125
+ ## Theme — design tokens
126
+
127
+ Skill dédié : **`concorde-theme`**.
128
+
129
+ `<sonic-theme background color font>` + variables CSS `--sc-*` (surfaces, sémantique, typo). Pas pour l’API.
130
+
131
+ ```typescript
132
+ import "@supersoniks/concorde/theme";
133
+ ```
134
+
135
+ ## @publish / @subscribe
136
+
137
+ ```typescript
138
+ @publish(counterKey.count)
139
+ @state()
140
+ count = 0;
141
+
142
+ @subscribe(counterKey.count)
143
+ @state()
144
+ subscribedCount = 0;
145
+ ```
146
+
147
+ ### Règle impérative — une souscription par valeur affichée
148
+
149
+ Sur un composant **`LitElement`** métier, chaque donnée lue dans `render()` doit avoir **son propre** `@subscribe` + `@state()` (ou `@property` si besoin explicite) sur le **publisher feuille** — jamais un abonnement à l’objet parent + getters.
150
+
151
+ | Anti-pattern | Pourquoi |
152
+ |--------------|----------|
153
+ | `@subscribe(dpKeys.currentSession)` + `get edito()` | Si seul `edito` change (même référence `Session`), Lit peut **ne pas** re-rendre |
154
+ | `@subscribe` sur un parent + `sub()` enfant pour le détail | Mélange deux modèles ; préférer des feuilles cohérentes |
155
+
156
+ ```typescript
157
+ // ❌ — re-render non garanti sur sous-clés
158
+ @subscribe(dpKeys.currentSession)
159
+ @state() session?: Session;
160
+ get edito() { return this.session?.edito; }
161
+
162
+ // ✅ — une propriété réactive par champ du template
163
+ @subscribe(dpKeys.currentSession.slug)
164
+ @state() slug = "";
165
+
166
+ @subscribe(dpKeys.currentSession.edito)
167
+ @state() edito: Edito | null = null;
168
+
169
+ @subscribe(dpKeys.currentSession.settings)
170
+ @state() settings: SettingsSessionAPI | null = null;
171
+ ```
172
+
173
+ **Navigation typée** : chaîner `DataProviderKey` — `dpKeys.currentSession.edito` → `"app.currentSession.edito"`.
174
+
175
+ **Granularité** : descendre jusqu’à la **feuille** réellement affichée si les mutations ne remplacent pas l’objet intermédiaire (ex. `@subscribe(dpKeys.currentSession.edito.title)` si seul `title` change sans nouvel objet `edito`).
176
+
177
+ **Templates** : même règle avec `sub(dpKeys.currentSession.edito.title)` plutôt que `sub(dpKeys.currentSession)` + accès JS.
178
+
179
+ ### Cas hybride — hôte `Subscriber` + enfants sonic
180
+
181
+ Des composants catalogue (`sonic-event-location-hall`, `sonic-date`, `sonic-product-title`, …) remontent l’arbre pour `dataProvider` et utilisent le **template filling** du mixin `Subscriber` — pas un `@subscribe` local.
182
+
183
+ | Situation | Approche |
184
+ |-----------|----------|
185
+ | Composant métier **sans** enfants `Subscriber` | `LitElement` + `@subscribe` **feuille par feuille** |
186
+ | Composant **hôte** d’enfants sonic `Subscriber` | `extends Subscriber(LitElement)` + `@property` remplies par template filling ; **exposer** `dataProvider` aux descendants |
187
+
188
+ **`dataProvider` imbriqué** (éviter la clé plate `"app.currentSession"`) :
189
+
190
+ ```html
191
+ <mon-composant-hote dataProvider="app" subDataProvider="currentSession"></mon-composant-hote>
192
+ ```
193
+
194
+ Après `initPublisher`, refléter le chemin résolu sur l’attribut (ex. `app/currentSession`) pour que les **enfants** héritent le bon publisher — ils ne lisent pas `subDataProvider` sur l’ancêtre.
195
+
196
+ **Liste / queue** : chaque ligne a son publisher — l’hôte **hérite** de l’ancêtre ; ne pas forcer `app.currentSession` sur un item de liste.
197
+
198
+ ## formDataProvider
199
+
200
+ ```html
201
+ <div formDataProvider="myForm">
202
+ <sonic-input name="value" label="Texte"></sonic-input>
203
+ </div>
204
+ ```
205
+
206
+ Initialisation : `set("myForm", { value: "" })`.
207
+
208
+ ## sonic-queue — scroll infini + recherche
209
+
210
+ Pagination (`$offset` / `$limit`) **automatisée** avec `lazyload`.
211
+
212
+ ```html
213
+ <div formDataProvider="myFilter">
214
+ <sonic-input name="q" type="search" label="Rechercher"></sonic-input>
215
+ </div>
216
+ <sonic-queue
217
+ lazyload
218
+ dataFilterProvider="myFilter"
219
+ dataProviderExpression="users?offset=$offset&limit=$limit"
220
+ serviceurl="…"
221
+ key="data"
222
+ limit="4"
223
+ .items=${renderUser}
224
+ ></sonic-queue>
225
+ ```
226
+
227
+ ## Templates sonic-list / sonic-queue
228
+
229
+ Propriétés Lit (recommandé) :
230
+
231
+ | Propriété | Rôle |
232
+ |-----------|------|
233
+ | `.items=${fn}` | Rendu de chaque ligne — **point obligatoire** (Lit ne passe pas les fonctions en attribut HTML) |
234
+ | `.separator` | Entre chaque item (pas après le dernier) |
235
+ | `.noItems` | Liste vide |
236
+ | `.skeleton` | Chargement pendant un `fetch` |
237
+
238
+ `sonic-queue` transmet ces propriétés à chaque lot (`sonic-list` interne). `.noItems` ne s’applique qu’au **premier** lot.
239
+
240
+ ## Décorateurs
241
+
242
+ | Besoin | API |
243
+ |--------|-----|
244
+ | Lire (affichage Lit) | `@subscribe(dpKey.leaf)` + `@state()` — **une fois par champ** du `render()` |
245
+ | Effet typé | `@handle(dpKey.a, …)` |
246
+ | API HTTP | `@get(Endpoint)` ou `@get(Endpoint, apiConfigKey)` |
247
+ | Écriture classe (hors form) | `@publish` |
248
+ | Lecture classe (hors form) | `@subscribe` + `DataProviderKey` — pas `@bind` |
249
+
250
+ ## Migrations courantes
251
+
252
+ | Ancien | Nouveau |
253
+ |--------|---------|
254
+ | `PublisherManager.get(…)` | `get(…)` ou `set(…)` |
255
+ | « publisher » | DataProvider |
256
+ | `sonic-input` + `@input` | `formDataProvider` + `name` |
257
+ | `sonic-fetch` | `sonic-queue` + filtre, ou `@get` |
258
+ | `extends Subscriber(LitElement)` (métier seul) | `LitElement` + `@subscribe` feuille par feuille / `sub()` |
259
+ | `extends Subscriber` (hôte d’enfants sonic) | Conserver `Subscriber` + `dataProvider` / `subDataProvider` corrects |
260
+ | `data-bind` HTML | `@subscribe` / `sub()` |
261
+ | `@bind` décorateur | `@subscribe` + `DataProviderKey` (lecture) ou `@publish` (écriture) |
262
+ | `@onAssign` | `@handle` + `DataProviderKey` |
263
+ | Templates HTML list/queue | `items`, `.separator`, `.noItems`, `.skeleton` (propriétés Lit) |
264
+
265
+ ## Recettes
266
+
267
+ - **Initialiser un DataProvider** : `set(key, { … })` ou `set(key.path, { … })`.
268
+ - **Formulaire** : `formDataProvider` + `name` → `sub()` ou `@subscribe`.
269
+ - **Liste scroll infini** : `sonic-queue` + `lazyload` + `dataFilterProvider`.
270
+ - **Templates list/queue** : `items`, `separator`, `noItems`, `skeleton` (depuis un parent Lit).
271
+ - **Config API app-wide** : `<sonic-scope serviceURL="…">` ou `DataProviderKey<APIConfiguration>`.
272
+ - **Theme / couleurs** : `<sonic-theme>` + CSS `--sc-*`.
273
+ - **Clé dynamique** : `"chemin.${prop}"` + `DataProviderKey<T, { prop: … }>`.
@@ -0,0 +1,194 @@
1
+ ---
2
+ name: concorde-get-set-dp
3
+ description: >-
4
+ Migration et usage de get/set/dp avec DataProviderKey statique.
5
+ Rejet des placeholders ${} par resolveStaticPublisherPath ; sub() et
6
+ décorateurs pour le dynamique.
7
+ ---
8
+
9
+ # Concorde — get / set / dp (chemins statiques)
10
+
11
+ Skill de **migration** vers Concorde ≥ 4.6 avec `resolveStaticPublisherPath`.
12
+ À activer quand un projet monte de version ou refactorise vers `DataProviderKey`.
13
+
14
+ Référence framework : skill **`concorde`**, doc `src/docs/_misc/dataProviderKey.md`.
15
+
16
+ ## Quand utiliser cette skill
17
+
18
+ - Monter `@supersoniks/concorde` avec garde statique sur `get` / `set` / `dp`.
19
+ - Auditer un codebase TypeScript existant avant/après upgrade.
20
+ - Distinguer **chemin évalué en JS** vs **placeholder Concorde** `${prop}` / `{$prop}`.
21
+ - Choisir entre API programmatique (`get/set/dp`) et réactivité (`sub`, décorateurs).
22
+
23
+ ## Règle de rejet (runtime)
24
+
25
+ `resolveStaticPublisherPath` lève une erreur si la string passée à `get`, `set` ou `dp` contient **`${`** ou **`{$`** :
26
+
27
+ | Appel | Résultat |
28
+ |-------|----------|
29
+ | `dp(new DataProviderKey("users.${userIndex}"))` | **throw** |
30
+ | `get("users.${userIndex}")` | **throw** |
31
+ | `dp(staticKey.path)` si `.path` contient `${` | **throw** |
32
+
33
+ **OK** — le chemin est résolu **avant** l'appel (pas de placeholder littéral) :
34
+
35
+ ```typescript
36
+ set(`app.pages.${index}.isComplete`, true); // → "app.pages.2.isComplete"
37
+ dp(`${this.formDataProvider}/fieldName`); // → "checkoutForm/email"
38
+ ```
39
+
40
+ **Faux positif fréquent** : grep `${` dans les sources TS ne suffit pas — vérifier la **valeur runtime** passée à `get/set/dp`.
41
+
42
+ ## Rétrocompatibilité
43
+
44
+ | Avant | Après (recommandé) | Statut |
45
+ |-------|-------------------|--------|
46
+ | `dp(key.path)` | `dp(key)` | Équivalent si clé statique |
47
+ | `set(key.path, v)` | `set(key, v)` | Équivalent |
48
+ | `get("myCounter.count")` | `get(key.count)` | Typage amélioré |
49
+ | `key.path` dans attribut HTML | Inchangé | Toujours valide |
50
+ | `@onAssign(key.path)` | `@handle(key.leaf)` | Migration parallèle (skill `concorde`) |
51
+
52
+ Aucune rupture pour le code qui passait déjà des **strings résolues** ou des clés **statiques**.
53
+
54
+ ## Trois niveaux d'accès DataProvider
55
+
56
+ | Mécanisme | Contexte | Dynamique |
57
+ |-----------|----------|-----------|
58
+ | **`get` / `set` / `dp`** | Code impératif, snapshot instant T | **Non** (placeholder `${` interdit) |
59
+ | **`sub(chemin \| clé)`** | Template Lit, réactivité | **Oui** — string, concat JS, ou `DataProviderKey` avec `${prop}` |
60
+ | **`@subscribe` / `@publish` / `@handle`** | Propriétés composant | **Oui** — `DataProviderKey<T, U>` avec `"base.${prop}"` |
61
+ | **`@bind`** | Legacy / bidirectionnel | Éviter sur composants métier — préférer `@subscribe` |
62
+
63
+ ### `sub()` — dynamique OK (clés incluses), ne pas migrer vers `get()`
64
+
65
+ `sub()` accepte `string | DataProviderKey`, résout les placeholders `${prop}` depuis le **composant hôte** du template (comme `@subscribe`), et se ré-abonne quand les props observées changent.
66
+
67
+ ```typescript
68
+ const userKey = new DataProviderKey<User, { userIndex: number }>("users.${userIndex}");
69
+
70
+ html`<p>${sub(userKey.name)}</p>`
71
+ html`<p>${sub(counterKey.count)}</p>`
72
+ html`<p>${sub(this.formDataProvider + ".email")}</p>`
73
+ ```
74
+
75
+ Doc : `src/docs/_directives/sub.md`.
76
+
77
+ **Anti-pattern** : remplacer `sub(…)` par `get(…)` dans un template → perte de réactivité (snapshot unique).
78
+
79
+ ### Clé dynamique Concorde → décorateur ou factory
80
+
81
+ ```typescript
82
+ export const resourceKey = new DataProviderKey<ResourceData, { resourceId: string }>(
83
+ "${resourceId}",
84
+ );
85
+
86
+ // ❌ dp(resourceKey) ou dp(resourceKey.path) → throw
87
+ export const resourceProvider = (id: string) => dp<ResourceData>(id);
88
+
89
+ @handle(resourceKey.conf.mode)
90
+ onMode(mode: Mode) { /* … */ }
91
+ ```
92
+
93
+ ## Patterns avant / après
94
+
95
+ ### Statique typé (nouveau code)
96
+
97
+ ```typescript
98
+ const cartKey = new DataProviderKey<Cart>("cart");
99
+ set(cartKey, { items: [] });
100
+ dp(cartKey.items[0].qty).set(1);
101
+ ```
102
+
103
+ ### ID runtime (string pure, pas placeholder Concorde)
104
+
105
+ ```typescript
106
+ dp(resolvedId); // ex. "resource-abc123"
107
+ set(`forms/${formId}`, formData);
108
+ ```
109
+
110
+ ### Index ou segment dynamique via JS (compatible)
111
+
112
+ **Avant** :
113
+
114
+ ```typescript
115
+ set(`app.pages.${index}.isComplete`, isComplete);
116
+ return get(`app.labels.${language}.${key}`);
117
+ ```
118
+
119
+ **Après** (option typée, même runtime) :
120
+
121
+ ```typescript
122
+ const pagesKey = new DataProviderKey<AppState["pages"]>("app.pages");
123
+ set(pagesKey[index].isComplete, isComplete);
124
+
125
+ const labelsKey = new DataProviderKey<AppState["labels"]>("app.labels");
126
+ return get(labelsKey[language][key]);
127
+ ```
128
+
129
+ **Helper générique** — conserver si le suffixe ne contient jamais `${` :
130
+
131
+ ```typescript
132
+ export const appSet = <P extends string>(path: P, value: AppValue<P>) => {
133
+ set(`app.${path}`, value);
134
+ };
135
+ ```
136
+
137
+ ### Chemins entièrement statiques
138
+
139
+ **Avant** :
140
+
141
+ ```typescript
142
+ set("app.widget.computed", value);
143
+ const publisher = dp("app.widget.distance");
144
+ ```
145
+
146
+ **Après** :
147
+
148
+ ```typescript
149
+ const widgetKey = new DataProviderKey<AppState["widget"]>("app.widget");
150
+ set(widgetKey.computed, value);
151
+ const publisher = dp(widgetKey.distance);
152
+ ```
153
+
154
+ ## Profils de risque (générique)
155
+
156
+ | Profil codebase | Risque | Indice |
157
+ |-----------------|--------|--------|
158
+ | Strings JS uniquement, pas de `DataProviderKey` | Très faible | Chemins déjà résolus avant `get/set/dp` |
159
+ | Mix `DataProviderKey` statique + `.path` | Faible | Simplifier `dp(key)` progressivement |
160
+ | Clés avec `"${prop}"` passées à `dp()` | **Élevé** | Remplacer par factory `dp(idRésolu)` ou décorateur |
161
+ | Templates avec `sub()` | Aucun | Ne pas « migrer » vers `get()` |
162
+
163
+ Migrations **distinctes** (hors scope) : `PublisherManager`, `@onAssign` → `@handle`.
164
+
165
+ ## Checklist migration (agent)
166
+
167
+ 1. [ ] Confirmer version Concorde avec `resolveStaticPublisherPath` dans `dataProviderKey.ts`.
168
+ 2. [ ] Chercher `DataProviderKey(` dont le constructeur contient `${` ou `{$`.
169
+ 3. [ ] Pour chaque hit : si `get` / `set` / `dp` reçoit la clé → factory `dp(idRésolu)` ou décorateur.
170
+ 4. [ ] Simplifier `dp(staticKey.path)` → `dp(staticKey)` sur clés statiques.
171
+ 5. [ ] **Ne pas** toucher `sub(...)` ni décorateurs dynamiques sans raison.
172
+ 6. [ ] Distinguer grep `${` (source) vs valeur runtime.
173
+ 7. [ ] Tests manuels sur les flux métier qui utilisent clés dynamiques et formulaires `sub()`.
174
+
175
+ ## Anti-patterns
176
+
177
+ | Anti-pattern | Pourquoi |
178
+ |--------------|----------|
179
+ | `dp(key)` sur clé `"${resourceId}"` | Throw immédiat |
180
+ | `sub()` → `get()` dans template | Perte réactivité |
181
+ | Refactor global `.path` → clé sur toutes les clés sans tri statique/dynamique | Régressions |
182
+ | Confondre migration get/set/dp et suppression `PublisherManager` | Périmètres différents |
183
+
184
+ ## Fichiers de référence (package Concorde)
185
+
186
+ - `src/core/utils/dataProviderKey.ts` — `resolveStaticPublisherPath`
187
+ - `src/core/utils/PublisherProxy.ts` — overloads `get` / `set` / `dp`
188
+ - `src/core/utils/publisherPathKey.spec.ts` — tests
189
+ - `src/core/directives/DataProvider.ts` — `sub()`
190
+ - `src/docs/_misc/dataProviderKey.md`
191
+
192
+ ## Maintenance
193
+
194
+ Mettre à jour quand l'API Concorde évolue ou que de nouveaux cas edge sont documentés dans les specs.
@@ -0,0 +1,78 @@
1
+ # Imports Concorde — chemins courts
2
+
3
+ Toujours préférer le **chemin export le plus court** documenté dans `package.json` de `@supersoniks/concorde` — **dans les projets consommateurs** (apps qui installent le package).
4
+
5
+ Éviter les chemins `ui/…`, `functional/…`, `core/…` quand un alias racine existe **en dehors du monorepo Concorde**.
6
+
7
+ ## Dans le dépôt Concorde (lib + doc)
8
+
9
+ Les exports courts (`@supersoniks/concorde/list`, `/menu`, `/queue`, …) **ne résolvent pas** ici : l’alias Vite `@supersoniks/concorde` pointe sur `src/`, pas sur les sous-chemins `package.json`.
10
+
11
+ | Besoin | ✅ Dans ce repo | ❌ Ne pas utiliser ici |
12
+ |--------|----------------|------------------------|
13
+ | `sonic-list` | `import "../../core/components/functional/list/list"` ou `@supersoniks/concorde/core/components/functional/list/list` | `@supersoniks/concorde/list` |
14
+ | `sonic-queue` | `…/core/components/functional/queue/queue` | `@supersoniks/concorde/queue` |
15
+ | UI (button, input, …) | `…/core/components/ui/…` ou relatif depuis `src/docs` | `@supersoniks/concorde/button`, etc. |
16
+ | `@subscribe`, `@ancestorAttribute` | `@supersoniks/concorde/core/decorators/Subscriber` | — |
17
+ | `DataProviderKey` | `@supersoniks/concorde/core/utils/dataProviderKey` | `@supersoniks/concorde/dataProviderKey` (alias doc seulement si configuré) |
18
+
19
+ `@supersoniks/concorde/decorators` peut fonctionner (fichier `src/decorators.ts`) ; préférer quand même les chemins `core/…` dans `src/docs/example/**` pour cohérence.
20
+
21
+ ## Composants UI
22
+
23
+ | Composant | ✅ Préférer | ❌ Éviter |
24
+ |-----------|------------|----------|
25
+ | Menu | `@supersoniks/concorde/menu` | `…/ui/menu` |
26
+ | Menu item | `@supersoniks/concorde/menu-item` | `…/ui/menu-item` |
27
+ | Divider | `@supersoniks/concorde/divider` | `…/ui/divider` |
28
+ | Button | `@supersoniks/concorde/button` | `…/ui/button` |
29
+ | Theme | `@supersoniks/concorde/theme` | `…/ui/theme` |
30
+ | Scope | `@supersoniks/concorde/sonic-scope` | `…/functional/sonic-scope` |
31
+ | Input | `@supersoniks/concorde/input` | `…/ui/form/input` |
32
+ | Select | `@supersoniks/concorde/select` | `…/ui/form/select` |
33
+ | Checkbox | `@supersoniks/concorde/checkbox` | `…/ui/form/checkbox` |
34
+
35
+ ## Composants fonctionnels
36
+
37
+ | Composant | ✅ Préférer | ❌ Éviter |
38
+ |-----------|------------|----------|
39
+ | List | `@supersoniks/concorde/list` | `…/functional/list` |
40
+ | Queue | `@supersoniks/concorde/queue` | `…/functional/queue` |
41
+ | Router | `@supersoniks/concorde/router` | `…/core/components/functional/router/router` |
42
+ | Fetch | `@supersoniks/concorde/fetch` | `…/functional/fetch` |
43
+ | Value | `@supersoniks/concorde/value` | `…/functional/value` |
44
+
45
+ ## Utilitaires & types
46
+
47
+ | Besoin | ✅ Préférer | ❌ Éviter |
48
+ |--------|------------|----------|
49
+ | Décorateurs | `@supersoniks/concorde/decorators` | chemins `…/core/decorators/…` |
50
+ | Directives (`sub`) | `@supersoniks/concorde/directives` | — |
51
+ | `get` / `set` / `dp` | `@supersoniks/concorde/utils` | `PublisherManager` |
52
+ | `DataProviderKey` | `@supersoniks/concorde/dataProviderKey` | `…/core/utils/dataProviderKey` |
53
+ | `Endpoint` | `@supersoniks/concorde/utils/endpoint` | `…/core/utils/endpoint` |
54
+ | `APIConfiguration` | `@supersoniks/concorde/utils/api` | `…/core/utils/api` |
55
+ | Vite config | `@supersoniks/concorde/vite-config` | — |
56
+
57
+ ## Exemples
58
+
59
+ ```typescript
60
+ import "@supersoniks/concorde/menu";
61
+ import "@supersoniks/concorde/menu-item";
62
+ import "@supersoniks/concorde/divider";
63
+ import "@supersoniks/concorde/button";
64
+ import "@supersoniks/concorde/theme";
65
+ import "@supersoniks/concorde/input";
66
+ import "@supersoniks/concorde/list";
67
+ import "@supersoniks/concorde/queue";
68
+ import "@supersoniks/concorde/router";
69
+
70
+ import { subscribe, handle, get } from "@supersoniks/concorde/decorators";
71
+ import { sub } from "@supersoniks/concorde/directives";
72
+ import { get, set } from "@supersoniks/concorde/utils";
73
+ import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
74
+ import { Endpoint } from "@supersoniks/concorde/utils/endpoint";
75
+ import type { APIConfiguration } from "@supersoniks/concorde/utils/api";
76
+ ```
77
+
78
+ Side-effect imports (enregistrement custom elements) : toujours le chemin **composant** court ci-dessus.
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: concorde-menu
3
+ description: >-
4
+ Menus et navigation Concorde : sonic-menu, sonic-menu-item, sonic-divider.
5
+ Sidebar, sections, routes pushstate.
6
+ ---
7
+
8
+ # Concorde — menus et navigation
9
+
10
+ Référence doc : `node_modules/@supersoniks/concorde/src/core/components/ui/menu/menu.md`
11
+
12
+ ## Imports
13
+
14
+ ```typescript
15
+ import "@supersoniks/concorde/menu";
16
+ import "@supersoniks/concorde/divider";
17
+ ```
18
+
19
+ ## Structure type (navigation latérale)
20
+
21
+ ```html
22
+ <nav aria-label="Navigation latérale">
23
+ <sonic-menu direction="column" align="left" size="sm" class="w-full">
24
+ <sonic-menu-item pushstate href="/" autoActive="strict">Accueil</sonic-menu-item>
25
+
26
+ <sonic-divider label="Section A" align="left" size="sm"></sonic-divider>
27
+ <sonic-menu-item pushstate href="/a/page" autoActive="strict">Page A</sonic-menu-item>
28
+
29
+ <sonic-divider label="Section B" align="left" size="sm"></sonic-divider>
30
+ <sonic-menu-item pushstate href="/b/page" autoActive="strict">Page B</sonic-menu-item>
31
+ </sonic-menu>
32
+ </nav>
33
+ ```
34
+
35
+ ## Règles
36
+
37
+ | Contexte | Composants |
38
+ |----------|------------|
39
+ | Liste de liens / nav | **`sonic-menu`** + **`sonic-menu-item`** |
40
+ | Séparation de sections | **`sonic-divider`** avec `label` |
41
+ | Liens internes (router Concorde) | `pushstate` + `href` sur **`sonic-menu-item`** |
42
+ | Item actif | `autoActive="strict"` sur chaque item de route |
43
+
44
+ ## Interdits pour la navigation
45
+
46
+ - **Ne pas** empiler des **`sonic-button`** ghost pour simuler un menu latéral.
47
+ - **Ne pas** inventer des séparateurs custom quand **`sonic-divider`** suffit.
48
+ - **Ne pas** oublier `pushstate` sur les liens internes.
49
+ - **Ne pas** mettre `autoActive` sur le **`sonic-menu`** — c’est sur chaque **`sonic-menu-item`**.
50
+
51
+ ## Attributs utiles
52
+
53
+ ### sonic-menu
54
+
55
+ `direction="column"`, `align="left"`, `size="sm"` pour une sidebar.
56
+
57
+ ### sonic-menu-item
58
+
59
+ Hérite de **`sonic-button`** : `pushstate`, `href`, `autoActive`, `variant`.
60
+
61
+ ### sonic-divider
62
+
63
+ `label`, `align="left"`, `size="sm"`.
64
+
65
+ ## Routes fichier
66
+
67
+ Pas de **tirets** dans les noms de dossiers sous `routes/` — le générateur Concorde produit des identifiants JS invalides (`dpkey-dynamic` → erreur).
68
+
69
+ ## Checklist
70
+
71
+ - [ ] `sonic-menu` englobe tous les items
72
+ - [ ] Sections séparées par `sonic-divider` avec `label`
73
+ - [ ] Chaque route : `pushstate` + `href` + `autoActive="strict"`
74
+ - [ ] Imports `@supersoniks/concorde/menu`, `/menu-item`, `/divider` (pas `ui/…`)
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: concorde-scope
3
+ description: >-
4
+ Scope (sonic-scope) — inherited API, form, and icon defaults. Not UI.
5
+ APIConfiguration via attributes or DataProvider.
6
+ ---
7
+
8
+ # Scope — configuration héritée
9
+
10
+ **Scope** = defaults app-wide via `<sonic-scope>` (light DOM). Les descendants lisent les attributs ancêtres — pas de shadow root.
11
+
12
+ ```typescript
13
+ import "@supersoniks/concorde/sonic-scope";
14
+ ```
15
+
16
+ ## Attributs courants
17
+
18
+ | Zone | Attributs | Consommé par |
19
+ |------|-----------|--------------|
20
+ | API | `serviceURL`, `token`, `credentials`, `userName`, `password` | `@get`, `sonic-list`, `sonic-queue`, `sonic-submit` |
21
+ | Forms | `formDataProvider`, `dataProvider`, `submitResultDataProvider` | champs form, `sonic-submit` |
22
+ | Icons | `customIconLibraryPath`, `customIconDefaultPrefix` | `sonic-icon` `library="custom"` |
23
+
24
+ ## APIConfiguration
25
+
26
+ Type : `@supersoniks/concorde/utils/api`.
27
+
28
+ | Champ | Rôle |
29
+ |-------|------|
30
+ | `serviceURL` | URL de base API |
31
+ | `token` | Bearer |
32
+ | `userName` / `password` | Basic auth (si pas de token) |
33
+ | `credentials` | Mode `fetch` (`omit`, `same-origin`, `include`) |
34
+ | `authToken` | Refresh token (attribut scope : `eventsApiToken`) |
35
+ | `tokenProvider` | Chemin pour obtenir un nouveau bearer |
36
+ | `cache`, `blockUntilDone`, `keepAlive`, `addHTTPResponse` | Options avancées fetch |
37
+
38
+ ## Deux façons de configurer l’API
39
+
40
+ **1. Scope** — attributs sur `<sonic-scope>` ou `<sonic-theme>` (API seulement sur scope recommandé) :
41
+
42
+ ```html
43
+ <sonic-scope serviceURL="https://api.example.com" token="…">
44
+
45
+ </sonic-scope>
46
+ ```
47
+
48
+ ```typescript
49
+ @get(usersEndpoint) // pas de 2ᵉ arg — hérite serviceURL
50
+ @state() data: ApiGetResult<Users> | null = null;
51
+ ```
52
+
53
+ **2. DataProvider** — objet typé :
54
+
55
+ ```typescript
56
+ import type { APIConfiguration } from "@supersoniks/concorde/utils/api";
57
+
58
+ const apiKey = new DataProviderKey<APIConfiguration>("myApi");
59
+ set(apiKey, { serviceURL: "https://api.example.com", credentials: "same-origin" });
60
+
61
+ @get(usersEndpoint, apiKey)
62
+ @state() data: ApiGetResult<Users> | null = null;
63
+ ```
64
+
65
+ Les deux patterns peuvent coexister (scope pour l’URL par défaut, clé explicite quand nécessaire).
66
+
67
+ ## Séparation theme / scope
68
+
69
+ - **Scope** → API, forms, icônes app-wide
70
+ - **Theme** → couleurs, typo (`concorde-theme`) — **pas** `serviceURL`