Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion .talismanrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
fileignoreconfig:
- filename: package-lock.json
checksum: dffbcb14c5761976a9b6ab90b96c7e4fc5784eebe381cf48014618cc93355dbc
checksum: 17c3944c70209ee40840cee33e891b57cf1c672ecb03b410b71ad6fb0242a86e
- filename: test/unit/query-optimization-comprehensive.spec.ts
checksum: f5aaf6c784d7c101a05ca513c584bbd6e95f963d1e42779f2596050d9bcbac96
- filename: src/lib/entries.ts
checksum: f6a19da15baed75062ad0cc599573ed08926e28fffe3f6e4890a0efb4d58c910
- filename: src/lib/cache.ts
checksum: d8d32089b8a4b247e4ba71c22b56cbb0a54440ebf35b102af222eb8032919f02
- filename: test/unit/cache.spec.ts
checksum: e96f913a466a1f4d55a422e7032fc2c06eeed5fea86cdcc86a05fbe3eba29b7a
- filename: src/lib/query.ts
checksum: 073c47e46755eb79d1d7e9fcaf2864296a218bf650888dd37c42480cce7df379
- filename: test/api/retry-integration.spec.ts
checksum: dc07b0a8111fd8e155b99f56c31ccdddd4f46c86f1b162b17d73e15dfed8e3c8
- filename: test/unit/retry-configuration.spec.ts
checksum: 359c8601c6205a65f3395cc209a93b278dfe7f5bb547c91b2eeab250b2c85aa3
version: ""
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
### Version: 4.9.1
#### Date: Aug-25-2025
Fix: Enhance retry logic to use configured retryDelay
Enhancement: Caching logic to use combination of content type uid and entry uid

### Version: 4.9.0
#### Date: Aug-25-2025
Fix: Fixed Timeout parameter which is being ignored & Removed custom error object
Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/delivery-sdk",
"version": "4.9.0",
"version": "4.9.1",
"type": "module",
"license": "MIT",
"main": "./dist/legacy/index.cjs",
Expand Down Expand Up @@ -37,7 +37,7 @@
"dependencies": {
"@contentstack/core": "^1.3.0",
"@contentstack/utils": "^1.4.1",
"axios": "^1.11.0",
"axios": "^1.12.2",
"humps": "^2.0.1"
},
"files": [
Expand Down
20 changes: 19 additions & 1 deletion src/lib/base-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,11 @@ export class BaseQuery extends Pagination {
requestParams = { ...this._queryParams, query: queryParams };
}

const getRequestOptions: any = { params: requestParams };
const getRequestOptions: any = {
params: requestParams,
// Add contentTypeUid to config for improved caching (extract from URL if possible)
contentTypeUid: this.extractContentTypeUidFromUrl()
};

if (this._variants) {
getRequestOptions.headers = {
Expand All @@ -227,4 +231,18 @@ export class BaseQuery extends Pagination {

return response as FindResponse<T>;
}

/**
* Extracts content type UID from the URL path
* @returns content type UID if found, null otherwise
*/
private extractContentTypeUidFromUrl(): string | null {
if (!this._urlPath) return null;

// Match patterns like: /content_types/{content_type_uid}/entries
const contentTypePattern = /\/content_types\/([^\/]+)/;
const match = this._urlPath.match(contentTypePattern);

return match ? match[1] : null;
}
}
57 changes: 50 additions & 7 deletions src/lib/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,42 @@
import { PersistanceStore } from '../persistance';
import { CacheOptions, Policy } from './types';

/**
* Extracts entry UID from request URL if available
* @param config - Request config object
* @returns entry UID if found, null otherwise
*/
function extractEntryUidFromUrl(config: any): string | null {
if (!config.url) return null;

// Match patterns like: /content_types/{content_type_uid}/entries/{entry_uid}
const entryUrlPattern = /\/content_types\/[^\/]+\/entries\/([^\/\?]+)/;
const match = config.url.match(entryUrlPattern);

return match ? match[1] : null;
}

/**
* Generates an improved cache key using content type UID and entry UID
* @param originalKey - Original cache key (apiKey)
* @param contentTypeUid - Content type UID
* @param entryUid - Entry UID (optional)
* @returns Enhanced cache key
*/
function generateEnhancedCacheKey(originalKey: string, contentTypeUid?: string, entryUid?: string): string {
let cacheKey = originalKey;

if (contentTypeUid) {
cacheKey = `${contentTypeUid}_${cacheKey}`;
}

if (entryUid) {
cacheKey = `${cacheKey}_entry_${entryUid}`;
}

return cacheKey;
}

export async function handleRequest(
cacheOptions: CacheOptions,
apiKey: string,
Expand All @@ -12,16 +48,23 @@ export async function handleRequest(
config: any
) {
const cacheStore = new PersistanceStore(cacheOptions);

// Extract entry UID from URL or config
const entryUid = config.entryUid || extractEntryUidFromUrl(config);

// Generate enhanced cache key using content type UID and entry UID
const enhancedCacheKey = generateEnhancedCacheKey(apiKey, config.contentTypeUid, entryUid);

switch (cacheOptions.policy) {
case Policy.NETWORK_ELSE_CACHE: {
const apiResponse = await defaultAdapter(config);

if (apiResponse.data) {
cacheStore.setItem(apiKey, JSON.parse(apiResponse.data), config.contentTypeUid, cacheOptions.maxAge);
cacheStore.setItem(enhancedCacheKey, JSON.parse(apiResponse.data), config.contentTypeUid, cacheOptions.maxAge);

return resolve({data: JSON.parse(apiResponse.data)});
} else {
const cacheResponse = cacheStore.getItem(apiKey, config.contentTypeUid);
const cacheResponse = cacheStore.getItem(enhancedCacheKey, config.contentTypeUid);
if (cacheResponse)
return resolve({
data: cacheResponse,
Expand All @@ -35,7 +78,7 @@ export async function handleRequest(
return reject(apiResponse);
}
case Policy.CACHE_THEN_NETWORK: {
const cacheResponse = cacheStore.getItem(apiKey, config.contentTypeUid);
const cacheResponse = cacheStore.getItem(enhancedCacheKey, config.contentTypeUid);
if (cacheResponse)
return resolve({
data: cacheResponse,
Expand All @@ -48,15 +91,15 @@ export async function handleRequest(
const apiResponse = await defaultAdapter(config);

if (apiResponse.data) {
cacheStore.setItem(apiKey, JSON.parse(apiResponse.data), config.contentTypeUid, cacheOptions.maxAge);
cacheStore.setItem(enhancedCacheKey, JSON.parse(apiResponse.data), config.contentTypeUid, cacheOptions.maxAge);

return resolve({data: JSON.parse(apiResponse.data)});
} else {
return reject(apiResponse);
}
}
case Policy.CACHE_ELSE_NETWORK: {
const cacheResponse = cacheStore.getItem(apiKey, config.contentTypeUid);
const cacheResponse = cacheStore.getItem(enhancedCacheKey, config.contentTypeUid);

if (cacheResponse)
return resolve({
Expand All @@ -70,7 +113,7 @@ export async function handleRequest(
const apiResponse = await defaultAdapter(config);

if (apiResponse.data) {
cacheStore.setItem(apiKey, JSON.parse(apiResponse.data), config.contentTypeUid, cacheOptions.maxAge);
cacheStore.setItem(enhancedCacheKey, JSON.parse(apiResponse.data), config.contentTypeUid, cacheOptions.maxAge);

return resolve({data: JSON.parse(apiResponse.data)});
} else {
Expand All @@ -79,4 +122,4 @@ export async function handleRequest(
}
}
}
}
}
35 changes: 35 additions & 0 deletions src/lib/entries.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { AxiosInstance, getData } from '@contentstack/core';
import { Query } from './query';
import { BaseQuery } from './base-query';
import { FindResponse } from './types';
import { encodeQueryParams } from './utils';

export class Entries extends BaseQuery {
private _contentTypeUid: string;
Expand Down Expand Up @@ -266,4 +268,37 @@ export class Entries extends BaseQuery {
}
return this;
}

/**
* Override find method to include content type UID directly for better caching
*/
override async find<T>(encode: boolean = false): Promise<FindResponse<T>> {
let requestParams: { [key: string]: any } = this._queryParams;

if (Object.keys(this._parameters).length > 0) {
let queryParams = { ...this._parameters };

if (encode) {
queryParams = encodeQueryParams(queryParams);
}

requestParams = { ...this._queryParams, query: queryParams };
}

const getRequestOptions: any = {
params: requestParams,
// Add contentTypeUid directly for improved caching
contentTypeUid: this._contentTypeUid
};

if (this._variants) {
getRequestOptions.headers = {
...getRequestOptions.headers,
'x-cs-variant-uid': this._variants
};
}
const response = await getData(this._client, this._urlPath, getRequestOptions);

return response as FindResponse<T>;
}
}
7 changes: 6 additions & 1 deletion src/lib/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,12 @@ export class Entry {
* const result = await stack.contentType(contentType_uid).entry(entry_uid).fetch();
*/
async fetch<T>(): Promise<T> {
const getRequestOptions: any = { params: this._queryParams};
const getRequestOptions: any = {
params: this._queryParams,
// Add contentTypeUid and entryUid to config for improved caching
contentTypeUid: this._contentTypeUid,
entryUid: this._entryUid
};
if (this._variants) {
getRequestOptions.headers = {
...getRequestOptions.headers,
Expand Down
38 changes: 36 additions & 2 deletions src/lib/query.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AxiosInstance } from '@contentstack/core';
import { AxiosInstance, getData } from '@contentstack/core';
import { BaseQuery } from './base-query';
import { BaseQueryParameters, QueryOperation, QueryOperator, TaxonomyQueryOperation, params, queryParams } from './types';
import { BaseQueryParameters, QueryOperation, QueryOperator, TaxonomyQueryOperation, params, queryParams, FindResponse } from './types';
import { encodeQueryParams } from './utils';

export class Query extends BaseQuery {
private _contentTypeUid?: string;
Expand Down Expand Up @@ -594,4 +595,37 @@ export class Query extends BaseQuery {
this._parameters[key] = { '$gte': value };
return this;
}

/**
* Override find method to include content type UID directly for better caching
*/
override async find<T>(encode: boolean = false): Promise<FindResponse<T>> {
let requestParams: { [key: string]: any } = this._queryParams;

if (Object.keys(this._parameters).length > 0) {
let queryParams = { ...this._parameters };

if (encode) {
queryParams = encodeQueryParams(queryParams);
}

requestParams = { ...this._queryParams, query: queryParams };
}

const getRequestOptions: any = {
params: requestParams,
// Add contentTypeUid directly for improved caching
contentTypeUid: this._contentTypeUid
};

if (this._variants) {
getRequestOptions.headers = {
...getRequestOptions.headers,
'x-cs-variant-uid': this._variants
};
}
const response = await getData(this._client, this._urlPath, getRequestOptions);

return response as FindResponse<T>;
}
}
Loading