@podium/client 5.3.0-next.1 → 5.3.0

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/CHANGELOG.md CHANGED
@@ -1,9 +1,58 @@
1
- # [5.3.0-next.1](https://github.com/podium-lib/client/compare/v5.2.0...v5.3.0-next.1) (2024-11-13)
1
+ # [5.3.0](https://github.com/podium-lib/client/compare/v5.2.7...v5.3.0) (2025-02-07)
2
2
 
3
3
 
4
4
  ### Features
5
5
 
6
- * track podlet assets ([39022a3](https://github.com/podium-lib/client/commit/39022a3c87184e5f524828591c8185d8a3fc2995))
6
+ * add metric to track register podlets ([673a4e7](https://github.com/podium-lib/client/commit/673a4e7554282570d1a074ae7206873af7183dbe))
7
+
8
+ ## [5.2.7](https://github.com/podium-lib/client/compare/v5.2.6...v5.2.7) (2025-02-06)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * list exports so types work for imports other than root ([#454](https://github.com/podium-lib/client/issues/454)) ([b186bec](https://github.com/podium-lib/client/commit/b186bece4fd0feb82f4e64c4acf5f27dd6b06659))
14
+
15
+ ## [5.2.6](https://github.com/podium-lib/client/compare/v5.2.5...v5.2.6) (2025-01-16)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **deps:** update dependency undici to v6.21.1 ([#453](https://github.com/podium-lib/client/issues/453)) ([2cf54ca](https://github.com/podium-lib/client/commit/2cf54cac312cf6cbb91da0e0f636433f2de7d892))
21
+
22
+ ## [5.2.5](https://github.com/podium-lib/client/compare/v5.2.4...v5.2.5) (2024-11-27)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * correctly build asset urls from link headers ([#446](https://github.com/podium-lib/client/issues/446)) ([5a30079](https://github.com/podium-lib/client/commit/5a30079fb47ee15a49ae6ff77348c400df3fe99a))
28
+
29
+ ## [5.2.4](https://github.com/podium-lib/client/compare/v5.2.3...v5.2.4) (2024-11-21)
30
+
31
+
32
+ ### Bug Fixes
33
+
34
+ * reducing logl evel to info on 404 errors ([e6d6d04](https://github.com/podium-lib/client/commit/e6d6d04a4b30729691cafa68521bcfc743c3db64))
35
+
36
+ ## [5.2.3](https://github.com/podium-lib/client/compare/v5.2.2...v5.2.3) (2024-11-13)
37
+
38
+
39
+ ### Bug Fixes
40
+
41
+ * **deps:** update dependency undici to v6.21.0 ([#445](https://github.com/podium-lib/client/issues/445)) ([6d26507](https://github.com/podium-lib/client/commit/6d26507709beffe5301f89ff7a5d03c358bbb423))
42
+
43
+ ## [5.2.2](https://github.com/podium-lib/client/compare/v5.2.1...v5.2.2) (2024-11-13)
44
+
45
+
46
+ ### Bug Fixes
47
+
48
+ * **deps:** update dependency @metrics/client to v2.5.4 ([#444](https://github.com/podium-lib/client/issues/444)) ([4f6bc04](https://github.com/podium-lib/client/commit/4f6bc0470a7ad01f32c6ea757cf853f7d2125db5))
49
+
50
+ ## [5.2.1](https://github.com/podium-lib/client/compare/v5.2.0...v5.2.1) (2024-11-13)
51
+
52
+
53
+ ### Bug Fixes
54
+
55
+ * **deps:** update dependency @podium/utils to v5.4.0 ([#443](https://github.com/podium-lib/client/issues/443)) ([efbe56b](https://github.com/podium-lib/client/commit/efbe56bbed26871566705856adbfa622702bdfa0))
7
56
 
8
57
  # [5.2.0](https://github.com/podium-lib/client/compare/v5.1.18...v5.2.0) (2024-11-06)
9
58
 
package/README.md CHANGED
@@ -2,9 +2,7 @@
2
2
 
3
3
  Client for fetching podium component fragments over HTTP.
4
4
 
5
- [![Dependencies](https://img.shields.io/david/podium-lib/client.svg)](https://david-dm.org/podium-lib/client)
6
5
  [![GitHub Actions status](https://github.com/podium-lib/client/workflows/Run%20Lint%20and%20Tests/badge.svg)](https://github.com/podium-lib/client/actions?query=workflow%3A%22Run+Lint+and+Tests%22)
7
- [![Known Vulnerabilities](https://snyk.io/test/github/podium-lib/client/badge.svg?targetFile=package.json)](https://snyk.io/test/github/podium-lib/client?targetFile=package.json)
8
6
 
9
7
  This module is intended for internal use in Podium and is not a module an end
10
8
  user would use directly. End users will typically interact with this module
@@ -152,6 +150,7 @@ The following values can be provided:
152
150
  - `throwable` - {Boolean} - Defines whether an error should be thrown if a failure occurs during the process of fetching a podium component. Defaults to `false` - Optional.
153
151
  - `excludeBy` - {Object} - Lets you define a set of rules where a `fetch` call will not be resolved if it matches. - Optional.
154
152
  - `includeBy` - {Object} - Inverse of `excludeBy`. Setting both at the same time will throw. - Optional.
153
+ - `earlyHints` - {boolean} - Can be used to disable early hints from being sent to the browser for this resource, see [HTTP Status 103 Early Hints](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103).
155
154
 
156
155
  ##### `excludeBy` and `includeBy`
157
156
 
package/lib/client.js CHANGED
@@ -68,6 +68,7 @@ export default class PodiumClient extends EventEmitter {
68
68
  #resources;
69
69
  #registry;
70
70
  #metrics;
71
+ #counter;
71
72
  #histogram;
72
73
  #options;
73
74
  #state;
@@ -144,6 +145,16 @@ export default class PodiumClient extends EventEmitter {
144
145
  },
145
146
  });
146
147
 
148
+ this.#counter = this.#metrics.counter({
149
+ name: 'podium_client_registered_podlet_count',
150
+ description: 'Number of podlets registered with the client',
151
+ labels: {
152
+ clientName: this.#options.name,
153
+ resourceName: undefined,
154
+ resourceUri: undefined,
155
+ },
156
+ });
157
+
147
158
  this.#histogram = this.#metrics.histogram({
148
159
  name: 'podium_client_refresh_manifests',
149
160
  description: 'Time taken for podium client to refresh manifests',
@@ -221,6 +232,13 @@ export default class PodiumClient extends EventEmitter {
221
232
  resourceOptions,
222
233
  );
223
234
 
235
+ this.#counter.inc({
236
+ labels: {
237
+ resourceName: options.name,
238
+ resourceUri: options.uri,
239
+ },
240
+ });
241
+
224
242
  resource.metrics.pipe(this.#metrics);
225
243
 
226
244
  Object.defineProperty(this, options.name, {
@@ -1,6 +1,6 @@
1
1
  import { PassThrough } from 'stream';
2
2
  import assert from 'assert';
3
- import { filterAssets } from './utils.js';
3
+ import { toPreloadAssetObjects, filterAssets } from './utils.js';
4
4
 
5
5
  /**
6
6
  * @typedef {object} PodiumClientHttpOutgoingOptions
@@ -70,7 +70,6 @@ export default class PodletClientHttpOutgoing extends PassThrough {
70
70
  #uri;
71
71
  #js;
72
72
  #css;
73
- #assetsReceived = false;
74
73
 
75
74
  /**
76
75
  * @constructor
@@ -333,26 +332,6 @@ export default class PodletClientHttpOutgoing extends PassThrough {
333
332
  return this.#isFallback;
334
333
  }
335
334
 
336
- get assetsReceived() {
337
- return this.#assetsReceived;
338
- }
339
-
340
- /**
341
- * Set the assetsReceived flag.
342
- * This is used to signal to the assets object that the client has received assets for a given podlet so it can track
343
- * which podlets have sent their assets.
344
- * @param {boolean} value
345
- */
346
- set assetsReceived(value) {
347
- this.#assetsReceived = value;
348
- if (this.#assetsReceived) {
349
- this.#incoming?.assets?.addReceivedAsset(this.#name, {
350
- js: this.js,
351
- css: this.css,
352
- });
353
- }
354
- }
355
-
356
335
  pushFallback() {
357
336
  // @ts-expect-error Internal property
358
337
  this.push(this.#manifest._fallback);
@@ -370,8 +349,21 @@ export default class PodletClientHttpOutgoing extends PassThrough {
370
349
  : filterAssets('fallback', this.#manifest.css);
371
350
  this.push(null);
372
351
  this.#isFallback = true;
373
- // assume the assets from the podlet have failed and fallback assets will be used
374
- this.assetsReceived = true;
352
+ // assume the hints from the podlet have failed and fallback assets will be used
353
+ this.hintsReceived = true;
354
+ }
355
+
356
+ writeEarlyHints(cb = () => {}) {
357
+ if (this.#incoming.response.writeEarlyHints) {
358
+ const preloads = toPreloadAssetObjects([
359
+ ...(this.js || []),
360
+ ...(this.css || []),
361
+ ]);
362
+ const link = preloads.map((preload) => preload.toHeader());
363
+ if (link.length) {
364
+ this.#incoming.response.writeEarlyHints({ link }, cb);
365
+ }
366
+ }
375
367
  }
376
368
 
377
369
  get [Symbol.toStringTag]() {
@@ -159,7 +159,10 @@ export default class PodletClientContentResolver {
159
159
  body,
160
160
  } = await this.#http.request(uri, reqOptions);
161
161
 
162
- const parsedAssetObjects = parseLinkHeaders(hdrs.link);
162
+ const parsedAssetObjects = parseLinkHeaders(
163
+ hdrs.link,
164
+ outgoing.manifestUri,
165
+ );
163
166
 
164
167
  const scriptObjects = parsedAssetObjects.filter(
165
168
  (asset) => asset instanceof AssetJs,
@@ -205,7 +208,7 @@ export default class PodletClientContentResolver {
205
208
  },
206
209
  });
207
210
 
208
- this.#log.warn(
211
+ this.#log.debug(
209
212
  `remote resource responded with non 200 http status code for content - code: ${statusCode} - resource: ${outgoing.name} - url: ${uri}`,
210
213
  );
211
214
  outgoing.success = true;
@@ -267,9 +270,6 @@ export default class PodletClientContentResolver {
267
270
  }),
268
271
  );
269
272
 
270
- // mark assets as received
271
- outgoing.assetsReceived = true;
272
-
273
273
  // @ts-ignore
274
274
  pipeline([body, outgoing], (err) => {
275
275
  if (err) {
@@ -120,7 +120,10 @@ export default class PodletClientFallbackResolver {
120
120
  headers: resHeaders,
121
121
  } = await this.#http.request(outgoing.fallbackUri, reqOptions);
122
122
 
123
- const parsedAssetObjects = parseLinkHeaders(resHeaders.link);
123
+ const parsedAssetObjects = parseLinkHeaders(
124
+ resHeaders.link,
125
+ outgoing.manifestUri,
126
+ );
124
127
 
125
128
  const scriptObjects = parsedAssetObjects.filter(
126
129
  (asset) => asset instanceof AssetJs,
package/lib/resolver.js CHANGED
@@ -11,6 +11,7 @@ import Cache from './resolver.cache.js';
11
11
  * @typedef {object} PodletClientResolverOptions
12
12
  * @property {string} clientName
13
13
  * @property {import('abslog').AbstractLoggerOptions} [logger]
14
+ * @property {boolean} [earlyHints]
14
15
  */
15
16
 
16
17
  export default class PodletClientResolver {
package/lib/resource.js CHANGED
@@ -105,8 +105,6 @@ export default class PodiumClientResource {
105
105
  throw new TypeError(
106
106
  'you must pass an instance of "HttpIncoming" as the first argument to the .fetch() method',
107
107
  );
108
- // set expectation to receive a response from this podlet.
109
- incoming.assets.addExpectedAsset(this.#options.name);
110
108
  const outgoing = new HttpOutgoing(this.#options, reqOptions, incoming);
111
109
 
112
110
  if (this.#options.excludeBy) {
@@ -194,8 +192,6 @@ export default class PodiumClientResource {
194
192
  throw new TypeError(
195
193
  'you must pass an instance of "HttpIncoming" as the first argument to the .stream() method',
196
194
  );
197
- // set expectation to receive a response from this podlet.
198
- incoming.assets.addExpectedAsset(this.#options.name);
199
195
  const outgoing = new HttpOutgoing(this.#options, reqOptions, incoming);
200
196
  this.#state.setInitializingState();
201
197
  this.#resolver.resolve(outgoing);
package/lib/utils.js CHANGED
@@ -1,4 +1,4 @@
1
- import { AssetJs, AssetCss } from '@podium/utils';
1
+ import { AssetJs, AssetCss, uriRelativeToAbsolute } from '@podium/utils';
2
2
 
3
3
  /**
4
4
  * Checks if a header object has a header.
@@ -75,14 +75,17 @@ export const filterAssets = (scope, assets) => {
75
75
  };
76
76
 
77
77
  // parse link headers in AssetCss and AssetJs objects
78
- export const parseLinkHeaders = (headers) => {
78
+ export const parseLinkHeaders = (headers, manifestUri) => {
79
79
  const links = [];
80
80
 
81
81
  if (!headers) return links;
82
82
  headers.split(',').forEach((link) => {
83
83
  const parts = link.split(';');
84
84
  const [href, ...rest] = parts;
85
- const value = href.replace(/<|>|"/g, '').trim();
85
+ const value = uriRelativeToAbsolute(
86
+ href.replace(/<|>|"/g, '').trim(),
87
+ manifestUri,
88
+ );
86
89
 
87
90
  const asset = { value };
88
91
  for (const key of rest) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@podium/client",
3
- "version": "5.3.0-next.1",
3
+ "version": "5.3.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -28,6 +28,60 @@
28
28
  ],
29
29
  "main": "./lib/client.js",
30
30
  "types": "./types/client.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./types/client.js",
34
+ "default": "./lib/client.js"
35
+ },
36
+ "./lib/client.js": {
37
+ "types": "./types/client.js",
38
+ "default": "./lib/client.js"
39
+ },
40
+ "./lib/http-outgoing.js": {
41
+ "types": "./types/http-outgoing.js",
42
+ "default": "./lib/http-outgoing.js"
43
+ },
44
+ "./lib/http.js": {
45
+ "types": "./types/http.js",
46
+ "default": "./lib/http.js"
47
+ },
48
+ "./lib/resolver.cache.js": {
49
+ "types": "./types/resolver.cache.js",
50
+ "default": "./lib/resolver.cache.js"
51
+ },
52
+ "./lib/resolver.content.js": {
53
+ "types": "./types/resolver.content.js",
54
+ "default": "./lib/resolver.content.js"
55
+ },
56
+ "./lib/resolver.fallback.js": {
57
+ "types": "./types/resolver.fallback.js",
58
+ "default": "./lib/resolver.fallback.js"
59
+ },
60
+ "./lib/resolver.js": {
61
+ "types": "./types/resolver.js",
62
+ "default": "./lib/resolver.js"
63
+ },
64
+ "./lib/resolver.manifest.js": {
65
+ "types": "./types/resolver.manifest.js",
66
+ "default": "./lib/resolver.manifest.js"
67
+ },
68
+ "./lib/resource.js": {
69
+ "types": "./types/resource.js",
70
+ "default": "./lib/resource.js"
71
+ },
72
+ "./lib/response.js": {
73
+ "types": "./types/response.js",
74
+ "default": "./lib/response.js"
75
+ },
76
+ "./lib/state.js": {
77
+ "types": "./types/state.js",
78
+ "default": "./lib/state.js"
79
+ },
80
+ "./lib/utils.js": {
81
+ "types": "./types/utils.js",
82
+ "default": "./lib/utils.js"
83
+ }
84
+ },
31
85
  "scripts": {
32
86
  "lint": "eslint .",
33
87
  "lint:fix": "eslint --fix .",
@@ -38,17 +92,17 @@
38
92
  },
39
93
  "dependencies": {
40
94
  "@hapi/boom": "10.0.1",
41
- "@metrics/client": "2.5.3",
95
+ "@metrics/client": "2.5.4",
42
96
  "@podium/schemas": "5.1.0",
43
97
  "@podium/utils": "5.4.0",
44
98
  "abslog": "2.4.4",
45
99
  "http-cache-semantics": "^4.0.3",
46
100
  "lodash.clonedeep": "^4.5.0",
47
101
  "ttl-mem-cache": "4.1.0",
48
- "undici": "6.20.1"
102
+ "undici": "6.21.1"
49
103
  },
50
104
  "devDependencies": {
51
- "@podium/eslint-config": "1.0.0",
105
+ "@podium/eslint-config": "1.0.5",
52
106
  "@podium/semantic-release-config": "2.0.0",
53
107
  "@podium/test-utils": "3.1.0-next.5",
54
108
  "@podium/typescript-config": "1.0.0",
@@ -60,15 +114,15 @@
60
114
  "@sinonjs/fake-timers": "11.3.1",
61
115
  "@types/readable-stream": "4.0.18",
62
116
  "benchmark": "2.1.4",
63
- "eslint": "9.6.0",
117
+ "eslint": "9.17.0",
64
118
  "eslint-config-prettier": "9.1.0",
65
- "eslint-plugin-prettier": "5.1.3",
66
- "express": "4.21.1",
119
+ "eslint-plugin-prettier": "5.2.1",
120
+ "express": "4.21.2",
67
121
  "get-stream": "9.0.1",
68
122
  "http-proxy": "1.18.1",
69
123
  "is-stream": "4.0.1",
70
- "npm-run-all2": "6.2.3",
71
- "prettier": "3.3.2",
124
+ "npm-run-all2": "6.2.6",
125
+ "prettier": "3.4.2",
72
126
  "semantic-release": "24.1.2",
73
127
  "tap": "18.7.2",
74
128
  "typescript": "5.6.3",
@@ -124,15 +124,9 @@ export default class PodletClientHttpOutgoing extends PassThrough {
124
124
  * @see https://podium-lib.io/docs/podlet/fallbacks
125
125
  */
126
126
  get isFallback(): boolean;
127
- /**
128
- * Set the assetsReceived flag.
129
- * This is used to signal to the assets object that the client has received assets for a given podlet so it can track
130
- * which podlets have sent their assets.
131
- * @param {boolean} value
132
- */
133
- set assetsReceived(value: boolean);
134
- get assetsReceived(): boolean;
135
127
  pushFallback(): void;
128
+ hintsReceived: boolean;
129
+ writeEarlyHints(cb?: () => void): void;
136
130
  get [Symbol.toStringTag](): string;
137
131
  #private;
138
132
  }
@@ -2,6 +2,7 @@
2
2
  * @typedef {object} PodletClientResolverOptions
3
3
  * @property {string} clientName
4
4
  * @property {import('abslog').AbstractLoggerOptions} [logger]
5
+ * @property {boolean} [earlyHints]
5
6
  */
6
7
  export default class PodletClientResolver {
7
8
  /**
@@ -31,5 +32,6 @@ export default class PodletClientResolver {
31
32
  export type PodletClientResolverOptions = {
32
33
  clientName: string;
33
34
  logger?: import("abslog").AbstractLoggerOptions;
35
+ earlyHints?: boolean;
34
36
  };
35
37
  import Metrics from '@metrics/client';
package/types/utils.d.ts CHANGED
@@ -2,5 +2,5 @@ export function isHeaderDefined(headers: object, header: string): boolean;
2
2
  export function hasManifestChange(item: object): boolean;
3
3
  export function validateIncoming(incoming?: object): boolean;
4
4
  export function filterAssets<T extends import("@podium/utils").AssetCss | import("@podium/utils").AssetJs>(scope: "content" | "fallback" | "all", assets: T[]): T[];
5
- export function parseLinkHeaders(headers: any): any[];
5
+ export function parseLinkHeaders(headers: any, manifestUri: any): any[];
6
6
  export function toPreloadAssetObjects(assetObjects: any): any;