@podium/client 5.2.0-next.2 → 5.2.0-next.4
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 +19 -0
- package/README.md +1 -0
- package/lib/client.js +2 -0
- package/lib/http-outgoing.js +31 -0
- package/lib/resolver.content.js +9 -5
- package/lib/resolver.js +5 -1
- package/lib/resource.js +4 -0
- package/package.json +2 -2
- package/types/client.d.ts +2 -0
- package/types/http-outgoing.d.ts +3 -0
- package/types/resolver.content.d.ts +2 -0
- package/types/resolver.d.ts +2 -0
- package/types/utils.d.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
# [5.2.0-next.4](https://github.com/podium-lib/client/compare/v5.2.0-next.3...v5.2.0-next.4) (2024-09-24)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* update @podium/utils to support hints asset collection ([fe97c44](https://github.com/podium-lib/client/commit/fe97c44bc6d29d1e45d335235cd3b1ef6eeafeaa))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* keep track of which resources have emitted early hints and emit complete event once all resources have emitted ([7cf916a](https://github.com/podium-lib/client/commit/7cf916ab286a3c6cb8fbfdae46634b58f2be256f))
|
|
12
|
+
|
|
13
|
+
# [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)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* write early hints to browser ([42513a3](https://github.com/podium-lib/client/commit/42513a38f5304648f7b2fb915995c66dc1dd3594))
|
|
19
|
+
|
|
1
20
|
# [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
21
|
|
|
3
22
|
|
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
|
|
package/lib/http-outgoing.js
CHANGED
|
@@ -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
|
|
@@ -69,6 +70,7 @@ export default class PodletClientHttpOutgoing extends PassThrough {
|
|
|
69
70
|
#uri;
|
|
70
71
|
#js;
|
|
71
72
|
#css;
|
|
73
|
+
#hintsReceived = false;
|
|
72
74
|
|
|
73
75
|
/**
|
|
74
76
|
* @constructor
|
|
@@ -299,6 +301,20 @@ export default class PodletClientHttpOutgoing extends PassThrough {
|
|
|
299
301
|
this.#redirect = value;
|
|
300
302
|
}
|
|
301
303
|
|
|
304
|
+
get hintsReceived() {
|
|
305
|
+
return this.#hintsReceived;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
set hintsReceived(value) {
|
|
309
|
+
this.#hintsReceived = value;
|
|
310
|
+
if (this.#hintsReceived) {
|
|
311
|
+
this.#incoming?.hints?.addReceivedHint(this.#name, {
|
|
312
|
+
js: this.js,
|
|
313
|
+
css: this.css,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
302
318
|
/**
|
|
303
319
|
* Whether the podlet can signal redirects to the layout.
|
|
304
320
|
*
|
|
@@ -336,6 +352,21 @@ export default class PodletClientHttpOutgoing extends PassThrough {
|
|
|
336
352
|
this.css = this.#manifest._css;
|
|
337
353
|
this.push(null);
|
|
338
354
|
this.#isFallback = true;
|
|
355
|
+
// assume the hints from the podlet have failed and fallback assets will be used
|
|
356
|
+
this.hintsReceived = true;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
writeEarlyHints(cb = () => {}) {
|
|
360
|
+
if (this.#incoming.response.writeEarlyHints) {
|
|
361
|
+
const preloads = toPreloadAssetObjects([
|
|
362
|
+
...(this.js || []),
|
|
363
|
+
...(this.css || []),
|
|
364
|
+
]);
|
|
365
|
+
const link = preloads.map((preload) => preload.toHeader());
|
|
366
|
+
if (link.length) {
|
|
367
|
+
this.#incoming.response.writeEarlyHints({ link }, cb);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
339
370
|
}
|
|
340
371
|
|
|
341
372
|
get [Symbol.toStringTag]() {
|
package/lib/resolver.content.js
CHANGED
|
@@ -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',
|
|
@@ -135,8 +139,6 @@ export default class PodletClientContentResolver {
|
|
|
135
139
|
outgoing.contentUri,
|
|
136
140
|
);
|
|
137
141
|
|
|
138
|
-
let hintsReceived = false;
|
|
139
|
-
|
|
140
142
|
/** @type {import('./http.js').PodiumHttpClientRequestOptions} */
|
|
141
143
|
const reqOptions = {
|
|
142
144
|
rejectUnauthorized: outgoing.rejectUnauthorized,
|
|
@@ -144,8 +146,8 @@ export default class PodletClientContentResolver {
|
|
|
144
146
|
method: 'GET',
|
|
145
147
|
query: outgoing.reqOptions.query,
|
|
146
148
|
headers,
|
|
147
|
-
onInfo({ statusCode, headers }) {
|
|
148
|
-
if (statusCode === 103 && !hintsReceived) {
|
|
149
|
+
onInfo: ({ statusCode, headers }) => {
|
|
150
|
+
if (statusCode === 103 && !outgoing.hintsReceived) {
|
|
149
151
|
const parsedAssetObjects = parseLinkHeaders(headers.link);
|
|
150
152
|
|
|
151
153
|
const scriptObjects = parsedAssetObjects.filter(
|
|
@@ -158,8 +160,10 @@ export default class PodletClientContentResolver {
|
|
|
158
160
|
outgoing.js = filterAssets('content', scriptObjects);
|
|
159
161
|
// set the content css asset objects
|
|
160
162
|
outgoing.css = filterAssets('content', styleObjects);
|
|
163
|
+
// write the early hints to the browser
|
|
164
|
+
if (this.#earlyHints) outgoing.writeEarlyHints();
|
|
161
165
|
|
|
162
|
-
hintsReceived = true;
|
|
166
|
+
outgoing.hintsReceived = true;
|
|
163
167
|
}
|
|
164
168
|
},
|
|
165
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({
|
|
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/lib/resource.js
CHANGED
|
@@ -105,6 +105,10 @@ 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
|
+
// add the name of this resource as expecting a hint to be received
|
|
109
|
+
// we use this to track across resources and emit a hint completion event once
|
|
110
|
+
// all hints from all resources have been received.
|
|
111
|
+
incoming.hints.addExpectedHint(this.#options.name);
|
|
108
112
|
const outgoing = new HttpOutgoing(this.#options, reqOptions, incoming);
|
|
109
113
|
|
|
110
114
|
if (this.#options.excludeBy) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@podium/client",
|
|
3
|
-
"version": "5.2.0-next.
|
|
3
|
+
"version": "5.2.0-next.4",
|
|
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.
|
|
43
|
+
"@podium/utils": "5.3.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';
|
package/types/http-outgoing.d.ts
CHANGED
|
@@ -105,6 +105,8 @@ export default class PodletClientHttpOutgoing extends PassThrough {
|
|
|
105
105
|
* @see https://podium-lib.io/docs/layout/handling_redirects
|
|
106
106
|
*/
|
|
107
107
|
get redirect(): PodiumRedirect;
|
|
108
|
+
set hintsReceived(value: boolean);
|
|
109
|
+
get hintsReceived(): boolean;
|
|
108
110
|
set redirectable(value: boolean);
|
|
109
111
|
/**
|
|
110
112
|
* Whether the podlet can signal redirects to the layout.
|
|
@@ -125,6 +127,7 @@ export default class PodletClientHttpOutgoing extends PassThrough {
|
|
|
125
127
|
*/
|
|
126
128
|
get isFallback(): boolean;
|
|
127
129
|
pushFallback(): void;
|
|
130
|
+
writeEarlyHints(cb?: () => void): void;
|
|
128
131
|
get [Symbol.toStringTag](): string;
|
|
129
132
|
#private;
|
|
130
133
|
}
|
|
@@ -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';
|
package/types/resolver.d.ts
CHANGED
|
@@ -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
|
|
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';
|