@supersoniks/concorde 4.9.92 → 4.9.94

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 (111) hide show
  1. package/ai/cursor/rules/concorde.mdc +7 -0
  2. package/ai/jetbrains/rules/concorde.md +6 -0
  3. package/ai/skills/concorde/SKILL.md +48 -0
  4. package/build-infos.json +1 -1
  5. package/concorde-core.bundle.js +299 -292
  6. package/concorde-core.es.js +10139 -6035
  7. package/dist/concorde-core.bundle.js +299 -292
  8. package/dist/concorde-core.es.js +10139 -6035
  9. package/dist/docs-mock-api-sw.js +110 -0
  10. package/dist/docs-mock-api-sw.js.map +3 -3
  11. package/dist/js/core/components/functional/functional.js +1 -0
  12. package/dist/js/core/components/functional/functional.js.map +1 -1
  13. package/dist/js/core/components/functional/jsonata/jsonata.js +119 -0
  14. package/dist/js/core/components/functional/jsonata/jsonata.js.map +1 -0
  15. package/dist/js/core/components/ui/button/button.js +1 -1
  16. package/dist/js/core/components/ui/button/button.js.map +1 -1
  17. package/dist/js/core/decorators/Subscriber.js +1 -0
  18. package/dist/js/core/decorators/Subscriber.js.map +1 -1
  19. package/dist/js/core/decorators/api.js +108 -16
  20. package/dist/js/core/decorators/api.js.map +1 -1
  21. package/dist/js/core/decorators/subscriber/derive.js +60 -0
  22. package/dist/js/core/decorators/subscriber/derive.js.map +1 -0
  23. package/dist/js/core/utils/api.js +29 -8
  24. package/dist/js/core/utils/api.js.map +1 -1
  25. package/dist/js/core/utils/derive.js +246 -0
  26. package/dist/js/core/utils/derive.js.map +1 -0
  27. package/dist/js/core/utils/jsonataHelpers.js +69 -0
  28. package/dist/js/core/utils/jsonataHelpers.js.map +1 -0
  29. package/dist/js/core/utils/jsonataRuntime.js +75 -0
  30. package/dist/js/core/utils/jsonataRuntime.js.map +1 -0
  31. package/dist/js/utils.js +2 -0
  32. package/dist/js/utils.js.map +1 -1
  33. package/dist/types/core/components/functional/functional.d.ts +1 -0
  34. package/dist/types/core/components/functional/functional.d.ts.map +1 -1
  35. package/dist/types/core/components/functional/jsonata/jsonata.d.ts +29 -0
  36. package/dist/types/core/components/functional/jsonata/jsonata.d.ts.map +1 -0
  37. package/dist/types/core/components/ui/button/button.d.ts +1 -1
  38. package/dist/types/core/components/ui/button/button.d.ts.map +1 -1
  39. package/dist/types/core/components/ui/form/checkbox/checkbox.d.ts +3 -3
  40. package/dist/types/core/components/ui/form/input/input.d.ts +3 -3
  41. package/dist/types/core/components/ui/form/input-autocomplete/input-autocomplete.d.ts +3 -3
  42. package/dist/types/core/components/ui/form/textarea/textarea.d.ts +3 -3
  43. package/dist/types/core/decorators/Subscriber.d.ts +2 -0
  44. package/dist/types/core/decorators/Subscriber.d.ts.map +1 -1
  45. package/dist/types/core/decorators/api.d.ts +21 -12
  46. package/dist/types/core/decorators/api.d.ts.map +1 -1
  47. package/dist/types/core/decorators/subscriber/derive.d.ts +30 -0
  48. package/dist/types/core/decorators/subscriber/derive.d.ts.map +1 -0
  49. package/dist/types/core/utils/api.d.ts +1 -1
  50. package/dist/types/core/utils/api.d.ts.map +1 -1
  51. package/dist/types/core/utils/derive.d.ts +40 -0
  52. package/dist/types/core/utils/derive.d.ts.map +1 -0
  53. package/dist/types/core/utils/jsonataHelpers.d.ts +15 -0
  54. package/dist/types/core/utils/jsonataHelpers.d.ts.map +1 -0
  55. package/dist/types/core/utils/jsonataRuntime.d.ts +13 -0
  56. package/dist/types/core/utils/jsonataRuntime.d.ts.map +1 -0
  57. package/dist/types/mixins.d.ts +3 -3
  58. package/dist/types/utils.d.ts +2 -0
  59. package/dist/types/utils.d.ts.map +1 -1
  60. package/docs/assets/{index-DURr6O71.js → index-DpIkksht.js} +652 -479
  61. package/docs/crawl/docs/_decorators/get.html +27 -6
  62. package/docs/crawl/docs/_decorators/patch.html +6 -0
  63. package/docs/crawl/docs/_decorators/post.html +8 -0
  64. package/docs/crawl/docs/_decorators/put.html +6 -0
  65. package/docs/crawl/hubs/decorators.html +36 -31
  66. package/docs/crawl/hubs/remote-data.html +36 -31
  67. package/docs/index.html +1 -1
  68. package/docs/llms-full.txt +47 -3
  69. package/docs/src/docs/_decorators/get.md +31 -0
  70. package/docs/src/docs/_decorators/patch.md +9 -0
  71. package/docs/src/docs/_decorators/post.md +11 -0
  72. package/docs/src/docs/_decorators/put.md +9 -0
  73. package/docs/src/docs/search/docs-search.json +120 -5
  74. package/docs/src/tsconfig.json +268 -268
  75. package/package.json +28 -3
  76. package/public/docs-mock-api-sw.js +110 -0
  77. package/public/docs-mock-api-sw.js.map +3 -3
  78. package/src/core/components/functional/functional.ts +1 -0
  79. package/src/core/components/functional/jsonata/jsonata.md +56 -0
  80. package/src/core/components/functional/jsonata/jsonata.spec.ts +202 -0
  81. package/src/core/components/functional/jsonata/jsonata.ts +116 -0
  82. package/src/core/components/ui/button/button.ts +2 -2
  83. package/src/core/decorators/Subscriber.ts +2 -0
  84. package/src/core/decorators/api.post.spec.ts +99 -4
  85. package/src/core/decorators/api.spec.ts +90 -0
  86. package/src/core/decorators/api.ts +212 -92
  87. package/src/core/decorators/subscriber/derive.ts +95 -0
  88. package/src/core/utils/api.spec.ts +73 -0
  89. package/src/core/utils/api.ts +44 -11
  90. package/src/core/utils/derive.spec.ts +190 -0
  91. package/src/core/utils/derive.ts +357 -0
  92. package/src/core/utils/jsonataHelpers.ts +100 -0
  93. package/src/core/utils/jsonataRuntime.ts +88 -0
  94. package/src/docs/_core-concept/dataFlow.md +9 -0
  95. package/src/docs/_decorators/get.md +31 -0
  96. package/src/docs/_decorators/patch.md +9 -0
  97. package/src/docs/_decorators/post.md +11 -0
  98. package/src/docs/_decorators/put.md +9 -0
  99. package/src/docs/_misc/docs-mock-api.md +3 -0
  100. package/src/docs/components/docs-demo-sources.ts +40 -0
  101. package/src/docs/docs.ts +1 -0
  102. package/src/docs/example/decorators-demo-post.ts +70 -0
  103. package/src/docs/example/decorators-demo-subscribe-publish-get-demos.ts +59 -0
  104. package/src/docs/example/docs-jsonata-demos.ts +128 -0
  105. package/src/docs/mock-api/fixtures-jsonata.ts +131 -0
  106. package/src/docs/mock-api/router.ts +26 -0
  107. package/src/docs/navigation/navigation.ts +4 -0
  108. package/src/docs/search/docs-search.json +300 -5
  109. package/src/tsconfig.json +18 -0
  110. package/src/tsconfig.tsbuildinfo +1 -1
  111. package/src/utils.ts +13 -0
@@ -228,36 +228,72 @@ class ApiOperationController {
228
228
  }
229
229
  }
230
230
 
231
- type ApiPropertyDecorator<T, Host extends object> = <K extends string>(
232
- target: Host & {
233
- [P in K]?: ApiResult<T> | null | undefined;
234
- },
235
- propertyKey: K,
236
- ) => void;
237
-
238
- export type GetDecoratorHandle<T, Host extends object = object> =
239
- ApiPropertyDecorator<T, Host> & {
240
- /**
241
- * Relance le GET pour cette instance connectée.
242
- *
231
+ type ApiHandlerMethod<T> = (this: object, payload?: ApiResult<T>) => void;
232
+
233
+ export interface GetDecoratorHandle<T, Host extends object = object> {
234
+ (
235
+ target: object,
236
+ propertyKey: string | symbol,
237
+ descriptor: TypedPropertyDescriptor<ApiHandlerMethod<T>>,
238
+ ): void;
239
+ <K extends string>(
240
+ target: Host & {
241
+ [P in K]?: ApiResult<T> | null | undefined;
242
+ },
243
+ propertyKey: K,
244
+ ): void;
245
+ /**
246
+ * Relance le GET pour cette instance connectée.
247
+ *
243
248
  * Le handle peut être conservé :
244
249
  * `const usersGet = get(new Endpoint<User[]>("users"))`.
245
250
  *
246
- * La Promise est résolue après la fin du GET.
247
- */
248
- refetch(component: Host): Promise<ApiResult<T> | undefined>;
249
- };
251
+ * La Promise est résolue après la fin du GET.
252
+ */
253
+ refetch(component: Host): Promise<ApiResult<T> | undefined>;
254
+ }
250
255
 
251
- export type ApiSendDecoratorHandle<T, Host extends object = object> =
252
- ApiPropertyDecorator<T, Host> & {
253
- /**
254
- * Relance l'opération d'écriture avec le body courant.
255
- *
256
+ type ApiResultHandler<T> = (
257
+ component: object,
258
+ payload: ApiResult<T> | undefined,
259
+ ) => void;
260
+
261
+ type GetDecoratorHandleInternals<
262
+ T,
263
+ Host extends object = object,
264
+ > = GetDecoratorHandle<T, Host> & {
265
+ registerResultHandler(target: object, handler: ApiResultHandler<T>): void;
266
+ };
267
+
268
+ type ApiSendDecoratorHandleInternals<
269
+ T,
270
+ Host extends object = object,
271
+ > = ApiSendDecoratorHandle<T, Host> & {
272
+ registerResultHandler(target: object, handler: ApiResultHandler<T>): void;
273
+ };
274
+
275
+ let nextApiHandlerPropertyId = 0;
276
+
277
+ export interface ApiSendDecoratorHandle<T, Host extends object = object> {
278
+ (
279
+ target: object,
280
+ propertyKey: string | symbol,
281
+ descriptor: TypedPropertyDescriptor<ApiHandlerMethod<T>>,
282
+ ): void;
283
+ <K extends string>(
284
+ target: Host & {
285
+ [P in K]?: ApiResult<T> | null | undefined;
286
+ },
287
+ propertyKey: K,
288
+ ): void;
289
+ /**
290
+ * Relance l'opération d'écriture avec le body courant.
291
+ *
256
292
  * Disponible avec `post`, `put` et `patch`.
257
- * La Promise est résolue après la fin de l'opération.
258
- */
259
- send(component: Host): Promise<ApiResult<T> | undefined>;
260
- };
293
+ * La Promise est résolue après la fin de l'opération.
294
+ */
295
+ send(component: Host): Promise<ApiResult<T> | undefined>;
296
+ }
261
297
 
262
298
  export type ApiMethodActionOptions = {
263
299
  /**
@@ -352,7 +388,7 @@ function detachConfigPublisher(state: ApiGetState): void {
352
388
 
353
389
  /**
354
390
  * Décorateur **`@get`** : charge des données via `API.getDetailed` et assigne un
355
- * `ApiResult<T>` (`request`, `response`, `result` typé `T`) ou `null`.
391
+ * `ApiResult<T>` (`request`, `response`, `result` typé `T`) ou `undefined`.
356
392
  * Le path est un `Endpoint<T, Ue>` ; les placeholders `${nomPropriété}` sont résolus sur l'instance (`Ue` contraint l’hôte).
357
393
  *
358
394
  * **Scoped (défaut)** : `HTML.getApiConfiguration(host)` avec `host` = l’élément connecté
@@ -374,6 +410,9 @@ function detachConfigPublisher(state: ApiGetState): void {
374
410
  * @get(new Endpoint<User, { userId: string }>("users/${userId}"))
375
411
  * payload?: ApiResult<User>;
376
412
  *
413
+ * @get(new Endpoint<User[]>("users"))
414
+ * onUsers(payload?: ApiResult<User[]>) {}
415
+ *
377
416
  * @example
378
417
  * const apiConf = new DataProviderKey<APIConfiguration>("myApiConf");
379
418
  * PublisherManager.get("myApiConf").set({ serviceURL: "...", token: null, ... });
@@ -400,10 +439,7 @@ export function get<T, Ue = any>(
400
439
  export function get<T, Ue = any, Uk = any>(
401
440
  endpoint: Endpoint<T, Ue>,
402
441
  configurationKey: DataProviderKey<APIConfiguration, Uk>,
403
- ): GetDecoratorHandle<
404
- T,
405
- DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
406
- >;
442
+ ): GetDecoratorHandle<T, DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>>;
407
443
  export function get<T, Ue = any, Uk = any>(
408
444
  endpoint: Endpoint<T, Ue>,
409
445
  options: GetOptions,
@@ -412,15 +448,11 @@ export function get<T, Ue = any, Uk = any, Ut = any>(
412
448
  endpoint: Endpoint<T, Ue>,
413
449
  configurationKey: DataProviderKey<APIConfiguration, Uk>,
414
450
  options: GetOptions,
415
- ): GetDecoratorHandle<
416
- T,
417
- DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
418
- >;
451
+ ): GetDecoratorHandle<T, DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>>;
419
452
  export function get<T, Ue = any, Uk = any, _Ut = any>(
420
453
  endpoint: Endpoint<T, Ue>,
421
454
  configurationKeyOrOptions?:
422
- | DataProviderKey<APIConfiguration, Uk>
423
- | GetOptions,
455
+ DataProviderKey<APIConfiguration, Uk> | GetOptions,
424
456
  maybeOptions?: GetOptions,
425
457
  ): GetDecoratorHandle<T, object> {
426
458
  const pathTemplate = endpoint.path;
@@ -453,9 +485,33 @@ export function get<T, Ue = any, Uk = any, _Ut = any>(
453
485
  const usesPublisherConfig = Boolean(configurationKeyPath);
454
486
  const refetchEveryMs = options?.refetchEveryMs ?? 0;
455
487
  const operationController = new ApiOperationController();
456
-
457
- const decorator = function (target: object, propertyKey: string) {
488
+ const resultHandlers = new WeakMap<object, Set<ApiResultHandler<T>>>();
489
+ const decoratedTargets = new WeakSet<object>();
490
+ const operationPropertyKeys = new WeakMap<object, Set<string>>();
491
+ let handlerPropertyKey: string | undefined;
492
+
493
+ const decorator = function (
494
+ target: object,
495
+ propertyKey: string,
496
+ descriptor?: PropertyDescriptor,
497
+ ) {
498
+ if (descriptor) {
499
+ const handler = descriptor.value as ApiHandlerMethod<T> | undefined;
500
+ if (!handler) return;
501
+
502
+ (decorator as GetDecoratorHandleInternals<T>).registerResultHandler(
503
+ target,
504
+ (component, payload) => handler.call(component, payload),
505
+ );
506
+ return;
507
+ }
458
508
  if (!target) return;
509
+ if (decoratedTargets.has(target)) {
510
+ operationPropertyKeys.get(target)?.add(propertyKey);
511
+ return;
512
+ }
513
+ decoratedTargets.add(target);
514
+ operationPropertyKeys.set(target, new Set([propertyKey]));
459
515
  setSubscribable(target);
460
516
  const stateKey = `__get_state_${propertyKey}`;
461
517
 
@@ -495,12 +551,18 @@ export function get<T, Ue = any, Uk = any, _Ut = any>(
495
551
  }, state.refetchEveryMs);
496
552
  };
497
553
 
554
+ const assignPayload = (payload: ApiResult<T> | undefined) => {
555
+ operationPropertyKeys.get(target)?.forEach((key) => {
556
+ comp[key] = payload;
557
+ });
558
+ };
559
+
498
560
  const runFetch = async (): Promise<ApiResult<T> | undefined> => {
499
561
  const resolution = isDynamicPath
500
562
  ? resolveDynamicPath(component, pathTemplate, pathOptions)
501
563
  : { ready: true, path: pathTemplate };
502
564
  if (!resolution.ready || !resolution.path) {
503
- comp[propertyKey] = undefined;
565
+ assignPayload(undefined);
504
566
  scheduleRefetch();
505
567
  return undefined;
506
568
  }
@@ -512,7 +574,7 @@ export function get<T, Ue = any, Uk = any, _Ut = any>(
512
574
  pathOptions,
513
575
  );
514
576
  if (!configRes.ready || !configRes.path) {
515
- comp[propertyKey] = undefined;
577
+ assignPayload(undefined);
516
578
  scheduleRefetch();
517
579
  return undefined;
518
580
  }
@@ -540,8 +602,13 @@ export function get<T, Ue = any, Uk = any, _Ut = any>(
540
602
  const api = new API(config);
541
603
  const payload = await api.getDetailed<T>(resolution.path);
542
604
  if (generation !== state.requestGeneration) return payload;
543
- comp[propertyKey] = payload;
605
+ operationPropertyKeys.get(target)?.forEach((key) => {
606
+ comp[key] = payload;
607
+ });
544
608
  scheduleRefetch();
609
+ resultHandlers
610
+ .get(target)
611
+ ?.forEach((handler) => handler(component, payload));
545
612
  return payload;
546
613
  };
547
614
 
@@ -582,12 +649,12 @@ export function get<T, Ue = any, Uk = any, _Ut = any>(
582
649
  pathOptions,
583
650
  );
584
651
  if (!configRes.ready || !configRes.path) {
585
- comp[propertyKey] = undefined;
652
+ assignPayload(undefined);
586
653
  return;
587
654
  }
588
655
  const publisher = getPublisherFromPath(configRes.path);
589
656
  if (!publisher) {
590
- comp[propertyKey] = undefined;
657
+ assignPayload(undefined);
591
658
  return;
592
659
  }
593
660
  const mutationHandler = scheduleFetchAndIgnoreErrors;
@@ -651,9 +718,11 @@ export function get<T, Ue = any, Uk = any, _Ut = any>(
651
718
  state.cleanupWatchers.forEach((cleanup) => cleanup());
652
719
  state.cleanupWatchers = [];
653
720
  state.requestGeneration++;
654
- comp[propertyKey] = undefined;
721
+ operationPropertyKeys.get(target)?.forEach((key) => {
722
+ comp[key] = undefined;
723
+ });
655
724
  });
656
- } as GetDecoratorHandle<T, object>;
725
+ } as GetDecoratorHandleInternals<T>;
657
726
 
658
727
  decorator.refetch = (component: object) => {
659
728
  return operationController.run(component) as Promise<
@@ -661,6 +730,20 @@ export function get<T, Ue = any, Uk = any, _Ut = any>(
661
730
  >;
662
731
  };
663
732
 
733
+ decorator.registerResultHandler = (target, handler) => {
734
+ let handlers = resultHandlers.get(target);
735
+ if (!handlers) {
736
+ handlers = new Set();
737
+ resultHandlers.set(target, handlers);
738
+ }
739
+ handlers.add(handler);
740
+
741
+ if (!decoratedTargets.has(target)) {
742
+ handlerPropertyKey ??= `__get_handler_${nextApiHandlerPropertyId++}`;
743
+ decorator(target, handlerPropertyKey);
744
+ }
745
+ };
746
+
664
747
  return decorator;
665
748
  }
666
749
 
@@ -754,22 +837,21 @@ function readBodyFromPublisher(publisher: DataProvider | null): unknown {
754
837
 
755
838
  type ApiSendMethod = "POST" | "PUT" | "PATCH";
756
839
 
757
- type ApiSendDetailedCaller = (
840
+ type ApiSendDetailedCaller<T> = (
758
841
  api: API,
759
842
  path: string,
760
843
  body: unknown,
761
- ) => Promise<ApiResult<unknown> | undefined>;
844
+ ) => Promise<ApiResult<T> | undefined>;
762
845
 
763
846
  function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
764
847
  method: ApiSendMethod,
765
- callDetailed: ApiSendDetailedCaller,
848
+ callDetailed: ApiSendDetailedCaller<T>,
766
849
  ) {
767
850
  return function (
768
851
  endpoint: Endpoint<T, Ue>,
769
852
  bodyKey: DataProviderKey<B>,
770
853
  configurationKeyOrOptions?:
771
- | DataProviderKey<APIConfiguration, Uk>
772
- | ApiSendOptions<Ut>,
854
+ DataProviderKey<APIConfiguration, Uk> | ApiSendOptions<Ut>,
773
855
  maybeOptions?: ApiSendOptions<Ut>,
774
856
  ): ApiSendDecoratorHandle<T, object> {
775
857
  const pathTemplate = endpoint.path;
@@ -809,9 +891,33 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
809
891
  };
810
892
  const stateKeyPrefix = `__${method.toLowerCase()}_state_`;
811
893
  const operationController = new ApiOperationController();
812
-
813
- const decorator = function (target: object, propertyKey: string) {
894
+ const resultHandlers = new WeakMap<object, Set<ApiResultHandler<T>>>();
895
+ const decoratedTargets = new WeakSet<object>();
896
+ const operationPropertyKeys = new WeakMap<object, Set<string>>();
897
+ let handlerPropertyKey: string | undefined;
898
+
899
+ const decorator = function (
900
+ target: object,
901
+ propertyKey: string,
902
+ descriptor?: PropertyDescriptor,
903
+ ) {
904
+ if (descriptor) {
905
+ const handler = descriptor.value as ApiHandlerMethod<T> | undefined;
906
+ if (!handler) return;
907
+
908
+ (decorator as ApiSendDecoratorHandleInternals<T>).registerResultHandler(
909
+ target,
910
+ (component, payload) => handler.call(component, payload),
911
+ );
912
+ return;
913
+ }
814
914
  if (!target) return;
915
+ if (decoratedTargets.has(target)) {
916
+ operationPropertyKeys.get(target)?.add(propertyKey);
917
+ return;
918
+ }
919
+ decoratedTargets.add(target);
920
+ operationPropertyKeys.set(target, new Set([propertyKey]));
815
921
  setSubscribable(target);
816
922
  const stateKey = `${stateKeyPrefix}${propertyKey}`;
817
923
 
@@ -857,12 +963,18 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
857
963
  }, state.refetchEveryMs);
858
964
  };
859
965
 
860
- const runSend = async (): Promise<ApiResult<unknown> | undefined> => {
966
+ const assignPayload = (payload: ApiResult<T> | undefined) => {
967
+ operationPropertyKeys.get(target)?.forEach((key) => {
968
+ comp[key] = payload;
969
+ });
970
+ };
971
+
972
+ const runSend = async (): Promise<ApiResult<T> | undefined> => {
861
973
  const resolution = isDynamicPath
862
974
  ? resolveDynamicPath(component, pathTemplate, pathOptions)
863
975
  : { ready: true, path: pathTemplate };
864
976
  if (!resolution.ready || !resolution.path) {
865
- comp[propertyKey] = undefined;
977
+ assignPayload(undefined);
866
978
  scheduleRefetch();
867
979
  return undefined;
868
980
  }
@@ -873,7 +985,7 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
873
985
  pathOptions,
874
986
  );
875
987
  if (!bodyResolution.ready || !bodyResolution.path) {
876
- comp[propertyKey] = undefined;
988
+ assignPayload(undefined);
877
989
  scheduleRefetch();
878
990
  return undefined;
879
991
  }
@@ -896,7 +1008,7 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
896
1008
  pathOptions,
897
1009
  );
898
1010
  if (!configRes.ready || !configRes.path) {
899
- comp[propertyKey] = undefined;
1011
+ assignPayload(undefined);
900
1012
  scheduleRefetch();
901
1013
  return undefined;
902
1014
  }
@@ -924,10 +1036,23 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
924
1036
 
925
1037
  const generation = ++state.requestGeneration;
926
1038
  const api = new API(config);
927
- const payload = await callDetailed(api, resolution.path, body);
1039
+ let payload: ApiResult<T> | undefined;
1040
+ try {
1041
+ payload = await callDetailed(api, resolution.path, body);
1042
+ } catch (error) {
1043
+ if (generation === state.requestGeneration) {
1044
+ resultHandlers
1045
+ .get(target)
1046
+ ?.forEach((handler) => handler(component, undefined));
1047
+ }
1048
+ throw error;
1049
+ }
928
1050
  if (generation !== state.requestGeneration) return payload;
929
- comp[propertyKey] = payload;
1051
+ assignPayload(payload);
930
1052
  scheduleRefetch();
1053
+ resultHandlers
1054
+ .get(target)
1055
+ ?.forEach((handler) => handler(component, payload));
931
1056
  return payload;
932
1057
  };
933
1058
 
@@ -989,12 +1114,12 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
989
1114
  pathOptions,
990
1115
  );
991
1116
  if (!configRes.ready || !configRes.path) {
992
- comp[propertyKey] = undefined;
1117
+ assignPayload(undefined);
993
1118
  return;
994
1119
  }
995
1120
  const publisher = getPublisherFromPath(configRes.path);
996
1121
  if (!publisher) {
997
- comp[propertyKey] = undefined;
1122
+ assignPayload(undefined);
998
1123
  return;
999
1124
  }
1000
1125
  const mutationHandler = scheduleSendAndIgnoreErrors;
@@ -1067,9 +1192,11 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
1067
1192
  state.cleanupWatchers.forEach((cleanup) => cleanup());
1068
1193
  state.cleanupWatchers = [];
1069
1194
  state.requestGeneration++;
1070
- comp[propertyKey] = undefined;
1195
+ operationPropertyKeys.get(target)?.forEach((key) => {
1196
+ comp[key] = undefined;
1197
+ });
1071
1198
  });
1072
- } as ApiSendDecoratorHandle<T, object>;
1199
+ } as ApiSendDecoratorHandleInternals<T>;
1073
1200
 
1074
1201
  decorator.send = (component: object) => {
1075
1202
  return operationController.run(component) as Promise<
@@ -1077,6 +1204,20 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
1077
1204
  >;
1078
1205
  };
1079
1206
 
1207
+ decorator.registerResultHandler = (target, handler) => {
1208
+ let handlers = resultHandlers.get(target);
1209
+ if (!handlers) {
1210
+ handlers = new Set();
1211
+ resultHandlers.set(target, handlers);
1212
+ }
1213
+ handlers.add(handler);
1214
+
1215
+ if (!decoratedTargets.has(target)) {
1216
+ handlerPropertyKey ??= `__${method.toLowerCase()}_handler_${nextApiHandlerPropertyId++}`;
1217
+ decorator(target, handlerPropertyKey);
1218
+ }
1219
+ };
1220
+
1080
1221
  return decorator;
1081
1222
  };
1082
1223
  }
@@ -1107,10 +1248,7 @@ export function post<T, B, Ue = any, Uk = any>(
1107
1248
  endpoint: Endpoint<T, Ue>,
1108
1249
  bodyKey: DataProviderKey<B>,
1109
1250
  configurationKey: DataProviderKey<APIConfiguration, Uk>,
1110
- ): ApiSendDecoratorHandle<
1111
- T,
1112
- DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
1113
- >;
1251
+ ): ApiSendDecoratorHandle<T, DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>>;
1114
1252
  export function post<T, B, Ue = any, Uk = any>(
1115
1253
  endpoint: Endpoint<T, Ue>,
1116
1254
  bodyKey: DataProviderKey<B>,
@@ -1121,16 +1259,12 @@ export function post<T, B, Ue = any, Uk = any, Ut = any>(
1121
1259
  bodyKey: DataProviderKey<B>,
1122
1260
  configurationKey: DataProviderKey<APIConfiguration, Uk>,
1123
1261
  options: PostOptions<Ut>,
1124
- ): ApiSendDecoratorHandle<
1125
- T,
1126
- DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
1127
- >;
1262
+ ): ApiSendDecoratorHandle<T, DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>>;
1128
1263
  export function post<T, B, Ue = any, Uk = any, Ut = any>(
1129
1264
  endpoint: Endpoint<T, Ue>,
1130
1265
  bodyKey: DataProviderKey<B>,
1131
1266
  configurationKeyOrOptions?:
1132
- | DataProviderKey<APIConfiguration, Uk>
1133
- | PostOptions<Ut>,
1267
+ DataProviderKey<APIConfiguration, Uk> | PostOptions<Ut>,
1134
1268
  maybeOptions?: PostOptions<Ut>,
1135
1269
  ): ApiSendDecoratorHandle<T, object> {
1136
1270
  return createApiSendDecorator<T, B, Ue, Uk, Ut>("POST", (api, path, body) =>
@@ -1147,10 +1281,7 @@ export function put<T, B, Ue = any, Uk = any>(
1147
1281
  endpoint: Endpoint<T, Ue>,
1148
1282
  bodyKey: DataProviderKey<B>,
1149
1283
  configurationKey: DataProviderKey<APIConfiguration, Uk>,
1150
- ): ApiSendDecoratorHandle<
1151
- T,
1152
- DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
1153
- >;
1284
+ ): ApiSendDecoratorHandle<T, DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>>;
1154
1285
  export function put<T, B, Ue = any, Uk = any>(
1155
1286
  endpoint: Endpoint<T, Ue>,
1156
1287
  bodyKey: DataProviderKey<B>,
@@ -1161,16 +1292,12 @@ export function put<T, B, Ue = any, Uk = any, Ut = any>(
1161
1292
  bodyKey: DataProviderKey<B>,
1162
1293
  configurationKey: DataProviderKey<APIConfiguration, Uk>,
1163
1294
  options: PutOptions<Ut>,
1164
- ): ApiSendDecoratorHandle<
1165
- T,
1166
- DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
1167
- >;
1295
+ ): ApiSendDecoratorHandle<T, DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>>;
1168
1296
  export function put<T, B, Ue = any, Uk = any, Ut = any>(
1169
1297
  endpoint: Endpoint<T, Ue>,
1170
1298
  bodyKey: DataProviderKey<B>,
1171
1299
  configurationKeyOrOptions?:
1172
- | DataProviderKey<APIConfiguration, Uk>
1173
- | PutOptions<Ut>,
1300
+ DataProviderKey<APIConfiguration, Uk> | PutOptions<Ut>,
1174
1301
  maybeOptions?: PutOptions<Ut>,
1175
1302
  ): ApiSendDecoratorHandle<T, object> {
1176
1303
  return createApiSendDecorator<T, B, Ue, Uk, Ut>("PUT", (api, path, body) =>
@@ -1187,10 +1314,7 @@ export function patch<T, B, Ue = any, Uk = any>(
1187
1314
  endpoint: Endpoint<T, Ue>,
1188
1315
  bodyKey: DataProviderKey<B>,
1189
1316
  configurationKey: DataProviderKey<APIConfiguration, Uk>,
1190
- ): ApiSendDecoratorHandle<
1191
- T,
1192
- DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
1193
- >;
1317
+ ): ApiSendDecoratorHandle<T, DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>>;
1194
1318
  export function patch<T, B, Ue = any, Uk = any>(
1195
1319
  endpoint: Endpoint<T, Ue>,
1196
1320
  bodyKey: DataProviderKey<B>,
@@ -1201,16 +1325,12 @@ export function patch<T, B, Ue = any, Uk = any, Ut = any>(
1201
1325
  bodyKey: DataProviderKey<B>,
1202
1326
  configurationKey: DataProviderKey<APIConfiguration, Uk>,
1203
1327
  options: PatchOptions<Ut>,
1204
- ): ApiSendDecoratorHandle<
1205
- T,
1206
- DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
1207
- >;
1328
+ ): ApiSendDecoratorHandle<T, DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>>;
1208
1329
  export function patch<T, B, Ue = any, Uk = any, Ut = any>(
1209
1330
  endpoint: Endpoint<T, Ue>,
1210
1331
  bodyKey: DataProviderKey<B>,
1211
1332
  configurationKeyOrOptions?:
1212
- | DataProviderKey<APIConfiguration, Uk>
1213
- | PatchOptions<Ut>,
1333
+ DataProviderKey<APIConfiguration, Uk> | PatchOptions<Ut>,
1214
1334
  maybeOptions?: PatchOptions<Ut>,
1215
1335
  ): ApiSendDecoratorHandle<T, object> {
1216
1336
  return createApiSendDecorator<T, B, Ue, Uk, Ut>("PATCH", (api, path, body) =>
@@ -0,0 +1,95 @@
1
+ import type {
2
+ DataProviderKey,
3
+ DataProviderKeyHost,
4
+ } from "../../utils/dataProviderKey";
5
+ import {
6
+ derive as deriveFn,
7
+ type DeriveHandle,
8
+ } from "../../utils/derive";
9
+
10
+ export type DeriveDecoratorOptions<
11
+ I extends Record<string, unknown>,
12
+ O,
13
+ V = Record<string, unknown>,
14
+ > = {
15
+ from: { [K in keyof I]: DataProviderKey<I[K]> };
16
+ to: DataProviderKey<O>;
17
+ /** Expression JSONata (string) ou fonction TS. */
18
+ expr?: string;
19
+ fn?: (inputs: I, vars: V) => O | Promise<O>;
20
+ variables?: DataProviderKey<V>;
21
+ };
22
+
23
+ const HANDLES = new WeakMap<object, DeriveHandle[]>();
24
+
25
+ /**
26
+ * Décorateur optionnel aligné `@handle` : lance un `derive` au `connectedCallback`
27
+ * et l’arrête au `disconnectedCallback`.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * @derive({
32
+ * from: { s: sessionsKey },
33
+ * to: palmaresKey,
34
+ * variables: formKey,
35
+ * fn: (i, v) => i.s.slice(0, Number(v.top) || 5),
36
+ * })
37
+ * class MyWidget extends LitElement {}
38
+ * ```
39
+ */
40
+ export function deriveDecorator<
41
+ I extends Record<string, unknown>,
42
+ O,
43
+ V = Record<string, unknown>,
44
+ >(options: DeriveDecoratorOptions<I, O, V>) {
45
+ return function <T extends DataProviderKeyHost<object>>(
46
+ ctor: new (...args: any[]) => T,
47
+ ): void {
48
+ const proto = ctor.prototype as T & {
49
+ connectedCallback?: () => void;
50
+ disconnectedCallback?: () => void;
51
+ };
52
+ const connected = proto.connectedCallback;
53
+ const disconnected = proto.disconnectedCallback;
54
+
55
+ proto.connectedCallback = function (this: T) {
56
+ connected?.call(this);
57
+ const compute =
58
+ options.fn ??
59
+ (async (inputs: I, vars: V) => {
60
+ if (!options.expr) {
61
+ throw new Error("@derive : fournir `expr` ou `fn`.");
62
+ }
63
+ const { compileJsonata } = await import(
64
+ "../../utils/jsonataRuntime"
65
+ );
66
+ const expr = compileJsonata(options.expr);
67
+ return expr.evaluate({}, {
68
+ ...(vars as Record<string, unknown>),
69
+ ...inputs,
70
+ }) as O;
71
+ });
72
+ const handle = deriveFn({
73
+ inputs: options.from,
74
+ output: options.to,
75
+ variables: options.variables,
76
+ compute,
77
+ });
78
+ const list = HANDLES.get(this) ?? [];
79
+ list.push(handle);
80
+ HANDLES.set(this, list);
81
+ };
82
+
83
+ proto.disconnectedCallback = function (this: T) {
84
+ const list = HANDLES.get(this);
85
+ if (list) {
86
+ for (const h of list) h.stop();
87
+ HANDLES.delete(this);
88
+ }
89
+ disconnected?.call(this);
90
+ };
91
+ };
92
+ }
93
+
94
+ /** Alias public — éviter le conflit de nom avec la primitive `derive`. */
95
+ export { deriveDecorator as derive };