@supersoniks/concorde 4.2.1 → 4.3.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.
Files changed (55) hide show
  1. package/README.md +163 -0
  2. package/build-infos.json +1 -1
  3. package/concorde-core.bundle.js +175 -171
  4. package/concorde-core.es.js +2490 -2246
  5. package/dist/concorde-core.bundle.js +175 -171
  6. package/dist/concorde-core.es.js +2490 -2246
  7. package/package.json +22 -1
  8. package/php/get-challenge.php +34 -0
  9. package/php/some-service.php +42 -0
  10. package/scripts/pre-build.mjs +4 -0
  11. package/src/core/_types/endpoint.ts +4 -0
  12. package/src/core/_types/key.ts +1 -0
  13. package/src/core/components/functional/example/example.ts +38 -6
  14. package/src/core/decorators/Subscriber.ts +2 -0
  15. package/src/core/decorators/api.spec.ts +150 -0
  16. package/src/core/decorators/api.ts +244 -0
  17. package/src/core/decorators/subscriber/bind.ts +57 -145
  18. package/src/core/decorators/subscriber/dynamicPath.ts +77 -0
  19. package/src/core/decorators/subscriber/dynamicPropertyWatch.ts +105 -0
  20. package/src/core/decorators/subscriber/onAssign.ts +11 -147
  21. package/src/core/decorators/subscriber/publish.spec.ts +21 -0
  22. package/src/core/decorators/subscriber/publish.ts +148 -0
  23. package/src/core/decorators/subscriber/publisherPath.ts +13 -0
  24. package/src/core/decorators/subscriber/subscribe.spec.ts +21 -0
  25. package/src/core/decorators/subscriber/subscribe.ts +32 -0
  26. package/src/core/decorators/subscriber/subscribe.type-test.ts +32 -0
  27. package/src/core/utils/api.ts +83 -15
  28. package/src/core/utils/dataProviderKey.spec.ts +34 -0
  29. package/src/core/utils/dataProviderKey.ts +86 -0
  30. package/src/core/utils/endpoint.spec.ts +41 -0
  31. package/src/core/utils/endpoint.ts +87 -0
  32. package/src/decorators.ts +14 -0
  33. package/src/docs/{_misc → _decorators}/ancestor-attribute.md +15 -31
  34. package/src/docs/_decorators/bind.md +164 -0
  35. package/src/docs/_decorators/get.md +65 -0
  36. package/src/docs/_decorators/publish.md +54 -0
  37. package/src/docs/_decorators/subscribe.md +36 -0
  38. package/src/docs/_misc/dataProviderKey.md +135 -0
  39. package/src/docs/_misc/endpoint.md +42 -0
  40. package/src/docs/example/decorators-demo-bind-demos.ts +210 -0
  41. package/src/docs/example/decorators-demo-geo.ts +45 -0
  42. package/src/docs/example/decorators-demo-init.ts +228 -0
  43. package/src/docs/example/decorators-demo-subscribe-publish-get-demos.ts +324 -0
  44. package/src/docs/example/decorators-demo.ts +12 -459
  45. package/src/docs/navigation/navigation.ts +27 -10
  46. package/src/docs/search/docs-search.json +1059 -609
  47. package/src/tsconfig-model.json +1 -1
  48. package/src/tsconfig.json +65 -1
  49. package/src/tsconfig.tsbuildinfo +1 -1
  50. package/src/utils.ts +8 -1
  51. package/vite.config.mts +11 -0
  52. package/src/docs/_misc/bind.md +0 -362
  53. /package/src/docs/{_misc → _decorators}/auto-subscribe.md +0 -0
  54. /package/src/docs/{_misc → _decorators}/on-assign.md +0 -0
  55. /package/src/docs/{_misc → _decorators}/wait-for-ancestors.md +0 -0
@@ -0,0 +1,324 @@
1
+ import { html, LitElement } from "lit";
2
+ import { customElement, property, state } from "lit/decorators.js";
3
+ import {
4
+ get,
5
+ publish,
6
+ subscribe,
7
+ } from "@supersoniks/concorde/decorators";
8
+ import type {
9
+ APIConfiguration,
10
+ ApiGetResult,
11
+ } from "@supersoniks/concorde/core/utils/api";
12
+ import { DataProviderKey } from "@supersoniks/concorde/core/utils/dataProviderKey";
13
+ import { sub } from "@supersoniks/concorde/directives";
14
+ import { PublisherManager } from "@supersoniks/concorde/core/utils/PublisherProxy";
15
+ import { tailwind } from "../tailwind";
16
+ import "./decorators-demo-init";
17
+ import {
18
+ docsDemoDynApiConfKeyTemplate,
19
+ docsDemoGeoApiConfigurationKey,
20
+ geoCommunesApiGetEndpoint,
21
+ geoCommunesApiGetEndpointDynamic,
22
+ geoCommunesApiGetPublishKey,
23
+ type GeoCommuneRow,
24
+ } from "./decorators-demo-geo";
25
+
26
+ type PublishDemoData = {
27
+ email: string;
28
+ message: string;
29
+ };
30
+
31
+ const publishDemoKey = new DataProviderKey<PublishDemoData>("publishDemo");
32
+
33
+ @customElement("demo-publish")
34
+ export class DemoPublish extends LitElement {
35
+ static styles = [tailwind];
36
+
37
+ @publish(publishDemoKey.email)
38
+ @state()
39
+ email = "";
40
+
41
+ @publish(publishDemoKey.message)
42
+ @state()
43
+ message = "";
44
+
45
+ connectedCallback() {
46
+ super.connectedCallback();
47
+ PublisherManager.get("publishDemo").set({ email: "", message: "" });
48
+ }
49
+
50
+ render() {
51
+ return html`
52
+ <div class="space-y-4">
53
+ <p class="text-sm text-neutral-600 dark:text-neutral-400">
54
+ Les champs publient vers le publisher ; les valeurs ci-dessous passent
55
+ par <code>sub()</code>.
56
+ </p>
57
+ <sonic-input
58
+ .value=${this.email}
59
+ @input=${(e: Event) =>
60
+ (this.email = (e.target as HTMLInputElement).value)}
61
+ label="Email"
62
+ placeholder="your@email.com"
63
+ ></sonic-input>
64
+ <sonic-input
65
+ .value=${this.message}
66
+ @input=${(e: Event) =>
67
+ (this.message = (e.target as HTMLInputElement).value)}
68
+ label="Message"
69
+ placeholder="Type a message..."
70
+ ></sonic-input>
71
+ <div
72
+ class="rounded border border-neutral-200 dark:border-neutral-700 p-3"
73
+ >
74
+ <p
75
+ class="text-xs font-medium text-neutral-500 dark:text-neutral-400 mb-1"
76
+ >
77
+ Published values (from sub):
78
+ </p>
79
+ <p>Email: ${sub("publishDemo.email") || "—"}</p>
80
+ <p>Message: ${sub("publishDemo.message") || "—"}</p>
81
+ </div>
82
+ </div>
83
+ `;
84
+ }
85
+ }
86
+
87
+ @customElement("demo-api-get")
88
+ export class DemoApiGet extends LitElement {
89
+ static styles = [tailwind];
90
+
91
+ @get(geoCommunesApiGetEndpoint, docsDemoGeoApiConfigurationKey)
92
+ @state()
93
+ geoApiPayload?: ApiGetResult<GeoCommuneRow[]>;
94
+
95
+ render() {
96
+ const rows = this.geoApiPayload?.result;
97
+ const status = this.geoApiPayload?.response?.status;
98
+ return html`
99
+ <div class="space-y-2">
100
+ <p class="text-sm text-neutral-600 dark:text-neutral-400">
101
+ <code>@get</code> — même service que
102
+ <code>sonic-queue</code> (<code>https://geo.api.gouv.fr/</code>).
103
+ Propriété typée <code>ApiGetResult&lt;T&gt;</code>.
104
+ </p>
105
+ ${status != null
106
+ ? html`<p class="text-xs text-neutral-500">HTTP ${status}</p>`
107
+ : ""}
108
+ ${this.geoApiPayload === null
109
+ ? html`<p class="text-neutral-500">Chargement…</p>`
110
+ : !rows || rows.length === 0
111
+ ? html`<p class="text-neutral-500">Aucune commune</p>`
112
+ : html`
113
+ <ul class="list-disc pl-5 space-y-1">
114
+ ${rows.map(
115
+ (c) =>
116
+ html`<li>
117
+ <span class="font-medium">${c.nom}</span>
118
+ <span class="text-neutral-500 text-sm">
119
+ — ${c.code}</span
120
+ >
121
+ </li>`,
122
+ )}
123
+ </ul>
124
+ `}
125
+ </div>
126
+ `;
127
+ }
128
+ }
129
+
130
+ @customElement("demo-api-get-configuration-key")
131
+ export class DemoApiGetConfigurationKey extends LitElement {
132
+ static styles = [tailwind];
133
+
134
+ @property({ type: String })
135
+ configSlot: "A" | "B" = "A";
136
+
137
+ @property({ type: Number })
138
+ communeLimit = 5;
139
+
140
+ @get(geoCommunesApiGetEndpointDynamic, docsDemoDynApiConfKeyTemplate)
141
+ @state()
142
+ geoApiPayloadDyn?: ApiGetResult<GeoCommuneRow[]>;
143
+
144
+ private touchCurrentConfigPublisher() {
145
+ const id = `docsDemoDynApiConf${this.configSlot}`;
146
+ const pub = PublisherManager.get(id);
147
+ const cur = pub.get() as APIConfiguration;
148
+ pub.set({
149
+ ...cur,
150
+ blockUntilDone: !cur.blockUntilDone,
151
+ });
152
+ }
153
+
154
+ render() {
155
+ const rows = this.geoApiPayloadDyn?.result;
156
+ const status = this.geoApiPayloadDyn?.response?.status;
157
+ return html`
158
+ <div class="space-y-3">
159
+ <p class="text-sm text-neutral-600 dark:text-neutral-400">
160
+ Config dynamique (<code>docsDemoDynApiConf\${configSlot}</code>) +
161
+ path d’endpoint avec <code>communeLimit</code>. « Toucher la config »
162
+ déclenche un nouveau GET.
163
+ </p>
164
+ <div class="flex flex-wrap gap-2 items-center">
165
+ <span class="text-xs text-neutral-500">Limite API :</span>
166
+ ${[3, 5, 10].map(
167
+ (n) => html`
168
+ <sonic-button
169
+ size="sm"
170
+ type=${this.communeLimit === n ? "primary" : "neutral"}
171
+ @click=${() => {
172
+ this.communeLimit = n;
173
+ }}
174
+ >${n}</sonic-button
175
+ >
176
+ `,
177
+ )}
178
+ </div>
179
+ <div class="flex flex-wrap gap-2 items-center">
180
+ <span class="text-xs text-neutral-500">Publisher config :</span>
181
+ <sonic-button
182
+ size="sm"
183
+ type=${this.configSlot === "A" ? "primary" : "neutral"}
184
+ @click=${() => {
185
+ this.configSlot = "A";
186
+ }}
187
+ >docsDemoDynApiConfA</sonic-button
188
+ >
189
+ <sonic-button
190
+ size="sm"
191
+ type=${this.configSlot === "B" ? "primary" : "neutral"}
192
+ @click=${() => {
193
+ this.configSlot = "B";
194
+ }}
195
+ >docsDemoDynApiConfB</sonic-button
196
+ >
197
+ <sonic-button
198
+ size="sm"
199
+ @click=${() => this.touchCurrentConfigPublisher()}
200
+ >Toucher la config</sonic-button
201
+ >
202
+ </div>
203
+ ${status != null
204
+ ? html`<p class="text-xs text-neutral-500">
205
+ HTTP ${status} · limit=${this.communeLimit} · conf=
206
+ docsDemoDynApiConf${this.configSlot}
207
+ </p>`
208
+ : ""}
209
+ ${this.geoApiPayloadDyn === null
210
+ ? html`<p class="text-neutral-500">Chargement…</p>`
211
+ : !rows || rows.length === 0
212
+ ? html`<p class="text-neutral-500">Aucune commune</p>`
213
+ : html`
214
+ <ul class="list-disc pl-5 space-y-1">
215
+ ${rows.map(
216
+ (c) =>
217
+ html`<li>
218
+ <span class="font-medium">${c.nom}</span>
219
+ <span class="text-neutral-500 text-sm">
220
+ — ${c.code}</span
221
+ >
222
+ </li>`,
223
+ )}
224
+ </ul>
225
+ `}
226
+ </div>
227
+ `;
228
+ }
229
+ }
230
+
231
+ @customElement("demo-api-get-publish-subscribe")
232
+ export class DemoApiGetPublishSubscribe extends LitElement {
233
+ static styles = [tailwind];
234
+
235
+ @get(geoCommunesApiGetEndpoint)
236
+ @publish(geoCommunesApiGetPublishKey)
237
+ @state()
238
+ geoApiPayloadPublished: ApiGetResult<GeoCommuneRow[]> | null = null;
239
+
240
+ @state()
241
+ @subscribe(geoCommunesApiGetPublishKey.result)
242
+ geoCommunesFromSubscribe: GeoCommuneRow[] | null = null;
243
+
244
+ render() {
245
+ const rows =
246
+ this.geoCommunesFromSubscribe ?? this.geoApiPayloadPublished?.result;
247
+ const status = this.geoApiPayloadPublished?.response?.status;
248
+ return html`
249
+ <div class="space-y-2">
250
+ <p class="text-sm text-neutral-600 dark:text-neutral-400">
251
+ <code>@get</code> (scoped) + <code>@publish</code> sur la clé du
252
+ payload + <code>@subscribe</code> sur <code>.result</code> — même
253
+ rendu que <code>&lt;demo-api-get&gt;</code> (doc @get).
254
+ </p>
255
+ ${status != null
256
+ ? html`<p class="text-xs text-neutral-500">HTTP ${status}</p>`
257
+ : ""}
258
+ ${this.geoApiPayloadPublished === null
259
+ ? html`<p class="text-neutral-500">Chargement…</p>`
260
+ : !rows || rows.length === 0
261
+ ? html`<p class="text-neutral-500">Aucune commune</p>`
262
+ : html`
263
+ <ul class="list-disc pl-5 space-y-1">
264
+ ${rows.map(
265
+ (c) =>
266
+ html`<li>
267
+ <span class="font-medium">${c.nom}</span>
268
+ <span class="text-neutral-500 text-sm">
269
+ — ${c.code}</span
270
+ >
271
+ </li>`,
272
+ )}
273
+ </ul>
274
+ `}
275
+ </div>
276
+ `;
277
+ }
278
+ }
279
+
280
+ type User = { firstName: string; lastName: string; email: string };
281
+
282
+ @customElement("demo-subscribe-dynamic")
283
+ export class DemoSubscribeDynamic extends LitElement {
284
+ static styles = [tailwind];
285
+
286
+ @property({ type: Number })
287
+ userIndex = 0;
288
+
289
+ @subscribe(new DataProviderKey<User>("demoUsers.${userIndex}"))
290
+ @state()
291
+ user: User | null = null;
292
+
293
+ render() {
294
+ return html`
295
+ <div class="space-y-4">
296
+ <p class="text-sm text-neutral-600 dark:text-neutral-400">
297
+ <code>@subscribe</code> avec chemin dynamique
298
+ <code>demoUsers.\${userIndex}</code>
299
+ </p>
300
+ <sonic-input
301
+ type="number"
302
+ .value=${String(this.userIndex)}
303
+ @input=${(e: Event) =>
304
+ (this.userIndex = parseInt((e.target as HTMLInputElement).value))}
305
+ min="0"
306
+ max="9"
307
+ label="User index"
308
+ ></sonic-input>
309
+ ${this.user
310
+ ? html`
311
+ <div
312
+ class="rounded border border-neutral-200 dark:border-neutral-700 p-3"
313
+ >
314
+ <p>${this.user.firstName} ${this.user.lastName}</p>
315
+ <p class="text-sm text-neutral-500">${this.user.email}</p>
316
+ </div>
317
+ `
318
+ : html`<p class="text-neutral-500">
319
+ No user at index ${this.userIndex}
320
+ </p>`}
321
+ </div>
322
+ `;
323
+ }
324
+ }