@podium/client 5.0.34 → 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 +7 -0
- package/README.md +29 -2
- package/lib/client.js +13 -0
- package/lib/resource.js +57 -0
- package/package.json +4 -4
- package/types/client.d.ts +10 -0
- package/types/resource.d.ts +20 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [5.1.0](https://github.com/podium-lib/client/compare/v5.0.34...v5.1.0) (2024-07-02)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* conditional fetch by deviceType header ([de4e9c4](https://github.com/podium-lib/client/commit/de4e9c47eea6baf07f2dc72465699a080b6aace1))
|
|
7
|
+
|
|
1
8
|
## [5.0.34](https://github.com/podium-lib/client/compare/v5.0.33...v5.0.34) (2024-06-18)
|
|
2
9
|
|
|
3
10
|
|
package/README.md
CHANGED
|
@@ -150,8 +150,31 @@ The following values can be provided:
|
|
|
150
150
|
- `retries` - {Number} - The number of times the client should retry to settle a version number conflict before terminating. See the section "[on retrying](#on-retrying)" for more information. Default: 4 - Optional.
|
|
151
151
|
- `timeout` - {Number} - Defines how long, in milliseconds, a request should wait before the connection is terminated. Overrides the global default. Default: 1000 - Optional.
|
|
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
|
-
- `
|
|
154
|
-
- `
|
|
153
|
+
- `excludeBy` - {Object} - Lets you define a set of rules where a `fetch` call will not be resolved if it matches. - Optional.
|
|
154
|
+
- `includeBy` - {Object} - Inverse of `excludeBy`. Setting both at the same time will throw. - Optional.
|
|
155
|
+
|
|
156
|
+
##### `excludeBy` and `includeBy`
|
|
157
|
+
|
|
158
|
+
These options are used by `fetch` to conditionally skip fetching the podlet content based on values on the request. It's an alternative to conditionally fetching podlets in your request handler. Setting both at the same time will throw.
|
|
159
|
+
|
|
160
|
+
Allowed options:
|
|
161
|
+
|
|
162
|
+
- `deviceType` - {Array<String>} - List of values for the `x-podium-device-type` header. - Optional.
|
|
163
|
+
|
|
164
|
+
Example: exclude a header and footer in a hybrid web view.
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
import Client from '@podium/client';
|
|
168
|
+
const client = new Client();
|
|
169
|
+
|
|
170
|
+
const footer = client.register({
|
|
171
|
+
uri: 'http://footer.site.com/manifest.json',
|
|
172
|
+
name: 'footer',
|
|
173
|
+
excludeBy: {
|
|
174
|
+
deviceType: ['hybrid-ios', 'hybrid-android'], // when footer.fetch(incoming) is called, if the incoming request has the header `x-podium-device-type: hybrid-ios`, `fetch` will return an empty response.
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
```
|
|
155
178
|
|
|
156
179
|
### .js()
|
|
157
180
|
|
|
@@ -437,15 +460,19 @@ stream.once('beforeStream', (data) => {
|
|
|
437
460
|
Both the .fetch() method and the .stream() method give you access to podlet asset objects and these CSS and JS asset objects will be filtered if the asset objects contain a `scope` property and that `scope` property matches the current response type (either content or fallback).
|
|
438
461
|
|
|
439
462
|
For example, if the podlet manifest contains a JavaScript asset definition of the form:
|
|
463
|
+
|
|
440
464
|
```
|
|
441
465
|
{
|
|
442
466
|
js: [{ value: "https://assets.com/path/to/file.js", scope: "content" }],
|
|
443
467
|
}
|
|
444
468
|
```
|
|
469
|
+
|
|
445
470
|
And the client performs a fetch like so:
|
|
471
|
+
|
|
446
472
|
```js
|
|
447
473
|
const result = await component.fetch();
|
|
448
474
|
```
|
|
475
|
+
|
|
449
476
|
Then, if the podlet successfully responds from its content route, the `result.js` property will contain the asset defined above. If, however, the podlet's content route errors and the client is forced to use the podlet's fallback content, then `result.js` property will be an empty array.
|
|
450
477
|
|
|
451
478
|
Possible `scope` values are `content`, `fallback` and `all`. For backwards compatibility reasons, when assets do not provide a `scope` property, they will always be included in both `content` and `fallback` responses.
|
package/lib/client.js
CHANGED
|
@@ -60,6 +60,8 @@ const MAX_AGE = Infinity;
|
|
|
60
60
|
* @property {number} [timeout=1000] In milliseconds, the amount of time to wait before serving fallback.
|
|
61
61
|
* @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.
|
|
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
|
+
* @property {import('./resource.js').RequestFilterOptions} [excludeBy] Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
|
|
64
|
+
* @property {import('./resource.js').RequestFilterOptions} [includeBy] Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
|
|
63
65
|
*/
|
|
64
66
|
|
|
65
67
|
export default class PodiumClient extends EventEmitter {
|
|
@@ -94,6 +96,8 @@ export default class PodiumClient extends EventEmitter {
|
|
|
94
96
|
rejectUnauthorized: REJECT_UNAUTHORIZED,
|
|
95
97
|
httpAgent: HTTP_AGENT,
|
|
96
98
|
httpsAgent: HTTPS_AGENT,
|
|
99
|
+
includeBy: undefined,
|
|
100
|
+
excludeBy: undefined,
|
|
97
101
|
...options,
|
|
98
102
|
};
|
|
99
103
|
|
|
@@ -191,6 +195,12 @@ export default class PodiumClient extends EventEmitter {
|
|
|
191
195
|
);
|
|
192
196
|
}
|
|
193
197
|
|
|
198
|
+
if (options.includeBy && options.excludeBy) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
'A podlet can only be registered with either includeBy or excludeBy, not both.',
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
194
204
|
const resourceOptions = {
|
|
195
205
|
rejectUnauthorized: this.#options.rejectUnauthorized,
|
|
196
206
|
clientName: this.#options.name,
|
|
@@ -200,8 +210,11 @@ export default class PodiumClient extends EventEmitter {
|
|
|
200
210
|
maxAge: this.#options.maxAge,
|
|
201
211
|
httpsAgent: this.#options.httpsAgent,
|
|
202
212
|
httpAgent: this.#options.httpAgent,
|
|
213
|
+
includeBy: this.#options.includeBy,
|
|
214
|
+
excludeBy: this.#options.excludeBy,
|
|
203
215
|
...options,
|
|
204
216
|
};
|
|
217
|
+
|
|
205
218
|
const resource = new Resource(
|
|
206
219
|
this.#registry,
|
|
207
220
|
this.#state,
|
package/lib/resource.js
CHANGED
|
@@ -11,6 +11,11 @@ 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
|
+
|
|
14
19
|
/**
|
|
15
20
|
* @typedef {object} PodiumClientResourceOptions
|
|
16
21
|
* @property {import('abslog').AbstractLoggerOptions} [logger]
|
|
@@ -25,6 +30,8 @@ const inspect = Symbol.for('nodejs.util.inspect.custom');
|
|
|
25
30
|
* @property {boolean} [rejectUnauthorized]
|
|
26
31
|
* @property {import('http').Agent} [httpAgent]
|
|
27
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.
|
|
28
35
|
*/
|
|
29
36
|
|
|
30
37
|
export default class PodiumClientResource {
|
|
@@ -102,6 +109,56 @@ export default class PodiumClientResource {
|
|
|
102
109
|
);
|
|
103
110
|
const outgoing = new HttpOutgoing(this.#options, reqOptions, incoming);
|
|
104
111
|
|
|
112
|
+
if (this.#options.excludeBy) {
|
|
113
|
+
/**
|
|
114
|
+
* @type {string[] | undefined}
|
|
115
|
+
*/
|
|
116
|
+
const excludedDeviceTypes = this.#options.excludeBy.deviceType;
|
|
117
|
+
if (Array.isArray(excludedDeviceTypes)) {
|
|
118
|
+
const deviceTypeHeader =
|
|
119
|
+
incoming.request.headers['x-podium-device-type'];
|
|
120
|
+
|
|
121
|
+
for (let i = 0; i < excludedDeviceTypes.length; i += 1) {
|
|
122
|
+
const shouldSkip =
|
|
123
|
+
excludedDeviceTypes[i] === deviceTypeHeader;
|
|
124
|
+
if (shouldSkip) {
|
|
125
|
+
return new Response({
|
|
126
|
+
headers: {},
|
|
127
|
+
content: '',
|
|
128
|
+
css: [],
|
|
129
|
+
js: [],
|
|
130
|
+
redirect: null,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (this.#options.includeBy) {
|
|
138
|
+
/**
|
|
139
|
+
* @type {string[] | undefined}
|
|
140
|
+
*/
|
|
141
|
+
const includeDeviceTypes = this.#options.includeBy.deviceType;
|
|
142
|
+
if (Array.isArray(includeDeviceTypes)) {
|
|
143
|
+
const deviceTypeHeader =
|
|
144
|
+
incoming.request.headers['x-podium-device-type'];
|
|
145
|
+
|
|
146
|
+
const shouldRequest =
|
|
147
|
+
!deviceTypeHeader ||
|
|
148
|
+
includeDeviceTypes.includes(deviceTypeHeader);
|
|
149
|
+
|
|
150
|
+
if (!shouldRequest) {
|
|
151
|
+
return new Response({
|
|
152
|
+
headers: {},
|
|
153
|
+
content: '',
|
|
154
|
+
css: [],
|
|
155
|
+
js: [],
|
|
156
|
+
redirect: null,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
105
162
|
this.#state.setInitializingState();
|
|
106
163
|
|
|
107
164
|
const { manifest, headers, redirect, isFallback } =
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@podium/client",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
"undici": "6.19.2"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@babel/eslint-parser": "7.24.
|
|
49
|
+
"@babel/eslint-parser": "7.24.7",
|
|
50
50
|
"@podium/test-utils": "2.5.2",
|
|
51
51
|
"@semantic-release/changelog": "6.0.3",
|
|
52
52
|
"@semantic-release/git": "10.0.1",
|
|
53
|
-
"@semantic-release/github": "10.0.
|
|
53
|
+
"@semantic-release/github": "10.0.6",
|
|
54
54
|
"@semantic-release/npm": "12.0.1",
|
|
55
55
|
"@semantic-release/release-notes-generator": "13.0.0",
|
|
56
56
|
"@sinonjs/fake-timers": "11.2.2",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"get-stream": "9.0.1",
|
|
66
66
|
"http-proxy": "1.18.1",
|
|
67
67
|
"is-stream": "4.0.1",
|
|
68
|
-
"prettier": "3.3.
|
|
68
|
+
"prettier": "3.3.2",
|
|
69
69
|
"semantic-release": "23.1.1",
|
|
70
70
|
"tap": "18.7.2",
|
|
71
71
|
"typescript": "5.4.5"
|
package/types/client.d.ts
CHANGED
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
* @property {number} [timeout=1000] In milliseconds, the amount of time to wait before serving fallback.
|
|
28
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
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.
|
|
30
32
|
*/
|
|
31
33
|
export default class PodiumClient extends EventEmitter<[never]> {
|
|
32
34
|
/**
|
|
@@ -105,6 +107,14 @@ export type RegisterOptions = {
|
|
|
105
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.
|
|
106
108
|
*/
|
|
107
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;
|
|
108
118
|
};
|
|
109
119
|
import EventEmitter from 'events';
|
|
110
120
|
import Metrics from '@metrics/client';
|
package/types/resource.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {object} RequestFilterOptions
|
|
3
|
+
* @property {string[]} [deviceType] List of values for the `x-podium-device-type` HTTP request header.
|
|
4
|
+
*/
|
|
1
5
|
/**
|
|
2
6
|
* @typedef {object} PodiumClientResourceOptions
|
|
3
7
|
* @property {import('abslog').AbstractLoggerOptions} [logger]
|
|
@@ -12,6 +16,8 @@
|
|
|
12
16
|
* @property {boolean} [rejectUnauthorized]
|
|
13
17
|
* @property {import('http').Agent} [httpAgent]
|
|
14
18
|
* @property {import('https').Agent} [httpsAgent]
|
|
19
|
+
* @property {RequestFilterOptions} [excludeBy] Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
|
|
20
|
+
* @property {RequestFilterOptions} [includeBy] Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
|
|
15
21
|
*/
|
|
16
22
|
export default class PodiumClientResource {
|
|
17
23
|
/**
|
|
@@ -64,6 +70,12 @@ export default class PodiumClientResource {
|
|
|
64
70
|
get [Symbol.toStringTag](): string;
|
|
65
71
|
#private;
|
|
66
72
|
}
|
|
73
|
+
export type RequestFilterOptions = {
|
|
74
|
+
/**
|
|
75
|
+
* List of values for the `x-podium-device-type` HTTP request header.
|
|
76
|
+
*/
|
|
77
|
+
deviceType?: string[];
|
|
78
|
+
};
|
|
67
79
|
export type PodiumClientResourceOptions = {
|
|
68
80
|
logger?: import('abslog').AbstractLoggerOptions;
|
|
69
81
|
clientName: string;
|
|
@@ -83,6 +95,14 @@ export type PodiumClientResourceOptions = {
|
|
|
83
95
|
rejectUnauthorized?: boolean;
|
|
84
96
|
httpAgent?: import('http').Agent;
|
|
85
97
|
httpsAgent?: import('https').Agent;
|
|
98
|
+
/**
|
|
99
|
+
* Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
|
|
100
|
+
*/
|
|
101
|
+
excludeBy?: RequestFilterOptions;
|
|
102
|
+
/**
|
|
103
|
+
* Used by `fetch` to conditionally skip fetching the podlet content based on values on the request.
|
|
104
|
+
*/
|
|
105
|
+
includeBy?: RequestFilterOptions;
|
|
86
106
|
};
|
|
87
107
|
import Metrics from '@metrics/client';
|
|
88
108
|
declare const inspect: unique symbol;
|