@reidelsaltres/pureper 0.1.127 → 0.1.128

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.
@@ -1 +1 @@
1
- {"version":3,"file":"Fetcher.d.ts","sourceRoot":"","sources":["../../src/foundation/Fetcher.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,OAAO,OAAO,OAAO;WACX,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;WAUvC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAWjD,OAAO,CAAC,MAAM,CAAC,UAAU;mBAQJ,aAAa;CAOrC"}
1
+ {"version":3,"file":"Fetcher.d.ts","sourceRoot":"","sources":["../../src/foundation/Fetcher.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,OAAO,OAAO,OAAO;WACX,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;WAqBvC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAoBjD,OAAO,CAAC,MAAM,CAAC,UAAU;mBAQJ,aAAa;CAOrC"}
@@ -1,24 +1,50 @@
1
1
  import { HOSTING, HOSTING_ORIGIN } from "../index.js";
2
2
  // cache stores response bodies (text) by resolved URL so we can reuse them safely
3
3
  const temporaryCache = new Map();
4
+ // keep in-flight promises to deduplicate concurrent identical requests
5
+ const inFlightText = new Map();
6
+ const inFlightJSON = new Map();
4
7
  export default class Fetcher {
5
8
  static async fetchText(url) {
6
9
  const resolved = this.resolveUrl(url);
7
10
  if (temporaryCache.has(resolved))
8
11
  return temporaryCache.get(resolved);
9
- const response = await this.internalFetch(resolved);
10
- const text = await response.text();
11
- temporaryCache.set(resolved, text);
12
- return text;
12
+ // If a request for the same URL is already in-flight, reuse its promise
13
+ if (inFlightText.has(resolved))
14
+ return inFlightText.get(resolved);
15
+ const p = (async () => {
16
+ const response = await this.internalFetch(resolved);
17
+ const text = await response.text();
18
+ try {
19
+ temporaryCache.set(resolved, text);
20
+ }
21
+ catch { }
22
+ return text;
23
+ })();
24
+ inFlightText.set(resolved, p);
25
+ // cleanup entry when finished
26
+ p.finally(() => inFlightText.delete(resolved));
27
+ return p;
13
28
  }
14
29
  static async fetchJSON(url) {
15
30
  const resolved = this.resolveUrl(url);
16
31
  if (temporaryCache.has(resolved))
17
32
  return JSON.parse(temporaryCache.get(resolved));
18
- const response = await this.internalFetch(resolved);
19
- const json = await response.json();
20
- temporaryCache.set(resolved, JSON.stringify(json));
21
- return json;
33
+ // If a request for the same URL is already in-flight, reuse its promise
34
+ if (inFlightJSON.has(resolved))
35
+ return inFlightJSON.get(resolved);
36
+ const p = (async () => {
37
+ const response = await this.internalFetch(resolved);
38
+ const json = await response.json();
39
+ try {
40
+ temporaryCache.set(resolved, JSON.stringify(json));
41
+ }
42
+ catch { }
43
+ return json;
44
+ })();
45
+ inFlightJSON.set(resolved, p);
46
+ p.finally(() => inFlightJSON.delete(resolved));
47
+ return p;
22
48
  }
23
49
  static resolveUrl(url) {
24
50
  const urlObj = new URL(url, HOSTING_ORIGIN);
@@ -1 +1 @@
1
- {"version":3,"file":"Fetcher.js","sourceRoot":"","sources":["../../src/foundation/Fetcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAEtD,kFAAkF;AAClF,MAAM,cAAc,GAAwB,IAAI,GAAG,EAAE,CAAC;AACtD,MAAM,CAAC,OAAO,OAAO,OAAO;IACxB,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,GAAW;QAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC5B,OAAO,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAE,CAAC;QAEzC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,GAAW;QAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAE,CAAC,CAAC;QAErD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC;IAChB,CAAC;IAEO,MAAM,CAAC,UAAU,CAAC,GAAW;QACjC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACjC,OAAO,MAAM,CAAC,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC;QACrD,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC;IACvB,CAAC;IAEO,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,WAAmB;QAClD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,QAAQ,CAAC;IACpB,CAAC;CACJ"}
1
+ {"version":3,"file":"Fetcher.js","sourceRoot":"","sources":["../../src/foundation/Fetcher.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reidelsaltres/pureper",
3
- "version": "0.1.127",
3
+ "version": "0.1.128",
4
4
  "description": "Minimal library extracted from the Pureper SPA foundation — utilities and base classes for components/pages.",
5
5
  "type": "module",
6
6
  "main": "out/src/index.js",
@@ -2,26 +2,49 @@ import { HOSTING, HOSTING_ORIGIN } from "../index.js";
2
2
 
3
3
  // cache stores response bodies (text) by resolved URL so we can reuse them safely
4
4
  const temporaryCache: Map<string, string> = new Map();
5
+ // keep in-flight promises to deduplicate concurrent identical requests
6
+ const inFlightText: Map<string, Promise<string>> = new Map();
7
+ const inFlightJSON: Map<string, Promise<any>> = new Map();
5
8
  export default class Fetcher {
6
9
  static async fetchText(url: string): Promise<string> {
7
10
  const resolved = this.resolveUrl(url);
8
11
  if (temporaryCache.has(resolved))
9
12
  return temporaryCache.get(resolved)!;
10
13
 
11
- const response = await this.internalFetch(resolved);
12
- const text = await response.text();
13
- temporaryCache.set(resolved, text);
14
- return text;
14
+ // If a request for the same URL is already in-flight, reuse its promise
15
+ if (inFlightText.has(resolved)) return inFlightText.get(resolved)!;
16
+
17
+ const p = (async () => {
18
+ const response = await this.internalFetch(resolved);
19
+ const text = await response.text();
20
+ try { temporaryCache.set(resolved, text); } catch {}
21
+ return text;
22
+ })();
23
+
24
+ inFlightText.set(resolved, p);
25
+ // cleanup entry when finished
26
+ p.finally(() => inFlightText.delete(resolved));
27
+
28
+ return p;
15
29
  }
16
30
  static async fetchJSON(url: string): Promise<any> {
17
31
  const resolved = this.resolveUrl(url);
18
32
  if (temporaryCache.has(resolved))
19
33
  return JSON.parse(temporaryCache.get(resolved)!);
20
34
 
21
- const response = await this.internalFetch(resolved);
22
- const json = await response.json();
23
- temporaryCache.set(resolved, JSON.stringify(json));
24
- return json;
35
+ // If a request for the same URL is already in-flight, reuse its promise
36
+ if (inFlightJSON.has(resolved)) return inFlightJSON.get(resolved)!;
37
+
38
+ const p = (async () => {
39
+ const response = await this.internalFetch(resolved);
40
+ const json = await response.json();
41
+ try { temporaryCache.set(resolved, JSON.stringify(json)); } catch {}
42
+ return json;
43
+ })();
44
+
45
+ inFlightJSON.set(resolved, p);
46
+ p.finally(() => inFlightJSON.delete(resolved));
47
+ return p;
25
48
  }
26
49
 
27
50
  private static resolveUrl(url: string): string {