fresh-squeezy 0.1.11 → 0.1.13
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.es.md +1 -1
- package/README.ja.md +1 -1
- package/README.md +1 -1
- package/README.pt-BR.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/cli.js +677 -622
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +605 -578
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +264 -185
- package/dist/index.d.ts +264 -185
- package/dist/index.js +597 -578
- package/dist/index.js.map +1 -1
- package/package.json +12 -11
- package/assets/fresh-squeezy-hero.png +0 -0
- package/bin/fresh-squeezy.js +0 -2
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/core/errors.ts","../src/core/config.ts","../src/core/http.ts","../src/resources/users.ts","../src/core/mode.ts","../src/resources/stores.ts","../src/validate/rules.ts","../src/validate/probe.ts","../src/validate/connection.ts","../src/validate/store.ts","../src/resources/products.ts","../src/resources/variants.ts","../src/validate/product.ts","../src/core/equality.ts","../src/resources/webhooks.ts","../src/generated/lemonSqueezyApiTypes.ts","../src/support/manifest.ts","../src/validate/webhook.ts","../src/resources/discounts.ts","../src/validate/discount.ts","../src/resources/licenseKeys.ts","../src/validate/licenseKey.ts","../src/validate/subscriptionPlan.ts","../src/validate/doctor.ts","../src/createFreshSqueezy.ts","../src/resources/affiliates.ts","../src/resources/checkouts.ts","../src/resources/customers.ts","../src/resources/discountRedemptions.ts","../src/resources/files.ts","../src/resources/licenseKeyInstances.ts","../src/resources/orderItems.ts","../src/resources/orders.ts","../src/resources/prices.ts","../src/resources/subscriptions.ts","../src/resources/subscriptionInvoices.ts","../src/resources/subscriptionItems.ts","../src/resources/usageRecords.ts"],"sourcesContent":["export * from \"./augmentations.js\";\nexport * from \"./createFreshSqueezy.js\";\nexport * from \"./generated/lemonSqueezyApiTypes.js\";\nexport * from \"./core/equality.js\";\nexport * from \"./core/errors.js\";\nexport * from \"./core/config.js\";\nexport * from \"./core/mode.js\";\nexport * from \"./core/types.js\";\nexport * from \"./validate/rules.js\";\nexport * from \"./validate/connection.js\";\nexport * from \"./validate/product.js\";\nexport * from \"./validate/webhook.js\";\nexport * from \"./validate/doctor.js\";\nexport * from \"./resources/affiliates.js\";\nexport * from \"./resources/checkouts.js\";\nexport * from \"./resources/customers.js\";\nexport * from \"./resources/discountRedemptions.js\";\nexport * from \"./resources/files.js\";\nexport * from \"./resources/licenseKeyInstances.js\";\nexport * from \"./resources/orderItems.js\";\nexport * from \"./resources/orders.js\";\nexport * from \"./resources/prices.js\";\nexport * from \"./resources/stores.js\";\nexport * from \"./resources/products.js\";\nexport * from \"./resources/subscriptions.js\";\nexport * from \"./resources/subscriptionInvoices.js\";\nexport * from \"./resources/subscriptionItems.js\";\nexport * from \"./resources/usageRecords.js\";\nexport * from \"./resources/variants.js\";\nexport * from \"./resources/webhooks.js\";\nexport * from \"./resources/users.js\";\nexport * from \"./support/manifest.js\";\n","/**\n * Unified error type for fresh-squeezy. All HTTP and validation failures that\n * bubble up to consumers pass through this class so caller code can branch on\n * a single `instanceof` check.\n *\n * Why a class over a discriminated union: Node's `fetch` rejections interleave\n * with library errors in user stack traces. A class keeps the stack readable\n * and gives one stable prototype chain for consumer `catch` blocks.\n */\nexport class FreshSqueezyError extends Error {\n public readonly code: string;\n public readonly status?: number;\n public readonly detail?: unknown;\n\n constructor(opts: { code: string; message: string; status?: number; detail?: unknown }) {\n super(opts.message);\n this.name = \"FreshSqueezyError\";\n this.code = opts.code;\n this.status = opts.status;\n this.detail = opts.detail;\n Object.setPrototypeOf(this, FreshSqueezyError.prototype);\n }\n}\n","import type { FreshSqueezyConfig, Mode, ResolvedConfig } from \"./types.js\";\nimport { FreshSqueezyError } from \"./errors.js\";\n\n/**\n * Lemon Squeezy API root. Test and live share the same host — mode is determined\n * by which API key is used. Documented at https://docs.lemonsqueezy.com/api.\n */\nconst DEFAULT_BASE_URL = \"https://api.lemonsqueezy.com\";\n\n/**\n * Env variable names read when a field is not passed explicitly. Consuming\n * products rely on these names being stable — treat them as public API.\n */\nexport const ENV_KEYS = {\n apiKey: \"LEMON_SQUEEZY_API_KEY\",\n storeId: \"LEMON_SQUEEZY_STORE_ID\",\n mode: \"LEMON_SQUEEZY_MODE\",\n} as const;\n\n/**\n * Resolve the user-supplied config against environment variables and defaults.\n *\n * Precedence (highest → lowest): explicit argument → env var → built-in default.\n * Throws `FreshSqueezyError` only for fields that cannot be defaulted (currently\n * just `apiKey`), so callers can surface a clear setup error at construction\n * time rather than at first request.\n */\nexport function resolveConfig(input: FreshSqueezyConfig = {}): ResolvedConfig {\n const apiKey = input.apiKey ?? process.env[ENV_KEYS.apiKey];\n if (!apiKey) {\n throw new FreshSqueezyError({\n code: \"MISSING_API_KEY\",\n message: `No API key provided. Pass \\`apiKey\\` or set ${ENV_KEYS.apiKey}.`,\n });\n }\n\n const mode = normalizeMode(input.mode ?? process.env[ENV_KEYS.mode] ?? \"test\");\n const storeIdRaw = input.storeId ?? process.env[ENV_KEYS.storeId];\n\n return {\n apiKey,\n storeId: storeIdRaw == null ? undefined : String(storeIdRaw),\n mode,\n baseUrl: input.baseUrl ?? DEFAULT_BASE_URL,\n fetch: input.fetch ?? globalThis.fetch,\n };\n}\n\nfunction normalizeMode(value: string): Mode {\n if (value === \"test\" || value === \"live\") return value;\n throw new FreshSqueezyError({\n code: \"INVALID_MODE\",\n message: `Mode must be \"test\" or \"live\", got \"${value}\".`,\n });\n}\n","import { FreshSqueezyError } from \"./errors.js\";\nimport type {\n JsonApiCollection,\n JsonApiDocument,\n JsonApiResource,\n ResolvedConfig,\n} from \"./types.js\";\n\n/**\n * Options for a single HTTP request.\n *\n * `path` is a Lemon Squeezy API path starting with `/v1/...`. The `query`\n * record is serialized as URL search params with JSON:API-style bracketed\n * keys left untouched (e.g. `filter[store_id]`).\n */\nexport interface RequestOptions {\n method?: \"GET\" | \"POST\" | \"PATCH\" | \"DELETE\";\n path: string;\n query?: Record<string, string | number | undefined>;\n body?: Record<string, unknown>;\n signal?: AbortSignal;\n}\n\n/**\n * Lemon Squeezy JSON:API error object. Kept loose because the API occasionally\n * includes extra keys; the fields we surface are the stable ones.\n */\ninterface JsonApiError {\n status?: string;\n code?: string;\n title?: string;\n detail?: string;\n}\n\n/**\n * Low-level HTTP client. Callers usually go through resource/validator helpers,\n * but this is also exposed as the public escape hatch so consumers can reach\n * endpoints fresh-squeezy does not wrap yet.\n *\n * Responsibilities kept in this one place (per plan.md \"one source of truth\n * for transport\"):\n * - auth header injection\n * - query string serialization with JSON:API bracket keys preserved\n * - response parsing + error normalization\n * - surfacing HTTP status in `FreshSqueezyError`\n *\n * Retries, pagination helpers, and rate-limit handling live in separate files\n * so this layer stays small and obvious.\n */\nexport class HttpClient {\n constructor(private readonly config: ResolvedConfig) {}\n\n async request<T>(options: RequestOptions): Promise<T> {\n const url = this.buildUrl(options.path, options.query);\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.config.apiKey}`,\n Accept: \"application/vnd.api+json\",\n };\n if (options.body !== undefined) {\n headers[\"Content-Type\"] = \"application/vnd.api+json\";\n }\n\n let response: Response;\n try {\n response = await this.config.fetch(url, {\n method: options.method ?? \"GET\",\n headers,\n body: options.body === undefined ? undefined : JSON.stringify(options.body),\n signal: options.signal,\n });\n } catch (cause) {\n throw new FreshSqueezyError({\n code: \"NETWORK_ERROR\",\n message: cause instanceof Error ? cause.message : \"Network request failed\",\n detail: cause,\n });\n }\n\n const text = await response.text();\n const parsed = text.length > 0 ? safeJsonParse(text) : undefined;\n\n if (!response.ok) {\n throw toApiError(response.status, parsed);\n }\n\n return parsed as T;\n }\n\n /**\n * Fetch a single JSON:API resource and return its `data` object.\n */\n async getResource<TAttr>(path: string): Promise<JsonApiResource<TAttr>> {\n const doc = await this.request<JsonApiDocument<TAttr>>({ path });\n return doc.data;\n }\n\n /**\n * Fetch a single page of a JSON:API collection. Most callers want\n * `paginate()` instead — this method is kept for one-shot lookups where\n * the caller knows the result fits in a single page.\n */\n async getCollection<TAttr>(\n path: string,\n query?: RequestOptions[\"query\"]\n ): Promise<JsonApiResource<TAttr>[]> {\n const doc = await this.request<JsonApiCollection<TAttr>>({ path, query });\n return doc.data;\n }\n\n /**\n * Walk a JSON:API collection across every page and concatenate the\n * results. Lemon Squeezy returns 25 items per page by default; without\n * pagination, validators silently miss anything past the first page (e.g.\n * `validateWebhook` on a store with 26+ webhooks would falsely report\n * `WEBHOOK_NOT_FOUND` for any webhook on page 2).\n *\n * Stops as soon as `meta.page.lastPage` is reached. Falls back to a\n * single-page fetch if the API does not surface page metadata.\n *\n * Caller-supplied `page[number]` is honored as the starting page; we\n * advance from there. `page[size]` is left untouched so callers can tune\n * batch size (max 100 per Lemon Squeezy docs).\n */\n async paginate<TAttr>(\n path: string,\n query?: RequestOptions[\"query\"]\n ): Promise<JsonApiResource<TAttr>[]> {\n const startPage = Number(query?.[\"page[number]\"] ?? 1)\n const all: JsonApiResource<TAttr>[] = [];\n\n let pageNumber = startPage;\n while (true) {\n const pagedQuery = { ...(query ?? {}), \"page[number]\": pageNumber };\n const doc = await this.request<JsonApiCollection<TAttr>>({ path, query: pagedQuery });\n all.push(...doc.data);\n\n const lastPage = doc.meta?.page?.lastPage;\n if (lastPage === undefined || pageNumber >= lastPage) return all;\n pageNumber += 1;\n }\n }\n\n private buildUrl(path: string, query?: RequestOptions[\"query\"]): string {\n const url = new URL(path, this.config.baseUrl);\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined) continue;\n url.searchParams.append(key, String(value));\n }\n }\n return url.toString();\n }\n}\n\nfunction safeJsonParse(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction toApiError(status: number, body: unknown): FreshSqueezyError {\n const errors = extractJsonApiErrors(body);\n const first = errors[0];\n const code =\n status === 401\n ? \"UNAUTHORIZED\"\n : status === 404\n ? \"NOT_FOUND\"\n : status === 429\n ? \"RATE_LIMITED\"\n : (first?.code ?? `HTTP_${status}`);\n const message =\n first?.detail ?? first?.title ?? `Lemon Squeezy request failed with status ${status}`;\n return new FreshSqueezyError({ code, status, message, detail: body });\n}\n\nfunction extractJsonApiErrors(body: unknown): JsonApiError[] {\n if (!body || typeof body !== \"object\") return [];\n const errors = (body as { errors?: unknown }).errors;\n if (!Array.isArray(errors)) return [];\n return errors.filter(\n (entry): entry is JsonApiError => typeof entry === \"object\" && entry !== null\n );\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiDocument, JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedUserAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Subset of the Lemon Squeezy `users` resource attributes we rely on.\n * Full schema at https://docs.lemonsqueezy.com/api/users.\n */\nexport interface UserAttributes extends GeneratedUserAttributes {\n name: string;\n email: string;\n color?: string;\n avatar_url?: string | null;\n has_custom_avatar?: boolean;\n createdAt?: string;\n updatedAt?: string;\n}\n\n/**\n * Document-level metadata on `/v1/users/me`.\n *\n * `test_mode` was added to the endpoint on 2024-01-05 (per the Lemon Squeezy\n * API changelog: https://docs.lemonsqueezy.com/api/getting-started/changelog).\n * It reports whether the *key* being used is a test-mode key, independent of\n * what the caller declared. The connection validator compares this against\n * the caller's declared mode to catch the common \"prod key in staging\"\n * (or vice versa) misconfiguration.\n */\nexport interface UserMeta {\n test_mode?: boolean;\n}\n\n/**\n * Authenticated-user document with the `data` + `meta` block preserved.\n * The connection validator needs the meta flag, so we return the full\n * document here rather than just `data` as the collection helpers do.\n */\nexport type AuthenticatedUserDocument = JsonApiDocument<UserAttributes> & { meta?: UserMeta };\n\n/**\n * Fetch the user associated with the API key. Primary use is the connection\n * validator: a successful call confirms the key is valid, surfaces the\n * account identity for logs, and exposes `meta.test_mode` for mode\n * mismatch detection.\n */\nexport async function getAuthenticatedUser(\n http: HttpClient\n): Promise<AuthenticatedUserDocument> {\n return http.request<AuthenticatedUserDocument>({ path: \"/v1/users/me\" });\n}\n\n/**\n * Backwards-compatible helper if a caller only wants the resource (old\n * `getAuthenticatedUser` shape). Internal — not re-exported from the root.\n */\nexport function userResource(\n doc: AuthenticatedUserDocument\n): JsonApiResource<UserAttributes> {\n return doc.data;\n}\n","import type { HttpClient } from \"./http.js\";\nimport type { Mode } from \"./types.js\";\nimport { getAuthenticatedUser } from \"../resources/users.js\";\n\n/**\n * Map the boolean `meta.test_mode` flag from `/v1/users/me` to our `Mode` type.\n *\n * Returns `undefined` when the field is absent so older accounts and proxies\n * that don't surface the flag don't produce spurious mode mismatches. Lemon\n * Squeezy added `meta.test_mode` to the endpoint on 2024-01-05 (see API\n * changelog: https://docs.lemonsqueezy.com/api/getting-started/changelog).\n *\n * Pure function — exposed publicly so consumers can resolve a key's true mode\n * from a `/v1/users/me` document they already have, without re-running the\n * full connection validator.\n */\nexport function resolveActualMode(testMode: boolean | undefined): Mode | undefined {\n if (testMode === true) return \"test\";\n if (testMode === false) return \"live\";\n return undefined;\n}\n\n/**\n * Ask the Lemon Squeezy API which mode the configured key actually belongs to.\n *\n * Useful as a fail-fast check at app boot — e.g. refuse to start if a prod key\n * is loaded into a staging deployment — without paying for a full doctor run.\n * Returns `undefined` if the API doesn't surface `meta.test_mode` (older\n * proxies, partial responses).\n *\n * Throws `FreshSqueezyError` on auth or network failure; callers that want a\n * structured non-throwing result should use `validateConnection` instead.\n */\nexport async function fetchActualMode(http: HttpClient): Promise<Mode | undefined> {\n const doc = await getAuthenticatedUser(http);\n return resolveActualMode(doc.meta?.test_mode);\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedStoreAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Subset of store attributes fresh-squeezy reads.\n * Full schema at https://docs.lemonsqueezy.com/api/stores.\n */\nexport interface StoreAttributes extends GeneratedStoreAttributes {\n name: string;\n slug: string;\n domain?: string | null;\n url?: string | null;\n country?: string;\n currency?: string;\n plan?: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport async function getStore(\n http: HttpClient,\n storeId: string | number\n): Promise<JsonApiResource<StoreAttributes>> {\n return http.getResource<StoreAttributes>(`/v1/stores/${storeId}`);\n}\n\n/**\n * List every store reachable with the configured API key, paginated.\n * Used by `validateConnection` to populate the connection summary, and by\n * the CLI's `--all-stores` flag. Pagination matters here for accounts that\n * own a large catalogue of stores.\n */\nexport async function listStores(http: HttpClient): Promise<JsonApiResource<StoreAttributes>[]> {\n return http.paginate<StoreAttributes>(\"/v1/stores\");\n}\n","import type {\n ValidationIssue,\n ValidationResult,\n ValidationSeverity,\n ValidationTarget,\n} from \"../core/types.js\";\n\n/**\n * Stable issue codes. Consumers may switch on these in CI — do not rename\n * without a major version bump.\n */\nexport const ISSUE_CODES = {\n AUTH_FAILED: \"AUTH_FAILED\",\n MODE_MISMATCH: \"MODE_MISMATCH\",\n STORE_NOT_FOUND: \"STORE_NOT_FOUND\",\n STORE_NOT_OWNED: \"STORE_NOT_OWNED\",\n PRODUCT_NOT_FOUND: \"PRODUCT_NOT_FOUND\",\n PRODUCT_WRONG_STORE: \"PRODUCT_WRONG_STORE\",\n PRODUCT_UNPUBLISHED: \"PRODUCT_UNPUBLISHED\",\n PRODUCT_NO_BUY_URL: \"PRODUCT_NO_BUY_URL\",\n VARIANT_UNPUBLISHED: \"VARIANT_UNPUBLISHED\",\n VARIANT_MISSING: \"VARIANT_MISSING\",\n WEBHOOK_NOT_FOUND: \"WEBHOOK_NOT_FOUND\",\n WEBHOOK_EVENTS_MISSING: \"WEBHOOK_EVENTS_MISSING\",\n WEBHOOK_OPTIONAL_EVENTS: \"WEBHOOK_OPTIONAL_EVENTS\",\n DISCOUNT_NOT_FOUND: \"DISCOUNT_NOT_FOUND\",\n DISCOUNT_DRAFT: \"DISCOUNT_DRAFT\",\n DISCOUNT_EXPIRED: \"DISCOUNT_EXPIRED\",\n DISCOUNT_NOT_STARTED: \"DISCOUNT_NOT_STARTED\",\n DISCOUNT_REDEMPTIONS_EXHAUSTED: \"DISCOUNT_REDEMPTIONS_EXHAUSTED\",\n DISCOUNT_INVALID_AMOUNT: \"DISCOUNT_INVALID_AMOUNT\",\n DISCOUNT_STORE_MISMATCH: \"DISCOUNT_STORE_MISMATCH\",\n LICENSE_KEY_NOT_FOUND: \"LICENSE_KEY_NOT_FOUND\",\n LICENSE_KEY_DISABLED: \"LICENSE_KEY_DISABLED\",\n LICENSE_KEY_EXPIRED: \"LICENSE_KEY_EXPIRED\",\n LICENSE_KEY_AT_ACTIVATION_LIMIT: \"LICENSE_KEY_AT_ACTIVATION_LIMIT\",\n LICENSE_KEY_STORE_MISMATCH: \"LICENSE_KEY_STORE_MISMATCH\",\n PLAN_VARIANT_NOT_FOUND: \"PLAN_VARIANT_NOT_FOUND\",\n PLAN_NOT_SUBSCRIPTION: \"PLAN_NOT_SUBSCRIPTION\",\n PLAN_INVALID_INTERVAL: \"PLAN_INVALID_INTERVAL\",\n PLAN_FREE_PRICE: \"PLAN_FREE_PRICE\",\n PLAN_TRIAL_INCONSISTENT: \"PLAN_TRIAL_INCONSISTENT\",\n PLAN_DRAFT: \"PLAN_DRAFT\",\n PLAN_STORE_MISMATCH: \"PLAN_STORE_MISMATCH\",\n NETWORK_ERROR: \"NETWORK_ERROR\",\n UNKNOWN: \"UNKNOWN\",\n} as const;\n\n/**\n * Build a `ValidationIssue` with defaults for the common case.\n * Extracted so every validator produces consistently shaped issues.\n */\nexport function issue(\n code: string,\n severity: ValidationSeverity,\n message: string,\n extras: { suggestedFix?: string; context?: ValidationIssue[\"context\"] } = {}\n): ValidationIssue {\n const base: ValidationIssue = { code, severity, message };\n if (extras.suggestedFix !== undefined) base.suggestedFix = extras.suggestedFix;\n if (extras.context !== undefined) base.context = extras.context;\n return base;\n}\n\n/**\n * Fold an issue list into a boolean. Used by every validator so `ok` is\n * computed the same way everywhere.\n */\nexport function isOk(issues: ValidationIssue[]): boolean {\n return !issues.some((entry) => entry.severity === \"error\");\n}\n\n/**\n * Compose a `ValidationResult` with the `ok` flag derived from issues.\n */\nexport function buildResult<T>(\n name: string,\n mode: ValidationResult[\"mode\"],\n issues: ValidationIssue[],\n resource?: T,\n target?: ValidationTarget\n): ValidationResult<T> {\n const result: ValidationResult<T> = {\n name,\n ok: isOk(issues),\n mode,\n issues,\n };\n if (resource !== undefined) result.resource = resource;\n if (target !== undefined) result.target = target;\n return result;\n}\n","import { FreshSqueezyError } from \"../core/errors.js\";\nimport type { ValidationIssue } from \"../core/types.js\";\nimport { ISSUE_CODES, issue } from \"./rules.js\";\n\n/**\n * How a caller wants the error mapper to translate specific failure modes.\n * Every field is optional — omit a slot and that mode falls through to the\n * generic mappings (`NETWORK_ERROR`, `UNKNOWN`).\n */\nexport interface IssueMapping {\n /** Translate 401 / `UNAUTHORIZED` to a specific issue code (e.g. `AUTH_FAILED`). */\n unauthorized?: { code: string; message: string; suggestedFix?: string };\n /** Translate 404 to a resource-specific not-found issue. */\n notFound?: {\n code: string;\n message: string;\n suggestedFix?: string;\n context?: ValidationIssue[\"context\"];\n };\n}\n\n/**\n * Map any thrown error from a resource fetch to a `ValidationIssue`. The\n * single source of truth for \"how does this validator surface failures?\":\n *\n * - 401 → `mapping.unauthorized.code` if provided, else fall through\n * - 404 → `mapping.notFound.code` if provided, else fall through\n * - network failure → `NETWORK_ERROR`\n * - any other `FreshSqueezyError` → `UNKNOWN` (with status + code context)\n * - any other `Error` → `UNKNOWN`\n *\n * Public so validators that own a non-trivial composite call (e.g.\n * `validateConnection`, which fetches users + stores in one try-block) can\n * reuse the same mapper as `probeFetch` does for single-resource fetches.\n */\nexport function mapErrorToIssue(err: unknown, mapping: IssueMapping = {}): ValidationIssue {\n if (err instanceof FreshSqueezyError) {\n if (mapping.unauthorized && (err.status === 401 || err.code === \"UNAUTHORIZED\")) {\n return issue(mapping.unauthorized.code, \"error\", mapping.unauthorized.message, {\n ...(mapping.unauthorized.suggestedFix !== undefined\n ? { suggestedFix: mapping.unauthorized.suggestedFix }\n : {}),\n context: { status: err.status ?? null },\n });\n }\n if (mapping.notFound && err.status === 404) {\n return issue(mapping.notFound.code, \"error\", mapping.notFound.message, {\n ...(mapping.notFound.suggestedFix !== undefined\n ? { suggestedFix: mapping.notFound.suggestedFix }\n : {}),\n ...(mapping.notFound.context !== undefined ? { context: mapping.notFound.context } : {}),\n });\n }\n if (err.code === \"NETWORK_ERROR\") {\n return issue(\n ISSUE_CODES.NETWORK_ERROR,\n \"error\",\n `Could not reach Lemon Squeezy: ${err.message}`\n );\n }\n return issue(ISSUE_CODES.UNKNOWN, \"error\", err.message, {\n context: { status: err.status ?? null, code: err.code },\n });\n }\n const message = err instanceof Error ? err.message : \"Unknown error\";\n return issue(ISSUE_CODES.UNKNOWN, \"error\", message);\n}\n\n/**\n * Per-validator metadata for the resource-fetch probe.\n *\n * `notFoundCode` and `notFoundMessage` are resource-specific so each validator\n * keeps its stable `ISSUE_CODES.*_NOT_FOUND` identifier in callsite-visible\n * code. Everything else (mapping `FreshSqueezyError` shapes, falling back to\n * `UNKNOWN`, attaching status/code context) is handled here so validators\n * stop re-implementing the same try/catch skeleton.\n */\nexport interface FetchProbeOptions {\n notFoundCode: string;\n notFoundMessage: string;\n notFoundFix?: string;\n notFoundContext?: ValidationIssue[\"context\"];\n}\n\n/**\n * Outcome of a probed resource fetch. Either the resource (caller continues\n * with assertions) or one normalized issue (caller bails with `buildResult`).\n */\nexport type FetchProbeOutcome<T> =\n | { ok: true; resource: T }\n | { ok: false; issue: ValidationIssue };\n\n/**\n * Run a resource fetch with the same error mapping every single-resource\n * validator needs. Delegates to `mapErrorToIssue` so the rules live in\n * exactly one place.\n *\n * Validators keep their resource-specific assertion logic; only the I/O\n * scaffolding lives here. The deletion test: if this module disappeared every\n * validator would grow back the same try/catch skeleton.\n */\nexport async function probeFetch<T>(\n fetcher: () => Promise<T>,\n options: FetchProbeOptions\n): Promise<FetchProbeOutcome<T>> {\n try {\n return { ok: true, resource: await fetcher() };\n } catch (err) {\n return {\n ok: false,\n issue: mapErrorToIssue(err, {\n notFound: {\n code: options.notFoundCode,\n message: options.notFoundMessage,\n ...(options.notFoundFix !== undefined ? { suggestedFix: options.notFoundFix } : {}),\n ...(options.notFoundContext !== undefined ? { context: options.notFoundContext } : {}),\n },\n }),\n };\n }\n}\n\n/**\n * Inputs for a store-ownership cross-check.\n *\n * `code` and `label` are resource-specific so the issue stays informative\n * (\"Discount belongs to store …\" vs \"Product belongs to store …\"). `id` and\n * `extraContext` let validators describe which resource was inspected so\n * downstream tooling can correlate the mismatch with the request that\n * produced it.\n */\nexport interface OwnershipCheck {\n expectedStoreId: string | number;\n actualStoreId: string | number;\n /** Stable issue code (e.g. `ISSUE_CODES.DISCOUNT_STORE_MISMATCH`). */\n code: string;\n /** Resource label used in the issue message (e.g. `\"Discount\"`). */\n label: string;\n suggestedFix?: string;\n /** Extra context fields merged in alongside `expectedStoreId` / `actualStoreId`. */\n extraContext?: ValidationIssue[\"context\"];\n}\n\n/**\n * Compare an expected store ID against the value Lemon Squeezy returned on\n * the resource. Returns a `ValidationIssue` if they don't match, or `null` if\n * ownership is fine. Validators add the issue to their issue list — this\n * helper does not push for them.\n *\n * Both inputs are coerced via `String(...)` so a numeric `store_id` from JSON\n * compares correctly against a string-typed `storeId` from the CLI / env.\n */\nexport function checkStoreOwnership(check: OwnershipCheck): ValidationIssue | null {\n const expected = String(check.expectedStoreId);\n const actual = String(check.actualStoreId);\n if (expected === actual) return null;\n\n const context: ValidationIssue[\"context\"] = {\n expectedStoreId: expected,\n actualStoreId: actual,\n ...(check.extraContext ?? {}),\n };\n\n return issue(\n check.code,\n \"error\",\n `${check.label} belongs to store ${actual}, expected ${expected}.`,\n {\n ...(check.suggestedFix !== undefined ? { suggestedFix: check.suggestedFix } : {}),\n context,\n }\n );\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport { resolveActualMode } from \"../core/mode.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { getAuthenticatedUser, type UserAttributes } from \"../resources/users.js\";\nimport { listStores } from \"../resources/stores.js\";\nimport { mapErrorToIssue } from \"./probe.js\";\nimport { ISSUE_CODES, buildResult, issue } from \"./rules.js\";\n\n/**\n * Connection validator summary attached to the `resource` field. Keeps the\n * validator self-contained — consumers need not call `users/me` again.\n *\n * `actualMode` is derived from the `meta.test_mode` field Lemon Squeezy added\n * to `/v1/users/me` on 2024-01-05 (API changelog). When the caller declared\n * one mode but the key actually belongs to the other, the validator fires a\n * `MODE_MISMATCH` error — the single misconfiguration most likely to cause a\n * prod-in-staging (or vice versa) incident.\n */\nexport interface ConnectionSummary {\n user: UserAttributes;\n storeCount: number;\n storeIds: string[];\n /** The mode the API key actually belongs to (per `/v1/users/me` meta). */\n actualMode?: Mode;\n /** The mode the caller asked for at construction time. */\n declaredMode: Mode;\n}\n\n/**\n * Verify that the API key works, surface the account identity + reachable\n * stores, and cross-check declared mode vs the key's true mode.\n *\n * This is the first check every `doctor()` run performs; if it fails,\n * no downstream validator has anything useful to report.\n */\nexport async function validateConnection(\n http: HttpClient,\n mode: Mode\n): Promise<ValidationResult<ConnectionSummary>> {\n const issues: ValidationIssue[] = [];\n\n try {\n const userDoc = await getAuthenticatedUser(http);\n const stores = await listStores(http);\n\n const actualMode = resolveActualMode(userDoc.meta?.test_mode);\n const summary: ConnectionSummary = {\n user: userDoc.data.attributes,\n storeCount: stores.length,\n storeIds: stores.map((store) => store.id),\n declaredMode: mode,\n ...(actualMode ? { actualMode } : {}),\n };\n\n if (actualMode && actualMode !== mode) {\n issues.push(\n issue(\n ISSUE_CODES.MODE_MISMATCH,\n \"error\",\n `API key is a ${actualMode}-mode key but was run with --mode ${mode}.`,\n {\n suggestedFix: `Either pass --mode ${actualMode} or use a ${mode}-mode key from https://app.lemonsqueezy.com/settings/api.`,\n context: { declared: mode, actual: actualMode },\n }\n )\n );\n }\n\n if (stores.length === 0) {\n issues.push(\n issue(\n ISSUE_CODES.STORE_NOT_FOUND,\n \"warning\",\n \"API key authenticated but no stores are reachable.\",\n { suggestedFix: \"Confirm the API key belongs to an account that owns at least one store.\" }\n )\n );\n }\n\n return buildResult(\"connection\", mode, issues, summary);\n } catch (err) {\n issues.push(\n mapErrorToIssue(err, {\n unauthorized: {\n code: ISSUE_CODES.AUTH_FAILED,\n message: \"API key rejected by Lemon Squeezy.\",\n suggestedFix: \"Regenerate the key at https://app.lemonsqueezy.com/settings/api.\",\n },\n })\n );\n return buildResult(\"connection\", mode, issues);\n }\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { getStore, type StoreAttributes } from \"../resources/stores.js\";\nimport { probeFetch } from \"./probe.js\";\nimport { ISSUE_CODES, buildResult } from \"./rules.js\";\n\n/**\n * Verify a store exists and is reachable with the current API key. A 404\n * typically means the key belongs to a different account, not that the store\n * is gone — the suggested fix reflects that.\n */\nexport async function validateStore(\n http: HttpClient,\n mode: Mode,\n storeId: string | number\n): Promise<ValidationResult<StoreAttributes>> {\n const issues: ValidationIssue[] = [];\n\n const fetched = await probeFetch(() => getStore(http, storeId), {\n notFoundCode: ISSUE_CODES.STORE_NOT_FOUND,\n notFoundMessage: `Store ${storeId} not found with the current API key.`,\n notFoundFix: \"Check the store ID and confirm the API key belongs to the account that owns it.\",\n notFoundContext: { storeId: String(storeId) },\n });\n\n if (!fetched.ok) {\n issues.push(fetched.issue);\n return buildResult<StoreAttributes>(\"store\", mode, issues, undefined, {\n label: `store ${storeId}`,\n id: String(storeId),\n });\n }\n\n return buildResult(\"store\", mode, issues, fetched.resource.attributes, {\n label: fetched.resource.attributes.name,\n id: String(storeId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedProductAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Subset of product attributes we need for validation. `status` drives the\n * \"unpublished product\" check; `store_id` drives ownership checks.\n */\nexport interface ProductAttributes extends GeneratedProductAttributes {\n name: string;\n slug: string;\n description?: string | null;\n status: \"draft\" | \"published\";\n status_formatted?: string;\n store_id: number;\n buy_now_url?: string | null;\n from_price?: number | null;\n to_price?: number | null;\n created_at?: string;\n updated_at?: string;\n}\n\nexport async function getProduct(\n http: HttpClient,\n productId: string | number\n): Promise<JsonApiResource<ProductAttributes>> {\n return http.getResource<ProductAttributes>(`/v1/products/${productId}`);\n}\n\n/**\n * List every product in the store, paginated. Used by the CLI's\n * interactive product picker; without pagination only the first 25\n * products are offered.\n */\nexport async function listProducts(\n http: HttpClient,\n storeId: string | number\n): Promise<JsonApiResource<ProductAttributes>[]> {\n return http.paginate<ProductAttributes>(\"/v1/products\", {\n \"filter[store_id]\": String(storeId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedVariantAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport interface VariantLink {\n title: string;\n url: string;\n}\n\n/**\n * Variant attributes used by the product validator to detect\n * variant/price drift from the product they belong to.\n */\nexport interface VariantAttributes extends GeneratedVariantAttributes {\n product_id: number;\n name: string;\n slug: string;\n description?: string | null;\n has_license_keys?: boolean;\n license_activation_limit?: number | null;\n is_license_limit_unlimited?: boolean;\n license_length_value?: number | null;\n license_length_unit?: \"day\" | \"week\" | \"month\" | \"year\" | null;\n is_license_length_unlimited?: boolean;\n status: \"pending\" | \"draft\" | \"published\";\n status_formatted?: string;\n is_subscription?: boolean;\n interval?: string | null;\n interval_count?: number | null;\n sort?: number;\n /**\n * Hosted-checkout-style links keyed by purpose. Added 2024-06-09 — older\n * SDK and validator versions don't surface this field, so consumers had\n * to read `meta.product_links` manually before. Optional because the\n * field is absent on older variants.\n */\n links?: VariantLink[] | Record<string, string>;\n created_at?: string;\n updated_at?: string;\n test_mode?: boolean;\n}\n\n/**\n * Extended variant attributes for subscription plan validation. In Lemon\n * Squeezy, \"subscription plans\" live as variants with `is_subscription: true`.\n * These fields are only present on subscription variants and are checked by\n * the subscription plan validator to catch misconfigured trial periods,\n * zero-price plans, and invalid billing intervals.\n */\nexport interface SubscriptionVariantAttributes extends VariantAttributes {\n is_subscription: boolean;\n interval: string | null;\n interval_count: number | null;\n has_free_trial: boolean;\n trial_interval: string | null;\n trial_interval_count: number | null;\n price: number;\n}\n\n/**\n * Fetch a single variant by ID. Used by the subscription plan validator to\n * inspect subscription-specific fields (interval, trial, price).\n */\nexport async function getVariant<TAttr = VariantAttributes>(\n http: HttpClient,\n variantId: string | number\n): Promise<JsonApiResource<TAttr>> {\n return http.getResource<TAttr>(`/v1/variants/${variantId}`);\n}\n\n/**\n * List every variant of the product, paginated so products with more than\n * one page of variants don't trip the `VARIANT_UNPUBLISHED` heuristic when\n * the only published variants happen to live past page 1.\n */\nexport async function listVariantsForProduct(\n http: HttpClient,\n productId: string | number\n): Promise<JsonApiResource<VariantAttributes>[]> {\n return http.paginate<VariantAttributes>(\"/v1/variants\", {\n \"filter[product_id]\": String(productId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { getProduct, type ProductAttributes } from \"../resources/products.js\";\nimport { listVariantsForProduct } from \"../resources/variants.js\";\nimport { checkStoreOwnership, probeFetch } from \"./probe.js\";\nimport { ISSUE_CODES, buildResult, issue } from \"./rules.js\";\n\nexport interface ProductValidationOptions {\n productId: string | number;\n /** Optional: confirm the product belongs to this store. */\n expectedStoreId?: string | number;\n}\n\n/**\n * Validate a product's publish state, store ownership, and that it has at\n * least one published variant. Surfaces the most common misconfigurations\n * caught in the wild (unpublished product, wrong store, missing variants).\n */\nexport async function validateProduct(\n http: HttpClient,\n mode: Mode,\n options: ProductValidationOptions\n): Promise<ValidationResult<ProductAttributes>> {\n const issues: ValidationIssue[] = [];\n\n const fetched = await probeFetch(() => getProduct(http, options.productId), {\n notFoundCode: ISSUE_CODES.PRODUCT_NOT_FOUND,\n notFoundMessage: `Product ${options.productId} not found.`,\n notFoundFix: \"Verify the product ID in the Lemon Squeezy dashboard.\",\n notFoundContext: { productId: String(options.productId) },\n });\n\n if (!fetched.ok) {\n issues.push(fetched.issue);\n return buildResult<ProductAttributes>(\"product\", mode, issues, undefined, {\n label: `product ${options.productId}`,\n id: String(options.productId),\n });\n }\n\n const attrs = fetched.resource.attributes;\n\n if (options.expectedStoreId !== undefined) {\n const mismatch = checkStoreOwnership({\n expectedStoreId: options.expectedStoreId,\n actualStoreId: attrs.store_id,\n code: ISSUE_CODES.PRODUCT_WRONG_STORE,\n label: \"Product\",\n suggestedFix:\n \"Either use the correct store ID or the correct product ID — IDs should not cross stores.\",\n });\n if (mismatch) issues.push(mismatch);\n }\n\n if (attrs.status !== \"published\") {\n issues.push(\n issue(\n ISSUE_CODES.PRODUCT_UNPUBLISHED,\n \"error\",\n `Product is in \"${attrs.status}\" state, not \"published\".`,\n {\n suggestedFix: \"Publish the product in the Lemon Squeezy dashboard before selling.\",\n context: { status: attrs.status },\n }\n )\n );\n }\n\n if (!attrs.buy_now_url) {\n issues.push(\n issue(\n ISSUE_CODES.PRODUCT_NO_BUY_URL,\n \"warning\",\n \"Product has no buy-now URL. Hosted checkout may be disabled.\",\n { suggestedFix: \"Enable buy-now in product settings, or use a custom checkout flow.\" }\n )\n );\n }\n\n try {\n const variants = await listVariantsForProduct(http, options.productId);\n if (variants.length === 0) {\n issues.push(\n issue(\n ISSUE_CODES.VARIANT_MISSING,\n \"error\",\n \"Product has no variants. Customers cannot purchase it.\",\n { suggestedFix: \"Add at least one variant in the product configuration.\" }\n )\n );\n } else if (!variants.some((variant) => variant.attributes.status === \"published\")) {\n issues.push(\n issue(\n ISSUE_CODES.VARIANT_UNPUBLISHED,\n \"error\",\n \"Product has variants but none are published.\",\n { suggestedFix: \"Publish at least one variant.\" }\n )\n );\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Unknown error fetching variants\";\n issues.push(issue(ISSUE_CODES.UNKNOWN, \"warning\", message));\n }\n\n return buildResult(\"product\", mode, issues, attrs, {\n label: attrs.name,\n id: String(options.productId),\n });\n}\n","/**\n * Domain-equality primitives for fresh-squeezy.\n *\n * These helpers encode \"when are two values the same in our domain?\" — separate\n * from the validators that consume them so the rules are testable on their own\n * and reusable from any future surface (init, dedupe, repair, etc.).\n */\n\n/**\n * Compare two webhook URLs ignoring trailing slashes and casing.\n *\n * Lemon Squeezy strips trailing slashes when persisting a webhook, but users\n * often paste the trailing-slash form into their config. The webhook validator\n * relies on this rule to match a configured URL against the registered list.\n */\nexport function sameWebhookUrl(a: string, b: string): boolean {\n return normalizeWebhookUrl(a) === normalizeWebhookUrl(b);\n}\n\nfunction normalizeWebhookUrl(raw: string): string {\n return raw.replace(/\\/+$/, \"\").toLowerCase();\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedWebhookAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Subset of webhook attributes we read. `events` is an ordered list of\n * subscribed event names; the validator cross-references these against the\n * support manifest to catch missing subscriptions.\n */\nexport interface WebhookAttributes extends GeneratedWebhookAttributes {\n store_id: number;\n url: string;\n events: string[];\n last_sent_at?: string | null;\n created_at?: string;\n updated_at?: string;\n test_mode?: boolean;\n}\n\n/**\n * List every webhook registered on the store, walking pagination so a store\n * with more than one page of webhooks doesn't produce a false\n * `WEBHOOK_NOT_FOUND` from `validateWebhook`.\n */\nexport async function listWebhooksForStore(\n http: HttpClient,\n storeId: string | number\n): Promise<JsonApiResource<WebhookAttributes>[]> {\n return http.paginate<WebhookAttributes>(\"/v1/webhooks\", {\n \"filter[store_id]\": String(storeId),\n });\n}\n","/* eslint-disable */\n/**\n * AUTO-GENERATED by scripts/generate-api-types.mjs.\n * Do not edit this file directly; run `npm run generate:api-types`.\n *\n * Source index: https://docs.lemonsqueezy.com/api/getting-started/requests\n */\n\nexport const GENERATED_LEMON_SQUEEZY_RESOURCES = [\n \"users\",\n \"stores\",\n \"customers\",\n \"products\",\n \"variants\",\n \"prices\",\n \"files\",\n \"orders\",\n \"order-items\",\n \"subscriptions\",\n \"subscription-invoices\",\n \"subscription-items\",\n \"usage-records\",\n \"discounts\",\n \"discount-redemptions\",\n \"license-keys\",\n \"license-key-instances\",\n \"checkouts\",\n \"webhooks\",\n \"affiliates\",\n] as const;\n\nexport const GENERATED_LEMON_SQUEEZY_TYPE_SOURCE = {\n user: \"https://docs.lemonsqueezy.com/api/users/the-user-object\",\n store: \"https://docs.lemonsqueezy.com/api/stores/the-store-object\",\n customer: \"https://docs.lemonsqueezy.com/api/customers/the-customer-object\",\n product: \"https://docs.lemonsqueezy.com/api/products/the-product-object\",\n variant: \"https://docs.lemonsqueezy.com/api/variants/the-variant-object\",\n price: \"https://docs.lemonsqueezy.com/api/prices/the-price-object\",\n file: \"https://docs.lemonsqueezy.com/api/files/the-file-object\",\n order: \"https://docs.lemonsqueezy.com/api/orders/the-order-object\",\n orderItem: \"https://docs.lemonsqueezy.com/api/order-items/the-order-item-object\",\n subscription: \"https://docs.lemonsqueezy.com/api/subscriptions/the-subscription-object\",\n subscriptionInvoice: \"https://docs.lemonsqueezy.com/api/subscription-invoices/the-subscription-invoice-object\",\n subscriptionItem: \"https://docs.lemonsqueezy.com/api/subscription-items/the-subscription-item-object\",\n usageRecord: \"https://docs.lemonsqueezy.com/api/usage-records/the-usage-record-object\",\n discount: \"https://docs.lemonsqueezy.com/api/discounts/the-discount-object\",\n discountRedemption: \"https://docs.lemonsqueezy.com/api/discount-redemptions/the-discount-redemption-object\",\n licenseKey: \"https://docs.lemonsqueezy.com/api/license-keys/the-license-key-object\",\n licenseKeyInstance: \"https://docs.lemonsqueezy.com/api/license-key-instances/the-license-key-instance-object\",\n checkout: \"https://docs.lemonsqueezy.com/api/checkouts/the-checkout-object\",\n webhook: \"https://docs.lemonsqueezy.com/api/webhooks/the-webhook-object\",\n affiliate: \"https://docs.lemonsqueezy.com/api/affiliates/the-affiliate-object\",\n} as const;\n\nexport interface GeneratedUserAttributes {\n avatar_url?: string | null;\n color?: string | null;\n createdAt?: string | null;\n email?: string | null;\n has_custom_avatar?: boolean | null;\n name?: string | null;\n updatedAt?: string | null;\n}\n\nexport interface GeneratedStoreAttributes {\n avatar_url?: string | null;\n country?: string | null;\n country_nicename?: string | null;\n created_at?: string | null;\n currency?: string | null;\n domain?: string | null;\n name?: string | null;\n plan?: string | null;\n slug?: string | null;\n thirty_day_revenue?: number | null;\n thirty_day_sales?: number | null;\n total_revenue?: number | null;\n total_sales?: number | null;\n updated_at?: string | null;\n url?: string | null;\n}\n\nexport interface GeneratedCustomerAttributes {\n city?: string | null;\n country?: string | null;\n country_formatted?: string | null;\n created_at?: string | null;\n email?: string | null;\n mrr?: number | null;\n mrr_formatted?: string | null;\n name?: string | null;\n region?: string | null;\n status?: string | null;\n status_formatted?: string | null;\n store_id?: number | null;\n test_mode?: boolean | null;\n total_revenue_currency?: number | null;\n total_revenue_currency_formatted?: string | null;\n updated_at?: string | null;\n urls?: { customer_portal?: string | null; } | null;\n}\n\nexport interface GeneratedProductAttributes {\n buy_now_url?: string | null;\n created_at?: string | null;\n description?: string | null;\n from_price?: number | null;\n from_price_formatted?: number | null;\n large_thumb_url?: string | null;\n name?: string | null;\n pay_what_you_want?: boolean | null;\n price?: number | null;\n price_formatted?: string | null;\n slug?: string | null;\n status?: string | null;\n status_formatted?: string | null;\n store_id?: number | null;\n test_mode?: boolean | null;\n thumb_url?: string | null;\n to_price?: number | null;\n to_price_formatted?: number | null;\n updated_at?: string | null;\n}\n\nexport interface GeneratedVariantAttributes {\n created_at?: string | null;\n description?: string | null;\n has_free_trial?: boolean | null;\n has_license_keys?: boolean | null;\n interval?: string | null;\n interval_count?: number | null;\n is_license_length_unlimited?: boolean | null;\n is_license_limit_unlimited?: boolean | null;\n is_subscription?: boolean | null;\n license_activation_limit?: number | null;\n license_length_unit?: string | null;\n license_length_value?: number | null;\n min_price?: number | null;\n name?: string | null;\n pay_what_you_want?: boolean | null;\n price?: number | null;\n product_id?: number | null;\n slug?: string | null;\n sort?: number | null;\n status?: string | null;\n status_formatted?: string | null;\n suggested_price?: number | null;\n test_mode?: boolean | null;\n trial_interval?: string | null;\n trial_interval_count?: number | null;\n updated_at?: string | null;\n}\n\nexport interface GeneratedPriceAttributes {\n category?: string | null;\n created_at?: string | null;\n min_price?: number | null;\n package_size?: number | null;\n renewal_interval_quantity?: number | null;\n renewal_interval_unit?: string | null;\n scheme?: string | null;\n setup_fee?: number | null;\n setup_fee_enabled?: boolean | null;\n suggested_price?: number | null;\n tax_code?: string | null;\n tiers?: unknown;\n trial_interval_quantity?: number | null;\n trial_interval_unit?: string | null;\n unit_price?: number | null;\n unit_price_decimal?: string | number | null;\n updated_at?: string | null;\n usage_aggregation?: unknown | null;\n variant_id?: number | null;\n}\n\nexport interface GeneratedFileAttributes {\n createdAt?: string | null;\n download_url?: string | null;\n extension?: string | null;\n identifier?: string | null;\n name?: string | null;\n size?: number | null;\n size_formatted?: string | null;\n sort?: number | null;\n status?: string | null;\n test_mode?: boolean | null;\n updatedAt?: string | null;\n variant_id?: number | null;\n version?: string | null;\n}\n\nexport interface GeneratedOrderAttributes {\n created_at?: string | null;\n currency?: string | null;\n currency_rate?: string | number | null;\n customer_id?: number | null;\n discount_total?: number | null;\n discount_total_formatted?: string | null;\n discount_total_usd?: number | null;\n first_order_item?: { created_at?: string | null; id?: number | null; order_id?: number | null; price?: number | null; product_id?: number | null; product_name?: string | null; test_mode?: boolean | null; updated_at?: string | null; variant_id?: number | null; variant_name?: string | null; } | null;\n identifier?: string | null;\n order_number?: number | null;\n refunded?: boolean | null;\n refunded_at?: string | null;\n setup_fee?: number | null;\n setup_fee_formatted?: string | null;\n setup_fee_usd?: number | null;\n status?: string | null;\n status_formatted?: string | null;\n store_id?: number | null;\n subtotal?: number | null;\n subtotal_formatted?: string | null;\n subtotal_usd?: number | null;\n tax?: number | null;\n tax_formatted?: string | null;\n tax_inclusive?: boolean | null;\n tax_name?: string | null;\n tax_rate?: string | number | null;\n tax_usd?: number | null;\n test_mode?: boolean | null;\n total?: number | null;\n total_formatted?: string | null;\n total_usd?: number | null;\n updated_at?: string | null;\n urls?: { receipt?: string | null; } | null;\n user_email?: string | null;\n user_name?: string | null;\n}\n\nexport interface GeneratedOrderItemAttributes {\n created_at?: string | null;\n order_id?: number | null;\n price?: number | null;\n product_id?: number | null;\n product_name?: string | null;\n quantity?: number | null;\n updated_at?: string | null;\n variant_id?: number | null;\n variant_name?: string | null;\n}\n\nexport interface GeneratedSubscriptionAttributes {\n billing_anchor?: number | null;\n cancelled?: boolean | null;\n card_brand?: string | null;\n card_last_four?: string | null;\n created_at?: string | null;\n customer_id?: number | null;\n ends_at?: string | null;\n first_subscription_item?: { created_at?: string | null; id?: number | null; price_id?: number | null; quantity?: number | null; subscription_id?: number | null; updated_at?: string | null; } | null;\n order_id?: number | null;\n order_item_id?: number | null;\n pause?: unknown | null;\n payment_processor?: string | null;\n product_id?: number | null;\n product_name?: string | null;\n renews_at?: string | null;\n status?: string | null;\n status_formatted?: string | null;\n store_id?: number | null;\n test_mode?: boolean | null;\n trial_ends_at?: string | null;\n updated_at?: string | null;\n urls?: { customer_portal?: string | null; customer_portal_update_subscription?: string | null; update_payment_method?: string | null; } | null;\n user_email?: string | null;\n user_name?: string | null;\n variant_id?: number | null;\n variant_name?: string | null;\n}\n\nexport interface GeneratedSubscriptionInvoiceAttributes {\n billing_reason?: string | null;\n card_brand?: string | null;\n card_last_four?: string | null;\n created_at?: string | null;\n currency?: string | null;\n currency_rate?: string | number | null;\n customer_id?: number | null;\n discount_total?: number | null;\n discount_total_formatted?: string | null;\n discount_total_usd?: number | null;\n refunded?: boolean | null;\n refunded_amount?: boolean | number | null;\n refunded_amount_formatted?: string | null;\n refunded_amount_usd?: boolean | number | null;\n refunded_at?: string | null;\n status?: string | null;\n status_formatted?: string | null;\n store_id?: number | null;\n subscription_id?: number | null;\n subtotal?: number | null;\n subtotal_formatted?: string | null;\n subtotal_usd?: number | null;\n tax?: number | null;\n tax_formatted?: string | null;\n tax_inclusive?: boolean | null;\n tax_usd?: number | null;\n test_mode?: boolean | null;\n total?: number | null;\n total_formatted?: string | null;\n total_usd?: number | null;\n updated_at?: string | null;\n urls?: { invoice_url?: string | null; } | null;\n user_email?: string | null;\n user_name?: string | null;\n}\n\nexport interface GeneratedSubscriptionItemAttributes {\n created_at?: string | null;\n is_usage_based?: boolean | null;\n price_id?: number | null;\n quantity?: number | null;\n subscription_id?: number | null;\n updated_at?: string | null;\n}\n\nexport interface GeneratedUsageRecordAttributes {\n action?: string | null;\n created_at?: string | null;\n quantity?: number | null;\n subscription_item_id?: number | null;\n updated_at?: string | null;\n}\n\nexport interface GeneratedDiscountAttributes {\n amount?: number | null;\n amount_type?: string | null;\n code?: string | null;\n created_at?: string | null;\n duration?: string | null;\n duration_in_months?: number | null;\n expires_at?: string | null;\n is_limited_redemptions?: boolean | null;\n is_limited_to_products?: boolean | null;\n max_redemptions?: number | null;\n name?: string | null;\n starts_at?: string | null;\n status?: string | null;\n status_formatted?: string | null;\n store_id?: number | null;\n test_mode?: boolean | null;\n updated_at?: string | null;\n}\n\nexport interface GeneratedDiscountRedemptionAttributes {\n amount?: number | null;\n created_at?: string | null;\n discount_amount?: number | null;\n discount_amount_type?: string | null;\n discount_code?: string | null;\n discount_id?: number | null;\n discount_name?: string | null;\n order_id?: number | null;\n updated_at?: string | null;\n}\n\nexport interface GeneratedLicenseKeyAttributes {\n activation_limit?: number | null;\n created_at?: string | null;\n customer_id?: number | null;\n disabled?: boolean | number | null;\n expires_at?: string | null;\n instances_count?: number | null;\n key?: string | null;\n key_short?: string | null;\n order_id?: number | null;\n order_item_id?: number | null;\n product_id?: number | null;\n status?: string | null;\n status_formatted?: string | null;\n store_id?: number | null;\n updated_at?: string | null;\n user_email?: string | null;\n user_name?: string | null;\n}\n\nexport interface GeneratedLicenseKeyInstanceAttributes {\n created_at?: string | null;\n identifier?: string | null;\n license_key_id?: number | null;\n name?: string | null;\n updated_at?: string | null;\n}\n\nexport interface GeneratedCheckoutAttributes {\n checkout_data?: { billing_address?: unknown; custom?: unknown; discount_code?: string | null; email?: string | null; name?: string | null; tax_number?: string | null; variant_quantities?: unknown; } | null;\n checkout_options?: { button_color?: string | null; desc?: boolean | null; discount?: boolean | null; embed?: boolean | null; logo?: boolean | null; media?: boolean | null; skip_trial?: boolean | null; subscription_preview?: boolean | null; } | null;\n created_at?: string | null;\n custom_price?: number | null;\n expires_at?: string | null;\n product_options?: { description?: string | null; enabled_variants?: unknown; media?: unknown; name?: string | null; receipt_button_text?: string | null; receipt_link_url?: string | null; receipt_thank_you_note?: string | null; redirect_url?: string | null; } | null;\n store_id?: number | null;\n test_mode?: boolean | null;\n updated_at?: string | null;\n url?: string | null;\n variant_id?: number | null;\n}\n\nexport interface GeneratedWebhookAttributes {\n created_at?: string | null;\n events?: Array<string | null>;\n last_sent_at?: string | null;\n store_id?: number | null;\n test_mode?: boolean | null;\n updated_at?: string | null;\n url?: string | null;\n}\n\nexport interface GeneratedAffiliateAttributes {\n application_note?: string | null;\n created_at?: string | null;\n products?: unknown | null;\n share_domain?: string | null;\n status?: string | null;\n store_id?: number | null;\n total_earnings?: number | null;\n unpaid_earnings?: number | null;\n updated_at?: string | null;\n user_email?: string | null;\n user_id?: number | null;\n user_name?: string | null;\n}\n\nexport interface GeneratedLemonSqueezyFieldMap {\n user: GeneratedUserAttributes;\n store: GeneratedStoreAttributes;\n customer: GeneratedCustomerAttributes;\n product: GeneratedProductAttributes;\n variant: GeneratedVariantAttributes;\n price: GeneratedPriceAttributes;\n file: GeneratedFileAttributes;\n order: GeneratedOrderAttributes;\n orderItem: GeneratedOrderItemAttributes;\n subscription: GeneratedSubscriptionAttributes;\n subscriptionInvoice: GeneratedSubscriptionInvoiceAttributes;\n subscriptionItem: GeneratedSubscriptionItemAttributes;\n usageRecord: GeneratedUsageRecordAttributes;\n discount: GeneratedDiscountAttributes;\n discountRedemption: GeneratedDiscountRedemptionAttributes;\n licenseKey: GeneratedLicenseKeyAttributes;\n licenseKeyInstance: GeneratedLicenseKeyInstanceAttributes;\n checkout: GeneratedCheckoutAttributes;\n webhook: GeneratedWebhookAttributes;\n affiliate: GeneratedAffiliateAttributes;\n}\n\nexport type GeneratedLemonSqueezyResourceName = keyof GeneratedLemonSqueezyFieldMap;\nexport type GeneratedLemonSqueezyResourceType = (typeof GENERATED_LEMON_SQUEEZY_RESOURCES)[number];\n\n","import { GENERATED_LEMON_SQUEEZY_RESOURCES } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Support manifest: the locally reviewed source of truth for what\n * fresh-squeezy explicitly understands on the Lemon Squeezy platform.\n *\n * Resource/type coverage is generated from Lemon Squeezy object docs with\n * `npm run generate:api-types`. Validator policy remains reviewed here:\n * when the platform adds new webhook events or behavior, bump the entries\n * below and re-snapshot the changelog page with\n * `npm run check:changelog -- --update`.\n *\n * Changelog source: https://docs.lemonsqueezy.com/api/getting-started/changelog\n * Last reviewed: 2026-05-07\n */\n\n/** Resources fresh-squeezy understands today, generated from object docs. */\nexport const SUPPORTED_RESOURCES = GENERATED_LEMON_SQUEEZY_RESOURCES;\n\n/**\n * Webhook events fresh-squeezy expects a production integration to subscribe to\n * at minimum. Consumers can still subscribe to more; the validator only flags\n * missing ones from this list.\n *\n * Rationale:\n * - `order_*` covers one-off purchases and refunds.\n * - `subscription_*` covers the recurring-billing lifecycle.\n * - `subscription_payment_*` covers dunning / retry loops.\n *\n * Confirmed present in the Lemon Squeezy webhook topic list as of 2026-04-24.\n */\nexport const RECOMMENDED_WEBHOOK_EVENTS = [\n \"order_created\",\n \"order_refunded\",\n \"subscription_created\",\n \"subscription_updated\",\n \"subscription_cancelled\",\n \"subscription_resumed\",\n \"subscription_expired\",\n \"subscription_payment_success\",\n \"subscription_payment_failed\",\n] as const;\n\n/**\n * Newer or integration-specific events surfaced as info-level suggestions\n * rather than errors. Missing these is common and not necessarily a\n * misconfiguration.\n *\n * Per-entry changelog provenance (source:\n * https://docs.lemonsqueezy.com/api/getting-started/changelog):\n *\n * - `customer_updated` — added 2026-02-25. Fires when a customer record\n * changes (e.g. email, marketing consent). Needed if the app mirrors\n * customer data locally.\n * - `affiliate_activated` — added 2025-01-21 alongside the affiliates\n * endpoints. Only relevant if the store has an affiliate program.\n * - `license_key_created` / `license_key_updated` — License API events.\n * Only relevant when variants have `has_license_keys: true`.\n */\nexport const OPTIONAL_WEBHOOK_EVENTS = [\n \"customer_updated\",\n \"affiliate_activated\",\n \"license_key_created\",\n \"license_key_updated\",\n] as const;\n\n/**\n * Platform additions we have read and decided *not* to validate against yet,\n * documented here so maintainers see the deliberate gap during review.\n *\n * Tracked so the drift workflow has an \"expected state\" to compare against:\n * if the changelog page changes and none of these items explain it, the diff\n * is probably something new that needs a manifest update.\n */\nexport const ACKNOWLEDGED_CHANGELOG_ENTRIES = [\n {\n date: \"2026-02-25\",\n summary: \"Added customer_updated webhook event.\",\n handledBy: \"OPTIONAL_WEBHOOK_EVENTS\",\n },\n {\n date: \"2025-06-11\",\n summary: \"Added payment_processor attribute to Subscription objects.\",\n handledBy: \"Surfaced as SubscriptionAttributes.payment_processor in src/resources/subscriptions.ts.\",\n },\n {\n date: \"2025-01-21\",\n summary: \"Added Affiliates endpoints and affiliate_activated webhook.\",\n handledBy: \"OPTIONAL_WEBHOOK_EVENTS (event only; resource stays v2 scope)\",\n },\n {\n date: \"2024-12-06\",\n summary: \"Added quantity parameter to OrderItem objects.\",\n handledBy: \"OrderItemAttributes.quantity (src/resources/orderItems.ts).\",\n },\n {\n date: \"2024-09-10\",\n summary: \"Added fraudulent Order status.\",\n handledBy: \"OrderAttributes.status union includes 'fraudulent' (src/resources/orders.ts).\",\n },\n {\n date: \"2024-09-04\",\n summary: \"Added Issue an Order refund and Issue a Subscription Invoice refund endpoints.\",\n handledBy: \"Not wrapped — reachable via client.request('/v1/orders/:id/refund'). Refund-amount fields surfaced on OrderAttributes.\",\n },\n {\n date: \"2024-08-07\",\n summary: \"Added refunded_amount, refunded_amount_usd, refunded_amount_formatted to Order and Subscription invoice objects.\",\n handledBy: \"OrderAttributes.refunded_amount*, SubscriptionInvoiceAttributes.refunded_amount*.\",\n },\n {\n date: \"2024-06-09\",\n summary: \"Added links property to Variant objects.\",\n handledBy: \"VariantAttributes.links (src/resources/variants.ts).\",\n },\n {\n date: \"2024-05-20\",\n summary: \"Added Generate order invoice endpoint.\",\n handledBy: \"Not wrapped — reachable via client.request('/v1/orders/:id/generate-invoice').\",\n },\n {\n date: \"2024-04-09\",\n summary: \"License Key expires_at is now mutable via the update endpoint.\",\n handledBy: \"Type unchanged (LicenseKeyAttributes.expires_at already nullable string). Mutation reachable via client.request('PATCH /v1/license-keys/:id').\",\n },\n {\n date: \"2024-03-28\",\n summary: \"Subscription trial_ends_at is now mutable via the update endpoint.\",\n handledBy:\n \"SubscriptionAttributes.trial_ends_at plus CheckoutOptions.skip_trial. Mutations remain reachable via client.request().\",\n },\n {\n date: \"2024-02-20\",\n summary: \"Added urls.update_customer_portal to Subscription objects.\",\n handledBy: \"SubscriptionUrls.update_customer_portal (src/resources/subscriptions.ts).\",\n },\n {\n date: \"2024-02-12\",\n summary: \"Added Generate subscription invoice endpoint and invoice_immediately / disable_prorations parameters.\",\n handledBy: \"SubscriptionItemUpdateAttributes.invoice_immediately / disable_prorations; invoice endpoint reachable via client.request().\",\n },\n {\n date: \"2024-02-05\",\n summary: \"Added tax_inclusive parameter to Order objects and Subscription invoice objects.\",\n handledBy: \"OrderAttributes.tax_inclusive, SubscriptionInvoiceAttributes.tax_inclusive.\",\n },\n {\n date: \"2024-01-21\",\n summary: \"Added setup_fee_enabled / setup_fee to Price objects; setup_fee* to Subscription invoice.\",\n handledBy: \"PriceAttributes.setup_fee*, OrderAttributes.setup_fee*.\",\n },\n {\n date: \"2024-01-15\",\n summary: \"Added unit_price_decimal to Price objects.\",\n handledBy: \"PriceAttributes.unit_price_decimal.\",\n },\n {\n date: \"2024-01-05\",\n summary: \"Added test_mode flag to /v1/users/me meta.\",\n handledBy:\n \"Read in validateConnection to emit MODE_MISMATCH when the key's true mode differs from the caller's declared mode.\",\n },\n {\n date: \"2023-09-19\",\n summary: \"Added customer_portal URLs to Subscription and Customer objects.\",\n handledBy: \"SubscriptionUrls.customer_portal and CustomerUrls.customer_portal.\",\n },\n {\n date: \"2023-08-23\",\n summary: \"Added checkout_data.variant_quantities, subscription items, prices, and usage-based billing.\",\n handledBy:\n \"CheckoutData.variant_quantities, SubscriptionItemAttributes, UsageRecordAttributes, PriceAttributes.\",\n },\n {\n date: \"2023-08-14\",\n summary: \"Added customer_id, user_name, user_email to Subscription invoice objects.\",\n handledBy: \"SubscriptionInvoiceAttributes customer fields.\",\n },\n {\n date: \"2023-03-30\",\n summary: \"Added receipt URL to Order objects.\",\n handledBy: \"OrderUrls.receipt.\",\n },\n] as const;\n\nexport type RecommendedEvent = (typeof RECOMMENDED_WEBHOOK_EVENTS)[number];\nexport type OptionalEvent = (typeof OPTIONAL_WEBHOOK_EVENTS)[number];\n","import { sameWebhookUrl } from \"../core/equality.js\";\nimport { FreshSqueezyError } from \"../core/errors.js\";\nimport type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { listWebhooksForStore, type WebhookAttributes } from \"../resources/webhooks.js\";\nimport { OPTIONAL_WEBHOOK_EVENTS, RECOMMENDED_WEBHOOK_EVENTS } from \"../support/manifest.js\";\nimport { ISSUE_CODES, buildResult, issue } from \"./rules.js\";\n\nexport interface WebhookValidationOptions {\n storeId: string | number;\n /** The public URL your app exposes for Lemon Squeezy to POST to. */\n url: string;\n}\n\n/**\n * Confirm a webhook matching `options.url` is registered against the given\n * store, and cross-reference its subscribed events against the support\n * manifest's recommended + optional lists.\n *\n * Missing recommended events = error. Missing optional events = info, because\n * not every integration needs them.\n */\nexport async function validateWebhook(\n http: HttpClient,\n mode: Mode,\n options: WebhookValidationOptions\n): Promise<ValidationResult<WebhookAttributes>> {\n const issues: ValidationIssue[] = [];\n\n let webhooks;\n try {\n webhooks = await listWebhooksForStore(http, options.storeId);\n } catch (err) {\n if (err instanceof FreshSqueezyError) {\n issues.push(\n issue(ISSUE_CODES.UNKNOWN, \"error\", err.message, {\n context: { status: err.status ?? null, code: err.code },\n })\n );\n return buildResult<WebhookAttributes>(\"webhook\", mode, issues, undefined, {\n label: options.url,\n url: options.url,\n });\n }\n const message = err instanceof Error ? err.message : \"Unknown error\";\n issues.push(issue(ISSUE_CODES.UNKNOWN, \"error\", message));\n return buildResult<WebhookAttributes>(\"webhook\", mode, issues, undefined, {\n label: options.url,\n url: options.url,\n });\n }\n\n const match = webhooks.find((webhook) => sameWebhookUrl(webhook.attributes.url, options.url));\n if (!match) {\n issues.push(\n issue(\n ISSUE_CODES.WEBHOOK_NOT_FOUND,\n \"error\",\n `No webhook registered for URL ${options.url} on store ${options.storeId}.`,\n {\n suggestedFix:\n \"Register the webhook in Lemon Squeezy (Settings → Webhooks) and subscribe to the recommended events.\",\n context: { storeId: String(options.storeId), url: options.url },\n }\n )\n );\n return buildResult<WebhookAttributes>(\"webhook\", mode, issues, undefined, {\n label: options.url,\n url: options.url,\n });\n }\n\n const subscribed = new Set(match.attributes.events);\n const missingRecommended = RECOMMENDED_WEBHOOK_EVENTS.filter((event) => !subscribed.has(event));\n const missingOptional = OPTIONAL_WEBHOOK_EVENTS.filter((event) => !subscribed.has(event));\n\n if (missingRecommended.length > 0) {\n issues.push(\n issue(\n ISSUE_CODES.WEBHOOK_EVENTS_MISSING,\n \"error\",\n `Webhook is missing recommended events: ${missingRecommended.join(\", \")}.`,\n {\n suggestedFix: \"Subscribe to all recommended events so the integration survives plan changes and refunds.\",\n context: { missing: missingRecommended.join(\",\") },\n }\n )\n );\n }\n\n if (missingOptional.length > 0) {\n issues.push(\n issue(\n ISSUE_CODES.WEBHOOK_OPTIONAL_EVENTS,\n \"info\",\n `Optional events not subscribed: ${missingOptional.join(\", \")}.`,\n { context: { missing: missingOptional.join(\",\") } }\n )\n );\n }\n\n return buildResult(\"webhook\", mode, issues, match.attributes, {\n label: match.attributes.url,\n id: match.id,\n url: match.attributes.url,\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedDiscountAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Subset of Lemon Squeezy discount attributes used by the discount validator.\n * Full schema at https://docs.lemonsqueezy.com/api/discounts.\n */\nexport interface DiscountAttributes extends GeneratedDiscountAttributes {\n name: string;\n code: string;\n amount: number;\n amount_type: \"percent\" | \"fixed\";\n is_limited_to_products: boolean;\n is_limited_redemptions: boolean;\n max_redemptions: number;\n starts_at: string | null;\n expires_at: string | null;\n status: \"draft\" | \"published\";\n duration: \"once\" | \"repeating\" | \"forever\";\n store_id: number;\n created_at?: string;\n updated_at?: string;\n}\n\n/**\n * Fetch a single discount by ID. The validator uses the discount's\n * `relationships.store` to confirm ownership against the caller's storeId.\n */\nexport async function getDiscount(\n http: HttpClient,\n discountId: string | number\n): Promise<JsonApiResource<DiscountAttributes>> {\n return http.getResource<DiscountAttributes>(`/v1/discounts/${discountId}`);\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { getDiscount, type DiscountAttributes } from \"../resources/discounts.js\";\nimport { checkStoreOwnership, probeFetch } from \"./probe.js\";\nimport { ISSUE_CODES, buildResult, issue } from \"./rules.js\";\n\nexport interface DiscountValidationOptions {\n storeId: string | number;\n discountId: string | number;\n}\n\n/**\n * Validate a Lemon Squeezy discount code. Catches the most common\n * misconfigurations: draft discounts that can't be redeemed, expired or\n * not-yet-active windows, invalid amounts (negative or >100% for percent\n * type), and store ownership mismatches.\n *\n * The redemption exhaustion check is deliberately conservative (v1): it only\n * flags when `is_limited_redemptions` is true and `max_redemptions` is zero,\n * because a full redemption count fetch against\n * `/v1/discount-redemptions?filter[discount_id]=` is YAGNI until a consumer\n * asks for it.\n */\nexport async function validateDiscount(\n http: HttpClient,\n mode: Mode,\n options: DiscountValidationOptions\n): Promise<ValidationResult<DiscountAttributes>> {\n const issues: ValidationIssue[] = [];\n\n const fetched = await probeFetch(() => getDiscount(http, options.discountId), {\n notFoundCode: ISSUE_CODES.DISCOUNT_NOT_FOUND,\n notFoundMessage: `Discount ${options.discountId} not found.`,\n notFoundFix: \"Verify the discount ID in the Lemon Squeezy dashboard.\",\n notFoundContext: { discountId: String(options.discountId) },\n });\n\n if (!fetched.ok) {\n issues.push(fetched.issue);\n return buildResult<DiscountAttributes>(\"discount\", mode, issues, undefined, {\n label: `discount ${options.discountId}`,\n id: String(options.discountId),\n });\n }\n\n const attrs = fetched.resource.attributes;\n\n const mismatch = checkStoreOwnership({\n expectedStoreId: options.storeId,\n actualStoreId: attrs.store_id,\n code: ISSUE_CODES.DISCOUNT_STORE_MISMATCH,\n label: \"Discount\",\n suggestedFix: \"Use the correct store ID or discount ID — discounts should not cross stores.\",\n });\n if (mismatch) issues.push(mismatch);\n\n if (attrs.status === \"draft\") {\n issues.push(\n issue(\n ISSUE_CODES.DISCOUNT_DRAFT,\n \"warning\",\n `Discount \"${attrs.name}\" is in draft status — customers cannot redeem it.`,\n {\n suggestedFix: \"Publish the discount in the Lemon Squeezy dashboard before sharing the code.\",\n context: { name: attrs.name, code: attrs.code },\n }\n )\n );\n }\n\n const now = new Date();\n if (attrs.expires_at && new Date(attrs.expires_at) < now) {\n issues.push(\n issue(\n ISSUE_CODES.DISCOUNT_EXPIRED,\n \"error\",\n `Discount \"${attrs.name}\" expired at ${attrs.expires_at}.`,\n {\n suggestedFix: \"Extend the expiration date or create a new discount.\",\n context: { name: attrs.name, expiresAt: attrs.expires_at },\n }\n )\n );\n }\n\n if (attrs.starts_at && new Date(attrs.starts_at) > now) {\n issues.push(\n issue(\n ISSUE_CODES.DISCOUNT_NOT_STARTED,\n \"warning\",\n `Discount \"${attrs.name}\" starts at ${attrs.starts_at} — not yet active.`,\n {\n suggestedFix: \"Wait for the start date or adjust it in the dashboard.\",\n context: { name: attrs.name, startsAt: attrs.starts_at },\n }\n )\n );\n }\n\n if (attrs.is_limited_redemptions && attrs.max_redemptions <= 0) {\n issues.push(\n issue(\n ISSUE_CODES.DISCOUNT_REDEMPTIONS_EXHAUSTED,\n \"warning\",\n `Discount \"${attrs.name}\" has limited redemptions with max_redemptions ≤ 0.`,\n {\n suggestedFix: \"Increase max_redemptions or disable the redemption limit.\",\n context: { name: attrs.name, maxRedemptions: attrs.max_redemptions },\n }\n )\n );\n }\n\n if (attrs.amount <= 0) {\n issues.push(\n issue(\n ISSUE_CODES.DISCOUNT_INVALID_AMOUNT,\n \"error\",\n `Discount \"${attrs.name}\" has amount ${attrs.amount} — must be positive.`,\n {\n suggestedFix: \"Set a positive discount amount in the dashboard.\",\n context: { name: attrs.name, amount: attrs.amount },\n }\n )\n );\n } else if (attrs.amount_type === \"percent\" && attrs.amount > 100) {\n issues.push(\n issue(\n ISSUE_CODES.DISCOUNT_INVALID_AMOUNT,\n \"error\",\n `Discount \"${attrs.name}\" is ${attrs.amount}% — percent discounts cannot exceed 100%.`,\n {\n suggestedFix: \"Set the discount to 100% or less.\",\n context: { name: attrs.name, amount: attrs.amount, amountType: attrs.amount_type },\n }\n )\n );\n }\n\n return buildResult(\"discount\", mode, issues, attrs, {\n label: `${attrs.name} (${attrs.code})`,\n id: String(options.discountId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedLicenseKeyAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Subset of Lemon Squeezy license-key attributes used by the license key\n * validator. Full schema at https://docs.lemonsqueezy.com/api/license-keys.\n */\nexport interface LicenseKeyAttributes extends GeneratedLicenseKeyAttributes {\n key_short: string;\n status: \"active\" | \"inactive\" | \"expired\" | \"disabled\";\n expires_at: string | null;\n activation_limit: number | null;\n instances_count: number;\n disabled: boolean;\n store_id: number;\n created_at?: string;\n updated_at?: string;\n}\n\n/**\n * Fetch a single license key by ID. Used by the license key validator to\n * check activation limits, expiration, and store ownership.\n */\nexport async function getLicenseKey(\n http: HttpClient,\n licenseKeyId: string | number\n): Promise<JsonApiResource<LicenseKeyAttributes>> {\n return http.getResource<LicenseKeyAttributes>(`/v1/license-keys/${licenseKeyId}`);\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { getLicenseKey, type LicenseKeyAttributes } from \"../resources/licenseKeys.js\";\nimport { checkStoreOwnership, probeFetch } from \"./probe.js\";\nimport { ISSUE_CODES, buildResult, issue } from \"./rules.js\";\n\nexport interface LicenseKeyValidationOptions {\n storeId: string | number;\n licenseKeyId: string | number;\n}\n\n/**\n * Validate a Lemon Squeezy license key. Surfaces disabled keys, expired\n * keys, keys at their activation limit, and store ownership mismatches — the\n * four states most likely to cause \"why can't my customer activate?\"\n * support tickets.\n */\nexport async function validateLicenseKey(\n http: HttpClient,\n mode: Mode,\n options: LicenseKeyValidationOptions\n): Promise<ValidationResult<LicenseKeyAttributes>> {\n const issues: ValidationIssue[] = [];\n\n const fetched = await probeFetch(() => getLicenseKey(http, options.licenseKeyId), {\n notFoundCode: ISSUE_CODES.LICENSE_KEY_NOT_FOUND,\n notFoundMessage: `License key ${options.licenseKeyId} not found.`,\n notFoundFix: \"Verify the license key ID in the Lemon Squeezy dashboard.\",\n notFoundContext: { licenseKeyId: String(options.licenseKeyId) },\n });\n\n if (!fetched.ok) {\n issues.push(fetched.issue);\n return buildResult<LicenseKeyAttributes>(\"licenseKey\", mode, issues, undefined, {\n label: `license key ${options.licenseKeyId}`,\n id: String(options.licenseKeyId),\n });\n }\n\n const attrs = fetched.resource.attributes;\n\n const mismatch = checkStoreOwnership({\n expectedStoreId: options.storeId,\n actualStoreId: attrs.store_id,\n code: ISSUE_CODES.LICENSE_KEY_STORE_MISMATCH,\n label: \"License key\",\n suggestedFix: \"Use the correct store ID or license key ID — keys should not cross stores.\",\n });\n if (mismatch) issues.push(mismatch);\n\n if (attrs.disabled) {\n issues.push(\n issue(\n ISSUE_CODES.LICENSE_KEY_DISABLED,\n \"error\",\n `License key ${attrs.key_short} is disabled.`,\n {\n suggestedFix: \"Re-enable the license key in the Lemon Squeezy dashboard.\",\n context: { keyShort: attrs.key_short },\n }\n )\n );\n }\n\n if (attrs.expires_at && new Date(attrs.expires_at) < new Date()) {\n issues.push(\n issue(\n ISSUE_CODES.LICENSE_KEY_EXPIRED,\n \"error\",\n `License key ${attrs.key_short} expired at ${attrs.expires_at}.`,\n {\n suggestedFix: \"Extend the expiration date or issue a new license key.\",\n context: { keyShort: attrs.key_short, expiresAt: attrs.expires_at },\n }\n )\n );\n }\n\n if (attrs.activation_limit !== null && attrs.instances_count >= attrs.activation_limit) {\n issues.push(\n issue(\n ISSUE_CODES.LICENSE_KEY_AT_ACTIVATION_LIMIT,\n \"warning\",\n `License key ${attrs.key_short} has reached its activation limit (${attrs.instances_count}/${attrs.activation_limit}).`,\n {\n suggestedFix: \"Increase the activation limit or deactivate unused instances.\",\n context: {\n keyShort: attrs.key_short,\n instancesCount: attrs.instances_count,\n activationLimit: attrs.activation_limit,\n },\n }\n )\n );\n }\n\n return buildResult(\"licenseKey\", mode, issues, attrs, {\n label: attrs.key_short,\n id: String(options.licenseKeyId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { getProduct } from \"../resources/products.js\";\nimport { getVariant, type SubscriptionVariantAttributes } from \"../resources/variants.js\";\nimport { checkStoreOwnership, probeFetch } from \"./probe.js\";\nimport { ISSUE_CODES, buildResult, issue } from \"./rules.js\";\n\nexport interface SubscriptionPlanValidationOptions {\n storeId: string | number;\n variantId: string | number;\n}\n\n/** Summary of a subscription plan variant's validated state. */\nexport interface SubscriptionPlanSummary {\n variantId: string;\n interval: string | null;\n intervalCount: number | null;\n price: number;\n hasFreeTrial: boolean;\n status: string;\n}\n\nconst VALID_INTERVALS = new Set([\"day\", \"week\", \"month\", \"year\"]);\n\n/**\n * Validate a Lemon Squeezy \"subscription plan\", which is a variant with\n * `is_subscription: true`. Checks that the variant is actually a subscription,\n * has a valid billing interval, and that trial settings are consistent.\n *\n * Store cross-check requires fetching the parent product to read its\n * `store_id`. This is an extra network hop but catches the common mistake of\n * passing a variant ID from one store while targeting another. Accept the\n * `storeId` as advisory — if the product fetch fails the cross-check is\n * silently skipped rather than blocking the entire validation.\n */\nexport async function validateSubscriptionPlan(\n http: HttpClient,\n mode: Mode,\n options: SubscriptionPlanValidationOptions\n): Promise<ValidationResult<SubscriptionPlanSummary>> {\n const issues: ValidationIssue[] = [];\n\n const fetched = await probeFetch(\n () => getVariant<SubscriptionVariantAttributes>(http, options.variantId),\n {\n notFoundCode: ISSUE_CODES.PLAN_VARIANT_NOT_FOUND,\n notFoundMessage: `Variant ${options.variantId} not found.`,\n notFoundFix: \"Verify the variant ID in the Lemon Squeezy dashboard.\",\n notFoundContext: { variantId: String(options.variantId) },\n }\n );\n\n if (!fetched.ok) {\n issues.push(fetched.issue);\n return buildResult<SubscriptionPlanSummary>(\"subscriptionPlan\", mode, issues, undefined, {\n label: `variant ${options.variantId}`,\n id: String(options.variantId),\n });\n }\n\n const variant = fetched.resource;\n const attrs = variant.attributes;\n\n if (!attrs.is_subscription) {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_NOT_SUBSCRIPTION,\n \"error\",\n `Variant ${options.variantId} is not a subscription variant (is_subscription is false).`,\n {\n suggestedFix: \"Use a variant that has subscription billing enabled, or use the regular variant validator.\",\n context: { variantId: String(options.variantId) },\n }\n )\n );\n }\n\n if (!attrs.interval || !VALID_INTERVALS.has(attrs.interval)) {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_INVALID_INTERVAL,\n \"error\",\n `Subscription variant has invalid interval: \"${attrs.interval ?? \"missing\"}\". Expected one of: day, week, month, year.`,\n {\n suggestedFix: \"Set a valid billing interval in the variant configuration.\",\n context: { interval: attrs.interval ?? null },\n }\n )\n );\n }\n\n if (attrs.interval_count === null || attrs.interval_count <= 0) {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_INVALID_INTERVAL,\n \"error\",\n `Subscription variant has invalid interval_count: ${attrs.interval_count}. Must be a positive integer.`,\n {\n suggestedFix: \"Set interval_count to a positive value (e.g. 1 for monthly, 2 for biweekly).\",\n context: { intervalCount: attrs.interval_count },\n }\n )\n );\n }\n\n if (attrs.price === 0 && attrs.is_subscription) {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_FREE_PRICE,\n \"warning\",\n `Subscription variant has a price of 0 — this is almost always a misconfiguration for paid plans.`,\n {\n suggestedFix: \"Set the variant price to the intended amount in cents, or confirm this is intentionally free.\",\n context: { price: attrs.price },\n }\n )\n );\n }\n\n if (attrs.has_free_trial && (!attrs.trial_interval || (attrs.trial_interval_count ?? 0) <= 0)) {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_TRIAL_INCONSISTENT,\n \"warning\",\n `Subscription variant has free trial enabled but trial interval is misconfigured (interval: \"${attrs.trial_interval ?? \"missing\"}\", count: ${attrs.trial_interval_count ?? 0}).`,\n {\n suggestedFix: \"Set a valid trial interval and count, or disable the free trial.\",\n context: {\n trialInterval: attrs.trial_interval ?? null,\n trialIntervalCount: attrs.trial_interval_count ?? null,\n },\n }\n )\n );\n }\n\n if (attrs.status === \"draft\") {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_DRAFT,\n \"warning\",\n `Subscription variant is in draft status — customers cannot subscribe.`,\n {\n suggestedFix: \"Publish the variant in the Lemon Squeezy dashboard.\",\n context: { status: attrs.status },\n }\n )\n );\n }\n\n // Store cross-check: fetch the parent product to compare store ownership.\n // If the fetch fails, skip the check rather than blocking the whole validation.\n try {\n const product = await getProduct(http, attrs.product_id);\n const mismatch = checkStoreOwnership({\n expectedStoreId: options.storeId,\n actualStoreId: product.attributes.store_id,\n code: ISSUE_CODES.PLAN_STORE_MISMATCH,\n label: `Subscription variant (via product ${attrs.product_id})`,\n suggestedFix: \"Use the correct store ID or variant ID — plans should not cross stores.\",\n extraContext: { productId: String(attrs.product_id) },\n });\n if (mismatch) issues.push(mismatch);\n } catch {\n // Intentionally silent — the product fetch is advisory for the store\n // cross-check and should not block the rest of the validation.\n }\n\n const summary: SubscriptionPlanSummary = {\n variantId: variant.id,\n interval: attrs.interval,\n intervalCount: attrs.interval_count,\n price: attrs.price,\n hasFreeTrial: attrs.has_free_trial,\n status: attrs.status,\n };\n\n return buildResult(\"subscriptionPlan\", mode, issues, summary, {\n label: attrs.name,\n id: String(options.variantId),\n });\n}\n","import { FreshSqueezyError } from \"../core/errors.js\";\nimport type { HttpClient } from \"../core/http.js\";\nimport type { DoctorReport, Mode, ValidationResult } from \"../core/types.js\";\nimport { validateConnection, type ConnectionSummary } from \"./connection.js\";\nimport { validateStore } from \"./store.js\";\nimport { validateProduct, type ProductValidationOptions } from \"./product.js\";\nimport { validateWebhook, type WebhookValidationOptions } from \"./webhook.js\";\nimport { validateDiscount, type DiscountValidationOptions } from \"./discount.js\";\nimport { validateLicenseKey, type LicenseKeyValidationOptions } from \"./licenseKey.js\";\nimport {\n validateSubscriptionPlan,\n type SubscriptionPlanValidationOptions,\n} from \"./subscriptionPlan.js\";\nimport type { StoreAttributes } from \"../resources/stores.js\";\nimport type { ProductAttributes } from \"../resources/products.js\";\nimport type { WebhookAttributes } from \"../resources/webhooks.js\";\nimport type { DiscountAttributes } from \"../resources/discounts.js\";\nimport type { LicenseKeyAttributes } from \"../resources/licenseKeys.js\";\n\n/**\n * Optional targets for the doctor run. If a target is omitted, its validator\n * is skipped — consumers only pay for what they configure.\n */\nexport interface DoctorOptions {\n storeId?: string | number;\n productId?: string | number;\n productIds?: Array<string | number>;\n webhookUrl?: string;\n webhookUrls?: string[];\n discountId?: string | number;\n discountIds?: Array<string | number>;\n licenseKeyId?: string | number;\n licenseKeyIds?: Array<string | number>;\n variantId?: string | number;\n variantIds?: Array<string | number>;\n}\n\n/**\n * The set of validator functions doctor composes.\n *\n * Exposed as an injectable seam so tests can exercise composition order,\n * short-circuiting on connection failure, and option propagation without\n * setting up an HTTP route table for every validator. Production callers\n * normally rely on `DEFAULT_VALIDATORS`, which wires in the real\n * implementations from each `validate/*` module.\n */\nexport interface DoctorValidators {\n connection: (http: HttpClient, mode: Mode) => Promise<ValidationResult<ConnectionSummary>>;\n store: (\n http: HttpClient,\n mode: Mode,\n storeId: string | number\n ) => Promise<ValidationResult<StoreAttributes>>;\n product: (\n http: HttpClient,\n mode: Mode,\n options: ProductValidationOptions\n ) => Promise<ValidationResult<ProductAttributes>>;\n webhook: (\n http: HttpClient,\n mode: Mode,\n options: WebhookValidationOptions\n ) => Promise<ValidationResult<WebhookAttributes>>;\n discount: (\n http: HttpClient,\n mode: Mode,\n options: DiscountValidationOptions\n ) => Promise<ValidationResult<DiscountAttributes>>;\n licenseKey: (\n http: HttpClient,\n mode: Mode,\n options: LicenseKeyValidationOptions\n ) => Promise<ValidationResult<LicenseKeyAttributes>>;\n subscriptionPlan: (\n http: HttpClient,\n mode: Mode,\n options: SubscriptionPlanValidationOptions\n ) => Promise<ValidationResult>;\n}\n\n/** Default wiring used by `doctor` when no overrides are provided. */\nexport const DEFAULT_VALIDATORS: DoctorValidators = {\n connection: validateConnection,\n store: validateStore,\n product: validateProduct,\n webhook: validateWebhook,\n discount: validateDiscount,\n licenseKey: validateLicenseKey,\n subscriptionPlan: validateSubscriptionPlan,\n};\n\n/**\n * Compose every configured validator into a single report. This is the\n * primary entry point for CI health checks: one call, one structured result,\n * one exit code decision.\n *\n * Order is meaningful. Connection runs first because downstream validators\n * have nothing useful to say if the API key is broken.\n *\n * The optional `validators` parameter lets tests inject fakes without a fetch\n * mock; production callers omit it and get `DEFAULT_VALIDATORS`.\n */\nexport async function doctor(\n http: HttpClient,\n mode: Mode,\n options: DoctorOptions = {},\n validators: DoctorValidators = DEFAULT_VALIDATORS\n): Promise<DoctorReport> {\n assertOptionsCoherent(options);\n\n const results: ValidationResult[] = [];\n const productIds = collectValues(options.productId, options.productIds);\n const webhookUrls = collectValues(options.webhookUrl, options.webhookUrls);\n const discountIds = collectValues(options.discountId, options.discountIds);\n const licenseKeyIds = collectValues(options.licenseKeyId, options.licenseKeyIds);\n const variantIds = collectValues(options.variantId, options.variantIds);\n\n const connection = await validators.connection(http, mode);\n results.push(connection);\n\n if (!connection.ok) {\n return { ok: false, mode, results };\n }\n\n if (options.storeId !== undefined) {\n results.push(await validators.store(http, mode, options.storeId));\n }\n\n for (const productId of productIds) {\n results.push(\n await validators.product(http, mode, {\n productId,\n expectedStoreId: options.storeId,\n })\n );\n }\n\n if (options.storeId !== undefined) {\n for (const webhookUrl of webhookUrls) {\n results.push(\n await validators.webhook(http, mode, {\n storeId: options.storeId,\n url: webhookUrl,\n })\n );\n }\n }\n\n if (options.storeId !== undefined) {\n for (const discountId of discountIds) {\n results.push(\n await validators.discount(http, mode, {\n storeId: options.storeId,\n discountId,\n })\n );\n }\n }\n\n if (options.storeId !== undefined) {\n for (const licenseKeyId of licenseKeyIds) {\n results.push(\n await validators.licenseKey(http, mode, {\n storeId: options.storeId,\n licenseKeyId,\n })\n );\n }\n }\n\n if (options.storeId !== undefined) {\n for (const variantId of variantIds) {\n results.push(\n await validators.subscriptionPlan(http, mode, {\n storeId: options.storeId,\n variantId,\n })\n );\n }\n }\n\n const ok = results.every((result) => result.ok);\n return { ok, mode, results };\n}\n\n/**\n * Validators downstream of `storeId` (webhook, discount, licenseKey,\n * subscriptionPlan) cannot run without one. Previously, passing e.g.\n * `discountId` without `storeId` silently skipped the discount validator —\n * the doctor report came back \"ok\" and the consumer assumed coverage that\n * never ran.\n *\n * Treat this as a programmer error and fail fast at the boundary so the\n * miscall is surfaced at construction time rather than masked as a passing\n * doctor run.\n */\nfunction assertOptionsCoherent(options: DoctorOptions): void {\n if (options.storeId !== undefined) return;\n\n const dependent: Array<keyof DoctorOptions> = [];\n if (hasAny(options.webhookUrl, options.webhookUrls)) dependent.push(\"webhookUrl\");\n if (hasAny(options.discountId, options.discountIds)) dependent.push(\"discountId\");\n if (hasAny(options.licenseKeyId, options.licenseKeyIds)) dependent.push(\"licenseKeyId\");\n if (hasAny(options.variantId, options.variantIds)) dependent.push(\"variantId\");\n\n if (dependent.length === 0) return;\n\n throw new FreshSqueezyError({\n code: \"DOCTOR_OPTIONS_INVALID\",\n message: `doctor() received ${dependent.join(\", \")} without storeId. These validators cannot run without a store; pass \\`storeId\\` or remove the dependent options.`,\n });\n}\n\nfunction collectValues<T>(single: T | undefined, many: T[] | undefined): T[] {\n const values = new Set<T>();\n if (single !== undefined) values.add(single);\n for (const value of many ?? []) {\n if (value !== undefined) values.add(value);\n }\n return Array.from(values);\n}\n\nfunction hasAny<T>(single: T | undefined, many: T[] | undefined): boolean {\n return single !== undefined || (many?.length ?? 0) > 0;\n}\n","import { resolveConfig } from \"./core/config.js\";\nimport { HttpClient, type RequestOptions } from \"./core/http.js\";\nimport type { FreshSqueezyConfig, DoctorReport, Mode, ValidationResult } from \"./core/types.js\";\nimport { validateConnection, type ConnectionSummary } from \"./validate/connection.js\";\nimport { validateStore } from \"./validate/store.js\";\nimport { validateProduct, type ProductValidationOptions } from \"./validate/product.js\";\nimport { validateWebhook, type WebhookValidationOptions } from \"./validate/webhook.js\";\nimport { validateDiscount, type DiscountValidationOptions } from \"./validate/discount.js\";\nimport { validateLicenseKey, type LicenseKeyValidationOptions } from \"./validate/licenseKey.js\";\nimport { validateSubscriptionPlan, type SubscriptionPlanValidationOptions, type SubscriptionPlanSummary } from \"./validate/subscriptionPlan.js\";\nimport { doctor, type DoctorOptions } from \"./validate/doctor.js\";\nimport type { StoreAttributes } from \"./resources/stores.js\";\nimport type { ProductAttributes } from \"./resources/products.js\";\nimport type { WebhookAttributes } from \"./resources/webhooks.js\";\nimport type { DiscountAttributes } from \"./resources/discounts.js\";\nimport type { LicenseKeyAttributes } from \"./resources/licenseKeys.js\";\n\n/**\n * The public client. All consumer code flows through the factory below —\n * direct instantiation is intentionally not exposed so we can evolve the\n * internals without breaking callers.\n */\nexport interface FreshSqueezyClient {\n /** Resolved mode (test or live). Surfaced so consumers can log it. */\n readonly mode: Mode;\n\n /** Raw HTTP escape hatch for endpoints fresh-squeezy does not wrap. */\n request<T = unknown>(options: RequestOptions): Promise<T>;\n\n validateConnection(): Promise<ValidationResult<ConnectionSummary>>;\n validateStore(storeId: string | number): Promise<ValidationResult<StoreAttributes>>;\n validateProduct(options: ProductValidationOptions): Promise<ValidationResult<ProductAttributes>>;\n validateWebhook(options: WebhookValidationOptions): Promise<ValidationResult<WebhookAttributes>>;\n validateDiscount(options: DiscountValidationOptions): Promise<ValidationResult<DiscountAttributes>>;\n validateLicenseKey(options: LicenseKeyValidationOptions): Promise<ValidationResult<LicenseKeyAttributes>>;\n validateSubscriptionPlan(options: SubscriptionPlanValidationOptions): Promise<ValidationResult<SubscriptionPlanSummary>>;\n doctor(options?: DoctorOptions): Promise<DoctorReport>;\n}\n\n/**\n * Create a fresh-squeezy client. Zero-config usage reads\n * `LEMON_SQUEEZY_API_KEY`, `LEMON_SQUEEZY_STORE_ID`, and `LEMON_SQUEEZY_MODE`\n * from `process.env`.\n *\n * @example\n * ```ts\n * const lemon = createFreshSqueezy();\n * const report = await lemon.doctor();\n * if (!report.ok) process.exit(1);\n * ```\n */\nexport function createFreshSqueezy(config: FreshSqueezyConfig = {}): FreshSqueezyClient {\n const resolved = resolveConfig(config);\n const http = new HttpClient(resolved);\n\n return {\n mode: resolved.mode,\n request: (options) => http.request(options),\n validateConnection: () => validateConnection(http, resolved.mode),\n validateStore: (storeId) => validateStore(http, resolved.mode, storeId),\n validateProduct: (options) => validateProduct(http, resolved.mode, options),\n validateWebhook: (options) => validateWebhook(http, resolved.mode, options),\n validateDiscount: (options) => validateDiscount(http, resolved.mode, options),\n validateLicenseKey: (options) => validateLicenseKey(http, resolved.mode, options),\n validateSubscriptionPlan: (options) => validateSubscriptionPlan(http, resolved.mode, options),\n doctor: (options) =>\n doctor(http, resolved.mode, {\n storeId: options?.storeId ?? resolved.storeId,\n productId: options?.productId,\n productIds: options?.productIds,\n webhookUrl: options?.webhookUrl,\n webhookUrls: options?.webhookUrls,\n discountId: options?.discountId,\n discountIds: options?.discountIds,\n licenseKeyId: options?.licenseKeyId,\n licenseKeyIds: options?.licenseKeyIds,\n variantId: options?.variantId,\n variantIds: options?.variantIds,\n }),\n };\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedAffiliateAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport type AffiliateStatus = \"active\" | \"pending\" | \"disabled\";\n\n/**\n * Affiliate attributes added with the 2025-01-21 Affiliates API surface.\n * No validator consumes these yet; the helpers give typed access while\n * preserving the raw request escape hatch for mutations and includes.\n */\nexport interface AffiliateAttributes extends GeneratedAffiliateAttributes {\n store_id: number;\n user_id: number;\n user_name: string;\n user_email: string;\n share_domain: string;\n status: AffiliateStatus;\n application_note?: string | null;\n products?: unknown;\n total_earnings: number;\n unpaid_earnings: number;\n created_at?: string;\n updated_at?: string;\n}\n\nexport async function getAffiliate(\n http: HttpClient,\n affiliateId: string | number\n): Promise<JsonApiResource<AffiliateAttributes>> {\n return http.getResource<AffiliateAttributes>(`/v1/affiliates/${affiliateId}`);\n}\n\nexport async function listAffiliatesForStore(\n http: HttpClient,\n storeId: string | number\n): Promise<JsonApiResource<AffiliateAttributes>[]> {\n return http.paginate<AffiliateAttributes>(\"/v1/affiliates\", {\n \"filter[store_id]\": String(storeId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedCheckoutAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport type CheckoutLocale =\n | \"bg\"\n | \"hr\"\n | \"cs\"\n | \"da\"\n | \"nl\"\n | \"en\"\n | \"et\"\n | \"fil\"\n | \"fi\"\n | \"fr\"\n | \"de\"\n | \"el\"\n | \"hu\"\n | \"id\"\n | \"it\"\n | \"ja\"\n | \"ko\"\n | \"lv\"\n | \"lt\"\n | \"ms\"\n | \"mt\"\n | \"pl\"\n | \"pt\"\n | \"ro\"\n | \"ru\"\n | \"zh-CN\"\n | \"sk\"\n | \"sl\"\n | \"es\"\n | \"sv\"\n | \"th\"\n | \"tr\"\n | \"vi\";\n\nexport interface CheckoutProductOptions {\n name?: string;\n description?: string;\n media?: string[];\n redirect_url?: string;\n receipt_button_text?: string;\n receipt_link_url?: string;\n receipt_thank_you_note?: string;\n enabled_variants?: number[];\n}\n\nexport interface CheckoutOptions {\n embed?: boolean;\n media?: boolean;\n logo?: boolean;\n desc?: boolean;\n discount?: boolean;\n subscription_preview?: boolean;\n /** Added 2024-03-28. */\n skip_trial?: boolean;\n /** Deprecated 2024-09-04; keep typed so old payloads still compile. */\n dark?: boolean;\n background_color?: string;\n headings_color?: string;\n primary_text_color?: string;\n secondary_text_color?: string;\n links_color?: string;\n borders_color?: string;\n checkbox_color?: string;\n active_state_color?: string;\n button_color?: string;\n button_text_color?: string;\n terms_privacy_color?: string;\n locale?: CheckoutLocale | string | null;\n}\n\nexport interface CheckoutVariantQuantity {\n variant_id: number;\n quantity: number;\n}\n\nexport interface CheckoutData {\n email?: string;\n name?: string;\n billing_address?: {\n country?: string;\n zip?: string;\n };\n tax_number?: string;\n discount_code?: string;\n custom?: Record<string, unknown>;\n /** Added 2023-08-23 for multi-variant checkout quantities. */\n variant_quantities?: CheckoutVariantQuantity[];\n}\n\nexport interface CheckoutPreview {\n currency: string;\n currency_rate: number | string;\n subtotal: number;\n discount_total: number;\n tax: number;\n total: number;\n subtotal_usd: number;\n discount_total_usd: number;\n tax_usd: number;\n total_usd: number;\n subtotal_formatted: string;\n discount_total_formatted: string;\n tax_formatted: string;\n total_formatted: string;\n}\n\nexport interface CheckoutAttributes extends GeneratedCheckoutAttributes {\n store_id: number;\n variant_id: number;\n custom_price?: number | null;\n product_options?: CheckoutProductOptions;\n checkout_options?: CheckoutOptions;\n checkout_data?: CheckoutData;\n preview?: CheckoutPreview;\n expires_at?: string | null;\n url?: string;\n created_at?: string;\n updated_at?: string;\n test_mode?: boolean;\n}\n\nexport async function getCheckout(\n http: HttpClient,\n checkoutId: string | number\n): Promise<JsonApiResource<CheckoutAttributes>> {\n return http.getResource<CheckoutAttributes>(`/v1/checkouts/${checkoutId}`);\n}\n\nexport async function listCheckoutsForStore(\n http: HttpClient,\n storeId: string | number\n): Promise<JsonApiResource<CheckoutAttributes>[]> {\n return http.paginate<CheckoutAttributes>(\"/v1/checkouts\", {\n \"filter[store_id]\": String(storeId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedCustomerAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport type CustomerStatus =\n | \"subscribed\"\n | \"unsubscribed\"\n | \"archived\"\n | \"requires_verification\"\n | \"invalid_email\"\n | \"bounced\";\n\nexport interface CustomerUrls {\n customer_portal?: string | null;\n}\n\n/**\n * Lemon Squeezy customer attributes. The customer portal URL was called out\n * in the 2023-09-19 changelog and is useful for apps linking users back to\n * billing without another API shape.\n */\nexport interface CustomerAttributes extends GeneratedCustomerAttributes {\n store_id: number;\n name: string;\n email: string;\n status: CustomerStatus;\n city?: string | null;\n region?: string | null;\n country?: string | null;\n total_revenue_currency: number;\n mrr: number;\n status_formatted: string;\n country_formatted?: string | null;\n total_revenue_currency_formatted: string;\n mrr_formatted: string;\n urls?: CustomerUrls;\n created_at?: string;\n updated_at?: string;\n test_mode?: boolean;\n}\n\nexport async function getCustomer(\n http: HttpClient,\n customerId: string | number\n): Promise<JsonApiResource<CustomerAttributes>> {\n return http.getResource<CustomerAttributes>(`/v1/customers/${customerId}`);\n}\n\nexport async function listCustomersForStore(\n http: HttpClient,\n storeId: string | number\n): Promise<JsonApiResource<CustomerAttributes>[]> {\n return http.paginate<CustomerAttributes>(\"/v1/customers\", {\n \"filter[store_id]\": String(storeId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedDiscountRedemptionAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport interface DiscountRedemptionAttributes extends GeneratedDiscountRedemptionAttributes {\n discount_id: number;\n order_id: number;\n discount_name: string;\n discount_code: string;\n discount_amount: number;\n discount_amount_type: \"percent\" | \"fixed\";\n amount: number;\n created_at?: string;\n updated_at?: string;\n}\n\nexport async function getDiscountRedemption(\n http: HttpClient,\n discountRedemptionId: string | number\n): Promise<JsonApiResource<DiscountRedemptionAttributes>> {\n return http.getResource<DiscountRedemptionAttributes>(\n `/v1/discount-redemptions/${discountRedemptionId}`\n );\n}\n\nexport async function listDiscountRedemptionsForDiscount(\n http: HttpClient,\n discountId: string | number\n): Promise<JsonApiResource<DiscountRedemptionAttributes>[]> {\n return http.paginate<DiscountRedemptionAttributes>(\"/v1/discount-redemptions\", {\n \"filter[discount_id]\": String(discountId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedFileAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Lemon Squeezy file attributes, generated from the public object docs.\n * Exposed so consumers can type file-resource escape-hatch calls without\n * waiting for a hand-written validator.\n */\nexport interface FileAttributes extends GeneratedFileAttributes {}\n\nexport async function getFile(\n http: HttpClient,\n fileId: string | number\n): Promise<JsonApiResource<FileAttributes>> {\n return http.getResource<FileAttributes>(`/v1/files/${fileId}`);\n}\n\nexport async function listFilesForVariant(\n http: HttpClient,\n variantId: string | number\n): Promise<JsonApiResource<FileAttributes>[]> {\n return http.paginate<FileAttributes>(\"/v1/files\", {\n \"filter[variant_id]\": String(variantId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedLicenseKeyInstanceAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport interface LicenseKeyInstanceAttributes extends GeneratedLicenseKeyInstanceAttributes {\n license_key_id: number;\n identifier: string;\n name: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport async function getLicenseKeyInstance(\n http: HttpClient,\n licenseKeyInstanceId: string | number\n): Promise<JsonApiResource<LicenseKeyInstanceAttributes>> {\n return http.getResource<LicenseKeyInstanceAttributes>(\n `/v1/license-key-instances/${licenseKeyInstanceId}`\n );\n}\n\nexport async function listLicenseKeyInstancesForLicenseKey(\n http: HttpClient,\n licenseKeyId: string | number\n): Promise<JsonApiResource<LicenseKeyInstanceAttributes>[]> {\n return http.paginate<LicenseKeyInstanceAttributes>(\"/v1/license-key-instances\", {\n \"filter[license_key_id]\": String(licenseKeyId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedOrderItemAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Order item attributes. `quantity` was added to the documented object in\n * the 2024-12-06 changelog and is needed for multi-quantity reconciliation.\n */\nexport interface OrderItemAttributes extends GeneratedOrderItemAttributes {\n order_id: number;\n product_id: number;\n variant_id: number;\n product_name: string;\n variant_name: string;\n price: number;\n quantity: number;\n created_at?: string;\n updated_at?: string;\n}\n\nexport async function getOrderItem(\n http: HttpClient,\n orderItemId: string | number\n): Promise<JsonApiResource<OrderItemAttributes>> {\n return http.getResource<OrderItemAttributes>(`/v1/order-items/${orderItemId}`);\n}\n\nexport async function listOrderItemsForOrder(\n http: HttpClient,\n orderId: string | number\n): Promise<JsonApiResource<OrderItemAttributes>[]> {\n return http.paginate<OrderItemAttributes>(\"/v1/order-items\", {\n \"filter[order_id]\": String(orderId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedOrderAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport interface OrderUrls {\n receipt?: string;\n}\n\nexport interface OrderFirstItem {\n id: number;\n order_id?: number;\n product_id: number;\n variant_id: number;\n product_name: string;\n variant_name: string;\n price: number;\n quantity?: number;\n created_at?: string;\n updated_at?: string;\n}\n\n/**\n * Subset of Lemon Squeezy order attributes fresh-squeezy understands.\n *\n * fresh-squeezy does not (yet) validate orders — the type is exported so\n * consumers using the raw `client.request<OrderAttributes>()` escape hatch\n * get a typed response that includes platform additions the official SDK\n * has not yet picked up.\n *\n * Field provenance against the changelog\n * (https://docs.lemonsqueezy.com/api/getting-started/changelog):\n *\n * - `status: \"fraudulent\"` — added 2024-09-10. Indicates a manual fraud\n * flag; treat as terminal and refund or chargeback as appropriate.\n * - `refunded_amount`, `refunded_amount_usd`, `refunded_amount_formatted`\n * — added 2024-08-07. Useful for consumers reconciling partial refunds.\n * - `tax_inclusive` — added 2024-02-05.\n */\nexport interface OrderAttributes extends GeneratedOrderAttributes {\n store_id: number;\n customer_id?: number;\n identifier: string;\n order_number: number;\n user_name: string;\n user_email: string;\n currency: string;\n currency_rate: string;\n subtotal: number;\n setup_fee?: number;\n discount_total: number;\n tax: number;\n tax_inclusive?: boolean;\n total: number;\n refunded_amount?: number;\n subtotal_usd?: number;\n setup_fee_usd?: number;\n discount_total_usd?: number;\n tax_usd?: number;\n total_usd?: number;\n refunded_amount_usd?: number | null;\n tax_name?: string | null;\n tax_rate?: number | string | null;\n refunded_amount_formatted?: string | null;\n refunded?: boolean;\n refunded_at?: string | null;\n status:\n | \"pending\"\n | \"failed\"\n | \"paid\"\n | \"refunded\"\n | \"fraudulent\"\n | \"partial_refund\";\n status_formatted: string;\n subtotal_formatted?: string;\n setup_fee_formatted?: string;\n discount_total_formatted?: string;\n tax_formatted?: string;\n total_formatted?: string;\n first_order_item?: OrderFirstItem | null;\n urls?: OrderUrls;\n created_at?: string;\n updated_at?: string;\n test_mode?: boolean;\n}\n\n/**\n * Fetch a single order by ID. Provided for consumers that want typed\n * access to orders without hand-rolling the JSON:API envelope; the\n * library intentionally has no order *validator* yet.\n */\nexport async function getOrder(\n http: HttpClient,\n orderId: string | number\n): Promise<JsonApiResource<OrderAttributes>> {\n return http.getResource<OrderAttributes>(`/v1/orders/${orderId}`);\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedPriceAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Subset of Lemon Squeezy price attributes. Prices are nested under\n * variants in the API surface; consumers that read them directly via\n * `client.request<PriceAttributes>()` get a typed response that includes\n * the setup-fee fields the official SDK does not yet surface.\n *\n * Field provenance:\n * - `setup_fee_enabled`, `setup_fee` — added 2024-01-21.\n * - `unit_price_decimal` — added 2024-01-15.\n */\nexport interface PriceAttributes extends GeneratedPriceAttributes {\n variant_id: number;\n category: \"one_time\" | \"subscription\" | \"lead_magnet\" | \"pwyw\";\n scheme: \"standard\" | \"package\" | \"graduated\" | \"volume\";\n usage_aggregation?: string | null;\n unit_price?: number;\n /** Added 2024-01-15. Decimal string for sub-cent accuracy. */\n unit_price_decimal?: string | null;\n package_size?: number;\n tiers?: unknown;\n renewal_interval_unit?: \"day\" | \"week\" | \"month\" | \"year\" | null;\n renewal_interval_quantity?: number | null;\n trial_interval_unit?: \"day\" | \"week\" | \"month\" | \"year\" | null;\n trial_interval_quantity?: number | null;\n min_price?: number | null;\n suggested_price?: number | null;\n tax_code?: string;\n /** Added 2024-01-21. */\n setup_fee_enabled?: boolean | null;\n /** Added 2024-01-21. Setup fee in cents, paired with `setup_fee_enabled`. */\n setup_fee?: number | null;\n created_at?: string;\n updated_at?: string;\n}\n\n/**\n * Fetch a single price by ID. Provided for consumers reaching the prices\n * endpoint directly — fresh-squeezy reads price information through the\n * subscription plan validator and does not surface a price validator yet.\n */\nexport async function getPrice(\n http: HttpClient,\n priceId: string | number\n): Promise<JsonApiResource<PriceAttributes>> {\n return http.getResource<PriceAttributes>(`/v1/prices/${priceId}`);\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedSubscriptionAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * URLs Lemon Squeezy attaches to a subscription so consumers can deep-link\n * users to billing pages. The set has grown over time; we keep optional\n * fields for forward-compatibility.\n */\nexport interface SubscriptionUrls {\n update_payment_method?: string;\n customer_portal?: string;\n /** Added 2024-02-20: lets consumers offer \"manage subscription\" without an extra round-trip. */\n update_customer_portal?: string | null;\n /** Current API examples also surface this key name for PayPal update flows. */\n customer_portal_update_subscription?: string | null;\n}\n\nexport interface SubscriptionPause {\n mode: \"void\" | \"free\" | string;\n resumes_at?: string | null;\n}\n\nexport interface SubscriptionItemSummary {\n id: number;\n subscription_id: number;\n price_id: number;\n quantity: number;\n created_at?: string;\n updated_at?: string;\n}\n\n/**\n * Subset of subscription attributes. fresh-squeezy does not (yet) validate\n * subscriptions — this type is exported so consumers using the raw\n * `client.request<SubscriptionAttributes>()` escape hatch get a typed\n * response that includes platform additions the official SDK has not yet\n * picked up.\n *\n * Field provenance against the changelog:\n * - `payment_processor` — added 2025-06-11.\n * - `urls.update_customer_portal` — added 2024-02-20.\n * - `tax_inclusive` — invoice-only in current API docs; retained here as\n * a compatibility field for consumers that previously normalised\n * subscription and subscription invoice payloads together.\n */\nexport interface SubscriptionAttributes extends GeneratedSubscriptionAttributes {\n store_id: number;\n customer_id: number;\n order_id?: number;\n order_item_id?: number;\n product_id: number;\n variant_id: number;\n product_name: string;\n variant_name: string;\n user_name: string;\n user_email: string;\n status:\n | \"on_trial\"\n | \"active\"\n | \"paused\"\n | \"past_due\"\n | \"unpaid\"\n | \"cancelled\"\n | \"expired\";\n status_formatted: string;\n card_brand?: string | null;\n card_last_four?: string | null;\n /** Added 2025-06-11. Identifies which payment provider processed the transaction. */\n payment_processor?: \"stripe\" | \"lemonsqueezy\" | \"paypal\" | string;\n pause?: SubscriptionPause | null;\n cancelled?: boolean;\n trial_ends_at?: string | null;\n billing_anchor?: number;\n first_subscription_item?: SubscriptionItemSummary | null;\n urls?: SubscriptionUrls;\n renews_at?: string | null;\n ends_at?: string | null;\n created_at?: string;\n updated_at?: string;\n test_mode?: boolean;\n /** Compatibility field for older fresh-squeezy consumers; prefer SubscriptionInvoiceAttributes.tax_inclusive. */\n tax_inclusive?: boolean;\n}\n\n/**\n * Fetch a single subscription by ID. Provided for consumers that want\n * typed access without hand-rolling the JSON:API envelope; the library\n * has no subscription *validator* yet.\n */\nexport async function getSubscription(\n http: HttpClient,\n subscriptionId: string | number\n): Promise<JsonApiResource<SubscriptionAttributes>> {\n return http.getResource<SubscriptionAttributes>(`/v1/subscriptions/${subscriptionId}`);\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedSubscriptionInvoiceAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport type SubscriptionInvoiceStatus =\n | \"pending\"\n | \"paid\"\n | \"void\"\n | \"refunded\"\n | \"partial_refund\";\n\nexport type SubscriptionInvoiceBillingReason = \"initial\" | \"renewal\" | \"updated\";\n\nexport interface SubscriptionInvoiceUrls {\n invoice_url?: string;\n}\n\n/**\n * Subscription invoice attributes, including fields added across the\n * changelog: customer identity, tax inclusion, and partial refund amounts.\n */\nexport interface SubscriptionInvoiceAttributes extends GeneratedSubscriptionInvoiceAttributes {\n store_id: number;\n subscription_id: number;\n customer_id: number;\n user_name: string;\n user_email: string;\n billing_reason: SubscriptionInvoiceBillingReason | string;\n card_brand?: string | null;\n card_last_four?: string | null;\n currency: string;\n currency_rate: string;\n status: SubscriptionInvoiceStatus;\n status_formatted: string;\n refunded: boolean;\n refunded_at: string | null;\n subtotal: number;\n discount_total: number;\n tax: number;\n tax_inclusive: boolean;\n total: number;\n refunded_amount: number;\n subtotal_usd: number;\n discount_total_usd: number;\n tax_usd: number;\n total_usd: number;\n refunded_amount_usd: number;\n subtotal_formatted: string;\n discount_total_formatted: string;\n tax_formatted: string;\n total_formatted: string;\n refunded_amount_formatted: string;\n urls?: SubscriptionInvoiceUrls;\n created_at?: string;\n updated_at?: string;\n test_mode?: boolean;\n}\n\nexport async function getSubscriptionInvoice(\n http: HttpClient,\n subscriptionInvoiceId: string | number\n): Promise<JsonApiResource<SubscriptionInvoiceAttributes>> {\n return http.getResource<SubscriptionInvoiceAttributes>(\n `/v1/subscription-invoices/${subscriptionInvoiceId}`\n );\n}\n\nexport async function listSubscriptionInvoicesForSubscription(\n http: HttpClient,\n subscriptionId: string | number\n): Promise<JsonApiResource<SubscriptionInvoiceAttributes>[]> {\n return http.paginate<SubscriptionInvoiceAttributes>(\"/v1/subscription-invoices\", {\n \"filter[subscription_id]\": String(subscriptionId),\n });\n}\n","import type { HttpClient, RequestOptions } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedSubscriptionItemAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport interface SubscriptionItemAttributes extends GeneratedSubscriptionItemAttributes {\n subscription_id: number;\n price_id: number;\n quantity: number;\n is_usage_based: boolean;\n created_at?: string;\n updated_at?: string;\n}\n\n/**\n * Attributes accepted by the update subscription item endpoint. The proration\n * controls were added in the 2024-02-12 changelog.\n */\nexport interface SubscriptionItemUpdateAttributes {\n quantity?: number;\n invoice_immediately?: boolean;\n disable_prorations?: boolean;\n}\n\nexport async function getSubscriptionItem(\n http: HttpClient,\n subscriptionItemId: string | number\n): Promise<JsonApiResource<SubscriptionItemAttributes>> {\n return http.getResource<SubscriptionItemAttributes>(\n `/v1/subscription-items/${subscriptionItemId}`\n );\n}\n\nexport async function listSubscriptionItemsForSubscription(\n http: HttpClient,\n subscriptionId: string | number\n): Promise<JsonApiResource<SubscriptionItemAttributes>[]> {\n return http.paginate<SubscriptionItemAttributes>(\"/v1/subscription-items\", {\n \"filter[subscription_id]\": String(subscriptionId),\n });\n}\n\nexport function buildSubscriptionItemUpdateBody(\n subscriptionItemId: string | number,\n attributes: SubscriptionItemUpdateAttributes\n): RequestOptions[\"body\"] {\n return {\n data: {\n type: \"subscription-items\",\n id: String(subscriptionItemId),\n attributes,\n },\n };\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedUsageRecordAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport type UsageRecordAction = \"increment\" | \"set\";\n\nexport interface UsageRecordAttributes extends GeneratedUsageRecordAttributes {\n subscription_item_id: number;\n quantity: number;\n action: UsageRecordAction;\n created_at?: string;\n updated_at?: string;\n}\n\nexport async function getUsageRecord(\n http: HttpClient,\n usageRecordId: string | number\n): Promise<JsonApiResource<UsageRecordAttributes>> {\n return http.getResource<UsageRecordAttributes>(`/v1/usage-records/${usageRecordId}`);\n}\n\nexport async function listUsageRecordsForSubscriptionItem(\n http: HttpClient,\n subscriptionItemId: string | number\n): Promise<JsonApiResource<UsageRecordAttributes>[]> {\n return http.paginate<UsageRecordAttributes>(\"/v1/usage-records\", {\n \"filter[subscription_item_id]\": String(subscriptionItemId),\n });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/errors.ts","../src/core/config.ts","../src/core/http.ts","../src/core/mode.ts","../src/resources/stores.ts","../src/resources/users.ts","../src/validate/rules.ts","../src/validate/probe.ts","../src/validate/connection.ts","../src/resources/discounts.ts","../src/validate/discount.ts","../src/resources/licenseKeys.ts","../src/validate/licenseKey.ts","../src/resources/products.ts","../src/resources/variants.ts","../src/validate/product.ts","../src/validate/store.ts","../src/validate/subscriptionPlan.ts","../src/core/equality.ts","../src/resources/webhooks.ts","../src/generated/lemonSqueezyApiTypes.ts","../src/support/manifest.ts","../src/validate/webhook.ts","../src/validate/doctor.ts","../src/createFreshSqueezy.ts","../src/resources/affiliates.ts","../src/resources/checkouts.ts","../src/resources/customers.ts","../src/resources/discountRedemptions.ts","../src/resources/files.ts","../src/resources/licenseKeyInstances.ts","../src/resources/orderItems.ts","../src/resources/orders.ts","../src/resources/prices.ts","../src/resources/subscriptions.ts","../src/resources/subscriptionInvoices.ts","../src/resources/subscriptionItems.ts","../src/resources/usageRecords.ts"],"sourcesContent":["export * from \"./augmentations.js\";\nexport * from \"./createFreshSqueezy.js\";\nexport * from \"./generated/lemonSqueezyApiTypes.js\";\nexport * from \"./core/equality.js\";\nexport * from \"./core/errors.js\";\nexport * from \"./core/config.js\";\nexport * from \"./core/mode.js\";\nexport * from \"./core/types.js\";\nexport * from \"./validate/rules.js\";\nexport * from \"./validate/connection.js\";\nexport * from \"./validate/store.js\";\nexport * from \"./validate/product.js\";\nexport * from \"./validate/webhook.js\";\nexport * from \"./validate/discount.js\";\nexport * from \"./validate/licenseKey.js\";\nexport * from \"./validate/subscriptionPlan.js\";\nexport * from \"./validate/doctor.js\";\nexport * from \"./resources/affiliates.js\";\nexport * from \"./resources/checkouts.js\";\nexport * from \"./resources/customers.js\";\nexport * from \"./resources/discountRedemptions.js\";\nexport * from \"./resources/files.js\";\nexport * from \"./resources/licenseKeyInstances.js\";\nexport * from \"./resources/orderItems.js\";\nexport * from \"./resources/orders.js\";\nexport * from \"./resources/prices.js\";\nexport * from \"./resources/stores.js\";\nexport * from \"./resources/products.js\";\nexport * from \"./resources/subscriptions.js\";\nexport * from \"./resources/subscriptionInvoices.js\";\nexport * from \"./resources/subscriptionItems.js\";\nexport * from \"./resources/usageRecords.js\";\nexport * from \"./resources/variants.js\";\nexport * from \"./resources/webhooks.js\";\nexport * from \"./resources/users.js\";\nexport * from \"./support/manifest.js\";\n","/**\n * Unified error type for fresh-squeezy. All HTTP and validation failures that\n * bubble up to consumers pass through this class so caller code can branch on\n * a single `instanceof` check.\n *\n * Why a class over a discriminated union: Node's `fetch` rejections interleave\n * with library errors in user stack traces. A class keeps the stack readable\n * and gives one stable prototype chain for consumer `catch` blocks.\n */\nexport class FreshSqueezyError extends Error {\n public readonly code: string;\n public readonly status?: number;\n public readonly detail?: unknown;\n\n constructor(opts: { code: string; message: string; status?: number; detail?: unknown }) {\n super(opts.message);\n this.name = \"FreshSqueezyError\";\n this.code = opts.code;\n this.status = opts.status;\n this.detail = opts.detail;\n Object.setPrototypeOf(this, FreshSqueezyError.prototype);\n }\n}\n","import { FreshSqueezyError } from \"./errors.js\";\nimport type { FreshSqueezyConfig, Mode, ResolvedConfig } from \"./types.js\";\n\n/**\n * Lemon Squeezy API root. Test and live share the same host — mode is determined\n * by which API key is used. Documented at https://docs.lemonsqueezy.com/api.\n */\nconst DEFAULT_BASE_URL = \"https://api.lemonsqueezy.com\";\n\n/**\n * Env variable names read when a field is not passed explicitly. Consuming\n * products rely on these names being stable — treat them as public API.\n */\nexport const ENV_KEYS = {\n apiKey: \"LEMON_SQUEEZY_API_KEY\",\n storeId: \"LEMON_SQUEEZY_STORE_ID\",\n mode: \"LEMON_SQUEEZY_MODE\",\n} as const;\n\n/**\n * Resolve the user-supplied config against environment variables and defaults.\n *\n * Precedence (highest → lowest): explicit argument → env var → built-in default.\n * Throws `FreshSqueezyError` only for fields that cannot be defaulted (currently\n * just `apiKey`), so callers can surface a clear setup error at construction\n * time rather than at first request.\n */\nexport function resolveConfig(input: FreshSqueezyConfig = {}): ResolvedConfig {\n const apiKey = input.apiKey ?? process.env[ENV_KEYS.apiKey];\n if (!apiKey) {\n throw new FreshSqueezyError({\n code: \"MISSING_API_KEY\",\n message: `No API key provided. Pass \\`apiKey\\` or set ${ENV_KEYS.apiKey}.`,\n });\n }\n\n const mode = normalizeMode(input.mode ?? process.env[ENV_KEYS.mode] ?? \"test\");\n const storeIdRaw = input.storeId ?? process.env[ENV_KEYS.storeId];\n\n return {\n apiKey,\n storeId: storeIdRaw == null ? undefined : String(storeIdRaw),\n mode,\n baseUrl: input.baseUrl ?? DEFAULT_BASE_URL,\n fetch: input.fetch ?? globalThis.fetch,\n };\n}\n\nfunction normalizeMode(value: string): Mode {\n if (value === \"test\" || value === \"live\") return value;\n throw new FreshSqueezyError({\n code: \"INVALID_MODE\",\n message: `Mode must be \"test\" or \"live\", got \"${value}\".`,\n });\n}\n","import { FreshSqueezyError } from \"./errors.js\";\nimport type {\n JsonApiCollection,\n JsonApiDocument,\n JsonApiResource,\n ResolvedConfig,\n} from \"./types.js\";\n\n/**\n * Options for a single HTTP request.\n *\n * `path` is a Lemon Squeezy API path starting with `/v1/...`. The `query`\n * record is serialized as URL search params with JSON:API-style bracketed\n * keys left untouched (e.g. `filter[store_id]`).\n */\nexport interface RequestOptions {\n method?: \"GET\" | \"POST\" | \"PATCH\" | \"DELETE\";\n path: string;\n query?: Record<string, string | number | undefined>;\n body?: Record<string, unknown>;\n signal?: AbortSignal;\n}\n\n/**\n * Lemon Squeezy JSON:API error object. Kept loose because the API occasionally\n * includes extra keys; the fields we surface are the stable ones.\n */\ninterface JsonApiError {\n status?: string;\n code?: string;\n title?: string;\n detail?: string;\n}\n\n/**\n * Low-level HTTP client. Callers usually go through resource/validator helpers,\n * but this is also exposed as the public escape hatch so consumers can reach\n * endpoints fresh-squeezy does not wrap yet.\n *\n * Responsibilities kept in this one place (per plan.md \"one source of truth\n * for transport\"):\n * - auth header injection\n * - query string serialization with JSON:API bracket keys preserved\n * - response parsing + error normalization\n * - surfacing HTTP status in `FreshSqueezyError`\n *\n * Retries, pagination helpers, and rate-limit handling live in separate files\n * so this layer stays small and obvious.\n */\nexport class HttpClient {\n constructor(private readonly config: ResolvedConfig) {}\n\n async request<T>(options: RequestOptions): Promise<T> {\n const url = this.buildUrl(options.path, options.query);\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.config.apiKey}`,\n Accept: \"application/vnd.api+json\",\n };\n if (options.body !== undefined) {\n headers[\"Content-Type\"] = \"application/vnd.api+json\";\n }\n\n let response: Response;\n try {\n response = await this.config.fetch(url, {\n method: options.method ?? \"GET\",\n headers,\n body: options.body === undefined ? undefined : JSON.stringify(options.body),\n signal: options.signal,\n });\n } catch (cause) {\n throw new FreshSqueezyError({\n code: \"NETWORK_ERROR\",\n message: cause instanceof Error ? cause.message : \"Network request failed\",\n detail: cause,\n });\n }\n\n const text = await response.text();\n const parsed = text.length > 0 ? safeJsonParse(text) : undefined;\n\n if (!response.ok) {\n throw toApiError(response.status, parsed);\n }\n\n return parsed as T;\n }\n\n /**\n * Fetch a single JSON:API resource and return its `data` object.\n */\n async getResource<TAttr>(path: string): Promise<JsonApiResource<TAttr>> {\n const doc = await this.request<JsonApiDocument<TAttr>>({ path });\n return doc.data;\n }\n\n /**\n * Fetch a single page of a JSON:API collection. Most callers want\n * `paginate()` instead — this method is kept for one-shot lookups where\n * the caller knows the result fits in a single page.\n */\n async getCollection<TAttr>(\n path: string,\n query?: RequestOptions[\"query\"],\n ): Promise<JsonApiResource<TAttr>[]> {\n const doc = await this.request<JsonApiCollection<TAttr>>({ path, query });\n return doc.data;\n }\n\n /**\n * Walk a JSON:API collection across every page and concatenate the\n * results. Lemon Squeezy returns 25 items per page by default; without\n * pagination, validators silently miss anything past the first page (e.g.\n * `validateWebhook` on a store with 26+ webhooks would falsely report\n * `WEBHOOK_NOT_FOUND` for any webhook on page 2).\n *\n * Stops as soon as `meta.page.lastPage` is reached. Falls back to a\n * single-page fetch if the API does not surface page metadata.\n *\n * Caller-supplied `page[number]` is honored as the starting page; we\n * advance from there. `page[size]` is left untouched so callers can tune\n * batch size (max 100 per Lemon Squeezy docs).\n */\n async paginate<TAttr>(\n path: string,\n query?: RequestOptions[\"query\"],\n ): Promise<JsonApiResource<TAttr>[]> {\n const startPage = Number(query?.[\"page[number]\"] ?? 1);\n const all: JsonApiResource<TAttr>[] = [];\n\n let pageNumber = startPage;\n while (true) {\n const pagedQuery = { ...(query ?? {}), \"page[number]\": pageNumber };\n const doc = await this.request<JsonApiCollection<TAttr>>({ path, query: pagedQuery });\n all.push(...doc.data);\n\n const lastPage = doc.meta?.page?.lastPage;\n if (lastPage === undefined || pageNumber >= lastPage) return all;\n pageNumber += 1;\n }\n }\n\n private buildUrl(path: string, query?: RequestOptions[\"query\"]): string {\n const url = new URL(path, this.config.baseUrl);\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined) continue;\n url.searchParams.append(key, String(value));\n }\n }\n return url.toString();\n }\n}\n\nfunction safeJsonParse(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction toApiError(status: number, body: unknown): FreshSqueezyError {\n const errors = extractJsonApiErrors(body);\n const first = errors[0];\n const code =\n status === 401\n ? \"UNAUTHORIZED\"\n : status === 404\n ? \"NOT_FOUND\"\n : status === 429\n ? \"RATE_LIMITED\"\n : (first?.code ?? `HTTP_${status}`);\n const message =\n first?.detail ?? first?.title ?? `Lemon Squeezy request failed with status ${status}`;\n return new FreshSqueezyError({ code, status, message, detail: body });\n}\n\nfunction extractJsonApiErrors(body: unknown): JsonApiError[] {\n if (!body || typeof body !== \"object\") return [];\n const errors = (body as { errors?: unknown }).errors;\n if (!Array.isArray(errors)) return [];\n return errors.filter(\n (entry): entry is JsonApiError => typeof entry === \"object\" && entry !== null,\n );\n}\n","import type { Mode } from \"./types.js\";\n\n/**\n * Map the boolean `meta.test_mode` flag from `/v1/users/me` to our `Mode` type.\n *\n * Returns `undefined` when the field is absent so older accounts and proxies\n * that don't surface the flag don't produce spurious mode mismatches. Lemon\n * Squeezy added `meta.test_mode` to the endpoint on 2024-01-05 (see API\n * changelog: https://docs.lemonsqueezy.com/api/getting-started/changelog).\n *\n * Pure function — exposed publicly so consumers can resolve a key's true mode\n * from a `/v1/users/me` document they already have, without re-running the\n * full connection validator. Keeping it free of any resource import is what\n * lets `core/` stay foundational (it never reaches up into `resources/`); the\n * I/O counterpart `fetchActualMode` lives in `validate/connection.ts`.\n */\nexport function resolveActualMode(testMode: boolean | undefined): Mode | undefined {\n if (testMode === true) return \"test\";\n if (testMode === false) return \"live\";\n return undefined;\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedStoreAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Subset of store attributes fresh-squeezy reads.\n * Full schema at https://docs.lemonsqueezy.com/api/stores.\n */\nexport interface StoreAttributes extends GeneratedStoreAttributes {\n name: string;\n slug: string;\n domain?: string | null;\n url?: string | null;\n country?: string;\n currency?: string;\n plan?: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport async function getStore(\n http: HttpClient,\n storeId: string | number,\n): Promise<JsonApiResource<StoreAttributes>> {\n return http.getResource<StoreAttributes>(`/v1/stores/${storeId}`);\n}\n\n/**\n * List every store reachable with the configured API key, paginated.\n * Used by `validateConnection` to populate the connection summary, and by\n * the CLI's `--all-stores` flag. Pagination matters here for accounts that\n * own a large catalogue of stores.\n */\nexport async function listStores(http: HttpClient): Promise<JsonApiResource<StoreAttributes>[]> {\n return http.paginate<StoreAttributes>(\"/v1/stores\");\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiDocument, JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedUserAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Subset of the Lemon Squeezy `users` resource attributes we rely on.\n * Full schema at https://docs.lemonsqueezy.com/api/users.\n */\nexport interface UserAttributes extends GeneratedUserAttributes {\n name: string;\n email: string;\n color?: string;\n avatar_url?: string | null;\n has_custom_avatar?: boolean;\n createdAt?: string;\n updatedAt?: string;\n}\n\n/**\n * Document-level metadata on `/v1/users/me`.\n *\n * `test_mode` was added to the endpoint on 2024-01-05 (per the Lemon Squeezy\n * API changelog: https://docs.lemonsqueezy.com/api/getting-started/changelog).\n * It reports whether the *key* being used is a test-mode key, independent of\n * what the caller declared. The connection validator compares this against\n * the caller's declared mode to catch the common \"prod key in staging\"\n * (or vice versa) misconfiguration.\n */\nexport interface UserMeta {\n test_mode?: boolean;\n}\n\n/**\n * Authenticated-user document with the `data` + `meta` block preserved.\n * The connection validator needs the meta flag, so we return the full\n * document here rather than just `data` as the collection helpers do.\n */\nexport type AuthenticatedUserDocument = JsonApiDocument<UserAttributes> & { meta?: UserMeta };\n\n/**\n * Fetch the user associated with the API key. Primary use is the connection\n * validator: a successful call confirms the key is valid, surfaces the\n * account identity for logs, and exposes `meta.test_mode` for mode\n * mismatch detection.\n */\nexport async function getAuthenticatedUser(http: HttpClient): Promise<AuthenticatedUserDocument> {\n return http.request<AuthenticatedUserDocument>({ path: \"/v1/users/me\" });\n}\n\n/**\n * Backwards-compatible helper if a caller only wants the resource (old\n * `getAuthenticatedUser` shape). Internal — not re-exported from the root.\n */\nexport function userResource(doc: AuthenticatedUserDocument): JsonApiResource<UserAttributes> {\n return doc.data;\n}\n","import type {\n ValidationIssue,\n ValidationResult,\n ValidationSeverity,\n ValidationTarget,\n} from \"../core/types.js\";\n\n/**\n * Stable issue codes. Consumers may switch on these in CI — do not rename\n * without a major version bump.\n */\nexport const ISSUE_CODES = {\n AUTH_FAILED: \"AUTH_FAILED\",\n MODE_MISMATCH: \"MODE_MISMATCH\",\n STORE_NOT_FOUND: \"STORE_NOT_FOUND\",\n STORE_NOT_OWNED: \"STORE_NOT_OWNED\",\n PRODUCT_NOT_FOUND: \"PRODUCT_NOT_FOUND\",\n PRODUCT_WRONG_STORE: \"PRODUCT_WRONG_STORE\",\n PRODUCT_UNPUBLISHED: \"PRODUCT_UNPUBLISHED\",\n PRODUCT_NO_BUY_URL: \"PRODUCT_NO_BUY_URL\",\n VARIANT_UNPUBLISHED: \"VARIANT_UNPUBLISHED\",\n VARIANT_MISSING: \"VARIANT_MISSING\",\n WEBHOOK_NOT_FOUND: \"WEBHOOK_NOT_FOUND\",\n WEBHOOK_EVENTS_MISSING: \"WEBHOOK_EVENTS_MISSING\",\n WEBHOOK_OPTIONAL_EVENTS: \"WEBHOOK_OPTIONAL_EVENTS\",\n DISCOUNT_NOT_FOUND: \"DISCOUNT_NOT_FOUND\",\n DISCOUNT_DRAFT: \"DISCOUNT_DRAFT\",\n DISCOUNT_EXPIRED: \"DISCOUNT_EXPIRED\",\n DISCOUNT_NOT_STARTED: \"DISCOUNT_NOT_STARTED\",\n DISCOUNT_REDEMPTIONS_EXHAUSTED: \"DISCOUNT_REDEMPTIONS_EXHAUSTED\",\n DISCOUNT_INVALID_AMOUNT: \"DISCOUNT_INVALID_AMOUNT\",\n DISCOUNT_STORE_MISMATCH: \"DISCOUNT_STORE_MISMATCH\",\n LICENSE_KEY_NOT_FOUND: \"LICENSE_KEY_NOT_FOUND\",\n LICENSE_KEY_DISABLED: \"LICENSE_KEY_DISABLED\",\n LICENSE_KEY_EXPIRED: \"LICENSE_KEY_EXPIRED\",\n LICENSE_KEY_AT_ACTIVATION_LIMIT: \"LICENSE_KEY_AT_ACTIVATION_LIMIT\",\n LICENSE_KEY_STORE_MISMATCH: \"LICENSE_KEY_STORE_MISMATCH\",\n PLAN_VARIANT_NOT_FOUND: \"PLAN_VARIANT_NOT_FOUND\",\n PLAN_NOT_SUBSCRIPTION: \"PLAN_NOT_SUBSCRIPTION\",\n PLAN_INVALID_INTERVAL: \"PLAN_INVALID_INTERVAL\",\n PLAN_FREE_PRICE: \"PLAN_FREE_PRICE\",\n PLAN_TRIAL_INCONSISTENT: \"PLAN_TRIAL_INCONSISTENT\",\n PLAN_DRAFT: \"PLAN_DRAFT\",\n PLAN_STORE_MISMATCH: \"PLAN_STORE_MISMATCH\",\n NETWORK_ERROR: \"NETWORK_ERROR\",\n UNKNOWN: \"UNKNOWN\",\n} as const;\n\n/**\n * Build a `ValidationIssue` with defaults for the common case.\n * Extracted so every validator produces consistently shaped issues.\n */\nexport function issue(\n code: string,\n severity: ValidationSeverity,\n message: string,\n extras: { suggestedFix?: string; context?: ValidationIssue[\"context\"] } = {},\n): ValidationIssue {\n const base: ValidationIssue = { code, severity, message };\n if (extras.suggestedFix !== undefined) base.suggestedFix = extras.suggestedFix;\n if (extras.context !== undefined) base.context = extras.context;\n return base;\n}\n\n/**\n * Fold an issue list into a boolean. Used by every validator so `ok` is\n * computed the same way everywhere.\n */\nexport function isOk(issues: ValidationIssue[]): boolean {\n return !issues.some((entry) => entry.severity === \"error\");\n}\n\n/**\n * Compose a `ValidationResult` with the `ok` flag derived from issues.\n */\nexport function buildResult<T>(\n name: string,\n mode: ValidationResult[\"mode\"],\n issues: ValidationIssue[],\n resource?: T,\n target?: ValidationTarget,\n): ValidationResult<T> {\n const result: ValidationResult<T> = {\n name,\n ok: isOk(issues),\n mode,\n issues,\n };\n if (resource !== undefined) result.resource = resource;\n if (target !== undefined) result.target = target;\n return result;\n}\n","import { FreshSqueezyError } from \"../core/errors.js\";\nimport type { ValidationIssue } from \"../core/types.js\";\nimport { ISSUE_CODES, issue } from \"./rules.js\";\n\n/**\n * How a caller wants the error mapper to translate specific failure modes.\n * Every field is optional — omit a slot and that mode falls through to the\n * generic mappings (`NETWORK_ERROR`, `UNKNOWN`).\n */\nexport interface IssueMapping {\n /** Translate 401 / `UNAUTHORIZED` to a specific issue code (e.g. `AUTH_FAILED`). */\n unauthorized?: { code: string; message: string; suggestedFix?: string };\n /** Translate 404 to a resource-specific not-found issue. */\n notFound?: {\n code: string;\n message: string;\n suggestedFix?: string;\n context?: ValidationIssue[\"context\"];\n };\n}\n\n/**\n * Map any thrown error from a resource fetch to a `ValidationIssue`. The\n * single source of truth for \"how does this validator surface failures?\":\n *\n * - 401 → `mapping.unauthorized.code` if provided, else fall through\n * - 404 → `mapping.notFound.code` if provided, else fall through\n * - network failure → `NETWORK_ERROR`\n * - any other `FreshSqueezyError` → `UNKNOWN` (with status + code context)\n * - any other `Error` → `UNKNOWN`\n *\n * Public so validators that own a non-trivial composite call (e.g.\n * `validateConnection`, which fetches users + stores in one try-block) can\n * reuse the same mapper as `probeFetch` does for single-resource fetches.\n */\nexport function mapErrorToIssue(err: unknown, mapping: IssueMapping = {}): ValidationIssue {\n if (err instanceof FreshSqueezyError) {\n if (mapping.unauthorized && (err.status === 401 || err.code === \"UNAUTHORIZED\")) {\n return issue(mapping.unauthorized.code, \"error\", mapping.unauthorized.message, {\n ...(mapping.unauthorized.suggestedFix !== undefined\n ? { suggestedFix: mapping.unauthorized.suggestedFix }\n : {}),\n context: { status: err.status ?? null },\n });\n }\n if (mapping.notFound && err.status === 404) {\n return issue(mapping.notFound.code, \"error\", mapping.notFound.message, {\n ...(mapping.notFound.suggestedFix !== undefined\n ? { suggestedFix: mapping.notFound.suggestedFix }\n : {}),\n ...(mapping.notFound.context !== undefined ? { context: mapping.notFound.context } : {}),\n });\n }\n if (err.code === \"NETWORK_ERROR\") {\n return issue(\n ISSUE_CODES.NETWORK_ERROR,\n \"error\",\n `Could not reach Lemon Squeezy: ${err.message}`,\n );\n }\n return issue(ISSUE_CODES.UNKNOWN, \"error\", err.message, {\n context: { status: err.status ?? null, code: err.code },\n });\n }\n const message = err instanceof Error ? err.message : \"Unknown error\";\n return issue(ISSUE_CODES.UNKNOWN, \"error\", message);\n}\n\n/**\n * Per-validator metadata for the resource-fetch probe.\n *\n * `notFoundCode` and `notFoundMessage` are resource-specific so each validator\n * keeps its stable `ISSUE_CODES.*_NOT_FOUND` identifier in callsite-visible\n * code. Everything else (mapping `FreshSqueezyError` shapes, falling back to\n * `UNKNOWN`, attaching status/code context) is handled here so validators\n * stop re-implementing the same try/catch skeleton.\n */\nexport interface FetchProbeOptions {\n notFoundCode: string;\n notFoundMessage: string;\n notFoundFix?: string;\n notFoundContext?: ValidationIssue[\"context\"];\n}\n\n/**\n * Outcome of a probed resource fetch. Either the resource (caller continues\n * with assertions) or one normalized issue (caller bails with `buildResult`).\n */\nexport type FetchProbeOutcome<T> =\n | { ok: true; resource: T }\n | { ok: false; issue: ValidationIssue };\n\n/**\n * Run a resource fetch with the same error mapping every single-resource\n * validator needs. Delegates to `mapErrorToIssue` so the rules live in\n * exactly one place.\n *\n * Validators keep their resource-specific assertion logic; only the I/O\n * scaffolding lives here. The deletion test: if this module disappeared every\n * validator would grow back the same try/catch skeleton.\n */\nexport async function probeFetch<T>(\n fetcher: () => Promise<T>,\n options: FetchProbeOptions,\n): Promise<FetchProbeOutcome<T>> {\n return probeCollection(fetcher, {\n notFound: {\n code: options.notFoundCode,\n message: options.notFoundMessage,\n ...(options.notFoundFix !== undefined ? { suggestedFix: options.notFoundFix } : {}),\n ...(options.notFoundContext !== undefined ? { context: options.notFoundContext } : {}),\n },\n });\n}\n\n/**\n * Run a fetch — typically a list/collection endpoint — through the shared\n * error mapper without `probeFetch`'s single-resource `notFound` slot. Pass an\n * `IssueMapping` to translate specific statuses (e.g. 401 → `AUTH_FAILED` for\n * the connection validator); omit it and every failure normalizes to\n * `NETWORK_ERROR` / `UNKNOWN`.\n *\n * The deletion test: without this, `validateWebhook` and `validateConnection`\n * would each hand-roll the same try/catch → `mapErrorToIssue` skeleton that\n * `probeFetch` already centralizes for single-resource fetches.\n */\nexport async function probeCollection<T>(\n fetcher: () => Promise<T>,\n mapping: IssueMapping = {},\n): Promise<FetchProbeOutcome<T>> {\n try {\n return { ok: true, resource: await fetcher() };\n } catch (err) {\n return { ok: false, issue: mapErrorToIssue(err, mapping) };\n }\n}\n\n/**\n * Inputs for a store-ownership cross-check.\n *\n * `code` and `label` are resource-specific so the issue stays informative\n * (\"Discount belongs to store …\" vs \"Product belongs to store …\"). `id` and\n * `extraContext` let validators describe which resource was inspected so\n * downstream tooling can correlate the mismatch with the request that\n * produced it.\n */\nexport interface OwnershipCheck {\n expectedStoreId: string | number;\n actualStoreId: string | number;\n /** Stable issue code (e.g. `ISSUE_CODES.DISCOUNT_STORE_MISMATCH`). */\n code: string;\n /** Resource label used in the issue message (e.g. `\"Discount\"`). */\n label: string;\n suggestedFix?: string;\n /** Extra context fields merged in alongside `expectedStoreId` / `actualStoreId`. */\n extraContext?: ValidationIssue[\"context\"];\n}\n\n/**\n * Compare an expected store ID against the value Lemon Squeezy returned on\n * the resource. Returns a `ValidationIssue` if they don't match, or `null` if\n * ownership is fine. Validators add the issue to their issue list — this\n * helper does not push for them.\n *\n * Both inputs are coerced via `String(...)` so a numeric `store_id` from JSON\n * compares correctly against a string-typed `storeId` from the CLI / env.\n */\nexport function checkStoreOwnership(check: OwnershipCheck): ValidationIssue | null {\n const expected = String(check.expectedStoreId);\n const actual = String(check.actualStoreId);\n if (expected === actual) return null;\n\n const context: ValidationIssue[\"context\"] = {\n expectedStoreId: expected,\n actualStoreId: actual,\n ...(check.extraContext ?? {}),\n };\n\n return issue(\n check.code,\n \"error\",\n `${check.label} belongs to store ${actual}, expected ${expected}.`,\n {\n ...(check.suggestedFix !== undefined ? { suggestedFix: check.suggestedFix } : {}),\n context,\n },\n );\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport { resolveActualMode } from \"../core/mode.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { listStores } from \"../resources/stores.js\";\nimport { type UserAttributes, getAuthenticatedUser } from \"../resources/users.js\";\nimport { probeCollection } from \"./probe.js\";\nimport { ISSUE_CODES, buildResult, issue } from \"./rules.js\";\n\n/**\n * Connection validator summary attached to the `resource` field. Keeps the\n * validator self-contained — consumers need not call `users/me` again.\n *\n * `actualMode` is derived from the `meta.test_mode` field Lemon Squeezy added\n * to `/v1/users/me` on 2024-01-05 (API changelog). When the caller declared\n * one mode but the key actually belongs to the other, the validator fires a\n * `MODE_MISMATCH` error — the single misconfiguration most likely to cause a\n * prod-in-staging (or vice versa) incident.\n */\nexport interface ConnectionSummary {\n user: UserAttributes;\n storeCount: number;\n storeIds: string[];\n /** The mode the API key actually belongs to (per `/v1/users/me` meta). */\n actualMode?: Mode;\n /** The mode the caller asked for at construction time. */\n declaredMode: Mode;\n}\n\n/**\n * Verify that the API key works, surface the account identity + reachable\n * stores, and cross-check declared mode vs the key's true mode.\n *\n * This is the first check every `doctor()` run performs; if it fails,\n * no downstream validator has anything useful to report.\n */\nexport async function validateConnection(\n http: HttpClient,\n mode: Mode,\n): Promise<ValidationResult<ConnectionSummary>> {\n const issues: ValidationIssue[] = [];\n\n const fetched = await probeCollection(\n async () => {\n const userDoc = await getAuthenticatedUser(http);\n const stores = await listStores(http);\n return { userDoc, stores };\n },\n {\n unauthorized: {\n code: ISSUE_CODES.AUTH_FAILED,\n message: \"API key rejected by Lemon Squeezy.\",\n suggestedFix: \"Regenerate the key at https://app.lemonsqueezy.com/settings/api.\",\n },\n },\n );\n\n if (!fetched.ok) {\n issues.push(fetched.issue);\n return buildResult(\"connection\", mode, issues);\n }\n\n const { userDoc, stores } = fetched.resource;\n const actualMode = resolveActualMode(userDoc.meta?.test_mode);\n const summary: ConnectionSummary = {\n user: userDoc.data.attributes,\n storeCount: stores.length,\n storeIds: stores.map((store) => store.id),\n declaredMode: mode,\n ...(actualMode ? { actualMode } : {}),\n };\n\n if (actualMode && actualMode !== mode) {\n issues.push(\n issue(\n ISSUE_CODES.MODE_MISMATCH,\n \"error\",\n `API key is a ${actualMode}-mode key but was run with --mode ${mode}.`,\n {\n suggestedFix: `Either pass --mode ${actualMode} or use a ${mode}-mode key from https://app.lemonsqueezy.com/settings/api.`,\n context: { declared: mode, actual: actualMode },\n },\n ),\n );\n }\n\n if (stores.length === 0) {\n issues.push(\n issue(\n ISSUE_CODES.STORE_NOT_FOUND,\n \"warning\",\n \"API key authenticated but no stores are reachable.\",\n { suggestedFix: \"Confirm the API key belongs to an account that owns at least one store.\" },\n ),\n );\n }\n\n return buildResult(\"connection\", mode, issues, summary);\n}\n\n/**\n * Ask the Lemon Squeezy API which mode the configured key actually belongs to.\n *\n * Useful as a fail-fast check at app boot — e.g. refuse to start if a prod key\n * is loaded into a staging deployment — without paying for a full doctor run.\n * Returns `undefined` if the API doesn't surface `meta.test_mode` (older\n * proxies, partial responses).\n *\n * Lives here rather than in `core/mode.ts` so the foundational `core/` layer\n * never imports from `resources/`; the pure `resolveActualMode` it delegates to\n * still lives in `core/`. Throws `FreshSqueezyError` on auth or network\n * failure; callers that want a structured non-throwing result should use\n * `validateConnection` instead.\n */\nexport async function fetchActualMode(http: HttpClient): Promise<Mode | undefined> {\n const doc = await getAuthenticatedUser(http);\n return resolveActualMode(doc.meta?.test_mode);\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedDiscountAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Subset of Lemon Squeezy discount attributes used by the discount validator.\n * Full schema at https://docs.lemonsqueezy.com/api/discounts.\n */\nexport interface DiscountAttributes extends GeneratedDiscountAttributes {\n name: string;\n code: string;\n amount: number;\n amount_type: \"percent\" | \"fixed\";\n is_limited_to_products: boolean;\n is_limited_redemptions: boolean;\n max_redemptions: number;\n starts_at: string | null;\n expires_at: string | null;\n status: \"draft\" | \"published\";\n duration: \"once\" | \"repeating\" | \"forever\";\n store_id: number;\n created_at?: string;\n updated_at?: string;\n}\n\n/**\n * Fetch a single discount by ID. The validator uses the discount's\n * `relationships.store` to confirm ownership against the caller's storeId.\n */\nexport async function getDiscount(\n http: HttpClient,\n discountId: string | number,\n): Promise<JsonApiResource<DiscountAttributes>> {\n return http.getResource<DiscountAttributes>(`/v1/discounts/${discountId}`);\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { type DiscountAttributes, getDiscount } from \"../resources/discounts.js\";\nimport { checkStoreOwnership, probeFetch } from \"./probe.js\";\nimport { ISSUE_CODES, buildResult, issue } from \"./rules.js\";\n\nexport interface DiscountValidationOptions {\n storeId: string | number;\n discountId: string | number;\n}\n\n/**\n * Validate a Lemon Squeezy discount code. Catches the most common\n * misconfigurations: draft discounts that can't be redeemed, expired or\n * not-yet-active windows, invalid amounts (negative or >100% for percent\n * type), and store ownership mismatches.\n *\n * The redemption exhaustion check is deliberately conservative (v1): it only\n * flags when `is_limited_redemptions` is true and `max_redemptions` is zero,\n * because a full redemption count fetch against\n * `/v1/discount-redemptions?filter[discount_id]=` is YAGNI until a consumer\n * asks for it.\n */\nexport async function validateDiscount(\n http: HttpClient,\n mode: Mode,\n options: DiscountValidationOptions,\n): Promise<ValidationResult<DiscountAttributes>> {\n const issues: ValidationIssue[] = [];\n\n const fetched = await probeFetch(() => getDiscount(http, options.discountId), {\n notFoundCode: ISSUE_CODES.DISCOUNT_NOT_FOUND,\n notFoundMessage: `Discount ${options.discountId} not found.`,\n notFoundFix: \"Verify the discount ID in the Lemon Squeezy dashboard.\",\n notFoundContext: { discountId: String(options.discountId) },\n });\n\n if (!fetched.ok) {\n issues.push(fetched.issue);\n return buildResult<DiscountAttributes>(\"discount\", mode, issues, undefined, {\n label: `discount ${options.discountId}`,\n id: String(options.discountId),\n });\n }\n\n const attrs = fetched.resource.attributes;\n issues.push(...checkDiscount(attrs, { storeId: options.storeId }));\n\n return buildResult(\"discount\", mode, issues, attrs, {\n label: `${attrs.name} (${attrs.code})`,\n id: String(options.discountId),\n });\n}\n\n/**\n * Pure attribute assertions for a discount: store ownership, draft/expiry/\n * activation windows, redemption exhaustion, and amount validity. No I/O, so\n * the rules are unit-testable with plain data.\n */\nexport function checkDiscount(\n attrs: DiscountAttributes,\n options: { storeId: string | number },\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n const mismatch = checkStoreOwnership({\n expectedStoreId: options.storeId,\n actualStoreId: attrs.store_id,\n code: ISSUE_CODES.DISCOUNT_STORE_MISMATCH,\n label: \"Discount\",\n suggestedFix: \"Use the correct store ID or discount ID — discounts should not cross stores.\",\n });\n if (mismatch) issues.push(mismatch);\n\n if (attrs.status === \"draft\") {\n issues.push(\n issue(\n ISSUE_CODES.DISCOUNT_DRAFT,\n \"warning\",\n `Discount \"${attrs.name}\" is in draft status — customers cannot redeem it.`,\n {\n suggestedFix:\n \"Publish the discount in the Lemon Squeezy dashboard before sharing the code.\",\n context: { name: attrs.name, code: attrs.code },\n },\n ),\n );\n }\n\n const now = new Date();\n if (attrs.expires_at && new Date(attrs.expires_at) < now) {\n issues.push(\n issue(\n ISSUE_CODES.DISCOUNT_EXPIRED,\n \"error\",\n `Discount \"${attrs.name}\" expired at ${attrs.expires_at}.`,\n {\n suggestedFix: \"Extend the expiration date or create a new discount.\",\n context: { name: attrs.name, expiresAt: attrs.expires_at },\n },\n ),\n );\n }\n\n if (attrs.starts_at && new Date(attrs.starts_at) > now) {\n issues.push(\n issue(\n ISSUE_CODES.DISCOUNT_NOT_STARTED,\n \"warning\",\n `Discount \"${attrs.name}\" starts at ${attrs.starts_at} — not yet active.`,\n {\n suggestedFix: \"Wait for the start date or adjust it in the dashboard.\",\n context: { name: attrs.name, startsAt: attrs.starts_at },\n },\n ),\n );\n }\n\n if (attrs.is_limited_redemptions && attrs.max_redemptions <= 0) {\n issues.push(\n issue(\n ISSUE_CODES.DISCOUNT_REDEMPTIONS_EXHAUSTED,\n \"warning\",\n `Discount \"${attrs.name}\" has limited redemptions with max_redemptions ≤ 0.`,\n {\n suggestedFix: \"Increase max_redemptions or disable the redemption limit.\",\n context: { name: attrs.name, maxRedemptions: attrs.max_redemptions },\n },\n ),\n );\n }\n\n if (attrs.amount <= 0) {\n issues.push(\n issue(\n ISSUE_CODES.DISCOUNT_INVALID_AMOUNT,\n \"error\",\n `Discount \"${attrs.name}\" has amount ${attrs.amount} — must be positive.`,\n {\n suggestedFix: \"Set a positive discount amount in the dashboard.\",\n context: { name: attrs.name, amount: attrs.amount },\n },\n ),\n );\n } else if (attrs.amount_type === \"percent\" && attrs.amount > 100) {\n issues.push(\n issue(\n ISSUE_CODES.DISCOUNT_INVALID_AMOUNT,\n \"error\",\n `Discount \"${attrs.name}\" is ${attrs.amount}% — percent discounts cannot exceed 100%.`,\n {\n suggestedFix: \"Set the discount to 100% or less.\",\n context: { name: attrs.name, amount: attrs.amount, amountType: attrs.amount_type },\n },\n ),\n );\n }\n\n return issues;\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedLicenseKeyAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Subset of Lemon Squeezy license-key attributes used by the license key\n * validator. Full schema at https://docs.lemonsqueezy.com/api/license-keys.\n */\nexport interface LicenseKeyAttributes extends GeneratedLicenseKeyAttributes {\n key_short: string;\n status: \"active\" | \"inactive\" | \"expired\" | \"disabled\";\n expires_at: string | null;\n activation_limit: number | null;\n instances_count: number;\n disabled: boolean;\n store_id: number;\n created_at?: string;\n updated_at?: string;\n}\n\n/**\n * Fetch a single license key by ID. Used by the license key validator to\n * check activation limits, expiration, and store ownership.\n */\nexport async function getLicenseKey(\n http: HttpClient,\n licenseKeyId: string | number,\n): Promise<JsonApiResource<LicenseKeyAttributes>> {\n return http.getResource<LicenseKeyAttributes>(`/v1/license-keys/${licenseKeyId}`);\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { type LicenseKeyAttributes, getLicenseKey } from \"../resources/licenseKeys.js\";\nimport { checkStoreOwnership, probeFetch } from \"./probe.js\";\nimport { ISSUE_CODES, buildResult, issue } from \"./rules.js\";\n\nexport interface LicenseKeyValidationOptions {\n storeId: string | number;\n licenseKeyId: string | number;\n}\n\n/**\n * Validate a Lemon Squeezy license key. Surfaces disabled keys, expired\n * keys, keys at their activation limit, and store ownership mismatches — the\n * four states most likely to cause \"why can't my customer activate?\"\n * support tickets.\n */\nexport async function validateLicenseKey(\n http: HttpClient,\n mode: Mode,\n options: LicenseKeyValidationOptions,\n): Promise<ValidationResult<LicenseKeyAttributes>> {\n const issues: ValidationIssue[] = [];\n\n const fetched = await probeFetch(() => getLicenseKey(http, options.licenseKeyId), {\n notFoundCode: ISSUE_CODES.LICENSE_KEY_NOT_FOUND,\n notFoundMessage: `License key ${options.licenseKeyId} not found.`,\n notFoundFix: \"Verify the license key ID in the Lemon Squeezy dashboard.\",\n notFoundContext: { licenseKeyId: String(options.licenseKeyId) },\n });\n\n if (!fetched.ok) {\n issues.push(fetched.issue);\n return buildResult<LicenseKeyAttributes>(\"licenseKey\", mode, issues, undefined, {\n label: `license key ${options.licenseKeyId}`,\n id: String(options.licenseKeyId),\n });\n }\n\n const attrs = fetched.resource.attributes;\n issues.push(...checkLicenseKey(attrs, { storeId: options.storeId }));\n\n return buildResult(\"licenseKey\", mode, issues, attrs, {\n label: attrs.key_short,\n id: String(options.licenseKeyId),\n });\n}\n\n/**\n * Pure attribute assertions for a license key: store ownership, disabled/\n * expired state, and activation-limit exhaustion. No I/O, so the rules are\n * unit-testable with plain data.\n */\nexport function checkLicenseKey(\n attrs: LicenseKeyAttributes,\n options: { storeId: string | number },\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n const mismatch = checkStoreOwnership({\n expectedStoreId: options.storeId,\n actualStoreId: attrs.store_id,\n code: ISSUE_CODES.LICENSE_KEY_STORE_MISMATCH,\n label: \"License key\",\n suggestedFix: \"Use the correct store ID or license key ID — keys should not cross stores.\",\n });\n if (mismatch) issues.push(mismatch);\n\n if (attrs.disabled) {\n issues.push(\n issue(\n ISSUE_CODES.LICENSE_KEY_DISABLED,\n \"error\",\n `License key ${attrs.key_short} is disabled.`,\n {\n suggestedFix: \"Re-enable the license key in the Lemon Squeezy dashboard.\",\n context: { keyShort: attrs.key_short },\n },\n ),\n );\n }\n\n if (attrs.expires_at && new Date(attrs.expires_at) < new Date()) {\n issues.push(\n issue(\n ISSUE_CODES.LICENSE_KEY_EXPIRED,\n \"error\",\n `License key ${attrs.key_short} expired at ${attrs.expires_at}.`,\n {\n suggestedFix: \"Extend the expiration date or issue a new license key.\",\n context: { keyShort: attrs.key_short, expiresAt: attrs.expires_at },\n },\n ),\n );\n }\n\n if (attrs.activation_limit !== null && attrs.instances_count >= attrs.activation_limit) {\n issues.push(\n issue(\n ISSUE_CODES.LICENSE_KEY_AT_ACTIVATION_LIMIT,\n \"warning\",\n `License key ${attrs.key_short} has reached its activation limit (${attrs.instances_count}/${attrs.activation_limit}).`,\n {\n suggestedFix: \"Increase the activation limit or deactivate unused instances.\",\n context: {\n keyShort: attrs.key_short,\n instancesCount: attrs.instances_count,\n activationLimit: attrs.activation_limit,\n },\n },\n ),\n );\n }\n\n return issues;\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedProductAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Subset of product attributes we need for validation. `status` drives the\n * \"unpublished product\" check; `store_id` drives ownership checks.\n */\nexport interface ProductAttributes extends GeneratedProductAttributes {\n name: string;\n slug: string;\n description?: string | null;\n status: \"draft\" | \"published\";\n status_formatted?: string;\n store_id: number;\n buy_now_url?: string | null;\n from_price?: number | null;\n to_price?: number | null;\n created_at?: string;\n updated_at?: string;\n}\n\nexport async function getProduct(\n http: HttpClient,\n productId: string | number,\n): Promise<JsonApiResource<ProductAttributes>> {\n return http.getResource<ProductAttributes>(`/v1/products/${productId}`);\n}\n\n/**\n * List every product in the store, paginated. Used by the CLI's\n * interactive product picker; without pagination only the first 25\n * products are offered.\n */\nexport async function listProducts(\n http: HttpClient,\n storeId: string | number,\n): Promise<JsonApiResource<ProductAttributes>[]> {\n return http.paginate<ProductAttributes>(\"/v1/products\", {\n \"filter[store_id]\": String(storeId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedVariantAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport interface VariantLink {\n title: string;\n url: string;\n}\n\n/**\n * Variant attributes used by the product validator to detect\n * variant/price drift from the product they belong to.\n */\nexport interface VariantAttributes extends GeneratedVariantAttributes {\n product_id: number;\n name: string;\n slug: string;\n description?: string | null;\n has_license_keys?: boolean;\n license_activation_limit?: number | null;\n is_license_limit_unlimited?: boolean;\n license_length_value?: number | null;\n license_length_unit?: \"day\" | \"week\" | \"month\" | \"year\" | null;\n is_license_length_unlimited?: boolean;\n status: \"pending\" | \"draft\" | \"published\";\n status_formatted?: string;\n is_subscription?: boolean;\n interval?: string | null;\n interval_count?: number | null;\n sort?: number;\n /**\n * Hosted-checkout-style links keyed by purpose. Added 2024-06-09 — older\n * SDK and validator versions don't surface this field, so consumers had\n * to read `meta.product_links` manually before. Optional because the\n * field is absent on older variants.\n */\n links?: VariantLink[] | Record<string, string>;\n created_at?: string;\n updated_at?: string;\n test_mode?: boolean;\n}\n\n/**\n * Extended variant attributes for subscription plan validation. In Lemon\n * Squeezy, \"subscription plans\" live as variants with `is_subscription: true`.\n * These fields are only present on subscription variants and are checked by\n * the subscription plan validator to catch misconfigured trial periods,\n * zero-price plans, and invalid billing intervals.\n */\nexport interface SubscriptionVariantAttributes extends VariantAttributes {\n is_subscription: boolean;\n interval: string | null;\n interval_count: number | null;\n has_free_trial: boolean;\n trial_interval: string | null;\n trial_interval_count: number | null;\n price: number;\n}\n\n/**\n * Fetch a single variant by ID. Used by the subscription plan validator to\n * inspect subscription-specific fields (interval, trial, price).\n */\nexport async function getVariant<TAttr = VariantAttributes>(\n http: HttpClient,\n variantId: string | number,\n): Promise<JsonApiResource<TAttr>> {\n return http.getResource<TAttr>(`/v1/variants/${variantId}`);\n}\n\n/**\n * List every variant of the product, paginated so products with more than\n * one page of variants don't trip the `VARIANT_UNPUBLISHED` heuristic when\n * the only published variants happen to live past page 1.\n */\nexport async function listVariantsForProduct(\n http: HttpClient,\n productId: string | number,\n): Promise<JsonApiResource<VariantAttributes>[]> {\n return http.paginate<VariantAttributes>(\"/v1/variants\", {\n \"filter[product_id]\": String(productId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { type ProductAttributes, getProduct } from \"../resources/products.js\";\nimport { listVariantsForProduct } from \"../resources/variants.js\";\nimport { checkStoreOwnership, probeFetch } from \"./probe.js\";\nimport { ISSUE_CODES, buildResult, issue } from \"./rules.js\";\n\nexport interface ProductValidationOptions {\n productId: string | number;\n /** Optional: confirm the product belongs to this store. */\n expectedStoreId?: string | number;\n}\n\n/**\n * Validate a product's publish state, store ownership, and that it has at\n * least one published variant. Surfaces the most common misconfigurations\n * caught in the wild (unpublished product, wrong store, missing variants).\n */\nexport async function validateProduct(\n http: HttpClient,\n mode: Mode,\n options: ProductValidationOptions,\n): Promise<ValidationResult<ProductAttributes>> {\n const issues: ValidationIssue[] = [];\n\n const fetched = await probeFetch(() => getProduct(http, options.productId), {\n notFoundCode: ISSUE_CODES.PRODUCT_NOT_FOUND,\n notFoundMessage: `Product ${options.productId} not found.`,\n notFoundFix: \"Verify the product ID in the Lemon Squeezy dashboard.\",\n notFoundContext: { productId: String(options.productId) },\n });\n\n if (!fetched.ok) {\n issues.push(fetched.issue);\n return buildResult<ProductAttributes>(\"product\", mode, issues, undefined, {\n label: `product ${options.productId}`,\n id: String(options.productId),\n });\n }\n\n const attrs = fetched.resource.attributes;\n issues.push(...checkProduct(attrs, { expectedStoreId: options.expectedStoreId }));\n\n // Variant population needs a second fetch, so it stays out of the pure check.\n try {\n const variants = await listVariantsForProduct(http, options.productId);\n if (variants.length === 0) {\n issues.push(\n issue(\n ISSUE_CODES.VARIANT_MISSING,\n \"error\",\n \"Product has no variants. Customers cannot purchase it.\",\n { suggestedFix: \"Add at least one variant in the product configuration.\" },\n ),\n );\n } else if (!variants.some((variant) => variant.attributes.status === \"published\")) {\n issues.push(\n issue(\n ISSUE_CODES.VARIANT_UNPUBLISHED,\n \"error\",\n \"Product has variants but none are published.\",\n { suggestedFix: \"Publish at least one variant.\" },\n ),\n );\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Unknown error fetching variants\";\n issues.push(issue(ISSUE_CODES.UNKNOWN, \"warning\", message));\n }\n\n return buildResult(\"product\", mode, issues, attrs, {\n label: attrs.name,\n id: String(options.productId),\n });\n}\n\n/**\n * Pure attribute assertions for a product: store ownership, publish state, and\n * hosted-checkout availability. The variant-population rule needs a second\n * fetch and stays in `validateProduct`; everything here runs on plain data, so\n * the rules are unit-testable without a mock fetch.\n */\nexport function checkProduct(\n attrs: ProductAttributes,\n options: { expectedStoreId?: string | number } = {},\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n if (options.expectedStoreId !== undefined) {\n const mismatch = checkStoreOwnership({\n expectedStoreId: options.expectedStoreId,\n actualStoreId: attrs.store_id,\n code: ISSUE_CODES.PRODUCT_WRONG_STORE,\n label: \"Product\",\n suggestedFix:\n \"Either use the correct store ID or the correct product ID — IDs should not cross stores.\",\n });\n if (mismatch) issues.push(mismatch);\n }\n\n if (attrs.status !== \"published\") {\n issues.push(\n issue(\n ISSUE_CODES.PRODUCT_UNPUBLISHED,\n \"error\",\n `Product is in \"${attrs.status}\" state, not \"published\".`,\n {\n suggestedFix: \"Publish the product in the Lemon Squeezy dashboard before selling.\",\n context: { status: attrs.status },\n },\n ),\n );\n }\n\n if (!attrs.buy_now_url) {\n issues.push(\n issue(\n ISSUE_CODES.PRODUCT_NO_BUY_URL,\n \"warning\",\n \"Product has no buy-now URL. Hosted checkout may be disabled.\",\n { suggestedFix: \"Enable buy-now in product settings, or use a custom checkout flow.\" },\n ),\n );\n }\n\n return issues;\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { type StoreAttributes, getStore } from \"../resources/stores.js\";\nimport { probeFetch } from \"./probe.js\";\nimport { ISSUE_CODES, buildResult } from \"./rules.js\";\n\n/**\n * Verify a store exists and is reachable with the current API key. A 404\n * typically means the key belongs to a different account, not that the store\n * is gone — the suggested fix reflects that.\n */\nexport async function validateStore(\n http: HttpClient,\n mode: Mode,\n storeId: string | number,\n): Promise<ValidationResult<StoreAttributes>> {\n const issues: ValidationIssue[] = [];\n\n const fetched = await probeFetch(() => getStore(http, storeId), {\n notFoundCode: ISSUE_CODES.STORE_NOT_FOUND,\n notFoundMessage: `Store ${storeId} not found with the current API key.`,\n notFoundFix: \"Check the store ID and confirm the API key belongs to the account that owns it.\",\n notFoundContext: { storeId: String(storeId) },\n });\n\n if (!fetched.ok) {\n issues.push(fetched.issue);\n return buildResult<StoreAttributes>(\"store\", mode, issues, undefined, {\n label: `store ${storeId}`,\n id: String(storeId),\n });\n }\n\n return buildResult(\"store\", mode, issues, fetched.resource.attributes, {\n label: fetched.resource.attributes.name,\n id: String(storeId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { getProduct } from \"../resources/products.js\";\nimport { type SubscriptionVariantAttributes, getVariant } from \"../resources/variants.js\";\nimport { checkStoreOwnership, probeFetch } from \"./probe.js\";\nimport { ISSUE_CODES, buildResult, issue } from \"./rules.js\";\n\nexport interface SubscriptionPlanValidationOptions {\n storeId: string | number;\n variantId: string | number;\n}\n\n/** Summary of a subscription plan variant's validated state. */\nexport interface SubscriptionPlanSummary {\n variantId: string;\n interval: string | null;\n intervalCount: number | null;\n price: number;\n hasFreeTrial: boolean;\n status: string;\n}\n\nconst VALID_INTERVALS = new Set([\"day\", \"week\", \"month\", \"year\"]);\n\n/**\n * Validate a Lemon Squeezy \"subscription plan\", which is a variant with\n * `is_subscription: true`. Checks that the variant is actually a subscription,\n * has a valid billing interval, and that trial settings are consistent.\n *\n * Store cross-check requires fetching the parent product to read its\n * `store_id`. This is an extra network hop but catches the common mistake of\n * passing a variant ID from one store while targeting another. Accept the\n * `storeId` as advisory — if the product fetch fails the cross-check is\n * silently skipped rather than blocking the entire validation.\n */\nexport async function validateSubscriptionPlan(\n http: HttpClient,\n mode: Mode,\n options: SubscriptionPlanValidationOptions,\n): Promise<ValidationResult<SubscriptionPlanSummary>> {\n const issues: ValidationIssue[] = [];\n\n const fetched = await probeFetch(\n () => getVariant<SubscriptionVariantAttributes>(http, options.variantId),\n {\n notFoundCode: ISSUE_CODES.PLAN_VARIANT_NOT_FOUND,\n notFoundMessage: `Variant ${options.variantId} not found.`,\n notFoundFix: \"Verify the variant ID in the Lemon Squeezy dashboard.\",\n notFoundContext: { variantId: String(options.variantId) },\n },\n );\n\n if (!fetched.ok) {\n issues.push(fetched.issue);\n return buildResult<SubscriptionPlanSummary>(\"subscriptionPlan\", mode, issues, undefined, {\n label: `variant ${options.variantId}`,\n id: String(options.variantId),\n });\n }\n\n const variant = fetched.resource;\n const attrs = variant.attributes;\n\n issues.push(...checkSubscriptionPlan(attrs, { variantId: options.variantId }));\n\n // Store cross-check: fetch the parent product to compare store ownership.\n // Needs a second fetch, so it stays out of the pure check. If the fetch\n // fails, skip the check rather than blocking the whole validation.\n try {\n const product = await getProduct(http, attrs.product_id);\n const mismatch = checkStoreOwnership({\n expectedStoreId: options.storeId,\n actualStoreId: product.attributes.store_id,\n code: ISSUE_CODES.PLAN_STORE_MISMATCH,\n label: `Subscription variant (via product ${attrs.product_id})`,\n suggestedFix: \"Use the correct store ID or variant ID — plans should not cross stores.\",\n extraContext: { productId: String(attrs.product_id) },\n });\n if (mismatch) issues.push(mismatch);\n } catch {\n // Intentionally silent — the product fetch is advisory for the store\n // cross-check and should not block the rest of the validation.\n }\n\n const summary: SubscriptionPlanSummary = {\n variantId: variant.id,\n interval: attrs.interval,\n intervalCount: attrs.interval_count,\n price: attrs.price,\n hasFreeTrial: attrs.has_free_trial,\n status: attrs.status,\n };\n\n return buildResult(\"subscriptionPlan\", mode, issues, summary, {\n label: attrs.name,\n id: String(options.variantId),\n });\n}\n\n/**\n * Pure attribute assertions for a subscription variant: that it is actually a\n * subscription, has a valid interval / interval_count, is not free-priced, has\n * consistent trial settings, and is not a draft. The store cross-check needs a\n * second fetch and stays in `validateSubscriptionPlan`; everything here runs on\n * plain data, so the rules are unit-testable without a mock fetch.\n */\nexport function checkSubscriptionPlan(\n attrs: SubscriptionVariantAttributes,\n options: { variantId: string | number },\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n if (!attrs.is_subscription) {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_NOT_SUBSCRIPTION,\n \"error\",\n `Variant ${options.variantId} is not a subscription variant (is_subscription is false).`,\n {\n suggestedFix:\n \"Use a variant that has subscription billing enabled, or use the regular variant validator.\",\n context: { variantId: String(options.variantId) },\n },\n ),\n );\n }\n\n if (!attrs.interval || !VALID_INTERVALS.has(attrs.interval)) {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_INVALID_INTERVAL,\n \"error\",\n `Subscription variant has invalid interval: \"${attrs.interval ?? \"missing\"}\". Expected one of: day, week, month, year.`,\n {\n suggestedFix: \"Set a valid billing interval in the variant configuration.\",\n context: { interval: attrs.interval ?? null },\n },\n ),\n );\n }\n\n if (attrs.interval_count === null || attrs.interval_count <= 0) {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_INVALID_INTERVAL,\n \"error\",\n `Subscription variant has invalid interval_count: ${attrs.interval_count}. Must be a positive integer.`,\n {\n suggestedFix:\n \"Set interval_count to a positive value (e.g. 1 for monthly, 2 for biweekly).\",\n context: { intervalCount: attrs.interval_count },\n },\n ),\n );\n }\n\n if (attrs.price === 0 && attrs.is_subscription) {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_FREE_PRICE,\n \"warning\",\n \"Subscription variant has a price of 0 — this is almost always a misconfiguration for paid plans.\",\n {\n suggestedFix:\n \"Set the variant price to the intended amount in cents, or confirm this is intentionally free.\",\n context: { price: attrs.price },\n },\n ),\n );\n }\n\n if (attrs.has_free_trial && (!attrs.trial_interval || (attrs.trial_interval_count ?? 0) <= 0)) {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_TRIAL_INCONSISTENT,\n \"warning\",\n `Subscription variant has free trial enabled but trial interval is misconfigured (interval: \"${attrs.trial_interval ?? \"missing\"}\", count: ${attrs.trial_interval_count ?? 0}).`,\n {\n suggestedFix: \"Set a valid trial interval and count, or disable the free trial.\",\n context: {\n trialInterval: attrs.trial_interval ?? null,\n trialIntervalCount: attrs.trial_interval_count ?? null,\n },\n },\n ),\n );\n }\n\n if (attrs.status === \"draft\") {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_DRAFT,\n \"warning\",\n \"Subscription variant is in draft status — customers cannot subscribe.\",\n {\n suggestedFix: \"Publish the variant in the Lemon Squeezy dashboard.\",\n context: { status: attrs.status },\n },\n ),\n );\n }\n\n return issues;\n}\n","/**\n * Domain-equality primitives for fresh-squeezy.\n *\n * These helpers encode \"when are two values the same in our domain?\" — separate\n * from the validators that consume them so the rules are testable on their own\n * and reusable from any future surface (init, dedupe, repair, etc.).\n */\n\n/**\n * Compare two webhook URLs ignoring trailing slashes and casing.\n *\n * Lemon Squeezy strips trailing slashes when persisting a webhook, but users\n * often paste the trailing-slash form into their config. The webhook validator\n * relies on this rule to match a configured URL against the registered list.\n */\nexport function sameWebhookUrl(a: string, b: string): boolean {\n return normalizeWebhookUrl(a) === normalizeWebhookUrl(b);\n}\n\nfunction normalizeWebhookUrl(raw: string): string {\n return raw.replace(/\\/+$/, \"\").toLowerCase();\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedWebhookAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Subset of webhook attributes we read. `events` is an ordered list of\n * subscribed event names; the validator cross-references these against the\n * support manifest to catch missing subscriptions.\n */\nexport interface WebhookAttributes extends GeneratedWebhookAttributes {\n store_id: number;\n url: string;\n events: string[];\n last_sent_at?: string | null;\n created_at?: string;\n updated_at?: string;\n test_mode?: boolean;\n}\n\n/**\n * List every webhook registered on the store, walking pagination so a store\n * with more than one page of webhooks doesn't produce a false\n * `WEBHOOK_NOT_FOUND` from `validateWebhook`.\n */\nexport async function listWebhooksForStore(\n http: HttpClient,\n storeId: string | number,\n): Promise<JsonApiResource<WebhookAttributes>[]> {\n return http.paginate<WebhookAttributes>(\"/v1/webhooks\", {\n \"filter[store_id]\": String(storeId),\n });\n}\n","/* eslint-disable */\n/**\n * AUTO-GENERATED by scripts/generate-api-types.mjs.\n * Do not edit this file directly; run `npm run generate:api-types`.\n *\n * Source index: https://docs.lemonsqueezy.com/api/getting-started/requests\n */\n\nexport const GENERATED_LEMON_SQUEEZY_RESOURCES = [\n \"users\",\n \"stores\",\n \"customers\",\n \"products\",\n \"variants\",\n \"prices\",\n \"files\",\n \"orders\",\n \"order-items\",\n \"subscriptions\",\n \"subscription-invoices\",\n \"subscription-items\",\n \"usage-records\",\n \"discounts\",\n \"discount-redemptions\",\n \"license-keys\",\n \"license-key-instances\",\n \"checkouts\",\n \"webhooks\",\n \"affiliates\",\n] as const;\n\nexport const GENERATED_LEMON_SQUEEZY_TYPE_SOURCE = {\n user: \"https://docs.lemonsqueezy.com/api/users/the-user-object\",\n store: \"https://docs.lemonsqueezy.com/api/stores/the-store-object\",\n customer: \"https://docs.lemonsqueezy.com/api/customers/the-customer-object\",\n product: \"https://docs.lemonsqueezy.com/api/products/the-product-object\",\n variant: \"https://docs.lemonsqueezy.com/api/variants/the-variant-object\",\n price: \"https://docs.lemonsqueezy.com/api/prices/the-price-object\",\n file: \"https://docs.lemonsqueezy.com/api/files/the-file-object\",\n order: \"https://docs.lemonsqueezy.com/api/orders/the-order-object\",\n orderItem: \"https://docs.lemonsqueezy.com/api/order-items/the-order-item-object\",\n subscription: \"https://docs.lemonsqueezy.com/api/subscriptions/the-subscription-object\",\n subscriptionInvoice: \"https://docs.lemonsqueezy.com/api/subscription-invoices/the-subscription-invoice-object\",\n subscriptionItem: \"https://docs.lemonsqueezy.com/api/subscription-items/the-subscription-item-object\",\n usageRecord: \"https://docs.lemonsqueezy.com/api/usage-records/the-usage-record-object\",\n discount: \"https://docs.lemonsqueezy.com/api/discounts/the-discount-object\",\n discountRedemption: \"https://docs.lemonsqueezy.com/api/discount-redemptions/the-discount-redemption-object\",\n licenseKey: \"https://docs.lemonsqueezy.com/api/license-keys/the-license-key-object\",\n licenseKeyInstance: \"https://docs.lemonsqueezy.com/api/license-key-instances/the-license-key-instance-object\",\n checkout: \"https://docs.lemonsqueezy.com/api/checkouts/the-checkout-object\",\n webhook: \"https://docs.lemonsqueezy.com/api/webhooks/the-webhook-object\",\n affiliate: \"https://docs.lemonsqueezy.com/api/affiliates/the-affiliate-object\",\n} as const;\n\nexport interface GeneratedUserAttributes {\n avatar_url?: string | null;\n color?: string | null;\n createdAt?: string | null;\n email?: string | null;\n has_custom_avatar?: boolean | null;\n name?: string | null;\n updatedAt?: string | null;\n}\n\nexport interface GeneratedStoreAttributes {\n avatar_url?: string | null;\n country?: string | null;\n country_nicename?: string | null;\n created_at?: string | null;\n currency?: string | null;\n domain?: string | null;\n name?: string | null;\n plan?: string | null;\n slug?: string | null;\n thirty_day_revenue?: number | null;\n thirty_day_sales?: number | null;\n total_revenue?: number | null;\n total_sales?: number | null;\n updated_at?: string | null;\n url?: string | null;\n}\n\nexport interface GeneratedCustomerAttributes {\n city?: string | null;\n country?: string | null;\n country_formatted?: string | null;\n created_at?: string | null;\n email?: string | null;\n mrr?: number | null;\n mrr_formatted?: string | null;\n name?: string | null;\n region?: string | null;\n status?: string | null;\n status_formatted?: string | null;\n store_id?: number | null;\n test_mode?: boolean | null;\n total_revenue_currency?: number | null;\n total_revenue_currency_formatted?: string | null;\n updated_at?: string | null;\n urls?: { customer_portal?: string | null; } | null;\n}\n\nexport interface GeneratedProductAttributes {\n buy_now_url?: string | null;\n created_at?: string | null;\n description?: string | null;\n from_price?: number | null;\n from_price_formatted?: number | null;\n large_thumb_url?: string | null;\n name?: string | null;\n pay_what_you_want?: boolean | null;\n price?: number | null;\n price_formatted?: string | null;\n slug?: string | null;\n status?: string | null;\n status_formatted?: string | null;\n store_id?: number | null;\n test_mode?: boolean | null;\n thumb_url?: string | null;\n to_price?: number | null;\n to_price_formatted?: number | null;\n updated_at?: string | null;\n}\n\nexport interface GeneratedVariantAttributes {\n created_at?: string | null;\n description?: string | null;\n has_free_trial?: boolean | null;\n has_license_keys?: boolean | null;\n interval?: string | null;\n interval_count?: number | null;\n is_license_length_unlimited?: boolean | null;\n is_license_limit_unlimited?: boolean | null;\n is_subscription?: boolean | null;\n license_activation_limit?: number | null;\n license_length_unit?: string | null;\n license_length_value?: number | null;\n min_price?: number | null;\n name?: string | null;\n pay_what_you_want?: boolean | null;\n price?: number | null;\n product_id?: number | null;\n slug?: string | null;\n sort?: number | null;\n status?: string | null;\n status_formatted?: string | null;\n suggested_price?: number | null;\n test_mode?: boolean | null;\n trial_interval?: string | null;\n trial_interval_count?: number | null;\n updated_at?: string | null;\n}\n\nexport interface GeneratedPriceAttributes {\n category?: string | null;\n created_at?: string | null;\n min_price?: number | null;\n package_size?: number | null;\n renewal_interval_quantity?: number | null;\n renewal_interval_unit?: string | null;\n scheme?: string | null;\n setup_fee?: number | null;\n setup_fee_enabled?: boolean | null;\n suggested_price?: number | null;\n tax_code?: string | null;\n tiers?: unknown;\n trial_interval_quantity?: number | null;\n trial_interval_unit?: string | null;\n unit_price?: number | null;\n unit_price_decimal?: string | number | null;\n updated_at?: string | null;\n usage_aggregation?: unknown | null;\n variant_id?: number | null;\n}\n\nexport interface GeneratedFileAttributes {\n createdAt?: string | null;\n download_url?: string | null;\n extension?: string | null;\n identifier?: string | null;\n name?: string | null;\n size?: number | null;\n size_formatted?: string | null;\n sort?: number | null;\n status?: string | null;\n test_mode?: boolean | null;\n updatedAt?: string | null;\n variant_id?: number | null;\n version?: string | null;\n}\n\nexport interface GeneratedOrderAttributes {\n created_at?: string | null;\n currency?: string | null;\n currency_rate?: string | number | null;\n customer_id?: number | null;\n discount_total?: number | null;\n discount_total_formatted?: string | null;\n discount_total_usd?: number | null;\n first_order_item?: { created_at?: string | null; id?: number | null; order_id?: number | null; price?: number | null; product_id?: number | null; product_name?: string | null; test_mode?: boolean | null; updated_at?: string | null; variant_id?: number | null; variant_name?: string | null; } | null;\n identifier?: string | null;\n order_number?: number | null;\n refunded?: boolean | null;\n refunded_at?: string | null;\n setup_fee?: number | null;\n setup_fee_formatted?: string | null;\n setup_fee_usd?: number | null;\n status?: string | null;\n status_formatted?: string | null;\n store_id?: number | null;\n subtotal?: number | null;\n subtotal_formatted?: string | null;\n subtotal_usd?: number | null;\n tax?: number | null;\n tax_formatted?: string | null;\n tax_inclusive?: boolean | null;\n tax_name?: string | null;\n tax_rate?: string | number | null;\n tax_usd?: number | null;\n test_mode?: boolean | null;\n total?: number | null;\n total_formatted?: string | null;\n total_usd?: number | null;\n updated_at?: string | null;\n urls?: { receipt?: string | null; } | null;\n user_email?: string | null;\n user_name?: string | null;\n}\n\nexport interface GeneratedOrderItemAttributes {\n created_at?: string | null;\n order_id?: number | null;\n price?: number | null;\n product_id?: number | null;\n product_name?: string | null;\n quantity?: number | null;\n updated_at?: string | null;\n variant_id?: number | null;\n variant_name?: string | null;\n}\n\nexport interface GeneratedSubscriptionAttributes {\n billing_anchor?: number | null;\n cancelled?: boolean | null;\n card_brand?: string | null;\n card_last_four?: string | null;\n created_at?: string | null;\n customer_id?: number | null;\n ends_at?: string | null;\n first_subscription_item?: { created_at?: string | null; id?: number | null; price_id?: number | null; quantity?: number | null; subscription_id?: number | null; updated_at?: string | null; } | null;\n order_id?: number | null;\n order_item_id?: number | null;\n pause?: unknown | null;\n payment_processor?: string | null;\n product_id?: number | null;\n product_name?: string | null;\n renews_at?: string | null;\n status?: string | null;\n status_formatted?: string | null;\n store_id?: number | null;\n test_mode?: boolean | null;\n trial_ends_at?: string | null;\n updated_at?: string | null;\n urls?: { customer_portal?: string | null; customer_portal_update_subscription?: string | null; update_payment_method?: string | null; } | null;\n user_email?: string | null;\n user_name?: string | null;\n variant_id?: number | null;\n variant_name?: string | null;\n}\n\nexport interface GeneratedSubscriptionInvoiceAttributes {\n billing_reason?: string | null;\n card_brand?: string | null;\n card_last_four?: string | null;\n created_at?: string | null;\n currency?: string | null;\n currency_rate?: string | number | null;\n customer_id?: number | null;\n discount_total?: number | null;\n discount_total_formatted?: string | null;\n discount_total_usd?: number | null;\n refunded?: boolean | null;\n refunded_amount?: boolean | number | null;\n refunded_amount_formatted?: string | null;\n refunded_amount_usd?: boolean | number | null;\n refunded_at?: string | null;\n status?: string | null;\n status_formatted?: string | null;\n store_id?: number | null;\n subscription_id?: number | null;\n subtotal?: number | null;\n subtotal_formatted?: string | null;\n subtotal_usd?: number | null;\n tax?: number | null;\n tax_formatted?: string | null;\n tax_inclusive?: boolean | null;\n tax_usd?: number | null;\n test_mode?: boolean | null;\n total?: number | null;\n total_formatted?: string | null;\n total_usd?: number | null;\n updated_at?: string | null;\n urls?: { invoice_url?: string | null; } | null;\n user_email?: string | null;\n user_name?: string | null;\n}\n\nexport interface GeneratedSubscriptionItemAttributes {\n created_at?: string | null;\n is_usage_based?: boolean | null;\n price_id?: number | null;\n quantity?: number | null;\n subscription_id?: number | null;\n updated_at?: string | null;\n}\n\nexport interface GeneratedUsageRecordAttributes {\n action?: string | null;\n created_at?: string | null;\n quantity?: number | null;\n subscription_item_id?: number | null;\n updated_at?: string | null;\n}\n\nexport interface GeneratedDiscountAttributes {\n amount?: number | null;\n amount_type?: string | null;\n code?: string | null;\n created_at?: string | null;\n duration?: string | null;\n duration_in_months?: number | null;\n expires_at?: string | null;\n is_limited_redemptions?: boolean | null;\n is_limited_to_products?: boolean | null;\n max_redemptions?: number | null;\n name?: string | null;\n starts_at?: string | null;\n status?: string | null;\n status_formatted?: string | null;\n store_id?: number | null;\n test_mode?: boolean | null;\n updated_at?: string | null;\n}\n\nexport interface GeneratedDiscountRedemptionAttributes {\n amount?: number | null;\n created_at?: string | null;\n discount_amount?: number | null;\n discount_amount_type?: string | null;\n discount_code?: string | null;\n discount_id?: number | null;\n discount_name?: string | null;\n order_id?: number | null;\n updated_at?: string | null;\n}\n\nexport interface GeneratedLicenseKeyAttributes {\n activation_limit?: number | null;\n created_at?: string | null;\n customer_id?: number | null;\n disabled?: boolean | number | null;\n expires_at?: string | null;\n instances_count?: number | null;\n key?: string | null;\n key_short?: string | null;\n order_id?: number | null;\n order_item_id?: number | null;\n product_id?: number | null;\n status?: string | null;\n status_formatted?: string | null;\n store_id?: number | null;\n updated_at?: string | null;\n user_email?: string | null;\n user_name?: string | null;\n}\n\nexport interface GeneratedLicenseKeyInstanceAttributes {\n created_at?: string | null;\n identifier?: string | null;\n license_key_id?: number | null;\n name?: string | null;\n updated_at?: string | null;\n}\n\nexport interface GeneratedCheckoutAttributes {\n checkout_data?: { billing_address?: unknown; custom?: unknown; discount_code?: string | null; email?: string | null; name?: string | null; tax_number?: string | null; variant_quantities?: unknown; } | null;\n checkout_options?: { button_color?: string | null; desc?: boolean | null; discount?: boolean | null; embed?: boolean | null; logo?: boolean | null; media?: boolean | null; skip_trial?: boolean | null; subscription_preview?: boolean | null; } | null;\n created_at?: string | null;\n custom_price?: number | null;\n expires_at?: string | null;\n product_options?: { description?: string | null; enabled_variants?: unknown; media?: unknown; name?: string | null; receipt_button_text?: string | null; receipt_link_url?: string | null; receipt_thank_you_note?: string | null; redirect_url?: string | null; } | null;\n store_id?: number | null;\n test_mode?: boolean | null;\n updated_at?: string | null;\n url?: string | null;\n variant_id?: number | null;\n}\n\nexport interface GeneratedWebhookAttributes {\n created_at?: string | null;\n events?: Array<string | null>;\n last_sent_at?: string | null;\n store_id?: number | null;\n test_mode?: boolean | null;\n updated_at?: string | null;\n url?: string | null;\n}\n\nexport interface GeneratedAffiliateAttributes {\n application_note?: string | null;\n created_at?: string | null;\n products?: unknown | null;\n share_domain?: string | null;\n status?: string | null;\n store_id?: number | null;\n total_earnings?: number | null;\n unpaid_earnings?: number | null;\n updated_at?: string | null;\n user_email?: string | null;\n user_id?: number | null;\n user_name?: string | null;\n}\n\nexport interface GeneratedLemonSqueezyFieldMap {\n user: GeneratedUserAttributes;\n store: GeneratedStoreAttributes;\n customer: GeneratedCustomerAttributes;\n product: GeneratedProductAttributes;\n variant: GeneratedVariantAttributes;\n price: GeneratedPriceAttributes;\n file: GeneratedFileAttributes;\n order: GeneratedOrderAttributes;\n orderItem: GeneratedOrderItemAttributes;\n subscription: GeneratedSubscriptionAttributes;\n subscriptionInvoice: GeneratedSubscriptionInvoiceAttributes;\n subscriptionItem: GeneratedSubscriptionItemAttributes;\n usageRecord: GeneratedUsageRecordAttributes;\n discount: GeneratedDiscountAttributes;\n discountRedemption: GeneratedDiscountRedemptionAttributes;\n licenseKey: GeneratedLicenseKeyAttributes;\n licenseKeyInstance: GeneratedLicenseKeyInstanceAttributes;\n checkout: GeneratedCheckoutAttributes;\n webhook: GeneratedWebhookAttributes;\n affiliate: GeneratedAffiliateAttributes;\n}\n\nexport type GeneratedLemonSqueezyResourceName = keyof GeneratedLemonSqueezyFieldMap;\nexport type GeneratedLemonSqueezyResourceType = (typeof GENERATED_LEMON_SQUEEZY_RESOURCES)[number];\n\n","import { GENERATED_LEMON_SQUEEZY_RESOURCES } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Support manifest: the locally reviewed source of truth for what\n * fresh-squeezy explicitly understands on the Lemon Squeezy platform.\n *\n * Resource/type coverage is generated from Lemon Squeezy object docs with\n * `npm run generate:api-types`. Validator policy remains reviewed here:\n * when the platform adds new webhook events or behavior, bump the entries\n * below and re-snapshot the changelog page with\n * `npm run check:changelog -- --update`.\n *\n * Changelog source: https://docs.lemonsqueezy.com/api/getting-started/changelog\n * Last reviewed: 2026-05-07\n */\n\n/** Resources fresh-squeezy understands today, generated from object docs. */\nexport const SUPPORTED_RESOURCES = GENERATED_LEMON_SQUEEZY_RESOURCES;\n\n/**\n * Webhook events fresh-squeezy expects a production integration to subscribe to\n * at minimum. Consumers can still subscribe to more; the validator only flags\n * missing ones from this list.\n *\n * Rationale:\n * - `order_*` covers one-off purchases and refunds.\n * - `subscription_*` covers the recurring-billing lifecycle.\n * - `subscription_payment_*` covers dunning / retry loops.\n *\n * Confirmed present in the Lemon Squeezy webhook topic list as of 2026-04-24.\n */\nexport const RECOMMENDED_WEBHOOK_EVENTS = [\n \"order_created\",\n \"order_refunded\",\n \"subscription_created\",\n \"subscription_updated\",\n \"subscription_cancelled\",\n \"subscription_resumed\",\n \"subscription_expired\",\n \"subscription_payment_success\",\n \"subscription_payment_failed\",\n] as const;\n\n/**\n * Newer or integration-specific events surfaced as info-level suggestions\n * rather than errors. Missing these is common and not necessarily a\n * misconfiguration.\n *\n * Per-entry changelog provenance (source:\n * https://docs.lemonsqueezy.com/api/getting-started/changelog):\n *\n * - `customer_updated` — added 2026-02-25. Fires when a customer record\n * changes (e.g. email, marketing consent). Needed if the app mirrors\n * customer data locally.\n * - `affiliate_activated` — added 2025-01-21 alongside the affiliates\n * endpoints. Only relevant if the store has an affiliate program.\n * - `license_key_created` / `license_key_updated` — License API events.\n * Only relevant when variants have `has_license_keys: true`.\n */\nexport const OPTIONAL_WEBHOOK_EVENTS = [\n \"customer_updated\",\n \"affiliate_activated\",\n \"license_key_created\",\n \"license_key_updated\",\n] as const;\n\n/**\n * Platform additions we have read and decided *not* to validate against yet,\n * documented here so maintainers see the deliberate gap during review.\n *\n * Tracked so the drift workflow has an \"expected state\" to compare against:\n * if the changelog page changes and none of these items explain it, the diff\n * is probably something new that needs a manifest update.\n */\nexport const ACKNOWLEDGED_CHANGELOG_ENTRIES = [\n {\n date: \"2026-02-25\",\n summary: \"Added customer_updated webhook event.\",\n handledBy: \"OPTIONAL_WEBHOOK_EVENTS\",\n },\n {\n date: \"2025-06-11\",\n summary: \"Added payment_processor attribute to Subscription objects.\",\n handledBy:\n \"Surfaced as SubscriptionAttributes.payment_processor in src/resources/subscriptions.ts.\",\n },\n {\n date: \"2025-01-21\",\n summary: \"Added Affiliates endpoints and affiliate_activated webhook.\",\n handledBy: \"OPTIONAL_WEBHOOK_EVENTS (event only; resource stays v2 scope)\",\n },\n {\n date: \"2024-12-06\",\n summary: \"Added quantity parameter to OrderItem objects.\",\n handledBy: \"OrderItemAttributes.quantity (src/resources/orderItems.ts).\",\n },\n {\n date: \"2024-09-10\",\n summary: \"Added fraudulent Order status.\",\n handledBy: \"OrderAttributes.status union includes 'fraudulent' (src/resources/orders.ts).\",\n },\n {\n date: \"2024-09-04\",\n summary: \"Added Issue an Order refund and Issue a Subscription Invoice refund endpoints.\",\n handledBy:\n \"Not wrapped — reachable via client.request('/v1/orders/:id/refund'). Refund-amount fields surfaced on OrderAttributes.\",\n },\n {\n date: \"2024-08-07\",\n summary:\n \"Added refunded_amount, refunded_amount_usd, refunded_amount_formatted to Order and Subscription invoice objects.\",\n handledBy: \"OrderAttributes.refunded_amount*, SubscriptionInvoiceAttributes.refunded_amount*.\",\n },\n {\n date: \"2024-06-09\",\n summary: \"Added links property to Variant objects.\",\n handledBy: \"VariantAttributes.links (src/resources/variants.ts).\",\n },\n {\n date: \"2024-05-20\",\n summary: \"Added Generate order invoice endpoint.\",\n handledBy: \"Not wrapped — reachable via client.request('/v1/orders/:id/generate-invoice').\",\n },\n {\n date: \"2024-04-09\",\n summary: \"License Key expires_at is now mutable via the update endpoint.\",\n handledBy:\n \"Type unchanged (LicenseKeyAttributes.expires_at already nullable string). Mutation reachable via client.request('PATCH /v1/license-keys/:id').\",\n },\n {\n date: \"2024-03-28\",\n summary: \"Subscription trial_ends_at is now mutable via the update endpoint.\",\n handledBy:\n \"SubscriptionAttributes.trial_ends_at plus CheckoutOptions.skip_trial. Mutations remain reachable via client.request().\",\n },\n {\n date: \"2024-02-20\",\n summary: \"Added urls.update_customer_portal to Subscription objects.\",\n handledBy: \"SubscriptionUrls.update_customer_portal (src/resources/subscriptions.ts).\",\n },\n {\n date: \"2024-02-12\",\n summary:\n \"Added Generate subscription invoice endpoint and invoice_immediately / disable_prorations parameters.\",\n handledBy:\n \"SubscriptionItemUpdateAttributes.invoice_immediately / disable_prorations; invoice endpoint reachable via client.request().\",\n },\n {\n date: \"2024-02-05\",\n summary: \"Added tax_inclusive parameter to Order objects and Subscription invoice objects.\",\n handledBy: \"OrderAttributes.tax_inclusive, SubscriptionInvoiceAttributes.tax_inclusive.\",\n },\n {\n date: \"2024-01-21\",\n summary:\n \"Added setup_fee_enabled / setup_fee to Price objects; setup_fee* to Subscription invoice.\",\n handledBy: \"PriceAttributes.setup_fee*, OrderAttributes.setup_fee*.\",\n },\n {\n date: \"2024-01-15\",\n summary: \"Added unit_price_decimal to Price objects.\",\n handledBy: \"PriceAttributes.unit_price_decimal.\",\n },\n {\n date: \"2024-01-05\",\n summary: \"Added test_mode flag to /v1/users/me meta.\",\n handledBy:\n \"Read in validateConnection to emit MODE_MISMATCH when the key's true mode differs from the caller's declared mode.\",\n },\n {\n date: \"2023-09-19\",\n summary: \"Added customer_portal URLs to Subscription and Customer objects.\",\n handledBy: \"SubscriptionUrls.customer_portal and CustomerUrls.customer_portal.\",\n },\n {\n date: \"2023-08-23\",\n summary:\n \"Added checkout_data.variant_quantities, subscription items, prices, and usage-based billing.\",\n handledBy:\n \"CheckoutData.variant_quantities, SubscriptionItemAttributes, UsageRecordAttributes, PriceAttributes.\",\n },\n {\n date: \"2023-08-14\",\n summary: \"Added customer_id, user_name, user_email to Subscription invoice objects.\",\n handledBy: \"SubscriptionInvoiceAttributes customer fields.\",\n },\n {\n date: \"2023-03-30\",\n summary: \"Added receipt URL to Order objects.\",\n handledBy: \"OrderUrls.receipt.\",\n },\n] as const;\n\nexport type RecommendedEvent = (typeof RECOMMENDED_WEBHOOK_EVENTS)[number];\nexport type OptionalEvent = (typeof OPTIONAL_WEBHOOK_EVENTS)[number];\n","import { sameWebhookUrl } from \"../core/equality.js\";\nimport type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { type WebhookAttributes, listWebhooksForStore } from \"../resources/webhooks.js\";\nimport { OPTIONAL_WEBHOOK_EVENTS, RECOMMENDED_WEBHOOK_EVENTS } from \"../support/manifest.js\";\nimport { probeCollection } from \"./probe.js\";\nimport { ISSUE_CODES, buildResult, issue } from \"./rules.js\";\n\nexport interface WebhookValidationOptions {\n storeId: string | number;\n /** The public URL your app exposes for Lemon Squeezy to POST to. */\n url: string;\n}\n\n/**\n * Confirm a webhook matching `options.url` is registered against the given\n * store, and cross-reference its subscribed events against the support\n * manifest's recommended + optional lists.\n *\n * Missing recommended events = error. Missing optional events = info, because\n * not every integration needs them.\n */\nexport async function validateWebhook(\n http: HttpClient,\n mode: Mode,\n options: WebhookValidationOptions,\n): Promise<ValidationResult<WebhookAttributes>> {\n const issues: ValidationIssue[] = [];\n\n const fetched = await probeCollection(() => listWebhooksForStore(http, options.storeId));\n if (!fetched.ok) {\n issues.push(fetched.issue);\n return buildResult<WebhookAttributes>(\"webhook\", mode, issues, undefined, {\n label: options.url,\n url: options.url,\n });\n }\n const webhooks = fetched.resource;\n\n const match = webhooks.find((webhook) => sameWebhookUrl(webhook.attributes.url, options.url));\n if (!match) {\n issues.push(\n issue(\n ISSUE_CODES.WEBHOOK_NOT_FOUND,\n \"error\",\n `No webhook registered for URL ${options.url} on store ${options.storeId}.`,\n {\n suggestedFix:\n \"Register the webhook in Lemon Squeezy (Settings → Webhooks) and subscribe to the recommended events.\",\n context: { storeId: String(options.storeId), url: options.url },\n },\n ),\n );\n return buildResult<WebhookAttributes>(\"webhook\", mode, issues, undefined, {\n label: options.url,\n url: options.url,\n });\n }\n\n const subscribed = new Set(match.attributes.events);\n const missingRecommended = RECOMMENDED_WEBHOOK_EVENTS.filter((event) => !subscribed.has(event));\n const missingOptional = OPTIONAL_WEBHOOK_EVENTS.filter((event) => !subscribed.has(event));\n\n if (missingRecommended.length > 0) {\n issues.push(\n issue(\n ISSUE_CODES.WEBHOOK_EVENTS_MISSING,\n \"error\",\n `Webhook is missing recommended events: ${missingRecommended.join(\", \")}.`,\n {\n suggestedFix:\n \"Subscribe to all recommended events so the integration survives plan changes and refunds.\",\n context: { missing: missingRecommended.join(\",\") },\n },\n ),\n );\n }\n\n if (missingOptional.length > 0) {\n issues.push(\n issue(\n ISSUE_CODES.WEBHOOK_OPTIONAL_EVENTS,\n \"info\",\n `Optional events not subscribed: ${missingOptional.join(\", \")}.`,\n { context: { missing: missingOptional.join(\",\") } },\n ),\n );\n }\n\n return buildResult(\"webhook\", mode, issues, match.attributes, {\n label: match.attributes.url,\n id: match.id,\n url: match.attributes.url,\n });\n}\n","import { FreshSqueezyError } from \"../core/errors.js\";\nimport type { HttpClient } from \"../core/http.js\";\nimport type { DoctorReport, Mode, ValidationResult } from \"../core/types.js\";\nimport type { DiscountAttributes } from \"../resources/discounts.js\";\nimport type { LicenseKeyAttributes } from \"../resources/licenseKeys.js\";\nimport type { ProductAttributes } from \"../resources/products.js\";\nimport type { StoreAttributes } from \"../resources/stores.js\";\nimport type { WebhookAttributes } from \"../resources/webhooks.js\";\nimport { type ConnectionSummary, validateConnection } from \"./connection.js\";\nimport { type DiscountValidationOptions, validateDiscount } from \"./discount.js\";\nimport { type LicenseKeyValidationOptions, validateLicenseKey } from \"./licenseKey.js\";\nimport { type ProductValidationOptions, validateProduct } from \"./product.js\";\nimport { validateStore } from \"./store.js\";\nimport {\n type SubscriptionPlanValidationOptions,\n validateSubscriptionPlan,\n} from \"./subscriptionPlan.js\";\nimport { type WebhookValidationOptions, validateWebhook } from \"./webhook.js\";\n\n/**\n * Optional targets for the doctor run. If a target is omitted, its validator\n * is skipped — consumers only pay for what they configure.\n */\nexport interface DoctorOptions {\n storeId?: string | number;\n productId?: string | number;\n productIds?: Array<string | number>;\n webhookUrl?: string;\n webhookUrls?: string[];\n discountId?: string | number;\n discountIds?: Array<string | number>;\n licenseKeyId?: string | number;\n licenseKeyIds?: Array<string | number>;\n variantId?: string | number;\n variantIds?: Array<string | number>;\n}\n\n/**\n * The set of validator functions doctor composes.\n *\n * Exposed as an injectable seam so tests can exercise composition order,\n * short-circuiting on connection failure, and option propagation without\n * setting up an HTTP route table for every validator. Production callers\n * normally rely on `DEFAULT_VALIDATORS`, which wires in the real\n * implementations from each `validate/*` module.\n */\nexport interface DoctorValidators {\n connection: (http: HttpClient, mode: Mode) => Promise<ValidationResult<ConnectionSummary>>;\n store: (\n http: HttpClient,\n mode: Mode,\n storeId: string | number,\n ) => Promise<ValidationResult<StoreAttributes>>;\n product: (\n http: HttpClient,\n mode: Mode,\n options: ProductValidationOptions,\n ) => Promise<ValidationResult<ProductAttributes>>;\n webhook: (\n http: HttpClient,\n mode: Mode,\n options: WebhookValidationOptions,\n ) => Promise<ValidationResult<WebhookAttributes>>;\n discount: (\n http: HttpClient,\n mode: Mode,\n options: DiscountValidationOptions,\n ) => Promise<ValidationResult<DiscountAttributes>>;\n licenseKey: (\n http: HttpClient,\n mode: Mode,\n options: LicenseKeyValidationOptions,\n ) => Promise<ValidationResult<LicenseKeyAttributes>>;\n subscriptionPlan: (\n http: HttpClient,\n mode: Mode,\n options: SubscriptionPlanValidationOptions,\n ) => Promise<ValidationResult>;\n}\n\n/** Default wiring used by `doctor` when no overrides are provided. */\nexport const DEFAULT_VALIDATORS: DoctorValidators = {\n connection: validateConnection,\n store: validateStore,\n product: validateProduct,\n webhook: validateWebhook,\n discount: validateDiscount,\n licenseKey: validateLicenseKey,\n subscriptionPlan: validateSubscriptionPlan,\n};\n\n/**\n * Compose every configured validator into a single report. This is the\n * primary entry point for CI health checks: one call, one structured result,\n * one exit code decision.\n *\n * Order is meaningful. Connection runs first because downstream validators\n * have nothing useful to say if the API key is broken.\n *\n * The optional `validators` parameter lets tests inject fakes without a fetch\n * mock; production callers omit it and get `DEFAULT_VALIDATORS`.\n */\nexport async function doctor(\n http: HttpClient,\n mode: Mode,\n options: DoctorOptions = {},\n validators: DoctorValidators = DEFAULT_VALIDATORS,\n): Promise<DoctorReport> {\n assertOptionsCoherent(options);\n\n const results: ValidationResult[] = [];\n const productIds = collectValues(options.productId, options.productIds);\n const webhookUrls = collectValues(options.webhookUrl, options.webhookUrls);\n const discountIds = collectValues(options.discountId, options.discountIds);\n const licenseKeyIds = collectValues(options.licenseKeyId, options.licenseKeyIds);\n const variantIds = collectValues(options.variantId, options.variantIds);\n\n const connection = await validators.connection(http, mode);\n results.push(connection);\n\n if (!connection.ok) {\n return { ok: false, mode, results };\n }\n\n if (options.storeId !== undefined) {\n results.push(await validators.store(http, mode, options.storeId));\n }\n\n for (const productId of productIds) {\n results.push(\n await validators.product(http, mode, {\n productId,\n expectedStoreId: options.storeId,\n }),\n );\n }\n\n if (options.storeId !== undefined) {\n for (const webhookUrl of webhookUrls) {\n results.push(\n await validators.webhook(http, mode, {\n storeId: options.storeId,\n url: webhookUrl,\n }),\n );\n }\n }\n\n if (options.storeId !== undefined) {\n for (const discountId of discountIds) {\n results.push(\n await validators.discount(http, mode, {\n storeId: options.storeId,\n discountId,\n }),\n );\n }\n }\n\n if (options.storeId !== undefined) {\n for (const licenseKeyId of licenseKeyIds) {\n results.push(\n await validators.licenseKey(http, mode, {\n storeId: options.storeId,\n licenseKeyId,\n }),\n );\n }\n }\n\n if (options.storeId !== undefined) {\n for (const variantId of variantIds) {\n results.push(\n await validators.subscriptionPlan(http, mode, {\n storeId: options.storeId,\n variantId,\n }),\n );\n }\n }\n\n const ok = results.every((result) => result.ok);\n return { ok, mode, results };\n}\n\n/**\n * Validators downstream of `storeId` (webhook, discount, licenseKey,\n * subscriptionPlan) cannot run without one. Previously, passing e.g.\n * `discountId` without `storeId` silently skipped the discount validator —\n * the doctor report came back \"ok\" and the consumer assumed coverage that\n * never ran.\n *\n * Treat this as a programmer error and fail fast at the boundary so the\n * miscall is surfaced at construction time rather than masked as a passing\n * doctor run.\n */\nfunction assertOptionsCoherent(options: DoctorOptions): void {\n if (options.storeId !== undefined) return;\n\n const dependent: Array<keyof DoctorOptions> = [];\n if (hasAny(options.webhookUrl, options.webhookUrls)) dependent.push(\"webhookUrl\");\n if (hasAny(options.discountId, options.discountIds)) dependent.push(\"discountId\");\n if (hasAny(options.licenseKeyId, options.licenseKeyIds)) dependent.push(\"licenseKeyId\");\n if (hasAny(options.variantId, options.variantIds)) dependent.push(\"variantId\");\n\n if (dependent.length === 0) return;\n\n throw new FreshSqueezyError({\n code: \"DOCTOR_OPTIONS_INVALID\",\n message: `doctor() received ${dependent.join(\", \")} without storeId. These validators cannot run without a store; pass \\`storeId\\` or remove the dependent options.`,\n });\n}\n\nfunction collectValues<T>(single: T | undefined, many: T[] | undefined): T[] {\n const values = new Set<T>();\n if (single !== undefined) values.add(single);\n for (const value of many ?? []) {\n if (value !== undefined) values.add(value);\n }\n return Array.from(values);\n}\n\nfunction hasAny<T>(single: T | undefined, many: T[] | undefined): boolean {\n return single !== undefined || (many?.length ?? 0) > 0;\n}\n","import { resolveConfig } from \"./core/config.js\";\nimport { HttpClient, type RequestOptions } from \"./core/http.js\";\nimport type { DoctorReport, FreshSqueezyConfig, Mode, ValidationResult } from \"./core/types.js\";\nimport type { DiscountAttributes } from \"./resources/discounts.js\";\nimport type { LicenseKeyAttributes } from \"./resources/licenseKeys.js\";\nimport type { ProductAttributes } from \"./resources/products.js\";\nimport type { StoreAttributes } from \"./resources/stores.js\";\nimport type { WebhookAttributes } from \"./resources/webhooks.js\";\nimport { type ConnectionSummary, validateConnection } from \"./validate/connection.js\";\nimport { type DiscountValidationOptions, validateDiscount } from \"./validate/discount.js\";\nimport { type DoctorOptions, doctor } from \"./validate/doctor.js\";\nimport { type LicenseKeyValidationOptions, validateLicenseKey } from \"./validate/licenseKey.js\";\nimport { type ProductValidationOptions, validateProduct } from \"./validate/product.js\";\nimport { validateStore } from \"./validate/store.js\";\nimport {\n type SubscriptionPlanSummary,\n type SubscriptionPlanValidationOptions,\n validateSubscriptionPlan,\n} from \"./validate/subscriptionPlan.js\";\nimport { type WebhookValidationOptions, validateWebhook } from \"./validate/webhook.js\";\n\n/**\n * The public client. All consumer code flows through the factory below —\n * direct instantiation is intentionally not exposed so we can evolve the\n * internals without breaking callers.\n */\nexport interface FreshSqueezyClient {\n /** Resolved mode (test or live). Surfaced so consumers can log it. */\n readonly mode: Mode;\n\n /** Raw HTTP escape hatch for endpoints fresh-squeezy does not wrap. */\n request<T = unknown>(options: RequestOptions): Promise<T>;\n\n validateConnection(): Promise<ValidationResult<ConnectionSummary>>;\n validateStore(storeId: string | number): Promise<ValidationResult<StoreAttributes>>;\n validateProduct(options: ProductValidationOptions): Promise<ValidationResult<ProductAttributes>>;\n validateWebhook(options: WebhookValidationOptions): Promise<ValidationResult<WebhookAttributes>>;\n validateDiscount(\n options: DiscountValidationOptions,\n ): Promise<ValidationResult<DiscountAttributes>>;\n validateLicenseKey(\n options: LicenseKeyValidationOptions,\n ): Promise<ValidationResult<LicenseKeyAttributes>>;\n validateSubscriptionPlan(\n options: SubscriptionPlanValidationOptions,\n ): Promise<ValidationResult<SubscriptionPlanSummary>>;\n doctor(options?: DoctorOptions): Promise<DoctorReport>;\n}\n\n/**\n * Create a fresh-squeezy client. Zero-config usage reads\n * `LEMON_SQUEEZY_API_KEY`, `LEMON_SQUEEZY_STORE_ID`, and `LEMON_SQUEEZY_MODE`\n * from `process.env`.\n *\n * @example\n * ```ts\n * const lemon = createFreshSqueezy();\n * const report = await lemon.doctor();\n * if (!report.ok) process.exit(1);\n * ```\n */\nexport function createFreshSqueezy(config: FreshSqueezyConfig = {}): FreshSqueezyClient {\n const resolved = resolveConfig(config);\n const http = new HttpClient(resolved);\n\n return {\n mode: resolved.mode,\n request: (options) => http.request(options),\n validateConnection: () => validateConnection(http, resolved.mode),\n validateStore: (storeId) => validateStore(http, resolved.mode, storeId),\n validateProduct: (options) => validateProduct(http, resolved.mode, options),\n validateWebhook: (options) => validateWebhook(http, resolved.mode, options),\n validateDiscount: (options) => validateDiscount(http, resolved.mode, options),\n validateLicenseKey: (options) => validateLicenseKey(http, resolved.mode, options),\n validateSubscriptionPlan: (options) => validateSubscriptionPlan(http, resolved.mode, options),\n doctor: (options) =>\n doctor(http, resolved.mode, {\n storeId: options?.storeId ?? resolved.storeId,\n productId: options?.productId,\n productIds: options?.productIds,\n webhookUrl: options?.webhookUrl,\n webhookUrls: options?.webhookUrls,\n discountId: options?.discountId,\n discountIds: options?.discountIds,\n licenseKeyId: options?.licenseKeyId,\n licenseKeyIds: options?.licenseKeyIds,\n variantId: options?.variantId,\n variantIds: options?.variantIds,\n }),\n };\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedAffiliateAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport type AffiliateStatus = \"active\" | \"pending\" | \"disabled\";\n\n/**\n * Affiliate attributes added with the 2025-01-21 Affiliates API surface.\n * No validator consumes these yet; the helpers give typed access while\n * preserving the raw request escape hatch for mutations and includes.\n */\nexport interface AffiliateAttributes extends GeneratedAffiliateAttributes {\n store_id: number;\n user_id: number;\n user_name: string;\n user_email: string;\n share_domain: string;\n status: AffiliateStatus;\n application_note?: string | null;\n products?: unknown;\n total_earnings: number;\n unpaid_earnings: number;\n created_at?: string;\n updated_at?: string;\n}\n\nexport async function getAffiliate(\n http: HttpClient,\n affiliateId: string | number,\n): Promise<JsonApiResource<AffiliateAttributes>> {\n return http.getResource<AffiliateAttributes>(`/v1/affiliates/${affiliateId}`);\n}\n\nexport async function listAffiliatesForStore(\n http: HttpClient,\n storeId: string | number,\n): Promise<JsonApiResource<AffiliateAttributes>[]> {\n return http.paginate<AffiliateAttributes>(\"/v1/affiliates\", {\n \"filter[store_id]\": String(storeId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedCheckoutAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport type CheckoutLocale =\n | \"bg\"\n | \"hr\"\n | \"cs\"\n | \"da\"\n | \"nl\"\n | \"en\"\n | \"et\"\n | \"fil\"\n | \"fi\"\n | \"fr\"\n | \"de\"\n | \"el\"\n | \"hu\"\n | \"id\"\n | \"it\"\n | \"ja\"\n | \"ko\"\n | \"lv\"\n | \"lt\"\n | \"ms\"\n | \"mt\"\n | \"pl\"\n | \"pt\"\n | \"ro\"\n | \"ru\"\n | \"zh-CN\"\n | \"sk\"\n | \"sl\"\n | \"es\"\n | \"sv\"\n | \"th\"\n | \"tr\"\n | \"vi\";\n\nexport interface CheckoutProductOptions {\n name?: string;\n description?: string;\n media?: string[];\n redirect_url?: string;\n receipt_button_text?: string;\n receipt_link_url?: string;\n receipt_thank_you_note?: string;\n enabled_variants?: number[];\n}\n\nexport interface CheckoutOptions {\n embed?: boolean;\n media?: boolean;\n logo?: boolean;\n desc?: boolean;\n discount?: boolean;\n subscription_preview?: boolean;\n /** Added 2024-03-28. */\n skip_trial?: boolean;\n /** Deprecated 2024-09-04; keep typed so old payloads still compile. */\n dark?: boolean;\n background_color?: string;\n headings_color?: string;\n primary_text_color?: string;\n secondary_text_color?: string;\n links_color?: string;\n borders_color?: string;\n checkbox_color?: string;\n active_state_color?: string;\n button_color?: string;\n button_text_color?: string;\n terms_privacy_color?: string;\n locale?: CheckoutLocale | string | null;\n}\n\nexport interface CheckoutVariantQuantity {\n variant_id: number;\n quantity: number;\n}\n\nexport interface CheckoutData {\n email?: string;\n name?: string;\n billing_address?: {\n country?: string;\n zip?: string;\n };\n tax_number?: string;\n discount_code?: string;\n custom?: Record<string, unknown>;\n /** Added 2023-08-23 for multi-variant checkout quantities. */\n variant_quantities?: CheckoutVariantQuantity[];\n}\n\nexport interface CheckoutPreview {\n currency: string;\n currency_rate: number | string;\n subtotal: number;\n discount_total: number;\n tax: number;\n total: number;\n subtotal_usd: number;\n discount_total_usd: number;\n tax_usd: number;\n total_usd: number;\n subtotal_formatted: string;\n discount_total_formatted: string;\n tax_formatted: string;\n total_formatted: string;\n}\n\nexport interface CheckoutAttributes extends GeneratedCheckoutAttributes {\n store_id: number;\n variant_id: number;\n custom_price?: number | null;\n product_options?: CheckoutProductOptions;\n checkout_options?: CheckoutOptions;\n checkout_data?: CheckoutData;\n preview?: CheckoutPreview;\n expires_at?: string | null;\n url?: string;\n created_at?: string;\n updated_at?: string;\n test_mode?: boolean;\n}\n\nexport async function getCheckout(\n http: HttpClient,\n checkoutId: string | number,\n): Promise<JsonApiResource<CheckoutAttributes>> {\n return http.getResource<CheckoutAttributes>(`/v1/checkouts/${checkoutId}`);\n}\n\nexport async function listCheckoutsForStore(\n http: HttpClient,\n storeId: string | number,\n): Promise<JsonApiResource<CheckoutAttributes>[]> {\n return http.paginate<CheckoutAttributes>(\"/v1/checkouts\", {\n \"filter[store_id]\": String(storeId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedCustomerAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport type CustomerStatus =\n | \"subscribed\"\n | \"unsubscribed\"\n | \"archived\"\n | \"requires_verification\"\n | \"invalid_email\"\n | \"bounced\";\n\nexport interface CustomerUrls {\n customer_portal?: string | null;\n}\n\n/**\n * Lemon Squeezy customer attributes. The customer portal URL was called out\n * in the 2023-09-19 changelog and is useful for apps linking users back to\n * billing without another API shape.\n */\nexport interface CustomerAttributes extends GeneratedCustomerAttributes {\n store_id: number;\n name: string;\n email: string;\n status: CustomerStatus;\n city?: string | null;\n region?: string | null;\n country?: string | null;\n total_revenue_currency: number;\n mrr: number;\n status_formatted: string;\n country_formatted?: string | null;\n total_revenue_currency_formatted: string;\n mrr_formatted: string;\n urls?: CustomerUrls;\n created_at?: string;\n updated_at?: string;\n test_mode?: boolean;\n}\n\nexport async function getCustomer(\n http: HttpClient,\n customerId: string | number,\n): Promise<JsonApiResource<CustomerAttributes>> {\n return http.getResource<CustomerAttributes>(`/v1/customers/${customerId}`);\n}\n\nexport async function listCustomersForStore(\n http: HttpClient,\n storeId: string | number,\n): Promise<JsonApiResource<CustomerAttributes>[]> {\n return http.paginate<CustomerAttributes>(\"/v1/customers\", {\n \"filter[store_id]\": String(storeId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedDiscountRedemptionAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport interface DiscountRedemptionAttributes extends GeneratedDiscountRedemptionAttributes {\n discount_id: number;\n order_id: number;\n discount_name: string;\n discount_code: string;\n discount_amount: number;\n discount_amount_type: \"percent\" | \"fixed\";\n amount: number;\n created_at?: string;\n updated_at?: string;\n}\n\nexport async function getDiscountRedemption(\n http: HttpClient,\n discountRedemptionId: string | number,\n): Promise<JsonApiResource<DiscountRedemptionAttributes>> {\n return http.getResource<DiscountRedemptionAttributes>(\n `/v1/discount-redemptions/${discountRedemptionId}`,\n );\n}\n\nexport async function listDiscountRedemptionsForDiscount(\n http: HttpClient,\n discountId: string | number,\n): Promise<JsonApiResource<DiscountRedemptionAttributes>[]> {\n return http.paginate<DiscountRedemptionAttributes>(\"/v1/discount-redemptions\", {\n \"filter[discount_id]\": String(discountId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedFileAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Lemon Squeezy file attributes, generated from the public object docs.\n * Exposed so consumers can type file-resource escape-hatch calls without\n * waiting for a hand-written validator.\n */\nexport interface FileAttributes extends GeneratedFileAttributes {}\n\nexport async function getFile(\n http: HttpClient,\n fileId: string | number,\n): Promise<JsonApiResource<FileAttributes>> {\n return http.getResource<FileAttributes>(`/v1/files/${fileId}`);\n}\n\nexport async function listFilesForVariant(\n http: HttpClient,\n variantId: string | number,\n): Promise<JsonApiResource<FileAttributes>[]> {\n return http.paginate<FileAttributes>(\"/v1/files\", {\n \"filter[variant_id]\": String(variantId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedLicenseKeyInstanceAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport interface LicenseKeyInstanceAttributes extends GeneratedLicenseKeyInstanceAttributes {\n license_key_id: number;\n identifier: string;\n name: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport async function getLicenseKeyInstance(\n http: HttpClient,\n licenseKeyInstanceId: string | number,\n): Promise<JsonApiResource<LicenseKeyInstanceAttributes>> {\n return http.getResource<LicenseKeyInstanceAttributes>(\n `/v1/license-key-instances/${licenseKeyInstanceId}`,\n );\n}\n\nexport async function listLicenseKeyInstancesForLicenseKey(\n http: HttpClient,\n licenseKeyId: string | number,\n): Promise<JsonApiResource<LicenseKeyInstanceAttributes>[]> {\n return http.paginate<LicenseKeyInstanceAttributes>(\"/v1/license-key-instances\", {\n \"filter[license_key_id]\": String(licenseKeyId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedOrderItemAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Order item attributes. `quantity` was added to the documented object in\n * the 2024-12-06 changelog and is needed for multi-quantity reconciliation.\n */\nexport interface OrderItemAttributes extends GeneratedOrderItemAttributes {\n order_id: number;\n product_id: number;\n variant_id: number;\n product_name: string;\n variant_name: string;\n price: number;\n quantity: number;\n created_at?: string;\n updated_at?: string;\n}\n\nexport async function getOrderItem(\n http: HttpClient,\n orderItemId: string | number,\n): Promise<JsonApiResource<OrderItemAttributes>> {\n return http.getResource<OrderItemAttributes>(`/v1/order-items/${orderItemId}`);\n}\n\nexport async function listOrderItemsForOrder(\n http: HttpClient,\n orderId: string | number,\n): Promise<JsonApiResource<OrderItemAttributes>[]> {\n return http.paginate<OrderItemAttributes>(\"/v1/order-items\", {\n \"filter[order_id]\": String(orderId),\n });\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedOrderAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport interface OrderUrls {\n receipt?: string;\n}\n\nexport interface OrderFirstItem {\n id: number;\n order_id?: number;\n product_id: number;\n variant_id: number;\n product_name: string;\n variant_name: string;\n price: number;\n quantity?: number;\n created_at?: string;\n updated_at?: string;\n}\n\n/**\n * Subset of Lemon Squeezy order attributes fresh-squeezy understands.\n *\n * fresh-squeezy does not (yet) validate orders — the type is exported so\n * consumers using the raw `client.request<OrderAttributes>()` escape hatch\n * get a typed response that includes platform additions the official SDK\n * has not yet picked up.\n *\n * Field provenance against the changelog\n * (https://docs.lemonsqueezy.com/api/getting-started/changelog):\n *\n * - `status: \"fraudulent\"` — added 2024-09-10. Indicates a manual fraud\n * flag; treat as terminal and refund or chargeback as appropriate.\n * - `refunded_amount`, `refunded_amount_usd`, `refunded_amount_formatted`\n * — added 2024-08-07. Useful for consumers reconciling partial refunds.\n * - `tax_inclusive` — added 2024-02-05.\n */\nexport interface OrderAttributes extends GeneratedOrderAttributes {\n store_id: number;\n customer_id?: number;\n identifier: string;\n order_number: number;\n user_name: string;\n user_email: string;\n currency: string;\n currency_rate: string;\n subtotal: number;\n setup_fee?: number;\n discount_total: number;\n tax: number;\n tax_inclusive?: boolean;\n total: number;\n refunded_amount?: number;\n subtotal_usd?: number;\n setup_fee_usd?: number;\n discount_total_usd?: number;\n tax_usd?: number;\n total_usd?: number;\n refunded_amount_usd?: number | null;\n tax_name?: string | null;\n tax_rate?: number | string | null;\n refunded_amount_formatted?: string | null;\n refunded?: boolean;\n refunded_at?: string | null;\n status: \"pending\" | \"failed\" | \"paid\" | \"refunded\" | \"fraudulent\" | \"partial_refund\";\n status_formatted: string;\n subtotal_formatted?: string;\n setup_fee_formatted?: string;\n discount_total_formatted?: string;\n tax_formatted?: string;\n total_formatted?: string;\n first_order_item?: OrderFirstItem | null;\n urls?: OrderUrls;\n created_at?: string;\n updated_at?: string;\n test_mode?: boolean;\n}\n\n/**\n * Fetch a single order by ID. Provided for consumers that want typed\n * access to orders without hand-rolling the JSON:API envelope; the\n * library intentionally has no order *validator* yet.\n */\nexport async function getOrder(\n http: HttpClient,\n orderId: string | number,\n): Promise<JsonApiResource<OrderAttributes>> {\n return http.getResource<OrderAttributes>(`/v1/orders/${orderId}`);\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedPriceAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * Subset of Lemon Squeezy price attributes. Prices are nested under\n * variants in the API surface; consumers that read them directly via\n * `client.request<PriceAttributes>()` get a typed response that includes\n * the setup-fee fields the official SDK does not yet surface.\n *\n * Field provenance:\n * - `setup_fee_enabled`, `setup_fee` — added 2024-01-21.\n * - `unit_price_decimal` — added 2024-01-15.\n */\nexport interface PriceAttributes extends GeneratedPriceAttributes {\n variant_id: number;\n category: \"one_time\" | \"subscription\" | \"lead_magnet\" | \"pwyw\";\n scheme: \"standard\" | \"package\" | \"graduated\" | \"volume\";\n usage_aggregation?: string | null;\n unit_price?: number;\n /** Added 2024-01-15. Decimal string for sub-cent accuracy. */\n unit_price_decimal?: string | null;\n package_size?: number;\n tiers?: unknown;\n renewal_interval_unit?: \"day\" | \"week\" | \"month\" | \"year\" | null;\n renewal_interval_quantity?: number | null;\n trial_interval_unit?: \"day\" | \"week\" | \"month\" | \"year\" | null;\n trial_interval_quantity?: number | null;\n min_price?: number | null;\n suggested_price?: number | null;\n tax_code?: string;\n /** Added 2024-01-21. */\n setup_fee_enabled?: boolean | null;\n /** Added 2024-01-21. Setup fee in cents, paired with `setup_fee_enabled`. */\n setup_fee?: number | null;\n created_at?: string;\n updated_at?: string;\n}\n\n/**\n * Fetch a single price by ID. Provided for consumers reaching the prices\n * endpoint directly — fresh-squeezy reads price information through the\n * subscription plan validator and does not surface a price validator yet.\n */\nexport async function getPrice(\n http: HttpClient,\n priceId: string | number,\n): Promise<JsonApiResource<PriceAttributes>> {\n return http.getResource<PriceAttributes>(`/v1/prices/${priceId}`);\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedSubscriptionAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\n/**\n * URLs Lemon Squeezy attaches to a subscription so consumers can deep-link\n * users to billing pages. The set has grown over time; we keep optional\n * fields for forward-compatibility.\n */\nexport interface SubscriptionUrls {\n update_payment_method?: string;\n customer_portal?: string;\n /** Added 2024-02-20: lets consumers offer \"manage subscription\" without an extra round-trip. */\n update_customer_portal?: string | null;\n /** Current API examples also surface this key name for PayPal update flows. */\n customer_portal_update_subscription?: string | null;\n}\n\nexport interface SubscriptionPause {\n mode: \"void\" | \"free\" | string;\n resumes_at?: string | null;\n}\n\nexport interface SubscriptionItemSummary {\n id: number;\n subscription_id: number;\n price_id: number;\n quantity: number;\n created_at?: string;\n updated_at?: string;\n}\n\n/**\n * Subset of subscription attributes. fresh-squeezy does not (yet) validate\n * subscriptions — this type is exported so consumers using the raw\n * `client.request<SubscriptionAttributes>()` escape hatch get a typed\n * response that includes platform additions the official SDK has not yet\n * picked up.\n *\n * Field provenance against the changelog:\n * - `payment_processor` — added 2025-06-11.\n * - `urls.update_customer_portal` — added 2024-02-20.\n * - `tax_inclusive` — invoice-only in current API docs; retained here as\n * a compatibility field for consumers that previously normalised\n * subscription and subscription invoice payloads together.\n */\nexport interface SubscriptionAttributes extends GeneratedSubscriptionAttributes {\n store_id: number;\n customer_id: number;\n order_id?: number;\n order_item_id?: number;\n product_id: number;\n variant_id: number;\n product_name: string;\n variant_name: string;\n user_name: string;\n user_email: string;\n status: \"on_trial\" | \"active\" | \"paused\" | \"past_due\" | \"unpaid\" | \"cancelled\" | \"expired\";\n status_formatted: string;\n card_brand?: string | null;\n card_last_four?: string | null;\n /** Added 2025-06-11. Identifies which payment provider processed the transaction. */\n payment_processor?: \"stripe\" | \"lemonsqueezy\" | \"paypal\" | string;\n pause?: SubscriptionPause | null;\n cancelled?: boolean;\n trial_ends_at?: string | null;\n billing_anchor?: number;\n first_subscription_item?: SubscriptionItemSummary | null;\n urls?: SubscriptionUrls;\n renews_at?: string | null;\n ends_at?: string | null;\n created_at?: string;\n updated_at?: string;\n test_mode?: boolean;\n /** Compatibility field for older fresh-squeezy consumers; prefer SubscriptionInvoiceAttributes.tax_inclusive. */\n tax_inclusive?: boolean;\n}\n\n/**\n * Fetch a single subscription by ID. Provided for consumers that want\n * typed access without hand-rolling the JSON:API envelope; the library\n * has no subscription *validator* yet.\n */\nexport async function getSubscription(\n http: HttpClient,\n subscriptionId: string | number,\n): Promise<JsonApiResource<SubscriptionAttributes>> {\n return http.getResource<SubscriptionAttributes>(`/v1/subscriptions/${subscriptionId}`);\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedSubscriptionInvoiceAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport type SubscriptionInvoiceStatus = \"pending\" | \"paid\" | \"void\" | \"refunded\" | \"partial_refund\";\n\nexport type SubscriptionInvoiceBillingReason = \"initial\" | \"renewal\" | \"updated\";\n\nexport interface SubscriptionInvoiceUrls {\n invoice_url?: string;\n}\n\n/**\n * Subscription invoice attributes, including fields added across the\n * changelog: customer identity, tax inclusion, and partial refund amounts.\n */\nexport interface SubscriptionInvoiceAttributes extends GeneratedSubscriptionInvoiceAttributes {\n store_id: number;\n subscription_id: number;\n customer_id: number;\n user_name: string;\n user_email: string;\n billing_reason: SubscriptionInvoiceBillingReason | string;\n card_brand?: string | null;\n card_last_four?: string | null;\n currency: string;\n currency_rate: string;\n status: SubscriptionInvoiceStatus;\n status_formatted: string;\n refunded: boolean;\n refunded_at: string | null;\n subtotal: number;\n discount_total: number;\n tax: number;\n tax_inclusive: boolean;\n total: number;\n refunded_amount: number;\n subtotal_usd: number;\n discount_total_usd: number;\n tax_usd: number;\n total_usd: number;\n refunded_amount_usd: number;\n subtotal_formatted: string;\n discount_total_formatted: string;\n tax_formatted: string;\n total_formatted: string;\n refunded_amount_formatted: string;\n urls?: SubscriptionInvoiceUrls;\n created_at?: string;\n updated_at?: string;\n test_mode?: boolean;\n}\n\nexport async function getSubscriptionInvoice(\n http: HttpClient,\n subscriptionInvoiceId: string | number,\n): Promise<JsonApiResource<SubscriptionInvoiceAttributes>> {\n return http.getResource<SubscriptionInvoiceAttributes>(\n `/v1/subscription-invoices/${subscriptionInvoiceId}`,\n );\n}\n\nexport async function listSubscriptionInvoicesForSubscription(\n http: HttpClient,\n subscriptionId: string | number,\n): Promise<JsonApiResource<SubscriptionInvoiceAttributes>[]> {\n return http.paginate<SubscriptionInvoiceAttributes>(\"/v1/subscription-invoices\", {\n \"filter[subscription_id]\": String(subscriptionId),\n });\n}\n","import type { HttpClient, RequestOptions } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedSubscriptionItemAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport interface SubscriptionItemAttributes extends GeneratedSubscriptionItemAttributes {\n subscription_id: number;\n price_id: number;\n quantity: number;\n is_usage_based: boolean;\n created_at?: string;\n updated_at?: string;\n}\n\n/**\n * Attributes accepted by the update subscription item endpoint. The proration\n * controls were added in the 2024-02-12 changelog.\n */\nexport interface SubscriptionItemUpdateAttributes {\n quantity?: number;\n invoice_immediately?: boolean;\n disable_prorations?: boolean;\n}\n\nexport async function getSubscriptionItem(\n http: HttpClient,\n subscriptionItemId: string | number,\n): Promise<JsonApiResource<SubscriptionItemAttributes>> {\n return http.getResource<SubscriptionItemAttributes>(\n `/v1/subscription-items/${subscriptionItemId}`,\n );\n}\n\nexport async function listSubscriptionItemsForSubscription(\n http: HttpClient,\n subscriptionId: string | number,\n): Promise<JsonApiResource<SubscriptionItemAttributes>[]> {\n return http.paginate<SubscriptionItemAttributes>(\"/v1/subscription-items\", {\n \"filter[subscription_id]\": String(subscriptionId),\n });\n}\n\nexport function buildSubscriptionItemUpdateBody(\n subscriptionItemId: string | number,\n attributes: SubscriptionItemUpdateAttributes,\n): RequestOptions[\"body\"] {\n return {\n data: {\n type: \"subscription-items\",\n id: String(subscriptionItemId),\n attributes,\n },\n };\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.js\";\nimport type { GeneratedUsageRecordAttributes } from \"../generated/lemonSqueezyApiTypes.js\";\n\nexport type UsageRecordAction = \"increment\" | \"set\";\n\nexport interface UsageRecordAttributes extends GeneratedUsageRecordAttributes {\n subscription_item_id: number;\n quantity: number;\n action: UsageRecordAction;\n created_at?: string;\n updated_at?: string;\n}\n\nexport async function getUsageRecord(\n http: HttpClient,\n usageRecordId: string | number,\n): Promise<JsonApiResource<UsageRecordAttributes>> {\n return http.getResource<UsageRecordAttributes>(`/v1/usage-records/${usageRecordId}`);\n}\n\nexport async function listUsageRecordsForSubscriptionItem(\n http: HttpClient,\n subscriptionItemId: string | number,\n): Promise<JsonApiResource<UsageRecordAttributes>[]> {\n return http.paginate<UsageRecordAttributes>(\"/v1/usage-records\", {\n \"filter[subscription_item_id]\": String(subscriptionItemId),\n });\n}\n"],"mappings":"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","names":["userDoc","stores"]}
|