@podium/client
Advanced tools
Comparing version 5.1.10 to 5.2.0-next.1
@@ -1,8 +0,22 @@ | ||
## [5.1.10](https://github.com/podium-lib/client/compare/v5.1.9...v5.1.10) (2024-09-06) | ||
# [5.2.0-next.1](https://github.com/podium-lib/client/compare/v5.1.10-next.2...v5.2.0-next.1) (2024-09-10) | ||
### Features | ||
* read assets from podlets using 103 early hints ([64e4b27](https://github.com/podium-lib/client/commit/64e4b27773b87c220bcab1028ecdda82be2aa9fc)) | ||
## [5.1.10-next.2](https://github.com/podium-lib/client/compare/v5.1.10-next.1...v5.1.10-next.2) (2024-08-26) | ||
### Bug Fixes | ||
* **deps:** update dependency @podium/utils to v5.2.0 ([f7d4675](https://github.com/podium-lib/client/commit/f7d4675e6e2b9f2fba26cbd164d3e7a73b9c132e)) | ||
* use AbortController instead of AbortSignal to avoid unhandled exception ([#412](https://github.com/podium-lib/client/issues/412)) ([87f5ffe](https://github.com/podium-lib/client/commit/87f5ffe553aa49189658a9be0e19d1323878a55a)) | ||
## [5.1.10-next.1](https://github.com/podium-lib/client/compare/v5.1.9...v5.1.10-next.1) (2024-08-22) | ||
### Bug Fixes | ||
* use AbortSignal to ensure timeouts are respected ([08899d9](https://github.com/podium-lib/client/commit/08899d974246037cb1893a5b6d06bd6df58815e2)) | ||
## [5.1.9](https://github.com/podium-lib/client/compare/v5.1.8...v5.1.9) (2024-08-19) | ||
@@ -9,0 +23,0 @@ |
@@ -70,2 +70,4 @@ import { PassThrough } from 'stream'; | ||
#uri; | ||
#js; | ||
#css; | ||
@@ -124,2 +126,4 @@ /** | ||
_fallback: '', | ||
_js: [], | ||
_css: [], | ||
}; | ||
@@ -151,2 +155,20 @@ | ||
get js() { | ||
// return the internal js value or, fallback to the manifest for backwards compatibility | ||
return this.#js || this.#manifest.js; | ||
} | ||
set js(value) { | ||
this.#js = value; | ||
} | ||
get css() { | ||
// return the internal css value or, fallback to the manifest for backwards compatibility | ||
return this.#css || this.#manifest.css; | ||
} | ||
set css(value) { | ||
this.#css = value; | ||
} | ||
get rejectUnauthorized() { | ||
@@ -314,2 +336,6 @@ return this.#rejectUnauthorized; | ||
this.push(this.#manifest._fallback); | ||
// @ts-expect-error Internal property | ||
this.js = this.#manifest._js; | ||
// @ts-expect-error Internal property | ||
this.css = this.#manifest._css; | ||
this.push(null); | ||
@@ -316,0 +342,0 @@ this.#isFallback = true; |
@@ -1,2 +0,2 @@ | ||
import { request } from 'undici'; | ||
import { request as undiciRequest } from 'undici'; | ||
@@ -10,8 +10,12 @@ /** | ||
* @property {number} [timeout] | ||
* @property {number} [bodyTimeout] | ||
* @property {object} [query] | ||
* @property {import('http').IncomingHttpHeaders} [headers] | ||
* @property {(info: { statusCode: number; headers: Record<string, string | string[]>; }) => void} [onInfo] | ||
*/ | ||
export default class HTTP { | ||
constructor(requestFn = undiciRequest) { | ||
this.requestFn = requestFn; | ||
} | ||
/** | ||
@@ -23,8 +27,21 @@ * @param {string} url | ||
async request(url, options) { | ||
const { statusCode, headers, body } = await request( | ||
new URL(url), | ||
options, | ||
); | ||
return { statusCode, headers, body }; | ||
const abortController = new AbortController(); | ||
const timeoutId = setTimeout(() => { | ||
abortController.abort(); | ||
}, options.timeout || 1000); | ||
try { | ||
const { statusCode, headers, body } = await this.requestFn( | ||
new URL(url), | ||
{ | ||
...options, | ||
signal: abortController.signal, | ||
}, | ||
); | ||
return { statusCode, headers, body }; | ||
} finally { | ||
clearTimeout(timeoutId); | ||
} | ||
} | ||
} |
@@ -12,2 +12,4 @@ import { pipeline } from 'stream'; | ||
import HTTP from './http.js'; | ||
import { parseLinkHeaders, filterAssets } from './utils.js'; | ||
import { AssetJs, AssetCss } from '@podium/utils'; | ||
@@ -137,9 +139,29 @@ const currentDirectory = dirname(fileURLToPath(import.meta.url)); | ||
let hintsReceived = false; | ||
/** @type {import('./http.js').PodiumHttpClientRequestOptions} */ | ||
const reqOptions = { | ||
rejectUnauthorized: outgoing.rejectUnauthorized, | ||
bodyTimeout: outgoing.timeout, | ||
timeout: outgoing.timeout, | ||
method: 'GET', | ||
query: outgoing.reqOptions.query, | ||
headers, | ||
onInfo({ statusCode, headers }) { | ||
if (statusCode === 103 && !hintsReceived) { | ||
const parsedAssetObjects = parseLinkHeaders(headers.link); | ||
const scriptObjects = parsedAssetObjects.filter( | ||
(asset) => asset instanceof AssetJs, | ||
); | ||
const styleObjects = parsedAssetObjects.filter( | ||
(asset) => asset instanceof AssetCss, | ||
); | ||
// set the content js asset objects | ||
outgoing.js = filterAssets('content', scriptObjects); | ||
// set the content css asset objects | ||
outgoing.css = filterAssets('content', styleObjects); | ||
hintsReceived = true; | ||
} | ||
}, | ||
}; | ||
@@ -272,2 +294,3 @@ | ||
// @ts-ignore | ||
pipeline([body, outgoing], (err) => { | ||
@@ -311,4 +334,4 @@ if (err) { | ||
new Response({ | ||
js: utils.filterAssets('fallback', outgoing.manifest.js), | ||
css: utils.filterAssets('fallback', outgoing.manifest.css), | ||
js: utils.filterAssets('fallback', outgoing.js), | ||
css: utils.filterAssets('fallback', outgoing.css), | ||
}), | ||
@@ -315,0 +338,0 @@ ); |
@@ -7,2 +7,4 @@ import abslog from 'abslog'; | ||
import HTTP from './http.js'; | ||
import { parseLinkHeaders, filterAssets } from './utils.js'; | ||
import { AssetJs, AssetCss } from '@podium/utils'; | ||
@@ -98,2 +100,4 @@ const currentDirectory = dirname(fileURLToPath(import.meta.url)); | ||
let hintsReceived = false; | ||
/** @type {import('./http.js').PodiumHttpClientRequestOptions} */ | ||
@@ -105,2 +109,28 @@ const reqOptions = { | ||
headers, | ||
onInfo({ statusCode, headers }) { | ||
if (statusCode === 103 && !hintsReceived) { | ||
const parsedAssetObjects = parseLinkHeaders(headers.link); | ||
const scriptObjects = parsedAssetObjects.filter( | ||
(asset) => asset instanceof AssetJs, | ||
); | ||
const styleObjects = parsedAssetObjects.filter( | ||
(asset) => asset instanceof AssetCss, | ||
); | ||
// set the content js asset fallback objects | ||
// @ts-expect-error internal property | ||
outgoing.manifest._js = filterAssets( | ||
'fallback', | ||
scriptObjects, | ||
); | ||
// set the fallback css asset fallback objects | ||
// @ts-expect-error internal property | ||
outgoing.manifest._css = filterAssets( | ||
'fallback', | ||
styleObjects, | ||
); | ||
hintsReceived = true; | ||
} | ||
}, | ||
}; | ||
@@ -107,0 +137,0 @@ |
@@ -162,3 +162,3 @@ import Metrics from '@metrics/client'; | ||
const { manifest, headers, redirect, isFallback } = | ||
const { headers, redirect, isFallback } = | ||
await this.#resolver.resolve(outgoing); | ||
@@ -181,7 +181,7 @@ | ||
isFallback ? 'fallback' : 'content', | ||
manifest.css, | ||
outgoing.css, | ||
), | ||
js: utils.filterAssets( | ||
isFallback ? 'fallback' : 'content', | ||
manifest.js, | ||
outgoing.js, | ||
), | ||
@@ -188,0 +188,0 @@ redirect, |
@@ -0,1 +1,3 @@ | ||
import { AssetJs, AssetCss } from '@podium/utils'; | ||
/** | ||
@@ -74,1 +76,43 @@ * Checks if a header object has a header. | ||
}; | ||
// parse link headers in AssetCss and AssetJs objects | ||
export const parseLinkHeaders = (headers) => { | ||
const links = []; | ||
if (!headers) return links; | ||
headers.split(',').forEach((link) => { | ||
const parts = link.split(';'); | ||
const [href, ...rest] = parts; | ||
const value = href.replace(/<|>|"/g, '').trim(); | ||
const asset = { value }; | ||
for (const key of rest) { | ||
const [keyName, keyValue] = key.split('='); | ||
asset[keyName.trim()] = keyValue.trim(); | ||
} | ||
if (asset['asset-type'] === 'script') { | ||
links.push(new AssetJs(asset)); | ||
} | ||
if (asset['asset-type'] === 'style') { | ||
links.push(new AssetCss(asset)); | ||
} | ||
}); | ||
return links; | ||
}; | ||
export const toPreloadAssetObjects = (assetObjects) => { | ||
return assetObjects.map((assetObject) => { | ||
return new AssetCss({ | ||
type: | ||
assetObject instanceof AssetJs | ||
? 'application/javascript' | ||
: 'text/css', | ||
crossorigin: !!assetObject.crossorigin, | ||
rel: 'preload', | ||
as: assetObject instanceof AssetJs ? 'script' : 'style', | ||
media: assetObject.media || '', | ||
value: assetObject.value.trim(), | ||
}); | ||
}); | ||
}; |
{ | ||
"name": "@podium/client", | ||
"version": "5.1.10", | ||
"version": "5.2.0-next.1", | ||
"type": "module", | ||
@@ -51,9 +51,9 @@ "license": "MIT", | ||
"devDependencies": { | ||
"@podium/test-utils": "2.5.2", | ||
"@podium/test-utils": "3.1.0-next.3", | ||
"@semantic-release/changelog": "6.0.3", | ||
"@semantic-release/git": "10.0.1", | ||
"@semantic-release/github": "10.1.7", | ||
"@semantic-release/github": "10.0.6", | ||
"@semantic-release/npm": "12.0.1", | ||
"@semantic-release/release-notes-generator": "13.0.0", | ||
"@sinonjs/fake-timers": "11.3.1", | ||
"@sinonjs/fake-timers": "11.2.2", | ||
"@types/readable-stream": "4.0.15", | ||
@@ -66,6 +66,6 @@ "benchmark": "2.1.4", | ||
"get-stream": "9.0.1", | ||
"globals": "15.9.0", | ||
"globals": "15.8.0", | ||
"http-proxy": "1.18.1", | ||
"is-stream": "4.0.1", | ||
"npm-run-all2": "5.0.2", | ||
"npm-run-all2": "5.0.0", | ||
"prettier": "3.3.2", | ||
@@ -72,0 +72,0 @@ "semantic-release": "23.1.1", |
@@ -49,2 +49,6 @@ /** | ||
constructor(options?: PodiumClientHttpOutgoingOptions, reqOptions?: PodiumClientResourceOptions, incoming?: import('@podium/utils').HttpIncoming); | ||
set js(value: any); | ||
get js(): any; | ||
set css(value: any); | ||
get css(): any; | ||
get rejectUnauthorized(): boolean; | ||
@@ -51,0 +55,0 @@ get reqOptions(): { |
@@ -8,7 +8,9 @@ /** | ||
* @property {number} [timeout] | ||
* @property {number} [bodyTimeout] | ||
* @property {object} [query] | ||
* @property {import('http').IncomingHttpHeaders} [headers] | ||
* @property {(info: { statusCode: number; headers: Record<string, string | string[]>; }) => void} [onInfo] | ||
*/ | ||
export default class HTTP { | ||
constructor(requestFn?: typeof undiciRequest); | ||
requestFn: typeof undiciRequest; | ||
/** | ||
@@ -27,5 +29,9 @@ * @param {string} url | ||
timeout?: number; | ||
bodyTimeout?: number; | ||
query?: object; | ||
headers?: import('http').IncomingHttpHeaders; | ||
onInfo?: (info: { | ||
statusCode: number; | ||
headers: Record<string, string | string[]>; | ||
}) => void; | ||
}; | ||
import { request as undiciRequest } from 'undici'; |
export function isHeaderDefined(headers: object, header: string): boolean; | ||
export function hasManifestChange(item: object): boolean; | ||
export function validateIncoming(incoming?: object): boolean; | ||
export function filterAssets<T extends import("@podium/utils").AssetJs | import("@podium/utils").AssetCss>(scope: "content" | "fallback" | "all", assets: T[]): T[]; | ||
export function filterAssets<T extends AssetJs | AssetCss>(scope: "content" | "fallback" | "all", assets: T[]): T[]; | ||
export function parseLinkHeaders(headers: any): any[]; | ||
export function toPreloadAssetObjects(assetObjects: any): any; | ||
import { AssetJs } from '@podium/utils'; | ||
import { AssetCss } from '@podium/utils'; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
177623
2611
1