@altazion/commerce-sdk-core 26.409.7573 → 26.415.7673
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -3
- package/dist/index.cjs +12 -183
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -82
- package/dist/index.iife.js +12 -183
- package/dist/index.iife.js.map +1 -1
- package/dist/index.js +13 -184
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.iife.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.iife.js","sources":["../src/client/CommerceContext.ts","../src/client/errors.ts","../src/client/CommerceHttpAdapter.ts","../src/connectivity/ConnectivityManager.ts","../src/workers/WorkerBridge.ts","../src/workers/CacheStrategy.ts","../src/offline/OfflineQueue.ts","../src/session/BrowserSessionManager.ts","../src/session/KioskSessionManager.ts","../src/session/SessionManagerFactory.ts","../src/modules/session/index.ts","../src/modules/cart/index.ts","../src/modules/catalog/index.ts","../src/modules/shipping/index.ts","../src/modules/marketing/index.ts","../src/modules/stores/index.ts","../src/client/CommerceClient.ts"],"sourcesContent":["/**\n * Mode d'exécution du SDK.\n * - `browser` : navigateur classique — le SDK gère la création de session et le cookie.\n * - `kiosk` : borne avec \"Altazion Device Shell\" — le browser embarqué gère les cookies.\n */\nexport type CommerceMode = 'browser' | 'kiosk'\n\nexport interface CommerceContextOptions {\n /** URL de base de l'API (ex. : \"https://api.monsite.com\") */\n baseUrl: string\n /** Identifiant ou URL du site web pour la création de session (mode browser) */\n siteUrl?: string\n /**\n * Clé primaire du site (alternative à siteUrl).\n * Requiert aussi rjsId.\n */\n sitePk?: number\n /** Identifiant du tenant RJS (nécessaire avec sitePk) */\n rjsId?: number\n /** Locale par défaut (ex. : \"fr-FR\") */\n locale?: string\n /** Devise par défaut (ex. : \"EUR\") */\n currency?: string\n}\n\n/**\n * Contexte partagé du SDK, instancié une seule fois et transmis\n * à tous les sous-systèmes (HttpAdapter, SessionManager, modules…).\n */\nexport class CommerceContext {\n readonly baseUrl: string\n readonly siteUrl: string | undefined\n readonly sitePk: number | undefined\n readonly rjsId: number | undefined\n readonly locale: string\n readonly currency: string\n readonly mode: CommerceMode\n\n constructor(options: CommerceContextOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, '')\n this.siteUrl = options.siteUrl\n this.sitePk = options.sitePk\n this.rjsId = options.rjsId\n this.locale = options.locale ?? 'fr-FR'\n this.currency = options.currency ?? 'EUR'\n this.mode = CommerceContext.detectMode()\n }\n\n private static detectMode(): CommerceMode {\n if (typeof navigator !== 'undefined' && navigator.userAgent.includes('Altazion Device Shell')) {\n return 'kiosk'\n }\n return 'browser'\n }\n}\n","/**\n * Représentation d'une réponse d'erreur ProblemDetails (RFC 9457)\n * telle que renvoyée par GlobalExceptionFilter.cs\n */\nexport interface ProblemDetails {\n type?: string\n title?: string\n status?: number\n detail?: string\n instance?: string\n [key: string]: unknown\n}\n\n/**\n * Erreur générée par l'API Altazion Commerce (HTTP 4xx / 5xx).\n */\nexport class AltazionApiError extends Error {\n readonly status: number\n readonly problem: ProblemDetails\n\n constructor(status: number, problem: ProblemDetails) {\n super(problem.detail ?? problem.title ?? `HTTP ${status}`)\n this.name = 'AltazionApiError'\n this.status = status\n this.problem = problem\n }\n}\n\n/**\n * Erreur levée lorsque le SDK est hors-ligne et ne peut pas\n * satisfaire la requête depuis le cache.\n */\nexport class OfflineError extends Error {\n constructor(message = 'Le SDK est hors-ligne et aucun cache n\\'est disponible pour cette ressource') {\n super(message)\n this.name = 'OfflineError'\n }\n}\n\n/**\n * Erreur liée à une opération sur le cache (IndexedDB, SharedWorker).\n */\nexport class CacheError extends Error {\n readonly cause: unknown\n\n constructor(message: string, cause?: unknown) {\n super(message)\n this.name = 'CacheError'\n this.cause = cause\n }\n}\n\n/**\n * Erreur levée lors du rejeu de l'OfflineQueue (conflit serveur).\n */\nexport class QueueFlushError extends Error {\n readonly failedOperationId: string\n readonly apiError: AltazionApiError\n\n constructor(operationId: string, apiError: AltazionApiError) {\n super(`Échec du rejeu de l'opération ${operationId} : ${apiError.message}`)\n this.name = 'QueueFlushError'\n this.failedOperationId = operationId\n this.apiError = apiError\n }\n}\n","import { AltazionApiError, type ProblemDetails } from './errors.js'\nimport type { CommerceContext } from './CommerceContext.js'\n\nexport interface RequestOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n body?: unknown\n headers?: Record<string, string>\n /** Nombre maximum de tentatives réseau (uniquement sur erreurs réseau, jamais sur 4xx) */\n maxRetries?: number\n}\n\n/**\n * Adaptateur HTTP basé sur fetch natif.\n *\n * Responsabilités :\n * - Construction des URLs à partir du baseUrl du contexte\n * - Ajout des headers standard (Content-Type, Accept, locale)\n * - credentials: 'include' pour la transmission automatique des cookies (modes browser et kiosk)\n * - Parse des réponses ProblemDetails en AltazionApiError\n * - Retry limité sur les erreurs réseau (TypeError), jamais sur les 4xx/5xx\n */\nexport class CommerceHttpAdapter {\n private readonly context: CommerceContext\n\n constructor(context: CommerceContext) {\n this.context = context\n }\n\n async request<T>(path: string, options: RequestOptions = {}): Promise<T> {\n const { method = 'GET', body, headers = {}, maxRetries = 2 } = options\n const url = `${this.context.baseUrl}${path}`\n\n const requestHeaders: Record<string, string> = {\n Accept: 'application/json',\n 'Accept-Language': this.context.locale,\n ...headers\n }\n\n if (body !== undefined) {\n requestHeaders['Content-Type'] = 'application/json'\n }\n\n const init: RequestInit = {\n method,\n headers: requestHeaders,\n credentials: 'include',\n body: body !== undefined ? JSON.stringify(body) : undefined\n }\n\n let lastNetworkError: unknown\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const response = await fetch(url, init)\n return await this.handleResponse<T>(response)\n } catch (err) {\n // AltazionApiError n'est jamais retenté\n if (err instanceof AltazionApiError) throw err\n lastNetworkError = err\n // Petite pause exponentielle avant de réessayer (uniquement sur erreurs réseau)\n if (attempt < maxRetries) {\n await delay(150 * Math.pow(2, attempt))\n }\n }\n }\n\n throw lastNetworkError\n }\n\n private async handleResponse<T>(response: Response): Promise<T> {\n if (response.ok) {\n const contentType = response.headers.get('content-type') ?? ''\n if (response.status === 204 || !contentType.includes('application/json')) {\n return undefined as unknown as T\n }\n return response.json() as Promise<T>\n }\n\n // Tenter de parser un ProblemDetails\n let problem: ProblemDetails = { status: response.status }\n try {\n const contentType = response.headers.get('content-type') ?? ''\n if (contentType.includes('application/json') || contentType.includes('application/problem+json')) {\n problem = await response.json() as ProblemDetails\n problem.status ??= response.status\n }\n } catch {\n // JSON invalide — on garde le problem minimal\n }\n\n throw new AltazionApiError(response.status, problem)\n }\n\n /** Effectue un GET et retourne T */\n get<T>(path: string, headers?: Record<string, string>): Promise<T> {\n return this.request<T>(path, { method: 'GET', headers })\n }\n\n /** Effectue un POST avec un body JSON et retourne T */\n post<T>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T> {\n return this.request<T>(path, { method: 'POST', body, headers })\n }\n\n /** Effectue un PUT avec un body JSON et retourne T */\n put<T>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T> {\n return this.request<T>(path, { method: 'PUT', body, headers })\n }\n\n /** Effectue un PATCH avec un body JSON et retourne T */\n patch<T>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T> {\n return this.request<T>(path, { method: 'PATCH', body, headers })\n }\n\n /** Effectue un DELETE et retourne T */\n delete<T>(path: string, headers?: Record<string, string>): Promise<T> {\n return this.request<T>(path, { method: 'DELETE', headers })\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n","export type ConnectivityStatus = 'online' | 'offline'\n\nexport type ConnectivityListener = (status: ConnectivityStatus) => void\n\n/**\n * Surveille l'état de la connexion réseau.\n *\n * - Utilise navigator.onLine comme valeur initiale\n * - Écoute les événements window 'online' / 'offline'\n * - Permet d'abonner des listeners (pattern observable léger)\n */\nexport class ConnectivityManager {\n private status: ConnectivityStatus\n private readonly listeners = new Set<ConnectivityListener>()\n\n constructor() {\n this.status = this.readOnlineStatus()\n\n if (typeof window !== 'undefined') {\n window.addEventListener('online', this.handleOnline)\n window.addEventListener('offline', this.handleOffline)\n }\n }\n\n get isOnline(): boolean {\n return this.status === 'online'\n }\n\n get isOffline(): boolean {\n return this.status === 'offline'\n }\n\n /**\n * Abonne un listener aux changements de connectivité.\n * Retourne une fonction de désabonnement.\n */\n subscribe(listener: ConnectivityListener): () => void {\n this.listeners.add(listener)\n return () => this.listeners.delete(listener)\n }\n\n /** Libère les event listeners sur window. */\n dispose(): void {\n if (typeof window !== 'undefined') {\n window.removeEventListener('online', this.handleOnline)\n window.removeEventListener('offline', this.handleOffline)\n }\n this.listeners.clear()\n }\n\n private readOnlineStatus(): ConnectivityStatus {\n if (typeof navigator === 'undefined' || navigator.onLine === undefined) {\n return 'online' // SSR / environnement sans navigator : on suppose online\n }\n return navigator.onLine ? 'online' : 'offline'\n }\n\n private readonly handleOnline = (): void => {\n this.status = 'online'\n this.emit('online')\n }\n\n private readonly handleOffline = (): void => {\n this.status = 'offline'\n this.emit('offline')\n }\n\n private emit(status: ConnectivityStatus): void {\n for (const listener of this.listeners) {\n try {\n listener(status)\n } catch {\n // Un listener ne doit pas faire planter les autres\n }\n }\n }\n}\n","import { CacheError } from '../client/errors.js'\n\ninterface PendingRequest {\n resolve: (value: unknown) => void\n reject: (reason: unknown) => void\n}\n\n/**\n * Abstraction du canal de communication avec le SharedWorker de cache.\n *\n * - Utilise un SharedWorker si le navigateur le supporte\n * - Fallback : Map in-memory (données non persistantes, perdues au reload)\n *\n * Chaque appel retourne une Promise, résolue/rejetée via la réponse du worker.\n */\nexport class WorkerBridge {\n private port: MessagePort | null = null\n private readonly pending = new Map<string, PendingRequest>()\n private requestCounter = 0\n private readonly fallback = new Map<string, { data: unknown; expiresAt: number }>()\n private readonly usingFallback: boolean\n\n constructor() {\n this.usingFallback = !this.tryStartWorker()\n }\n\n private tryStartWorker(): boolean {\n if (typeof SharedWorker === 'undefined') return false\n\n try {\n const worker = new SharedWorker(\n new URL('./cache.worker.ts', import.meta.url),\n { type: 'module', name: 'altazion-cache-worker' }\n )\n this.port = worker.port\n this.port.addEventListener('message', this.handleMessage)\n this.port.start()\n return true\n } catch {\n return false\n }\n }\n\n private readonly handleMessage = (event: MessageEvent<{ id: string; result?: unknown; error?: string }>): void => {\n const { id, result, error } = event.data\n const pending = this.pending.get(id)\n if (!pending) return\n this.pending.delete(id)\n\n if (error !== undefined) {\n pending.reject(new CacheError(error))\n } else {\n pending.resolve(result)\n }\n }\n\n private nextId(): string {\n return String(++this.requestCounter)\n }\n\n private send(message: Record<string, unknown>): Promise<unknown> {\n return new Promise((resolve, reject) => {\n if (!this.port) {\n reject(new CacheError('Worker non initialisé'))\n return\n }\n const id = this.nextId()\n this.pending.set(id, { resolve, reject })\n this.port.postMessage({ ...message, id })\n })\n }\n\n async get<T>(key: string): Promise<T | null> {\n if (this.usingFallback) {\n const entry = this.fallback.get(key)\n if (!entry || Date.now() > entry.expiresAt) return null\n return entry.data as T\n }\n return this.send({ type: 'GET', key }) as Promise<T | null>\n }\n\n async set(key: string, data: unknown, ttlMs: number): Promise<void> {\n if (this.usingFallback) {\n this.fallback.set(key, { data, expiresAt: Date.now() + ttlMs })\n return\n }\n await this.send({ type: 'SET', key, data, ttl: ttlMs })\n }\n\n async invalidate(pattern: string): Promise<void> {\n if (this.usingFallback) {\n const regex = new RegExp(pattern)\n for (const key of this.fallback.keys()) {\n if (regex.test(key)) this.fallback.delete(key)\n }\n return\n }\n await this.send({ type: 'INVALIDATE', pattern })\n }\n\n async clearAll(): Promise<void> {\n if (this.usingFallback) {\n this.fallback.clear()\n return\n }\n await this.send({ type: 'CLEAR_ALL' })\n }\n\n dispose(): void {\n this.port?.removeEventListener('message', this.handleMessage)\n this.pending.clear()\n }\n}\n","import type { WorkerBridge } from './WorkerBridge.js'\nimport type { CommerceHttpAdapter } from '../client/CommerceHttpAdapter.js'\n\n/**\n * Stratégies de cache disponibles pour les requêtes HTTP.\n */\nexport type CacheStrategyName =\n | 'network-only' // Jamais de cache (mutations)\n | 'network-first' // Réseau, fallback cache si erreur réseau\n | 'cache-first' // Cache, revalidation réseau en background si périmé\n | 'stale-while-revalidate' // Retourne le cache immédiatement + revalide en arrière-plan\n\n/** TTL par stratégie, en millisecondes */\nexport const DEFAULT_TTL: Record<Exclude<CacheStrategyName, 'network-only'>, number> = {\n 'network-first': 30_000, // 30 secondes (session, panier)\n 'cache-first': 5 * 60_000, // 5 minutes (recherche)\n 'stale-while-revalidate': 60 * 60_000 // 1 heure (catalogue)\n}\n\nexport class CacheStrategy {\n constructor(\n private readonly bridge: WorkerBridge,\n private readonly http: CommerceHttpAdapter\n ) {}\n\n /**\n * Exécute la requête selon la stratégie choisie.\n *\n * @param key Clé de cache (généralement le path + params)\n * @param fetcher Fonction qui effectue la requête HTTP\n * @param strategy Stratégie à appliquer\n * @param ttlMs TTL du cache (override la valeur par défaut)\n */\n async execute<T>(\n key: string,\n fetcher: () => Promise<T>,\n strategy: CacheStrategyName,\n ttlMs?: number\n ): Promise<T> {\n switch (strategy) {\n case 'network-only':\n return fetcher()\n\n case 'network-first':\n return this.networkFirst<T>(key, fetcher, ttlMs ?? DEFAULT_TTL['network-first'])\n\n case 'cache-first':\n return this.cacheFirst<T>(key, fetcher, ttlMs ?? DEFAULT_TTL['cache-first'])\n\n case 'stale-while-revalidate':\n return this.staleWhileRevalidate<T>(key, fetcher, ttlMs ?? DEFAULT_TTL['stale-while-revalidate'])\n }\n }\n\n private async networkFirst<T>(key: string, fetcher: () => Promise<T>, ttlMs: number): Promise<T> {\n try {\n const data = await fetcher()\n await this.bridge.set(key, data, ttlMs)\n return data\n } catch (err) {\n // Erreur réseau uniquement (TypeError) : on tente le cache\n if (err instanceof TypeError) {\n const cached = await this.bridge.get<T>(key)\n if (cached !== null) return cached\n }\n throw err\n }\n }\n\n private async cacheFirst<T>(key: string, fetcher: () => Promise<T>, ttlMs: number): Promise<T> {\n const cached = await this.bridge.get<T>(key)\n if (cached !== null) return cached\n\n const data = await fetcher()\n await this.bridge.set(key, data, ttlMs)\n return data\n }\n\n private async staleWhileRevalidate<T>(key: string, fetcher: () => Promise<T>, ttlMs: number): Promise<T> {\n const cached = await this.bridge.get<T>(key)\n\n // Revalidation en arrière-plan (ne bloque pas le résultat)\n const revalidate = fetcher()\n .then((data) => this.bridge.set(key, data, ttlMs))\n .catch(() => { /* Échec silencieux : on garde le cache */ })\n\n if (cached !== null) {\n // On lance la revalidation sans attendre\n void revalidate\n return cached\n }\n\n // Pas de cache : on attend le réseau\n const data = await fetcher()\n await this.bridge.set(key, data, ttlMs)\n return data\n }\n}\n","import type { QueuedOperation } from './QueuedOperation.js'\nimport type { CommerceHttpAdapter } from '../client/CommerceHttpAdapter.js'\nimport { AltazionApiError, QueueFlushError } from '../client/errors.js'\n\nconst DB_NAME = 'altazion-queue'\nconst DB_VERSION = 1\nconst STORE_NAME = 'operations'\n\nexport type QueueEventType = 'enqueued' | 'flushed' | 'conflict' | 'emptied'\n\nexport interface QueueEvent {\n type: QueueEventType\n operation?: QueuedOperation\n error?: QueueFlushError\n}\n\nexport type QueueEventListener = (event: QueueEvent) => void\n\n/**\n * File d'attente persistante (IndexedDB) pour les mutations effectuées hors-ligne.\n *\n * Flux :\n * 1. `enqueue(operation)` — stocke l'opération en IndexedDB\n * 2. Au retour en ligne, `flush()` rejoue les opérations dans l'ordre d'enfilage\n * 3. En cas d'erreur 4xx (conflit) : émet un événement `conflict` et ne retire PAS l'opération\n * 4. En cas de succès : retire l'opération de la file\n */\nexport class OfflineQueue {\n private db: IDBDatabase | null = null\n private readonly listeners = new Set<QueueEventListener>()\n private counter = 0\n\n /** Abonne un listener aux événements de file. Retourne une fonction de désabonnement. */\n subscribe(listener: QueueEventListener): () => void {\n this.listeners.add(listener)\n return () => this.listeners.delete(listener)\n }\n\n get pendingCount(): Promise<number> {\n return this.openDb().then(\n (db) =>\n new Promise<number>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, 'readonly')\n const req = tx.objectStore(STORE_NAME).count()\n req.onsuccess = () => resolve(req.result)\n req.onerror = () => reject(req.error)\n })\n )\n }\n\n async enqueue(\n method: QueuedOperation['method'],\n path: string,\n body?: unknown,\n headers?: Record<string, string>\n ): Promise<QueuedOperation> {\n const operation: QueuedOperation = {\n id: `${Date.now()}-${++this.counter}`,\n enqueuedAt: Date.now(),\n method,\n path,\n body,\n headers,\n attempts: 0\n }\n\n const db = await this.openDb()\n await this.put(db, operation)\n this.emit({ type: 'enqueued', operation })\n return operation\n }\n\n /**\n * Rejoue toutes les opérations en attente dans l'ordre d'enfilage.\n * S'arrête sur un conflit (4xx) et émet un événement `conflict`.\n */\n async flush(http: CommerceHttpAdapter): Promise<void> {\n const operations = await this.getAll()\n if (operations.length === 0) return\n\n for (const op of operations) {\n try {\n op.attempts++\n await http.request(op.path, {\n method: op.method,\n body: op.body,\n headers: op.headers\n })\n await this.remove(op.id)\n } catch (err) {\n if (err instanceof AltazionApiError && err.status >= 400 && err.status < 500) {\n const flushError = new QueueFlushError(op.id, err)\n this.emit({ type: 'conflict', operation: op, error: flushError })\n // On arrête le flush pour éviter de rejouer des opérations dépendantes\n return\n }\n // Erreur réseau ou 5xx : on met à jour l'opération et on arrête (ce n'est pas le bon moment)\n await this.put(await this.openDb(), op)\n return\n }\n }\n\n this.emit({ type: 'flushed' })\n\n const remaining = await this.pendingCount\n if (remaining === 0) {\n this.emit({ type: 'emptied' })\n }\n }\n\n private emit(event: QueueEvent): void {\n for (const listener of this.listeners) {\n try { listener(event) } catch { /* isolation */ }\n }\n }\n\n private openDb(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n if (this.db) { resolve(this.db); return }\n\n const request = indexedDB.open(DB_NAME, DB_VERSION)\n\n request.onupgradeneeded = (event) => {\n const database = (event.target as IDBOpenDBRequest).result\n if (!database.objectStoreNames.contains(STORE_NAME)) {\n database.createObjectStore(STORE_NAME, { keyPath: 'id' })\n }\n }\n\n request.onsuccess = (event) => {\n this.db = (event.target as IDBOpenDBRequest).result\n resolve(this.db)\n }\n\n request.onerror = () => reject(request.error)\n })\n }\n\n private put(db: IDBDatabase, operation: QueuedOperation): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, 'readwrite')\n const req = tx.objectStore(STORE_NAME).put(operation)\n req.onsuccess = () => resolve()\n req.onerror = () => reject(req.error)\n })\n }\n\n private remove(id: string): Promise<void> {\n return this.openDb().then(\n (db) =>\n new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, 'readwrite')\n const req = tx.objectStore(STORE_NAME).delete(id)\n req.onsuccess = () => resolve()\n req.onerror = () => reject(req.error)\n })\n )\n }\n\n private getAll(): Promise<QueuedOperation[]> {\n return this.openDb().then(\n (db) =>\n new Promise<QueuedOperation[]>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, 'readonly')\n const req = tx.objectStore(STORE_NAME).getAll()\n req.onsuccess = () => {\n const ops = (req.result as QueuedOperation[]).sort(\n (a, b) => a.enqueuedAt - b.enqueuedAt\n )\n resolve(ops)\n }\n req.onerror = () => reject(req.error)\n })\n )\n }\n}\n","import type { SessionManager } from './SessionManager.js'\nimport type { CommerceHttpAdapter } from '../client/CommerceHttpAdapter.js'\nimport type { CommerceContext } from '../client/CommerceContext.js'\nimport type { SessionInfo } from '../types/index.js'\nimport { AltazionApiError } from '../client/errors.js'\n\n/**\n * Gestionnaire de session pour le mode navigateur classique.\n *\n * Responsabilités :\n * - Appel à POST /commerce/api/sessions/create pour créer la session\n * - La session est identifiée côté serveur via le cookie `altz_session_id`\n * (créé automatiquement par l'API et transmis grâce à credentials:'include')\n */\nexport class BrowserSessionManager implements SessionManager {\n private readonly http: CommerceHttpAdapter\n private readonly context: CommerceContext\n\n constructor(http: CommerceHttpAdapter, context: CommerceContext) {\n this.http = http\n this.context = context\n }\n\n async initialize(): Promise<SessionInfo | null> {\n // Vérifier si une session existe déjà\n try {\n const existing = await this.http.get<SessionInfo>('/commerce/api/sessions/raw')\n return existing\n } catch (err) {\n // 404 = pas de session, on en crée une\n if (err instanceof AltazionApiError && err.status === 404) {\n return this.createSession()\n }\n throw err\n }\n }\n\n async onSessionExpired(): Promise<void> {\n await this.createSession()\n }\n\n private async createSession(): Promise<SessionInfo> {\n const body = this.buildCreationRequest()\n return this.http.post<SessionInfo>('/commerce/api/sessions/create', body)\n }\n\n private buildCreationRequest(): Record<string, unknown> {\n if (this.context.sitePk !== undefined && this.context.rjsId !== undefined) {\n return { sitePk: this.context.sitePk, rjsId: this.context.rjsId }\n }\n if (this.context.siteUrl !== undefined) {\n return { siteUrl: this.context.siteUrl }\n }\n // Fallback : envoyer l'URL courante du navigateur\n const currentUrl = typeof window !== 'undefined' ? window.location.href : undefined\n return { siteUrl: currentUrl }\n }\n}\n","import type { SessionManager } from './SessionManager.js'\nimport type { SessionInfo } from '../types/index.js'\n\n/**\n * Gestionnaire de session pour le mode borne (Altazion Device Shell).\n *\n * Le browser embarqué gère intégralement les cookies de session.\n * Le SDK n'a pas à intervenir : fetch avec credentials:'include' suffit\n * pour que les cookies soient transmis automatiquement.\n *\n * Cette implémentation est un no-op intentionnel.\n */\nexport class KioskSessionManager implements SessionManager {\n async initialize(): Promise<SessionInfo | null> {\n // Le browser embarqué est responsable de la session.\n // Aucune action requise côté SDK.\n return null\n }\n\n async onSessionExpired(): Promise<void> {\n // Le browser embarqué gère le renouvellement de session.\n // Aucune action requise côté SDK.\n }\n}\n","import type { SessionManager } from './SessionManager.js'\nimport { BrowserSessionManager } from './BrowserSessionManager.js'\nimport { KioskSessionManager } from './KioskSessionManager.js'\nimport type { CommerceHttpAdapter } from '../client/CommerceHttpAdapter.js'\nimport type { CommerceContext } from '../client/CommerceContext.js'\n\n/**\n * Instancie le SessionManager adapté au contexte d'exécution.\n *\n * Détection du mode kiosk : présence de \"Altazion Device Shell\" dans le User-Agent.\n * Dans ce cas, le browser embarqué gère entièrement les cookies de session.\n */\nexport class SessionManagerFactory {\n static create(http: CommerceHttpAdapter, context: CommerceContext): SessionManager {\n if (context.mode === 'kiosk') {\n return new KioskSessionManager()\n }\n return new BrowserSessionManager(http, context)\n }\n}\n","import type { CommerceHttpAdapter } from '../../client/CommerceHttpAdapter.js'\nimport type { CacheStrategy } from '../../workers/CacheStrategy.js'\nimport type { SessionInfo } from '../../types/index.js'\n\nexport class SessionModule {\n constructor(\n private readonly http: CommerceHttpAdapter,\n private readonly cache: CacheStrategy\n ) {}\n\n /** Récupère les informations de la session en cours */\n getSession(): Promise<SessionInfo> {\n return this.cache.execute(\n 'session:raw',\n () => this.http.get<SessionInfo>('/commerce/api/sessions/raw'),\n 'network-first'\n )\n }\n\n /** Associe un magasin à la session */\n async associateStore(storeGuid: string): Promise<SessionInfo> {\n const result = await this.http.post<SessionInfo>(\n `/commerce/api/sessions/associate/store/${storeGuid}`\n )\n await this.cache.execute('session:raw', () => Promise.resolve(result), 'network-first')\n return result\n }\n}\n","import type { CommerceHttpAdapter } from '../../client/CommerceHttpAdapter.js'\nimport type { CacheStrategy } from '../../workers/CacheStrategy.js'\nimport type { OfflineQueue } from '../../offline/OfflineQueue.js'\nimport type { ConnectivityManager } from '../../connectivity/ConnectivityManager.js'\nimport type { Cart, CartValidationStatus } from '../../types/index.js'\n\nexport class CartModule {\n constructor(\n private readonly http: CommerceHttpAdapter,\n private readonly cache: CacheStrategy,\n private readonly queue: OfflineQueue,\n private readonly connectivity: ConnectivityManager\n ) {}\n\n /** Récupère le panier en cours */\n getCart(): Promise<Cart> {\n return this.cache.execute(\n 'cart:current',\n () => this.http.get<Cart>('/commerce/api/process/cart'),\n 'network-first'\n )\n }\n\n /** Récupère le statut de validation du panier */\n getValidationStatus(): Promise<CartValidationStatus> {\n return this.http.get<CartValidationStatus>('/commerce/api/process/cart/status/check')\n }\n\n /** Ajoute un article au panier */\n async addItem(reference: string, quantity: number, options?: Record<string, unknown>): Promise<Cart> {\n const body = { reference, quantity, ...options }\n if (this.connectivity.isOffline) {\n await this.queue.enqueue('POST', '/commerce/api/process/cart/items', body)\n // Retourner le panier depuis le cache\n return this.cache.execute('cart:current', () => this.http.get<Cart>('/commerce/api/process/cart'), 'network-first')\n }\n const cart = await this.http.post<Cart>('/commerce/api/process/cart/items', body)\n await this.cache.execute('cart:current', () => Promise.resolve(cart), 'network-first')\n return cart\n }\n\n /** Met à jour la quantité d'une ligne */\n async updateItem(lineId: string, quantity: number): Promise<Cart> {\n const body = { quantity }\n if (this.connectivity.isOffline) {\n await this.queue.enqueue('PUT', `/commerce/api/process/cart/items/${lineId}`, body)\n return this.cache.execute('cart:current', () => this.http.get<Cart>('/commerce/api/process/cart'), 'network-first')\n }\n const cart = await this.http.put<Cart>(`/commerce/api/process/cart/items/${lineId}`, body)\n await this.cache.execute('cart:current', () => Promise.resolve(cart), 'network-first')\n return cart\n }\n\n /** Supprime une ligne du panier */\n async removeItem(lineId: string): Promise<Cart> {\n if (this.connectivity.isOffline) {\n await this.queue.enqueue('DELETE', `/commerce/api/process/cart/items/${lineId}`)\n return this.cache.execute('cart:current', () => this.http.get<Cart>('/commerce/api/process/cart'), 'network-first')\n }\n const cart = await this.http.delete<Cart>(`/commerce/api/process/cart/items/${lineId}`)\n await this.cache.execute('cart:current', () => Promise.resolve(cart), 'network-first')\n return cart\n }\n\n /** Applique un coupon */\n async applyCoupon(code: string): Promise<Cart> {\n const cart = await this.http.post<Cart>('/commerce/api/process/cart/coupons', { code })\n await this.cache.execute('cart:current', () => Promise.resolve(cart), 'network-first')\n return cart\n }\n\n /** Retire un coupon */\n async removeCoupon(code: string): Promise<Cart> {\n const cart = await this.http.delete<Cart>(`/commerce/api/process/cart/coupons/${encodeURIComponent(code)}`)\n await this.cache.execute('cart:current', () => Promise.resolve(cart), 'network-first')\n return cart\n }\n}\n","import type { CommerceHttpAdapter } from '../../client/CommerceHttpAdapter.js'\nimport type { CacheStrategy } from '../../workers/CacheStrategy.js'\nimport type { ProductDetails, WebProduct, SearchRequest, SearchResult } from '../../types/index.js'\n\nexport class CatalogModule {\n constructor(\n private readonly http: CommerceHttpAdapter,\n private readonly cache: CacheStrategy\n ) {}\n\n /** Récupère le détail d'un produit par sa référence */\n getProduct(reference: string): Promise<ProductDetails> {\n return this.cache.execute(\n `catalog:product:${reference}`,\n () => this.http.get<ProductDetails>(`/commerce/api/pim/products/${encodeURIComponent(reference)}`),\n 'stale-while-revalidate'\n )\n }\n\n /** Récupère une liste de produits par leurs références */\n getProducts(references: string[]): Promise<WebProduct[]> {\n const key = `catalog:products:${references.sort().join(',')}`\n return this.cache.execute(\n key,\n () => this.http.post<WebProduct[]>('/commerce/api/pim/products/batch', { references }),\n 'stale-while-revalidate'\n )\n }\n\n /** Récupère les produits d'une catégorie */\n getCategory(categoryCode: string, pageIndex = 0, pageSize = 20): Promise<SearchResult> {\n const key = `catalog:category:${categoryCode}:${pageIndex}:${pageSize}`\n return this.cache.execute(\n key,\n () => this.http.get<SearchResult>(\n `/commerce/api/pim/categories/${encodeURIComponent(categoryCode)}/products?pageIndex=${pageIndex}&pageSize=${pageSize}`\n ),\n 'stale-while-revalidate'\n )\n }\n\n /** Effectue une recherche full-text */\n search(request: SearchRequest): Promise<SearchResult> {\n const key = `catalog:search:${JSON.stringify(request)}`\n return this.cache.execute(\n key,\n () => this.http.post<SearchResult>('/commerce/api/pim/search', request),\n 'cache-first'\n )\n }\n\n /** Récupère les suggestions de recherche (autocomplete) */\n suggest(query: string): Promise<string[]> {\n return this.cache.execute(\n `catalog:suggest:${query}`,\n () => this.http.get<string[]>(`/commerce/api/pim/search/suggest?q=${encodeURIComponent(query)}`),\n 'cache-first',\n 2 * 60_000 // 2 minutes pour les suggestions\n )\n }\n}\n","import type { CommerceHttpAdapter } from '../../client/CommerceHttpAdapter.js'\nimport type { CacheStrategy } from '../../workers/CacheStrategy.js'\nimport type { ShippingChoiceGroup, ShippingAddress, PickupPoint } from '../../types/index.js'\n\nexport class ShippingModule {\n constructor(\n private readonly http: CommerceHttpAdapter,\n private readonly cache: CacheStrategy\n ) {}\n\n /** Récupère les groupes de modes de livraison disponibles pour le panier en cours */\n getAvailableModes(): Promise<ShippingChoiceGroup[]> {\n return this.cache.execute(\n 'shipping:modes',\n () => this.http.get<ShippingChoiceGroup[]>('/commerce/api/process/cart/shipping/modes'),\n 'network-first'\n )\n }\n\n /** Sélectionne un mode de livraison */\n selectMode(shippingModeGuid: string): Promise<void> {\n return this.http.post('/commerce/api/process/cart/shipping/mode', { shippingModeGuid })\n }\n\n /** Met à jour l'adresse de livraison */\n setAddress(address: ShippingAddress): Promise<void> {\n return this.http.put('/commerce/api/process/cart/address/shipping', address)\n }\n\n /** Recherche des points relais à proximité */\n getRelayPoints(postalCode: string, countryCode = 'FR', shippingModeGuid?: string): Promise<PickupPoint[]> {\n const params = new URLSearchParams({ postalCode, countryCode })\n if (shippingModeGuid) params.set('shippingModeGuid', shippingModeGuid)\n const key = `shipping:relays:${postalCode}:${countryCode}:${shippingModeGuid ?? ''}`\n return this.cache.execute(\n key,\n () => this.http.get<PickupPoint[]>(`/commerce/api/process/shipping/relays?${params.toString()}`),\n 'cache-first',\n 10 * 60_000 // 10 minutes\n )\n }\n}\n","import type { CommerceHttpAdapter } from '../../client/CommerceHttpAdapter.js'\nimport type { CacheStrategy } from '../../workers/CacheStrategy.js'\nimport type { MarketingItem } from '../../types/index.js'\n\nexport class MarketingModule {\n constructor(\n private readonly http: CommerceHttpAdapter,\n private readonly cache: CacheStrategy\n ) {}\n\n /** Récupère un item marketing par son code */\n getItem(code: string): Promise<MarketingItem> {\n return this.cache.execute(\n `marketing:item:${code}`,\n () => this.http.get<MarketingItem>(`/commerce/api/marketing/items/${encodeURIComponent(code)}`),\n 'stale-while-revalidate'\n )\n }\n\n /** Récupère plusieurs items marketing par leurs codes */\n getItems(codes: string[]): Promise<MarketingItem[]> {\n const key = `marketing:items:${codes.sort().join(',')}`\n return this.cache.execute(\n key,\n () => this.http.post<MarketingItem[]>('/commerce/api/marketing/items/batch', { codes }),\n 'stale-while-revalidate'\n )\n }\n}\n","import type { CommerceHttpAdapter } from '../../client/CommerceHttpAdapter.js'\nimport type { CacheStrategy } from '../../workers/CacheStrategy.js'\nimport type { StoreWithOpeningHours } from '../../types/index.js'\n\nexport class StoresModule {\n constructor(\n private readonly http: CommerceHttpAdapter,\n private readonly cache: CacheStrategy\n ) {}\n\n /** Recherche des magasins par coordonnées géographiques */\n findByLocation(latitude: number, longitude: number, radiusKm = 50): Promise<StoreWithOpeningHours[]> {\n const key = `stores:geo:${latitude.toFixed(4)}:${longitude.toFixed(4)}:${radiusKm}`\n return this.cache.execute(\n key,\n () => this.http.get<StoreWithOpeningHours[]>(\n `/commerce/api/stores/locator?lat=${latitude}&lon=${longitude}&radius=${radiusKm}`\n ),\n 'cache-first',\n 15 * 60_000 // 15 minutes\n )\n }\n\n /** Recherche des magasins par code postal */\n findByPostalCode(postalCode: string, countryCode = 'FR'): Promise<StoreWithOpeningHours[]> {\n const key = `stores:postal:${postalCode}:${countryCode}`\n return this.cache.execute(\n key,\n () => this.http.get<StoreWithOpeningHours[]>(\n `/commerce/api/stores/locator?postalCode=${encodeURIComponent(postalCode)}&countryCode=${encodeURIComponent(countryCode)}`\n ),\n 'cache-first',\n 15 * 60_000\n )\n }\n\n /** Récupère le détail d'un magasin par son GUID */\n getStore(storeGuid: string): Promise<StoreWithOpeningHours> {\n return this.cache.execute(\n `stores:detail:${storeGuid}`,\n () => this.http.get<StoreWithOpeningHours>(`/commerce/api/stores/${storeGuid}`),\n 'stale-while-revalidate'\n )\n }\n}\n","import { CommerceContext, type CommerceContextOptions } from './CommerceContext.js'\nimport { CommerceHttpAdapter } from './CommerceHttpAdapter.js'\nimport { ConnectivityManager } from '../connectivity/ConnectivityManager.js'\nimport { WorkerBridge } from '../workers/WorkerBridge.js'\nimport { CacheStrategy } from '../workers/CacheStrategy.js'\nimport { OfflineQueue } from '../offline/OfflineQueue.js'\nimport { SessionManagerFactory } from '../session/SessionManagerFactory.js'\nimport { SessionModule } from '../modules/session/index.js'\nimport { CartModule } from '../modules/cart/index.js'\nimport { CatalogModule } from '../modules/catalog/index.js'\nimport { ShippingModule } from '../modules/shipping/index.js'\nimport { MarketingModule } from '../modules/marketing/index.js'\nimport { StoresModule } from '../modules/stores/index.js'\nimport type { SessionManager } from '../session/SessionManager.js'\nimport type { SessionInfo } from '../types/index.js'\n\nexport { CommerceContext, type CommerceContextOptions } from './CommerceContext.js'\n\n/**\n * Point d'entrée principal du SDK Altazion Commerce.\n *\n * Usage :\n * ```ts\n * const client = new CommerceClient({ baseUrl: 'https://api.monsite.com', siteUrl: window.location.href })\n * await client.initialize()\n *\n * const cart = await client.cart.getCart()\n * ```\n */\nexport class CommerceClient {\n readonly context: CommerceContext\n readonly connectivity: ConnectivityManager\n\n readonly session: SessionModule\n readonly cart: CartModule\n readonly catalog: CatalogModule\n readonly shipping: ShippingModule\n readonly marketing: MarketingModule\n readonly stores: StoresModule\n\n private readonly http: CommerceHttpAdapter\n private readonly workerBridge: WorkerBridge\n private readonly cacheStrategy: CacheStrategy\n private readonly offlineQueue: OfflineQueue\n private readonly sessionManager: SessionManager\n\n private unsubscribeOnline: (() => void) | null = null\n\n constructor(options: CommerceContextOptions) {\n this.context = new CommerceContext(options)\n this.http = new CommerceHttpAdapter(this.context)\n this.connectivity = new ConnectivityManager()\n this.workerBridge = new WorkerBridge()\n this.cacheStrategy = new CacheStrategy(this.workerBridge, this.http)\n this.offlineQueue = new OfflineQueue()\n this.sessionManager = SessionManagerFactory.create(this.http, this.context)\n\n // Modules API\n this.session = new SessionModule(this.http, this.cacheStrategy)\n this.cart = new CartModule(this.http, this.cacheStrategy, this.offlineQueue, this.connectivity)\n this.catalog = new CatalogModule(this.http, this.cacheStrategy)\n this.shipping = new ShippingModule(this.http, this.cacheStrategy)\n this.marketing = new MarketingModule(this.http, this.cacheStrategy)\n this.stores = new StoresModule(this.http, this.cacheStrategy)\n }\n\n /**\n * Initialise le SDK :\n * - Crée la session si nécessaire (mode browser uniquement)\n * - Abonne le flush automatique de la file hors-ligne au retour en ligne\n */\n async initialize(): Promise<SessionInfo | null> {\n // Flush automatique quand on revient en ligne\n this.unsubscribeOnline = this.connectivity.subscribe(async (status) => {\n if (status === 'online') {\n await this.offlineQueue.flush(this.http)\n }\n })\n\n return this.sessionManager.initialize()\n }\n\n /**\n * Retourne vrai si le SDK est hors-ligne.\n */\n get isOffline(): boolean {\n return this.connectivity.isOffline\n }\n\n /**\n * Retourne le nombre d'opérations en attente dans la file hors-ligne.\n */\n get pendingOperationsCount(): Promise<number> {\n return this.offlineQueue.pendingCount\n }\n\n /**\n * Abonne un listener aux événements de la file hors-ligne.\n * Retourne une fonction de désabonnement.\n */\n onQueueEvent(\n listener: Parameters<OfflineQueue['subscribe']>[0]\n ): () => void {\n return this.offlineQueue.subscribe(listener)\n }\n\n /**\n * Force le vidage du cache.\n */\n clearCache(): Promise<void> {\n return this.workerBridge.clearAll()\n }\n\n /**\n * Libère les ressources (listeners, worker).\n */\n dispose(): void {\n this.unsubscribeOnline?.()\n this.connectivity.dispose()\n this.workerBridge.dispose()\n }\n}\n"],"names":["data"],"mappings":";;;;;;EA6BO,MAAM,gBAAgB;AAAA,IAS3B,YAAY,SAAiC;AARpC;AACA;AACA;AACA;AACA;AACA;AACA;AAGP,WAAK,UAAU,QAAQ,QAAQ,QAAQ,OAAO,EAAE;AAChD,WAAK,UAAU,QAAQ;AACvB,WAAK,SAAS,QAAQ;AACtB,WAAK,QAAQ,QAAQ;AACrB,WAAK,SAAS,QAAQ,UAAU;AAChC,WAAK,WAAW,QAAQ,YAAY;AACpC,WAAK,OAAO,gBAAgB,WAAA;AAAA,IAC9B;AAAA,IAEA,OAAe,aAA2B;AACxC,UAAI,OAAO,cAAc,eAAe,UAAU,UAAU,SAAS,uBAAuB,GAAG;AAC7F,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,ECtCO,MAAM,yBAAyB,MAAM;AAAA,IAI1C,YAAY,QAAgB,SAAyB;AACnD,YAAM,QAAQ,UAAU,QAAQ,SAAS,QAAQ,MAAM,EAAE;AAJlD;AACA;AAIP,WAAK,OAAO;AACZ,WAAK,SAAS;AACd,WAAK,UAAU;AAAA,IACjB;AAAA,EACF;AAAA,EAMO,MAAM,qBAAqB,MAAM;AAAA,IACtC,YAAY,UAAU,8EAA+E;AACnG,YAAM,OAAO;AACb,WAAK,OAAO;AAAA,IACd;AAAA,EACF;AAAA,EAKO,MAAM,mBAAmB,MAAM;AAAA,IAGpC,YAAY,SAAiB,OAAiB;AAC5C,YAAM,OAAO;AAHN;AAIP,WAAK,OAAO;AACZ,WAAK,QAAQ;AAAA,IACf;AAAA,EACF;AAAA,EAKO,MAAM,wBAAwB,MAAM;AAAA,IAIzC,YAAY,aAAqB,UAA4B;AAC3D,YAAM,iCAAiC,WAAW,MAAM,SAAS,OAAO,EAAE;AAJnE;AACA;AAIP,WAAK,OAAO;AACZ,WAAK,oBAAoB;AACzB,WAAK,WAAW;AAAA,IAClB;AAAA,EACF;AAAA,EC5CO,MAAM,oBAAoB;AAAA,IAG/B,YAAY,SAA0B;AAFrB;AAGf,WAAK,UAAU;AAAA,IACjB;AAAA,IAEA,MAAM,QAAW,MAAc,UAA0B,IAAgB;AACvE,YAAM,EAAE,SAAS,OAAO,MAAM,UAAU,IAAI,aAAa,EAAA,IAAM;AAC/D,YAAM,MAAM,GAAG,KAAK,QAAQ,OAAO,GAAG,IAAI;AAE1C,YAAM,iBAAyC;AAAA,QAC7C,QAAQ;AAAA,QACR,mBAAmB,KAAK,QAAQ;AAAA,QAChC,GAAG;AAAA,MAAA;AAGL,UAAI,SAAS,QAAW;AACtB,uBAAe,cAAc,IAAI;AAAA,MACnC;AAEA,YAAM,OAAoB;AAAA,QACxB;AAAA,QACA,SAAS;AAAA,QACT,aAAa;AAAA,QACb,MAAM,SAAS,SAAY,KAAK,UAAU,IAAI,IAAI;AAAA,MAAA;AAGpD,UAAI;AACJ,eAAS,UAAU,GAAG,WAAW,YAAY,WAAW;AACtD,YAAI;AACF,gBAAM,WAAW,MAAM,MAAM,KAAK,IAAI;AACtC,iBAAO,MAAM,KAAK,eAAkB,QAAQ;AAAA,QAC9C,SAAS,KAAK;AAEZ,cAAI,eAAe,iBAAkB,OAAM;AAC3C,6BAAmB;AAEnB,cAAI,UAAU,YAAY;AACxB,kBAAM,MAAM,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC;AAAA,UACxC;AAAA,QACF;AAAA,MACF;AAEA,YAAM;AAAA,IACR;AAAA,IAEA,MAAc,eAAkB,UAAgC;AAC9D,UAAI,SAAS,IAAI;AACf,cAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,YAAI,SAAS,WAAW,OAAO,CAAC,YAAY,SAAS,kBAAkB,GAAG;AACxE,iBAAO;AAAA,QACT;AACA,eAAO,SAAS,KAAA;AAAA,MAClB;AAGA,UAAI,UAA0B,EAAE,QAAQ,SAAS,OAAA;AACjD,UAAI;AACF,cAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,YAAI,YAAY,SAAS,kBAAkB,KAAK,YAAY,SAAS,0BAA0B,GAAG;AAChG,oBAAU,MAAM,SAAS,KAAA;AACzB,kBAAQ,WAAR,QAAQ,SAAW,SAAS;AAAA,QAC9B;AAAA,MACF,QAAQ;AAAA,MAER;AAEA,YAAM,IAAI,iBAAiB,SAAS,QAAQ,OAAO;AAAA,IACrD;AAAA;AAAA,IAGA,IAAO,MAAc,SAA8C;AACjE,aAAO,KAAK,QAAW,MAAM,EAAE,QAAQ,OAAO,SAAS;AAAA,IACzD;AAAA;AAAA,IAGA,KAAQ,MAAc,MAAgB,SAA8C;AAClF,aAAO,KAAK,QAAW,MAAM,EAAE,QAAQ,QAAQ,MAAM,SAAS;AAAA,IAChE;AAAA;AAAA,IAGA,IAAO,MAAc,MAAgB,SAA8C;AACjF,aAAO,KAAK,QAAW,MAAM,EAAE,QAAQ,OAAO,MAAM,SAAS;AAAA,IAC/D;AAAA;AAAA,IAGA,MAAS,MAAc,MAAgB,SAA8C;AACnF,aAAO,KAAK,QAAW,MAAM,EAAE,QAAQ,SAAS,MAAM,SAAS;AAAA,IACjE;AAAA;AAAA,IAGA,OAAU,MAAc,SAA8C;AACpE,aAAO,KAAK,QAAW,MAAM,EAAE,QAAQ,UAAU,SAAS;AAAA,IAC5D;AAAA,EACF;AAEA,WAAS,MAAM,IAA2B;AACxC,WAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA,EACzD;AAAA,EC7GO,MAAM,oBAAoB;AAAA,IAI/B,cAAc;AAHN;AACS,2DAAgB,IAAA;AA4ChB,0CAAe,MAAY;AAC1C,aAAK,SAAS;AACd,aAAK,KAAK,QAAQ;AAAA,MACpB;AAEiB,2CAAgB,MAAY;AAC3C,aAAK,SAAS;AACd,aAAK,KAAK,SAAS;AAAA,MACrB;AAjDE,WAAK,SAAS,KAAK,iBAAA;AAEnB,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO,iBAAiB,UAAU,KAAK,YAAY;AACnD,eAAO,iBAAiB,WAAW,KAAK,aAAa;AAAA,MACvD;AAAA,IACF;AAAA,IAEA,IAAI,WAAoB;AACtB,aAAO,KAAK,WAAW;AAAA,IACzB;AAAA,IAEA,IAAI,YAAqB;AACvB,aAAO,KAAK,WAAW;AAAA,IACzB;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,UAAU,UAA4C;AACpD,WAAK,UAAU,IAAI,QAAQ;AAC3B,aAAO,MAAM,KAAK,UAAU,OAAO,QAAQ;AAAA,IAC7C;AAAA;AAAA,IAGA,UAAgB;AACd,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO,oBAAoB,UAAU,KAAK,YAAY;AACtD,eAAO,oBAAoB,WAAW,KAAK,aAAa;AAAA,MAC1D;AACA,WAAK,UAAU,MAAA;AAAA,IACjB;AAAA,IAEQ,mBAAuC;AAC7C,UAAI,OAAO,cAAc,eAAe,UAAU,WAAW,QAAW;AACtE,eAAO;AAAA,MACT;AACA,aAAO,UAAU,SAAS,WAAW;AAAA,IACvC;AAAA,IAYQ,KAAK,QAAkC;AAC7C,iBAAW,YAAY,KAAK,WAAW;AACrC,YAAI;AACF,mBAAS,MAAM;AAAA,QACjB,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EC7DO,MAAM,aAAa;AAAA,IAOxB,cAAc;AANN,kCAA2B;AAClB,yDAAc,IAAA;AACvB,4CAAiB;AACR,0DAAe,IAAA;AACf;AAuBA,2CAAgB,CAAC,UAAgF;AAChH,cAAM,EAAE,IAAI,QAAQ,MAAA,IAAU,MAAM;AACpC,cAAM,UAAU,KAAK,QAAQ,IAAI,EAAE;AACnC,YAAI,CAAC,QAAS;AACd,aAAK,QAAQ,OAAO,EAAE;AAEtB,YAAI,UAAU,QAAW;AACvB,kBAAQ,OAAO,IAAI,WAAW,KAAK,CAAC;AAAA,QACtC,OAAO;AACL,kBAAQ,QAAQ,MAAM;AAAA,QACxB;AAAA,MACF;AA/BE,WAAK,gBAAgB,CAAC,KAAK,eAAA;AAAA,IAC7B;AAAA,IAEQ,iBAA0B;AAChC,UAAI,OAAO,iBAAiB,YAAa,QAAO;AAEhD,UAAI;AACF,cAAM,SAAS,IAAI;AAAA,UACjB;;;;;UACA,EAAE,MAAM,UAAU,MAAM,wBAAA;AAAA,QAAwB;AAElD,aAAK,OAAO,OAAO;AACnB,aAAK,KAAK,iBAAiB,WAAW,KAAK,aAAa;AACxD,aAAK,KAAK,MAAA;AACV,eAAO;AAAA,MACT,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IAeQ,SAAiB;AACvB,aAAO,OAAO,EAAE,KAAK,cAAc;AAAA,IACrC;AAAA,IAEQ,KAAK,SAAoD;AAC/D,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAI,CAAC,KAAK,MAAM;AACd,iBAAO,IAAI,WAAW,uBAAuB,CAAC;AAC9C;AAAA,QACF;AACA,cAAM,KAAK,KAAK,OAAA;AAChB,aAAK,QAAQ,IAAI,IAAI,EAAE,SAAS,QAAQ;AACxC,aAAK,KAAK,YAAY,EAAE,GAAG,SAAS,IAAI;AAAA,MAC1C,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,IAAO,KAAgC;AAC3C,UAAI,KAAK,eAAe;AACtB,cAAM,QAAQ,KAAK,SAAS,IAAI,GAAG;AACnC,YAAI,CAAC,SAAS,KAAK,QAAQ,MAAM,UAAW,QAAO;AACnD,eAAO,MAAM;AAAA,MACf;AACA,aAAO,KAAK,KAAK,EAAE,MAAM,OAAO,KAAK;AAAA,IACvC;AAAA,IAEA,MAAM,IAAI,KAAa,MAAe,OAA8B;AAClE,UAAI,KAAK,eAAe;AACtB,aAAK,SAAS,IAAI,KAAK,EAAE,MAAM,WAAW,KAAK,QAAQ,OAAO;AAC9D;AAAA,MACF;AACA,YAAM,KAAK,KAAK,EAAE,MAAM,OAAO,KAAK,MAAM,KAAK,OAAO;AAAA,IACxD;AAAA,IAEA,MAAM,WAAW,SAAgC;AAC/C,UAAI,KAAK,eAAe;AACtB,cAAM,QAAQ,IAAI,OAAO,OAAO;AAChC,mBAAW,OAAO,KAAK,SAAS,KAAA,GAAQ;AACtC,cAAI,MAAM,KAAK,GAAG,EAAG,MAAK,SAAS,OAAO,GAAG;AAAA,QAC/C;AACA;AAAA,MACF;AACA,YAAM,KAAK,KAAK,EAAE,MAAM,cAAc,SAAS;AAAA,IACjD;AAAA,IAEA,MAAM,WAA0B;AAC9B,UAAI,KAAK,eAAe;AACtB,aAAK,SAAS,MAAA;AACd;AAAA,MACF;AACA,YAAM,KAAK,KAAK,EAAE,MAAM,aAAa;AAAA,IACvC;AAAA,IAEA,UAAgB;;AACd,iBAAK,SAAL,mBAAW,oBAAoB,WAAW,KAAK;AAC/C,WAAK,QAAQ,MAAA;AAAA,IACf;AAAA,EACF;ACnGO,QAAM,cAA0E;AAAA,IACrF,iBAAiB;AAAA;AAAA,IACjB,eAAe,IAAI;AAAA;AAAA,IACnB,0BAA0B,KAAK;AAAA;AAAA,EACjC;AAAA,EAEO,MAAM,cAAc;AAAA,IACzB,YACmB,QACA,MACjB;AAFiB,WAAA,SAAA;AACA,WAAA,OAAA;AAAA,IAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUH,MAAM,QACJ,KACA,SACA,UACA,OACY;AACZ,cAAQ,UAAA;AAAA,QACN,KAAK;AACH,iBAAO,QAAA;AAAA,QAET,KAAK;AACH,iBAAO,KAAK,aAAgB,KAAK,SAAS,SAAS,YAAY,eAAe,CAAC;AAAA,QAEjF,KAAK;AACH,iBAAO,KAAK,WAAc,KAAK,SAAS,SAAS,YAAY,aAAa,CAAC;AAAA,QAE7E,KAAK;AACH,iBAAO,KAAK,qBAAwB,KAAK,SAAS,SAAS,YAAY,wBAAwB,CAAC;AAAA,MAAA;AAAA,IAEtG;AAAA,IAEA,MAAc,aAAgB,KAAa,SAA2B,OAA2B;AAC/F,UAAI;AACF,cAAM,OAAO,MAAM,QAAA;AACnB,cAAM,KAAK,OAAO,IAAI,KAAK,MAAM,KAAK;AACtC,eAAO;AAAA,MACT,SAAS,KAAK;AAEZ,YAAI,eAAe,WAAW;AAC5B,gBAAM,SAAS,MAAM,KAAK,OAAO,IAAO,GAAG;AAC3C,cAAI,WAAW,KAAM,QAAO;AAAA,QAC9B;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IAEA,MAAc,WAAc,KAAa,SAA2B,OAA2B;AAC7F,YAAM,SAAS,MAAM,KAAK,OAAO,IAAO,GAAG;AAC3C,UAAI,WAAW,KAAM,QAAO;AAE5B,YAAM,OAAO,MAAM,QAAA;AACnB,YAAM,KAAK,OAAO,IAAI,KAAK,MAAM,KAAK;AACtC,aAAO;AAAA,IACT;AAAA,IAEA,MAAc,qBAAwB,KAAa,SAA2B,OAA2B;AACvG,YAAM,SAAS,MAAM,KAAK,OAAO,IAAO,GAAG;AAGxB,cAAA,EAChB,KAAK,CAACA,UAAS,KAAK,OAAO,IAAI,KAAKA,OAAM,KAAK,CAAC,EAChD,MAAM,MAAM;AAAA,MAA6C,CAAC;AAE7D,UAAI,WAAW,MAAM;AAGnB,eAAO;AAAA,MACT;AAGA,YAAM,OAAO,MAAM,QAAA;AACnB,YAAM,KAAK,OAAO,IAAI,KAAK,MAAM,KAAK;AACtC,aAAO;AAAA,IACT;AAAA,EACF;AC7FA,QAAM,UAAU;AAChB,QAAM,aAAa;AACnB,QAAM,aAAa;AAAA,EAqBZ,MAAM,aAAa;AAAA,IAAnB;AACG,gCAAyB;AAChB,2DAAgB,IAAA;AACzB,qCAAU;AAAA;AAAA;AAAA,IAGlB,UAAU,UAA0C;AAClD,WAAK,UAAU,IAAI,QAAQ;AAC3B,aAAO,MAAM,KAAK,UAAU,OAAO,QAAQ;AAAA,IAC7C;AAAA,IAEA,IAAI,eAAgC;AAClC,aAAO,KAAK,SAAS;AAAA,QACnB,CAAC,OACC,IAAI,QAAgB,CAAC,SAAS,WAAW;AACvC,gBAAM,KAAK,GAAG,YAAY,YAAY,UAAU;AAChD,gBAAM,MAAM,GAAG,YAAY,UAAU,EAAE,MAAA;AACvC,cAAI,YAAY,MAAM,QAAQ,IAAI,MAAM;AACxC,cAAI,UAAU,MAAM,OAAO,IAAI,KAAK;AAAA,QACtC,CAAC;AAAA,MAAA;AAAA,IAEP;AAAA,IAEA,MAAM,QACJ,QACA,MACA,MACA,SAC0B;AAC1B,YAAM,YAA6B;AAAA,QACjC,IAAI,GAAG,KAAK,IAAA,CAAK,IAAI,EAAE,KAAK,OAAO;AAAA,QACnC,YAAY,KAAK,IAAA;AAAA,QACjB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU;AAAA,MAAA;AAGZ,YAAM,KAAK,MAAM,KAAK,OAAA;AACtB,YAAM,KAAK,IAAI,IAAI,SAAS;AAC5B,WAAK,KAAK,EAAE,MAAM,YAAY,WAAW;AACzC,aAAO;AAAA,IACT;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,MAAM,MAAM,MAA0C;AACpD,YAAM,aAAa,MAAM,KAAK,OAAA;AAC9B,UAAI,WAAW,WAAW,EAAG;AAE7B,iBAAW,MAAM,YAAY;AAC3B,YAAI;AACF,aAAG;AACH,gBAAM,KAAK,QAAQ,GAAG,MAAM;AAAA,YAC1B,QAAQ,GAAG;AAAA,YACX,MAAM,GAAG;AAAA,YACT,SAAS,GAAG;AAAA,UAAA,CACb;AACD,gBAAM,KAAK,OAAO,GAAG,EAAE;AAAA,QACzB,SAAS,KAAK;AACZ,cAAI,eAAe,oBAAoB,IAAI,UAAU,OAAO,IAAI,SAAS,KAAK;AAC5E,kBAAM,aAAa,IAAI,gBAAgB,GAAG,IAAI,GAAG;AACjD,iBAAK,KAAK,EAAE,MAAM,YAAY,WAAW,IAAI,OAAO,YAAY;AAEhE;AAAA,UACF;AAEA,gBAAM,KAAK,IAAI,MAAM,KAAK,OAAA,GAAU,EAAE;AACtC;AAAA,QACF;AAAA,MACF;AAEA,WAAK,KAAK,EAAE,MAAM,UAAA,CAAW;AAE7B,YAAM,YAAY,MAAM,KAAK;AAC7B,UAAI,cAAc,GAAG;AACnB,aAAK,KAAK,EAAE,MAAM,UAAA,CAAW;AAAA,MAC/B;AAAA,IACF;AAAA,IAEQ,KAAK,OAAyB;AACpC,iBAAW,YAAY,KAAK,WAAW;AACrC,YAAI;AAAE,mBAAS,KAAK;AAAA,QAAE,QAAQ;AAAA,QAAkB;AAAA,MAClD;AAAA,IACF;AAAA,IAEQ,SAA+B;AACrC,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAI,KAAK,IAAI;AAAE,kBAAQ,KAAK,EAAE;AAAG;AAAA,QAAO;AAExC,cAAM,UAAU,UAAU,KAAK,SAAS,UAAU;AAElD,gBAAQ,kBAAkB,CAAC,UAAU;AACnC,gBAAM,WAAY,MAAM,OAA4B;AACpD,cAAI,CAAC,SAAS,iBAAiB,SAAS,UAAU,GAAG;AACnD,qBAAS,kBAAkB,YAAY,EAAE,SAAS,MAAM;AAAA,UAC1D;AAAA,QACF;AAEA,gBAAQ,YAAY,CAAC,UAAU;AAC7B,eAAK,KAAM,MAAM,OAA4B;AAC7C,kBAAQ,KAAK,EAAE;AAAA,QACjB;AAEA,gBAAQ,UAAU,MAAM,OAAO,QAAQ,KAAK;AAAA,MAC9C,CAAC;AAAA,IACH;AAAA,IAEQ,IAAI,IAAiB,WAA2C;AACtE,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,cAAM,KAAK,GAAG,YAAY,YAAY,WAAW;AACjD,cAAM,MAAM,GAAG,YAAY,UAAU,EAAE,IAAI,SAAS;AACpD,YAAI,YAAY,MAAM,QAAA;AACtB,YAAI,UAAU,MAAM,OAAO,IAAI,KAAK;AAAA,MACtC,CAAC;AAAA,IACH;AAAA,IAEQ,OAAO,IAA2B;AACxC,aAAO,KAAK,SAAS;AAAA,QACnB,CAAC,OACC,IAAI,QAAc,CAAC,SAAS,WAAW;AACrC,gBAAM,KAAK,GAAG,YAAY,YAAY,WAAW;AACjD,gBAAM,MAAM,GAAG,YAAY,UAAU,EAAE,OAAO,EAAE;AAChD,cAAI,YAAY,MAAM,QAAA;AACtB,cAAI,UAAU,MAAM,OAAO,IAAI,KAAK;AAAA,QACtC,CAAC;AAAA,MAAA;AAAA,IAEP;AAAA,IAEQ,SAAqC;AAC3C,aAAO,KAAK,SAAS;AAAA,QACnB,CAAC,OACC,IAAI,QAA2B,CAAC,SAAS,WAAW;AAClD,gBAAM,KAAK,GAAG,YAAY,YAAY,UAAU;AAChD,gBAAM,MAAM,GAAG,YAAY,UAAU,EAAE,OAAA;AACvC,cAAI,YAAY,MAAM;AACpB,kBAAM,MAAO,IAAI,OAA6B;AAAA,cAC5C,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE;AAAA,YAAA;AAE7B,oBAAQ,GAAG;AAAA,UACb;AACA,cAAI,UAAU,MAAM,OAAO,IAAI,KAAK;AAAA,QACtC,CAAC;AAAA,MAAA;AAAA,IAEP;AAAA,EACF;AAAA,ECjKO,MAAM,sBAAgD;AAAA,IAI3D,YAAY,MAA2B,SAA0B;AAHhD;AACA;AAGf,WAAK,OAAO;AACZ,WAAK,UAAU;AAAA,IACjB;AAAA,IAEA,MAAM,aAA0C;AAE9C,UAAI;AACF,cAAM,WAAW,MAAM,KAAK,KAAK,IAAiB,4BAA4B;AAC9E,eAAO;AAAA,MACT,SAAS,KAAK;AAEZ,YAAI,eAAe,oBAAoB,IAAI,WAAW,KAAK;AACzD,iBAAO,KAAK,cAAA;AAAA,QACd;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IAEA,MAAM,mBAAkC;AACtC,YAAM,KAAK,cAAA;AAAA,IACb;AAAA,IAEA,MAAc,gBAAsC;AAClD,YAAM,OAAO,KAAK,qBAAA;AAClB,aAAO,KAAK,KAAK,KAAkB,iCAAiC,IAAI;AAAA,IAC1E;AAAA,IAEQ,uBAAgD;AACtD,UAAI,KAAK,QAAQ,WAAW,UAAa,KAAK,QAAQ,UAAU,QAAW;AACzE,eAAO,EAAE,QAAQ,KAAK,QAAQ,QAAQ,OAAO,KAAK,QAAQ,MAAA;AAAA,MAC5D;AACA,UAAI,KAAK,QAAQ,YAAY,QAAW;AACtC,eAAO,EAAE,SAAS,KAAK,QAAQ,QAAA;AAAA,MACjC;AAEA,YAAM,aAAa,OAAO,WAAW,cAAc,OAAO,SAAS,OAAO;AAC1E,aAAO,EAAE,SAAS,WAAA;AAAA,IACpB;AAAA,EACF;AAAA,EC7CO,MAAM,oBAA8C;AAAA,IACzD,MAAM,aAA0C;AAG9C,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,mBAAkC;AAAA,IAGxC;AAAA,EACF;AAAA,ECXO,MAAM,sBAAsB;AAAA,IACjC,OAAO,OAAO,MAA2B,SAA0C;AACjF,UAAI,QAAQ,SAAS,SAAS;AAC5B,eAAO,IAAI,oBAAA;AAAA,MACb;AACA,aAAO,IAAI,sBAAsB,MAAM,OAAO;AAAA,IAChD;AAAA,EACF;AAAA,ECfO,MAAM,cAAc;AAAA,IACzB,YACmB,MACA,OACjB;AAFiB,WAAA,OAAA;AACA,WAAA,QAAA;AAAA,IAChB;AAAA;AAAA,IAGH,aAAmC;AACjC,aAAO,KAAK,MAAM;AAAA,QAChB;AAAA,QACA,MAAM,KAAK,KAAK,IAAiB,4BAA4B;AAAA,QAC7D;AAAA,MAAA;AAAA,IAEJ;AAAA;AAAA,IAGA,MAAM,eAAe,WAAyC;AAC5D,YAAM,SAAS,MAAM,KAAK,KAAK;AAAA,QAC7B,0CAA0C,SAAS;AAAA,MAAA;AAErD,YAAM,KAAK,MAAM,QAAQ,eAAe,MAAM,QAAQ,QAAQ,MAAM,GAAG,eAAe;AACtF,aAAO;AAAA,IACT;AAAA,EACF;AAAA,ECrBO,MAAM,WAAW;AAAA,IACtB,YACmB,MACA,OACA,OACA,cACjB;AAJiB,WAAA,OAAA;AACA,WAAA,QAAA;AACA,WAAA,QAAA;AACA,WAAA,eAAA;AAAA,IAChB;AAAA;AAAA,IAGH,UAAyB;AACvB,aAAO,KAAK,MAAM;AAAA,QAChB;AAAA,QACA,MAAM,KAAK,KAAK,IAAU,4BAA4B;AAAA,QACtD;AAAA,MAAA;AAAA,IAEJ;AAAA;AAAA,IAGA,sBAAqD;AACnD,aAAO,KAAK,KAAK,IAA0B,yCAAyC;AAAA,IACtF;AAAA;AAAA,IAGA,MAAM,QAAQ,WAAmB,UAAkB,SAAkD;AACnG,YAAM,OAAO,EAAE,WAAW,UAAU,GAAG,QAAA;AACvC,UAAI,KAAK,aAAa,WAAW;AAC/B,cAAM,KAAK,MAAM,QAAQ,QAAQ,oCAAoC,IAAI;AAEzE,eAAO,KAAK,MAAM,QAAQ,gBAAgB,MAAM,KAAK,KAAK,IAAU,4BAA4B,GAAG,eAAe;AAAA,MACpH;AACA,YAAM,OAAO,MAAM,KAAK,KAAK,KAAW,oCAAoC,IAAI;AAChF,YAAM,KAAK,MAAM,QAAQ,gBAAgB,MAAM,QAAQ,QAAQ,IAAI,GAAG,eAAe;AACrF,aAAO;AAAA,IACT;AAAA;AAAA,IAGA,MAAM,WAAW,QAAgB,UAAiC;AAChE,YAAM,OAAO,EAAE,SAAA;AACf,UAAI,KAAK,aAAa,WAAW;AAC/B,cAAM,KAAK,MAAM,QAAQ,OAAO,oCAAoC,MAAM,IAAI,IAAI;AAClF,eAAO,KAAK,MAAM,QAAQ,gBAAgB,MAAM,KAAK,KAAK,IAAU,4BAA4B,GAAG,eAAe;AAAA,MACpH;AACA,YAAM,OAAO,MAAM,KAAK,KAAK,IAAU,oCAAoC,MAAM,IAAI,IAAI;AACzF,YAAM,KAAK,MAAM,QAAQ,gBAAgB,MAAM,QAAQ,QAAQ,IAAI,GAAG,eAAe;AACrF,aAAO;AAAA,IACT;AAAA;AAAA,IAGA,MAAM,WAAW,QAA+B;AAC9C,UAAI,KAAK,aAAa,WAAW;AAC/B,cAAM,KAAK,MAAM,QAAQ,UAAU,oCAAoC,MAAM,EAAE;AAC/E,eAAO,KAAK,MAAM,QAAQ,gBAAgB,MAAM,KAAK,KAAK,IAAU,4BAA4B,GAAG,eAAe;AAAA,MACpH;AACA,YAAM,OAAO,MAAM,KAAK,KAAK,OAAa,oCAAoC,MAAM,EAAE;AACtF,YAAM,KAAK,MAAM,QAAQ,gBAAgB,MAAM,QAAQ,QAAQ,IAAI,GAAG,eAAe;AACrF,aAAO;AAAA,IACT;AAAA;AAAA,IAGA,MAAM,YAAY,MAA6B;AAC7C,YAAM,OAAO,MAAM,KAAK,KAAK,KAAW,sCAAsC,EAAE,MAAM;AACtF,YAAM,KAAK,MAAM,QAAQ,gBAAgB,MAAM,QAAQ,QAAQ,IAAI,GAAG,eAAe;AACrF,aAAO;AAAA,IACT;AAAA;AAAA,IAGA,MAAM,aAAa,MAA6B;AAC9C,YAAM,OAAO,MAAM,KAAK,KAAK,OAAa,sCAAsC,mBAAmB,IAAI,CAAC,EAAE;AAC1G,YAAM,KAAK,MAAM,QAAQ,gBAAgB,MAAM,QAAQ,QAAQ,IAAI,GAAG,eAAe;AACrF,aAAO;AAAA,IACT;AAAA,EACF;AAAA,ECzEO,MAAM,cAAc;AAAA,IACzB,YACmB,MACA,OACjB;AAFiB,WAAA,OAAA;AACA,WAAA,QAAA;AAAA,IAChB;AAAA;AAAA,IAGH,WAAW,WAA4C;AACrD,aAAO,KAAK,MAAM;AAAA,QAChB,mBAAmB,SAAS;AAAA,QAC5B,MAAM,KAAK,KAAK,IAAoB,8BAA8B,mBAAmB,SAAS,CAAC,EAAE;AAAA,QACjG;AAAA,MAAA;AAAA,IAEJ;AAAA;AAAA,IAGA,YAAY,YAA6C;AACvD,YAAM,MAAM,oBAAoB,WAAW,OAAO,KAAK,GAAG,CAAC;AAC3D,aAAO,KAAK,MAAM;AAAA,QAChB;AAAA,QACA,MAAM,KAAK,KAAK,KAAmB,oCAAoC,EAAE,YAAY;AAAA,QACrF;AAAA,MAAA;AAAA,IAEJ;AAAA;AAAA,IAGA,YAAY,cAAsB,YAAY,GAAG,WAAW,IAA2B;AACrF,YAAM,MAAM,oBAAoB,YAAY,IAAI,SAAS,IAAI,QAAQ;AACrE,aAAO,KAAK,MAAM;AAAA,QAChB;AAAA,QACA,MAAM,KAAK,KAAK;AAAA,UACd,gCAAgC,mBAAmB,YAAY,CAAC,uBAAuB,SAAS,aAAa,QAAQ;AAAA,QAAA;AAAA,QAEvH;AAAA,MAAA;AAAA,IAEJ;AAAA;AAAA,IAGA,OAAO,SAA+C;AACpD,YAAM,MAAM,kBAAkB,KAAK,UAAU,OAAO,CAAC;AACrD,aAAO,KAAK,MAAM;AAAA,QAChB;AAAA,QACA,MAAM,KAAK,KAAK,KAAmB,4BAA4B,OAAO;AAAA,QACtE;AAAA,MAAA;AAAA,IAEJ;AAAA;AAAA,IAGA,QAAQ,OAAkC;AACxC,aAAO,KAAK,MAAM;AAAA,QAChB,mBAAmB,KAAK;AAAA,QACxB,MAAM,KAAK,KAAK,IAAc,sCAAsC,mBAAmB,KAAK,CAAC,EAAE;AAAA,QAC/F;AAAA,QACA,IAAI;AAAA;AAAA,MAAA;AAAA,IAER;AAAA,EACF;AAAA,ECxDO,MAAM,eAAe;AAAA,IAC1B,YACmB,MACA,OACjB;AAFiB,WAAA,OAAA;AACA,WAAA,QAAA;AAAA,IAChB;AAAA;AAAA,IAGH,oBAAoD;AAClD,aAAO,KAAK,MAAM;AAAA,QAChB;AAAA,QACA,MAAM,KAAK,KAAK,IAA2B,2CAA2C;AAAA,QACtF;AAAA,MAAA;AAAA,IAEJ;AAAA;AAAA,IAGA,WAAW,kBAAyC;AAClD,aAAO,KAAK,KAAK,KAAK,4CAA4C,EAAE,kBAAkB;AAAA,IACxF;AAAA;AAAA,IAGA,WAAW,SAAyC;AAClD,aAAO,KAAK,KAAK,IAAI,+CAA+C,OAAO;AAAA,IAC7E;AAAA;AAAA,IAGA,eAAe,YAAoB,cAAc,MAAM,kBAAmD;AACxG,YAAM,SAAS,IAAI,gBAAgB,EAAE,YAAY,aAAa;AAC9D,UAAI,iBAAkB,QAAO,IAAI,oBAAoB,gBAAgB;AACrE,YAAM,MAAM,mBAAmB,UAAU,IAAI,WAAW,IAAI,oBAAoB,EAAE;AAClF,aAAO,KAAK,MAAM;AAAA,QAChB;AAAA,QACA,MAAM,KAAK,KAAK,IAAmB,yCAAyC,OAAO,SAAA,CAAU,EAAE;AAAA,QAC/F;AAAA,QACA,KAAK;AAAA;AAAA,MAAA;AAAA,IAET;AAAA,EACF;AAAA,ECrCO,MAAM,gBAAgB;AAAA,IAC3B,YACmB,MACA,OACjB;AAFiB,WAAA,OAAA;AACA,WAAA,QAAA;AAAA,IAChB;AAAA;AAAA,IAGH,QAAQ,MAAsC;AAC5C,aAAO,KAAK,MAAM;AAAA,QAChB,kBAAkB,IAAI;AAAA,QACtB,MAAM,KAAK,KAAK,IAAmB,iCAAiC,mBAAmB,IAAI,CAAC,EAAE;AAAA,QAC9F;AAAA,MAAA;AAAA,IAEJ;AAAA;AAAA,IAGA,SAAS,OAA2C;AAClD,YAAM,MAAM,mBAAmB,MAAM,OAAO,KAAK,GAAG,CAAC;AACrD,aAAO,KAAK,MAAM;AAAA,QAChB;AAAA,QACA,MAAM,KAAK,KAAK,KAAsB,uCAAuC,EAAE,OAAO;AAAA,QACtF;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA,ECxBO,MAAM,aAAa;AAAA,IACxB,YACmB,MACA,OACjB;AAFiB,WAAA,OAAA;AACA,WAAA,QAAA;AAAA,IAChB;AAAA;AAAA,IAGH,eAAe,UAAkB,WAAmB,WAAW,IAAsC;AACnG,YAAM,MAAM,cAAc,SAAS,QAAQ,CAAC,CAAC,IAAI,UAAU,QAAQ,CAAC,CAAC,IAAI,QAAQ;AACjF,aAAO,KAAK,MAAM;AAAA,QAChB;AAAA,QACA,MAAM,KAAK,KAAK;AAAA,UACd,oCAAoC,QAAQ,QAAQ,SAAS,WAAW,QAAQ;AAAA,QAAA;AAAA,QAElF;AAAA,QACA,KAAK;AAAA;AAAA,MAAA;AAAA,IAET;AAAA;AAAA,IAGA,iBAAiB,YAAoB,cAAc,MAAwC;AACzF,YAAM,MAAM,iBAAiB,UAAU,IAAI,WAAW;AACtD,aAAO,KAAK,MAAM;AAAA,QAChB;AAAA,QACA,MAAM,KAAK,KAAK;AAAA,UACd,2CAA2C,mBAAmB,UAAU,CAAC,gBAAgB,mBAAmB,WAAW,CAAC;AAAA,QAAA;AAAA,QAE1H;AAAA,QACA,KAAK;AAAA,MAAA;AAAA,IAET;AAAA;AAAA,IAGA,SAAS,WAAmD;AAC1D,aAAO,KAAK,MAAM;AAAA,QAChB,iBAAiB,SAAS;AAAA,QAC1B,MAAM,KAAK,KAAK,IAA2B,wBAAwB,SAAS,EAAE;AAAA,QAC9E;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA,ECfO,MAAM,eAAe;AAAA,IAmB1B,YAAY,SAAiC;AAlBpC;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEQ;AACA;AACA;AACA;AACA;AAET,+CAAyC;AAG/C,WAAK,UAAU,IAAI,gBAAgB,OAAO;AAC1C,WAAK,OAAO,IAAI,oBAAoB,KAAK,OAAO;AAChD,WAAK,eAAe,IAAI,oBAAA;AACxB,WAAK,eAAe,IAAI,aAAA;AACxB,WAAK,gBAAgB,IAAI,cAAc,KAAK,cAAc,KAAK,IAAI;AACnE,WAAK,eAAe,IAAI,aAAA;AACxB,WAAK,iBAAiB,sBAAsB,OAAO,KAAK,MAAM,KAAK,OAAO;AAG1E,WAAK,UAAU,IAAI,cAAc,KAAK,MAAM,KAAK,aAAa;AAC9D,WAAK,OAAO,IAAI,WAAW,KAAK,MAAM,KAAK,eAAe,KAAK,cAAc,KAAK,YAAY;AAC9F,WAAK,UAAU,IAAI,cAAc,KAAK,MAAM,KAAK,aAAa;AAC9D,WAAK,WAAW,IAAI,eAAe,KAAK,MAAM,KAAK,aAAa;AAChE,WAAK,YAAY,IAAI,gBAAgB,KAAK,MAAM,KAAK,aAAa;AAClE,WAAK,SAAS,IAAI,aAAa,KAAK,MAAM,KAAK,aAAa;AAAA,IAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,MAAM,aAA0C;AAE9C,WAAK,oBAAoB,KAAK,aAAa,UAAU,OAAO,WAAW;AACrE,YAAI,WAAW,UAAU;AACvB,gBAAM,KAAK,aAAa,MAAM,KAAK,IAAI;AAAA,QACzC;AAAA,MACF,CAAC;AAED,aAAO,KAAK,eAAe,WAAA;AAAA,IAC7B;AAAA;AAAA;AAAA;AAAA,IAKA,IAAI,YAAqB;AACvB,aAAO,KAAK,aAAa;AAAA,IAC3B;AAAA;AAAA;AAAA;AAAA,IAKA,IAAI,yBAA0C;AAC5C,aAAO,KAAK,aAAa;AAAA,IAC3B;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,aACE,UACY;AACZ,aAAO,KAAK,aAAa,UAAU,QAAQ;AAAA,IAC7C;AAAA;AAAA;AAAA;AAAA,IAKA,aAA4B;AAC1B,aAAO,KAAK,aAAa,SAAA;AAAA,IAC3B;AAAA;AAAA;AAAA;AAAA,IAKA,UAAgB;;AACd,iBAAK,sBAAL;AACA,WAAK,aAAa,QAAA;AAClB,WAAK,aAAa,QAAA;AAAA,IACpB;AAAA,EACF;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"index.iife.js","sources":["../src/client/CommerceContext.ts","../src/client/errors.ts","../src/client/CommerceHttpAdapter.ts","../src/connectivity/ConnectivityManager.ts","../src/workers/WorkerBridge.ts","../src/workers/CacheStrategy.ts","../src/session/BrowserSessionManager.ts","../src/session/KioskSessionManager.ts","../src/session/SessionManagerFactory.ts","../src/modules/session/index.ts","../src/modules/cart/index.ts","../src/modules/catalog/index.ts","../src/modules/shipping/index.ts","../src/modules/marketing/index.ts","../src/modules/stores/index.ts","../src/client/CommerceClient.ts"],"sourcesContent":["/**\n * Mode d'exécution du SDK.\n * - `browser` : navigateur classique — le SDK gère la création de session et le cookie.\n * - `kiosk` : borne avec \"Altazion Device Shell\" — le browser embarqué gère les cookies.\n */\nexport type CommerceMode = 'browser' | 'kiosk'\n\nexport interface CommerceContextOptions {\n /** URL de base de l'API (ex. : \"https://api.monsite.com\") */\n baseUrl: string\n /** Identifiant ou URL du site web pour la création de session (mode browser) */\n siteUrl?: string\n /**\n * Clé primaire du site (alternative à siteUrl).\n * Requiert aussi rjsId.\n */\n sitePk?: number\n /** Identifiant du tenant RJS (nécessaire avec sitePk) */\n rjsId?: number\n /** Locale par défaut (ex. : \"fr-FR\") */\n locale?: string\n /** Devise par défaut (ex. : \"EUR\") */\n currency?: string\n}\n\n/**\n * Contexte partagé du SDK, instancié une seule fois et transmis\n * à tous les sous-systèmes (HttpAdapter, SessionManager, modules…).\n */\nexport class CommerceContext {\n readonly baseUrl: string\n readonly siteUrl: string | undefined\n readonly sitePk: number | undefined\n readonly rjsId: number | undefined\n readonly locale: string\n readonly currency: string\n readonly mode: CommerceMode\n\n constructor(options: CommerceContextOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, '')\n this.siteUrl = options.siteUrl\n this.sitePk = options.sitePk\n this.rjsId = options.rjsId\n this.locale = options.locale ?? 'fr-FR'\n this.currency = options.currency ?? 'EUR'\n this.mode = CommerceContext.detectMode()\n }\n\n private static detectMode(): CommerceMode {\n if (typeof navigator !== 'undefined' && navigator.userAgent.includes('Altazion Device Shell')) {\n return 'kiosk'\n }\n return 'browser'\n }\n}\n","/**\n * Représentation d'une réponse d'erreur ProblemDetails (RFC 9457)\n * telle que renvoyée par GlobalExceptionFilter.cs\n */\nexport interface ProblemDetails {\n type?: string\n title?: string\n status?: number\n detail?: string\n instance?: string\n [key: string]: unknown\n}\n\n/**\n * Erreur générée par l'API Altazion Commerce (HTTP 4xx / 5xx).\n */\nexport class AltazionApiError extends Error {\n readonly status: number\n readonly problem: ProblemDetails\n\n constructor(status: number, problem: ProblemDetails) {\n super(problem.detail ?? problem.title ?? `HTTP ${status}`)\n this.name = 'AltazionApiError'\n this.status = status\n this.problem = problem\n }\n}\n\n/**\n * Erreur levée lorsque le SDK est hors-ligne et ne peut pas\n * satisfaire la requête depuis le cache.\n */\nexport class OfflineError extends Error {\n constructor(message = 'Le SDK est hors-ligne et aucun cache n\\'est disponible pour cette ressource') {\n super(message)\n this.name = 'OfflineError'\n }\n}\n\n/**\n * Erreur liée à une opération sur le cache (IndexedDB, SharedWorker).\n */\nexport class CacheError extends Error {\n readonly cause: unknown\n\n constructor(message: string, cause?: unknown) {\n super(message)\n this.name = 'CacheError'\n this.cause = cause\n }\n}\n","import { AltazionApiError, type ProblemDetails } from './errors.js'\nimport type { CommerceContext } from './CommerceContext.js'\n\nexport interface RequestOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n body?: unknown\n headers?: Record<string, string>\n /** Nombre maximum de tentatives réseau (uniquement sur erreurs réseau, jamais sur 4xx) */\n maxRetries?: number\n}\n\n/**\n * Adaptateur HTTP basé sur fetch natif.\n *\n * Responsabilités :\n * - Construction des URLs à partir du baseUrl du contexte\n * - Ajout des headers standard (Content-Type, Accept, locale)\n * - credentials: 'include' pour la transmission automatique des cookies (modes browser et kiosk)\n * - Parse des réponses ProblemDetails en AltazionApiError\n * - Retry limité sur les erreurs réseau (TypeError), jamais sur les 4xx/5xx\n */\nexport class CommerceHttpAdapter {\n private readonly context: CommerceContext\n\n constructor(context: CommerceContext) {\n this.context = context\n }\n\n async request<T>(path: string, options: RequestOptions = {}): Promise<T> {\n const { method = 'GET', body, headers = {}, maxRetries = 2 } = options\n const url = `${this.context.baseUrl}${path}`\n\n const requestHeaders: Record<string, string> = {\n Accept: 'application/json',\n 'Accept-Language': this.context.locale,\n ...headers\n }\n\n if (body !== undefined) {\n requestHeaders['Content-Type'] = 'application/json'\n }\n\n const init: RequestInit = {\n method,\n headers: requestHeaders,\n credentials: 'include',\n body: body !== undefined ? JSON.stringify(body) : undefined\n }\n\n let lastNetworkError: unknown\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const response = await fetch(url, init)\n return await this.handleResponse<T>(response)\n } catch (err) {\n // AltazionApiError n'est jamais retenté\n if (err instanceof AltazionApiError) throw err\n lastNetworkError = err\n // Petite pause exponentielle avant de réessayer (uniquement sur erreurs réseau)\n if (attempt < maxRetries) {\n await delay(150 * Math.pow(2, attempt))\n }\n }\n }\n\n throw lastNetworkError\n }\n\n private async handleResponse<T>(response: Response): Promise<T> {\n if (response.ok) {\n const contentType = response.headers.get('content-type') ?? ''\n if (response.status === 204 || !contentType.includes('application/json')) {\n return undefined as unknown as T\n }\n return response.json() as Promise<T>\n }\n\n // Tenter de parser un ProblemDetails\n let problem: ProblemDetails = { status: response.status }\n try {\n const contentType = response.headers.get('content-type') ?? ''\n if (contentType.includes('application/json') || contentType.includes('application/problem+json')) {\n problem = await response.json() as ProblemDetails\n problem.status ??= response.status\n }\n } catch {\n // JSON invalide — on garde le problem minimal\n }\n\n throw new AltazionApiError(response.status, problem)\n }\n\n /** Effectue un GET et retourne T */\n get<T>(path: string, headers?: Record<string, string>): Promise<T> {\n return this.request<T>(path, { method: 'GET', headers })\n }\n\n /** Effectue un POST avec un body JSON et retourne T */\n post<T>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T> {\n return this.request<T>(path, { method: 'POST', body, headers })\n }\n\n /** Effectue un PUT avec un body JSON et retourne T */\n put<T>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T> {\n return this.request<T>(path, { method: 'PUT', body, headers })\n }\n\n /** Effectue un PATCH avec un body JSON et retourne T */\n patch<T>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T> {\n return this.request<T>(path, { method: 'PATCH', body, headers })\n }\n\n /** Effectue un DELETE et retourne T */\n delete<T>(path: string, headers?: Record<string, string>): Promise<T> {\n return this.request<T>(path, { method: 'DELETE', headers })\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n","export type ConnectivityStatus = 'online' | 'offline'\n\nexport type ConnectivityListener = (status: ConnectivityStatus) => void\n\n/**\n * Surveille l'état de la connexion réseau.\n *\n * - Utilise navigator.onLine comme valeur initiale\n * - Écoute les événements window 'online' / 'offline'\n * - Permet d'abonner des listeners (pattern observable léger)\n */\nexport class ConnectivityManager {\n private status: ConnectivityStatus\n private readonly listeners = new Set<ConnectivityListener>()\n\n constructor() {\n this.status = this.readOnlineStatus()\n\n if (typeof window !== 'undefined') {\n window.addEventListener('online', this.handleOnline)\n window.addEventListener('offline', this.handleOffline)\n }\n }\n\n get isOnline(): boolean {\n return this.status === 'online'\n }\n\n get isOffline(): boolean {\n return this.status === 'offline'\n }\n\n /**\n * Abonne un listener aux changements de connectivité.\n * Retourne une fonction de désabonnement.\n */\n subscribe(listener: ConnectivityListener): () => void {\n this.listeners.add(listener)\n return () => this.listeners.delete(listener)\n }\n\n /** Libère les event listeners sur window. */\n dispose(): void {\n if (typeof window !== 'undefined') {\n window.removeEventListener('online', this.handleOnline)\n window.removeEventListener('offline', this.handleOffline)\n }\n this.listeners.clear()\n }\n\n private readOnlineStatus(): ConnectivityStatus {\n if (typeof navigator === 'undefined' || navigator.onLine === undefined) {\n return 'online' // SSR / environnement sans navigator : on suppose online\n }\n return navigator.onLine ? 'online' : 'offline'\n }\n\n private readonly handleOnline = (): void => {\n this.status = 'online'\n this.emit('online')\n }\n\n private readonly handleOffline = (): void => {\n this.status = 'offline'\n this.emit('offline')\n }\n\n private emit(status: ConnectivityStatus): void {\n for (const listener of this.listeners) {\n try {\n listener(status)\n } catch {\n // Un listener ne doit pas faire planter les autres\n }\n }\n }\n}\n","import { CacheError } from '../client/errors.js'\n\ninterface PendingRequest {\n resolve: (value: unknown) => void\n reject: (reason: unknown) => void\n}\n\n/**\n * Abstraction du canal de communication avec le SharedWorker de cache.\n *\n * - Utilise un SharedWorker si le navigateur le supporte\n * - Fallback : Map in-memory (données non persistantes, perdues au reload)\n *\n * Chaque appel retourne une Promise, résolue/rejetée via la réponse du worker.\n */\nexport class WorkerBridge {\n private port: MessagePort | null = null\n private readonly pending = new Map<string, PendingRequest>()\n private requestCounter = 0\n private readonly fallback = new Map<string, { data: unknown; expiresAt: number }>()\n private readonly usingFallback: boolean\n\n constructor() {\n this.usingFallback = !this.tryStartWorker()\n }\n\n private tryStartWorker(): boolean {\n if (typeof SharedWorker === 'undefined') return false\n\n try {\n const worker = new SharedWorker(\n new URL('./cache.worker.ts', import.meta.url),\n { type: 'module', name: 'altazion-cache-worker' }\n )\n this.port = worker.port\n this.port.addEventListener('message', this.handleMessage)\n this.port.start()\n return true\n } catch {\n return false\n }\n }\n\n private readonly handleMessage = (event: MessageEvent<{ id: string; result?: unknown; error?: string }>): void => {\n const { id, result, error } = event.data\n const pending = this.pending.get(id)\n if (!pending) return\n this.pending.delete(id)\n\n if (error !== undefined) {\n pending.reject(new CacheError(error))\n } else {\n pending.resolve(result)\n }\n }\n\n private nextId(): string {\n return String(++this.requestCounter)\n }\n\n private send(message: Record<string, unknown>): Promise<unknown> {\n return new Promise((resolve, reject) => {\n if (!this.port) {\n reject(new CacheError('Worker non initialisé'))\n return\n }\n const id = this.nextId()\n this.pending.set(id, { resolve, reject })\n this.port.postMessage({ ...message, id })\n })\n }\n\n async get<T>(key: string): Promise<T | null> {\n if (this.usingFallback) {\n const entry = this.fallback.get(key)\n if (!entry || Date.now() > entry.expiresAt) return null\n return entry.data as T\n }\n return this.send({ type: 'GET', key }) as Promise<T | null>\n }\n\n async set(key: string, data: unknown, ttlMs: number): Promise<void> {\n if (this.usingFallback) {\n this.fallback.set(key, { data, expiresAt: Date.now() + ttlMs })\n return\n }\n await this.send({ type: 'SET', key, data, ttl: ttlMs })\n }\n\n async invalidate(pattern: string): Promise<void> {\n if (this.usingFallback) {\n const regex = new RegExp(pattern)\n for (const key of this.fallback.keys()) {\n if (regex.test(key)) this.fallback.delete(key)\n }\n return\n }\n await this.send({ type: 'INVALIDATE', pattern })\n }\n\n async clearAll(): Promise<void> {\n if (this.usingFallback) {\n this.fallback.clear()\n return\n }\n await this.send({ type: 'CLEAR_ALL' })\n }\n\n dispose(): void {\n this.port?.removeEventListener('message', this.handleMessage)\n this.pending.clear()\n }\n}\n","import type { WorkerBridge } from './WorkerBridge.js'\nimport type { CommerceHttpAdapter } from '../client/CommerceHttpAdapter.js'\n\n/**\n * Stratégies de cache disponibles pour les requêtes HTTP.\n */\nexport type CacheStrategyName =\n | 'network-only' // Jamais de cache (mutations)\n | 'network-first' // Réseau, fallback cache si erreur réseau\n | 'cache-first' // Cache, revalidation réseau en background si périmé\n | 'stale-while-revalidate' // Retourne le cache immédiatement + revalide en arrière-plan\n\n/** TTL par stratégie, en millisecondes */\nexport const DEFAULT_TTL: Record<Exclude<CacheStrategyName, 'network-only'>, number> = {\n 'network-first': 30_000, // 30 secondes (session, panier)\n 'cache-first': 5 * 60_000, // 5 minutes (recherche)\n 'stale-while-revalidate': 60 * 60_000 // 1 heure (catalogue)\n}\n\nexport class CacheStrategy {\n constructor(\n private readonly bridge: WorkerBridge,\n private readonly http: CommerceHttpAdapter\n ) {}\n\n /**\n * Exécute la requête selon la stratégie choisie.\n *\n * @param key Clé de cache (généralement le path + params)\n * @param fetcher Fonction qui effectue la requête HTTP\n * @param strategy Stratégie à appliquer\n * @param ttlMs TTL du cache (override la valeur par défaut)\n */\n async execute<T>(\n key: string,\n fetcher: () => Promise<T>,\n strategy: CacheStrategyName,\n ttlMs?: number\n ): Promise<T> {\n switch (strategy) {\n case 'network-only':\n return fetcher()\n\n case 'network-first':\n return this.networkFirst<T>(key, fetcher, ttlMs ?? DEFAULT_TTL['network-first'])\n\n case 'cache-first':\n return this.cacheFirst<T>(key, fetcher, ttlMs ?? DEFAULT_TTL['cache-first'])\n\n case 'stale-while-revalidate':\n return this.staleWhileRevalidate<T>(key, fetcher, ttlMs ?? DEFAULT_TTL['stale-while-revalidate'])\n }\n }\n\n private async networkFirst<T>(key: string, fetcher: () => Promise<T>, ttlMs: number): Promise<T> {\n try {\n const data = await fetcher()\n await this.bridge.set(key, data, ttlMs)\n return data\n } catch (err) {\n // Erreur réseau uniquement (TypeError) : on tente le cache\n if (err instanceof TypeError) {\n const cached = await this.bridge.get<T>(key)\n if (cached !== null) return cached\n }\n throw err\n }\n }\n\n private async cacheFirst<T>(key: string, fetcher: () => Promise<T>, ttlMs: number): Promise<T> {\n const cached = await this.bridge.get<T>(key)\n if (cached !== null) return cached\n\n const data = await fetcher()\n await this.bridge.set(key, data, ttlMs)\n return data\n }\n\n private async staleWhileRevalidate<T>(key: string, fetcher: () => Promise<T>, ttlMs: number): Promise<T> {\n const cached = await this.bridge.get<T>(key)\n\n // Revalidation en arrière-plan (ne bloque pas le résultat)\n const revalidate = fetcher()\n .then((data) => this.bridge.set(key, data, ttlMs))\n .catch(() => { /* Échec silencieux : on garde le cache */ })\n\n if (cached !== null) {\n // On lance la revalidation sans attendre\n void revalidate\n return cached\n }\n\n // Pas de cache : on attend le réseau\n const data = await fetcher()\n await this.bridge.set(key, data, ttlMs)\n return data\n }\n}\n","import type { SessionManager } from './SessionManager.js'\nimport type { CommerceHttpAdapter } from '../client/CommerceHttpAdapter.js'\nimport type { CommerceContext } from '../client/CommerceContext.js'\nimport type { SessionInfo } from '../types/index.js'\nimport { AltazionApiError } from '../client/errors.js'\n\n/**\n * Gestionnaire de session pour le mode navigateur classique.\n *\n * Responsabilités :\n * - Appel à POST /commerce/api/sessions/create pour créer la session\n * - La session est identifiée côté serveur via le cookie `altz_session_id`\n * (créé automatiquement par l'API et transmis grâce à credentials:'include')\n */\nexport class BrowserSessionManager implements SessionManager {\n private readonly http: CommerceHttpAdapter\n private readonly context: CommerceContext\n\n constructor(http: CommerceHttpAdapter, context: CommerceContext) {\n this.http = http\n this.context = context\n }\n\n async initialize(): Promise<SessionInfo | null> {\n // Vérifier si une session existe déjà\n try {\n const existing = await this.http.get<SessionInfo>('/commerce/api/sessions/raw')\n return existing\n } catch (err) {\n // 404 = pas de session, on en crée une\n if (err instanceof AltazionApiError && err.status === 404) {\n return this.createSession()\n }\n throw err\n }\n }\n\n async onSessionExpired(): Promise<void> {\n await this.createSession()\n }\n\n private async createSession(): Promise<SessionInfo> {\n const body = this.buildCreationRequest()\n return this.http.post<SessionInfo>('/commerce/api/sessions/create', body)\n }\n\n private buildCreationRequest(): Record<string, unknown> {\n if (this.context.sitePk !== undefined && this.context.rjsId !== undefined) {\n return { sitePk: this.context.sitePk, rjsId: this.context.rjsId }\n }\n if (this.context.siteUrl !== undefined) {\n return { siteUrl: this.context.siteUrl }\n }\n // Fallback : envoyer l'URL courante du navigateur\n const currentUrl = typeof window !== 'undefined' ? window.location.href : undefined\n return { siteUrl: currentUrl }\n }\n}\n","import type { SessionManager } from './SessionManager.js'\nimport type { SessionInfo } from '../types/index.js'\n\n/**\n * Gestionnaire de session pour le mode borne (Altazion Device Shell).\n *\n * Le browser embarqué gère intégralement les cookies de session.\n * Le SDK n'a pas à intervenir : fetch avec credentials:'include' suffit\n * pour que les cookies soient transmis automatiquement.\n *\n * Cette implémentation est un no-op intentionnel.\n */\nexport class KioskSessionManager implements SessionManager {\n async initialize(): Promise<SessionInfo | null> {\n // Le browser embarqué est responsable de la session.\n // Aucune action requise côté SDK.\n return null\n }\n\n async onSessionExpired(): Promise<void> {\n // Le browser embarqué gère le renouvellement de session.\n // Aucune action requise côté SDK.\n }\n}\n","import type { SessionManager } from './SessionManager.js'\nimport { BrowserSessionManager } from './BrowserSessionManager.js'\nimport { KioskSessionManager } from './KioskSessionManager.js'\nimport type { CommerceHttpAdapter } from '../client/CommerceHttpAdapter.js'\nimport type { CommerceContext } from '../client/CommerceContext.js'\n\n/**\n * Instancie le SessionManager adapté au contexte d'exécution.\n *\n * Détection du mode kiosk : présence de \"Altazion Device Shell\" dans le User-Agent.\n * Dans ce cas, le browser embarqué gère entièrement les cookies de session.\n */\nexport class SessionManagerFactory {\n static create(http: CommerceHttpAdapter, context: CommerceContext): SessionManager {\n if (context.mode === 'kiosk') {\n return new KioskSessionManager()\n }\n return new BrowserSessionManager(http, context)\n }\n}\n","import type { CommerceHttpAdapter } from '../../client/CommerceHttpAdapter.js'\nimport type { CacheStrategy } from '../../workers/CacheStrategy.js'\nimport type { SessionInfo } from '../../types/index.js'\n\nexport class SessionModule {\n constructor(\n private readonly http: CommerceHttpAdapter,\n private readonly cache: CacheStrategy\n ) {}\n\n /** Récupère les informations de la session en cours */\n getSession(): Promise<SessionInfo> {\n return this.cache.execute(\n 'session:raw',\n () => this.http.get<SessionInfo>('/commerce/api/sessions/raw'),\n 'network-first'\n )\n }\n\n /** Associe un magasin à la session */\n async associateStore(storeGuid: string): Promise<SessionInfo> {\n const result = await this.http.post<SessionInfo>(\n `/commerce/api/sessions/associate/store/${storeGuid}`\n )\n await this.cache.execute('session:raw', () => Promise.resolve(result), 'network-first')\n return result\n }\n}\n","import type { CommerceHttpAdapter } from '../../client/CommerceHttpAdapter.js'\nimport { OfflineError } from '../../client/errors.js'\nimport type { CacheStrategy } from '../../workers/CacheStrategy.js'\nimport type { ConnectivityManager } from '../../connectivity/ConnectivityManager.js'\nimport type { Cart, CartValidationStatus } from '../../types/index.js'\n\nexport class CartModule {\n constructor(\n private readonly http: CommerceHttpAdapter,\n private readonly cache: CacheStrategy,\n private readonly connectivity: ConnectivityManager\n ) {}\n\n /** Récupère le panier en cours */\n getCart(): Promise<Cart> {\n return this.cache.execute(\n 'cart:current',\n () => this.http.get<Cart>('/commerce/api/process/cart'),\n 'network-first'\n )\n }\n\n /** Récupère le statut de validation du panier */\n getValidationStatus(): Promise<CartValidationStatus> {\n return this.http.get<CartValidationStatus>('/commerce/api/process/cart/status/check')\n }\n\n /** Ajoute un article au panier */\n async addItem(reference: string, quantity: number, options?: Record<string, unknown>): Promise<Cart> {\n this.ensureOnline()\n const body = { reference, quantity, ...options }\n const cart = await this.http.post<Cart>('/commerce/api/process/cart/items', body)\n await this.cache.execute('cart:current', () => Promise.resolve(cart), 'network-first')\n return cart\n }\n\n /** Met à jour la quantité d'une ligne */\n async updateItem(lineId: string, quantity: number): Promise<Cart> {\n this.ensureOnline()\n const body = { quantity }\n const cart = await this.http.put<Cart>(`/commerce/api/process/cart/items/${lineId}`, body)\n await this.cache.execute('cart:current', () => Promise.resolve(cart), 'network-first')\n return cart\n }\n\n /** Supprime une ligne du panier */\n async removeItem(lineId: string): Promise<Cart> {\n this.ensureOnline()\n const cart = await this.http.delete<Cart>(`/commerce/api/process/cart/items/${lineId}`)\n await this.cache.execute('cart:current', () => Promise.resolve(cart), 'network-first')\n return cart\n }\n\n /** Applique un coupon */\n async applyCoupon(code: string): Promise<Cart> {\n this.ensureOnline()\n const cart = await this.http.post<Cart>('/commerce/api/process/cart/coupons', { code })\n await this.cache.execute('cart:current', () => Promise.resolve(cart), 'network-first')\n return cart\n }\n\n /** Retire un coupon */\n async removeCoupon(code: string): Promise<Cart> {\n this.ensureOnline()\n const cart = await this.http.delete<Cart>(`/commerce/api/process/cart/coupons/${encodeURIComponent(code)}`)\n await this.cache.execute('cart:current', () => Promise.resolve(cart), 'network-first')\n return cart\n }\n\n private ensureOnline(): void {\n if (this.connectivity.isOffline) {\n throw new OfflineError('Le terminal est hors ligne, les modifications du panier sont indisponibles')\n }\n }\n}\n","import type { CommerceHttpAdapter } from '../../client/CommerceHttpAdapter.js'\nimport type { CacheStrategy } from '../../workers/CacheStrategy.js'\nimport type { ProductDetails, WebProduct, SearchRequest, SearchResult } from '../../types/index.js'\n\nexport class CatalogModule {\n constructor(\n private readonly http: CommerceHttpAdapter,\n private readonly cache: CacheStrategy\n ) {}\n\n /** Récupère le détail d'un produit par sa référence */\n getProduct(reference: string): Promise<ProductDetails> {\n return this.cache.execute(\n `catalog:product:${reference}`,\n () => this.http.get<ProductDetails>(`/commerce/api/pim/products/${encodeURIComponent(reference)}`),\n 'stale-while-revalidate'\n )\n }\n\n /** Récupère une liste de produits par leurs références */\n getProducts(references: string[]): Promise<WebProduct[]> {\n const key = `catalog:products:${references.sort().join(',')}`\n return this.cache.execute(\n key,\n () => this.http.post<WebProduct[]>('/commerce/api/pim/products/batch', { references }),\n 'stale-while-revalidate'\n )\n }\n\n /** Récupère les produits d'une catégorie */\n getCategory(categoryCode: string, pageIndex = 0, pageSize = 20): Promise<SearchResult> {\n const key = `catalog:category:${categoryCode}:${pageIndex}:${pageSize}`\n return this.cache.execute(\n key,\n () => this.http.get<SearchResult>(\n `/commerce/api/pim/categories/${encodeURIComponent(categoryCode)}/products?pageIndex=${pageIndex}&pageSize=${pageSize}`\n ),\n 'stale-while-revalidate'\n )\n }\n\n /** Effectue une recherche full-text */\n search(request: SearchRequest): Promise<SearchResult> {\n const key = `catalog:search:${JSON.stringify(request)}`\n return this.cache.execute(\n key,\n () => this.http.post<SearchResult>('/commerce/api/pim/search', request),\n 'cache-first'\n )\n }\n\n /** Récupère les suggestions de recherche (autocomplete) */\n suggest(query: string): Promise<string[]> {\n return this.cache.execute(\n `catalog:suggest:${query}`,\n () => this.http.get<string[]>(`/commerce/api/pim/search/suggest?q=${encodeURIComponent(query)}`),\n 'cache-first',\n 2 * 60_000 // 2 minutes pour les suggestions\n )\n }\n}\n","import type { CommerceHttpAdapter } from '../../client/CommerceHttpAdapter.js'\nimport type { CacheStrategy } from '../../workers/CacheStrategy.js'\nimport type { ShippingChoiceGroup, ShippingAddress, PickupPoint } from '../../types/index.js'\n\nexport class ShippingModule {\n constructor(\n private readonly http: CommerceHttpAdapter,\n private readonly cache: CacheStrategy\n ) {}\n\n /** Récupère les groupes de modes de livraison disponibles pour le panier en cours */\n getAvailableModes(): Promise<ShippingChoiceGroup[]> {\n return this.cache.execute(\n 'shipping:modes',\n () => this.http.get<ShippingChoiceGroup[]>('/commerce/api/process/cart/shipping/modes'),\n 'network-first'\n )\n }\n\n /** Sélectionne un mode de livraison */\n selectMode(shippingModeGuid: string): Promise<void> {\n return this.http.post('/commerce/api/process/cart/shipping/mode', { shippingModeGuid })\n }\n\n /** Met à jour l'adresse de livraison */\n setAddress(address: ShippingAddress): Promise<void> {\n return this.http.put('/commerce/api/process/cart/address/shipping', address)\n }\n\n /** Recherche des points relais à proximité */\n getRelayPoints(postalCode: string, countryCode = 'FR', shippingModeGuid?: string): Promise<PickupPoint[]> {\n const params = new URLSearchParams({ postalCode, countryCode })\n if (shippingModeGuid) params.set('shippingModeGuid', shippingModeGuid)\n const key = `shipping:relays:${postalCode}:${countryCode}:${shippingModeGuid ?? ''}`\n return this.cache.execute(\n key,\n () => this.http.get<PickupPoint[]>(`/commerce/api/process/shipping/relays?${params.toString()}`),\n 'cache-first',\n 10 * 60_000 // 10 minutes\n )\n }\n}\n","import type { CommerceHttpAdapter } from '../../client/CommerceHttpAdapter.js'\nimport type { CacheStrategy } from '../../workers/CacheStrategy.js'\nimport type { MarketingItem } from '../../types/index.js'\n\nexport class MarketingModule {\n constructor(\n private readonly http: CommerceHttpAdapter,\n private readonly cache: CacheStrategy\n ) {}\n\n /** Récupère un item marketing par son code */\n getItem(code: string): Promise<MarketingItem> {\n return this.cache.execute(\n `marketing:item:${code}`,\n () => this.http.get<MarketingItem>(`/commerce/api/marketing/items/${encodeURIComponent(code)}`),\n 'stale-while-revalidate'\n )\n }\n\n /** Récupère plusieurs items marketing par leurs codes */\n getItems(codes: string[]): Promise<MarketingItem[]> {\n const key = `marketing:items:${codes.sort().join(',')}`\n return this.cache.execute(\n key,\n () => this.http.post<MarketingItem[]>('/commerce/api/marketing/items/batch', { codes }),\n 'stale-while-revalidate'\n )\n }\n}\n","import type { CommerceHttpAdapter } from '../../client/CommerceHttpAdapter.js'\nimport type { CacheStrategy } from '../../workers/CacheStrategy.js'\nimport type { StoreWithOpeningHours } from '../../types/index.js'\n\nexport class StoresModule {\n constructor(\n private readonly http: CommerceHttpAdapter,\n private readonly cache: CacheStrategy\n ) {}\n\n /** Recherche des magasins par coordonnées géographiques */\n findByLocation(latitude: number, longitude: number, radiusKm = 50): Promise<StoreWithOpeningHours[]> {\n const key = `stores:geo:${latitude.toFixed(4)}:${longitude.toFixed(4)}:${radiusKm}`\n return this.cache.execute(\n key,\n () => this.http.get<StoreWithOpeningHours[]>(\n `/commerce/api/stores/locator?lat=${latitude}&lon=${longitude}&radius=${radiusKm}`\n ),\n 'cache-first',\n 15 * 60_000 // 15 minutes\n )\n }\n\n /** Recherche des magasins par code postal */\n findByPostalCode(postalCode: string, countryCode = 'FR'): Promise<StoreWithOpeningHours[]> {\n const key = `stores:postal:${postalCode}:${countryCode}`\n return this.cache.execute(\n key,\n () => this.http.get<StoreWithOpeningHours[]>(\n `/commerce/api/stores/locator?postalCode=${encodeURIComponent(postalCode)}&countryCode=${encodeURIComponent(countryCode)}`\n ),\n 'cache-first',\n 15 * 60_000\n )\n }\n\n /** Récupère le détail d'un magasin par son GUID */\n getStore(storeGuid: string): Promise<StoreWithOpeningHours> {\n return this.cache.execute(\n `stores:detail:${storeGuid}`,\n () => this.http.get<StoreWithOpeningHours>(`/commerce/api/stores/${storeGuid}`),\n 'stale-while-revalidate'\n )\n }\n}\n","import { CommerceContext, type CommerceContextOptions } from './CommerceContext.js'\nimport { CommerceHttpAdapter } from './CommerceHttpAdapter.js'\nimport { ConnectivityManager } from '../connectivity/ConnectivityManager.js'\nimport { WorkerBridge } from '../workers/WorkerBridge.js'\nimport { CacheStrategy } from '../workers/CacheStrategy.js'\nimport { SessionManagerFactory } from '../session/SessionManagerFactory.js'\nimport { SessionModule } from '../modules/session/index.js'\nimport { CartModule } from '../modules/cart/index.js'\nimport { CatalogModule } from '../modules/catalog/index.js'\nimport { ShippingModule } from '../modules/shipping/index.js'\nimport { MarketingModule } from '../modules/marketing/index.js'\nimport { StoresModule } from '../modules/stores/index.js'\nimport type { SessionManager } from '../session/SessionManager.js'\nimport type { SessionInfo } from '../types/index.js'\n\nexport { CommerceContext, type CommerceContextOptions } from './CommerceContext.js'\n\n/**\n * Point d'entrée principal du SDK Altazion Commerce.\n *\n * Usage :\n * ```ts\n * const client = new CommerceClient({ baseUrl: 'https://api.monsite.com', siteUrl: window.location.href })\n * await client.initialize()\n *\n * const cart = await client.cart.getCart()\n * ```\n */\nexport class CommerceClient {\n readonly context: CommerceContext\n readonly connectivity: ConnectivityManager\n\n readonly session: SessionModule\n readonly cart: CartModule\n readonly catalog: CatalogModule\n readonly shipping: ShippingModule\n readonly marketing: MarketingModule\n readonly stores: StoresModule\n\n private readonly http: CommerceHttpAdapter\n private readonly workerBridge: WorkerBridge\n private readonly cacheStrategy: CacheStrategy\n private readonly sessionManager: SessionManager\n\n constructor(options: CommerceContextOptions) {\n this.context = new CommerceContext(options)\n this.http = new CommerceHttpAdapter(this.context)\n this.connectivity = new ConnectivityManager()\n this.workerBridge = new WorkerBridge()\n this.cacheStrategy = new CacheStrategy(this.workerBridge, this.http)\n this.sessionManager = SessionManagerFactory.create(this.http, this.context)\n\n // Modules API\n this.session = new SessionModule(this.http, this.cacheStrategy)\n this.cart = new CartModule(this.http, this.cacheStrategy, this.connectivity)\n this.catalog = new CatalogModule(this.http, this.cacheStrategy)\n this.shipping = new ShippingModule(this.http, this.cacheStrategy)\n this.marketing = new MarketingModule(this.http, this.cacheStrategy)\n this.stores = new StoresModule(this.http, this.cacheStrategy)\n }\n\n /**\n * Initialise le SDK :\n * - Crée la session si nécessaire (mode browser uniquement)\n */\n async initialize(): Promise<SessionInfo | null> {\n return this.sessionManager.initialize()\n }\n\n /**\n * Retourne vrai si le SDK est hors-ligne.\n */\n get isOffline(): boolean {\n return this.connectivity.isOffline\n }\n\n /**\n * Force le vidage du cache.\n */\n clearCache(): Promise<void> {\n return this.workerBridge.clearAll()\n }\n\n /**\n * Libère les ressources (listeners, worker).\n */\n dispose(): void {\n this.connectivity.dispose()\n this.workerBridge.dispose()\n }\n}\n"],"names":["data"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -49,16 +49,6 @@ class CacheError extends Error {
|
|
|
49
49
|
this.cause = cause;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
class QueueFlushError extends Error {
|
|
53
|
-
constructor(operationId, apiError) {
|
|
54
|
-
super(`Échec du rejeu de l'opération ${operationId} : ${apiError.message}`);
|
|
55
|
-
__publicField(this, "failedOperationId");
|
|
56
|
-
__publicField(this, "apiError");
|
|
57
|
-
this.name = "QueueFlushError";
|
|
58
|
-
this.failedOperationId = operationId;
|
|
59
|
-
this.apiError = apiError;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
52
|
class CommerceHttpAdapter {
|
|
63
53
|
constructor(context) {
|
|
64
54
|
__publicField(this, "context");
|
|
@@ -350,139 +340,6 @@ class CacheStrategy {
|
|
|
350
340
|
return data;
|
|
351
341
|
}
|
|
352
342
|
}
|
|
353
|
-
const DB_NAME = "altazion-queue";
|
|
354
|
-
const DB_VERSION = 1;
|
|
355
|
-
const STORE_NAME = "operations";
|
|
356
|
-
class OfflineQueue {
|
|
357
|
-
constructor() {
|
|
358
|
-
__publicField(this, "db", null);
|
|
359
|
-
__publicField(this, "listeners", /* @__PURE__ */ new Set());
|
|
360
|
-
__publicField(this, "counter", 0);
|
|
361
|
-
}
|
|
362
|
-
/** Abonne un listener aux événements de file. Retourne une fonction de désabonnement. */
|
|
363
|
-
subscribe(listener) {
|
|
364
|
-
this.listeners.add(listener);
|
|
365
|
-
return () => this.listeners.delete(listener);
|
|
366
|
-
}
|
|
367
|
-
get pendingCount() {
|
|
368
|
-
return this.openDb().then(
|
|
369
|
-
(db) => new Promise((resolve, reject) => {
|
|
370
|
-
const tx = db.transaction(STORE_NAME, "readonly");
|
|
371
|
-
const req = tx.objectStore(STORE_NAME).count();
|
|
372
|
-
req.onsuccess = () => resolve(req.result);
|
|
373
|
-
req.onerror = () => reject(req.error);
|
|
374
|
-
})
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
async enqueue(method, path, body, headers) {
|
|
378
|
-
const operation = {
|
|
379
|
-
id: `${Date.now()}-${++this.counter}`,
|
|
380
|
-
enqueuedAt: Date.now(),
|
|
381
|
-
method,
|
|
382
|
-
path,
|
|
383
|
-
body,
|
|
384
|
-
headers,
|
|
385
|
-
attempts: 0
|
|
386
|
-
};
|
|
387
|
-
const db = await this.openDb();
|
|
388
|
-
await this.put(db, operation);
|
|
389
|
-
this.emit({ type: "enqueued", operation });
|
|
390
|
-
return operation;
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* Rejoue toutes les opérations en attente dans l'ordre d'enfilage.
|
|
394
|
-
* S'arrête sur un conflit (4xx) et émet un événement `conflict`.
|
|
395
|
-
*/
|
|
396
|
-
async flush(http) {
|
|
397
|
-
const operations = await this.getAll();
|
|
398
|
-
if (operations.length === 0) return;
|
|
399
|
-
for (const op of operations) {
|
|
400
|
-
try {
|
|
401
|
-
op.attempts++;
|
|
402
|
-
await http.request(op.path, {
|
|
403
|
-
method: op.method,
|
|
404
|
-
body: op.body,
|
|
405
|
-
headers: op.headers
|
|
406
|
-
});
|
|
407
|
-
await this.remove(op.id);
|
|
408
|
-
} catch (err) {
|
|
409
|
-
if (err instanceof AltazionApiError && err.status >= 400 && err.status < 500) {
|
|
410
|
-
const flushError = new QueueFlushError(op.id, err);
|
|
411
|
-
this.emit({ type: "conflict", operation: op, error: flushError });
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
await this.put(await this.openDb(), op);
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
this.emit({ type: "flushed" });
|
|
419
|
-
const remaining = await this.pendingCount;
|
|
420
|
-
if (remaining === 0) {
|
|
421
|
-
this.emit({ type: "emptied" });
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
emit(event) {
|
|
425
|
-
for (const listener of this.listeners) {
|
|
426
|
-
try {
|
|
427
|
-
listener(event);
|
|
428
|
-
} catch {
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
openDb() {
|
|
433
|
-
return new Promise((resolve, reject) => {
|
|
434
|
-
if (this.db) {
|
|
435
|
-
resolve(this.db);
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
439
|
-
request.onupgradeneeded = (event) => {
|
|
440
|
-
const database = event.target.result;
|
|
441
|
-
if (!database.objectStoreNames.contains(STORE_NAME)) {
|
|
442
|
-
database.createObjectStore(STORE_NAME, { keyPath: "id" });
|
|
443
|
-
}
|
|
444
|
-
};
|
|
445
|
-
request.onsuccess = (event) => {
|
|
446
|
-
this.db = event.target.result;
|
|
447
|
-
resolve(this.db);
|
|
448
|
-
};
|
|
449
|
-
request.onerror = () => reject(request.error);
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
put(db, operation) {
|
|
453
|
-
return new Promise((resolve, reject) => {
|
|
454
|
-
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
455
|
-
const req = tx.objectStore(STORE_NAME).put(operation);
|
|
456
|
-
req.onsuccess = () => resolve();
|
|
457
|
-
req.onerror = () => reject(req.error);
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
remove(id) {
|
|
461
|
-
return this.openDb().then(
|
|
462
|
-
(db) => new Promise((resolve, reject) => {
|
|
463
|
-
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
464
|
-
const req = tx.objectStore(STORE_NAME).delete(id);
|
|
465
|
-
req.onsuccess = () => resolve();
|
|
466
|
-
req.onerror = () => reject(req.error);
|
|
467
|
-
})
|
|
468
|
-
);
|
|
469
|
-
}
|
|
470
|
-
getAll() {
|
|
471
|
-
return this.openDb().then(
|
|
472
|
-
(db) => new Promise((resolve, reject) => {
|
|
473
|
-
const tx = db.transaction(STORE_NAME, "readonly");
|
|
474
|
-
const req = tx.objectStore(STORE_NAME).getAll();
|
|
475
|
-
req.onsuccess = () => {
|
|
476
|
-
const ops = req.result.sort(
|
|
477
|
-
(a, b) => a.enqueuedAt - b.enqueuedAt
|
|
478
|
-
);
|
|
479
|
-
resolve(ops);
|
|
480
|
-
};
|
|
481
|
-
req.onerror = () => reject(req.error);
|
|
482
|
-
})
|
|
483
|
-
);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
343
|
class BrowserSessionManager {
|
|
487
344
|
constructor(http, context) {
|
|
488
345
|
__publicField(this, "http");
|
|
@@ -557,10 +414,9 @@ class SessionModule {
|
|
|
557
414
|
}
|
|
558
415
|
}
|
|
559
416
|
class CartModule {
|
|
560
|
-
constructor(http, cache,
|
|
417
|
+
constructor(http, cache, connectivity) {
|
|
561
418
|
this.http = http;
|
|
562
419
|
this.cache = cache;
|
|
563
|
-
this.queue = queue;
|
|
564
420
|
this.connectivity = connectivity;
|
|
565
421
|
}
|
|
566
422
|
/** Récupère le panier en cours */
|
|
@@ -577,48 +433,46 @@ class CartModule {
|
|
|
577
433
|
}
|
|
578
434
|
/** Ajoute un article au panier */
|
|
579
435
|
async addItem(reference, quantity, options) {
|
|
436
|
+
this.ensureOnline();
|
|
580
437
|
const body = { reference, quantity, ...options };
|
|
581
|
-
if (this.connectivity.isOffline) {
|
|
582
|
-
await this.queue.enqueue("POST", "/commerce/api/process/cart/items", body);
|
|
583
|
-
return this.cache.execute("cart:current", () => this.http.get("/commerce/api/process/cart"), "network-first");
|
|
584
|
-
}
|
|
585
438
|
const cart = await this.http.post("/commerce/api/process/cart/items", body);
|
|
586
439
|
await this.cache.execute("cart:current", () => Promise.resolve(cart), "network-first");
|
|
587
440
|
return cart;
|
|
588
441
|
}
|
|
589
442
|
/** Met à jour la quantité d'une ligne */
|
|
590
443
|
async updateItem(lineId, quantity) {
|
|
444
|
+
this.ensureOnline();
|
|
591
445
|
const body = { quantity };
|
|
592
|
-
if (this.connectivity.isOffline) {
|
|
593
|
-
await this.queue.enqueue("PUT", `/commerce/api/process/cart/items/${lineId}`, body);
|
|
594
|
-
return this.cache.execute("cart:current", () => this.http.get("/commerce/api/process/cart"), "network-first");
|
|
595
|
-
}
|
|
596
446
|
const cart = await this.http.put(`/commerce/api/process/cart/items/${lineId}`, body);
|
|
597
447
|
await this.cache.execute("cart:current", () => Promise.resolve(cart), "network-first");
|
|
598
448
|
return cart;
|
|
599
449
|
}
|
|
600
450
|
/** Supprime une ligne du panier */
|
|
601
451
|
async removeItem(lineId) {
|
|
602
|
-
|
|
603
|
-
await this.queue.enqueue("DELETE", `/commerce/api/process/cart/items/${lineId}`);
|
|
604
|
-
return this.cache.execute("cart:current", () => this.http.get("/commerce/api/process/cart"), "network-first");
|
|
605
|
-
}
|
|
452
|
+
this.ensureOnline();
|
|
606
453
|
const cart = await this.http.delete(`/commerce/api/process/cart/items/${lineId}`);
|
|
607
454
|
await this.cache.execute("cart:current", () => Promise.resolve(cart), "network-first");
|
|
608
455
|
return cart;
|
|
609
456
|
}
|
|
610
457
|
/** Applique un coupon */
|
|
611
458
|
async applyCoupon(code) {
|
|
459
|
+
this.ensureOnline();
|
|
612
460
|
const cart = await this.http.post("/commerce/api/process/cart/coupons", { code });
|
|
613
461
|
await this.cache.execute("cart:current", () => Promise.resolve(cart), "network-first");
|
|
614
462
|
return cart;
|
|
615
463
|
}
|
|
616
464
|
/** Retire un coupon */
|
|
617
465
|
async removeCoupon(code) {
|
|
466
|
+
this.ensureOnline();
|
|
618
467
|
const cart = await this.http.delete(`/commerce/api/process/cart/coupons/${encodeURIComponent(code)}`);
|
|
619
468
|
await this.cache.execute("cart:current", () => Promise.resolve(cart), "network-first");
|
|
620
469
|
return cart;
|
|
621
470
|
}
|
|
471
|
+
ensureOnline() {
|
|
472
|
+
if (this.connectivity.isOffline) {
|
|
473
|
+
throw new OfflineError("Le terminal est hors ligne, les modifications du panier sont indisponibles");
|
|
474
|
+
}
|
|
475
|
+
}
|
|
622
476
|
}
|
|
623
477
|
class CatalogModule {
|
|
624
478
|
constructor(http, cache) {
|
|
@@ -783,18 +637,15 @@ class CommerceClient {
|
|
|
783
637
|
__publicField(this, "http");
|
|
784
638
|
__publicField(this, "workerBridge");
|
|
785
639
|
__publicField(this, "cacheStrategy");
|
|
786
|
-
__publicField(this, "offlineQueue");
|
|
787
640
|
__publicField(this, "sessionManager");
|
|
788
|
-
__publicField(this, "unsubscribeOnline", null);
|
|
789
641
|
this.context = new CommerceContext(options);
|
|
790
642
|
this.http = new CommerceHttpAdapter(this.context);
|
|
791
643
|
this.connectivity = new ConnectivityManager();
|
|
792
644
|
this.workerBridge = new WorkerBridge();
|
|
793
645
|
this.cacheStrategy = new CacheStrategy(this.workerBridge, this.http);
|
|
794
|
-
this.offlineQueue = new OfflineQueue();
|
|
795
646
|
this.sessionManager = SessionManagerFactory.create(this.http, this.context);
|
|
796
647
|
this.session = new SessionModule(this.http, this.cacheStrategy);
|
|
797
|
-
this.cart = new CartModule(this.http, this.cacheStrategy, this.
|
|
648
|
+
this.cart = new CartModule(this.http, this.cacheStrategy, this.connectivity);
|
|
798
649
|
this.catalog = new CatalogModule(this.http, this.cacheStrategy);
|
|
799
650
|
this.shipping = new ShippingModule(this.http, this.cacheStrategy);
|
|
800
651
|
this.marketing = new MarketingModule(this.http, this.cacheStrategy);
|
|
@@ -803,14 +654,8 @@ class CommerceClient {
|
|
|
803
654
|
/**
|
|
804
655
|
* Initialise le SDK :
|
|
805
656
|
* - Crée la session si nécessaire (mode browser uniquement)
|
|
806
|
-
* - Abonne le flush automatique de la file hors-ligne au retour en ligne
|
|
807
657
|
*/
|
|
808
658
|
async initialize() {
|
|
809
|
-
this.unsubscribeOnline = this.connectivity.subscribe(async (status) => {
|
|
810
|
-
if (status === "online") {
|
|
811
|
-
await this.offlineQueue.flush(this.http);
|
|
812
|
-
}
|
|
813
|
-
});
|
|
814
659
|
return this.sessionManager.initialize();
|
|
815
660
|
}
|
|
816
661
|
/**
|
|
@@ -819,19 +664,6 @@ class CommerceClient {
|
|
|
819
664
|
get isOffline() {
|
|
820
665
|
return this.connectivity.isOffline;
|
|
821
666
|
}
|
|
822
|
-
/**
|
|
823
|
-
* Retourne le nombre d'opérations en attente dans la file hors-ligne.
|
|
824
|
-
*/
|
|
825
|
-
get pendingOperationsCount() {
|
|
826
|
-
return this.offlineQueue.pendingCount;
|
|
827
|
-
}
|
|
828
|
-
/**
|
|
829
|
-
* Abonne un listener aux événements de la file hors-ligne.
|
|
830
|
-
* Retourne une fonction de désabonnement.
|
|
831
|
-
*/
|
|
832
|
-
onQueueEvent(listener) {
|
|
833
|
-
return this.offlineQueue.subscribe(listener);
|
|
834
|
-
}
|
|
835
667
|
/**
|
|
836
668
|
* Force le vidage du cache.
|
|
837
669
|
*/
|
|
@@ -842,8 +674,6 @@ class CommerceClient {
|
|
|
842
674
|
* Libère les ressources (listeners, worker).
|
|
843
675
|
*/
|
|
844
676
|
dispose() {
|
|
845
|
-
var _a;
|
|
846
|
-
(_a = this.unsubscribeOnline) == null ? void 0 : _a.call(this);
|
|
847
677
|
this.connectivity.dispose();
|
|
848
678
|
this.workerBridge.dispose();
|
|
849
679
|
}
|
|
@@ -854,7 +684,6 @@ export {
|
|
|
854
684
|
CommerceClient,
|
|
855
685
|
CommerceContext,
|
|
856
686
|
ConnectivityManager,
|
|
857
|
-
OfflineError
|
|
858
|
-
QueueFlushError
|
|
687
|
+
OfflineError
|
|
859
688
|
};
|
|
860
689
|
//# sourceMappingURL=index.js.map
|