@adobe/spacecat-shared-tokowaka-client 1.18.1 → 1.18.2
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 +6 -0
- package/package.json +1 -1
- package/src/index.js +16 -12
- package/test/index.test.js +190 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [@adobe/spacecat-shared-tokowaka-client-v1.18.2](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-tokowaka-client-v1.18.1...@adobe/spacecat-shared-tokowaka-client-v1.18.2) (2026-06-02)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
* **tokowaka-client:** use scraperConfig headers in edge optimize probe (LLMO-5280) ([#1647](https://github.com/adobe/spacecat-shared/issues/1647)) ([6be14a1](https://github.com/adobe/spacecat-shared/commit/6be14a153ea8d9de010880afaa1c3c9290a32f13))
|
|
6
|
+
|
|
1
7
|
## [@adobe/spacecat-shared-tokowaka-client-v1.18.1](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-tokowaka-client-v1.18.0...@adobe/spacecat-shared-tokowaka-client-v1.18.1) (2026-06-01)
|
|
2
8
|
|
|
3
9
|
### Bug Fixes
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1287,10 +1287,18 @@ class TokowakaClient {
|
|
|
1287
1287
|
return { edgeOptimizeEnabled: true };
|
|
1288
1288
|
}
|
|
1289
1289
|
|
|
1290
|
+
const defaultProbeHeaders = {
|
|
1291
|
+
// eslint-disable-next-line max-len
|
|
1292
|
+
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Tokowaka-AI Tokowaka/1.0 AdobeEdgeOptimize-AI AdobeEdgeOptimize/1.0',
|
|
1293
|
+
'fastly-debug': '1',
|
|
1294
|
+
};
|
|
1295
|
+
const customHeaders = currentConfig?.getScraperConfig?.()?.headers ?? {};
|
|
1296
|
+
const probeHeaders = { ...customHeaders, ...defaultProbeHeaders };
|
|
1297
|
+
|
|
1290
1298
|
const baseURL = getEffectiveBaseURL(site);
|
|
1291
1299
|
const targetUrl = new URL(path, baseURL).toString();
|
|
1292
1300
|
|
|
1293
|
-
this.log.info(`Checking edge optimize status for ${targetUrl}`);
|
|
1301
|
+
this.log.info(`[edge-optimize-status] Checking edge optimize status for ${targetUrl}`);
|
|
1294
1302
|
|
|
1295
1303
|
const maxRetries = 3;
|
|
1296
1304
|
let attempt = 0;
|
|
@@ -1300,25 +1308,21 @@ class TokowakaClient {
|
|
|
1300
1308
|
while (attempt <= maxRetries) {
|
|
1301
1309
|
try {
|
|
1302
1310
|
// eslint-disable-next-line max-len
|
|
1303
|
-
this.log.debug(`Attempt ${attempt + 1}/${maxRetries + 1}: Checking edge optimize status for ${targetUrl}`);
|
|
1311
|
+
this.log.debug(`[edge-optimize-status] Attempt ${attempt + 1}/${maxRetries + 1}: Checking edge optimize status for ${targetUrl}`);
|
|
1304
1312
|
|
|
1305
1313
|
// eslint-disable-next-line no-await-in-loop
|
|
1306
1314
|
const response = await tracingFetch(targetUrl, {
|
|
1307
1315
|
method: 'GET',
|
|
1308
|
-
headers:
|
|
1309
|
-
// eslint-disable-next-line max-len
|
|
1310
|
-
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Tokowaka-AI Tokowaka/1.0 AdobeEdgeOptimize-AI AdobeEdgeOptimize/1.0',
|
|
1311
|
-
'fastly-debug': '1',
|
|
1312
|
-
},
|
|
1316
|
+
headers: probeHeaders,
|
|
1313
1317
|
timeout: REQUEST_TIMEOUT_MS,
|
|
1314
1318
|
});
|
|
1315
1319
|
|
|
1316
|
-
this.log.
|
|
1320
|
+
this.log.info(`[edge-optimize-status] Response status: ${response.status} for ${targetUrl}`);
|
|
1317
1321
|
|
|
1318
1322
|
const edgeOptimizeEnabled = response.headers.get('x-tokowaka-request-id') !== null
|
|
1319
1323
|
|| response.headers.get('x-edgeoptimize-request-id') !== null;
|
|
1320
1324
|
|
|
1321
|
-
this.log.
|
|
1325
|
+
this.log.info(`[edge-optimize-status] Edge optimize headers found: ${edgeOptimizeEnabled} for ${targetUrl}`);
|
|
1322
1326
|
|
|
1323
1327
|
return {
|
|
1324
1328
|
edgeOptimizeEnabled,
|
|
@@ -1328,7 +1332,7 @@ class TokowakaClient {
|
|
|
1328
1332
|
|
|
1329
1333
|
if (isTimeout) {
|
|
1330
1334
|
// eslint-disable-next-line max-len
|
|
1331
|
-
this.log.warn(`Request timed out after ${REQUEST_TIMEOUT_MS}ms for ${targetUrl}, returning edgeOptimizeEnabled: false`);
|
|
1335
|
+
this.log.warn(`[edge-optimize-status] Request timed out after ${REQUEST_TIMEOUT_MS}ms for ${targetUrl}, returning edgeOptimizeEnabled: false`);
|
|
1332
1336
|
return { edgeOptimizeEnabled: false };
|
|
1333
1337
|
}
|
|
1334
1338
|
|
|
@@ -1336,7 +1340,7 @@ class TokowakaClient {
|
|
|
1336
1340
|
|
|
1337
1341
|
if (attempt > maxRetries) {
|
|
1338
1342
|
// All retries exhausted
|
|
1339
|
-
this.log.error(`Failed after ${maxRetries + 1} attempts: ${error.message}`);
|
|
1343
|
+
this.log.error(`[edge-optimize-status] Failed after ${maxRetries + 1} attempts: ${error.message} for ${targetUrl}`);
|
|
1340
1344
|
throw this.#createError(
|
|
1341
1345
|
`Failed to check edge optimize status: ${error.message}`,
|
|
1342
1346
|
HTTP_INTERNAL_SERVER_ERROR,
|
|
@@ -1346,7 +1350,7 @@ class TokowakaClient {
|
|
|
1346
1350
|
// Exponential backoff: 200ms, 400ms, 800ms
|
|
1347
1351
|
const delay = 100 * (2 ** attempt);
|
|
1348
1352
|
this.log.warn(
|
|
1349
|
-
`Attempt ${attempt} to fetch failed: ${error.message}. Retrying in ${delay}ms
|
|
1353
|
+
`[edge-optimize-status] Attempt ${attempt} to fetch failed: ${error.message}. Retrying in ${delay}ms... for ${targetUrl}`,
|
|
1350
1354
|
);
|
|
1351
1355
|
// eslint-disable-next-line no-await-in-loop
|
|
1352
1356
|
await new Promise((res) => {
|
package/test/index.test.js
CHANGED
|
@@ -5035,6 +5035,165 @@ describe('TokowakaClient', () => {
|
|
|
5035
5035
|
expect(userAgent).to.include('Mozilla/5.0');
|
|
5036
5036
|
});
|
|
5037
5037
|
|
|
5038
|
+
it('should ignore custom User-Agent from scraperConfig — default takes priority', async () => {
|
|
5039
|
+
const customSite = {
|
|
5040
|
+
getId: () => 'site-id',
|
|
5041
|
+
getBaseURL: () => 'https://example.com',
|
|
5042
|
+
getConfig: () => ({
|
|
5043
|
+
getEdgeOptimizeConfig: () => undefined,
|
|
5044
|
+
getScraperConfig: () => ({ headers: { 'User-Agent': 'AllowListedBot/1.0' } }),
|
|
5045
|
+
}),
|
|
5046
|
+
getDeliveryType: () => 'aem_edge',
|
|
5047
|
+
};
|
|
5048
|
+
const mockResponse = {
|
|
5049
|
+
status: 200,
|
|
5050
|
+
headers: new Map(),
|
|
5051
|
+
};
|
|
5052
|
+
mockResponse.headers.get = () => null;
|
|
5053
|
+
|
|
5054
|
+
tracingFetchStub.resolves(mockResponse);
|
|
5055
|
+
|
|
5056
|
+
await esmockClient.checkEdgeOptimizeStatus(customSite, '/');
|
|
5057
|
+
|
|
5058
|
+
const fetchOptions = tracingFetchStub.firstCall.args[1];
|
|
5059
|
+
expect(fetchOptions.headers['User-Agent']).to.include('Tokowaka-AI');
|
|
5060
|
+
expect(fetchOptions.headers['User-Agent']).to.not.include('AllowListedBot/1.0');
|
|
5061
|
+
});
|
|
5062
|
+
|
|
5063
|
+
it('should add extra custom headers while defaults always win on collision', async () => {
|
|
5064
|
+
const customSite = {
|
|
5065
|
+
getId: () => 'site-id',
|
|
5066
|
+
getBaseURL: () => 'https://example.com',
|
|
5067
|
+
getConfig: () => ({
|
|
5068
|
+
getEdgeOptimizeConfig: () => undefined,
|
|
5069
|
+
getScraperConfig: () => ({ headers: { 'x-allowlist-token': 'secret123', 'fastly-debug': 'custom' } }),
|
|
5070
|
+
}),
|
|
5071
|
+
getDeliveryType: () => 'aem_edge',
|
|
5072
|
+
};
|
|
5073
|
+
const mockResponse = {
|
|
5074
|
+
status: 200,
|
|
5075
|
+
headers: new Map(),
|
|
5076
|
+
};
|
|
5077
|
+
mockResponse.headers.get = () => null;
|
|
5078
|
+
|
|
5079
|
+
tracingFetchStub.resolves(mockResponse);
|
|
5080
|
+
|
|
5081
|
+
await esmockClient.checkEdgeOptimizeStatus(customSite, '/');
|
|
5082
|
+
|
|
5083
|
+
const fetchOptions = tracingFetchStub.firstCall.args[1];
|
|
5084
|
+
expect(fetchOptions.headers['x-allowlist-token']).to.equal('secret123');
|
|
5085
|
+
expect(fetchOptions.headers['fastly-debug']).to.equal('1');
|
|
5086
|
+
expect(fetchOptions.headers['User-Agent']).to.include('Mozilla/5.0');
|
|
5087
|
+
});
|
|
5088
|
+
|
|
5089
|
+
it('should use default probe headers when scraperConfig has no headers', async () => {
|
|
5090
|
+
const noHeadersSite = {
|
|
5091
|
+
getId: () => 'site-id',
|
|
5092
|
+
getBaseURL: () => 'https://example.com',
|
|
5093
|
+
getConfig: () => ({
|
|
5094
|
+
getEdgeOptimizeConfig: () => undefined,
|
|
5095
|
+
getScraperConfig: () => ({}),
|
|
5096
|
+
}),
|
|
5097
|
+
getDeliveryType: () => 'aem_edge',
|
|
5098
|
+
};
|
|
5099
|
+
const mockResponse = {
|
|
5100
|
+
status: 200,
|
|
5101
|
+
headers: new Map(),
|
|
5102
|
+
};
|
|
5103
|
+
mockResponse.headers.get = () => null;
|
|
5104
|
+
|
|
5105
|
+
tracingFetchStub.resolves(mockResponse);
|
|
5106
|
+
|
|
5107
|
+
await esmockClient.checkEdgeOptimizeStatus(noHeadersSite, '/');
|
|
5108
|
+
|
|
5109
|
+
const fetchOptions = tracingFetchStub.firstCall.args[1];
|
|
5110
|
+
expect(fetchOptions.headers['User-Agent']).to.include('Tokowaka-AI');
|
|
5111
|
+
expect(fetchOptions.headers['fastly-debug']).to.equal('1');
|
|
5112
|
+
});
|
|
5113
|
+
|
|
5114
|
+
it('should use default probe headers when getScraperConfig is not present on config', async () => {
|
|
5115
|
+
const noScraperSite = {
|
|
5116
|
+
getId: () => 'site-id',
|
|
5117
|
+
getBaseURL: () => 'https://example.com',
|
|
5118
|
+
getConfig: () => ({ getEdgeOptimizeConfig: () => undefined }),
|
|
5119
|
+
getDeliveryType: () => 'aem_edge',
|
|
5120
|
+
};
|
|
5121
|
+
const mockResponse = {
|
|
5122
|
+
status: 200,
|
|
5123
|
+
headers: new Map(),
|
|
5124
|
+
};
|
|
5125
|
+
mockResponse.headers.get = () => null;
|
|
5126
|
+
|
|
5127
|
+
tracingFetchStub.resolves(mockResponse);
|
|
5128
|
+
|
|
5129
|
+
await esmockClient.checkEdgeOptimizeStatus(noScraperSite, '/');
|
|
5130
|
+
|
|
5131
|
+
const fetchOptions = tracingFetchStub.firstCall.args[1];
|
|
5132
|
+
expect(fetchOptions.headers['User-Agent']).to.include('AdobeEdgeOptimize-AI');
|
|
5133
|
+
expect(fetchOptions.headers['fastly-debug']).to.equal('1');
|
|
5134
|
+
});
|
|
5135
|
+
|
|
5136
|
+
it('should use default probe headers when getScraperConfig returns null', async () => {
|
|
5137
|
+
const nullScraperSite = {
|
|
5138
|
+
getId: () => 'site-id',
|
|
5139
|
+
getBaseURL: () => 'https://example.com',
|
|
5140
|
+
getConfig: () => ({
|
|
5141
|
+
getEdgeOptimizeConfig: () => undefined,
|
|
5142
|
+
getScraperConfig: () => null,
|
|
5143
|
+
}),
|
|
5144
|
+
getDeliveryType: () => 'aem_edge',
|
|
5145
|
+
};
|
|
5146
|
+
const mockResponse = { status: 200, headers: new Map() };
|
|
5147
|
+
mockResponse.headers.get = () => null;
|
|
5148
|
+
tracingFetchStub.resolves(mockResponse);
|
|
5149
|
+
|
|
5150
|
+
await esmockClient.checkEdgeOptimizeStatus(nullScraperSite, '/');
|
|
5151
|
+
|
|
5152
|
+
const fetchOptions = tracingFetchStub.firstCall.args[1];
|
|
5153
|
+
expect(fetchOptions.headers['User-Agent']).to.include('Tokowaka-AI');
|
|
5154
|
+
expect(fetchOptions.headers['fastly-debug']).to.equal('1');
|
|
5155
|
+
});
|
|
5156
|
+
|
|
5157
|
+
it('should use default probe headers when getConfig returns null', async () => {
|
|
5158
|
+
const nullConfigSite = {
|
|
5159
|
+
getId: () => 'site-id',
|
|
5160
|
+
getBaseURL: () => 'https://example.com',
|
|
5161
|
+
getConfig: () => null,
|
|
5162
|
+
getDeliveryType: () => 'aem_edge',
|
|
5163
|
+
};
|
|
5164
|
+
const mockResponse = { status: 200, headers: new Map() };
|
|
5165
|
+
mockResponse.headers.get = () => null;
|
|
5166
|
+
tracingFetchStub.resolves(mockResponse);
|
|
5167
|
+
|
|
5168
|
+
await esmockClient.checkEdgeOptimizeStatus(nullConfigSite, '/');
|
|
5169
|
+
|
|
5170
|
+
const fetchOptions = tracingFetchStub.firstCall.args[1];
|
|
5171
|
+
expect(fetchOptions.headers['User-Agent']).to.include('Tokowaka-AI');
|
|
5172
|
+
expect(fetchOptions.headers['fastly-debug']).to.equal('1');
|
|
5173
|
+
});
|
|
5174
|
+
|
|
5175
|
+
it('should use default probe headers when scraperConfig has an empty headers object', async () => {
|
|
5176
|
+
const emptyHeadersSite = {
|
|
5177
|
+
getId: () => 'site-id',
|
|
5178
|
+
getBaseURL: () => 'https://example.com',
|
|
5179
|
+
getConfig: () => ({
|
|
5180
|
+
getEdgeOptimizeConfig: () => undefined,
|
|
5181
|
+
getScraperConfig: () => ({ headers: {} }),
|
|
5182
|
+
}),
|
|
5183
|
+
getDeliveryType: () => 'aem_edge',
|
|
5184
|
+
};
|
|
5185
|
+
const mockResponse = { status: 200, headers: new Map() };
|
|
5186
|
+
mockResponse.headers.get = () => null;
|
|
5187
|
+
tracingFetchStub.resolves(mockResponse);
|
|
5188
|
+
|
|
5189
|
+
await esmockClient.checkEdgeOptimizeStatus(emptyHeadersSite, '/');
|
|
5190
|
+
|
|
5191
|
+
const fetchOptions = tracingFetchStub.firstCall.args[1];
|
|
5192
|
+
expect(fetchOptions.headers['User-Agent']).to.include('Tokowaka-AI');
|
|
5193
|
+
expect(fetchOptions.headers['fastly-debug']).to.equal('1');
|
|
5194
|
+
expect(Object.keys(fetchOptions.headers)).to.have.lengthOf(2);
|
|
5195
|
+
});
|
|
5196
|
+
|
|
5038
5197
|
it('should pass timeout option to tracingFetch', async () => {
|
|
5039
5198
|
const mockResponse = {
|
|
5040
5199
|
status: 200,
|
|
@@ -5168,7 +5327,7 @@ describe('TokowakaClient', () => {
|
|
|
5168
5327
|
await promise;
|
|
5169
5328
|
|
|
5170
5329
|
expect(log.warn).to.have.been.calledWith(
|
|
5171
|
-
sinon.match(
|
|
5330
|
+
sinon.match(/\[edge-optimize-status\] Attempt 1 to fetch failed.*Connection refused.*Retrying in 200ms/),
|
|
5172
5331
|
);
|
|
5173
5332
|
});
|
|
5174
5333
|
|
|
@@ -5182,9 +5341,38 @@ describe('TokowakaClient', () => {
|
|
|
5182
5341
|
expect(result).to.deep.equal({ edgeOptimizeEnabled: false });
|
|
5183
5342
|
expect(tracingFetchStub).to.have.been.calledOnce;
|
|
5184
5343
|
expect(log.warn).to.have.been.calledWith(
|
|
5185
|
-
sinon.match(
|
|
5344
|
+
sinon.match(/\[edge-optimize-status\] Request timed out after 5000ms for https:\/\/example.com\/, returning edgeOptimizeEnabled: false/),
|
|
5186
5345
|
);
|
|
5187
5346
|
});
|
|
5347
|
+
|
|
5348
|
+
it('should send the same custom headers on every retry attempt', async () => {
|
|
5349
|
+
const customSite = {
|
|
5350
|
+
getId: () => 'site-id',
|
|
5351
|
+
getBaseURL: () => 'https://example.com',
|
|
5352
|
+
getConfig: () => ({
|
|
5353
|
+
getEdgeOptimizeConfig: () => undefined,
|
|
5354
|
+
getScraperConfig: () => ({ headers: { 'x-allowlist-token': 'secret123' } }),
|
|
5355
|
+
}),
|
|
5356
|
+
getDeliveryType: () => 'aem_edge',
|
|
5357
|
+
};
|
|
5358
|
+
const mockResponse = { status: 200, headers: new Map() };
|
|
5359
|
+
mockResponse.headers.get = () => null;
|
|
5360
|
+
|
|
5361
|
+
tracingFetchStub.onFirstCall().rejects(new Error('Temporary failure'));
|
|
5362
|
+
tracingFetchStub.onSecondCall().resolves(mockResponse);
|
|
5363
|
+
|
|
5364
|
+
const promise = esmockClient.checkEdgeOptimizeStatus(customSite, '/');
|
|
5365
|
+
await clock.tickAsync(200);
|
|
5366
|
+
await promise;
|
|
5367
|
+
|
|
5368
|
+
expect(tracingFetchStub).to.have.been.calledTwice;
|
|
5369
|
+
const firstCallHeaders = tracingFetchStub.firstCall.args[1].headers;
|
|
5370
|
+
const secondCallHeaders = tracingFetchStub.secondCall.args[1].headers;
|
|
5371
|
+
expect(firstCallHeaders['x-allowlist-token']).to.equal('secret123');
|
|
5372
|
+
expect(secondCallHeaders['x-allowlist-token']).to.equal('secret123');
|
|
5373
|
+
expect(firstCallHeaders['User-Agent']).to.include('Tokowaka-AI');
|
|
5374
|
+
expect(secondCallHeaders['User-Agent']).to.include('Tokowaka-AI');
|
|
5375
|
+
});
|
|
5188
5376
|
});
|
|
5189
5377
|
|
|
5190
5378
|
describe('URL Construction', () => {
|