storyblok-js-client
Version:
Universal JavaScript SDK for Storyblok's API
1 lines • 56.6 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","names":["msg: string","fn: T","limit: number","interval: number","queue: Queue<Parameters<T>>[]","timeouts: ReturnType<typeof setTimeout>[]","throttled: ISbThrottle<T>","options: ISbStoriesParams","ms: number","func: ArrayFn","i: number","arr: RangeFn[]","func: AsyncFn","arr: ISbResult[]","func: FlatMapFn","params: ISbParams","prefix?: string","isArray?: boolean","regionCode?: string","$c: ISbFetch","url: string","params: ISbStoriesParams","params?: ISbStoriesParams","res: Response","headers: string[]","method: Method","err: any","error: ISbError","fetchOptions: ISbCustomFetch","data: any","res: ISbResponse","memory: Partial<IMemoryType>","config: ISbConfig","pEndpoint?: string","headers: Headers","throttledQueue","SbFetch","params: ISbStoriesParams","url: string","per_page: number","page: number","fetchOptions?: ISbCustomFetch","slug: string","params: ISbStoriesParams | ISbLinksParams","entity?: string","restRes: any","i: number","res: ISbFlatMapped","params: ISbStoriesParams | ISbContentMangmntAPI","params: ISbStoryParams","params: ISbStoriesParams | ISbStoryParams","value: LinksType","jtree: ISbStoriesParams","treeItem: keyof ISbStoriesParams","resolveId: string","uuid: string","fields: string | string[]","story: ISbStoryData","fields: string | Array<string>","jtree: ISbStoriesParams | any","responseData: ISbResponseData","links: (ISbStoryData | ISbLinkURLObject | string)[]","rel: ISbStoryData | ISbLinkURLObject | string","story: ISbStoryData | any","relations: ISbStoryData<ISbComponentType<string> & { [index: string]: any }>[]","rel: ISbStoryData","relationParams: string[]","retries?: number","error: Error | any","type: Method","cv: number","key: string","content: ISbResult","response: ISbResult","node: ISbField","asset: any","story: any"],"sources":["../src/throttlePromise.ts","../src/utils.ts","../src/sbFetch.ts","../src/constants.ts","../src/index.ts"],"sourcesContent":["import type { ISbThrottle, Queue } from './interfaces';\n\nclass AbortError extends Error {\n constructor(msg: string) {\n super(msg);\n this.name = 'AbortError';\n }\n}\n\nfunction throttledQueue<T extends (...args: Parameters<T>) => ReturnType<T>>(\n fn: T,\n limit: number,\n interval: number,\n): ISbThrottle<T> {\n if (!Number.isFinite(limit)) {\n throw new TypeError('Expected `limit` to be a finite number');\n }\n\n if (!Number.isFinite(interval)) {\n throw new TypeError('Expected `interval` to be a finite number');\n }\n\n const queue: Queue<Parameters<T>>[] = [];\n let timeouts: ReturnType<typeof setTimeout>[] = [];\n let activeCount = 0;\n let isAborted = false;\n\n const next = async () => {\n activeCount++;\n\n const x = queue.shift();\n if (x) {\n try {\n const res = await fn(...x.args);\n x.resolve(res);\n }\n catch (error) {\n x.reject(error);\n }\n }\n\n const id = setTimeout(() => {\n activeCount--;\n\n if (queue.length > 0) {\n next();\n }\n\n timeouts = timeouts.filter(currentId => currentId !== id);\n }, interval);\n\n if (!timeouts.includes(id)) {\n timeouts.push(id);\n }\n };\n\n const throttled: ISbThrottle<T> = (...args) => {\n if (isAborted) {\n return Promise.reject(\n new Error(\n 'Throttled function is already aborted and not accepting new promises',\n ),\n );\n }\n\n return new Promise((resolve, reject) => {\n queue.push({\n resolve,\n reject,\n args,\n });\n\n if (activeCount < limit) {\n next();\n }\n });\n };\n\n throttled.abort = () => {\n isAborted = true;\n timeouts.forEach(clearTimeout);\n timeouts = [];\n\n queue.forEach(x =>\n x.reject(() => new AbortError('Throttle function aborted')),\n );\n queue.length = 0;\n };\n\n return throttled;\n}\n\nexport default throttledQueue;\n","import type {\n AsyncFn,\n HtmlEscapes,\n ISbResult,\n ISbStoriesParams,\n} from './interfaces';\n\n// TODO: Revise this type, is it needed?\ninterface ISbParams extends ISbStoriesParams {\n [key: string]: any;\n}\n\ntype ArrayFn = (...args: any) => void;\ntype FlatMapFn = (...args: any) => [] | any;\ntype RangeFn = (...args: any) => [];\n\n/**\n * Checks if a URL is a CDN URL\n * @param url - The URL to check\n * @returns boolean indicating if the URL is a CDN URL\n */\nexport const isCDNUrl = (url = ''): boolean => url.includes('/cdn/');\n\n/**\n * Gets pagination options for the API request\n * @param options - The base options\n * @param perPage - Number of items per page\n * @param page - Current page number\n * @returns Object with pagination options\n */\nexport const getOptionsPage = (\n options: ISbStoriesParams,\n perPage = 25,\n page = 1,\n) => ({\n ...options,\n per_page: perPage,\n page,\n});\n\n/**\n * Creates a promise that resolves after the specified milliseconds\n * @param ms - Milliseconds to delay\n * @returns Promise that resolves after the delay\n */\nexport const delay = (ms: number): Promise<void> =>\n new Promise(res => setTimeout(res, ms));\n\n/**\n * Creates an array of specified length using a mapping function\n * @param length - Length of the array\n * @param func - Mapping function\n * @returns Array of specified length\n */\nexport const arrayFrom = (length = 0, func: ArrayFn) =>\n Array.from({ length }, func);\n\n/**\n * Creates an array of numbers in the specified range\n * @param start - Start of the range\n * @param end - End of the range\n * @returns Array of numbers in the range\n */\nexport const range = (start = 0, end = start): Array<any> => {\n const length = Math.abs(end - start) || 0;\n const step = start < end ? 1 : -1;\n return arrayFrom(length, (_, i: number) => i * step + start);\n};\n\n/**\n * Maps an array asynchronously\n * @param arr - Array to map\n * @param func - Async mapping function\n * @returns Promise resolving to mapped array\n */\nexport const asyncMap = async (arr: RangeFn[], func: AsyncFn) =>\n Promise.all(arr.map(func));\n\n/**\n * Flattens an array using a mapping function\n * @param arr - Array to flatten\n * @param func - Mapping function\n * @returns Flattened array\n */\nexport const flatMap = (arr: ISbResult[] = [], func: FlatMapFn) =>\n arr.map(func).reduce((xs, ys) => [...xs, ...ys], []);\n\n/**\n * Stringifies an object into a URL query string\n * @param params - Parameters to stringify\n * @param prefix - Prefix for nested keys\n * @param isArray - Whether the current level is an array\n * @returns Stringified query parameters\n */\nexport const stringify = (\n params: ISbParams,\n prefix?: string,\n isArray?: boolean,\n): string => {\n const pairs = [];\n for (const key in params) {\n if (!Object.prototype.hasOwnProperty.call(params, key)) {\n continue;\n }\n const value = params[key];\n if (value === null || value === undefined) {\n continue;\n }\n const enkey = isArray ? '' : encodeURIComponent(key);\n let pair;\n if (typeof value === 'object') {\n pair = stringify(\n value,\n prefix ? prefix + encodeURIComponent(`[${enkey}]`) : enkey,\n Array.isArray(value),\n );\n }\n else {\n pair = `${\n prefix ? prefix + encodeURIComponent(`[${enkey}]`) : enkey\n }=${encodeURIComponent(value)}`;\n }\n pairs.push(pair);\n }\n return pairs.join('&');\n};\n\n/**\n * Gets the base URL for a specific region\n * @param regionCode - Region code (eu, us, cn, ap, ca)\n * @returns Base URL for the region\n */\nexport const getRegionURL = (regionCode?: string): string => {\n const REGION_URLS = {\n eu: 'api.storyblok.com',\n us: 'api-us.storyblok.com',\n cn: 'app.storyblokchina.cn',\n ap: 'api-ap.storyblok.com',\n ca: 'api-ca.storyblok.com',\n } as const;\n\n return REGION_URLS[regionCode as keyof typeof REGION_URLS] ?? REGION_URLS.eu;\n};\n\n/**\n * Escapes HTML special characters in a string\n * @param string - String to escape\n * @returns Escaped string\n */\nexport const escapeHTML = (string: string): string => {\n const htmlEscapes = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n '\\'': ''',\n } as HtmlEscapes;\n\n const reUnescapedHtml = /[&<>\"']/g;\n const reHasUnescapedHtml = new RegExp(reUnescapedHtml.source);\n\n return string && reHasUnescapedHtml.test(string)\n ? string.replace(reUnescapedHtml, chr => htmlEscapes[chr])\n : string;\n};\n","import { stringify } from './utils';\n\nimport type {\n ISbCustomFetch,\n ISbError,\n ISbResponse,\n ISbStoriesParams,\n} from './interfaces';\nimport type Method from './constants';\n\nexport interface ResponseFn {\n (arg?: ISbResponse | any): any;\n}\n\nexport interface ISbFetch {\n baseURL: string;\n timeout?: number;\n headers: Headers;\n responseInterceptor?: ResponseFn;\n fetch?: typeof fetch;\n}\n\nclass SbFetch {\n private baseURL: string;\n private timeout?: number;\n private headers: Headers;\n private responseInterceptor?: ResponseFn;\n private fetch: typeof fetch;\n private ejectInterceptor?: boolean;\n private url: string;\n private parameters: ISbStoriesParams;\n private fetchOptions: ISbCustomFetch;\n\n public constructor($c: ISbFetch) {\n this.baseURL = $c.baseURL;\n this.headers = $c.headers || new Headers();\n this.timeout = $c?.timeout ? $c.timeout * 1000 : 0;\n this.responseInterceptor = $c.responseInterceptor;\n this.fetch = (...args: [any]) =>\n $c.fetch ? $c.fetch(...args) : fetch(...args);\n this.ejectInterceptor = false;\n this.url = '';\n this.parameters = {} as ISbStoriesParams;\n this.fetchOptions = {};\n }\n\n /**\n *\n * @param url string\n * @param params ISbStoriesParams\n * @returns Promise<ISbResponse | Error>\n */\n public get(url: string, params: ISbStoriesParams) {\n this.url = url;\n this.parameters = params;\n return this._methodHandler('get');\n }\n\n public post(url: string, params: ISbStoriesParams) {\n this.url = url;\n this.parameters = params;\n return this._methodHandler('post');\n }\n\n public put(url: string, params: ISbStoriesParams) {\n this.url = url;\n this.parameters = params;\n return this._methodHandler('put');\n }\n\n public delete(url: string, params?: ISbStoriesParams) {\n this.url = url;\n this.parameters = params ?? {} as ISbStoriesParams;\n return this._methodHandler('delete');\n }\n\n private async _responseHandler(res: Response) {\n const headers: string[] = [];\n const response = {\n data: {},\n headers: {},\n status: 0,\n statusText: '',\n };\n\n if (res.status !== 204) {\n await res.json().then(($r) => {\n response.data = $r;\n });\n }\n\n for (const pair of res.headers.entries()) {\n headers[pair[0] as any] = pair[1];\n }\n\n response.headers = { ...headers };\n response.status = res.status;\n response.statusText = res.statusText;\n\n return response;\n }\n\n private async _methodHandler(\n method: Method,\n ): Promise<ISbResponse | ISbError> {\n let urlString = `${this.baseURL}${this.url}`;\n\n let body = null;\n\n if (method === 'get') {\n urlString = `${this.baseURL}${this.url}?${stringify(this.parameters)}`;\n }\n else {\n body = JSON.stringify(this.parameters);\n }\n\n const url = new URL(urlString);\n\n const controller = new AbortController();\n const { signal } = controller;\n\n let timeout;\n\n if (this.timeout) {\n timeout = setTimeout(() => controller.abort(), this.timeout);\n }\n\n try {\n const fetchResponse = await this.fetch(`${url}`, {\n method,\n headers: this.headers,\n body,\n signal,\n ...this.fetchOptions,\n });\n\n if (this.timeout) {\n clearTimeout(timeout);\n }\n\n const response = (await this._responseHandler(\n fetchResponse,\n )) as ISbResponse;\n\n if (this.responseInterceptor && !this.ejectInterceptor) {\n return this._statusHandler(this.responseInterceptor(response));\n }\n else {\n return this._statusHandler(response);\n }\n }\n catch (err: any) {\n const error: ISbError = {\n message: err,\n };\n return error;\n }\n }\n\n public setFetchOptions(fetchOptions: ISbCustomFetch = {}) {\n if (Object.keys(fetchOptions).length > 0 && 'method' in fetchOptions) {\n delete fetchOptions.method;\n }\n this.fetchOptions = { ...fetchOptions };\n }\n\n public eject() {\n this.ejectInterceptor = true;\n }\n\n /**\n * Normalizes error messages from different response structures\n * @param data The response data that might contain error information\n * @returns A normalized error message string\n */\n private _normalizeErrorMessage(data: any): string {\n // Handle array of error messages\n if (Array.isArray(data)) {\n return data[0] || 'Unknown error';\n }\n\n // Handle object with error property\n if (data && typeof data === 'object') {\n // Check for common error message patterns\n if (data.error) {\n return data.error;\n }\n\n // Handle nested error objects (like { name: ['has already been taken'] })\n for (const key in data) {\n if (Array.isArray(data[key])) {\n return `${key}: ${data[key][0]}`;\n }\n if (typeof data[key] === 'string') {\n return `${key}: ${data[key]}`;\n }\n }\n\n // If we have a slug, it might be an error message\n if (data.slug) {\n return data.slug;\n }\n }\n\n // Fallback for unknown error structures\n return 'Unknown error';\n }\n\n private _statusHandler(res: ISbResponse): Promise<ISbResponse | ISbError> {\n const statusOk = /20[0-6]/g;\n\n return new Promise((resolve, reject) => {\n if (statusOk.test(`${res.status}`)) {\n return resolve(res);\n }\n\n const error: ISbError = {\n message: this._normalizeErrorMessage(res.data),\n status: res.status,\n response: res,\n };\n\n reject(error);\n });\n }\n}\n\nexport default SbFetch;\n","const _METHOD = {\n GET: 'get',\n DELETE: 'delete',\n POST: 'post',\n PUT: 'put',\n} as const;\n\ntype ObjectValues<T> = T[keyof T];\ntype Method = ObjectValues<typeof _METHOD>;\n\nexport default Method;\n\nexport const STORYBLOK_AGENT = 'SB-Agent';\n\nexport const STORYBLOK_JS_CLIENT_AGENT = {\n defaultAgentName: 'SB-JS-CLIENT',\n defaultAgentVersion: 'SB-Agent-Version',\n packageVersion: '7.0.0',\n};\n\nexport const StoryblokContentVersion = {\n DRAFT: 'draft',\n PUBLISHED: 'published',\n} as const;\n\nexport type StoryblokContentVersionKeys =\n typeof StoryblokContentVersion[keyof typeof StoryblokContentVersion];\n\nexport const StoryblokContentVersionValues = Object.values(\n StoryblokContentVersion,\n) as StoryblokContentVersionKeys[];\n","import throttledQueue from './throttlePromise';\nimport {\n asyncMap,\n delay,\n flatMap,\n getOptionsPage,\n getRegionURL,\n isCDNUrl,\n range,\n stringify,\n} from './utils';\nimport SbFetch from './sbFetch';\nimport type Method from './constants';\nimport type { StoryblokContentVersionKeys } from './constants';\nimport { STORYBLOK_AGENT, STORYBLOK_JS_CLIENT_AGENT, StoryblokContentVersion } from './constants';\n\nimport type {\n ICacheProvider,\n IMemoryType,\n ISbCache,\n ISbComponentType,\n ISbConfig,\n ISbContentMangmntAPI,\n ISbCustomFetch,\n ISbField,\n ISbLinksParams,\n ISbLinksResult,\n ISbLinkURLObject,\n ISbResponse,\n ISbResponseData,\n ISbResult,\n ISbStories,\n ISbStoriesParams,\n ISbStory,\n ISbStoryData,\n ISbStoryParams,\n} from './interfaces';\n\nexport * from './interfaces';\n\nlet memory: Partial<IMemoryType> = {};\n\nconst cacheVersions = {} as CachedVersions;\n\ninterface CachedVersions {\n [key: string]: number;\n}\n\ninterface LinksType {\n [key: string]: any;\n}\n\ninterface RelationsType {\n [key: string]: any;\n}\n\ninterface ISbFlatMapped {\n data: any;\n}\n\nconst _VERSION = {\n V1: 'v1',\n V2: 'v2',\n} as const;\n\ntype ObjectValues<T> = T[keyof T];\ntype Version = ObjectValues<typeof _VERSION>;\n\nexport class Storyblok {\n private client: SbFetch;\n private maxRetries: number;\n private retriesDelay: number;\n private throttle: ReturnType<typeof throttledQueue>;\n private accessToken: string;\n private cache: ISbCache;\n private resolveCounter: number;\n public relations: RelationsType;\n public links: LinksType;\n public version: StoryblokContentVersionKeys | undefined;\n /**\n * @deprecated This property is deprecated. Use the standalone `richTextResolver` from `@storyblok/richtext` instead.\n * @see https://github.com/storyblok/richtext\n */\n public richTextResolver: unknown;\n public resolveNestedRelations: boolean;\n private stringifiedStoriesCache: Record<string, string>;\n private inlineAssets: boolean;\n\n /**\n *\n * @param config ISbConfig interface\n * @param pEndpoint string, optional\n */\n public constructor(config: ISbConfig, pEndpoint?: string) {\n let endpoint = config.endpoint || pEndpoint;\n\n if (!endpoint) {\n const protocol = config.https === false ? 'http' : 'https';\n\n if (!config.oauthToken) {\n endpoint = `${protocol}://${getRegionURL(config.region)}/${'v2' as Version}`;\n }\n else {\n endpoint = `${protocol}://${getRegionURL(config.region)}/${'v1' as Version}`;\n }\n }\n\n const headers: Headers = new Headers();\n\n headers.set('Content-Type', 'application/json');\n headers.set('Accept', 'application/json');\n\n if (config.headers) {\n const entries\n = config.headers.constructor.name === 'Headers'\n ? config.headers.entries().toArray()\n : Object.entries(config.headers);\n\n entries.forEach(([key, value]: [string, string]) => {\n headers.set(key, value);\n });\n }\n\n if (!headers.has(STORYBLOK_AGENT)) {\n headers.set(STORYBLOK_AGENT, STORYBLOK_JS_CLIENT_AGENT.defaultAgentName);\n headers.set(\n STORYBLOK_JS_CLIENT_AGENT.defaultAgentVersion,\n STORYBLOK_JS_CLIENT_AGENT.packageVersion,\n );\n }\n\n let rateLimit = 5; // per second for cdn api\n\n if (config.oauthToken) {\n headers.set('Authorization', config.oauthToken);\n rateLimit = 3; // per second for management api\n }\n\n if (config.rateLimit) {\n rateLimit = config.rateLimit;\n }\n\n this.maxRetries = config.maxRetries || 10;\n this.retriesDelay = 300;\n this.throttle = throttledQueue(\n this.throttledRequest.bind(this),\n rateLimit,\n 1000,\n );\n\n this.accessToken = config.accessToken || '';\n this.relations = {} as RelationsType;\n this.links = {} as LinksType;\n this.cache = config.cache || { clear: 'manual' };\n this.resolveCounter = 0;\n this.resolveNestedRelations = config.resolveNestedRelations || true;\n this.stringifiedStoriesCache = {} as Record<string, string>;\n this.version = config.version || StoryblokContentVersion.PUBLISHED; // the default version is published as per API documentation\n this.inlineAssets = config.inlineAssets || false;\n\n this.client = new SbFetch({\n baseURL: endpoint,\n timeout: config.timeout || 0,\n headers,\n responseInterceptor: config.responseInterceptor,\n fetch: config.fetch,\n });\n }\n\n private parseParams(params: ISbStoriesParams): ISbStoriesParams {\n if (!params.token) {\n params.token = this.getToken();\n }\n\n if (!params.cv) {\n params.cv = cacheVersions[params.token];\n }\n\n if (Array.isArray(params.resolve_relations)) {\n params.resolve_relations = params.resolve_relations.join(',');\n }\n\n if (typeof params.resolve_relations !== 'undefined') {\n params.resolve_level = 2;\n }\n\n return params;\n }\n\n private factoryParamOptions(\n url: string,\n params: ISbStoriesParams,\n ): ISbStoriesParams {\n if (isCDNUrl(url)) {\n return this.parseParams(params);\n }\n\n return params;\n }\n\n private makeRequest(\n url: string,\n params: ISbStoriesParams,\n per_page: number,\n page: number,\n fetchOptions?: ISbCustomFetch,\n ): Promise<ISbResult> {\n const query = this.factoryParamOptions(\n url,\n getOptionsPage(params, per_page, page),\n );\n\n return this.cacheResponse(url, query, undefined, fetchOptions);\n }\n\n public get(\n slug: 'cdn/links',\n params?: ISbLinksParams,\n fetchOptions?: ISbCustomFetch\n ): Promise<ISbLinksResult>;\n\n public get(\n slug: string,\n params?: ISbStoriesParams,\n fetchOptions?: ISbCustomFetch\n ): Promise<ISbResult>;\n\n public get(\n slug: string,\n params: ISbStoriesParams | ISbLinksParams = {},\n fetchOptions?: ISbCustomFetch,\n ): Promise<ISbResult | ISbLinksResult> {\n if (!params) {\n params = {} as ISbStoriesParams;\n }\n const url = `/${slug}`;\n\n // Only add version parameter for CDN URLs\n if (isCDNUrl(url)) {\n params.version = params.version || this.version;\n }\n\n const query = this.factoryParamOptions(url, params);\n\n return this.cacheResponse(url, query, undefined, fetchOptions);\n }\n\n public async getAll(\n slug: string,\n params: ISbStoriesParams = {},\n entity?: string,\n fetchOptions?: ISbCustomFetch,\n ): Promise<any[]> {\n const perPage = params?.per_page || 25;\n const url = `/${slug}`.replace(/\\/$/, '');\n const e = entity ?? url.substring(url.lastIndexOf('/') + 1);\n params.version = params.version || this.version;\n\n const firstPage = 1;\n const firstRes = await this.makeRequest(\n url,\n params,\n perPage,\n firstPage,\n fetchOptions,\n );\n const lastPage = firstRes.total ? Math.ceil(firstRes.total / (firstRes.perPage || perPage)) : 1;\n\n const restRes: any = await asyncMap(\n range(firstPage, lastPage),\n (i: number) => {\n return this.makeRequest(url, params, perPage, i + 1, fetchOptions);\n },\n );\n\n return flatMap([firstRes, ...restRes], (res: ISbFlatMapped) =>\n Object.values(res.data[e]));\n }\n\n public post(\n slug: string,\n params: ISbStoriesParams | ISbContentMangmntAPI = {},\n fetchOptions?: ISbCustomFetch,\n ): Promise<ISbResponseData> {\n const url = `/${slug}`;\n\n return this.throttle('post', url, params, fetchOptions) as Promise<ISbResponseData>;\n }\n\n public put(\n slug: string,\n params: ISbStoriesParams | ISbContentMangmntAPI = {},\n fetchOptions?: ISbCustomFetch,\n ): Promise<ISbResponseData> {\n const url = `/${slug}`;\n\n return this.throttle('put', url, params, fetchOptions) as Promise<ISbResponseData>;\n }\n\n public delete(\n slug: string,\n params: ISbStoriesParams | ISbContentMangmntAPI = {},\n fetchOptions?: ISbCustomFetch,\n ): Promise<ISbResponseData> {\n if (!params) {\n params = {} as ISbStoriesParams;\n }\n const url = `/${slug}`;\n\n return this.throttle('delete', url, params, fetchOptions) as Promise<ISbResponseData>;\n }\n\n public getStories(\n params: ISbStoriesParams = {},\n fetchOptions?: ISbCustomFetch,\n ): Promise<ISbStories> {\n this._addResolveLevel(params);\n\n return this.get('cdn/stories', params, fetchOptions);\n }\n\n public getStory(\n slug: string,\n params: ISbStoryParams = {},\n fetchOptions?: ISbCustomFetch,\n ): Promise<ISbStory> {\n this._addResolveLevel(params);\n\n return this.get(`cdn/stories/${slug}`, params, fetchOptions);\n }\n\n private getToken(): string {\n return this.accessToken;\n }\n\n public ejectInterceptor(): void {\n this.client.eject();\n }\n\n private _addResolveLevel(params: ISbStoriesParams | ISbStoryParams): void {\n if (typeof params.resolve_relations !== 'undefined') {\n params.resolve_level = 2;\n }\n }\n\n private _cleanCopy(value: LinksType): JSON {\n return JSON.parse(JSON.stringify(value));\n }\n\n private _insertLinks(\n jtree: ISbStoriesParams,\n treeItem: keyof ISbStoriesParams,\n resolveId: string,\n ): void {\n const node = jtree[treeItem];\n\n if (\n node\n && node.fieldtype === 'multilink'\n && node.linktype === 'story'\n && typeof node.id === 'string'\n && this.links[resolveId][node.id]\n ) {\n node.story = this._cleanCopy(this.links[resolveId][node.id]);\n }\n else if (\n node\n && node.linktype === 'story'\n && typeof node.uuid === 'string'\n && this.links[resolveId][node.uuid]\n ) {\n node.story = this._cleanCopy(this.links[resolveId][node.uuid]);\n }\n }\n\n /**\n *\n * @param resolveId A counter number as a string\n * @param uuid The uuid of the story\n * @returns string | object\n */\n private getStoryReference(resolveId: string, uuid: string): string | JSON {\n const result = this.relations[resolveId][uuid]\n ? JSON.parse(this.stringifiedStoriesCache[uuid] || JSON.stringify(this.relations[resolveId][uuid]))\n : uuid;\n return result;\n }\n\n /**\n * Resolves a field's value by replacing UUIDs with their corresponding story references\n * @param jtree - The JSON tree object containing the field to resolve\n * @param treeItem - The key of the field to resolve\n * @param resolveId - The unique identifier for the current resolution context\n *\n * This method handles both single string UUIDs and arrays of UUIDs:\n * - For single strings: directly replaces the UUID with the story reference\n * - For arrays: maps through each UUID and replaces with corresponding story references\n */\n private _resolveField(\n jtree: ISbStoriesParams,\n treeItem: keyof ISbStoriesParams,\n resolveId: string,\n ): void {\n const item = jtree[treeItem];\n if (typeof item === 'string') {\n jtree[treeItem] = this.getStoryReference(resolveId, item);\n }\n else if (Array.isArray(item)) {\n jtree[treeItem] = item.map(uuid =>\n this.getStoryReference(resolveId, uuid),\n ).filter(Boolean);\n }\n }\n\n /**\n * Inserts relations into the JSON tree by resolving references\n * @param jtree - The JSON tree object to process\n * @param treeItem - The current field being processed\n * @param fields - The relation patterns to resolve (string or array of strings)\n * @param resolveId - The unique identifier for the current resolution context\n *\n * This method handles two types of relation patterns:\n * 1. Nested relations: matches fields that end with the current field name\n * Example: If treeItem is \"event_type\", it matches patterns like \"*.event_type\"\n *\n * 2. Direct component relations: matches exact component.field patterns\n * Example: \"event.event_type\" for component \"event\" and field \"event_type\"\n *\n * The method supports both string and array formats for the fields parameter,\n * allowing flexible specification of relation patterns.\n */\n private _insertRelations(\n jtree: ISbStoriesParams,\n treeItem: keyof ISbStoriesParams,\n fields: string | string[],\n resolveId: string,\n ): void {\n // Check for nested relations (e.g., \"*.event_type\" or \"spots.event_type\")\n const fieldPattern = Array.isArray(fields)\n ? fields.find(f => f.endsWith(`.${treeItem}`))\n : fields.endsWith(`.${treeItem}`);\n\n if (fieldPattern) {\n // If we found a matching pattern, resolve this field\n this._resolveField(jtree, treeItem, resolveId);\n return;\n }\n\n // If no nested pattern matched, check for direct component.field pattern\n // e.g., \"event.event_type\" for a field within its immediate parent component\n const fieldPath = jtree.component ? `${jtree.component}.${treeItem}` : treeItem;\n // Check if this exact pattern exists in the fields to resolve\n if (Array.isArray(fields) ? fields.includes(fieldPath) : fields === fieldPath) {\n this._resolveField(jtree, treeItem, resolveId);\n }\n }\n\n /**\n * Recursively traverses and resolves relations in the story content tree\n * @param story - The story object containing the content to process\n * @param fields - The relation patterns to resolve\n * @param resolveId - The unique identifier for the current resolution context\n */\n private iterateTree(\n story: ISbStoryData,\n fields: string | Array<string>,\n resolveId: string,\n ): void {\n // Internal recursive function to process each node in the tree\n const enrich = (jtree: ISbStoriesParams | any, path = '') => {\n // Skip processing if node is null/undefined or marked to stop resolving\n if (!jtree || jtree._stopResolving) {\n return;\n }\n\n // Handle arrays by recursively processing each element\n // Maintains path context by adding array indices\n if (Array.isArray(jtree)) {\n jtree.forEach((item, index) => enrich(item, `${path}[${index}]`));\n }\n // Handle object nodes\n else if (typeof jtree === 'object') {\n // Process each property in the object\n for (const key in jtree) {\n // Build the current path for the context\n const newPath = path ? `${path}.${key}` : key;\n\n // If this is a component (has component and _uid) or a link,\n // attempt to resolve its relations and links\n if ((jtree.component && jtree._uid) || jtree.type === 'link') {\n this._insertRelations(jtree, key as keyof ISbStoriesParams, fields, resolveId);\n this._insertLinks(jtree, key as keyof ISbStoriesParams, resolveId);\n }\n\n // Continue traversing deeper into the tree\n // This ensures we process nested components and their relations\n enrich(jtree[key], newPath);\n }\n }\n };\n\n // Start the traversal from the story's content\n enrich(story.content);\n }\n\n private async resolveLinks(\n responseData: ISbResponseData,\n params: ISbStoriesParams,\n resolveId: string,\n ): Promise<void> {\n let links: (ISbStoryData | ISbLinkURLObject | string)[] = [];\n\n if (responseData.link_uuids) {\n const relSize = responseData.link_uuids.length;\n const chunks = [];\n const chunkSize = 50;\n\n for (let i = 0; i < relSize; i += chunkSize) {\n const end = Math.min(relSize, i + chunkSize);\n chunks.push(responseData.link_uuids.slice(i, end));\n }\n\n for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {\n const linksRes = await this.getStories({\n per_page: chunkSize,\n language: params.language,\n version: params.version,\n starts_with: params.starts_with,\n by_uuids: chunks[chunkIndex].join(','),\n });\n\n linksRes.data.stories.forEach(\n (rel: ISbStoryData | ISbLinkURLObject | string) => {\n links.push(rel);\n },\n );\n }\n }\n else {\n links = responseData.links;\n }\n\n links.forEach((story: ISbStoryData | any) => {\n this.links[resolveId][story.uuid] = {\n ...story,\n ...{ _stopResolving: true },\n };\n });\n }\n\n private async resolveRelations(\n responseData: ISbResponseData,\n params: ISbStoriesParams,\n resolveId: string,\n ): Promise<void> {\n let relations: ISbStoryData<ISbComponentType<string> & { [index: string]: any }>[] = [];\n\n if (responseData.rel_uuids) {\n const relSize = responseData.rel_uuids.length;\n const chunks = [];\n const chunkSize = 50;\n\n for (let i = 0; i < relSize; i += chunkSize) {\n const end = Math.min(relSize, i + chunkSize);\n chunks.push(responseData.rel_uuids.slice(i, end));\n }\n\n for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {\n const relationsRes = await this.getStories({\n per_page: chunkSize,\n language: params.language,\n version: params.version,\n starts_with: params.starts_with,\n by_uuids: chunks[chunkIndex].join(','),\n excluding_fields: params.excluding_fields,\n });\n\n relationsRes.data.stories.forEach((rel: ISbStoryData) => {\n relations.push(rel);\n });\n }\n\n // Replace rel_uuids with the fully resolved stories and clear it\n if (relations.length > 0) {\n responseData.rels = relations;\n delete responseData.rel_uuids;\n }\n }\n else {\n relations = responseData.rels;\n }\n\n if (relations && relations.length > 0) {\n relations.forEach((story: ISbStoryData) => {\n this.relations[resolveId][story.uuid] = {\n ...story,\n ...{ _stopResolving: true },\n };\n });\n }\n }\n\n /**\n *\n * @param responseData\n * @param params\n * @param resolveId\n * @description Resolves the relations and links of the stories\n * @returns Promise<void>\n *\n */\n private async resolveStories(\n responseData: ISbResponseData,\n params: ISbStoriesParams,\n resolveId: string,\n ): Promise<void> {\n let relationParams: string[] = [];\n\n this.links[resolveId] = {};\n this.relations[resolveId] = {};\n\n if (\n typeof params.resolve_relations !== 'undefined'\n && params.resolve_relations.length > 0\n ) {\n if (typeof params.resolve_relations === 'string') {\n relationParams = params.resolve_relations.split(',');\n }\n await this.resolveRelations(responseData, params, resolveId);\n }\n\n if (\n params.resolve_links\n && ['1', 'story', 'url', 'link'].includes(params.resolve_links)\n && (responseData.links?.length || responseData.link_uuids?.length)\n ) {\n await this.resolveLinks(responseData, params, resolveId);\n }\n\n if (this.resolveNestedRelations) {\n for (const relUuid in this.relations[resolveId]) {\n this.iterateTree(\n this.relations[resolveId][relUuid],\n relationParams,\n resolveId,\n );\n }\n }\n\n if (responseData.story) {\n this.iterateTree(responseData.story, relationParams, resolveId);\n }\n else {\n responseData.stories.forEach((story: ISbStoryData) => {\n this.iterateTree(story, relationParams, resolveId);\n });\n }\n\n this.stringifiedStoriesCache = {};\n\n delete this.links[resolveId];\n delete this.relations[resolveId];\n }\n\n private async cacheResponse(\n url: string,\n params: ISbStoriesParams,\n retries?: number,\n fetchOptions?: ISbCustomFetch,\n ): Promise<ISbResult> {\n const cacheKey = stringify({ url, params });\n const provider = this.cacheProvider();\n\n if (params.version === 'published' && url !== '/cdn/spaces/me') {\n const cache = await provider.get(cacheKey);\n if (cache) {\n return Promise.resolve(cache);\n }\n }\n\n return new Promise(async (resolve, reject) => {\n try {\n const res = (await this.throttle(\n 'get',\n url,\n params,\n fetchOptions,\n )) as ISbResponse;\n if (res.status !== 200) {\n return reject(res);\n }\n\n let response = { data: res.data, headers: res.headers } as ISbResult;\n\n if (res.headers?.['per-page']) {\n response = Object.assign({}, response, {\n perPage: res.headers['per-page']\n ? Number.parseInt(res.headers['per-page'])\n : 0,\n total: res.headers['per-page']\n ? Number.parseInt(res.headers.total)\n : 0,\n });\n }\n\n if (response.data.story || response.data.stories) {\n const resolveId = (this.resolveCounter\n = ++this.resolveCounter % 1000);\n await this.resolveStories(response.data, params, `${resolveId}`);\n response = await this.processInlineAssets(response);\n }\n\n if (params.version === 'published' && url !== '/cdn/spaces/me') {\n await provider.set(cacheKey, response);\n }\n\n const isCacheClearable = (this.cache.clear === 'onpreview' && params.version === 'draft')\n || this.cache.clear === 'auto';\n\n if (params.token && response.data.cv) {\n if (isCacheClearable\n && cacheVersions[params.token] // there is a cache\n && cacheVersions[params.token] !== response.data.cv // a new cv is incoming\n ) {\n await this.flushCache();\n }\n cacheVersions[params.token] = response.data.cv;\n }\n\n return resolve(response);\n }\n catch (error: Error | any) {\n if (error.response && error.status === 429) {\n retries = typeof retries === 'undefined' ? 0 : retries + 1;\n\n if (retries < this.maxRetries) {\n // eslint-disable-next-line no-console\n console.log(\n `Hit rate limit. Retrying in ${this.retriesDelay / 1000} seconds.`,\n );\n await delay(this.retriesDelay);\n return this.cacheResponse(url, params, retries)\n .then(resolve)\n .catch(reject);\n }\n }\n reject(error);\n }\n });\n }\n\n private throttledRequest(\n type: Method,\n url: string,\n params: ISbStoriesParams,\n fetchOptions?: ISbCustomFetch,\n ): Promise<unknown> {\n this.client.setFetchOptions(fetchOptions);\n return this.client[type](url, params);\n }\n\n public cacheVersions(): CachedVersions {\n return cacheVersions;\n }\n\n public cacheVersion(): number {\n return cacheVersions[this.accessToken];\n }\n\n public setCacheVersion(cv: number): void {\n if (this.accessToken) {\n cacheVersions[this.accessToken] = cv;\n }\n }\n\n public clearCacheVersion(): void {\n if (this.accessToken) {\n cacheVersions[this.accessToken] = 0;\n }\n }\n\n public cacheProvider(): ICacheProvider {\n switch (this.cache.type) {\n case 'memory':\n return {\n get(key: string) {\n return Promise.resolve(memory[key]);\n },\n getAll() {\n return Promise.resolve(memory as IMemoryType);\n },\n set(key: string, content: ISbResult) {\n memory[key] = content;\n return Promise.resolve(undefined);\n },\n flush() {\n memory = {};\n return Promise.resolve(undefined);\n },\n };\n case 'custom':\n if (this.cache.custom) {\n return this.cache.custom;\n }\n // eslint-disable-next-line no-fallthrough\n default:\n return {\n get() {\n return Promise.resolve();\n },\n getAll() {\n return Promise.resolve(undefined);\n },\n set() {\n return Promise.resolve(undefined);\n },\n flush() {\n return Promise.resolve(undefined);\n },\n };\n }\n }\n\n public async flushCache(): Promise<this> {\n await this.cacheProvider().flush();\n this.clearCacheVersion();\n return this;\n }\n\n private async processInlineAssets(response: ISbResult): Promise<ISbResult> {\n if (!this.inlineAssets) {\n return response;\n }\n\n const processNode = (node: ISbField): unknown => {\n if (!node || typeof node !== 'object') {\n return node;\n }\n\n // Handle arrays\n if (Array.isArray(node)) {\n return node.map(item => processNode(item));\n }\n\n // Process object\n let processedNode = { ...node };\n\n // Check if this is an asset field\n if (processedNode.fieldtype === 'asset' && Array.isArray(response.data.assets)) {\n // Replace the assets array with the actual asset objects\n processedNode = {\n ...processedNode,\n ...response.data.assets.find((asset: any) => asset.id === processedNode.id),\n };\n }\n\n // Recursively process all properties\n for (const key in processedNode) {\n if (typeof processedNode[key] === 'object') {\n processedNode[key] = processNode(processedNode[key] as ISbField);\n }\n }\n\n return processedNode;\n };\n\n // Process the story content\n if (response.data.story) {\n response.data.story.content = processNode(response.data.story.content);\n }\n\n // Process all stories if present\n if (response.data.stories) {\n response.data.stories = response.data.stories.map((story: any) => {\n story.content = processNode(story.content);\n return story;\n });\n }\n\n return response;\n }\n}\n\nexport default Storyblok;\n"],"mappings":"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