@podium/client 5.2.0-next.2 → 5.2.0-next.3

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,3 +1,10 @@
1
+ # [5.2.0-next.3](https://github.com/podium-lib/client/compare/v5.2.0-next.2...v5.2.0-next.3) (2024-09-20)
2
+
3
+
4
+ ### Features
5
+
6
+ * write early hints to browser ([42513a3](https://github.com/podium-lib/client/commit/42513a38f5304648f7b2fb915995c66dc1dd3594))
7
+
1
8
  # [5.2.0-next.2](https://github.com/podium-lib/client/compare/v5.2.0-next.1...v5.2.0-next.2) (2024-09-16)
2
9
 
3
10
 
package/README.md CHANGED
@@ -152,6 +152,7 @@ The following values can be provided:
152
152
  - `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
153
  - `excludeBy` - {Object} - Lets you define a set of rules where a `fetch` call will not be resolved if it matches. - Optional.
154
154
  - `includeBy` - {Object} - Inverse of `excludeBy`. Setting both at the same time will throw. - Optional.
155
+ - `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
156
 
156
157
  ##### `excludeBy` and `includeBy`
157
158
 
package/lib/client.js CHANGED
@@ -62,6 +62,7 @@ const MAX_AGE = Infinity;
62
62
  * @property {boolean} [redirectable=false] Set to `true` to allow podlet to respond with a redirect. You need to look for the redirect response from the podlet and return a redirect response to the browser yourself.
63
63
  * @property {import('./resource.js').RequestFilterOptions} [excludeBy] Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
64
64
  * @property {import('./resource.js').RequestFilterOptions} [includeBy] Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
65
+ * @property {boolean} [earlyHints=true]
65
66
  */
66
67
 
67
68
  export default class PodiumClient extends EventEmitter {
@@ -212,6 +213,7 @@ export default class PodiumClient extends EventEmitter {
212
213
  httpAgent: this.#options.httpAgent,
213
214
  includeBy: this.#options.includeBy,
214
215
  excludeBy: this.#options.excludeBy,
216
+ earlyHints: true,
215
217
  ...options,
216
218
  };
217
219
 
@@ -1,5 +1,6 @@
1
1
  import { PassThrough } from 'stream';
2
2
  import assert from 'assert';
3
+ import { toPreloadAssetObjects } from './utils.js';
3
4
 
4
5
  /**
5
6
  * @typedef {object} PodiumClientHttpOutgoingOptions
@@ -338,6 +339,19 @@ export default class PodletClientHttpOutgoing extends PassThrough {
338
339
  this.#isFallback = true;
339
340
  }
340
341
 
342
+ writeEarlyHints(cb = () => {}) {
343
+ if (this.#incoming.response.writeEarlyHints) {
344
+ const preloads = toPreloadAssetObjects([
345
+ ...(this.js || []),
346
+ ...(this.css || []),
347
+ ]);
348
+ const link = preloads.map((preload) => preload.toHeader());
349
+ if (link.length) {
350
+ this.#incoming.response.writeEarlyHints({ link }, cb);
351
+ }
352
+ }
353
+ }
354
+
341
355
  get [Symbol.toStringTag]() {
342
356
  return 'PodletClientHttpOutgoing';
343
357
  }
@@ -27,6 +27,7 @@ const UA_STRING = `${pkg.name} ${pkg.version}`;
27
27
  * @property {string} clientName
28
28
  * @property {import('./http.js').default} [http]
29
29
  * @property {import('abslog').AbstractLoggerOptions} [logger]
30
+ * @property {boolean} [earlyHints]
30
31
  */
31
32
 
32
33
  export default class PodletClientContentResolver {
@@ -34,6 +35,7 @@ export default class PodletClientContentResolver {
34
35
  #metrics;
35
36
  #histogram;
36
37
  #http;
38
+ #earlyHints;
37
39
 
38
40
  /**
39
41
  * @constructor
@@ -44,6 +46,8 @@ export default class PodletClientContentResolver {
44
46
  this.#http = options.http || new HTTP();
45
47
  const name = options.clientName;
46
48
  this.#log = abslog(options.logger);
49
+ this.#earlyHints =
50
+ typeof options.earlyHints === 'boolean' ? options.earlyHints : true;
47
51
  this.#metrics = new Metrics();
48
52
  this.#histogram = this.#metrics.histogram({
49
53
  name: 'podium_client_resolver_content_resolve',
@@ -144,7 +148,7 @@ export default class PodletClientContentResolver {
144
148
  method: 'GET',
145
149
  query: outgoing.reqOptions.query,
146
150
  headers,
147
- onInfo({ statusCode, headers }) {
151
+ onInfo: ({ statusCode, headers }) => {
148
152
  if (statusCode === 103 && !hintsReceived) {
149
153
  const parsedAssetObjects = parseLinkHeaders(headers.link);
150
154
 
@@ -158,6 +162,8 @@ export default class PodletClientContentResolver {
158
162
  outgoing.js = filterAssets('content', scriptObjects);
159
163
  // set the content css asset objects
160
164
  outgoing.css = filterAssets('content', styleObjects);
165
+ // write the early hints to the browser
166
+ if (this.#earlyHints) outgoing.writeEarlyHints();
161
167
 
162
168
  hintsReceived = true;
163
169
  }
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 {
@@ -41,7 +42,10 @@ export default class PodletClientResolver {
41
42
  http,
42
43
  });
43
44
  this.#fallback = new Fallback({ ...options, http });
44
- this.#content = new Content({ ...options, http });
45
+ this.#content = new Content({
46
+ ...options,
47
+ http,
48
+ });
45
49
  this.#metrics = new Metrics();
46
50
 
47
51
  this.#metrics.on('error', (error) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@podium/client",
3
- "version": "5.2.0-next.2",
3
+ "version": "5.2.0-next.3",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -40,7 +40,7 @@
40
40
  "@hapi/boom": "10.0.1",
41
41
  "@metrics/client": "2.5.3",
42
42
  "@podium/schemas": "5.0.6",
43
- "@podium/utils": "5.2.0",
43
+ "@podium/utils": "5.2.1",
44
44
  "abslog": "2.4.4",
45
45
  "http-cache-semantics": "^4.0.3",
46
46
  "lodash.clonedeep": "^4.5.0",
package/types/client.d.ts CHANGED
@@ -29,6 +29,7 @@
29
29
  * @property {boolean} [redirectable=false] Set to `true` to allow podlet to respond with a redirect. You need to look for the redirect response from the podlet and return a redirect response to the browser yourself.
30
30
  * @property {import('./resource.js').RequestFilterOptions} [excludeBy] Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
31
31
  * @property {import('./resource.js').RequestFilterOptions} [includeBy] Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
32
+ * @property {boolean} [earlyHints=true]
32
33
  */
33
34
  export default class PodiumClient extends EventEmitter<[never]> {
34
35
  /**
@@ -115,6 +116,7 @@ export type RegisterOptions = {
115
116
  * Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
116
117
  */
117
118
  includeBy?: import('./resource.js').RequestFilterOptions;
119
+ earlyHints?: boolean;
118
120
  };
119
121
  import EventEmitter from 'events';
120
122
  import Metrics from '@metrics/client';
@@ -125,6 +125,7 @@ export default class PodletClientHttpOutgoing extends PassThrough {
125
125
  */
126
126
  get isFallback(): boolean;
127
127
  pushFallback(): void;
128
+ writeEarlyHints(cb?: () => void): void;
128
129
  get [Symbol.toStringTag](): string;
129
130
  #private;
130
131
  }
@@ -3,6 +3,7 @@
3
3
  * @property {string} clientName
4
4
  * @property {import('./http.js').default} [http]
5
5
  * @property {import('abslog').AbstractLoggerOptions} [logger]
6
+ * @property {boolean} [earlyHints]
6
7
  */
7
8
  export default class PodletClientContentResolver {
8
9
  /**
@@ -25,5 +26,6 @@ export type PodletClientContentResolverOptions = {
25
26
  clientName: string;
26
27
  http?: import('./http.js').default;
27
28
  logger?: import('abslog').AbstractLoggerOptions;
29
+ earlyHints?: boolean;
28
30
  };
29
31
  import Metrics from '@metrics/client';
@@ -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
@@ -1,8 +1,8 @@
1
1
  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
- export function filterAssets<T extends AssetJs | AssetCss>(scope: "content" | "fallback" | "all", assets: T[]): T[];
4
+ export function filterAssets<T extends AssetCss | AssetJs>(scope: "content" | "fallback" | "all", assets: T[]): T[];
5
5
  export function parseLinkHeaders(headers: any): any[];
6
6
  export function toPreloadAssetObjects(assetObjects: any): any;
7
- import { AssetJs } from '@podium/utils';
8
7
  import { AssetCss } from '@podium/utils';
8
+ import { AssetJs } from '@podium/utils';