@podium/client 5.1.0-beta.1 → 5.1.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 +192 -3
- package/README.md +8 -6
- package/lib/client.js +79 -19
- package/lib/http-outgoing.js +106 -30
- package/lib/http.js +22 -17
- package/lib/resolver.cache.js +26 -3
- package/lib/resolver.content.js +54 -30
- package/lib/resolver.fallback.js +20 -0
- package/lib/resolver.js +40 -11
- package/lib/resolver.manifest.js +39 -14
- package/lib/resource.js +69 -9
- package/lib/response.js +17 -3
- package/lib/state.js +14 -1
- package/lib/utils.js +25 -27
- package/package.json +23 -21
- package/types/client.d.ts +120 -0
- package/types/http-outgoing.d.ts +177 -0
- package/types/http.d.ts +29 -0
- package/types/resolver.cache.d.ts +31 -0
- package/types/resolver.content.d.ts +29 -0
- package/types/resolver.d.ts +35 -0
- package/types/resolver.fallback.d.ts +29 -0
- package/types/resolver.manifest.d.ts +27 -0
- package/types/resource.d.ts +109 -0
- package/types/response.d.ts +47 -0
- package/types/state.d.ts +33 -0
- package/types/utils.d.ts +4 -0
- package/client.d.ts +0 -145
- package/dist/package.json +0 -3
package/lib/resolver.manifest.js
CHANGED
|
@@ -20,11 +20,24 @@ const pkg = JSON.parse(pkgJson);
|
|
|
20
20
|
|
|
21
21
|
const UA_STRING = `${pkg.name} ${pkg.version}`;
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {object} PodletClientManifestResolverOptions
|
|
25
|
+
* @property {string} clientName
|
|
26
|
+
* @property {import('abslog').AbstractLoggerOptions} [logger]
|
|
27
|
+
* @property {import('./http.js').default} [http]
|
|
28
|
+
*/
|
|
29
|
+
|
|
23
30
|
export default class PodletClientManifestResolver {
|
|
24
31
|
#log;
|
|
25
32
|
#metrics;
|
|
26
33
|
#histogram;
|
|
27
34
|
#http;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @constructor
|
|
38
|
+
* @param {PodletClientManifestResolverOptions} options
|
|
39
|
+
*/
|
|
40
|
+
// @ts-expect-error Deliberate default empty options for better error messages
|
|
28
41
|
constructor(options = {}) {
|
|
29
42
|
this.#http = options.http || new HTTP();
|
|
30
43
|
const name = options.clientName;
|
|
@@ -53,6 +66,10 @@ export default class PodletClientManifestResolver {
|
|
|
53
66
|
return this.#metrics;
|
|
54
67
|
}
|
|
55
68
|
|
|
69
|
+
/**
|
|
70
|
+
* @param {import('./http-outgoing.js').default} outgoing
|
|
71
|
+
* @returns {Promise<import('./http-outgoing.js').default>}
|
|
72
|
+
*/
|
|
56
73
|
async resolve(outgoing) {
|
|
57
74
|
if (outgoing.status === 'cached') {
|
|
58
75
|
return outgoing;
|
|
@@ -62,6 +79,7 @@ export default class PodletClientManifestResolver {
|
|
|
62
79
|
'User-Agent': UA_STRING,
|
|
63
80
|
};
|
|
64
81
|
|
|
82
|
+
/** @type {import('./http.js').PodiumHttpClientRequestOptions} */
|
|
65
83
|
const reqOptions = {
|
|
66
84
|
rejectUnauthorized: outgoing.rejectUnauthorized,
|
|
67
85
|
timeout: outgoing.timeout,
|
|
@@ -81,11 +99,11 @@ export default class PodletClientManifestResolver {
|
|
|
81
99
|
);
|
|
82
100
|
|
|
83
101
|
try {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
102
|
+
const response = await this.#http.request(
|
|
103
|
+
outgoing.manifestUri,
|
|
104
|
+
reqOptions,
|
|
105
|
+
);
|
|
106
|
+
const { statusCode, headers: hdrs, body } = response;
|
|
89
107
|
|
|
90
108
|
// Remote responds but with an http error code
|
|
91
109
|
const resError = statusCode !== 200;
|
|
@@ -105,7 +123,11 @@ export default class PodletClientManifestResolver {
|
|
|
105
123
|
return outgoing;
|
|
106
124
|
}
|
|
107
125
|
|
|
108
|
-
const manifest = validateManifest(
|
|
126
|
+
const manifest = validateManifest(
|
|
127
|
+
/** @type {import("@podium/schemas").PodletManifestSchema} */ (
|
|
128
|
+
await body.json()
|
|
129
|
+
),
|
|
130
|
+
);
|
|
109
131
|
|
|
110
132
|
// Manifest validation error
|
|
111
133
|
if (manifest.error) {
|
|
@@ -158,6 +180,7 @@ export default class PodletClientManifestResolver {
|
|
|
158
180
|
);
|
|
159
181
|
|
|
160
182
|
// Construct css and js objects with absolute URIs
|
|
183
|
+
// @ts-expect-error We assign here what will end up as PodletManifest as defined in http-outgoing.js
|
|
161
184
|
manifest.value.css = manifest.value.css.map((obj) => {
|
|
162
185
|
obj.value = utils.uriRelativeToAbsolute(
|
|
163
186
|
obj.value,
|
|
@@ -166,6 +189,7 @@ export default class PodletClientManifestResolver {
|
|
|
166
189
|
return new utils.AssetCss(obj);
|
|
167
190
|
});
|
|
168
191
|
|
|
192
|
+
// @ts-expect-error We assign here what will end up as PodletManifest as defined in http-outgoing.js
|
|
169
193
|
manifest.value.js = manifest.value.js.map((obj) => {
|
|
170
194
|
obj.value = utils.uriRelativeToAbsolute(
|
|
171
195
|
obj.value,
|
|
@@ -179,9 +203,9 @@ export default class PodletClientManifestResolver {
|
|
|
179
203
|
|
|
180
204
|
If .proxy is and Array, the podlet are of version 6 or newer. The Array is then an
|
|
181
205
|
Array of proxy Objects ({ name: 'foo', target: '/' }) which is the new structure
|
|
182
|
-
wanted from version 6 and onwards so leave this structure untouched.
|
|
206
|
+
wanted from version 6 and onwards so leave this structure untouched.
|
|
183
207
|
|
|
184
|
-
If .proxy is an Object, the podlet are of version 5 or older where the key of the
|
|
208
|
+
If .proxy is an Object, the podlet are of version 5 or older where the key of the
|
|
185
209
|
Object is the key of the target. If so, convert the structure to the new structure
|
|
186
210
|
consisting of an Array of proxy Objects.
|
|
187
211
|
|
|
@@ -191,12 +215,12 @@ export default class PodletClientManifestResolver {
|
|
|
191
215
|
if (Array.isArray(manifest.value.proxy)) {
|
|
192
216
|
// Build absolute proxy URIs
|
|
193
217
|
manifest.value.proxy = manifest.value.proxy.map((item) => ({
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
218
|
+
target: utils.uriRelativeToAbsolute(
|
|
219
|
+
item.target,
|
|
220
|
+
outgoing.manifestUri,
|
|
221
|
+
),
|
|
222
|
+
name: item.name,
|
|
223
|
+
}));
|
|
200
224
|
} else {
|
|
201
225
|
const proxies = [];
|
|
202
226
|
// Build absolute proxy URIs
|
|
@@ -216,6 +240,7 @@ export default class PodletClientManifestResolver {
|
|
|
216
240
|
END: Proxy backwards compabillity check and handling
|
|
217
241
|
*/
|
|
218
242
|
|
|
243
|
+
// @ts-expect-error We map to AssetCss and AssetJs above
|
|
219
244
|
outgoing.manifest = manifest.value;
|
|
220
245
|
outgoing.status = 'fresh';
|
|
221
246
|
|
package/lib/resource.js
CHANGED
|
@@ -11,11 +11,42 @@ import * as utils from './utils.js';
|
|
|
11
11
|
|
|
12
12
|
const inspect = Symbol.for('nodejs.util.inspect.custom');
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {object} RequestFilterOptions
|
|
16
|
+
* @property {string[]} [deviceType] List of values for the `x-podium-device-type` HTTP request header.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {object} PodiumClientResourceOptions
|
|
21
|
+
* @property {import('abslog').AbstractLoggerOptions} [logger]
|
|
22
|
+
* @property {string} clientName
|
|
23
|
+
* @property {string} name
|
|
24
|
+
* @property {string} uri To the podlet's `manifest.json`
|
|
25
|
+
* @property {number} timeout In milliseconds
|
|
26
|
+
* @property {number} maxAge
|
|
27
|
+
* @property {number} [retries]
|
|
28
|
+
* @property {boolean} [throwable]
|
|
29
|
+
* @property {boolean} [redirectable]
|
|
30
|
+
* @property {boolean} [rejectUnauthorized]
|
|
31
|
+
* @property {import('http').Agent} [httpAgent]
|
|
32
|
+
* @property {import('https').Agent} [httpsAgent]
|
|
33
|
+
* @property {RequestFilterOptions} [excludeBy] Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
|
|
34
|
+
* @property {RequestFilterOptions} [includeBy] Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
|
|
35
|
+
*/
|
|
36
|
+
|
|
14
37
|
export default class PodiumClientResource {
|
|
15
38
|
#resolver;
|
|
16
39
|
#options;
|
|
17
40
|
#metrics;
|
|
18
41
|
#state;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @constructor
|
|
45
|
+
* @param {import('ttl-mem-cache').default} registry
|
|
46
|
+
* @param {import('./state.js').default} state
|
|
47
|
+
* @param {PodiumClientResourceOptions} options
|
|
48
|
+
*/
|
|
49
|
+
// @ts-expect-error Deliberate for better error messages
|
|
19
50
|
constructor(registry, state, options = {}) {
|
|
20
51
|
assert(
|
|
21
52
|
registry,
|
|
@@ -56,7 +87,22 @@ export default class PodiumClientResource {
|
|
|
56
87
|
return this.#options.uri;
|
|
57
88
|
}
|
|
58
89
|
|
|
59
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Fetch the podlet's content, or fallback if the podlet is unavailable.
|
|
92
|
+
* The podlet response includes references to its CSS and JS assets which should be included in the final HTML document.
|
|
93
|
+
*
|
|
94
|
+
* @param {import('@podium/utils').HttpIncoming} incoming Instance of HttpIncoming
|
|
95
|
+
* @param {import('./http-outgoing.js').PodiumClientResourceOptions} [reqOptions={}] Optional parameters to the HTTP request, such as query parameters or HTTP request headers.
|
|
96
|
+
* @returns {Promise<import('./response.js').default>}
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```js
|
|
100
|
+
* const incoming = res.locals.podium; // Express server example
|
|
101
|
+
* const header = await headerPodlet.fetch(incoming);
|
|
102
|
+
* incoming.podlets = [header]; // Register the podlet's JS and CSS assets with the layout's HTML template
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
async fetch(incoming, reqOptions = {}) {
|
|
60
106
|
if (!utils.validateIncoming(incoming))
|
|
61
107
|
throw new TypeError(
|
|
62
108
|
'you must pass an instance of "HttpIncoming" as the first argument to the .fetch() method',
|
|
@@ -67,21 +113,21 @@ export default class PodiumClientResource {
|
|
|
67
113
|
/**
|
|
68
114
|
* @type {string[] | undefined}
|
|
69
115
|
*/
|
|
70
|
-
const
|
|
71
|
-
if (Array.isArray(
|
|
116
|
+
const excludedDeviceTypes = this.#options.excludeBy.deviceType;
|
|
117
|
+
if (Array.isArray(excludedDeviceTypes)) {
|
|
72
118
|
const deviceTypeHeader =
|
|
73
119
|
incoming.request.headers['x-podium-device-type'];
|
|
74
120
|
|
|
75
|
-
for (let i = 0; i <
|
|
121
|
+
for (let i = 0; i < excludedDeviceTypes.length; i += 1) {
|
|
76
122
|
const shouldSkip =
|
|
77
|
-
|
|
123
|
+
excludedDeviceTypes[i] === deviceTypeHeader;
|
|
78
124
|
if (shouldSkip) {
|
|
79
125
|
return new Response({
|
|
80
126
|
headers: {},
|
|
81
127
|
content: '',
|
|
82
128
|
css: [],
|
|
83
129
|
js: [],
|
|
84
|
-
redirect:
|
|
130
|
+
redirect: null,
|
|
85
131
|
});
|
|
86
132
|
}
|
|
87
133
|
}
|
|
@@ -107,7 +153,7 @@ export default class PodiumClientResource {
|
|
|
107
153
|
content: '',
|
|
108
154
|
css: [],
|
|
109
155
|
js: [],
|
|
110
|
-
redirect:
|
|
156
|
+
redirect: null,
|
|
111
157
|
});
|
|
112
158
|
}
|
|
113
159
|
}
|
|
@@ -143,7 +189,14 @@ export default class PodiumClientResource {
|
|
|
143
189
|
});
|
|
144
190
|
}
|
|
145
191
|
|
|
146
|
-
|
|
192
|
+
/**
|
|
193
|
+
* Stream the podlet's content, or fallback if the podlet is unavailable.
|
|
194
|
+
*
|
|
195
|
+
* @param {import('@podium/utils').HttpIncoming} incoming
|
|
196
|
+
* @param {import('./http-outgoing.js').PodiumClientResourceOptions} [reqOptions={}]
|
|
197
|
+
* @returns {import('./http-outgoing.js').default}
|
|
198
|
+
*/
|
|
199
|
+
stream(incoming, reqOptions = {}) {
|
|
147
200
|
if (!utils.validateIncoming(incoming))
|
|
148
201
|
throw new TypeError(
|
|
149
202
|
'you must pass an instance of "HttpIncoming" as the first argument to the .stream() method',
|
|
@@ -154,7 +207,14 @@ export default class PodiumClientResource {
|
|
|
154
207
|
return outgoing;
|
|
155
208
|
}
|
|
156
209
|
|
|
157
|
-
|
|
210
|
+
/**
|
|
211
|
+
* Refresh the podlet's manifest and fallback in the cache.
|
|
212
|
+
*
|
|
213
|
+
* @param {import('@podium/utils').HttpIncoming} [incoming]
|
|
214
|
+
* @param {import('./http-outgoing.js').PodiumClientResourceOptions} [reqOptions={}]
|
|
215
|
+
* @returns {Promise<boolean>} `true` if succesful
|
|
216
|
+
*/
|
|
217
|
+
refresh(incoming, reqOptions = {}) {
|
|
158
218
|
const outgoing = new HttpOutgoing(this.#options, reqOptions, incoming);
|
|
159
219
|
this.#state.setInitializingState();
|
|
160
220
|
return this.#resolver.refresh(outgoing).then((obj) => obj);
|
package/lib/response.js
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
const inspect = Symbol.for('nodejs.util.inspect.custom');
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {object} PodiumClientResponseOptions
|
|
5
|
+
* @property {string} [content]
|
|
6
|
+
* @property {object} [headers]
|
|
7
|
+
* @property {Array<import('@podium/utils').AssetJs>} [js]
|
|
8
|
+
* @property {Array<import('@podium/utils').AssetCss>} [css]
|
|
9
|
+
* @property {import('./http-outgoing.js').PodiumRedirect | null} [redirect]
|
|
10
|
+
*/
|
|
11
|
+
|
|
3
12
|
export default class PodiumClientResponse {
|
|
4
13
|
#redirect;
|
|
5
14
|
#content;
|
|
6
15
|
#headers;
|
|
7
16
|
#css;
|
|
8
17
|
#js;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @constructor
|
|
21
|
+
* @param {PodiumClientResponseOptions} options
|
|
22
|
+
*/
|
|
9
23
|
constructor({
|
|
10
24
|
content = '',
|
|
11
25
|
headers = {},
|
|
@@ -45,8 +59,8 @@ export default class PodiumClientResponse {
|
|
|
45
59
|
redirect: this.redirect,
|
|
46
60
|
content: this.content,
|
|
47
61
|
headers: this.headers,
|
|
48
|
-
css: this.css,
|
|
49
|
-
js: this.js,
|
|
62
|
+
css: this.css.map((a) => a.toJSON()),
|
|
63
|
+
js: this.js.map((a) => a.toJSON()),
|
|
50
64
|
};
|
|
51
65
|
}
|
|
52
66
|
|
|
@@ -71,4 +85,4 @@ export default class PodiumClientResponse {
|
|
|
71
85
|
get [Symbol.toStringTag]() {
|
|
72
86
|
return 'PodiumClientResponse';
|
|
73
87
|
}
|
|
74
|
-
}
|
|
88
|
+
}
|
package/lib/state.js
CHANGED
|
@@ -2,12 +2,25 @@ import EventEmitter from 'events';
|
|
|
2
2
|
|
|
3
3
|
const inspect = Symbol.for('nodejs.util.inspect.custom');
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {object} PodiumClientStateOptions
|
|
7
|
+
* @property {number} [resolveThreshold=10000]
|
|
8
|
+
* @property {number} [resolveMax=240000]
|
|
9
|
+
*/
|
|
10
|
+
|
|
5
11
|
export default class PodiumClientState extends EventEmitter {
|
|
12
|
+
/** @type {NodeJS.Timeout | undefined} */
|
|
6
13
|
#thresholdTimer;
|
|
7
14
|
#threshold;
|
|
15
|
+
/** @type {NodeJS.Timeout | undefined} */
|
|
8
16
|
#maxTimer;
|
|
17
|
+
/** @type {"instantiated" | "stable" | "initializing" | "unhealthy" | "unstable"} */
|
|
9
18
|
#state;
|
|
10
19
|
#max;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {PodiumClientStateOptions} [options]
|
|
23
|
+
*/
|
|
11
24
|
constructor({
|
|
12
25
|
resolveThreshold = 10 * 1000,
|
|
13
26
|
resolveMax = 4 * 60 * 1000,
|
|
@@ -113,4 +126,4 @@ export default class PodiumClientState extends EventEmitter {
|
|
|
113
126
|
get [Symbol.toStringTag]() {
|
|
114
127
|
return 'PodiumClientState';
|
|
115
128
|
}
|
|
116
|
-
}
|
|
129
|
+
}
|
package/lib/utils.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Checks if a header
|
|
3
|
-
* Will return true if the header exist and
|
|
4
|
-
*
|
|
2
|
+
* Checks if a header object has a header.
|
|
3
|
+
* Will return true if the header exist and is not an empty
|
|
4
|
+
* string or a string of whitespace.
|
|
5
5
|
*
|
|
6
|
-
* @param {
|
|
7
|
-
* @param {
|
|
8
|
-
*
|
|
9
|
-
* @returns {Boolean}
|
|
6
|
+
* @param {object} headers
|
|
7
|
+
* @param {string} header
|
|
8
|
+
* @returns {boolean}
|
|
10
9
|
*/
|
|
11
|
-
|
|
12
10
|
export const isHeaderDefined = (headers, header) => {
|
|
13
11
|
if (headers[header] === undefined || headers[header].trim() === '') {
|
|
14
12
|
return false;
|
|
@@ -21,31 +19,24 @@ export const isHeaderDefined = (headers, header) => {
|
|
|
21
19
|
* the changelog event object emitted from the internal
|
|
22
20
|
* cache registry.
|
|
23
21
|
*
|
|
24
|
-
* @param {
|
|
25
|
-
*
|
|
26
|
-
* @returns {Boolean}
|
|
22
|
+
* @param {object} item A changelog event object
|
|
23
|
+
* @returns {boolean}
|
|
27
24
|
*/
|
|
28
|
-
|
|
29
|
-
export const hasManifestChange = item => {
|
|
25
|
+
export const hasManifestChange = (item) => {
|
|
30
26
|
const oldVersion = item.oldVal ? item.oldVal.version : '';
|
|
31
27
|
const newVersion = item.newVal ? item.newVal.version : '';
|
|
32
28
|
return oldVersion !== newVersion;
|
|
33
29
|
};
|
|
34
30
|
|
|
35
|
-
|
|
36
31
|
/**
|
|
37
|
-
* Check if a value is a HttpIncoming object or not. If not, it
|
|
32
|
+
* Check if a value is a Podium HttpIncoming object or not. If not, it
|
|
38
33
|
* assume the incoming value is a context
|
|
39
34
|
*
|
|
40
|
-
* @param {
|
|
41
|
-
*
|
|
42
|
-
* @returns {HttpIncoming}
|
|
43
|
-
*/
|
|
44
|
-
export const validateIncoming = (incoming = {}) => (Object.prototype.toString.call(incoming) === '[object PodiumHttpIncoming]');
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @typedef {import("@podium/utils").AssetCss | import("@podium/utils").AssetJs} Asset
|
|
35
|
+
* @param {object} incoming
|
|
36
|
+
* @returns {boolean}
|
|
48
37
|
*/
|
|
38
|
+
export const validateIncoming = (incoming = {}) =>
|
|
39
|
+
Object.prototype.toString.call(incoming) === '[object PodiumHttpIncoming]';
|
|
49
40
|
|
|
50
41
|
/**
|
|
51
42
|
* Filter assets array based on scope.
|
|
@@ -53,9 +44,10 @@ export const validateIncoming = (incoming = {}) => (Object.prototype.toString.ca
|
|
|
53
44
|
* If scope property is set to "all", asset will be included.
|
|
54
45
|
* If scope is set to "content" and asset scope property is set to "fallback", asset will not be included
|
|
55
46
|
* If scope is set to "fallback" and asset scope property is set to "content", asset will not be included
|
|
47
|
+
* @template {import("@podium/utils").AssetCss | import("@podium/utils").AssetJs} T[]
|
|
56
48
|
* @param {"content" | "fallback" | "all"} scope
|
|
57
|
-
* @param {
|
|
58
|
-
* @returns {
|
|
49
|
+
* @param {T[]} assets
|
|
50
|
+
* @returns {T[]}
|
|
59
51
|
*
|
|
60
52
|
* @example
|
|
61
53
|
* ```
|
|
@@ -69,7 +61,13 @@ export const filterAssets = (scope, assets) => {
|
|
|
69
61
|
// if undefined or null, passthrough
|
|
70
62
|
if (!assets) return assets;
|
|
71
63
|
// if a non array value is given, throw
|
|
72
|
-
if (!Array.isArray(assets))
|
|
64
|
+
if (!Array.isArray(assets))
|
|
65
|
+
throw new TypeError(
|
|
66
|
+
`Asset definition must be of type array. Got ${typeof assets}`,
|
|
67
|
+
);
|
|
73
68
|
// filter the array of asset definitions to matchin scope or anything with all. Treat no scope the same as "all" for backwards compatibility.
|
|
74
|
-
return assets.filter(
|
|
69
|
+
return assets.filter(
|
|
70
|
+
(asset) =>
|
|
71
|
+
!asset.scope || asset.scope === scope || asset.scope === 'all',
|
|
72
|
+
);
|
|
75
73
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@podium/client",
|
|
3
|
-
"version": "5.1.0
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -21,51 +21,53 @@
|
|
|
21
21
|
"files": [
|
|
22
22
|
"package.json",
|
|
23
23
|
"CHANGELOG.md",
|
|
24
|
-
"client.d.ts",
|
|
25
24
|
"README.md",
|
|
26
25
|
"LICENSE",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
26
|
+
"lib",
|
|
27
|
+
"types"
|
|
29
28
|
],
|
|
30
29
|
"main": "./lib/client.js",
|
|
31
|
-
"types": "client.d.ts",
|
|
30
|
+
"types": "./types/client.d.ts",
|
|
32
31
|
"scripts": {
|
|
33
32
|
"lint": "eslint .",
|
|
34
33
|
"lint:fix": "eslint --fix .",
|
|
35
|
-
"test": "tap --disable-coverage --allow-empty-coverage"
|
|
34
|
+
"test": "tap tests/*.js --disable-coverage --allow-empty-coverage && tsc --project tsconfig.test.json",
|
|
35
|
+
"types": "tsc --declaration --emitDeclarationOnly"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@hapi/boom": "
|
|
38
|
+
"@hapi/boom": "10.0.1",
|
|
39
39
|
"@metrics/client": "2.5.2",
|
|
40
|
-
"@podium/schemas": "5.0.
|
|
41
|
-
"@podium/utils": "5.0.
|
|
42
|
-
"abslog": "2.4.
|
|
40
|
+
"@podium/schemas": "5.0.5",
|
|
41
|
+
"@podium/utils": "5.0.7",
|
|
42
|
+
"abslog": "2.4.4",
|
|
43
43
|
"http-cache-semantics": "^4.0.3",
|
|
44
44
|
"lodash.clonedeep": "^4.5.0",
|
|
45
45
|
"ttl-mem-cache": "4.1.0",
|
|
46
|
-
"undici": "
|
|
46
|
+
"undici": "6.19.2"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
+
"@babel/eslint-parser": "7.24.7",
|
|
49
50
|
"@podium/test-utils": "2.5.2",
|
|
50
51
|
"@semantic-release/changelog": "6.0.3",
|
|
51
52
|
"@semantic-release/git": "10.0.1",
|
|
52
|
-
"@
|
|
53
|
-
"@semantic-release/
|
|
54
|
-
"@semantic-release/
|
|
55
|
-
"@semantic-release/release-notes-generator": "12.1.0",
|
|
53
|
+
"@semantic-release/github": "10.0.6",
|
|
54
|
+
"@semantic-release/npm": "12.0.1",
|
|
55
|
+
"@semantic-release/release-notes-generator": "13.0.0",
|
|
56
56
|
"@sinonjs/fake-timers": "11.2.2",
|
|
57
|
+
"@types/readable-stream": "4.0.14",
|
|
57
58
|
"benchmark": "2.1.4",
|
|
58
59
|
"eslint": "8.57.0",
|
|
59
60
|
"eslint-config-airbnb-base": "15.0.0",
|
|
60
61
|
"eslint-config-prettier": "9.1.0",
|
|
61
62
|
"eslint-plugin-import": "2.29.1",
|
|
62
63
|
"eslint-plugin-prettier": "5.1.3",
|
|
63
|
-
"express": "4.
|
|
64
|
-
"get-stream": "
|
|
64
|
+
"express": "4.19.2",
|
|
65
|
+
"get-stream": "9.0.1",
|
|
65
66
|
"http-proxy": "1.18.1",
|
|
66
|
-
"is-stream": "
|
|
67
|
-
"prettier": "3.2
|
|
68
|
-
"semantic-release": "23.
|
|
69
|
-
"tap": "18.7.
|
|
67
|
+
"is-stream": "4.0.1",
|
|
68
|
+
"prettier": "3.3.2",
|
|
69
|
+
"semantic-release": "23.1.1",
|
|
70
|
+
"tap": "18.7.2",
|
|
71
|
+
"typescript": "5.4.5"
|
|
70
72
|
}
|
|
71
73
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {import('./resource.js').default} PodiumClientResource
|
|
4
|
+
* @typedef {import('./resource.js').PodiumClientResourceOptions} PodiumClientResourceOptions
|
|
5
|
+
* @typedef {import('./response.js').default} PodiumClientResponse
|
|
6
|
+
* @typedef {import('./http-outgoing.js').PodiumRedirect} PodiumRedirect
|
|
7
|
+
* @typedef {import('@podium/schemas').PodletManifestSchema} PodletManifest
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {object} PodiumClientOptions
|
|
11
|
+
* @property {string} name
|
|
12
|
+
* @property {import('abslog').AbstractLoggerOptions} [logger]
|
|
13
|
+
* @property {number} [retries=4]
|
|
14
|
+
* @property {number} [timeout=1000] In milliseconds
|
|
15
|
+
* @property {number} [maxAge=Infinity]
|
|
16
|
+
* @property {boolean} [rejectUnauthorized=true]
|
|
17
|
+
* @property {number} [resolveThreshold]
|
|
18
|
+
* @property {number} [resolveMax]
|
|
19
|
+
* @property {import('http').Agent} [httpAgent]
|
|
20
|
+
* @property {import('https').Agent} [httpsAgent]
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {object} RegisterOptions
|
|
24
|
+
* @property {string} name A unique name for the podlet
|
|
25
|
+
* @property {string} uri URL to the podlet's `manifest.json`
|
|
26
|
+
* @property {number} [retries=4] Number of retries before serving fallback
|
|
27
|
+
* @property {number} [timeout=1000] In milliseconds, the amount of time to wait before serving fallback.
|
|
28
|
+
* @property {boolean} [throwable=false] Set to `true` and surround `fetch` in `try/catch` to serve different content in case podlet is unavailable. Will not server fallback content.
|
|
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
|
+
* @property {import('./resource.js').RequestFilterOptions} [excludeBy] Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
|
|
31
|
+
* @property {import('./resource.js').RequestFilterOptions} [includeBy] Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
|
|
32
|
+
*/
|
|
33
|
+
export default class PodiumClient extends EventEmitter<[never]> {
|
|
34
|
+
/**
|
|
35
|
+
* @constructor
|
|
36
|
+
* @param {PodiumClientOptions} options
|
|
37
|
+
*/
|
|
38
|
+
constructor(options?: PodiumClientOptions);
|
|
39
|
+
get registry(): any;
|
|
40
|
+
get metrics(): Metrics;
|
|
41
|
+
get state(): "instantiated" | "stable" | "initializing" | "unhealthy" | "unstable";
|
|
42
|
+
/**
|
|
43
|
+
* Register a podlet so you can fetch its contents later with {@link PodiumClientResource.fetch}.
|
|
44
|
+
*
|
|
45
|
+
* @param {RegisterOptions} options
|
|
46
|
+
* @returns {PodiumClientResource}
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```js
|
|
50
|
+
* const headerPodlet = layout.client.register({
|
|
51
|
+
* name: 'header',
|
|
52
|
+
* uri: 'http://header/manifest.json',
|
|
53
|
+
* });
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
register(options: RegisterOptions): PodiumClientResource;
|
|
57
|
+
dump(): any;
|
|
58
|
+
load(dump: any): any;
|
|
59
|
+
/**
|
|
60
|
+
* Refreshes the cached podlet manifest for all {@link register}ed podlets.
|
|
61
|
+
*/
|
|
62
|
+
refreshManifests(): Promise<void>;
|
|
63
|
+
#private;
|
|
64
|
+
}
|
|
65
|
+
export type PodiumClientResource = import('./resource.js').default;
|
|
66
|
+
export type PodiumClientResourceOptions = import('./resource.js').PodiumClientResourceOptions;
|
|
67
|
+
export type PodiumClientResponse = import('./response.js').default;
|
|
68
|
+
export type PodiumRedirect = import('./http-outgoing.js').PodiumRedirect;
|
|
69
|
+
export type PodletManifest = import('@podium/schemas').PodletManifestSchema;
|
|
70
|
+
export type PodiumClientOptions = {
|
|
71
|
+
name: string;
|
|
72
|
+
logger?: import('abslog').AbstractLoggerOptions;
|
|
73
|
+
retries?: number;
|
|
74
|
+
/**
|
|
75
|
+
* In milliseconds
|
|
76
|
+
*/
|
|
77
|
+
timeout?: number;
|
|
78
|
+
maxAge?: number;
|
|
79
|
+
rejectUnauthorized?: boolean;
|
|
80
|
+
resolveThreshold?: number;
|
|
81
|
+
resolveMax?: number;
|
|
82
|
+
httpAgent?: import('http').Agent;
|
|
83
|
+
httpsAgent?: import('https').Agent;
|
|
84
|
+
};
|
|
85
|
+
export type RegisterOptions = {
|
|
86
|
+
/**
|
|
87
|
+
* A unique name for the podlet
|
|
88
|
+
*/
|
|
89
|
+
name: string;
|
|
90
|
+
/**
|
|
91
|
+
* URL to the podlet's `manifest.json`
|
|
92
|
+
*/
|
|
93
|
+
uri: string;
|
|
94
|
+
/**
|
|
95
|
+
* Number of retries before serving fallback
|
|
96
|
+
*/
|
|
97
|
+
retries?: number;
|
|
98
|
+
/**
|
|
99
|
+
* In milliseconds, the amount of time to wait before serving fallback.
|
|
100
|
+
*/
|
|
101
|
+
timeout?: number;
|
|
102
|
+
/**
|
|
103
|
+
* Set to `true` and surround `fetch` in `try/catch` to serve different content in case podlet is unavailable. Will not server fallback content.
|
|
104
|
+
*/
|
|
105
|
+
throwable?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* 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.
|
|
108
|
+
*/
|
|
109
|
+
redirectable?: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
|
|
112
|
+
*/
|
|
113
|
+
excludeBy?: import('./resource.js').RequestFilterOptions;
|
|
114
|
+
/**
|
|
115
|
+
* Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
|
|
116
|
+
*/
|
|
117
|
+
includeBy?: import('./resource.js').RequestFilterOptions;
|
|
118
|
+
};
|
|
119
|
+
import EventEmitter from 'events';
|
|
120
|
+
import Metrics from '@metrics/client';
|