@supersoniks/concorde 4.9.7 → 4.9.91

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 (72) hide show
  1. package/build-infos.json +1 -1
  2. package/concorde-core.bundle.js +265 -265
  3. package/concorde-core.es.js +4600 -4385
  4. package/dist/concorde-core.bundle.js +265 -265
  5. package/dist/concorde-core.es.js +4600 -4385
  6. package/dist/js/core/components/functional/queue/queue.js +25 -153
  7. package/dist/js/core/components/functional/queue/queue.js.map +1 -1
  8. package/dist/js/core/components/ui/_css/scroll.js +2 -0
  9. package/dist/js/core/components/ui/_css/scroll.js.map +1 -1
  10. package/dist/js/core/decorators/api.js +92 -2
  11. package/dist/js/core/decorators/api.js.map +1 -1
  12. package/dist/js/core/utils/SafariScrollGuard.js +384 -0
  13. package/dist/js/core/utils/SafariScrollGuard.js.map +1 -0
  14. package/dist/js/decorators.js +4 -0
  15. package/dist/js/decorators.js.map +1 -1
  16. package/dist/js/utils.js +3 -0
  17. package/dist/js/utils.js.map +1 -1
  18. package/dist/types/core/components/functional/queue/queue.d.ts +8 -24
  19. package/dist/types/core/components/functional/queue/queue.d.ts.map +1 -1
  20. package/dist/types/core/components/ui/_css/scroll.d.ts.map +1 -1
  21. package/dist/types/core/decorators/api.d.ts +82 -60
  22. package/dist/types/core/decorators/api.d.ts.map +1 -1
  23. package/dist/types/core/utils/SafariScrollGuard.d.ts +36 -0
  24. package/dist/types/core/utils/SafariScrollGuard.d.ts.map +1 -0
  25. package/dist/types/decorators.d.ts +3 -1
  26. package/dist/types/decorators.d.ts.map +1 -1
  27. package/dist/types/utils.d.ts +1 -0
  28. package/dist/types/utils.d.ts.map +1 -1
  29. package/docs/assets/index-BL6WVF7g.css +1 -0
  30. package/docs/assets/{index-hElUgV9_.js → index-DVn8AeyA.js} +572 -555
  31. package/docs/crawl/docs/_decorators/get.html +64 -81
  32. package/docs/crawl/docs/_decorators/patch.html +35 -23
  33. package/docs/crawl/docs/_decorators/post.html +72 -72
  34. package/docs/crawl/docs/_decorators/put.html +36 -21
  35. package/docs/crawl/docs/_getting-started/ai-agents.html +45 -6
  36. package/docs/crawl/hubs/decorators.html +141 -69
  37. package/docs/crawl/hubs/remote-data.html +141 -69
  38. package/docs/index.html +2 -2
  39. package/docs/llms-full.txt +203 -149
  40. package/docs/llms.txt +4 -4
  41. package/docs/src/core/components/functional/queue/queue.md +0 -0
  42. package/docs/src/core/components/ui/button/button.md +0 -0
  43. package/docs/src/docs/_decorators/get.md +55 -81
  44. package/docs/src/docs/_decorators/patch.md +36 -25
  45. package/docs/src/docs/_decorators/post.md +71 -62
  46. package/docs/src/docs/_decorators/put.md +37 -24
  47. package/docs/src/docs/_getting-started/ai-agents.md +24 -4
  48. package/docs/src/docs/search/docs-search.json +151 -1
  49. package/docs/src/tsconfig.json +3 -0
  50. package/package.json +6 -2
  51. package/src/core/components/functional/queue/queue.ts +33 -164
  52. package/src/core/components/ui/_css/scroll.ts +3 -0
  53. package/src/core/decorators/api.post.spec.ts +79 -1
  54. package/src/core/decorators/api.spec.ts +100 -1
  55. package/src/core/decorators/api.ts +252 -138
  56. package/src/core/utils/SafariScrollGuard.ts +429 -0
  57. package/src/decorators.ts +7 -0
  58. package/src/docs/_decorators/get.md +81 -80
  59. package/src/docs/_decorators/patch.md +36 -25
  60. package/src/docs/_decorators/post.md +99 -62
  61. package/src/docs/_decorators/put.md +37 -24
  62. package/src/docs/components/docs-demo-sources.ts +1 -6
  63. package/src/docs/example/decorators-demo-geo.ts +0 -21
  64. package/src/docs/example/decorators-demo-subscribe-publish-get-demos.ts +41 -132
  65. package/src/docs/search/docs-search.json +156 -241
  66. package/src/docs/tailwind/css/tailwind.css +0 -0
  67. package/src/tsconfig.json +3 -0
  68. package/src/tsconfig.tsbuildinfo +1 -1
  69. package/src/utils.ts +13 -0
  70. package/vite.config.mts +4 -0
  71. package/vitest.setup.ts +22 -0
  72. package/docs/assets/index-Bnp7gVaA.css +0 -1
@@ -1,45 +1,56 @@
1
1
  # @patch
2
2
 
3
- Sends data through [`API.patchDetailed`](../../core/utils/api.ts). Same model as [@post](#docs/_decorators/post.md/post): decorated property is **`ApiResult<T> | null`** (`request`, `response`, `result`).
3
+ `@patch` applies a partial update with the body from a publisher. It uses the
4
+ same model as [@post](#docs/_decorators/post.md/post):
5
+ `Endpoint<T>`, `DataProviderKey<B>`, `ApiResult<T>`, options, and the
6
+ `send(component)` handle.
4
7
 
5
- Pass an [`Endpoint<T>`](#docs/_misc/endpoint.md/endpoint) and a `DataProviderKey` for the **request body**. Import `patch` and `ApiResult` from `@supersoniks/concorde/decorators`.
8
+ ## Minimal usage
6
9
 
7
- ## Configuration
10
+ ```typescript
11
+ const bodyKey = new DataProviderKey<Partial<User>>("users.patch");
12
+ const patchUser = patch(new Endpoint<User>("users/42"), bodyKey);
8
13
 
9
- Same as [@post](#docs/_decorators/post.md/post) / [@get](#docs/_decorators/get.md/get): scoped `HTML.getApiConfiguration(host)` or `DataProviderKey<APIConfiguration>` as third argument.
14
+ @patchUser
15
+ @state()
16
+ payload?: ApiResult<User>;
17
+ ```
18
+
19
+ To run the operation again from the component:
20
+
21
+ ```typescript
22
+ savePatch() {
23
+ patchUser.send(this);
24
+ }
25
+ ```
26
+
27
+ The PATCH is also sent automatically when the body mutates, unless
28
+ `autoPostOnBodyMutation: false` is set. The available options are described in
29
+ [@post](#docs/_decorators/post.md/post):
10
30
 
11
- ## Options (`PatchOptions`)
31
+ - `autoPostOnBodyMutation`
32
+ - `skipIfBodyMissing`
33
+ - `refetchEveryMs`
34
+ - `skipEmptyPlaceholder`
12
35
 
13
- Same shape as `PostOptions` on [@post](#docs/_decorators/post.md/post) (`ApiSendOptions`): `refetchEveryMs`, `skipIfBodyMissing`, `autoPostOnBodyMutation`, `triggerKey`, `skipEmptyPlaceholder`. See [Dynamic path placeholders](#docs/_misc/dynamic-path.md/dynamic-path) for `${sessionId}` and empty-string behaviour.
36
+ Pass explicit API configuration as the third argument:
37
+
38
+ ```typescript
39
+ @patch(endpoint, bodyKey, apiConfigurationKey)
40
+ ```
14
41
 
15
42
  ## Import
16
43
 
17
44
  <sonic-code language="typescript">
18
45
  <template>
19
46
  import { patch, type ApiResult } from "@supersoniks/concorde/decorators";
20
- import { Endpoint } from "@supersoniks/concorde/utils/endpoint";
21
47
  import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
22
- </template>
23
- </sonic-code>
24
-
25
- ## Example
26
-
27
- <sonic-code language="typescript">
28
- <template>
29
- const patchBodyKey = new DataProviderKey&lt;Partial&lt;Resource&gt;&gt;("resourcePatch");
30
-
31
- @patch(
32
- new Endpoint&lt;Resource, { resourceId: string }&gt;("resources/${resourceId}"),
33
- patchBodyKey,
34
- { skipEmptyPlaceholder: true },
35
- )
36
- @state()
37
- payload?: ApiResult&lt;Resource&gt; | null;
48
+ import { Endpoint } from "@supersoniks/concorde/utils/endpoint";
38
49
  </template>
39
50
  </sonic-code>
40
51
 
41
52
  ## See also
42
53
 
43
- - [@post](#docs/_decorators/post.md/post) — POST (live demos)
44
- - [@put](#docs/_decorators/put.md/put) — full replacement (PUT)
54
+ - [@post](#docs/_decorators/post.md/post) — options and triggers
55
+ - [@put](#docs/_decorators/put.md/put) — full replacement
45
56
  - [Dynamic path placeholders](#docs/_misc/dynamic-path.md/dynamic-path)
@@ -1,93 +1,130 @@
1
1
  # @post
2
2
 
3
- Sends data through [`API.postDetailed`](../../core/utils/api.ts). The decorated property is **`ApiResult<T> | null`**: `request`, `response`, and typed `result`.
3
+ `@post` sends the contents of a publisher and stores the response in an
4
+ optional `ApiResult<T>` property. The property is `undefined` until a result
5
+ is available.
4
6
 
5
- Pass an [`Endpoint<T>`](#docs/_misc/endpoint.md/endpoint) as the first argument and a `DataProviderKey` for the **request body** as the second. Import `post` and `ApiResult` from `@supersoniks/concorde/decorators`, and `Endpoint` from `@supersoniks/concorde/utils/endpoint`.
7
+ ## Minimal usage
6
8
 
7
- ## Configuration
9
+ Pass an `Endpoint<T>` and a `DataProviderKey<B>` for the request body.
8
10
 
9
- Same as [@get](#docs/_decorators/get.md/get): scoped `HTML.getApiConfiguration(host)` by default, or `DataProviderKey<APIConfiguration>` as third argument. See [API configuration](#docs/_misc/api-configuration.md/api-configuration) for mock demos.
11
+ ```typescript
12
+ const userBodyKey = new DataProviderKey<CreateUser>("users.form");
13
+ const createUser = post(new Endpoint<User>("users"), userBodyKey);
10
14
 
11
- ## Optional `PostOptions` (third or fourth argument)
15
+ @createUser
16
+ @state()
17
+ payload?: ApiResult<User>;
18
+ ```
12
19
 
13
- | Option | Description |
14
- |--------|-------------|
15
- | `refetchEveryMs` | Re-post on an interval (ms). |
16
- | `skipIfBodyMissing` | Skip when body publisher is `null`/`undefined` (default: `true`). |
17
- | `autoPostOnBodyMutation` | Re-post when the body publisher mutates (default: `true`). `false` = manual via `triggerKey.invalidate()`. |
18
- | `skipEmptyPlaceholder` | If `true`, a placeholder resolved to `''` blocks the request (empty string only — not `0` or `false`). See [Dynamic paths](#docs/_misc/dynamic-path.md/dynamic-path). |
19
- | `triggerKey` | `DataProviderKey` — `invalidate()` re-runs the POST with the current body. |
20
+ The current body is read from `userBodyKey`. By default, the POST is sent when
21
+ the component connects and after each body mutation. Mutations in the same
22
+ frame are coalesced into one request.
20
23
 
21
- ## When the POST runs again
24
+ API configuration is read from the HTML scope, as with
25
+ [@get](#docs/_decorators/get.md/get).
22
26
 
23
- - Body publisher `onInternalMutation` when `autoPostOnBodyMutation` is `true` (default). Plusieurs mutations synchrones dans la même frame sont regroupées en **un seul POST** via `requestAnimationFrame`.
24
- - Endpoint path dependency changes (`${sessionId}` on the host).
25
- - Configuration publisher mutation.
26
- - `triggerKey` publisher `invalidate()`.
27
- - `refetchEveryMs` timer (if set).
27
+ ## Sending the POST again
28
28
 
29
- ## Import
29
+ ### From the component
30
30
 
31
- <sonic-code language="typescript">
32
- <template>
33
- import { post, publish, type ApiResult } from "@supersoniks/concorde/decorators";
34
- import { Endpoint } from "@supersoniks/concorde/utils/endpoint";
35
- import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
36
- </template>
37
- </sonic-code>
31
+ Keep the decorator to access `send(component)`. The body is read again when the
32
+ operation runs.
33
+
34
+ ```typescript
35
+ saveAgain() {
36
+ createUser.send(this);
37
+ }
38
+ ```
39
+
40
+ `send()` is useful for “Save” or “Retry” buttons. It can repeat an operation
41
+ with side effects.
42
+
43
+ ### From a method decorator
44
+
45
+ The send action can decorate a local method. It runs after the method by
46
+ default and waits for an eventual Promise to resolve:
47
+
48
+ ```typescript
49
+ import { send } from "@supersoniks/concorde/decorators";
50
+
51
+ @send(createUser)
52
+ async save() {
53
+ // Prepare local state or validate the form.
54
+ }
55
+ ```
56
+
57
+ Use `{ when: "before" }` when the body must be sent before the method runs:
58
+
59
+ ```typescript
60
+ @send(createUser, { when: "before" })
61
+ saveBefore() {
62
+ // The POST is scheduled before this method runs.
63
+ }
64
+ ```
65
+
66
+ The method decorator adds an explicit send. If the method also mutates the body
67
+ publisher, set `autoPostOnBodyMutation: false` when the decorated method must
68
+ be the only trigger.
69
+
70
+ ## Options (`PostOptions`)
71
+
72
+ Options are the third argument, or the fourth argument when a configuration key
73
+ is provided.
74
+
75
+ - `autoPostOnBodyMutation`: send after a body mutation (default: `true`). Set
76
+ it to `false` to use `send()`.
77
+ - `skipIfBodyMissing`: skip when the body is `null` or `undefined` (default:
78
+ `true`).
79
+ - `refetchEveryMs`: repeat the POST at this interval. Use it only when
80
+ repeating the operation is intended.
81
+ - `skipEmptyPlaceholder`: block the request when a `${...}` placeholder is
82
+ `""`.
83
+
84
+ With a configuration key:
38
85
 
39
- ## Minimal example
86
+ ```typescript
87
+ @post(endpoint, userBodyKey, apiConfigurationKey)
88
+ ```
40
89
 
41
- Mock service: `POST /docs-mock-api/api/register` (same route as [`sonic-submit`](#core/components/functional/submit/submit.md/submit) demos). Publisher setup lives in `decorators-demo-post.ts`.
90
+ See [Dynamic path placeholders](#docs/_misc/dynamic-path.md/dynamic-path) for
91
+ dynamic path details.
92
+
93
+ ## When the POST is sent
94
+
95
+ - when the component connects and the body and configuration are available;
96
+ - after a body mutation, unless `autoPostOnBodyMutation: false`;
97
+ - when a property used in `${...}` changes;
98
+ - when a configuration publisher changes;
99
+ - when `send()` is called;
100
+ - after each response when `refetchEveryMs` is set.
101
+
102
+ ## Import
42
103
 
43
104
  <sonic-code language="typescript">
44
105
  <template>
45
- const syncRequest = new DataProviderKey&lt;SyncRequest&gt;("syncRequest");
46
- const syncTrigger = new DataProviderKey&lt;number&gt;("syncTrigger");
47
-
48
- @post(
49
- new Endpoint&lt;SyncResponse, { sessionId: string }&gt;("sessions/${sessionId}/sync"),
50
- syncRequest,
51
- { triggerKey: syncTrigger },
52
- )
53
- @state()
54
- payload?: ApiResult&lt;SyncResponse&gt; | null;
106
+ import { post, type ApiResult } from "@supersoniks/concorde/decorators";
107
+ import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
108
+ import { Endpoint } from "@supersoniks/concorde/utils/endpoint";
55
109
  </template>
56
110
  </sonic-code>
57
111
 
58
- ## Live demos
112
+ ## Demos
59
113
 
60
114
  <sonic-code>
61
115
  <template>
62
116
  <docs-demo-sources for="demo-api-post"></docs-demo-sources>
63
117
  <demo-api-post></demo-api-post>
64
- </template>
65
- </sonic-code>
66
-
67
- Dynamic endpoint path — changing `sessionId` on the host re-runs the POST (`POST /docs-mock-api/api/sessions/{id}/sync`):
68
-
69
- <sonic-code>
70
- <template>
71
118
  <docs-demo-sources for="demo-api-post-dynamic"></docs-demo-sources>
72
119
  <demo-api-post-dynamic></demo-api-post-dynamic>
73
120
  </template>
74
121
  </sonic-code>
75
122
 
76
- `@post` + `@publish` on the same property (see [@publish](#docs/_decorators/publish.md/publish)):
77
-
78
- <sonic-code>
79
- <template>
80
- <docs-demo-sources for="demo-api-post-publish"></docs-demo-sources>
81
- <demo-api-post-publish></demo-api-post-publish>
82
- </template>
83
- </sonic-code>
84
-
85
- Stale responses are ignored if the path or generation changed before the request finished.
86
-
87
- **Note:** plusieurs composants `@post` sur la même page qui partagent le même `bodyKey` enverront chacun une requête à chaque mutation du body — utiliser une clé par composant (voir les deux démos live ci-dessus).
88
-
89
- Path placeholders (`${sessionId}`, …): [Dynamic path placeholders](#docs/_misc/dynamic-path.md/dynamic-path).
123
+ See [@put](#docs/_decorators/put.md/put) and
124
+ [@patch](#docs/_decorators/patch.md/patch): they use the same model with a
125
+ different HTTP method.
90
126
 
91
- ## See also
127
+ ## Advanced
92
128
 
93
- - [@put](#docs/_decorators/put.md/put) · [@patch](#docs/_decorators/patch.md/patch) — same decorator model, `PUT` / `PATCH` via `putDetailed` / `patchDetailed`
129
+ External invalidation is currently available through `triggerKey`; it is kept
130
+ out of the main usage examples.
@@ -1,43 +1,56 @@
1
1
  # @put
2
2
 
3
- Sends data through [`API.putDetailed`](../../core/utils/api.ts). Same model as [@post](#docs/_decorators/post.md/post): decorated property is **`ApiResult<T> | null`** (`request`, `response`, `result`).
3
+ `@put` replaces a resource with the body from a publisher. It uses exactly the
4
+ same model as [@post](#docs/_decorators/post.md/post):
5
+ `Endpoint<T>`, `DataProviderKey<B>`, `ApiResult<T>`, options, and the
6
+ `send(component)` handle.
4
7
 
5
- Pass an [`Endpoint<T>`](#docs/_misc/endpoint.md/endpoint) and a `DataProviderKey` for the **request body**. Import `put` and `ApiResult` from `@supersoniks/concorde/decorators`.
8
+ ## Minimal usage
6
9
 
7
- ## Configuration
10
+ ```typescript
11
+ const bodyKey = new DataProviderKey<UpdateUser>("users.form");
12
+ const updateUser = put(new Endpoint<User>("users/42"), bodyKey);
8
13
 
9
- Same as [@post](#docs/_decorators/post.md/post) / [@get](#docs/_decorators/get.md/get): scoped `HTML.getApiConfiguration(host)` or `DataProviderKey<APIConfiguration>` as third argument.
14
+ @updateUser
15
+ @state()
16
+ payload?: ApiResult<User>;
17
+ ```
18
+
19
+ To run the operation again from the component:
20
+
21
+ ```typescript
22
+ update() {
23
+ updateUser.send(this);
24
+ }
25
+ ```
26
+
27
+ The PUT is also sent automatically when the body mutates, unless
28
+ `autoPostOnBodyMutation: false` is set. See
29
+ [@post](#docs/_decorators/post.md/post) for the option details:
10
30
 
11
- ## Options (`PutOptions`)
31
+ - `autoPostOnBodyMutation`
32
+ - `skipIfBodyMissing`
33
+ - `refetchEveryMs`
34
+ - `skipEmptyPlaceholder`
12
35
 
13
- Same shape as `PostOptions` on [@post](#docs/_decorators/post.md/post) (`ApiSendOptions`): `refetchEveryMs`, `skipIfBodyMissing`, `autoPostOnBodyMutation`, `triggerKey`, `skipEmptyPlaceholder`. See [Dynamic path placeholders](#docs/_misc/dynamic-path.md/dynamic-path) for `${sessionId}` and empty-string behaviour.
36
+ Pass explicit API configuration as the third argument:
37
+
38
+ ```typescript
39
+ @put(endpoint, bodyKey, apiConfigurationKey)
40
+ ```
14
41
 
15
42
  ## Import
16
43
 
17
44
  <sonic-code language="typescript">
18
45
  <template>
19
46
  import { put, type ApiResult } from "@supersoniks/concorde/decorators";
20
- import { Endpoint } from "@supersoniks/concorde/utils/endpoint";
21
47
  import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
48
+ import { Endpoint } from "@supersoniks/concorde/utils/endpoint";
22
49
  </template>
23
50
  </sonic-code>
24
51
 
25
- ## Example
26
-
27
- <sonic-code language="typescript">
28
- <template>
29
- const bodyKey = new DataProviderKey&lt;UpdatePayload&gt;("resourceBody");
30
-
31
- @put(new Endpoint&lt;Resource, { resourceId: string }&gt;("resources/${resourceId}"), bodyKey)
32
- @state()
33
- payload?: ApiResult&lt;Resource&gt; | null;
34
- </template>
35
- </sonic-code>
36
-
37
- Changing `resourceId` on the host re-runs the PUT when the path becomes ready — see [Dynamic path placeholders](#docs/_misc/dynamic-path.md/dynamic-path).
38
-
39
52
  ## See also
40
53
 
41
- - [@post](#docs/_decorators/post.md/post) — POST (live demos, polling, `@publish`)
42
- - [@patch](#docs/_decorators/patch.md/patch) — partial update (PATCH)
43
- - [Endpoint](#docs/_misc/endpoint.md/endpoint) · [API configuration](#docs/_misc/api-configuration.md/api-configuration)
54
+ - [@post](#docs/_decorators/post.md/post) — options and triggers
55
+ - [@patch](#docs/_decorators/patch.md/patch) — partial update
56
+ - [Dynamic path placeholders](#docs/_misc/dynamic-path.md/dynamic-path)
@@ -258,12 +258,7 @@ export const DOCS_DEMO_SOURCE_REGISTRY: Record<string, DocsSource[]> = {
258
258
  line: 147,
259
259
  label: "demo-api-get-configuration-key",
260
260
  },
261
- {
262
- path: "src/docs/example/decorators-demo-subscribe-publish-get-demos.ts",
263
- line: 239,
264
- label: "demo-api-get-refresh-remote",
265
- },
266
- { path: "src/docs/example/decorators-demo-geo.ts", label: "geo API + triggerKey" },
261
+ { path: "src/docs/example/decorators-demo-geo.ts", label: "geo API" },
267
262
  ],
268
263
  "demo-api-get-publish-subscribe": [
269
264
  {
@@ -26,22 +26,6 @@ export const geoCommunesApiGetEndpoint = new Endpoint<GeoCommuneRow[]>(
26
26
  "communes?limit=5&fields=nom,code",
27
27
  );
28
28
 
29
- export const docsDemoDynApiConfKeyTemplate =
30
- new DataProviderKey<APIConfiguration>("docsDemoDynApiConf${configSlot}");
31
-
32
- export const docsDemoDynApiConfAKey = new DataProviderKey<APIConfiguration>(
33
- "docsDemoDynApiConfA",
34
- );
35
- export const docsDemoDynApiConfBKey = new DataProviderKey<APIConfiguration>(
36
- "docsDemoDynApiConfB",
37
- );
38
-
39
- set(docsDemoDynApiConfAKey, geoApiDemoConfiguration);
40
- set(docsDemoDynApiConfBKey, {
41
- ...geoApiDemoConfiguration,
42
- serviceURL: DOCS_MOCK_GEO_SERVICE,
43
- });
44
-
45
29
  export const geoCommunesApiGetEndpointDynamic = new Endpoint<GeoCommuneRow[]>(
46
30
  "communes?limit=${communeLimit}&fields=nom,code",
47
31
  );
@@ -49,8 +33,3 @@ export const geoCommunesApiGetEndpointDynamic = new Endpoint<GeoCommuneRow[]>(
49
33
  export const geoCommunesApiGetPublishKey = new DataProviderKey<
50
34
  ApiResult<GeoCommuneRow[]>
51
35
  >(geoCommunesApiGetEndpoint.path);
52
-
53
- /** Signal de refetch manuel pour les démos `@get` (voir `triggerKey`). */
54
- export const docsDemoGeoCommunesRefreshKey = new DataProviderKey<void>(
55
- "docsDemoGeoCommunesRefresh",
56
- );
@@ -5,7 +5,6 @@ import type { ApiResult } from "@supersoniks/concorde/core/utils/api";
5
5
  import { DataProviderKey } from "@supersoniks/concorde/core/utils/dataProviderKey";
6
6
  import { sub } from "@supersoniks/concorde/directives";
7
7
  import {
8
- dp,
9
8
  get as storeGet,
10
9
  set,
11
10
  } from "@supersoniks/concorde/core/utils/PublisherProxy";
@@ -13,30 +12,23 @@ import { publishDemoKey } from "./docs-provider-keys";
13
12
  import { tailwind } from "../tailwind";
14
13
  import "./decorators-demo-init";
15
14
  import {
16
- docsDemoDynApiConfKeyTemplate,
17
15
  docsDemoGeoApiConfigurationKey,
18
- docsDemoGeoCommunesRefreshKey,
19
16
  geoCommunesApiGetEndpoint,
20
17
  geoCommunesApiGetEndpointDynamic,
21
18
  geoCommunesApiGetPublishKey,
22
19
  type GeoCommuneRow,
23
20
  } from "./decorators-demo-geo";
24
21
 
25
- function formatFetchedAt(response: Response | null | undefined): string {
26
- const raw = response?.headers.get("X-Fetched-At");
27
- if (!raw) return "—";
28
- try {
29
- return new Date(raw).toLocaleTimeString();
30
- } catch {
31
- return raw;
32
- }
33
- }
34
-
35
22
  type PublishDemoData = {
36
23
  email: string;
37
24
  message: string;
38
25
  };
39
26
 
27
+ const geoCommunesGet = get(
28
+ geoCommunesApiGetEndpoint,
29
+ docsDemoGeoApiConfigurationKey,
30
+ );
31
+
40
32
  @customElement("demo-publish")
41
33
  export class DemoPublish extends LitElement {
42
34
  static styles = [tailwind];
@@ -95,50 +87,35 @@ export class DemoPublish extends LitElement {
95
87
  export class DemoApiGet extends LitElement {
96
88
  static styles = [tailwind];
97
89
 
98
- @get(geoCommunesApiGetEndpoint, docsDemoGeoApiConfigurationKey, {
99
- triggerKey: docsDemoGeoCommunesRefreshKey,
100
- })
90
+ @geoCommunesGet
101
91
  @state()
102
92
  geoApiPayload?: ApiResult<GeoCommuneRow[]>;
103
93
 
104
94
  private refresh() {
105
- dp(docsDemoGeoCommunesRefreshKey).invalidate();
95
+ geoCommunesGet.refetch(this);
106
96
  }
107
97
 
108
98
  render() {
109
99
  const rows = this.geoApiPayload?.result;
110
- const status = this.geoApiPayload?.response?.status;
111
- const fetchedAt = formatFetchedAt(this.geoApiPayload?.response ?? null);
112
100
  return html`
113
- <div class="space-y-2">
101
+ <div class="space-y-3">
102
+ <div class="flex flex-wrap items-center gap-3">
103
+ <span class="text-sm font-medium">🗺️ GET simple</span>
104
+ <sonic-button size="sm" @click=${this.refresh}>Rafraîchir</sonic-button>
105
+ </div>
114
106
  <p class="text-sm text-neutral-600 dark:text-neutral-400">
115
- <code>@get</code> — même service que
116
- <code>sonic-queue</code> (<code>/docs-mock-api/geo/</code>).
117
- Propriété typée <code>ApiResult&lt;T&gt;</code>.
118
- Refetch via <code>triggerKey</code> (<code
119
- >docsDemoGeoCommunesRefresh</code
120
- >).
107
+ Une liste de communes chargée avec <code>@get</code>.
121
108
  </p>
122
- <sonic-button size="sm" @click=${this.refresh}>Rafraîchir</sonic-button>
123
- ${status != null
124
- ? html`<p class="text-xs text-neutral-500">
125
- HTTP ${status} · fetch ${fetchedAt}
126
- </p>`
127
- : ""}
128
- ${this.geoApiPayload === null
129
- ? html`<p class="text-neutral-500">Chargement…</p>`
109
+ ${this.geoApiPayload == null
110
+ ? html`<p class="text-sm text-neutral-500">Chargement…</p>`
130
111
  : !rows || rows.length === 0
131
- ? html`<p class="text-neutral-500">Aucune commune</p>`
112
+ ? html`<p class="text-sm text-neutral-500">Aucune commune</p>`
132
113
  : html`
133
- <ul class="list-disc pl-5 space-y-1">
114
+ <ul class="grid gap-1 sm:grid-cols-2">
134
115
  ${rows.map(
135
- (c) =>
136
- html`<li>
137
- <span class="font-medium">${c.nom}</span>
138
- <span class="text-neutral-500 text-sm">
139
- — ${c.code}</span
140
- >
141
- </li>`,
116
+ (c) => html`<li class="text-sm">
117
+ ${c.nom} <span class="text-neutral-500">(${c.code})</span>
118
+ </li>`,
142
119
  )}
143
120
  </ul>
144
121
  `}
@@ -151,116 +128,48 @@ export class DemoApiGet extends LitElement {
151
128
  export class DemoApiGetConfigurationKey extends LitElement {
152
129
  static styles = [tailwind];
153
130
 
154
- @property({ type: String })
155
- configSlot: "A" | "B" = "A";
156
-
157
131
  @property({ type: Number })
158
- communeLimit = 5;
132
+ communeLimit = 3;
159
133
 
160
- @get(geoCommunesApiGetEndpointDynamic, docsDemoDynApiConfKeyTemplate, {
161
- triggerKey: docsDemoGeoCommunesRefreshKey,
162
- })
134
+ @get(geoCommunesApiGetEndpointDynamic, docsDemoGeoApiConfigurationKey)
163
135
  @state()
164
136
  geoApiPayloadDyn?: ApiResult<GeoCommuneRow[]>;
165
137
 
166
138
  render() {
167
139
  const rows = this.geoApiPayloadDyn?.result;
168
- const status = this.geoApiPayloadDyn?.response?.status;
169
- const fetchedAt = formatFetchedAt(this.geoApiPayloadDyn?.response ?? null);
170
140
  return html`
171
141
  <div class="space-y-3">
172
142
  <p class="text-sm text-neutral-600 dark:text-neutral-400">
173
- Config dynamique (<code>docsDemoDynApiConf\${configSlot}</code>) +
174
- path d’endpoint avec <code>communeLimit</code>. Le refetch manuel
175
- passe par <code>triggerKey</code> — voir le bouton « ailleurs »
176
- ci-dessous dans la doc.
143
+ <code>@get</code> avec un endpoint et une configuration dynamiques.
177
144
  </p>
178
- <div class="flex flex-wrap gap-2 items-center">
179
- <span class="text-xs text-neutral-500">Limite API :</span>
145
+ <div class="flex flex-wrap items-center gap-2">
146
+ <span class="text-sm">Nombre de communes :</span>
180
147
  ${[3, 5, 10].map(
181
- (n) => html`
148
+ (limit) => html`
182
149
  <sonic-button
183
150
  size="sm"
184
- type=${this.communeLimit === n ? "primary" : "neutral"}
151
+ type=${this.communeLimit === limit ? "primary" : "neutral"}
185
152
  @click=${() => {
186
- this.communeLimit = n;
153
+ this.communeLimit = limit;
187
154
  }}
188
- >${n}</sonic-button
189
155
  >
156
+ ${limit}
157
+ </sonic-button>
190
158
  `,
191
159
  )}
192
160
  </div>
193
- <div class="flex flex-wrap gap-2 items-center">
194
- <span class="text-xs text-neutral-500">Publisher config :</span>
195
- <sonic-button
196
- size="sm"
197
- type=${this.configSlot === "A" ? "primary" : "neutral"}
198
- @click=${() => {
199
- this.configSlot = "A";
200
- }}
201
- >docsDemoDynApiConfA</sonic-button
202
- >
203
- <sonic-button
204
- size="sm"
205
- type=${this.configSlot === "B" ? "primary" : "neutral"}
206
- @click=${() => {
207
- this.configSlot = "B";
208
- }}
209
- >docsDemoDynApiConfB</sonic-button
210
- >
211
- </div>
212
- ${status != null
213
- ? html`<p class="text-xs text-neutral-500">
214
- HTTP ${status} · fetch ${fetchedAt} · limit=${this.communeLimit} ·
215
- conf=docsDemoDynApiConf${this.configSlot}
216
- </p>`
217
- : ""}
218
- ${this.geoApiPayloadDyn === null
219
- ? html`<p class="text-neutral-500">Chargement…</p>`
220
- : !rows || rows.length === 0
221
- ? html`<p class="text-neutral-500">Aucune commune</p>`
222
- : html`
223
- <ul class="list-disc pl-5 space-y-1">
224
- ${rows.map(
225
- (c) =>
226
- html`<li>
227
- <span class="font-medium">${c.nom}</span>
228
- <span class="text-neutral-500 text-sm">
229
- — ${c.code}</span
230
- >
231
- </li>`,
232
- )}
233
- </ul>
234
- `}
235
- </div>
236
- `;
237
- }
238
- }
239
-
240
- /** Déclenche le refetch d’un `@get` distant via `triggerKey` (sans toucher la config). */
241
- @customElement("demo-api-get-refresh-remote")
242
- export class DemoApiGetRefreshRemote extends LitElement {
243
- static styles = [tailwind];
244
-
245
- private refreshFromElsewhere() {
246
- dp(docsDemoGeoCommunesRefreshKey).invalidate();
247
- }
248
-
249
- render() {
250
- return html`
251
- <div
252
- class="rounded border border-dashed border-neutral-300 dark:border-neutral-600 p-3 space-y-2"
253
- >
254
- <p class="text-sm text-neutral-600 dark:text-neutral-400">
255
- Composant séparé — même publisher
256
- <code>docsDemoGeoCommunesRefresh</code> :
161
+ <p class="font-mono text-xs text-neutral-500">
162
+ communes?limit=${this.communeLimit}
257
163
  </p>
258
- <pre
259
- class="text-xs bg-neutral-100 dark:bg-neutral-800 p-2 rounded overflow-x-auto"
260
- ><code>dp(docsDemoGeoCommunesRefreshKey).invalidate();</code></pre>
261
- <sonic-button size="sm" variant="secondary" @click=${this.refreshFromElsewhere}>
262
- Rafraîchir le GET (depuis ailleurs)
263
- </sonic-button>
164
+ ${this.geoApiPayloadDyn == null
165
+ ? html`<p class="text-sm text-neutral-500">Chargement…</p>`
166
+ : html`
167
+ <ul class="grid gap-1 sm:grid-cols-2">
168
+ ${(rows ?? []).map(
169
+ (c) => html`<li class="text-sm">${c.nom}</li>`,
170
+ )}
171
+ </ul>
172
+ `}
264
173
  </div>
265
174
  `;
266
175
  }