@open-mercato/sync-akeneo 0.6.7-develop.6630.1.c8a615e536 → 0.6.7-develop.6645.1.52f0b37813

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.
@@ -250,6 +250,7 @@ function createAkeneoClient(credentialsInput, deps = {}) {
250
250
  const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {
251
251
  method: "POST",
252
252
  timeoutMs: resolveAkeneoRequestTimeoutMs(),
253
+ redirect: "manual",
253
254
  headers: {
254
255
  accept: "application/json",
255
256
  "content-type": "application/json"
@@ -281,6 +282,7 @@ function createAkeneoClient(credentialsInput, deps = {}) {
281
282
  const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {
282
283
  method: "POST",
283
284
  timeoutMs: resolveAkeneoRequestTimeoutMs(),
285
+ redirect: "manual",
284
286
  headers: {
285
287
  accept: "application/json",
286
288
  "content-type": "application/json"
@@ -316,6 +318,7 @@ function createAkeneoClient(credentialsInput, deps = {}) {
316
318
  const response = await safeAkeneoFetchWithTimeout(url, {
317
319
  ...init,
318
320
  timeoutMs: resolveAkeneoRequestTimeoutMs(),
321
+ redirect: "manual",
319
322
  headers: {
320
323
  accept: "application/json",
321
324
  authorization: `Bearer ${token}`,
@@ -349,6 +352,7 @@ function createAkeneoClient(credentialsInput, deps = {}) {
349
352
  const response = await safeAkeneoFetchWithTimeout(url, {
350
353
  ...init,
351
354
  timeoutMs: resolveAkeneoRequestTimeoutMs(),
355
+ redirect: "manual",
352
356
  headers: {
353
357
  authorization: `Bearer ${token}`,
354
358
  ...init.headers
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sync_akeneo/lib/client.ts"],
4
- "sourcesContent": ["import { parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'\nimport {\n FetchTimeoutError,\n type FetchWithTimeoutInit,\n resolveTimeoutMs,\n} from '@open-mercato/shared/lib/http/fetchWithTimeout'\nimport {\n safeOutboundFetch,\n type HostLookup,\n type SafeOutboundFetchOptions,\n type UrlSafetyReason,\n} from '@open-mercato/shared/lib/url-safety'\nimport { dedupeStrings, labelFromLocalizedRecord, safeRecord, type AkeneoAttribute, type AkeneoAttributeOption, type AkeneoCategory, type AkeneoChannel, type AkeneoCredentialShape, type AkeneoFamily, type AkeneoFamilyVariant, type AkeneoLocale, type AkeneoProduct, type AkeneoProductModel } from './shared'\n\ntype TokenState = {\n accessToken: string\n refreshToken?: string | null\n expiresAt: number\n}\n\ntype AkeneoListResponse<T> = {\n _embedded?: {\n items?: T[]\n }\n _links?: {\n next?: {\n href?: string\n }\n }\n items_count?: number\n}\n\ntype AkeneoMediaFile = {\n code: string\n original_filename?: string | null\n mime_type?: string | null\n size?: number | null\n extension?: string | null\n _links?: {\n download?: {\n href?: string\n }\n }\n}\n\nexport type AkeneoClientDeps = {\n lookupHost?: HostLookup\n allowPrivate?: boolean\n fetchImpl?: SafeOutboundFetchOptions['fetchImpl']\n}\n\nexport class UnsafeAkeneoUrlError extends Error {\n public readonly reason: string\n\n constructor(reason: UrlSafetyReason, message?: string) {\n super(message ?? `Akeneo URL rejected: ${reason}`)\n this.name = 'UnsafeAkeneoUrlError'\n this.reason = reason\n }\n}\n\ntype AkeneoClient = ReturnType<typeof createAkeneoClient>\n\nconst AKENEO_URL_SUBJECT = 'Akeneo URL'\nconst DEFAULT_ALLOWED_AKENEO_HOST_PATTERNS = ['*.cloud.akeneo.com', '*.akeneo.cloud'] as const\nconst ALLOWED_AKENEO_HOSTS_ENV_KEYS = [\n 'OM_INTEGRATION_AKENEO_ALLOWED_HOSTS',\n 'OPENMERCATO_AKENEO_ALLOWED_HOSTS',\n 'AKENEO_ALLOWED_HOSTS',\n] as const\nconst ALLOW_PRIVATE_AKENEO_URLS_ENV_KEYS = [\n 'OM_INTEGRATION_AKENEO_ALLOW_PRIVATE_URLS',\n 'OPENMERCATO_AKENEO_ALLOW_PRIVATE_URLS',\n 'AKENEO_ALLOW_PRIVATE_URLS',\n] as const\n\nconst akeneoUrlErrorFactory = (reason: UrlSafetyReason, message: string) =>\n new UnsafeAkeneoUrlError(reason, message)\n\nfunction normalizeBaseUrl(url: string): string {\n return url.trim().replace(/\\/+$/g, '')\n}\n\nfunction readAllowedAkeneoHostPatterns(env: NodeJS.ProcessEnv = process.env): string[] {\n for (const key of ALLOWED_AKENEO_HOSTS_ENV_KEYS) {\n const raw = env[key]\n if (typeof raw !== 'string') continue\n const patterns = raw\n .split(',')\n .map((entry) => entry.trim().toLowerCase())\n .filter((entry) => entry.length > 0)\n if (patterns.length > 0) {\n return patterns\n }\n }\n\n return [...DEFAULT_ALLOWED_AKENEO_HOST_PATTERNS]\n}\n\nfunction isAllowPrivateAkeneoUrlsEnabled(env: NodeJS.ProcessEnv = process.env): boolean {\n for (const key of ALLOW_PRIVATE_AKENEO_URLS_ENV_KEYS) {\n const raw = env[key]\n if (typeof raw === 'string' && raw.trim().length > 0) {\n return parseBooleanWithDefault(raw, false)\n }\n }\n return false\n}\n\nfunction matchesAkeneoHostPattern(hostname: string, pattern: string): boolean {\n if (pattern.startsWith('*.')) {\n const suffix = pattern.slice(2)\n return hostname === suffix || hostname.endsWith(`.${suffix}`)\n }\n\n return hostname === pattern\n}\n\nexport function validateAkeneoApiUrl(rawUrl: string, env: NodeJS.ProcessEnv = process.env): string {\n let parsed: URL\n try {\n parsed = new URL(rawUrl.trim())\n } catch {\n throw new Error('Akeneo URL must be a valid absolute URL')\n }\n\n if (parsed.protocol !== 'https:') {\n throw new Error('Akeneo URL must use https')\n }\n if (parsed.username || parsed.password) {\n throw new Error('Akeneo URL must not include embedded credentials')\n }\n if (parsed.port && parsed.port !== '443') {\n throw new Error('Akeneo URL must not use a custom port')\n }\n if (parsed.pathname !== '/' && parsed.pathname !== '') {\n throw new Error('Akeneo URL must not include a path')\n }\n if (parsed.search || parsed.hash) {\n throw new Error('Akeneo URL must not include query parameters or fragments')\n }\n\n const hostname = parsed.hostname.toLowerCase()\n const allowedPatterns = readAllowedAkeneoHostPatterns(env)\n if (!allowedPatterns.some((pattern) => matchesAkeneoHostPattern(hostname, pattern))) {\n throw new Error(`Akeneo URL host is not allowed: ${hostname}`)\n }\n\n return normalizeBaseUrl(parsed.origin)\n}\n\nexport function normalizeAkeneoDateTime(value: string | null | undefined): string | null {\n if (typeof value !== 'string' || value.trim().length === 0) return null\n const trimmed = value.trim()\n if (/^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/.test(trimmed)) {\n return trimmed\n }\n\n const parsed = new Date(trimmed)\n if (Number.isNaN(parsed.getTime())) {\n return null\n }\n\n const year = parsed.getUTCFullYear()\n const month = String(parsed.getUTCMonth() + 1).padStart(2, '0')\n const day = String(parsed.getUTCDate()).padStart(2, '0')\n const hours = String(parsed.getUTCHours()).padStart(2, '0')\n const minutes = String(parsed.getUTCMinutes()).padStart(2, '0')\n const seconds = String(parsed.getUTCSeconds()).padStart(2, '0')\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`\n}\n\nexport function encodeAkeneoPathParam(value: string): string {\n return value\n .split('/')\n .map((segment) => encodeURIComponent(segment))\n .join('/')\n}\n\nexport function sanitizeAkeneoProductNextUrl(nextUrl: string): string {\n let url: URL\n try {\n url = new URL(nextUrl)\n } catch {\n return nextUrl\n }\n\n const rawSearch = url.searchParams.get('search')\n if (!rawSearch) {\n return url.toString()\n }\n\n try {\n const parsed = JSON.parse(rawSearch) as Record<string, unknown>\n const updatedFilters = Array.isArray(parsed.updated) ? parsed.updated : []\n const sanitizedUpdated = updatedFilters\n .map((entry) => {\n if (!entry || typeof entry !== 'object') return null\n const record = entry as Record<string, unknown>\n const normalizedValue = normalizeAkeneoDateTime(\n typeof record.value === 'string' ? record.value : null,\n )\n if (!normalizedValue) return null\n return {\n ...record,\n value: normalizedValue,\n }\n })\n .filter((entry) => entry !== null) as Record<string, unknown>[]\n\n if (sanitizedUpdated.length === 0) {\n delete parsed.updated\n } else {\n parsed.updated = sanitizedUpdated\n }\n\n if (Object.keys(parsed).length === 0) {\n url.searchParams.delete('search')\n } else {\n url.searchParams.set('search', JSON.stringify(parsed))\n }\n return url.toString()\n } catch {\n return nextUrl\n }\n}\n\nfunction coerceCredentials(credentials: Record<string, unknown>): AkeneoCredentialShape {\n const apiUrl = typeof credentials.apiUrl === 'string' ? credentials.apiUrl.trim() : ''\n const clientId = typeof credentials.clientId === 'string' ? credentials.clientId.trim() : ''\n const clientSecret = typeof credentials.clientSecret === 'string' ? credentials.clientSecret : ''\n const username = typeof credentials.username === 'string' ? credentials.username.trim() : ''\n const password = typeof credentials.password === 'string' ? credentials.password : ''\n if (!apiUrl || !clientId || !clientSecret || !username || !password) {\n throw new Error('Akeneo credentials are incomplete')\n }\n return {\n apiUrl: validateAkeneoApiUrl(apiUrl),\n clientId,\n clientSecret,\n username,\n password,\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nconst DEFAULT_AKENEO_REQUEST_TIMEOUT_MS = 30_000\nconst DEFAULT_AKENEO_MAX_RATE_LIMIT_RETRIES = 5\nconst DEFAULT_AKENEO_RETRY_AFTER_CAP_MS = 60_000\n\nfunction resolvePositiveIntEnv(raw: string | undefined, fallback: number): number {\n const parsed = Number.parseInt(raw ?? '', 10)\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback\n}\n\nfunction resolveAkeneoRequestTimeoutMs(env: NodeJS.ProcessEnv = process.env): number {\n return resolvePositiveIntEnv(env.OM_INTEGRATION_AKENEO_REQUEST_TIMEOUT_MS, DEFAULT_AKENEO_REQUEST_TIMEOUT_MS)\n}\n\nfunction resolveAkeneoMaxRateLimitRetries(env: NodeJS.ProcessEnv = process.env): number {\n return resolvePositiveIntEnv(env.OM_INTEGRATION_AKENEO_MAX_RATE_LIMIT_RETRIES, DEFAULT_AKENEO_MAX_RATE_LIMIT_RETRIES)\n}\n\nfunction clampAkeneoRetryAfterMs(retryAfterHeader: string | null, env: NodeJS.ProcessEnv = process.env): number {\n const retryAfter = Number(retryAfterHeader ?? '1')\n const requestedMs = Number.isFinite(retryAfter) ? retryAfter * 1000 : 1000\n const cap = resolvePositiveIntEnv(env.OM_INTEGRATION_AKENEO_RETRY_AFTER_CAP_MS, DEFAULT_AKENEO_RETRY_AFTER_CAP_MS)\n return Math.min(Math.max(requestedMs, 0), cap)\n}\n\nasync function safeAkeneoFetchWithTimeout(\n input: string,\n init: FetchWithTimeoutInit = {},\n deps: AkeneoClientDeps = {},\n): Promise<Response> {\n const { timeoutMs, signal, ...rest } = init\n const effectiveTimeout = resolveTimeoutMs(timeoutMs)\n const controller = new AbortController()\n const timer: ReturnType<typeof setTimeout> = setTimeout(() => {\n controller.abort(new FetchTimeoutError(input, effectiveTimeout))\n }, effectiveTimeout)\n\n const onExternalAbort = () => {\n controller.abort((signal as AbortSignal | undefined)?.reason)\n }\n\n if (signal) {\n if (signal.aborted) {\n clearTimeout(timer)\n throw signal.reason instanceof Error ? signal.reason : new DOMException('Aborted', 'AbortError')\n }\n signal.addEventListener('abort', onExternalAbort, { once: true })\n }\n\n try {\n return await safeOutboundFetch(input, { ...rest, signal: controller.signal }, {\n errorFactory: akeneoUrlErrorFactory,\n subject: AKENEO_URL_SUBJECT,\n allowPrivate: deps.allowPrivate ?? isAllowPrivateAkeneoUrlsEnabled(),\n lookupHost: deps.lookupHost,\n fetchImpl: deps.fetchImpl,\n })\n } catch (err) {\n if ((err as { name?: string } | null)?.name === 'AbortError') {\n const reason = (controller.signal as AbortSignal & { reason?: unknown }).reason\n if (reason instanceof FetchTimeoutError) throw reason\n if (reason instanceof Error) throw reason\n }\n throw err\n } finally {\n clearTimeout(timer)\n if (signal) signal.removeEventListener('abort', onExternalAbort)\n }\n}\n\nexport function createAkeneoClient(credentialsInput: Record<string, unknown>, deps: AkeneoClientDeps = {}) {\n const credentials = coerceCredentials(credentialsInput)\n const akeneoBaseUrl = new URL(`${credentials.apiUrl}/`)\n const tokenEndpointUrl = new URL('/api/oauth/v1/token', akeneoBaseUrl).toString()\n let tokenState: TokenState | null = null\n let lastAttributeOptionRequestAt = 0\n const familyCache = new Map<string, Promise<AkeneoFamily | null>>()\n const familyVariantCache = new Map<string, Promise<AkeneoFamilyVariant | null>>()\n const productModelCache = new Map<string, Promise<AkeneoProductModel | null>>()\n const attributeCache = new Map<string, Promise<AkeneoAttribute | null>>()\n const categoryCache = new Map<string, Promise<AkeneoCategory | null>>()\n const mediaFileCache = new Map<string, Promise<AkeneoMediaFile | null>>()\n\n function resolveAkeneoRequestUrl(pathOrUrl: string): string {\n const resolved = new URL(pathOrUrl, akeneoBaseUrl)\n if (resolved.origin !== akeneoBaseUrl.origin) {\n throw new Error(`Akeneo request URL must stay on the configured host: ${resolved.origin}`)\n }\n return resolved.toString()\n }\n\n function normalizeAkeneoNextUrl(nextUrl: string): string {\n return resolveAkeneoRequestUrl(nextUrl)\n }\n\n async function acquirePasswordGrantToken(): Promise<TokenState> {\n const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {\n method: 'POST',\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n grant_type: 'password',\n client_id: credentials.clientId,\n client_secret: credentials.clientSecret,\n username: credentials.username,\n password: credentials.password,\n }),\n }, deps)\n\n if (!response.ok) {\n const message = await response.text()\n throw new Error(`Akeneo authentication failed (${response.status}): ${message}`)\n }\n\n const payload = await response.json() as {\n access_token?: string\n refresh_token?: string\n expires_in?: number\n }\n if (!payload.access_token) {\n throw new Error('Akeneo authentication did not return an access token')\n }\n\n return {\n accessToken: payload.access_token,\n refreshToken: payload.refresh_token ?? null,\n expiresAt: Date.now() + ((payload.expires_in ?? 3600) * 1000) - 10_000,\n }\n }\n\n async function refreshAccessToken(current: TokenState): Promise<TokenState> {\n if (!current.refreshToken) return acquirePasswordGrantToken()\n const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {\n method: 'POST',\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n grant_type: 'refresh_token',\n client_id: credentials.clientId,\n client_secret: credentials.clientSecret,\n refresh_token: current.refreshToken,\n }),\n }, deps)\n\n if (!response.ok) {\n return acquirePasswordGrantToken()\n }\n\n const payload = await response.json() as {\n access_token?: string\n refresh_token?: string\n expires_in?: number\n }\n if (!payload.access_token) return acquirePasswordGrantToken()\n return {\n accessToken: payload.access_token,\n refreshToken: payload.refresh_token ?? current.refreshToken ?? null,\n expiresAt: Date.now() + ((payload.expires_in ?? 3600) * 1000) - 10_000,\n }\n }\n\n async function ensureToken(forceRefresh = false): Promise<string> {\n if (!forceRefresh && tokenState && tokenState.expiresAt > Date.now()) {\n return tokenState.accessToken\n }\n tokenState = tokenState ? await refreshAccessToken(tokenState) : await acquirePasswordGrantToken()\n return tokenState.accessToken\n }\n\n async function request<T>(\n pathOrUrl: string,\n init: RequestInit = {},\n retried = false,\n rateLimitAttempts = 0,\n ): Promise<T> {\n const url = resolveAkeneoRequestUrl(pathOrUrl)\n const token = await ensureToken()\n\n const response = await safeAkeneoFetchWithTimeout(url, {\n ...init,\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${token}`,\n ...init.headers,\n },\n }, deps)\n\n if (response.status === 401 && !retried) {\n await ensureToken(true)\n return request<T>(pathOrUrl, init, true, rateLimitAttempts)\n }\n\n if (response.status === 429) {\n if (rateLimitAttempts >= resolveAkeneoMaxRateLimitRetries()) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (429): ${body}`)\n }\n await sleep(clampAkeneoRetryAfterMs(response.headers.get('retry-after')))\n return request<T>(pathOrUrl, init, retried, rateLimitAttempts + 1)\n }\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (${response.status}): ${body}`)\n }\n\n if (response.status === 204) {\n return null as T\n }\n\n return response.json() as Promise<T>\n }\n\n async function requestBinary(\n pathOrUrl: string,\n init: RequestInit = {},\n retried = false,\n rateLimitAttempts = 0,\n ): Promise<{\n buffer: Buffer\n contentType: string | null\n contentLength: number | null\n }> {\n const url = resolveAkeneoRequestUrl(pathOrUrl)\n const token = await ensureToken()\n\n const response = await safeAkeneoFetchWithTimeout(url, {\n ...init,\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n headers: {\n authorization: `Bearer ${token}`,\n ...init.headers,\n },\n }, deps)\n\n if (response.status === 401 && !retried) {\n await ensureToken(true)\n return requestBinary(pathOrUrl, init, true, rateLimitAttempts)\n }\n\n if (response.status === 429) {\n if (rateLimitAttempts >= resolveAkeneoMaxRateLimitRetries()) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (429): ${body}`)\n }\n await sleep(clampAkeneoRetryAfterMs(response.headers.get('retry-after')))\n return requestBinary(pathOrUrl, init, retried, rateLimitAttempts + 1)\n }\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (${response.status}): ${body}`)\n }\n\n const arrayBuffer = await response.arrayBuffer()\n return {\n buffer: Buffer.from(arrayBuffer),\n contentType: response.headers.get('content-type'),\n contentLength: Number(response.headers.get('content-length') ?? '') || arrayBuffer.byteLength || null,\n }\n }\n\n function buildUrl(path: string, params: Record<string, string | number | boolean | undefined | null>): string {\n const url = new URL(resolveAkeneoRequestUrl(path))\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined || value === null || value === '') continue\n url.searchParams.set(key, String(value))\n }\n return url.toString()\n }\n\n async function readList<T>(pathOrUrl: string, params?: Record<string, string | number | boolean | undefined | null>): Promise<{\n items: T[]\n nextUrl: string | null\n totalEstimate: number | null\n }> {\n const payload = await request<AkeneoListResponse<T>>(\n params ? buildUrl(pathOrUrl, params) : pathOrUrl,\n )\n return {\n items: Array.isArray(payload?._embedded?.items) ? payload._embedded.items : [],\n nextUrl: typeof payload?._links?.next?.href === 'string'\n ? normalizeAkeneoNextUrl(payload._links.next.href)\n : null,\n totalEstimate: typeof payload?.items_count === 'number' ? payload.items_count : null,\n }\n }\n\n async function countProducts(updatedAfter?: string | null): Promise<number | null> {\n const params: Record<string, string | number | boolean | undefined | null> = {\n limit: 1,\n pagination_type: 'page',\n with_count: true,\n }\n const normalizedUpdatedAfter = normalizeAkeneoDateTime(updatedAfter)\n if (normalizedUpdatedAfter) {\n params.search = JSON.stringify({\n updated: [\n {\n operator: '>',\n value: normalizedUpdatedAfter,\n },\n ],\n })\n }\n\n const page = await readList<AkeneoProduct>('/api/rest/v1/products-uuid', params)\n return page.totalEstimate\n }\n\n async function getSystemProbe(): Promise<{ version: string | null }> {\n try {\n const result = await request<Record<string, unknown>>('/api/rest/v1/system-information')\n return {\n version: typeof result.pim_version === 'string' ? result.pim_version : null,\n }\n } catch {\n await readList<AkeneoAttribute>('/api/rest/v1/attributes', {\n limit: 1,\n pagination_type: 'page',\n })\n return { version: null }\n }\n }\n\n async function listProducts(options: {\n nextUrl?: string | null\n batchSize: number\n updatedAfter?: string | null\n }): Promise<{ items: AkeneoProduct[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (options.nextUrl) {\n const page = await readList<AkeneoProduct>(normalizeAkeneoNextUrl(sanitizeAkeneoProductNextUrl(options.nextUrl)))\n return {\n ...page,\n nextUrl: page.nextUrl ? normalizeAkeneoNextUrl(sanitizeAkeneoProductNextUrl(page.nextUrl)) : null,\n }\n }\n const params: Record<string, string | number | boolean | undefined | null> = {\n limit: Math.min(Math.max(options.batchSize, 1), 100),\n pagination_type: 'search_after',\n with_count: false,\n }\n const normalizedUpdatedAfter = normalizeAkeneoDateTime(options.updatedAfter)\n if (normalizedUpdatedAfter) {\n params.search = JSON.stringify({\n updated: [\n {\n operator: '>',\n value: normalizedUpdatedAfter,\n },\n ],\n })\n }\n const [page, totalEstimate] = await Promise.all([\n readList<AkeneoProduct>('/api/rest/v1/products-uuid', params),\n countProducts(normalizedUpdatedAfter).catch(() => null),\n ])\n return {\n ...page,\n totalEstimate: totalEstimate ?? page.totalEstimate,\n nextUrl: page.nextUrl ? normalizeAkeneoNextUrl(sanitizeAkeneoProductNextUrl(page.nextUrl)) : null,\n }\n }\n\n async function listCategories(nextUrl?: string | null, batchSize = 100): Promise<{ items: AkeneoCategory[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoCategory>(nextUrl)\n return readList<AkeneoCategory>('/api/rest/v1/categories', {\n limit: Math.min(Math.max(batchSize, 1), 100),\n pagination_type: 'page',\n with_count: true,\n })\n }\n\n async function listAttributes(nextUrl?: string | null, batchSize = 100): Promise<{ items: AkeneoAttribute[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoAttribute>(nextUrl)\n return readList<AkeneoAttribute>('/api/rest/v1/attributes', {\n limit: Math.min(Math.max(batchSize, 1), 100),\n pagination_type: 'page',\n with_count: true,\n })\n }\n\n async function listFamilies(nextUrl?: string | null, batchSize = 100): Promise<{ items: AkeneoFamily[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoFamily>(nextUrl)\n return readList<AkeneoFamily>('/api/rest/v1/families', {\n limit: Math.min(Math.max(batchSize, 1), 100),\n pagination_type: 'page',\n with_count: true,\n })\n }\n\n async function listFamilyVariants(\n familyCode: string,\n nextUrl?: string | null,\n batchSize = 100,\n ): Promise<{ items: AkeneoFamilyVariant[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoFamilyVariant>(nextUrl)\n return readList<AkeneoFamilyVariant>(`/api/rest/v1/families/${encodeURIComponent(familyCode)}/variants`, {\n limit: Math.min(Math.max(batchSize, 1), 100),\n with_count: false,\n })\n }\n\n async function listChannels(): Promise<AkeneoChannel[]> {\n const response = await readList<AkeneoChannel>('/api/rest/v1/channels', {\n limit: 100,\n pagination_type: 'page',\n with_count: true,\n })\n return response.items\n }\n\n async function listLocales(): Promise<AkeneoLocale[]> {\n const response = await readList<AkeneoLocale>('/api/rest/v1/locales', {\n limit: 100,\n pagination_type: 'page',\n with_count: true,\n })\n return response.items\n }\n\n async function getCategory(code: string): Promise<AkeneoCategory | null> {\n if (!categoryCache.has(code)) {\n categoryCache.set(code, request<AkeneoCategory>(`/api/rest/v1/categories/${encodeURIComponent(code)}`).catch(() => null))\n }\n return categoryCache.get(code) ?? null\n }\n\n async function getAttribute(code: string): Promise<AkeneoAttribute | null> {\n if (!attributeCache.has(code)) {\n attributeCache.set(code, request<AkeneoAttribute>(`/api/rest/v1/attributes/${encodeURIComponent(code)}`).catch(() => null))\n }\n return attributeCache.get(code) ?? null\n }\n\n async function listAttributeOptions(attributeCode: string): Promise<AkeneoAttributeOption[]> {\n const options: AkeneoAttributeOption[] = []\n let nextUrl: string | null | undefined = null\n do {\n const now = Date.now()\n const waitMs = Math.max(0, 350 - (now - lastAttributeOptionRequestAt))\n if (waitMs > 0) await sleep(waitMs)\n lastAttributeOptionRequestAt = Date.now()\n const page: { items: AkeneoAttributeOption[]; nextUrl: string | null; totalEstimate: number | null } = await readList<AkeneoAttributeOption>(\n nextUrl ?? `/api/rest/v1/attributes/${encodeURIComponent(attributeCode)}/options`,\n nextUrl\n ? undefined\n : {\n limit: 100,\n pagination_type: 'page',\n with_count: true,\n },\n )\n options.push(...page.items)\n nextUrl = page.nextUrl\n } while (nextUrl)\n return options\n }\n\n async function getFamily(code: string): Promise<AkeneoFamily | null> {\n if (!familyCache.has(code)) {\n familyCache.set(code, request<AkeneoFamily>(`/api/rest/v1/families/${encodeURIComponent(code)}`).catch(() => null))\n }\n return familyCache.get(code) ?? null\n }\n\n async function getFamilyVariant(familyCode: string, familyVariantCode: string): Promise<AkeneoFamilyVariant | null> {\n const cacheKey = `${familyCode}:${familyVariantCode}`\n if (!familyVariantCache.has(cacheKey)) {\n familyVariantCache.set(\n cacheKey,\n request<AkeneoFamilyVariant>(`/api/rest/v1/families/${encodeURIComponent(familyCode)}/variants/${encodeURIComponent(familyVariantCode)}`).catch(() => null),\n )\n }\n return familyVariantCache.get(cacheKey) ?? null\n }\n\n async function getProductModel(code: string): Promise<AkeneoProductModel | null> {\n if (!productModelCache.has(code)) {\n productModelCache.set(code, request<AkeneoProductModel>(`/api/rest/v1/product-models/${encodeURIComponent(code)}`).catch(() => null))\n }\n return productModelCache.get(code) ?? null\n }\n\n async function getMediaFile(code: string): Promise<AkeneoMediaFile | null> {\n if (!mediaFileCache.has(code)) {\n mediaFileCache.set(code, request<AkeneoMediaFile>(`/api/rest/v1/media-files/${encodeAkeneoPathParam(code)}`).catch(() => null))\n }\n return mediaFileCache.get(code) ?? null\n }\n\n async function downloadMediaFile(codeOrUrl: string): Promise<{\n buffer: Buffer\n contentType: string | null\n contentLength: number | null\n fileName: string | null\n code: string | null\n }> {\n if (codeOrUrl.startsWith('http://') || codeOrUrl.startsWith('https://') || codeOrUrl.startsWith('/')) {\n const binary = await requestBinary(codeOrUrl)\n return {\n ...binary,\n fileName: null,\n code: null,\n }\n }\n\n const mediaFile = await getMediaFile(codeOrUrl)\n if (!mediaFile) {\n throw new Error(`Akeneo media file ${codeOrUrl} was not found`)\n }\n const downloadHref = mediaFile._links?.download?.href\n const binary = await requestBinary(downloadHref || `/api/rest/v1/media-files/${encodeAkeneoPathParam(codeOrUrl)}/download`)\n return {\n ...binary,\n fileName: typeof mediaFile.original_filename === 'string' && mediaFile.original_filename.trim().length > 0\n ? mediaFile.original_filename.trim()\n : null,\n code: mediaFile.code ?? codeOrUrl,\n }\n }\n\n async function collectDiscoveryData(): Promise<{\n locales: Array<{ code: string; label: string; enabled: boolean }>\n channels: Array<{ code: string; label: string; locales: string[] }>\n attributes: Array<{ code: string; type: string; label: string; localizable: boolean; scopable: boolean; group?: string; metricFamily?: string }>\n families: Array<{ code: string; label: string; attributeCount: number }>\n familyVariants: Array<{ familyCode: string; code: string; label: string; axes: string[]; attributes: string[] }>\n version: string | null\n }> {\n const [locales, channels, attributes, families, probe] = await Promise.all([\n listLocales().catch(() => []),\n listChannels().catch(() => []),\n listAttributes(null, 100),\n listFamilies(null, 100),\n getSystemProbe().catch(() => ({ version: null })),\n ])\n\n const familyVariants: Array<{ familyCode: string; code: string; label: string; axes: string[]; attributes: string[] }> = []\n for (const family of families.items) {\n let nextUrl: string | null = null\n do {\n const page: { items: AkeneoFamilyVariant[]; nextUrl: string | null; totalEstimate: number | null } = await listFamilyVariants(family.code, nextUrl, 100).catch(() => ({\n items: [] as AkeneoFamilyVariant[],\n nextUrl: null,\n totalEstimate: null,\n }))\n familyVariants.push(...page.items.map((familyVariant) => ({\n familyCode: family.code,\n code: familyVariant.code,\n label: labelFromLocalizedRecord(familyVariant.labels ?? null, null, familyVariant.code),\n axes: dedupeStrings(familyVariant.variant_attribute_sets?.flatMap((set: { axes?: string[] }) => Array.isArray(set.axes) ? set.axes : []) ?? []),\n attributes: dedupeStrings(familyVariant.variant_attribute_sets?.flatMap((set: { attributes?: string[] }) => Array.isArray(set.attributes) ? set.attributes : []) ?? []),\n })))\n nextUrl = page.nextUrl\n } while (nextUrl)\n }\n\n return {\n locales: locales.map((locale) => ({\n code: locale.code,\n label: labelFromLocalizedRecord(locale.labels ?? null, null, locale.code),\n enabled: locale.enabled ?? false,\n })),\n channels: channels.map((channel) => ({\n code: channel.code,\n label: labelFromLocalizedRecord(channel.labels ?? null, null, channel.code),\n locales: dedupeStrings(channel.locales ?? []),\n })),\n attributes: attributes.items.map((attribute) => ({\n code: attribute.code,\n type: attribute.type,\n label: labelFromLocalizedRecord(attribute.labels ?? null, null, attribute.code),\n localizable: Boolean(attribute.localizable),\n scopable: Boolean(attribute.scopable),\n group: typeof attribute.group === 'string' && attribute.group.trim().length > 0 ? attribute.group.trim() : undefined,\n metricFamily: typeof attribute.metric_family === 'string' && attribute.metric_family.trim().length > 0\n ? attribute.metric_family.trim()\n : undefined,\n })),\n families: families.items.map((family) => ({\n code: family.code,\n label: labelFromLocalizedRecord(family.labels ?? null, null, family.code),\n attributeCount: Array.isArray(family.attributes) ? family.attributes.length : 0,\n })),\n familyVariants,\n version: probe.version,\n }\n }\n\n return {\n credentials,\n getSystemProbe,\n collectDiscoveryData,\n listProducts,\n listCategories,\n listAttributes,\n listFamilies,\n listFamilyVariants,\n listChannels,\n listLocales,\n getCategory,\n getAttribute,\n listAttributeOptions,\n getFamily,\n getFamilyVariant,\n getProductModel,\n getMediaFile,\n downloadMediaFile,\n }\n}\n\nexport type { AkeneoClient }\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'\nimport {\n FetchTimeoutError,\n type FetchWithTimeoutInit,\n resolveTimeoutMs,\n} from '@open-mercato/shared/lib/http/fetchWithTimeout'\nimport {\n safeOutboundFetch,\n type HostLookup,\n type SafeOutboundFetchOptions,\n type UrlSafetyReason,\n} from '@open-mercato/shared/lib/url-safety'\nimport { dedupeStrings, labelFromLocalizedRecord, safeRecord, type AkeneoAttribute, type AkeneoAttributeOption, type AkeneoCategory, type AkeneoChannel, type AkeneoCredentialShape, type AkeneoFamily, type AkeneoFamilyVariant, type AkeneoLocale, type AkeneoProduct, type AkeneoProductModel } from './shared'\n\ntype TokenState = {\n accessToken: string\n refreshToken?: string | null\n expiresAt: number\n}\n\ntype AkeneoListResponse<T> = {\n _embedded?: {\n items?: T[]\n }\n _links?: {\n next?: {\n href?: string\n }\n }\n items_count?: number\n}\n\ntype AkeneoMediaFile = {\n code: string\n original_filename?: string | null\n mime_type?: string | null\n size?: number | null\n extension?: string | null\n _links?: {\n download?: {\n href?: string\n }\n }\n}\n\nexport type AkeneoClientDeps = {\n lookupHost?: HostLookup\n allowPrivate?: boolean\n fetchImpl?: SafeOutboundFetchOptions['fetchImpl']\n}\n\nexport class UnsafeAkeneoUrlError extends Error {\n public readonly reason: string\n\n constructor(reason: UrlSafetyReason, message?: string) {\n super(message ?? `Akeneo URL rejected: ${reason}`)\n this.name = 'UnsafeAkeneoUrlError'\n this.reason = reason\n }\n}\n\ntype AkeneoClient = ReturnType<typeof createAkeneoClient>\n\nconst AKENEO_URL_SUBJECT = 'Akeneo URL'\nconst DEFAULT_ALLOWED_AKENEO_HOST_PATTERNS = ['*.cloud.akeneo.com', '*.akeneo.cloud'] as const\nconst ALLOWED_AKENEO_HOSTS_ENV_KEYS = [\n 'OM_INTEGRATION_AKENEO_ALLOWED_HOSTS',\n 'OPENMERCATO_AKENEO_ALLOWED_HOSTS',\n 'AKENEO_ALLOWED_HOSTS',\n] as const\nconst ALLOW_PRIVATE_AKENEO_URLS_ENV_KEYS = [\n 'OM_INTEGRATION_AKENEO_ALLOW_PRIVATE_URLS',\n 'OPENMERCATO_AKENEO_ALLOW_PRIVATE_URLS',\n 'AKENEO_ALLOW_PRIVATE_URLS',\n] as const\n\nconst akeneoUrlErrorFactory = (reason: UrlSafetyReason, message: string) =>\n new UnsafeAkeneoUrlError(reason, message)\n\nfunction normalizeBaseUrl(url: string): string {\n return url.trim().replace(/\\/+$/g, '')\n}\n\nfunction readAllowedAkeneoHostPatterns(env: NodeJS.ProcessEnv = process.env): string[] {\n for (const key of ALLOWED_AKENEO_HOSTS_ENV_KEYS) {\n const raw = env[key]\n if (typeof raw !== 'string') continue\n const patterns = raw\n .split(',')\n .map((entry) => entry.trim().toLowerCase())\n .filter((entry) => entry.length > 0)\n if (patterns.length > 0) {\n return patterns\n }\n }\n\n return [...DEFAULT_ALLOWED_AKENEO_HOST_PATTERNS]\n}\n\nfunction isAllowPrivateAkeneoUrlsEnabled(env: NodeJS.ProcessEnv = process.env): boolean {\n for (const key of ALLOW_PRIVATE_AKENEO_URLS_ENV_KEYS) {\n const raw = env[key]\n if (typeof raw === 'string' && raw.trim().length > 0) {\n return parseBooleanWithDefault(raw, false)\n }\n }\n return false\n}\n\nfunction matchesAkeneoHostPattern(hostname: string, pattern: string): boolean {\n if (pattern.startsWith('*.')) {\n const suffix = pattern.slice(2)\n return hostname === suffix || hostname.endsWith(`.${suffix}`)\n }\n\n return hostname === pattern\n}\n\nexport function validateAkeneoApiUrl(rawUrl: string, env: NodeJS.ProcessEnv = process.env): string {\n let parsed: URL\n try {\n parsed = new URL(rawUrl.trim())\n } catch {\n throw new Error('Akeneo URL must be a valid absolute URL')\n }\n\n if (parsed.protocol !== 'https:') {\n throw new Error('Akeneo URL must use https')\n }\n if (parsed.username || parsed.password) {\n throw new Error('Akeneo URL must not include embedded credentials')\n }\n if (parsed.port && parsed.port !== '443') {\n throw new Error('Akeneo URL must not use a custom port')\n }\n if (parsed.pathname !== '/' && parsed.pathname !== '') {\n throw new Error('Akeneo URL must not include a path')\n }\n if (parsed.search || parsed.hash) {\n throw new Error('Akeneo URL must not include query parameters or fragments')\n }\n\n const hostname = parsed.hostname.toLowerCase()\n const allowedPatterns = readAllowedAkeneoHostPatterns(env)\n if (!allowedPatterns.some((pattern) => matchesAkeneoHostPattern(hostname, pattern))) {\n throw new Error(`Akeneo URL host is not allowed: ${hostname}`)\n }\n\n return normalizeBaseUrl(parsed.origin)\n}\n\nexport function normalizeAkeneoDateTime(value: string | null | undefined): string | null {\n if (typeof value !== 'string' || value.trim().length === 0) return null\n const trimmed = value.trim()\n if (/^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/.test(trimmed)) {\n return trimmed\n }\n\n const parsed = new Date(trimmed)\n if (Number.isNaN(parsed.getTime())) {\n return null\n }\n\n const year = parsed.getUTCFullYear()\n const month = String(parsed.getUTCMonth() + 1).padStart(2, '0')\n const day = String(parsed.getUTCDate()).padStart(2, '0')\n const hours = String(parsed.getUTCHours()).padStart(2, '0')\n const minutes = String(parsed.getUTCMinutes()).padStart(2, '0')\n const seconds = String(parsed.getUTCSeconds()).padStart(2, '0')\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`\n}\n\nexport function encodeAkeneoPathParam(value: string): string {\n return value\n .split('/')\n .map((segment) => encodeURIComponent(segment))\n .join('/')\n}\n\nexport function sanitizeAkeneoProductNextUrl(nextUrl: string): string {\n let url: URL\n try {\n url = new URL(nextUrl)\n } catch {\n return nextUrl\n }\n\n const rawSearch = url.searchParams.get('search')\n if (!rawSearch) {\n return url.toString()\n }\n\n try {\n const parsed = JSON.parse(rawSearch) as Record<string, unknown>\n const updatedFilters = Array.isArray(parsed.updated) ? parsed.updated : []\n const sanitizedUpdated = updatedFilters\n .map((entry) => {\n if (!entry || typeof entry !== 'object') return null\n const record = entry as Record<string, unknown>\n const normalizedValue = normalizeAkeneoDateTime(\n typeof record.value === 'string' ? record.value : null,\n )\n if (!normalizedValue) return null\n return {\n ...record,\n value: normalizedValue,\n }\n })\n .filter((entry) => entry !== null) as Record<string, unknown>[]\n\n if (sanitizedUpdated.length === 0) {\n delete parsed.updated\n } else {\n parsed.updated = sanitizedUpdated\n }\n\n if (Object.keys(parsed).length === 0) {\n url.searchParams.delete('search')\n } else {\n url.searchParams.set('search', JSON.stringify(parsed))\n }\n return url.toString()\n } catch {\n return nextUrl\n }\n}\n\nfunction coerceCredentials(credentials: Record<string, unknown>): AkeneoCredentialShape {\n const apiUrl = typeof credentials.apiUrl === 'string' ? credentials.apiUrl.trim() : ''\n const clientId = typeof credentials.clientId === 'string' ? credentials.clientId.trim() : ''\n const clientSecret = typeof credentials.clientSecret === 'string' ? credentials.clientSecret : ''\n const username = typeof credentials.username === 'string' ? credentials.username.trim() : ''\n const password = typeof credentials.password === 'string' ? credentials.password : ''\n if (!apiUrl || !clientId || !clientSecret || !username || !password) {\n throw new Error('Akeneo credentials are incomplete')\n }\n return {\n apiUrl: validateAkeneoApiUrl(apiUrl),\n clientId,\n clientSecret,\n username,\n password,\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nconst DEFAULT_AKENEO_REQUEST_TIMEOUT_MS = 30_000\nconst DEFAULT_AKENEO_MAX_RATE_LIMIT_RETRIES = 5\nconst DEFAULT_AKENEO_RETRY_AFTER_CAP_MS = 60_000\n\nfunction resolvePositiveIntEnv(raw: string | undefined, fallback: number): number {\n const parsed = Number.parseInt(raw ?? '', 10)\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback\n}\n\nfunction resolveAkeneoRequestTimeoutMs(env: NodeJS.ProcessEnv = process.env): number {\n return resolvePositiveIntEnv(env.OM_INTEGRATION_AKENEO_REQUEST_TIMEOUT_MS, DEFAULT_AKENEO_REQUEST_TIMEOUT_MS)\n}\n\nfunction resolveAkeneoMaxRateLimitRetries(env: NodeJS.ProcessEnv = process.env): number {\n return resolvePositiveIntEnv(env.OM_INTEGRATION_AKENEO_MAX_RATE_LIMIT_RETRIES, DEFAULT_AKENEO_MAX_RATE_LIMIT_RETRIES)\n}\n\nfunction clampAkeneoRetryAfterMs(retryAfterHeader: string | null, env: NodeJS.ProcessEnv = process.env): number {\n const retryAfter = Number(retryAfterHeader ?? '1')\n const requestedMs = Number.isFinite(retryAfter) ? retryAfter * 1000 : 1000\n const cap = resolvePositiveIntEnv(env.OM_INTEGRATION_AKENEO_RETRY_AFTER_CAP_MS, DEFAULT_AKENEO_RETRY_AFTER_CAP_MS)\n return Math.min(Math.max(requestedMs, 0), cap)\n}\n\nasync function safeAkeneoFetchWithTimeout(\n input: string,\n init: FetchWithTimeoutInit = {},\n deps: AkeneoClientDeps = {},\n): Promise<Response> {\n const { timeoutMs, signal, ...rest } = init\n const effectiveTimeout = resolveTimeoutMs(timeoutMs)\n const controller = new AbortController()\n const timer: ReturnType<typeof setTimeout> = setTimeout(() => {\n controller.abort(new FetchTimeoutError(input, effectiveTimeout))\n }, effectiveTimeout)\n\n const onExternalAbort = () => {\n controller.abort((signal as AbortSignal | undefined)?.reason)\n }\n\n if (signal) {\n if (signal.aborted) {\n clearTimeout(timer)\n throw signal.reason instanceof Error ? signal.reason : new DOMException('Aborted', 'AbortError')\n }\n signal.addEventListener('abort', onExternalAbort, { once: true })\n }\n\n try {\n return await safeOutboundFetch(input, { ...rest, signal: controller.signal }, {\n errorFactory: akeneoUrlErrorFactory,\n subject: AKENEO_URL_SUBJECT,\n allowPrivate: deps.allowPrivate ?? isAllowPrivateAkeneoUrlsEnabled(),\n lookupHost: deps.lookupHost,\n fetchImpl: deps.fetchImpl,\n })\n } catch (err) {\n if ((err as { name?: string } | null)?.name === 'AbortError') {\n const reason = (controller.signal as AbortSignal & { reason?: unknown }).reason\n if (reason instanceof FetchTimeoutError) throw reason\n if (reason instanceof Error) throw reason\n }\n throw err\n } finally {\n clearTimeout(timer)\n if (signal) signal.removeEventListener('abort', onExternalAbort)\n }\n}\n\nexport function createAkeneoClient(credentialsInput: Record<string, unknown>, deps: AkeneoClientDeps = {}) {\n const credentials = coerceCredentials(credentialsInput)\n const akeneoBaseUrl = new URL(`${credentials.apiUrl}/`)\n const tokenEndpointUrl = new URL('/api/oauth/v1/token', akeneoBaseUrl).toString()\n let tokenState: TokenState | null = null\n let lastAttributeOptionRequestAt = 0\n const familyCache = new Map<string, Promise<AkeneoFamily | null>>()\n const familyVariantCache = new Map<string, Promise<AkeneoFamilyVariant | null>>()\n const productModelCache = new Map<string, Promise<AkeneoProductModel | null>>()\n const attributeCache = new Map<string, Promise<AkeneoAttribute | null>>()\n const categoryCache = new Map<string, Promise<AkeneoCategory | null>>()\n const mediaFileCache = new Map<string, Promise<AkeneoMediaFile | null>>()\n\n function resolveAkeneoRequestUrl(pathOrUrl: string): string {\n const resolved = new URL(pathOrUrl, akeneoBaseUrl)\n if (resolved.origin !== akeneoBaseUrl.origin) {\n throw new Error(`Akeneo request URL must stay on the configured host: ${resolved.origin}`)\n }\n return resolved.toString()\n }\n\n function normalizeAkeneoNextUrl(nextUrl: string): string {\n return resolveAkeneoRequestUrl(nextUrl)\n }\n\n async function acquirePasswordGrantToken(): Promise<TokenState> {\n const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {\n method: 'POST',\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n redirect: 'manual',\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n grant_type: 'password',\n client_id: credentials.clientId,\n client_secret: credentials.clientSecret,\n username: credentials.username,\n password: credentials.password,\n }),\n }, deps)\n\n if (!response.ok) {\n const message = await response.text()\n throw new Error(`Akeneo authentication failed (${response.status}): ${message}`)\n }\n\n const payload = await response.json() as {\n access_token?: string\n refresh_token?: string\n expires_in?: number\n }\n if (!payload.access_token) {\n throw new Error('Akeneo authentication did not return an access token')\n }\n\n return {\n accessToken: payload.access_token,\n refreshToken: payload.refresh_token ?? null,\n expiresAt: Date.now() + ((payload.expires_in ?? 3600) * 1000) - 10_000,\n }\n }\n\n async function refreshAccessToken(current: TokenState): Promise<TokenState> {\n if (!current.refreshToken) return acquirePasswordGrantToken()\n const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {\n method: 'POST',\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n redirect: 'manual',\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n grant_type: 'refresh_token',\n client_id: credentials.clientId,\n client_secret: credentials.clientSecret,\n refresh_token: current.refreshToken,\n }),\n }, deps)\n\n if (!response.ok) {\n return acquirePasswordGrantToken()\n }\n\n const payload = await response.json() as {\n access_token?: string\n refresh_token?: string\n expires_in?: number\n }\n if (!payload.access_token) return acquirePasswordGrantToken()\n return {\n accessToken: payload.access_token,\n refreshToken: payload.refresh_token ?? current.refreshToken ?? null,\n expiresAt: Date.now() + ((payload.expires_in ?? 3600) * 1000) - 10_000,\n }\n }\n\n async function ensureToken(forceRefresh = false): Promise<string> {\n if (!forceRefresh && tokenState && tokenState.expiresAt > Date.now()) {\n return tokenState.accessToken\n }\n tokenState = tokenState ? await refreshAccessToken(tokenState) : await acquirePasswordGrantToken()\n return tokenState.accessToken\n }\n\n async function request<T>(\n pathOrUrl: string,\n init: RequestInit = {},\n retried = false,\n rateLimitAttempts = 0,\n ): Promise<T> {\n const url = resolveAkeneoRequestUrl(pathOrUrl)\n const token = await ensureToken()\n\n const response = await safeAkeneoFetchWithTimeout(url, {\n ...init,\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n redirect: 'manual',\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${token}`,\n ...init.headers,\n },\n }, deps)\n\n if (response.status === 401 && !retried) {\n await ensureToken(true)\n return request<T>(pathOrUrl, init, true, rateLimitAttempts)\n }\n\n if (response.status === 429) {\n if (rateLimitAttempts >= resolveAkeneoMaxRateLimitRetries()) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (429): ${body}`)\n }\n await sleep(clampAkeneoRetryAfterMs(response.headers.get('retry-after')))\n return request<T>(pathOrUrl, init, retried, rateLimitAttempts + 1)\n }\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (${response.status}): ${body}`)\n }\n\n if (response.status === 204) {\n return null as T\n }\n\n return response.json() as Promise<T>\n }\n\n async function requestBinary(\n pathOrUrl: string,\n init: RequestInit = {},\n retried = false,\n rateLimitAttempts = 0,\n ): Promise<{\n buffer: Buffer\n contentType: string | null\n contentLength: number | null\n }> {\n const url = resolveAkeneoRequestUrl(pathOrUrl)\n const token = await ensureToken()\n\n const response = await safeAkeneoFetchWithTimeout(url, {\n ...init,\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n redirect: 'manual',\n headers: {\n authorization: `Bearer ${token}`,\n ...init.headers,\n },\n }, deps)\n\n if (response.status === 401 && !retried) {\n await ensureToken(true)\n return requestBinary(pathOrUrl, init, true, rateLimitAttempts)\n }\n\n if (response.status === 429) {\n if (rateLimitAttempts >= resolveAkeneoMaxRateLimitRetries()) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (429): ${body}`)\n }\n await sleep(clampAkeneoRetryAfterMs(response.headers.get('retry-after')))\n return requestBinary(pathOrUrl, init, retried, rateLimitAttempts + 1)\n }\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (${response.status}): ${body}`)\n }\n\n const arrayBuffer = await response.arrayBuffer()\n return {\n buffer: Buffer.from(arrayBuffer),\n contentType: response.headers.get('content-type'),\n contentLength: Number(response.headers.get('content-length') ?? '') || arrayBuffer.byteLength || null,\n }\n }\n\n function buildUrl(path: string, params: Record<string, string | number | boolean | undefined | null>): string {\n const url = new URL(resolveAkeneoRequestUrl(path))\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined || value === null || value === '') continue\n url.searchParams.set(key, String(value))\n }\n return url.toString()\n }\n\n async function readList<T>(pathOrUrl: string, params?: Record<string, string | number | boolean | undefined | null>): Promise<{\n items: T[]\n nextUrl: string | null\n totalEstimate: number | null\n }> {\n const payload = await request<AkeneoListResponse<T>>(\n params ? buildUrl(pathOrUrl, params) : pathOrUrl,\n )\n return {\n items: Array.isArray(payload?._embedded?.items) ? payload._embedded.items : [],\n nextUrl: typeof payload?._links?.next?.href === 'string'\n ? normalizeAkeneoNextUrl(payload._links.next.href)\n : null,\n totalEstimate: typeof payload?.items_count === 'number' ? payload.items_count : null,\n }\n }\n\n async function countProducts(updatedAfter?: string | null): Promise<number | null> {\n const params: Record<string, string | number | boolean | undefined | null> = {\n limit: 1,\n pagination_type: 'page',\n with_count: true,\n }\n const normalizedUpdatedAfter = normalizeAkeneoDateTime(updatedAfter)\n if (normalizedUpdatedAfter) {\n params.search = JSON.stringify({\n updated: [\n {\n operator: '>',\n value: normalizedUpdatedAfter,\n },\n ],\n })\n }\n\n const page = await readList<AkeneoProduct>('/api/rest/v1/products-uuid', params)\n return page.totalEstimate\n }\n\n async function getSystemProbe(): Promise<{ version: string | null }> {\n try {\n const result = await request<Record<string, unknown>>('/api/rest/v1/system-information')\n return {\n version: typeof result.pim_version === 'string' ? result.pim_version : null,\n }\n } catch {\n await readList<AkeneoAttribute>('/api/rest/v1/attributes', {\n limit: 1,\n pagination_type: 'page',\n })\n return { version: null }\n }\n }\n\n async function listProducts(options: {\n nextUrl?: string | null\n batchSize: number\n updatedAfter?: string | null\n }): Promise<{ items: AkeneoProduct[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (options.nextUrl) {\n const page = await readList<AkeneoProduct>(normalizeAkeneoNextUrl(sanitizeAkeneoProductNextUrl(options.nextUrl)))\n return {\n ...page,\n nextUrl: page.nextUrl ? normalizeAkeneoNextUrl(sanitizeAkeneoProductNextUrl(page.nextUrl)) : null,\n }\n }\n const params: Record<string, string | number | boolean | undefined | null> = {\n limit: Math.min(Math.max(options.batchSize, 1), 100),\n pagination_type: 'search_after',\n with_count: false,\n }\n const normalizedUpdatedAfter = normalizeAkeneoDateTime(options.updatedAfter)\n if (normalizedUpdatedAfter) {\n params.search = JSON.stringify({\n updated: [\n {\n operator: '>',\n value: normalizedUpdatedAfter,\n },\n ],\n })\n }\n const [page, totalEstimate] = await Promise.all([\n readList<AkeneoProduct>('/api/rest/v1/products-uuid', params),\n countProducts(normalizedUpdatedAfter).catch(() => null),\n ])\n return {\n ...page,\n totalEstimate: totalEstimate ?? page.totalEstimate,\n nextUrl: page.nextUrl ? normalizeAkeneoNextUrl(sanitizeAkeneoProductNextUrl(page.nextUrl)) : null,\n }\n }\n\n async function listCategories(nextUrl?: string | null, batchSize = 100): Promise<{ items: AkeneoCategory[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoCategory>(nextUrl)\n return readList<AkeneoCategory>('/api/rest/v1/categories', {\n limit: Math.min(Math.max(batchSize, 1), 100),\n pagination_type: 'page',\n with_count: true,\n })\n }\n\n async function listAttributes(nextUrl?: string | null, batchSize = 100): Promise<{ items: AkeneoAttribute[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoAttribute>(nextUrl)\n return readList<AkeneoAttribute>('/api/rest/v1/attributes', {\n limit: Math.min(Math.max(batchSize, 1), 100),\n pagination_type: 'page',\n with_count: true,\n })\n }\n\n async function listFamilies(nextUrl?: string | null, batchSize = 100): Promise<{ items: AkeneoFamily[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoFamily>(nextUrl)\n return readList<AkeneoFamily>('/api/rest/v1/families', {\n limit: Math.min(Math.max(batchSize, 1), 100),\n pagination_type: 'page',\n with_count: true,\n })\n }\n\n async function listFamilyVariants(\n familyCode: string,\n nextUrl?: string | null,\n batchSize = 100,\n ): Promise<{ items: AkeneoFamilyVariant[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoFamilyVariant>(nextUrl)\n return readList<AkeneoFamilyVariant>(`/api/rest/v1/families/${encodeURIComponent(familyCode)}/variants`, {\n limit: Math.min(Math.max(batchSize, 1), 100),\n with_count: false,\n })\n }\n\n async function listChannels(): Promise<AkeneoChannel[]> {\n const response = await readList<AkeneoChannel>('/api/rest/v1/channels', {\n limit: 100,\n pagination_type: 'page',\n with_count: true,\n })\n return response.items\n }\n\n async function listLocales(): Promise<AkeneoLocale[]> {\n const response = await readList<AkeneoLocale>('/api/rest/v1/locales', {\n limit: 100,\n pagination_type: 'page',\n with_count: true,\n })\n return response.items\n }\n\n async function getCategory(code: string): Promise<AkeneoCategory | null> {\n if (!categoryCache.has(code)) {\n categoryCache.set(code, request<AkeneoCategory>(`/api/rest/v1/categories/${encodeURIComponent(code)}`).catch(() => null))\n }\n return categoryCache.get(code) ?? null\n }\n\n async function getAttribute(code: string): Promise<AkeneoAttribute | null> {\n if (!attributeCache.has(code)) {\n attributeCache.set(code, request<AkeneoAttribute>(`/api/rest/v1/attributes/${encodeURIComponent(code)}`).catch(() => null))\n }\n return attributeCache.get(code) ?? null\n }\n\n async function listAttributeOptions(attributeCode: string): Promise<AkeneoAttributeOption[]> {\n const options: AkeneoAttributeOption[] = []\n let nextUrl: string | null | undefined = null\n do {\n const now = Date.now()\n const waitMs = Math.max(0, 350 - (now - lastAttributeOptionRequestAt))\n if (waitMs > 0) await sleep(waitMs)\n lastAttributeOptionRequestAt = Date.now()\n const page: { items: AkeneoAttributeOption[]; nextUrl: string | null; totalEstimate: number | null } = await readList<AkeneoAttributeOption>(\n nextUrl ?? `/api/rest/v1/attributes/${encodeURIComponent(attributeCode)}/options`,\n nextUrl\n ? undefined\n : {\n limit: 100,\n pagination_type: 'page',\n with_count: true,\n },\n )\n options.push(...page.items)\n nextUrl = page.nextUrl\n } while (nextUrl)\n return options\n }\n\n async function getFamily(code: string): Promise<AkeneoFamily | null> {\n if (!familyCache.has(code)) {\n familyCache.set(code, request<AkeneoFamily>(`/api/rest/v1/families/${encodeURIComponent(code)}`).catch(() => null))\n }\n return familyCache.get(code) ?? null\n }\n\n async function getFamilyVariant(familyCode: string, familyVariantCode: string): Promise<AkeneoFamilyVariant | null> {\n const cacheKey = `${familyCode}:${familyVariantCode}`\n if (!familyVariantCache.has(cacheKey)) {\n familyVariantCache.set(\n cacheKey,\n request<AkeneoFamilyVariant>(`/api/rest/v1/families/${encodeURIComponent(familyCode)}/variants/${encodeURIComponent(familyVariantCode)}`).catch(() => null),\n )\n }\n return familyVariantCache.get(cacheKey) ?? null\n }\n\n async function getProductModel(code: string): Promise<AkeneoProductModel | null> {\n if (!productModelCache.has(code)) {\n productModelCache.set(code, request<AkeneoProductModel>(`/api/rest/v1/product-models/${encodeURIComponent(code)}`).catch(() => null))\n }\n return productModelCache.get(code) ?? null\n }\n\n async function getMediaFile(code: string): Promise<AkeneoMediaFile | null> {\n if (!mediaFileCache.has(code)) {\n mediaFileCache.set(code, request<AkeneoMediaFile>(`/api/rest/v1/media-files/${encodeAkeneoPathParam(code)}`).catch(() => null))\n }\n return mediaFileCache.get(code) ?? null\n }\n\n async function downloadMediaFile(codeOrUrl: string): Promise<{\n buffer: Buffer\n contentType: string | null\n contentLength: number | null\n fileName: string | null\n code: string | null\n }> {\n if (codeOrUrl.startsWith('http://') || codeOrUrl.startsWith('https://') || codeOrUrl.startsWith('/')) {\n const binary = await requestBinary(codeOrUrl)\n return {\n ...binary,\n fileName: null,\n code: null,\n }\n }\n\n const mediaFile = await getMediaFile(codeOrUrl)\n if (!mediaFile) {\n throw new Error(`Akeneo media file ${codeOrUrl} was not found`)\n }\n const downloadHref = mediaFile._links?.download?.href\n const binary = await requestBinary(downloadHref || `/api/rest/v1/media-files/${encodeAkeneoPathParam(codeOrUrl)}/download`)\n return {\n ...binary,\n fileName: typeof mediaFile.original_filename === 'string' && mediaFile.original_filename.trim().length > 0\n ? mediaFile.original_filename.trim()\n : null,\n code: mediaFile.code ?? codeOrUrl,\n }\n }\n\n async function collectDiscoveryData(): Promise<{\n locales: Array<{ code: string; label: string; enabled: boolean }>\n channels: Array<{ code: string; label: string; locales: string[] }>\n attributes: Array<{ code: string; type: string; label: string; localizable: boolean; scopable: boolean; group?: string; metricFamily?: string }>\n families: Array<{ code: string; label: string; attributeCount: number }>\n familyVariants: Array<{ familyCode: string; code: string; label: string; axes: string[]; attributes: string[] }>\n version: string | null\n }> {\n const [locales, channels, attributes, families, probe] = await Promise.all([\n listLocales().catch(() => []),\n listChannels().catch(() => []),\n listAttributes(null, 100),\n listFamilies(null, 100),\n getSystemProbe().catch(() => ({ version: null })),\n ])\n\n const familyVariants: Array<{ familyCode: string; code: string; label: string; axes: string[]; attributes: string[] }> = []\n for (const family of families.items) {\n let nextUrl: string | null = null\n do {\n const page: { items: AkeneoFamilyVariant[]; nextUrl: string | null; totalEstimate: number | null } = await listFamilyVariants(family.code, nextUrl, 100).catch(() => ({\n items: [] as AkeneoFamilyVariant[],\n nextUrl: null,\n totalEstimate: null,\n }))\n familyVariants.push(...page.items.map((familyVariant) => ({\n familyCode: family.code,\n code: familyVariant.code,\n label: labelFromLocalizedRecord(familyVariant.labels ?? null, null, familyVariant.code),\n axes: dedupeStrings(familyVariant.variant_attribute_sets?.flatMap((set: { axes?: string[] }) => Array.isArray(set.axes) ? set.axes : []) ?? []),\n attributes: dedupeStrings(familyVariant.variant_attribute_sets?.flatMap((set: { attributes?: string[] }) => Array.isArray(set.attributes) ? set.attributes : []) ?? []),\n })))\n nextUrl = page.nextUrl\n } while (nextUrl)\n }\n\n return {\n locales: locales.map((locale) => ({\n code: locale.code,\n label: labelFromLocalizedRecord(locale.labels ?? null, null, locale.code),\n enabled: locale.enabled ?? false,\n })),\n channels: channels.map((channel) => ({\n code: channel.code,\n label: labelFromLocalizedRecord(channel.labels ?? null, null, channel.code),\n locales: dedupeStrings(channel.locales ?? []),\n })),\n attributes: attributes.items.map((attribute) => ({\n code: attribute.code,\n type: attribute.type,\n label: labelFromLocalizedRecord(attribute.labels ?? null, null, attribute.code),\n localizable: Boolean(attribute.localizable),\n scopable: Boolean(attribute.scopable),\n group: typeof attribute.group === 'string' && attribute.group.trim().length > 0 ? attribute.group.trim() : undefined,\n metricFamily: typeof attribute.metric_family === 'string' && attribute.metric_family.trim().length > 0\n ? attribute.metric_family.trim()\n : undefined,\n })),\n families: families.items.map((family) => ({\n code: family.code,\n label: labelFromLocalizedRecord(family.labels ?? null, null, family.code),\n attributeCount: Array.isArray(family.attributes) ? family.attributes.length : 0,\n })),\n familyVariants,\n version: probe.version,\n }\n }\n\n return {\n credentials,\n getSystemProbe,\n collectDiscoveryData,\n listProducts,\n listCategories,\n listAttributes,\n listFamilies,\n listFamilyVariants,\n listChannels,\n listLocales,\n getCategory,\n getAttribute,\n listAttributeOptions,\n getFamily,\n getFamilyVariant,\n getProductModel,\n getMediaFile,\n downloadMediaFile,\n }\n}\n\nexport type { AkeneoClient }\n"],
5
+ "mappings": "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",
6
6
  "names": ["page", "binary"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/sync-akeneo",
3
- "version": "0.6.7-develop.6630.1.c8a615e536",
3
+ "version": "0.6.7-develop.6645.1.52f0b37813",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -53,18 +53,18 @@
53
53
  }
54
54
  },
55
55
  "dependencies": {
56
- "@open-mercato/core": "0.6.7-develop.6630.1.c8a615e536",
57
- "@open-mercato/ui": "0.6.7-develop.6630.1.c8a615e536",
56
+ "@open-mercato/core": "0.6.7-develop.6645.1.52f0b37813",
57
+ "@open-mercato/ui": "0.6.7-develop.6645.1.52f0b37813",
58
58
  "node-html-markdown": "^2.0.0"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "@mikro-orm/postgresql": "^7.0.14",
62
- "@open-mercato/shared": "0.6.7-develop.6630.1.c8a615e536",
62
+ "@open-mercato/shared": "0.6.7-develop.6645.1.52f0b37813",
63
63
  "react": "^19.0.0",
64
64
  "react-dom": "^19.0.0"
65
65
  },
66
66
  "devDependencies": {
67
- "@open-mercato/shared": "0.6.7-develop.6630.1.c8a615e536",
67
+ "@open-mercato/shared": "0.6.7-develop.6645.1.52f0b37813",
68
68
  "@types/jest": "^30.0.0",
69
69
  "@types/react": "^19.2.17",
70
70
  "@types/react-dom": "^19.2.3",
@@ -32,6 +32,15 @@ function jsonResponse(body: unknown, init?: ResponseInit): Response {
32
32
  })
33
33
  }
34
34
 
35
+ function redirectResponse(): Response {
36
+ return new Response('redirected', {
37
+ status: 302,
38
+ headers: {
39
+ location: 'http://169.254.169.254/latest/meta-data/',
40
+ },
41
+ })
42
+ }
43
+
35
44
  describe('akeneo client helpers', () => {
36
45
  it('normalizes ISO timestamps to Akeneo query format', () => {
37
46
  expect(normalizeAkeneoDateTime('2026-03-10T12:15:30.000Z')).toBe('2026-03-10 12:15:30')
@@ -174,6 +183,60 @@ describe('akeneo client security', () => {
174
183
  }))
175
184
  })
176
185
 
186
+ it('does not automatically follow redirects while acquiring an access token', async () => {
187
+ const fetchMock = global.fetch as jest.MockedFunction<typeof fetch>
188
+ fetchMock.mockImplementation(async (_input, init) => (
189
+ (init as RequestInit | undefined)?.redirect === 'manual'
190
+ ? redirectResponse()
191
+ : new Response('internal metadata leaked through redirect', { status: 500 })
192
+ ))
193
+
194
+ const client = createAkeneoClient(validCredentials)
195
+ await expect(client.getSystemProbe()).rejects.toThrow('Akeneo authentication failed (302)')
196
+ })
197
+
198
+ it('does not automatically follow redirects on authenticated API requests', async () => {
199
+ const fetchMock = global.fetch as jest.MockedFunction<typeof fetch>
200
+ fetchMock.mockImplementation(async (input, init) => {
201
+ if (String(input).endsWith('/api/oauth/v1/token')) {
202
+ return jsonResponse({ access_token: 'token-123', expires_in: 3600 })
203
+ }
204
+ return (init as RequestInit | undefined)?.redirect === 'manual'
205
+ ? redirectResponse()
206
+ : new Response('internal metadata leaked through redirect', { status: 500 })
207
+ })
208
+
209
+ const client = createAkeneoClient(validCredentials)
210
+ await expect(client.getSystemProbe()).rejects.toThrow('Akeneo request failed (302)')
211
+ })
212
+
213
+ it('does not automatically follow redirects on media downloads', async () => {
214
+ const fetchMock = global.fetch as jest.MockedFunction<typeof fetch>
215
+ fetchMock.mockImplementation(async (input, init) => {
216
+ const url = String(input)
217
+ if (url.endsWith('/api/oauth/v1/token')) {
218
+ return jsonResponse({ access_token: 'token-123', expires_in: 3600 })
219
+ }
220
+ if (url.endsWith('/api/rest/v1/media-files/asset-1')) {
221
+ return jsonResponse({
222
+ code: 'asset-1',
223
+ original_filename: 'asset.jpg',
224
+ _links: {
225
+ download: {
226
+ href: 'https://tenant.cloud.akeneo.com/api/rest/v1/media-files/asset-1/download',
227
+ },
228
+ },
229
+ })
230
+ }
231
+ return (init as RequestInit | undefined)?.redirect === 'manual'
232
+ ? redirectResponse()
233
+ : new Response('internal metadata leaked through redirect', { status: 500 })
234
+ })
235
+
236
+ const client = createAkeneoClient(validCredentials)
237
+ await expect(client.downloadMediaFile('asset-1')).rejects.toThrow('Akeneo request failed (302)')
238
+ })
239
+
177
240
  it('falls back to the attributes reachability probe and reports an unknown version (issue #3621)', async () => {
178
241
  const fetchMock = global.fetch as jest.MockedFunction<typeof fetch>
179
242
  fetchMock
@@ -345,6 +345,7 @@ export function createAkeneoClient(credentialsInput: Record<string, unknown>, de
345
345
  const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {
346
346
  method: 'POST',
347
347
  timeoutMs: resolveAkeneoRequestTimeoutMs(),
348
+ redirect: 'manual',
348
349
  headers: {
349
350
  accept: 'application/json',
350
351
  'content-type': 'application/json',
@@ -384,6 +385,7 @@ export function createAkeneoClient(credentialsInput: Record<string, unknown>, de
384
385
  const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {
385
386
  method: 'POST',
386
387
  timeoutMs: resolveAkeneoRequestTimeoutMs(),
388
+ redirect: 'manual',
387
389
  headers: {
388
390
  accept: 'application/json',
389
391
  'content-type': 'application/json',
@@ -433,6 +435,7 @@ export function createAkeneoClient(credentialsInput: Record<string, unknown>, de
433
435
  const response = await safeAkeneoFetchWithTimeout(url, {
434
436
  ...init,
435
437
  timeoutMs: resolveAkeneoRequestTimeoutMs(),
438
+ redirect: 'manual',
436
439
  headers: {
437
440
  accept: 'application/json',
438
441
  authorization: `Bearer ${token}`,
@@ -482,6 +485,7 @@ export function createAkeneoClient(credentialsInput: Record<string, unknown>, de
482
485
  const response = await safeAkeneoFetchWithTimeout(url, {
483
486
  ...init,
484
487
  timeoutMs: resolveAkeneoRequestTimeoutMs(),
488
+ redirect: 'manual',
485
489
  headers: {
486
490
  authorization: `Bearer ${token}`,
487
491
  ...init.headers,