@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-tokowaka-client",
3
- "version": "1.18.1",
3
+ "version": "1.18.2",
4
4
  "description": "Tokowaka Client for SpaceCat - Edge optimization config management",
5
5
  "type": "module",
6
6
  "engines": {
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.debug(`Response status: ${response.status}`);
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.debug(`Edge optimize headers found: ${edgeOptimizeEnabled}`);
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) => {
@@ -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(/Attempt 1 to fetch failed.*Connection refused.*Retrying in 200ms/),
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(/Request timed out after 5000ms for https:\/\/example.com\/, returning edgeOptimizeEnabled: false/),
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', () => {