@supersoniks/concorde 4.9.91 → 4.9.93

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 (48) 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 +257 -257
  6. package/concorde-core.es.js +3174 -3086
  7. package/dist/concorde-core.bundle.js +257 -257
  8. package/dist/concorde-core.es.js +3174 -3086
  9. package/dist/js/core/decorators/api.js +168 -63
  10. package/dist/js/core/decorators/api.js.map +1 -1
  11. package/dist/js/core/utils/api.js +29 -8
  12. package/dist/js/core/utils/api.js.map +1 -1
  13. package/dist/types/core/decorators/api.d.ts +30 -16
  14. package/dist/types/core/decorators/api.d.ts.map +1 -1
  15. package/dist/types/core/utils/api.d.ts +1 -1
  16. package/dist/types/core/utils/api.d.ts.map +1 -1
  17. package/docs/assets/{index-BL6WVF7g.css → index-D2Q3JyQ6.css} +1 -1
  18. package/docs/assets/{index-DVn8AeyA.js → index-DURr6O71.js} +466 -568
  19. package/docs/crawl/docs/_decorators/get.html +35 -10
  20. package/docs/crawl/docs/_decorators/patch.html +13 -5
  21. package/docs/crawl/docs/_decorators/post.html +39 -10
  22. package/docs/crawl/docs/_decorators/put.html +13 -5
  23. package/docs/crawl/hubs/decorators.html +43 -40
  24. package/docs/crawl/hubs/remote-data.html +43 -40
  25. package/docs/index.html +2 -2
  26. package/docs/llms-full.txt +98 -23
  27. package/docs/llms.txt +1 -1
  28. package/docs/src/docs/_decorators/get.md +39 -5
  29. package/docs/src/docs/_decorators/patch.md +8 -3
  30. package/docs/src/docs/_decorators/post.md +39 -6
  31. package/docs/src/docs/_decorators/put.md +8 -3
  32. package/docs/src/docs/search/docs-search.json +103 -183
  33. package/docs/src/tsconfig.json +268 -268
  34. package/package.json +1 -1
  35. package/src/core/decorators/api.post.spec.ts +101 -6
  36. package/src/core/decorators/api.spec.ts +137 -6
  37. package/src/core/decorators/api.ts +304 -148
  38. package/src/core/utils/api.spec.ts +73 -0
  39. package/src/core/utils/api.ts +44 -11
  40. package/src/docs/_decorators/get.md +43 -5
  41. package/src/docs/_decorators/patch.md +16 -2
  42. package/src/docs/_decorators/post.md +23 -7
  43. package/src/docs/_decorators/put.md +16 -2
  44. package/src/docs/components/docs-demo-sources.ts +22 -8
  45. package/src/docs/example/decorators-demo-post.ts +70 -0
  46. package/src/docs/example/decorators-demo-subscribe-publish-get-demos.ts +61 -2
  47. package/src/docs/search/docs-search.json +122 -7
  48. package/src/tsconfig.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supersoniks/concorde",
3
- "version": "4.9.91",
3
+ "version": "4.9.93",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "",
@@ -95,14 +95,14 @@ describe("post", () => {
95
95
  vi.mocked(API.prototype.postDetailed).mockClear();
96
96
 
97
97
  pub.set({ value: "second" });
98
- localPost.send(el);
99
- await flushSendFrames();
98
+ const payload = await localPost.send(el);
100
99
 
101
100
  expect(API.prototype.postDetailed).toHaveBeenCalledTimes(1);
102
101
  expect(API.prototype.postDetailed).toHaveBeenCalledWith(
103
102
  "items/local-send",
104
103
  { value: "second" },
105
104
  );
105
+ expect(payload?.result).toEqual({ ok: true });
106
106
  });
107
107
 
108
108
  it("peut décorer une méthode avec send après sa résolution", async () => {
@@ -303,6 +303,91 @@ describe("post", () => {
303
303
  );
304
304
  expect(el.payload?.result).toEqual({ ok: true });
305
305
  });
306
+
307
+ it("transmet le payload complet et conserve le retrigger du POST", async () => {
308
+ const bodyPubId = "postSpecHandlerBody";
309
+ const triggerPubId = "postSpecHandlerTrigger";
310
+ PublisherManager.get(bodyPubId).set({ tick: 1 });
311
+ const bodyKey = new DataProviderKey<{ tick: number }>(bodyPubId);
312
+ const triggerKey = new DataProviderKey<number>(triggerPubId);
313
+ const ep = new Endpoint<{ ok: boolean }>("items/handler");
314
+ const localPost = post(ep, bodyKey, {
315
+ autoPostOnBodyMutation: false,
316
+ triggerKey,
317
+ });
318
+ const received: Array<ApiResult<{ ok: boolean }> | undefined> = [];
319
+ const errorPayload = mockPayload({ ok: false });
320
+ errorPayload.response = new Response(JSON.stringify({ error: true }), {
321
+ status: 409,
322
+ headers: { "Content-Type": "application/json" },
323
+ });
324
+ vi.mocked(API.prototype.postDetailed).mockResolvedValueOnce(
325
+ errorPayload as never,
326
+ );
327
+ const tag = "test-api-post-handler";
328
+
329
+ class C extends HTMLElement {
330
+ @localPost
331
+ onPayload(payload: ApiResult<{ ok: boolean }> | undefined) {
332
+ received.push(payload);
333
+ }
334
+ }
335
+ if (!customElements.get(tag)) {
336
+ customElements.define(tag, C);
337
+ }
338
+
339
+ const wrap = document.createElement("div");
340
+ wrap.setAttribute("serviceURL", "https://api.example.test");
341
+ const el = document.createElement(tag) as InstanceType<typeof C>;
342
+ wrap.appendChild(el);
343
+ document.body.appendChild(wrap);
344
+ await flushSendFrames();
345
+
346
+ expect(API.prototype.postDetailed).not.toHaveBeenCalled();
347
+ await localPost.send(el);
348
+ expect(received).toHaveLength(1);
349
+ expect(received[0]?.response?.status).toBe(409);
350
+
351
+ PublisherManager.get(triggerPubId).invalidate();
352
+ await flushSendFrames();
353
+ expect(API.prototype.postDetailed).toHaveBeenCalledTimes(2);
354
+ expect(received).toHaveLength(2);
355
+ });
356
+
357
+ it("transmet undefined à une méthode @post sur une erreur de transport", async () => {
358
+ const bodyPubId = "postSpecHandlerTransportBody";
359
+ PublisherManager.get(bodyPubId).set({ tick: 1 });
360
+ const bodyKey = new DataProviderKey<{ tick: number }>(bodyPubId);
361
+ const ep = new Endpoint<{ ok: boolean }>("items/handler-transport");
362
+ const localPost = post(ep, bodyKey, {
363
+ autoPostOnBodyMutation: false,
364
+ });
365
+ const received: Array<ApiResult<{ ok: boolean }> | undefined> = [];
366
+ vi.mocked(API.prototype.postDetailed).mockRejectedValueOnce(
367
+ new Error("network failure"),
368
+ );
369
+ const tag = "test-api-post-handler-transport";
370
+
371
+ class C extends HTMLElement {
372
+ @localPost
373
+ onPayload(payload: ApiResult<{ ok: boolean }> | undefined) {
374
+ received.push(payload);
375
+ }
376
+ }
377
+ if (!customElements.get(tag)) {
378
+ customElements.define(tag, C);
379
+ }
380
+
381
+ const wrap = document.createElement("div");
382
+ wrap.setAttribute("serviceURL", "https://api.example.test");
383
+ const el = document.createElement(tag) as InstanceType<typeof C>;
384
+ wrap.appendChild(el);
385
+ document.body.appendChild(wrap);
386
+ await flushSendFrames();
387
+
388
+ await expect(localPost.send(el)).rejects.toThrow("network failure");
389
+ expect(received).toEqual([undefined]);
390
+ });
306
391
  });
307
392
 
308
393
  describe("put / patch", () => {
@@ -319,11 +404,15 @@ describe("put / patch", () => {
319
404
  PublisherManager.get(bodyPubId).set({ name: "x" });
320
405
  const bodyKey = new DataProviderKey<{ name: string }>(bodyPubId);
321
406
  const ep = new Endpoint<{ ok: boolean }>("items/1");
407
+ const localPut = put(ep, bodyKey);
408
+ const received: Array<ApiResult<{ ok: boolean }> | undefined> = [];
322
409
  const tag = "test-api-put";
323
410
 
324
411
  class C extends HTMLElement {
325
- @put(ep, bodyKey)
326
- payload: ApiResult<{ ok: boolean }> | null = null;
412
+ @localPut
413
+ onPayload(payload: ApiResult<{ ok: boolean }> | undefined) {
414
+ received.push(payload);
415
+ }
327
416
  }
328
417
  if (!customElements.get(tag)) {
329
418
  customElements.define(tag, C);
@@ -338,6 +427,7 @@ describe("put / patch", () => {
338
427
  expect(API.prototype.putDetailed).toHaveBeenCalledWith("items/1", {
339
428
  name: "x",
340
429
  });
430
+ expect(received).toHaveLength(1);
341
431
  });
342
432
 
343
433
  it("@patch appelle patchDetailed", async () => {
@@ -348,11 +438,15 @@ describe("put / patch", () => {
348
438
  PublisherManager.get(bodyPubId).set({ name: "y" });
349
439
  const bodyKey = new DataProviderKey<{ name: string }>(bodyPubId);
350
440
  const ep = new Endpoint<{ ok: boolean }>("items/1");
441
+ const localPatch = patch(ep, bodyKey);
442
+ const received: Array<ApiResult<{ ok: boolean }> | undefined> = [];
351
443
  const tag = "test-api-patch";
352
444
 
353
445
  class C extends HTMLElement {
354
- @patch(ep, bodyKey)
355
- payload: ApiResult<{ ok: boolean }> | null = null;
446
+ @localPatch
447
+ onPayload(payload: ApiResult<{ ok: boolean }> | undefined) {
448
+ received.push(payload);
449
+ }
356
450
  }
357
451
  if (!customElements.get(tag)) {
358
452
  customElements.define(tag, C);
@@ -367,5 +461,6 @@ describe("put / patch", () => {
367
461
  expect(API.prototype.patchDetailed).toHaveBeenCalledWith("items/1", {
368
462
  name: "y",
369
463
  });
464
+ expect(received).toHaveLength(1);
370
465
  });
371
466
  });
@@ -190,6 +190,96 @@ describe("get", () => {
190
190
  expect(el.payload?.result).toEqual({ id: "loaded" });
191
191
  });
192
192
 
193
+ it("transmet le payload complet à une méthode @get", async () => {
194
+ const itemEp = new Endpoint<{ id: string }>("items/handler");
195
+ const received: Array<ApiResult<{ id: string }> | undefined> = [];
196
+ const tag = "test-api-get-handler";
197
+ const errorPayload = mockPayload({ id: "error" });
198
+ errorPayload.response = new Response(JSON.stringify({ error: true }), {
199
+ status: 422,
200
+ headers: { "Content-Type": "application/json" },
201
+ });
202
+ vi.mocked(API.prototype.getDetailed).mockResolvedValueOnce(
203
+ errorPayload as never,
204
+ );
205
+
206
+ class C extends HTMLElement {
207
+ @get(itemEp)
208
+ onPayload(payload: ApiResult<{ id: string }> | undefined) {
209
+ received.push(payload);
210
+ }
211
+ }
212
+ if (!customElements.get(tag)) {
213
+ customElements.define(tag, C);
214
+ }
215
+
216
+ const wrap = document.createElement("div");
217
+ wrap.setAttribute("serviceURL", "https://api.example.test");
218
+ wrap.appendChild(document.createElement(tag));
219
+ document.body.appendChild(wrap);
220
+ await flushGetFrames();
221
+
222
+ expect(received).toHaveLength(1);
223
+ expect(received[0]?.result).toEqual({ id: "error" });
224
+ expect(received[0]?.response?.status).toBe(422);
225
+ expect(received[0]?.request.url).toContain("api.example.test");
226
+ });
227
+
228
+ it("transmet undefined à une méthode @get quand aucun payload n'est produit", async () => {
229
+ vi.mocked(API.prototype.getDetailed).mockResolvedValueOnce(undefined);
230
+ const itemEp = new Endpoint<{ id: string }>("items/handler-error");
231
+ const received: Array<ApiResult<{ id: string }> | undefined> = [];
232
+ const tag = "test-api-get-handler-undefined";
233
+
234
+ class C extends HTMLElement {
235
+ @get(itemEp)
236
+ onPayload(payload: ApiResult<{ id: string }> | undefined) {
237
+ received.push(payload);
238
+ }
239
+ }
240
+ if (!customElements.get(tag)) {
241
+ customElements.define(tag, C);
242
+ }
243
+
244
+ const wrap = document.createElement("div");
245
+ wrap.setAttribute("serviceURL", "https://api.example.test");
246
+ wrap.appendChild(document.createElement(tag));
247
+ document.body.appendChild(wrap);
248
+ await flushGetFrames();
249
+
250
+ expect(received).toEqual([undefined]);
251
+ });
252
+
253
+ it("permet d'appliquer un handle get directement à une méthode", async () => {
254
+ const itemEp = new Endpoint<{ id: string }>("items/method");
255
+ const localGet = get(itemEp);
256
+ const received: Array<ApiResult<{ id: string }> | undefined> = [];
257
+ const tag = "test-api-get-method-handler";
258
+
259
+ class C extends HTMLElement {
260
+ @localGet
261
+ onPayload(payload: ApiResult<{ id: string }> | undefined) {
262
+ received.push(payload);
263
+ }
264
+ }
265
+ if (!customElements.get(tag)) {
266
+ customElements.define(tag, C);
267
+ }
268
+
269
+ const wrap = document.createElement("div");
270
+ wrap.setAttribute("serviceURL", "https://api.example.test");
271
+ const el = document.createElement(tag) as InstanceType<typeof C>;
272
+ wrap.appendChild(el);
273
+ document.body.appendChild(wrap);
274
+ await flushGetFrames();
275
+
276
+ expect(received).toHaveLength(1);
277
+ expect(received[0]?.result).toEqual({ id: "loaded" });
278
+
279
+ await localGet.refetch(el);
280
+ expect(received).toHaveLength(2);
281
+ });
282
+
193
283
  it("expose un refetch local avant et après le premier payload", async () => {
194
284
  const pubId = "getSpecLocalRefetch";
195
285
  PublisherManager.get(pubId).set(staticConfig);
@@ -209,16 +299,18 @@ describe("get", () => {
209
299
  const el = document.createElement(tag) as InstanceType<typeof C>;
210
300
  document.body.appendChild(el);
211
301
 
212
- localGet.refetch(el);
213
- await flushGetFrames();
302
+ const firstRefetch = localGet.refetch(el);
303
+ expect(firstRefetch).toBeInstanceOf(Promise);
304
+ const firstPayload = await firstRefetch;
214
305
  expect(API.prototype.getDetailed).toHaveBeenCalledTimes(1);
306
+ expect(firstPayload?.result).toEqual({ id: "loaded" });
215
307
 
216
308
  vi.mocked(API.prototype.getDetailed).mockClear();
217
309
  el.payload = mockPayload({ id: "manual" });
218
- localGet.refetch(el);
219
- await flushGetFrames();
310
+ const secondPayload = await localGet.refetch(el);
220
311
 
221
312
  expect(API.prototype.getDetailed).toHaveBeenCalledTimes(1);
313
+ expect(secondPayload?.result).toEqual({ id: "loaded" });
222
314
  expect(el.payload?.result).toEqual({ id: "loaded" });
223
315
  });
224
316
 
@@ -246,8 +338,7 @@ describe("get", () => {
246
338
  vi.mocked(API.prototype.getDetailed).mockClear();
247
339
 
248
340
  el.remove();
249
- localGet.refetch(el);
250
- await flushGetFrames();
341
+ await localGet.refetch(el);
251
342
 
252
343
  expect(API.prototype.getDetailed).not.toHaveBeenCalled();
253
344
  });
@@ -289,6 +380,46 @@ describe("get", () => {
289
380
  expect(API.prototype.getDetailed).toHaveBeenCalledWith("rows/84");
290
381
  });
291
382
 
383
+ it("attend le refetch avant la méthode avec when before", async () => {
384
+ const pubId = "getSpecMethodRefetchBefore";
385
+ PublisherManager.get(pubId).set(staticConfig);
386
+ const confKey = new DataProviderKey<APIConfiguration>(pubId);
387
+ const localGet = get(
388
+ new Endpoint<{ id: string }>("items/before"),
389
+ confKey,
390
+ );
391
+ const events: string[] = [];
392
+ const tag = "test-api-get-method-refetch-before";
393
+
394
+ class C extends HTMLElement {
395
+ @localGet
396
+ payload: ApiResult<{ id: string }> | null = null;
397
+
398
+ @refetch(localGet, { when: "before" })
399
+ reload() {
400
+ events.push("method");
401
+ }
402
+ }
403
+ if (!customElements.get(tag)) {
404
+ customElements.define(tag, C);
405
+ }
406
+
407
+ const el = document.createElement(tag) as InstanceType<typeof C>;
408
+ document.body.appendChild(el);
409
+ await flushGetFrames();
410
+ vi.mocked(API.prototype.getDetailed).mockClear();
411
+ events.length = 0;
412
+ vi.mocked(API.prototype.getDetailed).mockImplementation(async () => {
413
+ events.push("request");
414
+ return mockPayload({ id: "loaded" }) as never;
415
+ });
416
+
417
+ await el.reload();
418
+
419
+ expect(events).toEqual(["request", "method"]);
420
+ expect(API.prototype.getDetailed).toHaveBeenCalledWith("items/before");
421
+ });
422
+
292
423
  it("refetch automatique avec refetchEveryMs", async () => {
293
424
  vi.useFakeTimers();
294
425
  const itemEp = new Endpoint<{ id: string }>("items/1");