@asteby/storefront 0.4.0 → 0.5.0
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 +31 -1
- package/dist/index.cjs +27 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.js +26 -2
- package/dist/index.js.map +1 -1
- package/dist/mockup.cjs +218 -0
- package/dist/mockup.cjs.map +1 -0
- package/dist/mockup.d.cts +142 -0
- package/dist/mockup.d.ts +142 -0
- package/dist/mockup.js +205 -0
- package/dist/mockup.js.map +1 -0
- package/package.json +11 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/money.ts","../src/query.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { StorefrontErrorCode } from './types.js'\n\nexport type StorefrontErrorInit = {\n status: number\n code: StorefrontErrorCode\n message: string\n field?: string\n /** Request URL that failed (without credentials). */\n url?: string\n cause?: unknown\n}\n\n/**\n * Error thrown by the client for any non-2xx response, network failure,\n * timeout or abort. `status` is `0` when no HTTP response was received.\n */\nexport class StorefrontError extends Error {\n override readonly name = 'StorefrontError'\n readonly status: number\n readonly code: StorefrontErrorCode\n readonly field: string | undefined\n readonly url: string | undefined\n\n constructor(init: StorefrontErrorInit) {\n super(init.message, init.cause === undefined ? undefined : { cause: init.cause })\n this.status = init.status\n this.code = init.code\n this.field = init.field\n this.url = init.url\n }\n\n toJSON() {\n return { name: this.name, status: this.status, code: this.code, message: this.message, field: this.field }\n }\n}\n\nexport function isStorefrontError(err: unknown): err is StorefrontError {\n return err instanceof StorefrontError || (typeof err === 'object' && err !== null && (err as { name?: unknown }).name === 'StorefrontError')\n}\n\nconst STATUS_CODES: Record<number, StorefrontErrorCode> = {\n 400: 'validation',\n 401: 'unauthorized',\n 403: 'forbidden',\n 404: 'not_found',\n 409: 'conflict',\n 422: 'invalid_state',\n 429: 'rate_limited',\n}\n\nexport function defaultCodeForStatus(status: number): StorefrontErrorCode {\n return STATUS_CODES[status] ?? (status >= 500 ? 'server_error' : 'invalid_response')\n}\n","import type { Money } from './types.js'\n\nexport type FormatMoneyOptions = {\n /** Drop `.00` when the amount is a whole number. */\n withoutTrailingZeros?: boolean\n /** Format as a plain number, without the currency symbol. */\n withoutCurrency?: boolean\n /** `symbol` (default), `narrowSymbol`, `code` or `name`. */\n currencyDisplay?: 'symbol' | 'narrowSymbol' | 'code' | 'name'\n}\n\n/** Parses a contract `Money` amount into a JS number (`\"1899.00\"` → `1899`). */\nexport function parseMoney(money: Money | string | null | undefined): number {\n if (money == null) return 0\n const raw = typeof money === 'string' ? money : money.amount\n const n = Number.parseFloat(raw)\n return Number.isFinite(n) ? n : 0\n}\n\n/**\n * Formats `Money` with `Intl.NumberFormat`. Works in every runtime (no DOM).\n * Falls back to `\"<amount> <code>\"` when the runtime lacks the currency.\n */\nexport function formatMoney(money: Money, locale?: string, options: FormatMoneyOptions = {}): string {\n const value = parseMoney(money)\n const whole = Number.isInteger(value)\n const digits = options.withoutTrailingZeros && whole ? 0 : undefined\n try {\n const fmt = new Intl.NumberFormat(locale, {\n ...(options.withoutCurrency\n ? { style: 'decimal', minimumFractionDigits: digits ?? 2, maximumFractionDigits: digits ?? 2 }\n : {\n style: 'currency',\n currency: money.currency_code,\n currencyDisplay: options.currencyDisplay ?? 'symbol',\n ...(digits === 0 ? { minimumFractionDigits: 0, maximumFractionDigits: 0 } : {}),\n }),\n })\n return fmt.format(value)\n } catch {\n const amount = digits === 0 ? String(value) : value.toFixed(2)\n return options.withoutCurrency ? amount : `${amount} ${money.currency_code}`\n }\n}\n\n/** Builds a `Money` from a number, rounding to cents. */\nexport function toMoney(value: number, currencyCode: string): Money {\n return { amount: (Math.round(value * 100) / 100).toFixed(2), currency_code: currencyCode }\n}\n\n/**\n * True for public (`pk_`) tokens, the only ones that may reach a browser.\n * Private (`sk_`) tokens must stay on the server.\n */\nexport function isTokenPublishable(token: string | null | undefined): boolean {\n return typeof token === 'string' && token.startsWith('pk_')\n}\n","import type { ProductSort } from './types.js'\n\nexport type QueryValue = string | number | boolean | null | undefined\nexport type QueryParams = Record<string, QueryValue | QueryValue[]>\n\nexport type PaginationParams = {\n /** Page size (1–100; articles accept at most 50). */\n first?: number\n /** Cursor from `page_info.end_cursor`. */\n after?: string | null\n}\n\nexport type ProductListParams = PaginationParams & {\n /** Free-text search. */\n q?: string\n /** Collection handle. */\n collection?: string\n sort?: ProductSort\n /** Only products available for sale. */\n available?: boolean\n price_min?: number | string\n price_max?: number | string\n /**\n * Attribute filters, serialized as repeated `filter.<key>=<value>` params.\n * Values of one key are OR-ed; different keys are AND-ed.\n * e.g. `{ vendor: ['A', 'B'], rim: 16 }`.\n */\n filters?: Record<string, QueryValue | QueryValue[]>\n /** Restrict to these handles (max 100). Serialized comma-separated. */\n handles?: string[]\n}\n\nexport type CollectionListParams = PaginationParams\n\nexport type ArticleListParams = Omit<PaginationParams, 'first'> & {\n /** Page size (1–50). The API answers 400 `validation` above 50. */\n first?: number\n tag?: string\n q?: string\n}\n\n/** Appends params to a URLSearchParams, repeating keys for arrays and skipping nullish values. */\nexport function appendParams(search: URLSearchParams, params: QueryParams | undefined): URLSearchParams {\n if (!params) return search\n for (const [key, raw] of Object.entries(params)) {\n const values = Array.isArray(raw) ? raw : [raw]\n for (const v of values) {\n if (v === undefined || v === null || v === '') continue\n search.append(key, String(v))\n }\n }\n return search\n}\n\n/** Serializes product list params per the contract (`filter.<attr>` repeated, `handles` comma-separated). */\nexport function productListQuery(params: ProductListParams = {}): URLSearchParams {\n const { filters, handles, ...rest } = params\n const search = appendParams(new URLSearchParams(), rest as QueryParams)\n if (handles && handles.length > 0) search.set('handles', handles.join(','))\n if (filters) {\n for (const [key, raw] of Object.entries(filters)) {\n const name = key.startsWith('filter.') ? key : `filter.${key}`\n appendParams(search, { [name]: raw })\n }\n }\n return search\n}\n\n/** Reads `filter.<key>` params back into a filters record (useful in route loaders). */\nexport function parseProductFilters(search: URLSearchParams): Record<string, string[]> {\n const out: Record<string, string[]> = {}\n for (const [key, value] of search) {\n if (!key.startsWith('filter.')) continue\n const name = key.slice('filter.'.length)\n ;(out[name] ??= []).push(value)\n }\n return out\n}\n","import { StorefrontError, defaultCodeForStatus } from './errors.js'\nimport { isValidPoint, type GeoPoint } from './geo.js'\nimport { isTokenPublishable } from './money.js'\nimport {\n appendParams,\n productListQuery,\n type ArticleListParams,\n type CollectionListParams,\n type PaginationParams,\n type ProductListParams,\n type QueryParams,\n} from './query.js'\nimport type {\n Address,\n AddressInput,\n Article,\n Cart,\n CartBuyerInput,\n CartLineInput,\n CartLineUpdateInput,\n CheckoutInput,\n CheckoutResult,\n Collection,\n Connection,\n Customer,\n CustomerIdentifier,\n CustomerSession,\n CustomerSessionInput,\n DeliveryInput,\n DeliveryOptions,\n PaymentMethods,\n InvoiceRequest,\n Location,\n LocationListParams,\n NearestLocation,\n InvoiceResult,\n Menu,\n Order,\n Page,\n Product,\n ProductConnection,\n ReturnRequest,\n ReturnRequestInput,\n Shop,\n StockLevel,\n StorefrontErrorBody,\n Theme,\n} from './types.js'\n\nexport type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>\n\n/** Context passed to `onRequest` / `onResponse` hooks. */\nexport type StorefrontRequestContext = {\n method: string\n /** Absolute URL, including the query string. */\n url: string\n /** Route path relative to `baseUrl`, e.g. `/storefront/products/abc`. */\n path: string\n /** Final `RequestInit` (headers already merged). Hooks may mutate it. */\n init: RequestInit & { headers: Headers }\n /** Per-call options as given by the caller. */\n options: RequestOptions\n}\n\nexport type StorefrontResponseContext = StorefrontRequestContext & {\n response: Response\n /** Milliseconds spent waiting for the response. */\n durationMs: number\n}\n\nexport type StorefrontClientOptions = {\n /** Base URL of the gateway, e.g. `https://ops.example.com/api/gw`. Routes are appended as `/storefront/...`. */\n baseUrl: string\n /** `pk_...` (browser) or `sk_...` (server only). Sent as `Authorization: Bearer <token>`. */\n token: string\n /** Buyer session (`X-Customer-Token`) for `/storefront/customer/*` routes. */\n customerToken?: string | null\n /** Custom fetch (defaults to `globalThis.fetch`). */\n fetch?: FetchLike\n /** Sent as `Accept-Language`. */\n locale?: string\n /** Extra headers on every request. */\n headers?: Record<string, string>\n /** Abort requests that take longer than this (default: no timeout). */\n timeoutMs?: number\n /**\n * Runs before every request. Return a `Response` to short-circuit the\n * network (e.g. an SSR cache hit); return nothing to continue.\n */\n onRequest?: (ctx: StorefrontRequestContext) => void | Response | Promise<void | Response>\n /** Runs after every response (including short-circuited ones), before parsing. */\n onResponse?: (ctx: StorefrontResponseContext) => void | Promise<void>\n}\n\n/** Per-call options. Unknown keys (e.g. Next.js `next`) are forwarded to `fetch` as-is. */\nexport type RequestOptions = {\n cache?: RequestCache\n signal?: AbortSignal\n /** Framework fetch extensions, forwarded verbatim (e.g. `{ revalidate: 60, tags: ['products'] }`). */\n next?: Record<string, unknown>\n headers?: Record<string, string>\n /** Overrides the client-level timeout for this call. */\n timeoutMs?: number\n /** Overrides the client-level customer token for this call. */\n customerToken?: string | null\n [key: string]: unknown\n}\n\nexport type WriteOptions = RequestOptions & {\n /** `Idempotency-Key` header. Auto-generated for checkout when omitted. */\n idempotencyKey?: string\n}\n\nexport type PreviewOptions = RequestOptions & {\n /** Signed preview token: returns the draft (`private, no-store`). */\n preview?: string\n}\n\ntype HttpMethod = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE'\n\ntype RequestSpec = {\n method: HttpMethod\n path: string\n query?: URLSearchParams | QueryParams\n body?: unknown\n options?: WriteOptions\n /** Resolve `null` instead of throwing on 404. */\n nullOn404?: boolean\n}\n\nconst OWN_OPTION_KEYS = new Set(['headers', 'timeoutMs', 'customerToken', 'idempotencyKey', 'preview', 'signal'])\n\n/** Generates an idempotency key (crypto.randomUUID when available). */\nexport function generateIdempotencyKey(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto\n if (c?.randomUUID) return c.randomUUID()\n // Non-cryptographic fallback for very old runtimes; the key only needs to be unique.\n return 'idem-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2)\n}\n\nfunction joinUrl(baseUrl: string, path: string): string {\n return baseUrl.replace(/\\/+$/, '') + path\n}\n\nfunction enc(segment: string): string {\n return encodeURIComponent(segment)\n}\n\nfunction linkSignals(signals: (AbortSignal | undefined)[]): { signal: AbortSignal | undefined; cleanup: () => void } {\n const active = signals.filter((s): s is AbortSignal => !!s)\n if (active.length === 0) return { signal: undefined, cleanup: () => {} }\n if (active.length === 1) return { signal: active[0], cleanup: () => {} }\n const controller = new AbortController()\n const listeners: [AbortSignal, () => void][] = []\n for (const s of active) {\n if (s.aborted) {\n controller.abort(s.reason)\n break\n }\n const on = () => controller.abort(s.reason)\n s.addEventListener('abort', on, { once: true })\n listeners.push([s, on])\n }\n return {\n signal: controller.signal,\n cleanup: () => listeners.forEach(([s, on]) => s.removeEventListener('abort', on)),\n }\n}\n\nasync function readError(res: Response, url: string): Promise<StorefrontError> {\n let body: Partial<StorefrontErrorBody> | undefined\n try {\n const text = await res.text()\n body = text ? (JSON.parse(text) as Partial<StorefrontErrorBody>) : undefined\n } catch {\n body = undefined\n }\n const e = body?.error\n return new StorefrontError({\n status: res.status,\n code: e?.code ?? defaultCodeForStatus(res.status),\n message: e?.message ?? `Storefront API responded ${res.status}${res.statusText ? ' ' + res.statusText : ''}`,\n ...(e?.field ? { field: e.field } : {}),\n url,\n })\n}\n\nexport function createStorefrontClient(config: StorefrontClientOptions) {\n if (!config.baseUrl) throw new TypeError('createStorefrontClient: baseUrl is required')\n if (!config.token) throw new TypeError('createStorefrontClient: token is required')\n const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined'\n if (isBrowser && !isTokenPublishable(config.token) && config.token.startsWith('sk_')) {\n console.warn('[@asteby/storefront] A private sk_ token is being used in a browser. Use a public pk_ token instead.')\n }\n\n async function request<T>(spec: RequestSpec & { nullOn404: true }): Promise<T | null>\n async function request<T>(spec: RequestSpec): Promise<T>\n async function request<T>(spec: RequestSpec): Promise<T | null> {\n const fetchImpl: FetchLike | undefined = config.fetch ?? (globalThis.fetch ? globalThis.fetch.bind(globalThis) : undefined)\n if (!fetchImpl) throw new TypeError('createStorefrontClient: no fetch available; pass `fetch` in options')\n\n const options: WriteOptions = spec.options ?? {}\n const search = spec.query instanceof URLSearchParams ? spec.query : appendParams(new URLSearchParams(), spec.query)\n const qs = search.toString()\n const url = joinUrl(config.baseUrl, spec.path) + (qs ? `?${qs}` : '')\n\n const headers = new Headers(config.headers)\n headers.set('Accept', 'application/json')\n headers.set('Authorization', `Bearer ${config.token}`)\n if (config.locale) headers.set('Accept-Language', config.locale)\n const customerToken = options.customerToken !== undefined ? options.customerToken : config.customerToken\n if (customerToken) headers.set('X-Customer-Token', customerToken)\n if (options.idempotencyKey) headers.set('Idempotency-Key', options.idempotencyKey)\n if (spec.body !== undefined) headers.set('Content-Type', 'application/json')\n for (const [k, v] of Object.entries(options.headers ?? {})) headers.set(k, v)\n\n // Forward passthrough options (cache, next, ...) to fetch.\n const passthrough: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(options)) if (!OWN_OPTION_KEYS.has(k) && v !== undefined) passthrough[k] = v\n\n const timeoutMs = options.timeoutMs ?? config.timeoutMs\n const timeoutController = timeoutMs && timeoutMs > 0 ? new AbortController() : undefined\n const timer = timeoutController\n ? setTimeout(() => timeoutController.abort(new StorefrontError({ status: 0, code: 'timeout', message: `Request timed out after ${timeoutMs}ms`, url })), timeoutMs)\n : undefined\n const linked = linkSignals([options.signal, timeoutController?.signal])\n\n const init: RequestInit & { headers: Headers } = {\n ...passthrough,\n method: spec.method,\n headers,\n ...(spec.body !== undefined ? { body: JSON.stringify(spec.body) } : {}),\n ...(linked.signal ? { signal: linked.signal } : {}),\n }\n const ctx: StorefrontRequestContext = { method: spec.method, url, path: spec.path, init, options }\n\n const started = Date.now()\n let res: Response\n try {\n const hooked = config.onRequest ? await config.onRequest(ctx) : undefined\n res = hooked instanceof Response ? hooked : await fetchImpl(url, init)\n } catch (err) {\n if (timeoutController?.signal.aborted) {\n throw new StorefrontError({ status: 0, code: 'timeout', message: `Request timed out after ${timeoutMs}ms`, url, cause: err })\n }\n if (options.signal?.aborted) {\n throw new StorefrontError({ status: 0, code: 'aborted', message: 'Request aborted', url, cause: err })\n }\n if (err instanceof StorefrontError) throw err\n throw new StorefrontError({ status: 0, code: 'network', message: err instanceof Error ? err.message : 'Network error', url, cause: err })\n } finally {\n if (timer) clearTimeout(timer)\n linked.cleanup()\n }\n\n if (config.onResponse) await config.onResponse({ ...ctx, response: res, durationMs: Date.now() - started })\n\n if (res.status === 404 && spec.nullOn404) {\n // Drain the body so the connection can be reused.\n await res.text().catch(() => undefined)\n return null\n }\n if (res.status === 304) {\n throw new StorefrontError({ status: 304, code: 'not_modified', message: 'Not modified', url })\n }\n if (!res.ok) throw await readError(res, url)\n if (res.status === 204) return undefined as T\n\n const text = await res.text()\n if (!text) return undefined as T\n try {\n return JSON.parse(text) as T\n } catch (err) {\n throw new StorefrontError({ status: res.status, code: 'invalid_response', message: 'Response is not valid JSON', url, cause: err })\n }\n }\n\n const get = <T>(path: string, query?: URLSearchParams | QueryParams, options?: RequestOptions) =>\n request<T>({ method: 'GET', path, ...(query ? { query } : {}), ...(options ? { options } : {}) })\n const getOrNull = <T>(path: string, query?: URLSearchParams | QueryParams, options?: RequestOptions) =>\n request<T>({ method: 'GET', path, nullOn404: true, ...(query ? { query } : {}), ...(options ? { options } : {}) })\n const write = <T>(method: HttpMethod, path: string, body?: unknown, options?: WriteOptions) =>\n request<T>({ method, path, ...(body !== undefined ? { body } : {}), ...(options ? { options } : {}) })\n\n const cartPath = (id: string, rest = '') => `/storefront/carts/${enc(id)}${rest}`\n const previewQuery = (options?: PreviewOptions) => (options?.preview ? { preview: options.preview } : undefined)\n\n const client = {\n /** Resolved configuration (read-only). */\n config: Object.freeze({ ...config }) as Readonly<StorefrontClientOptions>,\n\n /** Low-level escape hatch: any route under `baseUrl`. */\n request,\n\n /** Returns a new client that sends the given buyer session token. */\n withCustomerToken(customerToken: string | null) {\n return createStorefrontClient({ ...config, customerToken })\n },\n\n shop: {\n get: (options?: RequestOptions) => get<Shop>('/storefront/shop', undefined, options),\n },\n\n products: {\n list: (params?: ProductListParams, options?: RequestOptions) =>\n get<ProductConnection>('/storefront/products', productListQuery(params), options),\n /** `null` when the product does not exist or is unpublished. */\n get: (handle: string, options?: RequestOptions) =>\n getOrNull<Product>(`/storefront/products/${enc(handle)}`, undefined, options),\n recommendations: (handle: string, options?: RequestOptions) =>\n get<{ nodes: Product[] }>(`/storefront/products/${enc(handle)}/recommendations`, undefined, options),\n },\n\n collections: {\n list: (params?: CollectionListParams, options?: RequestOptions) =>\n get<Connection<Collection>>('/storefront/collections', params as QueryParams, options),\n /** `null` when the collection does not exist. */\n get: (handle: string, options?: RequestOptions) =>\n getOrNull<Collection>(`/storefront/collections/${enc(handle)}`, undefined, options),\n },\n\n locations: {\n /** Branches of the shop. With `postal_code` they come ordered by distance (`distance_km`). */\n list: (params?: LocationListParams, options?: RequestOptions) =>\n get<{ nodes: Location[] }>('/storefront/locations', params as QueryParams, options),\n /** `null` when the branch does not exist or is not visible. */\n get: (id: string, options?: RequestOptions) =>\n getOrNull<Location>(`/storefront/locations/${enc(id)}`, undefined, options),\n /**\n * Branches ordered by distance to a point, closest first. The server rounds the coordinates (0.01°, ~1 km)\n * and never stores them; this client does not log or keep them either. The answer is `private, no-store`\n * (it depends on the buyer's position), so do not put it in a shared cache. Note `onRequest` hooks and\n * `fetch` wrappers do see the URL: do not log it. Throws `TypeError` for an invalid point.\n */\n nearest: (point: GeoPoint, options?: RequestOptions) => {\n if (!isValidPoint(point)) throw new TypeError('locations.nearest: lat/lng must be a valid point')\n return get<{ nodes: NearestLocation[] }>('/storefront/locations/nearest', { lat: point.lat, lng: point.lng }, { cache: 'no-store', ...options })\n },\n },\n\n stock: {\n /** Stock for up to 250 SKUs. */\n get: (skus: string[], options?: RequestOptions) =>\n get<{ nodes: StockLevel[] }>('/storefront/stock', { sku: skus }, options),\n },\n\n carts: {\n create: (input: { lines?: CartLineInput[] } = {}, options?: WriteOptions) =>\n write<Cart>('POST', '/storefront/carts', input, options),\n /** `null` when the cart does not exist (e.g. expired cookie). */\n get: (id: string, options?: RequestOptions) => getOrNull<Cart>(cartPath(id), undefined, options),\n addLines: (id: string, lines: CartLineInput[], options?: WriteOptions) =>\n write<Cart>('POST', cartPath(id, '/lines'), { lines }, options),\n /** Quantity `0` removes the line. */\n updateLines: (id: string, lines: CartLineUpdateInput[], options?: WriteOptions) =>\n write<Cart>('PATCH', cartPath(id, '/lines'), { lines }, options),\n removeLines: (id: string, lineIds: string[], options?: WriteOptions) =>\n write<Cart>('PATCH', cartPath(id, '/lines'), { lines: lineIds.map((lid) => ({ id: lid, quantity: 0 })) }, options),\n setDiscountCodes: (id: string, codes: string[], options?: WriteOptions) =>\n write<Cart>('PUT', cartPath(id, '/discount-codes'), { codes }, options),\n setBuyer: (id: string, buyer: CartBuyerInput, options?: WriteOptions) =>\n write<Cart>('PUT', cartPath(id, '/buyer'), buyer, options),\n setShippingAddress: (id: string, address: AddressInput, options?: WriteOptions) =>\n write<Cart>('PUT', cartPath(id, '/shipping-address'), address, options),\n deliveryOptions: (id: string, options?: RequestOptions) =>\n get<DeliveryOptions>(cartPath(id, '/delivery-options'), undefined, options),\n /** Payment providers active for the cart's market; send the chosen `key` as `payment_method`. */\n paymentMethods: (id: string, options?: RequestOptions) =>\n get<PaymentMethods>(cartPath(id, '/payment-methods'), undefined, options),\n setDelivery: (id: string, delivery: DeliveryInput, options?: WriteOptions) =>\n write<Cart>('PUT', cartPath(id, '/delivery'), delivery, options),\n /** Reserves stock and creates the order. Sends `Idempotency-Key` (auto-generated if omitted). */\n checkout: (id: string, input: CheckoutInput, options: WriteOptions = {}) =>\n write<CheckoutResult>('POST', cartPath(id, '/checkout'), input, {\n ...options,\n idempotencyKey: options.idempotencyKey ?? generateIdempotencyKey(),\n }),\n },\n\n checkouts: {\n /**\n * Reads a placed checkout (same shape as the `carts.checkout` result). `order.number` is `null` until OPS\n * has created the order (a few hundred ms after checkout); poll once or twice and, if it is still `null`,\n * keep telling the buyer the number will arrive by email. Needs a server token (`checkout:write`).\n * `null` when the checkout does not exist. Never cache.\n */\n get: (id: string, options?: RequestOptions) =>\n getOrNull<CheckoutResult>(`/storefront/checkouts/${enc(id)}`, undefined, options),\n },\n\n customer: {\n /** Sends a one-time code by email or SMS/WhatsApp. */\n requestOtp: (identifier: CustomerIdentifier, options?: WriteOptions) =>\n write<void>('POST', '/storefront/customer/otp', identifier, options),\n /** Exchanges the code for a buyer session (`token` goes in `customerToken`). */\n createSession: (input: CustomerSessionInput, options?: WriteOptions) =>\n write<CustomerSession>('POST', '/storefront/customer/session', input, options),\n get: (options?: RequestOptions) => get<Customer>('/storefront/customer', undefined, options),\n orders: (params?: PaginationParams, options?: RequestOptions) =>\n get<Connection<Order>>('/storefront/customer/orders', params as QueryParams, options),\n /** `null` when the order does not exist or belongs to someone else. */\n order: (id: string, options?: RequestOptions) =>\n getOrNull<Order>(`/storefront/customer/orders/${enc(id)}`, undefined, options),\n requestReturn: (orderId: string, input: ReturnRequestInput, options?: WriteOptions) =>\n write<ReturnRequest>('POST', `/storefront/customer/orders/${enc(orderId)}/returns`, input, options),\n requestInvoice: (orderId: string, input: InvoiceRequest, options?: WriteOptions) =>\n write<InvoiceResult>('POST', `/storefront/customer/orders/${enc(orderId)}/invoice`, input, options),\n addresses: {\n list: (options?: RequestOptions) => get<{ nodes: Address[] }>('/storefront/customer/addresses', undefined, options),\n create: (address: AddressInput, options?: WriteOptions) =>\n write<Address>('POST', '/storefront/customer/addresses', address, options),\n update: (id: string, address: Partial<AddressInput>, options?: WriteOptions) =>\n write<Address>('PATCH', `/storefront/customer/addresses/${enc(id)}`, address, options),\n delete: (id: string, options?: WriteOptions) =>\n write<void>('DELETE', `/storefront/customer/addresses/${enc(id)}`, undefined, options),\n },\n },\n\n content: {\n theme: (options?: RequestOptions) => get<Theme>('/storefront/content/theme', undefined, options),\n /** `null` when the page does not exist. */\n page: (handle: string, options?: PreviewOptions) =>\n getOrNull<Page>(`/storefront/content/pages/${enc(handle)}`, previewQuery(options), options),\n /** `null` when the menu does not exist. */\n menu: (handle: string, options?: RequestOptions) =>\n getOrNull<Menu>(`/storefront/content/menus/${enc(handle)}`, undefined, options),\n articles: (params?: ArticleListParams, options?: RequestOptions) =>\n get<Connection<Article>>('/storefront/content/articles', params as QueryParams, options),\n /** `null` when the article does not exist. */\n article: (handle: string, options?: PreviewOptions) =>\n getOrNull<Article>(`/storefront/content/articles/${enc(handle)}`, previewQuery(options), options),\n },\n }\n return client\n}\n\nexport type StorefrontClient = ReturnType<typeof createStorefrontClient>\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/money.ts","../src/query.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { StorefrontErrorCode } from './types.js'\n\nexport type StorefrontErrorInit = {\n status: number\n code: StorefrontErrorCode\n message: string\n field?: string\n /** Request URL that failed (without credentials). */\n url?: string\n cause?: unknown\n}\n\n/**\n * Error thrown by the client for any non-2xx response, network failure,\n * timeout or abort. `status` is `0` when no HTTP response was received.\n */\nexport class StorefrontError extends Error {\n override readonly name = 'StorefrontError'\n readonly status: number\n readonly code: StorefrontErrorCode\n readonly field: string | undefined\n readonly url: string | undefined\n\n constructor(init: StorefrontErrorInit) {\n super(init.message, init.cause === undefined ? undefined : { cause: init.cause })\n this.status = init.status\n this.code = init.code\n this.field = init.field\n this.url = init.url\n }\n\n toJSON() {\n return { name: this.name, status: this.status, code: this.code, message: this.message, field: this.field }\n }\n}\n\nexport function isStorefrontError(err: unknown): err is StorefrontError {\n return err instanceof StorefrontError || (typeof err === 'object' && err !== null && (err as { name?: unknown }).name === 'StorefrontError')\n}\n\nconst STATUS_CODES: Record<number, StorefrontErrorCode> = {\n 400: 'validation',\n 401: 'unauthorized',\n 403: 'forbidden',\n 404: 'not_found',\n 409: 'conflict',\n 422: 'invalid_state',\n 429: 'rate_limited',\n}\n\nexport function defaultCodeForStatus(status: number): StorefrontErrorCode {\n return STATUS_CODES[status] ?? (status >= 500 ? 'server_error' : 'invalid_response')\n}\n","import type { Money } from './types.js'\n\nexport type FormatMoneyOptions = {\n /** Drop `.00` when the amount is a whole number. */\n withoutTrailingZeros?: boolean\n /** Format as a plain number, without the currency symbol. */\n withoutCurrency?: boolean\n /** `symbol` (default), `narrowSymbol`, `code` or `name`. */\n currencyDisplay?: 'symbol' | 'narrowSymbol' | 'code' | 'name'\n}\n\n/** Parses a contract `Money` amount into a JS number (`\"1899.00\"` → `1899`). */\nexport function parseMoney(money: Money | string | null | undefined): number {\n if (money == null) return 0\n const raw = typeof money === 'string' ? money : money.amount\n const n = Number.parseFloat(raw)\n return Number.isFinite(n) ? n : 0\n}\n\n/**\n * Formats `Money` with `Intl.NumberFormat`. Works in every runtime (no DOM).\n * Falls back to `\"<amount> <code>\"` when the runtime lacks the currency.\n */\nexport function formatMoney(money: Money, locale?: string, options: FormatMoneyOptions = {}): string {\n const value = parseMoney(money)\n const whole = Number.isInteger(value)\n const digits = options.withoutTrailingZeros && whole ? 0 : undefined\n try {\n const fmt = new Intl.NumberFormat(locale, {\n ...(options.withoutCurrency\n ? { style: 'decimal', minimumFractionDigits: digits ?? 2, maximumFractionDigits: digits ?? 2 }\n : {\n style: 'currency',\n currency: money.currency_code,\n currencyDisplay: options.currencyDisplay ?? 'symbol',\n ...(digits === 0 ? { minimumFractionDigits: 0, maximumFractionDigits: 0 } : {}),\n }),\n })\n return fmt.format(value)\n } catch {\n const amount = digits === 0 ? String(value) : value.toFixed(2)\n return options.withoutCurrency ? amount : `${amount} ${money.currency_code}`\n }\n}\n\n/** Builds a `Money` from a number, rounding to cents. */\nexport function toMoney(value: number, currencyCode: string): Money {\n return { amount: (Math.round(value * 100) / 100).toFixed(2), currency_code: currencyCode }\n}\n\n/**\n * True for public (`pk_`) tokens, the only ones that may reach a browser.\n * Private (`sk_`) tokens must stay on the server.\n */\nexport function isTokenPublishable(token: string | null | undefined): boolean {\n return typeof token === 'string' && token.startsWith('pk_')\n}\n","import type { ProductSort } from './types.js'\n\nexport type QueryValue = string | number | boolean | null | undefined\nexport type QueryParams = Record<string, QueryValue | QueryValue[]>\n\nexport type PaginationParams = {\n /** Page size (1–100; articles accept at most 50). */\n first?: number\n /** Cursor from `page_info.end_cursor`. */\n after?: string | null\n}\n\nexport type ProductListParams = PaginationParams & {\n /** Free-text search. */\n q?: string\n /** Collection handle. */\n collection?: string\n sort?: ProductSort\n /** Only products available for sale. */\n available?: boolean\n price_min?: number | string\n price_max?: number | string\n /**\n * Attribute filters, serialized as repeated `filter.<key>=<value>` params.\n * Values of one key are OR-ed; different keys are AND-ed.\n * e.g. `{ vendor: ['A', 'B'], rim: 16 }`.\n */\n filters?: Record<string, QueryValue | QueryValue[]>\n /** Restrict to these handles (max 100). Serialized comma-separated. */\n handles?: string[]\n}\n\nexport type CollectionListParams = PaginationParams\n\nexport type ArticleListParams = Omit<PaginationParams, 'first'> & {\n /** Page size (1–50). The API answers 400 `validation` above 50. */\n first?: number\n tag?: string\n q?: string\n}\n\n/** Appends params to a URLSearchParams, repeating keys for arrays and skipping nullish values. */\nexport function appendParams(search: URLSearchParams, params: QueryParams | undefined): URLSearchParams {\n if (!params) return search\n for (const [key, raw] of Object.entries(params)) {\n const values = Array.isArray(raw) ? raw : [raw]\n for (const v of values) {\n if (v === undefined || v === null || v === '') continue\n search.append(key, String(v))\n }\n }\n return search\n}\n\n/** Serializes product list params per the contract (`filter.<attr>` repeated, `handles` comma-separated). */\nexport function productListQuery(params: ProductListParams = {}): URLSearchParams {\n const { filters, handles, ...rest } = params\n const search = appendParams(new URLSearchParams(), rest as QueryParams)\n if (handles && handles.length > 0) search.set('handles', handles.join(','))\n if (filters) {\n for (const [key, raw] of Object.entries(filters)) {\n const name = key.startsWith('filter.') ? key : `filter.${key}`\n appendParams(search, { [name]: raw })\n }\n }\n return search\n}\n\n/** Reads `filter.<key>` params back into a filters record (useful in route loaders). */\nexport function parseProductFilters(search: URLSearchParams): Record<string, string[]> {\n const out: Record<string, string[]> = {}\n for (const [key, value] of search) {\n if (!key.startsWith('filter.')) continue\n const name = key.slice('filter.'.length)\n ;(out[name] ??= []).push(value)\n }\n return out\n}\n","import { StorefrontError, defaultCodeForStatus } from './errors.js'\nimport { isValidPoint, type GeoPoint } from './geo.js'\nimport { isTokenPublishable } from './money.js'\nimport {\n appendParams,\n productListQuery,\n type ArticleListParams,\n type CollectionListParams,\n type PaginationParams,\n type ProductListParams,\n type QueryParams,\n} from './query.js'\nimport type {\n Address,\n AddressInput,\n Article,\n Cart,\n CartBuyerInput,\n CartLineInput,\n CartLineUpdateInput,\n CheckoutInput,\n CheckoutResult,\n Collection,\n Connection,\n Customer,\n CustomerIdentifier,\n CustomerSession,\n CustomerSessionInput,\n DeliveryInput,\n DeliveryOptions,\n PaymentMethods,\n InvoiceRequest,\n Location,\n LocationListParams,\n NearestLocation,\n InvoiceResult,\n Menu,\n Order,\n Page,\n Product,\n ProductConnection,\n ReturnRequest,\n ReturnRequestInput,\n Shop,\n StockLevel,\n StorefrontErrorBody,\n Theme,\n} from './types.js'\n\nexport type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>\n\n/** Context passed to `onRequest` / `onResponse` hooks. */\nexport type StorefrontRequestContext = {\n method: string\n /** Absolute URL, including the query string. */\n url: string\n /** Route path relative to `baseUrl`, e.g. `/storefront/products/abc`. */\n path: string\n /** Final `RequestInit` (headers already merged). Hooks may mutate it. */\n init: RequestInit & { headers: Headers }\n /** Per-call options as given by the caller. */\n options: RequestOptions\n}\n\nexport type StorefrontResponseContext = StorefrontRequestContext & {\n response: Response\n /** Milliseconds spent waiting for the response. */\n durationMs: number\n}\n\nexport type StorefrontClientOptions = {\n /** Base URL of the gateway, e.g. `https://ops.example.com/api/gw`. Routes are appended as `/storefront/...`. */\n baseUrl: string\n /** `pk_...` (browser) or `sk_...` (server only). Sent as `Authorization: Bearer <token>`. */\n token: string\n /** Buyer session (`X-Customer-Token`) for `/storefront/customer/*` routes. */\n customerToken?: string | null\n /** Custom fetch (defaults to `globalThis.fetch`). */\n fetch?: FetchLike\n /** Sent as `Accept-Language`. */\n locale?: string\n /** Extra headers on every request. */\n headers?: Record<string, string>\n /**\n * IP address of the shopper, for SERVER-side calls (SSR, route actions) made with an\n * `sk_`/`pt_` token. Sent as `X-Storefront-Client-IP`, which the gateway honors only for\n * tokens issued with the `trust_client_ip` option: the rate limit is then charged per\n * shopper instead of to the store's single token bucket, and the OTP flow limits the\n * shopper's IP instead of the server's. Pass the IP from your own connection or trusted\n * proxy, never from a header the browser can forge. Never sent with a `pk_` token (the\n * gateway already separates those by caller IP). Invalid values are dropped.\n */\n clientIp?: string | null\n /** Abort requests that take longer than this (default: no timeout). */\n timeoutMs?: number\n /**\n * Runs before every request. Return a `Response` to short-circuit the\n * network (e.g. an SSR cache hit); return nothing to continue.\n */\n onRequest?: (ctx: StorefrontRequestContext) => void | Response | Promise<void | Response>\n /** Runs after every response (including short-circuited ones), before parsing. */\n onResponse?: (ctx: StorefrontResponseContext) => void | Promise<void>\n}\n\n/** Per-call options. Unknown keys (e.g. Next.js `next`) are forwarded to `fetch` as-is. */\nexport type RequestOptions = {\n cache?: RequestCache\n signal?: AbortSignal\n /** Framework fetch extensions, forwarded verbatim (e.g. `{ revalidate: 60, tags: ['products'] }`). */\n next?: Record<string, unknown>\n headers?: Record<string, string>\n /** Overrides the client-level timeout for this call. */\n timeoutMs?: number\n /** Overrides the client-level customer token for this call. */\n customerToken?: string | null\n /** Overrides the client-level shopper IP for this call (see `StorefrontClientOptions.clientIp`). */\n clientIp?: string | null\n [key: string]: unknown\n}\n\nexport type WriteOptions = RequestOptions & {\n /** `Idempotency-Key` header. Auto-generated for checkout when omitted. */\n idempotencyKey?: string\n}\n\nexport type PreviewOptions = RequestOptions & {\n /** Signed preview token: returns the draft (`private, no-store`). */\n preview?: string\n}\n\ntype HttpMethod = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE'\n\ntype RequestSpec = {\n method: HttpMethod\n path: string\n query?: URLSearchParams | QueryParams\n body?: unknown\n options?: WriteOptions\n /** Resolve `null` instead of throwing on 404. */\n nullOn404?: boolean\n}\n\nconst OWN_OPTION_KEYS = new Set(['headers', 'timeoutMs', 'customerToken', 'clientIp', 'idempotencyKey', 'preview', 'signal'])\n\n/** Header the gateway reads (only for tokens with `trust_client_ip`). */\nexport const CLIENT_IP_HEADER = 'X-Storefront-Client-IP'\n\n/**\n * Returns the single IP literal in `raw`, or undefined. Takes the form a server hands over\n * (`::ffff:1.2.3.4`, `[2001:db8::1]`) and rejects lists, ports, zones and text so the\n * header is never a forgery vector.\n */\nexport function normalizeClientIp(raw: string | null | undefined): string | undefined {\n if (!raw) return undefined\n let v = raw.trim()\n if (v.startsWith('[') && v.endsWith(']')) v = v.slice(1, -1)\n if (!v || v.length > 45 || /[\\s,%/]/.test(v)) return undefined\n if (/^[0-9]{1,3}(\\.[0-9]{1,3}){3}$/.test(v)) {\n return v.split('.').every((o) => Number(o) <= 255) ? v : undefined\n }\n // IPv6 (incl. IPv4-mapped): hex groups and colons, at most one \"::\".\n if (!v.includes(':') || !/^[0-9a-fA-F:.]+$/.test(v) || v.split('::').length > 2) return undefined\n try {\n new URL(`http://[${v}]/`)\n } catch {\n return undefined\n }\n return v\n}\n\n/** Generates an idempotency key (crypto.randomUUID when available). */\nexport function generateIdempotencyKey(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto\n if (c?.randomUUID) return c.randomUUID()\n // Non-cryptographic fallback for very old runtimes; the key only needs to be unique.\n return 'idem-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2)\n}\n\nfunction joinUrl(baseUrl: string, path: string): string {\n return baseUrl.replace(/\\/+$/, '') + path\n}\n\nfunction enc(segment: string): string {\n return encodeURIComponent(segment)\n}\n\nfunction linkSignals(signals: (AbortSignal | undefined)[]): { signal: AbortSignal | undefined; cleanup: () => void } {\n const active = signals.filter((s): s is AbortSignal => !!s)\n if (active.length === 0) return { signal: undefined, cleanup: () => {} }\n if (active.length === 1) return { signal: active[0], cleanup: () => {} }\n const controller = new AbortController()\n const listeners: [AbortSignal, () => void][] = []\n for (const s of active) {\n if (s.aborted) {\n controller.abort(s.reason)\n break\n }\n const on = () => controller.abort(s.reason)\n s.addEventListener('abort', on, { once: true })\n listeners.push([s, on])\n }\n return {\n signal: controller.signal,\n cleanup: () => listeners.forEach(([s, on]) => s.removeEventListener('abort', on)),\n }\n}\n\nasync function readError(res: Response, url: string): Promise<StorefrontError> {\n let body: Partial<StorefrontErrorBody> | undefined\n try {\n const text = await res.text()\n body = text ? (JSON.parse(text) as Partial<StorefrontErrorBody>) : undefined\n } catch {\n body = undefined\n }\n const e = body?.error\n return new StorefrontError({\n status: res.status,\n code: e?.code ?? defaultCodeForStatus(res.status),\n message: e?.message ?? `Storefront API responded ${res.status}${res.statusText ? ' ' + res.statusText : ''}`,\n ...(e?.field ? { field: e.field } : {}),\n url,\n })\n}\n\nexport function createStorefrontClient(config: StorefrontClientOptions) {\n if (!config.baseUrl) throw new TypeError('createStorefrontClient: baseUrl is required')\n if (!config.token) throw new TypeError('createStorefrontClient: token is required')\n const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined'\n if (isBrowser && !isTokenPublishable(config.token) && config.token.startsWith('sk_')) {\n console.warn('[@asteby/storefront] A private sk_ token is being used in a browser. Use a public pk_ token instead.')\n }\n\n async function request<T>(spec: RequestSpec & { nullOn404: true }): Promise<T | null>\n async function request<T>(spec: RequestSpec): Promise<T>\n async function request<T>(spec: RequestSpec): Promise<T | null> {\n const fetchImpl: FetchLike | undefined = config.fetch ?? (globalThis.fetch ? globalThis.fetch.bind(globalThis) : undefined)\n if (!fetchImpl) throw new TypeError('createStorefrontClient: no fetch available; pass `fetch` in options')\n\n const options: WriteOptions = spec.options ?? {}\n const search = spec.query instanceof URLSearchParams ? spec.query : appendParams(new URLSearchParams(), spec.query)\n const qs = search.toString()\n const url = joinUrl(config.baseUrl, spec.path) + (qs ? `?${qs}` : '')\n\n const headers = new Headers(config.headers)\n headers.set('Accept', 'application/json')\n headers.set('Authorization', `Bearer ${config.token}`)\n if (config.locale) headers.set('Accept-Language', config.locale)\n const customerToken = options.customerToken !== undefined ? options.customerToken : config.customerToken\n if (customerToken) headers.set('X-Customer-Token', customerToken)\n const clientIp = options.clientIp !== undefined ? options.clientIp : config.clientIp\n // Server-side tokens only: a pk_ token already gets a per-caller bucket from the gateway.\n const ip = isTokenPublishable(config.token) ? undefined : normalizeClientIp(clientIp)\n if (ip) headers.set(CLIENT_IP_HEADER, ip)\n if (options.idempotencyKey) headers.set('Idempotency-Key', options.idempotencyKey)\n if (spec.body !== undefined) headers.set('Content-Type', 'application/json')\n for (const [k, v] of Object.entries(options.headers ?? {})) headers.set(k, v)\n\n // Forward passthrough options (cache, next, ...) to fetch.\n const passthrough: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(options)) if (!OWN_OPTION_KEYS.has(k) && v !== undefined) passthrough[k] = v\n\n const timeoutMs = options.timeoutMs ?? config.timeoutMs\n const timeoutController = timeoutMs && timeoutMs > 0 ? new AbortController() : undefined\n const timer = timeoutController\n ? setTimeout(() => timeoutController.abort(new StorefrontError({ status: 0, code: 'timeout', message: `Request timed out after ${timeoutMs}ms`, url })), timeoutMs)\n : undefined\n const linked = linkSignals([options.signal, timeoutController?.signal])\n\n const init: RequestInit & { headers: Headers } = {\n ...passthrough,\n method: spec.method,\n headers,\n ...(spec.body !== undefined ? { body: JSON.stringify(spec.body) } : {}),\n ...(linked.signal ? { signal: linked.signal } : {}),\n }\n const ctx: StorefrontRequestContext = { method: spec.method, url, path: spec.path, init, options }\n\n const started = Date.now()\n let res: Response\n try {\n const hooked = config.onRequest ? await config.onRequest(ctx) : undefined\n res = hooked instanceof Response ? hooked : await fetchImpl(url, init)\n } catch (err) {\n if (timeoutController?.signal.aborted) {\n throw new StorefrontError({ status: 0, code: 'timeout', message: `Request timed out after ${timeoutMs}ms`, url, cause: err })\n }\n if (options.signal?.aborted) {\n throw new StorefrontError({ status: 0, code: 'aborted', message: 'Request aborted', url, cause: err })\n }\n if (err instanceof StorefrontError) throw err\n throw new StorefrontError({ status: 0, code: 'network', message: err instanceof Error ? err.message : 'Network error', url, cause: err })\n } finally {\n if (timer) clearTimeout(timer)\n linked.cleanup()\n }\n\n if (config.onResponse) await config.onResponse({ ...ctx, response: res, durationMs: Date.now() - started })\n\n if (res.status === 404 && spec.nullOn404) {\n // Drain the body so the connection can be reused.\n await res.text().catch(() => undefined)\n return null\n }\n if (res.status === 304) {\n throw new StorefrontError({ status: 304, code: 'not_modified', message: 'Not modified', url })\n }\n if (!res.ok) throw await readError(res, url)\n if (res.status === 204) return undefined as T\n\n const text = await res.text()\n if (!text) return undefined as T\n try {\n return JSON.parse(text) as T\n } catch (err) {\n throw new StorefrontError({ status: res.status, code: 'invalid_response', message: 'Response is not valid JSON', url, cause: err })\n }\n }\n\n const get = <T>(path: string, query?: URLSearchParams | QueryParams, options?: RequestOptions) =>\n request<T>({ method: 'GET', path, ...(query ? { query } : {}), ...(options ? { options } : {}) })\n const getOrNull = <T>(path: string, query?: URLSearchParams | QueryParams, options?: RequestOptions) =>\n request<T>({ method: 'GET', path, nullOn404: true, ...(query ? { query } : {}), ...(options ? { options } : {}) })\n const write = <T>(method: HttpMethod, path: string, body?: unknown, options?: WriteOptions) =>\n request<T>({ method, path, ...(body !== undefined ? { body } : {}), ...(options ? { options } : {}) })\n\n const cartPath = (id: string, rest = '') => `/storefront/carts/${enc(id)}${rest}`\n const previewQuery = (options?: PreviewOptions) => (options?.preview ? { preview: options.preview } : undefined)\n\n const client = {\n /** Resolved configuration (read-only). */\n config: Object.freeze({ ...config }) as Readonly<StorefrontClientOptions>,\n\n /** Low-level escape hatch: any route under `baseUrl`. */\n request,\n\n /** Returns a new client that sends the given buyer session token. */\n withCustomerToken(customerToken: string | null) {\n return createStorefrontClient({ ...config, customerToken })\n },\n\n /** Returns a new client that reports the shopper's IP (`X-Storefront-Client-IP`) on every call. */\n withClientIp(clientIp: string | null | undefined) {\n return createStorefrontClient({ ...config, clientIp: clientIp ?? null })\n },\n\n shop: {\n get: (options?: RequestOptions) => get<Shop>('/storefront/shop', undefined, options),\n },\n\n products: {\n list: (params?: ProductListParams, options?: RequestOptions) =>\n get<ProductConnection>('/storefront/products', productListQuery(params), options),\n /** `null` when the product does not exist or is unpublished. */\n get: (handle: string, options?: RequestOptions) =>\n getOrNull<Product>(`/storefront/products/${enc(handle)}`, undefined, options),\n recommendations: (handle: string, options?: RequestOptions) =>\n get<{ nodes: Product[] }>(`/storefront/products/${enc(handle)}/recommendations`, undefined, options),\n },\n\n collections: {\n list: (params?: CollectionListParams, options?: RequestOptions) =>\n get<Connection<Collection>>('/storefront/collections', params as QueryParams, options),\n /** `null` when the collection does not exist. */\n get: (handle: string, options?: RequestOptions) =>\n getOrNull<Collection>(`/storefront/collections/${enc(handle)}`, undefined, options),\n },\n\n locations: {\n /** Branches of the shop. With `postal_code` they come ordered by distance (`distance_km`). */\n list: (params?: LocationListParams, options?: RequestOptions) =>\n get<{ nodes: Location[] }>('/storefront/locations', params as QueryParams, options),\n /** `null` when the branch does not exist or is not visible. */\n get: (id: string, options?: RequestOptions) =>\n getOrNull<Location>(`/storefront/locations/${enc(id)}`, undefined, options),\n /**\n * Branches ordered by distance to a point, closest first. The server rounds the coordinates (0.01°, ~1 km)\n * and never stores them; this client does not log or keep them either. The answer is `private, no-store`\n * (it depends on the buyer's position), so do not put it in a shared cache. Note `onRequest` hooks and\n * `fetch` wrappers do see the URL: do not log it. Throws `TypeError` for an invalid point.\n */\n nearest: (point: GeoPoint, options?: RequestOptions) => {\n if (!isValidPoint(point)) throw new TypeError('locations.nearest: lat/lng must be a valid point')\n return get<{ nodes: NearestLocation[] }>('/storefront/locations/nearest', { lat: point.lat, lng: point.lng }, { cache: 'no-store', ...options })\n },\n },\n\n stock: {\n /** Stock for up to 250 SKUs. */\n get: (skus: string[], options?: RequestOptions) =>\n get<{ nodes: StockLevel[] }>('/storefront/stock', { sku: skus }, options),\n },\n\n carts: {\n create: (input: { lines?: CartLineInput[] } = {}, options?: WriteOptions) =>\n write<Cart>('POST', '/storefront/carts', input, options),\n /** `null` when the cart does not exist (e.g. expired cookie). */\n get: (id: string, options?: RequestOptions) => getOrNull<Cart>(cartPath(id), undefined, options),\n addLines: (id: string, lines: CartLineInput[], options?: WriteOptions) =>\n write<Cart>('POST', cartPath(id, '/lines'), { lines }, options),\n /** Quantity `0` removes the line. */\n updateLines: (id: string, lines: CartLineUpdateInput[], options?: WriteOptions) =>\n write<Cart>('PATCH', cartPath(id, '/lines'), { lines }, options),\n removeLines: (id: string, lineIds: string[], options?: WriteOptions) =>\n write<Cart>('PATCH', cartPath(id, '/lines'), { lines: lineIds.map((lid) => ({ id: lid, quantity: 0 })) }, options),\n setDiscountCodes: (id: string, codes: string[], options?: WriteOptions) =>\n write<Cart>('PUT', cartPath(id, '/discount-codes'), { codes }, options),\n setBuyer: (id: string, buyer: CartBuyerInput, options?: WriteOptions) =>\n write<Cart>('PUT', cartPath(id, '/buyer'), buyer, options),\n setShippingAddress: (id: string, address: AddressInput, options?: WriteOptions) =>\n write<Cart>('PUT', cartPath(id, '/shipping-address'), address, options),\n deliveryOptions: (id: string, options?: RequestOptions) =>\n get<DeliveryOptions>(cartPath(id, '/delivery-options'), undefined, options),\n /** Payment providers active for the cart's market; send the chosen `key` as `payment_method`. */\n paymentMethods: (id: string, options?: RequestOptions) =>\n get<PaymentMethods>(cartPath(id, '/payment-methods'), undefined, options),\n setDelivery: (id: string, delivery: DeliveryInput, options?: WriteOptions) =>\n write<Cart>('PUT', cartPath(id, '/delivery'), delivery, options),\n /** Reserves stock and creates the order. Sends `Idempotency-Key` (auto-generated if omitted). */\n checkout: (id: string, input: CheckoutInput, options: WriteOptions = {}) =>\n write<CheckoutResult>('POST', cartPath(id, '/checkout'), input, {\n ...options,\n idempotencyKey: options.idempotencyKey ?? generateIdempotencyKey(),\n }),\n },\n\n checkouts: {\n /**\n * Reads a placed checkout (same shape as the `carts.checkout` result). `order.number` is `null` until OPS\n * has created the order (a few hundred ms after checkout); poll once or twice and, if it is still `null`,\n * keep telling the buyer the number will arrive by email. Needs a server token (`checkout:write`).\n * `null` when the checkout does not exist. Never cache.\n */\n get: (id: string, options?: RequestOptions) =>\n getOrNull<CheckoutResult>(`/storefront/checkouts/${enc(id)}`, undefined, options),\n },\n\n customer: {\n /** Sends a one-time code by email or SMS/WhatsApp. */\n requestOtp: (identifier: CustomerIdentifier, options?: WriteOptions) =>\n write<void>('POST', '/storefront/customer/otp', identifier, options),\n /** Exchanges the code for a buyer session (`token` goes in `customerToken`). */\n createSession: (input: CustomerSessionInput, options?: WriteOptions) =>\n write<CustomerSession>('POST', '/storefront/customer/session', input, options),\n get: (options?: RequestOptions) => get<Customer>('/storefront/customer', undefined, options),\n orders: (params?: PaginationParams, options?: RequestOptions) =>\n get<Connection<Order>>('/storefront/customer/orders', params as QueryParams, options),\n /** `null` when the order does not exist or belongs to someone else. */\n order: (id: string, options?: RequestOptions) =>\n getOrNull<Order>(`/storefront/customer/orders/${enc(id)}`, undefined, options),\n requestReturn: (orderId: string, input: ReturnRequestInput, options?: WriteOptions) =>\n write<ReturnRequest>('POST', `/storefront/customer/orders/${enc(orderId)}/returns`, input, options),\n requestInvoice: (orderId: string, input: InvoiceRequest, options?: WriteOptions) =>\n write<InvoiceResult>('POST', `/storefront/customer/orders/${enc(orderId)}/invoice`, input, options),\n addresses: {\n list: (options?: RequestOptions) => get<{ nodes: Address[] }>('/storefront/customer/addresses', undefined, options),\n create: (address: AddressInput, options?: WriteOptions) =>\n write<Address>('POST', '/storefront/customer/addresses', address, options),\n update: (id: string, address: Partial<AddressInput>, options?: WriteOptions) =>\n write<Address>('PATCH', `/storefront/customer/addresses/${enc(id)}`, address, options),\n delete: (id: string, options?: WriteOptions) =>\n write<void>('DELETE', `/storefront/customer/addresses/${enc(id)}`, undefined, options),\n },\n },\n\n content: {\n theme: (options?: RequestOptions) => get<Theme>('/storefront/content/theme', undefined, options),\n /** `null` when the page does not exist. */\n page: (handle: string, options?: PreviewOptions) =>\n getOrNull<Page>(`/storefront/content/pages/${enc(handle)}`, previewQuery(options), options),\n /** `null` when the menu does not exist. */\n menu: (handle: string, options?: RequestOptions) =>\n getOrNull<Menu>(`/storefront/content/menus/${enc(handle)}`, undefined, options),\n articles: (params?: ArticleListParams, options?: RequestOptions) =>\n get<Connection<Article>>('/storefront/content/articles', params as QueryParams, options),\n /** `null` when the article does not exist. */\n article: (handle: string, options?: PreviewOptions) =>\n getOrNull<Article>(`/storefront/content/articles/${enc(handle)}`, previewQuery(options), options),\n },\n }\n return client\n}\n\nexport type StorefrontClient = ReturnType<typeof createStorefrontClient>\n"]}
|
package/dist/mockup.cjs
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/mockup.ts
|
|
4
|
+
var BUILTIN_MOCKUP_KINDS = ["tire", "part", "supplement", "generic"];
|
|
5
|
+
var MOCKUP_TEXT_LIMITS = {
|
|
6
|
+
title: 80,
|
|
7
|
+
brand: 32,
|
|
8
|
+
model: 40,
|
|
9
|
+
size: 24,
|
|
10
|
+
service: 12,
|
|
11
|
+
partNumber: 32,
|
|
12
|
+
fitment: 90,
|
|
13
|
+
position: 24,
|
|
14
|
+
category: 40,
|
|
15
|
+
flavor: 32,
|
|
16
|
+
presentation: 32,
|
|
17
|
+
monogram: 2
|
|
18
|
+
};
|
|
19
|
+
var CONTROL_CHARS = /[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u2028-\u202e\u2066-\u2069\ufeff]/g;
|
|
20
|
+
function cleanMockupText(value, max = MOCKUP_TEXT_LIMITS.title) {
|
|
21
|
+
if (value === null || value === void 0 || typeof value === "boolean") return null;
|
|
22
|
+
if (typeof value === "number" && !Number.isFinite(value)) return null;
|
|
23
|
+
const text = String(value).replace(CONTROL_CHARS, " ").replace(/\s+/g, " ").trim();
|
|
24
|
+
if (!text) return null;
|
|
25
|
+
const chars = Array.from(text);
|
|
26
|
+
if (chars.length <= max) return text;
|
|
27
|
+
if (max <= 1) return "\u2026";
|
|
28
|
+
return chars.slice(0, max - 1).join("").trimEnd() + "\u2026";
|
|
29
|
+
}
|
|
30
|
+
function mockupMonogram(text) {
|
|
31
|
+
const words = (cleanMockupText(text, 200) ?? "").split(" ").map((w) => w.replace(/[^\p{L}\p{N}]/gu, "")).filter(Boolean);
|
|
32
|
+
if (!words.length) return null;
|
|
33
|
+
const letters = words.length === 1 ? Array.from(words[0]).slice(0, 2) : [Array.from(words[0])[0], Array.from(words[1])[0]];
|
|
34
|
+
return letters.join("").toLocaleUpperCase("es");
|
|
35
|
+
}
|
|
36
|
+
function attr(attrs, keys) {
|
|
37
|
+
if (!attrs) return void 0;
|
|
38
|
+
const lower = new Map(Object.entries(attrs).map(([k, v]) => [k.toLowerCase(), v]));
|
|
39
|
+
for (const key of keys) {
|
|
40
|
+
const v = lower.get(key);
|
|
41
|
+
if (v === void 0 || v === null) continue;
|
|
42
|
+
if (typeof v === "string" && !v.trim()) continue;
|
|
43
|
+
return v;
|
|
44
|
+
}
|
|
45
|
+
return void 0;
|
|
46
|
+
}
|
|
47
|
+
function attrText(attrs, keys, max) {
|
|
48
|
+
const v = attr(attrs, keys);
|
|
49
|
+
return typeof v === "boolean" ? null : cleanMockupText(v, max);
|
|
50
|
+
}
|
|
51
|
+
function attrNumber(attrs, keys) {
|
|
52
|
+
const v = attr(attrs, keys);
|
|
53
|
+
if (typeof v === "number") return Number.isFinite(v) && v > 0 ? v : null;
|
|
54
|
+
if (typeof v === "string" && /^\s*\d+(\.\d+)?\s*$/.test(v)) return Number.parseFloat(v);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
function attrFlag(attrs, keys) {
|
|
58
|
+
const v = attr(attrs, keys);
|
|
59
|
+
return v === true || typeof v === "string" && /^(true|s[ií]|yes|1)$/i.test(v.trim());
|
|
60
|
+
}
|
|
61
|
+
function optionValue(product, variant, names, max) {
|
|
62
|
+
const selected = variant?.selected_options?.find((o) => names.test(o.name.trim()));
|
|
63
|
+
if (selected) return cleanMockupText(selected.value, max);
|
|
64
|
+
const option = product.options?.find((o) => names.test(o.name.trim()));
|
|
65
|
+
return option && option.values.length === 1 ? cleanMockupText(option.values[0], max) : null;
|
|
66
|
+
}
|
|
67
|
+
var fmtNumber = (n) => Number.isInteger(n) ? String(n) : String(Number(n.toFixed(2)));
|
|
68
|
+
function common(product) {
|
|
69
|
+
return {
|
|
70
|
+
title: cleanMockupText(product.title, MOCKUP_TEXT_LIMITS.title) ?? "",
|
|
71
|
+
brand: cleanMockupText(product.vendor, MOCKUP_TEXT_LIMITS.brand)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
var TIRE_KEYS = {
|
|
75
|
+
size: ["size_key", "size", "tire_size", "medida"],
|
|
76
|
+
width: ["section_width_mm", "section_width", "width", "ancho"],
|
|
77
|
+
aspect: ["aspect_ratio", "aspect", "profile", "perfil"],
|
|
78
|
+
rim: ["rim_diameter_in", "rim_diameter", "rim", "rin"],
|
|
79
|
+
model: ["model", "modelo", "tread", "pattern", "line", "linea", "l\xEDnea"],
|
|
80
|
+
loadService: ["load_service", "service_description"],
|
|
81
|
+
load: ["load_index", "indice_carga", "\xEDndice_de_carga"],
|
|
82
|
+
speed: ["speed_rating", "speed_index", "indice_velocidad", "\xEDndice_de_velocidad"],
|
|
83
|
+
lightTruck: ["light_truck", "lt"],
|
|
84
|
+
runFlat: ["runflat", "run_flat"]
|
|
85
|
+
};
|
|
86
|
+
function tireMockupData(product) {
|
|
87
|
+
const a = product.attributes;
|
|
88
|
+
const w = attrNumber(a, TIRE_KEYS.width);
|
|
89
|
+
const ar = attrNumber(a, TIRE_KEYS.aspect);
|
|
90
|
+
const r = attrNumber(a, TIRE_KEYS.rim);
|
|
91
|
+
const split = w !== null && ar !== null && r !== null;
|
|
92
|
+
const written = attrText(a, TIRE_KEYS.size, MOCKUP_TEXT_LIMITS.size);
|
|
93
|
+
const size = written ?? (split ? `${fmtNumber(w)}/${fmtNumber(ar)}R${fmtNumber(r)}` : null);
|
|
94
|
+
const loadService = attrText(a, TIRE_KEYS.loadService, MOCKUP_TEXT_LIMITS.service);
|
|
95
|
+
const load = attrText(a, TIRE_KEYS.load, 7);
|
|
96
|
+
const speed = attrText(a, TIRE_KEYS.speed, 3);
|
|
97
|
+
const service = cleanMockupText(loadService ? loadService + (speed && !loadService.endsWith(speed) ? speed : "") : (load ?? "") + (speed ?? ""), MOCKUP_TEXT_LIMITS.service);
|
|
98
|
+
return {
|
|
99
|
+
kind: "tire",
|
|
100
|
+
...common(product),
|
|
101
|
+
size,
|
|
102
|
+
width: split ? fmtNumber(w) : null,
|
|
103
|
+
aspect: split ? fmtNumber(ar) : null,
|
|
104
|
+
rim: split ? fmtNumber(r) : null,
|
|
105
|
+
model: attrText(a, TIRE_KEYS.model, MOCKUP_TEXT_LIMITS.model),
|
|
106
|
+
service,
|
|
107
|
+
lightTruck: attrFlag(a, TIRE_KEYS.lightTruck),
|
|
108
|
+
runFlat: attrFlag(a, TIRE_KEYS.runFlat)
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
var PART_KEYS = {
|
|
112
|
+
partNumber: ["part_number", "mpn", "numero_de_parte", "n\xFAmero_de_parte", "no_parte"],
|
|
113
|
+
oem: ["oem_number", "oem", "oem_part_number"],
|
|
114
|
+
fitment: ["fitment", "fitment_summary", "compatibility", "compatible_with", "compatibilidad", "aplicaciones", "vehicle"],
|
|
115
|
+
position: ["position", "posicion", "posici\xF3n"]
|
|
116
|
+
};
|
|
117
|
+
function partMockupData(product) {
|
|
118
|
+
const a = product.attributes;
|
|
119
|
+
return {
|
|
120
|
+
kind: "part",
|
|
121
|
+
...common(product),
|
|
122
|
+
partNumber: attrText(a, PART_KEYS.partNumber, MOCKUP_TEXT_LIMITS.partNumber),
|
|
123
|
+
oemNumber: attrText(a, PART_KEYS.oem, MOCKUP_TEXT_LIMITS.partNumber),
|
|
124
|
+
fitment: attrText(a, PART_KEYS.fitment, MOCKUP_TEXT_LIMITS.fitment),
|
|
125
|
+
position: attrText(a, PART_KEYS.position, MOCKUP_TEXT_LIMITS.position),
|
|
126
|
+
category: cleanMockupText(product.category?.title, MOCKUP_TEXT_LIMITS.category)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
var SUPPLEMENT_KEYS = {
|
|
130
|
+
flavor: ["flavor", "flavour", "sabor"],
|
|
131
|
+
presentation: ["presentation", "presentacion", "presentaci\xF3n", "net_content", "contenido_neto", "net_weight", "peso_neto"],
|
|
132
|
+
servings: ["servings", "porciones", "servicios"],
|
|
133
|
+
form: ["container", "envase", "form", "forma", "presentation_form"]
|
|
134
|
+
};
|
|
135
|
+
function supplementShape(product) {
|
|
136
|
+
const form = String(attr(product.attributes, SUPPLEMENT_KEYS.form) ?? "").toLowerCase();
|
|
137
|
+
if (/bottle|frasco|c[aá]psula|capsule|tableta|tablet|softgel|gomita|gummy|l[ií]quido|liquid/.test(form)) return "bottle";
|
|
138
|
+
if (/bag|bolsa|doypack|pouch|sobre/.test(form)) return "bag";
|
|
139
|
+
return "tub";
|
|
140
|
+
}
|
|
141
|
+
function supplementMockupData(product, variant) {
|
|
142
|
+
const a = product.attributes;
|
|
143
|
+
const servings = attrNumber(a, SUPPLEMENT_KEYS.servings);
|
|
144
|
+
return {
|
|
145
|
+
kind: "supplement",
|
|
146
|
+
...common(product),
|
|
147
|
+
flavor: optionValue(product, variant, /^(sabor|flavou?r)$/i, MOCKUP_TEXT_LIMITS.flavor) ?? attrText(a, SUPPLEMENT_KEYS.flavor, MOCKUP_TEXT_LIMITS.flavor),
|
|
148
|
+
presentation: optionValue(product, variant, /^(presentaci[oó]n|tama[nñ]o|size|contenido|peso)$/i, MOCKUP_TEXT_LIMITS.presentation) ?? attrText(a, SUPPLEMENT_KEYS.presentation, MOCKUP_TEXT_LIMITS.presentation),
|
|
149
|
+
servings: servings !== null && Number.isInteger(servings) ? servings : null,
|
|
150
|
+
shape: supplementShape(product)
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function genericMockupData(product) {
|
|
154
|
+
return {
|
|
155
|
+
kind: "generic",
|
|
156
|
+
...common(product),
|
|
157
|
+
monogram: mockupMonogram(product.title),
|
|
158
|
+
category: cleanMockupText(product.category?.title ?? product.product_type, MOCKUP_TEXT_LIMITS.category)
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function mockupDataFromProduct(kind, product, variant) {
|
|
162
|
+
switch (kind) {
|
|
163
|
+
case "tire":
|
|
164
|
+
return tireMockupData(product);
|
|
165
|
+
case "part":
|
|
166
|
+
return partMockupData(product);
|
|
167
|
+
case "supplement":
|
|
168
|
+
return supplementMockupData(product, variant);
|
|
169
|
+
default:
|
|
170
|
+
return genericMockupData(product);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
var MOCKUP_KIND_ATTRIBUTES = ["mockup_kind", "mockup"];
|
|
174
|
+
var DEFAULT_MOCKUP_KIND_RULES = [
|
|
175
|
+
{ kind: "tire", attributes: ["size_key", "section_width_mm", "rim_diameter_in", "aspect_ratio", "speed_rating", "load_index"] },
|
|
176
|
+
{ kind: "part", attributes: ["part_number", "oem_number", "mpn"] },
|
|
177
|
+
{ kind: "tire", text: /\b(llantas?|neum[aá]ticos?|tires?|tyres?)\b/i },
|
|
178
|
+
{ kind: "part", text: /\b(refacci[oó]n(es)?|autopartes?|repuestos?|spare[ -]?parts?|auto[ -]?parts?)\b/i },
|
|
179
|
+
{
|
|
180
|
+
kind: "supplement",
|
|
181
|
+
text: /\b(suplementos?|supplements?|prote[ií]nas?|whey|creatina|creatine|amino[aá]cidos?|bcaa|pre-?entrenos?|pre-?workout|col[aá]geno|vitaminas?|multivitam[ií]nicos?|omega[ -]?3|electrolitos?|termog[eé]nicos?|ganadores? de masa)\b/i
|
|
182
|
+
}
|
|
183
|
+
];
|
|
184
|
+
function hasAttr(attrs, keys) {
|
|
185
|
+
if (!attrs) return false;
|
|
186
|
+
return keys.some((k) => {
|
|
187
|
+
const v = attrs[k];
|
|
188
|
+
return v !== void 0 && v !== null && v !== false && !(typeof v === "string" && !v.trim());
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
function resolveMockupKind(product, options = {}) {
|
|
192
|
+
const explicit = attr(product.attributes, MOCKUP_KIND_ATTRIBUTES);
|
|
193
|
+
if (typeof explicit === "string") {
|
|
194
|
+
const k = explicit.trim().toLowerCase();
|
|
195
|
+
if (/^[a-z][a-z0-9_-]{0,31}$/.test(k) && (!options.kinds || options.kinds.includes(k))) return k;
|
|
196
|
+
}
|
|
197
|
+
const haystack = [product.product_type, product.category?.title, product.category?.handle?.replace(/-/g, " "), ...product.tags ?? []].filter((s) => typeof s === "string" && s.length > 0).join(" \xB7 ");
|
|
198
|
+
for (const rule of options.rules ?? DEFAULT_MOCKUP_KIND_RULES) {
|
|
199
|
+
if (rule.attributes && hasAttr(product.attributes, rule.attributes)) return rule.kind;
|
|
200
|
+
if (rule.text && haystack && rule.text.test(haystack)) return rule.kind;
|
|
201
|
+
}
|
|
202
|
+
return options.fallback ?? "generic";
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
exports.BUILTIN_MOCKUP_KINDS = BUILTIN_MOCKUP_KINDS;
|
|
206
|
+
exports.DEFAULT_MOCKUP_KIND_RULES = DEFAULT_MOCKUP_KIND_RULES;
|
|
207
|
+
exports.MOCKUP_KIND_ATTRIBUTES = MOCKUP_KIND_ATTRIBUTES;
|
|
208
|
+
exports.MOCKUP_TEXT_LIMITS = MOCKUP_TEXT_LIMITS;
|
|
209
|
+
exports.cleanMockupText = cleanMockupText;
|
|
210
|
+
exports.genericMockupData = genericMockupData;
|
|
211
|
+
exports.mockupDataFromProduct = mockupDataFromProduct;
|
|
212
|
+
exports.mockupMonogram = mockupMonogram;
|
|
213
|
+
exports.partMockupData = partMockupData;
|
|
214
|
+
exports.resolveMockupKind = resolveMockupKind;
|
|
215
|
+
exports.supplementMockupData = supplementMockupData;
|
|
216
|
+
exports.tireMockupData = tireMockupData;
|
|
217
|
+
//# sourceMappingURL=mockup.cjs.map
|
|
218
|
+
//# sourceMappingURL=mockup.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mockup.ts"],"names":[],"mappings":"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","file":"mockup.cjs","sourcesContent":["/**\n * Fallback product mockups: the pure, framework-agnostic half.\n *\n * When a product from OPS has no image, a store can draw an illustration\n * (tire, part, supplement or a generic box) from the product's own data. This\n * module decides WHICH illustration (`resolveMockupKind`) and WHAT it may show\n * (`mockupDataFromProduct`). Rendering lives in `@asteby/storefront-react/mockups`.\n *\n * Rules every extractor follows:\n * - Only data that is in the product (attributes, vendor, title, options, the\n * selected variant). Nothing is guessed: a missing size stays missing.\n * - Never prices.\n * - Every string is cleaned (control characters removed, whitespace collapsed)\n * and capped by `MOCKUP_TEXT_LIMITS`. Escaping is the renderer's job (React\n * escapes text and attributes); these values are plain text, never markup.\n */\nimport type { Product, ProductAttributeValue, ProductVariant } from './types.js'\n\n// ---------------------------------------------------------------------------\n// Kinds\n// ---------------------------------------------------------------------------\n\n/** Built-in kinds. Stores can register more (any string). */\nexport type BuiltinMockupKind = 'tire' | 'part' | 'supplement' | 'generic'\n\n/** A mockup kind: a built-in one or any kind a store registers. */\nexport type MockupKind = BuiltinMockupKind | (string & {})\n\nexport const BUILTIN_MOCKUP_KINDS: readonly BuiltinMockupKind[] = ['tire', 'part', 'supplement', 'generic']\n\n/** The product fields mockups read. A full `Product` satisfies it. */\nexport type MockupProduct = Pick<Product, 'title'> &\n Partial<Pick<Product, 'handle' | 'vendor' | 'product_type' | 'category' | 'tags' | 'attributes' | 'options'>>\n\n/** The variant fields mockups read (selected flavor, presentation...). */\nexport type MockupVariant = Partial<Pick<ProductVariant, 'selected_options' | 'title'>>\n\n// ---------------------------------------------------------------------------\n// Text\n// ---------------------------------------------------------------------------\n\n/** Maximum characters per field (counted in code points, ellipsis included). */\nexport const MOCKUP_TEXT_LIMITS = {\n title: 80,\n brand: 32,\n model: 40,\n size: 24,\n service: 12,\n partNumber: 32,\n fitment: 90,\n position: 24,\n category: 40,\n flavor: 32,\n presentation: 32,\n monogram: 2,\n} as const\n\nconst CONTROL_CHARS = /[\\u0000-\\u001f\\u007f-\\u009f\\u200b-\\u200f\\u2028-\\u202e\\u2066-\\u2069\\ufeff]/g\n\n/**\n * Plain, single-line text capped at `max` code points (with `…` when cut).\n * Returns `null` for empty values, so callers can skip the line entirely.\n */\nexport function cleanMockupText(value: unknown, max: number = MOCKUP_TEXT_LIMITS.title): string | null {\n if (value === null || value === undefined || typeof value === 'boolean') return null\n if (typeof value === 'number' && !Number.isFinite(value)) return null\n const text = String(value).replace(CONTROL_CHARS, ' ').replace(/\\s+/g, ' ').trim()\n if (!text) return null\n const chars = Array.from(text)\n if (chars.length <= max) return text\n if (max <= 1) return '…'\n return chars.slice(0, max - 1).join('').trimEnd() + '…'\n}\n\n/** Up to two letters taken from the words of `text` (e.g. \"Casa Textil\" → \"CT\"). */\nexport function mockupMonogram(text: string | null | undefined): string | null {\n const words = (cleanMockupText(text, 200) ?? '').split(' ').map((w) => w.replace(/[^\\p{L}\\p{N}]/gu, '')).filter(Boolean)\n if (!words.length) return null\n const letters = words.length === 1 ? Array.from(words[0]!).slice(0, 2) : [Array.from(words[0]!)[0]!, Array.from(words[1]!)[0]!]\n return letters.join('').toLocaleUpperCase('es')\n}\n\n// ---------------------------------------------------------------------------\n// Attribute helpers\n// ---------------------------------------------------------------------------\n\ntype Attrs = Record<string, ProductAttributeValue>\n\n/** First attribute (by key, case-insensitive) that has a usable value. */\nfunction attr(attrs: Attrs | undefined, keys: readonly string[]): ProductAttributeValue | undefined {\n if (!attrs) return undefined\n const lower = new Map(Object.entries(attrs).map(([k, v]) => [k.toLowerCase(), v]))\n for (const key of keys) {\n const v = lower.get(key)\n if (v === undefined || v === null) continue\n if (typeof v === 'string' && !v.trim()) continue\n return v\n }\n return undefined\n}\n\nfunction attrText(attrs: Attrs | undefined, keys: readonly string[], max: number): string | null {\n const v = attr(attrs, keys)\n return typeof v === 'boolean' ? null : cleanMockupText(v, max)\n}\n\nfunction attrNumber(attrs: Attrs | undefined, keys: readonly string[]): number | null {\n const v = attr(attrs, keys)\n if (typeof v === 'number') return Number.isFinite(v) && v > 0 ? v : null\n if (typeof v === 'string' && /^\\s*\\d+(\\.\\d+)?\\s*$/.test(v)) return Number.parseFloat(v)\n return null\n}\n\nfunction attrFlag(attrs: Attrs | undefined, keys: readonly string[]): boolean {\n const v = attr(attrs, keys)\n return v === true || (typeof v === 'string' && /^(true|s[ií]|yes|1)$/i.test(v.trim()))\n}\n\n/** Value of an option: from the selected variant, or the product's only value. */\nfunction optionValue(product: MockupProduct, variant: MockupVariant | null | undefined, names: RegExp, max: number): string | null {\n const selected = variant?.selected_options?.find((o) => names.test(o.name.trim()))\n if (selected) return cleanMockupText(selected.value, max)\n const option = product.options?.find((o) => names.test(o.name.trim()))\n return option && option.values.length === 1 ? cleanMockupText(option.values[0], max) : null\n}\n\nconst fmtNumber = (n: number) => (Number.isInteger(n) ? String(n) : String(Number(n.toFixed(2))))\n\n// ---------------------------------------------------------------------------\n// Data per kind\n// ---------------------------------------------------------------------------\n\ntype MockupCommon = {\n /** Product title, always present (the accessible name is built from it). */\n title: string\n /** Brand (`vendor`). */\n brand: string | null\n}\n\nexport type TireMockupData = MockupCommon & {\n kind: 'tire'\n /** Size as written in OPS (`size_key`/`size`) or built from width/aspect/rim when all three exist. */\n size: string | null\n /** Split size when width, aspect and rim are all known (for the big digits). */\n width: string | null\n aspect: string | null\n rim: string | null\n /** Tread line / model (`model`, `line`, `tread`...). */\n model: string | null\n /** Service description, e.g. `91V` or `121/118S` (load index + speed rating). */\n service: string | null\n lightTruck: boolean\n runFlat: boolean\n}\n\nexport type PartMockupData = MockupCommon & {\n kind: 'part'\n partNumber: string | null\n oemNumber: string | null\n /** Compatibility as sent by OPS (`fitment`, `compatibility`...). */\n fitment: string | null\n position: string | null\n category: string | null\n}\n\nexport type SupplementShape = 'tub' | 'bottle' | 'bag'\n\nexport type SupplementMockupData = MockupCommon & {\n kind: 'supplement'\n flavor: string | null\n /** Net content / presentation, e.g. `5 lb`, `120 cápsulas`, or `servings` (number). */\n presentation: string | null\n /** Number of servings when OPS sends it as a number (the label adds the unit). */\n servings: number | null\n shape: SupplementShape\n}\n\nexport type GenericMockupData = MockupCommon & {\n kind: 'generic'\n monogram: string | null\n category: string | null\n}\n\nexport type MockupDataMap = {\n tire: TireMockupData\n part: PartMockupData\n supplement: SupplementMockupData\n generic: GenericMockupData\n}\n\n/** Data of any built-in kind. */\nexport type BuiltinMockupData = MockupDataMap[BuiltinMockupKind]\n\nfunction common(product: MockupProduct): MockupCommon {\n return {\n title: cleanMockupText(product.title, MOCKUP_TEXT_LIMITS.title) ?? '',\n brand: cleanMockupText(product.vendor, MOCKUP_TEXT_LIMITS.brand),\n }\n}\n\nconst TIRE_KEYS = {\n size: ['size_key', 'size', 'tire_size', 'medida'],\n width: ['section_width_mm', 'section_width', 'width', 'ancho'],\n aspect: ['aspect_ratio', 'aspect', 'profile', 'perfil'],\n rim: ['rim_diameter_in', 'rim_diameter', 'rim', 'rin'],\n model: ['model', 'modelo', 'tread', 'pattern', 'line', 'linea', 'línea'],\n loadService: ['load_service', 'service_description'],\n load: ['load_index', 'indice_carga', 'índice_de_carga'],\n speed: ['speed_rating', 'speed_index', 'indice_velocidad', 'índice_de_velocidad'],\n lightTruck: ['light_truck', 'lt'],\n runFlat: ['runflat', 'run_flat'],\n} as const\n\n/** Tire data from the product attributes (`size_key`, `section_width_mm`, `load_index`, `speed_rating`...). */\nexport function tireMockupData(product: MockupProduct): TireMockupData {\n const a = product.attributes\n const w = attrNumber(a, TIRE_KEYS.width)\n const ar = attrNumber(a, TIRE_KEYS.aspect)\n const r = attrNumber(a, TIRE_KEYS.rim)\n const split = w !== null && ar !== null && r !== null\n const written = attrText(a, TIRE_KEYS.size, MOCKUP_TEXT_LIMITS.size)\n const size = written ?? (split ? `${fmtNumber(w)}/${fmtNumber(ar)}R${fmtNumber(r)}` : null)\n const loadService = attrText(a, TIRE_KEYS.loadService, MOCKUP_TEXT_LIMITS.service)\n const load = attrText(a, TIRE_KEYS.load, 7)\n const speed = attrText(a, TIRE_KEYS.speed, 3)\n const service = cleanMockupText(loadService ? loadService + (speed && !loadService.endsWith(speed) ? speed : '') : (load ?? '') + (speed ?? ''), MOCKUP_TEXT_LIMITS.service)\n return {\n kind: 'tire',\n ...common(product),\n size,\n width: split ? fmtNumber(w) : null,\n aspect: split ? fmtNumber(ar) : null,\n rim: split ? fmtNumber(r) : null,\n model: attrText(a, TIRE_KEYS.model, MOCKUP_TEXT_LIMITS.model),\n service,\n lightTruck: attrFlag(a, TIRE_KEYS.lightTruck),\n runFlat: attrFlag(a, TIRE_KEYS.runFlat),\n }\n}\n\nconst PART_KEYS = {\n partNumber: ['part_number', 'mpn', 'numero_de_parte', 'número_de_parte', 'no_parte'],\n oem: ['oem_number', 'oem', 'oem_part_number'],\n fitment: ['fitment', 'fitment_summary', 'compatibility', 'compatible_with', 'compatibilidad', 'aplicaciones', 'vehicle'],\n position: ['position', 'posicion', 'posición'],\n} as const\n\n/** Part data: part number, OEM number, compatibility and position (attributes), category title. */\nexport function partMockupData(product: MockupProduct): PartMockupData {\n const a = product.attributes\n return {\n kind: 'part',\n ...common(product),\n partNumber: attrText(a, PART_KEYS.partNumber, MOCKUP_TEXT_LIMITS.partNumber),\n oemNumber: attrText(a, PART_KEYS.oem, MOCKUP_TEXT_LIMITS.partNumber),\n fitment: attrText(a, PART_KEYS.fitment, MOCKUP_TEXT_LIMITS.fitment),\n position: attrText(a, PART_KEYS.position, MOCKUP_TEXT_LIMITS.position),\n category: cleanMockupText(product.category?.title, MOCKUP_TEXT_LIMITS.category),\n }\n}\n\nconst SUPPLEMENT_KEYS = {\n flavor: ['flavor', 'flavour', 'sabor'],\n presentation: ['presentation', 'presentacion', 'presentación', 'net_content', 'contenido_neto', 'net_weight', 'peso_neto'],\n servings: ['servings', 'porciones', 'servicios'],\n form: ['container', 'envase', 'form', 'forma', 'presentation_form'],\n} as const\n\nfunction supplementShape(product: MockupProduct): SupplementShape {\n const form = String(attr(product.attributes, SUPPLEMENT_KEYS.form) ?? '').toLowerCase()\n if (/bottle|frasco|c[aá]psula|capsule|tableta|tablet|softgel|gomita|gummy|l[ií]quido|liquid/.test(form)) return 'bottle'\n if (/bag|bolsa|doypack|pouch|sobre/.test(form)) return 'bag'\n return 'tub'\n}\n\n/** Supplement data: flavor and presentation (selected variant, single-valued option or attribute). */\nexport function supplementMockupData(product: MockupProduct, variant?: MockupVariant | null): SupplementMockupData {\n const a = product.attributes\n const servings = attrNumber(a, SUPPLEMENT_KEYS.servings)\n return {\n kind: 'supplement',\n ...common(product),\n flavor: optionValue(product, variant, /^(sabor|flavou?r)$/i, MOCKUP_TEXT_LIMITS.flavor) ?? attrText(a, SUPPLEMENT_KEYS.flavor, MOCKUP_TEXT_LIMITS.flavor),\n presentation:\n optionValue(product, variant, /^(presentaci[oó]n|tama[nñ]o|size|contenido|peso)$/i, MOCKUP_TEXT_LIMITS.presentation) ??\n attrText(a, SUPPLEMENT_KEYS.presentation, MOCKUP_TEXT_LIMITS.presentation),\n servings: servings !== null && Number.isInteger(servings) ? servings : null,\n shape: supplementShape(product),\n }\n}\n\n/** Generic data: title, brand, a monogram of the title and the category. */\nexport function genericMockupData(product: MockupProduct): GenericMockupData {\n return {\n kind: 'generic',\n ...common(product),\n monogram: mockupMonogram(product.title),\n category: cleanMockupText(product.category?.title ?? product.product_type, MOCKUP_TEXT_LIMITS.category),\n }\n}\n\n/** Data for a built-in kind; unknown kinds get the generic data. */\nexport function mockupDataFromProduct<K extends BuiltinMockupKind>(kind: K, product: MockupProduct, variant?: MockupVariant | null): MockupDataMap[K]\nexport function mockupDataFromProduct(kind: MockupKind, product: MockupProduct, variant?: MockupVariant | null): BuiltinMockupData\nexport function mockupDataFromProduct(kind: MockupKind, product: MockupProduct, variant?: MockupVariant | null): BuiltinMockupData {\n switch (kind) {\n case 'tire':\n return tireMockupData(product)\n case 'part':\n return partMockupData(product)\n case 'supplement':\n return supplementMockupData(product, variant)\n default:\n return genericMockupData(product)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Kind resolution\n// ---------------------------------------------------------------------------\n\n/** Attribute a merchant can set in OPS to pick the mockup explicitly (`mockup`, `mockup_kind`). */\nexport const MOCKUP_KIND_ATTRIBUTES = ['mockup_kind', 'mockup'] as const\n\nexport type MockupKindRule = {\n kind: MockupKind\n /** Matched against `product_type`, `category.title/handle` and `tags`. */\n text?: RegExp\n /** Any of these attribute keys present (with a value) selects the kind. */\n attributes?: readonly string[]\n}\n\n/** Default rules, checked in order: attributes first (precise), then words. */\nexport const DEFAULT_MOCKUP_KIND_RULES: readonly MockupKindRule[] = [\n { kind: 'tire', attributes: ['size_key', 'section_width_mm', 'rim_diameter_in', 'aspect_ratio', 'speed_rating', 'load_index'] },\n { kind: 'part', attributes: ['part_number', 'oem_number', 'mpn'] },\n { kind: 'tire', text: /\\b(llantas?|neum[aá]ticos?|tires?|tyres?)\\b/i },\n { kind: 'part', text: /\\b(refacci[oó]n(es)?|autopartes?|repuestos?|spare[ -]?parts?|auto[ -]?parts?)\\b/i },\n {\n kind: 'supplement',\n text: /\\b(suplementos?|supplements?|prote[ií]nas?|whey|creatina|creatine|amino[aá]cidos?|bcaa|pre-?entrenos?|pre-?workout|col[aá]geno|vitaminas?|multivitam[ií]nicos?|omega[ -]?3|electrolitos?|termog[eé]nicos?|ganadores? de masa)\\b/i,\n },\n]\n\nexport type ResolveMockupKindOptions = {\n /** Rules to use instead of `DEFAULT_MOCKUP_KIND_RULES`. */\n rules?: readonly MockupKindRule[]\n /** When set, an explicit `mockup_kind` attribute is only honored if it is in this list. */\n kinds?: readonly MockupKind[]\n /** Kind when nothing matches (default `generic`). */\n fallback?: MockupKind\n}\n\nfunction hasAttr(attrs: Attrs | undefined, keys: readonly string[]): boolean {\n if (!attrs) return false\n return keys.some((k) => {\n const v = attrs[k]\n return v !== undefined && v !== null && v !== false && !(typeof v === 'string' && !v.trim())\n })\n}\n\n/**\n * Picks a mockup kind from the product: the explicit `mockup_kind` attribute,\n * then attribute rules (tire specs, part numbers), then words in the product\n * type, category and tags. Falls back to `generic`.\n */\nexport function resolveMockupKind(product: MockupProduct, options: ResolveMockupKindOptions = {}): MockupKind {\n const explicit = attr(product.attributes, MOCKUP_KIND_ATTRIBUTES)\n if (typeof explicit === 'string') {\n const k = explicit.trim().toLowerCase()\n if (/^[a-z][a-z0-9_-]{0,31}$/.test(k) && (!options.kinds || options.kinds.includes(k))) return k\n }\n const haystack = [product.product_type, product.category?.title, product.category?.handle?.replace(/-/g, ' '), ...(product.tags ?? [])]\n .filter((s): s is string => typeof s === 'string' && s.length > 0)\n .join(' · ')\n for (const rule of options.rules ?? DEFAULT_MOCKUP_KIND_RULES) {\n if (rule.attributes && hasAttr(product.attributes, rule.attributes)) return rule.kind\n if (rule.text && haystack && rule.text.test(haystack)) return rule.kind\n }\n return options.fallback ?? 'generic'\n}\n"]}
|