@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.
- package/build-infos.json +1 -1
- package/concorde-core.bundle.js +265 -265
- package/concorde-core.es.js +4600 -4385
- package/dist/concorde-core.bundle.js +265 -265
- package/dist/concorde-core.es.js +4600 -4385
- package/dist/js/core/components/functional/queue/queue.js +25 -153
- package/dist/js/core/components/functional/queue/queue.js.map +1 -1
- package/dist/js/core/components/ui/_css/scroll.js +2 -0
- package/dist/js/core/components/ui/_css/scroll.js.map +1 -1
- package/dist/js/core/decorators/api.js +92 -2
- package/dist/js/core/decorators/api.js.map +1 -1
- package/dist/js/core/utils/SafariScrollGuard.js +384 -0
- package/dist/js/core/utils/SafariScrollGuard.js.map +1 -0
- package/dist/js/decorators.js +4 -0
- package/dist/js/decorators.js.map +1 -1
- package/dist/js/utils.js +3 -0
- package/dist/js/utils.js.map +1 -1
- package/dist/types/core/components/functional/queue/queue.d.ts +8 -24
- package/dist/types/core/components/functional/queue/queue.d.ts.map +1 -1
- package/dist/types/core/components/ui/_css/scroll.d.ts.map +1 -1
- package/dist/types/core/decorators/api.d.ts +82 -60
- package/dist/types/core/decorators/api.d.ts.map +1 -1
- package/dist/types/core/utils/SafariScrollGuard.d.ts +36 -0
- package/dist/types/core/utils/SafariScrollGuard.d.ts.map +1 -0
- package/dist/types/decorators.d.ts +3 -1
- package/dist/types/decorators.d.ts.map +1 -1
- package/dist/types/utils.d.ts +1 -0
- package/dist/types/utils.d.ts.map +1 -1
- package/docs/assets/index-BL6WVF7g.css +1 -0
- package/docs/assets/{index-hElUgV9_.js → index-DVn8AeyA.js} +572 -555
- package/docs/crawl/docs/_decorators/get.html +64 -81
- package/docs/crawl/docs/_decorators/patch.html +35 -23
- package/docs/crawl/docs/_decorators/post.html +72 -72
- package/docs/crawl/docs/_decorators/put.html +36 -21
- package/docs/crawl/docs/_getting-started/ai-agents.html +45 -6
- package/docs/crawl/hubs/decorators.html +141 -69
- package/docs/crawl/hubs/remote-data.html +141 -69
- package/docs/index.html +2 -2
- package/docs/llms-full.txt +203 -149
- package/docs/llms.txt +4 -4
- package/docs/src/core/components/functional/queue/queue.md +0 -0
- package/docs/src/core/components/ui/button/button.md +0 -0
- package/docs/src/docs/_decorators/get.md +55 -81
- package/docs/src/docs/_decorators/patch.md +36 -25
- package/docs/src/docs/_decorators/post.md +71 -62
- package/docs/src/docs/_decorators/put.md +37 -24
- package/docs/src/docs/_getting-started/ai-agents.md +24 -4
- package/docs/src/docs/search/docs-search.json +151 -1
- package/docs/src/tsconfig.json +3 -0
- package/package.json +6 -2
- package/src/core/components/functional/queue/queue.ts +33 -164
- package/src/core/components/ui/_css/scroll.ts +3 -0
- package/src/core/decorators/api.post.spec.ts +79 -1
- package/src/core/decorators/api.spec.ts +100 -1
- package/src/core/decorators/api.ts +252 -138
- package/src/core/utils/SafariScrollGuard.ts +429 -0
- package/src/decorators.ts +7 -0
- package/src/docs/_decorators/get.md +81 -80
- package/src/docs/_decorators/patch.md +36 -25
- package/src/docs/_decorators/post.md +99 -62
- package/src/docs/_decorators/put.md +37 -24
- package/src/docs/components/docs-demo-sources.ts +1 -6
- package/src/docs/example/decorators-demo-geo.ts +0 -21
- package/src/docs/example/decorators-demo-subscribe-publish-get-demos.ts +41 -132
- package/src/docs/search/docs-search.json +156 -241
- package/src/docs/tailwind/css/tailwind.css +0 -0
- package/src/tsconfig.json +3 -0
- package/src/tsconfig.tsbuildinfo +1 -1
- package/src/utils.ts +13 -0
- package/vite.config.mts +4 -0
- package/vitest.setup.ts +22 -0
- package/docs/assets/index-Bnp7gVaA.css +0 -1
|
@@ -167,6 +167,142 @@ type TriggerPublisherBinding = {
|
|
|
167
167
|
triggerInvalidateHandler: (() => void) | null;
|
|
168
168
|
};
|
|
169
169
|
|
|
170
|
+
type OperationBinding = {
|
|
171
|
+
component: object;
|
|
172
|
+
run: () => void;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Handle interne des décorateurs API.
|
|
177
|
+
*
|
|
178
|
+
* Le WeakMap cible l'instance connectée, pas la valeur de payload : une
|
|
179
|
+
* opération peut donc être relancée avant son premier résultat et reste
|
|
180
|
+
* indépendante d'une affectation manuelle de la propriété décorée.
|
|
181
|
+
*/
|
|
182
|
+
class ApiOperationController {
|
|
183
|
+
private readonly bindings = new WeakMap<object, Set<OperationBinding>>();
|
|
184
|
+
|
|
185
|
+
bind(component: object, run: () => void): () => void {
|
|
186
|
+
const binding: OperationBinding = { component, run };
|
|
187
|
+
let componentBindings = this.bindings.get(component);
|
|
188
|
+
if (!componentBindings) {
|
|
189
|
+
componentBindings = new Set();
|
|
190
|
+
this.bindings.set(component, componentBindings);
|
|
191
|
+
}
|
|
192
|
+
componentBindings.add(binding);
|
|
193
|
+
|
|
194
|
+
return () => {
|
|
195
|
+
const currentBindings = this.bindings.get(component);
|
|
196
|
+
if (!currentBindings) return;
|
|
197
|
+
currentBindings.delete(binding);
|
|
198
|
+
if (currentBindings.size === 0) {
|
|
199
|
+
this.bindings.delete(component);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
run(component: object): void {
|
|
205
|
+
this.bindings.get(component)?.forEach(({ run }) => run());
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
type ApiPropertyDecorator<T, Host extends object> = <K extends string>(
|
|
210
|
+
target: Host & {
|
|
211
|
+
[P in K]?: ApiResult<T> | null | undefined;
|
|
212
|
+
},
|
|
213
|
+
propertyKey: K,
|
|
214
|
+
) => void;
|
|
215
|
+
|
|
216
|
+
export type GetDecoratorHandle<T, Host extends object = object> =
|
|
217
|
+
ApiPropertyDecorator<T, Host> & {
|
|
218
|
+
/**
|
|
219
|
+
* Relance le GET pour cette instance connectée.
|
|
220
|
+
*
|
|
221
|
+
* Le handle peut être conservé :
|
|
222
|
+
* `const usersGet = get(new Endpoint<User[]>("users"))`.
|
|
223
|
+
*/
|
|
224
|
+
refetch(component: Host): void;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export type ApiSendDecoratorHandle<T, Host extends object = object> =
|
|
228
|
+
ApiPropertyDecorator<T, Host> & {
|
|
229
|
+
/**
|
|
230
|
+
* Relance l'opération d'écriture avec le body courant.
|
|
231
|
+
*
|
|
232
|
+
* Disponible avec `post`, `put` et `patch`.
|
|
233
|
+
*/
|
|
234
|
+
send(component: Host): void;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export type ApiMethodActionOptions = {
|
|
238
|
+
/**
|
|
239
|
+
* Moment du déclenchement par rapport à la méthode.
|
|
240
|
+
* `after` est la valeur par défaut ; avec une Promise, l'action attend sa résolution.
|
|
241
|
+
*/
|
|
242
|
+
when?: "before" | "after";
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
type ApiMethod = (this: object, ...args: any[]) => unknown;
|
|
246
|
+
|
|
247
|
+
function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
|
|
248
|
+
return (
|
|
249
|
+
value !== null &&
|
|
250
|
+
(typeof value === "object" || typeof value === "function") &&
|
|
251
|
+
typeof (value as { then?: unknown }).then === "function"
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function createApiMethodActionDecorator(
|
|
256
|
+
action: (component: object) => void,
|
|
257
|
+
options?: ApiMethodActionOptions,
|
|
258
|
+
): MethodDecorator {
|
|
259
|
+
const when = options?.when ?? "after";
|
|
260
|
+
|
|
261
|
+
return (_target, _propertyKey, descriptor) => {
|
|
262
|
+
const original = descriptor.value as ApiMethod | undefined;
|
|
263
|
+
if (!original) return;
|
|
264
|
+
|
|
265
|
+
const wrapped = function (this: object, ...args: any[]) {
|
|
266
|
+
if (when === "before") {
|
|
267
|
+
action(this);
|
|
268
|
+
return original.apply(this, args);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const result = original.apply(this, args);
|
|
272
|
+
if (isPromiseLike(result)) {
|
|
273
|
+
return result.then((value) => {
|
|
274
|
+
action(this);
|
|
275
|
+
return value;
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
action(this);
|
|
280
|
+
return result;
|
|
281
|
+
};
|
|
282
|
+
descriptor.value = wrapped as unknown as typeof descriptor.value;
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export function refetch<T, Host extends object = object>(
|
|
287
|
+
handle: GetDecoratorHandle<T, Host>,
|
|
288
|
+
options?: ApiMethodActionOptions,
|
|
289
|
+
): MethodDecorator {
|
|
290
|
+
return createApiMethodActionDecorator(
|
|
291
|
+
(component) => handle.refetch(component as Host),
|
|
292
|
+
options,
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function send<T, Host extends object = object>(
|
|
297
|
+
handle: ApiSendDecoratorHandle<T, Host>,
|
|
298
|
+
options?: ApiMethodActionOptions,
|
|
299
|
+
): MethodDecorator {
|
|
300
|
+
return createApiMethodActionDecorator(
|
|
301
|
+
(component) => handle.send(component as Host),
|
|
302
|
+
options,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
170
306
|
type RefetchScheduleState = {
|
|
171
307
|
refetchTimeoutId: ReturnType<typeof setTimeout> | null;
|
|
172
308
|
refetchEveryMs: number;
|
|
@@ -175,6 +311,7 @@ type RefetchScheduleState = {
|
|
|
175
311
|
type ApiGetState = TriggerPublisherBinding &
|
|
176
312
|
RefetchScheduleState & {
|
|
177
313
|
cleanupWatchers: Array<() => void>;
|
|
314
|
+
cleanupOperation: (() => void) | null;
|
|
178
315
|
requestGeneration: number;
|
|
179
316
|
configPublisher: DataProvider | null;
|
|
180
317
|
configMutationHandler: (() => void) | null;
|
|
@@ -202,8 +339,12 @@ function detachConfigPublisher(state: ApiGetState): void {
|
|
|
202
339
|
* `PublisherManager` sur le chemin résolu (même syntaxe dynamique que `@subscribe`).
|
|
203
340
|
* Toute mutation interne du publisher (`onInternalMutation`) relance le GET.
|
|
204
341
|
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
342
|
+
* Le décorateur retourné expose `refetch(component)` pour relancer le GET d'une
|
|
343
|
+
* instance connectée. Pour déclencher le refetch depuis un autre composant,
|
|
344
|
+
* utiliser `triggerKey` puis `PublisherManager.get(...).invalidate()`.
|
|
345
|
+
*
|
|
346
|
+
* **Options** (`GetOptions`) : `triggerKey` — signal de refetch externe ;
|
|
347
|
+
* `refetchEveryMs` — polling automatique (0 ou absent = désactivé).
|
|
207
348
|
*
|
|
208
349
|
* @example
|
|
209
350
|
* @get(new Endpoint<User, { userId: string }>("users/${userId}"))
|
|
@@ -216,58 +357,48 @@ function detachConfigPublisher(state: ApiGetState): void {
|
|
|
216
357
|
* payload?: ApiResult<Thing>;
|
|
217
358
|
*
|
|
218
359
|
* @example
|
|
360
|
+
* const usersGet = get(new Endpoint<User[]>("users"));
|
|
361
|
+
* @usersGet
|
|
362
|
+
* payload?: ApiResult<User[]>;
|
|
363
|
+
*
|
|
364
|
+
* refresh() {
|
|
365
|
+
* usersGet.refetch(this);
|
|
366
|
+
* }
|
|
367
|
+
*
|
|
368
|
+
* @example
|
|
219
369
|
* @get(usersEndpoint, { triggerKey: refreshUsersKey })
|
|
220
370
|
* payload?: ApiResult<User[]>;
|
|
221
371
|
* // PublisherManager.get("users.refresh").invalidate() → refetch
|
|
222
372
|
*/
|
|
223
373
|
export function get<T, Ue = any>(
|
|
224
374
|
endpoint: Endpoint<T, Ue>,
|
|
225
|
-
): <
|
|
226
|
-
target: DataProviderKeyHost<Ue> & {
|
|
227
|
-
[P in K]?: ApiResult<T> | null | undefined;
|
|
228
|
-
},
|
|
229
|
-
propertyKey: K,
|
|
230
|
-
) => void;
|
|
375
|
+
): GetDecoratorHandle<T, DataProviderKeyHost<Ue>>;
|
|
231
376
|
export function get<T, Ue = any, Uk = any>(
|
|
232
377
|
endpoint: Endpoint<T, Ue>,
|
|
233
378
|
configurationKey: DataProviderKey<APIConfiguration, Uk>,
|
|
234
|
-
): <
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
},
|
|
239
|
-
propertyKey: K,
|
|
240
|
-
) => void;
|
|
379
|
+
): GetDecoratorHandle<
|
|
380
|
+
T,
|
|
381
|
+
DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
|
|
382
|
+
>;
|
|
241
383
|
export function get<T, Ue = any, Uk = any>(
|
|
242
384
|
endpoint: Endpoint<T, Ue>,
|
|
243
385
|
options: GetOptions,
|
|
244
|
-
): <
|
|
245
|
-
target: DataProviderKeyHost<Ue> & {
|
|
246
|
-
[P in K]?: ApiResult<T> | null | undefined;
|
|
247
|
-
},
|
|
248
|
-
propertyKey: K,
|
|
249
|
-
) => void;
|
|
386
|
+
): GetDecoratorHandle<T, DataProviderKeyHost<Ue>>;
|
|
250
387
|
export function get<T, Ue = any, Uk = any, Ut = any>(
|
|
251
388
|
endpoint: Endpoint<T, Ue>,
|
|
252
389
|
configurationKey: DataProviderKey<APIConfiguration, Uk>,
|
|
253
390
|
options: GetOptions,
|
|
254
|
-
): <
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
},
|
|
259
|
-
propertyKey: K,
|
|
260
|
-
) => void;
|
|
391
|
+
): GetDecoratorHandle<
|
|
392
|
+
T,
|
|
393
|
+
DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
|
|
394
|
+
>;
|
|
261
395
|
export function get<T, Ue = any, Uk = any, _Ut = any>(
|
|
262
396
|
endpoint: Endpoint<T, Ue>,
|
|
263
397
|
configurationKeyOrOptions?:
|
|
264
398
|
| DataProviderKey<APIConfiguration, Uk>
|
|
265
399
|
| GetOptions,
|
|
266
400
|
maybeOptions?: GetOptions,
|
|
267
|
-
): <
|
|
268
|
-
target: object & { [P in K]?: ApiResult<T> | null | undefined },
|
|
269
|
-
propertyKey: K,
|
|
270
|
-
) => void {
|
|
401
|
+
): GetDecoratorHandle<T, object> {
|
|
271
402
|
const pathTemplate = endpoint.path;
|
|
272
403
|
const configurationKey = isGetOptions(configurationKeyOrOptions)
|
|
273
404
|
? undefined
|
|
@@ -297,8 +428,9 @@ export function get<T, Ue = any, Uk = any, _Ut = any>(
|
|
|
297
428
|
const isDynamicPath = endpointDynamicDependencies.length > 0;
|
|
298
429
|
const usesPublisherConfig = Boolean(configurationKeyPath);
|
|
299
430
|
const refetchEveryMs = options?.refetchEveryMs ?? 0;
|
|
431
|
+
const operationController = new ApiOperationController();
|
|
300
432
|
|
|
301
|
-
|
|
433
|
+
const decorator = function (target: object, propertyKey: string) {
|
|
302
434
|
if (!target) return;
|
|
303
435
|
setSubscribable(target);
|
|
304
436
|
const stateKey = `__get_state_${propertyKey}`;
|
|
@@ -309,6 +441,7 @@ export function get<T, Ue = any, Uk = any, _Ut = any>(
|
|
|
309
441
|
if (!state) {
|
|
310
442
|
state = {
|
|
311
443
|
cleanupWatchers: [],
|
|
444
|
+
cleanupOperation: null,
|
|
312
445
|
requestGeneration: 0,
|
|
313
446
|
configPublisher: null,
|
|
314
447
|
configMutationHandler: null,
|
|
@@ -321,6 +454,8 @@ export function get<T, Ue = any, Uk = any, _Ut = any>(
|
|
|
321
454
|
comp[stateKey] = state;
|
|
322
455
|
}
|
|
323
456
|
|
|
457
|
+
state.cleanupOperation?.();
|
|
458
|
+
state.cleanupOperation = null;
|
|
324
459
|
state.cleanupWatchers.forEach((cleanup) => cleanup());
|
|
325
460
|
state.cleanupWatchers = [];
|
|
326
461
|
state.requestGeneration++;
|
|
@@ -386,6 +521,10 @@ export function get<T, Ue = any, Uk = any, _Ut = any>(
|
|
|
386
521
|
};
|
|
387
522
|
|
|
388
523
|
const scheduleFetch = createAnimationFrameCoalescedRunner(runFetch);
|
|
524
|
+
state.cleanupOperation = operationController.bind(
|
|
525
|
+
component,
|
|
526
|
+
scheduleFetch,
|
|
527
|
+
);
|
|
389
528
|
|
|
390
529
|
const rebindTriggerPublisher = () => {
|
|
391
530
|
detachTriggerPublisher(state);
|
|
@@ -476,6 +615,8 @@ export function get<T, Ue = any, Uk = any, _Ut = any>(
|
|
|
476
615
|
const comp = component as Record<string, unknown>;
|
|
477
616
|
const state = comp[stateKey] as ApiGetState | undefined;
|
|
478
617
|
if (!state) return;
|
|
618
|
+
state.cleanupOperation?.();
|
|
619
|
+
state.cleanupOperation = null;
|
|
479
620
|
detachConfigPublisher(state);
|
|
480
621
|
detachTriggerPublisher(state);
|
|
481
622
|
clearRefetch(state);
|
|
@@ -484,27 +625,36 @@ export function get<T, Ue = any, Uk = any, _Ut = any>(
|
|
|
484
625
|
state.requestGeneration++;
|
|
485
626
|
comp[propertyKey] = undefined;
|
|
486
627
|
});
|
|
628
|
+
} as GetDecoratorHandle<T, object>;
|
|
629
|
+
|
|
630
|
+
decorator.refetch = (component: object) => {
|
|
631
|
+
operationController.run(component);
|
|
487
632
|
};
|
|
633
|
+
|
|
634
|
+
return decorator;
|
|
488
635
|
}
|
|
489
636
|
|
|
490
637
|
export type GetOptions<Uk = any> = DynamicPathOptions & {
|
|
491
|
-
/** Intervalle de refetch automatique (ms). 0 ou absent =
|
|
638
|
+
/** Intervalle de refetch automatique (ms). 0 ou absent = désactivé. */
|
|
492
639
|
refetchEveryMs?: number;
|
|
493
|
-
/** Publisher dont `invalidate()` relance le GET. */
|
|
640
|
+
/** Publisher-signal dont `invalidate()` relance le GET. */
|
|
494
641
|
triggerKey?: DataProviderKey<unknown, Uk>;
|
|
495
642
|
};
|
|
496
643
|
|
|
497
644
|
export type ApiSendOptions<Uk = any> = DynamicPathOptions & {
|
|
498
|
-
/**
|
|
645
|
+
/**
|
|
646
|
+
* Intervalle de renvoi automatique (ms).
|
|
647
|
+
* 0 ou absent = désactivé ; attention aux effets de bord de l'opération.
|
|
648
|
+
*/
|
|
499
649
|
refetchEveryMs?: number;
|
|
500
|
-
/**
|
|
650
|
+
/** Ignore l'envoi si le body vaut null/undefined (défaut : true). */
|
|
501
651
|
skipIfBodyMissing?: boolean;
|
|
502
652
|
/**
|
|
503
|
-
*
|
|
504
|
-
* `false` : le body est lu au moment de
|
|
653
|
+
* Relance l'envoi quand le body publisher mute (défaut : true).
|
|
654
|
+
* `false` : le body est lu au moment de `send()` ou `triggerKey.invalidate()`.
|
|
505
655
|
*/
|
|
506
656
|
autoPostOnBodyMutation?: boolean;
|
|
507
|
-
/** Publisher dont `invalidate()` relance l'envoi
|
|
657
|
+
/** Publisher-signal dont `invalidate()` relance l'envoi avec le body courant. */
|
|
508
658
|
triggerKey?: DataProviderKey<unknown, Uk>;
|
|
509
659
|
};
|
|
510
660
|
|
|
@@ -591,10 +741,7 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
|
|
|
591
741
|
| DataProviderKey<APIConfiguration, Uk>
|
|
592
742
|
| ApiSendOptions<Ut>,
|
|
593
743
|
maybeOptions?: ApiSendOptions<Ut>,
|
|
594
|
-
): <
|
|
595
|
-
target: object & { [P in K]?: ApiResult<T> | null | undefined },
|
|
596
|
-
propertyKey: K,
|
|
597
|
-
) => void {
|
|
744
|
+
): ApiSendDecoratorHandle<T, object> {
|
|
598
745
|
const pathTemplate = endpoint.path;
|
|
599
746
|
const bodyKeyPath = bodyKey.path;
|
|
600
747
|
const configurationKey = isApiSendOptions(configurationKeyOrOptions)
|
|
@@ -631,8 +778,9 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
|
|
|
631
778
|
skipEmptyPlaceholder: options?.skipEmptyPlaceholder,
|
|
632
779
|
};
|
|
633
780
|
const stateKeyPrefix = `__${method.toLowerCase()}_state_`;
|
|
781
|
+
const operationController = new ApiOperationController();
|
|
634
782
|
|
|
635
|
-
|
|
783
|
+
const decorator = function (target: object, propertyKey: string) {
|
|
636
784
|
if (!target) return;
|
|
637
785
|
setSubscribable(target);
|
|
638
786
|
const stateKey = `${stateKeyPrefix}${propertyKey}`;
|
|
@@ -643,6 +791,7 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
|
|
|
643
791
|
if (!state) {
|
|
644
792
|
state = {
|
|
645
793
|
cleanupWatchers: [],
|
|
794
|
+
cleanupOperation: null,
|
|
646
795
|
requestGeneration: 0,
|
|
647
796
|
configPublisher: null,
|
|
648
797
|
configMutationHandler: null,
|
|
@@ -659,6 +808,8 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
|
|
|
659
808
|
comp[stateKey] = state;
|
|
660
809
|
}
|
|
661
810
|
|
|
811
|
+
state.cleanupOperation?.();
|
|
812
|
+
state.cleanupOperation = null;
|
|
662
813
|
state.cleanupWatchers.forEach((cleanup) => cleanup());
|
|
663
814
|
state.cleanupWatchers = [];
|
|
664
815
|
state.requestGeneration++;
|
|
@@ -750,6 +901,10 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
|
|
|
750
901
|
};
|
|
751
902
|
|
|
752
903
|
const scheduleSend = createAnimationFrameCoalescedRunner(runSend);
|
|
904
|
+
state.cleanupOperation = operationController.bind(
|
|
905
|
+
component,
|
|
906
|
+
scheduleSend,
|
|
907
|
+
);
|
|
753
908
|
|
|
754
909
|
const rebindBodyPublisher = () => {
|
|
755
910
|
detachBodyPublisher(state);
|
|
@@ -869,6 +1024,8 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
|
|
|
869
1024
|
const comp = component as Record<string, unknown>;
|
|
870
1025
|
const state = comp[stateKey] as ApiSendState | undefined;
|
|
871
1026
|
if (!state) return;
|
|
1027
|
+
state.cleanupOperation?.();
|
|
1028
|
+
state.cleanupOperation = null;
|
|
872
1029
|
detachConfigPublisher(state);
|
|
873
1030
|
detachBodyPublisher(state);
|
|
874
1031
|
detachTriggerPublisher(state);
|
|
@@ -878,7 +1035,13 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
|
|
|
878
1035
|
state.requestGeneration++;
|
|
879
1036
|
comp[propertyKey] = undefined;
|
|
880
1037
|
});
|
|
1038
|
+
} as ApiSendDecoratorHandle<T, object>;
|
|
1039
|
+
|
|
1040
|
+
decorator.send = (component: object) => {
|
|
1041
|
+
operationController.run(component);
|
|
881
1042
|
};
|
|
1043
|
+
|
|
1044
|
+
return decorator;
|
|
882
1045
|
};
|
|
883
1046
|
}
|
|
884
1047
|
|
|
@@ -886,54 +1049,46 @@ function createApiSendDecorator<T, B, Ue = any, Uk = any, Ut = any>(
|
|
|
886
1049
|
* Décorateur **`@post`** : envoie des données via `API.postDetailed` et assigne un
|
|
887
1050
|
* `ApiResult<T>` (`request`, `response`, `result` typé `T`).
|
|
888
1051
|
*
|
|
889
|
-
* Le body est lu depuis un `DataProviderKey
|
|
1052
|
+
* Le body est lu depuis un `DataProviderKey`. Par défaut, une mutation du body
|
|
1053
|
+
* relance le POST ; `send(component)` permet de le relancer explicitement.
|
|
890
1054
|
*
|
|
891
1055
|
* @example
|
|
892
1056
|
* @post(new Endpoint<SyncResult, { sessionId: string }>("sessions/${sessionId}/sync"), syncRequestKey)
|
|
893
1057
|
* payload?: ApiResult<SyncResult>;
|
|
1058
|
+
*
|
|
1059
|
+
* @example
|
|
1060
|
+
* const save = post(new Endpoint<User>("users"), userRequestKey);
|
|
1061
|
+
* @save
|
|
1062
|
+
* payload?: ApiResult<User>;
|
|
1063
|
+
* // Dans un handler du composant :
|
|
1064
|
+
* save.send(this);
|
|
894
1065
|
*/
|
|
895
1066
|
export function post<T, B, Ue = any>(
|
|
896
1067
|
endpoint: Endpoint<T, Ue>,
|
|
897
1068
|
bodyKey: DataProviderKey<B>,
|
|
898
|
-
): <
|
|
899
|
-
target: DataProviderKeyHost<Ue> & {
|
|
900
|
-
[P in K]?: ApiResult<T> | null | undefined;
|
|
901
|
-
},
|
|
902
|
-
propertyKey: K,
|
|
903
|
-
) => void;
|
|
1069
|
+
): ApiSendDecoratorHandle<T, DataProviderKeyHost<Ue>>;
|
|
904
1070
|
export function post<T, B, Ue = any, Uk = any>(
|
|
905
1071
|
endpoint: Endpoint<T, Ue>,
|
|
906
1072
|
bodyKey: DataProviderKey<B>,
|
|
907
1073
|
configurationKey: DataProviderKey<APIConfiguration, Uk>,
|
|
908
|
-
): <
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
},
|
|
913
|
-
propertyKey: K,
|
|
914
|
-
) => void;
|
|
1074
|
+
): ApiSendDecoratorHandle<
|
|
1075
|
+
T,
|
|
1076
|
+
DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
|
|
1077
|
+
>;
|
|
915
1078
|
export function post<T, B, Ue = any, Uk = any>(
|
|
916
1079
|
endpoint: Endpoint<T, Ue>,
|
|
917
1080
|
bodyKey: DataProviderKey<B>,
|
|
918
1081
|
options: PostOptions<Uk>,
|
|
919
|
-
): <
|
|
920
|
-
target: DataProviderKeyHost<Ue> & {
|
|
921
|
-
[P in K]?: ApiResult<T> | null | undefined;
|
|
922
|
-
},
|
|
923
|
-
propertyKey: K,
|
|
924
|
-
) => void;
|
|
1082
|
+
): ApiSendDecoratorHandle<T, DataProviderKeyHost<Ue>>;
|
|
925
1083
|
export function post<T, B, Ue = any, Uk = any, Ut = any>(
|
|
926
1084
|
endpoint: Endpoint<T, Ue>,
|
|
927
1085
|
bodyKey: DataProviderKey<B>,
|
|
928
1086
|
configurationKey: DataProviderKey<APIConfiguration, Uk>,
|
|
929
1087
|
options: PostOptions<Ut>,
|
|
930
|
-
): <
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
},
|
|
935
|
-
propertyKey: K,
|
|
936
|
-
) => void;
|
|
1088
|
+
): ApiSendDecoratorHandle<
|
|
1089
|
+
T,
|
|
1090
|
+
DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
|
|
1091
|
+
>;
|
|
937
1092
|
export function post<T, B, Ue = any, Uk = any, Ut = any>(
|
|
938
1093
|
endpoint: Endpoint<T, Ue>,
|
|
939
1094
|
bodyKey: DataProviderKey<B>,
|
|
@@ -941,58 +1096,39 @@ export function post<T, B, Ue = any, Uk = any, Ut = any>(
|
|
|
941
1096
|
| DataProviderKey<APIConfiguration, Uk>
|
|
942
1097
|
| PostOptions<Ut>,
|
|
943
1098
|
maybeOptions?: PostOptions<Ut>,
|
|
944
|
-
): <
|
|
945
|
-
target: object & { [P in K]?: ApiResult<T> | null | undefined },
|
|
946
|
-
propertyKey: K,
|
|
947
|
-
) => void {
|
|
1099
|
+
): ApiSendDecoratorHandle<T, object> {
|
|
948
1100
|
return createApiSendDecorator<T, B, Ue, Uk, Ut>("POST", (api, path, body) =>
|
|
949
1101
|
api.postDetailed<T, B>(path, body as B),
|
|
950
1102
|
)(endpoint, bodyKey, configurationKeyOrOptions, maybeOptions);
|
|
951
1103
|
}
|
|
952
1104
|
|
|
953
|
-
/** `@put` — même modèle que `@post
|
|
1105
|
+
/** `@put` — même modèle que `@post`, via `API.putDetailed` et `send()`. */
|
|
954
1106
|
export function put<T, B, Ue = any>(
|
|
955
1107
|
endpoint: Endpoint<T, Ue>,
|
|
956
1108
|
bodyKey: DataProviderKey<B>,
|
|
957
|
-
): <
|
|
958
|
-
target: DataProviderKeyHost<Ue> & {
|
|
959
|
-
[P in K]?: ApiResult<T> | null | undefined;
|
|
960
|
-
},
|
|
961
|
-
propertyKey: K,
|
|
962
|
-
) => void;
|
|
1109
|
+
): ApiSendDecoratorHandle<T, DataProviderKeyHost<Ue>>;
|
|
963
1110
|
export function put<T, B, Ue = any, Uk = any>(
|
|
964
1111
|
endpoint: Endpoint<T, Ue>,
|
|
965
1112
|
bodyKey: DataProviderKey<B>,
|
|
966
1113
|
configurationKey: DataProviderKey<APIConfiguration, Uk>,
|
|
967
|
-
): <
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
},
|
|
972
|
-
propertyKey: K,
|
|
973
|
-
) => void;
|
|
1114
|
+
): ApiSendDecoratorHandle<
|
|
1115
|
+
T,
|
|
1116
|
+
DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
|
|
1117
|
+
>;
|
|
974
1118
|
export function put<T, B, Ue = any, Uk = any>(
|
|
975
1119
|
endpoint: Endpoint<T, Ue>,
|
|
976
1120
|
bodyKey: DataProviderKey<B>,
|
|
977
1121
|
options: PutOptions<Uk>,
|
|
978
|
-
): <
|
|
979
|
-
target: DataProviderKeyHost<Ue> & {
|
|
980
|
-
[P in K]?: ApiResult<T> | null | undefined;
|
|
981
|
-
},
|
|
982
|
-
propertyKey: K,
|
|
983
|
-
) => void;
|
|
1122
|
+
): ApiSendDecoratorHandle<T, DataProviderKeyHost<Ue>>;
|
|
984
1123
|
export function put<T, B, Ue = any, Uk = any, Ut = any>(
|
|
985
1124
|
endpoint: Endpoint<T, Ue>,
|
|
986
1125
|
bodyKey: DataProviderKey<B>,
|
|
987
1126
|
configurationKey: DataProviderKey<APIConfiguration, Uk>,
|
|
988
1127
|
options: PutOptions<Ut>,
|
|
989
|
-
): <
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
},
|
|
994
|
-
propertyKey: K,
|
|
995
|
-
) => void;
|
|
1128
|
+
): ApiSendDecoratorHandle<
|
|
1129
|
+
T,
|
|
1130
|
+
DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
|
|
1131
|
+
>;
|
|
996
1132
|
export function put<T, B, Ue = any, Uk = any, Ut = any>(
|
|
997
1133
|
endpoint: Endpoint<T, Ue>,
|
|
998
1134
|
bodyKey: DataProviderKey<B>,
|
|
@@ -1000,58 +1136,39 @@ export function put<T, B, Ue = any, Uk = any, Ut = any>(
|
|
|
1000
1136
|
| DataProviderKey<APIConfiguration, Uk>
|
|
1001
1137
|
| PutOptions<Ut>,
|
|
1002
1138
|
maybeOptions?: PutOptions<Ut>,
|
|
1003
|
-
): <
|
|
1004
|
-
target: object & { [P in K]?: ApiResult<T> | null | undefined },
|
|
1005
|
-
propertyKey: K,
|
|
1006
|
-
) => void {
|
|
1139
|
+
): ApiSendDecoratorHandle<T, object> {
|
|
1007
1140
|
return createApiSendDecorator<T, B, Ue, Uk, Ut>("PUT", (api, path, body) =>
|
|
1008
1141
|
api.putDetailed<T, B>(path, body as B),
|
|
1009
1142
|
)(endpoint, bodyKey, configurationKeyOrOptions, maybeOptions);
|
|
1010
1143
|
}
|
|
1011
1144
|
|
|
1012
|
-
/** `@patch` — même modèle que `@post
|
|
1145
|
+
/** `@patch` — même modèle que `@post`, via `API.patchDetailed` et `send()`. */
|
|
1013
1146
|
export function patch<T, B, Ue = any>(
|
|
1014
1147
|
endpoint: Endpoint<T, Ue>,
|
|
1015
1148
|
bodyKey: DataProviderKey<B>,
|
|
1016
|
-
): <
|
|
1017
|
-
target: DataProviderKeyHost<Ue> & {
|
|
1018
|
-
[P in K]?: ApiResult<T> | null | undefined;
|
|
1019
|
-
},
|
|
1020
|
-
propertyKey: K,
|
|
1021
|
-
) => void;
|
|
1149
|
+
): ApiSendDecoratorHandle<T, DataProviderKeyHost<Ue>>;
|
|
1022
1150
|
export function patch<T, B, Ue = any, Uk = any>(
|
|
1023
1151
|
endpoint: Endpoint<T, Ue>,
|
|
1024
1152
|
bodyKey: DataProviderKey<B>,
|
|
1025
1153
|
configurationKey: DataProviderKey<APIConfiguration, Uk>,
|
|
1026
|
-
): <
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
},
|
|
1031
|
-
propertyKey: K,
|
|
1032
|
-
) => void;
|
|
1154
|
+
): ApiSendDecoratorHandle<
|
|
1155
|
+
T,
|
|
1156
|
+
DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
|
|
1157
|
+
>;
|
|
1033
1158
|
export function patch<T, B, Ue = any, Uk = any>(
|
|
1034
1159
|
endpoint: Endpoint<T, Ue>,
|
|
1035
1160
|
bodyKey: DataProviderKey<B>,
|
|
1036
1161
|
options: PatchOptions<Uk>,
|
|
1037
|
-
): <
|
|
1038
|
-
target: DataProviderKeyHost<Ue> & {
|
|
1039
|
-
[P in K]?: ApiResult<T> | null | undefined;
|
|
1040
|
-
},
|
|
1041
|
-
propertyKey: K,
|
|
1042
|
-
) => void;
|
|
1162
|
+
): ApiSendDecoratorHandle<T, DataProviderKeyHost<Ue>>;
|
|
1043
1163
|
export function patch<T, B, Ue = any, Uk = any, Ut = any>(
|
|
1044
1164
|
endpoint: Endpoint<T, Ue>,
|
|
1045
1165
|
bodyKey: DataProviderKey<B>,
|
|
1046
1166
|
configurationKey: DataProviderKey<APIConfiguration, Uk>,
|
|
1047
1167
|
options: PatchOptions<Ut>,
|
|
1048
|
-
): <
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
},
|
|
1053
|
-
propertyKey: K,
|
|
1054
|
-
) => void;
|
|
1168
|
+
): ApiSendDecoratorHandle<
|
|
1169
|
+
T,
|
|
1170
|
+
DataProviderKeyHost<Ue> & DataProviderKeyHost<Uk>
|
|
1171
|
+
>;
|
|
1055
1172
|
export function patch<T, B, Ue = any, Uk = any, Ut = any>(
|
|
1056
1173
|
endpoint: Endpoint<T, Ue>,
|
|
1057
1174
|
bodyKey: DataProviderKey<B>,
|
|
@@ -1059,10 +1176,7 @@ export function patch<T, B, Ue = any, Uk = any, Ut = any>(
|
|
|
1059
1176
|
| DataProviderKey<APIConfiguration, Uk>
|
|
1060
1177
|
| PatchOptions<Ut>,
|
|
1061
1178
|
maybeOptions?: PatchOptions<Ut>,
|
|
1062
|
-
): <
|
|
1063
|
-
target: object & { [P in K]?: ApiResult<T> | null | undefined },
|
|
1064
|
-
propertyKey: K,
|
|
1065
|
-
) => void {
|
|
1179
|
+
): ApiSendDecoratorHandle<T, object> {
|
|
1066
1180
|
return createApiSendDecorator<T, B, Ue, Uk, Ut>("PATCH", (api, path, body) =>
|
|
1067
1181
|
api.patchDetailed<T, B>(path, body as B),
|
|
1068
1182
|
)(endpoint, bodyKey, configurationKeyOrOptions, maybeOptions);
|