@open-mercato/sync-akeneo 0.9.1-develop.7553.1.8dbab05b5a → 0.9.1-develop.7567.1.b5e80ff620

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.
@@ -1,3 +1,4 @@
1
+ import { resolveAttachmentMaxBytes } from "@open-mercato/core/modules/attachments/lib/upload-limits";
1
2
  import { parseBooleanWithDefault } from "@open-mercato/shared/lib/boolean";
2
3
  import {
3
4
  FetchTimeoutError,
@@ -14,6 +15,16 @@ class UnsafeAkeneoUrlError extends Error {
14
15
  this.reason = reason;
15
16
  }
16
17
  }
18
+ class AkeneoResponseTooLargeError extends Error {
19
+ constructor(limitBytes) {
20
+ super(`Akeneo response exceeds the ${limitBytes} byte limit`);
21
+ this.name = "AkeneoResponseTooLargeError";
22
+ this.limitBytes = limitBytes;
23
+ }
24
+ }
25
+ function isAkeneoResponseTooLargeError(error) {
26
+ return error instanceof Error && error.name === "AkeneoResponseTooLargeError";
27
+ }
17
28
  const AKENEO_URL_SUBJECT = "Akeneo URL";
18
29
  const DEFAULT_ALLOWED_AKENEO_HOST_PATTERNS = ["*.cloud.akeneo.com", "*.akeneo.cloud"];
19
30
  const ALLOWED_AKENEO_HOSTS_ENV_KEYS = [
@@ -171,6 +182,8 @@ function sleep(ms) {
171
182
  const DEFAULT_AKENEO_REQUEST_TIMEOUT_MS = 3e4;
172
183
  const DEFAULT_AKENEO_MAX_RATE_LIMIT_RETRIES = 5;
173
184
  const DEFAULT_AKENEO_RETRY_AFTER_CAP_MS = 6e4;
185
+ const DEFAULT_AKENEO_MAX_RESPONSE_BYTES = 32 * 1024 * 1024;
186
+ const AKENEO_ERROR_BODY_MAX_BYTES = 64 * 1024;
174
187
  function resolvePositiveIntEnv(raw, fallback) {
175
188
  const parsed = Number.parseInt(raw ?? "", 10);
176
189
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
@@ -187,7 +200,74 @@ function clampAkeneoRetryAfterMs(retryAfterHeader, env = process.env) {
187
200
  const cap = resolvePositiveIntEnv(env.OM_INTEGRATION_AKENEO_RETRY_AFTER_CAP_MS, DEFAULT_AKENEO_RETRY_AFTER_CAP_MS);
188
201
  return Math.min(Math.max(requestedMs, 0), cap);
189
202
  }
190
- async function safeAkeneoFetchWithTimeout(input, init = {}, deps = {}) {
203
+ function resolveAkeneoMaxResponseBytes(env = process.env) {
204
+ return resolvePositiveIntEnv(env.OM_INTEGRATION_AKENEO_MAX_RESPONSE_BYTES, DEFAULT_AKENEO_MAX_RESPONSE_BYTES);
205
+ }
206
+ function abortReasonError(signal) {
207
+ return signal.reason instanceof Error ? signal.reason : new DOMException("Aborted", "AbortError");
208
+ }
209
+ function settleBeforeAbort(pending, signal) {
210
+ if (signal.aborted) return Promise.reject(abortReasonError(signal));
211
+ return new Promise((resolve, reject) => {
212
+ const onAbort = () => reject(abortReasonError(signal));
213
+ signal.addEventListener("abort", onAbort, { once: true });
214
+ pending.then(
215
+ (value) => {
216
+ signal.removeEventListener("abort", onAbort);
217
+ resolve(value);
218
+ },
219
+ (error) => {
220
+ signal.removeEventListener("abort", onAbort);
221
+ reject(error);
222
+ }
223
+ );
224
+ });
225
+ }
226
+ function discardAkeneoBody(response) {
227
+ void response.body?.cancel().catch(() => void 0);
228
+ }
229
+ async function readAkeneoBody(response, options) {
230
+ const { maxBytes, signal, truncate = false } = options;
231
+ const declaredLength = Number(response.headers.get("content-length") ?? "");
232
+ if (!truncate && Number.isFinite(declaredLength) && declaredLength > maxBytes) {
233
+ discardAkeneoBody(response);
234
+ throw new AkeneoResponseTooLargeError(maxBytes);
235
+ }
236
+ if (!response.body) return Buffer.alloc(0);
237
+ const reader = response.body.getReader();
238
+ const chunks = [];
239
+ let receivedBytes = 0;
240
+ let finished = false;
241
+ try {
242
+ while (true) {
243
+ const { done, value } = await settleBeforeAbort(reader.read(), signal);
244
+ if (done) {
245
+ finished = true;
246
+ break;
247
+ }
248
+ if (receivedBytes + value.byteLength > maxBytes) {
249
+ if (!truncate) throw new AkeneoResponseTooLargeError(maxBytes);
250
+ chunks.push(value.subarray(0, maxBytes - receivedBytes));
251
+ receivedBytes = maxBytes;
252
+ break;
253
+ }
254
+ chunks.push(value);
255
+ receivedBytes += value.byteLength;
256
+ }
257
+ } finally {
258
+ if (!finished) void reader.cancel().catch(() => void 0);
259
+ }
260
+ return Buffer.concat(chunks, receivedBytes);
261
+ }
262
+ async function readAkeneoJson(response, signal) {
263
+ const bytes = await readAkeneoBody(response, { maxBytes: resolveAkeneoMaxResponseBytes(), signal });
264
+ return JSON.parse(new TextDecoder().decode(bytes));
265
+ }
266
+ async function readAkeneoErrorText(response, signal) {
267
+ const bytes = await readAkeneoBody(response, { maxBytes: AKENEO_ERROR_BODY_MAX_BYTES, signal, truncate: true });
268
+ return new TextDecoder().decode(bytes);
269
+ }
270
+ async function withAkeneoResponse(input, init, deps, consume) {
191
271
  const { timeoutMs, signal, ...rest } = init;
192
272
  const effectiveTimeout = resolveTimeoutMs(timeoutMs);
193
273
  const controller = new AbortController();
@@ -205,13 +285,14 @@ async function safeAkeneoFetchWithTimeout(input, init = {}, deps = {}) {
205
285
  signal.addEventListener("abort", onExternalAbort, { once: true });
206
286
  }
207
287
  try {
208
- return await safeOutboundFetch(input, { ...rest, signal: controller.signal }, {
288
+ const response = await safeOutboundFetch(input, { ...rest, signal: controller.signal }, {
209
289
  errorFactory: akeneoUrlErrorFactory,
210
290
  subject: AKENEO_URL_SUBJECT,
211
291
  allowPrivate: deps.allowPrivate ?? isAllowPrivateAkeneoUrlsEnabled(),
212
292
  lookupHost: deps.lookupHost,
213
293
  fetchImpl: deps.fetchImpl
214
294
  });
295
+ return await consume(response, controller.signal);
215
296
  } catch (err) {
216
297
  if (err?.name === "AbortError") {
217
298
  const reason = controller.signal.reason;
@@ -247,7 +328,7 @@ function createAkeneoClient(credentialsInput, deps = {}) {
247
328
  return resolveAkeneoRequestUrl(nextUrl);
248
329
  }
249
330
  async function acquirePasswordGrantToken() {
250
- const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {
331
+ const payload = await withAkeneoResponse(tokenEndpointUrl, {
251
332
  method: "POST",
252
333
  timeoutMs: resolveAkeneoRequestTimeoutMs(),
253
334
  redirect: "manual",
@@ -262,12 +343,13 @@ function createAkeneoClient(credentialsInput, deps = {}) {
262
343
  username: credentials.username,
263
344
  password: credentials.password
264
345
  })
265
- }, deps);
266
- if (!response.ok) {
267
- const message = await response.text();
268
- throw new Error(`Akeneo authentication failed (${response.status}): ${message}`);
269
- }
270
- const payload = await response.json();
346
+ }, deps, async (response, signal) => {
347
+ if (!response.ok) {
348
+ const message = await readAkeneoErrorText(response, signal);
349
+ throw new Error(`Akeneo authentication failed (${response.status}): ${message}`);
350
+ }
351
+ return readAkeneoJson(response, signal);
352
+ });
271
353
  if (!payload.access_token) {
272
354
  throw new Error("Akeneo authentication did not return an access token");
273
355
  }
@@ -279,7 +361,7 @@ function createAkeneoClient(credentialsInput, deps = {}) {
279
361
  }
280
362
  async function refreshAccessToken(current) {
281
363
  if (!current.refreshToken) return acquirePasswordGrantToken();
282
- const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {
364
+ const payload = await withAkeneoResponse(tokenEndpointUrl, {
283
365
  method: "POST",
284
366
  timeoutMs: resolveAkeneoRequestTimeoutMs(),
285
367
  redirect: "manual",
@@ -293,12 +375,14 @@ function createAkeneoClient(credentialsInput, deps = {}) {
293
375
  client_secret: credentials.clientSecret,
294
376
  refresh_token: current.refreshToken
295
377
  })
296
- }, deps);
297
- if (!response.ok) {
298
- return acquirePasswordGrantToken();
299
- }
300
- const payload = await response.json();
301
- if (!payload.access_token) return acquirePasswordGrantToken();
378
+ }, deps, async (response, signal) => {
379
+ if (!response.ok) {
380
+ discardAkeneoBody(response);
381
+ return null;
382
+ }
383
+ return readAkeneoJson(response, signal);
384
+ });
385
+ if (!payload?.access_token) return acquirePasswordGrantToken();
302
386
  return {
303
387
  accessToken: payload.access_token,
304
388
  refreshToken: payload.refresh_token ?? current.refreshToken ?? null,
@@ -312,74 +396,61 @@ function createAkeneoClient(credentialsInput, deps = {}) {
312
396
  tokenState = tokenState ? await refreshAccessToken(tokenState) : await acquirePasswordGrantToken();
313
397
  return tokenState.accessToken;
314
398
  }
315
- async function request(pathOrUrl, init = {}, retried = false, rateLimitAttempts = 0) {
399
+ async function sendAuthenticated(pathOrUrl, init, defaultHeaders, readSuccess, retried = false, rateLimitAttempts = 0) {
316
400
  const url = resolveAkeneoRequestUrl(pathOrUrl);
317
401
  const token = await ensureToken();
318
- const response = await safeAkeneoFetchWithTimeout(url, {
402
+ const outcome = await withAkeneoResponse(url, {
319
403
  ...init,
320
404
  timeoutMs: resolveAkeneoRequestTimeoutMs(),
321
405
  redirect: "manual",
322
406
  headers: {
323
- accept: "application/json",
407
+ ...defaultHeaders,
324
408
  authorization: `Bearer ${token}`,
325
409
  ...init.headers
326
410
  }
327
- }, deps);
328
- if (response.status === 401 && !retried) {
329
- await ensureToken(true);
330
- return request(pathOrUrl, init, true, rateLimitAttempts);
331
- }
332
- if (response.status === 429) {
333
- if (rateLimitAttempts >= resolveAkeneoMaxRateLimitRetries()) {
334
- const body = await response.text();
335
- throw new Error(`Akeneo request failed (429): ${body}`);
411
+ }, deps, async (response, signal) => {
412
+ if (response.status === 401 && !retried) {
413
+ discardAkeneoBody(response);
414
+ return { kind: "reauthenticate" };
336
415
  }
337
- await sleep(clampAkeneoRetryAfterMs(response.headers.get("retry-after")));
338
- return request(pathOrUrl, init, retried, rateLimitAttempts + 1);
339
- }
340
- if (!response.ok) {
341
- const body = await response.text();
342
- throw new Error(`Akeneo request failed (${response.status}): ${body}`);
343
- }
344
- if (response.status === 204) {
345
- return null;
346
- }
347
- return response.json();
348
- }
349
- async function requestBinary(pathOrUrl, init = {}, retried = false, rateLimitAttempts = 0) {
350
- const url = resolveAkeneoRequestUrl(pathOrUrl);
351
- const token = await ensureToken();
352
- const response = await safeAkeneoFetchWithTimeout(url, {
353
- ...init,
354
- timeoutMs: resolveAkeneoRequestTimeoutMs(),
355
- redirect: "manual",
356
- headers: {
357
- authorization: `Bearer ${token}`,
358
- ...init.headers
416
+ if (response.status === 429 && rateLimitAttempts < resolveAkeneoMaxRateLimitRetries()) {
417
+ discardAkeneoBody(response);
418
+ return { kind: "rate_limited", retryAfter: response.headers.get("retry-after") };
359
419
  }
360
- }, deps);
361
- if (response.status === 401 && !retried) {
362
- await ensureToken(true);
363
- return requestBinary(pathOrUrl, init, true, rateLimitAttempts);
364
- }
365
- if (response.status === 429) {
366
- if (rateLimitAttempts >= resolveAkeneoMaxRateLimitRetries()) {
367
- const body = await response.text();
368
- throw new Error(`Akeneo request failed (429): ${body}`);
420
+ if (!response.ok) {
421
+ const body = await readAkeneoErrorText(response, signal);
422
+ throw new Error(`Akeneo request failed (${response.status}): ${body}`);
369
423
  }
370
- await sleep(clampAkeneoRetryAfterMs(response.headers.get("retry-after")));
371
- return requestBinary(pathOrUrl, init, retried, rateLimitAttempts + 1);
424
+ return { kind: "done", value: await readSuccess(response, signal) };
425
+ });
426
+ if (outcome.kind === "reauthenticate") {
427
+ await ensureToken(true);
428
+ return sendAuthenticated(pathOrUrl, init, defaultHeaders, readSuccess, true, rateLimitAttempts);
372
429
  }
373
- if (!response.ok) {
374
- const body = await response.text();
375
- throw new Error(`Akeneo request failed (${response.status}): ${body}`);
430
+ if (outcome.kind === "rate_limited") {
431
+ await sleep(clampAkeneoRetryAfterMs(outcome.retryAfter));
432
+ return sendAuthenticated(pathOrUrl, init, defaultHeaders, readSuccess, retried, rateLimitAttempts + 1);
376
433
  }
377
- const arrayBuffer = await response.arrayBuffer();
378
- return {
379
- buffer: Buffer.from(arrayBuffer),
380
- contentType: response.headers.get("content-type"),
381
- contentLength: Number(response.headers.get("content-length") ?? "") || arrayBuffer.byteLength || null
382
- };
434
+ return outcome.value;
435
+ }
436
+ async function request(pathOrUrl, init = {}) {
437
+ return sendAuthenticated(pathOrUrl, init, { accept: "application/json" }, async (response, signal) => {
438
+ if (response.status === 204) {
439
+ discardAkeneoBody(response);
440
+ return null;
441
+ }
442
+ return readAkeneoJson(response, signal);
443
+ });
444
+ }
445
+ async function requestBinary(pathOrUrl, init = {}) {
446
+ return sendAuthenticated(pathOrUrl, init, {}, async (response, signal) => {
447
+ const buffer = await readAkeneoBody(response, { maxBytes: resolveAttachmentMaxBytes(), signal });
448
+ return {
449
+ buffer,
450
+ contentType: response.headers.get("content-type"),
451
+ contentLength: buffer.byteLength
452
+ };
453
+ });
383
454
  }
384
455
  function buildUrl(path, params) {
385
456
  const url = new URL(resolveAkeneoRequestUrl(path));
@@ -674,9 +745,11 @@ function createAkeneoClient(credentialsInput, deps = {}) {
674
745
  };
675
746
  }
676
747
  export {
748
+ AkeneoResponseTooLargeError,
677
749
  UnsafeAkeneoUrlError,
678
750
  createAkeneoClient,
679
751
  encodeAkeneoPathParam,
752
+ isAkeneoResponseTooLargeError,
680
753
  normalizeAkeneoDateTime,
681
754
  sanitizeAkeneoProductNextUrl,
682
755
  validateAkeneoApiUrl
@@ -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 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": "AAAA,SAAS,+BAA+B;AACxC;AAAA,EACE;AAAA,EAEA;AAAA,OACK;AACP;AAAA,EACE;AAAA,OAIK;AACP,SAAS,eAAe,gCAAgR;AAuCjS,MAAM,6BAA6B,MAAM;AAAA,EAG9C,YAAY,QAAyB,SAAkB;AACrD,UAAM,WAAW,wBAAwB,MAAM,EAAE;AACjD,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AACF;AAIA,MAAM,qBAAqB;AAC3B,MAAM,uCAAuC,CAAC,sBAAsB,gBAAgB;AACpF,MAAM,gCAAgC;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AACF;AACA,MAAM,qCAAqC;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,wBAAwB,CAAC,QAAyB,YACtD,IAAI,qBAAqB,QAAQ,OAAO;AAE1C,SAAS,iBAAiB,KAAqB;AAC7C,SAAO,IAAI,KAAK,EAAE,QAAQ,SAAS,EAAE;AACvC;AAEA,SAAS,8BAA8B,MAAyB,QAAQ,KAAe;AACrF,aAAW,OAAO,+BAA+B;AAC/C,UAAM,MAAM,IAAI,GAAG;AACnB,QAAI,OAAO,QAAQ,SAAU;AAC7B,UAAM,WAAW,IACd,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,MAAM,KAAK,EAAE,YAAY,CAAC,EACzC,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AACrC,QAAI,SAAS,SAAS,GAAG;AACvB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO,CAAC,GAAG,oCAAoC;AACjD;AAEA,SAAS,gCAAgC,MAAyB,QAAQ,KAAc;AACtF,aAAW,OAAO,oCAAoC;AACpD,UAAM,MAAM,IAAI,GAAG;AACnB,QAAI,OAAO,QAAQ,YAAY,IAAI,KAAK,EAAE,SAAS,GAAG;AACpD,aAAO,wBAAwB,KAAK,KAAK;AAAA,IAC3C;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,yBAAyB,UAAkB,SAA0B;AAC5E,MAAI,QAAQ,WAAW,IAAI,GAAG;AAC5B,UAAM,SAAS,QAAQ,MAAM,CAAC;AAC9B,WAAO,aAAa,UAAU,SAAS,SAAS,IAAI,MAAM,EAAE;AAAA,EAC9D;AAEA,SAAO,aAAa;AACtB;AAEO,SAAS,qBAAqB,QAAgB,MAAyB,QAAQ,KAAa;AACjG,MAAI;AACJ,MAAI;AACF,aAAS,IAAI,IAAI,OAAO,KAAK,CAAC;AAAA,EAChC,QAAQ;AACN,UAAM,IAAI,MAAM,yCAAyC;AAAA,EAC3D;AAEA,MAAI,OAAO,aAAa,UAAU;AAChC,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC7C;AACA,MAAI,OAAO,YAAY,OAAO,UAAU;AACtC,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AACA,MAAI,OAAO,QAAQ,OAAO,SAAS,OAAO;AACxC,UAAM,IAAI,MAAM,uCAAuC;AAAA,EACzD;AACA,MAAI,OAAO,aAAa,OAAO,OAAO,aAAa,IAAI;AACrD,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AACA,MAAI,OAAO,UAAU,OAAO,MAAM;AAChC,UAAM,IAAI,MAAM,2DAA2D;AAAA,EAC7E;AAEA,QAAM,WAAW,OAAO,SAAS,YAAY;AAC7C,QAAM,kBAAkB,8BAA8B,GAAG;AACzD,MAAI,CAAC,gBAAgB,KAAK,CAAC,YAAY,yBAAyB,UAAU,OAAO,CAAC,GAAG;AACnF,UAAM,IAAI,MAAM,mCAAmC,QAAQ,EAAE;AAAA,EAC/D;AAEA,SAAO,iBAAiB,OAAO,MAAM;AACvC;AAEO,SAAS,wBAAwB,OAAiD;AACvF,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,WAAW,EAAG,QAAO;AACnE,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,wCAAwC,KAAK,OAAO,GAAG;AACzD,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,IAAI,KAAK,OAAO;AAC/B,MAAI,OAAO,MAAM,OAAO,QAAQ,CAAC,GAAG;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,OAAO,eAAe;AACnC,QAAM,QAAQ,OAAO,OAAO,YAAY,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG;AAC9D,QAAM,MAAM,OAAO,OAAO,WAAW,CAAC,EAAE,SAAS,GAAG,GAAG;AACvD,QAAM,QAAQ,OAAO,OAAO,YAAY,CAAC,EAAE,SAAS,GAAG,GAAG;AAC1D,QAAM,UAAU,OAAO,OAAO,cAAc,CAAC,EAAE,SAAS,GAAG,GAAG;AAC9D,QAAM,UAAU,OAAO,OAAO,cAAc,CAAC,EAAE,SAAS,GAAG,GAAG;AAC9D,SAAO,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,OAAO,IAAI,OAAO;AAC/D;AAEO,SAAS,sBAAsB,OAAuB;AAC3D,SAAO,MACJ,MAAM,GAAG,EACT,IAAI,CAAC,YAAY,mBAAmB,OAAO,CAAC,EAC5C,KAAK,GAAG;AACb;AAEO,SAAS,6BAA6B,SAAyB;AACpE,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,OAAO;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,IAAI,aAAa,IAAI,QAAQ;AAC/C,MAAI,CAAC,WAAW;AACd,WAAO,IAAI,SAAS;AAAA,EACtB;AAEA,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,SAAS;AACnC,UAAM,iBAAiB,MAAM,QAAQ,OAAO,OAAO,IAAI,OAAO,UAAU,CAAC;AACzE,UAAM,mBAAmB,eACtB,IAAI,CAAC,UAAU;AACd,UAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,YAAM,SAAS;AACf,YAAM,kBAAkB;AAAA,QACtB,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAAA,MACpD;AACA,UAAI,CAAC,gBAAiB,QAAO;AAC7B,aAAO;AAAA,QACL,GAAG;AAAA,QACH,OAAO;AAAA,MACT;AAAA,IACF,CAAC,EACA,OAAO,CAAC,UAAU,UAAU,IAAI;AAEnC,QAAI,iBAAiB,WAAW,GAAG;AACjC,aAAO,OAAO;AAAA,IAChB,OAAO;AACL,aAAO,UAAU;AAAA,IACnB;AAEA,QAAI,OAAO,KAAK,MAAM,EAAE,WAAW,GAAG;AACpC,UAAI,aAAa,OAAO,QAAQ;AAAA,IAClC,OAAO;AACL,UAAI,aAAa,IAAI,UAAU,KAAK,UAAU,MAAM,CAAC;AAAA,IACvD;AACA,WAAO,IAAI,SAAS;AAAA,EACtB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,kBAAkB,aAA6D;AACtF,QAAM,SAAS,OAAO,YAAY,WAAW,WAAW,YAAY,OAAO,KAAK,IAAI;AACpF,QAAM,WAAW,OAAO,YAAY,aAAa,WAAW,YAAY,SAAS,KAAK,IAAI;AAC1F,QAAM,eAAe,OAAO,YAAY,iBAAiB,WAAW,YAAY,eAAe;AAC/F,QAAM,WAAW,OAAO,YAAY,aAAa,WAAW,YAAY,SAAS,KAAK,IAAI;AAC1F,QAAM,WAAW,OAAO,YAAY,aAAa,WAAW,YAAY,WAAW;AACnF,MAAI,CAAC,UAAU,CAAC,YAAY,CAAC,gBAAgB,CAAC,YAAY,CAAC,UAAU;AACnE,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACrD;AACA,SAAO;AAAA,IACL,QAAQ,qBAAqB,MAAM;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAEA,MAAM,oCAAoC;AAC1C,MAAM,wCAAwC;AAC9C,MAAM,oCAAoC;AAE1C,SAAS,sBAAsB,KAAyB,UAA0B;AAChF,QAAM,SAAS,OAAO,SAAS,OAAO,IAAI,EAAE;AAC5C,SAAO,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,SAAS;AAC1D;AAEA,SAAS,8BAA8B,MAAyB,QAAQ,KAAa;AACnF,SAAO,sBAAsB,IAAI,0CAA0C,iCAAiC;AAC9G;AAEA,SAAS,iCAAiC,MAAyB,QAAQ,KAAa;AACtF,SAAO,sBAAsB,IAAI,8CAA8C,qCAAqC;AACtH;AAEA,SAAS,wBAAwB,kBAAiC,MAAyB,QAAQ,KAAa;AAC9G,QAAM,aAAa,OAAO,oBAAoB,GAAG;AACjD,QAAM,cAAc,OAAO,SAAS,UAAU,IAAI,aAAa,MAAO;AACtE,QAAM,MAAM,sBAAsB,IAAI,0CAA0C,iCAAiC;AACjH,SAAO,KAAK,IAAI,KAAK,IAAI,aAAa,CAAC,GAAG,GAAG;AAC/C;AAEA,eAAe,2BACb,OACA,OAA6B,CAAC,GAC9B,OAAyB,CAAC,GACP;AACnB,QAAM,EAAE,WAAW,QAAQ,GAAG,KAAK,IAAI;AACvC,QAAM,mBAAmB,iBAAiB,SAAS;AACnD,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,QAAuC,WAAW,MAAM;AAC5D,eAAW,MAAM,IAAI,kBAAkB,OAAO,gBAAgB,CAAC;AAAA,EACjE,GAAG,gBAAgB;AAEnB,QAAM,kBAAkB,MAAM;AAC5B,eAAW,MAAO,QAAoC,MAAM;AAAA,EAC9D;AAEA,MAAI,QAAQ;AACV,QAAI,OAAO,SAAS;AAClB,mBAAa,KAAK;AAClB,YAAM,OAAO,kBAAkB,QAAQ,OAAO,SAAS,IAAI,aAAa,WAAW,YAAY;AAAA,IACjG;AACA,WAAO,iBAAiB,SAAS,iBAAiB,EAAE,MAAM,KAAK,CAAC;AAAA,EAClE;AAEA,MAAI;AACF,WAAO,MAAM,kBAAkB,OAAO,EAAE,GAAG,MAAM,QAAQ,WAAW,OAAO,GAAG;AAAA,MAC5E,cAAc;AAAA,MACd,SAAS;AAAA,MACT,cAAc,KAAK,gBAAgB,gCAAgC;AAAA,MACnE,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,IAClB,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,QAAK,KAAkC,SAAS,cAAc;AAC5D,YAAM,SAAU,WAAW,OAA8C;AACzE,UAAI,kBAAkB,kBAAmB,OAAM;AAC/C,UAAI,kBAAkB,MAAO,OAAM;AAAA,IACrC;AACA,UAAM;AAAA,EACR,UAAE;AACA,iBAAa,KAAK;AAClB,QAAI,OAAQ,QAAO,oBAAoB,SAAS,eAAe;AAAA,EACjE;AACF;AAEO,SAAS,mBAAmB,kBAA2C,OAAyB,CAAC,GAAG;AACzG,QAAM,cAAc,kBAAkB,gBAAgB;AACtD,QAAM,gBAAgB,IAAI,IAAI,GAAG,YAAY,MAAM,GAAG;AACtD,QAAM,mBAAmB,IAAI,IAAI,uBAAuB,aAAa,EAAE,SAAS;AAChF,MAAI,aAAgC;AACpC,MAAI,+BAA+B;AACnC,QAAM,cAAc,oBAAI,IAA0C;AAClE,QAAM,qBAAqB,oBAAI,IAAiD;AAChF,QAAM,oBAAoB,oBAAI,IAAgD;AAC9E,QAAM,iBAAiB,oBAAI,IAA6C;AACxE,QAAM,gBAAgB,oBAAI,IAA4C;AACtE,QAAM,iBAAiB,oBAAI,IAA6C;AAExE,WAAS,wBAAwB,WAA2B;AAC1D,UAAM,WAAW,IAAI,IAAI,WAAW,aAAa;AACjD,QAAI,SAAS,WAAW,cAAc,QAAQ;AAC5C,YAAM,IAAI,MAAM,wDAAwD,SAAS,MAAM,EAAE;AAAA,IAC3F;AACA,WAAO,SAAS,SAAS;AAAA,EAC3B;AAEA,WAAS,uBAAuB,SAAyB;AACvD,WAAO,wBAAwB,OAAO;AAAA,EACxC;AAEA,iBAAe,4BAAiD;AAC9D,UAAM,WAAW,MAAM,2BAA2B,kBAAkB;AAAA,MAClE,QAAQ;AAAA,MACR,WAAW,8BAA8B;AAAA,MACzC,UAAU;AAAA,MACV,SAAS;AAAA,QACP,QAAQ;AAAA,QACR,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,YAAY;AAAA,QACZ,WAAW,YAAY;AAAA,QACvB,eAAe,YAAY;AAAA,QAC3B,UAAU,YAAY;AAAA,QACtB,UAAU,YAAY;AAAA,MACxB,CAAC;AAAA,IACH,GAAG,IAAI;AAEP,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,UAAU,MAAM,SAAS,KAAK;AACpC,YAAM,IAAI,MAAM,iCAAiC,SAAS,MAAM,MAAM,OAAO,EAAE;AAAA,IACjF;AAEA,UAAM,UAAU,MAAM,SAAS,KAAK;AAKpC,QAAI,CAAC,QAAQ,cAAc;AACzB,YAAM,IAAI,MAAM,sDAAsD;AAAA,IACxE;AAEA,WAAO;AAAA,MACL,aAAa,QAAQ;AAAA,MACrB,cAAc,QAAQ,iBAAiB;AAAA,MACvC,WAAW,KAAK,IAAI,KAAM,QAAQ,cAAc,QAAQ,MAAQ;AAAA,IAClE;AAAA,EACF;AAEA,iBAAe,mBAAmB,SAA0C;AAC1E,QAAI,CAAC,QAAQ,aAAc,QAAO,0BAA0B;AAC5D,UAAM,WAAW,MAAM,2BAA2B,kBAAkB;AAAA,MAClE,QAAQ;AAAA,MACR,WAAW,8BAA8B;AAAA,MACzC,UAAU;AAAA,MACV,SAAS;AAAA,QACP,QAAQ;AAAA,QACR,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,YAAY;AAAA,QACZ,WAAW,YAAY;AAAA,QACvB,eAAe,YAAY;AAAA,QAC3B,eAAe,QAAQ;AAAA,MACzB,CAAC;AAAA,IACH,GAAG,IAAI;AAEP,QAAI,CAAC,SAAS,IAAI;AAChB,aAAO,0BAA0B;AAAA,IACnC;AAEA,UAAM,UAAU,MAAM,SAAS,KAAK;AAKpC,QAAI,CAAC,QAAQ,aAAc,QAAO,0BAA0B;AAC5D,WAAO;AAAA,MACL,aAAa,QAAQ;AAAA,MACrB,cAAc,QAAQ,iBAAiB,QAAQ,gBAAgB;AAAA,MAC/D,WAAW,KAAK,IAAI,KAAM,QAAQ,cAAc,QAAQ,MAAQ;AAAA,IAClE;AAAA,EACF;AAEA,iBAAe,YAAY,eAAe,OAAwB;AAChE,QAAI,CAAC,gBAAgB,cAAc,WAAW,YAAY,KAAK,IAAI,GAAG;AACpE,aAAO,WAAW;AAAA,IACpB;AACA,iBAAa,aAAa,MAAM,mBAAmB,UAAU,IAAI,MAAM,0BAA0B;AACjG,WAAO,WAAW;AAAA,EACpB;AAEA,iBAAe,QACb,WACA,OAAoB,CAAC,GACrB,UAAU,OACV,oBAAoB,GACR;AACZ,UAAM,MAAM,wBAAwB,SAAS;AAC7C,UAAM,QAAQ,MAAM,YAAY;AAEhC,UAAM,WAAW,MAAM,2BAA2B,KAAK;AAAA,MACrD,GAAG;AAAA,MACH,WAAW,8BAA8B;AAAA,MACzC,UAAU;AAAA,MACV,SAAS;AAAA,QACP,QAAQ;AAAA,QACR,eAAe,UAAU,KAAK;AAAA,QAC9B,GAAG,KAAK;AAAA,MACV;AAAA,IACF,GAAG,IAAI;AAEP,QAAI,SAAS,WAAW,OAAO,CAAC,SAAS;AACvC,YAAM,YAAY,IAAI;AACtB,aAAO,QAAW,WAAW,MAAM,MAAM,iBAAiB;AAAA,IAC5D;AAEA,QAAI,SAAS,WAAW,KAAK;AAC3B,UAAI,qBAAqB,iCAAiC,GAAG;AAC3D,cAAM,OAAO,MAAM,SAAS,KAAK;AACjC,cAAM,IAAI,MAAM,gCAAgC,IAAI,EAAE;AAAA,MACxD;AACA,YAAM,MAAM,wBAAwB,SAAS,QAAQ,IAAI,aAAa,CAAC,CAAC;AACxE,aAAO,QAAW,WAAW,MAAM,SAAS,oBAAoB,CAAC;AAAA,IACnE;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,0BAA0B,SAAS,MAAM,MAAM,IAAI,EAAE;AAAA,IACvE;AAEA,QAAI,SAAS,WAAW,KAAK;AAC3B,aAAO;AAAA,IACT;AAEA,WAAO,SAAS,KAAK;AAAA,EACvB;AAEA,iBAAe,cACb,WACA,OAAoB,CAAC,GACrB,UAAU,OACV,oBAAoB,GAKnB;AACD,UAAM,MAAM,wBAAwB,SAAS;AAC7C,UAAM,QAAQ,MAAM,YAAY;AAEhC,UAAM,WAAW,MAAM,2BAA2B,KAAK;AAAA,MACrD,GAAG;AAAA,MACH,WAAW,8BAA8B;AAAA,MACzC,UAAU;AAAA,MACV,SAAS;AAAA,QACP,eAAe,UAAU,KAAK;AAAA,QAC9B,GAAG,KAAK;AAAA,MACV;AAAA,IACF,GAAG,IAAI;AAEP,QAAI,SAAS,WAAW,OAAO,CAAC,SAAS;AACvC,YAAM,YAAY,IAAI;AACtB,aAAO,cAAc,WAAW,MAAM,MAAM,iBAAiB;AAAA,IAC/D;AAEA,QAAI,SAAS,WAAW,KAAK;AAC3B,UAAI,qBAAqB,iCAAiC,GAAG;AAC3D,cAAM,OAAO,MAAM,SAAS,KAAK;AACjC,cAAM,IAAI,MAAM,gCAAgC,IAAI,EAAE;AAAA,MACxD;AACA,YAAM,MAAM,wBAAwB,SAAS,QAAQ,IAAI,aAAa,CAAC,CAAC;AACxE,aAAO,cAAc,WAAW,MAAM,SAAS,oBAAoB,CAAC;AAAA,IACtE;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,0BAA0B,SAAS,MAAM,MAAM,IAAI,EAAE;AAAA,IACvE;AAEA,UAAM,cAAc,MAAM,SAAS,YAAY;AAC/C,WAAO;AAAA,MACL,QAAQ,OAAO,KAAK,WAAW;AAAA,MAC/B,aAAa,SAAS,QAAQ,IAAI,cAAc;AAAA,MAChD,eAAe,OAAO,SAAS,QAAQ,IAAI,gBAAgB,KAAK,EAAE,KAAK,YAAY,cAAc;AAAA,IACnG;AAAA,EACF;AAEA,WAAS,SAAS,MAAc,QAA8E;AAC5G,UAAM,MAAM,IAAI,IAAI,wBAAwB,IAAI,CAAC;AACjD,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,UAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,GAAI;AAC3D,UAAI,aAAa,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,IACzC;AACA,WAAO,IAAI,SAAS;AAAA,EACtB;AAEA,iBAAe,SAAY,WAAmB,QAI3C;AACD,UAAM,UAAU,MAAM;AAAA,MACpB,SAAS,SAAS,WAAW,MAAM,IAAI;AAAA,IACzC;AACA,WAAO;AAAA,MACL,OAAO,MAAM,QAAQ,SAAS,WAAW,KAAK,IAAI,QAAQ,UAAU,QAAQ,CAAC;AAAA,MAC7E,SAAS,OAAO,SAAS,QAAQ,MAAM,SAAS,WAC5C,uBAAuB,QAAQ,OAAO,KAAK,IAAI,IAC/C;AAAA,MACJ,eAAe,OAAO,SAAS,gBAAgB,WAAW,QAAQ,cAAc;AAAA,IAClF;AAAA,EACF;AAEA,iBAAe,cAAc,cAAsD;AACjF,UAAM,SAAuE;AAAA,MAC3E,OAAO;AAAA,MACP,iBAAiB;AAAA,MACjB,YAAY;AAAA,IACd;AACA,UAAM,yBAAyB,wBAAwB,YAAY;AACnE,QAAI,wBAAwB;AAC1B,aAAO,SAAS,KAAK,UAAU;AAAA,QAC7B,SAAS;AAAA,UACP;AAAA,YACE,UAAU;AAAA,YACV,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,OAAO,MAAM,SAAwB,8BAA8B,MAAM;AAC/E,WAAO,KAAK;AAAA,EACd;AAEA,iBAAe,iBAAsD;AACnE,QAAI;AACF,YAAM,SAAS,MAAM,QAAiC,iCAAiC;AACvF,aAAO;AAAA,QACL,SAAS,OAAO,OAAO,gBAAgB,WAAW,OAAO,cAAc;AAAA,MACzE;AAAA,IACF,QAAQ;AACN,YAAM,SAA0B,2BAA2B;AAAA,QACzD,OAAO;AAAA,QACP,iBAAiB;AAAA,MACnB,CAAC;AACD,aAAO,EAAE,SAAS,KAAK;AAAA,IACzB;AAAA,EACF;AAEA,iBAAe,aAAa,SAIkE;AAC5F,QAAI,QAAQ,SAAS;AACnB,YAAMA,QAAO,MAAM,SAAwB,uBAAuB,6BAA6B,QAAQ,OAAO,CAAC,CAAC;AAChH,aAAO;AAAA,QACL,GAAGA;AAAA,QACH,SAASA,MAAK,UAAU,uBAAuB,6BAA6BA,MAAK,OAAO,CAAC,IAAI;AAAA,MAC/F;AAAA,IACF;AACA,UAAM,SAAuE;AAAA,MAC3E,OAAO,KAAK,IAAI,KAAK,IAAI,QAAQ,WAAW,CAAC,GAAG,GAAG;AAAA,MACnD,iBAAiB;AAAA,MACjB,YAAY;AAAA,IACd;AACA,UAAM,yBAAyB,wBAAwB,QAAQ,YAAY;AAC3E,QAAI,wBAAwB;AAC1B,aAAO,SAAS,KAAK,UAAU;AAAA,QAC7B,SAAS;AAAA,UACP;AAAA,YACE,UAAU;AAAA,YACV,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AACA,UAAM,CAAC,MAAM,aAAa,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC9C,SAAwB,8BAA8B,MAAM;AAAA,MAC5D,cAAc,sBAAsB,EAAE,MAAM,MAAM,IAAI;AAAA,IACxD,CAAC;AACD,WAAO;AAAA,MACL,GAAG;AAAA,MACH,eAAe,iBAAiB,KAAK;AAAA,MACrC,SAAS,KAAK,UAAU,uBAAuB,6BAA6B,KAAK,OAAO,CAAC,IAAI;AAAA,IAC/F;AAAA,EACF;AAEA,iBAAe,eAAe,SAAyB,YAAY,KAAiG;AAClK,QAAI,QAAS,QAAO,SAAyB,OAAO;AACpD,WAAO,SAAyB,2BAA2B;AAAA,MACzD,OAAO,KAAK,IAAI,KAAK,IAAI,WAAW,CAAC,GAAG,GAAG;AAAA,MAC3C,iBAAiB;AAAA,MACjB,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAEA,iBAAe,eAAe,SAAyB,YAAY,KAAkG;AACnK,QAAI,QAAS,QAAO,SAA0B,OAAO;AACrD,WAAO,SAA0B,2BAA2B;AAAA,MAC1D,OAAO,KAAK,IAAI,KAAK,IAAI,WAAW,CAAC,GAAG,GAAG;AAAA,MAC3C,iBAAiB;AAAA,MACjB,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAEA,iBAAe,aAAa,SAAyB,YAAY,KAA+F;AAC9J,QAAI,QAAS,QAAO,SAAuB,OAAO;AAClD,WAAO,SAAuB,yBAAyB;AAAA,MACrD,OAAO,KAAK,IAAI,KAAK,IAAI,WAAW,CAAC,GAAG,GAAG;AAAA,MAC3C,iBAAiB;AAAA,MACjB,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAEA,iBAAe,mBACb,YACA,SACA,YAAY,KACqF;AACjG,QAAI,QAAS,QAAO,SAA8B,OAAO;AACzD,WAAO,SAA8B,yBAAyB,mBAAmB,UAAU,CAAC,aAAa;AAAA,MACvG,OAAO,KAAK,IAAI,KAAK,IAAI,WAAW,CAAC,GAAG,GAAG;AAAA,MAC3C,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAEA,iBAAe,eAAyC;AACtD,UAAM,WAAW,MAAM,SAAwB,yBAAyB;AAAA,MACtE,OAAO;AAAA,MACP,iBAAiB;AAAA,MACjB,YAAY;AAAA,IACd,CAAC;AACD,WAAO,SAAS;AAAA,EAClB;AAEA,iBAAe,cAAuC;AACpD,UAAM,WAAW,MAAM,SAAuB,wBAAwB;AAAA,MACpE,OAAO;AAAA,MACP,iBAAiB;AAAA,MACjB,YAAY;AAAA,IACd,CAAC;AACD,WAAO,SAAS;AAAA,EAClB;AAEA,iBAAe,YAAY,MAA8C;AACvE,QAAI,CAAC,cAAc,IAAI,IAAI,GAAG;AAC5B,oBAAc,IAAI,MAAM,QAAwB,2BAA2B,mBAAmB,IAAI,CAAC,EAAE,EAAE,MAAM,MAAM,IAAI,CAAC;AAAA,IAC1H;AACA,WAAO,cAAc,IAAI,IAAI,KAAK;AAAA,EACpC;AAEA,iBAAe,aAAa,MAA+C;AACzE,QAAI,CAAC,eAAe,IAAI,IAAI,GAAG;AAC7B,qBAAe,IAAI,MAAM,QAAyB,2BAA2B,mBAAmB,IAAI,CAAC,EAAE,EAAE,MAAM,MAAM,IAAI,CAAC;AAAA,IAC5H;AACA,WAAO,eAAe,IAAI,IAAI,KAAK;AAAA,EACrC;AAEA,iBAAe,qBAAqB,eAAyD;AAC3F,UAAM,UAAmC,CAAC;AAC1C,QAAI,UAAqC;AACzC,OAAG;AACD,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,KAAK,IAAI,GAAG,OAAO,MAAM,6BAA6B;AACrE,UAAI,SAAS,EAAG,OAAM,MAAM,MAAM;AAClC,qCAA+B,KAAK,IAAI;AACxC,YAAM,OAAiG,MAAM;AAAA,QAC3G,WAAW,2BAA2B,mBAAmB,aAAa,CAAC;AAAA,QACvE,UACI,SACA;AAAA,UACE,OAAO;AAAA,UACP,iBAAiB;AAAA,UACjB,YAAY;AAAA,QACd;AAAA,MACN;AACA,cAAQ,KAAK,GAAG,KAAK,KAAK;AAC1B,gBAAU,KAAK;AAAA,IACjB,SAAS;AACT,WAAO;AAAA,EACT;AAEA,iBAAe,UAAU,MAA4C;AACnE,QAAI,CAAC,YAAY,IAAI,IAAI,GAAG;AAC1B,kBAAY,IAAI,MAAM,QAAsB,yBAAyB,mBAAmB,IAAI,CAAC,EAAE,EAAE,MAAM,MAAM,IAAI,CAAC;AAAA,IACpH;AACA,WAAO,YAAY,IAAI,IAAI,KAAK;AAAA,EAClC;AAEA,iBAAe,iBAAiB,YAAoB,mBAAgE;AAClH,UAAM,WAAW,GAAG,UAAU,IAAI,iBAAiB;AACnD,QAAI,CAAC,mBAAmB,IAAI,QAAQ,GAAG;AACrC,yBAAmB;AAAA,QACjB;AAAA,QACA,QAA6B,yBAAyB,mBAAmB,UAAU,CAAC,aAAa,mBAAmB,iBAAiB,CAAC,EAAE,EAAE,MAAM,MAAM,IAAI;AAAA,MAC5J;AAAA,IACF;AACA,WAAO,mBAAmB,IAAI,QAAQ,KAAK;AAAA,EAC7C;AAEA,iBAAe,gBAAgB,MAAkD;AAC/E,QAAI,CAAC,kBAAkB,IAAI,IAAI,GAAG;AAChC,wBAAkB,IAAI,MAAM,QAA4B,+BAA+B,mBAAmB,IAAI,CAAC,EAAE,EAAE,MAAM,MAAM,IAAI,CAAC;AAAA,IACtI;AACA,WAAO,kBAAkB,IAAI,IAAI,KAAK;AAAA,EACxC;AAEA,iBAAe,aAAa,MAA+C;AACzE,QAAI,CAAC,eAAe,IAAI,IAAI,GAAG;AAC7B,qBAAe,IAAI,MAAM,QAAyB,4BAA4B,sBAAsB,IAAI,CAAC,EAAE,EAAE,MAAM,MAAM,IAAI,CAAC;AAAA,IAChI;AACA,WAAO,eAAe,IAAI,IAAI,KAAK;AAAA,EACrC;AAEA,iBAAe,kBAAkB,WAM9B;AACD,QAAI,UAAU,WAAW,SAAS,KAAK,UAAU,WAAW,UAAU,KAAK,UAAU,WAAW,GAAG,GAAG;AACpG,YAAMC,UAAS,MAAM,cAAc,SAAS;AAC5C,aAAO;AAAA,QACL,GAAGA;AAAA,QACH,UAAU;AAAA,QACV,MAAM;AAAA,MACR;AAAA,IACF;AAEA,UAAM,YAAY,MAAM,aAAa,SAAS;AAC9C,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,MAAM,qBAAqB,SAAS,gBAAgB;AAAA,IAChE;AACA,UAAM,eAAe,UAAU,QAAQ,UAAU;AACjD,UAAM,SAAS,MAAM,cAAc,gBAAgB,4BAA4B,sBAAsB,SAAS,CAAC,WAAW;AAC1H,WAAO;AAAA,MACL,GAAG;AAAA,MACH,UAAU,OAAO,UAAU,sBAAsB,YAAY,UAAU,kBAAkB,KAAK,EAAE,SAAS,IACrG,UAAU,kBAAkB,KAAK,IACjC;AAAA,MACJ,MAAM,UAAU,QAAQ;AAAA,IAC1B;AAAA,EACF;AAEA,iBAAe,uBAOZ;AACD,UAAM,CAAC,SAAS,UAAU,YAAY,UAAU,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,MACzE,YAAY,EAAE,MAAM,MAAM,CAAC,CAAC;AAAA,MAC5B,aAAa,EAAE,MAAM,MAAM,CAAC,CAAC;AAAA,MAC7B,eAAe,MAAM,GAAG;AAAA,MACxB,aAAa,MAAM,GAAG;AAAA,MACtB,eAAe,EAAE,MAAM,OAAO,EAAE,SAAS,KAAK,EAAE;AAAA,IAClD,CAAC;AAED,UAAM,iBAAmH,CAAC;AAC1H,eAAW,UAAU,SAAS,OAAO;AACnC,UAAI,UAAyB;AAC7B,SAAG;AACD,cAAM,OAA+F,MAAM,mBAAmB,OAAO,MAAM,SAAS,GAAG,EAAE,MAAM,OAAO;AAAA,UACpK,OAAO,CAAC;AAAA,UACR,SAAS;AAAA,UACT,eAAe;AAAA,QACjB,EAAE;AACF,uBAAe,KAAK,GAAG,KAAK,MAAM,IAAI,CAAC,mBAAmB;AAAA,UACxD,YAAY,OAAO;AAAA,UACnB,MAAM,cAAc;AAAA,UACpB,OAAO,yBAAyB,cAAc,UAAU,MAAM,MAAM,cAAc,IAAI;AAAA,UACtF,MAAM,cAAc,cAAc,wBAAwB,QAAQ,CAAC,QAA6B,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;AAAA,UAC9I,YAAY,cAAc,cAAc,wBAAwB,QAAQ,CAAC,QAAmC,MAAM,QAAQ,IAAI,UAAU,IAAI,IAAI,aAAa,CAAC,CAAC,KAAK,CAAC,CAAC;AAAA,QACxK,EAAE,CAAC;AACH,kBAAU,KAAK;AAAA,MACjB,SAAS;AAAA,IACX;AAEA,WAAO;AAAA,MACL,SAAS,QAAQ,IAAI,CAAC,YAAY;AAAA,QAChC,MAAM,OAAO;AAAA,QACb,OAAO,yBAAyB,OAAO,UAAU,MAAM,MAAM,OAAO,IAAI;AAAA,QACxE,SAAS,OAAO,WAAW;AAAA,MAC7B,EAAE;AAAA,MACF,UAAU,SAAS,IAAI,CAAC,aAAa;AAAA,QACnC,MAAM,QAAQ;AAAA,QACd,OAAO,yBAAyB,QAAQ,UAAU,MAAM,MAAM,QAAQ,IAAI;AAAA,QAC1E,SAAS,cAAc,QAAQ,WAAW,CAAC,CAAC;AAAA,MAC9C,EAAE;AAAA,MACF,YAAY,WAAW,MAAM,IAAI,CAAC,eAAe;AAAA,QAC/C,MAAM,UAAU;AAAA,QAChB,MAAM,UAAU;AAAA,QAChB,OAAO,yBAAyB,UAAU,UAAU,MAAM,MAAM,UAAU,IAAI;AAAA,QAC9E,aAAa,QAAQ,UAAU,WAAW;AAAA,QAC1C,UAAU,QAAQ,UAAU,QAAQ;AAAA,QACpC,OAAO,OAAO,UAAU,UAAU,YAAY,UAAU,MAAM,KAAK,EAAE,SAAS,IAAI,UAAU,MAAM,KAAK,IAAI;AAAA,QAC3G,cAAc,OAAO,UAAU,kBAAkB,YAAY,UAAU,cAAc,KAAK,EAAE,SAAS,IACjG,UAAU,cAAc,KAAK,IAC7B;AAAA,MACN,EAAE;AAAA,MACF,UAAU,SAAS,MAAM,IAAI,CAAC,YAAY;AAAA,QACxC,MAAM,OAAO;AAAA,QACb,OAAO,yBAAyB,OAAO,UAAU,MAAM,MAAM,OAAO,IAAI;AAAA,QACxE,gBAAgB,MAAM,QAAQ,OAAO,UAAU,IAAI,OAAO,WAAW,SAAS;AAAA,MAChF,EAAE;AAAA,MACF;AAAA,MACA,SAAS,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { resolveAttachmentMaxBytes } from '@open-mercato/core/modules/attachments/lib/upload-limits'\nimport { 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 AkeneoTokenPayload = {\n access_token?: string\n refresh_token?: string\n expires_in?: number\n}\n\ntype AkeneoAttemptOutcome<T> =\n | { kind: 'done'; value: T }\n | { kind: 'reauthenticate' }\n | { kind: 'rate_limited'; retryAfter: string | null }\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\nexport class AkeneoResponseTooLargeError extends Error {\n public readonly limitBytes: number\n\n constructor(limitBytes: number) {\n super(`Akeneo response exceeds the ${limitBytes} byte limit`)\n this.name = 'AkeneoResponseTooLargeError'\n this.limitBytes = limitBytes\n }\n}\n\nexport function isAkeneoResponseTooLargeError(error: unknown): error is AkeneoResponseTooLargeError {\n return error instanceof Error && error.name === 'AkeneoResponseTooLargeError'\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\nconst DEFAULT_AKENEO_MAX_RESPONSE_BYTES = 32 * 1024 * 1024\nconst AKENEO_ERROR_BODY_MAX_BYTES = 64 * 1024\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\nfunction resolveAkeneoMaxResponseBytes(env: NodeJS.ProcessEnv = process.env): number {\n return resolvePositiveIntEnv(env.OM_INTEGRATION_AKENEO_MAX_RESPONSE_BYTES, DEFAULT_AKENEO_MAX_RESPONSE_BYTES)\n}\n\nfunction abortReasonError(signal: AbortSignal): Error {\n return signal.reason instanceof Error ? signal.reason : new DOMException('Aborted', 'AbortError')\n}\n\nfunction settleBeforeAbort<T>(pending: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) return Promise.reject(abortReasonError(signal))\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(abortReasonError(signal))\n signal.addEventListener('abort', onAbort, { once: true })\n pending.then(\n (value) => {\n signal.removeEventListener('abort', onAbort)\n resolve(value)\n },\n (error: unknown) => {\n signal.removeEventListener('abort', onAbort)\n reject(error)\n },\n )\n })\n}\n\nfunction discardAkeneoBody(response: Response): void {\n void response.body?.cancel().catch(() => undefined)\n}\n\nasync function readAkeneoBody(\n response: Response,\n options: { maxBytes: number; signal: AbortSignal; truncate?: boolean },\n): Promise<Buffer> {\n const { maxBytes, signal, truncate = false } = options\n const declaredLength = Number(response.headers.get('content-length') ?? '')\n if (!truncate && Number.isFinite(declaredLength) && declaredLength > maxBytes) {\n discardAkeneoBody(response)\n throw new AkeneoResponseTooLargeError(maxBytes)\n }\n if (!response.body) return Buffer.alloc(0)\n\n const reader = response.body.getReader()\n const chunks: Uint8Array[] = []\n let receivedBytes = 0\n let finished = false\n try {\n while (true) {\n const { done, value } = await settleBeforeAbort(reader.read(), signal)\n if (done) {\n finished = true\n break\n }\n if (receivedBytes + value.byteLength > maxBytes) {\n if (!truncate) throw new AkeneoResponseTooLargeError(maxBytes)\n chunks.push(value.subarray(0, maxBytes - receivedBytes))\n receivedBytes = maxBytes\n break\n }\n chunks.push(value)\n receivedBytes += value.byteLength\n }\n } finally {\n if (!finished) void reader.cancel().catch(() => undefined)\n }\n return Buffer.concat(chunks, receivedBytes)\n}\n\nasync function readAkeneoJson<T>(response: Response, signal: AbortSignal): Promise<T> {\n const bytes = await readAkeneoBody(response, { maxBytes: resolveAkeneoMaxResponseBytes(), signal })\n return JSON.parse(new TextDecoder().decode(bytes)) as T\n}\n\nasync function readAkeneoErrorText(response: Response, signal: AbortSignal): Promise<string> {\n const bytes = await readAkeneoBody(response, { maxBytes: AKENEO_ERROR_BODY_MAX_BYTES, signal, truncate: true })\n return new TextDecoder().decode(bytes)\n}\n\nasync function withAkeneoResponse<T>(\n input: string,\n init: FetchWithTimeoutInit,\n deps: AkeneoClientDeps,\n consume: (response: Response, signal: AbortSignal) => Promise<T>,\n): Promise<T> {\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 const response = 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 return await consume(response, controller.signal)\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 payload = await withAkeneoResponse(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, async (response, signal) => {\n if (!response.ok) {\n const message = await readAkeneoErrorText(response, signal)\n throw new Error(`Akeneo authentication failed (${response.status}): ${message}`)\n }\n return readAkeneoJson<AkeneoTokenPayload>(response, signal)\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 payload = await withAkeneoResponse(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, async (response, signal) => {\n if (!response.ok) {\n discardAkeneoBody(response)\n return null\n }\n return readAkeneoJson<AkeneoTokenPayload>(response, signal)\n })\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 sendAuthenticated<T>(\n pathOrUrl: string,\n init: RequestInit,\n defaultHeaders: Record<string, string>,\n readSuccess: (response: Response, signal: AbortSignal) => Promise<T>,\n retried = false,\n rateLimitAttempts = 0,\n ): Promise<T> {\n const url = resolveAkeneoRequestUrl(pathOrUrl)\n const token = await ensureToken()\n\n const outcome = await withAkeneoResponse<AkeneoAttemptOutcome<T>>(url, {\n ...init,\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n redirect: 'manual',\n headers: {\n ...defaultHeaders,\n authorization: `Bearer ${token}`,\n ...init.headers,\n },\n }, deps, async (response, signal) => {\n if (response.status === 401 && !retried) {\n discardAkeneoBody(response)\n return { kind: 'reauthenticate' }\n }\n if (response.status === 429 && rateLimitAttempts < resolveAkeneoMaxRateLimitRetries()) {\n discardAkeneoBody(response)\n return { kind: 'rate_limited', retryAfter: response.headers.get('retry-after') }\n }\n if (!response.ok) {\n const body = await readAkeneoErrorText(response, signal)\n throw new Error(`Akeneo request failed (${response.status}): ${body}`)\n }\n return { kind: 'done', value: await readSuccess(response, signal) }\n })\n\n if (outcome.kind === 'reauthenticate') {\n await ensureToken(true)\n return sendAuthenticated(pathOrUrl, init, defaultHeaders, readSuccess, true, rateLimitAttempts)\n }\n if (outcome.kind === 'rate_limited') {\n await sleep(clampAkeneoRetryAfterMs(outcome.retryAfter))\n return sendAuthenticated(pathOrUrl, init, defaultHeaders, readSuccess, retried, rateLimitAttempts + 1)\n }\n return outcome.value\n }\n\n async function request<T>(pathOrUrl: string, init: RequestInit = {}): Promise<T> {\n return sendAuthenticated<T>(pathOrUrl, init, { accept: 'application/json' }, async (response, signal) => {\n if (response.status === 204) {\n discardAkeneoBody(response)\n return null as T\n }\n return readAkeneoJson<T>(response, signal)\n })\n }\n\n async function requestBinary(pathOrUrl: string, init: RequestInit = {}): Promise<{\n buffer: Buffer\n contentType: string | null\n contentLength: number | null\n }> {\n return sendAuthenticated(pathOrUrl, init, {}, async (response, signal) => {\n const buffer = await readAkeneoBody(response, { maxBytes: resolveAttachmentMaxBytes(), signal })\n return {\n buffer,\n contentType: response.headers.get('content-type'),\n contentLength: buffer.byteLength,\n }\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.9.1-develop.7553.1.8dbab05b5a",
3
+ "version": "0.9.1-develop.7567.1.b5e80ff620",
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.9.1-develop.7553.1.8dbab05b5a",
57
- "@open-mercato/ui": "0.9.1-develop.7553.1.8dbab05b5a",
56
+ "@open-mercato/core": "0.9.1-develop.7567.1.b5e80ff620",
57
+ "@open-mercato/ui": "0.9.1-develop.7567.1.b5e80ff620",
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.9.1-develop.7553.1.8dbab05b5a",
62
+ "@open-mercato/shared": "0.9.1-develop.7567.1.b5e80ff620",
63
63
  "react": "^19.0.0",
64
64
  "react-dom": "^19.0.0"
65
65
  },
66
66
  "devDependencies": {
67
- "@open-mercato/shared": "0.9.1-develop.7553.1.8dbab05b5a",
67
+ "@open-mercato/shared": "0.9.1-develop.7567.1.b5e80ff620",
68
68
  "@types/jest": "^30.0.0",
69
69
  "@types/react": "^19.2.18",
70
70
  "@types/react-dom": "^19.2.5",